Luminous Blue · 2027
The complete, portable token and component reference behind dashone.ai — for reuse whenever you're building a new landing page, report, or self-contained artifact that should look and feel like a DashOne property.
Contents
01 — Color
Three color roles, mixed in fixed proportion. This ratio is the single most important rule in the system — get it right and everything else (contrast, hierarchy, restraint) follows.
Page backgrounds, card surfaces, section alternates. The silent base layer — it never competes with blue.
Anchors the layout. Never blue here.
Max 2 blue tokens in the same viewport. Never as body text, never as a section background.
--color-lb-core passes AA (4.8:1) — use it for button labels. --color-lb-core on white/chalk fails AA as text — never use it for body copy or links on a light surface. In dark mode, lb-core on --color-dark-bg reads as clean neon with no extra effects needed; for dark-surface cards use --color-lb-mist instead of lb-core — lb-core on a dark card surface lands around 2:1, which fails AA.
--color-lb-ice: #EAEBFF; --color-lb-ghost: #C5C8FF; --color-lb-mist: #7B80FF; --color-lb-glow: #4B52FF; --color-lb-vivid: #2328FF; --color-lb-core: #1B1FE8; --color-lb-deep: #0E11B0; --color-chalk: #F7F6F2; --color-warm: #EDE8D8; --color-maize: #F5E6A3; --color-stone: #D8D6CE; --color-ash: #888780; --color-ink: #444441; --color-void: #1C1A18; --color-deep-green:#1C2130; --color-forest: #252C3E; --color-pine: #1E2A22; --color-dust: #38405A; --color-sage: #C6E8D4; --color-dark-bg: #0A0B1A; --color-dark-surface: #12152B; --color-dark-card: #1A1E38; --color-dark-border: #252847; --color-dark-muted: #3A3D5C; --color-success: #1A5C42; --color-success-bg:#C6E8D4; --color-warning: #B07D00; --color-warning-bg:#FFF3C4; --color-error: #A32D2D; --color-error-bg: #FCEBEB; --color-info: var(--color-lb-core); --color-info-bg: var(--color-lb-ice);
02 — Typography
Fraunces for anything that should feel editorial and considered. DM Sans for everything functional. Never Inter, Roboto, Arial, or system-ui as a primary face.
--font-display
Fraunces
Display / hero headlines · Georgia, serif fallback
--font-body
DM Sans
Body copy, UI · system-ui fallback
--font-mono → JetBrains Mono, 'Courier New', monospace
| Token | Size | Used for | Preview |
|---|---|---|---|
--text-xs | 0.64rem / 10.2px | labels, badges | The quick brown fox |
--text-sm | 0.8rem / 12.8px | captions, meta | The quick brown fox |
--text-base | 1rem / 16px | body | The quick brown fox |
--text-md | 1.25rem / 20px | lead / intro | The quick brown fox |
--text-lg | 1.563rem / 25px | h3 | The quick brown fox |
--text-xl | 1.953rem / 31px | h2 | The quick brown |
--text-2xl | 2.441rem / 39px | h1 | The quick |
--text-3xl | 3.052rem / 48.8px | display | The quick |
--text-4xl | 4rem / 64px | hero (fixed) | Quick |
--text-hero | clamp(3rem, 7vw, 6rem) | fluid hero | Fluid |
Weights
light 300Aa
regular 400Aa
medium 500Aa
bold 600Aa
display 700Aa
Line-height
tight 1.1display
snug 1.3headings
normal 1.6body
loose 1.8prose
Letter-spacing
tight -0.03emdisplay
wide 0.04emlabels
wider 0.08emcaps badges
widest 0.12emeyebrows
Eyebrow label
Lead paragraph — light weight, loose leading, used for intros directly under a heading.
Caption — small, muted, for meta info and timestamps.
.mono → inline code / stats / mono figures
03 — Spacing & Layout
4px base grid. Containers, section rhythm and a z-index scale so nothing fights over stacking context.
Space scale (4px grid)
Radius scale
Containers
--container-sm | 640px |
--container-md | 768px |
--container-lg | 1024px |
--container-xl | 1280px (default .container) |
--container-2xl | 1440px |
--container-pad | clamp(1rem, 4vw, 2rem) |
| Token | Value |
|---|---|
--section-sm | clamp(3rem, 6vw, 5rem) |
--section-md | clamp(5rem, 10vw, 8rem) — default .section |
--section-lg | clamp(7rem, 14vw, 12rem) |
| Token | Value |
|---|---|
--z-below | -1 |
--z-raised | 10 |
--z-dropdown | 100 |
--z-sticky | 200 (nav uses this) |
--z-overlay | 300 |
--z-modal | 400 |
--z-toast | 500 |
.grid-auto — auto-fit, min 260px
.layout-feature — golden ratio 1 : 1.618
--space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 20px; --space-6: 24px; --space-8: 32px; --space-10: 40px; --space-12: 48px; --space-16: 64px; --space-20: 80px; --space-24: 96px; --space-32: 128px; --section-sm: clamp(3rem, 6vw, 5rem); --section-md: clamp(5rem, 10vw, 8rem); --section-lg: clamp(7rem, 14vw, 12rem); --container-sm: 640px; --container-md: 768px; --container-lg: 1024px; --container-xl: 1280px; --container-2xl: 1440px; --container-pad: clamp(1rem, 4vw, 2rem); --radius-sm: 4px; --radius-md: 8px; --radius-lg: 12px; --radius-xl: 16px; --radius-2xl: 24px; --radius-full: 9999px; --border-thin: 0.5px; --border-base: 1px; --border-accent: 2px; --z-below: -1; --z-base: 0; --z-raised: 10; --z-dropdown: 100; --z-sticky: 200; --z-overlay: 300; --z-modal: 400; --z-toast: 500;
04 — Motion
Purposeful, not decorative. One orchestrated reveal beats many micro-animations. prefers-reduced-motion is always respected — every animation collapses to 0.01ms.
fade-up
translateY(20px) → 0, opacity 0→1
scale-in
scale(.96) → 1, spring ease
fade-in
opacity 0 → 1
Click a card to replay its keyframe.
Duration
--duration-instant | 80ms |
--duration-fast | 150ms — hover transitions |
--duration-normal | 250ms |
--duration-slow | 400ms |
--duration-xslow | 600ms |
--duration-reveal | 800ms — page reveals |
Easing
--ease-out-expo | cubic-bezier(.16,1,.3,1) |
--ease-out-quart | cubic-bezier(.25,1,.5,1) |
--ease-in-out | cubic-bezier(.4,0,.2,1) |
--ease-spring | cubic-bezier(.34,1.56,.64,1) — slight overshoot |
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--duration-instant: 80ms; --duration-fast: 150ms; --duration-normal: 250ms;
--duration-slow: 400ms; --duration-xslow: 600ms; --duration-reveal: 800ms;
--stagger-1: 0ms; --stagger-2: 60ms; --stagger-3: 120ms;
--stagger-4: 180ms; --stagger-5: 240ms; --stagger-6: 300ms;
@keyframes fade-up { from{opacity:0;transform:translateY(20px)} to{opacity:1;transform:translateY(0)} }
@keyframes fade-in { from{opacity:0} to{opacity:1} }
@keyframes scale-in { from{opacity:0;transform:scale(.96)} to{opacity:1;transform:scale(1)} }
@keyframes slide-right{ from{opacity:0;transform:translateX(-16px)} to{opacity:1;transform:translateX(0)} }
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
05 — Components
Live-rendered with the actual components.css rules — what you see below is pixel-accurate to the site.
Buttons — .btn-primary max ONE per section
.btn-ghost-light — for dark / gradient hero surfaces
Badges
Cards
.card
Default surface
.card-warm
Warm neutral variant
.card-tinted
Blue-tinted callout
Input & divider
Focus state uses a 3px lb-ice ring (lb-mist ring at 20% in dark mode).
Nav bar (structure 30%)
.btn { /* base: inline-flex, radius-md, space-3/6 padding, duration-fast transitions */ }
.btn-primary{ background: var(--color-lb-core); color:#fff; } /* hover → lb-glow */
.btn-ghost { background: transparent; color: var(--color-lb-core); border-color: var(--color-lb-core); }
.btn-ghost-light { background: rgba(255,255,255,.12); color:#fff; border-color: rgba(255,255,255,.35); } /* dark hero only */
.btn-neutral{ background: var(--color-stone); color: var(--color-ink); }
.btn-sm / .btn-lg → padding + font-size overrides
.badge { radius-full, uppercase, border: currentColor }
.badge-blue / .badge-green / .badge-neutral
.card { chalk bg (dark: dark-surface), stone border, radius-lg, space-6 padding }
.card-warm { warm bg (dark: dark-card) }
.card-tinted{ lb-ice bg, lb-ghost border }
.input { chalk bg, stone border, radius-md; focus → lb-core border + 3px lb-ice ring }
.nav { deep-green bg always — never blue, never flips in dark mode }
.nav-link { white 45% → white 90% on hover/aria-current }
:focus-visible { outline: 2px solid var(--color-lb-core); outline-offset: 3px; }
06 — Theming
Every surface and text color on a content card must flip with prefers-color-scheme. Use semantic tokens — never raw color tokens — for anything sitting on a card or section.
CLAUDE.md, but as of 04 Aug 2026 these custom properties are not declared globally — tokens.css has no :root semantic layer and no dark-mode block. They're only used ad hoc, undeclared, inside two page files (fde.astro, ai-applied-layers.astro, plus their /en copies) — meaning var(--text-default) etc. currently resolve to nothing there unless a fallback is supplied. The recommended values below are a proposed canonical mapping (consistent with how .card / .input already flip via raw-token @media blocks in components.css) — not yet centralized. If you build on this system going forward, define these once at :root instead of re-deriving them per page.
| What you're styling | Use (semantic) | Never (raw) |
|---|---|---|
| Card background | --surface-card / --surface-card-alt | --color-chalk / --color-warm |
| Section background | --surface-page / --surface-page-alt | --color-chalk / --color-warm |
| Card body text | --text-default / --text-strong | --color-ink / --color-void |
| Card title text | --text-strong | --color-void |
| Muted / caption text | --text-muted | --color-ash |
| Tinted callout bg | --surface-tinted | --color-lb-ice |
| Text on tinted bg | --text-on-tinted | --color-lb-deep |
| Link text | --text-link | --color-lb-core |
| Card border | --border-default / --border-tinted | --color-stone |
Why: raw tokens like --color-deep-green are the same hex in both themes. color: var(--color-deep-green) on a card is fine on a chalk card in light mode — and dark-on-dark, unreadable, on a dark-surface card in dark mode. Semantic tokens swap their backing color via the dark-mode block instead.
:root {
--surface-page: var(--color-chalk);
--surface-page-alt: var(--color-warm);
--surface-card: var(--color-chalk);
--surface-card-alt: var(--color-warm);
--surface-tinted: var(--color-lb-ice);
--text-default: var(--color-ink);
--text-strong: var(--color-void);
--text-muted: var(--color-ash);
--text-on-tinted: var(--color-lb-deep);
--text-link: var(--color-lb-core);
--border-default: var(--color-stone);
--border-tinted: var(--color-lb-ghost);
}
@media (prefers-color-scheme: dark) {
:root {
--surface-page: var(--color-dark-bg);
--surface-page-alt: var(--color-dark-surface);
--surface-card: var(--color-dark-surface);
--surface-card-alt: var(--color-dark-card);
--surface-tinted: rgba(27, 31, 232, 0.15);
--text-default: var(--color-stone);
--text-strong: var(--color-chalk);
--text-muted: var(--color-lb-ghost);
--text-on-tinted: var(--color-lb-mist);
--text-link: var(--color-lb-mist); /* lb-core fails AA on dark cards */
--border-default: var(--color-dark-border);
--border-tinted: rgba(123, 128, 255, 0.3);
}
}
<span style={`color:${d.accent}`}>tag</span>
<p style="color: var(--color-deep-green)">
visible in light, invisible in dark
</p>
<div class="card" style="background:#fff;color:#000">
<span class="badge badge-blue">tag</span> <p style="color: var(--text-default)"> readable in both themes </p> <div class="card"><!-- bg + color from .card --></div>
If you reach for !important to fix a dark-mode contrast bug, the raw token is the bug — swap it for the matching semantic token instead of patching it.
07 — Governance
Non-negotiable constraints. Breaking these is what makes a page stop looking like a DashOne property.
Color
lb-core as body textlb-core as a section backgroundComponents
.btn-primary — ONE per section, max.btn-ghost for secondary actions.nav is always deep-green — no exceptions.cardTypography
.eyebrow for section labels above headingsMotion
--duration-fast (150ms)--duration-reveal (800ms)prefers-reduced-motion08 — Source
How the system is organized in the dashone.ai repo, for context if you're replicating this in another Astro project.
global.css chains tokens → typography → spacing → motion → layout → components, then resets. Never duplicate token values inside a component-scoped <style> block — always reference the variable.
09 — Portability
To build a self-contained artifact (Claude.ai artifact, a static report, a one-off landing page) that looks like a DashOne property, without the Astro build:
:root block.@import or <link> for Fraunces + DM Sans + JetBrains Mono (see this page's <head> for the exact URL).components.css in the repo.a9aee79). If dashone.ai's tokens change, this reference will drift; re-generate it from src/styles/ rather than trusting an old copy indefinitely.