theme

The theme system provides consistent CSS variables across all tosijs-ui components, with support for automatic dark mode via tosijs's Color class and invertLuminance.

Base Variables

All components use these foundational CSS variables with the --tosi- prefix:

Variable Default Description
--tosi-spacing-xs 4px Extra small spacing
--tosi-spacing-sm 8px Small spacing
--tosi-spacing 12px Default spacing
--tosi-spacing-lg 16px Large spacing
--tosi-spacing-xl 24px Extra large spacing
--tosi-bg #fafafa Background color
--tosi-bg-inset derived Inset/recessed background
--tosi-text #222 Text color
--tosi-accent #EE257B Accent/brand color
--tosi-accent-text derived Text on accent background
--tosi-font-family system-ui Font family
--tosi-font-size 16px Base font size
--tosi-line-height 1.5 Line height
--tosi-touch-size 44px Minimum touch target
--tosi-focus-ring derived Focus outline style

Creating Themes

import { Color } from 'tosijs'
import { createTheme, applyTheme } from 'tosijs-ui'

const myTheme = createTheme({
  accent: Color.fromCss('#007AFF'),
  background: Color.fromCss('#ffffff'),
  text: Color.fromCss('#1a1a1a'),
})

applyTheme(myTheme, 'my-theme')

Dark Mode

Toggle between light and dark themes. createTheme and createDarkTheme return XinStyleSheet objects — ordinary CSS variable maps you can apply however you like:

<label><input type="checkbox" class="dark-toggle"> Dark mode</label>
<div class="theme-sample">
  <span>Sample text</span>
  <button>Button</button>
</div>
.preview .theme-sample {
  padding: 12px;
  background: var(--sample-bg);
  color: var(--sample-text);
  border-radius: 8px;
  display: flex;
  gap: 12px;
  align-items: center;
  transition: all 0.3s;
}
.preview .theme-sample button {
  background: var(--sample-accent);
  color: var(--sample-accent-text);
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
}
import { Color } from 'tosijs'
import { createTheme, createDarkTheme } from 'tosijs-ui'

const colors = {
  accent: Color.fromCss('#007AFF'),
  background: Color.fromCss('#ffffff'),
  text: Color.fromCss('#1a1a1a'),
}

function applyToPreview(theme) {
  const vars = theme[':root']
  const sample = preview.querySelector('.theme-sample')
  sample.style.setProperty('--sample-bg', String(vars._tosiBg))
  sample.style.setProperty('--sample-text', String(vars._tosiText))
  sample.style.setProperty('--sample-accent', String(vars._tosiAccent))
  sample.style.setProperty('--sample-accent-text', String(vars._tosiAccentText))
}

applyToPreview(createTheme(colors))

preview.querySelector('.dark-toggle').addEventListener('change', (e) => {
  applyToPreview(e.target.checked ? createDarkTheme(colors) : createTheme(colors))
})

Component Variables

Each component defines its own variables that derive from base variables. For example, tosi-select derives from base:

--tosi-select-gap: var(--tosi-spacing-sm, 8px);
--tosi-select-touch-size: var(--tosi-touch-size, 44px);

This allows fine-grained customization while maintaining consistency.