How Instatic CSS Modules Work with Design Tokens: A Complete Architecture Guide
Instatic uses CSS Modules scoped to individual components that reference global CSS custom properties defined in src/styles/globals.css, ensuring a single source of truth for all visual styles while enforcing zero hard-coded values or fallbacks.
The CoreBunch/Instatic repository implements a strict design-token architecture where every visual style for the admin and editor UI flows from centralized definitions. By combining CSS Modules with globally scoped CSS custom properties, the codebase eliminates style fragmentation and guarantees consistent theming across the entire application.
The Architecture: Global Tokens Meet Scoped Modules
Instatic's styling architecture rests on two complementary layers. The foundation is src/styles/globals.css, which declares all design tokens as :root custom properties covering colors, spacing, typography, radius, overlays, and semantic states (lines 14–96). These tokens are globally available to any stylesheet in the application.
The second layer consists of CSS Modules—files like Button.module.css, Card.module.css, and Tooltip.module.css—that the build pipeline (Vite plus Bun) treats as locally scoped stylesheets. While class names generated from these modules are scoped to specific components, any var(--token) reference resolves to the global value defined in globals.css. This separation ensures that component styles remain encapsulated while values remain centralized.
How Design Tokens Are Defined
All visual primitives originate in the global stylesheet. The token definitions follow a strict naming convention using CSS custom properties:
/* src/styles/globals.css */
:root {
/* Surfaces */
--bg-surface: #1e1e1e;
--bg-surface-2: #282828;
/* Typography */
--text: #ffffff;
--text-secondary: #a0a0a0;
/* Spacing & Radius */
--space-s: 0.5rem;
--space-m: 1rem;
--radius: 0.5rem;
/* Semantic colors */
--accent-1: #3b82f6;
}
According to the Instatic source code, no hard-coded hex, RGB, or HSL values may appear in admin UI CSS. Every color must derive from this token set, preventing arbitrary values from fragmenting the design system.
Consuming Tokens in CSS Modules
Component-level styles import these global values using standard CSS var() syntax. The repository enforces a critical rule: no fallbacks are allowed in token usage. The pattern var(--token, fallback) is explicitly forbidden, ensuring that missing tokens surface as build-time errors rather than silently reverting to hard-coded defaults.
Consider the Button component implementation:
/* src/ui/components/Button/Button.module.css */
.root {
background: var(--bg-surface-2);
color: var(--text);
border-radius: var(--radius);
padding: var(--space-s) var(--space-m);
border: 1px solid var(--border-subtle);
}
This pattern repeats across the component library in files like Alert.module.css and Tooltip.module.css. Because the tokens are CSS custom properties rather than Sass variables or static values, changing a token value in globals.css automatically updates every component that references it without requiring a rebuild of individual module files.
Component Implementation Pattern
TypeScript components import their corresponding CSS Modules as plain objects. The build system maps the local class names to unique scoped identifiers while preserving the global variable references:
// src/ui/components/Button/Button.tsx
import styles from "./Button.module.css";
interface Props {
children: React.ReactNode;
onClick?: () => void;
}
export function Button({ children, onClick }: Props) {
return (
<button className={styles.root} onClick={onClick}>
{children}
</button>
);
}
The resulting DOM element receives a scoped class name (e.g., Button_root__xyz123) that references the global custom properties. This approach guarantees that the visual language remains consistent even as the component hierarchy grows complex.
Key Enforcement Rules
The architecture relies on strict enforcement to maintain integrity:
- Zero hard-coded values: As verified in
src/ui/components/Button/Button.module.css, all colors must reference design tokens - No fallback values: As demonstrated in
src/ui/components/Alert/Alert.module.css, the codebase forbidsvar(--token, fallback)syntax to prevent silent failures - Global availability: Because tokens live on the
:rootselector, they cascade naturally to every CSS Module without explicit imports
Summary
- Instatic stores all design tokens as CSS custom properties in
src/styles/globals.css(lines 14–96), creating a single source of truth for colors, spacing, and typography - CSS Modules provide scoped class names for components while referencing global token values via
var(--token)syntax - The build pipeline (Vite + Bun) processes
.module.cssfiles to ensure local scope for class names but global resolution for variable values - Strict rules prohibit hard-coded colors and fallback values in
var()functions, forcing immediate visibility of missing tokens - Runtime updates to
globals.csspropagate instantly across the entire UI without rebuilding individual components
Frequently Asked Questions
Why does Instatic forbid fallback values in CSS variables?
Instatic explicitly disallows var(--token, fallback) syntax to ensure design system integrity. When a token is missing or renamed, the build process surfaces the error immediately rather than silently rendering a hard-coded fallback color. This enforcement, visible in files like src/ui/components/Alert/Alert.module.css, prevents visual inconsistencies and forces developers to maintain the centralized token definitions.
How do CSS Modules access global custom properties without importing them?
CSS custom properties declared on the :root selector in src/styles/globals.css cascade to every element in the document, including those styled by CSS Modules. While the class names generated by CSS Modules (like .root in Button.module.css) are locally scoped to avoid naming collisions, the var(--token) references resolve against the global :root context where the values are defined.
What happens when I need to add a new color to the design system?
You must define the new token in src/styles/globals.css within the :root selector, following the existing naming convention (e.g., --accent-3 or --state-error). Once defined, any CSS Module can reference it immediately using var(--new-token). Because tokens are CSS custom properties rather than compiled constants, no rebuild of the CSS Modules is required—the new value becomes available instantly across all components.
Can I override tokens for specific themes or modes?
Yes. Because Instatic uses standard CSS custom properties, you can redefine token values under specific selectors or media queries in globals.css. For example, wrapping theme overrides in a [data-theme="dark"] selector automatically switches all referenced values across every CSS Module without modifying individual component styles. This cascade behavior ensures that theming remains maintainable at the global level while components remain agnostic to the active theme.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →