export

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 9, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package export serialises a theme.Theme emission into the project layout claude.ai/design consumes: theme.json, the machine-readable generative parameters; styles.css, the token sheet; readme.md, the project's front door; and the foundation pages under foundations/. Capture collects the first emission of each Theme observable into a Snapshot; Write renders the whole tree into a target directory. cmd/vg-tokens is the command-line front door.

The token sheet

styles.css carries one :root block (the light scheme plus every mode-invariant scale, comfortable density), one .dark override block (the paired dark colours only) and one .compact override block (the compact density metrics only). ADR-007 records the reference project's token families but not its dark-mode selector, so the sheet uses class overrides — chosen here, not ADR-recorded — and .compact follows the same pattern; the two switches are orthogonal.

Colour variables follow ADR-007's families exactly:

  • --color-<role>-100 … --color-<role>-900 — the nine-step functional ramps, roles neutral, primary, secondary, tertiary, error, success and warning. The last three are the status roles: hue-fixed rather than seed-derived, so a re-brand never rotates them.
  • Pinned bases and the semantic layer: --color-accent is the Primary pin (the reference project's .btn-primary consumes --color-accent, per ADR-007), with --color-on-accent its on-colour; --color-secondary, --color-tertiary, --color-error, --color-success and --color-warning are the other role pins with their --color-on-* companions; --color-bg, --color-text are the pinned background and body text; --color-surface and --color-divider are the semantic layer's ramp-resolved card and separator colours.

The remaining families, all emitted in :root only because they do not change with the scheme:

  • --font-family and --font-family-code (the code style's mono family), plus --font-<role>-size, -line-height, -weight and -tracking per type role (display-large … body-small, and code — the mono style outside the MD3 grid, at body-medium's metrics): sizes, line heights and tracking in px, weights as CSS numeric weights.
  • --density-control-height, --density-padding-x and --density-padding-y from tokens.Density: :root carries tokens.Comfortable, the .compact block overrides with tokens.Compact. --density-min-hit-target is the WCAG 2.5.5 pointer-target floor, emitted once and never overridden — density scales the drawn control, never the clickable area.
  • --space-<key> from tokens.SpacingScale, keys as the Go scale names them (0, 1, 2, … 24), in px.
  • --radius-<key> from tokens.RadiusScale in Tailwind naming (none, sm, base, md, lg, xl, 2xl, 3xl, full), in px; Base is also theme.json's base radius parameter.
  • --elevation-<level> (0–3): the tonal surface fills, the DEFAULT elevation cue (E2.1). Each level is emitted as a var() reference — var(--color-bg) for level 0's bg-pin sentinel, var(--color-neutral-N) for the ramp steps — so the surfaces flip with .dark through the colour overrides and the sheet itself states that an elevation level is a neutral-ramp step.
  • --shadow-<level> (0–3): CSS box-shadow approximations of the dp depths, the OPT-IN cue floating transients (menus, dialogs, tooltips) layer over their tonal fill (E2.2). Each level's dp depth d becomes "0 <d>px <2d>px 0 rgba(0, 0, 0, 0.2)" — y-offset the depth, blur twice it, no spread, black at 20% — and level 0 is "none".
  • --ease-<name> from tokens.MotionScale: the six MD3 easing presets as cubic-bezier() strings (standard and emphasized families, each plain / -accelerate / -decelerate).
  • --duration-<stop> (x-fast, fast, normal, slow, x-slow): the five MD3-pinned duration stops in ms. The spring presets have no CSS counterpart — springs are Go-side physics — and are serialised only in theme.json's motion parameters.

The foundation pages

foundations/color.html, type.html and layout.html render the scales at real sizes, and readme.md is the file a human or an agent reads first. The pages are static HTML that reads only from the emitted sheet: every styled colour, size, radius, shadow and font value is a var() reference into ../styles.css, so regenerating the sheet from another seed reflows every page. Literal token values appear only as annotation text — hexes, px numbers, and the measured APCA Lc and WCAG 2 ratio of each text pair — printed for both modes (labelled L and D) because text cannot flip with a class the way painted specimens do. Each page carries a light/dark toggle flipping the .dark class on the root element. The page test enforces the no-hard-coded-values rule: no hex colours or px lengths in any style context, and every referenced variable declared by the sheet.

The generative parameters

theme.json records what reproduces the theme: the seed (hex plus its OKLCh hue and sat), the pinned role hexes per mode, the heading, body and mono faces, the base radius, the shared CIELAB L* scales measured back from the emitted neutral ramps, the density model (the active setting by name, both settings' metrics and the invariant hit-target floor), the elevation model (surface step and shadow dp per level) and the motion set (durations in ms, easing control points, spring presets). tokens.FromSeed(seed) regenerates the full palette from the seed alone — the round-trip test asserts it. The per-role --font-*-size tokens come from Typography, which is the theme's only type source since v0.3.0 dropped the size-only Theme.Type stream.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Write

func Write(dir string, s Snapshot) error

Write renders s into dir as the full Claude Design project layout — theme.json, styles.css, readme.md and the foundation pages under foundations/ — creating directories as needed. Existing files are overwritten: the tree is generated output, regenerated whole.

Types

type DensityMetrics added in v0.0.15

type DensityMetrics struct {
	ControlHeight float64 `json:"controlHeight"`
	PaddingX      float64 `json:"paddingX"`
	PaddingY      float64 `json:"paddingY"`
}

DensityMetrics is one density setting's per-setting metrics in dp.

type DensityParams added in v0.0.15

type DensityParams struct {
	Setting      string         `json:"setting"`
	Comfortable  DensityMetrics `json:"comfortable"`
	Compact      DensityMetrics `json:"compact"`
	MinHitTarget float64        `json:"minHitTarget"`
}

DensityParams records the density model: the active setting's name ("comfortable" or "compact"), both settings' metrics, and the WCAG 2.5.5 pointer-target minimum in dp, which no setting scales.

type DurationParams added in v0.0.15

type DurationParams struct {
	XFast  float64 `json:"xFast"`
	Fast   float64 `json:"fast"`
	Normal float64 `json:"normal"`
	Slow   float64 `json:"slow"`
	XSlow  float64 `json:"xSlow"`
}

DurationParams carries the five duration stops in milliseconds.

type EasingParams added in v0.0.15

type EasingParams struct {
	Standard             [4]float64 `json:"standard"`
	StandardAccelerate   [4]float64 `json:"standardAccelerate"`
	StandardDecelerate   [4]float64 `json:"standardDecelerate"`
	Emphasized           [4]float64 `json:"emphasized"`
	EmphasizedAccelerate [4]float64 `json:"emphasizedAccelerate"`
	EmphasizedDecelerate [4]float64 `json:"emphasizedDecelerate"`
}

EasingParams carries each easing preset as its cubic-bezier control points [x1, y1, x2, y2] — the same four numbers the sheet's --ease-* variables carry inside cubic-bezier().

type ElevationParams added in v0.0.15

type ElevationParams struct {
	SurfaceSteps [4]int     `json:"surfaceSteps"`
	ShadowDp     [4]float64 `json:"shadowDp"`
}

ElevationParams pairs, indexed by level 0–3, the neutral-ramp surface step (0 = the bg pin sentinel) with the dp shadow depth. The arrays were six long through v0.1.x, when MD3 levels 4 and 5 survived as clamps onto level 3; F3.3 deleted them and the ladder is four storeys.

type Fonts

type Fonts struct {
	Heading string `json:"heading"`
	Body    string `json:"body"`
	Mono    string `json:"mono"`
}

Fonts names the typefaces. Mono is the code style's face — the sheet's --font-family-code.

type ModePins

type ModePins struct {
	Light Pins `json:"light"`
	Dark  Pins `json:"dark"`
}

ModePins carries the pinned bases for both modes.

type ModeScale

type ModeScale struct {
	Light [9]int `json:"light"`
	Dark  [9]int `json:"dark"`
}

ModeScale carries the L* scale for both modes.

type MotionParams added in v0.0.15

type MotionParams struct {
	Durations DurationParams `json:"durations"`
	Easings   EasingParams   `json:"easings"`
	Springs   SpringParams   `json:"springs"`
}

MotionParams records the motion set.

type Parameters

type Parameters struct {
	// Seed is the brand seed as lowercase #rrggbb; Hue and Sat are its
	// OKLCh hue (degrees, 2 decimals) and chroma (4 decimals), recorded for
	// the reader — regeneration starts from the hex.
	Seed string  `json:"seed"`
	Hue  float64 `json:"hue"`
	Sat  float64 `json:"sat"`

	// Pins are the pinned role bases per mode.
	Pins ModePins `json:"pins"`

	// Fonts names the heading, body and mono faces. Heading and body are
	// Roboto until a heading face exists; mono is the code style's face.
	Fonts Fonts `json:"fonts"`

	// Radius is the base radius in dp — tokens.RadiusScale.Base, the sheet's
	// --radius-base.
	Radius float64 `json:"radius"`

	// Scale is the shared CIELAB L* lightness scale per mode, steps
	// 100–900, measured back from the emitted neutral ramps. It documents
	// the generator's fixed scale (ADR-007); it is not itself an input —
	// FromSeed carries it.
	Scale ModeScale `json:"scale"`

	// Density records the theme's active setting by name plus both
	// published settings' metrics, and the density-invariant pointer-target
	// floor.
	Density DensityParams `json:"density"`

	// Elevation records the tonal model per level 0–3: the neutral-ramp
	// step of the surface fill (the default cue; 0 marks the bg pin, not a
	// ramp step) and the dp shadow depth (the opt-in cue for floating
	// transients).
	Elevation ElevationParams `json:"elevation"`

	// Motion records the captured MotionScale in full — duration stops,
	// easing beziers and spring presets — so the file reproduces it without
	// running the generator. Springs are Go-side physics with no CSS
	// counterpart; this is their only serialisation.
	Motion MotionParams `json:"motion"`
}

Parameters is theme.json's shape: the generative parameters that reproduce the theme. tokens.FromSeed(Seed) regenerates every ramp and pin — the round-trip test asserts it — so the file alone rebuilds the palette; the pins, scales, fonts, radius, density settings, elevation model and motion set are recorded alongside so a reader (or a prototype) need not run the generator to know them.

type Pins

type Pins struct {
	Bg        string `json:"bg"`
	Text      string `json:"text"`
	Accent    string `json:"accent"`
	Secondary string `json:"secondary"`
	Tertiary  string `json:"tertiary"`
	Error     string `json:"error"`
	Success   string `json:"success"`
	Warning   string `json:"warning"`
}

Pins records one mode's pinned bases as lowercase #rrggbb hexes. Accent is the primary pin — the sheet's --color-accent.

type Snapshot

type Snapshot struct {
	// Seed is the brand seed the colour schemes derive from — the light
	// scheme's pinned Primary, which FromSeed guarantees is the seed
	// byte-for-byte.
	Seed stdcolor.NRGBA

	// Light is the colour scheme the theme emitted; Dark is its paired
	// scheme, FromSeed(Seed)'s dark half.
	Light, Dark tokens.ColorTokens

	Typography tokens.Typography
	Density    tokens.Density
	Motion     tokens.MotionScale
	Spacing    tokens.SpacingScale
	Radius     tokens.RadiusScale
	Elevation  tokens.ElevationScale
}

Snapshot is one resolved theme: the first emission of each theme.Theme observable, with the paired dark colour scheme and the seed recovered from the light scheme's primary pin. It is the input Write serialises.

func Capture

func Capture(th theme.Theme) (Snapshot, error)

Capture collects the first emission of each observable a serialisation needs — Color, Typography, Density, Motion, Spacing, Radius and Elevation — into a Snapshot. (Type is not consumed: it duplicates Typography's sizes.)

The colour emission must be a seed-derived light scheme: FromSeed pins the light primary base to the seed exactly, so Capture recovers the seed from the emission's Primary and regenerates the pair. An emission FromSeed cannot reproduce — a dark scheme, or hand-assembled tokens — is an error, because theme.json could not honestly claim to reproduce it. The density emission must likewise be one of the two published settings — tokens.Comfortable or tokens.Compact — because theme.json records density as a named setting plus both settings' metrics, not as free-form numbers.

type SpringParam added in v0.0.15

type SpringParam struct {
	Mass      float64 `json:"mass"`
	Stiffness float64 `json:"stiffness"`
	Damping   float64 `json:"damping"`
}

SpringParam is one damped-oscillator preset. Damping is recorded at the shortest decimal that reproduces the float32 exactly (the critical presets are 2·√(k·m), an irrational number), so the file reproduces the Go value bit-for-bit — see f64.

type SpringParams added in v0.0.15

type SpringParams struct {
	Default SpringParam `json:"default"`
	Snappy  SpringParam `json:"snappy"`
	Gentle  SpringParam `json:"gentle"`
}

SpringParams carries the three spring presets.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL