PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / trunk
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin vtrunk
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 0.8.6 All 33 releases
desktop-mode / assets / css / window-overview.css

window-overview.css in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin trunk, at assets/css/window-overview.css

784 lines 23.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * OpenStation — Overview.
3 *
4 * Zoom-out grid view: windows transform into thumbnails, floating
5 * labels sit above them, and a top bar lists every virtual desktop
6 * with one tile per space plus a "+" to add a new one. The dock
7 * collapses in parallel with the enter transition.
8 *
9 * Windows keep their `left/top/width/height` intact; only `transform`
10 * animates (translate + scale) so the layout change is a GPU
11 * composite, not a reflow. `transform-origin: top left` makes
12 * translate + scale compose cleanly — a target coordinate maps to
13 * the window's top-left corner pre-scale. Iframes go
14 * `pointer-events: none` inside overview so clicks hit the window
15 * wrapper (and bubble to the desktop-area click handler) instead of
16 * the iframe content, which would otherwise eat the click.
17 *
18 * Base chrome lives in window-chrome.css; non-overview state classes
19 * live in window-states.css.
20 *
21 * @since 6.9.0
22 */
23 .os-area--overview {
24 /* Darken the backdrop and tint it so windows feel like they're
25 * floating over a neutral canvas, not the wallpaper. */
26 background-color: var( --os-ui-scrim, rgba( 0, 0, 0, 0.45 ) );
27 transition: background-color 0.28s ease;
28 }
29
30 .os-window--overview {
31 transform-origin: top left;
32 transition:
33 transform 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ),
34 box-shadow 0.2s ease,
35 opacity 0.2s ease;
36 cursor: pointer;
37 /* Hover effects apply a thick outline rather than another
38 * transform (which would fight the layout's own transform).
39 * Box-shadow is composite-only, so it's cheap. */
40 box-shadow: 0 10px 30px rgba( 0, 0, 0, 0.35 );
41 /* Overview thumbnails are click targets, not reading surfaces —
42 * suppress text selection so a drag across a thumbnail doesn't
43 * highlight paragraphs inside the title bar / native body. */
44 user-select: none;
45 -webkit-user-select: none;
46 }
47
48 /*
49 * While in overview, treat each window as a single click target —
50 * every inner element (title bar, buttons, iframe, native body) goes
51 * transparent to pointer events so drags/taps never start a drag,
52 * trip a close button, or launch an in-iframe action. Only the
53 * window's root element receives pointer events; our desktop-level
54 * pointerdown/pointerup handlers drive selection from there.
55 */
56 .os-window--overview * {
57 pointer-events: none;
58 }
59
60 .os-window--overview {
61 pointer-events: auto;
62 }
63
64 .os-window--overview:hover {
65 box-shadow:
66 0 14px 38px rgba( 0, 0, 0, 0.45 ),
67 0 0 0 3px var( --wp-admin-theme-color, #2271b1 );
68 z-index: 1;
69 }
70
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 /*
80 * Mission-Control-style dimming: once ANY window is hovered, every
81 * OTHER window dims and desaturates slightly — creating a clear
82 * focal point without needing the hovered one to grow or lift.
83 * `:has()` lands in every evergreen browser we target (Chrome 105,
84 * Safari 15.4, Firefox 121+); older engines miss the dimming but
85 * keep the accent-ring affordance, so it degrades gracefully.
86 */
87 .os-area--overview:has( .os-window--overview:hover )
88 .os-window--overview:not( :hover ) {
89 opacity: 0.5;
90 filter: brightness( 0.75 );
91 transition:
92 transform 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ),
93 opacity 0.18s ease,
94 filter 0.18s ease;
95 }
96
97 /*
98 * Dock slides out smoothly during overview — width collapses (the
99 * flex row reflows so the area grows into the freed space) while
100 * the dock itself slides left and fades. `display: none` would have
101 * been abrupt in both directions; animating the flex width does the
102 * heavy lifting and the user sees a single continuous motion.
103 *
104 * `pointer-events: none` comes in immediately on overview enter so
105 * stray clicks mid-animation can't open a window behind the user's
106 * back. `overflow: hidden` is scoped to the collapsed state so the
107 * shrinking dock doesn't leak icons past its boundary — outside
108 * overview, the dock keeps `overflow: visible` from dock.css so
109 * per-tile "+ open another" chips can float past the edge.
110 */
111 .os-dock {
112 transition:
113 width 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ),
114 min-width 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ),
115 padding 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ),
116 transform 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ),
117 opacity 0.22s ease;
118 }
119
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 /*
140 * Collapse EVERY dock (bottom + side rails, Classic / Unified /
141 * Spatial layouts alike) when overview is active.
142 *
143 * The placement-specific width rules in dock.css —
144 * `.os-dock[ data-os-dock-placement="left" ]
145 * { width: var( --os-dock-width ); }` — share equal
146 * specificity with `.os-shell--overview .os-dock`
147 * (two classes / class+attr) and load LATER in the cascade, so
148 * without `!important` the side rail keeps its placement width
149 * and stays visible during overview. The bottom dock happens to
150 * lack a placement-attr-keyed width override so it collapsed
151 * fine; the side rail did not — caught by the regression
152 * screenshot.
153 *
154 * `!important` is the right tool here: this rule is a transient,
155 * mode-scoped override (overview screen only) of a layout-mode
156 * default, and we want it to win unconditionally.
157 */
158 .os-shell--overview .os-dock,
159 .os-shell--overview #os-side-dock {
160 width: 0 !important;
161 min-width: 0 !important;
162 padding-inline: 0 !important;
163 padding-block: 0 !important;
164 transform: translateX( -16px );
165 opacity: 0;
166 overflow: hidden;
167 pointer-events: none;
168 transition:
169 width 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ),
170 min-width 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ),
171 padding 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ),
172 transform 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ),
173 opacity 0.22s ease;
174 }
175
176 @media ( prefers-reduced-motion: reduce ) {
177 .os-dock {
178 transition-duration: 0.01ms;
179 }
180 }
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
199 /* ---------------------------------------------------------------
200 * Overview thumbnail labels.
201 *
202 * Sit outside each window's transform (as absolute-positioned
203 * siblings in the desktop area) so they stay readable at any
204 * thumbnail scale — an icon-sized thumbnail still has its
205 * full-size caption hovering above it.
206 *
207 * Layout: one line, icon + title (+ optional meta). Horizontal
208 * center-of-thumbnail alignment is handled via the label's
209 * own flex + width set in JS to match the scaled thumbnail.
210 * --------------------------------------------------------------- */
211 .os-overview-label {
212 position: absolute;
213 height: 28px;
214 display: flex;
215 align-items: center;
216 justify-content: center;
217 gap: 6px;
218 padding: 0 10px;
219 color: var( --os-ui-fg-on-accent, #fff );
220 font-size: 13px;
221 font-weight: 500;
222 line-height: 1;
223 letter-spacing: 0.01em;
224 text-shadow: 0 1px 3px rgba( 0, 0, 0, 0.55 );
225 white-space: nowrap;
226 pointer-events: none;
227 opacity: 0;
228 transform: translateY( 4px );
229 transition:
230 opacity 0.22s ease 0.06s,
231 transform 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ) 0.06s;
232 z-index: 2;
233 }
234
235 /*
236 * Active labels fade/slide in once the area enters overview mode.
237 * The short delay on `transition-*` lets the thumbnails start
238 * moving first, so labels appear to "catch up" rather than race
239 * ahead of their windows.
240 */
241 .os-area--overview .os-overview-label {
242 opacity: 1;
243 transform: translateY( 0 );
244 }
245
246 /* On exit, flip the animation off. */
247 .os-overview-label--out {
248 opacity: 0 !important;
249 transform: translateY( -4px ) !important;
250 transition-delay: 0s !important;
251 }
252
253 .os-overview-label__icon {
254 font-size: 16px;
255 width: 16px;
256 height: 16px;
257 line-height: 1;
258 flex-shrink: 0;
259 opacity: 0.9;
260 }
261
262 .os-overview-label__title {
263 overflow: hidden;
264 text-overflow: ellipsis;
265 }
266
267 .os-overview-label__meta {
268 color: rgba( 255, 255, 255, 0.7 );
269 font-weight: 400;
270 flex-shrink: 0;
271 }
272
273 /*
274 * Dim labels when another window is hovered. DOM order places
275 * each label immediately after its window, so the adjacent-
276 * sibling selector picks out exactly the label belonging to the
277 * hovered window and keeps it bright; all other labels fall under
278 * the `:has()` rule and dim.
279 */
280 .os-area--overview:has( .os-window--overview:hover )
281 .os-overview-label {
282 opacity: 0.45;
283 transform: translateY( 0 );
284 }
285
286 .os-window--overview:hover + .os-overview-label {
287 opacity: 1 !important;
288 }
289
290 @media ( prefers-reduced-motion: reduce ) {
291 .os-overview-label {
292 transition-duration: 0.01ms;
293 transition-delay: 0s;
294 }
295 }
296
297 /*
298 * Focused-window label overlay is not strictly needed — the title
299 * bar stays visible in the scaled thumbnail. If we later want a
300 * cleaner label (to combat tiny text on 8+-window grids), slot it
301 * in here via a ::after pseudo-element.
302 */
303
304 @media ( prefers-reduced-motion: reduce ) {
305 .os-area--overview,
306 .os-window--overview {
307 transition-duration: 0.01ms;
308 }
309 .os-area--overview > .os-icons,
310 .os-area--overview > .os-files-layer {
311 transition-duration: 0.01ms;
312 }
313 }
314
315 /* ---------------------------------------------------------------
316 * Overview top bar — virtual desktops ("Spaces") strip.
317 *
318 * Lives at the top of the desktop area while overview is active,
319 * stacked above the dimmed backdrop and below the window
320 * thumbnails. Each tile represents one desktop; clicking switches
321 * to it (and exits overview), the trailing "+" tile creates a new
322 * one, and per-tile X buttons close.
323 * --------------------------------------------------------------- */
324
325 .os-overview-top-bar {
326 position: absolute;
327 top: 16px;
328 left: 50%;
329 transform: translateX( -50% );
330 z-index: 2;
331 display: flex;
332 flex-direction: column;
333 align-items: center;
334 gap: 10px;
335 opacity: 0;
336 pointer-events: none;
337 transition:
338 opacity 0.22s ease 0.06s,
339 transform 0.28s cubic-bezier( 0.2, 0, 0.2, 1 ) 0.06s;
340 }
341
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 {
387 opacity: 1;
388 pointer-events: auto;
389 }
390
391 .os-overview-top-bar--out {
392 opacity: 0 !important;
393 transform: translateX( -50% ) translateY( -6px ) !important;
394 transition-delay: 0s !important;
395 }
396
397 .os-overview-top-bar__list {
398 display: flex;
399 flex-direction: row;
400 align-items: stretch;
401 gap: 12px;
402 padding: 8px 12px;
403 border-radius: 14px;
404 background-color: var( --os-ui-scrim, rgba( 0, 0, 0, 0.32 ) );
405 backdrop-filter: blur( 18px ) saturate( 140% );
406 -webkit-backdrop-filter: blur( 18px ) saturate( 140% );
407 box-shadow:
408 0 8px 28px rgba( 0, 0, 0, 0.35 ),
409 inset 0 0 0 1px rgba( 255, 255, 255, 0.08 );
410 }
411
412 .os-overview-top-bar__tile {
413 position: relative;
414 display: flex;
415 flex-direction: column;
416 align-items: stretch;
417 justify-content: stretch;
418 width: 140px;
419 padding: 0;
420 margin: 0;
421 background: rgba( 255, 255, 255, 0.04 );
422 border: 2px solid rgba( 255, 255, 255, 0.18 );
423 border-radius: 10px;
424 color: var( --os-ui-fg-on-accent, #fff );
425 cursor: pointer;
426 overflow: hidden;
427 transition:
428 border-color 0.18s ease,
429 background-color 0.18s ease,
430 transform 0.18s ease;
431 font: inherit;
432 }
433
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 ) {
443 border-color: rgba( 255, 255, 255, 0.4 );
444 }
445
446 .os-overview-top-bar__tile:focus-visible {
447 outline: 2px solid var( --wp-admin-theme-color, #2271b1 );
448 outline-offset: 2px;
449 }
450
451 .os-overview-top-bar__tile--active {
452 border-color: var( --wp-admin-theme-color, #2271b1 );
453 box-shadow: 0 0 0 1px var( --wp-admin-theme-color, #2271b1 );
454 }
455
456 .os-overview-top-bar__tile-preview {
457 display: flex;
458 align-items: center;
459 justify-content: center;
460 height: 60px;
461 background: linear-gradient(
462 135deg,
463 rgba( 255, 255, 255, 0.04 ),
464 rgba( 255, 255, 255, 0.12 )
465 );
466 border-bottom: 1px solid rgba( 255, 255, 255, 0.08 );
467 }
468
469 .os-overview-top-bar__tile-count {
470 font-size: 18px;
471 font-weight: 600;
472 color: rgba( 255, 255, 255, 0.7 );
473 letter-spacing: 0.02em;
474 }
475
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 {
510 display: block;
511 padding: 6px 10px 8px;
512 text-align: center;
513 font-size: 12px;
514 font-weight: 500;
515 color: rgba( 255, 255, 255, 0.85 );
516 white-space: nowrap;
517 overflow: hidden;
518 text-overflow: ellipsis;
519 }
520
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: rename 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-rename {
546 position: absolute;
547 top: 4px;
548 width: 18px;
549 height: 18px;
550 display: flex;
551 align-items: center;
552 justify-content: center;
553 color: rgba( 255, 255, 255, 0.7 );
554 background: var( --os-ui-scrim, rgba( 0, 0, 0, 0.45 ) );
555 border-radius: 50%;
556 cursor: pointer;
557 opacity: 0;
558 transition:
559 opacity 0.15s ease,
560 background-color 0.15s ease,
561 color 0.15s ease;
562 border: none;
563 padding: 0;
564 }
565
566 .os-overview-top-bar__tile-rename {
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-rename
578 ),
579 .os-overview-top-bar__tile-close:focus-visible,
580 .os-overview-top-bar__tile-rename:focus-visible {
581 opacity: 1;
582 }
583
584 .os-overview-top-bar__tile-close:hover {
585 background: var( --os-ui-danger, #d63638 );
586 color: var( --os-ui-fg-on-accent, #fff );
587 }
588
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 — rename and
594 * close have those — and these two are the actions that need a word,
595 * because no glyph says "rebuild this desk" or "change what it is".
596 *
597 * Two rows. **Restore** is always visible, but only painted on a desk
598 * with something to restore, so its absence is information. **Edit**
599 * is revealed on hover and keyboard focus, like rename and close: it
600 * is a way to change the desk rather than a thing about it, and a row
601 * of Edit buttons at rest would make the bar read as a form. The
602 * column reserves both rows' height whether or not either is present,
603 * so every tile box stays on the same line.
604 */
605 .os-overview-top-bar__tile-actions {
606 display: flex;
607 flex-direction: column;
608 gap: 4px;
609 /*
610 * One 22px row: Edit, which every desk has. Reserving two here
611 * made every bar as tall as its most elaborate desk, so a user
612 * with no workspaces at all paid a row of empty space under every
613 * tile for a button none of their desks could show.
614 */
615 min-height: 22px;
616 }
617
618 /* Two 22px rows and the gap, once some desk on the bar can restore. */
619 .os-overview-top-bar__list--restorable .os-overview-top-bar__tile-actions {
620 min-height: 48px;
621 }
622
623 .os-overview-top-bar__tile-action {
624 display: flex;
625 align-items: center;
626 justify-content: center;
627 gap: 5px;
628 height: 22px;
629 padding: 0 8px;
630 border: 1px solid rgba( 255, 255, 255, 0.14 );
631 border-radius: 8px;
632 background: var( --os-ui-scrim, rgba( 0, 0, 0, 0.42 ) );
633 color: rgba( 255, 255, 255, 0.78 );
634 font: inherit;
635 font-size: 10px;
636 font-weight: 600;
637 letter-spacing: 0.04em;
638 text-transform: uppercase;
639 cursor: pointer;
640 transition:
641 opacity 0.15s ease,
642 background-color 0.15s ease,
643 border-color 0.15s ease,
644 color 0.15s ease;
645 }
646
647 .os-overview-top-bar__tile-action svg {
648 width: 12px;
649 height: 12px;
650 flex: 0 0 auto;
651 }
652
653 .os-overview-top-bar__tile-action:hover {
654 background: var( --os-ui-accent, #f252fc );
655 border-color: var( --os-ui-accent, #f252fc );
656 color: var( --os-ui-fg-on-accent, #fff );
657 }
658
659 .os-overview-top-bar__tile-action:focus-visible {
660 outline: 2px solid var( --wp-admin-theme-color, #2271b1 );
661 outline-offset: 2px;
662 }
663
664 /* Edit rests hidden; the wrapper's hover / focus brings it up. */
665 .os-overview-top-bar__tile-edit {
666 opacity: 0;
667 }
668
669 .os-overview-top-bar__tile-wrapper:is( :hover, :focus-within )
670 .os-overview-top-bar__tile-edit,
671 .os-overview-top-bar__tile-edit:focus-visible {
672 opacity: 1;
673 }
674
675 @media ( prefers-reduced-motion: reduce ) {
676 .os-overview-top-bar__tile-action {
677 transition: none;
678 }
679 }
680
681 .os-overview-top-bar__tile-rename:hover {
682 background: var( --os-ui-accent, #f252fc );
683 color: var( --os-ui-fg-on-accent, #fff );
684 }
685
686 /*
687 * The label while it is being edited. Neutral hairline, not the
688 * accent: the active tile already wears an accent ring, and a second
689 * one reads as a rendering fault. `outline: none` is safe — the
690 * element is only editable while focused, so its treatment IS the
691 * focus indicator.
692 */
693 .os-overview-top-bar__tile-label[contenteditable] {
694 outline: none;
695 cursor: text;
696 background: rgba( 0, 0, 0, 0.55 );
697 border-radius: 6px;
698 box-shadow: inset 0 0 0 1px rgba( 255, 255, 255, 0.28 );
699 }
700
701
702 /*
703 * Hide the close X when there's only one tile left — the tile
704 * list contains both desktop wrappers AND the trailing "+".
705 * So "only one desktop" means exactly one wrapper in the list.
706 */
707 .os-overview-top-bar__list:has(
708 .os-overview-top-bar__tile-wrapper:only-of-type
709 )
710 .os-overview-top-bar__tile-close {
711 display: none;
712 }
713
714 /*
715 * The "+" is the place another desk goes, so it is the size of one.
716 * It sits in the same wrapper a desk does, above an empty actions
717 * block, which is what keeps the dashed box level with the tiles
718 * instead of stretching over the buttons underneath them.
719 */
720 .os-overview-top-bar__tile--add {
721 width: 60px;
722 background: transparent;
723 border-style: dashed;
724 }
725
726 /*
727 * The slot's own preview band: no gradient and no rule under it, since
728 * there is nothing to preview. It keeps the band's height, which is
729 * half of what makes the dashed box a tile tall.
730 */
731 .os-overview-top-bar__tile--add .os-overview-top-bar__tile-preview {
732 background: none;
733 /* Transparent, not removed. The rule is 1px of the tile's height,
734 and dropping it left the slot a pixel short of its neighbours. */
735 border-bottom-color: transparent;
736 }
737
738 /*
739 * The glyph centres in the whole slot, not in the preview band it
740 * happens to sit in. The two rows are there to make the box a tile
741 * tall; a desk splits them into a picture and a name, and the slot has
742 * neither, so its one mark belongs in the middle of the box. Taking it
743 * out of flow is what lets the rows still do the measuring.
744 */
745 .os-overview-top-bar__tile--add .os-overview-top-bar__tile-plus {
746 position: absolute;
747 inset: 0;
748 }
749
750 .os-overview-top-bar__tile--add:hover {
751 background: rgba( 255, 255, 255, 0.08 );
752 }
753
754 /*
755 * Keyboard cursor parked on the "+" tile. Mirrors the visual weight
756 * of `--active` (solid accent border + halo) so the user sees at a
757 * glance "Enter will land here". The dashed border on the add tile
758 * flips to solid when the cursor lands, matching the desktop tiles'
759 * solid borders.
760 */
761 .os-overview-top-bar__tile--add.os-overview-top-bar__tile--cursor {
762 border-style: solid;
763 border-color: var( --wp-admin-theme-color, #2271b1 );
764 box-shadow: 0 0 0 1px var( --wp-admin-theme-color, #2271b1 );
765 background: rgba( 255, 255, 255, 0.08 );
766 }
767
768 .os-overview-top-bar__tile-plus {
769 display: flex;
770 align-items: center;
771 justify-content: center;
772 flex: 1;
773 font-size: 28px;
774 font-weight: 200;
775 color: rgba( 255, 255, 255, 0.7 );
776 line-height: 1;
777 }
778
779 @media ( prefers-reduced-motion: reduce ) {
780 .os-overview-top-bar {
781 transition-duration: 0.01ms;
782 }
783 }
784