Styling
Use variants to change how a component looks and class: to place it on the page. Colours come from tokens, and your classes take priority.
Semantic tokens only
Components take every colour from a shadcn token, so light mode, dark mode and a new theme all work without changes to the component code. Do the same in your own markup.
Tokens come in pairs, <surface> and
<surface>-foreground:
bg-primary text-primary-foreground,
bg-muted text-muted-foreground,
bg-destructive text-destructive-foreground. The full list is on the
Theming page.
Don't use dark: to set colours. Keep the prefix for differences that
aren't about colour, such as showing a different image in dark mode.
class: and class_name: are the same thing
Both end up in the same place. Use whichever you prefer; class: is
what Rails code usually uses.
Your classes take priority
Classes are combined in this order, with yours last:
When two classes set the same property, like the component's w-full
and your w-80, the earlier one is dropped, the same way
cn() works in shadcn/ui. Otherwise both would end up on the element and
whichever Tailwind happened to emit last would apply. This works for the common
utilities: colour, text, size, spacing, radius, shadow, display, position and alignment.
For anything else, add ! to the end of your class to force it.
So class: can override the component's own classes. Even so, don't use
it to restyle a component:
Use class: for layout:
width, margin, where it sits in a grid.
HTML attributes are passed through
Any argument the component doesn't recognise goes to the rendered element as an HTML
attribute, and Rails' nested hashes like data: { … } work too.
Use tag: to change the element, on components that support it.
button renders a <button> by default and an
<a> with tag: :a.
Prefer variants and sizes over utilities
Check which variants and sizes exist before writing your own classes:
Utility conventions
Follow the same conventions as the components, so your code looks like the code the CLI installed:
-
size-9rather than h-9 w-9, when width and height match. -
gap-*with flex or grid, rather than space-x-* / space-y-*. -
truncaterather than overflow-hidden text-ellipsis whitespace-nowrap. -
no manual z-indexon overlays: dialog, sheet, popover and dropdown-menu already handle their own stacking.