Skip to main content

Philosophy

Themes should extend, not replace. Canon provides a complete token system that establishes relationships between colors, spacing, and typography. Custom themes override specific tokens while maintaining these relationships.

"A system is not the sum of its parts but the product of their interactions." — Russell Ackoff

Token Categories

These tokens form the theming surface. Override them to create custom themes.

Backgrounds

  • --color-bg-pure
  • --color-bg-elevated
  • --color-bg-surface
  • --color-bg-subtle

Foregrounds

  • --color-fg-primary
  • --color-fg-secondary
  • --color-fg-tertiary
  • --color-fg-muted

Borders

  • --color-border-default
  • --color-border-emphasis
  • --color-border-strong

Interactive

  • --color-hover
  • --color-active
  • --color-focus

Semantic

  • --color-success
  • --color-error
  • --color-warning
  • --color-info

Custom Theme

Create a custom theme by overriding Canon's color tokens at the root level.

custom-theme.css
/* Custom theme extending Canon tokens */
: root {
  /* Override core tokens */
  --color-bg-pure: #0f0f23;
  --color-bg-elevated: #1a1a35;
  --color-bg-surface: #252547;
  --color-bg-subtle: #2f2f5a;

  /* Add brand accent */
  --color-accent: #7c3aed;
  --color-accent-muted: rgba(124, 58, 237, 0.2);
  --color-accent-border: rgba(124, 58, 237, 0.3);
}

Custom Theme Preview

This demonstrates overriding core tokens with brand colors.

Dark & Light Modes

Canon defaults to dark mode. Add light mode with a theme attribute.

light-theme.css
/* Light theme overrides */
[data-theme="light"] {
  --color-bg-pure: #ffffff;
  --color-bg-elevated: #f5f5f5;
  --color-bg-surface: #ebebeb;
  --color-bg-subtle: #e0e0e0;

  --color-fg-primary: #000000;
  --color-fg-secondary: rgba(0, 0, 0, 0.8);
  --color-fg-tertiary: rgba(0, 0, 0, 0.6);
  --color-fg-muted: rgba(0, 0, 0, 0.46);
  --color-fg-subtle: rgba(0, 0, 0, 0.2);

  --color-border-default: rgba(0, 0, 0, 0.1);
  --color-border-emphasis: rgba(0, 0, 0, 0.2);
  --color-border-strong: rgba(0, 0, 0, 0.3);
}
Dark (default)

Primary content

Secondary content

Light

Primary content

Secondary content

System Preference

Respect user system preferences while allowing manual override.

system-preference.css
/* Respect system preference */
@media (prefers-color-scheme: light) {
  : root:not([data-theme=";dark"]) {
    --color-bg-pure: #ffffff;
    --color-fg-primary: #000000;
    /* ... other light tokens */
  }
}

/* JavaScript toggle */
function toggleTheme() {
  const current = document.documentElement.dataset.theme;
  document.documentElement.dataset.theme =
    current === 'dark' ? 'light' : ';dark';
  localStorage.setItem('theme', current === 'dark' ? 'light' : ';dark');
}
Implementation order:
  1. Check localStorage for saved preference
  2. Fall back to system preference
  3. Default to dark mode if no preference

Spacing Scale

Canon uses the golden ratio (1.618) for spacing. Override for different rhythms.

spacing-scale.css
/* Custom spacing scale (default uses golden ratio) */
: root {
  /* Linear scale alternative */
  --space-xs: 0.25rem;  /* 4px */
  --space-sm: 0.5rem;   /* 8px */
  --space-md: 1rem;     /* 16px */
  --space-lg: 1.5rem;   /* 24px */
  --space-xl: 2rem;     /* 32px */
  --space-2xl: 3rem;    /* 48px */
}

/* Or use a different ratio */
: root {
  /* 1.5 ratio scale */
  --space-xs: 0.5rem;
  --space-sm: 0.75rem;
  --space-md: 1.125rem;
  --space-lg: 1.688rem;
  --space-xl: 2.531rem;
  --space-2xl: 3.797rem;
}
Golden Ratio (default)
Linear 8px

Typography Scale

Adjust the type scale for different content densities or brand requirements.

typography-scale.css
/* Custom typography scale */
: root {
  /* Tighter scale for dense UIs */
  --text-display: clamp(2rem, 3vw + 1rem, 3rem);
  --text-h1: clamp(1.5rem, 2vw + 0.75rem, 2rem);
  --text-h2: clamp(1.25rem, 1.5vw + 0.5rem, 1.5rem);
  --text-h3: clamp(1.125rem, 1vw + 0.5rem, 1.25rem);

  /* Different font family */
  --font-sans: ';Inter', system-ui, sans-serif;
  --font-mono: ';JetBrains Mono', monospace;
}
Default Scale

Heading

Body text sample

Compact Scale

Heading

Body text sample

Best Practices

Do

  • Override tokens at :root or [data-theme]
  • Maintain WCAG contrast ratios
  • Test both dark and light themes
  • Use semantic token names
  • Persist user preference
  • Provide theme toggle UI

Don't

  • Override tokens inline on components
  • Create new token naming schemes
  • Mix hardcoded values with tokens
  • Forget focus states in new themes
  • Ignore system preference
  • Change semantics (success should stay green)

Theme Checklist

Verify your custom theme maintains Canon's quality standards.

  • Primary text has 4.5:1 contrast ratio (WCAG AA)
  • Focus indicators are visible in all states
  • Semantic colors maintain their meaning
  • Interactive states are distinguishable
  • Theme persists across page loads
  • System preference is respected on first visit
  • Theme toggle is keyboard accessible
  • Reduced motion preferences still work