You want components to share styles and to change their look by state or by option. In Stillwind, you share a base style, and you express states and options with variants. The element keeps the same classes, and its attributes decide which styles apply.
Share a base style
Store a style() result, and pass it to other style() calls. You can export
the base and import it in other modules:
import { style } from "stillwind/react";
export const button = style(
"inline-flex rounded-md bg-gray-100 px-4 py-2 font-medium text-gray-900",
);
const primaryButton = style(button, "bg-blue-600 text-white");
const dangerButton = style(button, "bg-red-600 text-white");
export function Actions() {
return (
<div {...style("flex gap-2")}>
<button {...primaryButton}>Save</button>
<button {...dangerButton}>Delete</button>
</div>
);
}In this example, the derived styles replace the base’s background and text colors. Their CSS comes after the base’s CSS, and their selectors have equal specificity. The order of the class names does not affect the result.
A base’s hover: selector can be more specific than a plain class in the
derived style. CSS ordering explains variants,
conditions, and multiple bases.
A base passes on its classes only. Custom properties such as
{ "--progress": value } in the base do not carry over, and the CLI reports
them. Set custom properties in the style() call that is spread onto the element.
Express states with variants
Set the state as an attribute, and style it with a variant:
import { style } from "stillwind/react";
export function ToggleButton({ pressed }: { pressed: boolean }) {
return (
<button
aria-pressed={pressed}
{...style(
"rounded-md bg-gray-100 px-4 py-2",
"aria-pressed:bg-blue-600 aria-pressed:text-white",
)}
>
Bold
</button>
);
}The aria-pressed attribute is needed for accessibility anyway. Use variants
such as disabled:, aria-expanded:, and open: in the same way. The element
always has the same classes, so you do not need to think about which toggled
class wins.
Express options with data attributes
For an option without a matching ARIA attribute, set a data-* attribute:
import { style } from "stillwind/react";
export function Badge({ tone, label }: { tone: "neutral" | "danger"; label: string }) {
return (
<span
data-tone={tone}
{...style(
"rounded-full bg-gray-100 px-2 text-gray-900",
"data-[tone=danger]:bg-red-600 data-[tone=danger]:text-white",
)}
>
{label}
</span>
);
}The attribute also shows the option in the browser’s developer tools and in test queries.
For a value from a continuous range, such as a width or a progress percentage, set a custom property instead. See Use runtime values.
Migrate CVA components
style.cva() accepts the configuration of cva from class-variance-authority.
Use it to migrate existing CVA components. See
Replace class helpers
and the style.cva() reference.