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.