Usage with CSS Modules
CSS Modules is a way to write CSS that is scoped locally by default. It uses the same pattern as React components, where CSS is written as JS, but outputs plain CSS files. If you don't want to use Tailwind, this guide is perfect for you.
Creating a simple Button
tw-classed
works excellently with CSS Modules. You can use the classes
function to generate class names, and toggle them using props.
For this guide some Typescript knowledge is assumed.
Lets create some CSS Modules for our Button.
// Button.module.scss
.button {
display: flex;
align-items: center;
justify-content: center;
padding: 0.5rem 1rem;
border-radius: 0.25rem;
border: 1px solid transparent;
font-size: 1rem;
font-weight: 500;
&.primary {
background-color: #3182ce;
color: #fff;
border-color: #2b6cb0;
}
&.secondary {
background-color: #cbd5e0;
color: #2d3748;
border-color: #a0aec0;
}
&.danger {
background-color: #e53e3e;
color: #fff;
border-color: #c53030;
}
}
Then apply the styles to our Button component.
import classes from "./Button.module.scss";
import { classed } from "@tw-classed/react";
const Button = classed("button", classes.button, {
variants: {
color: {
primary: classes.primary,
secondary: classes.secondary,
danger: classes.danger,
},
},
defaultVariants: {
color: "primary",
},
});
export type ButtonProps = React.ComponentProps<typeof Button>;
Now we can use the Button component like this:
<Button color="primary">Primary</Button>
<Button color="secondary">Secondary</Button>
<Button color="danger">Danger</Button>
Creating a simple button without tw-classed
If you don't want to use tw-classed
, you can still use CSS Modules. You can use the classnames
package to toggle classes.
import classes from "./Button.module.scss";
import cn from "classnames";
export interface ButtonProps {
color?: "primary" | "secondary" | "danger";
}
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ color, ...props }, ref) => {
return (
<button
ref={ref}
className={cn(classes.button, {
[classes.primary]: color === "primary",
[classes.secondary]: color === "secondary",
[classes.danger]: color === "danger",
})}
{...props}
/>
);
}
);
You can see how this is a lot more verbose than using tw-classed
. You have to manually toggle the classes, and you have to manually add the base class. If you add another variant, you have to add another class name to the cn
function and also manually update the ButtonProps
interface.
Additionally, there is no as
prop. This button will always be a button
element. There is no way to simply grab the styles of the button and apply them to a different element like an a
element. This is where tw-classed
shines.
Remapping the button
to an a
element
tw-classed
allows you to remap the button
element to an a
element. This is useful if you want to use the styles of a button, but you want to use an a
element instead.
// Link.tsx
// ...
import { Button } from "./Button";
export const Link = classed("a", Button);
Thats it! Now you can use the Link
component like this:
<Link href="/home" color="primary">
Primary
</Link>
Or you can map it in using the as
prop
import { Button } from "./Button";
() => (
<Button as="a" href="/home" color="primary">
Primary
</Button>
);