Add an admin-bar presentation mode to OS Settings and the theme system - #458
Merged
Conversation
Adds `adminBarMode` — a Static / Dynamic / Hidden tristate in
OS Settings → Appearance, modeled on the existing Window corners
control, controlling how the WordPress admin bar presents above the
shell.
- `static` — pinned above the shell. Vanilla behavior, and the default.
- `dynamic` — parks off the top edge behind a 4px seam and slides back
in when the pointer reaches the top of the viewport or
something inside it takes keyboard focus. Classic Windows
auto-hide taskbar.
- `hidden` — not rendered at all.
The mode reaches CSS as a `desktop-mode-admin-bar-<mode>` body class,
written twice on purpose: PHP emits it on `admin_body_class` so the
first paint is already correct (the bar has painted long before the
shell's JS boots, so a `hidden` user would otherwise see it flash on
every navigation), and the shell's apply pass re-writes it so a pick
in OS Settings takes effect without a reload.
`dynamic` uses `transform` rather than repositioning, because a
transformed element keeps hit-testing at its painted position — the
parked remainder stops catching the pointer the moment it goes
off-screen. The pointer target is deliberately larger than the visible
seam: an invisible `#wpadminbar::after` extends the hit area 16px below
the parked bar (hovering a pseudo-element counts as hovering its
originating element), giving a 20px band from the top of the viewport
while the seam stays a hairline. The zone collapses to 0 once the bar
is out, so it can't swallow clicks on windows underneath. Both
distances are tokens (`--desktop-mode-admin-bar-peek` and
`--desktop-mode-admin-bar-reveal-zone`) with the oscillation ceiling
documented on the latter.
`hidden` removes the admin bar's "Switch to Classic Admin" toggle, but
is not a one-way door: the dock's core rail always carries an "Exit
Desktop Mode" tile hitting the same endpoint, and system tiles aren't
reachable by `itemVisibility`. That dependency is called out in the
CSS, the hooks reference, and the section's own helper copy.
Surfaced to themes as a `recommendedOsSettings` key, wired through both
halves of the enum mirror, and to plugins as a new `Stable` filter,
`desktop_mode_admin_bar_mode`, for pinning the mode per request (kiosk
`hidden`, or forcing `static` for users who shouldn't lose the toggle).
Out-of-enum and non-string filter returns fail closed to `static`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sKW1mu1SGg5k9oSHgxv5s
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
adminBarMode— a Static / Dynamic / Hidden tristate in OS Settings → Appearance, modeled on the existing Window corners control, controlling how the WordPress admin bar presents above the shell.Screen.Recording.2026-07-30.at.17.36.04.mov
How the mode reaches CSS
A
desktop-mode-admin-bar-<mode>body class, written twice on purpose:admin_body_class— the admin bar paints long before the shell's JS boots, so without this ahiddenuser would see it flash on every navigation.Why
transform, and why the hit area is bigger than the seamdynamicmoves the bar withtransformrather than repositioning it, because a transformed element keeps hit-testing at its painted position — the parked remainder stops catching the pointer the moment it goes off-screen.That alone would make the seam and the pointer target the same 4px, which is a miserable thing to aim at. So an invisible
#wpadminbar::afterextends the hit area 16px below the parked bar — hovering a pseudo-element counts as hovering its originating element, so:hoverfires across a 20px band from the top of the viewport while the seam stays a hairline. The zone collapses to0once the bar is out, so it can't swallow clicks on windows underneath; it's also what keeps the reveal stable, since post-reveal the pointer is inside the bar's real box.Both distances are tokens on
:root:--desktop-mode-admin-bar-peek4px(visible seam)--desktop-mode-admin-bar-reveal-zone16px(invisible extension)There's a ceiling on the second one, documented on the token:
peek + reveal-zonemust stay under the bar's own height (32px, 46px below 783px). Past that, leaving the revealed bar drops the pointer back into the zone, which re-reveals it — an oscillation loop rather than a wider target. Coarse pointers get10px + 20pxand reveal on:active.Verified against the running WordPress that core ships no bare
#wpadminbar::before/::after(278#wpadminbarrules, none of them that) and that its fiveoverflow: hiddenrules are all on descendants, so nothing clips the zone.hiddenis not a one-way doorIt removes the admin bar's "Switch to Classic Admin" toggle, but the dock's core rail always carries an Exit Desktop Mode tile hitting the same
save-desktop-modeendpoint, and system tiles aren't reachable byitemVisibility(that only filters menu items and desktop icons). Thedesktop.csscomment claiming the admin bar is "the ONLY escape hatch" was already stale; that dependency is now called out in the CSS, the hooks reference, and the section's own helper copy. If system tiles ever become hideable,hiddenbecomes a trap.Extensibility
adminBarModeis arecommendedOsSettingskey (static|dynamic|hidden), wired through both halves of the enum mirror (desktop_mode_desktop_theme_recommended_os_settings_schema()andsrc/desktop-themes/recommended.ts). Usual once-per-user contract: it never overwrites a later choice. A theme wanting an edge-to-edge desk recommendsdynamic.Stablefilterdesktop_mode_admin_bar_modepins the mode per request (kioskhidden, or forcingstaticfor users who shouldn't lose the toggle). Out-of-enum and non-string returns fail closed tostatic, deliberately without a(string)cast so an array return can't emit an "Array to string conversion" warning on its way to failing anyway.Known trade-off
In
dynamicmode the top ~20px of the desktop is reveal territory. That's inherent to auto-hide UI (Windows does the same at the bottom edge) and no worse in practice than the old 4px, since hovering brings out a bar that covers 32px regardless. Window drags are naturally immune —setPointerCaptureroutes events to the dragged window, so#wpadminbar:hoverdoesn't fire mid-drag.Docs
hooks-reference.md(new filter entry + the recommendation-schema count),javascript-reference.md(snapshot key, apply-live list,RecommendedOsSettingstable),desktop-themes.md(fields table + manifest example).Testing
npm run build,lint,typecheck,test:js(2538 tests) and the full PHPUnit suite (1687 tests) all pass. The one CSS minify warning is pre-existing inwidget-drafts.css, untouched here.New coverage:
tests/vitest/os-settings-admin-bar-mode.test.ts— class exclusivity across modes, switching clears the previous class, default, unknown-value fallback at deserialization (not just paint), snapshot exposure.desktopModeRender.php— body class carries the default and the saved mode, omitted when desktop mode is off, filter override, filter-result validation.osSettings.php— sanitizer round-trip plus the two fallback paths.desktop-themes-recommended.test.ts— every mode accepted, unknown mode drops, updated key-list assertion.Manual QA
🤖 Generated with Claude Code
https://claude.ai/code/session_015sKW1mu1SGg5k9oSHgxv5s