design system · v1

nebula

nebula is the design system that powers bryanchasko.com — a cosmic palette of deep navy, lavender accent, and aws orange highlight. every page on this site is built from the tokens documented below. dark mode is the default voice; light mode is a courtesy.

palette

brand chips first, semantic mappings second. semantic vars are the right citation in component css — brand vars are the source

brand

dark navy --nebula-dark-navy #1C2230

dark theme app shell

card navy --nebula-card-navy #0e1324

dark theme card surface

deep space --nebula-deep-space #0b0e14

dark theme background

nebula purple --nebula-purple #5E41A2

primary brand, links, active states

lavender --nebula-lavender #8169C5

hover states, secondary brand

lavender chip --nebula-lavender-chip #d8b4fe

dark mode link, soft accents

aws orange --nebula-orange #FF9900

highlight, cta accent, aws affiliation

aws orange dark --nebula-orange-dark #E68A00

orange hover state

semantic

--color-primary --nebula-purple primary action color
--color-primary-hover --nebula-lavender primary hover state
--color-accent --nebula-orange highlight + cta accent
--color-link --nebula-purple anchor color (light)
--color-link-hover --nebula-lavender-chip anchor hover (light)
--color-border --gray-200 card + divider borders
--color-border-hover --nebula-lavender interactive border highlight

typography

system stack — defaulting to platform fonts means crisp render at every viewport, zero web-font cost, no flash of unstyled text

font stacks

body --font-body

the quick brown fox jumps over the lazy dog

ui + prose

mono --font-mono

the quick brown fox jumps over the lazy dog

code, terminal greeting, var names

type scale

--font-size-xs 12px · 0.75rem

cosmic chip caption

--font-size-sm 14px · 0.875rem

body-secondary, meta

--font-size-base 16px · 1rem

paragraph body

--font-size-lg 18px · 1.125rem

lead paragraph

--font-size-xl 20px · 1.25rem

card title

--font-size-2xl 24px · 1.5rem

section h3

--font-size-3xl 30px · 1.875rem

section h2

--font-size-4xl 36px · 2.25rem

page h1

line height

tight --line-height-tight 1.25

stacking lines at this rhythm. the cosmic ledger keeps its accounting clean even as it stretches across the viewport. each line earns its place

headings

base --line-height-base 1.6

stacking lines at this rhythm. the cosmic ledger keeps its accounting clean even as it stretches across the viewport. each line earns its place

body prose

relaxed --line-height-relaxed 1.75

stacking lines at this rhythm. the cosmic ledger keeps its accounting clean even as it stretches across the viewport. each line earns its place

long-form notes

spacing

a 4-based scale doubling at each step until the gutter takes over. measure twice, gap once

xs --space-xs 4px · 0.25rem
sm --space-sm 8px · 0.5rem
md --space-md 16px · 1rem
lg --space-lg 24px · 1.5rem
xl --space-xl 32px · 2rem
2xl --space-2xl 48px · 3rem
3xl --space-3xl 64px · 4rem

radius

chip → pill, the curve grows as the surface widens. nothing is ever fully square except code

sm --radius-sm 4px

chips, tag pills

md --radius-md 6px

inputs, code spans

lg --radius-lg 8px

cards, buttons

xl --radius-xl 12px

hero panels, feature cards

full --radius-full 9999px

pills, avatars

elevation

dark mode reads as depth; light mode reads as paper. the elevation stack is the same idea drawn twice

surface 0 --color-background

page canvas

surface 1 --color-background-secondary

subtle separation, code backgrounds

surface 2 --color-builder-card-bg

builder card section, dark callouts

card surface --nebula-card-navy

dark theme cards (lifted)

shadow

shadows do the work of suggesting depth without committing to it. small for separation, xl for modal weight

sm --shadow-sm

subtle separation

md --shadow-md

cards, dropdowns

lg --shadow-lg

floating panels

xl --shadow-xl

modals, hero overlays

motion

three transition durations. fast for attention, base for confirmation, slow for arrival. every easing is the same cubic-bezier — variation is duration, not curve

fast --transition-fast 150ms cubic-bezier(0.4, 0, 0.2, 1)

