DEV Community

Cover image for Create design primitives with React and TailwindCSS
Zetrik
Zetrik

Posted on • Edited on

2 1

Create design primitives with React and TailwindCSS

TailwindCSS is a powerful utility-first CSS framework for rapidly building your UI. It also removes all unused utility classes from the resulting bundle automatically. But this only works if a full class name provided by Tailwind like bg-gray-900 is inside your code.

A utility function to the rescue

To overcome this limitation, I played a bit around before I ended up with this:

function cx(...cns: (boolean | string | undefined)[]): string {
  return cns.filter(Boolean).join(" ");
}
Enter fullscreen mode Exit fullscreen mode

This small function is quite handy in this case. It allows us to concatenate utility classes in various ways. For example:

const styles = {
  base: "bg-gray-700",
  abc: {
    a: "hover:bg-gray-600",
    b: "hover:bg-gray-500",
    c: "hover:bg-gray-400",
  },
};

const classes = cx(styles.base, styles.abc["b"]);
Enter fullscreen mode Exit fullscreen mode

It's also possible to use bool expressions.

const classes = cx(true && "bg-gray-700", false && "hover:bg-gray-600");
Enter fullscreen mode Exit fullscreen mode

The class hover:bg-gray-600 will be filtered out by cx as the expression resulted in false.

Full Example

import { PropsWithChildren, ReactElement } from "react";

const styles = {
  base: "rounded",
  variants: {
    default: "bg-gray-600",
    primary: "bg-blue-600",
    secondary: "bg-green-600",
  },
  sizes: {
    sm: "px-1 py-1",
    md: "px-2 py-1",
    lg: "px-3 py-1",
  },
};

type Variant = keyof typeof styles.variants;

type Size = keyof typeof styles.sizes;

type Props = {
  type?: "button" | "submit";
  variant?: Variant;
  size?: Size;
  disabled?: boolean;
};

function Button({
  type = "button",
  variant = "default",
  size = "md",
  disabled = false,
  children,
}: PropsWithChildren<Props>): ReactElement {
  const classes = cx(
    styles.base,
    !disabled && styles.variants[variant],
    styles.sizes[size],
    disabled && "bg-gray-800"
  );

  return (
    <button type={type} className={classes} disabled={disabled}>
      {children}
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Somewhere in your codebase, this can be used as follows:

<Button>Click</Button>
<Button variant="primary">Click</Button>
<Button variant="secondary" sizes="lg">Click</Button>
<Button disabled>Click</Button>
Enter fullscreen mode Exit fullscreen mode

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

Top comments (1)

Collapse
 
maxiim3 profile image
maxiim3

The pattern is interesting and applicable to other styling paradigm. Nice article!