Wireframe CSS
Overview
The StarSpec wireframe system provides low-fidelity UI sketches that focus on structure and flow rather than high-fidelity design. Styles are implemented in wireframe.css (the Sketch base) and per-theme overrides in wireframe-{id}.css.
Wireframe CSS Conventions
StarSpec uses a low-fidelity, themeable CSS system for UI wireframes. It follows a BEM-lite convention for predictable styling and easy theme overrides.
Naming Convention
- Block:
.wf-{component}(e.g.,.wf-tabs) - Element:
.wf-{component}__{element}(e.g.,.wf-tabs__header) - Modifier/State:
[data-{modifier}](e.g.,.wf-tabs__header[data-active="true"])
Selector Pattern
Core variables are defined in :root and must be overridden in two compound selectors per theme β one for light mode and one for dark mode. Both wa-dark/wa-light and data-wf-theme are on the same <html> element, so the compound selectors resolve correctly by specificity alone.
/* Light (default) */:root[data-wf-theme="ios"],:root.wa-light[data-wf-theme="ios"] { β¦ }
/* Dark */:root.wa-dark[data-wf-theme="ios"] { β¦ }For the full copy-paste template including component override scoping, see get_instructions("starspec/fragments/wireframe/theme-selector-pattern").
Variable Reference
For the full --wf-* variable reference (brand, semantic, geometry, font), see get_instructions("starspec/fragments/wireframe/theme-variables").
Theme Architecture
StarSpec supports multiple themes through a data-wf-theme attribute on the <html> element, set by the wireframe theme picker in the page header.
<html data-wf-theme="ios" class="wa-dark">Themes override the default CSS variables and provide platform-specific layout adjustments. Every theme must support both light and dark mode. Web Awesome controls light/dark by adding wa-light or wa-dark to <html>, synced from Starlightβs data-theme attribute.
Active Themes
| ID | Label | CSS file | Light | Dark |
|---|---|---|---|---|
"" | Sketch | wireframe.css (base, always loaded) | β | β |
mui | Material | wireframe-mui.css | β | β |
ios | iOS | wireframe-ios.css | β | β |
tamagui | Tamagui | wireframe-tamagui.css | β | β |
Compound Selector Pattern
Both wa-dark/wa-light and data-wf-theme are set on the same <html> element. Themes must define variables in two compound selector blocks so they cooperate with Web Awesomeβs dark/light class mechanism:
/* ββ Light (default) ββββββββββββββββββββββββββββββββββββββββ *//* Applies when wa-light is present, and also before any WA *//* class is set (prevents flash of unstyled content at paint). */:root[data-wf-theme="{id}"],:root.wa-light[data-wf-theme="{id}"] { --wf-color-primary: #β¦; --wf-color-primary-text: #β¦; --wf-color-bg: #β¦; --wf-color-bg-light: #β¦; --wf-color-bg-elevated: #β¦; --wf-color-text: #β¦; --wf-color-text-muted: #β¦; --wf-color-border: #β¦; --wf-color-border-dark: #β¦; --wf-color-success: #β¦; --wf-color-warning: #β¦; --wf-color-danger: #β¦; --wf-radius-card: β¦px; --wf-radius-button: β¦px; --wf-border-width: 1px; --wf-spacing-gap: 1rem; /* Optional: --wf-font-body, --wf-font-heading, --wf-font-mono */}
/* ββ Dark βββββββββββββββββββββββββββββββββββββββββββββββββββ *//* Overrides when wa-dark is present on <html>. */:root.wa-dark[data-wf-theme="{id}"] { --wf-color-primary: #β¦; /* often a lighter tint than light mode */ --wf-color-bg: #β¦; --wf-color-bg-light: #β¦; --wf-color-bg-elevated: #β¦; --wf-color-text: #β¦; --wf-color-text-muted: #β¦; --wf-color-border: #β¦; --wf-color-border-dark: #β¦;}
/* ββ Component overrides βββββββββββββββββββββββββββββββββββ *//* Scope to the theme to avoid polluting other themes. *//* Use :root[data-wf-theme="{id}"] .wf-{component} for *//* overrides that apply in both modes; narrow further with *//* .wa-light / .wa-dark when mode-specific shapes are needed. */:root[data-wf-theme="{id}"] .wf-card { β¦ }:root.wa-dark[data-wf-theme="{id}"] .wf-card { β¦ }Specificity notes
| Selector | Specificity | When it wins |
|---|---|---|
:root[data-wf-theme="{id}"] | (0,1,1) | Default / light mode |
:root.wa-light[data-wf-theme="{id}"] | (0,2,1) | Explicit light mode (same result) |
:root.wa-dark[data-wf-theme="{id}"] | (0,2,1) | Dark mode β beats the single-selector default |
The dark block wins by specificity alone β no !important or load-order tricks required. The plain :root[data-wf-theme] block handles the moment before any WA class is applied (pre-paint flash prevention).
Creating a New Wireframe Theme
Step 1 β Create the CSS file
Create src/styles/wireframe-{id}.css. Use the compound selector template above. All --wf-color-*, --wf-radius-*, and --wf-spacing-* variables must be defined in both the light and dark blocks.
Optionally, create src/styles/{id}.theme.toml to declare anchor values for StarVisionβs visual extraction loop β see Theme TOML Specification.
Step 2 β Register in themes.json
Add an entry to src/themes.json:
{ "id": "{id}", "label": "{Label}" }astro.config.ts auto-discovers wireframe-{id}.css via fs.existsSync() β no manual config change is needed.
Step 3 β Verify
- Toggle light and dark mode with each theme selected β wireframe area must update correctly
- Confirm the theme appears in the header dropdown
- Check that no
wf-*component renders with hardcoded colors from a previous theme
For the full authoring guide including dark palette guidelines, see Authoring Wireframe Themes.
Layout Structure
Wireframes are composed of high-level blocks that use standard layouts:
- Sections (
.wf-section): Root container for a wireframe, often with a title - Stacks (
.wf-stack): Vertical layout with configurable[data-gap] - Rows (
.wf-row): Horizontal layout with[data-gap]and[data-justify] - Cards (
.wf-card): Semantic groupings with optional[data-elevated="true"]
Component Modifiers
Modifiers are applied via data-* attributes, keeping the DSL clean while providing flexible styling.
Buttons
[data-variant="primary"]: Prominent action[data-variant="secondary"]: Default/Outline[data-variant="ghost"]: Text-only link[data-variant="danger"]: Destructive action
Progress & Sliders
[data-variant="success"]: Green fill[data-variant="warning"]: Orange fill[data-variant="danger"]: Red fill
Extending the CSS
To add a new component style:
- Define a new block class
.wf-{name} - Use existing variables (
var(--wf-color-primary)) to ensure theme compatibility - Use
data-*attributes for any variants or state - Add element classes with the
__separator for internal parts (e.g.,.wf-{name}__indicator)
Never use hardcoded hex values inside component rules. All color values must reference --wf-* variables so they respond to theme and mode changes.