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 / desktop-files.css

desktop-files.css in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 0.8.8, at assets/css/desktop-files.css

1,126 lines 30.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Desktop Mode — Files-on-the-Desktop tile + layer styles.
3 *
4 * The layer is an absolutely-positioned container inside
5 * `#desktop-mode-area`; tiles inside use absolute positioning
6 * keyed off `(x, y)` coordinates persisted server-side.
7 *
8 * Tiles are intentionally close to the look of `desktop-mode-icon`
9 * (the plugin-shortcut tiles) so the desktop reads as one
10 * coherent surface — but they live in their own class namespace
11 * (`desktop-mode-file-tile`) so plugins can theme each rail
12 * independently.
13 *
14 * @since 0.9.0
15 */
16
17 /*
18 * Unified rail (since 0.9.0): plugin shortcuts now ride on the
19 * same files layer as folders / posts / etc — they're a
20 * `'shortcut'` file type. The legacy `.desktop-mode-icons` DIV
21 * still renders for backwards compatibility (existing tests and
22 * non-files-active code paths), but when a files layer is
23 * mounted as a sibling we hide it so we don't end up with two
24 * rails competing for the same wallpaper.
25 */
26 #desktop-mode-area:has( > .desktop-mode-files-layer ) > .desktop-mode-icons {
27 display: none;
28 }
29
30 /*
31 * Drop-target highlight — the `data-files-drop-active` attribute
32 * is set on the FilesLayer host (desktop area or folder window
33 * body) while a shortcut payload is being dragged over it.
34 *
35 * Marching-ants animation makes the affordance unmissable even
36 * against the busiest wallpaper, and the soft inner glow draws
37 * the eye when the user is "shopping" for a drop target. Both
38 * effects respect prefers-reduced-motion below.
39 *
40 * @since 0.18.0; visuals strengthened in 0.20.0.
41 */
42 @keyframes desktop-mode-drop-marching-ants {
43 to {
44 background-position: 24px 0, -24px 0, 0 24px, 0 -24px;
45 }
46 }
47
48 [ data-files-drop-active ] {
49 position: relative;
50 outline: 3px dashed var( --wp-admin-theme-color, #2271b1 );
51 outline-offset: -8px;
52 background-image:
53 linear-gradient(
54 90deg,
55 rgba( 34, 113, 177, 0.45 ) 50%,
56 transparent 0
57 ),
58 linear-gradient(
59 90deg,
60 rgba( 34, 113, 177, 0.45 ) 50%,
61 transparent 0
62 ),
63 linear-gradient(
64 0deg,
65 rgba( 34, 113, 177, 0.45 ) 50%,
66 transparent 0
67 ),
68 linear-gradient(
69 0deg,
70 rgba( 34, 113, 177, 0.45 ) 50%,
71 transparent 0
72 );
73 background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
74 background-size: 12px 3px, 12px 3px, 3px 12px, 3px 12px;
75 background-position: 0 6px, 0 calc( 100% - 6px ), 6px 0, calc( 100% - 6px ) 0;
76 box-shadow: inset 0 0 32px 4px rgba( 34, 113, 177, 0.12 );
77 animation: desktop-mode-drop-marching-ants 0.6s linear infinite;
78 transition: box-shadow 0.18s ease-out;
79 }
80
81 @media ( prefers-reduced-motion: reduce ) {
82 [ data-files-drop-active ] {
83 animation: none;
84 }
85 }
86
87 .desktop-mode-files-layer {
88 position: absolute;
89 inset: 0;
90 z-index: 0;
91 /*
92 * Layer doesn't intercept pointer events itself — tiles and
93 * the wallpaper own that. Phase 4's wallpaper context menu
94 * binds clicks on the parent `#desktop-mode-area`, which is
95 * unblocked because of this rule.
96 */
97 pointer-events: none;
98 }
99
100 /*
101 * Live drop-cell preview. A soft outline that hovers at the cell
102 * the dropped tile will land in, so the user can predict where the
103 * grid snap will place it before releasing. Positioned absolutely
104 * inside the files layer; `translate3d` updated from JS on every
105 * pointermove while a desktop-file drag hovers the canvas.
106 *
107 * The tile dimensions are baked into `grid.ts` (88 × 96 visual,
108 * 96 × 110 cell pitch). The outline matches the cell pitch — slightly
109 * larger than the tile — so the highlight reads as "this slot" rather
110 * than "this tile."
111 *
112 * @since 0.20.0
113 */
114 .desktop-mode-files-drop-preview {
115 position: absolute;
116 top: 0;
117 left: 0;
118 width: 88px;
119 height: 96px;
120 pointer-events: none;
121 box-sizing: border-box;
122 border-radius: 12px;
123 background: var(
124 --desktop-mode-drop-preview-bg,
125 rgba( 34, 113, 177, 0.08 )
126 );
127 border: 2px dashed
128 var(
129 --desktop-mode-drop-preview-border,
130 rgba( 34, 113, 177, 0.55 )
131 );
132 transition: transform 90ms ease-out;
133 will-change: transform;
134 /* Sit BELOW tiles so a tile being dragged over the cell isn't
135 * visually clipped by the dashed outline; the outline still reads
136 * because the tile is semi-transparent during drag (`--dragging`
137 * sets `opacity: 0.4`). */
138 z-index: 0;
139 }
140
141 @media ( prefers-reduced-motion: reduce ) {
142 .desktop-mode-files-drop-preview {
143 transition: none;
144 }
145 }
146
147 .desktop-mode-file-tile {
148 pointer-events: auto;
149 position: absolute;
150 display: flex;
151 flex-direction: column;
152 align-items: center;
153 justify-content: flex-start;
154 gap: 6px;
155 width: 88px;
156 padding: 8px 4px;
157 border: 0;
158 background: transparent;
159 /* Tile foreground driven by `--desktop-mode-tile-fg` so any
160 * light-on-dark surface (folder window, My WordPress preview)
161 * can retint tiles by overriding the variable in its scope —
162 * no per-surface selectors needed here. */
163 color: var( --desktop-mode-tile-fg, var( --desktop-mode-fg, #fff ) );
164 cursor: pointer;
165 border-radius: 8px;
166 transition: background-color 0.15s ease, transform 0.05s linear;
167 -webkit-user-select: none;
168 user-select: none;
169 touch-action: none;
170 /* Soft fade-in on mount — each tile carries its own randomised
171 * `--enter-delay` / `--enter-duration` (set inline by the
172 * tile builder), so a freshly-painted grid reads as a cascade
173 * rather than a synchronised pop-in. Both values land in the
174 * decimal-of-a-second range — perceivable, never sluggish.
175 *
176 * `backwards` keeps the tile invisible during its delay so the
177 * cascade is honest; the animation runs once, leaves the tile
178 * at its static `opacity: 1`, and never re-fires from later
179 * state changes (drag, hover, drop-target).
180 */
181 animation: desktop-mode-file-tile-enter
182 var( --desktop-mode-file-tile-enter-duration, 0.4s )
183 ease-out
184 var( --desktop-mode-file-tile-enter-delay, 0s )
185 backwards;
186 }
187
188 @keyframes desktop-mode-file-tile-enter {
189 from {
190 opacity: 0;
191 }
192 to {
193 opacity: 1;
194 }
195 }
196
197 @media ( prefers-reduced-motion: reduce ) {
198 .desktop-mode-file-tile {
199 animation: none;
200 }
201 }
202
203 .desktop-mode-file-tile:hover,
204 .desktop-mode-file-tile:focus-visible {
205 background: var(
206 --desktop-mode-tile-hover-bg,
207 rgba( 255, 255, 255, 0.12 )
208 );
209 outline: none;
210 }
211
212 .desktop-mode-file-tile:focus-visible {
213 /* Inset focus ring — outset rings extended past the tile and
214 * visually overlapped the colindant icon when the canvas cell
215 * pitch is tight (96 px cell, 88 px tile → only 4 px clearance
216 * per side). Inset keeps the ring fully inside the tile. */
217 box-shadow: inset 0 0 0 2px
218 var( --desktop-mode-tile-focus-ring, var( --wp-admin-theme-color, #2271b1 ) );
219 }
220
221 /* Status ribbon sizing inside tiles. The `<wpd-ribbon>` defaults
222 * are tuned for cards (~90 px clipping window); 88 px-wide tiles
223 * need a tighter footprint or the ribbon dominates the icon. We
224 * override the component's CSS custom-property theming surface so
225 * the ribbon stays a small accent in the corner. Authors who want
226 * different sizing can override per-tile or per-section. */
227 .desktop-mode-file-tile > wpd-ribbon {
228 --wpd-ribbon-size: 48px;
229 --wpd-ribbon-banner-width: 72px;
230 --wpd-ribbon-banner-offset: 12px;
231 --wpd-ribbon-banner-pull: -18px;
232 --wpd-ribbon-padding: 1px 0;
233 --wpd-ribbon-font: 700 7px/1.4 var( --desktop-mode-font, system-ui );
234 --wpd-ribbon-tracking: 0.05em;
235 --wpd-ribbon-shadow: 0 1px 2px rgba( 0, 0, 0, 0.2 );
236 }
237
238 .desktop-mode-file-tile--dragging {
239 /* The DragManager renders a separate ghost element that follows
240 * the pointer; the SOURCE tile stays in place but is dimmed so
241 * the user has a stable reference for "this is what I'm
242 * dragging."
243 *
244 * @since 0.18.0
245 */
246 opacity: 0.4;
247 cursor: grabbing;
248 }
249
250 /* Pinned tiles ("My WordPress", Recycle Bin) anchor to a fixed slot
251 * and DO NOT wire drag — but they look + behave identically to any
252 * other tile until the user attempts a drag (which silently fails).
253 * No upfront visual cue: feedback now happens at the moment of the
254 * gesture, not before, per @since 0.9.0 design feedback.
255 *
256 * Default `cursor: pointer` from the base `.desktop-mode-file-tile`
257 * rule covers click-to-open. Touch interactions stay as
258 * `touch-action: auto` so the OS scroll gesture works on top of
259 * the tile.
260 */
261 .desktop-mode-file-tile--pinned {
262 touch-action: auto;
263 }
264
265 /* Folder tile is the drop target during a cross-window shortcut
266 * drag — the user dragged a post / page / user tile out of My
267 * WordPress and is hovering over a folder. Bright outline, soft
268 * pulse, and a slight scale so the folder reads unmistakably as
269 * "yes, drop here." @since 0.8.0; pulse added 0.20.0. */
270 @keyframes desktop-mode-drop-target-pulse {
271 0% {
272 box-shadow:
273 0 0 0 0 rgba( 34, 113, 177, 0.5 ),
274 inset 0 0 0 2px var( --wp-admin-theme-color, #2271b1 );
275 }
276 100% {
277 box-shadow:
278 0 0 0 8px rgba( 34, 113, 177, 0 ),
279 inset 0 0 0 2px var( --wp-admin-theme-color, #2271b1 );
280 }
281 }
282
283 .desktop-mode-file-tile--drop-target {
284 outline: 2px solid var( --wp-admin-theme-color, #2271b1 );
285 outline-offset: -4px;
286 background: rgba( 34, 113, 177, 0.2 );
287 transform: scale( 1.08 );
288 z-index: 5;
289 animation: desktop-mode-drop-target-pulse 1s ease-out infinite;
290 border-radius: 12px;
291 }
292
293 @media ( prefers-reduced-motion: reduce ) {
294 .desktop-mode-file-tile--drop-target {
295 animation: none;
296 }
297 }
298
299 /* Drop-hint chip — floats next to the cursor during a drag and
300 * shows context-aware text ("Drop here to create shortcut",
301 * "Can't drop here", etc.). The chip text is owned by the
302 * ghost module (`src/drag/ghost.ts`); these styles control the
303 * appearance per accept/reject/neutral mode.
304 *
305 * @since 0.20.0
306 */
307 .desktop-mode-drag-hint {
308 position: fixed;
309 top: 0;
310 left: 0;
311 padding: 6px 10px;
312 border-radius: 999px;
313 font-size: 12px;
314 font-weight: 600;
315 letter-spacing: 0.02em;
316 white-space: nowrap;
317 pointer-events: none;
318 user-select: none;
319 box-shadow: 0 4px 14px rgba( 0, 0, 0, 0.22 );
320 background: rgba( 30, 30, 30, 0.85 );
321 color: #fff;
322 z-index: 2147483647;
323 transition:
324 background-color 0.12s ease,
325 color 0.12s ease,
326 transform 0.06s ease-out;
327 }
328
329 .desktop-mode-drag-hint--neutral {
330 background: rgba( 30, 30, 30, 0.85 );
331 color: #fff;
332 }
333
334 .desktop-mode-drag-hint--accept {
335 background: #1e8449;
336 color: #fff;
337 box-shadow:
338 0 0 0 2px rgba( 30, 132, 73, 0.25 ),
339 0 6px 18px rgba( 30, 132, 73, 0.3 );
340 }
341
342 .desktop-mode-drag-hint--accept::before {
343 /* Tiny checkmark chevron prefix so "Drop here" reads as an
344 * actionable affirmation rather than a passive label. The
345 * checkmark is a real character (U+2713) so screen readers
346 * skip past it via the `aria-hidden` on the host chip. */
347 content: '';
348 }
349
350 .desktop-mode-drag-hint--reject {
351 background: #b32d2e;
352 color: #fff;
353 box-shadow:
354 0 0 0 2px rgba( 179, 45, 46, 0.25 ),
355 0 6px 18px rgba( 179, 45, 46, 0.3 );
356 }
357
358 .desktop-mode-drag-hint--reject::before {
359 content: '';
360 }
361
362 /*
363 * No body-level cursor swap during drag — the user prefers the
364 * default arrow cursor regardless of state, so the accept/reject
365 * feedback lives entirely in the hint chip + outline animation.
366 * Don't reintroduce a `cursor: grabbing/copy/no-drop` here.
367 */
368
369 /* Shortcut overlay — small arrow badge in the BOTTOM-RIGHT corner
370 * of the icon visual on any tile that's a reference to an external
371 * entity (post, attachment, comment, user, term — anything that
372 * ISN'T a folder or a plugin shortcut). Mirrors the macOS / Windows
373 * convention of an arrow decoration on alias/shortcut icons.
374 *
375 * The geometry: tile is 88px wide with `padding: 8px 4px`. The icon
376 * visual is 48×48 centered horizontally, so the icon's bottom-right
377 * corner lands at roughly (68, 56) from the tile's top-left. The
378 * 18×18 arrow circle is anchored just inside that corner so it
379 * reads as part of the icon, not floating off in space.
380 *
381 * @since 0.8.0
382 */
383 /* Only real placements get the badge — i.e. tiles built by
384 * `buildTile(placement)` on the desktop / folder windows, which
385 * are the only surface where "this is a shortcut" actually means
386 * something. Tiles in My WordPress grids (and any future content
387 * surface) are the entity, not a shortcut to it, so the arrow
388 * would just be noise. `[data-placement-id]` is the canonical
389 * placement marker. */
390 .desktop-mode-file-tile[ data-placement-id ][ data-file-type="post" ]::after,
391 .desktop-mode-file-tile[ data-placement-id ][ data-file-type="attachment" ]::after,
392 .desktop-mode-file-tile[ data-placement-id ][ data-file-type="comment" ]::after,
393 .desktop-mode-file-tile[ data-placement-id ][ data-file-type="user" ]::after,
394 .desktop-mode-file-tile[ data-placement-id ][ data-file-type="term" ]::after {
395 content: "";
396 position: absolute;
397 right: 14px;
398 top: 44px;
399 width: 18px;
400 height: 18px;
401 border-radius: 50%;
402 background: var( --desktop-mode-tile-shortcut-bg, rgba( 0, 0, 0, 0.65 ) );
403 color: var( --desktop-mode-tile-shortcut-fg, #fff );
404 font-size: 11px;
405 line-height: 18px;
406 text-align: center;
407 box-shadow: var(
408 --desktop-mode-tile-shortcut-shadow,
409 0 1px 3px rgba( 0, 0, 0, 0.5 )
410 );
411 pointer-events: none;
412 }
413
414 /*
415 * Missing files (the underlying entity was deleted but the
416 * placement still exists) render the tile dimmed so the user
417 * can right-click → Remove without confusing it for an active
418 * file. Phase 6's per-placement permission re-check uses the
419 * same affordance for "you can't see this" placeholders.
420 */
421 .desktop-mode-file-tile--missing {
422 opacity: 0.5;
423 }
424
425 /**
426 * Access-gated tile — the recipient sees the icon (the owner
427 * shared the folder containing it) but lacks `can_read` on the
428 * underlying entity. Dim the tile, kill the pointer affordance,
429 * and overlay a lock badge so the state reads at a glance.
430 */
431 .desktop-mode-file-tile--access-gated {
432 opacity: 0.65;
433 cursor: not-allowed;
434 }
435 .desktop-mode-file-tile--access-gated .desktop-mode-file-tile__visual,
436 .desktop-mode-file-tile--access-gated .desktop-mode-file-tile__preview {
437 filter: grayscale( 0.6 );
438 }
439 .desktop-mode-file-tile--access-gated:hover,
440 .desktop-mode-file-tile--access-gated:focus-visible {
441 background: rgba( 0, 0, 0, 0.06 );
442 }
443 .desktop-mode-file-tile__lock {
444 position: absolute;
445 top: 4px;
446 inset-inline-end: 4px;
447 width: 20px;
448 height: 20px;
449 border-radius: 50%;
450 background: rgba( 0, 0, 0, 0.7 );
451 color: #fff;
452 font-size: 14px;
453 line-height: 20px;
454 text-align: center;
455 pointer-events: none;
456 box-shadow: 0 1px 3px rgba( 0, 0, 0, 0.5 );
457 }
458
459 /**
460 * Preview-pane empty state when a recipient selects a tile they
461 * can't open. Big lock ring + clear copy + secondary hint. Designed
462 * to read at a glance in the otherwise-busy preview column.
463 */
464 .desktop-mode-files__access-gated {
465 display: flex;
466 flex-direction: column;
467 align-items: center;
468 justify-content: center;
469 gap: 16px;
470 padding: 32px 24px;
471 min-height: 240px;
472 text-align: center;
473 color: var( --desktop-mode-fg, inherit );
474 }
475 .desktop-mode-files__access-gated-ring {
476 display: flex;
477 align-items: center;
478 justify-content: center;
479 width: 96px;
480 height: 96px;
481 border-radius: 50%;
482 background: linear-gradient(
483 145deg,
484 rgba( 214, 54, 56, 0.22 ),
485 rgba( 214, 54, 56, 0.08 )
486 );
487 border: 2px solid rgba( 214, 54, 56, 0.45 );
488 box-shadow:
489 inset 0 1px 0 rgba( 255, 255, 255, 0.08 ),
490 0 8px 24px rgba( 214, 54, 56, 0.18 );
491 }
492 .desktop-mode-files__access-gated-glyph {
493 font-size: 44px;
494 width: 44px;
495 height: 44px;
496 color: #ff8080;
497 filter: drop-shadow( 0 1px 2px rgba( 0, 0, 0, 0.4 ) );
498 }
499 .desktop-mode-files__access-gated-title {
500 margin: 0;
501 font-size: 17px;
502 font-weight: 600;
503 letter-spacing: 0.2px;
504 }
505 .desktop-mode-files__access-gated-sub {
506 margin: 0;
507 font-size: 13px;
508 line-height: 1.5;
509 max-width: 340px;
510 opacity: 0.85;
511 }
512 .desktop-mode-files__access-gated-hint {
513 margin: 0;
514 font-size: 12px;
515 line-height: 1.5;
516 max-width: 340px;
517 opacity: 0.6;
518 }
519
520 .desktop-mode-file-tile__visual {
521 display: inline-flex;
522 align-items: center;
523 justify-content: center;
524 width: 48px;
525 height: 48px;
526 font-size: 32px;
527 line-height: 1;
528 }
529
530 .desktop-mode-file-tile__icon {
531 display: inline-flex;
532 align-items: center;
533 justify-content: center;
534 width: 48px;
535 height: 48px;
536 font-size: 32px;
537 line-height: 1;
538 }
539
540 .desktop-mode-file-tile__preview {
541 width: 48px;
542 height: 48px;
543 object-fit: cover;
544 border-radius: 4px;
545 }
546
547 /* `<img>` defaults to `draggable=true`, which lets the browser
548 * start a native HTML5 image-drag from a pointerdown — that
549 * pre-empts the DragManager's pointer-event-driven tile rearrange
550 * and the tile silently refuses to move. The renderer also sets
551 * `draggable="false"` on every `<img>` it produces; this CSS rule
552 * covers anything injected later (plugin filters, decorators)
553 * without having to remember the attribute. */
554 .desktop-mode-file-tile img {
555 -webkit-user-drag: none;
556 user-select: none;
557 }
558
559 /*
560 * Wallpaper + tile context menus use `<wpd-context-menu>` and
561 * `<wpd-context-menu-option>` (Phase 4 / 0.9.0). Their host-level
562 * styles live inside the component shadow DOM — see
563 * `src/ui/components/wpd-context-menu/wpd-context-menu.styles.ts`.
564 * No project CSS rules needed here; the legacy `.desktop-mode-
565 * wallpaper-menu*` rules were removed when the components landed.
566 */
567
568 /*
569 * Folder native window — the body of `desktop-mode-folder-<id>`
570 * contains a `FilesLayer` rendering that folder's contents.
571 * We ensure the body has positioning context so absolutely-
572 * positioned tiles inside the layer anchor correctly.
573 */
574 .desktop-mode-folder-window {
575 position: relative;
576 min-height: 200px;
577 /* Same window background recipe as My WordPress — defaults to
578 * the standard window bg (#fff) and themable per-window. */
579 background: var(
580 --desktop-mode-folder-window-bg,
581 var( --desktop-mode-window-bg, #fff )
582 );
583 color: var( --desktop-mode-fg-on-light, #1d2327 );
584 display: flex;
585 flex-direction: column;
586 /* Light-on-dark context: the folder window opens inside a
587 * standard window chrome whose body is white-ish. Retint the
588 * tile color tokens here so dashicons + labels read against
589 * a light background. Plugins / themes can override these
590 * same variables at any scope to retint further. */
591 --desktop-mode-tile-fg: var( --desktop-mode-fg-on-light, #1d2327 );
592 --desktop-mode-tile-fg-muted: rgba( 0, 0, 0, 0.55 );
593 --desktop-mode-tile-hover-bg: rgba( 0, 0, 0, 0.06 );
594 /* Light-context label rendering — share the same recipe with
595 * every other light-bg tile surface (My WordPress, future
596 * windows). One set of tokens, no per-surface duplication. */
597 --desktop-mode-tile-label-shadow: none;
598 --desktop-mode-tile-label-weight: 500;
599 --desktop-mode-tile-label-smoothing: antialiased;
600 --desktop-mode-tile-label-color: var( --desktop-mode-fg-on-light, #1d2327 );
601 }
602
603 /* Folder window's bottom status bar — paint with the same light-
604 * context treatment My WordPress uses (transparent bg, soft border,
605 * muted dark text). Without this, the wallpaper-targeted defaults
606 * (`background: rgba(0,0,0,0.18); color: white`) would show as a
607 * dark band against the white window bg. */
608 .desktop-mode-folder-window .desktop-mode-folder-status-bar {
609 border-top: 1px solid var( --desktop-mode-border, #dcdcde );
610 background: transparent;
611 color: var( --desktop-mode-muted, #50575e );
612 }
613
614 .desktop-mode-folder-window
615 .desktop-mode-folder-status-bar
616 button.desktop-mode-folder-status-bar__segment:hover {
617 background: var( --desktop-mode-hover, rgba( 0, 0, 0, 0.06 ) );
618 }
619
620 /* Generic breadcrumb header — any surface that grows a navigation
621 * stack (folder window, My WordPress folder, future drill-down
622 * windows) renders one of these via `renderBreadcrumbs` from
623 * `src/desktop-files/breadcrumbs.ts`. Visual vocabulary: small
624 * icon-only Back button on the left, `›`-separated crumb buttons,
625 * current segment is bold + non-interactive.
626 *
627 * @since 0.8.0
628 */
629 .desktop-mode-breadcrumbs {
630 display: flex;
631 align-items: center;
632 gap: 8px;
633 padding: 6px 10px;
634 border-bottom: 1px solid var( --desktop-mode-border, #dcdcde );
635 background: transparent;
636 flex: 0 0 auto;
637 }
638
639 .desktop-mode-breadcrumbs__back {
640 display: inline-flex;
641 align-items: center;
642 justify-content: center;
643 width: 26px;
644 height: 26px;
645 padding: 0;
646 border: 0;
647 background: transparent;
648 border-radius: 4px;
649 cursor: pointer;
650 color: inherit;
651 flex: 0 0 auto;
652 }
653
654 .desktop-mode-breadcrumbs__back:hover:not(:disabled),
655 .desktop-mode-breadcrumbs__back:focus-visible:not(:disabled) {
656 background: var( --desktop-mode-hover, rgba( 0, 0, 0, 0.06 ) );
657 outline: none;
658 }
659
660 .desktop-mode-breadcrumbs__back:disabled {
661 opacity: 0.35;
662 cursor: default;
663 }
664
665 .desktop-mode-breadcrumbs__back .dashicons {
666 font-size: 18px;
667 width: 18px;
668 height: 18px;
669 line-height: 1;
670 }
671
672 .desktop-mode-breadcrumbs__crumbs {
673 display: flex;
674 align-items: center;
675 gap: 6px;
676 font-size: 13px;
677 min-width: 0;
678 overflow: hidden;
679 text-overflow: ellipsis;
680 white-space: nowrap;
681 }
682
683 .desktop-mode-breadcrumbs__crumb {
684 border: 0;
685 background: transparent;
686 color: var( --desktop-mode-link, #2271b1 );
687 cursor: pointer;
688 padding: 2px 4px;
689 border-radius: 4px;
690 font: inherit;
691 }
692
693 .desktop-mode-breadcrumbs__crumb:hover {
694 background: var( --desktop-mode-hover, rgba( 0, 0, 0, 0.06 ) );
695 }
696
697 .desktop-mode-breadcrumbs__crumb--current {
698 color: var( --desktop-mode-fg-on-light, #1d2327 );
699 cursor: default;
700 font-weight: 600;
701 }
702
703 .desktop-mode-breadcrumbs__crumb--current:hover {
704 background: transparent;
705 }
706
707 .desktop-mode-breadcrumbs__sep {
708 color: var( --desktop-mode-muted, #787c82 );
709 }
710
711 .desktop-mode-folder-window__layer {
712 position: relative;
713 flex: 1;
714 min-height: 0;
715 overflow: auto;
716 }
717
718 /* Two-pane shell — left: tile canvas, right: preview pane that
719 * reflects the currently-selected tile. Same model as the My
720 * WordPress two-pane view so the UX is unified.
721 *
722 * @since 0.8.0 */
723 .desktop-mode-folder-window__split {
724 display: grid;
725 grid-template-columns: minmax( 240px, 60% ) minmax( 0, 1fr );
726 flex: 1 1 auto;
727 min-height: 0;
728 }
729
730 .desktop-mode-folder-window__preview {
731 overflow-y: auto;
732 min-width: 0;
733 background: var( --desktop-mode-window-bg, #fff );
734 border-inline-start: 1px solid var( --desktop-mode-border, #dcdcde );
735 color: var( --desktop-mode-fg-on-light, #1d2327 );
736 }
737
738 /* Selected file tile — light-blue accent on the wallpaper, soft
739 * accent inside light-context windows. Variable-driven so plugins
740 * can retune. */
741 .desktop-mode-file-tile--selected,
742 .desktop-mode-file-tile--selected:hover {
743 background: var(
744 --desktop-mode-tile-selected-bg,
745 rgba( 34, 113, 177, 0.18 )
746 );
747 box-shadow: inset 0 0 0 1px
748 var( --desktop-mode-tile-focus-ring, var( --wp-admin-theme-color, #2271b1 ) );
749 }
750
751 /* Inside a folder window the selected highlight should read on
752 * white. */
753 .desktop-mode-folder-window {
754 --desktop-mode-tile-selected-bg: rgba( 34, 113, 177, 0.12 );
755 }
756
757 .desktop-mode-folder-status-bar {
758 display: flex;
759 justify-content: space-between;
760 align-items: center;
761 gap: 12px;
762 padding: 6px 12px;
763 border-top: 1px solid rgba( 255, 255, 255, 0.08 );
764 background: rgba( 0, 0, 0, 0.18 );
765 color: var( --desktop-mode-fg-muted, rgba( 255, 255, 255, 0.7 ) );
766 font-size: 12px;
767 flex-shrink: 0;
768 }
769
770 .desktop-mode-folder-status-bar__cluster {
771 display: flex;
772 align-items: center;
773 gap: 12px;
774 }
775
776 .desktop-mode-folder-status-bar__segment {
777 display: inline-flex;
778 align-items: center;
779 gap: 4px;
780 padding: 0;
781 border: 0;
782 background: transparent;
783 color: inherit;
784 font: inherit;
785 }
786
787 button.desktop-mode-folder-status-bar__segment {
788 cursor: pointer;
789 border-radius: 4px;
790 padding: 2px 6px;
791 }
792
793 button.desktop-mode-folder-status-bar__segment:hover {
794 background: rgba( 255, 255, 255, 0.1 );
795 }
796
797 .desktop-mode-folder-status-bar__icon {
798 font-size: 14px;
799 }
800
801 /*
802 * File Associations OS Settings tab (Phase 5). One row per file
803 * type: label on the left, a `<wpd-select>` of compatible
804 * openers on the right. Color tokens use the OS-Settings-canonical
805 * `--wpd-text-muted` so the tab inherits theme colors instead of
806 * falling back to white-on-light invisibility.
807 */
808 .desktop-mode-file-associations__intro {
809 display: block;
810 margin: 0 0 16px;
811 color: var( --wpd-text-muted, #50575e );
812 font-size: 13px;
813 line-height: 1.5;
814 }
815
816 .desktop-mode-file-associations__list {
817 display: flex;
818 flex-direction: column;
819 gap: 8px;
820 }
821
822 .desktop-mode-file-associations__row {
823 display: flex;
824 align-items: center;
825 justify-content: space-between;
826 gap: 16px;
827 padding: 8px 12px;
828 background: rgba( 0, 0, 0, 0.04 );
829 border-radius: 6px;
830 }
831
832 .desktop-mode-file-associations__label {
833 font-weight: 500;
834 flex: 1;
835 }
836
837 .desktop-mode-file-associations__select {
838 min-width: 220px;
839 }
840
841 .desktop-mode-file-associations__none,
842 .desktop-mode-file-associations__empty {
843 color: var( --wpd-text-muted, #50575e );
844 font-size: 12px;
845 }
846
847 /*
848 * "New folder" inline dialog. Centered modal with a focused
849 * input — replaces the placeholder window.prompt. Plain HTML
850 * controls so it works as a building block before richer
851 * dialog primitives land.
852 */
853 .desktop-mode-create-folder-dialog__overlay {
854 position: fixed;
855 inset: 0;
856 background: rgba( 0, 0, 0, 0.45 );
857 backdrop-filter: blur( 2px );
858 display: flex;
859 align-items: center;
860 justify-content: center;
861 z-index: 10000;
862 }
863
864 .desktop-mode-create-folder-dialog {
865 width: min( 420px, 92vw );
866 background: var( --desktop-mode-bg, #1d2327 );
867 color: var( --desktop-mode-fg, #fff );
868 border: 1px solid rgba( 255, 255, 255, 0.08 );
869 border-radius: 10px;
870 box-shadow: 0 20px 50px rgba( 0, 0, 0, 0.6 );
871 padding: 20px 22px 18px;
872 display: flex;
873 flex-direction: column;
874 gap: 10px;
875 }
876
877 .desktop-mode-create-folder-dialog__title {
878 margin: 0 0 4px;
879 font-size: 16px;
880 font-weight: 600;
881 }
882
883 .desktop-mode-create-folder-dialog__label {
884 font-size: 12px;
885 color: var( --desktop-mode-fg-muted, rgba( 255, 255, 255, 0.7 ) );
886 }
887
888 .desktop-mode-create-folder-dialog__input {
889 width: 100%;
890 padding: 8px 10px;
891 background: rgba( 255, 255, 255, 0.06 );
892 color: inherit;
893 border: 1px solid rgba( 255, 255, 255, 0.16 );
894 border-radius: 6px;
895 font-size: 14px;
896 outline: none;
897 box-sizing: border-box;
898 }
899
900 .desktop-mode-create-folder-dialog__input:focus {
901 border-color: var( --wp-admin-theme-color, #2271b1 );
902 box-shadow: 0 0 0 2px rgba( 34, 113, 177, 0.4 );
903 }
904
905 .desktop-mode-create-folder-dialog__error {
906 margin: 0;
907 color: #ff8a8a;
908 font-size: 12px;
909 }
910
911 .desktop-mode-create-folder-dialog__actions {
912 display: flex;
913 justify-content: flex-end;
914 gap: 8px;
915 margin-top: 6px;
916 }
917
918 .desktop-mode-create-folder-dialog__btn {
919 border: 0;
920 border-radius: 6px;
921 padding: 8px 14px;
922 font-size: 13px;
923 cursor: pointer;
924 font-weight: 500;
925 }
926
927 .desktop-mode-create-folder-dialog__btn--secondary {
928 background: rgba( 255, 255, 255, 0.08 );
929 color: inherit;
930 }
931
932 .desktop-mode-create-folder-dialog__btn--secondary:hover {
933 background: rgba( 255, 255, 255, 0.14 );
934 }
935
936 .desktop-mode-create-folder-dialog__btn--primary {
937 background: var( --wp-admin-theme-color, #2271b1 );
938 color: #fff;
939 }
940
941 .desktop-mode-create-folder-dialog__btn--primary:hover {
942 filter: brightness( 1.08 );
943 }
944
945 .desktop-mode-create-folder-dialog__btn:disabled {
946 opacity: 0.55;
947 cursor: not-allowed;
948 }
949
950 .desktop-mode-create-folder-dialog--busy .desktop-mode-create-folder-dialog__input {
951 opacity: 0.7;
952 }
953
954 /*
955 * Single label rule for every tile, regardless of host surface.
956 * Visual properties read from `--desktop-mode-tile-label-*`
957 * tokens — defined in `variables.css` for the dark wallpaper and
958 * rebound by any light-context scope (folder window, My
959 * WordPress, …) so all surfaces share one source of truth.
960 */
961 .desktop-mode-file-tile__label {
962 font-size: 12px;
963 line-height: 1.2;
964 max-width: 84px;
965 text-align: center;
966 overflow: hidden;
967 text-overflow: ellipsis;
968 display: -webkit-box;
969 -webkit-line-clamp: 2;
970 -webkit-box-orient: vertical;
971 color: var( --desktop-mode-tile-label-color, var( --desktop-mode-tile-fg, #fff ) );
972 font-weight: var( --desktop-mode-tile-label-weight, 400 );
973 text-shadow: var( --desktop-mode-tile-label-shadow, 0 1px 2px rgba( 0, 0, 0, 0.5 ) );
974 -webkit-font-smoothing: var( --desktop-mode-tile-label-smoothing, auto );
975 -moz-osx-font-smoothing: grayscale;
976 }
977
978 /*
979 * DragManager ghost element (the visual that follows the cursor
980 * during a drag). Cloned from the source by `mountGhost()` and
981 * pinned to the body with `position: fixed` + transform-based
982 * movement. `pointer-events: none` ensures the ghost itself
983 * never obscures the drop targets underneath during hit-testing.
984 *
985 * The accept / reject classes are toggled on each hover transition
986 * so the cursor matches the drop semantics: `copy` over an
987 * accepting target, `no-drop` over rejecting (or no) target.
988 *
989 * @since 0.18.0
990 */
991 .desktop-mode-drag-ghost {
992 box-shadow: 0 6px 24px rgba( 0, 0, 0, 0.45 );
993 border-radius: 6px;
994 opacity: 0.95;
995 transition: none;
996 }
997
998 .desktop-mode-drag-ghost--accept {
999 cursor: copy;
1000 }
1001
1002 .desktop-mode-drag-ghost--reject {
1003 cursor: no-drop;
1004 }
1005
1006 /* ============================================================ *
1007 * Upload progress HUD (since 0.31.0)
1008 *
1009 * Pinned floating panel that surfaces the OS-file-drop manager's
1010 * in-flight uploads. One row per file with a `<wpd-progress-bar>`,
1011 * a filename + status, and a Cancel/Dismiss action. The panel
1012 * hides itself when there are no rows; it's purely reactive to
1013 * the four upload-lifecycle hooks.
1014 * ============================================================ */
1015
1016 .desktop-mode-upload-hud {
1017 position: fixed;
1018 inset-block-end: 16px;
1019 inset-inline-end: 16px;
1020 width: 320px;
1021 max-width: calc( 100vw - 32px );
1022 background: var( --desktop-mode-panel-bg, #fff );
1023 color: var( --desktop-mode-fg-on-light, #1d2327 );
1024 border: 1px solid var( --desktop-mode-border, #dcdcde );
1025 border-radius: 10px;
1026 box-shadow: 0 8px 28px rgba( 0, 0, 0, 0.18 );
1027 z-index: 99998;
1028 font: inherit;
1029 pointer-events: auto;
1030 overflow: hidden;
1031 }
1032
1033 .desktop-mode-upload-hud[ hidden ] {
1034 display: none;
1035 }
1036
1037 .desktop-mode-upload-hud__header {
1038 display: flex;
1039 align-items: center;
1040 justify-content: space-between;
1041 gap: 8px;
1042 padding: 8px 12px;
1043 background: var( --desktop-mode-panel-header-bg, rgba( 0, 0, 0, 0.04 ) );
1044 border-block-end: 1px solid var( --desktop-mode-border, #dcdcde );
1045 font-weight: 600;
1046 font-size: 12px;
1047 }
1048
1049 .desktop-mode-upload-hud__title {
1050 min-width: 0;
1051 overflow: hidden;
1052 text-overflow: ellipsis;
1053 white-space: nowrap;
1054 }
1055
1056 .desktop-mode-upload-hud__close {
1057 border: 0;
1058 background: transparent;
1059 color: inherit;
1060 font-size: 16px;
1061 line-height: 1;
1062 width: 22px;
1063 height: 22px;
1064 border-radius: 4px;
1065 cursor: pointer;
1066 display: inline-flex;
1067 align-items: center;
1068 justify-content: center;
1069 }
1070 .desktop-mode-upload-hud__close:hover,
1071 .desktop-mode-upload-hud__close:focus-visible {
1072 background: rgba( 0, 0, 0, 0.08 );
1073 outline: none;
1074 }
1075
1076 .desktop-mode-upload-hud__list {
1077 display: flex;
1078 flex-direction: column;
1079 gap: 10px;
1080 padding: 10px 12px;
1081 max-height: 50vh;
1082 overflow-y: auto;
1083 }
1084
1085 .desktop-mode-upload-hud__row {
1086 display: grid;
1087 grid-template-columns: 1fr auto;
1088 grid-template-areas:
1089 'meta actions'
1090 'bar bar';
1091 gap: 6px 8px;
1092 align-items: center;
1093 font-size: 12px;
1094 }
1095
1096 .desktop-mode-upload-hud__meta {
1097 grid-area: meta;
1098 min-width: 0;
1099 display: flex;
1100 flex-direction: column;
1101 gap: 2px;
1102 }
1103
1104 .desktop-mode-upload-hud__name {
1105 overflow: hidden;
1106 text-overflow: ellipsis;
1107 white-space: nowrap;
1108 font-weight: 500;
1109 }
1110
1111 .desktop-mode-upload-hud__status {
1112 font-size: 11px;
1113 opacity: 0.75;
1114 font-variant-numeric: tabular-nums;
1115 }
1116
1117 .desktop-mode-upload-hud__row wpd-progress-bar {
1118 grid-area: bar;
1119 }
1120
1121 .desktop-mode-upload-hud__actions {
1122 grid-area: actions;
1123 display: inline-flex;
1124 gap: 4px;
1125 }
1126