Skip to main content

Themes

Senco supports two themes: light and dark. Themes remap semantic tokens to different base values while keeping component code unchanged.

Available themes

ThemeContextBackgroundText
Light (default)Standard usage, daylightcolor-bg maps to whitecolor-fg maps to gray-700
DarkLow-light, OLED, night modecolor-bg maps to gray-900color-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.

Side-by-side theme comparison
Light theme
JD
Fatima B.
Transfer to USD wallet
$120.00
Completed
Dark theme
JD
Fatima B.
Transfer to USD wallet
$120.00
Completed

Token remapping

Key semantic tokens and their resolved values per theme:

TokenLight valueDark 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.

Live token remap table
TokenLightDark
--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 #0F62FE to maintain contrast compliance on dark surfaces
  • Borders and surfaces shift: gray-200 borders in light become primary-700 borders in dark
  • Shadows increase in opacity for dark mode (dark surfaces need stronger shadows to create depth)
  • Disabled states use #7E8EA3 for text in dark mode (distinct from --ds-muted)
Do / Don't: semantic tokens vs hardcoded values
Do
color: var(--ds-fg);
Light: readable
Dark: readable

Use semantic tokens. They adapt to both themes automatically.

Don't
color: #374151;
Light: readable
Dark: invisible

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-focus remapping
  • Never rely on color alone to convey meaning — both themes must preserve non-color cues (icons, labels, patterns)