micro-interactions, hover color shifts

base --transition-base 200ms cubic-bezier(0.4, 0, 0.2, 1)

button press, link hover

slow --transition-slow 300ms cubic-bezier(0.4, 0, 0.2, 1)

card lift, panel entry

hover any swatch above to feel the timing

components

specimens of the most-used composed atoms — buttons, cards, code, callouts. each renders with the live theme css so this page stays honest

buttons

card

service card aws

cards use the glassmorphism gradient + nebula border. hover lifts via translate-Y and amplifies the corner glow

learn more →

callout / chip

tag aws hero --nebula-purple

code block

:root {
	--color-primary: var(--nebula-purple);
	--color-accent: var(--nebula-orange);
	--space-md: 1rem;
}

containers

three boundaries do all the layout work — page, prose, and gutter

max width --container-max-width 1200px

outer page bound

content width --content-max-width 65ch

prose column (45-75 char read-width)

container padding --container-padding var(--space-lg)

gutter on narrow screens

source

the canonical token file. this page renders from a curated mirror at data/theme.yaml. when the two diverge the css below wins. PRs welcome

themes/bryan-chasko-theme/assets/css/core/tokens.css
/* ============================================
   CANONICAL DESIGN TOKENS — single source of truth

   This is the ONE place every CSS custom property (design token) is defined.
   Loaded FIRST in the core bundle (see layouts/partials/head.html) so every
   consumer sees tokens defined before use.

   Layers:
   1. layout primitives (relocated from core/theme-vars.css)
   2. brand + palette primitives (relocated from extended/nebula.css)
   3. semantic color mappings (light) + dark overrides
   4. legacy PaperMod bridge (--theme/--entry/--primary/... -> semantics)
   5. resolved dangling refs (container/content max-width, brand aliases, social gradients)

   Do NOT redefine base color tokens in component or extended files.
   ============================================ */

