Theme Switching
Themes are controlled via the data-theme attribute on <html> or <body>:
<!-- Light theme --> <html data-theme="light"> <!-- Dark theme --> <html data-theme="dark"> <!-- Auto (follows system preference) --> <html>
JavaScript Toggle
function toggleTheme() {
const current = document.documentElement.getAttribute('data-theme');
const next = current === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', next);
localStorage.setItem('theme', next);
}
// Restore on page load
const saved = localStorage.getItem('theme');
if (saved) {
document.documentElement.setAttribute('data-theme', saved);
}
Color Tokens
Background Colors
Text Colors
Semantic Colors
Palette Colors
Named colors from the design palette:
Typography
Font sizes are calculated from --font-size-base (1rem). Adjust the base to scale all typography proportionally.
| Token | Calculation | Default | Usage |
|---|---|---|---|
--font-size-base |
1rem (reference) | 16px | Body text |
--font-size-2xs |
base Γ 0.625 | 10px | Timeline labels, hints |
--font-size-xs |
base Γ 0.75 | 12px | Small labels |
--font-size-sm |
base Γ 0.875 | 14px | Secondary text |
--font-size-lg |
base Γ 1.125 | 18px | Emphasis |
--font-size-xl |
base Γ 1.25 | 20px | H3 |
--font-size-2xl |
base Γ 1.5 | 24px | H2 |
--font-size-3xl |
base Γ 1.875 | 30px | H1 |
Font Families
| Token | Stack | Usage |
|---|---|---|
--font-sans |
Inter, system-ui, sans-serif | Body text, UI |
--font-mono |
ui-monospace, monospace | Code, time displays |
Line Heights
| Token | Value | Usage |
|---|---|---|
--line-height-small |
1 | Icons, single-line buttons |
--line-height-tight |
1.25 | Headings |
--line-height-normal |
1.5 | Body text |
--line-height-relaxed |
1.75 | Long-form content |
Spacing
Spacing tokens are calculated from --space-1 (0.25rem = 4px). The scale follows a consistent multiplier pattern.
| Token | Multiplier | Default |
|---|---|---|
--space-1 | 1Γ (reference) | 4px |
--space-1-5 | 1.5Γ | 6px |
--space-2 | 2Γ | 8px |
--space-2-5 | 2.5Γ | 10px |
--space-3 | 3Γ | 12px |
--space-4 | 4Γ | 16px |
--space-5 | 5Γ | 20px |
--space-6 | 6Γ | 24px |
--space-8 | 8Γ | 32px |
--space-10 | 10Γ | 40px |
--space-12 | 12Γ | 48px |
/* Scale the entire spacing system */
:root {
--space-1: 0.3rem; /* Now 4.8px base β all spaces scale up */
}
Interactive Sizing
Button and icon sizes follow WCAG touch target guidelines. --size-touch-min (44px) is the minimum recommended touch target size.
| Token | Value | Usage |
|---|---|---|
--size-touch-min |
2.75rem (44px) | WCAG minimum touch target |
--size-btn-sm |
1.75rem (28px) | Compact action buttons |
--size-btn-md |
2.25rem (36px) | Standard buttons |
--size-btn-lg |
var(--size-touch-min) | Mobile-friendly buttons |
--size-btn-xl |
3rem (48px) | Large touch buttons |
Icon Sizes
Icon sizes reference button sizes for consistency:
| Token | References | Default |
|---|---|---|
--size-icon-sm |
--size-btn-sm | 28px |
--size-icon-md |
--size-btn-md | 36px |
--size-icon-lg |
--size-btn-lg | 44px |
Slider & Timeline Tokens
| Token | References | Default | Usage |
|---|---|---|---|
--slider-track-height |
--space-1-5 | 6px | Slider/timeline track thickness |
--slider-thumb-size |
--font-size-sm | 14px | Slider thumb diameter |
--timeline-handle-width |
--space-6 | 24px | Loop handle width |
--timeline-handle-height |
--space-8 | 32px | Loop handle height |
Container Widths
Use container tokens for consistent max-width constraints across layouts:
| Token | Value | Use Case |
|---|---|---|
--container-2xs |
20rem (320px) | Small cards, compact forms |
--container-xs |
24rem (384px) | Cards, sidebars |
--container-sm |
30rem (480px) | Forms, narrow content |
--container-md |
36rem (576px) | Medium content blocks |
--container-lg |
48rem (768px) | Article content, main content |
--container-xl |
64rem (1024px) | Wide content areas |
--container-2xl |
80rem (1280px) | Full-width layouts |
/* Usage examples */
.card { max-width: var(--container-xs); }
.form { max-width: var(--container-sm); }
.article { max-width: var(--container-lg); }
.page { max-width: var(--container-xl); margin: 0 auto; }
Border & Outline
Border and outline widths are kept in pixels for crisp rendering at all zoom levels:
| Token | Value | Usage |
|---|---|---|
--border-width |
1px | Standard borders |
--border-width-thick |
2px | Emphasized borders |
--outline-width |
2px | Focus outlines |
--outline-offset |
2px | Focus outline spacing |
Border Radius
Border radius tokens reference the spacing scale for consistency:
--radius-sm = --space-1 (4px)--radius-md = --space-2 (8px)--radius-lg = --space-3 (12px)--radius-xl = --space-4 (16px)--radius-full (9999px - pill/circle)Buttons
Buttons use the .btn class or the native <button> element with shared styles from ui.css.
Button Style Decision Guide
Use this table to choose the right button style for your use case:
| Style | When to Use | Example |
|---|---|---|
.btn (default) |
Primary actions: Submit, Save, Confirm | |
.secondary |
Secondary actions: Cancel, Back, alternative options | |
.ghost |
Tertiary actions: Settings, subtle links, toolbars | |
.icon |
Icon-only button with content-based width | |
.icon.square |
Icon-only with fixed dimensions for uniform toolbars | |
.xs |
Very compact spaces: popovers, inline controls | |
.active |
Toggle state: button is currently "on" | |
.danger |
Destructive actions: Delete, Remove | |
.success |
Positive confirmations: Done, Complete |
Primary Buttons
Default button style uses the accent color (olive). Use for main actions.
Button Variants
<button>Primary</button> <button class="secondary">Secondary</button> <button class="danger">Danger</button> <button class="success">Success</button>
Icon Buttons: .icon vs .icon.square
Key difference:
.iconβ Equal padding around icon, width adapts to content.icon.squareβ Fixed width AND height (perfect square), ideal for toolbars
Comparison
.icon (content-based width):
.icon.square (fixed dimensions):
Square Button Size Variants
Use size modifiers for different contexts:
| Class | Size | Use Case |
|---|---|---|
.square.sm | 28px | Compact toolbars, inline actions |
.square (md) | 36px | Default, most toolbars |
.square.lg | 44px | Touch-friendly, mobile controls |
.square.xl | 48px | Large touch targets, prominent actions |
<!-- Content-based icon button --> <button class="icon">βΆ</button> <!-- Fixed-size square button (for uniform toolbars) --> <button class="icon square">βΆ</button> <button class="icon square lg">βΆ</button>
Ghost Buttons
Minimal buttons with transparent background. Use for tertiary actions or when you want buttons to blend with the background until hovered.
<button class="ghost">Settings</button> <button class="ghost icon">β</button>
Extra Small Buttons
Compact buttons for popovers and tight spaces:
<button class="xs">β1s</button> <button class="xs secondary">Cancel</button>
Active/Toggle State
Use .active for toggled-on buttons (e.g., loop mode ON, filter selected):
<button class="icon square secondary">π</button> <button class="icon square active">π</button>
SVG Icons in Buttons
SVG icons inside buttons are automatically sized via .btn svg rules:
<button class="icon square secondary">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M8 5v14l11-7z"/>
</svg>
</button>
Button Color Palette
Button colors are derived from the design system palette. Each state has a distinct visual treatment:
| State | Background | Text | Border | Example |
|---|---|---|---|---|
| Primary | #849324 (olive) |
#fff |
none | |
| Secondary | surface | primary | #d0d3db |
|
| Active (Toggle) | rgba(132,147,36,0.15) |
#849324 |
#849324 (2px) |
|
| Ghost | transparent | primary | #d0d3db |
|
| Danger | #ba5624 (burnt orange) |
#fff |
none | |
| Success | #2a7b6f (teal) |
#fff |
none |
Color Variables
Button colors reference these CSS custom properties:
--player-accent/--color-accent:#849324(olive green)--player-danger/--color-danger:#ba5624(burnt orange)--color-success:#2a7b6f(teal)--color-border:#d0d3db(neutral gray)
Active State Design Pattern
The .active class uses a semi-transparent accent background with colored textβa standard UI pattern for toggle buttons that:
- β Differentiates toggle state from primary actions
- β Works in both light and dark themes
- β Shows clear "on" state without competing with primary buttons
Popovers
Floating UI using the native Popover API. Zero JavaScript required!
Browser support: Chrome 114+, Firefox 125+, Safari 17+ (all major browsers).
Basic Usage
popover="manual" - won't close on outside click.<!-- Toggle button - no JavaScript! -->
<button popovertarget="my-popover">Toggle</button>
<div id="my-popover" popover class="popover">
<div class="popover-header">
<span class="popover-title">Title</span>
<button class="popover-close" popovertarget="my-popover" popovertargetaction="hide">Γ</button>
</div>
<div class="popover-body">Content</div>
</div>
<!-- Accent border for edit modes -->
<div id="edit" popover class="popover accent">...</div>
<!-- Modal (no light dismiss) -->
<div id="modal" popover="manual" class="popover modal">...</div>
Positioning (CSS Anchor Positioning)
Position popovers relative to their trigger using position-area. Browser support: Chrome/Edge 125+ (May 2024). Falls back to centered on unsupported browsers.
Centered Positions
Popover appears centered on the specified side of the button:
Edge-Aligned Positions
Popover edge aligns with the button's edge. Use .start (left/top) or .end (right/bottom):
Horizontal: .top.start = above + left-aligned, .top.end = above + right-aligned
Vertical: .left.start = left + top-aligned, .left.end = left + bottom-aligned
Corner Area Positions
Popover appears in the corner area (outside the button's bounds):
<!-- Anchor positioning requires anchor-name on trigger -->
<button popovertarget="my-pop" style="anchor-name: --my-anchor;">Open</button>
<div id="my-pop" popover class="popover top" style="position-anchor: --my-anchor;">
Centered above button
</div>
<!-- Edge-aligned: popover's left edge aligns with button's left edge -->
<div id="my-pop" popover class="popover top start" style="position-anchor: --my-anchor;">
Left-aligned above button
</div>
<!-- Position classes:
Centered: top, bottom, left, right
Edge-aligned: top start, top end, bottom start, bottom end,
left start, left end, right start, right end
Corner areas: top-left, top-right, bottom-left, bottom-right
-->
CSS Classes Reference
| Class | Description |
|---|---|
.popover |
Base container with background, border, shadow |
.popover.accent |
Accent border color for edit modes |
.popover.compact |
Reduced spacing |
.popover.modal |
Adds backdrop styling (use with popover="manual") |
| Centered Positions | |
.top, .bottom |
Centered above/below anchor |
.left, .right |
Centered left/right of anchor |
| Edge-Aligned Positions | |
.top.start, .bottom.start |
Above/below, popover's left edge aligned with button's left edge |
.top.end, .bottom.end |
Above/below, popover's right edge aligned with button's right edge |
.left.start, .right.start |
Left/right, popover's top edge aligned with button's top edge |
.left.end, .right.end |
Left/right, popover's bottom edge aligned with button's bottom edge |
| Corner Area Positions | |
.top-left, .top-right |
In corner area above-left / above-right |
.bottom-left, .bottom-right |
In corner area below-left / below-right |
| Structure Elements | |
.popover-header |
Header with title and optional close button |
.popover-title |
Title text |
.popover-close |
Close button |
.popover-body |
Main content area |
.popover-footer |
Footer with action buttons |
.popover-input |
Styled input for editing values |
Dialogs
Modal dialogs using the native <dialog> element. Requires minimal JavaScript (.showModal() / .close()).
Browser support: Chrome 37+, Firefox 98+, Safari 15.4+ (excellent support).
Basic Usage
<!-- Button opens dialog -->
<button onclick="document.getElementById('my-dialog').showModal()">Open</button>
<dialog id="my-dialog" class="dialog">
<div class="dialog-header">
<span class="dialog-title">Title</span>
<button class="dialog-close" onclick="this.closest('dialog').close()">Γ</button>
</div>
<div class="dialog-body">
<p>Dialog content here.</p>
</div>
<div class="dialog-footer">
<button class="btn ghost" onclick="this.closest('dialog').close()">Cancel</button>
<button class="btn" onclick="this.closest('dialog').close()">OK</button>
</div>
</dialog>
<!-- Using form method="dialog" auto-closes on submit -->
<dialog id="edit-dialog" class="dialog">
<form method="dialog">
<div class="dialog-body">
<input type="text" class="dialog-input">
</div>
<div class="dialog-footer">
<button type="submit" class="btn">Save</button>
</div>
</form>
</dialog>
CSS Classes Reference
| Class | Description |
|---|---|
.dialog |
Base container with background, border, shadow, animation |
.dialog-header |
Header with title and optional close button |
.dialog-title |
Title text |
.dialog-close |
Close button (same as .popover-close) |
.dialog-body |
Main content area |
.dialog-footer |
Footer with action buttons |
.dialog-input |
Styled input for forms |
Popover vs Dialog
| Feature | Popover | Dialog |
|---|---|---|
| JavaScript | None required | Minimal (.showModal()) |
| Backdrop | Optional (popover="manual") |
Always with ::backdrop |
| Position | Anchor-relative (CSS) | Centered (fixed) |
| Focus trap | No | Yes (native) |
| Use case | Tooltips, menus, inline edit | Confirmations, forms, settings |
Media Player Controls
Styles for media player components. Use standard button classes with .secondary and .icon.square for consistent controls.
Player Control Buttons
Use .btn.icon.square.secondary for player controls, .active for toggled state:
<button class="icon square secondary">βΆ</button> <button class="icon square active">π</button> <button class="icon square secondary" disabled>β</button>
Time Display
Monospace time display for current time / duration:
<span class="time-display">0:00 / 3:45</span>
Timeline Slider
Styled range input for media progress:
<input type="range" class="timeline" min="0" max="100" value="35">
Complete Player Controls Example
Unified Player Color Variables
Both audio and video players share a unified color system. These variables adapt to light/dark themes automatically.
Structure Colors
| Variable | Light Mode | Dark Mode | Usage |
|---|---|---|---|
--player-bg |
#f5f5f5 | #1a1a1a | Player container background |
--player-panel-bg |
#ffffff | #282828 | Control panels, cards |
--player-surface |
#e0e0e0 | #3f3f3f | Timeline tracks, borders |
--player-item-bg |
#e8e9ed | #1a1a1a | List items, cards |
--player-item-hover-bg |
#d0d3db | #3f3f3f | Hover state |
Accent Colors
Text Colors
| Variable | Light Mode | Dark Mode | Usage |
|---|---|---|---|
--player-text-primary |
#202030 | #ffffff | Primary text, labels |
--player-text-secondary |
#666666 | #aaaaaa | Time display, hints |
--player-item-selected-text |
#ffffff | #1a1a1a | Text on selected items |
/* Customizing player colors */
audio-loop-player,
youtube-loop-player {
--player-accent: #3ea6ff; /* Blue accent */
--player-danger: #ff4444; /* Red for stop/delete */
--player-panel-bg: #1e1e2e; /* Custom panel color */
}