PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.10
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.10
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
← All changes | assets/css/window-states.css +353 -63 0.9.21.1.10 View file →
@@ -1,6 +1,6 @@
1 1 /**
2 - * Desktop Mode — Window state visuals.
2 + * OpenStation — Window state visuals.
3 3 *
4 4 * Every rule that keys off a `--state` modifier class: the resize
5 5 * handle + its overlay shield (drag/resize iframe-interception),
6 6 * maximize, fullscreen, the opening animation, minimize + closing
@@ -20,21 +20,108 @@
20 20 * visible even without hover; the other three remain transparent
21 21 * (discoverable by cursor change only) to keep the window chrome
22 22 * clean.
23 23 *
24 - * Hit-area is `--desktop-mode-resize-size` (20 px by default). The
24 + * Hit-area is `--os-resize-size` (20 px by default). The
25 25 * handles extend a few pixels OUTSIDE the window edge so a cursor
26 26 * landing just past the border still activates them — matches how
27 27 * native OS windows forgive edge-grabs.
28 28 */
29 -.desktop-mode-window__resize-handle {
29 +.os-window__resize-handle {
30 30 position: absolute;
31 - width: var(--desktop-mode-resize-size);
32 - height: var(--desktop-mode-resize-size);
31 + width: var(--os-resize-size);
32 + height: var(--os-resize-size);
33 33 z-index: 999;
34 34 }
35 35
36 -.desktop-mode-window__resize-handle--se {
36 +/*
37 + * Desktop-theme corner ornaments (WINDOW_CORNER_{NE,NW,SE,SW}
38 + * texture slots).
39 + *
40 + * ## Why they are INSET rather than centred on the handle
41 + *
42 + * The obvious placement — centre the ornament on the resize handle —
43 + * is wrong, and looks it. `.os-window` sets
44 + * `overflow: hidden` (it clips iframe content to the rounded
45 + * corners), and the handles deliberately sit 3px OUTSIDE the window
46 + * box so an edge-grab still lands. So a handle-centred ornament gets
47 + * clipped twice: the part past the window edge is cut, and then
48 + * `border-radius` shaves what remains. The result reads as if the
49 + * artwork were painted behind the window.
50 + *
51 + * Escaping the clip is not an option worth its cost: the ornaments
52 + * would have to leave `.os-window` entirely — a wrapper
53 + * element, or an overlay in `.os-area` whose geometry is
54 + * re-tracked on every drag/resize frame. A theme that genuinely
55 + * wants artwork OUTSIDE the frame already has the right tool in the
56 + * `WINDOW_FRAME` slot: `border-image` paints in the border area,
57 + * which `overflow` does not clip.
58 + *
59 + * So the ornament is anchored inside the window's rounded corner
60 + * instead. The default inset clears the handle's 3px overhang plus a
61 + * share of the corner radius, which is what keeps the arc from
62 + * cutting it at larger radii. Themes that want it tighter or looser
63 + * override `--os-window-corner-inset`.
64 + *
65 + * Physical `top`/`left`/`right`/`bottom` rather than logical
66 + * properties here, deliberately: these track the resize handles,
67 + * which are physical corners by nature (the NW handle is top-left in
68 + * both LTR and RTL) and are themselves positioned physically.
69 + *
70 + * With no theme active every `--os-window-corner-*-image`
71 + * is unset, the shorthand resolves to `none`, and the pseudo-element
72 + * paints nothing — zero visual change and no image request.
73 + *
74 + * NOTE the BEM modifiers are `--ne` / `--nw` / `--se` / `--sw` on
75 + * `.os-window__resize-handle`, not bare `.--ne`.
76 + */
77 +.os-window__resize-handle--ne::after,
78 +.os-window__resize-handle--nw::after,
79 +.os-window__resize-handle--se::after,
80 +.os-window__resize-handle--sw::after {
81 + content: "";
82 + position: absolute;
83 + /* 3px = the handle's overhang past the window edge (see the
84 + * `--nw` / `--ne` / `--sw` / `--se` rules above). The radius
85 + * share keeps the ornament clear of the corner arc. */
86 + --_dm-corner-inset: var(
87 + --os-window-corner-inset,
88 + calc( 3px + var( --os-window-radius, 8px ) * 0.4 )
89 + );
90 + width: var( --os-window-corner-size, 16px );
91 + height: var( --os-window-corner-size, 16px );
92 + /* Ornaments are decoration; the handle keeps the drag affordance. */
93 + pointer-events: none;
94 + background-repeat: no-repeat;
95 + background-position: center;
96 + background-size: contain;
97 +}
98 +
99 +.os-window__resize-handle--nw::after {
100 + top: var( --_dm-corner-inset );
101 + left: var( --_dm-corner-inset );
102 + background-image: var( --os-window-corner-nw-image, none );
103 +}
104 +
105 +.os-window__resize-handle--ne::after {
106 + top: var( --_dm-corner-inset );
107 + right: var( --_dm-corner-inset );
108 + background-image: var( --os-window-corner-ne-image, none );
109 +}
110 +
111 +.os-window__resize-handle--sw::after {
112 + bottom: var( --_dm-corner-inset );
113 + left: var( --_dm-corner-inset );
114 + background-image: var( --os-window-corner-sw-image, none );
115 +}
116 +
117 +.os-window__resize-handle--se::after {
118 + bottom: var( --_dm-corner-inset );
119 + right: var( --_dm-corner-inset );
120 + background-image: var( --os-window-corner-se-image, none );
121 +}
122 +
123 +.os-window__resize-handle--se {
37 124 right: -3px;
38 125 bottom: -3px;
39 126 cursor: nwse-resize;
40 127 background: linear-gradient(
@@ -39,33 +126,33 @@
39 126 cursor: nwse-resize;
40 127 background: linear-gradient(
41 128 135deg,
42 129 transparent 30%,
43 - var(--desktop-mode-resize-color) 30%,
44 - var(--desktop-mode-resize-color) 40%,
130 + var(--os-resize-color) 30%,
131 + var(--os-resize-color) 40%,
45 132 transparent 40%,
46 133 transparent 55%,
47 - var(--desktop-mode-resize-color) 55%,
48 - var(--desktop-mode-resize-color) 65%,
134 + var(--os-resize-color) 55%,
135 + var(--os-resize-color) 65%,
49 136 transparent 65%,
50 137 transparent 80%,
51 - var(--desktop-mode-resize-color) 80%
138 + var(--os-resize-color) 80%
52 139 );
53 140 }
54 141
55 -.desktop-mode-window__resize-handle--sw {
142 +.os-window__resize-handle--sw {
56 143 left: -3px;
57 144 bottom: -3px;
58 145 cursor: nesw-resize;
59 146 }
60 147
61 -.desktop-mode-window__resize-handle--ne {
148 +.os-window__resize-handle--ne {
62 149 right: -3px;
63 150 top: -3px;
64 151 cursor: nesw-resize;
65 152 }
66 153
67 -.desktop-mode-window__resize-handle--nw {
154 +.os-window__resize-handle--nw {
68 155 left: -3px;
69 156 top: -3px;
70 157 cursor: nwse-resize;
71 158 }
@@ -73,10 +160,10 @@
73 160 /*
74 161 * During drag/resize, overlay the iframe to prevent it from
75 162 * capturing pointer events and breaking the drag.
76 163 */
77 -.desktop-mode-window--dragging .desktop-mode-window__body::after,
78 -.desktop-mode-window--resizing .desktop-mode-window__body::after {
164 +.os-window--dragging .os-window__body::after,
165 +.os-window--resizing .os-window__body::after {
79 166 content: "";
80 167 position: absolute;
81 168 inset: 0;
82 169 z-index: 10;
@@ -86,13 +173,13 @@
86 173 * Maximized state: the pixel geometry is applied inline from JS
87 174 * (matching the desktop area) so left/top/width/height can animate.
88 175 * Only cosmetic overrides here.
89 176 */
90 -.desktop-mode-window--maximized {
177 +.os-window--maximized {
91 178 border-radius: 0;
92 179 }
93 180
94 -.desktop-mode-window--maximized .desktop-mode-window__resize-handle {
181 +.os-window--maximized .os-window__resize-handle {
95 182 display: none;
96 183 }
97 184
98 185 /*
@@ -98,24 +185,24 @@
98 185 /*
99 186 * Fullscreen ("focus") state — the window escapes the desktop area and
100 187 * covers the entire viewport, hiding the admin bar and dock behind
101 188 * it. Uses position: fixed so no ancestor overflow clips it, and
102 - * a z-index above the admin bar (--desktop-mode-z-adminbar). Inline
189 + * a z-index above the admin bar (--os-z-adminbar). Inline
103 190 * geometry set by toggleMaximize() is overridden with !important because
104 191 * we never want fullscreen to respect a saved x/y/width/height.
105 192 */
106 -.desktop-mode-window--fullscreen {
193 +.os-window--fullscreen {
107 194 position: fixed !important;
108 195 left: 0 !important;
109 196 top: 0 !important;
110 197 width: 100vw !important;
111 198 height: 100vh !important;
112 - z-index: var(--desktop-mode-z-fullscreen) !important;
199 + z-index: var(--os-z-fullscreen) !important;
113 200 border-radius: 0 !important;
114 201 border: none;
115 202 }
116 203
117 -.desktop-mode-window--fullscreen .desktop-mode-window__resize-handle {
204 +.os-window--fullscreen .os-window__resize-handle {
118 205 display: none;
119 206 }
120 207
121 208 /*
@@ -121,9 +208,9 @@
121 208 /*
122 209 * Opening animation — subtle scale-up + fade-in.
123 210 * The class is added on creation and removed after the animation ends.
124 211 */
125 -@keyframes desktop-mode-window-open {
212 +@keyframes os-window-open {
126 213 from {
127 214 opacity: 0;
128 215 transform: scale(0.92);
129 216 }
@@ -132,10 +219,10 @@
132 219 transform: scale(1);
133 220 }
134 221 }
135 222
136 -.desktop-mode-window--opening {
137 - animation: desktop-mode-window-open 0.2s ease forwards;
223 +.os-window--opening {
224 + animation: os-window-open 0.2s ease forwards;
138 225 }
139 226
140 227 /*
141 228 * Shake animation — used by `Window.shake()` for nudge / attention-
@@ -147,9 +234,9 @@
147 234 * Reduced-motion fallback: a static accent ring for the same
148 235 * duration so users with prefers-reduced-motion still see the
149 236 * affordance without movement.
150 237 */
151 -@keyframes desktop-mode-window-shake {
238 +@keyframes os-window-shake {
152 239 0%, 100% { transform: translateX( 0 ); }
153 240 8% { transform: translateX( -10px ); }
154 241 18% { transform: translateX( 9px ); }
155 242 28% { transform: translateX( -8px ); }
@@ -159,14 +246,14 @@
159 246 72% { transform: translateX( -3px ); }
160 247 84% { transform: translateX( 2px ); }
161 248 }
162 249
163 -.desktop-mode-window--shaking {
164 - animation: desktop-mode-window-shake 0.6s cubic-bezier( 0.36, 0.07, 0.19, 0.97 ) both;
250 +.os-window--shaking {
251 + animation: os-window-shake 0.6s cubic-bezier( 0.36, 0.07, 0.19, 0.97 ) both;
165 252 }
166 253
167 254 @media ( prefers-reduced-motion: reduce ) {
168 - .desktop-mode-window--shaking {
255 + .os-window--shaking {
169 256 animation: none;
170 257 outline: 3px solid var( --wp-admin-theme-color, #2271b1 );
171 258 outline-offset: 2px;
172 259 transition: outline 0.6s ease;
@@ -173,12 +260,12 @@
173 260 }
174 261 }
175 262
176 263 /*
177 - * Desktop-switch slide — applied to `.desktop-mode-area` (the windows
264 + * Desktop-switch slide — applied to `.os-area` (the windows
178 265 * + widgets container) when the user moves between virtual desktops
179 266 * via arrow keys or any other directional caller. The wallpaper lives
180 - * OUTSIDE this element (sibling under `.desktop-mode-shell`), so the
267 + * OUTSIDE this element (sibling under `.os-shell`), so the
181 268 * slide reveals the wallpaper as a backdrop — no black flash, no
182 269 * awkward gap on the leading edge.
183 270 *
184 271 * `--sliding-from-right` plays when moving to the next desktop (the
@@ -195,9 +282,9 @@
195 282 *
196 283 * `forwards` isn't needed — the JS strips the class on `animationend`
197 284 * so the element returns to its natural transform / opacity state.
198 285 */
199 -@keyframes desktop-mode-area-slide-from-right {
286 +@keyframes os-area-slide-from-right {
200 287 from {
201 288 transform: translateX(64px);
202 289 opacity: 0.35;
203 290 }
@@ -206,9 +293,9 @@
206 293 opacity: 1;
207 294 }
208 295 }
209 296
210 -@keyframes desktop-mode-area-slide-from-left {
297 +@keyframes os-area-slide-from-left {
211 298 from {
212 299 transform: translateX(-64px);
213 300 opacity: 0.35;
214 301 }
@@ -217,39 +304,93 @@
217 304 opacity: 1;
218 305 }
219 306 }
220 307
221 -.desktop-mode-area--sliding-from-right {
222 - animation: desktop-mode-area-slide-from-right 0.28s cubic-bezier( 0.2, 0.7, 0.2, 1 );
308 +.os-area--sliding-from-right {
309 + animation: os-area-slide-from-right 0.28s cubic-bezier( 0.2, 0.7, 0.2, 1 );
223 310 }
224 311
225 -.desktop-mode-area--sliding-from-left {
226 - animation: desktop-mode-area-slide-from-left 0.28s cubic-bezier( 0.2, 0.7, 0.2, 1 );
312 +.os-area--sliding-from-left {
313 + animation: os-area-slide-from-left 0.28s cubic-bezier( 0.2, 0.7, 0.2, 1 );
227 314 }
228 315
229 316 @media ( prefers-reduced-motion: reduce ) {
230 - .desktop-mode-area--sliding-from-right,
231 - .desktop-mode-area--sliding-from-left {
317 + .os-area--sliding-from-right,
318 + .os-area--sliding-from-left {
232 319 animation: none;
233 320 }
234 321 }
235 322
236 323 /*
237 - * Minimized state — fade + scale down.
238 - * Uses opacity + pointer-events instead of display: none so the
239 - * CSS transition can animate the change. The window is invisible
240 - * and non-interactive but stays in the DOM for a smooth restore.
324 + * Desktop-name HUD. `pointer-events: none` is required, not cosmetic:
325 + * it sits over the wallpaper's click surface, which minimizes every
326 + * window.
241 327 */
242 -.desktop-mode-window--minimized {
328 +.os-desktop-name-hud {
329 + position: absolute;
330 + top: 50%;
331 + left: 50%;
332 + transform: translate( -50%, -50% );
333 + z-index: var( --os-z-desktop-name, 195 );
334 + max-width: min( 70%, 520px );
335 + padding: 14px 28px;
336 + border-radius: 16px;
337 + font-size: 20px;
338 + font-weight: 600;
339 + letter-spacing: 0.01em;
340 + text-align: center;
341 + white-space: nowrap;
342 + overflow: hidden;
343 + text-overflow: ellipsis;
344 + color: var( --os-ui-fg-on-accent, #fff );
345 + background-color: var( --os-ui-scrim, rgba( 0, 0, 0, 0.32 ) );
346 + backdrop-filter: blur( 18px ) saturate( 140% );
347 + -webkit-backdrop-filter: blur( 18px ) saturate( 140% );
348 + box-shadow:
349 + 0 8px 28px rgba( 0, 0, 0, 0.35 ),
350 + inset 0 0 0 1px rgba( 255, 255, 255, 0.08 );
351 + pointer-events: none;
352 + opacity: 1;
353 + transition: opacity 0.26s ease;
354 +}
355 +
356 +.os-desktop-name-hud--out {
243 357 opacity: 0;
244 - transform: scale(0.92) translateY(16px);
358 +}
359 +
360 +@media ( prefers-reduced-motion: reduce ) {
361 + .os-desktop-name-hud {
362 + transition: none;
363 + }
364 +}
365 +
366 +/*
367 + * Minimized state — window is hidden and non-interactive at the dock.
368 + * Uses opacity + pointer-events + transition: none so neither state
369 + * changes nor WAAPI animation teardown trigger accidental transitions.
370 + */
371 +.os-window--minimized {
372 + opacity: 0;
245 373 pointer-events: none;
246 - will-change: transform, opacity;
374 + transition: none !important;
247 375 }
248 376
249 377 /*
378 + * Genie helpers — active only during the WAAPI flight to/from the
379 + * dock. Disable the base `left/top/width/height/transform/opacity`
380 + * transition so the CSS transition doesn't fight the WAAPI
381 + * `element.animate()` that drives the scale-to-dock motion. The
382 + * minimized class is applied AFTER the minimizing flight lands, and
383 + * the restoring class is removed AFTER the restoring flight lands.
384 + */
385 +.os-window--minimizing,
386 +.os-window--restoring {
387 + transition: none !important;
388 +}
389 +
390 +/*
250 391 * `pointer-events: none` on the window root above is not enough on
251 - * its own: a number of descendants (most notably `.desktop-mode-
392 + * its own: a number of descendants (most notably `.os-
252 393 * file-tile`, but also other tile renderers) explicitly set
253 394 * `pointer-events: auto` so the wallpaper / folder layer doesn't
254 395 * eat their clicks. That `auto` overrides the parent's `none`, and
255 396 * after "Show Desktop" minimizes every window, hovering the empty
@@ -259,10 +400,10 @@
259 400 * The universal selector is scoped INSIDE the minimized window so
260 401 * it doesn't touch anything else, and `!important` is required to
261 402 * win over the per-tile `pointer-events: auto`.
262 403 */
263 -.desktop-mode-window--minimized *,
264 -.desktop-mode-window--closing * {
404 +.os-window--minimized *,
405 +.os-window--closing * {
265 406 pointer-events: none !important;
266 407 }
267 408
268 409 /*
@@ -268,9 +409,9 @@
268 409 /*
269 410 * Closing animation — fade out + slight scale down.
270 411 * The JS waits for transitionend then removes the element.
271 412 */
272 -.desktop-mode-window--closing {
413 +.os-window--closing {
273 414 opacity: 0;
274 415 transform: scale(0.95);
275 416 pointer-events: none;
276 417 will-change: transform, opacity;
@@ -281,14 +422,19 @@
281 422 * so state changes are instant but still technically transition
282 423 * (avoids layout jumps from display: none).
283 424 */
284 425 @media ( prefers-reduced-motion: reduce ) {
285 - .desktop-mode-window {
426 + .os-window {
286 427 transition-duration: 0.01ms !important;
287 428 }
288 - .desktop-mode-window--opening {
429 + .os-window--opening {
289 430 animation-duration: 0.01ms !important;
290 431 }
432 + .os-window--minimizing,
433 + .os-window--restoring {
434 + animation: none !important;
435 + transition: none !important;
436 + }
291 437 }
292 438
293 439 /*
294 440 * RTL note: resize handles use physical `left` / `right` anchors
@@ -306,9 +452,9 @@
306 452 * of the left/right edge (see window-manager/snap-zones.ts). It's a
307 453 * translucent accent-colored rectangle covering the target half, with
308 454 * a bright border so the user sees exactly where the window will land.
309 455 * --------------------------------------------------------------- */
310 -.desktop-mode-snap-preview {
456 +.os-snap-preview {
311 457 position: absolute;
312 458 background: color-mix(
313 459 in srgb,
314 460 var( --wp-admin-theme-color, #2271b1 ) 14%,
@@ -330,13 +476,157 @@
330 476 width 0.18s cubic-bezier( 0.2, 0, 0.2, 1 ),
331 477 height 0.18s cubic-bezier( 0.2, 0, 0.2, 1 );
332 478 }
333 479
334 -.desktop-mode-snap-preview--visible {
480 +.os-snap-preview--visible {
335 481 opacity: 1;
336 482 }
337 483
484 +/* ---------------------------------------------------------------
485 + * Grid snap — the 6×6 desk shown while Option / Alt is held during a
486 + * drag (see window-manager/grid-snap.ts).
487 + *
488 + * The overlay is the work area; the lines are one repeating gradient
489 + * sized to a cell, and a cell is a fraction of the overlay — so the
490 + * same rule draws the grid at any dimensions, on any display, and
491 + * follows the work area if the dock folds away mid-drag. The target
492 + * is the span the window will land on, in the same accent as the
493 + * edge-snap preview so both previews read as one vocabulary.
494 + * --------------------------------------------------------------- */
495 +.os-grid-snap {
496 + position: absolute;
497 + /* Same layer as the edge-snap preview: under the windows, so the
498 + one being dragged stays on top of its own landing zone. */
499 + z-index: 99;
500 + pointer-events: none;
501 + opacity: 0;
502 + transition: opacity 0.2s ease;
503 + --os-grid-snap-cols: 6;
504 + --os-grid-snap-rows: 6;
505 + --os-grid-snap-line: color-mix(
506 + in srgb,
507 + var( --wp-admin-theme-color, #2271b1 ) 55%,
508 + transparent
509 + );
510 + background-image:
511 + linear-gradient( to right, var( --os-grid-snap-line ) 1px, transparent 1px ),
512 + linear-gradient( to bottom, var( --os-grid-snap-line ) 1px, transparent 1px );
513 + background-size:
514 + calc( 100% / var( --os-grid-snap-cols ) ) 100%,
515 + 100% calc( 100% / var( --os-grid-snap-rows ) );
516 + /* The gradient draws a line at the START of every cell; the
517 + inset ring supplies the far edges so the grid is closed. */
518 + box-shadow: inset 0 0 0 1px var( --os-grid-snap-line );
519 +}
520 +
521 +.os-grid-snap--visible {
522 + opacity: 1;
523 +}
524 +
525 +.os-grid-snap__target {
526 + position: absolute;
527 + background: color-mix(
528 + in srgb,
529 + var( --wp-admin-theme-color, #2271b1 ) 16%,
530 + transparent
531 + );
532 + border: 2px solid var( --wp-admin-theme-color, #2271b1 );
533 + border-radius: 10px;
534 + box-shadow:
535 + 0 0 0 1px rgba( 255, 255, 255, 0.15 ) inset,
536 + 0 12px 36px rgba( 0, 0, 0, 0.22 );
537 + /* Geometry animates so a cell-to-cell change slides rather than
538 + jumps; short, so it never lags the pointer by a cell. */
539 + transition:
540 + left 0.12s cubic-bezier( 0.2, 0, 0.2, 1 ),
541 + top 0.12s cubic-bezier( 0.2, 0, 0.2, 1 ),
542 + width 0.12s cubic-bezier( 0.2, 0, 0.2, 1 ),
543 + height 0.12s cubic-bezier( 0.2, 0, 0.2, 1 );
544 +}
545 +
338 546 /*
547 + * The span, as "cols × rows", in the MIDDLE of the target. A user
548 + * picking a 3×2 wants to know it is 3×2 before letting go; the count
549 + * is what makes the grid a tool rather than a decoration, but only
550 + * if it is read, and in the corner it was not. The pointer is
551 + * somewhere inside the target by definition, so the centre is the
552 + * shortest average trip for the eye; on a 2×5 the corner could be
553 + * several hundred pixels from the hand.
554 + *
555 + * Centred rather than pinned to the pointer on purpose: a badge that
556 + * tracked the cursor would rewrite its position every move, and a
557 + * number that slides while you read it is harder to read than one
558 + * that waits. It only moves when the span does.
559 + */
560 +.os-grid-snap__target::after {
561 + content: attr( data-cols ) ' × ' attr( data-rows );
562 + position: absolute;
563 + top: 50%;
564 + left: 50%;
565 + transform: translate( -50%, -50% );
566 + padding: 2px 7px;
567 + border-radius: 6px;
568 + background: var( --os-ui-scrim, rgba( 0, 0, 0, 0.55 ) );
569 + color: var( --os-ui-fg-on-accent, #fff );
570 + font-size: 11px;
571 + font-weight: 600;
572 + letter-spacing: 0.03em;
573 + font-variant-numeric: tabular-nums;
574 +}
575 +
576 +/*
577 + * The desk in grid mode. Every OTHER window recedes so the grid and
578 + * the landing zone read through them; the one in hand stays solid —
579 + * it is the thing being placed, and the user needs to see it, not
580 + * through it. Translucent enough that the grid shows, opaque enough
581 + * that the other windows are still recognisably where they are,
582 + * because "will this land on top of Posts?" is the question the grid
583 + * is there to answer.
584 + *
585 + * Opacity only: a blur or a desaturate on a live iframe is a
586 + * per-frame filter on a large surface, and the drag has to stay
587 + * pixel-accurate at 120Hz. Minimized windows are already hidden and
588 + * need no rule.
589 + */
590 +.os-area--grid-snapping .os-window:not( .os-window--grid-snapping ) {
591 + opacity: 0.45;
592 + transition: opacity 0.16s ease;
593 +}
594 +
595 +/* A shake moved the anchor: one pulse so the reset is seen, not inferred. */
596 +.os-grid-snap__target--reset {
597 + animation: os-grid-snap-reset 0.32s ease;
598 +}
599 +
600 +@keyframes os-grid-snap-reset {
601 + 0% {
602 + box-shadow:
603 + 0 0 0 1px rgba( 255, 255, 255, 0.15 ) inset,
604 + 0 0 0 0 var( --wp-admin-theme-color, #2271b1 );
605 + }
606 + 60% {
607 + box-shadow:
608 + 0 0 0 1px rgba( 255, 255, 255, 0.15 ) inset,
609 + 0 0 0 10px transparent;
610 + }
611 + 100% {
612 + box-shadow:
613 + 0 0 0 1px rgba( 255, 255, 255, 0.15 ) inset,
614 + 0 12px 36px rgba( 0, 0, 0, 0.22 );
615 + }
616 +}
617 +
618 +@media ( prefers-reduced-motion: reduce ) {
619 + .os-grid-snap,
620 + .os-grid-snap__target {
621 + transition: none;
622 + }
623 + .os-grid-snap__target--reset {
624 + animation: none;
625 + }
626 +}
627 +
628 +/*
339 629 * Snapped window state — `--snapped-left` / `--snapped-right` carry
340 630 * cosmetic tweaks only. Resize handles stay visible and functional:
341 631 * the user can grab any corner to shrink the window back to floating
342 632 * (the pointer layer drops the snapped-* class on first resize so
@@ -351,10 +641,10 @@
351 641 * ghost line at the seam — and compete visually with the partner
352 642 * window. Flat edges + no shadow gives the two-up feel of a native
353 643 * split view: windows adjacent, no separator.
354 644 */
355 -.desktop-mode-window--snapped-left,
356 -.desktop-mode-window--snapped-right {
645 +.os-window--snapped-left,
646 +.os-window--snapped-right {
357 647 box-shadow: none;
358 648 }
359 649
360 650 /* Override the elevated shadow applied to `--focused` windows too —
@@ -360,20 +650,20 @@
360 650 /* Override the elevated shadow applied to `--focused` windows too —
361 651 the focused selector runs at equal specificity, so without this
362 652 rule the focus shadow would re-introduce the seam once the snapped
363 653 window is active. */
364 -.desktop-mode-window--focused.desktop-mode-window--snapped-left,
365 -.desktop-mode-window--focused.desktop-mode-window--snapped-right {
654 +.os-window--focused.os-window--snapped-left,
655 +.os-window--focused.os-window--snapped-right {
366 656 box-shadow: none;
367 657 }
368 658
369 -.desktop-mode-window--snapped-left {
659 +.os-window--snapped-left {
370 660 /* Inner (right) edge flush; outer corners keep the radius. */
371 661 border-top-right-radius: 0;
372 662 border-bottom-right-radius: 0;
373 663 }
374 664
375 -.desktop-mode-window--snapped-right {
665 +.os-window--snapped-right {
376 666 border-top-left-radius: 0;
377 667 border-bottom-left-radius: 0;
378 668 }
379 669
@@ -383,12 +673,12 @@
383 673 * thumbnail visuals as the main overview; only the backdrop tint is
384 674 * lighter (the anchor window still takes the other half, so we want
385 675 * the picker to feel "peripheral" not "modal").
386 676 */
387 -.desktop-mode-area--split-overview {
677 +.os-area--split-overview {
388 678 background-color: rgba( 0, 0, 0, 0.28 );
389 679 transition: background-color 0.24s ease;
390 680 }
391 681
392 -/* Toast styling moved into `<wpd-toast-container>` and `<wpd-toast>`
393 - * (src/ui/components/wpd-toast/). The components own their shadow
682 +/* Toast styling moved into `<os-toast-container>` and `<os-toast>`
683 + * (src/ui/components/os-toast/). The components own their shadow
394 684 * DOM styling; nothing at host level needs to reach inside. */