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

755 lines 24.1 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 /* System tiles — same footprint as menu tiles, no badges, no rails. */
213 .desktop-mode-dock__item--system .desktop-mode-dock__item-primary {
214 color: rgba( 255, 255, 255, 0.8 );
215 }
216
217 /* -----------------------------------------------------------------------------
218 * Attention animations — driven by `Dock.setAttention()` /
219 * `Window.requestAttention()`. The class lands on the tile root,
220 * the animation runs on the primary icon button so the badge stays
221 * stable. Three modes (pulse / shake / bounce) and three intensities
222 * (subtle / normal / strong). All animations gated on
223 * `prefers-reduced-motion: no-preference`; the reduced-motion fallback
224 * shows a static accent ring for the same duration so the affordance
225 * still works.
226 * ------------------------------------------------------------------------- */
227
228 @keyframes wpd-dock-attention-pulse {
229 0%, 100% {
230 transform: scale( 1 );
231 box-shadow: 0 0 0 0 rgba( 255, 90, 90, 0.55 );
232 }
233 50% {
234 transform: scale( 1.06 );
235 box-shadow: 0 0 0 8px rgba( 255, 90, 90, 0 );
236 }
237 }
238
239 @keyframes wpd-dock-attention-shake {
240 0%, 100% {
241 transform: translateX( 0 );
242 }
243 20%, 60% {
244 transform: translateX( -3px );
245 }
246 40%, 80% {
247 transform: translateX( 3px );
248 }
249 }
250
251 @keyframes wpd-dock-attention-bounce {
252 0%, 100% {
253 transform: translateY( 0 );
254 }
255 30% {
256 transform: translateY( -6px );
257 }
258 60% {
259 transform: translateY( -2px );
260 }
261 }
262
263 .desktop-mode-dock__item--attention-pulse .desktop-mode-dock__item-primary {
264 animation: wpd-dock-attention-pulse 1.4s ease-in-out infinite;
265 }
266 .desktop-mode-dock__item--attention-shake .desktop-mode-dock__item-primary {
267 animation: wpd-dock-attention-shake 0.8s ease-in-out infinite;
268 }
269 .desktop-mode-dock__item--attention-bounce .desktop-mode-dock__item-primary {
270 animation: wpd-dock-attention-bounce 1.2s ease-in-out infinite;
271 }
272
273 /* Intensity modulates animation duration — strong = faster, more
274 urgent; subtle = slower, calmer. */
275 .desktop-mode-dock__item--intensity-strong.desktop-mode-dock__item--attention-pulse .desktop-mode-dock__item-primary {
276 animation-duration: 1s;
277 }
278 .desktop-mode-dock__item--intensity-strong.desktop-mode-dock__item--attention-shake .desktop-mode-dock__item-primary {
279 animation-duration: 0.6s;
280 }
281 .desktop-mode-dock__item--intensity-strong.desktop-mode-dock__item--attention-bounce .desktop-mode-dock__item-primary {
282 animation-duration: 0.9s;
283 }
284 .desktop-mode-dock__item--intensity-subtle.desktop-mode-dock__item--attention-pulse .desktop-mode-dock__item-primary {
285 animation-duration: 1.8s;
286 }
287 .desktop-mode-dock__item--intensity-subtle.desktop-mode-dock__item--attention-shake .desktop-mode-dock__item-primary {
288 animation-duration: 1s;
289 }
290 .desktop-mode-dock__item--intensity-subtle.desktop-mode-dock__item--attention-bounce .desktop-mode-dock__item-primary {
291 animation-duration: 1.6s;
292 }
293
294 /* Reduced-motion: kill the animation, replace with a static accent ring
295 for the same visual prominence without the movement. */
296 @media ( prefers-reduced-motion: reduce ) {
297 .desktop-mode-dock__item--attention-pulse .desktop-mode-dock__item-primary,
298 .desktop-mode-dock__item--attention-shake .desktop-mode-dock__item-primary,
299 .desktop-mode-dock__item--attention-bounce .desktop-mode-dock__item-primary {
300 animation: none;
301 outline: 2px solid var( --wp-admin-theme-color, #2271b1 );
302 outline-offset: 2px;
303 border-radius: 8px;
304 }
305 }
306
307 /* Shared tooltip — placement-specific anchor modifiers applied by JS. */
308 .desktop-mode-dock__tooltip {
309 position: fixed;
310 padding: 6px 12px;
311 background: rgba( 0, 0, 0, 0.85 );
312 color: #fff;
313 font-size: 12px;
314 font-weight: 400;
315 line-height: 1.4;
316 white-space: nowrap;
317 border-radius: 6px;
318 pointer-events: none;
319 z-index: calc( var( --desktop-mode-z-dock ) + 1 );
320 opacity: 0;
321 transition: opacity 0.15s ease, transform 0.15s ease;
322 }
323
324 .desktop-mode-dock__tooltip--visible {
325 opacity: 1;
326 }
327
328 /* ------------------------------------------------------------------
329 * Vertical placement (left + right) — dock runs the full shell body
330 * height along one edge, with items stacked vertically.
331 * ------------------------------------------------------------------ */
332
333 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ],
334 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ] {
335 width: var( --desktop-mode-dock-width );
336 flex-direction: column;
337 padding: 16px 0 12px;
338 gap: 6px;
339 /*
340 * Vertical docks split into two inner wrappers:
341 * - `__scroll` — flex: 1, scrollable, hosts menu tiles + the
342 * inline core/plugin separator. When the menu
343 * exceeds the dock's height, this is what
344 * scrolls; the outer dock stays a fixed pillar.
345 * - `__pinned` — sized to content, hosts system tiles
346 * (Recycle Bin, OS Settings, …) + their hairline
347 * separator. Always visible at the bottom edge
348 * regardless of scroll position.
349 *
350 * `min-height: 0` on the dock is defensive: even though `__scroll`
351 * absorbs the overflow, the dock is a flex item inside the shell
352 * body (a flex row), and the default `min-height: auto` would let
353 * a tall `__pinned` push the dock past the parent's height.
354 */
355 min-height: 0;
356 }
357
358 /*
359 * Scrollable menu-tile area inside vertical docks. `flex: 1 1 0` plus
360 * `min-height: 0` lets it shrink below its content's natural height so
361 * `overflow-y: auto` actually engages. `overflow-x: clip` keeps the
362 * active-dot / focused-pill indicators visible (they sit at x≈2px
363 * inside the wrapper's content box) while preventing `overflow-y: auto`
364 * from coercing horizontal scroll. Scrollbar is hidden visually —
365 * Firefox via `scrollbar-width: none`, WebKit via the pseudo-element
366 * rule below — so the rail keeps its clean macOS look. Mouse wheel,
367 * touchpad, touch, and keyboard focus-into-view all still work.
368 */
369 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ] .desktop-mode-dock__scroll,
370 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ] .desktop-mode-dock__scroll {
371 display: flex;
372 flex-direction: column;
373 align-items: center;
374 gap: 6px;
375 flex: 1 1 0;
376 min-height: 0;
377 width: 100%;
378 overflow-y: auto;
379 overflow-x: clip;
380 scrollbar-width: none;
381 }
382
383 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ] .desktop-mode-dock__scroll::-webkit-scrollbar,
384 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ] .desktop-mode-dock__scroll::-webkit-scrollbar {
385 display: none;
386 }
387
388 /*
389 * Pinned area below `__scroll` — system tiles stay reachable here
390 * regardless of how the menu list scrolls. `flex-shrink: 0` so it's
391 * never compressed; sized to its content's natural height.
392 */
393 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ] .desktop-mode-dock__pinned,
394 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ] .desktop-mode-dock__pinned {
395 display: flex;
396 flex-direction: column;
397 align-items: center;
398 gap: 6px;
399 flex-shrink: 0;
400 width: 100%;
401 }
402
403 /* Left: dock is the first flex item in __body (row layout). */
404 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ] {
405 border-inline-end: 1px solid var( --desktop-mode-dock-border );
406 order: -1;
407 }
408
409 /* Right: push the dock to the end of __body and flip the border edge. */
410 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ] {
411 border-inline-start: 1px solid var( --desktop-mode-dock-border );
412 order: 1;
413 }
414
415 /* Active-indicator dot — left dock: inside-edge vertical pill. */
416 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ]
417 .desktop-mode-dock__item--active::before {
418 content: "";
419 position: absolute;
420 inset-inline-start: -6px;
421 top: 50%;
422 transform: translateY( -50% );
423 width: 4px;
424 height: 4px;
425 border-radius: 50%;
426 background: #fff;
427 }
428
429 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ]
430 .desktop-mode-dock__item--focused::before {
431 width: 4px;
432 height: 16px;
433 border-radius: 2px;
434 }
435
436 /* Right dock: indicator on the opposite edge (inside-facing). */
437 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ]
438 .desktop-mode-dock__item--active::before {
439 content: "";
440 position: absolute;
441 inset-inline-end: -6px;
442 top: 50%;
443 transform: translateY( -50% );
444 width: 4px;
445 height: 4px;
446 border-radius: 50%;
447 background: #fff;
448 }
449
450 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ]
451 .desktop-mode-dock__item--focused::before {
452 width: 4px;
453 height: 16px;
454 border-radius: 2px;
455 }
456
457 /* "Open another" chip — left: right of tile; right: left of tile. */
458 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ]
459 .desktop-mode-dock__item-new {
460 top: 50%;
461 inset-inline-end: -13px;
462 transform: translateY( -50% );
463 }
464
465 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ]
466 .desktop-mode-dock__item-new:hover {
467 transform: translateY( -50% ) scale( 1.1 );
468 }
469
470 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ]
471 .desktop-mode-dock__item-new {
472 top: 50%;
473 inset-inline-start: -13px;
474 transform: translateY( -50% );
475 }
476
477 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ]
478 .desktop-mode-dock__item-new:hover {
479 transform: translateY( -50% ) scale( 1.1 );
480 }
481
482 /* Separator — horizontal hairline between menu tiles and system
483 * tiles. With the `__scroll` / `__pinned` split the system separator
484 * lives at the top of `__pinned`; flex-flow handles the "system
485 * tiles at the bottom" placement, no margin-top: auto needed. The
486 * `--group` variant is used inline between core and plugin menu
487 * tiles in `__scroll`; it keeps the hairline styling and sits
488 * exactly where it's inserted. */
489 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ]
490 .desktop-mode-dock__separator,
491 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ]
492 .desktop-mode-dock__separator {
493 width: 60%;
494 height: 1px;
495 margin: 8px auto 4px;
496 background: var( --desktop-mode-dock-border );
497 flex-shrink: 0;
498 }
499
500 .desktop-mode-dock[ data-desktop-mode-dock-placement="left" ]
501 .desktop-mode-dock__separator--group,
502 .desktop-mode-dock[ data-desktop-mode-dock-placement="right" ]
503 .desktop-mode-dock__separator--group {
504 margin: 10px auto;
505 background: rgba( 255, 255, 255, 0.22 );
506 }
507
508 /*
509 * Hide the system separator when nothing precedes it — on a clean
510 * install (or a low-cap user with zero menu items) the separator
511 * would render as a stray hairline with nothing to divide.
512 *
513 * Two cases:
514 * 1. Bottom dock — separator is a direct child of the dock; hide
515 * when it's the first such child.
516 * 2. Vertical docks — separator lives inside `__pinned`; hide when
517 * `__scroll` (its sibling above) is empty of menu tiles.
518 */
519 .desktop-mode-dock > .desktop-mode-dock__separator:first-child {
520 display: none;
521 }
522
523 .desktop-mode-dock__scroll:not(:has( .desktop-mode-dock__item ))
524 + .desktop-mode-dock__pinned
525 .desktop-mode-dock__separator {
526 display: none;
527 }
528
529 /* Tooltip — anchor modifiers are applied directly to the tooltip
530 * element (NOT via a descendant selector on `.desktop-mode-dock`)
531 * because the tooltip lives in `document.body`. JS writes the
532 * absolute `top` / `left` coords; CSS handles the slide-in
533 * animation direction per modifier.
534 *
535 * - `--after` : left-placed dock (tooltip to the RIGHT of tile)
536 * - `--before` : right-placed dock (tooltip to the LEFT of tile)
537 * - `--above` : bottom-placed dock (tooltip ABOVE the tile)
538 */
539
540 /* Left dock — tooltip slides in from the left by 4px. JS sets
541 * `left` to `tile.right + 8px`; the transform animates the entry. */
542 .desktop-mode-dock__tooltip--after {
543 transform: translateX( -4px );
544 }
545
546 .desktop-mode-dock__tooltip--after.desktop-mode-dock__tooltip--visible {
547 transform: translateX( 0 );
548 }
549
550 .desktop-mode-dock__tooltip--before {
551 /* Right-placed dock — tooltip sits to the left of the tile. JS
552 * writes the left coord to the tile's left edge; this translate
553 * pulls the tooltip further left by its own width. */
554 transform: translate( calc( -100% - 8px ), 0 );
555 }
556
557 .desktop-mode-dock__tooltip--before.desktop-mode-dock__tooltip--visible {
558 transform: translate( calc( -100% - 8px ), 0 );
559 opacity: 1;
560 }
561
562 /* ------------------------------------------------------------------
563 * Bottom placement — floating macOS-style pill centered above the
564 * viewport floor. Absolutely positioned so it floats as pure chrome
565 * rather than stealing vertical space from the shell body.
566 * ------------------------------------------------------------------ */
567
568 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ] {
569 position: absolute;
570 inset-inline-start: 0;
571 inset-inline-end: 0;
572 bottom: 12px;
573 margin: 0 auto;
574 width: fit-content;
575 max-width: calc( 100% - 32px );
576 flex-direction: row;
577 justify-content: center;
578 /* Top padding is 4px instead of 8px so that the inner `__scroll`
579 * wrapper can claim its own 4px padding-top for badge clearance
580 * (the tile's badge sits at top: -3px). Net vertical chrome stays
581 * symmetric: 4px pill padding + 4px wrapper padding above the
582 * tile, 8px pill padding below — tile is 8px from both edges. */
583 padding: 4px 12px 8px;
584 gap: 6px;
585 border-radius: 18px;
586
587 /* Warmer, more saturated glass tint than vertical placements so
588 * the floating pill reads as a distinct piece of chrome rather
589 * than a section of the sidebar. */
590 background: rgba( 22, 22, 26, 0.42 );
591 backdrop-filter: blur( 28px ) saturate( 170% );
592 -webkit-backdrop-filter: blur( 28px ) saturate( 170% );
593
594 border: 1px solid rgba( 255, 255, 255, 0.12 );
595 border-block-start: 1px solid rgba( 255, 255, 255, 0.18 );
596 box-shadow:
597 inset 0 1px 0 rgba( 255, 255, 255, 0.08 ),
598 0 10px 32px rgba( 0, 0, 0, 0.45 );
599 }
600
601 /* Active-indicator dot — below the tile, macOS-style. */
602 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
603 .desktop-mode-dock__item--active::before {
604 content: "";
605 position: absolute;
606 inset-inline-start: 50%;
607 top: auto;
608 bottom: -4px;
609 transform: translateX( -50% );
610 width: 4px;
611 height: 4px;
612 border-radius: 50%;
613 background: #fff;
614 }
615
616 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
617 .desktop-mode-dock__item--focused::before {
618 width: 16px;
619 height: 4px;
620 border-radius: 2px;
621 }
622
623 /* "Open another" chip — floats above the tile's top-right corner so
624 * it doesn't collide with neighbouring tiles in the horizontal row. */
625 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
626 .desktop-mode-dock__item-new {
627 top: -6px;
628 inset-inline-end: -6px;
629 transform: none;
630 }
631
632 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
633 .desktop-mode-dock__item-new:hover {
634 transform: scale( 1.1 );
635 }
636
637 /*
638 * Bottom-dock inner wrappers — `__scroll` carries the menu tiles and
639 * absorbs horizontal overflow; `__pinned` carries the system tiles and
640 * stays anchored to the trailing edge. Padding-top on `__scroll` gives
641 * the badge (top: -3px on the tile) room so `overflow-y: hidden`
642 * doesn't crop it. `min-width: 0` lets the wrapper shrink below its
643 * content's natural width, so the dock pill's `max-width` is what
644 * decides when scroll kicks in instead of the content forcing the
645 * pill wider. `justify-content: center` keeps tiles balanced inside
646 * the pill when content fits; when it overflows, scroll engages and
647 * items extend past the visible area.
648 */
649 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ] .desktop-mode-dock__scroll {
650 display: flex;
651 flex-direction: row;
652 align-items: center;
653 justify-content: center;
654 gap: 6px;
655 flex: 1 1 auto;
656 min-width: 0;
657 padding-top: 4px;
658 overflow-x: auto;
659 overflow-y: hidden;
660 scrollbar-width: none;
661 }
662
663 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ] .desktop-mode-dock__scroll::-webkit-scrollbar {
664 display: none;
665 }
666
667 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ] .desktop-mode-dock__pinned {
668 display: flex;
669 flex-direction: row;
670 align-items: center;
671 gap: 6px;
672 flex-shrink: 0;
673 /* Match the scroll wrapper's badge clearance so system tiles sit
674 * on the same vertical baseline as the menu tiles. */
675 padding-top: 4px;
676 }
677
678 /* Separator — vertical hairline between menu tiles and system tiles.
679 * Lives at the leading edge of `__pinned`; flex flow handles the
680 * "pinned at the trailing edge" placement, no margin-inline-start:
681 * auto needed. The `--group` variant keeps the hairline styling for
682 * the inline core→plugin boundary inside `__scroll` and sits where
683 * inserted. */
684 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
685 .desktop-mode-dock__separator {
686 width: 1px;
687 /* Fixed height — `60%` collapses against the floating pill's
688 intrinsic height, leaving the hairline effectively invisible.
689 28px matches the dock tile inner glyph area. */
690 height: 28px;
691 margin: auto 4px auto 8px;
692 background: var( --desktop-mode-dock-border );
693 flex-shrink: 0;
694 }
695
696 /*
697 * Group separator — sits inline between the core cluster and the
698 * plugin cluster. Brighter than the menu-vs-system separator because
699 * it carries user meaning ("this is where your installed apps
700 * begin"), and flanked by extra gap so the boundary reads at a
701 * glance rather than disappearing into the dock tint.
702 */
703 .desktop-mode-dock[ data-desktop-mode-dock-placement="bottom" ]
704 .desktop-mode-dock__separator--group {
705 width: 1px;
706 height: 60%;
707 margin: auto 10px;
708 margin-inline-start: 10px;
709 background: rgba( 255, 255, 255, 0.22 );
710 flex-shrink: 0;
711 }
712
713 /* Tooltip — above the hovered tile. JS writes the horizontal center
714 * + a top near the tile's top edge; CSS translates the tooltip up by
715 * its own height so it clears the tile. */
716 .desktop-mode-dock__tooltip--above {
717 inset-inline-start: auto;
718 transform: translate( -50%, calc( -100% - 4px ) );
719 }
720
721 .desktop-mode-dock__tooltip--above.desktop-mode-dock__tooltip--visible {
722 transform: translate( -50%, -100% );
723 }
724
725
726
727 /* Drag-to-reorder (since 0.25.0).
728 *
729 * The dragged tile follows the cursor via inline `transform:
730 * translate()` written from JS. It stays IN flow so flex doesn't
731 * collapse the row — the visual lift is a z-index bump and
732 * `pointer-events: none` so `elementFromPoint` finds siblings
733 * underneath the cursor. Sibling tiles animate via FLIP — the JS
734 * writes inline `transform` + `transition` per leg, so no CSS
735 * transition is declared here (it would race with the per-leg
736 * inline transition). */
737 .desktop-mode-dock__item--dragging {
738 pointer-events: none;
739 z-index: 50;
740 opacity: 0.92;
741 cursor: grabbing;
742 will-change: transform;
743 /* Subtle shadow so the dragged tile reads as "lifted" against
744 * the dock surface it floats above. */
745 filter: drop-shadow( 0 6px 12px rgba( 0, 0, 0, 0.35 ) );
746 }
747
748 .desktop-mode-dock__item:not( .desktop-mode-dock__item--system ) {
749 cursor: grab;
750 }
751
752 .desktop-mode-dock__item:not( .desktop-mode-dock__item--system ):active {
753 cursor: grabbing;
754 }
755