docs: establish approved Phase 1 design + Phase 2 spec as project foundation

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
cesnimda
2026-07-03 21:07:25 +02:00
commit 1f695d3932
39 changed files with 2686 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
# COLOUR_SYSTEM.md
## 1. Palette philosophy
Near-monochrome surfaces + **one** accent ("aurora" teal-green — a nod to Norway without cliché flags/fjords) + **one** functional secondary (amber, status-only). The accent behaves like a **status LED**: it marks liveness, focus, and action — it is never decoration or large fills.
## 2. Core tokens
### Dark theme (design lead)
| Token | Value | Contrast vs surface-0 | Use |
|---|---|---|---|
| `surface-0` | `#0B0E14` | — | Page bg (blue-black, not pure black) |
| `surface-1` | `#11151D` | — | Cards |
| `surface-2` | `#171C26` | — | Nested panels, TL;DR |
| `ink` | `#E8ECF2` | 15.4:1 | Headings, body |
| `ink-muted` | `#9AA4B2` | 7.1:1 | Secondary text, captions |
| `ink-faint` | `#5C6675` | 3.4:1 | Disabled, decorative only (never body) |
| `border` | `#E8ECF2` @ 8% | — | Hairlines |
| `accent` | `#3ECFAE` | 9.8:1 | Links, primary buttons (dark text on it), status dots, trace motif, focus rings |
| `accent-ink` | `#062A22` | 12:1 on accent | Text on accent fills |
| `amber` | `#E5A93D` | 8.3:1 | `IN DEVELOPMENT` chips, form warnings only |
| `danger` | `#E5606B` | 5.6:1 | Form errors only |
### Light theme
| Token | Value | Contrast vs surface-0 | Use |
|---|---|---|---|
| `surface-0` | `#FAFAF8` | — | Warm paper white |
| `surface-1` | `#FFFFFF` | — | Cards |
| `surface-2` | `#F1F2EF` | — | Nested panels |
| `ink` | `#171A20` | 15.9:1 | Text |
| `ink-muted` | `#555E6B` | 6.8:1 | Secondary |
| `border` | `#171A20` @ 10% | — | Hairlines |
| `accent` | `#0E8C72` | 4.9:1 | Links, buttons (white text: 5.1:1), focus |
| `amber` | `#8A6114` | 5.6:1 | Status text (paired w/ `#F5E5C2` chip bg) |
| `danger` | `#B3323E` | 6.2:1 | Errors |
**Note:** the accent is *perceptually the same identity* in both themes but shifted per theme to hold WCAG AA (dark uses the luminous `#3ECFAE`; light uses the deepened `#0E8C72`). This is deliberate — never reuse the dark accent on white (2.1:1, fails).
## 3. Usage rules
1. **Accent budget:** ≤ 3 accent moments per viewport (e.g. hero: trace line + primary CTA + one chip dot). If a screen feels flat, fix hierarchy with type/spacing — not more accent.
2. **Large fills:** accent may fill only buttons and chips; never section backgrounds or headings-as-blocks.
3. **Text on accent:** always `accent-ink`/white per table; never accent-on-accent.
4. **Status semantics (fixed):** accent dot = active/live · amber dot = in development · muted dot = archived. Consistent across chips, timeline, project cards.
5. **Charts/diagrams:** surfaces + border + ink-muted, accent for the *one* flow being explained, amber for external systems (e.g. Gmail API node). Max 4 hues in any diagram.
6. **Duotone photo treatment (dark):** shadows → `#0B0E14`, highlights → warm neutral; accent never tints skin.
7. **Never colour-only meaning:** every status pairs dot + text label; errors pair colour + icon + message.
## 4. Gradients
Exactly one permitted gradient: hero background halo — radial, accent @ 6% → transparent, anchored behind the trace motif. Everything else flat. (The Stripe lesson: one identity moment, not a gradient system.)
## 5. Accessibility audit summary
- All body/secondary text ≥ 4.5:1 in both themes (see tables).
- UI components/borders on interactive elements ≥ 3:1 against adjacent colours.
- Focus ring `accent` at 2px passes 3:1 against both surface-0 values.
- `ink-faint` is documented decorative-only. High-contrast/forced-colors mode: dot-grid and halo disabled, hairlines → CanvasText.
+80
View File
@@ -0,0 +1,80 @@
# DESIGN_SYSTEM.md
Design direction: **"Kontrollrom"** (control room) — a quiet, Nordic-restrained engineering aesthetic.
---
## 1. Concept
The site should feel like **well-run production infrastructure given a public face**: calm surfaces, precise alignment, information-dense but never busy, one living accent colour that behaves like a status light. Not a marketing site pretending to be technical; a technical artefact polished until it feels premium.
Three ideas fused, none copied:
1. **Nordic restraint** — generous whitespace, muted surfaces, understatement (matches the Norwegian market and the candidate's voice).
2. **Console/ops vernacular as *seasoning*** — monospace metadata labels, status chips, subtle grid/blueprint texture. Used at ~10% intensity: labels, captions, chips — never full terminal cosplay, no fake CLIs, no green-on-black.
3. **SaaS product craft** — case studies presented like product launch pages (framed screenshots, TL;DR boxes, proof strips).
**Litmus test for every design decision:** *"Would this look at home in the admin console of a product you trust?"* If it's decorative noise — cut it.
## 2. Identity elements
- **Monogram:** `CB` set in the mono face inside a rounded square, with a 2px accent tick in the corner — reads as both initials and a status LED. Used as favicon, header mark, OG-image anchor.
- **Signature motif — "the trace":** a thin (1.5px) accent line that draws horizontally then steps (like an oscilloscope/PCB trace). Appears once in the hero (animated draw on load), as section-divider punctuation, and in the 404. This is the site's single ownable visual move.
- **Status chips:** rounded-full, mono, 12px, used for facts (location, permit, availability) and project status (`ACTIVE`, `IN DEVELOPMENT`). The chip *is* the brand's information atom.
- **Photography:** the outdoor headshot, duotone-treated in dark mode (slight cool grade) and natural in light mode; always in a hairline-bordered rounded frame with a mono caption (`tønsberg, norway · 2025`).
## 3. Surfaces & elevation
Flat-first. Depth via **hairline borders (1px, low-alpha)** and background steps, not drop shadows.
| Token | Dark | Light | Use |
|---|---|---|---|
| `surface-0` | #0B0E14 | #FAFAF8 | Page background |
| `surface-1` | #11151D | #FFFFFF | Cards, panels |
| `surface-2` | #171C26 | #F1F2EF | Nested/hover panels, code-ish blocks |
| `border` | #FFFFFF @ 8% | #16181D @ 10% | All hairlines |
| `border-strong` | #FFFFFF @ 16% | #16181D @ 20% | Focused/hover cards |
Shadows: only two, both subtle, only in light mode + overlays (`shadow-card`, `shadow-overlay`). Dark mode uses border-strong + surface step instead.
Radius scale: `4px` (chips inner) · `8px` (buttons, inputs) · `12px` (cards) · `16px` (screenshot frames) · `full` (chips).
## 4. Grid & layout
- Max content width **1200px**; case-study prose measure capped at **72ch**.
- 12-column grid, 24px gutters desktop; 4-column, 16px gutters mobile.
- Left-rail section labels on desktop (mono, vertical rhythm anchor): `01 — ABOUT`, `02 — SKILLS`… echoing the current site's numbering, done properly.
- Breakpoints: 480 / 768 / 1024 / 1280 / 1536.
- Background texture: an extremely faint dot-grid (border-colour dots, 24px pitch) on `surface-0` hero + section headers only — the "blueprint" whisper. Disabled in forced-colors/high-contrast.
## 5. Core components (design-level inventory)
| Component | States to design | Notes |
|---|---|---|
| Header / nav | default, scrolled (compressed + blur), mobile overlay | see mockups M7 |
| Split CV button | default, hover, open (menu shows both languages + file size) | locale-aware default |
| Language switch | EN active, NO active, hover, focus, keyboard | segmented control, see M11 |
| Theme toggle | dark, light, hover (icon morph sun⇄moon) | respects system on first load |
| Status chip | fact, status-active (accent dot pulse ≤ 2 loops), status-dev (amber dot) | |
| Skill group card | default, hover (border-strong) | chips inside |
| Project card | default, hover (screenshot parallax-shift 4px + border glow), focus | whole card clickable, single tab stop |
| Timeline item | featured (expanded), compact (one-liner), hover | |
| TL;DR box | — | surface-2, accent left edge (3px) |
| Architecture diagram frame | static SVG, nodes highlight on hover (desktop only) | must read perfectly with zero interaction |
| Screenshot frame | loading (skeleton), loaded, lightbox open | browser-chrome mock, aspect-locked |
| Form field | default, focus, filled, error, success | error text + icon, never colour-only |
| Buttons | primary (accent), secondary (outline), ghost | see MICRO_INTERACTIONS |
| Footer | — | dense, mono-flavoured, sitemap + channels |
| 404 | — | trace motif breaks/frays; bilingual links home |
## 6. Iconography & illustration
- Icons: single stroke-icon set, 1.5px stroke, 20px grid (Lucide-class aesthetic — final set is a Phase 2 choice). Never filled+outlined mixed.
- No stock illustration, no 3D blobs, no particles. The only "illustrations" are: the trace motif, architecture diagrams (drawn in-system: surface-2 nodes, border hairlines, accent flow arrows, mono labels), and framed product screenshots.
## 7. Theming rules
- Dark is the **design-lead theme** (P2 audience default; screenshots of both apps present well on dark), light must be equally finished — P1/P3 recruiters often browse light.
- Every token has a pair; no theme-conditional layouts. Accent identical in both (passes contrast both ways — see COLOUR_SYSTEM).
- First paint must match user preference without flash (implementation constraint flagged for Phase 2).
## 8. What this system deliberately avoids
Percentage skill bars · testimonial carousels · particle heroes · scroll-hijacking · fake terminals · gradient-everything · cookie-consent theatre (no tracking → no banner; a privacy footnote instead — itself a trust signal).
@@ -0,0 +1,50 @@
# MOTION_GUIDELINES.md
## 1. Motion philosophy
**"Motion confirms; it never performs."** Every animation must answer a user question — *did my click register? what changed? where did this come from?* — or it doesn't ship. One exception: the hero trace draw, the site's single identity moment.
This is the strongest anti-junior signal available: restraint executed perfectly.
## 2. Tokens
| Token | Duration | Easing | Use |
|---|---|---|---|
| `instant` | 80ms | linear | Colour/opacity on hover |
| `quick` | 160ms | ease-out (0.2, 0, 0, 1) | Buttons, chips, toggles, focus |
| `standard` | 240ms | ease-out | Card hover lift, menu open, theme cross-fade |
| `entrance` | 400ms | ease-out, slight overshoot-free | Section reveals (opacity + 12px rise) |
| `signature` | 700ms | ease-in-out | Hero trace draw only, once per page load |
Rules: nothing exceeds 700ms; entrances stagger max 3 items × 60ms; **only `transform` and `opacity` animate** (performance budget — no layout/box-shadow/filter animation); parallax capped at 4px (project-card screenshots).
## 3. Reduced motion (first-class variant, not an off-switch)
`prefers-reduced-motion: reduce`
- Entrances become pure opacity fades (120ms), no translation.
- Trace motif renders complete, static.
- Theme switch: instant swap.
- Status-dot pulse: static dot.
- Scroll behaviour: instant anchor jumps, no smooth scroll.
Nothing is *removed* — content parity is exact.
## 4. Loading states
| Context | Design |
|---|---|
| Initial page | No spinner, no splash: text renders immediately (fonts swap-safe), hero trace draws as the "loaded" confirmation. Perceived performance *is* the loading design. |
| Screenshots / gallery | Aspect-ratio-locked skeleton: `surface-2` block + faint dot-grid + mono caption already visible (`loading capture…` / `laster skjermbilde…`); cross-fade 240ms on load. Zero layout shift, ever. |
| Form submit | Button label → inline three-dot mono ellipsis animation (·, ··, ···), button stays same width; success swaps band to confirmation panel (see MICRO_INTERACTIONS §form). |
| Language switch | Content cross-fade 160ms; header/footer chrome does not blink (only text nodes swap). |
| Lightbox open | Backdrop fade 240ms + image scale 0.98→1. |
## 5. Page & theme transitions
- Route changes: outgoing fade 120ms → incoming rise-fade 240ms, header persistent. No full-screen wipes.
- Theme toggle: 240ms token cross-fade; the toggle icon morphs (sun⇄moon path) in `quick`. Screenshots swap themed variants where available (JobTrack/InboxIntel both have dark UIs — capture both if the apps support it, else keep one honest capture).
## 6. Scroll-linked motion (summary — full spec in SCROLL_EXPERIENCE.md)
- Section reveals trigger at 20% viewport entry, once (no re-trigger on scroll-up).
- Header compresses 72→56px between 0120px scroll, linear-mapped.
- No scroll-jacking, no pinned scenes, no horizontal hijack — recruiters skim; the scrollbar must behave like a document's.
+28
View File
@@ -0,0 +1,28 @@
# SPACING_SYSTEM.md
## 1. Scale (8pt base, 4pt half-step)
| Token | px | Use |
|---|---|---|
| `space-1` | 4 | Chip inner gaps, icon-to-label |
| `space-2` | 8 | Inside chips/buttons vertical, tight stacks |
| `space-3` | 12 | Button horizontal padding unit, list gaps |
| `space-4` | 16 | Card inner padding (mobile), paragraph gaps |
| `space-5` | 24 | Card inner padding (desktop), grid gutters |
| `space-6` | 32 | Between related blocks |
| `space-7` | 48 | Between component groups |
| `space-8` | 64 | Section inner top/bottom (mobile) |
| `space-9` | 96 | Section rhythm (desktop) |
| `space-10` | 128 | Hero breathing room, page top/bottom |
Only these ten values exist. No 20px, no 30px, no eyeballing.
## 2. Rules
1. **Section rhythm:** homepage sections separated by `space-9` desktop / `space-8` mobile; the mono section label sits `space-6` above its H2.
2. **Proximity encodes relationship:** gap within a skill group (`space-2`) < gap between groups (`space-5`) < gap between sections (`space-9`). If two things feel related but aren't, increase the gap before adding a divider; hairlines are the *second* resort.
3. **Cards:** `space-5` padding desktop, `space-4` mobile; media flush to card edge (screenshots bleed to the frame), text content padded.
4. **Touch targets:** min 44×44px tap area on mobile (chips visually smaller but hit-area padded); nav/footer link spacing ≥ `space-3` vertically on mobile.
5. **Header:** 72px tall default, compresses to 56px on scroll; content top-padding accounts for it (anchor scroll offset = header height + `space-5`).
6. **Prose:** paragraph gap `space-4`; heading top `space-7`/bottom `space-3` (the 2:1 before/after rule from TYPOGRAPHY_GUIDE).
7. **Density gradient:** marketing-calm at top of pages → denser in technical sections (case-study decision lists, footer). Density shift is achieved by stepping down *one* spacing token, never by shrinking type below `small`.
+43
View File
@@ -0,0 +1,43 @@
# TYPOGRAPHY_GUIDE.md
## 1. Type roles (three voices, strict casting)
| Role | Direction | Character | Used for |
|---|---|---|---|
| **Display / headings** | Modern grotesque with slight technical edge — *Space Grotesk-class* (alternatives in that register: General Sans, Hanken Grotesk) | Confident, engineered, not cold | H1H3, nav, buttons, stat numbers |
| **Body** | Highly-legible humanist/neo-grotesque — *Inter-class* (alternatives: Source Sans 3, Figtree) | Invisible, effortless at 1618px | Paragraphs, lists, form fields |
| **Mono (identity voice)** | *JetBrains Mono-class* (alternatives: IBM Plex Mono, Commit Mono) | The "console seasoning" | Section labels, chips, captions, metadata, diagram labels, footer |
Exact typeface licensing/loading = Phase 2. Design intent: **two families max loaded + mono**, variable weights preferred, `ø æ å` coverage mandatory in all three (Norwegian body text is first-class, not fallback).
## 2. Scale (1.25 major-third, 16px base)
| Token | Size / line-height | Weight | Tracking | Use |
|---|---|---|---|---|
| `display` | 61/1.05 (desktop) · 40/1.1 (mobile) | 600 | 0.02em | Hero name only |
| `h1` | 49/1.1 · 34/1.15 | 600 | 0.015em | Page titles |
| `h2` | 39/1.15 · 28/1.2 | 600 | 0.01em | Section titles |
| `h3` | 31/1.2 · 24/1.25 | 550 | 0 | Card titles, case-study subsections |
| `h4` | 20/1.35 | 550 | 0 | Minor headings |
| `body-lg` | 18/1.6 | 400 | 0 | Hero summary, TL;DR, lead paragraphs |
| `body` | 16/1.65 | 400 | 0 | Default prose |
| `small` | 14/1.5 | 400 | 0 | Secondary info, form hints |
| `mono-label` | 12/1.4 | 500 | +0.08em, UPPERCASE | Section labels (`01 — PROSJEKTER`), chips |
| `mono-meta` | 13/1.5 | 400 | 0 | Captions, dates, file sizes |
**Rule: uppercase + letter-spacing lives only in `mono-label`.** (The old site letter-spaced everything; the CV letter-spaces headings into A T S soup. Reserve the effect and it becomes identity instead of noise.)
## 3. Rhythm & measure
- Prose measure 6072ch; hero summary ≤ 55ch.
- Space *before* a heading ≈ 2× space after (sections cohere downward).
- Baseline rhythm on the 8pt grid — line-heights above are chosen to land on it at 16px base.
- Norwegian headline audit: NO strings run ~1015% longer and compounds don't hyphenate freely (`systemadministrasjon`); every headline layout must be tested with the NO string at the mobile breakpoint. Wrapping rule: allow hyphenation in body NO text, never in headings — reword instead.
## 4. Typographic details (the seniority tells)
- Real dashes ( —), curly quotes per language convention (EN “ ” · NO « »), non-breaking spaces before units and inside `8+ yrs`.
- Numbers in stats use tabular lining figures (mono or `tnum`).
- Links in prose: accent-coloured underline (2px offset), full colour+underline — never colour-only (a11y).
- Code/tech names inline (C#, .NET) stay in body face — mono inline is reserved for actual paths/commands in case studies.
- Focus states use a 2px accent outline + 2px offset, same in both themes.