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

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

896 lines 29.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Desktop Mode — Dock.
3 *
4 * A rail that hosts admin menu tiles plus shell-level system tiles.
5 * Placement (left / right / bottom) is reflected on each dock element
6 * itself as `data-desktop-mode-dock-placement`; this stylesheet keys
7 * off that attribute for layout, tooltip anchor, and indicator
8 * position. Two instances can coexist (Classic layout: a left side
9 * bar with core menus + a bottom dock with plugin menus); each
10 * carries its own placement attribute, so the two rails are styled
11 * independently without leaking selector state to one another.
12 *
13 * @since 6.9.0
14 */
15
16 /* ------------------------------------------------------------------
17 * Shared dock chrome — applies to every placement.
18 * ------------------------------------------------------------------ */
19
20 .desktop-mode-dock {
21 display: flex;
22 align-items: center;
23 z-index: var( --desktop-mode-z-dock );
24 background: var( --desktop-mode-dock-bg );
25 backdrop-filter: blur( 20px );
26 -webkit-backdrop-filter: blur( 20px );
27 flex-shrink: 0;
28 /*
29 * overflow: visible so the per-tile "Open another" chip can float
30 * past the dock's outer edge. Real-world bottom-dock menus fit
31 * the viewport without a scrollbar; vertical placements route
32 * their overflow through the inner `__scroll` wrapper, leaving
33 * the dock itself non-scrolling.
34 */
35 overflow: visible;
36 }
37
38 /* A single dock tile — icon button (+ optional instance rail below). */
39 .desktop-mode-dock__item {
40 position: relative;
41 display: flex;
42 flex-direction: column;
43 align-items: center;
44 width: 40px;
45 flex-shrink: 0;
46 }
47
48 /* Primary icon button — opens or focuses the page. */
49 .desktop-mode-dock__item-primary {
50 position: relative;
51 display: flex;
52 align-items: center;
53 justify-content: center;
54 width: 40px;
55 height: 40px;
56 border: none;
57 border-radius: 10px;
58 cursor: pointer;
59 padding: 0;
60 background: transparent;
61 color: rgba( 255, 255, 255, 0.7 );
62 transition: background-color 0.15s ease, transform 0.15s ease, color 0.15s ease;
63 }
64
65 .desktop-mode-dock__item-primary:hover {
66 background: rgba( 255, 255, 255, 0.15 );
67 color: #fff;
68 transform: scale( 1.1 );
69 }
70
71 .desktop-mode-dock__item-primary:active {
72 transform: scale( 0.95 );
73 }
74
75 .desktop-mode-dock__item-primary:focus-visible {
76 outline: 2px solid rgba( 255, 255, 255, 0.7 );
77 outline-offset: 2px;
78 }
79
80 /* Stacked card hint on hover — a second card peeks from behind the
81 icon when the tile has multiple open windows. Only visible on hover so
82 it never conflicts with focused tiles. */
83 .desktop-mode-dock__item--stacked:hover .desktop-mode-dock__item-primary::before,
84 .desktop-mode-dock__item--stacked[data-peek-active] .desktop-mode-dock__item-primary::before {
85 content: "";
86 position: absolute;
87 top: -4px;
88 inset-inline-start: -4px;
89 width: 100%;
90 height: 100%;
91 border-radius: inherit;
92 background: rgba( 255, 255, 255, 0.06 );
93 border: 1px solid rgba( 255, 255, 255, 0.10 );
94 z-index: -1;
95 pointer-events: none;
96 }
97
98 /* Dashicon inside dock item. */
99 .desktop-mode-dock__item .dashicons {
100 font-size: var( --desktop-mode-dock-icon-size, 20px );
101 width: var( --desktop-mode-dock-icon-size, 20px );
102 height: var( --desktop-mode-dock-icon-size, 20px );
103 line-height: 1;
104 }
105
106 /* SVG icon (for custom post types with data:image/svg+xml icons). */
107 .desktop-mode-dock__item-svg {
108 width: var( --desktop-mode-dock-icon-size, 20px );
109 height: var( --desktop-mode-dock-icon-size, 20px );
110 display: block;
111 fill: currentColor;
112 opacity: 0.7;
113 /* Some plugin SVGs ship hardcoded `fill="..."` attributes inside
114 the markup, which override `fill: currentColor` and make the
115 icon keep its brand color. Force the monochrome white palette
116 to match dashicons. */
117 filter: brightness(0) invert(1);
118 transition: opacity 0.15s ease;
119 }
120
121 .desktop-mode-dock__item-primary:hover .desktop-mode-dock__item-svg {
122 opacity: 1;
123 }
124
125 /*
126 * Letter-badge icon — rendered for items registered without a
127 * dashicon / SVG / image URL. Miniature "app placeholder": rounded
128 * square tinted with a title-derived hue, first letter centered.
129 */
130 .desktop-mode-dock__item-letter {
131 display: flex;
132 align-items: center;
133 justify-content: center;
134 width: 28px;
135 height: 28px;
136 border-radius: 8px;
137 color: #fff;
138 font-size: 15px;
139 font-weight: 700;
140 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
141 line-height: 1;
142 letter-spacing: 0;
143 text-shadow: 0 1px 1px rgba( 0, 0, 0, 0.22 );
144 box-shadow:
145 inset 0 1px 0 rgba( 255, 255, 255, 0.2 ),
146 inset 0 -1px 0 rgba( 0, 0, 0, 0.15 );
147 }
148
149 .desktop-mode-dock__item img.desktop-mode-dock__item-img {
150 width: var( --desktop-mode-dock-icon-size, 20px );
151 height: var( --desktop-mode-dock-icon-size, 20px );
152 display: block;
153 opacity: 0.7;
154 transition: opacity 0.15s ease;
155 }
156
157 .desktop-mode-dock__item-primary:hover img.desktop-mode-dock__item-img {
158 opacity: 1;
159 }
160
161 /* "Open another" floating chip — shown on multi-capable tiles that
162 * already have ≥1 open instance. Positioned inside each orientation
163 * block so the chip hugs the outward edge of the tile. */
164 .desktop-mode-dock__item-new {
165 position: absolute;
166 display: flex;
167 align-items: center;
168 justify-content: center;
169 width: 20px;
170 height: 20px;
171 padding: 0;
172 border: 2px solid var( --desktop-mode-dock-bg, rgba( 0, 0, 0, 0.4 ) );
173 border-radius: 50%;
174 background: var( --wp-admin-theme-color, #2271b1 );
175 color: #fff;
176 cursor: pointer;
177 transition: background-color 0.15s ease, transform 0.15s ease;
178 z-index: 1;
179 }
180
181 .desktop-mode-dock__item-new[hidden] {
182 display: none;
183 }
184
185 .desktop-mode-dock__item-new:hover {
186 background: #fff;
187 color: var( --wp-admin-theme-color, #2271b1 );
188 }
189
190 .desktop-mode-dock__item-new:focus-visible {
191 outline: 2px solid rgba( 255, 255, 255, 0.85 );
192 outline-offset: 2px;
193 }
194
195 /* Notification badge — iOS/macOS-style pill straddling the tile
196 * corner. Ring matches the dock bg so it reads as "punched out."
197 * Badge positioning is placement-aware via the blocks below. */
198 .desktop-mode-dock__badge {
199 position: absolute;
200 min-width: 16px;
201 height: 16px;
202 padding: 0 5px;
203 box-sizing: border-box;
204 border-radius: 999px;
205 background: linear-gradient( 180deg, #ff5a5a 0%, #d63638 100% );
206 color: #fff;
207 display: inline-flex;
208 align-items: center;
209 justify-content: center;
210 font-size: 10px;
211 font-weight: 700;
212 line-height: 1;
213 letter-spacing: 0.01em;
214 text-align: center;
215 font-variant-numeric: tabular-nums;
216 box-shadow:
217 0 0 0 1.5px var( --desktop-mode-dock-bg, rgba( 0, 0, 0, 0.4 ) ),
218 0 1px 3px rgba( 0, 0, 0, 0.35 );
219 pointer-events: none;
220 transition: transform 0.15s ease;
221 top: -3px;
222 inset-inline-end: -3px;
223 }
224
225 .desktop-mode-dock__item:hover .desktop-mode-dock__badge {
226 transform: scale( 1.05 );
227 top: -4px;
228 }
229
230 /*
231 * Recycle Bin count — unlike unread/update badges, the trash count
232 * is ambient state. Keep it inside the bin glyph as a quiet neutral
233 * marker instead of a red alert on the tile corner.
234 */
235 .desktop-mode-dock__item[ data-system-id="desktop-mode-recycle-bin" ] .desktop-mode-dock__badge {
236 top: 20px;
237 inset-inline-end: 7px;
238 min-width: 13px;
239 height: 13px;
240 padding: 0 3px;
241 border-radius: 999px;
242 background: rgba( 29, 35, 39, 0.72 );
243 color: rgba( 255, 255, 255, 0.92 );
244 font-size: 8px;
245 font-weight: 700;
246 letter-spacing: 0;
247 box-shadow:
248 inset 0 0 0 1px rgba( 255, 255, 255, 0.26 ),
249 0 1px 2px rgba( 0, 0, 0, 0.24 );
250 }
251
252 .desktop-mode-dock__item[ data-system-id="desktop-mode-recycle-bin" ]:hover .desktop-mode-dock__badge {
253 top: 20px;
254 transform: scale( 1.03 );
255 }
256
257 /* System tiles — same footprint as menu tiles, no badges, no rails. */
258 .desktop-mode-dock__item--system .desktop-mode-dock__item-primary {
259 color: rgba( 255, 255, 255, 0.8 );
260 }
261
262 /* -----------------------------------------------------------------------------
263 * Attention animations — driven by `Dock.setAttention()` /
264 * `Window.requestAttention()`. The class lands on the tile root,
265 * the animation runs on the primary icon button so the badge stays
266 * stable. Three modes (pulse / shake / bounce) and three intensities
267 * (subtle / normal / strong). All animations gated on
268 * `prefers-reduced-motion: no-preference`; the reduced-motion fallback
269 * shows a static accent ring for the same duration so the affordance
270 * still works.
271 * ------------------------------------------------------------------------- */
272
273 @keyframes wpd-dock-attention-pulse {
274 0%, 100% {
275 transform: scale( 1 );
276 box-shadow: 0 0 0 0 rgba( 255, 90, 90, 0.55 );
277 }
278 50% {
279 transform: scale( 1.06 );
280 box-shadow: 0 0 0 8px rgba( 255, 90, 90, 0 );
281 }
282 }
283
284 @keyframes wpd-dock-attention-shake {
285 0%, 100% {
286 transform: translateX( 0 );
287 }
288 20%, 60% {
289 transform: translateX( -3px );
290 }
291 40%, 80% {
292 transform: translateX( 3px );
293 }
294 }
295
296 @keyframes wpd-dock-attention-bounce {
297 0%, 100% {
298 transform: translateY( 0 );
299 }
300 30% {
301 transform: translateY( -6px );
302 }
303 60% {
304 transform: translateY( -2px );
305 }
306 }
307
308 .desktop-mode-dock__item--attention-pulse .desktop-mode-dock__item-primary {
309 animation: wpd-dock-attention-pulse 1.4s ease-in-out infinite;
310 }
311 .desktop-mode-dock__item--attention-shake .desktop-mode-dock__item-primary {
312 animation: wpd-dock-attention-shake 0.8s ease-in-out infinite;
313 }
314 .desktop-mode-dock__item--attention-bounce .desktop-mode-dock__item-primary {
315 animation: wpd-dock-attention-bounce 1.2s ease-in-out infinite;
316 }
317
318 /* Intensity modulates animation duration — strong = faster, more
319 urgent; subtle = slower, calmer. */
320 .desktop-mode-dock__item--intensity-strong.desktop-mode-dock__item--attention-pulse .desktop-mode-dock__item-primary {
321 animation-duration: 1s;
322 }
323 .desktop-mode-dock__item--intensity-strong.desktop-mode-dock__item--attention-shake .desktop-mode-dock__item-primary {
324 animation-duration: 0.6s;
325 }
326 .desktop-mode-dock__item--intensity-strong.desktop-mode-dock__item--attention-bounce .desktop-mode-dock__item-primary {
327 animation-duration: 0.9s;
328 }
329 .desktop-mode-dock__item--intensity-subtle.desktop-mode-dock__item--attention-pulse .desktop-mode-dock__item-primary {
330 animation-duration: 1.8s;
331 }
332 .desktop-mode-dock__item--intensity-subtle.desktop-mode-dock__item--attention-shake .desktop-mode-dock__item-primary {
333 animation-duration: 1s;
334 }
335 .desktop-mode-dock__item--intensity-subtle.desktop-mode-dock__item--attention-bounce .desktop-mode-dock__item-primary {
336 animation-duration: 1.6s;
337 }
338
339 /* Reduced-motion: kill the animation, replace with a static accent ring
340 for the same visual prominence without the movement. */
341 @media ( prefers-reduced-motion: reduce ) {
342 .desktop-mode-dock__item--attention-pulse .desktop-mode-dock__item-primary,
343 .desktop-mode-dock__item--attention-shake .desktop-mode-dock__item-primary,
344 .desktop-mode-dock__item--attention-bounce .desktop-mode-dock__item-primary {
345 animation: none;
346 outline: 2px solid var( --wp-admin-theme-color, #2271b1 );
347 outline-offset: 2px;
348 border-radius: 8px;
349 }
350 }
351
352 /* Shared tooltip — placement-specific anchor modifiers applied by JS. */
353 .desktop-mode-dock__tooltip {
354 position: fixed;
355 padding: 6px 12px;
356 background: rgba( 0, 0, 0, 0.85 );
357 color: #fff;
358 font-size: 12px;
359 font-weight: 400;
360 line-height: 1.4;
361 white-space: nowrap;
362 border-radius: 6px;
363 pointer-events: none;
364 z-index: calc( var( --desktop-mode-z-dock ) + 1 );
365 opacity: 0;
366 transition: opacity 0.15s ease, transform 0.15s ease;
367 }
368
369 .desktop-mode-dock__tooltip--visible {
370 opacity: 1;
371 }
372
373 /* ------------------------------------------------------------------
374 * Vertical placement (left + right) — dock runs the full shell body
375 * height along one edge, with items stacked vertically.
376 * ------------------------------------------------------------------ */
377
378 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ],
379 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ] {
380 width: var( --desktop-mode-dock-width );
381 flex-direction: column;
382 padding: 16px 0 12px;
383 gap: 6px;
384 /*
385 * Vertical docks split into two inner wrappers:
386 * - `__scroll` — flex: 1, scrollable, hosts menu tiles + the
387 * inline core/plugin separator. When the menu
388 * exceeds the dock's height, this is what
389 * scrolls; the outer dock stays a fixed pillar.
390 * - `__pinned` — sized to content, hosts system tiles
391 * (Recycle Bin, OS Settings, …) + their hairline
392 * separator. Always visible at the bottom edge
393 * regardless of scroll position.
394 *
395 * `min-height: 0` on the dock is defensive: even though `__scroll`
396 * absorbs the overflow, the dock is a flex item inside the shell
397 * body (a flex row), and the default `min-height: auto` would let
398 * a tall `__pinned` push the dock past the parent's height.
399 */
400 min-height: 0;
401 }
402
403 /*
404 * Scrollable menu-tile area inside vertical docks. `flex: 1 1 0` plus
405 * `min-height: 0` lets it shrink below its content's natural height so
406 * `overflow-y: auto` actually engages. `overflow-x: clip` keeps the
407 * active-dot / focused-pill indicators visible (they sit at x≈2px
408 * inside the wrapper's content box) while preventing `overflow-y: auto`
409 * from coercing horizontal scroll. Scrollbar is hidden visually —
410 * Firefox via `scrollbar-width: none`, WebKit via the pseudo-element
411 * rule below — so the rail keeps its clean macOS look. Mouse wheel,
412 * touchpad, touch, and keyboard focus-into-view all still work.
413 */
414 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ] .desktop-mode-dock__scroll,
415 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ] .desktop-mode-dock__scroll {
416 display: flex;
417 flex-direction: column;
418 align-items: center;
419 gap: 6px;
420 flex: 1 1 0;
421 min-height: 0;
422 width: 100%;
423 overflow-y: auto;
424 overflow-x: clip;
425 scrollbar-width: none;
426 }
427
428 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ] .desktop-mode-dock__scroll::-webkit-scrollbar,
429 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ] .desktop-mode-dock__scroll::-webkit-scrollbar {
430 display: none;
431 }
432
433 /*
434 * Pinned area below `__scroll` — system tiles stay reachable here
435 * regardless of how the menu list scrolls. `flex-shrink: 0` so it's
436 * never compressed; sized to its content's natural height.
437 */
438 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ] .desktop-mode-dock__pinned,
439 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ] .desktop-mode-dock__pinned {
440 display: flex;
441 flex-direction: column;
442 align-items: center;
443 gap: 6px;
444 flex-shrink: 0;
445 width: 100%;
446 }
447
448 /* Left: dock is the first flex item in __body (row layout). */
449 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ] {
450 border-inline-end: 1px solid var( --desktop-mode-dock-border );
451 order: -1;
452 }
453
454 /* Right: push the dock to the end of __body and flip the border edge. */
455 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ] {
456 border-inline-start: 1px solid var( --desktop-mode-dock-border );
457 order: 1;
458 }
459
460 /* Active-indicator dot — left dock: inside-edge vertical pill. */
461 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ]
462 .desktop-mode-dock__item--active::before {
463 content: "";
464 position: absolute;
465 inset-inline-start: -6px;
466 top: 50%;
467 transform: translateY( -50% );
468 width: 4px;
469 height: 4px;
470 border-radius: 50%;
471 background: #fff;
472 }
473
474 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ]
475 .desktop-mode-dock__item--focused::before {
476 width: 4px;
477 height: 16px;
478 border-radius: 2px;
479 }
480
481 /*
482 * "All instances minimized" — the tile still has windows, they're
483 * just hidden. Swap the solid dot for a hollow ring so the user sees
484 * "I have something here, currently tucked away" instead of "I have
485 * a focused / visible window here." `--all-minimized` is layered on
486 * top of `--active`; the rule below wins on selector specificity.
487 *
488 * Sized slightly larger than the solid dot so the ring is legible at
489 * dock size — a 4×4 outline reads as a dirty pixel.
490 */
491 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ]
492 .desktop-mode-dock__item--all-minimized::before {
493 width: 6px;
494 height: 6px;
495 border-radius: 50%;
496 background: transparent;
497 border: 1px solid rgba( 255, 255, 255, 0.85 );
498 }
499
500 /* Right dock: indicator on the opposite edge (inside-facing). */
501 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ]
502 .desktop-mode-dock__item--active::before {
503 content: "";
504 position: absolute;
505 inset-inline-end: -6px;
506 top: 50%;
507 transform: translateY( -50% );
508 width: 4px;
509 height: 4px;
510 border-radius: 50%;
511 background: #fff;
512 }
513
514 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ]
515 .desktop-mode-dock__item--focused::before {
516 width: 4px;
517 height: 16px;
518 border-radius: 2px;
519 }
520
521 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ]
522 .desktop-mode-dock__item--all-minimized::before {
523 width: 6px;
524 height: 6px;
525 border-radius: 50%;
526 background: transparent;
527 border: 1px solid rgba( 255, 255, 255, 0.85 );
528 }
529
530 /* "Open another" chip — left: right of tile; right: left of tile. */
531 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ]
532 .desktop-mode-dock__item-new {
533 top: 50%;
534 inset-inline-end: -13px;
535 transform: translateY( -50% );
536 }
537
538 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ]
539 .desktop-mode-dock__item-new:hover {
540 transform: translateY( -50% ) scale( 1.1 );
541 }
542
543 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ]
544 .desktop-mode-dock__item-new {
545 top: 50%;
546 inset-inline-start: -13px;
547 transform: translateY( -50% );
548 }
549
550 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ]
551 .desktop-mode-dock__item-new:hover {
552 transform: translateY( -50% ) scale( 1.1 );
553 }
554
555 /* Separator — horizontal hairline between menu tiles and system
556 * tiles. With the `__scroll` / `__pinned` split the system separator
557 * lives at the top of `__pinned`; flex-flow handles the "system
558 * tiles at the bottom" placement, no margin-top: auto needed. The
559 * `--group` variant is used inline between core and plugin menu
560 * tiles in `__scroll`; it keeps the hairline styling and sits
561 * exactly where it's inserted. */
562 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ]
563 .desktop-mode-dock__separator,
564 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ]
565 .desktop-mode-dock__separator {
566 width: 60%;
567 height: 1px;
568 margin: 8px auto 4px;
569 background: var( --desktop-mode-dock-border );
570 flex-shrink: 0;
571 }
572
573 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ]
574 .desktop-mode-dock__separator--group,
575 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ]
576 .desktop-mode-dock__separator--group {
577 margin: 10px auto;
578 background: rgba( 255, 255, 255, 0.22 );
579 }
580
581 /*
582 * Hide the system separator when nothing precedes it — on a clean
583 * install (or a low-cap user with zero menu items) the separator
584 * would render as a stray hairline with nothing to divide.
585 *
586 * Two cases:
587 * 1. Bottom dock — separator is a direct child of the dock; hide
588 * when it's the first such child.
589 * 2. Vertical docks — separator lives inside `__pinned`; hide when
590 * `__scroll` (its sibling above) is empty of menu tiles.
591 */
592 .desktop-mode-dock > .desktop-mode-dock__separator:first-child {
593 display: none;
594 }
595
596 .desktop-mode-dock__scroll:not(:has( .desktop-mode-dock__item ))
597 + .desktop-mode-dock__pinned
598 .desktop-mode-dock__separator {
599 display: none;
600 }
601
602 /*
603 * Drop an empty `__scroll` from the flex flow entirely. When only
604 * system tiles are present (e.g. just the Recycle Bin), the empty
605 * wrapper would otherwise still occupy the rail: its `flex-grow`
606 * stretches it — shoving the lone `__pinned` tile to the far edge on
607 * vertical docks — and the inter-wrapper `gap` leaves a phantom
608 * offset that nudges the tile off-center on the bottom dock. Removing
609 * it lets the dock center the lone system tile. As soon as a menu
610 * tile is added the `:has()` no longer matches and normal flow
611 * resumes.
612 *
613 * The `[data-desktop-mode-dock-placement]` qualifier is load-bearing:
614 * the per-placement rules that set `display: flex` on `__scroll` are
615 * (0,3,0); this selector must out-specify them, so it carries the
616 * placement attribute to reach (0,4,0).
617 */
618 .desktop-mode-dock[ data-desktop-mode-dock-placement ]
619 .desktop-mode-dock__scroll:not(:has( .desktop-mode-dock__item )) {
620 display: none;
621 }
622
623 /* Tooltip — anchor modifiers are applied directly to the tooltip
624 * element (NOT via a descendant selector on `.desktop-mode-dock`)
625 * because the tooltip lives in `document.body`. JS writes the
626 * absolute `top` / `left` coords; CSS handles the slide-in
627 * animation direction per modifier.
628 *
629 * - `--after` : left-placed dock (tooltip to the RIGHT of tile)
630 * - `--before` : right-placed dock (tooltip to the LEFT of tile)
631 * - `--above` : bottom-placed dock (tooltip ABOVE the tile)
632 */
633
634 /* Left dock — tooltip slides in from the left by 4px. JS sets
635 * `left` to `tile.right + 8px`; the transform animates the entry. */
636 .desktop-mode-dock__tooltip--after {
637 transform: translateX( -4px );
638 }
639
640 .desktop-mode-dock__tooltip--after.desktop-mode-dock__tooltip--visible {
641 transform: translateX( 0 );
642 }
643
644 .desktop-mode-dock__tooltip--before {
645 /* Right-placed dock — tooltip sits to the left of the tile. JS
646 * writes the left coord to the tile's left edge; this translate
647 * pulls the tooltip further left by its own width. */
648 transform: translate( calc( -100% - 8px ), 0 );
649 }
650
651 .desktop-mode-dock__tooltip--before.desktop-mode-dock__tooltip--visible {
652 transform: translate( calc( -100% - 8px ), 0 );
653 opacity: 1;
654 }
655
656 /* ------------------------------------------------------------------
657 * Bottom placement — floating macOS-style pill centered above the
658 * viewport floor. Absolutely positioned so it floats as pure chrome
659 * rather than stealing vertical space from the shell body.
660 * ------------------------------------------------------------------ */
661
662 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ] {
663 position: absolute;
664 inset-inline-start: 0;
665 inset-inline-end: 0;
666 bottom: 12px;
667 margin: 0 auto;
668 width: fit-content;
669 max-width: calc( 100% - 32px );
670 flex-direction: row;
671 justify-content: center;
672 /* Top padding is 4px instead of 8px so that the inner `__scroll`
673 * wrapper can claim its own 4px padding-top for badge clearance
674 * (the tile's badge sits at top: -3px). Net vertical chrome stays
675 * symmetric: 4px pill padding + 4px wrapper padding above the
676 * tile, 8px pill padding below — tile is 8px from both edges. */
677 padding: 4px 12px 8px;
678 gap: 6px;
679 border-radius: 18px;
680
681 /* Warmer, more saturated glass tint than vertical placements so
682 * the floating pill reads as a distinct piece of chrome rather
683 * than a section of the sidebar. */
684 background: rgba( 22, 22, 26, 0.42 );
685 backdrop-filter: blur( 28px ) saturate( 170% );
686 -webkit-backdrop-filter: blur( 28px ) saturate( 170% );
687
688 border: 1px solid rgba( 255, 255, 255, 0.12 );
689 border-block-start: 1px solid rgba( 255, 255, 255, 0.18 );
690 box-shadow:
691 inset 0 1px 0 rgba( 255, 255, 255, 0.08 ),
692 0 10px 32px rgba( 0, 0, 0, 0.45 );
693 }
694
695 /* Active-indicator dot — below the tile, macOS-style. */
696 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
697 .desktop-mode-dock__item--active::before {
698 content: "";
699 position: absolute;
700 inset-inline-start: 50%;
701 top: auto;
702 bottom: -3px;
703 transform: translateX( -50% );
704 width: 4px;
705 height: 4px;
706 border-radius: 50%;
707 background: #fff;
708 }
709
710 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
711 .desktop-mode-dock__item--focused::before {
712 width: 16px;
713 height: 4px;
714 border-radius: 2px;
715 }
716
717 /*
718 * "All instances minimized" on the bottom dock — solid dot becomes
719 * a hollow ring. Matches the vertical-dock treatment so the cue is
720 * consistent across placements.
721 */
722 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
723 .desktop-mode-dock__item--all-minimized::before {
724 width: 6px;
725 height: 6px;
726 border-radius: 50%;
727 background: transparent;
728 border: 1px solid rgba( 255, 255, 255, 0.85 );
729 }
730
731 /*
732 * Global "Show Desktop" indicator — set on `<body>` by the dock when
733 * every live window on the active desktop is in the minimized state.
734 * The bottom-dock pill picks up a soft theme-color ring so the user
735 * has a global cue that the windows haven't disappeared, they're just
736 * minimized. ArrowUp / a wallpaper click / clicking any dock tile
737 * brings them back; without this the empty wallpaper looks identical
738 * to a freshly-loaded desktop with no windows open.
739 */
740 body.desktop-mode-show-desktop-active
741 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ] {
742 box-shadow:
743 inset 0 0 0 1px var( --wp-admin-theme-color, rgba( 255, 255, 255, 0.45 ) ),
744 inset 0 1px 0 rgba( 255, 255, 255, 0.08 ),
745 0 10px 32px rgba( 0, 0, 0, 0.45 );
746 }
747
748 /* Vertical docks get the same treatment along their inner edge. */
749 body.desktop-mode-show-desktop-active
750 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ],
751 body.desktop-mode-show-desktop-active
752 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ] {
753 box-shadow: inset 0 0 0 1px var( --wp-admin-theme-color, rgba( 255, 255, 255, 0.45 ) );
754 }
755
756 /* "Open another" chip — floats above the tile's top-right corner so
757 * it doesn't collide with neighbouring tiles in the horizontal row. */
758 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
759 .desktop-mode-dock__item-new {
760 top: -6px;
761 inset-inline-end: -6px;
762 transform: none;
763 }
764
765 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
766 .desktop-mode-dock__item-new:hover {
767 transform: scale( 1.1 );
768 }
769
770 /*
771 * Bottom-dock inner wrappers — `__scroll` carries the menu tiles and
772 * absorbs horizontal overflow; `__pinned` carries the system tiles and
773 * stays anchored to the trailing edge. Padding-top on `__scroll` gives
774 * the badge (top: -3px on the tile) room so `overflow-y: hidden`
775 * doesn't crop it. `min-width: 0` lets the wrapper shrink below its
776 * content's natural width, so the dock pill's `max-width` is what
777 * decides when scroll kicks in instead of the content forcing the
778 * pill wider. `justify-content: center` keeps tiles balanced inside
779 * the pill when content fits; when it overflows, scroll engages and
780 * items extend past the visible area.
781 */
782 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ] .desktop-mode-dock__scroll {
783 display: flex;
784 flex-direction: row;
785 align-items: center;
786 justify-content: center;
787 gap: 6px;
788 flex: 1 1 auto;
789 min-width: 0;
790 padding-top: 4px;
791 padding-bottom: 6px;
792 overflow-x: auto;
793 overflow-y: visible;
794 scrollbar-width: none;
795 }
796
797 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ] .desktop-mode-dock__scroll::-webkit-scrollbar {
798 display: none;
799 }
800
801 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ] .desktop-mode-dock__pinned {
802 display: flex;
803 flex-direction: row;
804 align-items: center;
805 gap: 6px;
806 flex-shrink: 0;
807 /* Match the scroll wrapper's badge clearance so system tiles sit
808 * on the same vertical baseline as the menu tiles. */
809 padding-top: 4px;
810 padding-bottom: 6px;
811 }
812
813 /* Separator — vertical hairline between menu tiles and system tiles.
814 * Lives at the leading edge of `__pinned`; flex flow handles the
815 * "pinned at the trailing edge" placement, no margin-inline-start:
816 * auto needed. The `--group` variant keeps the hairline styling for
817 * the inline core→plugin boundary inside `__scroll` and sits where
818 * inserted. */
819 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
820 .desktop-mode-dock__separator {
821 width: 1px;
822 /* Fixed height — `60%` collapses against the floating pill's
823 intrinsic height, leaving the hairline effectively invisible.
824 28px matches the dock tile inner glyph area. */
825 height: 28px;
826 /* Symmetric horizontal margin so the divider sits centered in the
827 * inter-tile gap: the 6px flex gap on each side (`__scroll`→`__pinned`
828 * on the left, `__pinned`'s own gap on the right) plus an equal 6px
829 * margin per side balances out. An asymmetric margin here nudges the
830 * divider — and the whole menu cluster with it — off the pill's
831 * center. */
832 margin: auto 6px;
833 background: var( --desktop-mode-dock-border );
834 flex-shrink: 0;
835 }
836
837 /*
838 * Group separator — sits inline between the core cluster and the
839 * plugin cluster. Brighter than the menu-vs-system separator because
840 * it carries user meaning ("this is where your installed apps
841 * begin"), and flanked by extra gap so the boundary reads at a
842 * glance rather than disappearing into the dock tint.
843 */
844 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
845 .desktop-mode-dock__separator--group {
846 width: 1px;
847 height: 60%;
848 margin: auto 10px;
849 margin-inline-start: 10px;
850 background: rgba( 255, 255, 255, 0.22 );
851 flex-shrink: 0;
852 }
853
854 /* Tooltip — above the hovered tile. JS writes the horizontal center
855 * + a top near the tile's top edge; CSS translates the tooltip up by
856 * its own height so it clears the tile. */
857 .desktop-mode-dock__tooltip--above {
858 inset-inline-start: auto;
859 transform: translate( -50%, calc( -100% - 4px ) );
860 }
861
862 .desktop-mode-dock__tooltip--above.desktop-mode-dock__tooltip--visible {
863 transform: translate( -50%, -100% );
864 }
865
866
867
868 /* Drag-to-reorder (since 0.25.0).
869 *
870 * The dragged tile follows the cursor via inline `transform:
871 * translate()` written from JS. It stays IN flow so flex doesn't
872 * collapse the row — the visual lift is a z-index bump and
873 * `pointer-events: none` so `elementFromPoint` finds siblings
874 * underneath the cursor. Sibling tiles animate via FLIP — the JS
875 * writes inline `transform` + `transition` per leg, so no CSS
876 * transition is declared here (it would race with the per-leg
877 * inline transition). */
878 .desktop-mode-dock__item--dragging {
879 pointer-events: none;
880 z-index: 50;
881 opacity: 0.92;
882 cursor: grabbing;
883 will-change: transform;
884 /* Subtle shadow so the dragged tile reads as "lifted" against
885 * the dock surface it floats above. */
886 filter: drop-shadow( 0 6px 12px rgba( 0, 0, 0, 0.35 ) );
887 }
888
889 .desktop-mode-dock__item:not( .desktop-mode-dock__item--system ) {
890 cursor: grab;
891 }
892
893 .desktop-mode-dock__item:not( .desktop-mode-dock__item--system ):active {
894 cursor: grabbing;
895 }
896