Logo

dev-resources.site

for different kinds of informations.

Let's create Data Table. Part 7: Dark theme and refactoring

Published at
1/14/2025
Categories
webdev
tailwindcss
ui
ux
Author
morewings
Categories
4 categories in total
webdev
open
tailwindcss
open
ui
open
ux
open
Author
9 person written this
morewings
open
Let's create Data Table. Part 7: Dark theme and refactoring

This is a single article from a series about creating of an advanced Data table component using React, TanStack Table 8, Tailwind CSS and Headless UI.

In the previous episode, we implemented a column filtering feature for the table columns.

This article will focus on a collection of smaller, yet impactful enhancements to our project. Think of it as an Interdimensional Cable episode in Rick and Morty, addressing a few important features and refactorings that, while not the main event, are crucial for improving the overall code quality, maintainability, and user experience.

Rick and Morty

Tokenization

Tokenization refers to the practice of representing design elements and their properties as reusable variables (tokens).

Since we are using sizes and paddings from Tailwind, we need to tokenize only color values. Instead of hard-coding specific values (e.g., teal-600), we will assign meaningful names to these elements (primary, secondary, etc.).

This way, changes to a token affect all elements that use it, ensuring consistency across the entire application. This will be particularly useful for the dark theme.

We put our tokenized colors into ./tailwind.config.js file as a theme.

const colors = require('tailwindcss/colors');

