| @@ -25,10 +25,10 @@ | ||
| 25 | 25 | |
| 26 | 26 | .os-content-graph__toolbar { |
| 27 | 27 | display: flex; |
| 28 | 28 | align-items: center; |
| 29 | - gap: 12px; | |
| 30 | - padding: 10px 14px; | |
| 29 | + gap: 8px; | |
| 30 | + padding: 8px 16px; | |
| 31 | 31 | border-bottom: 1px solid var( --os-ui-border, #e2e6ec ); |
| 32 | 32 | background: var( --os-ui-surface-elevated, #fff ); |
| 33 | 33 | flex-wrap: wrap; |
| 34 | 34 | } |
| @@ -35,22 +35,37 @@ | ||
| 35 | 35 | |
| 36 | 36 | .os-content-graph__filters { |
| 37 | 37 | display: flex; |
| 38 | 38 | align-items: center; |
| 39 | - gap: 6px; | |
| 39 | + gap: 8px; | |
| 40 | 40 | flex-wrap: wrap; |
| 41 | 41 | } |
| 42 | 42 | |
| 43 | +/* | |
| 44 | + * Filter chips: toggle buttons, one per post type, all on by default. | |
| 45 | + * | |
| 46 | + * Same box as the `<os-select>` trigger beside them (8px block | |
| 47 | + * padding, 13px type at 1.5, a 1px border), so the row holds one | |
| 48 | + * height instead of three. | |
| 49 | + * | |
| 50 | + * The edge has its own token because an OFF chip is only an edge and a | |
| 51 | + * label, and it is the chip the user has to find again to undo a | |
| 52 | + * filter. The palette's plain border is the toolbar's own colour, so | |
| 53 | + * an off chip drawn with it has no outline at all. | |
| 54 | + */ | |
| 43 | 55 | .os-content-graph__chip { |
| 44 | 56 | display: inline-flex; |
| 45 | 57 | align-items: center; |
| 46 | 58 | gap: 6px; |
| 47 | - padding: 5px 10px; | |
| 48 | - border: 1px solid var( --os-ui-border, #d8dde4 ); | |
| 59 | + padding: 8px 12px; | |
| 60 | + border: 1px solid | |
| 61 | + var( --os-ui-cg-chip-border, var( --os-ui-border, #d8dde4 ) ); | |
| 49 | 62 | border-radius: 999px; |
| 50 | 63 | background: transparent; |
| 51 | 64 | color: var( --os-ui-fg-muted, #5a6473 ); |
| 52 | - font-size: 12px; | |
| 65 | + font: inherit; | |
| 66 | + font-size: 13px; | |
| 67 | + line-height: 1.5; | |
| 53 | 68 | cursor: pointer; |
| 54 | 69 | transition: background 0.12s ease, border-color 0.12s ease, |
| 55 | 70 | color 0.12s ease; |
| 56 | 71 | } |
| @@ -59,45 +74,91 @@ | ||
| 59 | 74 | background: var( --os-ui-hover, rgba( 79, 139, 243, 0.08 ) ); |
| 60 | 75 | color: var( --os-ui-fg, #1a1f2b ); |
| 61 | 76 | } |
| 62 | 77 | |
| 63 | -.os-content-graph__chip.is-active { | |
| 64 | - border-color: var( --os-ui-accent, #2c6be5 ); | |
| 65 | - background: var( --os-ui-accent-soft, rgba( 44, 107, 229, 0.12 ) ); | |
| 66 | - color: var( --os-ui-accent-strong, #1f4ea8 ); | |
| 78 | +.os-content-graph__chip:focus-visible { | |
| 79 | + outline: none; | |
| 80 | + box-shadow: var( | |
| 81 | + --os-ui-focus-ring, | |
| 82 | + 0 0 0 2px var( --wp-admin-theme-color, #2271b1 ) | |
| 83 | + ); | |
| 67 | 84 | } |
| 68 | 85 | |
| 86 | +/* | |
| 87 | + * On. Colour still derives from the accent (edge, wash and text), so a | |
| 88 | + * picked accent and a desktop theme both reach the chip. How MUCH of it | |
| 89 | + * shows is `--os-ui-cg-chip-accent`, a literal a palette answers on its | |
| 90 | + * own: each of the three layers is mixed from the accent by that | |
| 91 | + * amount, towards a neutral the palette also names. This palette | |
| 92 | + * answers 0%, so an on chip is the selected theme card in miniature: a | |
| 93 | + * Starlight outline around a dark fill. Legacy answers 100%, which is | |
| 94 | + * the accent chip exactly as it was. | |
| 95 | + * | |
| 96 | + * On and off differ in more than a shade: the outline is the brightest | |
| 97 | + * edge on the row and the fill is the darkest, against an off chip's | |
| 98 | + * grey edge on the toolbar itself. | |
| 99 | + */ | |
| 100 | +.os-content-graph__chip[aria-pressed='true'] { | |
| 101 | + border-color: color-mix( | |
| 102 | + in srgb, | |
| 103 | + var( --os-ui-accent, #2c6be5 ) var( --os-ui-cg-chip-accent, 100% ), | |
| 104 | + var( --os-ui-cg-chip-border-active, var( --os-ui-border-strong, #8c8f94 ) ) | |
| 105 | + ); | |
| 106 | + background-color: color-mix( | |
| 107 | + in srgb, | |
| 108 | + var( --os-ui-accent-soft, rgba( 44, 107, 229, 0.12 ) ) | |
| 109 | + var( --os-ui-cg-chip-accent, 100% ), | |
| 110 | + transparent | |
| 111 | + ); | |
| 112 | + background-image: linear-gradient( | |
| 113 | + var( --os-ui-cg-chip-fill-active, transparent ), | |
| 114 | + var( --os-ui-cg-chip-fill-active, transparent ) | |
| 115 | + ); | |
| 116 | + color: color-mix( | |
| 117 | + in srgb, | |
| 118 | + var( --os-ui-accent-strong, #1f4ea8 ) var( --os-ui-cg-chip-accent, 100% ), | |
| 119 | + var( --os-ui-fg, #1a1f2b ) | |
| 120 | + ); | |
| 121 | +} | |
| 122 | + | |
| 69 | 123 | .os-content-graph__chip .dashicons { |
| 70 | - font-size: 14px; | |
| 71 | - width: 14px; | |
| 72 | - height: 14px; | |
| 73 | - line-height: 14px; | |
| 124 | + font-size: 16px; | |
| 125 | + width: 16px; | |
| 126 | + height: 16px; | |
| 127 | + line-height: 16px; | |
| 74 | 128 | } |
| 75 | 129 | |
| 76 | 130 | .os-content-graph__chip-count { |
| 77 | 131 | font-variant-numeric: tabular-nums; |
| 78 | - font-size: 11px; | |
| 79 | - background: var( --os-ui-surface, #f1f3f7 ); | |
| 80 | - padding: 1px 6px; | |
| 132 | + font-size: 12px; | |
| 133 | + line-height: 16px; | |
| 134 | + background: var( | |
| 135 | + --os-ui-cg-chip-count-bg, | |
| 136 | + var( --os-ui-surface, #f1f3f7 ) | |
| 137 | + ); | |
| 138 | + color: var( --os-ui-cg-chip-count-fg, inherit ); | |
| 139 | + padding: 0 6px; | |
| 81 | 140 | border-radius: 999px; |
| 82 | 141 | } |
| 83 | 142 | |
| 84 | 143 | /* |
| 85 | - * The count pill on an ACTIVE chip is a bright fill, and the chip's | |
| 86 | - * own text colour is `--os-ui-accent-strong` — a light tint, by design, | |
| 87 | - * because the chip body around it is a dark accent wash. The two meet | |
| 88 | - * on this pill and cancel out (Nebula on 70% white is ~1.7:1). | |
| 89 | - * | |
| 90 | - * So the pill names its own text, exactly the way `--os-ui-ribbon-fg` | |
| 91 | - * and `--os-ui-step-chip-fg` do for the other bright fills in the kit. | |
| 92 | - * Unset it inherits the chip's colour, which is what a light theme | |
| 93 | - * wants — there the chip text is already dark. | |
| 144 | + * The count pill on an ON chip. Pre-brand it is a bright 70%-white fill | |
| 145 | + * over the accent wash, and it names its own text colour, exactly the | |
| 146 | + * way `--os-ui-ribbon-fg` and `--os-ui-step-chip-fg` do for the other | |
| 147 | + * bright fills in the kit: the chip's own colour on that pill would | |
| 148 | + * cancel out. Both halves are tokens, so a palette that has no bright | |
| 149 | + * fill here can say so. Unset, the text inherits the chip's colour, | |
| 150 | + * which is what a light theme wants. | |
| 94 | 151 | */ |
| 95 | -.os-content-graph__chip.is-active .os-content-graph__chip-count { | |
| 96 | - background: rgba( 255, 255, 255, 0.7 ); | |
| 152 | +.os-content-graph__chip[aria-pressed='true'] .os-content-graph__chip-count { | |
| 153 | + background: var( | |
| 154 | + --os-ui-cg-chip-count-bg-active, | |
| 155 | + rgba( 255, 255, 255, 0.7 ) | |
| 156 | + ); | |
| 97 | 157 | color: var( --os-ui-cg-chip-count-fg-active, inherit ); |
| 98 | 158 | } |
| 99 | 159 | |
| 160 | +/* The kit field fills the column; the results list hangs off it. */ | |
| 100 | 161 | .os-content-graph__search { |
| 101 | 162 | position: relative; |
| 102 | 163 | flex: 1; |
| 103 | 164 | min-width: 220px; |
| @@ -103,24 +164,13 @@ | ||
| 103 | 164 | min-width: 220px; |
| 104 | 165 | max-width: 320px; |
| 105 | 166 | } |
| 106 | 167 | |
| 107 | -.os-content-graph__search-input { | |
| 168 | +.os-content-graph__search os-text-field { | |
| 169 | + display: block; | |
| 108 | 170 | width: 100%; |
| 109 | - padding: 6px 10px; | |
| 110 | - border: 1px solid var( --os-ui-border, #d8dde4 ); | |
| 111 | - border-radius: 6px; | |
| 112 | - background: var( --os-ui-surface, #fff ); | |
| 113 | - color: inherit; | |
| 114 | - font-size: 13px; | |
| 115 | 171 | } |
| 116 | 172 | |
| 117 | -.os-content-graph__search-input:focus { | |
| 118 | - outline: none; | |
| 119 | - border-color: var( --os-ui-accent, #2c6be5 ); | |
| 120 | - box-shadow: 0 0 0 3px var( --os-ui-accent-soft, rgba( 44, 107, 229, 0.18 ) ); | |
| 121 | -} | |
| 122 | - | |
| 123 | 173 | .os-content-graph__search-results { |
| 124 | 174 | position: absolute; |
| 125 | 175 | top: calc( 100% + 4px ); |
| 126 | 176 | left: 0; |
| @@ -210,9 +260,9 @@ | ||
| 210 | 260 | } |
| 211 | 261 | |
| 212 | 262 | .os-content-graph__toolbar-status { |
| 213 | 263 | margin-left: 8px; |
| 214 | - font-size: 11px; | |
| 264 | + font-size: 12px; | |
| 215 | 265 | color: var( --os-ui-fg-muted, #6b7280 ); |
| 216 | 266 | font-variant-numeric: tabular-nums; |
| 217 | 267 | } |
| 218 | 268 | |
| @@ -303,8 +353,15 @@ | ||
| 303 | 353 | width: 100%; |
| 304 | 354 | height: 100%; |
| 305 | 355 | } |
| 306 | 356 | |
| 357 | +/* | |
| 358 | + * Full-canvas loading wash. Resting state is invisible: the bundle | |
| 359 | + * arms it on every graph fetch and adds `--visible` only once the | |
| 360 | + * fetch has run long enough to be worth interrupting the board for | |
| 361 | + * (see `src/content-graph/loading-overlay.ts`). A class rather than | |
| 362 | + * `[hidden]` so the wash fades instead of popping. | |
| 363 | + */ | |
| 307 | 364 | .os-content-graph__loading { |
| 308 | 365 | position: absolute; |
| 309 | 366 | inset: 0; |
| 310 | 367 | display: flex; |
| @@ -309,17 +366,92 @@ | ||
| 309 | 366 | inset: 0; |
| 310 | 367 | display: flex; |
| 311 | 368 | align-items: center; |
| 312 | 369 | justify-content: center; |
| 313 | - background: rgba( 247, 248, 251, 0.7 ); | |
| 370 | + /* | |
| 371 | + * A translucent sheet of the stage's own surface, so it dims a | |
| 372 | + * dark board as well as a light one. The literal is the pre-brand | |
| 373 | + * light surface, kept as the floor if the token is missing. | |
| 374 | + */ | |
| 375 | + background: color-mix( | |
| 376 | + in srgb, | |
| 377 | + var( --os-ui-surface, #f7f8fb ) 72%, | |
| 378 | + transparent | |
| 379 | + ); | |
| 314 | 380 | pointer-events: none; |
| 315 | - z-index: 2; | |
| 381 | + z-index: 3; | |
| 382 | + opacity: 0; | |
| 383 | + visibility: hidden; | |
| 384 | + transition: opacity 0.2s ease, visibility 0s linear 0.2s; | |
| 316 | 385 | } |
| 317 | 386 | |
| 387 | +.os-content-graph__loading--visible { | |
| 388 | + opacity: 1; | |
| 389 | + visibility: visible; | |
| 390 | + transition: opacity 0.2s ease; | |
| 391 | +} | |
| 392 | + | |
| 318 | 393 | .os-content-graph__loading[ hidden ] { |
| 319 | 394 | display: none; |
| 320 | 395 | } |
| 321 | 396 | |
| 397 | +@media ( prefers-reduced-motion: reduce ) { | |
| 398 | + .os-content-graph__loading { | |
| 399 | + transition: none; | |
| 400 | + } | |
| 401 | +} | |
| 402 | + | |
| 403 | +/* ---------- Board notices (empty board, no threads yet) ----------- */ | |
| 404 | + | |
| 405 | +/* | |
| 406 | + * <os-empty-state> owns the icon + copy; this is placement only. It | |
| 407 | + * covers the (empty) canvas and lets pointer events through so the | |
| 408 | + * stage still pans and the dot grid still reads as a board. | |
| 409 | + */ | |
| 410 | +.os-content-graph__empty { | |
| 411 | + position: absolute; | |
| 412 | + inset: 0; | |
| 413 | + z-index: 2; | |
| 414 | + pointer-events: none; | |
| 415 | +} | |
| 416 | + | |
| 417 | +/* | |
| 418 | + * "No threads yet" note. Top-left, out of the way of a small board | |
| 419 | + * centred in the stage and of the side panel on the right. Real DOM | |
| 420 | + * text over the canvas, so it is translatable and follows the theme | |
| 421 | + * tokens like the rest of the chrome; pointer-events are off so a | |
| 422 | + * pan that starts on it still pans the board. The bundle hides it | |
| 423 | + * while a grouping is active, when the cluster labels need the space. | |
| 424 | + */ | |
| 425 | +.os-content-graph__hint { | |
| 426 | + position: absolute; | |
| 427 | + top: 12px; | |
| 428 | + left: 12px; | |
| 429 | + z-index: 2; | |
| 430 | + max-width: 320px; | |
| 431 | + padding: 10px 14px 12px; | |
| 432 | + border: 1px solid var( --os-ui-border, #e2e6ec ); | |
| 433 | + border-radius: 8px; | |
| 434 | + background: var( --os-ui-surface-elevated, #fff ); | |
| 435 | + color: var( --os-ui-fg-muted, #5a6473 ); | |
| 436 | + box-shadow: 0 1px 3px rgba( 0, 0, 0, 0.08 ); | |
| 437 | + font-size: 12px; | |
| 438 | + line-height: 1.45; | |
| 439 | + pointer-events: none; | |
| 440 | +} | |
| 441 | + | |
| 442 | +.os-content-graph__hint-title { | |
| 443 | + display: block; | |
| 444 | + margin-bottom: 3px; | |
| 445 | + color: var( --os-ui-fg, #1a1f2b ); | |
| 446 | + font-size: 12.5px; | |
| 447 | + font-weight: 600; | |
| 448 | +} | |
| 449 | + | |
| 450 | +.os-content-graph__hint-body { | |
| 451 | + margin: 0; | |
| 452 | +} | |
| 453 | + | |
| 322 | 454 | /* ---------- Right side panel: breadcrumb --------------------------- */ |
| 323 | 455 | |
| 324 | 456 | .os-content-graph__panel-breadcrumb { |
| 325 | 457 | display: flex; |
| @@ -664,9 +796,9 @@ | ||
| 664 | 796 | |
| 665 | 797 | .os-content-graph__btn--primary { |
| 666 | 798 | border-color: var( --os-ui-accent, #2c6be5 ); |
| 667 | 799 | background: var( --os-ui-accent, #2c6be5 ); |
| 668 | - color: var( --os-ui-fg-on-accent, #fff ); | |
| 800 | + color: var( --os-ui-accent-ink, var( --os-ui-fg-on-accent, #fff ) ); | |
| 669 | 801 | } |
| 670 | 802 | |
| 671 | 803 | .os-content-graph__btn--primary:hover { |
| 672 | 804 | background: var( --os-ui-accent-strong, #1f4ea8 ); |