Files
portal/DESIGN.md
T

126 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CTAO Science Portal — design system
Single source of truth for look-and-feel. All values live as tokens in
`src/styles/global.css`; this file states the rules. Brand basis: CTAO Brand
Guidelines (Apr 2024) + techniques observed on ctao.org (nebula motion, generous
spacing, 15/30/60 rounding). No frameworks, no JS for presentation.
## Color tokens (brand-fixed — never add hues)
| Token | Value | Use |
|---|---|---|
| `--galaxy` | `#00004A` | Headings, header/footer/hero bands |
| `--cherenkov` | `#00E4D8` | Accents, flash, CTAs, hero subtitle. Never small text on light bg |
| `--moon` | `#F5F5F5` | Utility surfaces (bands, TOC, chips) |
| `--azure` | `#007AFF` | Focus rings, hover borders (UI only on light) |
| `--indigo` | `#00009C` | Gradient depth, link hover |
| `--text` / `--muted` / `--border` / `--link` | derived | Body ink; `--link #0057C2` is azure darkened to pass AA |
## Typography
| Role | Font | Rule |
|---|---|---|
| h1, h2 | Space Grotesk (`--font-display`) | Headlines ONLY (brand D.3.2) |
| Everything else | Inter (`--font-body`) | Includes h3+, cards, UI |
Always end `font-family` with a generic (`sans-serif`) — lint requirement.
## Spacing scale (fluid; mobile ≈ min, desktop ≈ max)
| Token | Value | Use |
|---|---|---|
| `--gutter` | `clamp(18px, 4vw, 28px)` | Container/menu side padding |
| `--space-s` | `12px` | Chip gaps, small stacks |
| `--space-m` | `20px` | Card/panel padding, grid gaps |
| `--space-l` | `24–40px` | Article top, meta/cover margins, featured body |
| `--space-xl` | `40–72px` | Section rhythm: every `.page` block, footer top |
| `--space-band` | `48–96px` | Hero + auth band vertical padding |
Rule: section/container/card padding comes from this table — no ad-hoc px.
Prose-internal margins (h2/p/blockquote) are content-local and stay as written.
## Radius & elevation
Radius: `--radius 16px` (cards/panels), `--radius-pill` (buttons, chips, inputs).
Only two shadows exist, both Galaxy-tinted (`rgba(0,0,74,…)`):
| Token | Use |
|---|---|
| `--shadow-ambient` | Resting cards, panels, search rows |
| `--shadow-lifted` | Hover/focus cards, dropdowns (suggest, mobile menu), auth card |
Never write a literal `box-shadow`; compose focus rings as
`0 0 0 2px var(--azure), var(--shadow-lifted)`.
## Surfaces
Rhythm per page: Galaxy hero band → content → Galaxy footer (flush, no margin).
White = editorial (news grids, articles). `.band--moon` = service/utility
sections (home Services, /search, /support, /proposals, /dashboard). Cards and
panels are always white and pop against Moon via border + ambient shadow.
## Motion policy (CSS-only, Apple-tier restraint)
ctao.org animates nebula videos under Galaxy vignettes; our translation:
1. **Band nebula** — one `--nebula` stack (Cherenkov .38 / Azure .33 /
Indigo .80 radial fields) used by hero + auth bands, static and animated
alike; the animated variant is an oversized pseudo-layer drifting via
`transform` only, 22s alternate. Clearly visible glow, never neon.
2. **Whole-page ambient** — `.has-ambient::before`: fixed brand-hue wash
(Cherenkov .16 / Azure .12 / Indigo .10 — visible pastel fields, white
cards pop against them) drifting on a 60s alternate cycle. Opt-in per page
via `<Base ambient>`: **home only**. Login's full-viewport auth nebula
already is its ambient; news/article/utility pages stay calm (reading
focus). Body text NEVER sits on a moving gradient — cards, panels and
bands are solid layers above it.
3. One-shot hero `rise` stagger; hover lift/transitions. Nothing else.
Rules:
- ALL `animation`/`transition` declarations live inside the single
`@media (prefers-reduced-motion: no-preference)` block at the end of
global.css; the `reduce` block additionally force-disables everything.
- Animate `transform`/`opacity` only (compositor-only, per web.dev/MDN); never
background-position/box-shadow in keyframes. No `will-change` (animated
properties are auto-promoted — MDN). No `filter: blur` on large layers and no
`background-attachment: fixed` (broken on iOS) — the wash is a fixed-position
layer with pre-blurred radial gradients. No JS, no scroll-triggered motion.
- Static gradients remain as the reduced-motion fallback (bands swap them for
the animated layer — never both, keeping contrast bounded; the ambient wash
simply stops moving).
- If an effect calls attention to itself as an effect, cut it.
## WCAG 2.1 AA notes (computed, worst-case animation frames)
- Nebula blob peaks over Galaxy: Cherenkov α=.38 → white 7.9:1, Cherenkov
subtitle 4.9:1, eyebrow `#C7CDE9` 5.0:1. Azure α=.33 → all ≥ 7.7:1.
Indigo α=.80 → all ≥ 9.6:1. Cherenkov/Azure CORES must stay in opposite
corners (core-over-core would fail; fringe overlap ≈ 6:1).
- On Moon: `--link` 6.1:1, `--muted` 7.1:1, `--text` 16.9:1.
- Ambient wash worst overlap (`rgb(188,237,249)`): `--link` 5.3:1, `--muted`
6.2:1, Galaxy h2 15.2:1.
- Ceilings: nebula ≤ .38 Cherenkov / ≤ .35 Azure; wash ≤ .16/.12/.10 —
recompute (WCAG relative luminance) if any alpha or hue placement changes.
- Focus visible everywhere; tap targets ≥ 44px; skip link first.
## Article TOC pattern
Rendered only when ≥3 h2/h3. Two renderings of the same list (only one ever
displayed): `<details class="toc toc--inline">` collapsed under the title
(< 1200px) and `<nav class="toc toc--rail">` sticky right rail (≥ 1200px,
outside the 70ch column via `.article-layout` grid). MDN/Stripe/NN-g pattern.
## Do not
- No new hues, tints, or grays — derive via `color-mix` from brand tokens only.
- No Space Grotesk outside h1/h2. No dark theme.
- No ad-hoc spacing/shadow/radius values; extend the token tables instead.
- No animation/transition outside the `no-preference` block; no JS motion.
- No dependencies/frameworks (CSS or JS). No removal of "Mock" badges.
- Don't lighten text over navy bands below the alphas listed above.
- No clip-path/CSS approximations of brand assets — the flash is the real SVG
path from the official logo, or nothing.
- Decoration budget: max ONE accent element per view (currently: footer flash,
static). No twinkles, floating shapes, or stacked decorations.