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

849 lines 27.4 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 /* Tooltip — anchor modifiers are applied directly to the tooltip
585 * element (NOT via a descendant selector on `.desktop-mode-dock`)
586 * because the tooltip lives in `document.body`. JS writes the
587 * absolute `top` / `left` coords; CSS handles the slide-in
588 * animation direction per modifier.
589 *
590 * - `--after` : left-placed dock (tooltip to the RIGHT of tile)
591 * - `--before` : right-placed dock (tooltip to the LEFT of tile)
592 * - `--above` : bottom-placed dock (tooltip ABOVE the tile)
593 */
594
595 /* Left dock — tooltip slides in from the left by 4px. JS sets
596 * `left` to `tile.right + 8px`; the transform animates the entry. */
597 .desktop-mode-dock__tooltip--after {
598 transform: translateX( -4px );
599 }
600
601 .desktop-mode-dock__tooltip--after.desktop-mode-dock__tooltip--visible {
602 transform: translateX( 0 );
603 }
604
605 .desktop-mode-dock__tooltip--before {
606 /* Right-placed dock — tooltip sits to the left of the tile. JS
607 * writes the left coord to the tile's left edge; this translate
608 * pulls the tooltip further left by its own width. */
609 transform: translate( calc( -100% - 8px ), 0 );
610 }
611
612 .desktop-mode-dock__tooltip--before.desktop-mode-dock__tooltip--visible {
613 transform: translate( calc( -100% - 8px ), 0 );
614 opacity: 1;
615 }
616
617 /* ------------------------------------------------------------------
618 * Bottom placement — floating macOS-style pill centered above the
619 * viewport floor. Absolutely positioned so it floats as pure chrome
620 * rather than stealing vertical space from the shell body.
621 * ------------------------------------------------------------------ */
622
623 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ] {
624 position: absolute;
625 inset-inline-start: 0;
626 inset-inline-end: 0;
627 bottom: 12px;
628 margin: 0 auto;
629 width: fit-content;
630 max-width: calc( 100% - 32px );
631 flex-direction: row;
632 justify-content: center;
633 /* Top padding is 4px instead of 8px so that the inner `__scroll`
634 * wrapper can claim its own 4px padding-top for badge clearance
635 * (the tile's badge sits at top: -3px). Net vertical chrome stays
636 * symmetric: 4px pill padding + 4px wrapper padding above the
637 * tile, 8px pill padding below — tile is 8px from both edges. */
638 padding: 4px 12px 8px;
639 gap: 6px;
640 border-radius: 18px;
641
642 /* Warmer, more saturated glass tint than vertical placements so
643 * the floating pill reads as a distinct piece of chrome rather
644 * than a section of the sidebar. */
645 background: rgba( 22, 22, 26, 0.42 );
646 backdrop-filter: blur( 28px ) saturate( 170% );
647 -webkit-backdrop-filter: blur( 28px ) saturate( 170% );
648
649 border: 1px solid rgba( 255, 255, 255, 0.12 );
650 border-block-start: 1px solid rgba( 255, 255, 255, 0.18 );
651 box-shadow:
652 inset 0 1px 0 rgba( 255, 255, 255, 0.08 ),
653 0 10px 32px rgba( 0, 0, 0, 0.45 );
654 }
655
656 /* Active-indicator dot — below the tile, macOS-style. */
657 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
658 .desktop-mode-dock__item--active::before {
659 content: "";
660 position: absolute;
661 inset-inline-start: 50%;
662 top: auto;
663 bottom: -4px;
664 transform: translateX( -50% );
665 width: 4px;
666 height: 4px;
667 border-radius: 50%;
668 background: #fff;
669 }
670
671 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
672 .desktop-mode-dock__item--focused::before {
673 width: 16px;
674 height: 4px;
675 border-radius: 2px;
676 }
677
678 /*
679 * "All instances minimized" on the bottom dock — solid dot becomes
680 * a hollow ring. Matches the vertical-dock treatment so the cue is
681 * consistent across placements.
682 */
683 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
684 .desktop-mode-dock__item--all-minimized::before {
685 width: 6px;
686 height: 6px;
687 border-radius: 50%;
688 background: transparent;
689 border: 1px solid rgba( 255, 255, 255, 0.85 );
690 }
691
692 /*
693 * Global "Show Desktop" indicator — set on `<body>` by the dock when
694 * every live window on the active desktop is in the minimized state.
695 * The bottom-dock pill picks up a soft theme-color ring so the user
696 * has a global cue that the windows haven't disappeared, they're just
697 * minimized. ArrowUp / a wallpaper click / clicking any dock tile
698 * brings them back; without this the empty wallpaper looks identical
699 * to a freshly-loaded desktop with no windows open.
700 */
701 body.desktop-mode-show-desktop-active
702 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ] {
703 box-shadow:
704 inset 0 0 0 1px var( --wp-admin-theme-color, rgba( 255, 255, 255, 0.45 ) ),
705 inset 0 1px 0 rgba( 255, 255, 255, 0.08 ),
706 0 10px 32px rgba( 0, 0, 0, 0.45 );
707 }
708
709 /* Vertical docks get the same treatment along their inner edge. */
710 body.desktop-mode-show-desktop-active
711 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ],
712 body.desktop-mode-show-desktop-active
713 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ] {
714 box-shadow: inset 0 0 0 1px var( --wp-admin-theme-color, rgba( 255, 255, 255, 0.45 ) );
715 }
716
717 /* "Open another" chip — floats above the tile's top-right corner so
718 * it doesn't collide with neighbouring tiles in the horizontal row. */
719 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
720 .desktop-mode-dock__item-new {
721 top: -6px;
722 inset-inline-end: -6px;
723 transform: none;
724 }
725
726 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
727 .desktop-mode-dock__item-new:hover {
728 transform: scale( 1.1 );
729 }
730
731 /*
732 * Bottom-dock inner wrappers — `__scroll` carries the menu tiles and
733 * absorbs horizontal overflow; `__pinned` carries the system tiles and
734 * stays anchored to the trailing edge. Padding-top on `__scroll` gives
735 * the badge (top: -3px on the tile) room so `overflow-y: hidden`
736 * doesn't crop it. `min-width: 0` lets the wrapper shrink below its
737 * content's natural width, so the dock pill's `max-width` is what
738 * decides when scroll kicks in instead of the content forcing the
739 * pill wider. `justify-content: center` keeps tiles balanced inside
740 * the pill when content fits; when it overflows, scroll engages and
741 * items extend past the visible area.
742 */
743 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ] .desktop-mode-dock__scroll {
744 display: flex;
745 flex-direction: row;
746 align-items: center;
747 justify-content: center;
748 gap: 6px;
749 flex: 1 1 auto;
750 min-width: 0;
751 padding-top: 4px;
752 overflow-x: auto;
753 overflow-y: hidden;
754 scrollbar-width: none;
755 }
756
757 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ] .desktop-mode-dock__scroll::-webkit-scrollbar {
758 display: none;
759 }
760
761 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ] .desktop-mode-dock__pinned {
762 display: flex;
763 flex-direction: row;
764 align-items: center;
765 gap: 6px;
766 flex-shrink: 0;
767 /* Match the scroll wrapper's badge clearance so system tiles sit
768 * on the same vertical baseline as the menu tiles. */
769 padding-top: 4px;
770 }
771
772 /* Separator — vertical hairline between menu tiles and system tiles.
773 * Lives at the leading edge of `__pinned`; flex flow handles the
774 * "pinned at the trailing edge" placement, no margin-inline-start:
775 * auto needed. The `--group` variant keeps the hairline styling for
776 * the inline core→plugin boundary inside `__scroll` and sits where
777 * inserted. */
778 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
779 .desktop-mode-dock__separator {
780 width: 1px;
781 /* Fixed height — `60%` collapses against the floating pill's
782 intrinsic height, leaving the hairline effectively invisible.
783 28px matches the dock tile inner glyph area. */
784 height: 28px;
785 margin: auto 4px auto 8px;
786 background: var( --desktop-mode-dock-border );
787 flex-shrink: 0;
788 }
789
790 /*
791 * Group separator — sits inline between the core cluster and the
792 * plugin cluster. Brighter than the menu-vs-system separator because
793 * it carries user meaning ("this is where your installed apps
794 * begin"), and flanked by extra gap so the boundary reads at a
795 * glance rather than disappearing into the dock tint.
796 */
797 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
798 .desktop-mode-dock__separator--group {
799 width: 1px;
800 height: 60%;
801 margin: auto 10px;
802 margin-inline-start: 10px;
803 background: rgba( 255, 255, 255, 0.22 );
804 flex-shrink: 0;
805 }
806
807 /* Tooltip — above the hovered tile. JS writes the horizontal center
808 * + a top near the tile's top edge; CSS translates the tooltip up by
809 * its own height so it clears the tile. */
810 .desktop-mode-dock__tooltip--above {
811 inset-inline-start: auto;
812 transform: translate( -50%, calc( -100% - 4px ) );
813 }
814
815 .desktop-mode-dock__tooltip--above.desktop-mode-dock__tooltip--visible {
816 transform: translate( -50%, -100% );
817 }
818
819
820
821 /* Drag-to-reorder (since 0.25.0).
822 *
823 * The dragged tile follows the cursor via inline `transform:
824 * translate()` written from JS. It stays IN flow so flex doesn't
825 * collapse the row — the visual lift is a z-index bump and
826 * `pointer-events: none` so `elementFromPoint` finds siblings
827 * underneath the cursor. Sibling tiles animate via FLIP — the JS
828 * writes inline `transform` + `transition` per leg, so no CSS
829 * transition is declared here (it would race with the per-leg
830 * inline transition). */
831 .desktop-mode-dock__item--dragging {
832 pointer-events: none;
833 z-index: 50;
834 opacity: 0.92;
835 cursor: grabbing;
836 will-change: transform;
837 /* Subtle shadow so the dragged tile reads as "lifted" against
838 * the dock surface it floats above. */
839 filter: drop-shadow( 0 6px 12px rgba( 0, 0, 0, 0.35 ) );
840 }
841
842 .desktop-mode-dock__item:not( .desktop-mode-dock__item--system ) {
843 cursor: grab;
844 }
845
846 .desktop-mode-dock__item:not( .desktop-mode-dock__item--system ):active {
847 cursor: grabbing;
848 }
849