| @@ -53,9 +53,10 @@ | ||
| 53 | 53 | margin: 0 -8px; |
| 54 | 54 | border-radius: 8px; |
| 55 | 55 | } |
| 56 | 56 | |
| 57 | -.dm-drafts__row:hover { | |
| 57 | +.dm-drafts__row:hover, | |
| 58 | +.dm-drafts__row:has( a.dm-drafts__link:focus-visible ) { | |
| 58 | 59 | background: color-mix( in srgb, currentColor 10%, transparent ); |
| 59 | 60 | } |
| 60 | 61 | |
| 61 | 62 | .dm-drafts__row.is-trashing { |
| @@ -70,13 +71,69 @@ | ||
| 70 | 71 | align-items: baseline; |
| 71 | 72 | justify-content: space-between; |
| 72 | 73 | gap: 10px; |
| 73 | 74 | padding: 6px 8px; |
| 75 | + text-decoration: none; | |
| 76 | +} | |
| 77 | + | |
| 78 | +/* | |
| 79 | + * Colour and focus ring, restated with the element in the selector. | |
| 80 | + * | |
| 81 | + * wp-admin repaints every `<a>` on interaction: `a:hover, a:active` | |
| 82 | + * to #135e96 and `a:focus` to #043959, plus a hairline ring and a 2px | |
| 83 | + * radius. Those selectors out-specify a plain | |
| 84 | + * `.dm-drafts__link { color: inherit }`, so the row a user had just | |
| 85 | + * clicked went dark navy on dark glass, about 1.5:1 against the card | |
| 86 | + * it sits on. The one draft they could no longer read was the one | |
| 87 | + * they were working on. Leading `a.` out-specifies core in turn, and | |
| 88 | + * every interactive state is named so none of them can leak back. | |
| 89 | + * | |
| 90 | + * The ring is ours for the same reason the text is. Core's is the | |
| 91 | + * neat accent on a 2px radius: it neither follows the row's 8px | |
| 92 | + * corner nor holds contrast on a card that can be arbitrarily light | |
| 93 | + * or dark, since the widget is glass over the wallpaper. | |
| 94 | + * `--dm-drafts-accent` (defined further down) is the accent already | |
| 95 | + * mixed toward `currentColor` for exactly that reason. | |
| 96 | + */ | |
| 97 | +a.dm-drafts__link, | |
| 98 | +a.dm-drafts__link:hover, | |
| 99 | +a.dm-drafts__link:focus, | |
| 100 | +a.dm-drafts__link:active, | |
| 101 | +a.dm-drafts__link:visited { | |
| 74 | 102 | color: inherit; |
| 75 | - text-decoration: none; | |
| 103 | + border-radius: 8px; | |
| 76 | 104 | } |
| 77 | 105 | |
| 78 | 106 | /* |
| 107 | + * Press feedback. | |
| 108 | + * | |
| 109 | + * Core's was a colour swap (`a:active` to #135e96), and it went out | |
| 110 | + * with the rest of the leak above, which left a press saying nothing | |
| 111 | + * at all. The shell answers a press with a scale rather than a hue: | |
| 112 | + * `.os-constellation__row`, a row of this same shape, uses the same | |
| 113 | + * 0.985. It costs no colour, which is what a card sitting on an | |
| 114 | + * arbitrary wallpaper can least afford to spend, and unlike a hover | |
| 115 | + * fill it works under a finger as well as a pointer. | |
| 116 | + */ | |
| 117 | +a.dm-drafts__link:active { | |
| 118 | + transform: scale( 0.985 ); | |
| 119 | +} | |
| 120 | + | |
| 121 | +a.dm-drafts__link:focus-visible { | |
| 122 | + outline: 2px solid var( --dm-drafts-accent, #2271b1 ); | |
| 123 | + outline-offset: -2px; | |
| 124 | + box-shadow: none; | |
| 125 | +} | |
| 126 | + | |
| 127 | +/* Core rings on `:focus`, not `:focus-visible`, so a click parks a | |
| 128 | + ring on the row long after the draft has opened in its window. | |
| 129 | + Keyboard focus keeps the ring above; pointer focus drops it. */ | |
| 130 | +a.dm-drafts__link:focus:not( :focus-visible ) { | |
| 131 | + outline: none; | |
| 132 | + box-shadow: none; | |
| 133 | +} | |
| 134 | + | |
| 135 | +/* | |
| 79 | 136 | * Row actions (Trash, Suggest) are `<os-button variant="ghost">` hosts — |
| 80 | 137 | * the component owns the focus ring, disabled semantics and theming; these |
| 81 | 138 | * rules only tune it into a 26px icon square via the documented |
| 82 | 139 | * `--os-ui-button-*` surface and the `button` shadow part. Both stay hidden |
| @@ -106,12 +163,20 @@ | ||
| 106 | 163 | height: 26px; |
| 107 | 164 | } |
| 108 | 165 | |
| 109 | 166 | .dm-drafts__row:hover .dm-drafts__trash, |
| 110 | -.dm-drafts__row:hover .dm-drafts__spark, | |
| 167 | +.dm-drafts__row:hover .dm-drafts__spark { | |
| 168 | + opacity: 0.65; | |
| 169 | +} | |
| 170 | + | |
| 171 | +/* Keyboard focus goes to full strength, where the pointer only gets | |
| 172 | + there on hover: at 0.65 the control's own focus ring was being faded | |
| 173 | + along with it, and a focus indicator is the one thing that cannot | |
| 174 | + afford to be three-quarters there. `:focus-within` on the host | |
| 175 | + matches when the button inside the shadow root has focus. */ | |
| 111 | 176 | .dm-drafts__trash:focus-within, |
| 112 | 177 | .dm-drafts__spark:focus-within { |
| 113 | - opacity: 0.65; | |
| 178 | + opacity: 1; | |
| 114 | 179 | } |
| 115 | 180 | |
| 116 | 181 | .dm-drafts__trash:hover { |
| 117 | 182 | opacity: 1; |
| @@ -210,18 +275,27 @@ | ||
| 210 | 275 | * |
| 211 | 276 | * `<os-notice>`'s built-in palette is tuned for a light window surface — |
| 212 | 277 | * its text defaults to `--os-ui-fg` (#1d2327) over an 8%-alpha tone wash. |
| 213 | 278 | * A widget card is glass over the wallpaper and can be arbitrarily dark, |
| 214 | - * so that pairing collapses to unreadable. Drive the component's | |
| 215 | - * documented color surface from `currentColor` instead: the text tracks | |
| 216 | - * whatever the card already proved legible, and the tone survives in the | |
| 217 | - * accent stripe + icon, each mixed toward `currentColor` so it stays | |
| 218 | - * visible on a dark and a light card alike. | |
| 279 | + * so that pairing collapses to unreadable. Drive the component's text | |
| 280 | + * from `currentColor` instead: it tracks whatever the card already | |
| 281 | + * proved legible. | |
| 282 | + * | |
| 283 | + * The tone has to carry the surface, not only the stripe. A neutral | |
| 284 | + * currentColor wash over the panel's own currentColor wash is a grey | |
| 285 | + * box inside a grey box, and an error read as background — the exact | |
| 286 | + * complaint that landed the failure notices here. So each tone paints | |
| 287 | + * its wash and a full hairline border from the palette's own notice | |
| 288 | + * tokens (declared for a dark surface), falling back to the tone | |
| 289 | + * colour mixed toward transparent, which survives a light card too. | |
| 290 | + * The stripe colour is mixed toward currentColor so it stays visible | |
| 291 | + * on either. | |
| 292 | + * | |
| 293 | + * These are document-tree rules on the host, so they outrank the | |
| 294 | + * component's own `:host( [ tone ] )` declarations by design. | |
| 219 | 295 | */ |
| 220 | 296 | .dm-drafts__notice { |
| 221 | 297 | --os-ui-notice-color: currentColor; |
| 222 | - --os-ui-notice-bg: color-mix( in srgb, currentColor 8%, transparent ); | |
| 223 | - --os-ui-notice-border: transparent; | |
| 224 | 298 | --os-ui-notice-font: inherit; |
| 225 | 299 | --os-ui-notice-success: color-mix( |
| 226 | 300 | in srgb, |
| 227 | 301 | var( --os-ui-success-fg, #46b450 ) 65%, |
| @@ -240,8 +314,47 @@ | ||
| 240 | 314 | /* Out-specifies the component's own `:host` padding — 10px/14px is |
| 241 | 315 | window-banner scale and reads as a slab inside an 8px panel. */ |
| 242 | 316 | padding: 8px 10px; |
| 243 | 317 | border-radius: 6px; |
| 318 | + border: 1px solid var( --os-ui-notice-border ); | |
| 319 | + border-inline-start: 4px solid var( --os-ui-notice-accent ); | |
| 320 | +} | |
| 321 | + | |
| 322 | +.dm-drafts__notice[ tone='success' ] { | |
| 323 | + --os-ui-notice-accent: var( --os-ui-notice-success ); | |
| 324 | + --os-ui-notice-bg: var( | |
| 325 | + --os-ui-notice-success-bg, | |
| 326 | + color-mix( in srgb, var( --os-ui-success-fg, #46b450 ) 14%, transparent ) | |
| 327 | + ); | |
| 328 | + --os-ui-notice-border: var( | |
| 329 | + --os-ui-notice-success-border, | |
| 330 | + color-mix( in srgb, var( --os-ui-success-fg, #46b450 ) 40%, transparent ) | |
| 331 | + ); | |
| 332 | +} | |
| 333 | + | |
| 334 | +.dm-drafts__notice[ tone='warning' ] { | |
| 335 | + --os-ui-notice-accent: var( --os-ui-notice-warning ); | |
| 336 | + --os-ui-notice-bg: var( | |
| 337 | + --os-ui-notice-warning-bg, | |
| 338 | + color-mix( in srgb, var( --os-ui-warning-fg, #dba617 ) 14%, transparent ) | |
| 339 | + ); | |
| 340 | + --os-ui-notice-border: var( | |
| 341 | + --os-ui-notice-warning-border, | |
| 342 | + color-mix( in srgb, var( --os-ui-warning-fg, #dba617 ) 40%, transparent ) | |
| 343 | + ); | |
| 344 | +} | |
| 345 | + | |
| 346 | +.dm-drafts__notice[ tone='error' ], | |
| 347 | +.dm-drafts__notice[ tone='danger' ] { | |
| 348 | + --os-ui-notice-accent: var( --os-ui-notice-error ); | |
| 349 | + --os-ui-notice-bg: var( | |
| 350 | + --os-ui-notice-error-bg, | |
| 351 | + color-mix( in srgb, var( --os-ui-danger, #d63638 ) 14%, transparent ) | |
| 352 | + ); | |
| 353 | + --os-ui-notice-border: var( | |
| 354 | + --os-ui-notice-error-border, | |
| 355 | + color-mix( in srgb, var( --os-ui-danger, #d63638 ) 40%, transparent ) | |
| 356 | + ); | |
| 244 | 357 | } |
| 245 | 358 | |
| 246 | 359 | /* Readiness — read-only diagnosis at the top of the panel, tone-coded |
| 247 | 360 | success when nothing is missing, warning otherwise. */ |