Senren UI

Foundations

Theming

Senren is themed entirely through CSS variables. One file, one source of truth, dark mode included.

Design tokens #

Tokens are stored as HSL channels (without the hsl() wrapper) so Tailwind utilities can compose them with opacity modifiers like bg-[hsl(var(--senren-primary)/0.2)].

app/assets/stylesheets/senren.css
:root {
  --senren-background: 0 0% 100%;
  --senren-foreground: 222.2 84% 4.9%;

  --senren-muted: 210 40% 96.1%;
  --senren-muted-foreground: 215.4 16.3% 46.9%;

  --senren-border: 214.3 31.8% 91.4%;
  --senren-input: 214.3 31.8% 91.4%;
  --senren-ring: 222.2 84% 4.9%;

  --senren-primary: 222.2 47.4% 11.2%;
  --senren-primary-foreground: 210 40% 98%;

  --senren-destructive: 0 84.2% 60.2%;
  --senren-destructive-foreground: 210 40% 98%;

  --senren-radius: 0.5rem;
}

.dark {
  --senren-background: 222.2 84% 4.9%;
  --senren-foreground: 210 40% 98%;
  /* ...etc */
}

Color swatches #

Background
--senren-background
Foreground
--senren-foreground
Muted
--senren-muted
Primary
--senren-primary
Secondary
--senren-secondary
Accent
--senren-accent
Destructive
--senren-destructive
Border
--senren-border

Use tokens, not raw colors #

Reach for tokens in your own templates so the whole app re-skins from one CSS file.

ERB
<%# Always reach for tokens, never raw colors %>
<div class="bg-[hsl(var(--senren-background))] text-[hsl(var(--senren-foreground))]">
  <%= render(Senren::ButtonComponent.new(variant: :primary)) { "Save" } %>
</div>

Dark mode #

Senren uses the standard Tailwind .dark class on <html>. Toggle it however you want; the design tokens redefine themselves under .dark in senren.css. The site you are reading uses a small Stimulus controller, site--theme, to flip the class and persist the user's preference.