| @@ -82,8 +82,38 @@ | ||
| 82 | 82 | font-display: swap; |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | 85 | /* |
| 86 | + * ---- Registered, so "undeclared" never means transparent --------- | |
| 87 | + * | |
| 88 | + * The scope note below keeps this palette out of iframe documents on | |
| 89 | + * purpose: an admin page inside a window renders with the values its | |
| 90 | + * own stylesheets were written with. That promise has one hole CSS | |
| 91 | + * digs by itself — a stylesheet that consumes | |
| 92 | + * `var(--wp-admin-theme-color)` with no fallback, in a document where | |
| 93 | + * nothing declares it, does not fall back to an earlier declaration | |
| 94 | + * in the cascade. An unresolved `var()` is invalid at computed-value | |
| 95 | + * time, so the property computes to `initial` — and an unregistered | |
| 96 | + * custom property's initial value is the guaranteed-invalid value, | |
| 97 | + * which for a `background` means transparent. Core's own scheme files | |
| 98 | + * declare the property, but a third-party admin scheme that consumes | |
| 99 | + * it without declaring it renders `.button-primary` white-on-white | |
| 100 | + * inside every chromeless window (#702). | |
| 101 | + * | |
| 102 | + * Registering the property gives "undeclared" a real answer: | |
| 103 | + * WordPress's default admin colour, the same value Core's fallbacks | |
| 104 | + * reach for. Declarations are untouched — a scheme, the palette | |
| 105 | + * below, or the accent picker's inline style all keep winning exactly | |
| 106 | + * as before. Registration only decides what the absence of all of | |
| 107 | + * them computes to. | |
| 108 | + */ | |
| 109 | +@property --wp-admin-theme-color { | |
| 110 | + syntax: "<color>"; | |
| 111 | + inherits: true; | |
| 112 | + initial-value: #3858e9; | |
| 113 | +} | |
| 114 | + | |
| 115 | +/* | |
| 86 | 116 | * ---- Scope: the shell document, and nothing else ----------------- |
| 87 | 117 | * |
| 88 | 118 | * `body.os-active` rather than `:root`, and that is |
| 89 | 119 | * load-bearing rather than tidy. |
| @@ -110,8 +140,22 @@ | ||
| 110 | 140 | * Do not remove that dependency; see the note in |
| 111 | 141 | * `openstation_enqueue_desktop_theme_style()`. |
| 112 | 142 | */ |
| 113 | 143 | body.os-active { |
| 144 | + /* MIO's conversation follows desktop themes through these public tokens. */ | |
| 145 | + --os-mio-chat-bg: var(--os-ui-modal-bg, #15151e); | |
| 146 | + --os-mio-chat-fg: var(--os-ui-modal-text, #f4f4fb); | |
| 147 | + --os-mio-chat-muted: var(--os-ui-modal-text-muted, #b4b4c6); | |
| 148 | + --os-mio-chat-hover-bg: color-mix(in srgb, var(--os-mio-chat-bg, #fff) 90%, var(--os-mio-chat-fg, #1d2327)); | |
| 149 | + --os-mio-chat-border: color-mix(in srgb, var(--os-ui-accent, #a855f7) 40%, transparent); | |
| 150 | + --os-mio-chat-user-bg: color-mix(in srgb, var(--os-ui-accent, #3f6dff) 15%, transparent); | |
| 151 | + --os-mio-chat-glow: color-mix(in srgb, var(--os-ui-accent-dim, #ff4fd8) 10%, transparent); | |
| 152 | + --os-mio-launcher-bg: color-mix(in srgb, var(--os-mio-chat-bg, #fff) 62%, transparent); | |
| 153 | + --os-mio-callout-bg: var(--os-mio-chat-bg, #15151e); | |
| 154 | + --os-mio-callout-fg: var(--os-mio-chat-fg, #f4f4fb); | |
| 155 | + --os-mio-callout-border: color-mix(in srgb, var(--os-ui-accent-dim, #a855f7) 32%, transparent); | |
| 156 | + --os-mio-chat-radius: max(18px, var(--os-window-radius, 22px)); | |
| 157 | + | |
| 114 | 158 | /* |
| 115 | 159 | * ---- Desk -------------------------------------------------- |
| 116 | 160 | * |
| 117 | 161 | * `Space`, the brand's quiet deep-space backdrop: a linear |
| @@ -191,10 +235,22 @@ | ||
| 191 | 235 | |
| 192 | 236 | /* Window chrome — Obsidian body inside a Starlight hairline. */ |
| 193 | 237 | --os-window-bg: #1a1721; |
| 194 | 238 | --os-window-border: rgba(255, 251, 255, 0.12); |
| 195 | - --os-window-radius: 8px; | |
| 196 | 239 | /* |
| 240 | + * Window corner. Softer than the 8px the shell carried before, so | |
| 241 | + * a window reads as an object on the desk rather than a panel | |
| 242 | + * pinned to it. | |
| 243 | + * | |
| 244 | + * The tab strip's own radius is deliberately NOT tied to this one. | |
| 245 | + * A tab is 30px tall, so a corner anywhere near this size eats the | |
| 246 | + * whole straight edge and turns the tab into a capsule — it stops | |
| 247 | + * reading as a folder tab attached to the page, which is the one | |
| 248 | + * thing the tab has to say. `--os-tabs-radius` stays smaller and | |
| 249 | + * independent on purpose. | |
| 250 | + */ | |
| 251 | + --os-window-radius: 16px; | |
| 252 | + /* | |
| 197 | 253 | * Shadows stay black and go deeper than they were: on a Void desk |
| 198 | 254 | * a soft grey shadow is invisible, and depth is the only thing |
| 199 | 255 | * separating a window from the sky behind it. |
| 200 | 256 | */ |
| @@ -234,16 +290,21 @@ | ||
| 234 | 290 | |
| 235 | 291 | /* |
| 236 | 292 | * ---- Title bar --------------------------------------------- |
| 237 | 293 | * |
| 238 | - * Astro when focused, Obsidian when not: the focused window's | |
| 239 | - * chrome lifts one step up the Shade ramp while its unfocused | |
| 240 | - * neighbours sit flush with their own bodies. That is the entire | |
| 241 | - * focus signal, and it costs no colour — which matters, because | |
| 242 | - * the accent is spoken for. | |
| 294 | + * Obsidian when focused, Void when not: the focused window's | |
| 295 | + * chrome sits one step up the Shade ramp while its unfocused | |
| 296 | + * neighbours sink toward the desk they are lying on. That is the | |
| 297 | + * entire focus signal, and it costs no colour — which matters, | |
| 298 | + * because the accent is spoken for. | |
| 299 | + * | |
| 300 | + * The pair used to be Astro-over-Obsidian, a step higher. It came | |
| 301 | + * down so a focused window's title bar and its tab strip are the | |
| 302 | + * SAME Obsidian, and the only thing lifting out of that surface is | |
| 303 | + * the tab itself. The step is the same size; the whole ramp moved. | |
| 243 | 304 | */ |
| 244 | - --os-titlebar-bg: #1a1721; | |
| 245 | - --os-titlebar-bg-focused: #33303a; | |
| 305 | + --os-titlebar-bg: #0c0b0f; | |
| 306 | + --os-titlebar-bg-focused: #1a1721; | |
| 246 | 307 | --os-titlebar-color: #99969c; |
| 247 | 308 | --os-titlebar-color-focused: #fffbff; |
| 248 | 309 | --os-titlebar-height: 40px; |
| 249 | 310 | /* Hairline between page chrome and window chrome. */ |
| @@ -250,8 +311,35 @@ | ||
| 250 | 311 | --os-titlebar-divider: rgba(255, 251, 255, 0.12); |
| 251 | 312 | --os-titlebar-divider-unfocused: rgba(255, 251, 255, 0.06); |
| 252 | 313 | |
| 253 | 314 | /* |
| 315 | + * The status ring — leading mark of the title bar, where the app | |
| 316 | + * icon used to be. Four states: the resting ring is Starlight, | |
| 317 | + * work and success are both Pulse, failure is the danger colour. | |
| 318 | + * | |
| 319 | + * Pulse twice over is not an oversight. In flight and landed | |
| 320 | + * differ in FILL — an open ring breathing, then a solid disc with | |
| 321 | + * a check — and failure differs from both in fill AND glyph AND | |
| 322 | + * hue. Shape carries the distinction that colour alone cannot for | |
| 323 | + * a user who can't separate the two hues, and the accent stays the | |
| 324 | + * shell's own voice for "this is your station working". | |
| 325 | + * | |
| 326 | + * Failure resolves through `--os-ui-danger` and is the one value | |
| 327 | + * here that must not be quietened: a failure that whispers is a | |
| 328 | + * failure the user misses. | |
| 329 | + * | |
| 330 | + * The resting ring is one value in both title-bar states, not two: | |
| 331 | + * the ring reports a phase, and dimming it on an unfocused window | |
| 332 | + * would make `idle` say something different depending on which | |
| 333 | + * window you last clicked. | |
| 334 | + */ | |
| 335 | + --os-titlebar-activity-idle-color: #fffbff; | |
| 336 | + --os-titlebar-activity-color: var(--os-ui-accent, #2271b1); | |
| 337 | + --os-titlebar-activity-saved-color: var(--os-ui-accent, #2271b1); | |
| 338 | + --os-titlebar-activity-failed-color: var(--os-ui-danger, #d63638); | |
| 339 | + --os-titlebar-activity-size: 16px; | |
| 340 | + | |
| 341 | + /* | |
| 254 | 342 | * Title-bar control glyphs — minimise / maximise / close, the ⋯ |
| 255 | 343 | * menu trigger, and the screen-meta cluster — in each of the two |
| 256 | 344 | * title-bar states. |
| 257 | 345 | * |
| @@ -342,8 +430,45 @@ | ||
| 342 | 430 | --os-ui-surface-subtle: rgba(255, 251, 255, 0.03); |
| 343 | 431 | --os-ui-bg: #1a1721; |
| 344 | 432 | --os-ui-bg-subtle: rgba(255, 251, 255, 0.03); |
| 345 | 433 | |
| 434 | + /* | |
| 435 | + * Fields — the surface an input paints, for markup in the LIGHT | |
| 436 | + * DOM. | |
| 437 | + * | |
| 438 | + * The kit's own controls don't need these: `<os-text-field>` and | |
| 439 | + * friends resolve `--os-window-bg` inside their shadow roots, | |
| 440 | + * which the window chrome sets. A plugin writing a plain | |
| 441 | + * `<input>` into a native window's body has neither, and the | |
| 442 | + * palette had no general answer — no `surface` variant means | |
| 443 | + * "input". What it had was `--os-ui-modal-field-bg`, which is a | |
| 444 | + * MODAL token: right value, wrong scope, and the sort of thing a | |
| 445 | + * plugin author finds by grepping for a colour that looks close. | |
| 446 | + * | |
| 447 | + * Consumers keep the pre-brand WordPress-admin literals as their | |
| 448 | + * fallbacks — `#fff`, `#8c8f94`, `#2c3338` — so an unstyled page | |
| 449 | + * lands on the classic admin's own field, not on nothing: | |
| 450 | + * | |
| 451 | + * background: var( --os-ui-field-bg, #fff ); | |
| 452 | + * border: 1px solid var( --os-ui-field-border, #8c8f94 ); | |
| 453 | + * color: var( --os-ui-field-fg, #2c3338 ); | |
| 454 | + */ | |
| 455 | + --os-ui-field-bg: #33303a; | |
| 456 | + --os-ui-field-border: #4d4a52; | |
| 457 | + --os-ui-field-fg: #fffbff; | |
| 458 | + | |
| 459 | + /* | |
| 460 | + * Field sizing. The kit's own `<os-text-field>` and `<os-textarea>` | |
| 461 | + * (and every kit control that hosts a typing input) read these for | |
| 462 | + * the control's type size and corner. The phone layer raises the | |
| 463 | + * size to 16px — below that, iOS zooms the whole page into a | |
| 464 | + * focused control and never zooms back — and rounds its search; | |
| 465 | + * a theme may do the same anywhere. | |
| 466 | + */ | |
| 467 | + --os-ui-field-font-size: 13px; | |
| 468 | + --os-ui-field-font-size-compact: 12px; | |
| 469 | + --os-ui-field-radius: 6px; | |
| 470 | + | |
| 346 | 471 | /* Text — Starlight down through the Shade ramp. */ |
| 347 | 472 | --os-ui-fg: #fffbff; |
| 348 | 473 | --os-ui-fg-muted: #b3afb5; |
| 349 | 474 | --os-ui-fg-faint: #66636b; |
| @@ -407,8 +532,18 @@ | ||
| 407 | 532 | --os-ui-selection-fg: #fffbff; |
| 408 | 533 | |
| 409 | 534 | /* Accent — the identity moment. */ |
| 410 | 535 | --os-ui-accent: #f252fc; |
| 536 | + /* | |
| 537 | + * Ink on the accent FILL: the tick in a checked box, the dot in a | |
| 538 | + * radio, the label of a primary button. Distinct from | |
| 539 | + * `--os-ui-fg-on-accent`, which the shell reads as "light text on | |
| 540 | + * a dark surface" in seventy-odd places that are not the accent. | |
| 541 | + * Starlight here for Pulse; the accent picker rewrites it inline | |
| 542 | + * to Void whenever the chosen accent is light (Starlight, Sirius, | |
| 543 | + * Nebula, Lagoon), so the pair always agree. | |
| 544 | + */ | |
| 545 | + --os-ui-accent-ink: #fffbff; | |
| 411 | 546 | --os-ui-accent-strong: #ec9bff; |
| 412 | 547 | --os-ui-accent-soft: color-mix(in srgb, var(--os-ui-accent-dim) 14%, transparent); |
| 413 | 548 | /* |
| 414 | 549 | * Pulse, one step back — and the single knob for how loud the |
| @@ -527,8 +662,47 @@ | ||
| 527 | 662 | |
| 528 | 663 | /* The fill itself. Holomesh, so the mesh is the default look. */ |
| 529 | 664 | --os-ui-holo-fill: var(--os-mesh-holo); |
| 530 | 665 | /* |
| 666 | + * A hero FIELD, as distinct from a hero control. | |
| 667 | + * | |
| 668 | + * `--os-ui-holo-fill` is sized for a surface a control wears — a | |
| 669 | + * button, a chip, a progress bar. Stretch that same value across | |
| 670 | + * a few hundred square pixels of panel and a theme's answer stops | |
| 671 | + * working: Legacy's `#f0f6fc -> #f6f7f7` reads as "on" on a 40px | |
| 672 | + * chip and as nothing at all on a rail, where it lands within a | |
| 673 | + * few units of the surface behind it. | |
| 674 | + * | |
| 675 | + * So a large decorative field reads THIS instead, and a theme | |
| 676 | + * gets to answer the two cases separately — how a control looks | |
| 677 | + * when it is on, and how much of itself the station shows in the | |
| 678 | + * background. Legacy sets a flat pre-brand wash here while | |
| 679 | + * leaving its control fill alone. | |
| 680 | + * | |
| 681 | + * The default is an aurora, and it is built differently from the | |
| 682 | + * five meshes on purpose. Those are OPAQUE light surfaces — the | |
| 683 | + * darkest stop in Holomesh still sits above 60% luminance — which | |
| 684 | + * is right for a control that has to read as lit against whatever | |
| 685 | + * is behind it. Spread one across a rail and the only way to calm | |
| 686 | + * it down is `opacity`, which drags every hue toward the surface | |
| 687 | + * underneath: on Void that is grey, and the mesh dies as a | |
| 688 | + * grey-mauve smear. | |
| 689 | + * | |
| 690 | + * So the alpha lives in the COLOUR STOPS here, over transparent. | |
| 691 | + * The desk shows through instead of being covered, the hues keep | |
| 692 | + * their chroma at any strength, and the field stays dark the way | |
| 693 | + * an aurora is dark — light added to a night sky, not a pale | |
| 694 | + * sheet laid over one. Hues are the brand's: violet and Nebula | |
| 695 | + * through the upper curtains, Sirius cyan and Holomesh's mint | |
| 696 | + * below, one wide Pulse bloom tying them together. | |
| 697 | + */ | |
| 698 | + --os-ui-hero-mesh: | |
| 699 | + radial-gradient(ellipse 62% 38% at 34% 20%, rgba(170, 103, 255, 0.58) 0%, rgba(170, 103, 255, 0) 74%), | |
| 700 | + radial-gradient(ellipse 54% 33% at 58% 36%, rgba(236, 155, 255, 0.45) 0%, rgba(236, 155, 255, 0) 76%), | |
| 701 | + radial-gradient(ellipse 68% 30% at 26% 50%, rgba(125, 239, 245, 0.32) 0%, rgba(125, 239, 245, 0) 74%), | |
| 702 | + radial-gradient(ellipse 58% 28% at 46% 66%, rgba(147, 240, 198, 0.26) 0%, rgba(147, 240, 198, 0) 76%), | |
| 703 | + radial-gradient(ellipse 96% 62% at 40% 42%, rgba(242, 82, 252, 0.18) 0%, rgba(242, 82, 252, 0) 82%); | |
| 704 | + /* | |
| 531 | 705 | * Glyphs and text ON that fill. Void, not Starlight — every mesh |
| 532 | 706 | * in the brand is a LIGHT surface (its darkest stop, Holomesh's |
| 533 | 707 | * #afa2e8, still sits at 62% luminance), so Starlight on it is a |
| 534 | 708 | * white-on-lilac 1.4:1 that looks fine in a mock and is unreadable |
| @@ -610,21 +784,21 @@ | ||
| 610 | 784 | * satisfy WCAG 1.4.11. |
| 611 | 785 | */ |
| 612 | 786 | --os-ui-holo-track-edge: #66636b; |
| 613 | 787 | /* |
| 614 | - * Focus. One ring for the whole kit — Pulse, offset, over a soft | |
| 615 | - * halo, so it reads on Obsidian, on a mesh, and on the near-white | |
| 788 | + * Focus. One ring for the whole kit — the accent, offset by a Void | |
| 789 | + * gap, so it reads on Obsidian, on a mesh, and on the near-white | |
| 616 | 790 | * of a legacy theme alike. |
| 617 | 791 | * |
| 618 | - * Note which layer dims and which does not. The RING is full Pulse | |
| 619 | - * and stays there: it is the only thing on screen saying where the | |
| 620 | - * keyboard is, and a focus indicator is the last place to trade | |
| 621 | - * legibility for calm. Only the bloom behind it comes down, from | |
| 622 | - * 45% to 30%, and onto the dim — which is the layer that was | |
| 623 | - * making a focused control look like it was on fire. | |
| 792 | + * The RING is full accent and stays there: it is the only thing on | |
| 793 | + * screen saying where the keyboard is, and a focus indicator is the | |
| 794 | + * last place to trade legibility for calm. The bloom that used to | |
| 795 | + * sit behind it is gone: it was the layer that made a focused | |
| 796 | + * control look like it was on fire, and the Void gap already does | |
| 797 | + * the work of separating the ring from whatever it lands on. | |
| 624 | 798 | */ |
| 625 | - --os-ui-focus-ring: 0 0 0 2px rgba(12, 11, 15, 0.9), 0 0 0 4px #f252fc, | |
| 626 | - 0 0 12px 2px color-mix(in srgb, var(--os-ui-accent-dim) 30%, transparent); | |
| 799 | + --os-ui-focus-ring: 0 0 0 2px rgba(12, 11, 15, 0.9), | |
| 800 | + 0 0 0 4px var(--os-ui-accent, #f252fc); | |
| 627 | 801 | /* |
| 628 | 802 | * …and one for FIELDS, which is a different problem. A text input |
| 629 | 803 | * already has a border to thicken and it sits in a column of other |
| 630 | 804 | * inputs, so the ring above — designed to survive landing on a |
| @@ -632,12 +806,112 @@ | ||
| 632 | 806 | * tightens the field's own edge to Pulse and puts a soft halo |
| 633 | 807 | * outside it: unmistakable, and quiet enough to live in a stack of |
| 634 | 808 | * twelve. |
| 635 | 809 | */ |
| 636 | - --os-ui-focus-ring-field: 0 0 0 1px #f252fc, | |
| 810 | + --os-ui-focus-ring-field: 0 0 0 1px var(--os-ui-accent, #f252fc), | |
| 637 | 811 | 0 0 0 4px color-mix(in srgb, var(--os-ui-accent-dim) 16%, transparent); |
| 638 | 812 | |
| 639 | 813 | /* |
| 814 | + * The selected row of a VERTICAL `<os-tabs>`: the sidebar in | |
| 815 | + * OpenStation Preferences, and any native window that lays its | |
| 816 | + * pages down the side rather than across the top. | |
| 817 | + * | |
| 818 | + * The one selection on the station that keeps the accent. The row | |
| 819 | + * runs full-bleed and says "this one" with a 2px accent line at its | |
| 820 | + * leading edge, the bloom that line throws back across the row, and | |
| 821 | + * a wash fading out towards the label. Everywhere else a selection | |
| 822 | + * is a surface (a grey key, an outline, a flat fill); the sidebar is | |
| 823 | + * where you are, and the brand marks it. | |
| 824 | + * | |
| 825 | + * The three coloured layers are DERIVED from the accent: `-edge` is | |
| 826 | + * the accent flat, `-wash` and `-bloom` resolve through | |
| 827 | + * `--os-ui-accent-dim` like every other ambient use, so a picked | |
| 828 | + * accent reaches the row on this palette and on Legacy alike (see | |
| 829 | + * `desktopThemesLegacy.php`: a theme cannot answer a derived token, | |
| 830 | + * so a derivation is shared). How much of each shows, and the row's | |
| 831 | + * shape, are literals a theme answers on its own: edge width, wash | |
| 832 | + * and bloom opacity, and a fill, radius and inset that together | |
| 833 | + * would turn the row into a lifted pill. The wash's opacity scales | |
| 834 | + * every stop of the gradient, because a background image has no | |
| 835 | + * opacity of its own and both of the row's pseudo-elements are | |
| 836 | + * already spent on the edge and the bloom. | |
| 837 | + */ | |
| 838 | + --os-ui-tab-edge: linear-gradient( | |
| 839 | + var(--os-ui-accent, #f252fc), | |
| 840 | + var(--os-ui-accent, #f252fc) | |
| 841 | + ); | |
| 842 | + --os-ui-tab-wash: linear-gradient( | |
| 843 | + 90deg, | |
| 844 | + color-mix( | |
| 845 | + in srgb, | |
| 846 | + var(--os-ui-accent-dim) calc(16% * var(--os-ui-tab-wash-opacity, 1)), | |
| 847 | + transparent | |
| 848 | + ) | |
| 849 | + 0%, | |
| 850 | + rgba(255, 251, 255, calc(0.04 * var(--os-ui-tab-wash-opacity, 1))) 42%, | |
| 851 | + transparent 100% | |
| 852 | + ); | |
| 853 | + --os-ui-tab-bloom: linear-gradient( | |
| 854 | + 90deg, | |
| 855 | + color-mix(in srgb, var(--os-ui-accent-dim) 26%, transparent), | |
| 856 | + transparent | |
| 857 | + ); | |
| 858 | + --os-ui-tab-edge-width: 2px; | |
| 859 | + --os-ui-tab-wash-opacity: 1; | |
| 860 | + --os-ui-tab-bloom-opacity: 1; | |
| 861 | + --os-ui-tab-fill: transparent; | |
| 862 | + --os-ui-tab-radius: 0px; | |
| 863 | + --os-ui-tab-inset: 0px; | |
| 864 | + | |
| 865 | + /* | |
| 866 | + * `<os-segmented>`, same split again. The thumb and its label still | |
| 867 | + * mix from the accent, so Legacy and any theme that answers 100% | |
| 868 | + * keep a coloured pill that follows the picker. This palette answers | |
| 869 | + * 0%: a Void track with the chosen option as a mid-grey key and a | |
| 870 | + * Starlight label, whatever accent is picked. | |
| 871 | + * | |
| 872 | + * The key's grey is set by contrast, not by taste. The first pass | |
| 873 | + * used the sidebar row's grey, and it sat at 1.37:1 against the | |
| 874 | + * track and 1.15:1 against the card around it, so the key read as a | |
| 875 | + * hole in the track rather than a thing pressed into it. This grey | |
| 876 | + * clears 3:1 against Void, what a selected state needs against its | |
| 877 | + * neighbours, and still carries its Starlight label at 5.7:1. A | |
| 878 | + * small shadow under it and a hairline around the track do the | |
| 879 | + * rest: without the hairline, a Void track on the page behind the | |
| 880 | + * dock options measured 1.05:1 and all but disappeared. | |
| 881 | + * | |
| 882 | + * Under the pointer an unselected option takes a faint Starlight | |
| 883 | + * lift instead of the holographic film. | |
| 884 | + */ | |
| 885 | + --os-ui-segmented-bg: #0c0b0f; | |
| 886 | + --os-ui-segmented-edge: inset 0 0 0 1px rgba(255, 251, 255, 0.1); | |
| 887 | + --os-ui-segmented-selected-accent: 0%; | |
| 888 | + --os-ui-segmented-selected-base: #67636e; | |
| 889 | + --os-ui-segmented-selected-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); | |
| 890 | + --os-ui-segmented-hover-bg: rgba(255, 251, 255, 0.06); | |
| 891 | + --os-ui-segmented-hover-sheen: none; | |
| 892 | + | |
| 893 | + /* | |
| 894 | + * The chosen tile in a swatch grid (wallpapers, the theme and | |
| 895 | + * layout cards in Preferences, and any grid a plugin builds from | |
| 896 | + * `<os-swatch>`), same split. The ring is the accent and stays | |
| 897 | + * derived; this palette sets its width to 0 and lifts the tile | |
| 898 | + * instead: a solid Starlight stroke outlines it, a shadow sets it | |
| 899 | + * above the grid, and a Starlight disc with a tick cut out of it | |
| 900 | + * sits in the corner. The stroke is Starlight rather than the | |
| 901 | + * accent, like the badge, so the chosen tile stands out the same | |
| 902 | + * way whichever accent is picked; a translucent hairline was too | |
| 903 | + * faint on the dark theme cards. Legacy answers 2px, no lift, no | |
| 904 | + * badge. The accent chips in the colour row keep their ring | |
| 905 | + * whatever these say: a colour is the whole content there, and a | |
| 906 | + * lift on a 28px square is invisible. | |
| 907 | + */ | |
| 908 | + --os-ui-swatch-ring-width: 0px; | |
| 909 | + --os-ui-swatch-lift: 0 0 0 1px #fffbff, | |
| 910 | + 0 12px 24px -12px rgba(0, 0, 0, 0.8); | |
| 911 | + --os-ui-swatch-badge-bg: #fffbff; | |
| 912 | + | |
| 913 | + /* | |
| 640 | 914 | * ---- Motion -------------------------------------------------- |
| 641 | 915 | * |
| 642 | 916 | * Four durations and three curves, and every animated thing in the |
| 643 | 917 | * kit picks from them. Not for tidiness: a panel where the switch |
| @@ -670,8 +944,16 @@ | ||
| 670 | 944 | */ |
| 671 | 945 | --os-ui-ease-spring: cubic-bezier(0.32, 1.5, 0.55, 1); |
| 672 | 946 | /* Decelerating. The default for anything arriving. */ |
| 673 | 947 | --os-ui-ease-out: cubic-bezier(0.22, 0.9, 0.28, 1); |
| 948 | + /* | |
| 949 | + * Accelerating — the mirror of `out`, and the default for anything | |
| 950 | + * LEAVING. A surface that eases out on the way in and eases in on | |
| 951 | + * the way out is the difference between "dismissed" and "undone": | |
| 952 | + * decelerating into nothing reads as the thing hesitating, which | |
| 953 | + * is the wrong note when the user has already moved on. | |
| 954 | + */ | |
| 955 | + --os-ui-ease-in: cubic-bezier(0.4, 0, 1, 1); | |
| 674 | 956 | /* Symmetric, for a loop that has to come back where it started. */ |
| 675 | 957 | --os-ui-ease-loop: cubic-bezier(0.45, 0, 0.55, 1); |
| 676 | 958 | |
| 677 | 959 | /* |
| @@ -701,9 +983,9 @@ | ||
| 701 | 983 | |
| 702 | 984 | /* |
| 703 | 985 | * Dialogs, menus and flyouts. |
| 704 | 986 | * |
| 705 | - * The five `--os-ui-modal-*` names below are the escape hatch | |
| 987 | + * The `--os-ui-modal-*` names below are the escape hatch | |
| 706 | 988 | * `<os-modal>` needs. Its dialog surface is dark whatever the |
| 707 | 989 | * admin colour scheme says, so it re-points `--os-ui-fg`, |
| 708 | 990 | * `--os-ui-fg-muted`, `--os-ui-border`, `--os-window-bg` and |
| 709 | 991 | * `--os-ui-button-bg-hover` on its own `:host` — and a declaration |
| @@ -710,8 +992,22 @@ | ||
| 710 | 992 | * on the host outranks anything the host inherits, which is how |
| 711 | 993 | * the palette used to stop at the dialog's edge. It reads these |
| 712 | 994 | * instead, so the station owns the dialog again and the literals |
| 713 | 995 | * behind them stay as the no-stylesheet floor. |
| 996 | + * | |
| 997 | + * The set has to cover a dark context COMPLETELY, and the four | |
| 998 | + * added last are the ones that proved it. A dialog re-pointing | |
| 999 | + * only its text colours is fine while the palette around it is | |
| 1000 | + * dark too — every unre-pointed token happens to agree. Put a | |
| 1001 | + * LIGHT palette outside it (any theme in the admin's own colours, | |
| 1002 | + * Legacy included) and the halves come apart: `--os-ui-surface` | |
| 1003 | + * stays white, so `<os-select>` inside the dialog painted a white | |
| 1004 | + * trigger and the re-pointed `--os-ui-fg` wrote near-white text | |
| 1005 | + * onto it. `--os-ui-hover` was a black wash over a dark row. | |
| 1006 | + * | |
| 1007 | + * So the rule for anything added here: if a token names a SURFACE | |
| 1008 | + * or a wash on one, the dialog owns it. Leaving one out is not | |
| 1009 | + * "inherit the theme", it is "disagree with the other five". | |
| 714 | 1010 | */ |
| 715 | 1011 | --os-ui-modal-bg: #1a1721; |
| 716 | 1012 | --os-ui-modal-text: #fffbff; |
| 717 | 1013 | --os-ui-modal-text-muted: #b3afb5; |
| @@ -717,8 +1013,12 @@ | ||
| 717 | 1013 | --os-ui-modal-text-muted: #b3afb5; |
| 718 | 1014 | --os-ui-modal-border: #4d4a52; |
| 719 | 1015 | --os-ui-modal-field-bg: #33303a; |
| 720 | 1016 | --os-ui-modal-button-bg-hover: rgba(255, 251, 255, 0.14); |
| 1017 | + --os-ui-modal-surface: #1a1721; | |
| 1018 | + --os-ui-modal-surface-elevated: #33303a; | |
| 1019 | + --os-ui-modal-border-strong: #4d4a52; | |
| 1020 | + --os-ui-modal-hover: rgba(255, 251, 255, 0.06); | |
| 721 | 1021 | --os-ui-confirm-dialog-bg: #1a1721; |
| 722 | 1022 | --os-ui-context-menu-bg: #1a1721; |
| 723 | 1023 | --os-ui-context-menu-separator-color: rgba(255, 251, 255, 0.1); |
| 724 | 1024 | --os-ui-flyout-shadow: 0 16px 48px rgba(0, 0, 0, 0.6); |
| @@ -761,10 +1061,10 @@ | ||
| 761 | 1061 | * only from the component's own `::slotted( a )`. See the comment |
| 762 | 1062 | * there: a slotted link is a document-tree element, so wp-admin's |
| 763 | 1063 | * bare `a` rule outranks anything the shadow root says about it. |
| 764 | 1064 | */ |
| 765 | - --os-ui-notice-link: #ec9bff; | |
| 766 | - --os-ui-notice-link-hover: #fffbff; | |
| 1065 | + --os-ui-notice-link: #fffbff; | |
| 1066 | + --os-ui-notice-link-hover: #ccc8ce; | |
| 767 | 1067 | --os-ui-badge-danger-bg: rgba(255, 90, 90, 0.16); |
| 768 | 1068 | --os-ui-badge-success-bg: rgba(147, 240, 198, 0.16); |
| 769 | 1069 | --os-ui-badge-warning-bg: rgba(248, 242, 182, 0.16); |
| 770 | 1070 | --os-ui-badge-info-bg: rgba(194, 241, 241, 0.16); |
| @@ -859,9 +1159,8 @@ | ||
| 859 | 1159 | /* Search-match highlight inside a label. A WASH, not a fill: the |
| 860 | 1160 | text keeps its own colour, so the wash has to stay light enough |
| 861 | 1161 | for Starlight to carry over it. */ |
| 862 | 1162 | --os-ui-search-highlight-bg: rgba(248, 242, 182, 0.3); |
| 863 | - --os-ui-color-accent: #f252fc; | |
| 864 | 1163 | /* |
| 865 | 1164 | * The content graph's cluster hue is NOT retinted. Graph node |
| 866 | 1165 | * colours are composed artwork that encodes meaning — the docs list |
| 867 | 1166 | * them under "what stays fixed" — and moving this one to Nebula put |
| @@ -867,18 +1166,82 @@ | ||
| 867 | 1166 | * them under "what stays fixed" — and moving this one to Nebula put |
| 868 | 1167 | * white cluster labels on a pale lilac chip at 1.9:1. It keeps its |
| 869 | 1168 | * own colour, and the label keeps carrying on it. |
| 870 | 1169 | * |
| 871 | - * The graph's toolbar is chrome, though, and it does follow. The | |
| 872 | - * count pill on an ACTIVE filter chip is a 70%-white fill, and it | |
| 873 | - * was carrying `--os-ui-accent-strong` numerals — Nebula on white, | |
| 874 | - * ~1.7:1. It is one more bright fill naming its own dark text, the | |
| 875 | - * same escape hatch `--os-ui-ribbon-fg` and `--os-ui-step-chip-fg` | |
| 876 | - * take, and for the same reason. | |
| 1170 | + * The graph's toolbar is chrome, though, and it follows the same | |
| 1171 | + * split as selection everywhere else: the accent stays derived, a | |
| 1172 | + * literal says how much of it shows. An ON filter chip mixes its | |
| 1173 | + * edge, wash and text from the accent by `-accent`, towards the | |
| 1174 | + * neutrals below; this palette answers 0%, so an on chip reads like | |
| 1175 | + * the selected theme card in Preferences, a Starlight outline around | |
| 1176 | + * the dark surface. Legacy answers 100%. An OFF chip sits on the | |
| 1177 | + * toolbar with the strong border as its edge, because the plain | |
| 1178 | + * border is the toolbar's own colour and a chip drawn with it has no | |
| 1179 | + * outline at all. | |
| 1180 | + * | |
| 1181 | + * The count pill is the strong border's grey on both, with Starlight | |
| 1182 | + * numerals: muted numerals on that grey land at 4.0:1, under what | |
| 1183 | + * 12px text needs. | |
| 877 | 1184 | */ |
| 878 | - --os-ui-cg-chip-count-fg-active: #0c0b0f; | |
| 1185 | + --os-ui-cg-chip-accent: 0%; | |
| 1186 | + --os-ui-cg-chip-border: #4d4a52; | |
| 1187 | + --os-ui-cg-chip-border-active: #fffbff; | |
| 1188 | + --os-ui-cg-chip-fill-active: #1a1721; | |
| 1189 | + --os-ui-cg-chip-count-bg: #4d4a52; | |
| 1190 | + --os-ui-cg-chip-count-fg: #fffbff; | |
| 1191 | + --os-ui-cg-chip-count-bg-active: #4d4a52; | |
| 1192 | + --os-ui-cg-chip-count-fg-active: #fffbff; | |
| 879 | 1193 | |
| 880 | 1194 | /* |
| 1195 | + * ---- The widget card's public token contract ---------------- | |
| 1196 | + * | |
| 1197 | + * Five names a third-party widget stylesheet is told to read. | |
| 1198 | + * The list lives in `src/plugins/starter-widget/styles.css`, | |
| 1199 | + * which is the file plugin authors copy — so it is a contract, | |
| 1200 | + * not a convenience, and four of the five were documented | |
| 1201 | + * without ever being declared. Every consumer fell through to | |
| 1202 | + * the light-mode grey in its own `var()` fallback, on a card | |
| 1203 | + * whose glass is dark: Post Stats' metadata measured 3.81:1 | |
| 1204 | + * where `.os-widgets__title` on the same surface measures 18.4. | |
| 1205 | + * | |
| 1206 | + * They are mostly literals rather than aliases of the text ramp, | |
| 1207 | + * and that is the point. `.os-widgets__card` paints a fixed dark | |
| 1208 | + * glass — `--os-ui-color-surface` below IS that colour, and the | |
| 1209 | + * card reads it — under every desktop theme, so a name that | |
| 1210 | + * flips with the theme is the wrong chain here: Legacy's | |
| 1211 | + * `--os-ui-fg-muted` is `#50575e`, which lands at 2.54:1 on the | |
| 1212 | + * glass and makes the bug WORSE for anyone wearing it. The two | |
| 1213 | + * text names below do chain, but through the two names Legacy | |
| 1214 | + * keeps light — the ones describing the same on-dark chrome | |
| 1215 | + * this card is. | |
| 1216 | + * | |
| 1217 | + * A theme that wants to move a widget card declares these names | |
| 1218 | + * itself, the way Legacy already declares the accent. | |
| 1219 | + */ | |
| 1220 | + --os-ui-color-surface: rgba(20, 20, 22, 0.55); | |
| 1221 | + /* What the card's own `color` already resolves to — so a widget | |
| 1222 | + asking for "primary text" gets the text it inherited. */ | |
| 1223 | + --os-ui-color-text: var(--os-ui-fg-on-accent, #fffbff); | |
| 1224 | + /* The shell's muted-on-dark. 9.2:1 on the glass, and Legacy | |
| 1225 | + keeps it at `rgba( 255, 255, 255, 0.7 )`. */ | |
| 1226 | + --os-ui-color-text-subtle: var(--os-fg-muted, rgba(255, 251, 255, 0.7)); | |
| 1227 | + /* A hairline INSIDE the card, so a touch stronger than the | |
| 1228 | + card's own 0.08 edge, which is drawn against the wallpaper. | |
| 1229 | + Literal because no general on-dark hairline exists: the ones | |
| 1230 | + Legacy keeps light are `--os-dock-border` and | |
| 1231 | + `--os-cn-border`, and borrowing either is the right value at | |
| 1232 | + the wrong scope. It reads quieter than what it replaces — | |
| 1233 | + the widgets' own fallback was `#e5e7eb`, a light-theme | |
| 1234 | + divider that on dark glass drew the loudest line in the | |
| 1235 | + station. 0.12 is what the window and dock edges use. */ | |
| 1236 | + --os-ui-color-border: rgba(255, 251, 255, 0.12); | |
| 1237 | + /* The one name that has to reach the accent picker. It was a | |
| 1238 | + hard `#f252fc`, so a widget told to "use the accent for | |
| 1239 | + buttons and links" stayed brand pink after the user chose | |
| 1240 | + teal, while every control around it moved. */ | |
| 1241 | + --os-ui-color-accent: var(--os-ui-accent, #f252fc); | |
| 1242 | + | |
| 1243 | + /* | |
| 881 | 1244 | * ---- Typography -------------------------------------------- |
| 882 | 1245 | * |
| 883 | 1246 | * Geist for everything human — headlines, UI copy, body text — and |
| 884 | 1247 | * Geist Mono for the instrument voice: labels, metadata, statuses, |
| @@ -984,21 +1347,26 @@ | ||
| 984 | 1347 | * / -bg / -fg. Falls through to the generic |
| 985 | 1348 | * icon-badge token, so "badges are 22px" reaches |
| 986 | 1349 | * the bin too. |
| 987 | 1350 | * |
| 988 | - * The count badges keep their red, and keep it as a LITERAL: a | |
| 989 | - * badge is a status, not an identity moment, and Pulse next to a | |
| 990 | - * Pulse-accented dock tile would read as decoration rather than as | |
| 991 | - * "three things need you". Naming the gradient here also keeps it | |
| 992 | - * a gradient — both stops used to resolve through `--os-ui-danger`, | |
| 993 | - * which the palette moved, flattening the badge to one flat red | |
| 994 | - * and turning its white numerals Void. The bin's neutral badge has | |
| 995 | - * no such constraint and follows the station. | |
| 1351 | + * The count badges wear Pulse, and wear it as a LITERAL gradient: | |
| 1352 | + * both stops used to resolve through `--os-ui-danger`, which the | |
| 1353 | + * palette moved, flattening the badge to one flat fill and turning | |
| 1354 | + * its numerals Void by accident. Naming the stops here keeps it a | |
| 1355 | + * gradient on purpose. The bin's neutral badge is deliberately not | |
| 1356 | + * part of this: a count of things in the trash is ambient state, | |
| 1357 | + * not something asking for you. | |
| 1358 | + * | |
| 1359 | + * **The numerals are Void, not Starlight, and that is a | |
| 1360 | + * requirement rather than a preference.** Starlight on Pulse is | |
| 1361 | + * 2.9:1, which fails AA for text this small; Void on Pulse is | |
| 1362 | + * 6.8:1. Any future retint of these has to keep the pair legible, | |
| 1363 | + * so check the ratio before swapping the ink back to white. | |
| 996 | 1364 | */ |
| 997 | - --os-dock-badge-bg: linear-gradient(180deg, #ff5a5a 0%, #d63638 100%); | |
| 998 | - --os-dock-badge-fg: #fffbff; | |
| 999 | - --os-icon-badge-bg: linear-gradient(180deg, #ff5a5a 0%, #d63638 100%); | |
| 1000 | - --os-icon-badge-fg: #fffbff; | |
| 1365 | + --os-dock-badge-bg: linear-gradient(180deg, #f97dff 0%, #f252fc 100%); | |
| 1366 | + --os-dock-badge-fg: #0c0b0f; | |
| 1367 | + --os-icon-badge-bg: linear-gradient(180deg, #f97dff 0%, #f252fc 100%); | |
| 1368 | + --os-icon-badge-fg: #0c0b0f; | |
| 1001 | 1369 | --os-recycle-badge-bg: rgba(26, 23, 33, 0.85); |
| 1002 | 1370 | --os-recycle-badge-fg: rgba(255, 251, 255, 0.96); |
| 1003 | 1371 | --os-dock-recycle-badge-bg: rgba(26, 23, 33, 0.85); |
| 1004 | 1372 | --os-dock-recycle-badge-fg: rgba(255, 251, 255, 0.92); |
| @@ -1005,11 +1373,10 @@ | ||
| 1005 | 1373 | |
| 1006 | 1374 | /* |
| 1007 | 1375 | * ---- Tooltips --------------------------------------------- |
| 1008 | 1376 | * |
| 1009 | - * Two tokens own every tooltip in the shell — the dock tile | |
| 1010 | - * tooltip, the content-graph satellite tooltip, and the My | |
| 1011 | - * WordPress entity hover card. | |
| 1377 | + * Two tokens own every tooltip CHIP in the shell — the dock tile | |
| 1378 | + * tooltip and the content-graph satellite tooltip. | |
| 1012 | 1379 | * |
| 1013 | 1380 | * They are worth naming rather than leaving derived. Without them |
| 1014 | 1381 | * tooltips borrow their colours from unrelated families — |
| 1015 | 1382 | * `--os-ui-scrim` (an overlay BACKDROP) or `--os-ui-surface-elevated` |
| @@ -1017,10 +1384,16 @@ | ||
| 1017 | 1384 | * pairings come apart the moment the palette moves. On this |
| 1018 | 1385 | * palette `--os-ui-fg-on-accent` is Void, which would have rendered |
| 1019 | 1386 | * near-black text on a near-black chip. |
| 1020 | 1387 | * |
| 1021 | - * Secondary text inside the richer tooltips — the hover card's | |
| 1022 | - * excerpt — still follows `--os-ui-fg-muted`. | |
| 1388 | + * A chip is deliberately the SAME dark lozenge on every desktop | |
| 1389 | + * theme, which is why these two are flat values and not derived | |
| 1390 | + * from anything. That is right for one line of text pinned to a | |
| 1391 | + * control, and wrong for a rich card: the My WordPress entity | |
| 1392 | + * hover card used to read them and showed up Obsidian over a white | |
| 1393 | + * Legacy window. It has its own derived family now — | |
| 1394 | + * `--os-my-wordpress-card-*`, further down — and it still names | |
| 1395 | + * these two as its last fallback. | |
| 1023 | 1396 | */ |
| 1024 | 1397 | --os-tooltip-bg: #33303a; |
| 1025 | 1398 | --os-tooltip-fg: #fffbff; |
| 1026 | 1399 | |
| @@ -1042,14 +1415,39 @@ | ||
| 1042 | 1415 | * rule and so must read the same two tokens). |
| 1043 | 1416 | */ |
| 1044 | 1417 | --os-dock-width: 56px; |
| 1045 | 1418 | --os-dock-icon-size: 20px; |
| 1419 | + /* | |
| 1420 | + * The indicator line a dynamic dock collapses into while parked | |
| 1421 | + * (Preferences → Appearance → Desktop layout → Dock behavior): | |
| 1422 | + * the iOS home indicator, painted in the accent at partial alpha | |
| 1423 | + * so it reads as a mark on the wallpaper rather than a control. | |
| 1424 | + * `-length` runs along the rail's edge, `-thickness` across it; a | |
| 1425 | + * theme retunes any of the three. | |
| 1426 | + */ | |
| 1427 | + --os-dock-indicator-length: 180px; | |
| 1428 | + --os-dock-indicator-thickness: 5px; | |
| 1429 | + --os-dock-indicator-bg: color-mix( in srgb, var( --os-ui-accent, #f252fc ) 72%, transparent ); | |
| 1046 | 1430 | --os-dock-bg: rgba(12, 11, 15, 0.72); |
| 1047 | 1431 | --os-dock-icon-color: rgba(255, 251, 255, 0.72); |
| 1048 | 1432 | --os-dock-icon-color-hover: #fffbff; |
| 1049 | - --os-dock-item-bg-hover: rgba(242, 82, 252, 0.18); | |
| 1050 | - --os-dock-item-outline: #f252fc; | |
| 1433 | + --os-dock-item-bg-hover: rgba(255, 251, 255, 0.08); | |
| 1051 | 1434 | /* |
| 1435 | + * The tile whose window is in front sits on a lit plate: the same | |
| 1436 | + * Starlight wash the hover uses, one step brighter, so "this is the | |
| 1437 | + * app you are in" reads as depth rather than as a coloured mark. | |
| 1438 | + * The running dot under every open app stays; a lit plate says | |
| 1439 | + * which one is in front, the dots say which ones are open. | |
| 1440 | + */ | |
| 1441 | + --os-dock-item-bg-focused: rgba(255, 251, 255, 0.1); | |
| 1442 | + /* | |
| 1443 | + * The status indicator and the focus ring, and both say "this | |
| 1444 | + * one" about a tile the user chose — so they read the accent | |
| 1445 | + * rather than a fixed colour. Starlight is what an untouched | |
| 1446 | + * install shows, because that is the accent the picker starts on. | |
| 1447 | + */ | |
| 1448 | + --os-dock-item-outline: var(--os-ui-accent, #f252fc); | |
| 1449 | + /* | |
| 1052 | 1450 | * Hairline between the dock and the desktop. Reaches the side |
| 1053 | 1451 | * placements (as a single inline-edge border) AND the floating |
| 1054 | 1452 | * bottom pill (as its full outline). Set `transparent` for a |
| 1055 | 1453 | * borderless dock. |
| @@ -1059,8 +1457,27 @@ | ||
| 1059 | 1457 | * brighter top edge, an inset highlight, a drop shadow. Each falls |
| 1060 | 1458 | * back to the shared value above it. |
| 1061 | 1459 | */ |
| 1062 | 1460 | --os-dock-border: rgba(255, 251, 255, 0.1); |
| 1461 | + | |
| 1462 | + /* | |
| 1463 | + * The dividers INSIDE the rail, which are a different job from the | |
| 1464 | + * outline around it: they separate tiles that belong to different | |
| 1465 | + * groups — core menus, plugin apps, and OpenStation's own controls. | |
| 1466 | + * | |
| 1467 | + * One treatment for all of them. An earlier pass gave the | |
| 1468 | + * WordPress-to-OpenStation boundary Pulse and dropped the | |
| 1469 | + * core-to-plugin one to a hairline, on the theory that only one | |
| 1470 | + * boundary was worth reading. Two weights in one short rail read as | |
| 1471 | + * two unrelated ideas instead of one system, so both are now the | |
| 1472 | + * same line: Pulse at the waist, falling away to nothing at both | |
| 1473 | + * ends, with a soft glow to hold it against the dock tint. | |
| 1474 | + * | |
| 1475 | + * Resolved one step back from the accent through | |
| 1476 | + * `--os-ui-accent-dim`, where every ambient use of Pulse resolves, | |
| 1477 | + * so "tone the station down" stays one edit. | |
| 1478 | + */ | |
| 1479 | + --os-dock-divider: color-mix(in srgb, var(--os-ui-accent-dim) 70%, transparent); | |
| 1063 | 1480 | --os-dock-floating-bg: rgba(12, 11, 15, 0.62); |
| 1064 | 1481 | --os-dock-floating-border: rgba(255, 251, 255, 0.12); |
| 1065 | 1482 | --os-dock-floating-border-top: rgba(255, 251, 255, 0.18); |
| 1066 | 1483 | --os-dock-floating-highlight: rgba(255, 251, 255, 0.1); |
| @@ -1066,8 +1483,67 @@ | ||
| 1066 | 1483 | --os-dock-floating-highlight: rgba(255, 251, 255, 0.1); |
| 1067 | 1484 | --os-dock-floating-shadow: rgba(0, 0, 0, 0.55); |
| 1068 | 1485 | |
| 1069 | 1486 | /* |
| 1487 | + * The phone layer (`mobile.css`). Its surfaces are the dock's | |
| 1488 | + * family — translucent glass over the wallpaper, light glyphs — | |
| 1489 | + * and read the dock tokens above for fill, text and lines. What | |
| 1490 | + * the dock has no word for is declared here: the glass a tile, a | |
| 1491 | + * round control and a switcher card are cut from, its pressed | |
| 1492 | + * state, the shadow under a tile, the scrim behind the switcher, | |
| 1493 | + * and the label shadow that keeps text legible over any wallpaper. | |
| 1494 | + * A desktop theme that reskins the dock reskins the phone with it; | |
| 1495 | + * one that wants the phone alone has these. | |
| 1496 | + */ | |
| 1497 | + --os-mobile-glass: rgba(255, 251, 255, 0.1); | |
| 1498 | + --os-mobile-glass-strong: rgba(255, 251, 255, 0.16); | |
| 1499 | + --os-mobile-glass-border: rgba(255, 251, 255, 0.14); | |
| 1500 | + --os-mobile-tile-shadow: 0 8px 20px rgba(0, 0, 0, 0.35); | |
| 1501 | + --os-mobile-card-shadow: 0 -8px 24px rgba(0, 0, 0, 0.4); | |
| 1502 | + --os-mobile-sheet-shadow: 0 -12px 40px rgba(0, 0, 0, 0.45); | |
| 1503 | + --os-mobile-window-shadow: 0 12px 32px rgba(0, 0, 0, 0.45); | |
| 1504 | + --os-mobile-scrim: rgba(0, 0, 0, 0.55); | |
| 1505 | + --os-mobile-top-scrim: rgba(0, 0, 0, 0.45); | |
| 1506 | + --os-mobile-label-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); | |
| 1507 | + | |
| 1508 | + /* | |
| 1509 | + * ---- The OpenStation layout --------------------------------- | |
| 1510 | + * | |
| 1511 | + * The constellation: the flyout that fans a menu's submenu out of | |
| 1512 | + * its tile on hover, on every rail in every layout. The rail's | |
| 1513 | + * divider used to live here too; it is shared chrome now and reads | |
| 1514 | + * through `--os-dock-divider`. | |
| 1515 | + * | |
| 1516 | + * The constellation's SURFACE is Obsidian, deliberately. The mesh | |
| 1517 | + * is spent on the row under the pointer and on the head's icon | |
| 1518 | + * halo — the two moments where the panel is answering the user — | |
| 1519 | + * and nowhere else. A flyout that was iridescent edge to edge | |
| 1520 | + * would have nothing left to say when you actually pointed at | |
| 1521 | + * something in it. | |
| 1522 | + */ | |
| 1523 | + --os-cn-surface: rgba(26, 23, 33, 0.82); | |
| 1524 | + --os-cn-border: rgba(255, 251, 255, 0.14); | |
| 1525 | + --os-cn-shadow: 0 24px 64px rgba(0, 0, 0, 0.62), | |
| 1526 | + 0 2px 8px rgba(0, 0, 0, 0.4); | |
| 1527 | + --os-cn-fg: #fffbff; | |
| 1528 | + --os-cn-fg-muted: rgba(255, 251, 255, 0.62); | |
| 1529 | + --os-cn-legend: rgba(255, 251, 255, 0.4); | |
| 1530 | + --os-cn-divider: rgba(255, 251, 255, 0.1); | |
| 1531 | + /* The row under the pointer — the panel's identity moment. */ | |
| 1532 | + --os-cn-row-fill: var(--os-ui-holo-fill); | |
| 1533 | + --os-cn-row-ink: var(--os-ui-holo-ink); | |
| 1534 | + /* Beam: the thread from the panel's underside down to the tile. */ | |
| 1535 | + --os-cn-beam: color-mix(in srgb, var(--os-ui-accent-dim) 80%, transparent); | |
| 1536 | + --os-cn-radius: 14px; | |
| 1537 | + /* | |
| 1538 | + * Above every window and above the dock. Two panels coexist while | |
| 1539 | + * the pointer moves along the rail — one dismissing, one arriving | |
| 1540 | + * — and the retiring one is painted one step below so it can | |
| 1541 | + * never fade out on top of the menu being read. | |
| 1542 | + */ | |
| 1543 | + --os-cn-z: 2147483000; | |
| 1544 | + | |
| 1545 | + /* | |
| 1070 | 1546 | * ---- Shell surfaces ---------------------------------------- |
| 1071 | 1547 | * |
| 1072 | 1548 | * The panels, tab strips and washes that belong to the shell |
| 1073 | 1549 | * rather than to the component kit. Same Obsidian-on-Void |
| @@ -1103,22 +1579,194 @@ | ||
| 1103 | 1579 | --os-folder-window-bg: #1a1721; |
| 1104 | 1580 | --os-my-wordpress-bg: #1a1721; |
| 1105 | 1581 | --os-my-wordpress-fg: #fffbff; |
| 1106 | 1582 | --os-my-wordpress-surface: rgba(255, 251, 255, 0.06); |
| 1583 | + /* | |
| 1584 | + * The entity hover card — the "thumb" that floats over a tile with | |
| 1585 | + * a title, a featured image and an excerpt. | |
| 1586 | + * | |
| 1587 | + * It is NOT a dock chip, and it used to be painted as one. | |
| 1588 | + * `--os-tooltip-bg` is a fixed dark lozenge on every theme by | |
| 1589 | + * design — one line of text, always the same object, no reason to | |
| 1590 | + * follow anything. The card borrowed it, so a light desktop theme | |
| 1591 | + * summoned an Obsidian card over a white window, with a border in | |
| 1592 | + * `--os-ui-border` and an excerpt in `--os-ui-fg-muted` that both | |
| 1593 | + * followed the WINDOW instead. Three families, one surface. | |
| 1594 | + * | |
| 1595 | + * The card is window furniture: it belongs to the surface it was | |
| 1596 | + * summoned from, so it follows the theme that paints that surface. | |
| 1597 | + * Every value below is DERIVED rather than declared flat, which is | |
| 1598 | + * what makes that true — a theme that names only | |
| 1599 | + * `--os-my-wordpress-bg` (Legacy names exactly that, `#fff`) moves | |
| 1600 | + * the card with the window for free, and a theme that wants the | |
| 1601 | + * card alone names a `-card-` token. | |
| 1602 | + * | |
| 1603 | + * A card lands on the same colour as the window under it, so the | |
| 1604 | + * border and the shadow are the whole reason it reads as a | |
| 1605 | + * separate object. Neither is decoration here and neither may | |
| 1606 | + * collapse into the background — which is exactly what the border | |
| 1607 | + * did: `--os-ui-border` is #33303a on this palette, the SAME value | |
| 1608 | + * `--os-tooltip-bg` had, so the card had no edge at all. | |
| 1609 | + * | |
| 1610 | + * The shadow is the one flat value in the family. A drop shadow is | |
| 1611 | + * cast light, dark on a light theme and dark on a dark one, so | |
| 1612 | + * there is no window token for it to follow; it is named so a | |
| 1613 | + * theme can still reach it. | |
| 1614 | + */ | |
| 1615 | + --os-my-wordpress-card-bg: var(--os-my-wordpress-bg, #1a1721); | |
| 1616 | + --os-my-wordpress-card-fg: var(--os-my-wordpress-fg, #fffbff); | |
| 1617 | + --os-my-wordpress-card-fg-muted: var(--os-ui-fg-muted, #b3afb5); | |
| 1618 | + --os-my-wordpress-card-border: var(--os-ui-border, #33303a); | |
| 1619 | + --os-my-wordpress-card-shadow: 0 8px 28px rgba(0, 0, 0, 0.45); | |
| 1620 | + --os-my-wordpress-card-thumb-bg: var( | |
| 1621 | + --os-media-tile-bg, | |
| 1622 | + rgba(255, 251, 255, 0.05) | |
| 1623 | + ); | |
| 1624 | + --os-my-wordpress-card-lock-bg: var( | |
| 1625 | + --os-ui-badge-danger-bg, | |
| 1626 | + rgba(255, 90, 90, 0.16) | |
| 1627 | + ); | |
| 1107 | 1628 | --os-ai-panel-bg: rgba(26, 23, 33, 0.97); |
| 1108 | - --os-tabs-bg: #33303a; | |
| 1629 | + /* | |
| 1630 | + * The Site assistant, the same split as selection everywhere else: | |
| 1631 | + * its header tint and mark, search icon, lit row and icon tiles still | |
| 1632 | + * mix from the accent, and so does its send button; --os-ai-accent | |
| 1633 | + * says how much. This palette | |
| 1634 | + * answers 0%, so the chrome is neutral and a lit row is the flat 8% | |
| 1635 | + * Starlight of the Preferences sidebar; Legacy answers 100%. The | |
| 1636 | + * command icons keep the accent here too: they read it directly, so | |
| 1637 | + * the colour stays in the pictures and out of the chrome. | |
| 1638 | + */ | |
| 1639 | + --os-ai-accent: 0%; | |
| 1640 | + --os-ai-row-fill: rgba(255, 251, 255, 0.08); | |
| 1641 | + --os-ai-tile-fill: rgba(255, 251, 255, 0.06); | |
| 1642 | + /* | |
| 1643 | + * ---- Window tab strip --------------------------------------- | |
| 1644 | + * | |
| 1645 | + * A window with sub-pages reads as three surfaces stacked in | |
| 1646 | + * depth, and the values below are that ramp: Astro on the focused | |
| 1647 | + * title bar, VOID for the track the tabs sit in, then the page | |
| 1648 | + * itself. The track is the darkest of the three on purpose — it | |
| 1649 | + * has to stay a distinct band under BOTH title-bar states, and | |
| 1650 | + * Obsidian would collapse into the unfocused bar (also Obsidian) | |
| 1651 | + * exactly when a window has the least going on to distinguish it. | |
| 1652 | + * | |
| 1653 | + * The active tab is not a colour, it is the page arriving early. | |
| 1654 | + * `chromeless.css` paints every admin page inside a window `#fff` | |
| 1655 | + * whatever the admin colour scheme says, so the tab can name that | |
| 1656 | + * white and the joint between the two is seamless. Retinting the | |
| 1657 | + * tab means retinting the page it belongs to, and these two move | |
| 1658 | + * together or the seam comes back. | |
| 1659 | + * | |
| 1660 | + * `-color-muted` is the second tone anything nested in an active | |
| 1661 | + * tab needs — the external tab's detach and close chips. It is | |
| 1662 | + * the only reason a "muted on light" value exists this far down a | |
| 1663 | + * dark palette. | |
| 1664 | + */ | |
| 1665 | + --os-tabs-bg: #1a1721; | |
| 1666 | + /* | |
| 1667 | + * The track on an UNFOCUSED window, which follows the title bar | |
| 1668 | + * down to Void rather than holding Obsidian. | |
| 1669 | + * | |
| 1670 | + * A focused window already reads as two surfaces: title bar and | |
| 1671 | + * track are the same Obsidian, and the tab lifts out of them. Let | |
| 1672 | + * the track keep that Obsidian while the bar above it dims and | |
| 1673 | + * the window reads as THREE, with the strip belonging to neither | |
| 1674 | + * the chrome above nor the page below. Following the bar down | |
| 1675 | + * keeps the count at two in both states. | |
| 1676 | + * | |
| 1677 | + * Both tracks read this: the shell's strip on an iframe window, | |
| 1678 | + * and `<os-tabs variant="window">` inside a native one. The track | |
| 1679 | + * is chrome in both cases, so it dims with the chrome; what keeps | |
| 1680 | + * the tab attached to the content while that happens is the | |
| 1681 | + * plate's fill, which wears the body's own colour rather than the | |
| 1682 | + * track's. | |
| 1683 | + * | |
| 1684 | + * Note the asymmetry with the title bar's pair, which is | |
| 1685 | + * deliberate: there, `--os-titlebar-bg` is the unfocused base and | |
| 1686 | + * `-focused` is the modifier. Here the base is the LIT value, | |
| 1687 | + * so a theme naming one track colour lands on the state where the | |
| 1688 | + * track is doing the most work. | |
| 1689 | + * | |
| 1690 | + * A theme that sets only `--os-tabs-bg` (Legacy, and anything | |
| 1691 | + * written before this token existed) resolves through it in both | |
| 1692 | + * states and keeps its single strip colour. | |
| 1693 | + */ | |
| 1694 | + --os-tabs-bg-unfocused: #0c0b0f; | |
| 1109 | 1695 | --os-tabs-color: #b3afb5; |
| 1696 | + --os-tabs-active-bg: #fff; | |
| 1697 | + --os-tabs-active-color: #0c0b0f; | |
| 1698 | + --os-tabs-active-color-muted: rgba(12, 11, 15, 0.6); | |
| 1699 | + /* | |
| 1700 | + * The plate's own body: a frosted crown resolving to flat page | |
| 1701 | + * white well before the joint. | |
| 1702 | + * | |
| 1703 | + * Every stop is OPAQUE, and that is a requirement rather than a | |
| 1704 | + * preference. The face used to be translucent over a | |
| 1705 | + * `backdrop-filter`, which promotes it to its own compositor layer | |
| 1706 | + * and leaves a faint grey hairline where that layer is clipped — | |
| 1707 | + * down the plate's sides and across its bottom, which is precisely | |
| 1708 | + * where this design has a bright rail and needs everything else to | |
| 1709 | + * be invisible. | |
| 1710 | + * | |
| 1711 | + * The last stop must also land above the joint (the bottom | |
| 1712 | + * `--os-tabs-radius` of the plate), because the joint is painted | |
| 1713 | + * in flat `--os-tabs-active-bg` and a body still tinted where the | |
| 1714 | + * two meet draws a seam of its own. If you retune these stops, | |
| 1715 | + * check the bottom edge first. | |
| 1716 | + */ | |
| 1717 | + --os-tabs-active-frost: linear-gradient( | |
| 1718 | + 180deg, | |
| 1719 | + #f4eff9 0%, | |
| 1720 | + #ffffff 58% | |
| 1721 | + ); | |
| 1722 | + /* | |
| 1723 | + * The crown. Holomesh over the top of the plate, masked away before | |
| 1724 | + * the joint, on exactly ONE tab at a time. Off in this palette: the | |
| 1725 | + * plain frosted plate is the tab, and the one identity moment on a | |
| 1726 | + * window is left to the wallpaper behind it. A theme that wants the | |
| 1727 | + * mesh back declares `var(--os-mesh-holo)` here, at around 0.3. | |
| 1728 | + */ | |
| 1729 | + --os-tabs-active-crown: none; | |
| 1730 | + --os-tabs-active-crown-opacity: 0.3; | |
| 1731 | + /* | |
| 1732 | + * The rail: one continuous line around the silhouette of | |
| 1733 | + * page-plus-tab, painted as a background-image through a mask, | |
| 1734 | + * which is why it is a gradient and not a colour. It is the accent | |
| 1735 | + * the user picked, flat, and it stays derived so the pick reaches | |
| 1736 | + * it on every theme. | |
| 1737 | + * | |
| 1738 | + * On this palette it is drawn at 0 opacity. The plate already | |
| 1739 | + * reads as "this tab" by being the one white shape on an Obsidian | |
| 1740 | + * strip that joins the page below it; a coloured line around that | |
| 1741 | + * silhouette was saying the same thing a second time, on every | |
| 1742 | + * window on the desk. The opacity is the literal a theme answers | |
| 1743 | + * (Legacy: 1), and the width stays at 2px because the strip's | |
| 1744 | + * geometry is built on it: the plate sits on the rail whether or | |
| 1745 | + * not the rail is visible, so turning it back on costs no seam. | |
| 1746 | + */ | |
| 1747 | + --os-tabs-rail: linear-gradient( | |
| 1748 | + var(--os-ui-accent, #f252fc), | |
| 1749 | + var(--os-ui-accent, #f252fc) | |
| 1750 | + ); | |
| 1751 | + --os-tabs-rail-opacity: 0; | |
| 1752 | + --os-tabs-rail-width: 2px; | |
| 1110 | 1753 | --os-media-tile-bg: rgba(255, 251, 255, 0.05); |
| 1111 | 1754 | --os-media-visual-bg: rgba(255, 251, 255, 0.04); |
| 1112 | 1755 | --os-skeleton-low: rgba(255, 251, 255, 0.05); |
| 1113 | 1756 | --os-skeleton-high: rgba(255, 251, 255, 0.13); |
| 1114 | - --os-drop-preview-bg: rgba(242, 82, 252, 0.1); | |
| 1115 | - --os-drop-preview-border: rgba(242, 82, 252, 0.55); | |
| 1757 | + --os-drop-preview-bg: rgba(255, 251, 255, 0.08); | |
| 1758 | + --os-drop-preview-border: rgba(255, 251, 255, 0.45); | |
| 1116 | 1759 | |
| 1117 | 1760 | /* |
| 1118 | 1761 | * File tiles — anywhere a `.os-file-tile` (or sibling |
| 1119 | 1762 | * canvas tile) renders. The defaults match the desk: Starlight |
| 1120 | - * labels over a dark sky, with a Pulse selection wash. | |
| 1763 | + * labels over a dark sky, and a selected tile is a lifted | |
| 1764 | + * Starlight plate, the same language as the selected row in the | |
| 1765 | + * Preferences sidebar, one step brighter than the hover wash so | |
| 1766 | + * the two states stay apart. The accent ring around it is a | |
| 1767 | + * derived colour with a literal width; this palette sets the | |
| 1768 | + * width to 0 (Legacy keeps 1px) so selection is depth, not colour. | |
| 1121 | 1769 | * |
| 1122 | 1770 | * Surfaces that are light in their own right override these in |
| 1123 | 1771 | * their own scope, e.g.: |
| 1124 | 1772 | * |
| @@ -1132,9 +1780,10 @@ | ||
| 1132 | 1780 | */ |
| 1133 | 1781 | --os-tile-fg: var(--os-fg, #fffbff); |
| 1134 | 1782 | --os-tile-fg-muted: rgba(255, 251, 255, 0.7); |
| 1135 | 1783 | --os-tile-hover-bg: rgba(255, 251, 255, 0.12); |
| 1136 | - --os-tile-selected-bg: rgba(242, 82, 252, 0.24); | |
| 1784 | + --os-tile-selected-bg: rgba(255, 251, 255, 0.16); | |
| 1785 | + --os-tile-selected-ring-width: 0px; | |
| 1137 | 1786 | --os-tile-focus-ring: var(--wp-admin-theme-color, #f252fc); |
| 1138 | 1787 | --os-tile-shortcut-bg: rgba(12, 11, 15, 0.7); |
| 1139 | 1788 | --os-tile-shortcut-fg: #fffbff; |
| 1140 | 1789 | --os-tile-shortcut-shadow: 0 1px 3px rgba(0, 0, 0, 0.6); |
| @@ -1188,8 +1837,23 @@ | ||
| 1188 | 1837 | * index) so the companion can perch on top of them, but below |
| 1189 | 1838 | * the dock (200) so it never covers navigation. |
| 1190 | 1839 | */ |
| 1191 | 1840 | --os-z-mio: 190; |
| 1841 | + /* | |
| 1842 | + * The desktop-name caption. Same reasoning as Mio, one step up: | |
| 1843 | + * above every window so a maximized Dashboard cannot hide it, and | |
| 1844 | + * above the companion, since a momentary message should not be | |
| 1845 | + * the thing that gets perched on. Still under the dock (200). | |
| 1846 | + */ | |
| 1847 | + --os-z-desktop-name: 195; | |
| 1848 | + /* | |
| 1849 | + * The notch — on the desk, under the windows (100 + stack index), | |
| 1850 | + * above the window-link wires (50) so a tie never draws across the | |
| 1851 | + * pill. It hangs over the strip a window's title bar occupies, and | |
| 1852 | + * a pill floating above someone's window is the shell talking over | |
| 1853 | + * the thing they are working in. | |
| 1854 | + */ | |
| 1855 | + --os-z-notch: 60; | |
| 1192 | 1856 | |
| 1193 | 1857 | /* |
| 1194 | 1858 | * Window-link ties (relation splines between windows). The accent |
| 1195 | 1859 | * follows the admin color scheme. Solid colors only — the resting |
| @@ -1266,4 +1930,42 @@ | ||
| 1266 | 1930 | * user who wants their admin scheme's hue picks it in the same panel. |
| 1267 | 1931 | * The `data-os-scheme` attribute is still stamped on the |
| 1268 | 1932 | * shell root for plugins and themes that key off it. |
| 1269 | 1933 | */ |
| 1934 | + | |
| 1935 | +/* The chat is a MIO surface even when its owning window wears a light theme. | |
| 1936 | + * Component colours remain reachable through the public MIO token family. */ | |
| 1937 | +body.os-active .os-mio-chat, | |
| 1938 | +body.os-active .os-mio-chat-launcher { | |
| 1939 | + --os-ui-fg: var(--os-mio-chat-fg, #1d2327); | |
| 1940 | + --os-ui-fg-muted: var(--os-mio-chat-muted, #646970); | |
| 1941 | + --os-window-bg: var(--os-mio-chat-bg, #fff); | |
| 1942 | + --os-ui-border: var(--os-mio-chat-border, #c3c4c7); | |
| 1943 | +} | |
| 1944 | + | |
| 1945 | +/* Ghost labels sit on the chat; the hero button owns a lighter mesh. */ | |
| 1946 | +body.os-active .os-mio-chat os-button[variant="ghost"], | |
| 1947 | +body.os-active .os-mio-chat-launcher { | |
| 1948 | + --os-ui-button-fg: var(--os-mio-chat-fg, #1d2327); | |
| 1949 | + --os-ui-button-bg-hover: var(--os-mio-chat-hover-bg, #f0f0f1); | |
| 1950 | +} | |
| 1951 | + | |
| 1952 | +body.os-active .os-mio-chat os-button[variant="primary"], | |
| 1953 | +body.os-active .os-mio-chat os-button[variant="holo"] { | |
| 1954 | + --os-ui-button-fg: var(--os-ui-holo-ink, #1d2327); | |
| 1955 | +} | |
| 1956 | + | |
| 1957 | +/* The component owns the launcher's single border and rounded surface. */ | |
| 1958 | +body.os-active .os-mio-chat-launcher { | |
| 1959 | + --os-ui-button-bg: var(--os-mio-launcher-bg, #ffffffa0); | |
| 1960 | + --os-ui-button-border: 1px solid var(--os-mio-chat-border, #c3c4c7); | |
| 1961 | + --os-ui-button-border-radius: var(--os-mio-chat-radius, 22px); | |
| 1962 | +} | |
| 1963 | + | |
| 1964 | +body.os-active .os-mio-callout { | |
| 1965 | + --os-ui-button-border: 0; | |
| 1966 | + --os-ui-button-border-radius: 50%; | |
| 1967 | + --os-ui-button-padding: 0; | |
| 1968 | + --os-ui-button-bg: transparent; | |
| 1969 | + --os-ui-button-fg: var(--os-mio-callout-fg, #382b38); | |
| 1970 | + --os-ui-button-bg-hover: color-mix(in srgb, var(--os-mio-callout-fg, #382b38) 10%, transparent); | |
| 1971 | +} | |