Subtle elevation: one shared shadow scale, no big halos

Merged
TE
22d ago
+811-301

Menus, popovers, the composer, the New Tab box, find bars, palettes, sheets and the tab drag ghost each cast their own drop shadow, many of them big and soft (0 16px 34px at 28-30%, 0 12px 40px at 22%, an opaque 12 pt halo around a docked column). Every shadow now comes from one two-step scale, the same numbers on the web and in AppKit: a tight, low-alpha drop under the surface, with the thin rim carrying the edge. Nothing gets a new look; shadows only get smaller.

Built on current base, which has #20461 (one popup hairline) and #20481 (device-pixel rims). The rims are untouched; only the drops change.

Tokens

StepWeb (pages/shared/desktop.css)Native (PopupStyle)Used by
--elevation-1 / .elevation10 1px 2px black 7% (dark 12%)offset 1, blur 2, alpha 0.07rows, cards, inline surfaces, a selected segment, a switch knob
--elevation-2 / .elevation20 2px 6px black 11% (dark 16%)offset 2, blur 6, alpha 0.11popovers, menus, palette, hover cards, composer, New Tab box, sheets, toasts, find bars, lifted tabs
  • Dark mode keeps the geometry and darkens the alpha a little (prefers-color-scheme: dark, or data-theme="dark" in the agent pane).
  • --ui-popup-drop is now var(--elevation-2), so --ui-popup-shadow and --acpmux-popup-elevation (every Base UI menu and pane popover) follow. Rims stay separate: #20461's --ui-popup-edge hairline and #20481's --cmux-hairline rings are untouched.
  • Native: Elevation (offset, blur, alpha, layerRadius = blur / 2 for Core Animation, layerOffset(flipped:), shadow() for NSShadow). PopupStyle.standard.elevation is .elevation2, and the old shadowOffset/shadowBlur/shadowAlpha/shadow() are gone, so no call site can keep its own numbers.
  • appearance.borders = none clears rims only, so shadows stay. Reduce Transparency and Increase Contrast behave as before.

Inventory (before → after)

Web (webviews/src):

SurfaceFileBeforeAfterOwner
Every Base UI menu/popover, pane menus, model picker, omnibox, slash/@ menus, summary card, .nt-popover, .lg-paletteui/popupSurface.css --ui-popup-drop0 2px 8px 12%--elevation-2Leo 2841396b
Composer (rest and focus)acpmux/composerLocation.css0 8px 24px 18%--elevation-2Leo 0f8d6805 (#20303)
Permission cardacpmux/composerLocation.css0 4px 14px 10%--elevation-2Leo 0f8d6805
New Tab box (rest and focus)acpmux/newtab/screen.css0 12px 32px -18px 40%--elevation-2Lawrence 663bd736
New Tab box (old pane)acpmux/styles.css0 12px 32px -18px 40%--elevation-2Leo b75d89c9
New Tab selected kindacpmux/styles.css0 1px 2px 25%--elevation-1Leo b75d89c9
File search paletteacpmux/styles.css0 16px 40px 35%--elevation-2Leo e2e74d24
Pane sidebar overlayacpmux/styles.css0 8px 24px 35%--elevation-2Leo bd5b4722
Chat find baracpmux/conversation/conversation.cssfallback 0 4px 14px 18%fallback --elevation-2Leo 622e57ee
Jump-to-latest buttonacpmux/conversation/conversation.css0 4px 14px 14% + 0 1px 2px 10%--elevation-2Leo 641a5af3
Minimap hover cardacpmux/threadMinimap/threadMinimap.css0 4px 14px 9%--elevation-2Lawrence 225ea274
Handoff targets menuacpmux/handoff/styles.css0 4px 16px 12% (text color)--elevation-2Leo a15090ff
Add project dialogacpmux/addProject.css0 12px 36px 27%--elevation-2Leo 224eda31
PDF viewer / large page / pageacpmux/composerAttachments.css0 16px 44px 28% / 0 8px 24px 18% / 0 1px 2px 15%-2 / -2 / -1Leo e08117d6
Effort slider thumbacpmux/composerControls.css0 1px 3px 20%--elevation-1Lawrence 8a44d921
Tooltips (Base UI and title)pages/shared/desktop.css0 2px 8px 16%--elevation-2Leo 66dd3104
Page menupages/shared/pageBase.css0 6px 24px 18%--elevation-2Lawrence 82ae6117
Markdown link card/popoverpages/markdown/styles.css0 6px 24px 18%--elevation-2Lawrence 68855685
Cloud create sheetpages/cloud/styles.css0 12px 40px 22%--elevation-2Lawrence bdaaf04c
Bottom sheetui/ui.css .ui-sheet0 -8px 30px 22%--elevation-2Lawrence d5189aab
Channels side sheetpages/home-channels/ledger.css-12px 0 32px -16px 35%--elevation-2Lawrence c93267a0
Channels message toolbarpages/home-channels/styles.css0 2px 8px 12%--elevation-2Lawrence 9f8d556c
Icon picker baricon-picker/styles.css0 4px 18px 16%--elevation-2Lawrence 6d7b696a
Viewer pickerviewer-empty/styles.css0 14px 44px 24%--elevation-2Lawrence 99df3371
Diff base picker, toolbar menu, diff find barstyles.css0 16px 34px 28-30%--elevation-2Lawrence 14aa604e
Diff file-tree context menustyles.css0 8px 24px 18%--elevation-2Lawrence d13dde39
Diff pill buttonstyles.css0 6px 18px 22%--elevation-2Lawrence 14aa604e
Diff file search sidebarstyles.css-18px 0 42px 34%--elevation-2Lawrence b129a866
Diff comment cardstyles.css0 1px 2px 10% + 0 2px 8px 6%--elevation-1Lawrence ed02a990
Dev only: optchat tip, pane preview frame, gallery window, prototypevariousup to 0 24px 64px 50%--elevation-2Lawrence, Leo

Native (Packages/macOS/CmuxNext):

SurfaceFileBeforeAfterOwner
Popover host (hover cards, group editors, icon picker, omnibox suggestions, sidebar update notes, web popups)CmuxNextDesign/PopupStyle.swift PopupHostViewblur 8, offset 2, 12%.elevation2Leo e17da11b
ToastCmuxNextDesign/Toast/CmuxToastView.swiftblur 8, 12%.elevation2Leo
Command palette, page info bubble (NSShadow)CmuxNextPalette/PaletteContentView.swift, PageInfoPanel.swift (now drawn by PopupHostView)blur 8, 12%.elevation2Leo
Omnibar card topCmuxNextBrowser/UI/OmnibarViews.swiftblur 8, 12%.elevation2Leo
Omnibar bar (opt-in shadow)CmuxNextBrowser/UI/OmnibarViews.swiftCA radius 6 (≈ blur 12), 18%.elevation2Lawrence ff755bea
Tab drag ghostCmuxNextApp/Drag/TabDragGhostPanel.swiftCA radius 10 (≈ blur 20), offset 3, 28%.elevation2Lawrence a367ef42
Lifted tab, group chip, sidebar drag liftTabCell.swift, TabGroupChipCell.swift, DragLiftView.swiftCA radius 8 (≈ blur 16), 12%.elevation2Leo 16566c51
Docked column backdropCmuxNextLayout/Views/DockBackdropView.swiftCA radius 12, offset 0, 100% of the theme shadow color.elevation2Lawrence fa21a064
Task board cardCmuxNextTasks/Views/TasksBoardView.swiftradius 2 / 5 (hover), 30% / 60%.elevation1 / .elevation2Lawrence 2964b216
Page info switch knobPageInfoToggle.swiftCA radius 1, 18%.elevation1Lawrence ddeefff0

Left as they are:

  • Cmd-hold shortcut hint chips (ShortcutHintChipView, Lawrence's): CA radius 2 at 22% black. Tight, so unchanged; the alpha is above the scale's 16% cap, noted here rather than changed.
  • Group editor bubble (sidebar groups and tab group chips): it has no shadow code of its own and draws through PopupHostView, so it follows the shared value swap. Its openers and code are untouched (deferred to Lawrence's chief).
  • Three directional shadows became the plain downward step: the bottom sheet (0 -8px 30px), the diff file-search drawer (-18px 0 42px) and the Channels side sheet (-12px 0 32px -16px). Their rims carry the edge now.
  • The OptChat inspector's tip (0 4px 14px 18%): a standalone page bundled into optchat-chief without the desktop layer, so the tokens are undefined there; the web ratchet skips it.
  • System window shadows (hasShadow = true on the native tooltip, Quick Composer, restart notice, feed, notifications, update sheet and onboarding drag panels): AppKit's own panel shadow.
  • Focus glows (PaneOverlayView ring glow, launch mark), which are colored rings, not elevation.
  • Sidebar notice card and terminal find bar: they cast no shadow today, so nothing to retoken. No sidebar layout changes.

Captures

Fleet DEV builds on a cua-4 capture slot, the same seeded window: before = feat-cmux-next 772b6ea30a5b (build nxtip3-v1), after = this branch 10d7d6927907 (build subtleelev2). Dark, light, and dark over a desktop wallpaper. Each still is before | after. The mini's host name is blurred.

The difference is easiest to see on the New Tab box (right pane: the soft 32px halo under it is gone), the model picker and the command palette (a tight edge instead of a spread). The hover card was already on the shared popup shadow, so it changes only slightly.

dark 1 composer

dark 2 model picker

dark 3 palette

dark 4 hover card

light 1 composer

light 2 model picker

light 3 palette

light 4 hover card

wallpaper 1 composer

wallpaper 2 model picker

wallpaper 3 palette

wallpaper 4 hover card

Recording (dark, 8 s, watched): the main window from both builds side by side. debug.window_record captures the main window only, so it shows the composer and New Tab box at rest; the menus, palette and hover card are in the stills above.

plan image 20519/dark-1-composer.jpg
plan image 20519/dark-2-model-picker.jpg
plan image 20519/dark-3-palette.jpg
plan image 20519/dark-4-hover-card.jpg
plan image 20519/light-1-composer.jpg
plan image 20519/light-2-model-picker.jpg
plan image 20519/light-3-palette.jpg
plan image 20519/light-4-hover-card.jpg
plan image 20519/wallpaper-1-composer.jpg
plan image 20519/wallpaper-2-model-picker.jpg
plan image 20519/wallpaper-3-palette.jpg
plan image 20519/wallpaper-4-hover-card.jpg
plan gif 20519/elevation-dark-before-after.gif (from elevation-dark-before-after.mp4)
plan video 20519/elevation-dark-before-after.mp4
uploaded 20519/dark-1-composer.jpg
uploaded 20519/dark-2-model-picker.jpg
uploaded 20519/dark-3-palette.jpg
uploaded 20519/dark-4-hover-card.jpg
uploaded 20519/light-1-composer.jpg
uploaded 20519/light-2-model-picker.jpg
uploaded 20519/light-3-palette.jpg
uploaded 20519/light-4-hover-card.jpg
uploaded 20519/wallpaper-1-composer.jpg
uploaded 20519/wallpaper-2-model-picker.jpg
uploaded 20519/wallpaper-3-palette.jpg
uploaded 20519/wallpaper-4-hover-card.jpg
uploaded 20519/elevation-dark-before-after.gif
uploaded 20519/elevation-dark-before-after.mp4

Full quality:

Not captured: a toast, a tab drag and a native context menu. A no-activate slot can't raise them without closing tabs. The toast, the tab drag ghost and the lifted tab are covered by SubtleElevationTests below.

Testing (red and green)

  • Web, heavy --mem 4G bun test test/subtle-elevation.test.ts: red on 2da8059cd2ac (38 of 39 failed: no tokens, 30+ literal blurred drops, every swept surface on its own shadow) → green on the head (39/39). It fails on any blurred drop that is not --elevation-1/2, on tokens over 8px blur or 16% alpha (light and dark), when the native numbers drift from the web ones, and when a swept surface stops naming the scale.
  • Native, fleet run-swift-testing-suites.sh Packages/macOS/CmuxNext (Xcode 26.6, aws-m4pro):
    • red on 2da8059cd2ac (step key op-next-theme-shadows-red-2da8059cd2ac…): SubtleElevationTests 7 of 8 failed with 18 issues, for example the drag ghost at radius 10 / 28%, a lifted tab at radius 8, and the palette NSShadow at blur 8 / 12%. Every other suite passed.
    • green on 10d7d6927907 (op-next-theme-shadows-green-10d7d6927907…): 291/291 suites PASS, SubtleElevationTests 8/8. The updated PolishCalmChromeTests, PaletteShadowTests and OmnibarCardElevationTests pass too.
    • Both steps exit 1 only on the fleet's same-tree guard (AcpmuxBundleReplacedTests skipped: the head's cmux-tui tree isn't published). That is unrelated to this change.
    • The dock backdrop check in noNativeShadowPassesTheCap was added after the red run, so its red is not recorded.
  • Also passing: popup-surface.test.ts (browser lane, CI only; it now reads elevation2) and ui-rules.
  • Guards:
    • Clean: check-theme-scope, check-system-blue, check-motion, crash_ratchet (ok), check-no-godfiles --base <merge-base> (no touched file flagged), vp check, type-scale, text-focus.
    • Base reds in files this PR doesn't touch:
      • check-concurrency (ControlWorkspaceInfoCache.swift)
      • lint-ios-package-conventions (PaneAnimationsSetting.swift)
      • check-layers (summary.css raw z-index)
  • A review subagent found three problems, now fixed: the test target didn't compile (TabCell isn't a view), the OptChat inspector's committed page went stale, and a host without desktop.css would lose the popup shadow and rim (--ui-popup-drop now falls back to the same step).

Changelog

Changed: Menus, popovers, cards and the composer use a subtle shadow instead of a large soft one.

🤖 Generated with Claude Code