Skip to content

Logo

ControlCraft mark

The wordmark is the primary brand surface. Use the icon-only variant when space is tight; the horizontal lockup everywhere else.

ControlCraft
size="lg"
ControlCraft
size="md" (default)
ControlCraft
size="sm"

Palette · layer 1

Static brand scales

Hex CSS vars under --color-{name}-{stop}. Theme-independent — these are the same color in light and dark mode. Use scale stops directly (e.g. text-primary-900) when you need a specific shade; reach for the semantic surface tokens (next section) when you want something that flips with the theme.

primary · deep ink

primary-50
primary-100
primary-200
primary-300
primary-400
primary-500
primary-600
primary-700
primary-800
primary-900

Default text. Inverse surfaces. The authority register.

accent · emerald

accent-50
accent-100
accent-200
accent-300
accent-400
accent-500
accent-600
accent-700
accent-800
accent-900

Motion spark. CTAs. Focus rings. The brand color.

secondary · warm amber

secondary-50
secondary-100
secondary-200
secondary-300
secondary-400
secondary-500
secondary-600
secondary-700
secondary-800
secondary-900

Pricing emphasis, secondary CTAs. Used sparingly.

neutral · cool grey

neutral-50
neutral-100
neutral-200
neutral-300
neutral-400
neutral-500
neutral-600
neutral-700
neutral-800
neutral-900

Borders, dividers, fills with no brand weight.

Palette · layer 2

Themed semantic surfaces

Hex CSS vars under --color-{bg|text|border}-*. Flip on [data-theme="dark"]. Component code never branches on theme — it always uses the named token, and the var resolves to the right hex per theme. Toggle the theme above and the swatches below all retune.

Surfaces

bg-primary

--color-bg-primary

Page surface. White / #0e1014.

bg-secondary

--color-bg-secondary

Card surface, sticky header tint.

bg-tertiary

--color-bg-tertiary

Hover states, nested panels.

bg-inverse

--color-bg-inverse

Inverted pill / badge surface.

Text + borders

Text

  • Aa · the quick brown foxtext-primary
  • Aa · the quick brown foxtext-secondary
  • Aa · the quick brown foxtext-tertiary
  • Aa · the quick brown foxtext-accent

Borders

  • --color-border-lightborder-light
  • --color-border-defaultborder
  • --color-border-darkborder-dark

Elevation

Themed shadow scale

The Tailwind tokens shadow-{soft,card,lift,glow} resolve through CSS vars in globals.css, so dark mode automatically re-tunes them with deeper rgba so cards still lift off the surface. Toggle the theme to confirm — the shadow under each card visibly changes.

elevation

shadow-soft

--shadow-soft

baseline card lift

elevation

shadow-card

--shadow-card

card on hover

elevation

shadow-lift

--shadow-lift

modal / overlay

elevation

shadow-glow

--shadow-glow

focus / featured CTA

Backgrounds

Hero gradient

bg-hero-grid is the only branded gradient in the system. It layers two radial glows (emerald top-left, amber bottom-right) over the bg-primary → bg-secondary base. Dark mode bumps the emerald to 12% and the amber to 8% so the page doesn't read as a flat slab of black.

bg-hero-grid

--gradient-hero-grid · light / dark variants in globals.css

Typography

Three voices

The system ships three named scales: h-display (the hero h1), h-section (page h2), and eyebrow (the small uppercase accent above headings). Body text isn't a named utility — compose it with text-* + leading-relaxed and the text-secondary token.

.h-displayhero h1, 4xl/5xl/6xl

Type a prompt, get a motion graphic.

.h-sectionpage h2, 3xl/4xl

Iteration is the product.

.eyebrowaccent label above a heading

How it works

body (lead)text-lg leading-relaxed text-text-secondary

Drop your illustration pack, type a prompt, get a broadcast-ready MP4 and editable Lottie in 90 seconds. Iteration thereafter is sub-second.

body (base)text-sm leading-relaxed text-text-secondary

Same engine across all three tiers. The difference is who runs it.

captiontext-xs text-text-tertiary

Used by brand, growth and product teams that would rather ship than wait on an agency.

mono captionfont-mono text-2xs / text-3xs

v0.1.0 · local-first · cloud-optional

Chrome

Card · chip · section

The three named container classes. Reach for them before composing new ones.

.card

Default container. Rounded-2xl, border-border-light, shadow-soft, hovers to shadow-card. Wraps grid items, list items, anything that needs a visible edge.

.chip successwarning

.chip

Tiny pill — status, count, label. Override the border / bg utility classes inline when you need a semantic tint.

