Themes
Senco supports two themes: light and dark. Themes remap semantic tokens to different base values while keeping component code unchanged.
Available themes
| Theme | Context | Background | Text |
|---|---|---|---|
| Light (default) | Standard usage, daylight | color-bg maps to white | color-fg maps to gray-700 |
| Dark | Low-light, OLED, night mode | color-bg maps to gray-900 | color-fg maps to gray-200 |
How themes work
Theming is a token remapping layer. Every component references semantic tokens like --ds-bg, --ds-fg, and --ds-primary. The theme layer reassigns these to different base palette values depending on context. The component never changes — only the resolved value changes.
Token remapping
Key semantic tokens and their resolved values per theme:
| Token | Light value | Dark value |
|---|---|---|
--ds-bg | #FFFFFF (white) | #0F172A (navy-900) |
--ds-fg | #374151 (gray-700) | #CBD5E1 (primary-300) |
--ds-heading | #111827 (primary-900) | #F1F5F9 (primary-100) |
--ds-muted | #636971 (gray-500) | #94A3B8 (primary-400) |
--ds-border | #E5E7EB (gray-200) | #334155 (primary-700) |
--ds-surface | #F3F4F6 (gray-100) | #1E293B (primary-800) |
--ds-primary | #0F62FE (accent-600) | #458DFF (accent-500) |
--ds-primary-hover | #0043CE (accent-700) | #3079FF |
--ds-primary-light | #F5F8FF (accent-50) | #0F2847 |
--ds-link | #0F62FE (accent-600) | #458DFF (accent-500) |
--ds-focus | #0F62FE (accent-600) | #458DFF (accent-500) |
Feedback and status tokens (info, success, warning, error) are included in the live token remap table above.
| Token | Light | Dark |
|---|---|---|
--ds-bg | ...white | ...navy-900 |
--ds-fg | ...gray-700 | ...primary-300 |
--ds-heading | ...primary-900 | ...primary-100 |
--ds-muted | ...gray-500 | ...primary-400 |
--ds-border | ...gray-200 | ...primary-700 |
--ds-surface | ...gray-100 | ...primary-800 |
--ds-white | ...#FFFFFF | ...#FFFFFF |
--ds-on-accent | ...#FFFFFF | ...#FFFFFF |
--ds-primary | ...accent-600 | ...accent-500 |
--ds-primary-hover | ...accent-700 | ...accent-400 |
--ds-primary-light | ...accent-50 | ...accent-900 |
--ds-primary-900 | ...gray-900 | ...gray-900 |
--ds-link | ...accent-600 | ...accent-500 |
--ds-focus | ...accent-600 | ...accent-500 |
--ds-disabled-bg | ...gray-200 | ...gray-700 |
--ds-disabled-text | ...gray-400 | ...gray-500 |
--ds-info-bg | ...info-50 | ...#0C2340 |
--ds-info-border | ...info-200 | ...info-700 |
--ds-info-text | ...info-800 | ...#93C5E8 |
--ds-success-bg | ...green-50 | ...#0A2E1A |
--ds-success-border | ...green-200 | ...green-700 |
--ds-success-text | ...green-800 | ...#A3DFBA |
--ds-warning-bg | ...yellow-50 | ...#3D2008 |
--ds-warning-border | ...yellow-200 | ...yellow-700 |
--ds-warning-text | ...yellow-900 | ...#F0CE70 |
--ds-error-bg | ...red-50 | ...#3D1212 |
--ds-error-border | ...red-200 | ...red-700 |
--ds-error-text | ...red-800 | ...#EBADAD |
Switching themes
Themes are applied via a CSS class on the root layout container. The default is light mode. Adding the .ds-dark class triggers the full token remap to dark mode. No JavaScript runtime switching is required. This approach works with SSR, requires no flash-of-wrong-theme, and keeps bundle size at zero.
Design guidelines
- Test all components in both themes before shipping
- Never hardcode color values — always use semantic tokens (
var(--ds-fg), not#374151) - Dark mode uses a lighter primary blue (
#458DFF) instead of#0F62FEto maintain contrast compliance on dark surfaces - Borders and surfaces shift:
gray-200borders in light becomeprimary-700borders in dark - Shadows increase in opacity for dark mode (dark surfaces need stronger shadows to create depth)
- Disabled states use
#7E8EA3for text in dark mode (distinct from--ds-muted)
color: var(--ds-fg);Use semantic tokens. They adapt to both themes automatically.
color: #374151;Hardcoded hex values break in the opposite theme. gray-700 on dark bg = 2.5:1 contrast (fail).
Accessibility
- WCAG 2.1 AA contrast requirements (4.5:1 for text, 3:1 for UI elements) apply equally in both themes
- Every semantic token pairing has been validated — see the Color page for full contrast tables
- Dark mode primary (
#458DFF) on dark background (#0F172A) achieves 5.53:1 contrast - Focus rings remain visible in both themes via
--ds-focusremapping - Never rely on color alone to convey meaning — both themes must preserve non-color cues (icons, labels, patterns)