mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-08 04:08:24 +00:00
127 lines
4.6 KiB
Markdown
127 lines
4.6 KiB
Markdown
# Theming & Motion Guide
|
|
|
|
How color, light/dark mode, section tone, and animation work on this site.
|
|
|
|
---
|
|
|
|
## Palette
|
|
|
|
From the spec, exposed as CSS variables in `src/styles/themes.css` and mapped to
|
|
Tailwind utilities in `src/styles/global.css`:
|
|
|
|
| Token | Value | Meaning |
|
|
|---|---|---|
|
|
| `--color-slate` | `#1E293B` | Professional confidence (primary) |
|
|
| `--color-amber` | `#F59E0B` | Warm accent (the making/creative side) |
|
|
| `--color-teal` | `#699a94` | Soft teal (secondary, favorite) |
|
|
| `--color-ink` | `#0F172A` | Near-black text on light |
|
|
| `--color-cream` | `#FFFBEB` | Cream-white text on dark |
|
|
|
|
Brand colors are fixed. **Semantic** tokens (`--surface`, `--text`, `--accent`,
|
|
`--secondary`, `--border`, ...) resolve differently per theme + section, and are
|
|
what components actually use.
|
|
|
|
---
|
|
|
|
## Two theming axes
|
|
|
|
### Axis 1 — Global light/dark
|
|
|
|
Set on `<html data-theme="light|dark">`:
|
|
|
|
- A blocking inline script (`src/scripts/theme-init.ts`, inlined in `<head>`)
|
|
reads `localStorage['gk-theme']`, falling back to the OS
|
|
`prefers-color-scheme`. This runs **before first paint**, so there is no flash.
|
|
- The nav `ThemeToggle` island flips it and persists the choice.
|
|
- If the user has never chosen, the site follows OS changes live.
|
|
|
|
### Axis 2 — Section tone
|
|
|
|
Set on `<body data-surface="neutral|professional|interest">` via the `surface`
|
|
prop on `<BaseLayout>`:
|
|
|
|
| Surface | Used on | Tone |
|
|
|---|---|---|
|
|
| `neutral` | Home, About, Contact | Balanced — clean in light, calm in dark |
|
|
| `professional` | Professional page | Dark slate, polished, amber accents |
|
|
| `interest` | Interests pages | Warm cream/amber, inviting |
|
|
|
|
The Professional surface leans dark even in light mode (it is the "polished
|
|
wing"). The Interest surface is warm in both modes. This is what gives the two
|
|
wings their distinct feel while sharing the same typography and spacing.
|
|
|
|
Each `(data-theme, data-surface)` combination redefines the semantic tokens in
|
|
`themes.css`. Add or adjust a combination there to change any section's palette.
|
|
|
|
---
|
|
|
|
## Changing colors
|
|
|
|
1. **Tweak a brand color:** edit the fixed values in `global.css` under `@theme`
|
|
(e.g. `--color-amber`).
|
|
2. **Tweak a section's tone:** edit the relevant `[data-surface="..."]` block in
|
|
`themes.css`. Each block lists `--surface`, `--surface-raised`,
|
|
`--surface-sunken`, `--text`, `--text-muted`, `--text-subtle`, `--border`,
|
|
`--accent`, `--accent-contrast`, `--secondary`.
|
|
3. Components use utilities like `bg-surface`, `text-text`, `text-accent`
|
|
(mapped from these variables), so they update automatically.
|
|
|
|
### Contrast
|
|
|
|
Verify WCAG AA (4.5:1 for body text) whenever you change a token pair. The
|
|
current combinations were chosen for AA compliance. The `--accent` on the neutral
|
|
light surface uses a deeper amber (`#b45309`) specifically to meet AA on white.
|
|
|
|
---
|
|
|
|
## Motion
|
|
|
|
Motion is layered and **always** respects `prefers-reduced-motion`:
|
|
|
|
1. **Hero background** (`AnimatedBackground.tsx`):
|
|
- A CSS mesh-gradient layer (GPU-friendly, animated via the
|
|
`animate-gradient-shift` keyframes in `animations.css`).
|
|
- An optional `<canvas>` particle field (slow drifting dots) on the home hero
|
|
only. It renders at devicePixelRatio (capped at 2), pauses when off-screen,
|
|
and is **skipped entirely** under reduced motion.
|
|
2. **Scroll reveals** (`Reveal.astro` + the `.reveal` class):
|
|
- IntersectionObserver toggles `.is-visible`, triggering a CSS fade-up. Zero
|
|
framework cost. The reduced-motion media query in `global.css` forces these
|
|
to their final state instantly.
|
|
3. **Page transitions**: Astro `<ClientRouter />` (View Transitions API) morphs
|
|
between pages and persists the nav.
|
|
4. **Nav blur on scroll**: a tiny inline script adds `.scrolled` after a few px,
|
|
enabling backdrop-blur + a hairline border + shadow.
|
|
5. **Hover micro-interactions**: Tailwind transitions on cards/buttons (lift,
|
|
accent glow, arrow nudge).
|
|
|
|
### Disabling motion
|
|
|
|
Everything is gated by the global reduced-motion rule in `global.css`:
|
|
|
|
```css
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*, *::before, *::after {
|
|
animation-duration: 0.001ms !important;
|
|
transition-duration: 0.001ms !important;
|
|
...
|
|
}
|
|
}
|
|
```
|
|
|
|
The canvas island additionally checks `matchMedia('(prefers-reduced-motion:
|
|
reduce)')` before drawing, so even JS-driven motion is skipped.
|
|
|
|
---
|
|
|
|
## Typography
|
|
|
|
Self-hosted variable fonts via Fontsource (woff2, no external requests):
|
|
|
|
- **Headings / UI:** Plus Jakarta Sans Variable (geometric sans)
|
|
- **Body:** Inter Variable
|
|
- **Mono / kickers / quotes:** JetBrains Mono Variable
|
|
|
|
Font sizes use `clamp()` for fluid scaling (see the `h1`-`h4` rules in
|
|
`global.css`).
|