PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.10
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.10
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
← All changes | assets/css/window-overview.css +357 -63 0.9.01.1.10 View file →
@@ -1,6 +1,6 @@
1 1 /**
2 - * Desktop Mode — Overview.
2 + * OpenStation — Overview.
3 3 *
4 4 * Zoom-out grid view: windows transform into thumbnails, floating
5 5 * labels sit above them, and a top bar lists every virtual desktop
6 6 * with one tile per space plus a "+" to add a new one. The dock
@@ -19,16 +19,16 @@
19 19 * live in window-states.css.
20 20 *
21 21 * @since 6.9.0
22 22 */
23 -.desktop-mode-area--overview {
23 +.os-area--overview {
24 24 /* Darken the backdrop and tint it so windows feel like they're
25 25 * floating over a neutral canvas, not the wallpaper. */
26 - background-color: rgba( 0, 0, 0, 0.45 );
26 + background-color: var( --os-ui-scrim, rgba( 0, 0, 0, 0.45 ) );
27 27 transition: background-color 0.28s ease;
28 28 }
29 29
30 -.desktop-mode-window--overview {
30 +.os-window--overview {
31 31 transform-origin: top left;
32 32 transition:
33 33 transform 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ),
34 34 box-shadow 0.2s ease,
@@ -52,17 +52,17 @@
52 52 * trip a close button, or launch an in-iframe action. Only the
53 53 * window's root element receives pointer events; our desktop-level
54 54 * pointerdown/pointerup handlers drive selection from there.
55 55 */
56 -.desktop-mode-window--overview * {
56 +.os-window--overview * {
57 57 pointer-events: none;
58 58 }
59 59
60 -.desktop-mode-window--overview {
60 +.os-window--overview {
61 61 pointer-events: auto;
62 62 }
63 63
64 -.desktop-mode-window--overview:hover {
64 +.os-window--overview:hover {
65 65 box-shadow:
66 66 0 14px 38px rgba( 0, 0, 0, 0.45 ),
67 67 0 0 0 3px var( --wp-admin-theme-color, #2271b1 );
68 68 z-index: 1;
@@ -68,8 +68,16 @@
68 68 z-index: 1;
69 69 }
70 70
71 71 /*
72 + * Minimized windows shown in overview are rendered slightly dimmed
73 + * so the user can distinguish them from active windows at a glance.
74 + */
75 +.os-window--overview.os-window--minimized {
76 + opacity: 0.6;
77 +}
78 +
79 +/*
72 80 * Mission-Control-style dimming: once ANY window is hovered, every
73 81 * OTHER window dims and desaturates slightly — creating a clear
74 82 * focal point without needing the hovered one to grow or lift.
75 83 * `:has()` lands in every evergreen browser we target (Chrome 105,
@@ -75,10 +83,10 @@
75 83 * `:has()` lands in every evergreen browser we target (Chrome 105,
76 84 * Safari 15.4, Firefox 121+); older engines miss the dimming but
77 85 * keep the accent-ring affordance, so it degrades gracefully.
78 86 */
79 -.desktop-mode-area--overview:has( .desktop-mode-window--overview:hover )
80 - .desktop-mode-window--overview:not( :hover ) {
87 +.os-area--overview:has( .os-window--overview:hover )
88 + .os-window--overview:not( :hover ) {
81 89 opacity: 0.5;
82 90 filter: brightness( 0.75 );
83 91 transition:
84 92 transform 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ),
@@ -99,9 +107,9 @@
99 107 * shrinking dock doesn't leak icons past its boundary — outside
100 108 * overview, the dock keeps `overflow: visible` from dock.css so
101 109 * per-tile "+ open another" chips can float past the edge.
102 110 */
103 -.desktop-mode-dock {
111 +.os-dock {
104 112 transition:
105 113 width 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ),
106 114 min-width 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ),
107 115 padding 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ),
@@ -109,15 +117,34 @@
109 117 opacity 0.22s ease;
110 118 }
111 119
112 120 /*
121 + * The escape hatch for the rule above, and the reason it lives here
122 + * rather than in `dock.css`: this is the only place `.os-dock` is
123 + * given a transition, so it is the only place worth looking when one
124 + * fires where it should not.
125 + *
126 + * The properties above are geometry, and geometry also changes for a
127 + * reason that is not a state change: moving the dock to another edge
128 + * tears the rail down and builds a new one on the SAME element, which
129 + * still carries the previous placement's padding and width. Animated,
130 + * the new dock slides out of the old one's shape — it arrives a size
131 + * too big and settles. `Dock`'s constructor wears this class while it
132 + * commits the placement, so that change lands with nothing to tween
133 + * from.
134 + */
135 +.os-dock--no-transition {
136 + transition: none !important;
137 +}
138 +
139 +/*
113 140 * Collapse EVERY dock (bottom + side rails, Classic / Unified /
114 141 * Spatial layouts alike) when overview is active.
115 142 *
116 143 * The placement-specific width rules in dock.css —
117 - * `.desktop-mode-dock[ data-desktop-mode-dock-placement="left" ]
118 - * { width: var( --desktop-mode-dock-width ); }` — share equal
119 - * specificity with `.desktop-mode-shell--overview .desktop-mode-dock`
144 + * `.os-dock[ data-os-dock-placement="left" ]
145 + * { width: var( --os-dock-width ); }` — share equal
146 + * specificity with `.os-shell--overview .os-dock`
120 147 * (two classes / class+attr) and load LATER in the cascade, so
121 148 * without `!important` the side rail keeps its placement width
122 149 * and stays visible during overview. The bottom dock happens to
123 150 * lack a placement-attr-keyed width override so it collapsed
@@ -127,10 +154,10 @@
127 154 * `!important` is the right tool here: this rule is a transient,
128 155 * mode-scoped override (overview screen only) of a layout-mode
129 156 * default, and we want it to win unconditionally.
130 157 */
131 -.desktop-mode-shell--overview .desktop-mode-dock,
132 -.desktop-mode-shell--overview #desktop-mode-side-dock {
158 +.os-shell--overview .os-dock,
159 +.os-shell--overview #os-side-dock {
133 160 width: 0 !important;
134 161 min-width: 0 !important;
135 162 padding-inline: 0 !important;
136 163 padding-block: 0 !important;
@@ -146,13 +173,30 @@
146 173 opacity 0.22s ease;
147 174 }
148 175
149 176 @media ( prefers-reduced-motion: reduce ) {
150 - .desktop-mode-dock {
177 + .os-dock {
151 178 transition-duration: 0.01ms;
152 179 }
153 180 }
154 181
182 +/*
183 + * Desktop shortcut icons & files-layer tiles —
184 + * hidden during overview so they don't compete with window
185 + * thumbnails or overlap them (especially noticeable with few
186 + * windows or Spatial layout). Matches the fade-out pattern
187 + * used by widgets and pinned notes. Scoped to DIRECT children
188 + * of the desktop area only: folder windows mount their own
189 + * files layer inside the window body, and that must stay
190 + * visible in its thumbnail.
191 + */
192 +.os-area--overview > .os-icons,
193 +.os-area--overview > .os-files-layer {
194 + opacity: 0;
195 + pointer-events: none;
196 + transition: opacity 0.22s ease;
197 +}
198 +
155 199 /* ---------------------------------------------------------------
156 200 * Overview thumbnail labels.
157 201 *
158 202 * Sit outside each window's transform (as absolute-positioned
@@ -163,9 +207,9 @@
163 207 * Layout: one line, icon + title (+ optional meta). Horizontal
164 208 * center-of-thumbnail alignment is handled via the label's
165 209 * own flex + width set in JS to match the scaled thumbnail.
166 210 * --------------------------------------------------------------- */
167 -.desktop-mode-overview-label {
211 +.os-overview-label {
168 212 position: absolute;
169 213 height: 28px;
170 214 display: flex;
171 215 align-items: center;
@@ -171,9 +215,9 @@
171 215 align-items: center;
172 216 justify-content: center;
173 217 gap: 6px;
174 218 padding: 0 10px;
175 - color: #fff;
219 + color: var( --os-ui-fg-on-accent, #fff );
176 220 font-size: 13px;
177 221 font-weight: 500;
178 222 line-height: 1;
179 223 letter-spacing: 0.01em;
@@ -193,21 +237,21 @@
193 237 * The short delay on `transition-*` lets the thumbnails start
194 238 * moving first, so labels appear to "catch up" rather than race
195 239 * ahead of their windows.
196 240 */
197 -.desktop-mode-area--overview .desktop-mode-overview-label {
241 +.os-area--overview .os-overview-label {
198 242 opacity: 1;
199 243 transform: translateY( 0 );
200 244 }
201 245
202 246 /* On exit, flip the animation off. */
203 -.desktop-mode-overview-label--out {
247 +.os-overview-label--out {
204 248 opacity: 0 !important;
205 249 transform: translateY( -4px ) !important;
206 250 transition-delay: 0s !important;
207 251 }
208 252
209 -.desktop-mode-overview-label__icon {
253 +.os-overview-label__icon {
210 254 font-size: 16px;
211 255 width: 16px;
212 256 height: 16px;
213 257 line-height: 1;
@@ -214,14 +258,14 @@
214 258 flex-shrink: 0;
215 259 opacity: 0.9;
216 260 }
217 261
218 -.desktop-mode-overview-label__title {
262 +.os-overview-label__title {
219 263 overflow: hidden;
220 264 text-overflow: ellipsis;
221 265 }
222 266
223 -.desktop-mode-overview-label__meta {
267 +.os-overview-label__meta {
224 268 color: rgba( 255, 255, 255, 0.7 );
225 269 font-weight: 400;
226 270 flex-shrink: 0;
227 271 }
@@ -232,20 +276,20 @@
232 276 * sibling selector picks out exactly the label belonging to the
233 277 * hovered window and keeps it bright; all other labels fall under
234 278 * the `:has()` rule and dim.
235 279 */
236 -.desktop-mode-area--overview:has( .desktop-mode-window--overview:hover )
237 - .desktop-mode-overview-label {
280 +.os-area--overview:has( .os-window--overview:hover )
281 + .os-overview-label {
238 282 opacity: 0.45;
239 283 transform: translateY( 0 );
240 284 }
241 285
242 -.desktop-mode-window--overview:hover + .desktop-mode-overview-label {
286 +.os-window--overview:hover + .os-overview-label {
243 287 opacity: 1 !important;
244 288 }
245 289
246 290 @media ( prefers-reduced-motion: reduce ) {
247 - .desktop-mode-overview-label {
291 + .os-overview-label {
248 292 transition-duration: 0.01ms;
249 293 transition-delay: 0s;
250 294 }
251 295 }
@@ -257,12 +301,16 @@
257 301 * in here via a ::after pseudo-element.
258 302 */
259 303
260 304 @media ( prefers-reduced-motion: reduce ) {
261 - .desktop-mode-area--overview,
262 - .desktop-mode-window--overview {
305 + .os-area--overview,
306 + .os-window--overview {
263 307 transition-duration: 0.01ms;
264 308 }
309 + .os-area--overview > .os-icons,
310 + .os-area--overview > .os-files-layer {
311 + transition-duration: 0.01ms;
312 + }
265 313 }
266 314
267 315 /* ---------------------------------------------------------------
268 316 * Overview top bar — virtual desktops ("Spaces") strip.
@@ -273,14 +321,18 @@
273 321 * to it (and exits overview), the trailing "+" tile creates a new
274 322 * one, and per-tile X buttons close.
275 323 * --------------------------------------------------------------- */
276 324
277 -.desktop-mode-overview-top-bar {
325 +.os-overview-top-bar {
278 326 position: absolute;
279 327 top: 16px;
280 328 left: 50%;
281 329 transform: translateX( -50% );
282 330 z-index: 2;
331 + display: flex;
332 + flex-direction: column;
333 + align-items: center;
334 + gap: 10px;
283 335 opacity: 0;
284 336 pointer-events: none;
285 337 transition:
286 338 opacity 0.22s ease 0.06s,
@@ -286,20 +338,64 @@
286 338 opacity 0.22s ease 0.06s,
287 339 transform 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ) 0.06s;
288 340 }
289 341
290 -.desktop-mode-area--overview .desktop-mode-overview-top-bar {
342 +/* The row above the desktop tiles — the site switcher on a network,
343 + * where every site is its own OpenStation with its own desks. Its own
344 + * panel, so the switcher reads as the thing the tiles hang under, not
345 + * as one more tile. `OVERVIEW_TOP_BAR_HEADER_RESERVE` in
346 + * `overview-constants.ts` is this row's height plus the gap. */
347 +.os-overview-top-bar__header {
348 + display: flex;
349 + align-items: center;
350 + justify-content: center;
351 + padding: 6px 8px;
352 + border-radius: 14px;
353 + background-color: var( --os-ui-scrim, rgba( 0, 0, 0, 0.32 ) );
354 + backdrop-filter: blur( 18px ) saturate( 140% );
355 + -webkit-backdrop-filter: blur( 18px ) saturate( 140% );
356 + box-shadow:
357 + 0 8px 28px rgba( 0, 0, 0, 0.35 ),
358 + inset 0 0 0 1px rgba( 255, 255, 255, 0.08 );
359 + max-width: min( 92vw, 960px );
360 + overflow-x: auto;
361 +}
362 +
363 +/* An install that joined from elsewhere wears a mark before its name,
364 + * and one line stands before the first of them, so the row says which
365 + * sites are this network's own and which are external. Both are light
366 + * DOM the switcher writes into the segment; its button is out of
367 + * reach behind the shadow root. */
368 +.os-site-switcher__divider {
369 + flex: none;
370 + align-self: stretch;
371 + width: 1px;
372 + margin: 5px 3px;
373 + background-color: var( --os-ui-border-strong, #c3c4c7 );
374 +}
375 +
376 +.os-site-switcher__mark.dashicons {
377 + font-size: 14px;
378 + width: 14px;
379 + height: 14px;
380 + line-height: 14px;
381 + margin-right: 5px;
382 + vertical-align: -2px;
383 + opacity: 0.75;
384 +}
385 +
386 +.os-area--overview .os-overview-top-bar {
291 387 opacity: 1;
292 388 pointer-events: auto;
293 389 }
294 390
295 -.desktop-mode-overview-top-bar--out {
391 +.os-overview-top-bar--out {
296 392 opacity: 0 !important;
297 393 transform: translateX( -50% ) translateY( -6px ) !important;
298 394 transition-delay: 0s !important;
299 395 }
300 396
301 -.desktop-mode-overview-top-bar__list {
397 +.os-overview-top-bar__list {
302 398 display: flex;
303 399 flex-direction: row;
304 400 align-items: stretch;
305 401 gap: 12px;
@@ -304,9 +400,9 @@
304 400 align-items: stretch;
305 401 gap: 12px;
306 402 padding: 8px 12px;
307 403 border-radius: 14px;
308 - background-color: rgba( 0, 0, 0, 0.32 );
404 + background-color: var( --os-ui-scrim, rgba( 0, 0, 0, 0.32 ) );
309 405 backdrop-filter: blur( 18px ) saturate( 140% );
310 406 -webkit-backdrop-filter: blur( 18px ) saturate( 140% );
311 407 box-shadow:
312 408 0 8px 28px rgba( 0, 0, 0, 0.35 ),
@@ -312,9 +408,9 @@
312 408 0 8px 28px rgba( 0, 0, 0, 0.35 ),
313 409 inset 0 0 0 1px rgba( 255, 255, 255, 0.08 );
314 410 }
315 411
316 -.desktop-mode-overview-top-bar__tile {
412 +.os-overview-top-bar__tile {
317 413 position: relative;
318 414 display: flex;
319 415 flex-direction: column;
320 416 align-items: stretch;
@@ -324,9 +420,9 @@
324 420 margin: 0;
325 421 background: rgba( 255, 255, 255, 0.04 );
326 422 border: 2px solid rgba( 255, 255, 255, 0.18 );
327 423 border-radius: 10px;
328 - color: #fff;
424 + color: var( --os-ui-fg-on-accent, #fff );
329 425 cursor: pointer;
330 426 overflow: hidden;
331 427 transition:
332 428 border-color 0.18s ease,
@@ -334,24 +430,31 @@
334 430 transform 0.18s ease;
335 431 font: inherit;
336 432 }
337 433
338 -.desktop-mode-overview-top-bar__tile:hover {
434 +.os-overview-top-bar__tile:hover {
435 + background: rgba( 255, 255, 255, 0.08 );
436 +}
437 +
438 +/*
439 + * Excluded because `:hover` (0,2,0) outranks `--active` (0,1,0) — an
440 + * unguarded hover border replaces the active tile's accent ring.
441 + */
442 +.os-overview-top-bar__tile:hover:not( .os-overview-top-bar__tile--active ) {
339 443 border-color: rgba( 255, 255, 255, 0.4 );
340 - background: rgba( 255, 255, 255, 0.08 );
341 444 }
342 445
343 -.desktop-mode-overview-top-bar__tile:focus-visible {
446 +.os-overview-top-bar__tile:focus-visible {
344 447 outline: 2px solid var( --wp-admin-theme-color, #2271b1 );
345 448 outline-offset: 2px;
346 449 }
347 450
348 -.desktop-mode-overview-top-bar__tile--active {
451 +.os-overview-top-bar__tile--active {
349 452 border-color: var( --wp-admin-theme-color, #2271b1 );
350 453 box-shadow: 0 0 0 1px var( --wp-admin-theme-color, #2271b1 );
351 454 }
352 455
353 -.desktop-mode-overview-top-bar__tile-preview {
456 +.os-overview-top-bar__tile-preview {
354 457 display: flex;
355 458 align-items: center;
356 459 justify-content: center;
357 460 height: 60px;
@@ -362,9 +465,9 @@
362 465 );
363 466 border-bottom: 1px solid rgba( 255, 255, 255, 0.08 );
364 467 }
365 468
366 -.desktop-mode-overview-top-bar__tile-count {
469 +.os-overview-top-bar__tile-count {
367 470 font-size: 18px;
368 471 font-weight: 600;
369 472 color: rgba( 255, 255, 255, 0.7 );
370 473 letter-spacing: 0.02em;
@@ -369,9 +472,42 @@
369 472 color: rgba( 255, 255, 255, 0.7 );
370 473 letter-spacing: 0.02em;
371 474 }
372 475
373 -.desktop-mode-overview-top-bar__tile-label {
476 +/*
477 + * A workspace's identity on its overview tile — the glyph and the
478 + * accent it wears on the switcher pill. Overview is where desks are
479 + * compared, and a row of identical grey tiles is exactly where "which
480 + * one was the shop?" gets asked. A plain Space carries neither, so
481 + * these rules simply never match for one.
482 + */
483 +.os-overview-top-bar__tile-glyph {
484 + width: 24px;
485 + height: 24px;
486 + margin-inline-end: 6px;
487 + font-size: 24px;
488 + line-height: 24px;
489 + color: var( --os-workspace-accent, rgba( 255, 255, 255, 0.7 ) );
490 +}
491 +
492 +.os-overview-top-bar__tile--tinted {
493 + border-color: color-mix(
494 + in srgb,
495 + var( --os-workspace-accent, #f252fc ) 60%,
496 + transparent
497 + );
498 +}
499 +
500 +/*
501 + * The active ring still wins: a tinted tile says which workspace it
502 + * is, the ring says which one you are on, and the second question is
503 + * the one overview is open to answer.
504 + */
505 +.os-overview-top-bar__tile--tinted.os-overview-top-bar__tile--active {
506 + border-color: var( --wp-admin-theme-color, #2271b1 );
507 +}
508 +
509 +.os-overview-top-bar__tile-label {
374 510 display: block;
375 511 padding: 6px 10px 8px;
376 512 text-align: center;
377 513 font-size: 12px;
@@ -381,12 +517,35 @@
381 517 overflow: hidden;
382 518 text-overflow: ellipsis;
383 519 }
384 520
385 -.desktop-mode-overview-top-bar__tile-close {
521 +/*
522 + * The tile, plus whatever hangs off it.
523 + *
524 + * A column so the Restore button can sit BELOW the tile rather than
525 + * over its preview — the preview is the tile's picture of the desk,
526 + * and a control laid across it covers the one thing the tile is for.
527 + * `position: relative` stays for the rename / close corner buttons,
528 + * which are still positioned against the wrapper.
529 + *
530 + * The tile keeps its content height (no `flex` on it), so when the
531 + * list stretches every wrapper to the tallest, a desk with no Restore
532 + * simply has empty space under its tile instead of a taller tile —
533 + * every tile box stays the same size and on the same line.
534 + */
535 +.os-overview-top-bar__tile-wrapper {
536 + position: relative;
537 + display: inline-flex;
538 + flex-direction: column;
539 + gap: 6px;
540 +}
541 +
542 +/* Tile corner buttons: edit on the leading edge, close on the
543 + * trailing one, otherwise identical. */
544 +.os-overview-top-bar__tile-close,
545 +.os-overview-top-bar__tile-edit {
386 546 position: absolute;
387 547 top: 4px;
388 - inset-inline-end: 4px;
389 548 width: 18px;
390 549 height: 18px;
391 550 display: flex;
392 551 align-items: center;
@@ -391,9 +550,9 @@
391 550 display: flex;
392 551 align-items: center;
393 552 justify-content: center;
394 553 color: rgba( 255, 255, 255, 0.7 );
395 - background: rgba( 0, 0, 0, 0.45 );
554 + background: var( --os-ui-scrim, rgba( 0, 0, 0, 0.45 ) );
396 555 border-radius: 50%;
397 556 cursor: pointer;
398 557 opacity: 0;
399 558 transition:
@@ -399,41 +558,176 @@
399 558 transition:
400 559 opacity 0.15s ease,
401 560 background-color 0.15s ease,
402 561 color 0.15s ease;
562 + border: none;
563 + padding: 0;
403 564 }
404 565
405 -.desktop-mode-overview-top-bar__tile:hover
406 - .desktop-mode-overview-top-bar__tile-close {
566 +.os-overview-top-bar__tile-edit {
567 + inset-inline-start: 4px;
568 +}
569 +
570 +.os-overview-top-bar__tile-close {
571 + inset-inline-end: 4px;
572 +}
573 +
574 +.os-overview-top-bar__tile-wrapper:is( :hover, :focus-within )
575 + :is(
576 + .os-overview-top-bar__tile-close,
577 + .os-overview-top-bar__tile-edit
578 + ),
579 +.os-overview-top-bar__tile-close:focus-visible,
580 +.os-overview-top-bar__tile-edit:focus-visible {
407 581 opacity: 1;
408 582 }
409 583
410 -.desktop-mode-overview-top-bar__tile-close:hover {
411 - background: #d63638;
412 - color: #fff;
584 +.os-overview-top-bar__tile-close:hover {
585 + background: var( --os-ui-danger, #d63638 );
586 + color: var( --os-ui-fg-on-accent, #fff );
413 587 }
414 588
415 589 /*
590 + * The desk's actions — a column below the tile, separated by the
591 + * wrapper's gap. Not over the preview: the preview is the tile's
592 + * picture of the desk, and a bar laid across it covers the one thing
593 + * the tile is there to show. Not the corners either — edit and close
594 + * have those — and Restore needs a word, because no glyph says
595 + * "rebuild this desk".
596 + *
597 + * **Restore** is always visible, but only painted on a desk with
598 + * something to restore, so its absence is information. Once any desk
599 + * on the bar can restore, every column reserves the row, so every
600 + * tile box stays on the same line.
601 + */
602 +.os-overview-top-bar__tile-actions {
603 + display: flex;
604 + flex-direction: column;
605 + gap: 4px;
606 +}
607 +
608 +/* One 22px row, once some desk on the bar can restore. */
609 +.os-overview-top-bar__list--restorable .os-overview-top-bar__tile-actions {
610 + min-height: 22px;
611 +}
612 +
613 +.os-overview-top-bar__tile-action {
614 + display: flex;
615 + align-items: center;
616 + justify-content: center;
617 + gap: 5px;
618 + height: 22px;
619 + padding: 0 8px;
620 + border: 1px solid rgba( 255, 255, 255, 0.14 );
621 + border-radius: 8px;
622 + background: var( --os-ui-scrim, rgba( 0, 0, 0, 0.42 ) );
623 + color: rgba( 255, 255, 255, 0.78 );
624 + font: inherit;
625 + font-size: 10px;
626 + font-weight: 600;
627 + letter-spacing: 0.04em;
628 + text-transform: uppercase;
629 + cursor: pointer;
630 + transition:
631 + opacity 0.15s ease,
632 + background-color 0.15s ease,
633 + border-color 0.15s ease,
634 + color 0.15s ease;
635 +}
636 +
637 +.os-overview-top-bar__tile-action svg {
638 + width: 12px;
639 + height: 12px;
640 + flex: 0 0 auto;
641 +}
642 +
643 +.os-overview-top-bar__tile-action:hover {
644 + background: var( --os-ui-accent, #f252fc );
645 + border-color: var( --os-ui-accent, #f252fc );
646 + color: var( --os-ui-accent-ink, var( --os-ui-fg-on-accent, #fff ) );
647 +}
648 +
649 +.os-overview-top-bar__tile-action:focus-visible {
650 + outline: 2px solid var( --wp-admin-theme-color, #2271b1 );
651 + outline-offset: 2px;
652 +}
653 +
654 +@media ( prefers-reduced-motion: reduce ) {
655 + .os-overview-top-bar__tile-action {
656 + transition: none;
657 + }
658 +}
659 +
660 +.os-overview-top-bar__tile-edit:hover {
661 + background: var( --os-ui-accent, #f252fc );
662 + color: var( --os-ui-accent-ink, var( --os-ui-fg-on-accent, #fff ) );
663 +}
664 +
665 +/*
666 + * The label while it is being edited. Neutral hairline, not the
667 + * accent: the active tile already wears an accent ring, and a second
668 + * one reads as a rendering fault. `outline: none` is safe — the
669 + * element is only editable while focused, so its treatment IS the
670 + * focus indicator.
671 + */
672 +.os-overview-top-bar__tile-label[contenteditable] {
673 + outline: none;
674 + cursor: text;
675 + background: rgba( 0, 0, 0, 0.55 );
676 + border-radius: 6px;
677 + box-shadow: inset 0 0 0 1px rgba( 255, 255, 255, 0.28 );
678 +}
679 +
680 +
681 +/*
416 682 * Hide the close X when there's only one tile left — the tile
417 - * counts both real desktops AND the trailing "+", so "only one
418 - * desktop" means exactly two tiles in the list.
683 + * list contains both desktop wrappers AND the trailing "+".
684 + * So "only one desktop" means exactly one wrapper in the list.
419 685 */
420 -.desktop-mode-overview-top-bar__list:has(
421 - .desktop-mode-overview-top-bar__tile:not(
422 - .desktop-mode-overview-top-bar__tile--add
423 - ):only-of-type
686 +.os-overview-top-bar__list:has(
687 + .os-overview-top-bar__tile-wrapper:only-of-type
424 688 )
425 - .desktop-mode-overview-top-bar__tile-close {
689 + .os-overview-top-bar__tile-close {
426 690 display: none;
427 691 }
428 692
429 -.desktop-mode-overview-top-bar__tile--add {
693 +/*
694 + * The "+" is the place another desk goes, so it is the size of one.
695 + * It sits in the same wrapper a desk does, above an empty actions
696 + * block, which is what keeps the dashed box level with the tiles
697 + * instead of stretching over the buttons underneath them.
698 + */
699 +.os-overview-top-bar__tile--add {
430 700 width: 60px;
431 701 background: transparent;
432 702 border-style: dashed;
433 703 }
434 704
435 -.desktop-mode-overview-top-bar__tile--add:hover {
705 +/*
706 + * The slot's own preview band: no gradient and no rule under it, since
707 + * there is nothing to preview. It keeps the band's height, which is
708 + * half of what makes the dashed box a tile tall.
709 + */
710 +.os-overview-top-bar__tile--add .os-overview-top-bar__tile-preview {
711 + background: none;
712 + /* Transparent, not removed. The rule is 1px of the tile's height,
713 + and dropping it left the slot a pixel short of its neighbours. */
714 + border-bottom-color: transparent;
715 +}
716 +
717 +/*
718 + * The glyph centres in the whole slot, not in the preview band it
719 + * happens to sit in. The two rows are there to make the box a tile
720 + * tall; a desk splits them into a picture and a name, and the slot has
721 + * neither, so its one mark belongs in the middle of the box. Taking it
722 + * out of flow is what lets the rows still do the measuring.
723 + */
724 +.os-overview-top-bar__tile--add .os-overview-top-bar__tile-plus {
725 + position: absolute;
726 + inset: 0;
727 +}
728 +
729 +.os-overview-top-bar__tile--add:hover {
436 730 background: rgba( 255, 255, 255, 0.08 );
437 731 }
438 732
439 733 /*
@@ -442,9 +736,9 @@
442 736 * glance "Enter will land here". The dashed border on the add tile
443 737 * flips to solid when the cursor lands, matching the desktop tiles'
444 738 * solid borders.
445 739 */
446 -.desktop-mode-overview-top-bar__tile--add.desktop-mode-overview-top-bar__tile--cursor {
740 +.os-overview-top-bar__tile--add.os-overview-top-bar__tile--cursor {
447 741 border-style: solid;
448 742 border-color: var( --wp-admin-theme-color, #2271b1 );
449 743 box-shadow: 0 0 0 1px var( --wp-admin-theme-color, #2271b1 );
450 744 background: rgba( 255, 255, 255, 0.08 );
@@ -449,9 +743,9 @@
449 743 box-shadow: 0 0 0 1px var( --wp-admin-theme-color, #2271b1 );
450 744 background: rgba( 255, 255, 255, 0.08 );
451 745 }
452 746
453 -.desktop-mode-overview-top-bar__tile-plus {
747 +.os-overview-top-bar__tile-plus {
454 748 display: flex;
455 749 align-items: center;
456 750 justify-content: center;
457 751 flex: 1;
@@ -461,8 +755,8 @@
461 755 line-height: 1;
462 756 }
463 757
464 758 @media ( prefers-reduced-motion: reduce ) {
465 - .desktop-mode-overview-top-bar {
759 + .os-overview-top-bar {
466 760 transition-duration: 0.01ms;
467 761 }
468 762 }