diff --git a/docs/analysis/2026-09-08-night-palette-candidates/README.md b/docs/analysis/2026-09-08-night-palette-candidates/README.md new file mode 100644 index 000000000..e41888c67 --- /dev/null +++ b/docs/analysis/2026-09-08-night-palette-candidates/README.md @@ -0,0 +1,21 @@ +# Night palette candidate checkpoint + +This numeric evidence package supports the maintainer choice requested in [issue #2009](https://github.com/Chris0Jeky/Taskdeck/issues/2009). It does not select or adopt a palette. The shipped primary ink value stays exact, all accent and status tokens stay exact, and the 3:1 non-text boundary target is reported as an aspiration rather than a release gate. + +The package compares the current night palette with three warm directions: + +- **A. Walnut outline** adds the clearest improvement with the smallest surface change. +- **B. Open folio** lightens the page substrate around recessed dark cards, reducing page-scale primary-ink contrast. +- **C. Cedar layers** makes cards rise from a dark substrate and gives the strongest surface hierarchy. + +[Measured palette data](./contrast-summary.md) contains the issue comparison table, method, and complete CSV matrix. The [rendered token receipt](./rendered-token-measurements.json) records browser-computed values at the real render roots, selected Paper mode, viewport, PNG dimensions, and the synthetic fixture identifiers. The [local contact sheet](./harness/contact-sheet.html) compares the full-resolution Home, Today, and Review captures at 1440 by 1000. + +The capture harness uses the repository Playwright fixture helpers with one synthetic Mock user, board, and pending capture proposal. The user selection is explicit: each page navigation seeds `td.paper.mode.v2` with `paper` or `paper-night`; the harness does not infer a theme from computed colors. The fixed capture title is `Night palette proof`, while generated user, board, capture, and proposal IDs remain in the receipt for traceability. No real data or remote provider was used. + +The current night baseline keeps the shipped behavior. It records a real parity finding on Review: the body is selected as `paper-night`, but `PaperReviewView` mounts a static `.paper` root, so its inner surface computes the shipped light tokens. Candidate Review rows are clearly experimental comparisons: the harness applies the full night token set plus the selected `palettes.json` surface overrides to the real `.paper` root with page-local CSS only. This makes the candidate surfaces comparable without claiming a production fix or palette adoption. Home and Today inherit the selected body scope directly. + +The harness asserts the Playwright page viewport and PNG IHDR dimensions are both exactly 1440 by 1000, verifies selected candidate values on the document and actual surface roots, and checks that populated app text is present before each capture. Its source is retained under [`harness/`](./harness/); the disposable Mock database and server state are excluded from delivery. + +## Source binding + +The screenshots were rendered from application base `64508d3e2f2d9dc8f8ea9aa5e9eb4539173ccb08`. The candidate measurements and palette inputs were saved at `209d5af1c5758f10305e4fe68b28f44602807dcb` before this evidence package was integrated. The later `origin/main` merge into this branch is integration context only; it does not imply a recapture or change the source binding of these screenshots and measurements. diff --git a/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/home/baseline.png b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/home/baseline.png new file mode 100644 index 000000000..0a87e293d Binary files /dev/null and b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/home/baseline.png differ diff --git a/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/home/cedar-layers.png b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/home/cedar-layers.png new file mode 100644 index 000000000..9e89d349b Binary files /dev/null and b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/home/cedar-layers.png differ diff --git a/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/home/light-baseline.png b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/home/light-baseline.png new file mode 100644 index 000000000..5a0504a47 Binary files /dev/null and b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/home/light-baseline.png differ diff --git a/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/home/open-folio.png b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/home/open-folio.png new file mode 100644 index 000000000..01668e487 Binary files /dev/null and b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/home/open-folio.png differ diff --git a/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/home/walnut-outline.png b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/home/walnut-outline.png new file mode 100644 index 000000000..63ddff43c Binary files /dev/null and b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/home/walnut-outline.png differ diff --git a/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/review/baseline.png b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/review/baseline.png new file mode 100644 index 000000000..78907b4fe Binary files /dev/null and b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/review/baseline.png differ diff --git a/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/review/cedar-layers.png b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/review/cedar-layers.png new file mode 100644 index 000000000..7803e9026 Binary files /dev/null and b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/review/cedar-layers.png differ diff --git a/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/review/light-baseline.png b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/review/light-baseline.png new file mode 100644 index 000000000..007df81f9 Binary files /dev/null and b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/review/light-baseline.png differ diff --git a/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/review/open-folio.png b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/review/open-folio.png new file mode 100644 index 000000000..47085e9e0 Binary files /dev/null and b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/review/open-folio.png differ diff --git a/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/review/walnut-outline.png b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/review/walnut-outline.png new file mode 100644 index 000000000..f2302832f Binary files /dev/null and b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/review/walnut-outline.png differ diff --git a/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/today/baseline.png b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/today/baseline.png new file mode 100644 index 000000000..f2d3a4896 Binary files /dev/null and b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/today/baseline.png differ diff --git a/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/today/cedar-layers.png b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/today/cedar-layers.png new file mode 100644 index 000000000..57616e260 Binary files /dev/null and b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/today/cedar-layers.png differ diff --git a/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/today/light-baseline.png b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/today/light-baseline.png new file mode 100644 index 000000000..28f3b52ed Binary files /dev/null and b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/today/light-baseline.png differ diff --git a/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/today/open-folio.png b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/today/open-folio.png new file mode 100644 index 000000000..0769910d2 Binary files /dev/null and b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/today/open-folio.png differ diff --git a/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/today/walnut-outline.png b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/today/walnut-outline.png new file mode 100644 index 000000000..49024781d Binary files /dev/null and b/docs/analysis/2026-09-08-night-palette-candidates/captures/1440x1000/today/walnut-outline.png differ diff --git a/docs/analysis/2026-09-08-night-palette-candidates/contrast-matrix.csv b/docs/analysis/2026-09-08-night-palette-candidates/contrast-matrix.csv new file mode 100644 index 000000000..a23299a27 --- /dev/null +++ b/docs/analysis/2026-09-08-night-palette-candidates/contrast-matrix.csv @@ -0,0 +1,59 @@ +"category","pair","Current night","A. Walnut outline","B. Open folio","C. Cedar layers" +"foreground / surface","--ink / --paper","14.88","14.36","12.44","14.46" +"foreground / surface","--ink / --paper-2","15.32","14.85","13.33","14.94" +"foreground / surface","--ink / --paper-card","13.96","13.22","13.96","12.44" +"foreground / surface","--ink / --paper-edge","13.86","12.50","10.29","11.49" +"foreground / surface","--ink-deep / --paper","17.02","16.42","14.22","16.54" +"foreground / surface","--ink-deep / --paper-2","17.52","16.99","15.24","17.09" +"foreground / surface","--ink-deep / --paper-card","15.96","15.12","15.96","14.22" +"foreground / surface","--ink-deep / --paper-edge","15.84","14.29","11.77","13.14" +"foreground / surface","--ink-2 / --paper","10.29","9.93","8.60","10.00" +"foreground / surface","--ink-2 / --paper-2","10.59","10.27","9.22","10.33" +"foreground / surface","--ink-2 / --paper-card","9.65","9.14","9.65","8.60" +"foreground / surface","--ink-2 / --paper-edge","9.58","8.64","7.12","7.95" +"foreground / surface","--mute / --paper","5.90","5.70","4.93","5.73" +"foreground / surface","--mute / --paper-2","6.07","5.89","5.29","5.92" +"foreground / surface","--mute / --paper-card","5.54","5.24","5.54","4.93" +"foreground / surface","--mute / --paper-edge","5.49","4.96","4.08","4.56" +"foreground / surface","--faint / --paper","5.11","4.93","4.62","5.37" +"foreground / surface","--faint / --paper-2","5.26","5.10","4.95","5.55" +"foreground / surface","--faint / --paper-card","4.79","4.54","5.19","4.62" +"foreground / surface","--faint / --paper-edge","4.75","4.29","3.82","4.27" +"foreground / surface","--whisper / --paper","1.58","1.94","2.25","2.38" +"foreground / surface","--whisper / --paper-2","1.63","2.01","2.41","2.46" +"foreground / surface","--whisper / --paper-card","1.48","1.79","2.52","2.05" +"foreground / surface","--whisper / --paper-edge","1.47","1.69","1.86","1.89" +"foreground / surface","--ember / --paper","5.45","5.26","4.56","5.30" +"foreground / surface","--ember / --paper-2","5.61","5.44","4.88","5.47" +"foreground / surface","--ember / --paper-card","5.11","4.84","5.11","4.56" +"foreground / surface","--ember / --paper-edge","5.07","4.58","3.77","4.21" +"foreground / surface","--ember-deep / --paper","3.66","3.53","3.06","3.56" +"foreground / surface","--ember-deep / --paper-2","3.77","3.65","3.28","3.67" +"foreground / surface","--ember-deep / --paper-card","3.43","3.25","3.43","3.06" +"foreground / surface","--ember-deep / --paper-edge","3.41","3.07","2.53","2.83" +"foreground / surface","--ember-ink / --paper","10.83","10.45","9.05","10.52" +"foreground / surface","--ember-ink / --paper-2","11.14","10.81","9.70","10.87" +"foreground / surface","--ember-ink / --paper-card","10.16","9.62","10.16","9.05" +"foreground / surface","--ember-ink / --paper-edge","10.08","9.09","7.49","8.36" +"foreground / surface","--applied / --paper","7.51","7.25","6.28","7.30" +"foreground / surface","--applied / --paper-2","7.73","7.49","6.73","7.54" +"foreground / surface","--applied / --paper-card","7.04","6.67","7.04","6.28" +"foreground / surface","--applied / --paper-edge","6.99","6.31","5.19","5.80" +"foreground / surface","--overdue / --paper","7.11","6.86","5.94","6.91" +"foreground / surface","--overdue / --paper-2","7.32","7.09","6.37","7.14" +"foreground / surface","--overdue / --paper-card","6.67","6.31","6.67","5.94" +"foreground / surface","--overdue / --paper-edge","6.62","5.97","4.92","5.49" +"foreground / surface","--line / --paper","1.23","3.33","2.89","3.91" +"foreground / surface","--line / --paper-2","1.26","3.45","3.09","4.04" +"foreground / surface","--line / --paper-card","1.15","3.07","3.24","3.37" +"foreground / surface","--line / --paper-edge","1.14","2.90","2.39","3.11" +"foreground / surface","--line-soft / --paper","1.12","1.87","1.62","2.22" +"foreground / surface","--line-soft / --paper-2","1.15","1.93","1.74","2.30" +"foreground / surface","--line-soft / --paper-card","1.05","1.72","1.82","1.91" +"foreground / surface","--line-soft / --paper-edge","1.04","1.63","1.34","1.77" +"surface hierarchy","--paper-card / --paper","1.07","1.09","1.12","1.16" +"surface hierarchy","--paper-card / --paper-2","1.10","1.12","1.05","1.20" +"surface hierarchy","--paper-edge / --paper","1.07","1.15","1.21","1.26" +"control text","--td-on-ember / --ember","5.49","5.49","5.49","5.49" +"control text","--td-on-ember / brightness(1.1) --ember","6.58","6.58","6.58","6.58" +"status text","--ember-ink / --ember-tint","9.53","9.53","9.53","9.53" diff --git a/docs/analysis/2026-09-08-night-palette-candidates/contrast-summary.md b/docs/analysis/2026-09-08-night-palette-candidates/contrast-summary.md new file mode 100644 index 000000000..8c59ffe04 --- /dev/null +++ b/docs/analysis/2026-09-08-night-palette-candidates/contrast-summary.md @@ -0,0 +1,25 @@ +# Measured palette data + +Generated by measure-contrast.mjs from the shipped .paper-night token block and [palettes.json](./palettes.json). + +The calculation is the same WCAG sRGB relative-luminance formula used by the existing frontend contrast specs. Ratios are rounded to two decimals for presentation; assertions use unrounded values. The 3:1 boundary figure is characterized for comparison and is not treated as an acceptance gate. + +## Issue measures + +| Measure | Current night | A. Walnut outline | B. Open folio | C. Cedar layers | +| --- | ---: | ---: | ---: | ---: | +| Boundary | 1.15:1 | 3.07:1 | 3.24:1 | 3.37:1 | +| Soft boundary | 1.05:1 | 1.72:1 | 1.82:1 | 1.91:1 | +| Boundary on substrate | 1.23:1 | 3.33:1 | 2.89:1 | 3.91:1 | +| Card step | 1.07:1 | 1.09:1 | 1.12:1 | 1.16:1 | +| Card step on paper 2 | 1.10:1 | 1.12:1 | 1.05:1 | 1.20:1 | +| Whisper on card | 1.48:1 | 1.79:1 | 2.52:1 | 2.05:1 | +| Primary ink on substrate | 14.88:1 | 14.36:1 | 12.44:1 | 14.46:1 | +| Primary ink on card | 13.96:1 | 13.22:1 | 13.96:1 | 12.44:1 | +| Secondary ink on card | 9.65:1 | 9.14:1 | 9.65:1 | 8.60:1 | +| Muted ink on card | 5.54:1 | 5.24:1 | 5.54:1 | 4.93:1 | +| Faint ink on card | 4.79:1 | 4.54:1 | 5.19:1 | 4.62:1 | + +Only --faint changes outside the surface and boundary group, in Open folio and Cedar layers. That small lift keeps faint text at or above 4.5:1 on their brighter surfaces. --ink, the rest of the ink ladder, and every accent and status token remain exactly at the shipped values. + +The complete foreground-by-surface matrix, surface hierarchy measures, CTA base and hover checks, and error-tint pairing are in [contrast-matrix.csv](./contrast-matrix.csv). diff --git a/docs/analysis/2026-09-08-night-palette-candidates/harness/capture-night-palettes.spec.ts b/docs/analysis/2026-09-08-night-palette-candidates/harness/capture-night-palettes.spec.ts new file mode 100644 index 000000000..b90e2f4ac --- /dev/null +++ b/docs/analysis/2026-09-08-night-palette-candidates/harness/capture-night-palettes.spec.ts @@ -0,0 +1,245 @@ +import { expect, test, type Page } from '@playwright/test' +import { readFileSync, writeFileSync, mkdirSync } from 'node:fs' +import { resolve } from 'node:path' +import { API_BASE_URL, registerAndAttachSession, type AuthResult } from '../../../../frontend/taskdeck-web/tests/e2e/support/authSession' +import { createBoardWithColumn } from '../../../../frontend/taskdeck-web/tests/e2e/support/boardHelpers' +import { + createCaptureItem, + triageCaptureItem, + waitForProposalCreated, +} from '../../../../frontend/taskdeck-web/tests/e2e/support/captureFlow' + +type Palette = { + id: string + label: string + intent: string + overrides: Record +} + +type PaletteConfig = { + source: string + viewport: { width: number; height: number } + variants: Palette[] +} + +type Surface = { id: string; label: string; path: string; ready: string } + +const reportDir = resolve(process.cwd(), '..', '..', 'docs', 'analysis', '2026-09-08-night-palette-candidates') +const harnessDir = resolve(reportDir, 'harness') +const captureDir = resolve(reportDir, 'captures', '1440x1000') +const palettes = JSON.parse(readFileSync(resolve(reportDir, 'palettes.json'), 'utf8')) as PaletteConfig +const tokenCss = readFileSync(resolve(reportDir, '..', '..', '..', 'frontend', 'taskdeck-web', 'src', 'paper-tokens.css'), 'utf8') +const surfaces: Surface[] = [ + { id: 'home', label: 'Home', path: '/workspace/home', ready: '[data-testid="paper-home"]' }, + { id: 'today', label: 'Today', path: '/workspace/today', ready: '[data-paper-today]' }, + { id: 'review', label: 'Review', path: '/workspace/review', ready: '[data-testid="paper-review-view"]' }, +] +const lightBaseline: Palette = { + id: 'light-baseline', + label: 'Light baseline parity', + intent: 'The shipped light Paper mode, captured with no token overrides.', + overrides: {}, +} + +const inspectedTokens = [ + '--paper', '--paper-2', '--paper-card', '--paper-edge', '--line', '--line-soft', '--whisper', '--faint', + '--ink', '--ink-deep', '--ink-2', '--mute', '--ember', '--ember-deep', '--ember-bloom', '--ember-tint', + '--ember-ink', '--td-on-ember', '--applied', '--applied-tint', '--overdue', '--overdue-tint', +] + +function readTokenBlock(selector: string): Record { + const escapedSelector = selector.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + const block = tokenCss.match(new RegExp(`${escapedSelector}\\s*\\{([\\s\\S]*?)\\n\\}`))?.[1] + if (!block) throw new Error(`Could not locate ${selector} in paper-tokens.css`) + return Object.fromEntries( + [...block.matchAll(/(--[a-z0-9-]+):\s*(#[0-9a-fA-F]{3,8})/g)] + .map((match) => [match[1], match[2].toLowerCase()]), + ) +} + +const baseTokens = { + paper: readTokenBlock('.paper'), + 'paper-night': readTokenBlock('.paper-night'), +} + +function asCssRule(overrides: Record): string { + return Object.entries(overrides).map(([name, value]) => `${name}: ${value} !important;`).join(' ') +} + +async function selectPaperMode(page: Page, mode: 'paper' | 'paper-night') { + await page.addInitScript((selectedMode) => { + window.localStorage.setItem('td.paper.mode.v2', selectedMode) + }, mode) +} + +async function applyVariant(page: Page, palette: Palette, mode: 'paper' | 'paper-night') { + const experimentalNightRootTokens = mode === 'paper-night' && palette.id !== 'baseline' + ? { ...baseTokens['paper-night'], ...palette.overrides } + : {} + const cssText = asCssRule(experimentalNightRootTokens) + await page.evaluate(({ selectedMode, cssText }) => { + document.body.classList.toggle('paper-night', selectedMode === 'paper-night') + document.body.classList.toggle('paper', selectedMode === 'paper') + document.querySelector('[data-night-palette-override]')?.remove() + if (!cssText) return + const style = document.createElement('style') + style.dataset.nightPaletteOverride = 'true' + // Review's real root currently carries `.paper` even when the selected + // user mode is `paper-night`. Candidate captures scope the experimental + // night token set to that real root so the comparison shows the proposed + // surfaces while preserving the shipped baseline parity finding. + style.textContent = `body.${selectedMode}, body.${selectedMode} .paper { ${cssText} }` + document.head.append(style) + }, { selectedMode: mode, cssText }) + await expect.poll(() => page.evaluate((selectedMode) => { + return document.body.classList.contains(selectedMode) + }, mode)).toBe(true) +} + +async function readComputedTokens(page: Page): Promise> { + return await page.evaluate((names) => { + const styles = window.getComputedStyle(document.body) + return Object.fromEntries(names.map((name) => [name, styles.getPropertyValue(name).trim().toLowerCase()])) + }, inspectedTokens) +} + +async function readRootMeasurements(page: Page, surface: Surface) { + return await page.evaluate(({ ready, names }) => { + const root = document.querySelector(ready) + if (!root) throw new Error(`Missing render root ${ready}`) + const read = (element: Element) => { + const styles = window.getComputedStyle(element) + return { + tokens: Object.fromEntries(names.map((name) => [name, styles.getPropertyValue(name).trim().toLowerCase()])), + backgroundColor: styles.backgroundColor, + } + } + const sampleCard = root.querySelector('.card, [class*="card"]') + return { + rootTag: root.tagName.toLowerCase(), + rootClasses: root.className, + root: read(root), + sampleCard: sampleCard ? read(sampleCard) : null, + } + }, { ready: surface.ready, names: inspectedTokens }) +} + +function readPngDimensions(path: string): { width: number; height: number } { + const bytes = readFileSync(path) + if (bytes.length < 24 || bytes.readUInt32BE(0) !== 0x89504e47 || bytes.toString('ascii', 12, 16) !== 'IHDR') { + throw new Error(`Not a PNG: ${path}`) + } + return { width: bytes.readUInt32BE(16), height: bytes.readUInt32BE(20) } +} + +async function captureSurface(page: Page, surface: Surface, palette: Palette, mode: 'paper' | 'paper-night') { + await selectPaperMode(page, mode) + await page.goto(surface.path) + await expect(page.locator(surface.ready)).toBeVisible() + await expect(page.locator('body')).toHaveClass(new RegExp(`(^|\\s)${mode}(\\s|$)`)) + await applyVariant(page, palette, mode) + + const tokenValues = await readComputedTokens(page) + const rootMeasurements = await readRootMeasurements(page, surface) + const viewport = page.viewportSize() + expect(viewport).toEqual(palettes.viewport) + const screenshotDir = resolve(captureDir, surface.id) + mkdirSync(screenshotDir, { recursive: true }) + const screenshotPath = resolve(screenshotDir, `${palette.id}.png`) + await page.screenshot({ path: screenshotPath, fullPage: false }) + const pngDimensions = readPngDimensions(screenshotPath) + expect(pngDimensions).toEqual(palettes.viewport) + + const visibleText = (await page.locator('body').innerText()).trim() + expect(visibleText.length, `${surface.label}/${palette.label} should contain rendered app content`).toBeGreaterThan(120) + return { + screenshot: `captures/1440x1000/${surface.id}/${palette.id}.png`, + tokenValues, + rootMeasurements, + viewport, + pngDimensions, + visibleTextLength: visibleText.length, + } +} + +test('capture populated Paper surfaces across night candidates and light parity', async ({ page, request }) => { + test.setTimeout(180_000) + const auth = await registerAndAttachSession(page, request, 'nightpalette') + const seed = `${Date.now()}-${Math.floor(Math.random() * 1_000_000)}` + const boardId = await createBoardWithColumn(request, auth, seed, { + boardNamePrefix: 'Night palette evidence', + description: 'Synthetic Mock dataset for issue 2009 rendered palette comparison.', + columnNamePrefix: 'Review queue', + }) + const captureText = '- [ ] Night palette proof' + const capture = await createCaptureItem(request, auth, boardId, captureText) + await triageCaptureItem(request, auth, capture.id) + const triaged = await waitForProposalCreated(request, auth, capture.id) + expect(triaged.provenance?.proposalId).toBeTruthy() + + const nightVariants = palettes.variants.map((variant) => ({ palette: variant, mode: 'paper-night' as const })) + const allVariants = [ + { palette: lightBaseline, mode: 'paper' as const }, + ...nightVariants, + ] + const records: Array> = [] + for (const variant of allVariants) { + for (const surface of surfaces) { + const result = await captureSurface(page, surface, variant.palette, variant.mode) + const expectedTokens = { + ...baseTokens[variant.mode], + ...(variant.mode === 'paper-night' ? variant.palette.overrides : {}), + } + const rootExpectedTokens = variant.mode === 'paper-night' && variant.palette.id === 'baseline' && surface.id !== 'review' + ? expectedTokens + : variant.mode === 'paper-night' && variant.palette.id !== 'baseline' + ? expectedTokens + : baseTokens.paper + const mismatches = inspectedTokens.filter((name) => { + const expected = expectedTokens[name] + const actual = result.tokenValues[name] + return expected !== undefined && actual !== expected.toLowerCase() + }).map((name) => ({ name, expected: expectedTokens[name], actual: result.tokenValues[name] })) + const rootMismatches = inspectedTokens.filter((name) => { + const expected = rootExpectedTokens[name] + const actual = result.rootMeasurements.root.tokens[name] + return expected !== undefined && actual !== expected.toLowerCase() + }).map((name) => ({ + name, + expected: rootExpectedTokens[name], + actual: result.rootMeasurements.root.tokens[name], + })) + expect(mismatches, `${surface.label}/${variant.palette.label} browser tokens`).toEqual([]) + if (variant.palette.id !== 'baseline' || variant.mode === 'paper') { + expect(rootMismatches, `${surface.label}/${variant.palette.label} render-root tokens`).toEqual([]) + } + records.push({ + surface: surface.id, + surfaceLabel: surface.label, + palette: variant.palette.id, + paletteLabel: variant.palette.label, + selectedMode: variant.mode, + screenshot: result.screenshot, + viewport: result.viewport, + pngDimensions: result.pngDimensions, + visibleTextLength: result.visibleTextLength, + expectedTokens, + computedTokens: result.tokenValues, + renderRoot: result.rootMeasurements, + mismatches, + rootMismatches, + parityFinding: variant.palette.id === 'baseline' && variant.mode === 'paper-night' && surface.id === 'review' + ? 'Shipped Review root remains .paper/light while body selection is paper-night; candidate rows apply an ephemeral night token set to the real .paper root.' + : null, + }) + } + } + + writeFileSync(resolve(reportDir, 'rendered-token-measurements.json'), JSON.stringify({ + generatedAt: new Date().toISOString(), + viewport: palettes.viewport, + syntheticDataset: { boardId, captureId: capture.id, proposalId: triaged.provenance?.proposalId, seed }, + selectedModes: { night: 'paper-night', lightParity: 'paper' }, + records, + }, null, 2) + '\n') +}) diff --git a/docs/analysis/2026-09-08-night-palette-candidates/harness/contact-sheet.html b/docs/analysis/2026-09-08-night-palette-candidates/harness/contact-sheet.html new file mode 100644 index 000000000..65ff7a022 --- /dev/null +++ b/docs/analysis/2026-09-08-night-palette-candidates/harness/contact-sheet.html @@ -0,0 +1,35 @@ + + +Night palette candidate comparison + +

