PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 0.9.5
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v0.9.5
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.5, at assets/css/dock.css

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