Shadwire

Search the documentation

Search the documentation and component pages by title and by content.

Get started
Introduction
Installation
shadwire.json
Theming
Dark mode
Tools
CLI
Agent skill
Registry
llms.txt
Guides
Composition
Styling
Forms
Icons
Accessibility
Localisation
Blocks
Overview
Components
Overview
Accordion
Alert
Alert Dialog
Aspect Ratio
Avatar
Badge
Breadcrumb
Button
Button Group
Calendar
Card
Carousel
Chart
Checkbox
Collapsible
Combobox
Command
Context Menu
Data Table
Date Picker
Dialog
Drawer
Dropdown Menu
Empty
Field
Hover Card
Icon
Input
Input Group
Input OTP
Item
Kbd
Label
Menubar
Native Select
Navigation Menu
Pagination
Popover
Progress
Radio Group
Resizable
Scroll Area
Select
Separator
Sheet
Sidebar
Skeleton
Slider
Sonner
Spinner
Switch
Table
Tabs
Textarea
Toggle
Toggle Group
Tooltip

Theming

Components take their colours from CSS variables. To change the theme, you edit those variables and leave the components alone.

Where the tokens live

In vendor/shadwire/shadwire.css. shadwire init installs it and imports it in your app's Tailwind entrypoint, whose path bin/shadwire status --json shows under tailwind.css.

The file has three layers:

@custom-variant dark (&:is(.dark *));   /* class-based dark mode */

@theme inline {                         /* maps tokens to utilities */
  --color-background: var(--background);
  --color-primary: var(--primary);
  /* ... */
}

@theme inline turns --primary into the bg-primary and text-primary utilities. The colours are written in OKLCH.

The tokens

Each background token has a -foreground token for the text on top of it. Use the two together, as in bg-muted text-muted-foreground, rather than bg-muted with the default text colour.

Token Controls Used by
background / foreground Application background and default text Page shell, sections, default text
card / card-foreground Raised surfaces Card, dashboard and settings panels
popover / popover-foreground Floating surfaces Popover, DropdownMenu, ContextMenu, overlays
primary / primary-foreground High-emphasis actions, brand Default Button, selected states, badges
secondary / secondary-foreground Lower-emphasis filled actions Secondary buttons and badges
muted / muted-foreground Subtle surfaces, secondary content Descriptions, placeholders, empty states, supporting text
accent / accent-foreground Hover, focus and active surfaces Ghost buttons, menu highlight, hovered rows
destructive / destructive-foreground Destructive actions, errors Destructive buttons, invalid states
border Borders and separators Cards, menus, tables, dividers
input Form control borders Input, Textarea, Select, outline controls
ring Focus rings Buttons, inputs, checkboxes, menus
chart-1 … chart-5 Chart palette Charts and chart-driven blocks
sidebar / sidebar-foreground Sidebar surface and text The Sidebar container
sidebar-primary / -foreground High-emphasis actions in the sidebar Active items, icon tiles, CTAs
sidebar-accent / -foreground Hover and selection in the sidebar Menu hover, open items
sidebar-border Sidebar borders Sidebar headers, groups and dividers
sidebar-ring Sidebar focus rings Focused controls in the sidebar
radius Base corner radius Cards, inputs, buttons, popovers

Changing the brand colour

Set --primary and --primary-foreground in both blocks, and every component that uses bg-primary picks up the new colour.

:root {
  --primary: oklch(0.55 0.22 264);
  --primary-foreground: oklch(0.98 0 0);
}

.dark {
  --primary: oklch(0.7 0.19 264);
  --primary-foreground: oklch(0.15 0 0);

Choose a foreground with enough contrast against the new primary, and check it in both light and dark mode.

Adding a token

Declare the variable in both blocks and map it in @theme inline so Tailwind generates the utilities:

@theme inline {
  --color-success: var(--success);
  --color-success-foreground: var(--success-foreground);
}

:root {
  --success: oklch(0.72 0.19 145);
  --success-foreground: oklch(0.98 0 0);

After that, bg-success and text-success-foreground work like any other token.

Corner radius

radius-sm, radius-md and radius-lg are all derived from --radius, so changing it changes the corners of cards, inputs, buttons and popovers in one go.

:root { --radius: 0.75rem; }

Adding a variant

Variants are frozen Ruby hashes in the component's class. The file is yours, so adding a variant means adding a key:

# app/components/ui/button_component.rb
VARIANTS = {
  default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
  success: "bg-success text-success-foreground shadow-xs hover:bg-success/90",
  # ...
}.freeze
<%= ui_button(variant: :success) { "Publicar" } %>

This counts as a local edit: bin/shadwire diff will list the file as modified, and bin/shadwire update would overwrite it.

Keeping your edits

  1. bin/shadwire diff <name>: see what you changed.
  2. bin/shadwire update <name> without --yes: it asks about each modified file and shows the diff.
  3. Put your edits back on top of the new version.
bin/shadwire diff button      # see what you changed
bin/shadwire update button    # without --yes: asks per file and shows the diff

Where you can, add things (a new variant, a new token) instead of rewriting existing classes. Additions are much easier to carry over when you update.