.section · py-20 sm:py-24 lg:py-28
.section-tight · py-14 sm:py-16 lg:py-20

.section

Vertical rhythm. Two tiers — the standard section is the default, the tight one for short stat rows or proof bars.

Actions

Three buttons, three jobs

The whole site uses these three classes. btn-primary is the ink-on-light authoritative CTA; btn-accent is the emerald spark for the brand action; btn-ghost is the bordered tertiary.

.btn-primary

Authority. The primary CTA on most surfaces — deep ink reads as the most serious choice.

.btn-accent

The brand spark. Use for the one action you want the eye to land on first (top tier on /pricing, the generate button).

.btn-ghost

Tertiary. Pairs with btn-primary or btn-accent — don't use two of these next to each other.

Motion

Calm authority. Linear-style restraint. Tablix-clean.

Motion always communicates state. It never decorates. The system enforces this with named easing/duration tokens — never inline cubic-bezier or arbitrary ms values.

60%

--ease-out-quart

Workhorse. Fast start, gentle landing. Use for hover, focus, reveal, enter — anything responding to user input.

30%

--ease-snap

Snap. Near-instant with a hairline tail. Toggles, checkboxes, tab switches — anything that should feel decisive.

10%

--ease-in-quart

Exit. Slow start, fast finish. Drawer close, toast dismiss — conveys departure.

Spring (--ease-spring) is reserved for one moment per session (the generate-success punctuation). Linear (--ease-linear) is for indeterminate progress only — skeleton shimmer, spinner.

Motion · tokens

Durations & easings

Every token below is exposed to Tailwind too (duration-base, ease-out-quart, etc.) so you rarely need to reach for arbitrary values.

Durations

TokenValueUse
--dur-instant90mspress, color shift
--dur-fast180mshover, focus ring, chip toggle
--dur-base240msdrawer slide, panel reveal (default)
--dur-slow320mspreview reveal, large surface entry
--dur-stagger30ms× index for sibling sequences

Easings

TokenValueUse
--ease-out-quart0.2 0.8 0.2 160% — workhorse
--ease-snap0.12 0 0.08 130% — toggles
--ease-in-quart0.55 0 0.85 0.310% — exits
--ease-spring0.34 1.56 0.64 1once per session
--ease-linearlinearindeterminate progress only

Two more delay tokens worth knowing: --delay-loader-show (200ms) is how long to wait before showing a spinner so fast responses don't flash one, and --delay-loader-min (400ms) is how long to keep a visible loader so it doesn't jitter.

Motion · utilities

Reach for these instead of writing transition-*

Each utility binds a transition to a named token pair so you stay inside the design system. Hover the demos to see them fire — toggle to dark to confirm they hold up.

.motion-hover

Background, border, color, shadow on hover. The default.

.motion-press

Instant scale on active. Pairs with motion-hover for buttons.

.motion-reveal

Transform + opacity. For elements that enter the viewport.

.motion-snap

Tabs, segmented controls. Snappy.

.motion-drawer

Side drawer slide. Transform only, base duration.

Toast

.motion-exit

Exit transition — slow start, fast finish.

Motion · keyframes

Named animations

A tight catalog. Skeleton shimmer + mic-listening pulse + toast in/out are the only ones the production app ships.

.animate-skeleton-shimmer

The only background animation we ship. Linear-easing — it's indeterminate progress.

.animate-pulse-accent

Mic-listening ring. Subtle accent expansion — never a color flash.

Generated 22 panels

.animate-toast-in / -out

Toast enter (base, out-quart) and dismiss (fast, in-quart).

loader pattern

--delay-loader-show 200ms before spinner, --delay-loader-min 400ms once visible. Demo spins on click.

.stagger-child

Set style={{ '--i': index }} on each child to space sibling animations by --dur-stagger.

transition-transform
duration-fast ease-out-quart

Tailwind escape hatch

When you really need a one-off, use the duration-* / ease-* tokens — never raw ms / cubic-bezier.

When in doubt

Style decisions, encoded

Picking a color

  • Does it need to flip with the theme? → use a bg-bg-*, text-text-*, border-border-* token.
  • Is it a specific brand shade (e.g. a button bg)? → use the palette directly (bg-primary-900).
  • Need a focus ring? → default is focus-visible:ring-accent-500; :focus-visible is already handled at the base layer.

Picking a motion

  • Hover / focus state? → .motion-hover.
  • Snappy toggle or tab? → .motion-snap.
  • Element entering view? → .motion-reveal.
  • Drawer / panel slide? → .motion-drawer.
  • Dismiss / exit? → .motion-exit.