Loomio DS
Visual reference for every token in app/globals.css. Single source of truth — what renders here is what the rest of the app gets.
Click a variant — it applies to the whole app and persists across pages. To add a new DS: declare :root[data-ds="slug"] in globals.css + add an entry to DS_VARIANTS here.
This page IS the Loomio design system — a single browsable reference for every visual decision in the app. It's structured in three layers, top to bottom:
- Foundation tokens — colours, type scale, spacing, radii, motion, semantic signals. These cascade through the entire app already; swapping a DS variant (Ember / Ink) changes them everywhere.
- Chrome primitives — class-based components the app currently uses (
.ui-card,.signal-pill,<Button>). Today's screens are built from these. - Bloom primitives — the new editorial direction (
.loomio-glass,.loomio-display,.loomio-btn-accent, …). Opt-in classes. Use them in any screen to bring it into Bloom-feel.
How it rolls out: per-screen migration. We pick a screen (landing first), swap chrome → Bloom, you review the preview, sign off. Repeat for dashboard, editor, auth. No big bang — each step is reversible and one preview URL away.
Surfaces
Layered backgrounds from page (0) to floating panels.
surface-0surface-1surface-2surface-3surface-4bg-nodebg-node-headercanvas-bgText
Five tiers of foreground colour. Use `var(--text-*)` directly.
text-primaryThe quick brown fox jumpstext-secondaryThe quick brown fox jumpstext-mutedThe quick brown fox jumpstext-placeholderThe quick brown fox jumpstext-inverseThe quick brown fox jumpsBorders
Four chrome borders + three node-specific.
border-subtleborderborder-strongborder-mutedborder-nodeborder-node-hoverborder-node-selBrand & accents
Greens carry continuity from Canvas. Six accents for node categories and emphasis.
brand-green-neonbrand-green-midbrand-green-darkbrand-bluebrand-cyanbrand-pinkbrand-orangebrand-amberNode categories
Each node type gets a coloured accent strip + signal-rail on the canvas.
--brand-green-neon--node-accent-ai--node-accent-image--node-accent-video--node-accent-3d--node-accent-tools--node-accent-html--node-accent-design--node-accent-exportType scale
Geist for body/UI, Geist Mono for code/meta, Instrument Serif italic for editorial accents.
--text-2xsLoomio · the canvas--text-xsLoomio · the canvas--text-smLoomio · the canvas--text-mdLoomio · the canvas--text-baseLoomio · the canvas--text-lgLoomio · the canvas--text-xlLoomio · the canvas--text-2xlLoomio · the canvas--text-3xlLoomio · the canvas--text-4xlLoomio · the canvas--text-5xlLoomio · the canvas--text-displayLoomio · the canvas--text-heroLoomio · the canvasSpacing
Tailwind-aligned scale. Use Tailwind classes (p-2, gap-3) by default; reach for var(--space-N) inside inline style or CSS.
--space-px--space-0_5--space-1--space-1_5--space-2--space-2_5--space-3--space-4--space-5--space-6--space-8--space-10--space-12--space-16--space-20--space-24Radii
Canonical card/button radius is --radius-lg (8px). Pill for chips.
--radius-sm--radius-md--radius-lg--radius-xl--radius-2xl--radius-pillMotion
Durations + easings. Hover the bars to feel each curve.
--duration-instant--duration-fast--duration-normal--duration-slow--duration-slower--ease-linear--ease-out--ease-in--ease-in-out--ease-springSignal
Semantic feedback. Use for toasts, chips, edge errors — beyond brand green.
successwarning-fgdanger-fginfo-fgButtons (UI primitive)
The shared <Button> component — five variants × three sizes. For Bloom-flavour pill buttons see the next section.
smmdlgThe new direction
Editorial-italic accents, drifting orbs, liquid glass, pill buttons, pulsing status dots. Each piece below is an opt-in CSS class in app/globals.css — sprinkle into any screen.
Typography roles
.loomio-{display, italic, eyebrow, body, mono} — semantic vocabulary, not raw sizes.
.loomio-display.loomio-eyebrow.loomio-bodyLoomio reads as editorial software — a slightly larger body, generous leading, and the occasional italic flourish where a wordmark or accent earns it. Reads like Bloom; works like a tool.
.loomio-monoBloom buttons
.loomio-btn / -btn-solid / -btn-accent. Pill-shaped CTAs with glow on accent.
Bloom input
.loomio-input — soft glass field with brand-green focus ring.
Tags + status dot
.loomio-tag with -coral / -teal / -active variants. .loomio-dot pulses.
Liquid glass
.loomio-glass — backdrop-blur 28px + saturate 160%. -active variant adds brand-green inner glow.
Floating glass card. Reads against orbs in the background.
Same backdrop, plus brand-green inner glow + halo for emphasis state.
Canvas (editor) — truthful mock
Mirror of the real /editor: top-bar with project label + RUN FLOW, vertical icon rail, three nodes with real form fields, NeonEdge connectors, bottom zoom toolbar. The DS preview shouldn't lie about what production looks like.
Dashboard — truthful mock
Mirror of the real /dashboard: 'Projects' heading + inline usage stats, templates row with category icons and tags, project cards with mini 3-node snapshots + optional DS chip + relative time.
Truthful integration: the dashboard mock now matches what users see in production — inline usage stats, templates row with category icons + tags, project cards with mini-canvas snapshots and DS chips. Migration replaces inline styles with .ui-card + Bloom typography + tag/dot primitives.
Components
Class-based primitives from globals.css. Reuse before inventing.
Coverage
What a DS swap actually controls. Full = override the listed tokens and the whole surface flips. Partial / manual = some sites still bake values inline.
hex + RGB tuples, --brand-*--node-accent-{ai,image,video,3d,tools,html,design,export}--signal-* incl. bg/border/fg variants--surface-*, --bg-*--text-* incl. light-theme overrides--border-*, --radius-*--text-2xs..--text-hero, --tracking-*, --leading-*--duration-*, --ease-*--button-{variant}-{bg,fg,border,bg-hover}--modal-accent-gradient--edge-*--white, --black + RGB tuples; alpha variants via rgb(var(--*-rgb) / a)brand + 13 accents + white/black swept; no literal hex remains in JSXbrand + node accents + white/black/text rgba swept to rgb(var / a)9/10/11/12/13/14/16/18px swept to --text-* vars (6 outliers left)Tailwind p-N/m-N aligns 1:1 with --space-N; both produce identical px valuesa handful of one-off shades remain hard-coded; sweep when a DS needs themapp/globals.css. Edit there; this page reflects automatically.