Type · implementation defect (CSS selector coverage). Not a planning mistake — the card that shipped the style asked for exactly the right thing; the implementation bound "every button" to ONE utility class. No lesson owed.
Parent · parentless at the ROOT. Found out-of-band (manual dogfooding), so there is no finding story to carry a blocked_by edge into; the discovery subtask MOTIR-1052 and its whole ancestor chain (story MOTIR-804, Epic 7 MOTIR-464) are done, so the epic rung is disqualified and the ladder falls through to a root sibling. relates_to MOTIR-1052.
Discovered in · MOTIR-1052 (7.3.39 Style: 3D / Immersive), shipped in PR #1296. Reported by Yue while running data-style="3d-immersive" in the app.
[data-style='3d-immersive'] gives a control its "physical key" thickness through exactly two selectors, at packages/design-system/theme.css:1276-1277 (verified on origin/main @ 052965b3):
[data-style='3d-immersive'] .rounded-\(--radius-btn\),
[data-style='3d-immersive'] button.rounded-\(--radius-input\) { box-shadow: 0 3px 0 0 …; }
The rule keys on the compiled radius utility class, not on the element being interactive. The block's own comment shows the sweep that produced it: the author enumerated the ~35 hand-rolled rounded-(--radius-btn) <button>s and widened the rule to cover that class. Every control the app builds on a different radius utility was never in the sample, so it gets no box-shadow, no base edge, and no press-down — while the panel it sits on floats on a four-layer --shadow-card. That is exactly the "half-3D" failure docs/styles/3d-immersive.md §8 names as its first ❌ ("Leave buttons flat — a flat button beside floating 3D cards is the inconsistency that reads as 'half-3D'"), and it contradicts the shipped registry copy in packages/design-system/src/theme/styles.ts:314 — "PHYSICAL buttons with real thickness … Nothing is flat."
A JSX-opening-tag scan (regex over <button|<a|<Link|<input|<textarea|<select and the rounded-* utility in the same tag) across app/, components/ and packages/design-system/src at origin/main @ 052965b3 returns 300 tags carrying a radius utility — 280 of them interactive controls (excluding the 20 text fields):
| radius utility | tags | reached by the rule? |
|---|---|---|
rounded-(--radius-btn) | 59 (button 32 · Link 21 · a 6) | ✅ raised |
button.rounded-(--radius-input) | 6 | ✅ raised |
rounded-(--radius-control) | 180 (button 143 · Link 23 · a 14) | ❌ box-shadow: none |
rounded-(--radius-badge) | 12 (button 8 · Link 4) | ❌ box-shadow: none |
rounded-full | 6 | ❌ box-shadow: none |
rounded-sm | 1 | ❌ box-shadow: none |
rounded-(--radius-card) | 16 (button 8 · Link 8) | ⚠️ floats on --shadow-card (panel rule) but never presses |
text fields (input/select/textarea + the Input primitive's wrapper div) | 20 + primitive | ❌ no treatment of any kind |
65 raised · 16 float-but-never-press · 199 with no depth at all.
Computed box-shadow measured in headless Chromium against the real compiled token layer (@tailwindcss/postcss over packages/design-system/theme.css, data-style="3d-immersive" data-palette="motir"):
| # | control | file | radius class | computed box-shadow |
|---|---|---|---|---|
| 1 | "Plan with AI" pill | components/planning/PlanWithAILauncher.tsx:88 | rounded-(--radius-badge) | its own inline hero glow only — no base edge |
| 2 | "Building in public" header link | app/(authed)/_components/build-in-public/BuildingInPublicHeaderLink.tsx:63 | rounded-(--radius-badge) | none |
| 3 | "Re-plan" entrance (item detail) | components/planning/WorkItemPlanEntrance.tsx:123 | rounded-(--radius-badge) | none |
| 4 | 3-dots menu trigger (item detail) | components/issues/actions/WorkItemActionsMenu.tsx:59 (MENU_TRIGGER_CLASS) | rounded-(--radius-control) | none |
| 5 | "Search the roadmap" input | components/planning/ProjectRoadmapCanvas.tsx:889 → packages/design-system/src/components/ui/Input.tsx:114 | div.rounded-(--radius-input) | none |
| + | "Plan with AI" floating orb (FAB) | components/planning/PlanWithAIFab.tsx:136 | rounded-full | none |
Controls on rounded-(--radius-btn) measured in the same render for contrast: rgb(69,52,179) 0 4px 0 0, rgba(15,15,15,.32) 0 7px 12px -3px (primary) / rgb(200,196,190) 0 3px 0 0, … (neutral). Side-by-side render attached to this card's discussion.
Note on #2: the pre-public BuildInPublicButton (…/BuildInPublicButton.tsx) is on rounded-(--radius-btn) and is raised. The flat one is its post-public successor in the same header slot, the BuildingInPublicHeaderLink badge — and the placement="drawer" variants of both.
--radius-control controls are menu / option ROWS inside popovers (WorkItemActionsMenu's ITEM_CLASS, combobox options, sidebar rows — w-full / role="menuitem" / role="option"). A 3px base edge on every row of an open menu is worse than flat. ~62 are standalone icon buttons that should be keys; the remaining ~64 need reading. The fix needs a hook that separates the two — a data- attribute on the primitives, or a structural exclusion under [data-surface='popover'] / [data-menu-surface] — not a bare class rule.boxShadow in an inline style prop (HERO_STYLE, PlanWithAILauncher.tsx:59-69). An inline declaration beats any style-layer rule, so widening the selector alone leaves this one control unchanged. It needs the base edge folded into HERO_STYLE or a variable the style layer can re-point.--radius-pill untouched so avatars / dots / the spinner stay circular — but the FAB is a circular button. A rounded-full rule must raise the button without touching the non-interactive circles.theme.css:1272-1275 scopes the input arm to button. explicitly "so a real <input>/<textarea> text field (same radius) is NEVER raised" — correct, a text field should not stick up. But nothing was put in its place: docs/styles/3d-immersive.md §4 says only "Input / control — On the face; generous rounded dimensional silhouette", and the shipped result is a 1px-bordered rectangle with box-shadow: none on a page where every panel carries a four-layer float. The skeuomorphic counterpart of a raised key is a recessed field (an inset shadow, pressed INTO the surface); §4 never decides that, so the fix has to amend the spec doc before it can implement it. Settle it in docs/styles/3d-immersive.md §4/§6a as part of this card — it is a two-line amendment to an existing design asset, not a new surface.
docs/styles/3d-immersive.md §4's plane ladder states, per control family, whether it is a raised key, a recessed field, or deliberately flat — and §4 gains the text-field decision (recessed vs on-the-face) that it currently leaves unwritten. The §4 table and the [data-style='3d-immersive'] CSS agree row for row.data-style="3d-immersive", getComputedStyle(el).boxShadow !== 'none' for each of the six controls named in the table above, measured on the real compiled token layer.role="menuitem" / role="option", the w-full rows inside [data-surface='popover'] and [data-menu-surface]) still compute box-shadow: none — the widening must not raise them.[data-variant='ghost'] still computes box-shadow: none; the quiet button stays flat.:active:not(:disabled) press rule covers the same set the resting rule does — every newly-raised control presses down onto its base, none is left with a resting edge and no press.tests/theme/ enumerates the radius utilities the codebase emits on interactive tags (the scan above, run in-test rather than hard-coded) and asserts each one is classified — raised, recessed, or flat-with-a-named-reason. A new radius utility appearing on a control with no classification fails the test. Assert the CLASSIFICATION, not a count: the tag counts in this card's table are a 052965b3 reading and will drift.--color-* / --el-* colour token is added or changed by the fix — the colour axis stays disjoint (the existing material-rule test still passes).pnpm lint, pnpm typecheck and the theme suite are green on the branch.packages/design-system/theme.css:1258-1322 — the physical-key + press rules (the defect) and their comment describing the sweep that produced them.packages/design-system/theme.css:1014-1077 — the [data-style='3d-immersive'] token block: --radius-btn: 14px, --radius-input: 14px, --radius-control: 12px, --radius-badge: 9999px, --radius-card: 20px.packages/design-system/theme.css:1121-1125 — the global panel float on .rounded-\(--radius-card\), the pattern the button rule was modelled on.docs/styles/3d-immersive.md — §4 plane ladder, §6a static depth, §8 ❌ "Leave buttons flat". The design asset this card amends.packages/design-system/src/theme/styles.ts:314-340 — STYLE_REGISTRY['3d-immersive'], whose dimensions.components promises "PHYSICAL buttons … Nothing is flat."packages/design-system/src/components/ui/Button.tsx:29,79 — the primitive: rounded-(--radius-btn) + the data-variant hook the filled/ghost overrides key on.packages/design-system/src/components/ui/Input.tsx:110-128 — the field shell (data-surface="input", border only, no shadow in any style).components/planning/PlanWithAILauncher.tsx:59,88 · app/(authed)/_components/build-in-public/BuildingInPublicHeaderLink.tsx:63 · components/planning/WorkItemPlanEntrance.tsx:123 · components/issues/actions/WorkItemActionsMenu.tsx:59 · components/planning/ProjectRoadmapCanvas.tsx:889 · components/planning/PlanWithAIFab.tsx:136.tests/theme/ — the existing theme suite; tests/theme/styleRegistry.test.ts is today the ONLY test that names this style, and it asserts the registry entry's name only. Nothing asserts the style's rendered coverage, which is why the gap shipped.Open.