Files
PaperClipAI/ui/public/document-highlight-exploration.html

1147 lines
52 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="dark" />
<title>Document highlight exploration</title>
<style>
@font-face {
font-family: "InterVariable";
src: url("/fonts/InterVariable.woff2") format("woff2");
font-style: normal;
font-weight: 100 900;
font-display: swap;
}
:root {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--muted-foreground: oklch(0.708 0 0);
--border: oklch(1 0 0 / 10%);
--subtle: oklch(1 0 0 / 6%);
--subtle-hover: oklch(1 0 0 / 9%);
--focus: oklch(0.72 0.15 282);
--reading-width: 32rem;
}
* { box-sizing: border-box; }
html {
background: var(--background);
color: var(--foreground);
font-family: InterVariable, Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
body {
margin: 0;
min-width: 20rem;
background: var(--background);
color: var(--foreground);
}
button { font: inherit; }
.page {
width: min(100%, 90rem);
margin: 0 auto;
padding: 3rem clamp(1.25rem, 4vw, 4rem) 6rem;
}
.eyebrow {
margin: 0 0 0.75rem;
color: var(--muted-foreground);
font-size: 0.75rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.hero {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(18rem, 26rem);
gap: 3rem;
align-items: end;
padding-bottom: 2rem;
border-bottom: 1px solid var(--border);
}
.hero h1 {
max-width: 46rem;
margin: 0;
font-size: clamp(2rem, 4vw, 3.5rem);
font-weight: 600;
letter-spacing: -0.035em;
line-height: 1.05;
}
.hero p {
max-width: 43rem;
margin: 1.25rem 0 0;
color: var(--muted-foreground);
font-size: 0.9375rem;
line-height: 1.5rem;
}
.fixed-tokens {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.75rem;
margin: 0;
}
.fixed-tokens div {
min-width: 0;
padding: 0.75rem 0.875rem;
border: 1px solid var(--border);
border-radius: 0.5rem;
background: var(--subtle);
}
.fixed-tokens dt {
margin: 0 0 0.25rem;
color: var(--muted-foreground);
font-size: 0.6875rem;
font-weight: 600;
letter-spacing: 0.05em;
text-transform: uppercase;
}
.fixed-tokens dd {
margin: 0;
overflow: hidden;
font-size: 0.8125rem;
line-height: 1.125rem;
text-overflow: ellipsis;
white-space: nowrap;
}
.toolbar {
position: sticky;
top: 0;
z-index: 10;
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: center;
justify-content: space-between;
margin: 0 -0.75rem;
padding: 1rem 0.75rem;
border-bottom: 1px solid var(--border);
background: color-mix(in oklab, var(--background) 94%, transparent);
-webkit-backdrop-filter: blur(14px);
backdrop-filter: blur(14px);
}
.toolbar-group {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
}
.toolbar-label,
.shortlist {
color: var(--muted-foreground);
font-size: 0.75rem;
line-height: 1rem;
}
.segment {
display: inline-flex;
gap: 0.1875rem;
padding: 0.1875rem;
border: 1px solid var(--border);
border-radius: 0.5rem;
background: var(--subtle);
}
.segment button,
.copy-button,
.pick {
border: 0;
color: var(--muted-foreground);
background: transparent;
cursor: pointer;
}
.segment button {
min-height: 1.75rem;
padding: 0.25rem 0.625rem;
border-radius: 0.3125rem;
font-size: 0.75rem;
font-weight: 500;
}
.segment button:hover,
.copy-button:hover,
.pick:hover { color: var(--foreground); background: var(--subtle-hover); }
.segment button[aria-pressed="true"] {
color: var(--foreground);
background: oklch(1 0 0 / 11%);
box-shadow: 0 1px 0 oklch(0 0 0 / 28%);
}
.copy-button {
min-height: 1.75rem;
padding: 0.25rem 0.625rem;
border: 1px solid var(--border);
border-radius: 0.375rem;
font-size: 0.75rem;
}
.intro-note {
max-width: 56rem;
margin: 2.25rem 0 0;
color: var(--muted-foreground);
font-size: 0.8125rem;
line-height: 1.25rem;
}
.intro-note strong { color: var(--foreground); font-weight: 550; }
.grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
column-gap: clamp(2rem, 5vw, 5rem);
row-gap: 0;
margin-top: 1.25rem;
}
.page[data-width="wide"] .grid { grid-template-columns: 1fr; }
.specimen {
position: relative;
min-width: 0;
padding: 2.5rem 0 3rem;
border-bottom: 1px solid var(--border);
}
.specimen::before {
position: absolute;
inset: 1rem -1rem;
z-index: -1;
border: 1px solid transparent;
border-radius: 0.75rem;
content: "";
pointer-events: none;
transition: 120ms ease;
transition-property: background-color, border-color;
}
.specimen[data-selected="true"]::before {
border-color: color-mix(in oklab, var(--focus) 45%, transparent);
background: color-mix(in oklab, var(--focus) 4%, transparent);
}
.specimen-header {
display: grid;
grid-template-columns: 2rem minmax(0, 1fr) auto;
gap: 0.75rem;
align-items: start;
max-width: var(--reading-width);
margin-bottom: 1.5rem;
}
.number {
color: var(--muted-foreground);
font-size: 0.75rem;
font-variant-numeric: tabular-nums;
line-height: 1.5rem;
}
.specimen h2 {
margin: 0;
font-size: 0.9375rem;
font-weight: 600;
letter-spacing: -0.01em;
line-height: 1.5rem;
}
.specimen-description {
margin: 0.125rem 0 0;
color: var(--muted-foreground);
font-size: 0.75rem;
line-height: 1.125rem;
}
.pick {
display: inline-flex;
gap: 0.375rem;
align-items: center;
min-height: 1.75rem;
padding: 0.25rem 0.5rem;
border: 1px solid var(--border);
border-radius: 0.375rem;
font-size: 0.6875rem;
line-height: 1rem;
}
.pick::before {
display: block;
width: 0.5rem;
height: 0.5rem;
border: 1px solid currentColor;
border-radius: 999px;
content: "";
}
.pick[aria-pressed="true"] { color: var(--foreground); }
.pick[aria-pressed="true"]::before { border-color: var(--focus); background: var(--focus); }
.document {
width: 100%;
max-width: var(--reading-width);
color: var(--foreground);
font-size: 0.9375rem;
font-weight: 400;
line-height: 1.5rem;
}
.document h3 {
margin: 0 0 0.75rem;
font-size: 1.25rem;
font-weight: 600;
letter-spacing: -0.01em;
line-height: 1.625rem;
}
.document p { margin: 0; }
.document code {
border-radius: 0.1875rem;
background: oklch(1 0 0 / 6%);
color: inherit;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
font-size: 0.9375rem;
font-weight: 500;
line-height: 1.5rem;
padding: 0.140625rem 0.328125rem;
}
mark {
color: inherit;
-webkit-box-decoration-break: clone;
box-decoration-break: clone;
}
/* 01 — the live dark-mode token, included as the control. */
.style-current mark {
background: #a16207;
box-shadow: 0 0.11em 0 #a16207, 0 -0.11em 0 #a16207;
}
/* 02 — asymmetric shadows create 4px x / 2px y visual padding without changing line metrics. */
.style-amber-halo mark {
--highlight: color-mix(in srgb, oklch(0.69 0.12 78) 28%, var(--background));
border-radius: 0.16rem;
background: var(--highlight);
box-shadow:
-0.12rem 0 0 0.12rem var(--highlight),
0.12rem 0 0 0.12rem var(--highlight);
}
/* 03 — low-chroma warmth, closer to paper than warning yellow. */
.style-sand mark {
border-radius: 0.1rem;
background: oklch(0.64 0.055 78 / 32%);
box-shadow: 0 0.05rem 0 0.07rem oklch(0.64 0.055 78 / 24%);
}
/* 04 — softer collaborative/comment color with a two-pixel visual outset. */
.style-lilac mark {
border-radius: 0.18rem;
background: oklch(0.66 0.15 300 / 25%);
box-shadow: 0 0 0 0.12rem oklch(0.66 0.15 300 / 25%);
}
/* 05 — cool utility color; reads as selection rather than status. */
.style-indigo mark {
border-radius: 0.18rem;
background: oklch(0.62 0.14 255 / 27%);
box-shadow: 0 0 0 0.1rem oklch(0.62 0.14 255 / 23%);
}
/* 06 — teal keeps contrast gentle against the warm black page. */
.style-teal mark {
border-radius: 0.16rem;
background: oklch(0.68 0.105 175 / 23%);
box-shadow: 0 0 0 0.1rem oklch(0.68 0.105 175 / 20%);
}
/* 07 — a restrained warm alternative that is visibly non-warning. */
.style-rose mark {
border-radius: 0.18rem;
background: oklch(0.68 0.14 18 / 23%);
box-shadow: 0 0 0 0.11rem oklch(0.68 0.14 18 / 20%);
}
/* 08 — no rectangle: an imperfect marker band through the lower glyph area. */
.style-marker mark {
background:
linear-gradient(
178deg,
transparent 18%,
oklch(0.78 0.12 90 / 15%) 18% 34%,
oklch(0.78 0.12 90 / 43%) 34% 84%,
transparent 84%
);
}
/* 09 — lightweight at rest, with a precise edge for range boundaries. */
.style-underline mark {
border-radius: 0.08rem;
background:
linear-gradient(
to top,
oklch(0.69 0.15 262 / 78%) 0 0.1rem,
oklch(0.69 0.15 262 / 13%) 0.1rem 100%
);
box-shadow: 0 0.08rem 0.28rem oklch(0.69 0.15 262 / 10%);
}
/* 10 — mostly neutral; the hairline outline carries the selection boundary. */
.style-glass mark {
border-radius: 0.18rem;
background: oklch(1 0 0 / 8%);
box-shadow:
0 0 0 0.11rem oklch(1 0 0 / 6%),
inset 0 0 0 0.055rem oklch(0.73 0.12 292 / 58%);
}
.round-heading {
display: grid;
grid-template-columns: 2rem minmax(0, 1fr);
gap: 0.75rem;
margin-top: 4.5rem;
padding-top: 2.5rem;
border-top: 1px solid var(--border);
}
.round-heading .round-number {
color: var(--muted-foreground);
font-size: 0.75rem;
font-variant-numeric: tabular-nums;
line-height: 1.5rem;
}
.round-heading h2 {
margin: 0;
font-size: clamp(1.5rem, 3vw, 2.25rem);
font-weight: 600;
letter-spacing: -0.025em;
line-height: 1.15;
}
.round-heading p {
max-width: 49rem;
margin: 0.75rem 0 0;
color: var(--muted-foreground);
font-size: 0.8125rem;
line-height: 1.25rem;
}
/* 11–20 — one geometry, ten stops around the yellow hue family. */
.style-yellow-halo mark {
--highlight: color-mix(in srgb, var(--highlight-color) 28%, var(--background));
border-radius: 0.16rem;
background: var(--highlight);
box-shadow:
-0.12rem 0 0 0.12rem var(--highlight),
0.12rem 0 0 0.12rem var(--highlight);
}
.tone-lemon { --highlight-color: oklch(0.84 0.13 102); }
.tone-citron { --highlight-color: oklch(0.81 0.16 108); }
.tone-canary { --highlight-color: oklch(0.82 0.15 96); }
.tone-sunflower { --highlight-color: oklch(0.78 0.16 89); }
.tone-marigold { --highlight-color: oklch(0.74 0.15 83); }
.tone-saffron { --highlight-color: oklch(0.72 0.16 76); }
.tone-honey { --highlight-color: oklch(0.68 0.12 72); }
.tone-wheat { --highlight-color: oklch(0.81 0.07 88); }
.tone-khaki { --highlight-color: oklch(0.68 0.08 103); }
.tone-antique-gold { --highlight-color: oklch(0.62 0.10 78); }
/* 21–30 — one balanced yellow pushed toward the WCAG AA contrast boundary. */
.style-brightness mark {
--highlight: color-mix(in srgb, oklch(0.78 0.16 89) var(--mix), var(--background));
border-radius: 0.16rem;
background: var(--highlight);
box-shadow:
-0.12rem 0 0 0.12rem var(--highlight),
0.12rem 0 0 0.12rem var(--highlight);
}
.brightness-25 { --mix: 25%; }
.brightness-30 { --mix: 30%; }
.brightness-35 { --mix: 35%; }
.brightness-40 { --mix: 40%; }
.brightness-45 { --mix: 45%; }
.brightness-50 { --mix: 50%; }
.brightness-55 { --mix: 55%; }
.brightness-58 { --mix: 58%; }
.brightness-60 { --mix: 60%; }
.brightness-62 { --mix: 62%; }
.mechanics {
display: flex;
flex-wrap: wrap;
gap: 0.375rem;
max-width: var(--reading-width);
margin-top: 1.25rem;
}
.mechanics span {
padding: 0.1875rem 0.4375rem;
border: 1px solid var(--border);
border-radius: 999px;
color: var(--muted-foreground);
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
font-size: 0.625rem;
line-height: 0.875rem;
}
.footer {
display: flex;
flex-wrap: wrap;
gap: 1rem 2rem;
justify-content: space-between;
padding-top: 2rem;
color: var(--muted-foreground);
font-size: 0.75rem;
line-height: 1.125rem;
}
.footer p { max-width: 44rem; margin: 0; }
.footer a { color: var(--foreground); text-underline-offset: 0.18em; }
.page[data-width="compact"] { --reading-width: 26rem; }
.page[data-width="panel"] { --reading-width: 32rem; }
.page[data-width="wide"] { --reading-width: 44rem; }
:focus-visible {
outline: 2px solid var(--focus);
outline-offset: 2px;
}
@media (max-width: 68rem) {
.hero { grid-template-columns: 1fr; gap: 2rem; }
.fixed-tokens { max-width: 36rem; }
.grid { grid-template-columns: 1fr; }
.document,
.specimen-header,
.mechanics { max-width: min(100%, var(--reading-width)); }
}
@media (max-width: 40rem) {
.page { padding-top: 2rem; }
.fixed-tokens { grid-template-columns: 1fr; }
.toolbar { position: static; }
.toolbar-group { width: 100%; }
.specimen-header { grid-template-columns: 1.75rem minmax(0, 1fr); }
.pick { grid-column: 2; justify-self: start; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { scroll-behavior: auto !important; transition-duration: 0.01ms !important; }
}
</style>
</head>
<body>
<main class="page" data-width="panel">
<header class="hero">
<div>
<p class="eyebrow">Document annotations · visual study</p>
<h1>Ten shapes, ten yellows, one brightness limit</h1>
<p>The document surface is held constant so the highlight is the only moving part. Round two keeps option 02’s soft shape and moves through ten yellow-family colors; round three pushes one of them to the normal-text contrast boundary.</p>
</div>
<dl class="fixed-tokens" aria-label="Fixed document styles">
<div><dt>Typeface</dt><dd>Inter Variable</dd></div>
<div><dt>Body</dt><dd>15px / 24px</dd></div>
<div><dt>Background</dt><dd>oklch(0.145 0 0)</dd></div>
<div><dt>Foreground</dt><dd>oklch(0.985 0 0)</dd></div>
</dl>
</header>
<div class="toolbar" aria-label="Exploration controls">
<div class="toolbar-group">
<span class="toolbar-label">Reading width</span>
<div class="segment" role="group" aria-label="Reading width">
<button type="button" data-width-button="compact" aria-pressed="false">Compact</button>
<button type="button" data-width-button="panel" aria-pressed="true">Side panel</button>
<button type="button" data-width-button="wide" aria-pressed="false">Wide</button>
</div>
</div>
<div class="toolbar-group">
<span class="shortlist" aria-live="polite">Shortlist: none</span>
<button class="copy-button" type="button" disabled>Copy shortlist</button>
</div>
</div>
<p class="intro-note"><strong>The padding trick:</strong> options marked “visual outset” grow with paint (usually a shadow or an expanded overlay rectangle), not CSS padding. Option 02 and all of round two now use about 4px on the x-axis and 2px on the y-axis without changing line height or text wrapping.</p>
<div class="grid">
<section class="specimen style-current" data-option="01 Current solid ochre">
<div class="specimen-header">
<span class="number">01</span>
<div>
<h2>Current solid ochre</h2>
<p class="specimen-description">Control · high coverage, square fragments, warning-adjacent hue.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>solid fill</span><span>native ::highlight</span><span>0px radius</span></div>
</section>
<section class="specimen style-amber-halo" data-option="02 Soft amber halo">
<div class="specimen-header">
<span class="number">02</span>
<div>
<h2>Soft amber halo</h2>
<p class="specimen-description">Keeps the familiar hue, lowers the chroma, and softens every edge.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>28% fill</span><span>4px x / 2px y outset</span><span>3px radius</span></div>
</section>
<section class="specimen style-sand" data-option="03 Smoked sand">
<div class="specimen-header">
<span class="number">03</span>
<div>
<h2>Smoked sand</h2>
<p class="specimen-description">A low-saturation warm neutral that reads more like paper than status.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>low chroma</span><span>1px visual outset</span><span>2px radius</span></div>
</section>
<section class="specimen style-lilac" data-option="04 Lilac halo">
<div class="specimen-header">
<span class="number">04</span>
<div>
<h2>Lilac halo</h2>
<p class="specimen-description">A collaboration-like color that separates comments from warnings.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>25% fill</span><span>2px visual outset</span><span>3px radius</span></div>
</section>
<section class="specimen style-indigo" data-option="05 Indigo wash">
<div class="specimen-header">
<span class="number">05</span>
<div>
<h2>Indigo wash</h2>
<p class="specimen-description">Cool and workmanlike; reads as selected text instead of a warning.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>27% fill</span><span>2px visual outset</span><span>3px radius</span></div>
</section>
<section class="specimen style-teal" data-option="06 Teal wash">
<div class="specimen-header">
<span class="number">06</span>
<div>
<h2>Teal wash</h2>
<p class="specimen-description">Quiet on the warm-black surface while remaining easy to scan.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>23% fill</span><span>1.5px visual outset</span><span>3px radius</span></div>
</section>
<section class="specimen style-rose" data-option="07 Dusty rose">
<div class="specimen-header">
<span class="number">07</span>
<div>
<h2>Dusty rose</h2>
<p class="specimen-description">Still warm, but clearly distinct from amber warning and review states.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>23% fill</span><span>2px visual outset</span><span>3px radius</span></div>
</section>
<section class="specimen style-marker" data-option="08 Marker band">
<div class="specimen-header">
<span class="number">08</span>
<div>
<h2>Marker band</h2>
<p class="specimen-description">A translucent stroke across the lower glyph area instead of a box.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>gradient paint</span><span>no outset</span><span>native ::highlight*</span></div>
</section>
<section class="specimen style-underline" data-option="09 Underline and haze">
<div class="specimen-header">
<span class="number">09</span>
<div>
<h2>Underline + haze</h2>
<p class="specimen-description">The lightest block treatment; a precise edge anchors a faint wash.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>13% fill</span><span>2px underline</span><span>native-friendly</span></div>
</section>
<section class="specimen style-glass" data-option="10 Neutral glass outline">
<div class="specimen-header">
<span class="number">10</span>
<div>
<h2>Neutral glass outline</h2>
<p class="specimen-description">Almost colorless fill; a violet hairline defines the selected range.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>8% neutral fill</span><span>1px inset edge</span><span>2px visual outset</span></div>
</section>
</div>
<section class="round-heading" aria-labelledby="round-two-title">
<span class="round-number">02</span>
<div>
<h2 id="round-two-title">One shape, ten yellows</h2>
<p>Every sample below uses option 02’s translucent, rounded treatment with the wider 4px horizontal outset. Opacity and geometry stay fixed; only OKLCH lightness, chroma, and hue move.</p>
</div>
</section>
<div class="grid">
<section class="specimen style-yellow-halo tone-lemon" data-option="11 Pale lemon">
<div class="specimen-header">
<span class="number">11</span>
<div>
<h2>Pale lemon</h2>
<p class="specimen-description">The brightest, cleanest yellow in the set.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>L 84</span><span>C 13</span><span>H 102</span></div>
</section>
<section class="specimen style-yellow-halo tone-citron" data-option="12 Citron">
<div class="specimen-header">
<span class="number">12</span>
<div>
<h2>Citron</h2>
<p class="specimen-description">A sharper yellow with a slight greenward pull.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>L 81</span><span>C 16</span><span>H 108</span></div>
</section>
<section class="specimen style-yellow-halo tone-canary" data-option="13 Canary">
<div class="specimen-header">
<span class="number">13</span>
<div>
<h2>Canary</h2>
<p class="specimen-description">A familiar highlighter yellow without the solid-marker density.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>L 82</span><span>C 15</span><span>H 96</span></div>
</section>
<section class="specimen style-yellow-halo tone-sunflower" data-option="14 Sunflower">
<div class="specimen-header">
<span class="number">14</span>
<div>
<h2>Sunflower</h2>
<p class="specimen-description">Balanced yellow-gold with a little more body.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>L 78</span><span>C 16</span><span>H 89</span></div>
</section>
<section class="specimen style-yellow-halo tone-marigold" data-option="15 Marigold">
<div class="specimen-header">
<span class="number">15</span>
<div>
<h2>Marigold</h2>
<p class="specimen-description">Warmer and slightly deeper, without reaching orange.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>L 74</span><span>C 15</span><span>H 83</span></div>
</section>
<section class="specimen style-yellow-halo tone-saffron" data-option="16 Saffron">
<div class="specimen-header">
<span class="number">16</span>
<div>
<h2>Saffron</h2>
<p class="specimen-description">The warmest saturated option in this round.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>L 72</span><span>C 16</span><span>H 76</span></div>
</section>
<section class="specimen style-yellow-halo tone-honey" data-option="17 Honey">
<div class="specimen-header">
<span class="number">17</span>
<div>
<h2>Honey</h2>
<p class="specimen-description">A darker, quieter golden yellow for long selections.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>L 68</span><span>C 12</span><span>H 72</span></div>
</section>
<section class="specimen style-yellow-halo tone-wheat" data-option="18 Wheat">
<div class="specimen-header">
<span class="number">18</span>
<div>
<h2>Wheat</h2>
<p class="specimen-description">Pale and desaturated; closer to parchment than marker ink.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>L 81</span><span>C 07</span><span>H 88</span></div>
</section>
<section class="specimen style-yellow-halo tone-khaki" data-option="19 Khaki">
<div class="specimen-header">
<span class="number">19</span>
<div>
<h2>Khaki</h2>
<p class="specimen-description">A subdued, slightly olive yellow with low visual urgency.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>L 68</span><span>C 08</span><span>H 103</span></div>
</section>
<section class="specimen style-yellow-halo tone-antique-gold" data-option="20 Antique gold">
<div class="specimen-header">
<span class="number">20</span>
<div>
<h2>Antique gold</h2>
<p class="specimen-description">The deepest yellow; restrained but still clearly highlighted.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>L 62</span><span>C 10</span><span>H 78</span></div>
</section>
</div>
<section class="round-heading" aria-labelledby="round-three-title">
<span class="round-number">03</span>
<div>
<h2 id="round-three-title">How bright can it go?</h2>
<p>This ramp uses Sunflower (option 14), the unchanged white foreground, and the same borderless geometry. At 15px / 400 weight the text is “normal text,” so WCAG 2.2 AA requires at least 4.5:1 contrast. Sixty percent is the brightest practical stop here at 4.66:1; 62% falls below the requirement.</p>
</div>
</section>
<div class="grid">
<section class="specimen style-brightness brightness-25" data-option="21 Sunflower 25%">
<div class="specimen-header">
<span class="number">21</span>
<div>
<h2>Sunflower · 25%</h2>
<p class="specimen-description">Quiet baseline, slightly dimmer than the color round.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>25% blend</span><span>11.81:1</span><span>AAA pass</span></div>
</section>
<section class="specimen style-brightness brightness-30" data-option="22 Sunflower 30%">
<div class="specimen-header">
<span class="number">22</span>
<div>
<h2>Sunflower · 30%</h2>
<p class="specimen-description">Close to option 14, with a small brightness lift.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>30% blend</span><span>10.35:1</span><span>AAA pass</span></div>
</section>
<section class="specimen style-brightness brightness-35" data-option="23 Sunflower 35%">
<div class="specimen-header">
<span class="number">23</span>
<div>
<h2>Sunflower · 35%</h2>
<p class="specimen-description">Clearly highlighted, still soft across long ranges.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>35% blend</span><span>9.05:1</span><span>AAA pass</span></div>
</section>
<section class="specimen style-brightness brightness-40" data-option="24 Sunflower 40%">
<div class="specimen-header">
<span class="number">24</span>
<div>
<h2>Sunflower · 40%</h2>
<p class="specimen-description">A strong but comfortable mid-range treatment.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>40% blend</span><span>7.89:1</span><span>AAA pass</span></div>
</section>
<section class="specimen style-brightness brightness-45" data-option="25 Sunflower 45%">
<div class="specimen-header">
<span class="number">25</span>
<div>
<h2>Sunflower · 45%</h2>
<p class="specimen-description">Bright and unmistakable, just below enhanced contrast.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>45% blend</span><span>6.89:1</span><span>AA pass</span></div>
</section>
<section class="specimen style-brightness brightness-50" data-option="26 Sunflower 50%">
<div class="specimen-header">
<span class="number">26</span>
<div>
<h2>Sunflower · 50%</h2>
<p class="specimen-description">Halfway to the source yellow; intentionally prominent.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>50% blend</span><span>6.03:1</span><span>AA pass</span></div>
</section>
<section class="specimen style-brightness brightness-55" data-option="27 Sunflower 55%">
<div class="specimen-header">
<span class="number">27</span>
<div>
<h2>Sunflower · 55%</h2>
<p class="specimen-description">Very bright, with a modest accessibility buffer.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>55% blend</span><span>5.29:1</span><span>AA pass</span></div>
</section>
<section class="specimen style-brightness brightness-58" data-option="28 Sunflower 58%">
<div class="specimen-header">
<span class="number">28</span>
<div>
<h2>Sunflower · 58%</h2>
<p class="specimen-description">Near the limit, but retains a useful contrast margin.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>58% blend</span><span>4.90:1</span><span>AA pass</span></div>
</section>
<section class="specimen style-brightness brightness-60" data-option="29 Sunflower 60%">
<div class="specimen-header">
<span class="number">29</span>
<div>
<h2>Sunflower · 60%</h2>
<p class="specimen-description">Brightest recommended stop for this exact color and text.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>60% blend</span><span>4.66:1</span><span>AA pass</span></div>
</section>
<section class="specimen style-brightness brightness-62" data-option="30 Sunflower 62%">
<div class="specimen-header">
<span class="number">30</span>
<div>
<h2>Sunflower · 62%</h2>
<p class="specimen-description">Boundary proof: visibly brighter, but no longer compliant.</p>
</div>
<button class="pick" type="button" aria-pressed="false">Shortlist</button>
</div>
<div class="document">
<h3>Summary</h3>
<p>Build a TypeScript Node package that plugs into an existing Express application and serves a <mark>local, read-only traffic minimap</mark>. Each node represents a normalized <code>HTTP method + Express route</code>; the dashboard polls an in-memory snapshot and uses node size for request volume and color for latency/error health. <mark>The MVP is developer-only, single-process, ephemeral, and requires no database or authentication.</mark></p>
</div>
<div class="mechanics"><span>62% blend</span><span>4.43:1</span><span>AA fail</span></div>
</section>
</div>
<footer class="footer">
<p>* Contrast uses the page’s actual foreground and background with WCAG relative luminance. <a href="https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html" target="_blank" rel="noreferrer">WCAG 2.2 AA requires 4.5:1 for this 15px normal-weight text.</a></p>
<p>Click “Shortlist” on any option, then send the numbers back for a tighter second pass.</p>
</footer>
</main>
<script>
const page = document.querySelector(".page");
const widthButtons = Array.from(document.querySelectorAll("[data-width-button]"));
const shortlistLabel = document.querySelector(".shortlist");
const copyButton = document.querySelector(".copy-button");
const pickButtons = Array.from(document.querySelectorAll(".pick"));
widthButtons.forEach((button) => {
button.addEventListener("click", () => {
page.dataset.width = button.dataset.widthButton;
widthButtons.forEach((candidate) => {
candidate.setAttribute("aria-pressed", String(candidate === button));
});
});
});
function selectedOptions() {
return pickButtons
.filter((button) => button.getAttribute("aria-pressed") === "true")
.map((button) => button.closest(".specimen").dataset.option);
}
function renderShortlist() {
const selected = selectedOptions();
shortlistLabel.textContent = selected.length
? `Shortlist: ${selected.map((option) => option.slice(0, 2)).join(", ")}`
: "Shortlist: none";
copyButton.disabled = selected.length === 0;
copyButton.textContent = "Copy shortlist";
}
pickButtons.forEach((button) => {
button.addEventListener("click", () => {
const pressed = button.getAttribute("aria-pressed") === "true";
button.setAttribute("aria-pressed", String(!pressed));
button.closest(".specimen").dataset.selected = String(!pressed);
renderShortlist();
});
});
copyButton.addEventListener("click", async () => {
const text = `Document highlight shortlist: ${selectedOptions().join(", ")}`;
try {
await navigator.clipboard.writeText(text);
copyButton.textContent = "Copied";
} catch {
copyButton.textContent = "Copy failed";
}
});
</script>
</body>
</html>