:root {
	/* ============================================
     LAYOUT PRIMITIVES (relocated from core/theme-vars.css)
     ============================================ */
	--gap: 24px;
	--content-gap: 20px;
	--nav-width: 1024px;
	--main-width: 720px;
	--header-height: 60px;
	--footer-height: 60px;
	--radius: 8px;
	color-scheme: light;

	/* ============================================
     CORE BRAND COLORS
     Primary identity colors used across all UI
     ============================================ */
	--brand-dark-navy: #1c2230;
	--brand-purple: #5e41a2;
	--brand-lavender: #8169c5;
	--brand-orange: #ff9900;
	--brand-orange-dark: #e68a00;
	--brand-lavender-light: #a78bfa;

	/* Legacy aliases (backwards compatibility) */
	--nebula-dark-navy: var(--brand-dark-navy);
	--nebula-purple: var(--brand-purple);
	--nebula-lavender: var(--brand-lavender);
	--nebula-orange: var(--brand-orange);
	--nebula-orange-dark: var(--brand-orange-dark);

	/* ============================================
     WEBGL PALETTE A: VIBRANT COSMIC
     Purple/Teal clash for dynamic energy effects
     Use for: Particle systems, active states,
              high-energy animations
     ============================================ */
	--cosmic-base: #1c2230; /* Dark navy foundation */
	--cosmic-primary: #5e41a2; /* Deep purple */
	--cosmic-accent: #ff9900; /* Vibrant orange */
	--cosmic-energy: #00fa9a; /* Medium spring green - NEW */
	--cosmic-teal: #00ced1; /* Dark turquoise */
	--cosmic-purple-bright: #9d4edd; /* Bright purple highlight */

	/* ============================================
     WEBGL PALETTE B: SOFT TECH
     Clean, readable modern aesthetic
     Use for: UI overlays, tooltips, cards,
              professional/business contexts
     ============================================ */
	--tech-base: #f9fafb; /* Light gray foundation */
	--tech-primary: #8169c5; /* Lavender */
	--tech-accent: #a78bfa; /* Light lavender */
	--tech-gold: #ffd700; /* Gold - NEW (replaces orange) */
	--tech-slate: #475569; /* Slate gray for contrast */
	--tech-cyan: #06b6d4; /* Cyan for interactive elements */

	/* ============================================
     NEUTRAL GRAYS
     Systematic gray scale for backgrounds,
     borders, text hierarchy
     ============================================ */
	--white: #ffffff;
	--gray-50: #f9fafb;
	--gray-100: #f3f4f6;
	--gray-200: #e5e7eb;
	--gray-300: #d1d5db;
	--gray-400: #9ca3af;
	--gray-500: #6b7280;
	--gray-600: #4b5563;
	--gray-700: #374151;
	--gray-800: #2d3748;
	--gray-900: #1f2937;
	--gray-950: #111827;
	--black-light: #444;
	--black: #333;

	/* ============================================
     SYNTAX HIGHLIGHTING - CATPPUCCIN MACCHIATO
     Code editor theme colors
     ============================================ */
	--syntax-bg: #24273a;
	--syntax-text: #cad3f5;
	--syntax-error: #ed8796;
	--syntax-keyword: #c6a0f6;
	--syntax-constant: #f5a97f;
	--syntax-name: #8bd5ca;
	--syntax-function: #8aadf4;
	--syntax-builtin: #91d7e3;
	--syntax-class: #eed49f;
	--syntax-variable: #f4dbd6;
	--syntax-string: #a6da95;
	--syntax-comment: #6e738d;
	--syntax-highlight-bg: #474733;
	--syntax-line-number: #8087a2;
	--syntax-diff-bg: #363a4f;

	/* ============================================
     SEMANTIC COLOR MAPPINGS - LIGHT MODE
     Context-specific color assignments
     ============================================ */
	--color-primary: var(--brand-purple);
	--color-primary-hover: var(--brand-lavender);
	--color-accent: var(--brand-orange);
	--color-accent-dark: var(--brand-orange-dark);
	--color-dark: var(--brand-dark-navy);

	/* Light Theme Colors */
	--color-background: var(--white);
	--color-background-secondary: var(--gray-50);
	--color-text: var(--gray-900);
	--color-text-secondary: var(--gray-500);
	--color-text-muted: var(--black-light);
	--color-link: var(--brand-purple);
	--color-link-hover: var(--brand-lavender);
	--color-border: var(--gray-200);
	--color-border-hover: var(--brand-lavender);

	/* WebGL scene defaults (light mode) */
	--webgl-palette: vibrant-cosmic;
	--webgl-primary: var(--cosmic-primary);
	--webgl-accent: var(--cosmic-accent);
	--webgl-energy: var(--cosmic-energy);

	/* Spacing Scale */
	--space-xs: 0.25rem; /* 4px */
	--space-sm: 0.5rem; /* 8px */
	--space-md: 1rem; /* 16px */
	--space-lg: 1.5rem; /* 24px */
	--space-xl: 2rem; /* 32px */
	--space-2xl: 3rem; /* 48px */
	--space-3xl: 4rem; /* 64px */

	/* Typography */
	--font-body:
		-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
		Cantarell, sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
		"Segoe UI Symbol", "Noto Color Emoji";
	--font-heading: var(--font-body);
	--font-mono:
		ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo,
		monospace, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
		"Noto Color Emoji";

	--font-size-xs: 0.75rem; /* 12px */
	--font-size-sm: 0.875rem; /* 14px */
	--font-size-base: 1rem; /* 16px */
	--font-size-lg: 1.125rem; /* 18px */
	--font-size-xl: 1.25rem; /* 20px */
	--font-size-2xl: 1.5rem; /* 24px */
	--font-size-3xl: 1.875rem; /* 30px */
	--font-size-4xl: 2.25rem; /* 36px */

	--line-height-tight: 1.25;
	--line-height-base: 1.6;
	--line-height-relaxed: 1.75;

	/* Border Radius */
	--radius-sm: 4px;
	--radius-md: 6px;
	--radius-lg: 8px;
	--radius-xl: 12px;
	--radius-full: 9999px;

	/* Shadows */
	--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
	--shadow-md:
		0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
	--shadow-lg:
		0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
	--shadow-xl:
		0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);

	/* Transitions */
	--transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
	--transition-base: 200ms cubic-bezier(0.4, 0, 0.2, 1);
	--transition-slow: 300ms cubic-bezier(0.4, 0, 0.2, 1);

	/* ============================================
     DECOMPOSED TIMING + EASING TOKENS (#27)
     Additive — --transition-* remain unchanged.
     Consumers opt in; existing code unaffected.
     ============================================ */
	--ease-out: cubic-bezier(0.4, 0, 0.2, 1);
	--duration-fast: 150ms;
	--duration-base: 200ms;
	--duration-slow: 300ms;

	/* Entrance stagger — per-card delay increment for page-load reveal (#34) */
	--stagger: 80ms;

	/* ============================================
     SEMANTIC STATE COLORS (#27)
     Coordinates with existing palette:
     --color-info aligns with --tech-cyan
     --color-warning aligns with --brand-orange-dark
     ============================================ */
	--color-success: #10b981;
	--color-danger: #ef4444;
	--color-info: var(--tech-cyan); /* #06b6d4 */
	--color-warning: var(--brand-orange-dark); /* #e68a00 */

	/* Focus ring — light mode default (#27) */
	--focus-ring: 2px solid var(--color-primary);

	/* ============================================
     RESOLVED DANGLING REFS (backlog item 1, step 6)
     Previously consumed via var() but never defined — the
     property silently dropped. Defined here once.
     ============================================ */
	/* outer container width — matches --nav-width */
	--container-max-width: 1024px;
	/* text column width — matches --main-width */
	--content-max-width: 720px;
	/* rename drift: aliased to the current deep-space background token.
	   no existing token held this value; canonical deep-space is #0b0e14. */
	--nebula-deep-space: #0b0e14;
	/* rename drift: alias to --nebula-purple (#5E41A2), one true value */
	--nebula-core-brand-purple: var(--nebula-purple);
	/* social card branded backgrounds (team decision).
	   github: dark neutral via surface tokens where possible */
	--gradient-github: linear-gradient(135deg, #2b3137 0%, #1a1e22 100%);
	/* instagram brand gradient */
	--gradient-insta: linear-gradient(
		135deg,
		#833ab4 0%,
		#fd1d1d 50%,
		#fcb045 100%
	);
}

