Hero is now a data-native 0pt artifact (no photos, keeps grain/reveal/float); header is the 0pt wordmark with Swedish nav; layout is sv + loads JetBrains Mono + 0pt metadata; globals wires the loaded fonts. Adds CLAUDE.md + docs/01-09 as the build contract. next build green (5 routes). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.0 KiB
05 · Design system (mono)
Self-contained spec. Component-level v0 prompts live in docs/design/0pt-v0-build-brief.md; the full page/component inventory + visual rules in docs/design/0pt-design-coverage-and-visuals.md. This file is the source of truth for tokens + motion; build them once and reuse.
Derived from the mono / EVASION template (itself v0-generated). Light, editorial, monochrome, sharp, with a single red accent and animated grain + scroll-reveal motion. No photography — imagery is rendered data.
Tokens
THEME (light, default):
--background #ffffff --foreground #0a0a0a --muted-foreground #737373
--border #e5e5e5 --card #ffffff --input #f5f5f5
THEME (dark sections — footer, one editorial block):
--background #0a0a0a --foreground #fafafa --muted-foreground #a3a3a3 --border #262626
ACCENT (single, sparing): red --accent #e40014 (one CTA / one number / one marker per screen)
PROVENANCE squares (ONLY non-mono color besides the red CTA):
level 0 #737373 (unavailable/licensed) · 1 #143aff (single source) · 2 #fcbb00 (partial) · 3 #00d294 (corroborated)
RADIUS: 0 everywhere (max 2px on tiny chips). No drop shadows; structure via 1px hairlines.
TYPE: Inter (everything; headlines weight 500–600, letter-spacing -0.02em, large) · JetBrains Mono (eyebrows, labels, numbers, codes, table headers — UPPERCASE, letter-spacing 0.12em, ~11px, #737373)
GRID: 8px base; graph-paper hairlines; generous whitespace; left-aligned headlines.
SECTION SPINE: real ordered indices 00–NN as mono eyebrows.
globals.css (add verbatim — guarantees identical motion + grain)
@keyframes reveal-up{0%{opacity:0;transform:translateY(60px)}to{opacity:1;transform:translateY(0)}}
@keyframes reveal-left{0%{opacity:0;transform:translateX(-60px)}to{opacity:1;transform:translateX(0)}}
@keyframes reveal-right{0%{opacity:0;transform:translateX(60px)}to{opacity:1;transform:translateX(0)}}
@keyframes scale-in{0%{opacity:0;transform:scale(.9)}to{opacity:1;transform:scale(1)}}
@keyframes fadeIn{0%{opacity:0;transform:scale(1.02)}to{opacity:1;transform:scale(1)}}
@keyframes slideUp{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}
@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-20px)}}
@keyframes grain{0%,100%{transform:translate(0,0)}10%{transform:translate(-5%,-10%)}20%{transform:translate(-15%,5%)}30%{transform:translate(7%,-25%)}40%{transform:translate(-5%,25%)}50%{transform:translate(-15%,10%)}60%{transform:translate(15%,0)}70%{transform:translateY(15%)}80%{transform:translate(3%,35%)}90%{transform:translate(-10%,10%)}}
.reveal{opacity:0}
.reveal.in-view{animation:.8s ease-out forwards reveal-up}
.reveal-left.in-view{animation:.8s ease-out forwards reveal-left}
.reveal-right.in-view{animation:.8s ease-out forwards reveal-right}
.reveal-scale.in-view{animation:.6s ease-out forwards scale-in}
.reveal-fade.in-view{animation:.7s ease-out forwards fadeIn}
.delay-1{animation-delay:.1s}.delay-2{animation-delay:.2s}.delay-3{animation-delay:.3s}.delay-4{animation-delay:.4s}.delay-5{animation-delay:.5s}.delay-6{animation-delay:.6s}
.animate-float{animation:6s ease-in-out infinite float}
.grain-overlay::before{content:"";position:absolute;inset:-100%;opacity:.03;pointer-events:none;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");animation:8s steps(10,end) infinite grain}
@media(prefers-reduced-motion:reduce){.reveal,.reveal-left,.reveal-right,.reveal-scale,.reveal-fade{opacity:1;animation:none}.animate-float,.grain-overlay::before{animation:none}}
useReveal hook
"use client";
import { useEffect, useRef } from "react";
export function useReveal<T extends HTMLElement = HTMLElement>() {
const ref = useRef<T>(null);
useEffect(() => {
const el = ref.current; if (!el) return;
const io = new IntersectionObserver((es) => es.forEach((e) => {
if (e.isIntersecting) {
el.querySelectorAll(".reveal,.reveal-left,.reveal-right,.reveal-scale,.reveal-fade")
.forEach((n) => n.classList.add("in-view"));
io.unobserve(el);
}
}), { threshold: 0.15, rootMargin: "0px 0px -10% 0px" });
io.observe(el); return () => io.disconnect();
}, []);
return ref;
}
Every section wraps in a tag using useReveal(); children get reveal[/-left/-right/-scale/-fade] + delay-N.
Buttons & labels
- Primary: solid
#0a0a0abg, white text, square, mono uppercase 12px / spacing 0.1em, padding 14px 26px; hover → bg#e40014. - Secondary: transparent, 1px
#0a0a0aborder; hover → bg#0a0a0a, white text. - Eyebrow: mono uppercase 11px, spacing 0.14em,
#737373, with a 2-digit index ("01 — KATALOG"). - Headlines: sentence case, never uppercase.
Visual system — no photos (DataArtifact)
Build one generative primitive; every "image" surface uses it, animating with the effect it replaces:
table(rows cascade in, provenance square per fact cell) — hero/cards.map(country-code cells, fill bars animate width on view) — coverage.schema(ER mini-diagram, key boxes red border) — schema previews.chart(black bars/lines, exactly ONE red series, grow on view) — dashboards.grid(hairline graph-paper + faint oversized numeral + one floating red square) — pure texture.
Mapping of template image elements → replacement + effect kept is in the coverage doc. Rules: code-only (CSS/SVG/canvas), color discipline inside visuals (mono + provenance squares + one red series), grain over any "image" surface, prefers-reduced-motion settles to static. Optional: add Lenis smooth scroll on top — preserves all effects.
The signature (carry everywhere)
Provenance + confidence is the identity: the provenance square on every fact, one red, mono numerals as texture, section spine, identical motion. A screen that lacks these is off-brand.