# How Instatic Handles CSS Generation Through Core Framework

> Learn how Instatic generates CSS using a two-pass architecture. Explore its core framework for creating custom properties and utility classes from design tokens.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: internals
- Published: 2026-07-03

---

**Instatic generates CSS via a two-pass architecture that produces both `:root` CSS custom properties and locked utility classes from design tokens, orchestrated by the `buildFrameworkPlan` function in [`src/core/framework/generate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/framework/generate.ts).**

The Core Framework powers Instatic's visual design system by converting user-defined settings into optimized stylesheets. This repository (CoreBunch/Instatic) implements a deterministic generation pipeline that creates matching CSS variables and utility classes without duplicating token traversal.

## The Architecture: `buildFrameworkPlan` Orchestration

The generation process centers on `buildFrameworkPlan`, located in [`src/core/framework/generate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/framework/generate.ts) at lines 91-109. This function serves as the central dispatcher for all CSS generation, accepting `FrameworkGenerationSettings` and returning a `FrameworkPlan` containing both the `:root` CSS block and utility class mappings.

```ts
export function buildFrameworkPlan(
  settings: FrameworkGenerationSettings | null | undefined,
): FrameworkPlan {
  const preferences = resolveFrameworkPreferences(settings?.preferences)
  const colors = generateFrameworkColorPlan(settings?.colors)
  const typography = generateFrameworkTypographyPlan(settings?.typography, preferences)
  const spacing = generateFrameworkSpacingPlan(settings?.spacing, preferences)

  return {
    rootCss: composeFrameworkRootCss(colors.variableSets, [
      ...typography.variables,
      ...spacing.variables,
    ]),
    utilityClasses: {
      ...colors.utilityClasses,
      ...typography.utilityClasses,
      ...spacing.utilityClasses,
    },
  }
}

```

### Resolving User Preferences

Before generating CSS, the system calls `resolveFrameworkPreferences` from [`src/core/framework/preferences.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/framework/preferences.ts) to normalize user-defined settings like light versus dark mode defaults. This ensures consistent values across both variable and utility class generation.

## Token Enumeration and the Shared Plan Pattern

Instatic optimizes performance by planning each token family exactly once. The `planColorTokens` function in [`src/core/framework/colors.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/framework/colors.ts) (lines 81-92) demonstrates this pattern:

```ts
function planColorTokens(settings: FrameworkColorSettings | null | undefined): ColorTokenPlan[] {
  // … sort, de‑duplicate slugs, expand variants …
}

```

