PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 0.9.0
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v0.9.0
1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 All 34 releases
desktop-mode / assets / css / chromeless.css

chromeless.css in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 0.9.0, at assets/css/chromeless.css

429 lines 18.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Desktop Mode — Chromeless Overrides.
3 *
4 * CSS adjustments for legacy admin pages rendered inside desktop mode
5 * iframes (chromeless mode). All rules are scoped to .desktop-mode-chromeless
6 * so they never affect the classic admin or the desktop shell.
7 *
8 * Plugin and theme developers: to add your own chromeless overrides,
9 * enqueue a stylesheet on the 'desktop_mode_chromeless_styles' action.
10 * Your CSS just needs to target .desktop-mode-chromeless as the body class.
11 *
12 * Example:
13 * add_action( 'desktop_mode_chromeless_styles', function() {
14 * wp_enqueue_style( 'my-plugin-chromeless', plugin_dir_url( __FILE__ ) . 'chromeless.css' );
15 * } );
16 *
17 * @since 0.1.0
18 */
19
20 /* ---------------------------------------------------------------
21 * Hide the classic admin chrome elements. These would otherwise
22 * reserve space (sidebar gutter, footer, admin bar) around the
23 * chromeless page content.
24 * --------------------------------------------------------------- */
25 .desktop-mode-chromeless #adminmenuwrap,
26 .desktop-mode-chromeless #adminmenuback,
27 .desktop-mode-chromeless #wpfooter,
28 .desktop-mode-chromeless #wpadminbar,
29 .desktop-mode-chromeless .wp-responsive-toggle,
30 .desktop-mode-chromeless #collapse-menu {
31 display: none !important;
32 }
33
34 /*
35 * _wp_admin_html_begin() adds the `wp-toolbar` class to <html> whenever
36 * is_admin_bar_showing() is true — which, in admin, is unconditional.
37 * The class carries a `padding-top: var(--wp-admin--admin-bar--height)`
38 * that would leave a 32px (or 46px) dead gap at the top of the iframe.
39 * Zero it out inside chromeless iframes.
40 *
41 * We also rebind `--wp-admin--admin-bar--height` (and the derived
42 * `--wp-admin--admin-bar--position-offset` from block-library) to 0px
43 * inside chromeless so plugins that position UI relative to the admin
44 * bar resolve their math against the iframe's actual chrome state.
45 *
46 * WooCommerce's activity-panel wrapper, the block editor's sticky
47 * header, and several others use `top: var(--wp-admin--admin-bar--height)`
48 * to clear the bar. Without this override they reserve a 32px (or 46px
49 * on small screens) gap that no longer exists, producing visible jumps
50 * on first paint and dead space at the top of the content area.
51 */
52 html.wp-toolbar:has( body.desktop-mode-chromeless ) {
53 padding-top: 0 !important;
54 --wp-admin--admin-bar--height: 0px;
55 --wp-admin--admin-bar--position-offset: 0px;
56 }
57
58 /* Remove the sidebar gutter on #wpcontent and let the body fill the frame. */
59 .desktop-mode-chromeless #wpcontent {
60 margin-inline-start: 0 !important;
61 padding-inline-start: 0 !important;
62 }
63
64 /*
65 * WooCommerce sidebar-reservation override.
66 *
67 * `.woocommerce-layout__header` is a `position: fixed` bar WC
68 * mounts on every wc-admin and wc-embedded page. Its width is
69 * compiled from the SCSS source (header/style.scss) as a literal:
70 *
71 * position: fixed;
72 * top: 32px; // $adminbar-height
73 * width: calc(100% - 160px); // reserves classic sidebar
74 * z-index: 1001;
75 *
76 * The 160px subtraction is the classic admin menu width — WC bakes
77 * it in at build time so the header doesn't overlap the sidebar
78 * in standard admin. Inside chromeless we hide the sidebar, but
79 * the header keeps the reservation — so the header ends 160px
80 * short of the iframe right edge, and `.woocommerce-layout__activity-panel-wrapper`
81 * (`position: absolute; right: 0; top: 100%; transform: translateX(100%)`,
82 * containing block = the fixed header) translates past the
83 * header's right edge, NOT past the iframe's right edge. The
84 * resulting visible strip is exactly 160px wide — the size of the
85 * sidebar gap WC reserved for nothing.
86 *
87 * Reclaim the reservation: pin the header to full iframe width
88 * inside chromeless. The activity panel then translates past the
89 * iframe edge as WC's design intended, the gray strip disappears,
90 * and the header's content uses the full window width. No
91 * transform / overflow / visibility tricks needed.
92 */
93 .desktop-mode-chromeless .woocommerce-layout__header {
94 width: 100% !important;
95 }
96
97 /* ---------------------------------------------------------------
98 * Screen Meta (Screen Options / Help panels)
99 * The toggle buttons (#screen-meta-links) are hidden because
100 * the parent desktop shell adds its own buttons to the window
101 * title bar. The panels themselves stay visible and functional —
102 * they're toggled via postMessage from the parent.
103 *
104 * Collapse all margins on the hidden links and the panel container
105 * so they don't leave a gap at the top of the iframe content.
106 * --------------------------------------------------------------- */
107 .desktop-mode-chromeless #screen-meta-links {
108 display: none;
109 margin: 0;
110 }
111
112 .desktop-mode-chromeless #screen-meta {
113 margin: 0;
114 border: none;
115 }
116
117 /* ---------------------------------------------------------------
118 * Wrap container
119 * Remove the default left margin that accounts for the sidebar
120 * which doesn't exist in chromeless mode.
121 * --------------------------------------------------------------- */
122 .desktop-mode-chromeless .wrap {
123 margin: 0;
124 }
125
126 /* ---------------------------------------------------------------
127 * Page title & header
128 * The window title bar already shows the page title, so the
129 * in-page <h1> + header separator (.wp-header-end) are redundant
130 * inside chromeless iframes. Hide them so the window content
131 * starts flush.
132 *
133 * `.page-title-action` (the "Add New" / "Add Order" button next to
134 * the H1) stays VISIBLE by default — it's the only entry point to
135 * the add-new flow on many plugin pages (WooCommerce Orders, custom
136 * CPTs, plugin settings pages, etc.). Earlier versions of this CSS
137 * hid it on the assumption that the submenu tab strip exposed the
138 * same action — which holds for WP Core pages with a registered
139 * "Add New" submenu (Posts, Pages, Users) but breaks every third-
140 * party plugin page that has no submenu equivalent. A small bit of
141 * redundancy with the submenu strip on Core pages is the better
142 * trade-off vs. losing the primary affordance on plugin pages
143 * entirely.
144 *
145 * Sites that prefer the cleaner Core-page look can hide the button
146 * on specific pages via the `desktop_mode_chromeless_styles`
147 * action — e.g.:
148 *
149 * body.edit-php .wrap > .page-title-action { display: none; }
150 *
151 * --------------------------------------------------------------- */
152 .desktop-mode-chromeless .wrap > h1,
153 .desktop-mode-chromeless .wrap > h1.wp-heading-inline,
154 .desktop-mode-chromeless #wpbody-content > .wrap > h1,
155 .desktop-mode-chromeless .wrap > .wp-header-end {
156 display: none;
157 }
158
159 /*
160 * The H1 above it is hidden, so the button would float at the very
161 * top of the iframe area without breathing room — give it a small
162 * margin so it lands cleanly. inline-block matches WP Core's
163 * computed style on this element (matters for vertical-align with
164 * any adjacent inline content like the `.subsubsub` filter row).
165 *
166 * Themes.php's native "Add Theme" button lives in the submenu tab
167 * strip via `desktop_mode_inject_appearance_tabs`
168 * (includes/themes-tabs.php), so its in-page page-title-action is
169 * redundant on that one screen — keep the per-page hide rule below
170 * so we don't ship two "Add Theme" entry points.
171 */
172 .desktop-mode-chromeless .wrap > .page-title-action {
173 display: inline-block;
174 margin-top: 12px;
175 }
176
177 .desktop-mode-chromeless.themes-php .wrap > .page-title-action {
178 display: none;
179 }
180
181 /* ---------------------------------------------------------------
182 * WooCommerce "embed page" header overlay — page-scoped.
183 *
184 * Background: WC renders TWO layouts simultaneously on "connected"
185 * pages like `wc-orders` — the PHP-rendered legacy `.wrap` (with
186 * the h1 + the "Add order" `.page-title-action`) AND a React-mounted
187 * `EmbedHeader` (`client/admin/client/header/embed.tsx`) that
188 * `position: fixed`-overlays the page from the top. References:
189 * - `src/Internal/Admin/Loader.php::embed_page_header`
190 * - `includes/react-admin/connect-existing-pages.php` (registers
191 * wc-orders as a connected page when HPOS is on)
192 * - `client/admin/client/header/shared.tsx::useUpdateBodyMargin`
193 * (the hook that pushes `#wpbody.style.marginTop` to make
194 * room for the fixed header)
195 *
196 * The conflict, narrowly:
197 * 1. The legacy `.wrap > .page-title-action` ("Add order") at
198 * the top of the document body — primary add-new affordance.
199 * 2. The React `EmbedHeader` overlays the top of the iframe with
200 * a redundant `<h1>Orders</h1>` and an Activity Panel toggle.
201 *
202 * The fixed React header OBSCURES the legacy `.page-title-action`
203 * button when we reset `#wpbody`'s margin-top.
204 *
205 * Scope: ONLY the pages where the dual rendering is genuinely a
206 * problem — `wc-orders` (Orders list, the user-reported failure
207 * mode) and `wc-orders--shop_order` (the trash view variant). Other
208 * WC-admin pages (Analytics, Marketing, Customers, Coupons,
209 * Products, Reports, …) DON'T render a competing legacy `.wrap`
210 * with a `.page-title-action` button; their primary content IS the
211 * React app. On those pages the Activity Panel + EmbedHeader are
212 * the only header affordance the user has, so we keep them visible.
213 *
214 * If new HPOS-style connected pages emerge where the same dual
215 * layout creates the same conflict, add their per-page body class
216 * (`.woocommerce_page_<slug>`) to the selector — don't broaden the
217 * rule to `.woocommerce-admin-page` (that would silently strip the
218 * Activity Panel from every WC screen).
219 *
220 * @since 0.8.9
221 */
222 .desktop-mode-chromeless.woocommerce_page_wc-orders .woocommerce-layout__header,
223 .desktop-mode-chromeless.woocommerce_page_wc-orders--shop_order .woocommerce-layout__header {
224 display: none !important;
225 }
226 .desktop-mode-chromeless.woocommerce_page_wc-orders #wpbody,
227 .desktop-mode-chromeless.woocommerce_page_wc-orders--shop_order #wpbody {
228 margin-top: 0 !important;
229 }
230
231 /* ---------------------------------------------------------------
232 * Dashboard welcome panel
233 * The default 16px top margin pushes the panel down and creates a
234 * visible gap at the top of the iframe. Collapse it in chromeless
235 * mode so the panel sits flush with the top of the window body.
236 * --------------------------------------------------------------- */
237 .desktop-mode-chromeless #welcome-panel,
238 .desktop-mode-chromeless .welcome-panel {
239 margin-top: 0.5em;
240 }
241
242 /* ---------------------------------------------------------------
243 * Footer
244 * The classic footer is not rendered in chromeless mode,
245 * but some pages have bottom padding assuming it exists.
246 * --------------------------------------------------------------- */
247 .desktop-mode-chromeless #wpbody-content {
248 margin: 0;
249 padding: 0 8px 8px;
250 float: none;
251 width: auto;
252 }
253
254 /* ---------------------------------------------------------------
255 * Block Editor — hide Gutenberg chrome that would break the window.
256 *
257 * The fullscreen-mode close button (the "W" logo top-left of the editor)
258 * is an <a href="/wp-admin/edit.php"> that navigates the iframe to a
259 * non-chromeless URL — which re-renders the entire classic admin
260 * inside our desktop window. The link interceptor in the chromeless
261 * bridge catches it too, but hiding the button removes the visual
262 * affordance so users never try to click "back to dashboard" inside
263 * what looks like a self-contained window.
264 *
265 * The site editor's equivalent navigation affordances (site hub toggle,
266 * "back to dashboard" link) get the same treatment.
267 *
268 * The welcome guide is intentionally NOT hidden anymore. Earlier
269 * iterations both CSS-hid the dialog AND flipped `core/edit-post:
270 * welcomeGuide` to `false` at the data layer on every chromeless
271 * mount — but Gutenberg already persists the user's "Get started"
272 * dismissal to user meta the moment they close it, so the override
273 * was permanently stealing the one-time orientation tour from every
274 * user who never got to see it. Modal's focus trap is Tab-only and
275 * doesn't fight the shell. Let it run. (See git history for the
276 * removed `desktop_mode_chromeless_editor_preferences` override.)
277 * --------------------------------------------------------------- */
278 .desktop-mode-chromeless .edit-post-fullscreen-mode-close,
279 .desktop-mode-chromeless .edit-post-fullscreen-mode-close__view-mode-toggle,
280 .desktop-mode-chromeless .edit-site-navigation-link,
281 .desktop-mode-chromeless .edit-site-site-hub,
282 .desktop-mode-chromeless .edit-site-site-hub__toggle {
283 display: none !important;
284 }
285
286 /* ---------------------------------------------------------------
287 * Block Editor & Site Editor — full-bleed layouts.
288 * Gutenberg owns its entire viewport (its own header bar, side
289 * panels, etc.). Any padding around #wpbody-content crops the
290 * editor and breaks its layout, so reset to zero on those pages
291 * and let the editor render edge-to-edge.
292 * --------------------------------------------------------------- */
293 .desktop-mode-chromeless.block-editor-page #wpbody-content,
294 .desktop-mode-chromeless.site-editor-php #wpbody-content,
295 .desktop-mode-chromeless.is-fullscreen-mode #wpbody-content {
296 padding: 0;
297 }
298
299 /* ---------------------------------------------------------------
300 * "Boot" SPA pages (Font Library, Options → Connectors).
301 *
302 * These screens mount a React app into `.boot-layout-container` and
303 * paint `#wpwrap` a dark `#1e1e1e` via inline <style>. Our 8px
304 * right/bottom padding on `#wpbody-content` lets that dark color
305 * bleed through as two black gaps on the right and bottom edges of
306 * the window. Zero the padding for any page hosting a boot layout
307 * — detection is purely structural (`:has()`), so a plugin that
308 * adopts the same container class inherits the fix automatically.
309 * --------------------------------------------------------------- */
310 .desktop-mode-chromeless #wpbody-content:has( .boot-layout-container ) {
311 padding: 0;
312 }
313
314 .desktop-mode-chromeless:has( .boot-layout-container ) #wpwrap {
315 /* Neutralize the inline dark background so any stray overflow
316 * matches the window body instead of flashing black. */
317 background: transparent;
318 }
319
320 /*
321 * `boot-layout__surfaces` is the white card the React app draws its
322 * content onto. In classic admin it sits on the dark #wpwrap with
323 * rounded corners and a margin that makes it read as a "card." Inside
324 * a desktop window the surface IS the content — rounded corners and
325 * side margins leave ugly bleed at the edges. Collapse it to a plain
326 * edge-to-edge surface.
327 */
328 .desktop-mode-chromeless .boot-layout__surfaces,
329 .desktop-mode-chromeless .boot-layout__stage,
330 .desktop-mode-chromeless .boot-layout__canvas {
331 border-radius: 0 !important;
332 margin: 0 !important;
333 }
334
335 /*
336 * The boot screens render their own H1 (`.boot-navigation-screen__title`,
337 * e.g., "Fonts") at the top of the surface. The desktop window already
338 * shows the page title in its title bar, so the in-page title is
339 * redundant. Visually hide it but keep it in the DOM for screen readers
340 * and for the boot app's own focus management.
341 */
342 .desktop-mode-chromeless .boot-navigation-screen__title,
343 .desktop-mode-chromeless .boot-navigation-screen__title-icon {
344 position: absolute;
345 width: 1px;
346 height: 1px;
347 padding: 0;
348 margin: -1px;
349 overflow: hidden;
350 clip: rect( 0, 0, 0, 0 );
351 white-space: nowrap;
352 border: 0;
353 }
354
355 /* ---------------------------------------------------------------
356 * Snackbars
357 *
358 * `components-snackbar-list` (Gutenberg `@wordpress/components`) and
359 * `boot-notices__snackbar` (Font Library / Connectors) position
360 * themselves `fixed` at the viewport bottom. Because a chromeless
361 * iframe IS its own viewport, those anchors already land inside the
362 * window — but our 8px bottom padding on `#wpbody-content` (applied
363 * to non-boot pages) plus some plugin-level `bottom: 40px` offsets
364 * meant for the classic admin footer can push them out of sight.
365 *
366 * Pin them flush to the bottom of the iframe and make sure nothing
367 * in the window chrome can occlude them.
368 *
369 * Triggering a snackbar to verify (desktop mode enabled):
370 * - Open Posts → Add New, save/publish → "Post published" toast.
371 * - Open Media, upload a file → "Uploaded" toast.
372 * - Open Appearance → Fonts, install a Google Font → toast.
373 * --------------------------------------------------------------- */
374 .desktop-mode-chromeless .components-snackbar-list,
375 .desktop-mode-chromeless .boot-notices__snackbar {
376 bottom: 16px !important;
377 z-index: 100000;
378 }
379
380 /*
381 * Force the Gutenberg interface skeleton to fill the iframe regardless
382 * of fullscreen state. When fullscreenMode is off, Gutenberg hardcodes
383 * `top: 32px` (admin bar reservation) and `left: 160px` (sidebar
384 * reservation) on `.interface-interface-skeleton` — both chromes we've
385 * already hidden, so those offsets become dead gaps at the top and
386 * left of the window. Zero them out unconditionally.
387 *
388 * The default is fullscreen (inset: 0), but users who turned fullscreen
389 * off in classic admin carry that preference into chromeless via the
390 * shared persistence layer. This rule keeps chromeless rendering
391 * edge-to-edge either way.
392 */
393 .desktop-mode-chromeless .interface-interface-skeleton {
394 top: 0 !important;
395 left: 0 !important;
396 right: 0 !important;
397 bottom: 0 !important;
398 }
399
400 /* ---------------------------------------------------------------
401 * Notices
402 * Give notices a bit of top margin since there's no admin bar above.
403 * --------------------------------------------------------------- */
404 .desktop-mode-chromeless .notice:first-child {
405 margin-top: 4px;
406 }
407
408 /* ---------------------------------------------------------------
409 * Update nag
410 * Red top border signals a pending WordPress update; keep it so
411 * the nag reads as urgent even without the surrounding admin chrome.
412 * --------------------------------------------------------------- */
413 .desktop-mode-chromeless .update-nag {
414 border-top: 1px solid #d63638;
415 }
416
417 /* ---------------------------------------------------------------
418 * Suppress WordPress's native command palette inside chromeless
419 * iframes. The chromeless bridge intercepts Cmd+K and forwards to
420 * the desktop shell, but if an in-page component triggers the
421 * palette via another path (programmatic dispatch, a menu item)
422 * the dialog would still render. Hide it so the shell's palette
423 * stays the only surface users ever see.
424 * --------------------------------------------------------------- */
425 .desktop-mode-chromeless .commands-command-menu__container,
426 .desktop-mode-chromeless .commands-command-menu {
427 display: none !important;
428 }
429