Issue #2009 · rendered Paper palette candidates

+

Real Chromium captures from the synthetic Mock stack at 1440×1000. Each image is the live Paper surface with a selected theme mode and an ephemeral page-local token override. Open the full-resolution PNGs under captures/1440x1000/.

+

Review parity: the shipped night selection leaves Review’s static .paper root light inside the dark shell; that baseline is retained as evidence. Candidate Review tiles apply an explicitly experimental page-local night token set to the real root for comparison only. Click any tile to open its full-resolution PNG.

+
+
Home light baseline
Home · Light baselineselected mode: paper
+
Home current night
Home · Current nightselected mode: paper-night
+
Home walnut outline
Home · A. Walnut outline
+
Home open folio
Home · B. Open folio
+
Home cedar layers
Home · C. Cedar layers
+
Today light baseline
Today · Light baseline
+
Today current night
Today · Current night
+
Today walnut outline
Today · A. Walnut outline
+
Today open folio
Today · B. Open folio
+
Today cedar layers
Today · C. Cedar layers
+
Review light baseline
Review · Light baseline
+
Review current night
Review · Current night
+
Review walnut outline
Review · A. Walnut outline
+
Review open folio
Review · B. Open folio
+
Review cedar layers
Review · C. Cedar layers
+
diff --git a/docs/analysis/2026-09-08-night-palette-candidates/harness/night-palette.config.ts b/docs/analysis/2026-09-08-night-palette-candidates/harness/night-palette.config.ts new file mode 100644 index 000000000..6de34b66f --- /dev/null +++ b/docs/analysis/2026-09-08-night-palette-candidates/harness/night-palette.config.ts @@ -0,0 +1,65 @@ +import { defineConfig, devices } from '@playwright/test' +import { mkdirSync } from 'node:fs' +import { resolve } from 'node:path' + +const repoRoot = resolve(process.cwd(), '..', '..') +const reportDir = resolve(repoRoot, 'docs', 'analysis', '2026-09-08-night-palette-candidates') +const harnessDir = resolve(reportDir, 'harness') +const frontendDir = resolve(repoRoot, 'frontend', 'taskdeck-web') +const runtimeDir = resolve(reportDir, 'runtime') +mkdirSync(runtimeDir, { recursive: true }) + +const apiOrigin = 'http://127.0.0.1:58743' +const frontendOrigin = 'http://127.0.0.1:5181' +const e2eDb = resolve(runtimeDir, 'taskdeck.e2e.issue2009-night.db') + +export default defineConfig({ + testDir: harnessDir, + fullyParallel: false, + workers: 1, + timeout: 180_000, + expect: { timeout: 15_000 }, + reporter: [['line'], ['html', { outputFolder: resolve(reportDir, 'playwright-report'), open: 'never' }]], + use: { + baseURL: frontendOrigin, + trace: 'retain-on-failure', + viewport: { width: 1440, height: 1000 }, + colorScheme: 'dark', + }, + projects: [{ + name: 'chromium', + use: { ...devices['Desktop Chrome'], viewport: { width: 1440, height: 1000 } }, + }], + webServer: [ + { + command: 'dotnet run --no-launch-profile --project ../../backend/src/Taskdeck.Api/Taskdeck.Api.csproj', + cwd: frontendDir, + url: `${apiOrigin}/api/boards`, + timeout: 120_000, + reuseExistingServer: false, + stdout: 'ignore', + stderr: 'pipe', + env: { + ASPNETCORE_ENVIRONMENT: 'Development', + ConnectionStrings__DefaultConnection: `Data Source=${e2eDb};Pooling=True;Default Timeout=30`, + ASPNETCORE_URLS: apiOrigin, + Cors__DevelopmentAllowedOrigins__0: frontendOrigin, + Llm__EnableLiveProviders: 'false', + Llm__AllowLiveProvidersInDevelopment: 'false', + Llm__Provider: 'Mock', + }, + }, + { + command: 'npm run dev -- --host 127.0.0.1 --port 5181', + cwd: frontendDir, + url: frontendOrigin, + timeout: 120_000, + reuseExistingServer: false, + stdout: 'ignore', + stderr: 'pipe', + env: { + VITE_API_BASE_URL: `${apiOrigin}/api`, + }, + }, + ], +}) diff --git a/docs/analysis/2026-09-08-night-palette-candidates/measure-contrast.mjs b/docs/analysis/2026-09-08-night-palette-candidates/measure-contrast.mjs new file mode 100644 index 000000000..977c54c1c --- /dev/null +++ b/docs/analysis/2026-09-08-night-palette-candidates/measure-contrast.mjs @@ -0,0 +1,205 @@ +import { readFileSync, writeFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' + +const reportDir = dirname(fileURLToPath(import.meta.url)) +const repoRoot = resolve(reportDir, '..', '..', '..') +const config = JSON.parse(readFileSync(resolve(reportDir, 'palettes.json'), 'utf8')) +const css = readFileSync(resolve(repoRoot, config.source), 'utf8') +const block = css.match(/\.paper-night\s*\{([\s\S]*?)\n\}/)?.[1] +if (!block) throw new Error('Could not locate .paper-night in paper-tokens.css') + +const baseline = Object.fromEntries( + [...block.matchAll(/(--[a-z0-9-]+):\s*(#[0-9a-fA-F]{3,8})/g)] + .map((match) => [match[1], match[2].toLowerCase()]), +) +const variants = config.variants.map((variant) => ({ + ...variant, + tokens: { ...baseline, ...variant.overrides }, +})) + +function hexToRgb(hex) { + let value = hex.replace('#', '') + if (value.length === 3) value = value.split('').map((channel) => channel + channel).join('') + if (value.length === 8) value = value.slice(0, 6) + return [0, 2, 4].map((offset) => Number.parseInt(value.slice(offset, offset + 2), 16)) +} + +// Same WCAG sRGB calculation as the existing frontend contrast specs. +function luminance(hex) { + const linear = hexToRgb(hex).map((channel) => { + const value = channel / 255 + return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4 + }) + return 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2] +} + +function contrast(first, second) { + const values = [luminance(first), luminance(second)].sort((left, right) => right - left) + return (values[0] + 0.05) / (values[1] + 0.05) +} + +function brighten(hex, factor) { + const channels = hexToRgb(hex).map((channel) => Math.min(255, Math.round(channel * factor))) + return '#' + channels.map((channel) => channel.toString(16).padStart(2, '0')).join('') +} + +function rounded(value) { + return Math.round(value * 100) / 100 +} + +const expectedBaseline = { + '--line/--paper-card': 1.15, + '--line-soft/--paper-card': 1.05, + '--line/--paper': 1.23, + '--paper-card/--paper': 1.07, + '--paper-card/--paper-2': 1.10, + '--whisper/--paper-card': 1.48, + '--ink/--paper': 14.88, + '--ink/--paper-card': 13.96, + '--ink-2/--paper-card': 9.65, + '--mute/--paper-card': 5.54, + '--faint/--paper-card': 4.79 +} + +for (const [pair, expected] of Object.entries(expectedBaseline)) { + const [foreground, background] = pair.split('/') + const actual = rounded(contrast(baseline[foreground], baseline[background])) + if (actual !== expected) throw new Error('Baseline drift for ' + pair + ': ' + actual) +} + +const stableTokens = [ + '--ink', '--ink-deep', '--ink-2', '--mute', + '--ember', '--ember-deep', '--ember-bloom', '--ember-tint', + '--ember-ink', '--td-on-ember', '--applied', '--applied-tint', + '--overdue', '--overdue-tint' +] +for (const variant of variants.slice(1)) { + for (const token of stableTokens) { + if (variant.tokens[token] !== baseline[token]) { + throw new Error(variant.id + ' unexpectedly changes ' + token) + } + } +} + +const surfaces = ['--paper', '--paper-2', '--paper-card', '--paper-edge'] +const foregrounds = [ + '--ink', '--ink-deep', '--ink-2', '--mute', '--faint', '--whisper', + '--ember', '--ember-deep', '--ember-ink', '--applied', '--overdue', + '--line', '--line-soft' +] +const pairs = [] +for (const foreground of foregrounds) { + for (const background of surfaces) { + pairs.push({ + category: 'foreground / surface', + pair: foreground + ' / ' + background, + measure: (tokens) => contrast(tokens[foreground], tokens[background]) + }) + } +} +pairs.push( + { + category: 'surface hierarchy', + pair: '--paper-card / --paper', + measure: (tokens) => contrast(tokens['--paper-card'], tokens['--paper']) + }, + { + category: 'surface hierarchy', + pair: '--paper-card / --paper-2', + measure: (tokens) => contrast(tokens['--paper-card'], tokens['--paper-2']) + }, + { + category: 'surface hierarchy', + pair: '--paper-edge / --paper', + measure: (tokens) => contrast(tokens['--paper-edge'], tokens['--paper']) + }, + { + category: 'control text', + pair: '--td-on-ember / --ember', + measure: (tokens) => contrast(tokens['--td-on-ember'], tokens['--ember']) + }, + { + category: 'control text', + pair: '--td-on-ember / brightness(1.1) --ember', + measure: (tokens) => contrast(tokens['--td-on-ember'], brighten(tokens['--ember'], 1.1)) + }, + { + category: 'status text', + pair: '--ember-ink / --ember-tint', + measure: (tokens) => contrast(tokens['--ember-ink'], tokens['--ember-tint']) + } +) + +for (const variant of variants) { + for (const foreground of ['--ink', '--ink-2', '--mute', '--faint']) { + for (const background of ['--paper', '--paper-2', '--paper-card']) { + const ratio = contrast(variant.tokens[foreground], variant.tokens[background]) + if (ratio < 4.5) { + throw new Error(variant.id + ': ' + foreground + ' / ' + background + ' is ' + ratio.toFixed(2)) + } + } + } + for (const foreground of ['--ember', '--applied', '--overdue']) { + const ratio = contrast(variant.tokens[foreground], variant.tokens['--paper-card']) + if (ratio < 4.5) { + throw new Error(variant.id + ': ' + foreground + ' / --paper-card is ' + ratio.toFixed(2)) + } + } +} + +const csvRows = [ + ['category', 'pair', ...variants.map((variant) => variant.label)], + ...pairs.map((pair) => [ + pair.category, + pair.pair, + ...variants.map((variant) => pair.measure(variant.tokens).toFixed(2)) + ]) +] +const csv = csvRows + .map((row) => row.map((value) => '"' + String(value).replaceAll('"', '""') + '"').join(',')) + .join('\n') +writeFileSync(resolve(reportDir, 'contrast-matrix.csv'), csv + '\n') + +const reportPairs = [ + ['Boundary', '--line / --paper-card'], + ['Soft boundary', '--line-soft / --paper-card'], + ['Boundary on substrate', '--line / --paper'], + ['Card step', '--paper-card / --paper'], + ['Card step on paper 2', '--paper-card / --paper-2'], + ['Whisper on card', '--whisper / --paper-card'], + ['Primary ink on substrate', '--ink / --paper'], + ['Primary ink on card', '--ink / --paper-card'], + ['Secondary ink on card', '--ink-2 / --paper-card'], + ['Muted ink on card', '--mute / --paper-card'], + ['Faint ink on card', '--faint / --paper-card'] +] +const header = '| Measure | ' + variants.map((variant) => variant.label).join(' | ') + ' |' +const rule = '| --- | ' + variants.map(() => '---:').join(' | ') + ' |' +const rows = reportPairs.map(([label, key]) => { + const pair = pairs.find((candidate) => candidate.pair === key) + return '| ' + label + ' | ' + variants + .map((variant) => pair.measure(variant.tokens).toFixed(2) + ':1') + .join(' | ') + ' |' +}) + +const summary = [ + '# Measured palette data', + '', + 'Generated by measure-contrast.mjs from the shipped .paper-night token block and [palettes.json](./palettes.json).', + '', + 'The calculation is the same WCAG sRGB relative-luminance formula used by the existing frontend contrast specs. Ratios are rounded to two decimals for presentation; assertions use unrounded values. The 3:1 boundary figure is characterized for comparison and is not treated as an acceptance gate.', + '', + '## Issue measures', + '', + header, + rule, + ...rows, + '', + 'Only --faint changes outside the surface and boundary group, in Open folio and Cedar layers. That small lift keeps faint text at or above 4.5:1 on their brighter surfaces. --ink, the rest of the ink ladder, and every accent and status token remain exactly at the shipped values.', + '', + 'The complete foreground-by-surface matrix, surface hierarchy measures, CTA base and hover checks, and error-tint pairing are in [contrast-matrix.csv](./contrast-matrix.csv).', + '' +].join('\n') +writeFileSync(resolve(reportDir, 'contrast-summary.md'), summary) +console.log('Measured ' + pairs.length + ' pairs across ' + variants.length + ' palettes.') diff --git a/docs/analysis/2026-09-08-night-palette-candidates/palettes.json b/docs/analysis/2026-09-08-night-palette-candidates/palettes.json new file mode 100644 index 000000000..830793efc --- /dev/null +++ b/docs/analysis/2026-09-08-night-palette-candidates/palettes.json @@ -0,0 +1,56 @@ +{ + "source": "frontend/taskdeck-web/src/paper-tokens.css", + "viewport": { "width": 1440, "height": 1000 }, + "variants": [ + { + "id": "baseline", + "label": "Current night", + "intent": "The shipped reference, measured without overrides.", + "overrides": {} + }, + { + "id": "walnut-outline", + "label": "A. Walnut outline", + "intent": "Keep the nearly-black reading field and introduce legible warm boundaries with only a small surface lift.", + "overrides": { + "--paper": "#191510", + "--paper-2": "#15110d", + "--paper-card": "#221d17", + "--paper-edge": "#2a2117", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#504536" + } + }, + { + "id": "open-folio", + "label": "B. Open folio", + "intent": "Lift the substrate around darker recessed cards to reduce page-scale contrast while retaining warm depth.", + "overrides": { + "--paper": "#28221b", + "--paper-2": "#221c16", + "--paper-card": "#1c1813", + "--paper-edge": "#3b2f20", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#655744", + "--faint": "#958a75" + } + }, + { + "id": "cedar-layers", + "label": "C. Cedar layers", + "intent": "Make cards visibly rise from a dark substrate, with the strongest surface and boundary hierarchy of the set.", + "overrides": { + "--paper": "#19140f", + "--paper-2": "#15100c", + "--paper-card": "#28221b", + "--paper-edge": "#332719", + "--line": "#857156", + "--line-soft": "#5d4c37", + "--whisper": "#5f513e", + "--faint": "#958a75" + } + } + ] +} diff --git a/docs/analysis/2026-09-08-night-palette-candidates/rendered-token-measurements.json b/docs/analysis/2026-09-08-night-palette-candidates/rendered-token-measurements.json new file mode 100644 index 000000000..5d45e84f8 --- /dev/null +++ b/docs/analysis/2026-09-08-night-palette-candidates/rendered-token-measurements.json @@ -0,0 +1,1901 @@ +{ + "generatedAt": "2026-09-09T10:53:36.743Z", + "viewport": { + "width": 1440, + "height": 1000 + }, + "syntheticDataset": { + "boardId": "5535988f-e035-4cda-9c25-fa1ac3ec371b", + "captureId": "ab5e897e-15cc-4810-8804-927049df84f7", + "proposalId": "f483a522-49e2-4182-9a47-6aab9dd2e755", + "seed": "1788951206513-317313" + }, + "selectedModes": { + "night": "paper-night", + "lightParity": "paper" + }, + "records": [ + { + "surface": "home", + "surfaceLabel": "Home", + "palette": "light-baseline", + "paletteLabel": "Light baseline parity", + "selectedMode": "paper", + "screenshot": "captures/1440x1000/home/light-baseline.png", + "viewport": { + "width": 1440, + "height": 1000 + }, + "pngDimensions": { + "width": 1440, + "height": 1000 + }, + "visibleTextLength": 1335, + "expectedTokens": { + "--paper": "#f3eee5", + "--paper-2": "#ebe5d8", + "--paper-card": "#fbf7ee", + "--paper-edge": "#e3dac8", + "--line": "#d8d0bf", + "--line-soft": "#e3dcc9", + "--rule": "#1a181410", + "--ink": "#1a1814", + "--ink-deep": "#0a0908", + "--ink-2": "#3a352d", + "--mute": "#635c4e", + "--faint": "#6c6557", + "--whisper": "#c2bba8", + "--ember": "#a8421f", + "--ember-deep": "#7a2e15", + "--ember-bloom": "#a8421f1a", + "--ember-tint": "#f0d9c8", + "--ember-ink": "#6e2810", + "--td-on-ember": "#fefaf6", + "--applied": "#4a6b3f", + "--applied-tint": "#d8e0ce", + "--overdue": "#8c4a26", + "--overdue-tint": "#ecd9c4", + "--awaits": "#6c6557" + }, + "computedTokens": { + "--paper": "#f3eee5", + "--paper-2": "#ebe5d8", + "--paper-card": "#fbf7ee", + "--paper-edge": "#e3dac8", + "--line": "#d8d0bf", + "--line-soft": "#e3dcc9", + "--whisper": "#c2bba8", + "--faint": "#6c6557", + "--ink": "#1a1814", + "--ink-deep": "#0a0908", + "--ink-2": "#3a352d", + "--mute": "#635c4e", + "--ember": "#a8421f", + "--ember-deep": "#7a2e15", + "--ember-bloom": "#a8421f1a", + "--ember-tint": "#f0d9c8", + "--ember-ink": "#6e2810", + "--td-on-ember": "#fefaf6", + "--applied": "#4a6b3f", + "--applied-tint": "#d8e0ce", + "--overdue": "#8c4a26", + "--overdue-tint": "#ecd9c4" + }, + "renderRoot": { + "rootTag": "div", + "rootClasses": "paper-home", + "root": { + "tokens": { + "--paper": "#f3eee5", + "--paper-2": "#ebe5d8", + "--paper-card": "#fbf7ee", + "--paper-edge": "#e3dac8", + "--line": "#d8d0bf", + "--line-soft": "#e3dcc9", + "--whisper": "#c2bba8", + "--faint": "#6c6557", + "--ink": "#1a1814", + "--ink-deep": "#0a0908", + "--ink-2": "#3a352d", + "--mute": "#635c4e", + "--ember": "#a8421f", + "--ember-deep": "#7a2e15", + "--ember-bloom": "#a8421f1a", + "--ember-tint": "#f0d9c8", + "--ember-ink": "#6e2810", + "--td-on-ember": "#fefaf6", + "--applied": "#4a6b3f", + "--applied-tint": "#d8e0ce", + "--overdue": "#8c4a26", + "--overdue-tint": "#ecd9c4" + }, + "backgroundColor": "rgba(0, 0, 0, 0)" + }, + "sampleCard": { + "tokens": { + "--paper": "#f3eee5", + "--paper-2": "#ebe5d8", + "--paper-card": "#fbf7ee", + "--paper-edge": "#e3dac8", + "--line": "#d8d0bf", + "--line-soft": "#e3dcc9", + "--whisper": "#c2bba8", + "--faint": "#6c6557", + "--ink": "#1a1814", + "--ink-deep": "#0a0908", + "--ink-2": "#3a352d", + "--mute": "#635c4e", + "--ember": "#a8421f", + "--ember-deep": "#7a2e15", + "--ember-bloom": "#a8421f1a", + "--ember-tint": "#f0d9c8", + "--ember-ink": "#6e2810", + "--td-on-ember": "#fefaf6", + "--applied": "#4a6b3f", + "--applied-tint": "#d8e0ce", + "--overdue": "#8c4a26", + "--overdue-tint": "#ecd9c4" + }, + "backgroundColor": "rgb(251, 247, 238)" + } + }, + "mismatches": [], + "rootMismatches": [], + "parityFinding": null + }, + { + "surface": "today", + "surfaceLabel": "Today", + "palette": "light-baseline", + "paletteLabel": "Light baseline parity", + "selectedMode": "paper", + "screenshot": "captures/1440x1000/today/light-baseline.png", + "viewport": { + "width": 1440, + "height": 1000 + }, + "pngDimensions": { + "width": 1440, + "height": 1000 + }, + "visibleTextLength": 2096, + "expectedTokens": { + "--paper": "#f3eee5", + "--paper-2": "#ebe5d8", + "--paper-card": "#fbf7ee", + "--paper-edge": "#e3dac8", + "--line": "#d8d0bf", + "--line-soft": "#e3dcc9", + "--rule": "#1a181410", + "--ink": "#1a1814", + "--ink-deep": "#0a0908", + "--ink-2": "#3a352d", + "--mute": "#635c4e", + "--faint": "#6c6557", + "--whisper": "#c2bba8", + "--ember": "#a8421f", + "--ember-deep": "#7a2e15", + "--ember-bloom": "#a8421f1a", + "--ember-tint": "#f0d9c8", + "--ember-ink": "#6e2810", + "--td-on-ember": "#fefaf6", + "--applied": "#4a6b3f", + "--applied-tint": "#d8e0ce", + "--overdue": "#8c4a26", + "--overdue-tint": "#ecd9c4", + "--awaits": "#6c6557" + }, + "computedTokens": { + "--paper": "#f3eee5", + "--paper-2": "#ebe5d8", + "--paper-card": "#fbf7ee", + "--paper-edge": "#e3dac8", + "--line": "#d8d0bf", + "--line-soft": "#e3dcc9", + "--whisper": "#c2bba8", + "--faint": "#6c6557", + "--ink": "#1a1814", + "--ink-deep": "#0a0908", + "--ink-2": "#3a352d", + "--mute": "#635c4e", + "--ember": "#a8421f", + "--ember-deep": "#7a2e15", + "--ember-bloom": "#a8421f1a", + "--ember-tint": "#f0d9c8", + "--ember-ink": "#6e2810", + "--td-on-ember": "#fefaf6", + "--applied": "#4a6b3f", + "--applied-tint": "#d8e0ce", + "--overdue": "#8c4a26", + "--overdue-tint": "#ecd9c4" + }, + "renderRoot": { + "rootTag": "div", + "rootClasses": "paper-today", + "root": { + "tokens": { + "--paper": "#f3eee5", + "--paper-2": "#ebe5d8", + "--paper-card": "#fbf7ee", + "--paper-edge": "#e3dac8", + "--line": "#d8d0bf", + "--line-soft": "#e3dcc9", + "--whisper": "#c2bba8", + "--faint": "#6c6557", + "--ink": "#1a1814", + "--ink-deep": "#0a0908", + "--ink-2": "#3a352d", + "--mute": "#635c4e", + "--ember": "#a8421f", + "--ember-deep": "#7a2e15", + "--ember-bloom": "#a8421f1a", + "--ember-tint": "#f0d9c8", + "--ember-ink": "#6e2810", + "--td-on-ember": "#fefaf6", + "--applied": "#4a6b3f", + "--applied-tint": "#d8e0ce", + "--overdue": "#8c4a26", + "--overdue-tint": "#ecd9c4" + }, + "backgroundColor": "rgb(243, 238, 229)" + }, + "sampleCard": { + "tokens": { + "--paper": "#f3eee5", + "--paper-2": "#ebe5d8", + "--paper-card": "#fbf7ee", + "--paper-edge": "#e3dac8", + "--line": "#d8d0bf", + "--line-soft": "#e3dcc9", + "--whisper": "#c2bba8", + "--faint": "#6c6557", + "--ink": "#1a1814", + "--ink-deep": "#0a0908", + "--ink-2": "#3a352d", + "--mute": "#635c4e", + "--ember": "#a8421f", + "--ember-deep": "#7a2e15", + "--ember-bloom": "#a8421f1a", + "--ember-tint": "#f0d9c8", + "--ember-ink": "#6e2810", + "--td-on-ember": "#fefaf6", + "--applied": "#4a6b3f", + "--applied-tint": "#d8e0ce", + "--overdue": "#8c4a26", + "--overdue-tint": "#ecd9c4" + }, + "backgroundColor": "rgb(251, 247, 238)" + } + }, + "mismatches": [], + "rootMismatches": [], + "parityFinding": null + }, + { + "surface": "review", + "surfaceLabel": "Review", + "palette": "light-baseline", + "paletteLabel": "Light baseline parity", + "selectedMode": "paper", + "screenshot": "captures/1440x1000/review/light-baseline.png", + "viewport": { + "width": 1440, + "height": 1000 + }, + "pngDimensions": { + "width": 1440, + "height": 1000 + }, + "visibleTextLength": 3186, + "expectedTokens": { + "--paper": "#f3eee5", + "--paper-2": "#ebe5d8", + "--paper-card": "#fbf7ee", + "--paper-edge": "#e3dac8", + "--line": "#d8d0bf", + "--line-soft": "#e3dcc9", + "--rule": "#1a181410", + "--ink": "#1a1814", + "--ink-deep": "#0a0908", + "--ink-2": "#3a352d", + "--mute": "#635c4e", + "--faint": "#6c6557", + "--whisper": "#c2bba8", + "--ember": "#a8421f", + "--ember-deep": "#7a2e15", + "--ember-bloom": "#a8421f1a", + "--ember-tint": "#f0d9c8", + "--ember-ink": "#6e2810", + "--td-on-ember": "#fefaf6", + "--applied": "#4a6b3f", + "--applied-tint": "#d8e0ce", + "--overdue": "#8c4a26", + "--overdue-tint": "#ecd9c4", + "--awaits": "#6c6557" + }, + "computedTokens": { + "--paper": "#f3eee5", + "--paper-2": "#ebe5d8", + "--paper-card": "#fbf7ee", + "--paper-edge": "#e3dac8", + "--line": "#d8d0bf", + "--line-soft": "#e3dcc9", + "--whisper": "#c2bba8", + "--faint": "#6c6557", + "--ink": "#1a1814", + "--ink-deep": "#0a0908", + "--ink-2": "#3a352d", + "--mute": "#635c4e", + "--ember": "#a8421f", + "--ember-deep": "#7a2e15", + "--ember-bloom": "#a8421f1a", + "--ember-tint": "#f0d9c8", + "--ember-ink": "#6e2810", + "--td-on-ember": "#fefaf6", + "--applied": "#4a6b3f", + "--applied-tint": "#d8e0ce", + "--overdue": "#8c4a26", + "--overdue-tint": "#ecd9c4" + }, + "renderRoot": { + "rootTag": "div", + "rootClasses": "paper paper-review-deep", + "root": { + "tokens": { + "--paper": "#f3eee5", + "--paper-2": "#ebe5d8", + "--paper-card": "#fbf7ee", + "--paper-edge": "#e3dac8", + "--line": "#d8d0bf", + "--line-soft": "#e3dcc9", + "--whisper": "#c2bba8", + "--faint": "#6c6557", + "--ink": "#1a1814", + "--ink-deep": "#0a0908", + "--ink-2": "#3a352d", + "--mute": "#635c4e", + "--ember": "#a8421f", + "--ember-deep": "#7a2e15", + "--ember-bloom": "#a8421f1a", + "--ember-tint": "#f0d9c8", + "--ember-ink": "#6e2810", + "--td-on-ember": "#fefaf6", + "--applied": "#4a6b3f", + "--applied-tint": "#d8e0ce", + "--overdue": "#8c4a26", + "--overdue-tint": "#ecd9c4" + }, + "backgroundColor": "rgb(243, 238, 229)" + }, + "sampleCard": null + }, + "mismatches": [], + "rootMismatches": [], + "parityFinding": null + }, + { + "surface": "home", + "surfaceLabel": "Home", + "palette": "baseline", + "paletteLabel": "Current night", + "selectedMode": "paper-night", + "screenshot": "captures/1440x1000/home/baseline.png", + "viewport": { + "width": 1440, + "height": 1000 + }, + "pngDimensions": { + "width": 1440, + "height": 1000 + }, + "visibleTextLength": 1335, + "expectedTokens": { + "--paper": "#14110d", + "--paper-2": "#100d0a", + "--paper-card": "#1c1813", + "--paper-edge": "#20180e", + "--line": "#2a241c", + "--line-soft": "#221c14", + "--rule": "#ffffff0a", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--faint": "#8f846f", + "--whisper": "#3e362a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "computedTokens": { + "--paper": "#14110d", + "--paper-2": "#100d0a", + "--paper-card": "#1c1813", + "--paper-edge": "#20180e", + "--line": "#2a241c", + "--line-soft": "#221c14", + "--whisper": "#3e362a", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "renderRoot": { + "rootTag": "div", + "rootClasses": "paper-home", + "root": { + "tokens": { + "--paper": "#14110d", + "--paper-2": "#100d0a", + "--paper-card": "#1c1813", + "--paper-edge": "#20180e", + "--line": "#2a241c", + "--line-soft": "#221c14", + "--whisper": "#3e362a", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgba(0, 0, 0, 0)" + }, + "sampleCard": { + "tokens": { + "--paper": "#14110d", + "--paper-2": "#100d0a", + "--paper-card": "#1c1813", + "--paper-edge": "#20180e", + "--line": "#2a241c", + "--line-soft": "#221c14", + "--whisper": "#3e362a", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(28, 24, 19)" + } + }, + "mismatches": [], + "rootMismatches": [], + "parityFinding": null + }, + { + "surface": "today", + "surfaceLabel": "Today", + "palette": "baseline", + "paletteLabel": "Current night", + "selectedMode": "paper-night", + "screenshot": "captures/1440x1000/today/baseline.png", + "viewport": { + "width": 1440, + "height": 1000 + }, + "pngDimensions": { + "width": 1440, + "height": 1000 + }, + "visibleTextLength": 2096, + "expectedTokens": { + "--paper": "#14110d", + "--paper-2": "#100d0a", + "--paper-card": "#1c1813", + "--paper-edge": "#20180e", + "--line": "#2a241c", + "--line-soft": "#221c14", + "--rule": "#ffffff0a", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--faint": "#8f846f", + "--whisper": "#3e362a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "computedTokens": { + "--paper": "#14110d", + "--paper-2": "#100d0a", + "--paper-card": "#1c1813", + "--paper-edge": "#20180e", + "--line": "#2a241c", + "--line-soft": "#221c14", + "--whisper": "#3e362a", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "renderRoot": { + "rootTag": "div", + "rootClasses": "paper-today", + "root": { + "tokens": { + "--paper": "#14110d", + "--paper-2": "#100d0a", + "--paper-card": "#1c1813", + "--paper-edge": "#20180e", + "--line": "#2a241c", + "--line-soft": "#221c14", + "--whisper": "#3e362a", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(20, 17, 13)" + }, + "sampleCard": { + "tokens": { + "--paper": "#14110d", + "--paper-2": "#100d0a", + "--paper-card": "#1c1813", + "--paper-edge": "#20180e", + "--line": "#2a241c", + "--line-soft": "#221c14", + "--whisper": "#3e362a", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(28, 24, 19)" + } + }, + "mismatches": [], + "rootMismatches": [], + "parityFinding": null + }, + { + "surface": "review", + "surfaceLabel": "Review", + "palette": "baseline", + "paletteLabel": "Current night", + "selectedMode": "paper-night", + "screenshot": "captures/1440x1000/review/baseline.png", + "viewport": { + "width": 1440, + "height": 1000 + }, + "pngDimensions": { + "width": 1440, + "height": 1000 + }, + "visibleTextLength": 3186, + "expectedTokens": { + "--paper": "#14110d", + "--paper-2": "#100d0a", + "--paper-card": "#1c1813", + "--paper-edge": "#20180e", + "--line": "#2a241c", + "--line-soft": "#221c14", + "--rule": "#ffffff0a", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--faint": "#8f846f", + "--whisper": "#3e362a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "computedTokens": { + "--paper": "#14110d", + "--paper-2": "#100d0a", + "--paper-card": "#1c1813", + "--paper-edge": "#20180e", + "--line": "#2a241c", + "--line-soft": "#221c14", + "--whisper": "#3e362a", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "renderRoot": { + "rootTag": "div", + "rootClasses": "paper paper-review-deep", + "root": { + "tokens": { + "--paper": "#f3eee5", + "--paper-2": "#ebe5d8", + "--paper-card": "#fbf7ee", + "--paper-edge": "#e3dac8", + "--line": "#d8d0bf", + "--line-soft": "#e3dcc9", + "--whisper": "#c2bba8", + "--faint": "#6c6557", + "--ink": "#1a1814", + "--ink-deep": "#0a0908", + "--ink-2": "#3a352d", + "--mute": "#635c4e", + "--ember": "#a8421f", + "--ember-deep": "#7a2e15", + "--ember-bloom": "#a8421f1a", + "--ember-tint": "#f0d9c8", + "--ember-ink": "#6e2810", + "--td-on-ember": "#fefaf6", + "--applied": "#4a6b3f", + "--applied-tint": "#d8e0ce", + "--overdue": "#8c4a26", + "--overdue-tint": "#ecd9c4" + }, + "backgroundColor": "rgb(243, 238, 229)" + }, + "sampleCard": { + "tokens": { + "--paper": "#f3eee5", + "--paper-2": "#ebe5d8", + "--paper-card": "#fbf7ee", + "--paper-edge": "#e3dac8", + "--line": "#d8d0bf", + "--line-soft": "#e3dcc9", + "--whisper": "#c2bba8", + "--faint": "#6c6557", + "--ink": "#1a1814", + "--ink-deep": "#0a0908", + "--ink-2": "#3a352d", + "--mute": "#635c4e", + "--ember": "#a8421f", + "--ember-deep": "#7a2e15", + "--ember-bloom": "#a8421f1a", + "--ember-tint": "#f0d9c8", + "--ember-ink": "#6e2810", + "--td-on-ember": "#fefaf6", + "--applied": "#4a6b3f", + "--applied-tint": "#d8e0ce", + "--overdue": "#8c4a26", + "--overdue-tint": "#ecd9c4" + }, + "backgroundColor": "rgb(251, 247, 238)" + } + }, + "mismatches": [], + "rootMismatches": [], + "parityFinding": "Shipped Review root remains .paper/light while body selection is paper-night; candidate rows apply an ephemeral night token set to the real .paper root." + }, + { + "surface": "home", + "surfaceLabel": "Home", + "palette": "walnut-outline", + "paletteLabel": "A. Walnut outline", + "selectedMode": "paper-night", + "screenshot": "captures/1440x1000/home/walnut-outline.png", + "viewport": { + "width": 1440, + "height": 1000 + }, + "pngDimensions": { + "width": 1440, + "height": 1000 + }, + "visibleTextLength": 1335, + "expectedTokens": { + "--paper": "#191510", + "--paper-2": "#15110d", + "--paper-card": "#221d17", + "--paper-edge": "#2a2117", + "--line": "#786750", + "--line-soft": "#50422f", + "--rule": "#ffffff0a", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--faint": "#8f846f", + "--whisper": "#504536", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "computedTokens": { + "--paper": "#191510", + "--paper-2": "#15110d", + "--paper-card": "#221d17", + "--paper-edge": "#2a2117", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#504536", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "renderRoot": { + "rootTag": "div", + "rootClasses": "paper-home", + "root": { + "tokens": { + "--paper": "#191510", + "--paper-2": "#15110d", + "--paper-card": "#221d17", + "--paper-edge": "#2a2117", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#504536", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgba(0, 0, 0, 0)" + }, + "sampleCard": { + "tokens": { + "--paper": "#191510", + "--paper-2": "#15110d", + "--paper-card": "#221d17", + "--paper-edge": "#2a2117", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#504536", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(34, 29, 23)" + } + }, + "mismatches": [], + "rootMismatches": [], + "parityFinding": null + }, + { + "surface": "today", + "surfaceLabel": "Today", + "palette": "walnut-outline", + "paletteLabel": "A. Walnut outline", + "selectedMode": "paper-night", + "screenshot": "captures/1440x1000/today/walnut-outline.png", + "viewport": { + "width": 1440, + "height": 1000 + }, + "pngDimensions": { + "width": 1440, + "height": 1000 + }, + "visibleTextLength": 2096, + "expectedTokens": { + "--paper": "#191510", + "--paper-2": "#15110d", + "--paper-card": "#221d17", + "--paper-edge": "#2a2117", + "--line": "#786750", + "--line-soft": "#50422f", + "--rule": "#ffffff0a", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--faint": "#8f846f", + "--whisper": "#504536", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "computedTokens": { + "--paper": "#191510", + "--paper-2": "#15110d", + "--paper-card": "#221d17", + "--paper-edge": "#2a2117", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#504536", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "renderRoot": { + "rootTag": "div", + "rootClasses": "paper-today", + "root": { + "tokens": { + "--paper": "#191510", + "--paper-2": "#15110d", + "--paper-card": "#221d17", + "--paper-edge": "#2a2117", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#504536", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(25, 21, 16)" + }, + "sampleCard": { + "tokens": { + "--paper": "#191510", + "--paper-2": "#15110d", + "--paper-card": "#221d17", + "--paper-edge": "#2a2117", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#504536", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(34, 29, 23)" + } + }, + "mismatches": [], + "rootMismatches": [], + "parityFinding": null + }, + { + "surface": "review", + "surfaceLabel": "Review", + "palette": "walnut-outline", + "paletteLabel": "A. Walnut outline", + "selectedMode": "paper-night", + "screenshot": "captures/1440x1000/review/walnut-outline.png", + "viewport": { + "width": 1440, + "height": 1000 + }, + "pngDimensions": { + "width": 1440, + "height": 1000 + }, + "visibleTextLength": 3186, + "expectedTokens": { + "--paper": "#191510", + "--paper-2": "#15110d", + "--paper-card": "#221d17", + "--paper-edge": "#2a2117", + "--line": "#786750", + "--line-soft": "#50422f", + "--rule": "#ffffff0a", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--faint": "#8f846f", + "--whisper": "#504536", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "computedTokens": { + "--paper": "#191510", + "--paper-2": "#15110d", + "--paper-card": "#221d17", + "--paper-edge": "#2a2117", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#504536", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "renderRoot": { + "rootTag": "div", + "rootClasses": "paper paper-review-deep", + "root": { + "tokens": { + "--paper": "#191510", + "--paper-2": "#15110d", + "--paper-card": "#221d17", + "--paper-edge": "#2a2117", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#504536", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(25, 21, 16)" + }, + "sampleCard": { + "tokens": { + "--paper": "#191510", + "--paper-2": "#15110d", + "--paper-card": "#221d17", + "--paper-edge": "#2a2117", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#504536", + "--faint": "#8f846f", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(34, 29, 23)" + } + }, + "mismatches": [], + "rootMismatches": [], + "parityFinding": null + }, + { + "surface": "home", + "surfaceLabel": "Home", + "palette": "open-folio", + "paletteLabel": "B. Open folio", + "selectedMode": "paper-night", + "screenshot": "captures/1440x1000/home/open-folio.png", + "viewport": { + "width": 1440, + "height": 1000 + }, + "pngDimensions": { + "width": 1440, + "height": 1000 + }, + "visibleTextLength": 1335, + "expectedTokens": { + "--paper": "#28221b", + "--paper-2": "#221c16", + "--paper-card": "#1c1813", + "--paper-edge": "#3b2f20", + "--line": "#786750", + "--line-soft": "#50422f", + "--rule": "#ffffff0a", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--faint": "#958a75", + "--whisper": "#655744", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "computedTokens": { + "--paper": "#28221b", + "--paper-2": "#221c16", + "--paper-card": "#1c1813", + "--paper-edge": "#3b2f20", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#655744", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "renderRoot": { + "rootTag": "div", + "rootClasses": "paper-home", + "root": { + "tokens": { + "--paper": "#28221b", + "--paper-2": "#221c16", + "--paper-card": "#1c1813", + "--paper-edge": "#3b2f20", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#655744", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgba(0, 0, 0, 0)" + }, + "sampleCard": { + "tokens": { + "--paper": "#28221b", + "--paper-2": "#221c16", + "--paper-card": "#1c1813", + "--paper-edge": "#3b2f20", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#655744", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(28, 24, 19)" + } + }, + "mismatches": [], + "rootMismatches": [], + "parityFinding": null + }, + { + "surface": "today", + "surfaceLabel": "Today", + "palette": "open-folio", + "paletteLabel": "B. Open folio", + "selectedMode": "paper-night", + "screenshot": "captures/1440x1000/today/open-folio.png", + "viewport": { + "width": 1440, + "height": 1000 + }, + "pngDimensions": { + "width": 1440, + "height": 1000 + }, + "visibleTextLength": 2096, + "expectedTokens": { + "--paper": "#28221b", + "--paper-2": "#221c16", + "--paper-card": "#1c1813", + "--paper-edge": "#3b2f20", + "--line": "#786750", + "--line-soft": "#50422f", + "--rule": "#ffffff0a", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--faint": "#958a75", + "--whisper": "#655744", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "computedTokens": { + "--paper": "#28221b", + "--paper-2": "#221c16", + "--paper-card": "#1c1813", + "--paper-edge": "#3b2f20", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#655744", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "renderRoot": { + "rootTag": "div", + "rootClasses": "paper-today", + "root": { + "tokens": { + "--paper": "#28221b", + "--paper-2": "#221c16", + "--paper-card": "#1c1813", + "--paper-edge": "#3b2f20", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#655744", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(40, 34, 27)" + }, + "sampleCard": { + "tokens": { + "--paper": "#28221b", + "--paper-2": "#221c16", + "--paper-card": "#1c1813", + "--paper-edge": "#3b2f20", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#655744", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(28, 24, 19)" + } + }, + "mismatches": [], + "rootMismatches": [], + "parityFinding": null + }, + { + "surface": "review", + "surfaceLabel": "Review", + "palette": "open-folio", + "paletteLabel": "B. Open folio", + "selectedMode": "paper-night", + "screenshot": "captures/1440x1000/review/open-folio.png", + "viewport": { + "width": 1440, + "height": 1000 + }, + "pngDimensions": { + "width": 1440, + "height": 1000 + }, + "visibleTextLength": 3186, + "expectedTokens": { + "--paper": "#28221b", + "--paper-2": "#221c16", + "--paper-card": "#1c1813", + "--paper-edge": "#3b2f20", + "--line": "#786750", + "--line-soft": "#50422f", + "--rule": "#ffffff0a", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--faint": "#958a75", + "--whisper": "#655744", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "computedTokens": { + "--paper": "#28221b", + "--paper-2": "#221c16", + "--paper-card": "#1c1813", + "--paper-edge": "#3b2f20", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#655744", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "renderRoot": { + "rootTag": "div", + "rootClasses": "paper paper-review-deep", + "root": { + "tokens": { + "--paper": "#28221b", + "--paper-2": "#221c16", + "--paper-card": "#1c1813", + "--paper-edge": "#3b2f20", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#655744", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(40, 34, 27)" + }, + "sampleCard": { + "tokens": { + "--paper": "#28221b", + "--paper-2": "#221c16", + "--paper-card": "#1c1813", + "--paper-edge": "#3b2f20", + "--line": "#786750", + "--line-soft": "#50422f", + "--whisper": "#655744", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(28, 24, 19)" + } + }, + "mismatches": [], + "rootMismatches": [], + "parityFinding": null + }, + { + "surface": "home", + "surfaceLabel": "Home", + "palette": "cedar-layers", + "paletteLabel": "C. Cedar layers", + "selectedMode": "paper-night", + "screenshot": "captures/1440x1000/home/cedar-layers.png", + "viewport": { + "width": 1440, + "height": 1000 + }, + "pngDimensions": { + "width": 1440, + "height": 1000 + }, + "visibleTextLength": 1335, + "expectedTokens": { + "--paper": "#19140f", + "--paper-2": "#15100c", + "--paper-card": "#28221b", + "--paper-edge": "#332719", + "--line": "#857156", + "--line-soft": "#5d4c37", + "--rule": "#ffffff0a", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--faint": "#958a75", + "--whisper": "#5f513e", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "computedTokens": { + "--paper": "#19140f", + "--paper-2": "#15100c", + "--paper-card": "#28221b", + "--paper-edge": "#332719", + "--line": "#857156", + "--line-soft": "#5d4c37", + "--whisper": "#5f513e", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "renderRoot": { + "rootTag": "div", + "rootClasses": "paper-home", + "root": { + "tokens": { + "--paper": "#19140f", + "--paper-2": "#15100c", + "--paper-card": "#28221b", + "--paper-edge": "#332719", + "--line": "#857156", + "--line-soft": "#5d4c37", + "--whisper": "#5f513e", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgba(0, 0, 0, 0)" + }, + "sampleCard": { + "tokens": { + "--paper": "#19140f", + "--paper-2": "#15100c", + "--paper-card": "#28221b", + "--paper-edge": "#332719", + "--line": "#857156", + "--line-soft": "#5d4c37", + "--whisper": "#5f513e", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(40, 34, 27)" + } + }, + "mismatches": [], + "rootMismatches": [], + "parityFinding": null + }, + { + "surface": "today", + "surfaceLabel": "Today", + "palette": "cedar-layers", + "paletteLabel": "C. Cedar layers", + "selectedMode": "paper-night", + "screenshot": "captures/1440x1000/today/cedar-layers.png", + "viewport": { + "width": 1440, + "height": 1000 + }, + "pngDimensions": { + "width": 1440, + "height": 1000 + }, + "visibleTextLength": 2096, + "expectedTokens": { + "--paper": "#19140f", + "--paper-2": "#15100c", + "--paper-card": "#28221b", + "--paper-edge": "#332719", + "--line": "#857156", + "--line-soft": "#5d4c37", + "--rule": "#ffffff0a", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--faint": "#958a75", + "--whisper": "#5f513e", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "computedTokens": { + "--paper": "#19140f", + "--paper-2": "#15100c", + "--paper-card": "#28221b", + "--paper-edge": "#332719", + "--line": "#857156", + "--line-soft": "#5d4c37", + "--whisper": "#5f513e", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "renderRoot": { + "rootTag": "div", + "rootClasses": "paper-today", + "root": { + "tokens": { + "--paper": "#19140f", + "--paper-2": "#15100c", + "--paper-card": "#28221b", + "--paper-edge": "#332719", + "--line": "#857156", + "--line-soft": "#5d4c37", + "--whisper": "#5f513e", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(25, 20, 15)" + }, + "sampleCard": { + "tokens": { + "--paper": "#19140f", + "--paper-2": "#15100c", + "--paper-card": "#28221b", + "--paper-edge": "#332719", + "--line": "#857156", + "--line-soft": "#5d4c37", + "--whisper": "#5f513e", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(40, 34, 27)" + } + }, + "mismatches": [], + "rootMismatches": [], + "parityFinding": null + }, + { + "surface": "review", + "surfaceLabel": "Review", + "palette": "cedar-layers", + "paletteLabel": "C. Cedar layers", + "selectedMode": "paper-night", + "screenshot": "captures/1440x1000/review/cedar-layers.png", + "viewport": { + "width": 1440, + "height": 1000 + }, + "pngDimensions": { + "width": 1440, + "height": 1000 + }, + "visibleTextLength": 3186, + "expectedTokens": { + "--paper": "#19140f", + "--paper-2": "#15100c", + "--paper-card": "#28221b", + "--paper-edge": "#332719", + "--line": "#857156", + "--line-soft": "#5d4c37", + "--rule": "#ffffff0a", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--faint": "#958a75", + "--whisper": "#5f513e", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "computedTokens": { + "--paper": "#19140f", + "--paper-2": "#15100c", + "--paper-card": "#28221b", + "--paper-edge": "#332719", + "--line": "#857156", + "--line-soft": "#5d4c37", + "--whisper": "#5f513e", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "renderRoot": { + "rootTag": "div", + "rootClasses": "paper paper-review-deep", + "root": { + "tokens": { + "--paper": "#19140f", + "--paper-2": "#15100c", + "--paper-card": "#28221b", + "--paper-edge": "#332719", + "--line": "#857156", + "--line-soft": "#5d4c37", + "--whisper": "#5f513e", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(25, 20, 15)" + }, + "sampleCard": { + "tokens": { + "--paper": "#19140f", + "--paper-2": "#15100c", + "--paper-card": "#28221b", + "--paper-edge": "#332719", + "--line": "#857156", + "--line-soft": "#5d4c37", + "--whisper": "#5f513e", + "--faint": "#958a75", + "--ink": "#ece4d3", + "--ink-deep": "#fbf3df", + "--ink-2": "#c8bfa9", + "--mute": "#9a8f7a", + "--ember": "#d96a3e", + "--ember-deep": "#b54e26", + "--ember-bloom": "#d96a3e22", + "--ember-tint": "#2d1a12", + "--ember-ink": "#ffb38c", + "--td-on-ember": "#1c0d04", + "--applied": "#8aae72", + "--applied-tint": "#1d2417", + "--overdue": "#d09257", + "--overdue-tint": "#2a1f12" + }, + "backgroundColor": "rgb(40, 34, 27)" + } + }, + "mismatches": [], + "rootMismatches": [], + "parityFinding": null + } + ] +} diff --git a/frontend/taskdeck-web/src/paper-tokens.css b/frontend/taskdeck-web/src/paper-tokens.css index be216bae8..8c5c49ffd 100644 --- a/frontend/taskdeck-web/src/paper-tokens.css +++ b/frontend/taskdeck-web/src/paper-tokens.css @@ -65,42 +65,12 @@ --sans: 'Inter', -apple-system, system-ui, sans-serif; --mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace; - /* --- Scale --- */ - --t-xs: 10.5px; - --t-sm: 12px; - --t-md: 13.5px; - --t-bd: 15px; - --t-lg: 18px; - --t-h3: 22px; - --t-h2: 32px; - --t-h1: 44px; - --t-display: 64px; - - /* --- Spacing & radius --- */ - --r-0: 0px; - --r-1: 2px; - --r-2: 4px; - --r-3: 6px; - --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px; - --s-5: 20px; --s-6: 24px; --s-8: 32px; --s-10: 40px; --s-12: 56px; - /* --- Shadows --- */ --shadow-card: 0 1px 0 var(--line), 0 .5px 0 #1a18140d; --shadow-lift: 0 .5px 0 #1a18140f, 0 6px 14px -8px #1a181430, 0 1.5px 0 var(--line); --shadow-stamp: 0 2px 0 var(--line); --shadow-press: inset 0 1px 0 #1a181410; - /* --- Motion --- */ - --ease-paper: cubic-bezier(.2, .65, .25, 1); - --ease-press: cubic-bezier(.4, 0, .15, 1); - --d-quick: 140ms; - --d-base: 240ms; - --d-press: 320ms; - --d-amb: 880ms; - - /* --- Narrow scaling (PAPER-11 hook) --- */ - --paper-scale: 1; - color-scheme: light; } @@ -145,6 +115,39 @@ color-scheme: dark; } +/* Scale, spacing, radius and motion stay the same in both Paper themes. */ +.paper, .paper-night { + /* --- Scale --- */ + --t-xs: 10.5px; + --t-sm: 12px; + --t-md: 13.5px; + --t-bd: 15px; + --t-lg: 18px; + --t-h3: 22px; + --t-h2: 32px; + --t-h1: 44px; + --t-display: 64px; + + /* --- Spacing & radius --- */ + --r-0: 0px; + --r-1: 2px; + --r-2: 4px; + --r-3: 6px; + --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px; + --s-5: 20px; --s-6: 24px; --s-8: 32px; --s-10: 40px; --s-12: 56px; + + /* --- Motion --- */ + --ease-paper: cubic-bezier(.2, .65, .25, 1); + --ease-press: cubic-bezier(.4, 0, .15, 1); + --d-quick: 140ms; + --d-base: 240ms; + --d-press: 320ms; + --d-amb: 880ms; + + /* --- Narrow scaling (PAPER-11 hook) --- */ + --paper-scale: 1; +} + /* ========================================================= Paper substrate — fiber texture (light + night) ========================================================= */ diff --git a/frontend/taskdeck-web/src/views/paper/PaperReviewView.vue b/frontend/taskdeck-web/src/views/paper/PaperReviewView.vue index 48b25ee30..dc87548cb 100644 --- a/frontend/taskdeck-web/src/views/paper/PaperReviewView.vue +++ b/frontend/taskdeck-web/src/views/paper/PaperReviewView.vue @@ -2717,7 +2717,7 @@ async function onClearBoardScope() {