/* Dark Theme Overrides */
[data-theme="dark"] {
	color-scheme: dark;

	--color-background: var(--brand-dark-navy);
	--color-background-secondary: var(--gray-800);
	--color-text: var(--gray-100);
	--color-text-secondary: var(--gray-300);
	--color-text-muted: var(--gray-400);
	--color-link: var(--brand-lavender);
	--color-link-hover: var(--brand-lavender-light);
	--color-border: var(--gray-700);
	--color-border-hover: var(--brand-lavender);

	/* Focus ring override — lavender for dark-mode contrast (#27) */
	--focus-ring: 2px solid var(--brand-lavender);

	/* WebGL scene defaults (dark mode) */
	--webgl-primary: var(--cosmic-purple-bright);
	--webgl-accent: var(--cosmic-energy);
	--webgl-energy: var(--cosmic-teal);
}

/* Legacy PaperMod Variable Mappings for Compatibility */
:root {
	--theme: var(--color-background);
	--entry: var(--color-background-secondary);
	--primary: var(--color-text);
	--secondary: var(--color-text-secondary);
	--tertiary: var(--color-background-secondary);
	--content: var(--color-text);
	--hljs-bg: var(--gray-50);
	--code-bg: var(--gray-100);
	--border: var(--color-border);
	/* code-block-bg: NOT part of the PaperMod semantic bridge — kept as a
	   literal (relocated from core/theme-vars.css) to preserve resolved value */
	--code-block-bg: rgb(28, 29, 33);
}

[data-theme="dark"] {
	--theme: var(--color-background);
	--entry: var(--color-background-secondary);
	--primary: var(--color-text);
	--secondary: var(--color-text-secondary);
	--tertiary: var(--color-background-secondary);
	--content: var(--color-text);
	--hljs-bg: var(--gray-800);
	--code-bg: var(--gray-700);
	--border: var(--color-border);
	--code-block-bg: rgb(46, 46, 51);
}