--- name: know-yourself skin: "clean warm-white daylight (default) · dark night lamp · fresh cool gray-blue (2026-09-30)" palette_source: "src/app/styles/base.css (:root / .dark) and src/app/styles/fresh.css (html.fresh); structural scale: tokens.css" generated: 2026-10-01 note: "Generated by scripts/gen-design-tokens.mjs (npm run design:tokens). Never hand-edit this header — regenerate it." colors: light: color-paper: "oklch(99.1% 0.005 85)" color-paper-strong: "oklch(99.4% 0.003 85)" color-ink: "oklch(25% 0.012 55)" color-night: "oklch(21% 0.008 60)" color-night-ink: "oklch(92% 0.01 85)" color-accent: "oklch(52% 0.1 45)" color-accent-ink: "oklch(99% 0.006 85)" color-accent-soft: "oklch(93% 0.025 60)" color-teal: "oklch(48% 0.05 155)" color-teal-soft: "oklch(94% 0.015 150)" color-signal: "oklch(78% 0.09 85)" color-signal-ink: "oklch(30% 0.035 70)" color-sky: "oklch(85% 0.05 80)" color-muted: "oklch(95.5% 0.008 85)" color-muted-text: "oklch(47% 0.014 60)" color-sheet-wash: "oklch(94.5% 0.012 85)" color-sheet-surface: "oklch(98.8% 0.008 87)" color-sheet-ink: "oklch(46% 0.028 55)" color-blue-wash: "oklch(92.5% 0.03 75)" color-danger: "oklch(52% 0.14 25)" color-overlay-ink: "oklch(100% 0 0)" color-print-paper: "oklch(100% 0 0)" color-print-ink: "oklch(25% 0.03 48)" color-print-muted: "oklch(44% 0.02 50)" color-topic-self: "oklch(50% 0.08 45)" color-topic-emotion: "oklch(51% 0.07 25)" color-topic-relationship: "oklch(51% 0.055 70)" color-topic-life: "oklch(49% 0.05 150)" surface-contrast: "oklch(25% 0.012 55)" surface-contrast-ink: "oklch(99.1% 0.005 85)" dark: color-paper: "oklch(21% 0.008 60)" color-paper-strong: "oklch(27% 0.01 60)" color-ink: "oklch(92% 0.01 85)" color-night: "oklch(21% 0.008 60)" color-night-ink: "oklch(92% 0.01 85)" color-accent: "oklch(74% 0.075 65)" color-accent-ink: "oklch(20% 0.02 55)" color-accent-soft: "oklch(30% 0.03 60)" color-teal: "oklch(72% 0.045 150)" color-teal-soft: "oklch(30% 0.022 150)" color-signal: "oklch(80% 0.09 82)" color-signal-ink: "oklch(22% 0.035 78)" color-sky: "oklch(76% 0.045 78)" color-muted: "oklch(29% 0.01 60)" color-muted-text: "oklch(76% 0.012 85)" color-sheet-wash: "oklch(29% 0.014 65)" color-sheet-surface: "oklch(26% 0.01 62)" color-sheet-ink: "oklch(80% 0.028 85)" color-blue-wash: "oklch(28% 0.03 60)" color-danger: "oklch(72% 0.12 25)" color-overlay-ink: "oklch(100% 0 0)" color-print-paper: "oklch(100% 0 0)" color-print-ink: "oklch(25% 0.03 48)" color-print-muted: "oklch(44% 0.02 50)" color-topic-self: "oklch(75% 0.09 45)" color-topic-emotion: "oklch(78% 0.08 25)" color-topic-relationship: "oklch(80% 0.06 70)" color-topic-life: "oklch(78% 0.05 150)" surface-contrast: "oklch(30% 0.012 60)" surface-contrast-ink: "oklch(92% 0.01 85)" fresh: color-paper: "oklch(96.8% 0.008 235)" color-paper-strong: "oklch(98.6% 0.004 235)" color-ink: "oklch(27% 0.022 255)" color-night: "oklch(20% 0.01 250)" color-night-ink: "oklch(93% 0.008 235)" color-accent: "oklch(48% 0.11 285)" color-accent-ink: "oklch(99% 0.004 285)" color-accent-soft: "oklch(92% 0.03 285)" color-teal: "oklch(48% 0.05 180)" color-teal-soft: "oklch(94% 0.015 180)" color-signal: "oklch(70% 0.11 280)" color-signal-ink: "oklch(26% 0.06 280)" color-sky: "oklch(85% 0.05 80)" color-muted: "oklch(94.5% 0.006 235)" color-muted-text: "oklch(44% 0.02 250)" color-sheet-wash: "oklch(93.5% 0.01 235)" color-sheet-surface: "oklch(98.2% 0.005 235)" color-sheet-ink: "oklch(42% 0.025 250)" color-blue-wash: "oklch(92.5% 0.03 75)" color-danger: "oklch(50% 0.14 25)" color-overlay-ink: "oklch(100% 0 0)" color-print-paper: "oklch(100% 0 0)" color-print-ink: "oklch(25% 0.03 48)" color-print-muted: "oklch(44% 0.02 50)" color-topic-self: "oklch(50% 0.09 280)" color-topic-emotion: "oklch(49% 0.08 300)" color-topic-relationship: "oklch(49% 0.06 230)" color-topic-life: "oklch(48% 0.05 170)" surface-contrast: "oklch(26% 0.015 250)" surface-contrast-ink: "oklch(93% 0.008 235)" type: font-display: "var(--font-archivo), \"Segoe UI\", \"PingFang SC\", \"Microsoft YaHei\", \"Noto Sans CJK SC\", sans-serif" font-body: "\"Segoe UI\", \"PingFang SC\", \"Microsoft YaHei\", \"Noto Sans CJK SC\", sans-serif" font-code: "var(--font-plex-mono), \"Cascadia Code\", \"SFMono-Regular\", Consolas, monospace" scale: text-2xs: "0.6875rem" text-xs: "0.75rem" text-sm: "0.875rem" text-md: "1rem" text-lg: "1.25rem" text-xl: "1.75rem" text-2xl: "2.5rem" text-3xl: "3.25rem" text-display: "4rem" tracking: tracking-display: "-0.015em" tracking-title: "-0.01em" tracking-body: "0em" tracking-label: "0.08em" space: space-3xs: "0.25rem" space-2xs: "0.5rem" space-xs: "0.75rem" space-sm: "1rem" space-md: "1.5rem" space-lg: "2rem" space-xl: "3rem" space-2xl: "4.5rem" space-3xl: "7rem" radius: radius-control: "0.875rem" radius-panel: "1.375rem" radius-media: "1.125rem" radius-pill: "999px" motion: duration-short: "180ms" duration-medium: "280ms" ease-out: "cubic-bezier(0.16, 1, 0.3, 1)" layout: layout-max: "80rem" header-height: "5rem" banner-height: "4.5rem" meter: meter-track: "color-mix(in oklab, var(--ink) 7%, transparent)" meter-rule: "color-mix(in oklab, var(--ink) 20%, transparent)" meter-fill: "oklch(52% 0.1 45)" meter-needle: "oklch(78% 0.09 85)" shadows: shadow-card-hover: "0 1.25rem 2.5rem color-mix(in oklab, var(--color-ink) 14%, transparent)" shadow-feedback: "0 0.875rem 2rem color-mix(in oklab, var(--color-ink) 17%, transparent)" shadow-dialog: "0 1.5rem 4rem color-mix(in oklab, var(--color-night) 26%, transparent)" shadow-soft: "0 1.25rem 2.75rem color-mix(in oklab, var(--color-ink) 11%, transparent)" shadow-lift: "0 1.75rem 3.5rem color-mix(in oklab, var(--color-ink) 16%, transparent)" --- # Design — 认识你自己 | Know Yourself A locked design system for the rebuilt application. Product truth lives in `PRODUCT.md`; this file owns the visual and interaction world. ## Direction **Creative north star: a community broadsheet assembled one observation at a time.** The interface borrows the hierarchy and material honesty of a small independent publication without imitating distressed paper or retro decoration. Posts are the issue. Assessments are recurring editorial instruments. Private drafts are the writer's desk. ## Genre Editorial social product: tactile, direct, text-led, and operationally clear. ## Macrostructure families - **Front page:** full-bleed photo hero + four facet sections, each in its own layout family (two-column, reversed, inline pill strip, compact split). Every facet leads with its head — in the reversed family the sticker badge closes the head row instead of opening it, so the title still starts the section (2026-10-01). The inline strip lays its entries into equal cells across the whole band. - **Community and discovery:** masthead + editorial statement + creation desk + single reading column. - **Focused tasks:** compact masthead + narrow workbench. - **Libraries and operations:** index rail + working column. - **Long-form content:** bounded reading column with marginal metadata. ## Theme - `--color-paper`: clean warm white, `oklch(99.1% 0.005 85)` - `--color-paper-strong`: warm stock, `oklch(99.4% 0.003 85)` - `--color-ink`: warm near-black, `oklch(25% 0.012 55)` - `--color-muted-text`: warm grey, `oklch(47% 0.014 60)` - `--color-accent`: deep clay, `oklch(52% 0.1 45)` - `--color-teal`: sage, `oklch(48% 0.05 155)` — secondary information - `--color-signal`: honey, `oklch(78% 0.09 85)` - `--color-danger`: red, `oklch(52% 0.14 25)` - Topics are four lighter, less saturated exposures of the same room: self apricot coral `oklch(50% 0.08 45)`, feeling rose `oklch(51% 0.07 25)`, relating honey `oklch(51% 0.055 70)`, living mint `oklch(49% 0.05 150)`. The daylight skin (2026-09-26 refresh of the 2026-09-19 morning light): a clean warm-white page with warm near-black ink. Deep clay marks actions and current state. Sage identifies secondary information. Honey is reserved for measured values — a golden pixel always means "you are here". The palette reads as an everyday room in daylight rather than simulated newsprint or a clinical instrument panel. The paper carries a faint tooth — a tileable grain (baked by `scripts/generate-paper-grain.mjs`) laid over every surface at ~2% opacity (3% in dark) through a fixed `body::after`; material honesty, never distress (2026-09-26). Dark (the night lamp): the page stays at `oklch(21%)` so the room stays dark, and the elevation ladder does the separating — cards sit at 26–27%, header/footer/panels at 30%, and dark draws its own stronger borders (24% / 17% ink mixes) instead of borrowing the light ones (2026-09-30; before this, cards sat only four points over the page and dark borders all but vanished). Fresh (2026-09-29): the cool gray-blue daylight skin, selectable alongside the daylight and night skins. `html.fresh` overrides the palette in `src/app/styles/fresh.css` — an airy near-white ground with blue-gray ink and one violet-blue accent, four cool topic exposures, and its own hero wash (`data-wash="fresh"`). It is a self-contained LIGHT skin: selecting it never follows the system dark preference. All values are contrast-verified with `node scripts/check-palette-contrast.mjs`, which reads all three palette blocks — 45 foreground/background pairs at 5.4:1 or better. The homepage carries a handmade cuteness layer (2026-09-20) on top of this skin: a round CJK question voice, emoji at fixed decorative positions, sticker badges, washi-tape tabs, and a watercolor hero wash. Every element is specified under Signature components; none of them may drift onto other surfaces. ## Typography - Display: Archivo variable, weights 600–700 in use (620 for section titles, 640 for headlines), upright only. - Question voice: the system round CJK face (`YouYuan`/`幼圆`, weight 500) speaks in first person — it carries only the homepage hero question and the facet entry lines, then falls back through the display stack. - Body: system sans with native CJK metrics. - Data: IBM Plex Mono, only for scores, counts, dates, and question indices. - Display tracking: `-0.025em` for Latin and `0` for Chinese. - Body measure: 65–75 characters. - Heavy 750–850 weights are prohibited: CJK falls back to system sans, where synthetic bold reads as chunky rather than editorial. - Both display faces are committed to the repo and served through `next/font/local` (`src/app/fonts/`, 2026-10-01): builds never fetch from Google, so the type system cannot break on a blocked network the way the dev server did when `fonts.gstatic.com` was unreachable. ## Spacing and shape - Four-point named spacing scale in `tokens.css`. - Page width: 80rem maximum; primary reading column: 46rem maximum. - Corners are large and soft (since 2026-09-19): controls `--radius-control` 0.875rem, cards `--radius-panel` 1.375rem, media `--radius-media` 1.125rem. - Pills are reserved for filters, tags, compact statuses, and the single catalog CTA. - Separation prefers rules and whitespace. Shadows are reserved for overlays or a genuinely lifted editor surface. ## Motion - Active publication/filter strips settle with a short overshoot. - Ordinary feedback uses color, opacity, and transforms under 240ms. - Content is visible before animation. - On the quiz workbench the meter fill eases to its new width, a hovered answer rises one pixel with its key letter warming toward the accent, and a selected answer keeps a quiet accent ring (2026-10-01). - Route arrival is the one exception (2026-09-26), and it speaks a small vocabulary — the destination picks the gesture: result = **letter** (the answer unfolds from both edges toward the center, 420ms); test detail = **turn** (the page swings in from its left edge, 460ms); quiz = **focus** (content blooms out of one point, 480ms); journal piece = **curl** (peels open from its bottom-right corner, 500ms); everything else = **rise** (the quiet lift, 260ms). Hard loads never animate, and reduced motion skips every gesture entirely; all other motion still shows content first. - Reduced motion removes movement without removing state. - No pointer-driven page distortion (2026-09-26): the liquid ripple (LiquidDistort, 2026-09-19) was judged ugly by the owner and removed — the page must never warp under the pointer. ## Shared component grammar - The masthead is a horizontal editorial rule, not a floating navigation card. - Feed items are articles. Their typography and media determine their rhythm. - Author, date, content type, badges, and moderation state share one metadata system. - Inputs resemble writing fields with visible labels and complete recovery text. - Empty states explain the missing material and offer one useful next action. ## Signature components The handmade homepage layer (2026-09-20). These are front-page-scoped; other surfaces must not copy them piecemeal — scattered reuse is how a system loses its signature and starts looking generated. - **Watercolor hero** (`press-home-lede`): baked warm washes — clay, honey, honey-earth, and sage with turbulence-roughened edges (`public/bg/hero-wash-light/-dark.webp`, regenerated by `scripts/generate-hero-wash.mjs`, `npm run hero:wash`) — bloom full-bleed behind the lede on transparent grounds; a gentle paper scrim (62% → 0%, bottom to top) keeps the question readable in both themes; the band closes on a 1px rule (2026-09-26). - **Sticker badge** (`press-home-facet-index`): a 2.1rem circle in `--accent-soft` with a 2px `--paper-strong` edge, holding the facet emoji, tilted −5° on odd facets and +4° on even ones. - **Washi-tape tab** (`press-home-facet-anchor::before`): one 3.2rem × 1rem strip of accent-washed paper across the anchor card's top edge — 2px radius, +3° tilt, 0.85 opacity. One per anchor card, never a repeating texture. - **Round question voice** (`press-home-lede h1`, `press-home-facet-entry`): the round CJK face at weight 500 speaks in first person — hero question and facet entry lines only. - **Mascot rain** (`press-mascot-rain`, 2026-09-26; mascots switch to いらすとや 2026-09-30): eight sprites — five いらすとや mascots (bear, cat, hamster, cloud, sprout; converted to webp in `public/stickers/`, © みふねたかし / いらすとや, credited in the home footer) and two tiny hand-drawn shapes (heart, star) drift down through the hero's morning sky on invisible lanes, each with its own speed, sway, and gentle tilt (never a full spin — the mascots must not hang upside down). Decorative only: aria-hidden, pointer-transparent, painted behind the lede text and faded by the paper scrim near the bottom. The random layout is generated client-side only; under reduced motion no sprite exists. - **Catalog CTA capsule** (`press-home-catalog-cta`): the page's one pill call-to-action, an accent outline that fills with `--accent` on hover. ## Accessibility and privacy - All controls remain keyboard operable with visible focus. - Touch targets are at least 44 CSS pixels. - Layouts work at 320, 375, 414, and 768px, at 200% zoom, and without motion. - Chinese and English keep equivalent hierarchy and actions. - Publication, draft, processing, hidden, unpublished, and deletion states are named before the user acts. ## Prohibited defaults Each ban names the default it blocks and the do that replaces it. - No bento dashboard, glass cards, gradient text, decorative mesh, fake browser chrome, or icon-tile feature grid. Do: separate material with 1px `--line` rules and `--paper-strong` surfaces; mark emphasis with `--accent-soft` washes. - No diagnosis language, invented usage figures, testimonials, or clinical claims. Do: describe tendencies in plain language; every number shown is a real measurement set in the mono face. - No decorative monospace, italic display headings, or duplicated heading kickers. Do: monospace only for scores, counts, dates, and indices; display headings stay upright; one kicker per block. - No emoji as functional icons, status indicators, or body-text decoration. Do: emoji only at the fixed decorative positions named in Signature components (hero question, hero lede copy, facet badges and titles, catalog CTA, voice headings). - No inaccessible hover-only actions or drag-only ordering. Do: actions stay visible on keyboard focus; ordering always offers a keyboard path. - No runtime third-party fonts, analytics, or AI image generation. Do: system stacks plus locally served variable fonts; first-party aggregate events only. ## Iteration guide 1. Change one component at a time and reference token names, never hex values. 2. After any palette change, run `node scripts/check-palette-contrast.mjs` — the 45 foreground/background pairs must stay at 5.4:1 or better across all three skins (daylight, dark, fresh). 3. After any import or file-structure change, run `npm run check:registry`; a new runtime relative import must also be added to the compile allow-list in `scripts/test-storage.mjs`. 4. For skin trials, screenshot `design-preview.html` (it renders both themes) with a throwaway Edge profile (`--user-data-dir` on a fresh temp dir). Byte-identical screenshots mean a cached fake render, not a real one. 5. Local `npm test` only runs with `NODE_ENV` cleared. ## Known gaps - Resolved 2026-10-01: ink-ground text no longer borrows page-ink tones. The assessment catalog's intro band, the result letter panel's summary and note, and the focus headers (quiz / result / journal piece) now derive their text from the band's own ink (`--surface-contrast-ink`); the result page's focus header also returned to the ink band the quiz workbench wears, instead of a light band carrying white legacy link text. The feed's empty state is one grid item again (the community feed root wraps its toolbar and states, so the quiet-hours card can no longer auto-place under the heading), the compact facet's heading carries the display tier again, and the wordmark seal keeps its 己 glyph on `wellness-page` routes whose legacy sheet zeroed the mark's font size. - Resolved 2026-09-27: `--accent-soft` is now declared as `var(--color-accent-soft)`. It had been referenced by nine `refactor.css` rules (selected quiz answers, active toolbar buttons, mobile-nav state, sticker badge) while declared nowhere, so every selected/active surface rendered transparent — production users read the missing states as "parts of the page not loading". The sticker badge gains the soft wash that was originally written for it. - Homepage micro-interactions use literal 160/200ms while the motion tokens (`--duration-short` 180ms, `--duration-medium` 280ms) sit unused by them. - The round question voice depends on a system font; devices without YouYuan fall back to the display stack, so the cute voice varies by platform. - The hero washes are baked at 16:9; the portrait crop keeps the quiet text zone but trims the right-side blooms, and the far-left sage whisper may touch the question's first line — recheck if the composition moves. - The washi-tape tab is positioned for the current 16–22rem anchor column; it does not adapt to other card widths. ## Implementation source `tokens.css` keeps the structural scale; the active colour palette lives in the `:root` and `.dark` blocks of `src/app/styles/base.css`, with the fresh skin's overrides in the `html.fresh` block of `src/app/styles/fresh.css`. The other seven `styles/*.css` modules (shell, assessments, quiz, result, journal, home, motion — split from the old single refactor.css on 2026-09-27) implement the shell and migrated surfaces. Legacy selectors may remain only while an unmigrated route still consumes them; new components use the `press-*` namespace. The YAML header of this file is generated from the palette blocks by `scripts/gen-design-tokens.mjs` (`npm run design:tokens`), which reads tokens.css, base.css, and fresh.css; never hand-edit the header — regenerate it.