PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.4
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.4
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 1.1.4, at assets/css/dock-peek.css

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