From 24bf48ae5ef7ab98fe9ce47f4ea5a1d9f334ffde Mon Sep 17 00:00:00 2001 From: mrleemurray Date: Fri, 10 Jul 2026 11:06:52 +0100 Subject: [PATCH] Add design philosophy and UI guidelines to documentation Co-authored-by: Copilot --- .github/copilot-instructions.md | 4 + .../design-philosophy.instructions.md | 36 ++ .../design-tokens.instructions.md | 7 + .github/skills/design-philosophy/SKILL.md | 481 ++++++++++++++++++ 4 files changed, 528 insertions(+) create mode 100644 .github/instructions/design-philosophy.instructions.md create mode 100644 .github/skills/design-philosophy/SKILL.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 60153416e41..f68c357d628 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -99,6 +99,10 @@ We use tabs, not spaces. - Use title-style capitalization for command labels, buttons and menu items (each word is capitalized). - Don't capitalize prepositions of four or fewer letters unless it's the first or last word (e.g. "in", "with", "for"). +### Designing UI +- When creating, editing, or reviewing any visual surface, reason in **design terms, not pixels**: name the **feeling** (Calm, Focused, Consistent, Delightful), find the **principle** it breaks, then reach for the **move** (token/tier/ramp) that restores it. Describe a bug by its role/tier/ramp (e.g. "this overlay is rounded at the control tier"), not its number. +- See the [`design-philosophy` skill](skills/design-philosophy/SKILL.md) for the full Values→Principles→Moves vocabulary, worked examples, and feedback guidance, and [design-tokens.instructions.md](instructions/design-tokens.instructions.md) for the token reference. + ### Style - Use arrow functions `=>` over anonymous function expressions diff --git a/.github/instructions/design-philosophy.instructions.md b/.github/instructions/design-philosophy.instructions.md new file mode 100644 index 00000000000..c2b7287ca8d --- /dev/null +++ b/.github/instructions/design-philosophy.instructions.md @@ -0,0 +1,36 @@ +--- +description: VS Code design philosophy — the shared Values→Principles→Moves vocabulary for reasoning about UI in design terms rather than raw pixels. Use when creating, editing, or reviewing any visual surface (CSS, DOM, theming, icons, motion). Name the value/principle before reaching for a token. +applyTo: "src/vs/**/browser/**/*.{ts,css}" +--- + +# Design philosophy + +When you touch a visual surface, reason about it in **design terms, not pixels**. +Every size, radius, weight, and color is a **named token with a role** - a raw +`px`/hex value is only ever a stand-in for the token it lands on. + +Work in three layers - name the **feeling**, find the **principle** it breaks, +then reach for the **move** that restores it: + +- **Values (how it should feel):** **Calm** · **Focused** · **Consistent** · **Delightful** +- **Principles (questions to ask):** + 1. *Quiet at rest, present on intent* - is anything shouting when nothing's happening? + 2. *Room to breathe* - does this feel cramped, tight, or jagged? + 3. *The interface explains itself* - could someone understand this at a glance? + 4. *One thing leads; the rest supports* - where does the eye go first? + 5. *Elevation is encoded, not eyeballed* - does roundness match how far the surface floats? + 6. *Sameness signals sameness* - do two like things look identical, and is any difference intentional? + 7. *Delight earns its keep* - what does this help the user do? +- **Moves (the mechanics):** elevation tiers (Control 4 / Inner 6 / Outer 8; + pills fully round), the spacing ramp, the type ramp (roles + `regular`/`semiBold` + only, no 500), two icon sizes (16 base / 12 compact), one 1px stroke, + `--vscode-*` theme color, reveal-on-intent chrome, prefer a word to a mystery + glyph. + +Reach for a **move** only *after* naming the feeling and the principle - never +instead. Describe a bug by its role/tier/ramp (*"this overlay is rounded at the +control tier"*), not its number (*"border-radius should be 6"*). + +**Full reference** (values, principles, moves, worked examples, phrasebook, and +feedback guidance): the [`design-philosophy` skill](../skills/design-philosophy/SKILL.md). +**Token mechanics:** [design-tokens.instructions.md](./design-tokens.instructions.md). diff --git a/.github/instructions/design-tokens.instructions.md b/.github/instructions/design-tokens.instructions.md index b38a5f5ff4d..b286b7785cf 100644 --- a/.github/instructions/design-tokens.instructions.md +++ b/.github/instructions/design-tokens.instructions.md @@ -5,6 +5,13 @@ applyTo: src/vs/**/*.css # Design tokens for sizing, spacing & radii +> **These tokens are the *moves*, not the reasoning.** They implement the +> **design philosophy** (Values → Principles → Moves): reach for a token *after* +> naming the feeling and the principle it serves - a radius is an *elevation +> tier*, a font is a *type role*, not a number. See the +> [`design-philosophy` skill](../skills/design-philosophy/SKILL.md) for the full +> vocabulary, worked examples, and how to give UI feedback in design terms. + VS Code ships a design-system **size** ramp. These tokens are registered in [baseSizes.ts](../../src/vs/platform/theme/common/sizes/baseSizes.ts) (and the agents font ramp in [sizes.ts](../../src/vs/sessions/common/sizes.ts)) and are diff --git a/.github/skills/design-philosophy/SKILL.md b/.github/skills/design-philosophy/SKILL.md new file mode 100644 index 00000000000..8e5cdf14361 --- /dev/null +++ b/.github/skills/design-philosophy/SKILL.md @@ -0,0 +1,481 @@ +--- +name: design-philosophy +description: The VS Code design philosophy — a shared Values→Principles→Moves vocabulary for reasoning about UI in design terms instead of raw pixels. Use when designing, building, reviewing, or giving feedback on any visual surface; when deciding a radius, spacing, type role, icon size, border, color, or motion; or when translating a "this feels off" observation into a concrete, principled fix. +--- + +# VS Code Design Philosophy + +This skill is the **canonical VS Code design philosophy** — the single source of +truth for how we reason about UI, for both developers and agents. + +As more and more of the UI is implemented via agents and tooling, the pixels +increasingly take care of themselves, and the scarce, human part becomes the +design judgment behind them. + +This is about *shifting left* - talking about UI in design terms, not pixels, and +having that conversation early. When something looks off, "this is 8px, should be +6px" fixes one number; "this surface is rounded at the wrong *elevation tier*" +names the rule, so the fix lands everywhere and we catch it on a mockup or +screenshot rather than in code review. The aim is to agree on *what the UI should +be and why*, in language a designer and an engineer can share - the implementation +(which token, which file) tends to follow once the design is settled. + +--- + +## How to read this: three layers + +The hardest part of any design language is getting from *"this feels off"* to a +clear, shared fix everyone agrees on. This bridges that in three steps, each +explaining the one below it: + +| Layer | What it is | Example | +|-------|------------|---------| +| **Values** | what we want people to *feel*, and why it's at stake | *Focused* | +| **Principles** | evaluative rules you can hold a design up against, phrased as a question | *"One thing leads; the rest supports" - where does the eye go first?* | +| **Moves** | the concrete mechanics, each with a rule for *which one and why* | *demote the secondary panel to a recessed background token / a quieter type role* | + +Read top-down when you're learning the system, and bottom-up when you're fixing a +bug: name the **feeling**, find the **principle** it breaks, then reach for the +**move** that restores it. + +> **Worked example - a busy panel** +> +> - **See:** a panel feels busy and keeps pulling your eye off the editor. +> - **Feeling:** not *Calm*, not *Focused*. +> - **Principle:** breaks *Quiet at rest* (it carries a permanent fill it shouldn't) and *One thing leads* (secondary chrome competing with the editor). +> - **Move:** drop the panel to a recessed/transparent background token, reveal its border only on hover, and demote its labels to a quieter type role. + +--- + +## The values: what we design for + +These four values name what we want people to *feel* using VS Code - they're the +top layer, the thing every principle and move below is ultimately in service of. +Learn them and you can describe almost any bug. Each is a *feeling* the UI should +give; each is upheld by one or more **principles** below. Reach for the value +first, the principle second, the move last. + +| Value | Means | When it's broken it feels… | Principles | +|------|-------|----------------------------|------------| +| **Calm** | quiet at rest, uncluttered; room to breathe; soft, not sharp; plain-spoken and human | busy, noisy, cramped, jagged, cryptic, shouty | 1, 2, 3 | +| **Focused** | clear hierarchy; one thing leads, the rest supports | flat, samey, the eye has nowhere to go | 4, 5 | +| **Consistent** | everything sits on the same ramps and tiers | random, off-grid, drifting | 5, 6 | +| **Delightful** | small moments that *do a job* - guide, confirm, orient | lifeless, abrupt, or gimmicky | 7 | + +> Say *"this feels noisy, it's not **Calm**"* before *"the background should be transparent."* The value points everyone at the same principle. + +### Why these four? + +It's fair to ask: who would ever *want* a UI that isn't calm, focused, consistent, +and delightful? That's exactly the point. These aren't differentiators we're +claiming over other products - they're the qualities most **under threat in a tool +like ours**, and naming them is how we defend them. We chose them because they're +the values VS Code is structurally inclined to *lose*: + +- **Calm** is the first casualty of an IDE. VS Code is information-dense and lived + in for hours; every feature team has a good reason to add one more affordance, + and the sum is noise. Calm is the value that pushes back on our own gravity. +- **Focused** matters because our surfaces are *deep*. A screen full of + equally-weighted, equally-valid controls is the default failure mode of a power + tool. Naming focus forces us to decide what leads. +- **Consistent** has the largest surface area to defend. VS Code is built by many + hands over many years; drift is the natural state. Consistency is less an + aspiration than a maintenance discipline. +- **Delightful** is the one we deliberately **ration**. It earns its place only by + doing a job (Principle 7). We name it not to add more polish but to keep it + honest, so delight guides and confirms rather than decorates. + +And each value names a real **tension**, not a free win - which is what makes it a +choice worth stating rather than a platitude: + +- **Calm** trades against **density** - how much do we surface at rest? +- **Focused** trades against **equal access** - what do we demote so one thing can lead? +- **Consistent** trades against the **genuinely special case** - when is a difference earned rather than drift? +- **Delightful** trades against **restraint** - when does a flourish cross into noise? + +So the useful question isn't *"would we ever not want these?"* but *"which one is +this surface trading away, and did we mean to?"* When a value really must yield - +say, a critical warning that *should* break calm to seize attention - that's a +deliberate, named exception, not a reason to drop the value. + +--- + +## Principles + +Principles are **evaluative**: each one is a question you can ask of any surface, +and the answer tells you whether the design is right - independent of any pixel +value. They're grouped by the value they uphold. + +--- + +### Calm + +#### 1. Quiet at rest, present on intent + +> **Ask:** *is anything shouting when nothing is happening?* + +Chrome should recede until you engage it. A surface carrying a permanent fill, +border, or focus ring it hasn't earned is competing with the user's content for +attention it doesn't need. The modern look is **restrained**: things reveal +themselves *on intent* - hover, focus, interaction - and sit quietly otherwise. + +When this is wrong it sounds like *"this is loud at rest / should be quiet until +hover"* or *"the focus ring is firing on click"* - a behavioral description, not +a color value. + +*Carried out by:* [reveal-on-intent behaviors](#reveal-on-intent), +[one stroke](#one-stroke), [theme color](#theme-color). + +#### 2. Room to breathe + +> **Ask:** *does this feel cramped, tight, or jagged?* + +Calm UI is generous and soft. Space isn't wasted; it's what lets the eye rest and +groups things without drawing a single line. Edges are rounded rather than sharp. +When a surface feels boxed-in or busy, the fix is usually more breathing room on +the spacing ramp, not smaller content. + +*Carried out by:* [the spacing ramp](#spacing-ramp), +[elevation tiers](#elevation-tiers) (rounded, not sharp). + +#### 3. The interface explains itself, plainly and kindly + +> **Ask:** *could someone understand this at a glance, without guessing?* + +The UI should read like a person talking. Prefer a word to a mystery glyph; write +in sentence case; keep the tone human. A lone unlabeled icon, or a Title-Cased +Marketing Headline, makes the user work harder than they should - never cryptic, +never shouting. + +*Carried out by:* [words & casing](#words-casing). + +> **Worked example - a boxed-in toolbar** +> - **See:** a toolbar sits on a solid filled bar, separator lines between every button, the buttons pushed up against each other. +> - **Feeling:** not *Calm* - busy and boxed-in. +> - **Principle:** breaks *Quiet at rest* (the fill and separators are permanent +> chrome that hasn't earned its place) and *Room to breathe* (buttons jammed together). +> - **Move:** let the bar read transparent at rest, drop the separators (one stroke - and here the answer is *no*), and space the buttons out on the ramp. + +### Focused + +#### 4. One thing leads; the rest supports + +> **Ask:** *where does the eye go first - and is anything secondary competing with it?* + +Every view should have a clear **center of attention** - the one thing the eye +should land on first. Keep secondary information *out* of that spot: demote it +with a quieter type role, a recessed surface, or a smaller icon, so the thing +that matters leads and everything else supports it. A view where everything has +equal weight is *flat* - the eye has nowhere to go. + +*Carried out by:* [type roles](#type-ramp), [elevation tiers](#elevation-tiers), +[icon size by context](#icon-sizes). + +#### 5. Elevation is encoded, not eyeballed + +> **Ask:** *does this surface's roundness match how far it floats above what's beneath it?* + +How rounded - and how raised - a surface is *means something*: it tells you where +the surface sits in the stack. A floating overlay reads as more rounded than a +control sitting flat inside a pane. Radius is chosen by the surface's role in the +hierarchy, never by taste. (This serves both **Focused** and **Consistent**.) + +*Carried out by:* [elevation tiers](#elevation-tiers), +[reveal-on-intent](#reveal-on-intent) (scroll shadows). + +> **Worked example - a flat settings row** +> - **See:** a settings row shows its label, its help text, and a "Reset" action all at the same size and weight. +> - **Feeling:** not *Focused* - flat; the eye doesn't know where to land. +> - **Principle:** breaks *One thing leads* - nothing is the center of attention. +> - **Move:** lift the label to the leading type role, demote the help text to `body2`, and make "Reset" a quiet secondary action so the row reads top-down. + +### Consistent + +#### 6. Sameness signals sameness + +> **Ask:** *do two like things look identical - and is any difference here intentional?* + +Equivalent elements must look equivalent, because they pull from the same named +scale. When two similar things differ, that difference should *mean* something; +accidental drift is the enemy. This is why every size, radius, weight, and color +is a **named token with a role**, not a literal - the literal is only ever a +stand-in for the token it lands on. + +*Carried out by:* every move below - they *are* the shared scales. See especially +[design tokens](#design-tokens), [the spacing ramp](#spacing-ramp), +[the type ramp](#type-ramp), [icon sizes](#icon-sizes), [one stroke](#one-stroke). + +> **Worked example - two mismatched dialogs** +> - **See:** two dialogs open side by side; one has 8px corners, the other 6px - +> both are floating overlays. +> - **Feeling:** not *Consistent* - the UI feels like it's drifting. +> - **Principle:** breaks *Sameness signals sameness* (two equivalent surfaces look +> different for no reason) and *Elevation is encoded* (an overlay belongs on the +> Outer tier). +> - **Move:** point both at the Outer radius token; the 6px was a literal someone +> typed instead of naming the tier. + +### Delightful + +#### 7. Delight earns its keep + +> **Ask:** *what does this help the user do?* + +Motion, reveals, and finishing touches are welcome - but each must **do a job**: +guide the eye, confirm an action, orient the user, or smooth a jump. If there's no +answer to the question, it's decoration, and decoration reads as noise (back to +**Calm**). Keep it subtle, short, and interruptible; honor +`prefers-reduced-motion`; never animate for its own sake. + +A delight bug sounds like *"this transition is doing nothing for me"* (cut it) or +*"this jump is disorienting - it needs a functional transition"* (add one) - not a +duration or easing value to fiddle with. + +*Carried out by:* [reveal-on-intent behaviors](#reveal-on-intent). + +> **Worked example - a bouncing panel** +> - **See:** a side panel slides in with a 600ms bounce every time you toggle it. +> - **Feeling:** not *Delightful* (gimmicky) and not *Calm* (slow and shouty). +> - **Principle:** breaks *Delight earns its keep* - the bounce isn't doing a job, +> and the toggle is already obvious. +> - **Move:** if nothing needs orienting, cut the animation; if the panel's arrival +> genuinely needs explaining, replace it with a short, subtle, interruptible fade +> that honors `prefers-reduced-motion`. + +--- + +## The moves: the mechanical toolbox + +Moves are the **concrete mechanics** - the tokens, ramps, and tiers. On their own +they're just lists of allowed values; what makes each one usable is the +**decision rule** that says *which value, and why*, plus the **principle** it +serves. When you reach for a move, lead with the rule, not the literal. + +This is the one section that touches implementation - and even here, the goal is +to keep the *conversation* about design. Treat the Moves as the **shared +vocabulary that lets an agreed design be built consistently**, not as the opening +move in a review. Reach for them *after* you've named the feeling and the +principle, never instead of it. + +The size tokens live in +[`baseSizes.ts`](../../../src/vs/platform/theme/common/sizes/baseSizes.ts) and the +font ramp in [`sizes.ts`](../../../src/vs/sessions/common/sizes.ts); the full +reference is in +[design-tokens.instructions.md](../../instructions/design-tokens.instructions.md). + + +### Tokens are the source of truth, not the pixel + +Every size, radius, weight, and color belongs to a **named token** with a defined +role. A raw `px` value is only ever a *stand-in* for the token it lands on. +- **Decision rule:** when something looks wrong, find the token it should be + expressing and reason about *that* - not the literal. +- **Serves:** *Sameness signals sameness* (6). + +> A `12px` corner isn't "12px." It's an **Outer surface radius** that someone +> typed as a literal. Name the role and the fix follows. + + +### Elevation tiers - round by role, not by literal + +Corner radius encodes **how far a surface floats above the one beneath it**. +Every rounded thing belongs to exactly one tier: + +| Tier | Radius | Token | What it is | +|------|--------|-------|------------| +| **Control** | 4px | `--vscode-cornerRadius-small` | interactible elements - buttons, inputs, list rows, tabs | +| **Inner** | 6px | `--vscode-cornerRadius-medium` | non-control containers sitting *inside* a surface | +| **Outer** | 8px | `--vscode-cornerRadius-large` | floating / overlay surfaces - menus, hovers, dialogs, toasts | + +Pills (radius ≈ half the height) are **fully round** +(`--vscode-cornerRadius-circle`), not "a big radius." +- **Decision rule:** pick the tier by the surface's **role in the stack**, not by + how the corner looks. A bug here sounds like *"this overlay is rounded at the + control tier,"* never *"this needs more border-radius."* +- **Serves:** *Elevation is encoded* (5), *Room to breathe* (2). + + +### The spacing ramp - on-scale or off-scale + +Padding, margin, and gap come from the **spacing ramp** (0, 2, 4, 6, 8, 10, 12, +16, 20, 24, 28, 32, 36, 40). +- **Decision rule:** a value either lands *on* the ramp or it doesn't. Off-scale + values (3, 5, 7, 14, 26…) break the rhythm; snap to the nearest step, ties + rounding up. Report rhythm bugs as *"this is off the spacing ramp,"* not *"add a + couple of pixels."* +- **Serves:** *Room to breathe* (2), *Sameness signals sameness* (6). + + +### The type ramp - roles and two weights, not free sizes + +Text styles are **roles**, not arbitrary sizes: `heading1–3`, `body1–2`, +`label1–3`. There are exactly **two weights** - `regular` (400) and `semiBold` +(600). There is no medium (500). "Strong" is not a bigger size; it is the *same +role token at `semiBold`*. +- **Decision rule:** choose the role by the text's **rank in the hierarchy**, not + by eyeballing a size. A heading that looks weak is *"the wrong type role"* or + *"missing the semiBold weight,"* not *"bump the font to 14."* A `500` anywhere + is **off the ramp**. +- **Serves:** *One thing leads* (4), *Sameness signals sameness* (6). + + +### Icon sizes - two sizes, chosen by context + +Codicons are **16px (base)** or **12px (compact)** - nothing in between; `14px` is +always a bug. At the compact size, also swap to the `*Compact` glyph +(`Codicon.close` → `Codicon.closeCompact`) so the icon is *optically* tuned, not +just scaled. +- **Decision rule:** size tracks the **density and rank of the context**. Use + **base (16px)** for standalone or primary actions and comfortable click targets; + use **compact (12px)** for dense rows, inline glyphs, and secondary chrome where + the icon rides alongside text. So the answer to "16 or 12?" is *"what is this + icon's role here?"* - not a taste call. Say *"this icon should be compact,"* not + *"shrink it a bit."* +- **Serves:** *One thing leads* (4), *Sameness signals sameness* (6). + + +### One stroke + +The system has a single border thickness: **1px** (`--vscode-strokeThickness`). +- **Decision rule:** borders and separators are a **yes/no** decision, not a width + to tune. (And often the answer is *no* - see *Quiet at rest*.) +- **Serves:** *Quiet at rest* (1), *Sameness signals sameness* (6). + + +### Color comes from the theme + +Every color is a `--vscode-*` theme token, so the UI tracks the active theme and +high-contrast modes. +- **Decision rule:** a hardcoded hex is a bug by construction. Color bugs are + *"wrong theme token"* (or *"a token that disappears in light/HC"*), never a hex + value to nudge. +- **Serves:** *Quiet at rest* (1), *Sameness signals sameness* (6). + + +### Reveal-on-intent behaviors + +Chrome that used to carry a permanent fill now reads transparent at rest and only +reveals on interaction. Each behavior also *does a job*, so they pull double duty: + +- `commandCenter` - transparent at rest; shows its background/border on hover, so + a pointer *confirms* the thing it's over. +- `statusBar` - neutralized to the window; only the **background** changes on + hover, the foreground stays put. +- `keyboardFocusOnly` - the focus ring appears for **keyboard** focus + (`:focus-visible`), not on mouse click, so the affordance shows exactly when + it's useful. +- `scrollShadows` - content *fades* under a surface edge instead of being cut at a + hard line, so you feel there's *more* beyond the fold. + +- **Serves:** *Quiet at rest* (1), *Delight earns its keep* (7), and (scroll + shadows) *Elevation is encoded* (5). + + +### Words & casing + +**Prefer a text label to an icon.** A word is read; an icon is *guessed*. Reach +for a glyph only when its meaning is near-universal (close, search) or space is +genuinely scarce - otherwise label the thing, or pair icon + label when you need +both recognition and recall. A lone mystery glyph is the bug, not a badly-chosen +one: *"this action is unlabeled - give it a word,"* not *"find a clearer icon."* + +**Write in sentence case.** Labels, buttons, menus, and messages capitalize the +first word and proper nouns only - *"Start new session,"* not *"Start New +Session."* It's calmer, friendlier, and quicker to scan. Report it as *"this is +Title Case - make it sentence case,"* a tone fix, not a reword. + +> **⚠️ Known conflict - reconcile later.** This sentence-case guidance is the +> intended modern-UI direction, but it **contradicts** the repo-wide coding +> guideline that mandates title-style capitalization for command labels, buttons, +> and menu items (see +> [coding-guidelines.instructions.md](../../instructions/coding-guidelines.instructions.md), +> "UI labels"). The two conventions have not yet been reconciled. Until they are, +> **follow the existing title-case rule for shipped, non-experimental UI**, and +> apply sentence case only within surfaces that have explicitly adopted the modern +> UI direction. TODO: agree a single casing convention and update whichever doc +> loses. + +- **Serves:** *The interface explains itself* (3). + +--- + +## The phrasebook: describe the bug, name the principle + +Lead with the **role / tier / ramp**, not the number - then name the principle so +the fix lands everywhere, not just here. The number is a symptom. + +| ❌ Don't say | ✅ Do say | Principle | +|----------------------|--------------------------|-----------| +| "border-radius should be 6, not 8" | "this is an **Inner** surface rounded at the **Outer** tier" | 5 · Elevation | +| "this menu corner is too sharp" | "this **overlay** should be on the **Outer** radius tier" | 5 · Elevation | +| "add 2px of padding" | "this content is **off the spacing ramp** / needs ramp breathing room" | 2 · Room to breathe | +| "make this 14px" | "this should use the **`label1` / `body1` type role**" | 4 · One thing leads | +| "the title looks thin" | "the heading is **missing the `semiBold` weight**" | 4 · One thing leads | +| "font-weight 500 here" | "**500 is off the ramp** - snap to `semiBold` (600)" | 6 · Sameness | +| "shrink this icon a touch" | "this icon should be the **compact (12px) size + `*Compact` glyph**" | 4 · One thing leads | +| "this icon is 14px" | "codicons are **16 or 12 only** - pick base or compact" | 6 · Sameness | +| "this border is too thick" | "borders are **one stroke (1px)** - this should/shouldn't have one" | 1 · Quiet at rest | +| "change this grey hex" | "this is the **wrong theme token** / it **vanishes in light/HC**" | 6 · Sameness | +| "the command center has a box around it" | "chrome should be **quiet at rest, reveal on hover**" | 1 · Quiet at rest | +| "the focus outline shows when I click" | "the ring is **keyboard-focus only** (`:focus-visible`)" | 1 · Quiet at rest | +| "the list is cut off at the bottom" | "the surface is **missing its scroll-shadow fade**" | 7 · Delight / 5 · Elevation | +| "the pane header has a line / hard edge" | "pane headers are **rounded, separator-less section titles**" | 1 · Quiet at rest | + +--- + +## Giving and receiving feedback + +**Describe the design, not the pixel.** When you name the rule that's broken, the +fix lands everywhere it's broken - not just in the one spot you happened to notice. + +When something looks wrong, say it in this order: + +1. **The feeling** - which of the four values is it missing? *Calm, Focused, + Consistent, Delightful.* +2. **The surface** - what and where? *"the sidebar pane header," "the Save dialog."* +3. **The principle it breaks** - the rule behind the feeling. +4. **The move that would fix it** - the mechanic that restores it. +5. **Only then, if useful, the number** - *"shows 4px, looks like it wants 6px."* + +> **Example** +> "The activity bar doesn't feel **Calm** - it's loud at rest. The icons carry a +> permanent fill that's competing with the editor (*Quiet at rest, present on +> intent*). Could the fill reveal on hover instead? It reads about 2px too tight as +> well, but the fill is the real thing." + +### Lead with the feeling, not the fix + +The word points everyone at the same principle, even before anyone agrees on a +pixel: + +| Say this first | Not this | +|----------------|----------| +| "this doesn't feel **Calm** - it's noisy at rest" | "make the background transparent" | +| "this isn't **Focused** - the eye has nowhere to land" | "make the title bigger" | +| "this feels **inconsistent** - these two dialogs don't match" | "change the radius to 6" | +| "this transition isn't **Delightful**, it's just decoration" | "make the animation faster" | + +### What makes feedback hard to act on + +These aren't wrong, they're just *incomplete* - they fix one spot and drift back +out of sync: + +- **A bare number.** *"This should be 6px."* Which token? Why? Name the role and + the fix lands everywhere. +- **A taste claim with no rule.** *"I don't like this corner."* Tie it to a value + or principle so it's a shared standard, not a preference. +- **A fix with no feeling.** *"Make it transparent."* Maybe - but say what's wrong + first (*"it's loud at rest"*), so we fix the cause, not just this instance. +- **Several things at once.** Split *"this panel is busy, off-grid, and the icon's + wrong"* into three, each with its own value. + +### Practical notes + +- **Attach the surface.** A screenshot or a short clip (for motion) plus the name + of the view beats a paragraph of description. Circle the spot. +- **One observation per thread.** Keep each piece of feedback to a single + surface + value, so it can be discussed and resolved on its own. +- **Light vs. dark vs. high-contrast.** If something only breaks in one theme, say + so - that usually points at a *theme token* problem, not a color choice. +- **Reduced motion.** For animation feedback, note whether you have + `prefers-reduced-motion` on; a transition should still make sense without it.