/**
* Desktop Mode — Chromeless Overrides.
*
* CSS adjustments for legacy admin pages rendered inside desktop mode
* iframes (chromeless mode). All rules are scoped to .desktop-mode-chromeless
* so they never affect the classic admin or the desktop shell.
*
* Plugin and theme developers: to add your own chromeless overrides,
* enqueue a stylesheet on the 'desktop_mode_chromeless_styles' action.
* Your CSS just needs to target .desktop-mode-chromeless as the body class.
*
* Example:
* add_action( 'desktop_mode_chromeless_styles', function() {
* wp_enqueue_style( 'my-plugin-chromeless', plugin_dir_url( __FILE__ ) . 'chromeless.css' );
* } );
*
* @since 0.1.0
*/
/* ---------------------------------------------------------------
* Hide the classic admin chrome elements. These would otherwise
* reserve space (sidebar gutter, footer, admin bar) around the
* chromeless page content.
* --------------------------------------------------------------- */
.desktop-mode-chromeless #adminmenuwrap,
.desktop-mode-chromeless #adminmenuback,
.desktop-mode-chromeless #wpfooter,
.desktop-mode-chromeless #wpadminbar,
.desktop-mode-chromeless .wp-responsive-toggle,
.desktop-mode-chromeless #collapse-menu {
display: none !important;
}
/*
* _wp_admin_html_begin() adds the `wp-toolbar` class to whenever
* is_admin_bar_showing() is true — which, in admin, is unconditional.
* The class carries a `padding-top: var(--wp-admin--admin-bar--height)`
* that would leave a 32px (or 46px) dead gap at the top of the iframe.
* Zero it out inside chromeless iframes.
*
* We also rebind `--wp-admin--admin-bar--height` (and the derived
* `--wp-admin--admin-bar--position-offset` from block-library) to 0px
* inside chromeless so plugins that position UI relative to the admin
* bar resolve their math against the iframe's actual chrome state.
*
* WooCommerce's activity-panel wrapper, the block editor's sticky
* header, and several others use `top: var(--wp-admin--admin-bar--height)`
* to clear the bar. Without this override they reserve a 32px (or 46px
* on small screens) gap that no longer exists, producing visible jumps
* on first paint and dead space at the top of the content area.
*/
html.wp-toolbar:has( body.desktop-mode-chromeless ) {
padding-top: 0 !important;
--wp-admin--admin-bar--height: 0px;
--wp-admin--admin-bar--position-offset: 0px;
}
/* Remove the sidebar gutter on #wpcontent and let the body fill the frame. */
.desktop-mode-chromeless #wpcontent {
margin-inline-start: 0 !important;
padding-inline-start: 0 !important;
}
/*
* WooCommerce sidebar-reservation override.
*
* `.woocommerce-layout__header` is a `position: fixed` bar WC
* mounts on every wc-admin and wc-embedded page. Its width is
* compiled from the SCSS source (header/style.scss) as a literal:
*
* position: fixed;
* top: 32px; // $adminbar-height
* width: calc(100% - 160px); // reserves classic sidebar
* z-index: 1001;
*
* The 160px subtraction is the classic admin menu width — WC bakes
* it in at build time so the header doesn't overlap the sidebar
* in standard admin. Inside chromeless we hide the sidebar, but
* the header keeps the reservation — so the header ends 160px
* short of the iframe right edge, and `.woocommerce-layout__activity-panel-wrapper`
* (`position: absolute; right: 0; top: 100%; transform: translateX(100%)`,
* containing block = the fixed header) translates past the
* header's right edge, NOT past the iframe's right edge. The
* resulting visible strip is exactly 160px wide — the size of the
* sidebar gap WC reserved for nothing.
*
* Reclaim the reservation: pin the header to full iframe width
* inside chromeless. The activity panel then translates past the
* iframe edge as WC's design intended, the gray strip disappears,
* and the header's content uses the full window width. No
* transform / overflow / visibility tricks needed.
*/
.desktop-mode-chromeless .woocommerce-layout__header {
width: 100% !important;
}
/* ---------------------------------------------------------------
* Screen Meta (Screen Options / Help panels)
* The toggle buttons (#screen-meta-links) are hidden because
* the parent desktop shell adds its own buttons to the window
* title bar. The panels themselves stay visible and functional —
* they're toggled via postMessage from the parent.
*
* Collapse all margins on the hidden links and the panel container
* so they don't leave a gap at the top of the iframe content.
* --------------------------------------------------------------- */
.desktop-mode-chromeless #screen-meta-links {
display: none;
margin: 0;
}
.desktop-mode-chromeless #screen-meta {
margin: 0;
border: none;
}
/* ---------------------------------------------------------------
* Wrap container
* Remove the default left margin that accounts for the sidebar
* which doesn't exist in chromeless mode.
* --------------------------------------------------------------- */
.desktop-mode-chromeless .wrap {
margin: 0;
}
/* ---------------------------------------------------------------
* Page title & header
* The window title bar already shows the page title, so the
* in-page
+ header separator (.wp-header-end) are redundant
* inside chromeless iframes. Hide them so the window content
* starts flush.
*
* `.page-title-action` (the "Add New" / "Add Order" button next to
* the H1) stays VISIBLE by default — it's the only entry point to
* the add-new flow on many plugin pages (WooCommerce Orders, custom
* CPTs, plugin settings pages, etc.). Earlier versions of this CSS
* hid it on the assumption that the submenu tab strip exposed the
* same action — which holds for WP Core pages with a registered
* "Add New" submenu (Posts, Pages, Users) but breaks every third-
* party plugin page that has no submenu equivalent. A small bit of
* redundancy with the submenu strip on Core pages is the better
* trade-off vs. losing the primary affordance on plugin pages
* entirely.
*
* Sites that prefer the cleaner Core-page look can hide the button
* on specific pages via the `desktop_mode_chromeless_styles`
* action — e.g.:
*
* body.edit-php .wrap > .page-title-action { display: none; }
*
* --------------------------------------------------------------- */
.desktop-mode-chromeless .wrap > h1,
.desktop-mode-chromeless .wrap > h1.wp-heading-inline,
.desktop-mode-chromeless #wpbody-content > .wrap > h1,
.desktop-mode-chromeless .wrap > .wp-header-end {
display: none;
}
/*
* The H1 above it is hidden, so the button would float at the very
* top of the iframe area without breathing room — give it a small
* margin so it lands cleanly. inline-block matches WP Core's
* computed style on this element (matters for vertical-align with
* any adjacent inline content like the `.subsubsub` filter row).
*
* Themes.php's native "Add Theme" button lives in the submenu tab
* strip via `desktop_mode_inject_appearance_tabs`
* (includes/themes-tabs.php), so its in-page page-title-action is
* redundant on that one screen — keep the per-page hide rule below
* so we don't ship two "Add Theme" entry points.
*/
.desktop-mode-chromeless .wrap > .page-title-action {
display: inline-block;
margin-top: 12px;
}
.desktop-mode-chromeless.themes-php .wrap > .page-title-action {
display: none;
}
/* ---------------------------------------------------------------
* WooCommerce "embed page" header overlay — page-scoped.
*
* Background: WC renders TWO layouts simultaneously on "connected"
* pages like `wc-orders` — the PHP-rendered legacy `.wrap` (with
* the h1 + the "Add order" `.page-title-action`) AND a React-mounted
* `EmbedHeader` (`client/admin/client/header/embed.tsx`) that
* `position: fixed`-overlays the page from the top. References:
* - `src/Internal/Admin/Loader.php::embed_page_header`
* - `includes/react-admin/connect-existing-pages.php` (registers
* wc-orders as a connected page when HPOS is on)
* - `client/admin/client/header/shared.tsx::useUpdateBodyMargin`
* (the hook that pushes `#wpbody.style.marginTop` to make
* room for the fixed header)
*
* The conflict, narrowly:
* 1. The legacy `.wrap > .page-title-action` ("Add order") at
* the top of the document body — primary add-new affordance.
* 2. The React `EmbedHeader` overlays the top of the iframe with
* a redundant `Orders
` and an Activity Panel toggle.
*
* The fixed React header OBSCURES the legacy `.page-title-action`
* button when we reset `#wpbody`'s margin-top.
*
* Scope: ONLY the pages where the dual rendering is genuinely a
* problem — `wc-orders` (Orders list, the user-reported failure
* mode) and `wc-orders--shop_order` (the trash view variant). Other
* WC-admin pages (Analytics, Marketing, Customers, Coupons,
* Products, Reports, …) DON'T render a competing legacy `.wrap`
* with a `.page-title-action` button; their primary content IS the
* React app. On those pages the Activity Panel + EmbedHeader are
* the only header affordance the user has, so we keep them visible.
*
* If new HPOS-style connected pages emerge where the same dual
* layout creates the same conflict, add their per-page body class
* (`.woocommerce_page_`) to the selector — don't broaden the
* rule to `.woocommerce-admin-page` (that would silently strip the
* Activity Panel from every WC screen).
*
* @since 0.8.9
*/
.desktop-mode-chromeless.woocommerce_page_wc-orders .woocommerce-layout__header,
.desktop-mode-chromeless.woocommerce_page_wc-orders--shop_order .woocommerce-layout__header {
display: none !important;
}
.desktop-mode-chromeless.woocommerce_page_wc-orders #wpbody,
.desktop-mode-chromeless.woocommerce_page_wc-orders--shop_order #wpbody {
margin-top: 0 !important;
}
/* ---------------------------------------------------------------
* Dashboard welcome panel
* The default 16px top margin pushes the panel down and creates a
* visible gap at the top of the iframe. Collapse it in chromeless
* mode so the panel sits flush with the top of the window body.
* --------------------------------------------------------------- */
.desktop-mode-chromeless #welcome-panel,
.desktop-mode-chromeless .welcome-panel {
margin-top: 0.5em;
}
/* ---------------------------------------------------------------
* Footer
* The classic footer is not rendered in chromeless mode,
* but some pages have bottom padding assuming it exists.
* --------------------------------------------------------------- */
.desktop-mode-chromeless #wpbody-content {
margin: 0;
padding: 0 8px 8px;
float: none;
width: auto;
}
/* ---------------------------------------------------------------
* Block Editor — hide Gutenberg chrome that would break the window.
*
* The fullscreen-mode close button (the "W" logo top-left of the editor)
* is an that navigates the iframe to a
* non-chromeless URL — which re-renders the entire classic admin
* inside our desktop window. The link interceptor in the chromeless
* bridge catches it too, but hiding the button removes the visual
* affordance so users never try to click "back to dashboard" inside
* what looks like a self-contained window.
*
* The site editor's equivalent navigation affordances (site hub toggle,
* "back to dashboard" link) get the same treatment.
*
* The welcome guide is intentionally NOT hidden anymore. Earlier
* iterations both CSS-hid the dialog AND flipped `core/edit-post:
* welcomeGuide` to `false` at the data layer on every chromeless
* mount — but Gutenberg already persists the user's "Get started"
* dismissal to user meta the moment they close it, so the override
* was permanently stealing the one-time orientation tour from every
* user who never got to see it. Modal's focus trap is Tab-only and
* doesn't fight the shell. Let it run. (See git history for the
* removed `desktop_mode_chromeless_editor_preferences` override.)
* --------------------------------------------------------------- */
.desktop-mode-chromeless .edit-post-fullscreen-mode-close,
.desktop-mode-chromeless .edit-post-fullscreen-mode-close__view-mode-toggle,
.desktop-mode-chromeless .edit-site-navigation-link,
.desktop-mode-chromeless .edit-site-site-hub,
.desktop-mode-chromeless .edit-site-site-hub__toggle {
display: none !important;
}
/* ---------------------------------------------------------------
* Block Editor & Site Editor — full-bleed layouts.
* Gutenberg owns its entire viewport (its own header bar, side
* panels, etc.). Any padding around #wpbody-content crops the
* editor and breaks its layout, so reset to zero on those pages
* and let the editor render edge-to-edge.
* --------------------------------------------------------------- */
.desktop-mode-chromeless.block-editor-page #wpbody-content,
.desktop-mode-chromeless.site-editor-php #wpbody-content,
.desktop-mode-chromeless.is-fullscreen-mode #wpbody-content {
padding: 0;
}
/* ---------------------------------------------------------------
* "Boot" SPA pages (Font Library, Options → Connectors).
*
* These screens mount a React app into `.boot-layout-container` and
* paint `#wpwrap` a dark `#1e1e1e` via inline