Similar planning functions exist in [`typography.ts`](https://github.com/CoreBunch/Instatic/blob/main/typography.ts) and [`spacing.ts`](https://github.com/CoreBunch/Instatic/blob/main/spacing.ts). By creating a single ordered enumeration per family, both the variable-set generation and utility-class generation reuse identical data structures. This eliminates duplicated traversals and guarantees that token order, slug deduplication, and variant expansion remain synchronized between the `:root` definitions and utility selectors.

## Generating the `:root` CSS Variable Block

The `:root` CSS is constructed by `composeFrameworkRootCss` in [`src/core/framework/generate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/framework/generate.ts) (lines 50-60):

```ts
function composeFrameworkRootCss(
  colorVariables: FrameworkColorVariableSets,
  scaleVariables: FrameworkScaleVariable[],
): string {
  return [
    formatCssVariableBlock(':root', [...colorVariables.light, ...scaleVariables]),
    formatFrameworkColorThemeCss(colorVariables),
  ]
    .filter(Boolean)
    .join('\n\n')
}

```

This function concatenates the light-mode variables with typography and spacing scales, then appends dark-mode overrides if present.

### Dark Mode Implementation

When a dark-mode palette exists, `formatFrameworkColorThemeCss` adds secondary selector blocks using constants defined in [`src/core/framework/colors.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/framework/colors.ts): `DEFAULT_THEME_OVERRIDE_SELECTOR` and `ALT_THEME_SELECTOR`. The resulting CSS injects variables under selectors like `:root.theme-alt` to support theme switching.

### Variable Formatting

The `formatCssVariableBlock` function in [`src/core/framework/cssVariables.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/framework/cssVariables.ts) emits the final CSS:

```css
:root {
  --primary: hsla(...);
  --spacing-1: 0.25rem;
  …
}

```

## Utility Class Generation

Alongside CSS variables, the framework generates locked utility classes that implement the same tokens. Each token family produces its own utility class map.

### Color Utilities

In [`src/core/framework/colors.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/framework/colors.ts), the `colorUtilityClassesFromPlan` function (lines 89-102) and `generateFrameworkColorUtilityClasses` create classes like `text-primary`, `bg-primary-d-1`, and `border-primary-l-2`.

### Typography and Spacing Utilities

The pattern repeats in [`src/core/framework/typography.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/framework/typography.ts) with `generateFrameworkTypographyUtilityClasses` emitting classes for `font-size`, `line-height`, and `font-weight`. Similarly, [`src/core/framework/spacing.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/framework/spacing.ts) contains `generateFrameworkSpacingUtilityClasses` which generates margin and padding classes such as `m-4` and `p-2`.

All generated utility classes are **locked** (`generated.locked: true`), preventing the publisher from rewriting them during the rendering phase.

## Integration Points

### Admin Canvas Rendering

The admin UI imports the generator directly in [`src/admin/pages/site/canvas/canvasClassCss.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/site/canvas/canvasClassCss.ts) to style the live preview:

```ts
import { generateFrameworkRootCss } from '@core/framework'

const frameworkCss = generateFrameworkRootCss({
  colors: userColorSettings,
  typography: userTypographySettings,
  spacing: userSpacingSettings,
})

```

This ensures the canvas reflects the exact same design tokens as the final published site.

### Publishing Pipeline

During page publication, the server invokes `buildFrameworkPlan` and injects the results into the static artifact. The `rootCss` content is embedded directly into the HTML:

```html
<style>
  :root { … }
  :root.theme-alt { … }
</style>
<link rel="stylesheet" href="/_instatic/framework-utilities.css">

```

The utility stylesheet is serialized from the `utilityClasses` map by the publisher's CSS serializer in [`src/core/publisher/frameworkCss.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/publisher/frameworkCss.ts).

## Why the Two-Pass Design Matters

The architecture deliberately separates token planning from CSS emission to achieve three critical goals:

- **Performance**: Token lists are traversed once per family rather than twice (once for variables and once for utilities).
- **Determinism**: Shared `ColorTokenPlan`, typography plans, and spacing plans ensure identical slug ordering and variant IDs across both outputs, preventing mismatches between `:root` definitions and utility selectors.
- **Cacheability**: The resulting `FrameworkPlan` object can be cached in the render cache and reused for subsequent renders of the same site version, avoiding recomputation.

## Summary

- Instatic's **Core Framework** generates CSS through the `buildFrameworkPlan` function in [`src/core/framework/generate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/framework/generate.ts), which orchestrates the creation of both `:root` variables and utility classes.
- The system uses a **single-pass token planning** strategy via `planColorTokens`, `planTypographyTokens`, and `planSpacingTokens` to ensure consistency and performance.
- **CSS custom properties** are formatted by `composeFrameworkRootCss` and `formatCssVariableBlock`, with optional dark-mode support via `formatFrameworkColorThemeCss`.
- **Utility classes** are generated as locked entities (`generated.locked: true`) for colors, typography, and spacing, ready for the publisher's CSS serializer.
- The architecture serves both the **admin canvas** (via `generateFrameworkRootCss`) and the **publishing pipeline** (via `buildFrameworkPlan`), ensuring identical styling between the editor and final output.

## Frequently Asked Questions

### How does Instatic ensure CSS variables match utility classes?

Instatic uses a shared planning phase where `planColorTokens`, `planTypographyTokens`, and `planSpacingTokens` create single ordered enumerations for each token family. Both the `:root` variable generator and utility class generator consume these same data structures, guaranteeing identical slug ordering, variant expansion, and deduplication.

### What is the purpose of locked utility classes in Instatic?

Utility classes are marked with `generated.locked: true` to prevent the publisher from rewriting them during the rendering phase. This ensures that the framework-generated classes remain immutable and performant in the final static output.

### Where does the dark mode CSS get generated in Instatic?

Dark mode CSS is handled by `formatFrameworkColorThemeCss` in [`src/core/framework/generate.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/framework/generate.ts), which uses constants `DEFAULT_THEME_OVERRIDE_SELECTOR` and `ALT_THEME_SELECTOR` from [`src/core/framework/colors.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/framework/colors.ts) to emit override blocks under selectors like `:root.theme-alt`.

### Can the generated CSS be cached for performance?

Yes. The `FrameworkPlan` object returned by `buildFrameworkPlan` can be cached in the render cache and reused for subsequent renders of the same site version, avoiding the computational cost of regenerating CSS variables and utility classes on every request.