From 16b8dd3464e5641bb9d405aa3e78fc5eb41da899 Mon Sep 17 00:00:00 2001 From: morgmart <98432065+morgmart@users.noreply.github.com> Date: Mon, 7 Sep 2026 17:10:36 -0700 Subject: [PATCH] desktop-new: document the decision path from context to token A shared role that fails in a real context is a defect in the shared decision, not a reason to name a nearby ramp step locally. The specimen frames exposed this with border-primary: neutral-4 was visible enough in light mode and 1.08:1 against a dark panel. The first response bypassed it with neutral-6 just for the documentation. The correct response was to measure every reader, repair the role to neutral-4 light / neutral-6 dark, and return every caller to the same role. DESIGN.md now gives the complete decision order: use a component first; add a variant if the background changes; choose a surface by its spatial job; use the shared boundary role; choose ramp steps directly for a hue decision; add a proposed role only when one step cannot express both modes; and stop and ask rather than making a local exception. It also records why the border uses two different steps and why neutral-5 could not be the one-step dark adjustment. Signed-off-by: Morgan Martin Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com> --- desktop-new/DESIGN.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/desktop-new/DESIGN.md b/desktop-new/DESIGN.md index 56da700a61..66197a5f08 100644 --- a/desktop-new/DESIGN.md +++ b/desktop-new/DESIGN.md @@ -12,7 +12,7 @@ Buzz is a place where people build together and bring their agents into the room - **Panels sit on the backdrop; the backdrop is a gradient.** Everything else is a panel in a different place. The navigation column is not a special kind of surface. - **A region is separated by a soft fill, not by an outline.** Reach for `bg-inset` before reaching for a border. A bordered box announces its own edges; a filled one lets the content sit in a place. Grouping is the common case, so the quiet treatment is the default one. -- **A border is for a genuine boundary, and there is one weight: `border-primary`.** It is neutral 4: visible enough to hold the edge of a panel over the gradient, but quiet enough that internal dividers do not become the hierarchy. A second weight arrives only with the design that proves a different boundary needs it. Text and borders still hold different values — text at the dark end of the neutral ramp, borders at the light end — so if a divider looks like text, it is pointed at the wrong role. +- **A border is for a genuine boundary, and there is one weight: `border-primary`.** It is `neutral-4` in light and `neutral-6` in dark. A hairline needs more separation on a dark surface than the same step number supplies: `neutral-4` measured 1.08:1 on a dark panel and 1.09:1 on the floating composer — drawn, and effectively invisible. `neutral-5` looked like the obvious one-step move but is `bg-float` in dark, so a border there would be 1.00:1: the same colour as the surface it is meant to bound. `neutral-6` clears the actual surfaces at 1.35–1.43 on panel/inset and 1.14 on float. **That mode difference is the reason this is a role rather than `border-neutral-4`.** A second weight arrives only with the design that proves a different boundary needs it. Text and borders still hold different values — text at the dark end of the neutral ramp, borders at the light end — so if a divider looks like text, it is pointed at the wrong role. - **No page-wide gradient behind documentation or dense reading.** The gradient is the product's backdrop for chrome and panels. Behind a column of prose it fights the text and makes contrast position-dependent — such surfaces sit on `bg-panel`. - **Shadows stay at the threshold of perception.** If a shadow is obvious, it is too strong. The two elevation values are the whole vocabulary. - **Elevation is carried by shadow in light mode and by lightness in dark mode.** On a near-black background there is nothing darker for a shadow to cast, so a floating surface becomes a step lighter instead. Never reach for a stronger shadow to make something float in dark mode. @@ -393,4 +393,13 @@ Every addition lands in `src/shared/tokens/registry.ts` in the same change that - **Use an existing component before creating one, and an existing role before adding one.** - **A new visual treatment that repeats belongs in the system, not in the feature.** +- **If a shared role fails in a real context, repair the role — never work around it locally.** A documentation specimen frame needed a border but `border-primary` was neutral-4 in both modes, which measured 1.08:1 on the dark page. The wrong response was the one we made first: name `neutral-6` directly and call documentation furniture a special case. The right response was to ask whether the one shared boundary role was wrong, measure it on every surface it reaches, and make it `neutral-4` light / `neutral-6` dark. The frame then returned to `border-primary`, and every product divider improved with it. **A local exception is evidence the shared decision is incomplete, not a licence to bypass it.** +- **When choosing a colour, surface, or boundary, use this order.** + 1. **Is there already a component for the thing?** Use it. Its variants are the decisions already made. Do not assemble its fill, border, shadow, or states yourself. + 2. **If the component has no variant for its background, add a variant rather than a sibling component.** `Tabs` is `chrome` (glass pill on the gradient) or `panel` (underline on a plain surface): same behaviour, different appearance. A second component would duplicate its keyboard and accessibility contract just to change styling. + 3. **If building a surface directly, choose its job before its colour.** Backdrop → `bg-app`; opaque region on it → `bg-panel`; region pushed in → `bg-inset`; thing floating above → `bg-float`; something the backdrop should show through → `glass-primary` or `glass-secondary`. Do not use a border to do the work of an inset fill. + 4. **If drawing a boundary, use `border-primary`.** It is the one shared hairline, already authored for both modes. If it does not read in the actual context, measure that context and fix this role — do not name a neutral step at the call site. + 5. **If choosing an accent or status colour, choose a ramp step and measure it.** `bg-purple-9`, `bg-purple-3`, `text-red-12`, `border-purple-8`: every step responds to mode. Do not invent a semantic name for one choice; a repeated *pattern* earns a name when Morgan says it does. + 6. **If the choice cannot be expressed by one step in both modes, make a proposed role with both values and a one-sentence job.** If the name only restates one step, it has not earned a role. If it is a whole treatment — glass is the example — make a utility that carries every inseparable part together. + 7. **If none of this feels clearly right, stop and ask.** Choosing a raw literal or a local exception is never the escape hatch. The system is deliberately allowed to grow; uncertainty is evidence of a missing decision, not a prompt to hide one. - **If a screen looks right but breaks these rules, the rules are probably wrong — say so.** This document is meant to be argued with, not worked around.