Skip to content

3.4 Styling and Theming

coding man edited this page Nov 4, 2026 · 1 revision

Styling and Theming

Relevant source files

The following files were used as context for generating this wiki page:

The styling and theming system of VariantScope provides a responsive, accessible design system built on CSS custom properties (design tokens), modular layout rules, component styles, runtime dark mode toggling, and automated WCAG AA contrast validation.

Sources: src/styles/tokens.css:1-58, src/styles/base.css:1-94, src/styles/components.css:1-345, src/ui/theme.js:1-29, tests/unit/contrast.test.js:1-81


1. Design Tokens and Color Palette

The design token architecture (src/styles/tokens.css) defines global variables for colors, typography, layout radii, shadows, and semantic variant pills. Theme-switching is handled natively through CSS selector scoping ([data-theme='dark']), altering variables dynamically without mutating class names across components.

Token Categories

  • Surfaces and Backgrounds: --bg, --surface, --surface-2, and --viewer-bg define the background depth hierarchy for cards, panels, and 3D viewers.
  • Typography: --text and --text-muted maintain readable contrast tiers across light and dark backgrounds.
  • Semantic Variants: --accent (pathogenic), --benign, and --vus provide clinical classification colours.
  • Pills and Banners: Tinted backgrounds paired with dark/light text tokens (--pill-pathogenic-bg, --pill-pathogenic-text, --banner-info-bg, etc.) guarantee text contrast exceeds WCAG AA thresholds.
:root {
  --bg: #f4f7fa;
  --surface: #ffffff;
  --surface-2: #eaf0f5;
  --text: #0f1b2d;
  --text-muted: #4b5b70;
  --border: #d5dee8;
  --primary: #0b1f3a;
  --accent-ui: #12486b;
  --secondary: #14b8a6;
  --accent: #ff6b6b;
  --benign: #22c55e;
  --vus: #94a3b8;
  color-scheme: light;
}

Sources: src/styles/tokens.css:1-58


2. Base Layout, Typography, and Accessibility

The base stylesheet (src/styles/base.css) establishes global box-sizing resets, typography scaling, root flex layout constraints, and accessibility utilities.

Key Base Rules

  • Box Sizing: Enforces box-sizing: border-box globally on all elements and pseudo-elements.
  • Body Frame: Configures a minimum height of 100vh, flex column direction, and tabular numeric font variants (font-variant-numeric: tabular-nums) to align data tables and numeric columns cleanly.
  • Accessibility Enhancements:
    • [hidden] attribute enforcement with display: none !important that overrides conflicting layout classes src/base.css:19-22.
    • Focus visibility rings using :focus-visible styled with --secondary outline and offset src/base.css:51-54.
    • Screen-reader utilities (.visually-hidden and .skip-link) for keyboard navigation src/base.css:56-77.
    • Reduced-motion query media handling that strips transitions and animations when prefers-reduced-motion: reduce is active src/base.css:89-94.

Sources: src/styles/base.css:1-94


3. Component Architecture

The component stylesheet (src/styles/components.css) encapsulates modular UI elements including top-bar navigation, search inputs, interactive filter chips, buttons, card grids, stat cards, and dynamic panel skeletons.

Structural Components

  • Top Bar and Search: Flexbox-based header .topbar featuring a navy-to-teal gradient, responsive brand button (.brand-button), and a fluid search input container (.search) src/styles/components.css:4-42.
  • Interactive Chips and Buttons: Styled .chip and .button elements with hover state transitions and [aria-pressed='true'] states for filter toggles src/styles/components.css:87-150.
  • Layout Grids: .layout-grid establishes a responsive two-column layout (3fr / 2fr) for primary visualizations and sidebar widgets, wrapping gracefully on smaller viewports src/styles/components.css:222-228.
  • Panel Skeletons and Shimmer: Asynchronous loading states use .panel-skeleton with animated linear gradient .shimmer keyframes simulating data fetching latencies src/styles/components.css:298-323.

Sources: src/styles/components.css:1-345


4. Dark Mode Runtime Orchestration (theme.js)

Dark mode switching is managed by src/ui/theme.js, which synchronizes user preferences between DOM attributes, localStorage (via cache helpers), and system media queries.

Runtime Flow

  1. Initialization (initTheme): Reads any saved preference from cacheGet('theme'). If none exists, evaluates window.matchMedia('(prefers-color-scheme: dark)') src/ui/theme.js:12-16.
  2. Application (apply): Sets document.documentElement.dataset.theme = theme and dispatches a custom window event vs:theme carrying the active theme name. This allows canvases (3D protein viewers and Chart.js lollipops) to redraw themselves with updated color variables src/ui/theme.js:5-9.
  3. Toggle Action (toggleTheme): Flips between 'dark' and 'light' and persists the choice via cacheSet('theme', next, null) src/ui/theme.js:19-23.
---
title: Theme Initialization and Event Flow
---
graph TD
    A["initTheme(button)"] --> B["initialTheme()"]
    B --> C{"cacheGet('theme')?"}
    C -- "Yes" --> D["Use Saved Theme"]
    C -- "No" --> E["matchMedia('(prefers-color-scheme: dark)')"]
    D --> F["apply(theme)"]
    E --> F
    F --> G["document.documentElement.dataset.theme = theme"]
    F --> H["window.dispatchEvent(vs:theme)"]
    H --> I["Canvases / Charts Redraw"]
    
    J["User Clicks Button"] --> K["toggleTheme()"]
    K --> F
    K --> L["cacheSet('theme', next, null)"]

    style subZ fill:none,stroke:none
Loading

Sources: src/ui/theme.js:1-29 Sources: src/ui/theme.js:1-29


5. Automated Contrast Testing

To guarantee WCAG AA accessibility compliance, tests/unit/contrast.test.js parses the CSS token files directly, computes relative luminance and contrast ratios, and asserts that all text/background pairs meet or exceed the 4.5:1 minimum threshold for both light and dark themes.

Testing Mechanics

---
title: Contrast Testing Pipeline
---
graph TD
    A["src/styles/tokens.css"] --> B["tokens(selector) Parser"]
    C["src/styles/components.css"] --> D["Header Gradient Extractor"]
    B --> E["Light and Dark Token Objects"]
    E --> F["PAIRS Matrix Iteration"]
    F --> G["luminance(hex) Calculation"]
    G --> H["contrast(a, b) Ratio Computation"]
    H --> I["Vitest Expectation: >= 4.5:1"]
    D --> H

    style subZ fill:none,stroke:none
Loading

Sources: tests/unit/contrast.test.js:1-81 Sources: tests/unit/contrast.test.js:1-81


Clone this wiki locally