PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 0.8.9
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v0.8.9
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-peek.css

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

620 lines 20.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Desktop Mode — Dock Peek.
3 *
4 * Hover-reveal popover that fans out from a multi-instance dock tile.
5 * Each card is styled as a miniature window — faux titlebar with
6 * traffic-light dots + the page icon + the live window title, tinted
7 * body with ghosted content lines. The trailing Ghost Card matches
8 * the geometry but is dashed + breathing to announce itself as the
9 * "open new" affordance.
10 *
11 * Click an instance card → `document.startViewTransition()` morphs
12 * it into the focused window. Click the Ghost Card → same API
13 * morphs it into the freshly-spawned window.
14 *
15 * Three motion layers:
16 * 1. Tile lift — the dock icon springs up + magnifies on hover.
17 * 2. Card fan-out — cards slide in staggered, with a subtle peel.
18 * 3. Ghost pulse — the trailing card breathes until hovered.
19 *
20 * @since 0.6.2
21 */
22
23 /* ──────────────────────────────────────────────────────────────────
24 Tile lift — applied to ANY dock tile on hover. Cheap and additive
25 to the existing dock styles; doesn't depend on the peek being
26 present (the icon should feel responsive even when there's no
27 peek to show).
28 ────────────────────────────────────────────────────────────────── */
29
30 .desktop-mode-dock__item-primary {
31 transition:
32 transform 240ms cubic-bezier( 0.34, 1.56, 0.64, 1 ),
33 filter 240ms ease-out;
34 }
35
36 .desktop-mode-dock__item:hover .desktop-mode-dock__item-primary {
37 transform: scale( 1.08 );
38 filter: drop-shadow( 0 4px 8px rgba( 0, 0, 0, 0.18 ) );
39 }
40
41 @media ( prefers-reduced-motion: reduce ) {
42 .desktop-mode-dock__item-primary,
43 .desktop-mode-dock__item:hover .desktop-mode-dock__item-primary {
44 transition: none;
45 transform: none;
46 }
47 }
48
49 /* ──────────────────────────────────────────────────────────────────
50 Popover surface — glass material, body-attached.
51 ────────────────────────────────────────────────────────────────── */
52
53 .desktop-mode-dock-peek {
54 position: fixed;
55 z-index: 999999;
56 padding: 10px;
57 border-radius: 16px;
58 background: color-mix( in srgb, var( --desktop-mode-bg, #1d2327 ) 76%, transparent );
59 backdrop-filter: blur( 28px ) saturate( 180% );
60 -webkit-backdrop-filter: blur( 28px ) saturate( 180% );
61 border: 1px solid rgba( 255, 255, 255, 0.12 );
62 box-shadow:
63 0 1px 0 rgba( 255, 255, 255, 0.08 ) inset,
64 0 16px 40px -10px rgba( 0, 0, 0, 0.55 ),
65 0 6px 16px -4px rgba( 0, 0, 0, 0.36 );
66 color: var( --desktop-mode-fg, #fff );
67 pointer-events: auto;
68 opacity: 0;
69 display: flex;
70 flex-direction: column;
71 transition: opacity 160ms ease-out;
72 /* Default (left/right docks) — vertical column. The popover caps
73 in height; cards scroll inside. */
74 max-height: min( 80vh, 480px );
75 max-width: min( 90vw, 320px );
76 }
77
78 /* Bottom-dock orientation: cards form a horizontal reel. The popover
79 caps in WIDTH instead of height, and the cards container scrolls
80 horizontally. Visually mirrors how taskbar-style docks work — a
81 row of mini-windows above the icon, not a stack. */
82 .desktop-mode-dock-peek[ data-orientation = "bottom" ] {
83 max-height: none;
84 max-width: min( 92vw, 920px );
85 }
86
87 .desktop-mode-dock-peek--open {
88 opacity: 1;
89 }
90
91 /* Anchor flips by orientation. The popover's anchor point (the
92 coordinate set in JS) sits ON the tile edge; we translate from
93 there so the popover floats off-edge with a gap. The
94 `--peek-clamp-x/y` props are added by JS when the popover would
95 otherwise overflow the viewport — they nudge it inward without
96 losing the orientation translate. */
97 .desktop-mode-dock-peek[ data-orientation = "left" ] {
98 transform:
99 translate(
100 var( --peek-clamp-x, 0 ),
101 calc( -50% + var( --peek-clamp-y, 0px ) )
102 );
103 }
104 .desktop-mode-dock-peek[ data-orientation = "right" ] {
105 transform:
106 translate(
107 calc( -100% + var( --peek-clamp-x, 0px ) ),
108 calc( -50% + var( --peek-clamp-y, 0px ) )
109 );
110 }
111 .desktop-mode-dock-peek[ data-orientation = "bottom" ] {
112 transform:
113 translate(
114 calc( -50% + var( --peek-clamp-x, 0px ) ),
115 calc( -100% + var( --peek-clamp-y, 0px ) )
116 );
117 }
118
119 /* ──────────────────────────────────────────────────────────────────
120 Cards.
121 ────────────────────────────────────────────────────────────────── */
122
123 .desktop-mode-dock-peek__cards {
124 display: flex;
125 flex-direction: column;
126 gap: 8px;
127 min-width: 280px;
128 /* Take up whatever vertical space the popover offers and scroll
129 internally once the cards spill beyond it. Subtract the
130 popover's padding so the scrollbar doesn't flush against the
131 rounded outer edge. */
132 flex: 1 1 auto;
133 min-height: 0;
134 overflow-y: auto;
135 overflow-x: hidden;
136 /* Hide the scrollbar visually until hover — the peek surface is
137 meant to feel weightless, not "yet another panel with a
138 scrollbar." Falls back to the platform scrollbar where the
139 custom-scrollbar pseudo isn't supported. */
140 scrollbar-width: thin;
141 scrollbar-color: rgba( 255, 255, 255, 0.18 ) transparent;
142 }
143
144 /* Bottom dock: cards lay out as a horizontal reel and scroll on the
145 X-axis instead of Y. */
146 .desktop-mode-dock-peek[ data-orientation = "bottom" ] .desktop-mode-dock-peek__cards {
147 flex-direction: row;
148 min-width: 0;
149 overflow-x: auto;
150 overflow-y: hidden;
151 max-width: 100%;
152 }
153
154 .desktop-mode-dock-peek[ data-orientation = "bottom" ] .desktop-mode-dock-peek__card {
155 /* Bottom-dock cards are narrower than left/right dock cards —
156 a horizontal reel reads better with thumbnail-shaped tiles
157 (Mission Control / Stage Manager) than with full-width
158 sidebar-shaped ones. flex: 0 0 auto so a long reel doesn't
159 squish each card down to nothing. */
160 flex: 0 0 auto;
161 width: 200px;
162 min-height: 116px;
163 /* Cards translate from BELOW into the reel (towards the dock
164 tile they peeled off of), so the directional motion still
165 points back at the icon. */
166 transform: translateY( 10px ) scale( 0.96 );
167 }
168
169 .desktop-mode-dock-peek[ data-orientation = "bottom" ] .desktop-mode-dock-peek__card--ghost {
170 width: 200px;
171 min-height: 116px;
172 }
173
174 .desktop-mode-dock-peek[ data-orientation = "bottom" ].desktop-mode-dock-peek--open
175 .desktop-mode-dock-peek__card {
176 transform: translateY( 0 ) scale( 1 );
177 }
178
179 .desktop-mode-dock-peek__cards::-webkit-scrollbar {
180 width: 8px;
181 height: 8px;
182 }
183 .desktop-mode-dock-peek__cards::-webkit-scrollbar-track {
184 background: transparent;
185 }
186 .desktop-mode-dock-peek__cards::-webkit-scrollbar-thumb {
187 background: rgba( 255, 255, 255, 0.18 );
188 border-radius: 8px;
189 border: 2px solid transparent;
190 background-clip: padding-box;
191 }
192 .desktop-mode-dock-peek__cards::-webkit-scrollbar-thumb:hover {
193 background: rgba( 255, 255, 255, 0.32 );
194 background-clip: padding-box;
195 }
196
197 .desktop-mode-dock-peek__card {
198 /* Card frame — looks like a tiny window. */
199 display: flex;
200 flex-direction: column;
201 overflow: hidden;
202 width: 280px;
203 min-height: 92px;
204 padding: 0;
205 border: 1px solid rgba( 255, 255, 255, 0.10 );
206 border-radius: 10px;
207 background: rgba( 255, 255, 255, 0.04 );
208 color: inherit;
209 font: inherit;
210 text-align: start;
211 cursor: pointer;
212 view-transition-name: var( --peek-card-vt-name, none );
213 /* Animation start state — cards begin slightly displaced toward
214 the tile they "peeled off of". CSS resets to identity once the
215 `--open` class on the parent triggers the transition. */
216 opacity: 0;
217 transform: translateX( -10px ) scale( 0.96 );
218 transition:
219 opacity 240ms ease-out var( --peek-card-delay, 0ms ),
220 transform 320ms cubic-bezier( 0.34, 1.56, 0.64, 1 ) var( --peek-card-delay, 0ms ),
221 border-color 180ms ease-out,
222 box-shadow 180ms ease-out;
223 }
224
225 .desktop-mode-dock-peek--open .desktop-mode-dock-peek__card {
226 opacity: 1;
227 transform: translateX( 0 ) scale( 1 );
228 }
229
230 .desktop-mode-dock-peek__card:hover {
231 border-color: rgba( 255, 255, 255, 0.24 );
232 box-shadow: 0 4px 14px -4px rgba( 0, 0, 0, 0.4 );
233 }
234
235 .desktop-mode-dock-peek__card:focus-visible {
236 outline: 2px solid var( --desktop-mode-accent, #2271b1 );
237 outline-offset: 2px;
238 }
239
240 /* ──────────────────────────────────────────────────────────────────
241 Mini-window chrome — instance card faux titlebar.
242 ────────────────────────────────────────────────────────────────── */
243
244 .desktop-mode-dock-peek__card-titlebar {
245 display: flex;
246 align-items: center;
247 gap: 8px;
248 padding: 8px 10px;
249 /* Use the live window's titlebar background so the mini-window
250 reads as a faithful shrink of the real window. Falls back to a
251 neutral translucent gradient when the variable isn't set (early
252 load, theme without the token, tests). */
253 background: var(
254 --desktop-mode-titlebar-bg-focused,
255 linear-gradient(
256 to bottom,
257 rgba( 255, 255, 255, 0.10 ),
258 rgba( 255, 255, 255, 0.04 )
259 )
260 );
261 color: var(
262 --desktop-mode-titlebar-color-focused,
263 var( --desktop-mode-fg, #fff )
264 );
265 border-bottom: 1px solid rgba( 0, 0, 0, 0.12 );
266 font-size: 12px;
267 line-height: 1.2;
268 }
269
270 .desktop-mode-dock-peek__card-dots {
271 display: inline-flex;
272 gap: 4px;
273 flex: 0 0 auto;
274 }
275
276 .desktop-mode-dock-peek__card-dots i {
277 width: 8px;
278 height: 8px;
279 border-radius: 50%;
280 background: rgba( 255, 255, 255, 0.22 );
281 }
282
283 .desktop-mode-dock-peek__card-dots i:nth-child( 1 ) {
284 background: #ff5f57;
285 }
286 .desktop-mode-dock-peek__card-dots i:nth-child( 2 ) {
287 background: #febc2e;
288 }
289 .desktop-mode-dock-peek__card-dots i:nth-child( 3 ) {
290 background: #28c840;
291 }
292
293 .desktop-mode-dock-peek__card-icon {
294 flex: 0 0 auto;
295 width: 14px;
296 height: 14px;
297 font-size: 14px;
298 line-height: 1;
299 opacity: 0.92;
300 }
301
302 .desktop-mode-dock-peek__card-label {
303 flex: 1 1 auto;
304 overflow: hidden;
305 text-overflow: ellipsis;
306 white-space: nowrap;
307 font-weight: 500;
308 }
309
310 /* ──────────────────────────────────────────────────────────────────
311 Mini-window body — tinted by the page hue, ghosted content lines.
312
313 The hue comes from `hashTitleToHue(window.id || title)`, so two
314 instances of the same page (`edit.php` and `edit.php-2`) read as
315 visually distinct without us having to invent unique colors.
316 ────────────────────────────────────────────────────────────────── */
317
318 .desktop-mode-dock-peek__card-body {
319 flex: 1 1 auto;
320 display: flex;
321 flex-direction: column;
322 gap: 6px;
323 padding: 12px 12px 14px;
324 background:
325 linear-gradient(
326 135deg,
327 hsl( var( --peek-card-hue, 210 ) 55% 22% / 0.65 ),
328 hsl( var( --peek-card-hue, 210 ) 55% 14% / 0.5 )
329 );
330 }
331
332 .desktop-mode-dock-peek__card-line {
333 display: block;
334 height: 6px;
335 border-radius: 3px;
336 background: rgba( 255, 255, 255, 0.16 );
337 }
338
339 .desktop-mode-dock-peek__card-line:nth-child( 1 ) {
340 width: 80%;
341 }
342 .desktop-mode-dock-peek__card-line:nth-child( 2 ) {
343 width: 60%;
344 }
345 .desktop-mode-dock-peek__card-line:nth-child( 3 ) {
346 width: 70%;
347 }
348
349 /* Plugin-customized body — drop the default tinted background +
350 ghost-line padding so the plugin's own markup gets a clean canvas.
351 Plugins that want the tinted look back can re-add their own
352 background; otherwise they get a transparent flex container. */
353 .desktop-mode-dock-peek__card-body--custom {
354 background: transparent;
355 padding: 0;
356 gap: 0;
357 }
358
359 /* ──────────────────────────────────────────────────────────────────
360 Built-in renderer: OS Settings.
361
362 A big tinted hero icon + a phrase + a row of three tab-icon
363 chips representing the inner Settings tabs. Tinted with the
364 user's WP profile accent so the card "belongs" to the active
365 color scheme.
366 ────────────────────────────────────────────────────────────────── */
367
368 .desktop-mode-dock-peek__card-body--os-settings {
369 display: flex;
370 flex-direction: column;
371 align-items: center;
372 justify-content: center;
373 gap: 6px;
374 padding: 14px 12px;
375 background: linear-gradient(
376 135deg,
377 color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 65%, #1d2327 ) 0%,
378 color-mix( in srgb, var( --wp-admin-theme-color, #2271b1 ) 35%, #1d2327 ) 100%
379 );
380 color: #fff;
381 }
382
383 .desktop-mode-dock-peek__os-hero {
384 font-size: 32px;
385 width: 32px;
386 height: 32px;
387 line-height: 1;
388 opacity: 0.95;
389 filter: drop-shadow( 0 1px 2px rgba( 0, 0, 0, 0.4 ) );
390 }
391
392 .desktop-mode-dock-peek__os-subtitle {
393 font-size: 11px;
394 font-weight: 500;
395 letter-spacing: 0.04em;
396 text-transform: uppercase;
397 opacity: 0.85;
398 text-shadow: 0 1px 2px rgba( 0, 0, 0, 0.3 );
399 }
400
401 .desktop-mode-dock-peek__os-tabs {
402 display: flex;
403 gap: 6px;
404 margin-top: 2px;
405 }
406
407 .desktop-mode-dock-peek__os-tab {
408 width: 18px;
409 height: 18px;
410 font-size: 13px;
411 line-height: 18px;
412 border-radius: 4px;
413 background: rgba( 255, 255, 255, 0.18 );
414 color: rgba( 255, 255, 255, 0.92 );
415 text-align: center;
416 }
417
418 /* ──────────────────────────────────────────────────────────────────
419 Built-in renderer: Recycle Bin.
420
421 Empty state: a single trash icon, dim. Full state: a stack of
422 three "slips" peeking from behind the icon + a count chip in the
423 corner. The `data-empty` attribute toggles between the two.
424 ────────────────────────────────────────────────────────────────── */
425
426 .desktop-mode-dock-peek__card-body--recycle-bin {
427 display: flex;
428 flex-direction: column;
429 align-items: center;
430 justify-content: center;
431 gap: 8px;
432 padding: 14px 12px;
433 background: linear-gradient(
434 135deg,
435 #3a3f47 0%,
436 #23272b 100%
437 );
438 color: rgba( 255, 255, 255, 0.92 );
439 }
440
441 .desktop-mode-dock-peek__bin-stage {
442 position: relative;
443 width: 44px;
444 height: 44px;
445 display: flex;
446 align-items: center;
447 justify-content: center;
448 }
449
450 .desktop-mode-dock-peek__bin-icon {
451 font-size: 32px;
452 width: 32px;
453 height: 32px;
454 line-height: 1;
455 opacity: 0.95;
456 filter: drop-shadow( 0 1px 2px rgba( 0, 0, 0, 0.4 ) );
457 z-index: 1;
458 }
459
460 .desktop-mode-dock-peek__bin-stack {
461 position: absolute;
462 inset: 0;
463 display: flex;
464 align-items: flex-end;
465 justify-content: center;
466 pointer-events: none;
467 opacity: 0;
468 transition: opacity 200ms ease-out;
469 }
470
471 .desktop-mode-dock-peek__card-body--recycle-bin[ data-empty = "false" ]
472 .desktop-mode-dock-peek__bin-stack {
473 opacity: 1;
474 }
475
476 .desktop-mode-dock-peek__bin-slip {
477 position: absolute;
478 width: 22px;
479 height: 4px;
480 border-radius: 1px;
481 background: rgba( 255, 255, 255, 0.55 );
482 bottom: 6px;
483 }
484
485 .desktop-mode-dock-peek__bin-slip:nth-child( 1 ) {
486 transform: translate( -10px, -16px ) rotate( -8deg );
487 background: rgba( 255, 255, 255, 0.45 );
488 }
489 .desktop-mode-dock-peek__bin-slip:nth-child( 2 ) {
490 transform: translate( 8px, -20px ) rotate( 6deg );
491 background: rgba( 255, 255, 255, 0.5 );
492 }
493 .desktop-mode-dock-peek__bin-slip:nth-child( 3 ) {
494 transform: translate( -2px, -22px ) rotate( -2deg );
495 background: rgba( 255, 255, 255, 0.6 );
496 }
497
498 .desktop-mode-dock-peek__bin-label {
499 font-size: 11px;
500 font-weight: 500;
501 letter-spacing: 0.02em;
502 color: rgba( 255, 255, 255, 0.78 );
503 }
504
505 .desktop-mode-dock-peek__card-body--recycle-bin[ data-empty = "false" ]
506 .desktop-mode-dock-peek__bin-label {
507 color: var( --wp-admin-theme-color, #4f9cff );
508 font-weight: 600;
509 }
510
511 /* ──────────────────────────────────────────────────────────────────
512 Ghost Card — the "open new" affordance.
513
514 Same geometry as instance cards (so the fan reads as one stack)
515 but visually distinct via:
516 * Dashed border (vs solid)
517 * Lower base opacity
518 * Breathing pulse (slow scale + opacity loop)
519 * Centered "+" + label, no faux titlebar / body lines
520 On hover, the dashes resolve to a solid line, the pulse stops,
521 and opacity ramps to full — the card "solidifies" into the same
522 visual weight as an instance card.
523 ────────────────────────────────────────────────────────────────── */
524
525 .desktop-mode-dock-peek__card--ghost {
526 display: flex;
527 align-items: center;
528 justify-content: center;
529 gap: 10px;
530 width: 280px;
531 min-height: 64px;
532 padding: 16px;
533 border-style: dashed;
534 border-color: rgba( 255, 255, 255, 0.34 );
535 /* A self-contained dark translucent fill so the white label
536 reads on light wallpapers / light color schemes too. The
537 popover's parent surface uses `color-mix(--desktop-mode-bg)`,
538 which goes near-white on light themes — without our own
539 backdrop the Ghost Card was white-on-white. */
540 background: rgba( 0, 0, 0, 0.32 );
541 color: #fff;
542 text-shadow: 0 1px 2px rgba( 0, 0, 0, 0.45 );
543 font-size: 13px;
544 font-weight: 500;
545 animation: desktop-mode-dock-peek-breathe 2400ms ease-in-out infinite;
546 }
547
548 .desktop-mode-dock-peek__card--ghost:hover {
549 border-style: solid;
550 border-color: rgba( 255, 255, 255, 0.55 );
551 background: rgba( 0, 0, 0, 0.45 );
552 color: #fff;
553 animation: none;
554 transform: scale( 1.02 );
555 box-shadow: 0 4px 14px -4px rgba( 0, 0, 0, 0.5 );
556 }
557
558 .desktop-mode-dock-peek__card-plus {
559 flex: 0 0 auto;
560 display: inline-flex;
561 align-items: center;
562 justify-content: center;
563 width: 22px;
564 height: 22px;
565 font-size: 22px;
566 line-height: 1;
567 font-weight: 300;
568 /* Slightly more presence than before (was 0.42 alpha). The
569 "+" is the only glyph the user sees in the resting state,
570 so it has to read at a glance even against the breathing
571 pulse's lower-opacity moments. */
572 color: rgba( 255, 255, 255, 0.78 );
573 text-shadow: 0 1px 2px rgba( 0, 0, 0, 0.45 );
574 transition: color 160ms ease-out;
575 }
576
577 .desktop-mode-dock-peek__card--ghost:hover .desktop-mode-dock-peek__card-plus {
578 color: var( --desktop-mode-accent, #4f9cff );
579 }
580
581 @keyframes desktop-mode-dock-peek-breathe {
582 0%, 100% {
583 opacity: 0.74;
584 transform: scale( 1 );
585 }
586 50% {
587 opacity: 1;
588 transform: scale( 1.018 );
589 }
590 }
591
592 @media ( prefers-reduced-motion: reduce ) {
593 .desktop-mode-dock-peek,
594 .desktop-mode-dock-peek__card,
595 .desktop-mode-dock-peek__card--ghost {
596 transition: opacity 80ms linear;
597 animation: none !important;
598 transform: none !important;
599 }
600 .desktop-mode-dock-peek--open .desktop-mode-dock-peek__card {
601 transform: none;
602 }
603 }
604
605 /* ──────────────────────────────────────────────────────────────────
606 View Transitions — when supported, an instance card morphs into
607 its window on click (and the Ghost Card morphs into the new
608 spawned window). The transition group is named per-card via
609 `--peek-card-vt-name`, set in JS at click time so the source +
610 destination share an identity.
611 ────────────────────────────────────────────────────────────────── */
612
613 @supports ( view-transition-name: none ) {
614 ::view-transition-old( root ),
615 ::view-transition-new( root ) {
616 animation-duration: 280ms;
617 animation-timing-function: cubic-bezier( 0.22, 1, 0.36, 1 );
618 }
619 }
620