| 1 |
/** |
| 2 |
* OpenStation — Chromeless Overrides. |
| 3 |
* |
| 4 |
* CSS adjustments for legacy admin pages rendered inside OpenStation |
| 5 |
* iframes (chromeless mode). All rules are scoped to .os-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 'openstation_chromeless_styles' action. |
| 10 |
* Your CSS just needs to target .os-chromeless as the body class. |
| 11 |
* |
| 12 |
* Example: |
| 13 |
* add_action( 'openstation_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 |
.os-chromeless #adminmenuwrap, |
| 26 |
.os-chromeless #adminmenuback, |
| 27 |
.os-chromeless #wpfooter, |
| 28 |
.os-chromeless #wpadminbar, |
| 29 |
.os-chromeless .wp-responsive-toggle, |
| 30 |
.os-chromeless #collapse-menu { |
| 31 |
display: none !important; |
| 32 |
} |
| 33 |
|
| 34 |
/* --------------------------------------------------------------- |
| 35 |
* The iframe paints its own canvas. |
| 36 |
* |
| 37 |
* An admin page leaves whole regions unpainted — the gutter around |
| 38 |
* `#wpbody-content`, the strip behind `.subsubsub` and `.tablenav`, |
| 39 |
* everything below short content. In a normal admin tab the browser |
| 40 |
* canvas is white underneath, so nobody notices. Inside an iframe the |
| 41 |
* canvas is TRANSPARENT, and what shows through is the window element |
| 42 |
* behind it — `--os-window-bg`. |
| 43 |
* |
| 44 |
* That was invisible while the window body was white and became a |
| 45 |
* black band the moment the station's palette made it Obsidian. It is |
| 46 |
* a real coupling either way: the look of a wp-admin page must not |
| 47 |
* depend on the colour of the frame around it. |
| 48 |
* |
| 49 |
* So the chromeless document paints itself, in the colour the browser |
| 50 |
* canvas would have been. Deliberately a literal and deliberately NOT |
| 51 |
* a token: nothing about the shell's palette — or any desktop theme — |
| 52 |
* should reach a real admin page. **An admin page in a window renders |
| 53 |
* exactly as it does outside one.** |
| 54 |
* --------------------------------------------------------------- */ |
| 55 |
body.os-chromeless { |
| 56 |
background: #fff; |
| 57 |
} |
| 58 |
|
| 59 |
/* |
| 60 |
* _wp_admin_html_begin() adds the `wp-toolbar` class to <html> whenever |
| 61 |
* is_admin_bar_showing() is true — which, in admin, is unconditional. |
| 62 |
* The class carries a `padding-top: var(--wp-admin--admin-bar--height)` |
| 63 |
* that would leave a 32px (or 46px) dead gap at the top of the iframe. |
| 64 |
* Zero it out inside chromeless iframes. |
| 65 |
* |
| 66 |
* We also rebind `--wp-admin--admin-bar--height` (and the derived |
| 67 |
* `--wp-admin--admin-bar--position-offset` from block-library) to 0px |
| 68 |
* inside chromeless so plugins that position UI relative to the admin |
| 69 |
* bar resolve their math against the iframe's actual chrome state. |
| 70 |
* |
| 71 |
* WooCommerce's activity-panel wrapper, the block editor's sticky |
| 72 |
* header, and several others use `top: var(--wp-admin--admin-bar--height)` |
| 73 |
* to clear the bar. Without this override they reserve a 32px (or 46px |
| 74 |
* on small screens) gap that no longer exists, producing visible jumps |
| 75 |
* on first paint and dead space at the top of the content area. |
| 76 |
*/ |
| 77 |
html.wp-toolbar:has( body.os-chromeless ) { |
| 78 |
padding-top: 0 !important; |
| 79 |
--wp-admin--admin-bar--height: 0px; |
| 80 |
--wp-admin--admin-bar--position-offset: 0px; |
| 81 |
} |
| 82 |
|
| 83 |
/* Remove the sidebar gutter on #wpcontent and let the body fill the frame. */ |
| 84 |
.os-chromeless #wpcontent { |
| 85 |
margin-inline-start: 0 !important; |
| 86 |
padding-inline-start: 0 !important; |
| 87 |
} |
| 88 |
|
| 89 |
/* |
| 90 |
* WooCommerce sidebar-reservation override. |
| 91 |
* |
| 92 |
* `.woocommerce-layout__header` is a `position: fixed` bar WC |
| 93 |
* mounts on every wc-admin and wc-embedded page. Its width is |
| 94 |
* compiled from the SCSS source (header/style.scss) as a literal: |
| 95 |
* |
| 96 |
* position: fixed; |
| 97 |
* top: 32px; // $adminbar-height |
| 98 |
* width: calc(100% - 160px); // reserves classic sidebar |
| 99 |
* z-index: 1001; |
| 100 |
* |
| 101 |
* The 160px subtraction is the classic admin menu width — WC bakes |
| 102 |
* it in at build time so the header doesn't overlap the sidebar |
| 103 |
* in standard admin. Inside chromeless we hide the sidebar, but |
| 104 |
* the header keeps the reservation — so the header ends 160px |
| 105 |
* short of the iframe right edge, and `.woocommerce-layout__activity-panel-wrapper` |
| 106 |
* (`position: absolute; right: 0; top: 100%; transform: translateX(100%)`, |
| 107 |
* containing block = the fixed header) translates past the |
| 108 |
* header's right edge, NOT past the iframe's right edge. The |
| 109 |
* resulting visible strip is exactly 160px wide — the size of the |
| 110 |
* sidebar gap WC reserved for nothing. |
| 111 |
* |
| 112 |
* Reclaim the reservation: pin the header to full iframe width |
| 113 |
* inside chromeless. The activity panel then translates past the |
| 114 |
* iframe edge as WC's design intended, the gray strip disappears, |
| 115 |
* and the header's content uses the full window width. No |
| 116 |
* transform / overflow / visibility tricks needed. |
| 117 |
*/ |
| 118 |
.os-chromeless .woocommerce-layout__header { |
| 119 |
width: 100% !important; |
| 120 |
} |
| 121 |
|
| 122 |
/* |
| 123 |
* MailPoet top-bar overlap fix. |
| 124 |
* |
| 125 |
* MailPoet mounts a 64px-tall brand bar on its screens as |
| 126 |
* `.wrap .mailpoet-top-bar { position: absolute; top: 0 }` — |
| 127 |
* anchored to `#wpbody` (core's `position: relative`), overlaying |
| 128 |
* whatever the page's first 64px contain. In classic admin nothing |
| 129 |
* is visibly covered only by accident of geometry: the in-flow |
| 130 |
* space above `.wrap` — the `#screen-meta-links` row plus `.wrap`'s |
| 131 |
* own top margin — happens to add up to more than the bar's height, |
| 132 |
* so the content starts below it (measured: `.wrap` at y=114 vs |
| 133 |
* bar bottom at y=96). |
| 134 |
* |
| 135 |
* Chromeless collapses exactly that space on purpose — screen-meta |
| 136 |
* links are hidden (the window title bar owns those buttons) and |
| 137 |
* `.wrap` margins are trimmed — which slides MailPoet's content up |
| 138 |
* underneath the overlaid bar. Symptom: the page's heading sits |
| 139 |
* half-hidden behind the white logo bar inside the window. |
| 140 |
* |
| 141 |
* Reserve the bar's height in flow instead. `:has()` scopes the |
| 142 |
* rule to precisely the pages that render the in-wrap bar — |
| 143 |
* whatever MailPoet screen shape it is, present or future — and to |
| 144 |
* nothing else. |
| 145 |
*/ |
| 146 |
.os-chromeless .wrap:has( .mailpoet-top-bar ) { |
| 147 |
padding-top: 64px; |
| 148 |
} |
| 149 |
|
| 150 |
/* --------------------------------------------------------------- |
| 151 |
* Screen Meta (Screen Options / Help panels) |
| 152 |
* The toggle buttons (#screen-meta-links) are hidden because |
| 153 |
* the parent desktop shell adds its own buttons to the window |
| 154 |
* title bar. The panels themselves stay visible and functional — |
| 155 |
* they're toggled via postMessage from the parent. |
| 156 |
* |
| 157 |
* Collapse all margins on the hidden links and the panel container |
| 158 |
* so they don't leave a gap at the top of the iframe content. |
| 159 |
* --------------------------------------------------------------- */ |
| 160 |
.os-chromeless #screen-meta-links { |
| 161 |
display: none; |
| 162 |
margin: 0; |
| 163 |
} |
| 164 |
|
| 165 |
.os-chromeless #screen-meta { |
| 166 |
margin: 0; |
| 167 |
border: none; |
| 168 |
} |
| 169 |
|
| 170 |
/* --------------------------------------------------------------- |
| 171 |
* Wrap container |
| 172 |
* Remove the default left margin that accounts for the sidebar |
| 173 |
* which doesn't exist in chromeless mode. |
| 174 |
* --------------------------------------------------------------- */ |
| 175 |
.os-chromeless .wrap { |
| 176 |
margin: 0; |
| 177 |
} |
| 178 |
|
| 179 |
/* --------------------------------------------------------------- |
| 180 |
* Page title & header |
| 181 |
* The window title bar already shows the page title, so the |
| 182 |
* in-page <h1> + header separator (.wp-header-end) are redundant |
| 183 |
* inside chromeless iframes. Hide them so the window content |
| 184 |
* starts flush. |
| 185 |
* |
| 186 |
* `.page-title-action` (the "Add New" / "Add Order" button next to |
| 187 |
* the H1) stays VISIBLE by default — it's the only entry point to |
| 188 |
* the add-new flow on many plugin pages (WooCommerce Orders, custom |
| 189 |
* CPTs, plugin settings pages, etc.). A blanket hide would break |
| 190 |
* every third-party plugin page that has no submenu equivalent, so |
| 191 |
* the button is only removed where the window's own tab strip |
| 192 |
* demonstrably leads to the same place — see |
| 193 |
* `includes/render/chromeless-title-actions.php`, which emits one |
| 194 |
* `[href="…"]` rule per submenu tab URL of the current screen. |
| 195 |
* |
| 196 |
* Sites that want to hide it somewhere that rule deliberately |
| 197 |
* doesn't reach can add their own via the |
| 198 |
* `openstation_chromeless_styles` action — e.g. WooCommerce's "Add |
| 199 |
* order", which we keep because it points at `&action=new` and the |
| 200 |
* Orders tab doesn't: |
| 201 |
* |
| 202 |
* body.woocommerce_page_wc-orders .wrap > .page-title-action { |
| 203 |
* display: none; |
| 204 |
* } |
| 205 |
* |
| 206 |
* --------------------------------------------------------------- */ |
| 207 |
.os-chromeless .wrap > h1, |
| 208 |
.os-chromeless .wrap > h1.wp-heading-inline, |
| 209 |
.os-chromeless #wpbody-content > .wrap > h1, |
| 210 |
.os-chromeless .wrap > .wp-header-end { |
| 211 |
display: none; |
| 212 |
} |
| 213 |
|
| 214 |
/* |
| 215 |
* The H1 above it is hidden, so the button would float at the very |
| 216 |
* top of the iframe area without breathing room — give it a small |
| 217 |
* margin so it lands cleanly. |
| 218 |
* |
| 219 |
* `block` + `fit-content` instead of Core's `inline-block`: with the |
| 220 |
* H1 hidden, an inline button ends up on the same line as the |
| 221 |
* floated `.subsubsub` filter row and reads as one more filter link |
| 222 |
* ("All (6) | Published (4) | Trash (2) Add Post"). Its own row is |
| 223 |
* also what the screen already looks like when an admin notice is |
| 224 |
* showing. `clear` keeps it below anything floated before it. |
| 225 |
* |
| 226 |
* Themes.php's native "Add Theme" button lives in the submenu tab |
| 227 |
* strip via `openstation_inject_appearance_tabs` |
| 228 |
* (includes/themes-tabs.php), so its in-page page-title-action is |
| 229 |
* redundant on that one screen. The generic href-matching hide in |
| 230 |
* `includes/render/chromeless-title-actions.php` only matches exact |
| 231 |
* URLs, and the injected tab points at |
| 232 |
* `theme-install.php?browse=popular` while the button points at |
| 233 |
* plain `theme-install.php`. Hence the per-page rule below. |
| 234 |
*/ |
| 235 |
.os-chromeless .wrap > .page-title-action { |
| 236 |
display: block; |
| 237 |
width: fit-content; |
| 238 |
margin-top: 12px; |
| 239 |
margin-bottom: 12px; |
| 240 |
clear: both; |
| 241 |
} |
| 242 |
|
| 243 |
.os-chromeless.themes-php .wrap > .page-title-action { |
| 244 |
display: none; |
| 245 |
} |
| 246 |
|
| 247 |
/* |
| 248 |
* Core offsets every title action by `top: -3px` (common.css) so it |
| 249 |
* sits on the H1's baseline. Chromeless hides the H1, and the first |
| 250 |
* thing in the frame has nothing above it to overlap: the offset |
| 251 |
* pulls the button's top border under the window's content edge and |
| 252 |
* it renders cropped. The offset has no baseline left to meet here, |
| 253 |
* so drop it. |
| 254 |
* |
| 255 |
* The margins are the same 12px the rule above gives a lone button, |
| 256 |
* applied to whatever element a plugin grouped its buttons in. On a |
| 257 |
* Jetpack site that is `div.wpcom-media-library-action-buttons` |
| 258 |
* (external-media moves core's "Add Media File" into it so it can |
| 259 |
* append "Import Media"; Big Sky adds "Generate Image" beside them), |
| 260 |
* and a group is not a `.page-title-action`, so it picks up neither |
| 261 |
* the margin nor the block layout and lands flush against the top. |
| 262 |
* `:has()` reaches the group whatever a plugin decided to call it. |
| 263 |
*/ |
| 264 |
.os-chromeless .wrap .page-title-action { |
| 265 |
top: 0; |
| 266 |
} |
| 267 |
|
| 268 |
.os-chromeless .wrap :has( > .page-title-action ) { |
| 269 |
margin-top: 12px; |
| 270 |
margin-bottom: 12px; |
| 271 |
} |
| 272 |
|
| 273 |
/* --------------------------------------------------------------- |
| 274 |
* WooCommerce "embed page" header overlay — page-scoped. |
| 275 |
* |
| 276 |
* Background: WC renders TWO layouts simultaneously on "connected" |
| 277 |
* pages like `wc-orders` — the PHP-rendered legacy `.wrap` (with |
| 278 |
* the h1 + the "Add order" `.page-title-action`) AND a React-mounted |
| 279 |
* `EmbedHeader` (`client/admin/client/header/embed.tsx`) that |
| 280 |
* `position: fixed`-overlays the page from the top. References: |
| 281 |
* - `src/Internal/Admin/Loader.php::embed_page_header` |
| 282 |
* - `includes/react-admin/connect-existing-pages.php` (registers |
| 283 |
* wc-orders as a connected page when HPOS is on) |
| 284 |
* - `client/admin/client/header/shared.tsx::useUpdateBodyMargin` |
| 285 |
* (the hook that pushes `#wpbody.style.marginTop` to make |
| 286 |
* room for the fixed header) |
| 287 |
* |
| 288 |
* The conflict, narrowly: |
| 289 |
* 1. The legacy `.wrap > .page-title-action` ("Add order") at |
| 290 |
* the top of the document body — primary add-new affordance. |
| 291 |
* 2. The React `EmbedHeader` overlays the top of the iframe with |
| 292 |
* a redundant `<h1>Orders</h1>` and an Activity Panel toggle. |
| 293 |
* |
| 294 |
* The fixed React header OBSCURES the legacy `.page-title-action` |
| 295 |
* button when we reset `#wpbody`'s margin-top. |
| 296 |
* |
| 297 |
* Scope: ONLY the pages where the dual rendering is genuinely a |
| 298 |
* problem — `wc-orders` (Orders list, the user-reported failure |
| 299 |
* mode) and `wc-orders--shop_order` (the trash view variant). Other |
| 300 |
* WC-admin pages (Analytics, Marketing, Customers, Coupons, |
| 301 |
* Products, Reports, …) DON'T render a competing legacy `.wrap` |
| 302 |
* with a `.page-title-action` button; their primary content IS the |
| 303 |
* React app. On those pages the Activity Panel + EmbedHeader are |
| 304 |
* the only header affordance the user has, so we keep them visible. |
| 305 |
* |
| 306 |
* If new HPOS-style connected pages emerge where the same dual |
| 307 |
* layout creates the same conflict, add their per-page body class |
| 308 |
* (`.woocommerce_page_<slug>`) to the selector — don't broaden the |
| 309 |
* rule to `.woocommerce-admin-page` (that would silently strip the |
| 310 |
* Activity Panel from every WC screen). |
| 311 |
* |
| 312 |
* @since 0.8.9 |
| 313 |
*/ |
| 314 |
.os-chromeless.woocommerce_page_wc-orders .woocommerce-layout__header, |
| 315 |
.os-chromeless.woocommerce_page_wc-orders--shop_order .woocommerce-layout__header { |
| 316 |
display: none !important; |
| 317 |
} |
| 318 |
.os-chromeless.woocommerce_page_wc-orders #wpbody, |
| 319 |
.os-chromeless.woocommerce_page_wc-orders--shop_order #wpbody { |
| 320 |
margin-top: 0 !important; |
| 321 |
} |
| 322 |
|
| 323 |
/* --------------------------------------------------------------- |
| 324 |
* Revisions screen — page-scoped. |
| 325 |
* |
| 326 |
* "← Go to editor" is a back button for a screen the user navigated |
| 327 |
* into. Here they didn't: the editor is open in its own window |
| 328 |
* behind this one, and closing this window is the way back. |
| 329 |
* |
| 330 |
* The revision tooltip is positioned upward from the bottom of |
| 331 |
* `.revisions-control-frame` and clears the top of the viewport only |
| 332 |
* thanks to the screen H1, which chromeless hides. An iframe can't |
| 333 |
* overflow its box, so the space has to be given back: 48px covers |
| 334 |
* both slider modes. |
| 335 |
* |
| 336 |
* The padding goes on `.revisions`, not on the frame — the frame is |
| 337 |
* the positioned ancestor for the compare-mode checkbox and the |
| 338 |
* tooltip, so padding it would leave both behind at the old top edge. |
| 339 |
* --------------------------------------------------------------- */ |
| 340 |
.os-chromeless.revision-php .wrap > h1.long-header + a { |
| 341 |
display: none; |
| 342 |
} |
| 343 |
|
| 344 |
.os-chromeless.revision-php .revisions { |
| 345 |
padding-top: 48px; |
| 346 |
} |
| 347 |
|
| 348 |
/* --------------------------------------------------------------- |
| 349 |
* Dashboard welcome panel |
| 350 |
* The default 16px top margin pushes the panel down and creates a |
| 351 |
* visible gap at the top of the iframe. Collapse it in chromeless |
| 352 |
* mode so the panel sits flush with the top of the window body. |
| 353 |
* --------------------------------------------------------------- */ |
| 354 |
.os-chromeless #welcome-panel, |
| 355 |
.os-chromeless .welcome-panel { |
| 356 |
margin-top: 0.5em; |
| 357 |
} |
| 358 |
|
| 359 |
/* --------------------------------------------------------------- |
| 360 |
* Footer |
| 361 |
* The classic footer is not rendered in chromeless mode, |
| 362 |
* but some pages have bottom padding assuming it exists. |
| 363 |
* --------------------------------------------------------------- */ |
| 364 |
.os-chromeless #wpbody-content { |
| 365 |
margin: 0; |
| 366 |
padding: 0 8px 8px; |
| 367 |
float: none; |
| 368 |
width: auto; |
| 369 |
} |
| 370 |
|
| 371 |
/* --------------------------------------------------------------- |
| 372 |
* Block Editor — hide Gutenberg chrome that would break the window. |
| 373 |
* |
| 374 |
* The fullscreen-mode close button (the "W" logo top-left of the editor) |
| 375 |
* is an <a href="/wp-admin/edit.php"> that navigates the iframe to a |
| 376 |
* non-chromeless URL — which re-renders the entire classic admin |
| 377 |
* inside our desktop window. The link interceptor in the chromeless |
| 378 |
* bridge catches it too, but hiding the button removes the visual |
| 379 |
* affordance so users never try to click "back to dashboard" inside |
| 380 |
* what looks like a self-contained window. |
| 381 |
* |
| 382 |
* The site editor's equivalent navigation affordances (site hub toggle, |
| 383 |
* "back to dashboard" link) get the same treatment. |
| 384 |
* |
| 385 |
* The welcome guide is intentionally NOT hidden anymore. Earlier |
| 386 |
* iterations both CSS-hid the dialog AND flipped `core/edit-post: |
| 387 |
* welcomeGuide` to `false` at the data layer on every chromeless |
| 388 |
* mount — but Gutenberg already persists the user's "Get started" |
| 389 |
* dismissal to user meta the moment they close it, so the override |
| 390 |
* was permanently stealing the one-time orientation tour from every |
| 391 |
* user who never got to see it. Modal's focus trap is Tab-only and |
| 392 |
* doesn't fight the shell. Let it run. (See git history for the |
| 393 |
* removed `openstation_chromeless_editor_preferences` override.) |
| 394 |
* --------------------------------------------------------------- */ |
| 395 |
.os-chromeless .edit-post-fullscreen-mode-close, |
| 396 |
.os-chromeless .edit-post-fullscreen-mode-close__view-mode-toggle, |
| 397 |
.os-chromeless .edit-site-navigation-link, |
| 398 |
.os-chromeless .edit-site-site-hub, |
| 399 |
.os-chromeless .edit-site-site-hub__toggle { |
| 400 |
display: none !important; |
| 401 |
} |
| 402 |
|
| 403 |
/* --------------------------------------------------------------- |
| 404 |
* Block Editor & Site Editor — full-bleed layouts. |
| 405 |
* Gutenberg owns its entire viewport (its own header bar, side |
| 406 |
* panels, etc.). Any padding around #wpbody-content crops the |
| 407 |
* editor and breaks its layout, so reset to zero on those pages |
| 408 |
* and let the editor render edge-to-edge. |
| 409 |
* --------------------------------------------------------------- */ |
| 410 |
.os-chromeless.block-editor-page #wpbody-content, |
| 411 |
.os-chromeless.site-editor-php #wpbody-content, |
| 412 |
.os-chromeless.is-fullscreen-mode #wpbody-content { |
| 413 |
padding: 0; |
| 414 |
} |
| 415 |
|
| 416 |
/* --------------------------------------------------------------- |
| 417 |
* "Boot" SPA pages (Font Library, Options → Connectors). |
| 418 |
* |
| 419 |
* These screens mount a React app into `.boot-layout-container` and |
| 420 |
* paint `#wpwrap` a dark `#1e1e1e` via inline <style>. Our 8px |
| 421 |
* right/bottom padding on `#wpbody-content` lets that dark color |
| 422 |
* bleed through as two black gaps on the right and bottom edges of |
| 423 |
* the window. Zero the padding for any page hosting a boot layout |
| 424 |
* — detection is purely structural (`:has()`), so a plugin that |
| 425 |
* adopts the same container class inherits the fix automatically. |
| 426 |
* --------------------------------------------------------------- */ |
| 427 |
.os-chromeless #wpbody-content:has( .boot-layout-container ) { |
| 428 |
padding: 0; |
| 429 |
} |
| 430 |
|
| 431 |
.os-chromeless:has( .boot-layout-container ) #wpwrap { |
| 432 |
/* Neutralize the inline dark background so any stray overflow |
| 433 |
* matches the window body instead of flashing black. */ |
| 434 |
background: transparent; |
| 435 |
} |
| 436 |
|
| 437 |
/* |
| 438 |
* `boot-layout__surfaces` is the white card the React app draws its |
| 439 |
* content onto. In classic admin it sits on the dark #wpwrap with |
| 440 |
* rounded corners and a margin that makes it read as a "card." Inside |
| 441 |
* a desktop window the surface IS the content — rounded corners and |
| 442 |
* side margins leave ugly bleed at the edges. Collapse it to a plain |
| 443 |
* edge-to-edge surface. |
| 444 |
*/ |
| 445 |
.os-chromeless .boot-layout__surfaces, |
| 446 |
.os-chromeless .boot-layout__stage, |
| 447 |
.os-chromeless .boot-layout__canvas { |
| 448 |
border-radius: 0 !important; |
| 449 |
margin: 0 !important; |
| 450 |
} |
| 451 |
|
| 452 |
/* |
| 453 |
* The boot screens render their own H1 (`.boot-navigation-screen__title`, |
| 454 |
* e.g., "Fonts") at the top of the surface. The desktop window already |
| 455 |
* shows the page title in its title bar, so the in-page title is |
| 456 |
* redundant. Visually hide it but keep it in the DOM for screen readers |
| 457 |
* and for the boot app's own focus management. |
| 458 |
*/ |
| 459 |
.os-chromeless .boot-navigation-screen__title, |
| 460 |
.os-chromeless .boot-navigation-screen__title-icon { |
| 461 |
position: absolute; |
| 462 |
width: 1px; |
| 463 |
height: 1px; |
| 464 |
padding: 0; |
| 465 |
margin: -1px; |
| 466 |
overflow: hidden; |
| 467 |
clip: rect( 0, 0, 0, 0 ); |
| 468 |
white-space: nowrap; |
| 469 |
border: 0; |
| 470 |
} |
| 471 |
|
| 472 |
/* --------------------------------------------------------------- |
| 473 |
* Snackbars |
| 474 |
* |
| 475 |
* `components-snackbar-list` (Gutenberg `@wordpress/components`) and |
| 476 |
* `boot-notices__snackbar` (Font Library / Connectors) position |
| 477 |
* themselves `fixed` at the viewport bottom. Because a chromeless |
| 478 |
* iframe IS its own viewport, those anchors already land inside the |
| 479 |
* window — but our 8px bottom padding on `#wpbody-content` (applied |
| 480 |
* to non-boot pages) plus some plugin-level `bottom: 40px` offsets |
| 481 |
* meant for the classic admin footer can push them out of sight. |
| 482 |
* |
| 483 |
* Pin them flush to the bottom of the iframe and make sure nothing |
| 484 |
* in the window chrome can occlude them. |
| 485 |
* |
| 486 |
* Triggering a snackbar to verify (OpenStation enabled): |
| 487 |
* - Open Posts → Add New, save/publish → "Post published" toast. |
| 488 |
* - Open Media, upload a file → "Uploaded" toast. |
| 489 |
* - Open Appearance → Fonts, install a Google Font → toast. |
| 490 |
* --------------------------------------------------------------- */ |
| 491 |
.os-chromeless .components-snackbar-list, |
| 492 |
.os-chromeless .boot-notices__snackbar { |
| 493 |
bottom: 16px !important; |
| 494 |
z-index: 100000; |
| 495 |
} |
| 496 |
|
| 497 |
/* |
| 498 |
* Force the Gutenberg interface skeleton to fill the iframe regardless |
| 499 |
* of fullscreen state. When fullscreenMode is off, Gutenberg hardcodes |
| 500 |
* `top: 32px` (admin bar reservation) and `left: 160px` (sidebar |
| 501 |
* reservation) on `.interface-interface-skeleton` — both chromes we've |
| 502 |
* already hidden, so those offsets become dead gaps at the top and |
| 503 |
* left of the window. Zero them out unconditionally. |
| 504 |
* |
| 505 |
* The default is fullscreen (inset: 0), but users who turned fullscreen |
| 506 |
* off in classic admin carry that preference into chromeless via the |
| 507 |
* shared persistence layer. This rule keeps chromeless rendering |
| 508 |
* edge-to-edge either way. |
| 509 |
*/ |
| 510 |
.os-chromeless .interface-interface-skeleton { |
| 511 |
top: 0 !important; |
| 512 |
left: 0 !important; |
| 513 |
right: 0 !important; |
| 514 |
bottom: 0 !important; |
| 515 |
} |
| 516 |
|
| 517 |
/* --------------------------------------------------------------- |
| 518 |
* Notices |
| 519 |
* Give notices a bit of top margin since there's no admin bar above. |
| 520 |
* --------------------------------------------------------------- */ |
| 521 |
.os-chromeless .notice:first-child { |
| 522 |
margin-top: 4px; |
| 523 |
} |
| 524 |
|
| 525 |
/* --------------------------------------------------------------- |
| 526 |
* Update nag |
| 527 |
* Core's global update / maintenance nags are detached server-side |
| 528 |
* inside chromeless iframes (see |
| 529 |
* `openstation_chromeless_suppress_update_nags()`) and re-surfaced |
| 530 |
* once by the shell as a single notice. |
| 531 |
* --------------------------------------------------------------- */ |
| 532 |
.os-chromeless .update-nag { |
| 533 |
display: none !important; |
| 534 |
} |
| 535 |
|
| 536 |
/* --------------------------------------------------------------- |
| 537 |
* Suppress WordPress's native command palette inside chromeless |
| 538 |
* iframes. The chromeless bridge intercepts Cmd+K and forwards to |
| 539 |
* the desktop shell, but if an in-page component triggers the |
| 540 |
* palette via another path (programmatic dispatch, a menu item) |
| 541 |
* the dialog would still render. Hide it so the shell's palette |
| 542 |
* stays the only surface users ever see. |
| 543 |
* --------------------------------------------------------------- */ |
| 544 |
.os-chromeless .commands-command-menu__container, |
| 545 |
.os-chromeless .commands-command-menu { |
| 546 |
display: none !important; |
| 547 |
} |
| 548 |
|
| 549 |
/* --------------------------------------------------------------- |
| 550 |
* Appearance → Themes workspace |
| 551 |
* |
| 552 |
* Core's one-theme state renders the Theme Details dialog inline and |
| 553 |
* removes its header. Inside a short desktop window that leaves a large, |
| 554 |
* unexplained panel whose actions fall below the fold. Treat it as an |
| 555 |
* active-site identity card instead: screenshot first, useful context at |
| 556 |
* the side, and actions kept in the card's visible edge. |
| 557 |
* |
| 558 |
* With multiple installed themes the native cards become a deliberate |
| 559 |
* library grid and Core's details view remains a dialog. All selectors are |
| 560 |
* scoped to the chromeless Themes screen; the classic wp-admin page keeps |
| 561 |
* Core's own presentation and the shell's palette cannot leak in here. |
| 562 |
* --------------------------------------------------------------- */ |
| 563 |
.os-chromeless.themes-php #wpbody-content { |
| 564 |
padding: 10px 16px; |
| 565 |
background: #f6f7f7; |
| 566 |
} |
| 567 |
|
| 568 |
.os-chromeless.themes-php .openstation-themes-intro { |
| 569 |
display: flex; |
| 570 |
align-items: center; |
| 571 |
justify-content: space-between; |
| 572 |
gap: 24px; |
| 573 |
margin: 0 0 8px; |
| 574 |
padding: 14px 18px; |
| 575 |
background: #fff; |
| 576 |
border: 1px solid #dcdcde; |
| 577 |
border-radius: 12px; |
| 578 |
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04); |
| 579 |
box-sizing: border-box; |
| 580 |
} |
| 581 |
|
| 582 |
.os-chromeless.themes-php .openstation-themes-intro__copy { |
| 583 |
max-width: 680px; |
| 584 |
} |
| 585 |
|
| 586 |
.os-chromeless.themes-php .openstation-themes-intro__eyebrow { |
| 587 |
margin: 0 0 5px; |
| 588 |
color: var(--wp-admin-theme-color, #2271b1); |
| 589 |
font-size: 11px; |
| 590 |
font-weight: 700; |
| 591 |
letter-spacing: 0.08em; |
| 592 |
line-height: 1.4; |
| 593 |
text-transform: uppercase; |
| 594 |
} |
| 595 |
|
| 596 |
.os-chromeless.themes-php .openstation-themes-intro h1 { |
| 597 |
margin: 0; |
| 598 |
color: #1d2327; |
| 599 |
font-size: clamp(22px, 2.8vw, 30px); |
| 600 |
font-weight: 650; |
| 601 |
letter-spacing: -0.025em; |
| 602 |
line-height: 1.12; |
| 603 |
} |
| 604 |
|
| 605 |
.os-chromeless.themes-php .openstation-themes-intro__copy > p:last-child { |
| 606 |
max-width: 650px; |
| 607 |
margin: 6px 0 0; |
| 608 |
color: #50575e; |
| 609 |
font-size: 13px; |
| 610 |
line-height: 1.45; |
| 611 |
} |
| 612 |
|
| 613 |
.os-chromeless.themes-php .openstation-themes-intro__count { |
| 614 |
display: grid; |
| 615 |
flex: 0 0 auto; |
| 616 |
min-width: 104px; |
| 617 |
margin: 0; |
| 618 |
padding: 10px 14px; |
| 619 |
background: #f6f7f7; |
| 620 |
border: 1px solid #dcdcde; |
| 621 |
border-radius: 9px; |
| 622 |
box-sizing: border-box; |
| 623 |
text-align: center; |
| 624 |
} |
| 625 |
|
| 626 |
.os-chromeless.themes-php .openstation-themes-intro__count strong { |
| 627 |
color: #1d2327; |
| 628 |
font-size: 22px; |
| 629 |
font-weight: 650; |
| 630 |
letter-spacing: -0.03em; |
| 631 |
line-height: 1; |
| 632 |
} |
| 633 |
|
| 634 |
.os-chromeless.themes-php .openstation-themes-intro__count span { |
| 635 |
margin-top: 5px; |
| 636 |
color: #646970; |
| 637 |
font-size: 10px; |
| 638 |
font-weight: 600; |
| 639 |
letter-spacing: 0.04em; |
| 640 |
line-height: 1.25; |
| 641 |
text-transform: uppercase; |
| 642 |
} |
| 643 |
|
| 644 |
.os-chromeless.themes-php .wrap { |
| 645 |
margin: 0; |
| 646 |
} |
| 647 |
|
| 648 |
.os-chromeless.themes-php .search-form { |
| 649 |
display: flex; |
| 650 |
margin: 0 0 16px; |
| 651 |
padding: 0; |
| 652 |
} |
| 653 |
|
| 654 |
.os-chromeless.themes-php .wp-filter-search { |
| 655 |
width: min(320px, 100%); |
| 656 |
min-height: 36px; |
| 657 |
padding-inline: 12px; |
| 658 |
background: #fff; |
| 659 |
border-color: #8c8f94; |
| 660 |
border-radius: 7px; |
| 661 |
} |
| 662 |
|
| 663 |
/* Searching a library of one is noise, so the field goes away in Core's |
| 664 |
* single-theme state. The general sibling combinator rather than `+`: |
| 665 |
* themes.php emits `wp_admin_notice()` output between the search form and |
| 666 |
* `.theme-browser` on `?activated`, `?deleted`, `?broken` and |
| 667 |
* `?delete-active-child` — exactly the loads that follow activating or |
| 668 |
* deleting a theme — which breaks strict adjacency. */ |
| 669 |
.os-chromeless.themes-php |
| 670 |
.search-form:has(~ .theme-browser .themes.single-theme) { |
| 671 |
display: none; |
| 672 |
} |
| 673 |
|
| 674 |
/* Installed-theme library. The Add Theme destination already has its own |
| 675 |
* persistent window tab, so Core's duplicate dashed card is unnecessary. */ |
| 676 |
.os-chromeless.themes-php .theme-browser .themes:not(.single-theme) { |
| 677 |
display: grid; |
| 678 |
grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr)); |
| 679 |
gap: 18px; |
| 680 |
clear: both; |
| 681 |
} |
| 682 |
|
| 683 |
.os-chromeless.themes-php .theme-browser .themes:not(.single-theme) > .theme { |
| 684 |
float: none; |
| 685 |
width: auto; |
| 686 |
margin: 0; |
| 687 |
background: #fff; |
| 688 |
border: 1px solid #c3c4c7; |
| 689 |
border-radius: 10px; |
| 690 |
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04); |
| 691 |
overflow: hidden; |
| 692 |
transition: |
| 693 |
border-color 120ms ease, |
| 694 |
box-shadow 120ms ease, |
| 695 |
transform 120ms ease; |
| 696 |
} |
| 697 |
|
| 698 |
.os-chromeless.themes-php |
| 699 |
.theme-browser |
| 700 |
.themes:not(.single-theme) |
| 701 |
> .theme:hover, |
| 702 |
.os-chromeless.themes-php |
| 703 |
.theme-browser |
| 704 |
.themes:not(.single-theme) |
| 705 |
> .theme.focus { |
| 706 |
border-color: #8c8f94; |
| 707 |
box-shadow: 0 7px 20px rgba(0, 0, 0, 0.1); |
| 708 |
transform: translateY(-2px); |
| 709 |
} |
| 710 |
|
| 711 |
.os-chromeless.themes-php |
| 712 |
.theme-browser |
| 713 |
.themes:not(.single-theme) |
| 714 |
> .theme.active { |
| 715 |
border-color: var(--wp-admin-theme-color, #2271b1); |
| 716 |
box-shadow: |
| 717 |
0 0 0 1px var(--wp-admin-theme-color, #2271b1), |
| 718 |
0 7px 20px rgba(0, 0, 0, 0.08); |
| 719 |
} |
| 720 |
|
| 721 |
.os-chromeless.themes-php |
| 722 |
.theme-browser |
| 723 |
.themes:not(.single-theme) |
| 724 |
> .add-new-theme { |
| 725 |
display: none; |
| 726 |
} |
| 727 |
|
| 728 |
.os-chromeless.themes-php |
| 729 |
.theme-browser |
| 730 |
.themes:not(.single-theme) |
| 731 |
.theme-screenshot { |
| 732 |
background: #f0f0f1; |
| 733 |
} |
| 734 |
|
| 735 |
.os-chromeless.themes-php |
| 736 |
.theme-browser |
| 737 |
.themes:not(.single-theme) |
| 738 |
.theme-id-container { |
| 739 |
display: flex; |
| 740 |
align-items: center; |
| 741 |
justify-content: space-between; |
| 742 |
min-height: 58px; |
| 743 |
background: #fff; |
| 744 |
} |
| 745 |
|
| 746 |
.os-chromeless.themes-php .theme-browser .themes:not(.single-theme) .theme-name, |
| 747 |
.os-chromeless.themes-php |
| 748 |
.theme-browser |
| 749 |
.themes:not(.single-theme) |
| 750 |
.theme.active |
| 751 |
.theme-name { |
| 752 |
flex: 1 1 auto; |
| 753 |
height: auto; |
| 754 |
min-width: 0; |
| 755 |
margin: 0; |
| 756 |
padding: 14px 12px 14px 14px; |
| 757 |
background: #fff; |
| 758 |
box-shadow: none; |
| 759 |
color: #1d2327; |
| 760 |
font-size: 14px; |
| 761 |
font-weight: 600; |
| 762 |
line-height: 1.3; |
| 763 |
} |
| 764 |
|
| 765 |
.os-chromeless.themes-php |
| 766 |
.theme-browser |
| 767 |
.themes:not(.single-theme) |
| 768 |
.theme.active |
| 769 |
.theme-name |
| 770 |
span { |
| 771 |
display: block; |
| 772 |
margin-bottom: 2px; |
| 773 |
color: var(--wp-admin-theme-color, #2271b1); |
| 774 |
font-size: 10px; |
| 775 |
font-weight: 700; |
| 776 |
letter-spacing: 0.06em; |
| 777 |
line-height: 1.2; |
| 778 |
text-transform: uppercase; |
| 779 |
} |
| 780 |
|
| 781 |
.os-chromeless.themes-php |
| 782 |
.theme-browser |
| 783 |
.themes:not(.single-theme) |
| 784 |
.theme-actions, |
| 785 |
.os-chromeless.themes-php |
| 786 |
.theme-browser |
| 787 |
.themes:not(.single-theme) |
| 788 |
.theme.active |
| 789 |
.theme-actions { |
| 790 |
display: flex; |
| 791 |
align-items: center; |
| 792 |
flex: 0 0 auto; |
| 793 |
position: static; |
| 794 |
opacity: 1; |
| 795 |
padding: 10px 12px 10px 0; |
| 796 |
background: #fff; |
| 797 |
border: 0; |
| 798 |
box-shadow: none; |
| 799 |
transform: none; |
| 800 |
} |
| 801 |
|
| 802 |
/* Multiple-theme details remain a modal, but fit the iframe rather than |
| 803 |
* reserving the classic admin sidebar's 160px gutter. */ |
| 804 |
.os-chromeless.themes-php .theme-overlay.active .theme-backdrop { |
| 805 |
position: fixed; |
| 806 |
left: 0; |
| 807 |
background: rgba(29, 35, 39, 0.62); |
| 808 |
backdrop-filter: blur(2px); |
| 809 |
} |
| 810 |
|
| 811 |
.os-chromeless.themes-php .theme-overlay.active .theme-wrap { |
| 812 |
top: 18px; |
| 813 |
right: 18px; |
| 814 |
bottom: 18px; |
| 815 |
left: 18px; |
| 816 |
border: 1px solid #c3c4c7; |
| 817 |
border-radius: 12px; |
| 818 |
box-shadow: 0 20px 55px rgba(0, 0, 0, 0.28); |
| 819 |
overflow: hidden; |
| 820 |
} |
| 821 |
|
| 822 |
.os-chromeless.themes-php .theme-overlay.active .theme-header { |
| 823 |
background: #fff; |
| 824 |
} |
| 825 |
|
| 826 |
.os-chromeless.themes-php .theme-overlay.active .theme-about { |
| 827 |
padding: 24px; |
| 828 |
} |
| 829 |
|
| 830 |
.os-chromeless.themes-php .theme-overlay.active .theme-actions { |
| 831 |
justify-content: flex-end; |
| 832 |
padding: 10px 16px 5px; |
| 833 |
} |
| 834 |
|
| 835 |
/* Single-theme mode is a workspace card, not a stranded modal. */ |
| 836 |
.os-chromeless.themes-php .themes.single-theme { |
| 837 |
display: block; |
| 838 |
margin: 0; |
| 839 |
} |
| 840 |
|
| 841 |
.os-chromeless.themes-php .themes.single-theme .theme-overlay.active { |
| 842 |
display: block; |
| 843 |
position: static; |
| 844 |
} |
| 845 |
|
| 846 |
.os-chromeless.themes-php |
| 847 |
.themes.single-theme |
| 848 |
.theme-overlay.active |
| 849 |
.theme-backdrop, |
| 850 |
.os-chromeless.themes-php |
| 851 |
.themes.single-theme |
| 852 |
.theme-overlay.active |
| 853 |
.theme-header { |
| 854 |
display: none; |
| 855 |
} |
| 856 |
|
| 857 |
.os-chromeless.themes-php |
| 858 |
.themes.single-theme |
| 859 |
.theme-overlay.active |
| 860 |
.theme-wrap { |
| 861 |
display: grid; |
| 862 |
grid-template-columns: minmax(320px, 0.95fr) minmax(300px, 1.05fr); |
| 863 |
grid-template-areas: "screenshot details"; |
| 864 |
column-gap: 24px; |
| 865 |
align-items: start; |
| 866 |
position: relative; |
| 867 |
top: auto; |
| 868 |
right: auto; |
| 869 |
bottom: auto; |
| 870 |
left: auto; |
| 871 |
min-height: 0; |
| 872 |
padding: 12px 16px; |
| 873 |
background: #fff; |
| 874 |
border: 1px solid #c3c4c7; |
| 875 |
border-radius: 12px; |
| 876 |
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08); |
| 877 |
overflow: visible; |
| 878 |
z-index: 10; |
| 879 |
} |
| 880 |
|
| 881 |
.os-chromeless.themes-php |
| 882 |
.themes.single-theme |
| 883 |
.theme-overlay.active |
| 884 |
.theme-about { |
| 885 |
display: contents; |
| 886 |
} |
| 887 |
|
| 888 |
.os-chromeless.themes-php |
| 889 |
.themes.single-theme |
| 890 |
.theme-overlay.active |
| 891 |
.theme-screenshots { |
| 892 |
grid-area: screenshot; |
| 893 |
float: none; |
| 894 |
width: 100%; |
| 895 |
max-width: none; |
| 896 |
margin: 0; |
| 897 |
} |
| 898 |
|
| 899 |
.os-chromeless.themes-php |
| 900 |
.themes.single-theme |
| 901 |
.theme-overlay.active |
| 902 |
.screenshot { |
| 903 |
background: #f0f0f1; |
| 904 |
border: 0; |
| 905 |
border-radius: 8px; |
| 906 |
box-shadow: 0 0 0 1px #dcdcde; |
| 907 |
overflow: hidden; |
| 908 |
} |
| 909 |
|
| 910 |
.os-chromeless.themes-php |
| 911 |
.themes.single-theme |
| 912 |
.theme-overlay.active |
| 913 |
.theme-info { |
| 914 |
grid-area: details; |
| 915 |
align-self: start; |
| 916 |
float: none; |
| 917 |
width: auto; |
| 918 |
max-height: 240px; |
| 919 |
padding: 2px 10px 2px 0; |
| 920 |
overflow: auto; |
| 921 |
scrollbar-gutter: stable; |
| 922 |
} |
| 923 |
|
| 924 |
.os-chromeless.themes-php |
| 925 |
.themes.single-theme |
| 926 |
.theme-overlay.active |
| 927 |
.current-label { |
| 928 |
margin: 0 0 10px; |
| 929 |
padding: 4px 9px; |
| 930 |
background: #e7f5ed; |
| 931 |
border: 1px solid #9ad7b5; |
| 932 |
border-radius: 999px; |
| 933 |
color: #0a5c36; |
| 934 |
font-size: 10px; |
| 935 |
font-weight: 700; |
| 936 |
letter-spacing: 0.05em; |
| 937 |
line-height: 1.4; |
| 938 |
text-transform: uppercase; |
| 939 |
} |
| 940 |
|
| 941 |
.os-chromeless.themes-php |
| 942 |
.themes.single-theme |
| 943 |
.theme-overlay.active |
| 944 |
.theme-name { |
| 945 |
margin: 0; |
| 946 |
color: #1d2327; |
| 947 |
font-size: clamp(25px, 3vw, 34px); |
| 948 |
font-weight: 650; |
| 949 |
letter-spacing: -0.03em; |
| 950 |
line-height: 1.12; |
| 951 |
} |
| 952 |
|
| 953 |
.os-chromeless.themes-php |
| 954 |
.themes.single-theme |
| 955 |
.theme-overlay.active |
| 956 |
.theme-version { |
| 957 |
margin-left: 8px; |
| 958 |
color: #646970; |
| 959 |
font-size: 11px; |
| 960 |
font-weight: 500; |
| 961 |
letter-spacing: 0; |
| 962 |
} |
| 963 |
|
| 964 |
.os-chromeless.themes-php |
| 965 |
.themes.single-theme |
| 966 |
.theme-overlay.active |
| 967 |
.theme-author { |
| 968 |
margin: 8px 0 14px; |
| 969 |
color: #646970; |
| 970 |
font-size: 13px; |
| 971 |
} |
| 972 |
|
| 973 |
.os-chromeless.themes-php |
| 974 |
.themes.single-theme |
| 975 |
.theme-overlay.active |
| 976 |
.theme-autoupdate { |
| 977 |
margin: 0 0 14px; |
| 978 |
padding: 9px 10px; |
| 979 |
background: #f6f7f7; |
| 980 |
border: 1px solid #dcdcde; |
| 981 |
border-radius: 6px; |
| 982 |
font-size: 12px; |
| 983 |
} |
| 984 |
|
| 985 |
.os-chromeless.themes-php |
| 986 |
.themes.single-theme |
| 987 |
.theme-overlay.active |
| 988 |
.theme-description { |
| 989 |
margin: 0; |
| 990 |
color: #3c434a; |
| 991 |
font-size: 13px; |
| 992 |
line-height: 1.55; |
| 993 |
} |
| 994 |
|
| 995 |
.os-chromeless.themes-php |
| 996 |
.themes.single-theme |
| 997 |
.theme-overlay.active |
| 998 |
.theme-tags, |
| 999 |
.os-chromeless.themes-php |
| 1000 |
.themes.single-theme |
| 1001 |
.theme-overlay.active |
| 1002 |
.parent-theme { |
| 1003 |
margin-top: 16px; |
| 1004 |
padding-top: 12px; |
| 1005 |
border-top-width: 1px; |
| 1006 |
color: #646970; |
| 1007 |
font-size: 11px; |
| 1008 |
line-height: 1.5; |
| 1009 |
} |
| 1010 |
|
| 1011 |
.os-chromeless.themes-php |
| 1012 |
.themes.single-theme |
| 1013 |
.theme-overlay.active |
| 1014 |
.theme-actions { |
| 1015 |
display: flex; |
| 1016 |
grid-area: details; |
| 1017 |
align-items: center; |
| 1018 |
align-self: end; |
| 1019 |
justify-content: flex-start; |
| 1020 |
position: static; |
| 1021 |
padding: 14px 0 0; |
| 1022 |
background: transparent; |
| 1023 |
border-top: 1px solid #dcdcde; |
| 1024 |
text-align: left; |
| 1025 |
} |
| 1026 |
|
| 1027 |
.os-chromeless.themes-php |
| 1028 |
.themes.single-theme |
| 1029 |
.theme-overlay.active |
| 1030 |
.theme-actions |
| 1031 |
.active-theme { |
| 1032 |
display: flex; |
| 1033 |
flex-wrap: wrap; |
| 1034 |
gap: 7px; |
| 1035 |
} |
| 1036 |
|
| 1037 |
.os-chromeless.themes-php |
| 1038 |
.themes.single-theme |
| 1039 |
.theme-overlay.active |
| 1040 |
.theme-actions |
| 1041 |
.inactive-theme { |
| 1042 |
display: none; |
| 1043 |
} |
| 1044 |
|
| 1045 |
.os-chromeless.themes-php |
| 1046 |
.themes.single-theme |
| 1047 |
.theme-overlay.active |
| 1048 |
.theme-actions |
| 1049 |
.button { |
| 1050 |
margin: 0; |
| 1051 |
} |
| 1052 |
|
| 1053 |
@media (max-width: 760px) { |
| 1054 |
.os-chromeless.themes-php #wpbody-content { |
| 1055 |
padding: 12px; |
| 1056 |
} |
| 1057 |
|
| 1058 |
.os-chromeless.themes-php .openstation-themes-intro { |
| 1059 |
align-items: flex-start; |
| 1060 |
gap: 16px; |
| 1061 |
padding: 16px; |
| 1062 |
} |
| 1063 |
|
| 1064 |
.os-chromeless.themes-php .openstation-themes-intro__copy > p:last-child { |
| 1065 |
display: none; |
| 1066 |
} |
| 1067 |
|
| 1068 |
.os-chromeless.themes-php |
| 1069 |
.themes.single-theme |
| 1070 |
.theme-overlay.active |
| 1071 |
.theme-wrap { |
| 1072 |
grid-template-columns: minmax(0, 1fr); |
| 1073 |
grid-template-areas: |
| 1074 |
"screenshot" |
| 1075 |
"info" |
| 1076 |
"actions"; |
| 1077 |
padding: 16px; |
| 1078 |
} |
| 1079 |
|
| 1080 |
.os-chromeless.themes-php |
| 1081 |
.themes.single-theme |
| 1082 |
.theme-overlay.active |
| 1083 |
.theme-info { |
| 1084 |
grid-area: info; |
| 1085 |
max-height: none; |
| 1086 |
padding-right: 0; |
| 1087 |
overflow: visible; |
| 1088 |
} |
| 1089 |
|
| 1090 |
.os-chromeless.themes-php |
| 1091 |
.themes.single-theme |
| 1092 |
.theme-overlay.active |
| 1093 |
.theme-actions { |
| 1094 |
grid-area: actions; |
| 1095 |
} |
| 1096 |
|
| 1097 |
.os-chromeless.themes-php .theme-overlay.active .theme-wrap { |
| 1098 |
top: 10px; |
| 1099 |
right: 10px; |
| 1100 |
bottom: 10px; |
| 1101 |
left: 10px; |
| 1102 |
} |
| 1103 |
} |
| 1104 |
|
| 1105 |
@media (max-width: 480px) { |
| 1106 |
.os-chromeless.themes-php .openstation-themes-intro__count { |
| 1107 |
display: none; |
| 1108 |
} |
| 1109 |
|
| 1110 |
.os-chromeless.themes-php .openstation-themes-intro h1 { |
| 1111 |
font-size: 21px; |
| 1112 |
} |
| 1113 |
} |
| 1114 |
|
| 1115 |
@media (prefers-reduced-motion: reduce) { |
| 1116 |
.os-chromeless.themes-php |
| 1117 |
.theme-browser |
| 1118 |
.themes:not(.single-theme) |
| 1119 |
> .theme { |
| 1120 |
transition: none; |
| 1121 |
} |
| 1122 |
} |
| 1123 |
|