export default {
  //...
  theme: {
    extend: {
      colors: {
        primary: colors.stone['600'],
        secondary: colors.cyan['800'],
        backgroundLight: colors.white,
        textDark: colors.stone['100'],
        textLight: colors.stone['950'],
        hoverColor: colors.cyan['100'],
        borderColor: colors.stone['300'],
      },
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

Then we apply these theme variables instead of directly using Tailwind colors. So bg-stone-600 CSS class becomes bg-primary and so on.

Dark theme

In the past, Windows 95 graced our screens with its predominantly white-blueish backgrounds, a hallmark of its time. As an advanced operating system, it came equipped with state-of-the-art hardware and software, which garnered much appreciation from my generation. WYSYWIG interface paradigm was a refreshing departure from the older, terminal-based environments dominated by black and green hues. Interestingly, today's trend has shifted in the opposite direction.

Now when we have our colors organized we can start making dark theme to respect modern generation's aesthetic preferences.

Dark table demo

Modern browsers provide built-in support for dark mode preferences through the prefers-color-scheme media query. This media query acts as a bridge between the user's system-wide dark mode setting and your application's styles.

Tailwind CSS offers a convenient way to implement dark mode with its dark: helper class. This class allows you to easily define styles specific to the dark mode presentation.

Here are our new dark color tokens. We have background token assigned #000 (pure black) value to make the table dark. For the rest of the tokens, we adjusted saturation to achieve the same contrast as we had with a white background.

To achieve a seamless dark mode experience, we'll begin by extending your existing theme to include dark variants of your color tokens. These dark color tokens will be used to style the Data table in dark mode. For instance, we can set the background token to #000 (pure black) to create a dark background for the table.

For the remaining color tokens, it's essential to adjust their saturation levels. This ensures that the text and other elements maintain sufficient contrast against the dark background, preserving readability.

primaryDark: colors.stone['700'],
backgroundDark: colors.black,
textDark: colors.stone['100'],
hoverColorDark: colors.cyan['600'],
borderColorDark: colors.stone['600'],
Enter fullscreen mode Exit fullscreen mode

Here is how we apply the dark theme to the component.

<Input
  //...
  className={classNames(
    //..
    // focus styles light
    'focus:bg-backgroundLight/85 focus:text-textLight focus:placeholder-transparent',
    // focus styles dark
    'dark:focus:bg-backgroundDark/85 dark:focus:text-textDark',
    'transition-all duration-200',
  )}
/>
Enter fullscreen mode Exit fullscreen mode

Columns permissions

To improve the User Experience of the table, we are going to fine tune column capabilities. The goal is to disable some features for some columns.

Disabled actions demo

We will extend the existing hook in src/DataTable/features/useColumnActions.tsx to include a new property: disabled.
This property will be a boolean flag indicating whether a specific feature is disabled for the current column. The necessary information about the column's configuration will be retrieved from the TanStack Table Context API.

type ColumnAction = {
  //...
  /** True when table capability is disabled by config */
  disabled?: boolean;
};

/**
 * React hook which returns an array of table column actions config objects
 */
export const useColumnActions = (
  context: HeaderContext<Row, unknown>,
): ColumnAction[] => {
  //...
  return useMemo<ColumnAction[]>(
    () => [
      // Pin left button
      {
        // ...
        disabled: !context.column.getCanPin(),
      },
      // Pin right button
      {
        //...
        disabled: !context.column.getCanPin(),
      },
      // Sort ASC button
      {
        //...
        disabled: !context.column.getCanSort(),
      },
      // Sort DESC button
      {
        //...
        disabled: !context.column.getCanSort(),
      },
      // Filter button
      {
        //...
        disabled: !context.column.getCanFilter(),
      },
    ],
    [/*...*/],
  );
};
Enter fullscreen mode Exit fullscreen mode

Now, we will use this logic inside src/DataTable/cells/HeaderCell.tsx. We connect the disabled property from the hook to the corresponding Button component prop. Also, we disable pointer events and set 30% opacity for the disabled state using the corresponding Tailwind CSS helper.

In src/DataTable/cells/HeaderCell.tsx, we will integrate the disabled property obtained from the useColumnActions hook into the corresponding Button component.

If the disabled flag is set to true:

  • Button component will be rendered in a disabled state.

  • Pointer events will be disabled for the button using Tailwind CSS pointer-events-none class.

  • Text and icon opacity will be reduced to 30% to visually indicate button state.

{items.map(({ label, icon: Icon, onClick, disabled }) => (
  <MenuItem key={label} as={Fragment}>
    {() => (
      <Button
        disabled={disabled}
        onClick={onClick}
        className={classNames(
          // ...
          // disabled styles
          'disabled:text-textDark/30 disabled:pointer-events-none'
        )}
      >
        {Icon}
        <div>{label}</div>
      </Button>
    )}
  </MenuItem>
))}
Enter fullscreen mode Exit fullscreen mode

Finally, we have to set enableColumnFilter, enablePinning and enableSorting properties for table columns inside src/DataTable/columnsConfig.tsx.

const columnHelper = createColumnHelper<Row>();

export const columns = [
  //...
  columnHelper.accessor('randomDecimal', {
    enableColumnFilter: false,
    enablePinning: false,
    enableSorting: false,
    //...
  }
  //...
]
Enter fullscreen mode Exit fullscreen mode

Debug mode

Debug mode

The last change is a quality of life improvement. Tanstack has a convenient debugging tools available. So we are going to implement debugAll mode.

Inside src/DataTable/DataTable.tsx we will extend component props with debug property. And set this property at useReactTable hook parameters.

type Props = {
  //...
  /**
   * Enable TanStack table debug mode
   * @see https://tanstack.com/table/latest/docs/api/core/table#debugall
   */
  debug?: boolean;
};

export const DataTable: FC<Props> = ({ tableData, locale = 'en-US', debug }) => {
  //...

  const table = useReactTable({
    //...
    debugAll: debug
  });
 //...
};
Enter fullscreen mode Exit fullscreen mode

Chapter summary

In these 7 articles we created the Data table component from scratch, using TanStack table, Tailwind CSS and Headless UI. We implemented the following features:

  • Multidimensional Scroll: Enables smooth and synchronized scrolling in both horizontal and vertical directions, with sticky header maintaining alignment across the viewport

  • Responsive Design: Adapts seamlessly to various screen sizes and supports a wide range of data types, including text, numbers, dates, and limited enumerable lists.

  • Customizable Subcomponents: Dialogs, Dropdown menus, and custom input fields for enhanced user interaction.

  • High-Performance Virtualization: Enables virtualization techniques to efficiently render around 50,000 rows without significant performance degradation, even on less powerful machines.

  • Column Pinning: Allows users to pin columns to the left or right of the viewport, improving data focus and readability.

  • Advanced Sorting and Filtering: Implements intelligent sorting and filtering mechanisms that account for the specific data formats of each column.

  • Dark Mode Support: Provides a user-friendly dark mode option.

Here is the complete demo of the exercise.

To be continued.

ui Article's
30 articles in total
Favicon
UX Writing Challenge: Day 9
Favicon
Understanding Galadriel CSS and Nenyr: A Comprehensive Guide to Declarative Styling
Favicon
Let's create Data Table. Part 7: Dark theme and refactoring
Favicon
UX Writing Challenge: Day 8
Favicon
Car Services Booking Mobile App - UX UI🪄
Favicon
The System of UI Components in Front-end Projects
Favicon
UX Writing Challenge: Day 7
Favicon
UX Writing Challenge: Day 6
Favicon
Best UI Libraries for Developers (trust me, I'm a dev)
Favicon
UX Writing Challenge: Day 5
Favicon
The System of Front-end UI Components
Favicon
UX Writing Challenge: Day 4
Favicon
Creating a Product Overview Layout Using Tailwind CSS
Favicon
UI Design Trends: Exploring Modern Aesthetics
Favicon
The Features of Front-end UI Components
Favicon
UX Writing Challenge: Day 3
Favicon
Color Theory in UI Design
Favicon
Context vs. State: Why Context is King in Software Systems
Favicon
The UI/UX Challenges in Building a Financial App and Ways to Overcome Them
Favicon
How to Easily Load JSON Data in .NET MAUI TreeView?
Favicon
The Core Concepts of Front-end UI Components
Favicon
UX Writing Challenge: Day 2
Favicon
My IT Internship at Aditya Birla Fashion and Retail Ltd.
Favicon
What’s New in WPF Diagram: 2024 Volume 4
Favicon
Introducing the New Flutter AI AssistView Widget
Favicon
Create PrepareDinner workfow in StepWise
Favicon
Why CSS Grid Isn’t Enough for Masonry Layouts
Favicon
The Best bit about UI is its Speed and Performance :-)
Favicon
Ui cards
Favicon
Introducing the New WinUI Kanban Board

Featured ones: