PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 0.9.5
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v0.9.5
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 / os-settings.css

os-settings.css in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 0.9.5, at assets/css/os-settings.css

1,251 lines 32.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Desktop Mode — OS Settings native panel.
3 *
4 * The preferences panel rendered inside the OS Settings native
5 * window: wallpaper swatches + editor slot, accent colors, dock size,
6 * and the "Or use your own image" uploader + Media Library grid.
7 * Currently shipped alongside the window-chrome stylesheet because
8 * historically the rules lived there; marked for a future move to its
9 * own enqueue handle once the coupling to the shell CSS is removed.
10 *
11 * @since 0.5.0
12 */
13 .desktop-mode-os-settings {
14 font-size: 13px;
15 line-height: 1.5;
16 }
17
18 .desktop-mode-os-settings__intro {
19 margin: 0 0 16px;
20 color: #50575e;
21 }
22
23 /*
24 * Card styling for each OS-Settings section. `<wpd-section>`'s
25 * shadow DOM handles the heading + description; the outer card
26 * (padding, background, border) lives here because it's a
27 * per-surface visual choice, not a property of the component
28 * itself. Targets the tag so shadow-DOM internals stay private.
29 */
30 .desktop-mode-os-settings wpd-section {
31 display: block;
32 margin: 0 0 20px;
33 padding: 16px 16px 18px;
34 background: #f6f7f7;
35 border: 1px solid #dcdcde;
36 border-radius: 8px;
37 }
38
39 /*
40 * Generic grid. Auto-fill + minmax gives us a truly responsive
41 * layout — the panel reflows columns as OS Settings is resized,
42 * rather than locking to a fixed 3-across.
43 */
44 .desktop-mode-os-settings__grid {
45 display: grid;
46 grid-template-columns: repeat( auto-fill, minmax( 140px, 1fr ) );
47 gap: 10px;
48 }
49
50 /*
51 * Wallpaper swatches — each is a `<wpd-swatch variant="wallpaper">`
52 * with its own shadow-DOM button + aspect ratio + selected state.
53 * The only outer knobs we need are: wider minmax for wallpapers
54 * (so each tile shows meaningful preview area), and the 16:9
55 * aspect ratio override. Everything else lives in the component.
56 */
57 .desktop-mode-os-settings__grid--wallpapers {
58 grid-template-columns: repeat( auto-fill, minmax( 160px, 1fr ) );
59 }
60
61 /*
62 * Wallpaper preset label — sits above the gradient/image preview.
63 * Presets span the full tonal range (graphite dark, mono near-
64 * black, aurora blue, sunset hot pink) so neither pure white nor
65 * pure black text is universally readable. A frosted glass chip
66 * behind the text gives the label its own local contrast that's
67 * legible on every preset and respects the tile's visual language
68 * (rounded pill shape matches the selected-state corners).
69 */
70 .desktop-mode-os-settings__swatch-label {
71 display: inline-block;
72 /* Sit above the live-preview overlay (z-index 0) — the caption
73 * must stay readable over a moving canvas. */
74 position: relative;
75 z-index: 1;
76 padding: 3px 10px;
77 font-size: 11px;
78 font-weight: 600;
79 color: #fff;
80 background: rgba(0, 0, 0, 0.45);
81 backdrop-filter: blur(8px) saturate(160%);
82 -webkit-backdrop-filter: blur(8px) saturate(160%);
83 border-radius: 999px;
84 text-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
85 letter-spacing: 0.01em;
86 /* Stop the chip from hugging the swatch edge — the 6 8 padding
87 * on the parent already gives a gap, but this keeps things
88 * consistent if the parent padding is ever tuned. */
89 pointer-events: none;
90 }
91
92 /*
93 * Live wallpaper preview — an overlay div slotted into a wallpaper
94 * `<wpd-swatch>` by the preview manager (see
95 * settings/sections/wallpaper-previews.ts). Fills the tile button
96 * (which is position: relative in the component's shadow styles) and
97 * paints between the CSS `preview` background and the label chip.
98 * pointer-events: none so the tile button keeps receiving the click.
99 */
100 .desktop-mode-os-settings__wallpaper-live-preview {
101 position: absolute;
102 inset: 0;
103 z-index: 0;
104 overflow: hidden;
105 pointer-events: none;
106 /* Match the swatch button's rounding so canvas corners don't
107 * poke out of the tile. */
108 border-radius: 8px;
109 }
110
111 .desktop-mode-os-settings__wallpaper-live-preview canvas {
112 display: block;
113 width: 100%;
114 height: 100%;
115 }
116
117 /* Accent swatches now render as `<wpd-swatch>` via `<wpd-swatch-grid>`.
118 * The old `__swatch--accent` / `__swatch-label` / `__segmented` /
119 * `__segment` rules moved into those component shadow-DOM
120 * stylesheets; nothing at host level needs to override them. */
121
122 .desktop-mode-os-settings__footer {
123 display: flex;
124 justify-content: flex-end;
125 margin-top: 12px;
126 }
127
128 /* Top-level tab strip separating Appearance from AI Settings.
129 * Don't add a `display` rule for `wpd-tabpanel` here — the component's
130 * shadow-DOM `:host([hidden]) { display: none }` has lower specificity
131 * than any outer descendant selector, and overriding it from outside
132 * breaks the auto-swap (hidden panels stay visible). The component's
133 * own `:host { display: block }` already handles the shown state. */
134 .desktop-mode-os-settings wpd-tabs {
135 display: block;
136 }
137
138 /* `__reset` now renders as `<wpd-button variant="ghost">`. */
139
140 /* ---------------------------------------------------------------
141 * Custom gradient editor — color inputs + angle slider.
142 *
143 * Toggled by `data-expanded` on the outer wrapper. Uses the modern
144 * grid-template-rows 0fr ↔ 1fr trick so we can animate to natural
145 * content height without hard-coding a max-height. The inner wrapper
146 * owns the visible chrome (padding, border, background) and is
147 * clipped during the transition by overflow: hidden; the outer
148 * wrapper animates the row track + spacing + opacity.
149 *
150 * Supported in all evergreen browsers (Chrome 117+, Safari 17.2+,
151 * Firefox 128+). On older engines the transition is simply skipped —
152 * the editor still appears, just without the glide.
153 * --------------------------------------------------------------- */
154 /*
155 * Editor slot — the per-wallpaper panel that slides in below the
156 * swatch grid when a wallpaper with `renderEditor` is selected.
157 * Collapses via the grid-template-rows 0fr → 1fr trick; TS drives
158 * the `[data-expanded]` attribute on the slot wrapper.
159 */
160 .desktop-mode-os-settings__editor-slot {
161 display: grid;
162 grid-template-rows: 0fr;
163 margin-top: 0;
164 opacity: 0;
165 transition:
166 grid-template-rows 0.25s cubic-bezier(0.2, 0, 0.2, 1),
167 margin-top 0.25s cubic-bezier(0.2, 0, 0.2, 1),
168 opacity 0.2s ease;
169 }
170
171 .desktop-mode-os-settings__editor-slot[data-expanded="true"] {
172 grid-template-rows: 1fr;
173 margin-top: 12px;
174 opacity: 1;
175 }
176
177 .desktop-mode-os-settings__editor-slot-inner {
178 overflow: hidden;
179 min-height: 0;
180 padding: 12px;
181 background: #fff;
182 border: 1px solid #dcdcde;
183 border-radius: 6px;
184 }
185
186 /*
187 * Wallpaper description slot — a card that slides in below the swatch
188 * grid describing the ACTIVE wallpaper (its story, where its data comes
189 * from). Same 0fr → 1fr collapse as the editor slot; TS drives
190 * `[data-expanded]` from `syncWallpaperDescription()`.
191 */
192 .desktop-mode-os-settings__wallpaper-description-slot {
193 display: grid;
194 grid-template-rows: 0fr;
195 margin-top: 0;
196 opacity: 0;
197 transition:
198 grid-template-rows 0.25s cubic-bezier(0.2, 0, 0.2, 1),
199 margin-top 0.25s cubic-bezier(0.2, 0, 0.2, 1),
200 opacity 0.2s ease;
201 }
202
203 .desktop-mode-os-settings__wallpaper-description-slot[data-expanded="true"] {
204 grid-template-rows: 1fr;
205 margin-top: 12px;
206 opacity: 1;
207 }
208
209 .desktop-mode-os-settings__wallpaper-description-slot-inner {
210 overflow: hidden;
211 min-height: 0;
212 }
213
214 .desktop-mode-os-settings__wallpaper-description {
215 padding: 12px 14px;
216 background: color-mix(in srgb, var(--desktop-mode-accent, #2271b1) 6%, #fff);
217 border: 1px solid #dcdcde;
218 border-inline-start: 3px solid var(--desktop-mode-accent, #2271b1);
219 border-radius: 6px;
220 }
221
222 /* Glyph sits inline, right before the wallpaper's name. */
223 .desktop-mode-os-settings__wallpaper-description-header {
224 display: flex;
225 align-items: center;
226 gap: 7px;
227 margin-bottom: 4px;
228 }
229
230 .desktop-mode-os-settings__wallpaper-description-icon {
231 color: var(--desktop-mode-accent, #2271b1);
232 flex-shrink: 0;
233 }
234
235 .desktop-mode-os-settings__wallpaper-description-header strong {
236 font-size: 13px;
237 color: #1d2327;
238 }
239
240 .desktop-mode-os-settings__wallpaper-description p {
241 margin: 0;
242 font-size: 12px;
243 line-height: 1.55;
244 color: #50575e;
245 }
246
247 /*
248 * Wallpaper config slot — hosts the "Wallpaper settings" button when
249 * the ACTIVE wallpaper ships a `renderConfig` dialog. Same 0fr → 1fr
250 * collapse as the slots above; TS drives `[data-expanded]` from
251 * `syncWallpaperConfigButton()`.
252 */
253 .desktop-mode-os-settings__wallpaper-config-slot {
254 display: grid;
255 grid-template-rows: 0fr;
256 margin-top: 0;
257 opacity: 0;
258 transition:
259 grid-template-rows 0.25s cubic-bezier(0.2, 0, 0.2, 1),
260 margin-top 0.25s cubic-bezier(0.2, 0, 0.2, 1),
261 opacity 0.2s ease;
262 }
263
264 .desktop-mode-os-settings__wallpaper-config-slot[data-expanded="true"] {
265 grid-template-rows: 1fr;
266 margin-top: 12px;
267 opacity: 1;
268 }
269
270 .desktop-mode-os-settings__wallpaper-config-slot-inner {
271 overflow: hidden;
272 min-height: 0;
273 }
274
275 .desktop-mode-os-settings__wallpaper-config wpd-icon {
276 margin-inline-end: 4px;
277 }
278
279 /*
280 * Wallpaper config dialog body — the container `renderConfig` renders
281 * into inside the `<wpd-modal>`. A simple vertical rhythm; each
282 * `<wpd-*>` field owns its own label / control styling. The critical
283 * column rules are ALSO inlined by the dialog opener (long-lived
284 * sessions can lazy-load the dialog with a stylesheet that predates
285 * this block); keep the two in sync.
286 */
287 .desktop-mode-os-settings__wallpaper-config-form {
288 display: flex;
289 flex-direction: column;
290 gap: 14px;
291 min-width: 0;
292 }
293
294 /* Bare buttons in a config form (Reset to defaults and friends) are
295 * actions, not fields — don't stretch them to the column width. */
296 .desktop-mode-os-settings__wallpaper-config-form > wpd-button {
297 align-self: flex-start;
298 }
299
300 /*
301 * Custom-gradient editor fills the inner slot. Kept as a separate
302 * class (vs. scoping to __editor-slot-inner) so a future plugin
303 * renderEditor can opt out of these per-control rules.
304 */
305 .desktop-mode-os-settings__gradient-editor-inner {
306 display: flex;
307 flex-direction: column;
308 gap: 12px;
309 }
310
311 /*
312 * Gradient editor row — laid out as a flex of `<wpd-color-field>` +
313 * `<wpd-range-field>`. Each field owns its own label / control /
314 * readout styling internally; only spacing between fields lives here.
315 */
316 .desktop-mode-os-settings__gradient-row {
317 display: flex;
318 gap: 16px;
319 }
320
321 .desktop-mode-os-settings__gradient-row wpd-color-field {
322 flex: 1;
323 }
324
325 .desktop-mode-os-settings__gradient-editor-inner wpd-range-field {
326 margin-top: 12px;
327 }
328
329 /* ---------------------------------------------------------------
330 * Image uploader tile.
331 *
332 * Empty state: dashed border, "+" glyph, drop-and-click zone.
333 * Filled state: thumbnail preview that doubles as a wallpaper swatch.
334 * --------------------------------------------------------------- */
335 .desktop-mode-os-settings__uploader {
336 margin-top: 16px;
337 }
338
339 .desktop-mode-os-settings__uploader-heading {
340 margin: 0 0 6px;
341 font-size: 12px;
342 font-weight: 600;
343 color: #50575e;
344 text-transform: uppercase;
345 letter-spacing: 0.04em;
346 }
347
348 .desktop-mode-os-settings__file-input {
349 display: none;
350 }
351
352 .desktop-mode-os-settings__upload-tile {
353 position: relative;
354 display: flex;
355 align-items: center;
356 justify-content: center;
357 min-height: 120px;
358 padding: 16px;
359 background: #fff;
360 background-size: cover;
361 background-position: center;
362 border: 2px dashed #c3c4c7;
363 border-radius: 8px;
364 color: #50575e;
365 cursor: pointer;
366 text-align: center;
367 transition: border-color 0.15s ease, background-color 0.15s ease, transform 0.15s ease;
368 }
369
370 .desktop-mode-os-settings__upload-tile:hover {
371 border-color: var(--wp-admin-theme-color, #2271b1);
372 color: var(--wp-admin-theme-color, #2271b1);
373 }
374
375 .desktop-mode-os-settings__upload-tile:focus-within,
376 .desktop-mode-os-settings__upload-tile:focus-visible {
377 outline: 2px solid var(--wp-admin-theme-color, #2271b1);
378 outline-offset: 2px;
379 }
380
381 .desktop-mode-os-settings__upload-tile--dragover {
382 border-style: solid;
383 border-color: var(--wp-admin-theme-color, #2271b1);
384 background-color: rgba(34, 113, 177, 0.06);
385 transform: scale(1.01);
386 }
387
388 .desktop-mode-os-settings__upload-tile--busy {
389 cursor: progress;
390 opacity: 0.8;
391 }
392
393 /* Filled state — thumbnail is the whole tile; selection uses the same
394 * aria-pressed pattern as the other swatches. */
395 .desktop-mode-os-settings__upload-tile--filled {
396 border-style: solid;
397 border-color: transparent;
398 padding: 0;
399 min-height: 140px;
400 color: transparent;
401 }
402
403 .desktop-mode-os-settings__upload-tile--filled:hover {
404 color: transparent;
405 }
406
407 .desktop-mode-os-settings__upload-tile[aria-pressed="true"] {
408 border-color: var(--wp-admin-theme-color, #2271b1);
409 box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9) inset;
410 }
411
412 .desktop-mode-os-settings__upload-inner {
413 display: flex;
414 flex-direction: column;
415 align-items: center;
416 gap: 6px;
417 pointer-events: none;
418 }
419
420 .desktop-mode-os-settings__upload-plus {
421 display: inline-flex;
422 align-items: center;
423 justify-content: center;
424 width: 32px;
425 height: 32px;
426 border-radius: 50%;
427 background: rgba(0, 0, 0, 0.05);
428 font-size: 22px;
429 font-weight: 300;
430 line-height: 1;
431 color: inherit;
432 }
433
434 .desktop-mode-os-settings__upload-tile:hover .desktop-mode-os-settings__upload-plus {
435 background: rgba(34, 113, 177, 0.12);
436 }
437
438 .desktop-mode-os-settings__upload-prompt {
439 font-size: 13px;
440 font-weight: 500;
441 }
442
443 .desktop-mode-os-settings__upload-hint {
444 font-size: 11px;
445 color: #8c8f94;
446 }
447
448 .desktop-mode-os-settings__upload-status {
449 font-size: 13px;
450 font-weight: 500;
451 color: #50575e;
452 }
453
454 .desktop-mode-os-settings__upload-error {
455 position: absolute;
456 inset-inline: 10px;
457 bottom: 10px;
458 padding: 6px 10px;
459 background: #d63638;
460 color: #fff;
461 border-radius: 4px;
462 font-size: 11px;
463 line-height: 1.3;
464 text-align: start;
465 box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
466 }
467
468 /*
469 * `__upload-remove` renders as `<wpd-button variant="danger">`
470 * with component-owned styling. Only the positioning on the
471 * filled tile is caller-specific — stays here.
472 */
473 .desktop-mode-os-settings__upload-remove {
474 position: absolute;
475 top: 8px;
476 inset-inline-end: 8px;
477 }
478
479 /* Tab strip moved into `<wpd-tabs>` + `<wpd-tab>`
480 * (src/ui/components/wpd-tabs/). Only the surrounding pane keeps
481 * a host-level rule — it's an OS-Settings layout choice, not
482 * component-intrinsic. */
483 .desktop-mode-os-settings__tab-pane {
484 min-height: 140px;
485 }
486
487 /* ---------------------------------------------------------------
488 * Media Library picker.
489 *
490 * Toolbar (search + HD toggle), responsive thumbnail grid, footer
491 * with count + Load more button.
492 * --------------------------------------------------------------- */
493 .desktop-mode-os-settings__library-toolbar {
494 display: flex;
495 align-items: center;
496 gap: 12px;
497 margin-bottom: 10px;
498 flex-wrap: wrap;
499 }
500
501 .desktop-mode-os-settings__library-search {
502 flex: 1;
503 min-width: 180px;
504 padding: 6px 10px;
505 border: 1px solid #dcdcde;
506 border-radius: 4px;
507 background: #fff;
508 font: inherit;
509 font-size: 12px;
510 color: #1d2327;
511 }
512
513 .desktop-mode-os-settings__library-search:focus {
514 border-color: var(--wp-admin-theme-color, #2271b1);
515 box-shadow: 0 0 0 1px var(--wp-admin-theme-color, #2271b1);
516 outline: none;
517 }
518
519 /* `__library-hd` now renders as `<wpd-checkbox-label>`. */
520
521 .desktop-mode-os-settings__library-grid {
522 display: grid;
523 grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
524 gap: 8px;
525 min-height: 140px;
526 }
527
528 .desktop-mode-os-settings__library-tile {
529 position: relative;
530 aspect-ratio: 16 / 10;
531 padding: 0;
532 border: 2px solid transparent;
533 border-radius: 6px;
534 background-color: #eef0f1;
535 background-size: cover;
536 background-position: center;
537 background-repeat: no-repeat;
538 cursor: pointer;
539 overflow: hidden;
540 transition: border-color 0.15s ease, transform 0.15s ease;
541 }
542
543 .desktop-mode-os-settings__library-tile:hover {
544 transform: translateY(-1px);
545 border-color: rgba(34, 113, 177, 0.4);
546 }
547
548 .desktop-mode-os-settings__library-tile:focus-visible {
549 outline: 2px solid var(--wp-admin-theme-color, #2271b1);
550 outline-offset: 2px;
551 }
552
553 .desktop-mode-os-settings__library-tile--selected,
554 .desktop-mode-os-settings__library-tile[aria-pressed="true"] {
555 border-color: var(--wp-admin-theme-color, #2271b1);
556 box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.9) inset;
557 }
558
559 /* Dimension badge in the corner — readable over any thumbnail. */
560 .desktop-mode-os-settings__library-tile-dims {
561 position: absolute;
562 bottom: 4px;
563 inset-inline-end: 4px;
564 padding: 2px 6px;
565 background: rgba(0, 0, 0, 0.6);
566 color: #fff;
567 font-size: 10px;
568 font-weight: 500;
569 line-height: 1.3;
570 border-radius: 3px;
571 letter-spacing: 0.02em;
572 font-variant-numeric: tabular-nums;
573 pointer-events: none;
574 }
575
576 /* Skeleton tiles during the first-page fetch. Animated subtle pulse
577 * so the user perceives progress without us needing a spinner. */
578 .desktop-mode-os-settings__library-tile--skeleton {
579 cursor: default;
580 background: linear-gradient(
581 90deg,
582 #eef0f1 0%,
583 #dcdcde 50%,
584 #eef0f1 100%
585 );
586 background-size: 200% 100%;
587 animation: desktop-mode-library-shimmer 1.1s ease-in-out infinite;
588 }
589
590 .desktop-mode-os-settings__library-tile--skeleton:hover {
591 transform: none;
592 border-color: transparent;
593 }
594
595 @keyframes desktop-mode-library-shimmer {
596 from {
597 background-position: 200% 0;
598 }
599 to {
600 background-position: -200% 0;
601 }
602 }
603
604 .desktop-mode-os-settings__library-empty,
605 .desktop-mode-os-settings__library-error {
606 grid-column: 1 / -1;
607 margin: 0;
608 padding: 24px 16px;
609 text-align: center;
610 color: #646970;
611 font-size: 12px;
612 }
613
614 .desktop-mode-os-settings__library-error {
615 color: #b32d2e;
616 }
617
618 .desktop-mode-os-settings__library-footer {
619 display: flex;
620 align-items: center;
621 justify-content: space-between;
622 margin-top: 10px;
623 gap: 10px;
624 min-height: 24px;
625 }
626
627 .desktop-mode-os-settings__library-meta {
628 color: #8c8f94;
629 font-size: 11px;
630 }
631
632 /* `__library-load-more` now renders as `<wpd-button variant="ghost">`. */
633
634 /* ---------------------------------------------------------------
635 * Reduced-motion: collapse every OS-Settings animation/transition to
636 * near-zero so motion-sensitive users still get the layout changes
637 * (hover feedback, expanded editor, tile highlights) but without the
638 * glide. Kept as a single scoped block so the intent is readable at
639 * a glance rather than spread across each rule.
640 * --------------------------------------------------------------- */
641 @media (prefers-reduced-motion: reduce) {
642 .desktop-mode-os-settings__editor-slot,
643 .desktop-mode-os-settings__wallpaper-config-slot,
644 .desktop-mode-os-settings__upload-tile,
645 .desktop-mode-os-settings__library-tile,
646 wpd-swatch {
647 transition-duration: 0.01ms !important;
648 }
649
650 .desktop-mode-os-settings__library-tile--skeleton {
651 animation: none;
652 }
653 }
654
655 /* AI settings section — error and saving feedback */
656 .desktop-mode-ai-settings__error {
657 margin: 6px 0 0;
658 font-size: 12px;
659 color: #d63638;
660 }
661
662 .desktop-mode-ai-settings__saving {
663 margin: 6px 0 0;
664 font-size: 12px;
665 color: #646970;
666 font-style: italic;
667 }
668
669 /* Extended options section — hint paragraph + error/saving states */
670 .desktop-mode-ext__hint {
671 margin: 6px 0 0;
672 font-size: 12px;
673 color: #646970;
674 line-height: 1.5;
675 }
676
677 .desktop-mode-ext__error {
678 margin: 6px 0 0;
679 font-size: 12px;
680 color: #d63638;
681 }
682
683 .desktop-mode-ext__saving {
684 margin: 6px 0 0;
685 font-size: 12px;
686 color: #646970;
687 font-style: italic;
688 }
689
690 /*
691 * Help tab — developer-facing component reference. Two-column layout
692 * (nav + detail) on desktop, stacks naturally on narrow windows via
693 * the same container the OS Settings panel lives in.
694 */
695 .desktop-mode-os-settings__help-count {
696 margin: 4px 0 0;
697 font-size: 12px;
698 color: #646970;
699 }
700
701 .desktop-mode-os-settings__help-layout {
702 display: grid;
703 grid-template-columns: 200px 1fr;
704 gap: 20px;
705 }
706
707 @container (max-width: 640px) {
708 .desktop-mode-os-settings__help-layout {
709 grid-template-columns: 1fr;
710 }
711 }
712
713 .desktop-mode-os-settings__help-nav {
714 display: flex;
715 flex-direction: column;
716 gap: 2px;
717 align-self: stretch;
718 overflow-y: auto;
719 padding: 4px;
720 background: #f6f7f7;
721 border: 1px solid #dcdcde;
722 border-radius: 8px;
723 }
724
725 .desktop-mode-os-settings__help-nav-item {
726 display: flex;
727 flex-direction: column;
728 gap: 2px;
729 align-items: flex-start;
730 width: 100%;
731 padding: 6px 10px;
732 background: transparent;
733 border: 1px solid transparent;
734 border-radius: 6px;
735 text-align: start;
736 cursor: pointer;
737 font: inherit;
738 color: inherit;
739 }
740
741 .desktop-mode-os-settings__help-nav-item:hover {
742 background: #fff;
743 border-color: #dcdcde;
744 }
745
746 .desktop-mode-os-settings__help-nav-item.is-active {
747 background: #fff;
748 border-color: var( --wp-admin-theme-color, #2271b1 );
749 }
750
751 .desktop-mode-os-settings__help-nav-title {
752 font-weight: 600;
753 font-size: 13px;
754 }
755
756 .desktop-mode-os-settings__help-nav-tag {
757 font-family: Menlo, Consolas, monospace;
758 font-size: 11px;
759 color: #646970;
760 }
761
762 .desktop-mode-os-settings__help-detail {
763 min-width: 0;
764 padding: 16px 18px 18px;
765 background: #f6f7f7;
766 border: 1px solid #dcdcde;
767 border-radius: 8px;
768 }
769
770 .desktop-mode-os-settings__help-head {
771 display: flex;
772 flex-wrap: wrap;
773 align-items: baseline;
774 gap: 8px;
775 margin: 0 0 8px;
776 }
777
778 .desktop-mode-os-settings__help-title {
779 margin: 0;
780 font-size: 16px;
781 font-weight: 600;
782 }
783
784 .desktop-mode-os-settings__help-code {
785 font-family: Menlo, Consolas, monospace;
786 font-size: 12px;
787 color: #646970;
788 }
789
790 .desktop-mode-os-settings__help-badge {
791 padding: 1px 8px;
792 border-radius: 999px;
793 font-size: 11px;
794 font-weight: 600;
795 text-transform: uppercase;
796 letter-spacing: 0.04em;
797 background: #e4e7eb;
798 color: #1d2327;
799 }
800
801 .desktop-mode-os-settings__help-badge.is-experimental {
802 background: #fcf9e8;
803 color: #996800;
804 }
805
806 .desktop-mode-os-settings__help-badge.is-planned {
807 background: #e1e8ff;
808 color: #123fb3;
809 }
810
811 .desktop-mode-os-settings__help-since {
812 font-size: 11px;
813 color: #646970;
814 }
815
816 .desktop-mode-os-settings__help-summary {
817 margin: 0 0 16px;
818 color: #1d2327;
819 }
820
821 .desktop-mode-os-settings__help-group {
822 margin: 0 0 16px;
823 }
824
825 .desktop-mode-os-settings__help-group h4 {
826 margin: 0 0 8px;
827 font-size: 12px;
828 font-weight: 600;
829 text-transform: uppercase;
830 letter-spacing: 0.04em;
831 color: #646970;
832 }
833
834 .desktop-mode-os-settings__help-example {
835 padding: 16px;
836 background: #fff;
837 border: 1px dashed #c3c4c7;
838 border-radius: 6px;
839 }
840
841 .desktop-mode-os-settings__help-table {
842 width: 100%;
843 border-collapse: collapse;
844 font-size: 12px;
845 }
846
847 .desktop-mode-os-settings__help-table th,
848 .desktop-mode-os-settings__help-table td {
849 padding: 6px 8px;
850 border-bottom: 1px solid #dcdcde;
851 text-align: start;
852 vertical-align: top;
853 }
854
855 .desktop-mode-os-settings__help-table th {
856 font-weight: 600;
857 color: #646970;
858 background: #fff;
859 }
860
861 .desktop-mode-os-settings__help-table code,
862 .desktop-mode-os-settings__help-list code {
863 font-family: Menlo, Consolas, monospace;
864 font-size: 11px;
865 padding: 1px 4px;
866 background: #fff;
867 border: 1px solid #dcdcde;
868 border-radius: 3px;
869 }
870
871 .desktop-mode-os-settings__help-list {
872 margin: 0;
873 padding: 0;
874 list-style: none;
875 }
876
877 .desktop-mode-os-settings__help-list li {
878 padding: 4px 0;
879 border-bottom: 1px solid #ececee;
880 font-size: 12px;
881 }
882
883 .desktop-mode-os-settings__help-list li:last-child {
884 border-bottom: 0;
885 }
886
887 .desktop-mode-os-settings__help-note {
888 margin: 12px 0 0;
889 padding: 10px 12px;
890 background: #fcf9e8;
891 border: 1px solid #f5e6a5;
892 border-radius: 6px;
893 font-size: 12px;
894 color: #996800;
895 }
896
897 /* ---------------------------------------------------------------
898 * Window-fit scrolling.
899 *
900 * Without this block the OS Settings panel grows past the native
901 * window's viewport and the whole body scrolls — fine for short tabs
902 * but actively wrong for the Components tab, where a 200px nav rail
903 * and a tall detail pane should each scroll inside their own column,
904 * not in lockstep down a giant single-column page.
905 *
906 * Strategy:
907 * 1. The native window body normally has overflow:auto. Override it
908 * to `hidden` ONLY when the OS Settings root is mounted (via
909 * :has()) so other native windows keep their default page-scroll.
910 * 2. Make the OS Settings root a flex column that fills the body.
911 * The tab strip + footer hug the top/bottom; the active
912 * tabpanel takes the remaining height.
913 * 3. Each tabpanel scrolls itself (overflow:auto) — replaces the
914 * whole-body scroll, visually identical for the simple tabs
915 * (Appearance, AI, Extended) but pins the reset footer.
916 * 4. The Components tab opts OUT of overflow:auto so its inner
917 * grid (nav + detail) can scroll independently per pane.
918 *
919 * `:has()` is supported in every evergreen engine (Chrome 105+,
920 * Safari 15.4+, Firefox 121+); fine for an admin-context shell.
921 * --------------------------------------------------------------- */
922 .desktop-mode-window__body--native:has( .desktop-mode-os-settings ) {
923 overflow: hidden;
924 display: flex;
925 flex-direction: column;
926 }
927
928 .desktop-mode-os-settings {
929 flex: 1;
930 min-height: 0;
931 display: flex;
932 flex-direction: column;
933 }
934
935 /* The tab strip + footer must NOT shrink when the active panel
936 wants more height. Marking them flex-shrink:0 also keeps them
937 pinned visually as the panel below scrolls. */
938 .desktop-mode-os-settings > wpd-tabs,
939 .desktop-mode-os-settings > .desktop-mode-os-settings__footer {
940 flex: 0 0 auto;
941 }
942
943 .desktop-mode-os-settings > wpd-tabpanel {
944 flex: 1;
945 min-height: 0;
946 overflow: auto;
947 }
948
949 /* Components tab — split-pane layout. Don't scroll the whole panel;
950 let the inner grid distribute height to nav + detail, each with
951 their own overflow.
952 *
953 * The `:not([hidden])` guard is load-bearing: a bare
954 * `wpd-tabpanel[for='help'] { display: flex }` rule has specificity
955 * (0,0,2,1) which outranks the shadow-DOM `:host([hidden]) {
956 * display: none }` rule (specificity 0,0,1,0), so the panel would
957 * keep rendering when it should be hidden — bleeding the Components
958 * UI into every other tab. Pinning the rule to the visible state
959 * leaves the hidden state to the shadow-DOM default. */
960 .desktop-mode-os-settings > wpd-tabpanel[ for='help' ]:not( [ hidden ] ) {
961 overflow: hidden;
962 display: flex;
963 flex-direction: column;
964 }
965 .desktop-mode-os-settings
966 > wpd-tabpanel[ for='help' ]:not( [ hidden ] )
967 > wpd-panel {
968 flex: 1;
969 min-height: 0;
970 display: flex;
971 flex-direction: column;
972 }
973
974 .desktop-mode-os-settings__help {
975 flex: 1;
976 min-height: 0;
977 display: flex;
978 flex-direction: column;
979 gap: 12px;
980 }
981 /* The intro card (Component library description) shouldn't grow —
982 it's content-sized so the grid below gets the remaining height. */
983 .desktop-mode-os-settings__help > wpd-section {
984 flex: 0 0 auto;
985 }
986 .desktop-mode-os-settings__help-layout {
987 flex: 1;
988 min-height: 0;
989 }
990
991 /* Each split pane is its own scroll container. min-height:0 unlocks
992 shrinking in a grid item (default is `auto` which equals content
993 height — would defeat the overflow). max-height:100% pins the
994 pane to the grid track. */
995 .desktop-mode-os-settings__help-nav,
996 .desktop-mode-os-settings__help-detail {
997 min-height: 0;
998 max-height: 100%;
999 overflow-y: auto;
1000 }
1001
1002 /*
1003 * Features section — per-user opt-in toggles.
1004 *
1005 * Save status pill renders the live OS-settings save lifecycle
1006 * (`pending` → `saving` → `saved` / `failed`) so toggling a
1007 * per-user feature flag gets immediate, honest feedback instead
1008 * of an optimistic flicker.
1009 */
1010 .desktop-mode-features__status-row {
1011 min-height: 22px;
1012 }
1013
1014 /* One toggle + hint pair. Stacking these without separators reads as
1015 * a wall of checkboxes; a hairline above each item (after the first)
1016 * groups the label with its description and gives the eye a stopping
1017 * point between unrelated settings. The first item sits flush so the
1018 * section heading already serves as its top boundary. */
1019 .desktop-mode-features__item {
1020 display: flex;
1021 flex-direction: column;
1022 gap: 6px;
1023 }
1024
1025 .desktop-mode-features__item + .desktop-mode-features__item {
1026 margin-top: 16px;
1027 padding-top: 16px;
1028 border-top: 1px solid var( --wpd-border, rgba( 0, 0, 0, 0.08 ) );
1029 }
1030
1031 /* "Reset what's-new dialogs" row — separates the action from the
1032 * native-Posts toggle above and gives breathing room around the
1033 * button. `align-items: flex-start` keeps the inline-flex
1034 * `<wpd-button>` from stretching full-width inside the column,
1035 * and the explicit margin on the button below works around the
1036 * fact that flex `gap` doesn't always render visibly when one
1037 * neighbour is an inline-flex custom element with zero outer
1038 * margin. */
1039 .desktop-mode-features__row {
1040 display: flex;
1041 flex-direction: column;
1042 align-items: flex-start;
1043 gap: 12px;
1044 margin-top: 20px;
1045 padding-top: 20px;
1046 border-top: 1px solid var( --wpd-border, rgba( 0, 0, 0, 0.08 ) );
1047 }
1048
1049 .desktop-mode-features__row > wpd-button {
1050 display: inline-flex;
1051 margin-block: 4px 8px;
1052 }
1053
1054 .desktop-mode-features__row > .desktop-mode-features__hint {
1055 margin: 0;
1056 }
1057
1058 .desktop-mode-features__hint {
1059 margin: 0;
1060 font-size: 12px;
1061 color: var( --wpd-text-muted, #50575e );
1062 line-height: 1.5;
1063 }
1064
1065 .desktop-mode-features__status {
1066 display: inline-flex;
1067 align-items: center;
1068 gap: 6px;
1069 font-size: 12px;
1070 font-weight: 500;
1071 padding: 2px 10px;
1072 border-radius: 999px;
1073 white-space: nowrap;
1074 transition: opacity 200ms ease;
1075 }
1076
1077 .desktop-mode-features__status--saving {
1078 background: var( --wpd-surface-elevated, #f0f0f1 );
1079 color: var( --wpd-text-muted, #50575e );
1080 }
1081
1082 .desktop-mode-features__status--saved {
1083 background: rgba( 30, 132, 73, 0.12 );
1084 color: #1d6f42;
1085 }
1086
1087 .desktop-mode-features__status--saved .dashicons {
1088 font-size: 14px;
1089 width: 14px;
1090 height: 14px;
1091 }
1092
1093 .desktop-mode-features__status--failed {
1094 background: rgba( 214, 54, 56, 0.12 );
1095 color: #a02622;
1096 }
1097
1098 .desktop-mode-features__status--failed .dashicons {
1099 font-size: 14px;
1100 width: 14px;
1101 height: 14px;
1102 }
1103
1104 .desktop-mode-features__status-dot {
1105 display: inline-block;
1106 width: 8px;
1107 height: 8px;
1108 border-radius: 50%;
1109 background: currentColor;
1110 animation: desktop-mode-features-pulse 1.2s ease-in-out infinite;
1111 }
1112
1113 @keyframes desktop-mode-features-pulse {
1114 0%, 100% { opacity: 0.35; transform: scale( 0.85 ); }
1115 50% { opacity: 1; transform: scale( 1 ); }
1116 }
1117
1118 /*
1119 * OS Settings panel header — hosts the global tabs strip and a single
1120 * `<wpd-save-status>` indicator that auto-listens to the save
1121 * lifecycle. The save status sits on the trailing edge of the header
1122 * so it's visible across every tab without competing with section
1123 * content.
1124 */
1125 .desktop-mode-os-settings__header {
1126 display: flex;
1127 align-items: center;
1128 justify-content: space-between;
1129 gap: 16px;
1130 margin-bottom: 12px;
1131 }
1132
1133 .desktop-mode-os-settings__header wpd-tabs {
1134 flex: 1 1 auto;
1135 min-width: 0;
1136 }
1137
1138 .desktop-mode-os-settings__header wpd-save-status {
1139 flex: 0 0 auto;
1140 margin-inline-start: auto;
1141 }
1142
1143 /*
1144 * About tab — full-tabpanel PixiJS canvas. Every credit string and
1145 * the AUTOMATTIC logotype (formed by particles) render inside Pixi
1146 * itself; this stylesheet only sizes the host so the canvas fills
1147 * the available height + width without any HTML chrome around it.
1148 *
1149 * The chain is: tabpanel → wpd-panel → .about (flex column) →
1150 * .about-stage-host (the canvas mounts here). Each link sets
1151 * `flex: 1; min-height: 0;` so the canvas inherits the tabpanel's
1152 * full inner height instead of hugging an intrinsic minimum.
1153 *
1154 * @since 0.8.0
1155 */
1156 .desktop-mode-os-settings
1157 > wpd-tabpanel[ for='about' ]:not( [ hidden ] ) {
1158 display: flex;
1159 flex-direction: column;
1160 overflow: hidden;
1161 }
1162
1163 .desktop-mode-os-settings
1164 > wpd-tabpanel[ for='about' ]:not( [ hidden ] )
1165 > wpd-panel {
1166 flex: 1;
1167 min-height: 0;
1168 display: flex;
1169 flex-direction: column;
1170 }
1171
1172 .desktop-mode-os-settings__about {
1173 flex: 1;
1174 min-height: 0;
1175 display: flex;
1176 flex-direction: column;
1177 margin: 0;
1178 /* Edge-to-edge — no border-radius / box-shadow framing — so the
1179 * canvas (and the logotype centred inside it) consume the entire
1180 * tabpanel surface. The gradient is just the colour-of-last-resort
1181 * underneath while Pixi initialises; once the canvas is rendering
1182 * it covers every pixel of this element anyway. */
1183 background: linear-gradient( 180deg, #060a1c 0%, #0c1733 100% );
1184 overflow: hidden;
1185 }
1186
1187 .desktop-mode-os-settings__about-stage-host {
1188 flex: 1;
1189 min-height: 0;
1190 width: 100%;
1191 overflow: hidden;
1192 cursor: crosshair;
1193 }
1194
1195 .desktop-mode-os-settings__about-stage-host canvas {
1196 display: block;
1197 width: 100%;
1198 height: 100%;
1199 }
1200
1201
1202 /* Apps & Icons section (since 0.25.0) — per-item placement chooser. */
1203 .desktop-mode-apps-icons__list {
1204 display: flex;
1205 flex-direction: column;
1206 gap: 4px;
1207 }
1208
1209 .desktop-mode-apps-icons__row {
1210 display: flex;
1211 align-items: center;
1212 gap: 12px;
1213 padding: 8px 4px;
1214 border-bottom: 1px solid var( --desktop-mode-border, rgba( 0, 0, 0, 0.08 ) );
1215 }
1216
1217 .desktop-mode-apps-icons__row:last-child {
1218 border-bottom: 0;
1219 }
1220
1221 .desktop-mode-apps-icons__identity {
1222 display: flex;
1223 align-items: center;
1224 gap: 10px;
1225 flex: 1;
1226 min-width: 0;
1227 }
1228
1229 .desktop-mode-apps-icons__icon {
1230 width: 24px;
1231 height: 24px;
1232 flex-shrink: 0;
1233 display: inline-flex;
1234 align-items: center;
1235 justify-content: center;
1236 }
1237
1238 .desktop-mode-apps-icons__icon.dashicons {
1239 font-size: 20px;
1240 }
1241
1242 .desktop-mode-apps-icons__title {
1243 overflow: hidden;
1244 text-overflow: ellipsis;
1245 white-space: nowrap;
1246 }
1247
1248 .desktop-mode-apps-icons__row wpd-select {
1249 min-width: 180px;
1250 }
1251