Three small accessibility items, done together because they share one probe and
one measurement method. tests/a11y_check.py, 22 checks, all passing.
S10 — ANNOUNCEMENTS. The app had zero aria-live regions; login.html's
role="alert" / role="status" pair was the only correct example in the codebase.
Both toasts now take an optional kind and set the role BEFORE the text, because
assistive technology announces on the content change and a role applied after
describes the next message rather than this one. The sync badge announces
politely.
Admin banners are handled by a rule rather than by editing thirteen assignment
sites: a MutationObserver in console-util.js marks anything carrying `.bad` as
role=alert and everything else role=status. Thirteen edits is thirteen chances to
get it wrong, and any banner added later would have missed out. The probe checks
a banner created after load, which is the case that would have regressed.
S11 — CONTRAST. Re-measured rather than quoting either published figure, as the
file map asked. #8d8d8d is 3.32:1 on white, not "about 2.9:1" as the plan says;
console.css:103's 3.3:1 was right. On the shaded surfaces it is worse - 3.01:1 on
a success banner. --cds-text-helper (#6f6f6f) clears 4.5:1 on all seven
backgrounds the app actually paints, tightest 4.56:1.
Fixed once, in the token, so all three sheets inherit it. console.css's local
override is gone, as the task requires. Disabled text was repointed to
--cds-text-disabled rather than darkened with everything else: making disabled
text MORE legible makes a disabled control look enabled.
The probe measures against the background actually painted behind each element,
walking ancestors for the first non-transparent one - not an assumed white, which
is how "passes on paper" and "fails on the page" come to disagree.
S12 — FOCUS. An app-wide :focus-visible floor in theme-light.css at zero
specificity, so any component can still draw its own. Filled controls get an
explicit rule at class specificity: a blue ring inside a blue button measures
1.00:1, which is not a subtle problem but no indicator at all. console.css's
inset ring had exactly that defect on button.primary.
`outline: none` is down from six to one, and that one (.wpc-search-input) has its
replacement in the rule above it - the shell rings on :focus-within, and ringing
both would draw two rectangles.
TWO REAL DEFECTS THE PROBE FOUND that reading would not have:
- .wp-navbtn's ring is white, which is right on the near-black app bar and
invisible on the creator's white header. Same button, same class, two hosts,
1.00:1 on one of them.
- The comment drawer is translated off-screen when closed, and a transform
moves a thing without removing it from the tab order. Its name field,
textarea, Add button and close button were all still focusable: a keyboard
user could tab into a panel they could not see and could not tell they were
in. Now visibility:hidden while closed, with the transition delayed so it
still animates both ways.
The probe itself needed three corrections, each of which was a wrong answer
before it was a right one, and each worth knowing:
- focus emulation must be ON, or :focus-visible never matches in headless and
every element reports clean - a pass that means nothing.
- which surface a ring is drawn against depends on the offset the BROWSER uses,
not the one the stylesheet asked for. Chromium redraws a low-contrast author
ring in white at offset 0 on a filled control, which is more contrast than was
requested; measuring that against the parent scores it 1.00:1 and calls a
correct ring a defect.
- focus() on a hidden control does nothing, so the probe has to ask whether the
focus actually landed. A closed drawer still has layout; a bounding box is not
evidence that anyone can reach it.
Metric 7, aria-live regions: was 0 at wave 0, now 13 role/aria-live sites across
7 files.
browser_check 71/71, f_items 5 FIXED / F6 REPRODUCES.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
567 lines
24 KiB
CSS
567 lines
24 KiB
CSS
/* IBM Carbon Design System - Light Theme (g10) */
|
|
:root {
|
|
--cds-interactive-01: #0f62fe;
|
|
--cds-interactive-02: #393939;
|
|
--cds-interactive-03: #0f62fe;
|
|
--cds-interactive-04: #0f62fe;
|
|
--cds-ui-background: #f4f4f4;
|
|
--cds-ui-01: #ffffff;
|
|
--cds-ui-02: #f4f4f4;
|
|
--cds-ui-03: #e0e0e0;
|
|
--cds-ui-04: #8d8d8d;
|
|
--cds-ui-05: #161616;
|
|
--cds-text-01: #161616;
|
|
--cds-text-02: #525252;
|
|
--cds-text-03: #a8a8a8;
|
|
--cds-text-04: #ffffff;
|
|
--cds-text-05: #6f6f6f;
|
|
--cds-text-error: #da1e28;
|
|
--cds-icon-01: #161616;
|
|
--cds-icon-02: #525252;
|
|
--cds-icon-03: #ffffff;
|
|
--cds-link-01: #0f62fe;
|
|
--cds-link-02: #0043ce;
|
|
--cds-inverse-link: #78a9ff;
|
|
--cds-field-01: #ffffff;
|
|
--cds-field-02: #f4f4f4;
|
|
--cds-inverse-01: #ffffff;
|
|
--cds-inverse-02: #393939;
|
|
--cds-support-01: #da1e28;
|
|
--cds-support-02: #198038;
|
|
--cds-support-03: #f1c21b;
|
|
--cds-support-04: #0043ce;
|
|
--cds-inverse-support-01: #fa4d56;
|
|
--cds-inverse-support-02: #42be65;
|
|
--cds-inverse-support-03: #f1c21b;
|
|
--cds-inverse-support-04: #4589ff;
|
|
--cds-overlay: rgba(22, 22, 22, .5);
|
|
--cds-danger: #da1e28;
|
|
--cds-focus: #0f62fe;
|
|
--cds-hover-primary: #0353e9;
|
|
--cds-active-primary: #002d9c;
|
|
--cds-hover-primary-text: #0043ce;
|
|
--cds-hover-secondary: #4c4c4c;
|
|
--cds-active-secondary: #6f6f6f;
|
|
--cds-hover-tertiary: #0353e9;
|
|
--cds-active-tertiary: #002d9c;
|
|
--cds-hover-ui: #e5e5e5;
|
|
--cds-hover-light-ui: #e5e5e5;
|
|
--cds-hover-selected-ui: #cacaca;
|
|
--cds-active-ui: #c6c6c6;
|
|
--cds-active-light-ui: #cacaca;
|
|
--cds-selected-ui: #e5e5e5;
|
|
--cds-selected-light-ui: #e5e5e5;
|
|
--cds-inverse-hover-ui: #353535;
|
|
--cds-hover-danger: #ba121b;
|
|
--cds-active-danger: #750e13;
|
|
--cds-hover-row: #f4f4f4;
|
|
--cds-visited-link: #0043ce;
|
|
--cds-disabled-01: #f4f4f4;
|
|
--cds-disabled-02: #bdbdbd;
|
|
--cds-disabled-03: #8d8d8d;
|
|
--cds-button-primary: #0f62fe;
|
|
--cds-button-secondary: #393939;
|
|
--cds-button-tertiary: #0f62fe;
|
|
--cds-button-danger-primary: #da1e28;
|
|
--cds-button-danger-secondary: #da1e28;
|
|
--cds-background: #f4f4f4;
|
|
--cds-background-inverse: #161616;
|
|
--cds-background-brand: #0f62fe;
|
|
--cds-background-active: #e5e5e5;
|
|
--cds-background-hover: #e8e8e8;
|
|
--cds-background-selected: #e5e5e5;
|
|
--cds-background-selected-hover: #cacaca;
|
|
--cds-layer: #ffffff;
|
|
--cds-layer-accent: #f4f4f4;
|
|
--cds-layer-accent-hover: #e8e8e8;
|
|
--cds-layer-accent-active: #e0e0e0;
|
|
--cds-layer-hover: #e8e8e8;
|
|
--cds-layer-active: #e5e5e5;
|
|
--cds-layer-selected: #e5e5e5;
|
|
--cds-layer-selected-hover: #cacaca;
|
|
--cds-layer-selected-inverse: #393939;
|
|
--cds-field: #ffffff;
|
|
--cds-field-hover: #e8e8e8;
|
|
--cds-border-subtle: #e0e0e0;
|
|
--cds-border-strong: #8d8d8d;
|
|
--cds-border-inverse: #393939;
|
|
--cds-border-interactive: #0f62fe;
|
|
--cds-border-subtle-selected: #c6c6c6;
|
|
--cds-text-primary: #161616;
|
|
--cds-text-secondary: #525252;
|
|
--cds-text-placeholder: #a8a8a8;
|
|
--cds-text-helper: #6f6f6f;
|
|
--cds-text-on-color: #ffffff;
|
|
--cds-text-inverse: #f4f4f4;
|
|
--cds-text-disabled: #bdbdbd;
|
|
--cds-link-primary: #0f62fe;
|
|
--cds-link-secondary: #0043ce;
|
|
--cds-link-visited: #0043ce;
|
|
--cds-link-inverse: #78a9ff;
|
|
--cds-link-primary-hover: #0353e9;
|
|
--cds-icon-primary: #161616;
|
|
--cds-icon-secondary: #525252;
|
|
--cds-icon-on-color: #ffffff;
|
|
--cds-icon-inverse: #f4f4f4;
|
|
--cds-icon-disabled: #bdbdbd;
|
|
--cds-support-error: #da1e28;
|
|
--cds-support-success: #198038;
|
|
--cds-support-warning: #f1c21b;
|
|
--cds-support-info: #0043ce;
|
|
--cds-support-error-inverse: #fa4d56;
|
|
--cds-support-success-inverse: #42be65;
|
|
--cds-support-warning-inverse: #f1c21b;
|
|
--cds-support-info-inverse: #4589ff;
|
|
--cds-interactive: #0f62fe;
|
|
--cds-shadow: rgba(0, 0, 0, .16);
|
|
/* Carbon's own $highlight, which the page sheets each declared for themselves
|
|
as --accent-soft / --primary-light / --accent-dim. Real Carbon name and
|
|
real Carbon value; this sheet simply never carried it. (T3.2 / S5) */
|
|
--cds-highlight: #edf5ff; /* blue-10 */
|
|
}
|
|
|
|
/* ============================================================================
|
|
SUITE TOKENS (--wp-*) — roles IBM Carbon does not define
|
|
----------------------------------------------------------------------------
|
|
Everything above is Carbon g10 and should stay that way. Everything here is a
|
|
value the suite invented because Carbon has no slot for it. Keeping the two
|
|
namespaces apart is what stops the next person "correcting" #8e6a00 into a
|
|
Carbon yellow and breaking the warning banners.
|
|
|
|
Together with the --cds-* block above, this is the one place a colour,
|
|
spacing or type value is defined (CLAUDE.md, "The token rule"). Page
|
|
stylesheets alias these; they declare nothing of their own.
|
|
|
|
Full mapping, and the provenance of every value: docs/reference/tokens.md
|
|
============================================================================ */
|
|
:root {
|
|
/* -- status fills and their text ------------------------------------------
|
|
The greens and reds are Carbon 10-step values; the amber pair is not
|
|
Carbon at all (Carbon yellow-10 is #fcf4d6). --wp-status-warning-text is
|
|
the suite's own accessible amber for text sitting on --wp-status-warning-bg. */
|
|
--wp-status-success-bg: #defbe6;
|
|
--wp-status-error-bg: #fff1f1;
|
|
--wp-status-warning-bg: #fdf6dd;
|
|
--wp-status-warning-text: #8e6a00;
|
|
/* Four points from --wp-status-warning-text and doing the same job, on the
|
|
field view's warn pill. Almost certainly a typo rather than a decision, but
|
|
merging it moves a rendered colour, so T3.2 names it and T3.5 merges it.
|
|
BL-009 / docs/reference/tokens.md section 8-K. */
|
|
--wp-status-warning-text-alt: #8a6d00;
|
|
/* Carbon green-70. The value is Carbon, the role is not — Carbon has no
|
|
"hover for a green fill", because green is not one of its action colours.
|
|
Declared in no sheet today; written raw in five places. */
|
|
--wp-hover-success: #0e6027;
|
|
|
|
/* -- status borders -------------------------------------------------------
|
|
Eight values doing four jobs, because two sheets each picked their own.
|
|
Every one of them renders somewhere today, so T3.2 names all eight rather
|
|
than choosing four winners — choosing is a visual change. T3.5 decides.
|
|
See docs/reference/tokens.md section 8-D. */
|
|
--wp-status-success-border-a: #a7f0ba; /* green-20, Carbon — console */
|
|
--wp-status-success-border-b: #b6e3c6; /* not Carbon — creator */
|
|
--wp-status-error-border-a: #ffd7d9; /* red-20, Carbon — console */
|
|
--wp-status-error-border-b: #f3c4c4; /* not Carbon — creator, release banner */
|
|
--wp-status-error-border-c: #ffc4c4; /* not Carbon — creator, .crit-tag */
|
|
--wp-status-warning-border-a: #fddc69; /* yellow-30, Carbon — console */
|
|
--wp-status-warning-border-b: #f0d9ad; /* not Carbon — creator */
|
|
--wp-accent-border-a: #b9d2fb; /* neither is Carbon blue-20 (#d0e2ff) */
|
|
--wp-accent-border-b: #cdd9f2;
|
|
|
|
/* -- type ------------------------------------------------------------------
|
|
Three mono stacks and two sans stacks exist because five sheets each wrote
|
|
their own. They are NOT interchangeable: on a machine without IBM Plex Mono
|
|
installed — which is most of the target environment — each falls through to
|
|
a different face. Unifying them changes what admin and users render, so
|
|
T3.2 names all five and T3.5 picks. See docs/reference/tokens.md 6d, 8-H. */
|
|
--wp-font-sans: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
|
|
--wp-font-sans-2: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
--wp-font-mono: 'IBM Plex Mono', ui-monospace, 'Cascadia Mono', 'Segoe UI Mono', Consolas, monospace;
|
|
--wp-font-mono-2: 'IBM Plex Mono', 'Cascadia Mono', Consolas, monospace;
|
|
--wp-font-mono-3: 'IBM Plex Mono', ui-monospace, Consolas, monospace;
|
|
|
|
/* -- spacing ---------------------------------------------------------------
|
|
Promoted from console.css, which is the only sheet in the repo whose
|
|
spacing is a scale rather than a histogram. The wizard's rem ladder and the
|
|
creator's raw px are inventoried in docs/reference/tokens.md 6b and convert
|
|
as those pages are rebuilt (T5.x, T7.x) — not here, where any change to a
|
|
padding is a visual change. */
|
|
--wp-s1: 4px;
|
|
--wp-s2: 8px;
|
|
--wp-s3: 12px;
|
|
--wp-s4: 16px;
|
|
--wp-s5: 20px;
|
|
--wp-s6: 28px;
|
|
|
|
/* Control heights: --wp-ctl for anything in a form row, --wp-ctl-sm for
|
|
anything inside a table cell. This is what keeps a toolbar one clean band
|
|
and a table row ~34px instead of ~100px. */
|
|
--wp-ctl: 32px;
|
|
--wp-ctl-sm: 26px;
|
|
|
|
/* -- shape and elevation ---------------------------------------------------
|
|
Square corners are the Carbon idiom and the intent everywhere except the
|
|
creator, which drifted to 12 different radii (BL-007, owned by T7.1).
|
|
The two large shadows share their geometry exactly — 0 4px 16px — and
|
|
differ only in tint, so they can be unified later with no layout
|
|
consequence. Until then, both are named. */
|
|
--wp-radius-0: 0;
|
|
--wp-shadow-none: none;
|
|
--wp-shadow-lg-neutral: 0 4px 16px rgba(0, 0, 0, .16);
|
|
--wp-shadow-lg-cool: 0 4px 16px rgba(20, 30, 50, .12);
|
|
|
|
/* -- elevation -------------------------------------------------------------
|
|
Twelve distinct shadows, which is eleven more than a flat Carbon UI wants.
|
|
They are listed rather than merged for the same reason as the borders: each
|
|
one renders today and merging is a visual change. That the list is this long
|
|
is itself the finding — a later pass can collapse it to two or three now
|
|
that they are all visible in one place. */
|
|
--wp-shadow-pop: 0 8px 28px rgba(20, 30, 50, .22);
|
|
--wp-shadow-menu: 0 8px 24px rgba(20, 30, 50, .18);
|
|
--wp-shadow-menu-lg: 0 10px 26px rgba(20, 30, 50, .18);
|
|
--wp-shadow-modal: 0 12px 40px rgba(20, 30, 50, .3);
|
|
--wp-shadow-modal-lg: 0 20px 60px rgba(0, 0, 0, .3);
|
|
--wp-shadow-gate: 0 8px 30px rgba(20, 30, 50, .12);
|
|
--wp-shadow-rail: 6px 0 22px rgba(20, 30, 50, .16);
|
|
--wp-shadow-drawer: 2px 0 16px rgba(0, 0, 0, .4);
|
|
--wp-shadow-sticky: 0 -2px 10px rgba(20, 30, 50, .08);
|
|
--wp-shadow-navbar: 0 1px 4px rgba(20, 30, 50, .06);
|
|
--wp-shadow-tooltip: 0 4px 14px rgba(20, 30, 50, .22);
|
|
--wp-shadow-toast: 0 6px 24px rgba(0, 0, 0, .25);
|
|
|
|
/* -- scrims ----------------------------------------------------------------
|
|
Five overlay fills in three tints. --cds-overlay (rgba(22,22,22,.5)) is the
|
|
Carbon one; the drawer's is the same colour 5% darker for no recorded
|
|
reason, which makes it the cheapest merge in the set — but still a merge,
|
|
so still not here. See docs/reference/tokens.md section 8-F. */
|
|
--wp-scrim-drawer: rgba(22, 22, 22, .55);
|
|
--wp-scrim-cool: rgba(20, 30, 50, .28);
|
|
--wp-scrim-cool-modal: rgba(20, 30, 50, .5);
|
|
--wp-scrim-cool-strong: rgba(20, 28, 40, .55);
|
|
--wp-scrim-neutral: rgba(0, 0, 0, .5);
|
|
--wp-scrim-loading: rgba(244, 245, 247, .82);
|
|
--wp-scrim-frosted: rgba(255, 255, 255, .94); /* sticky section-nav backdrop */
|
|
--wp-on-accent-divider: rgba(255, 255, 255, .28);
|
|
|
|
/* -- surfaces that are not Carbon greys ------------------------------------
|
|
Four neutrals the suite invented, all within a few points of a Carbon step
|
|
and none of them equal to one. --wp-table-zebra is the dangerous one: it is
|
|
six points from --cds-layer-accent, and collapsing them erases the striping
|
|
on the nine-column user table. Section 8-A. */
|
|
--wp-table-zebra: #fafafa;
|
|
--wp-table-row-hover: #eef0f2;
|
|
--wp-nav-bg: #fbfbfc;
|
|
--wp-nav-hover: #eef0f3;
|
|
--wp-nav-active: #e8eaed;
|
|
--wp-pop-divider: #f0f0f0; /* popover hairlines in the shared chrome */
|
|
--wp-accent-soft-hover: #e2ecfc; /* hover on an --cds-highlight fill */
|
|
|
|
/* -- the Project Super User tag --------------------------------------------
|
|
Carbon purple-10 / purple-60. Real Carbon values with no --cds-* slot in
|
|
the subset this sheet carries. One consumer; promote into --cds-* if a
|
|
second ever appears. */
|
|
--wp-tag-super-bg: #e8daff;
|
|
--wp-tag-super-text: #6929c4;
|
|
|
|
/* -- console output --------------------------------------------------------
|
|
pre.out is a terminal emulator, not product surface. Green-on-dark pass and
|
|
red-on-dark fail is the convention being quoted; it should not track the
|
|
palette and must not be "corrected" to Carbon's support colours. */
|
|
--wp-term-bg: #0f1525;
|
|
--wp-term-fg: #d7e0f5;
|
|
--wp-term-pass: #56d364;
|
|
--wp-term-fail: #ff7b72;
|
|
|
|
/* -- dev mode --------------------------------------------------------------
|
|
Deliberately outside the palette. The banner is MEANT to look wrong; giving
|
|
it the product's amber would make it look intentional, which defeats it. */
|
|
--wp-dev-bg: #3a2a00;
|
|
--wp-dev-fg: #ffd479;
|
|
--wp-dev-rule: #ffb000;
|
|
|
|
/* -- button roles (A5) -----------------------------------------------------
|
|
Four roles and no fifth. Every button in the app is one of them, and the
|
|
class-to-role mapping is in docs/reference/tokens.md section 12.
|
|
|
|
primary the one action this screen exists for. Filled accent. At most one
|
|
per view.
|
|
secondary every other real action. White fill, --border-strong hairline,
|
|
accent on hover. This is the app's workhorse.
|
|
tertiary navigational or undoing. No fill, no border, accent text.
|
|
danger destructive. Outlined red by default; filled red only where the
|
|
control is small enough that an outline would not read (the ✕ on
|
|
a sequence row).
|
|
|
|
GREEN IS A STATUS COLOUR AND NEVER FILLS A BUTTON. It says cleared, ready,
|
|
complete — on a dot, a pill, a banner, a badge. Two buttons broke that rule
|
|
and are what A5 recorded: "SOP Complete" and "Save & View" shouted louder
|
|
than every other action in the suite while doing nothing more dangerous than
|
|
saving. They are primary now. The green did not go anywhere: the launcher
|
|
card still turns green when its SOP completes, on the border and the status
|
|
line, which is where a status belongs.
|
|
|
|
No value here is new — these are the fills the sheets already rendered,
|
|
given one definition so that "primary" means one thing. */
|
|
--wp-btn-primary-bg: var(--cds-interactive-01);
|
|
--wp-btn-primary-fg: var(--cds-text-on-color);
|
|
--wp-btn-primary-hover: var(--cds-hover-primary);
|
|
--wp-btn-primary-active: var(--cds-active-primary);
|
|
|
|
--wp-btn-secondary-bg: var(--cds-field);
|
|
--wp-btn-secondary-fg: var(--cds-text-primary);
|
|
--wp-btn-secondary-border: var(--cds-border-strong);
|
|
--wp-btn-secondary-hover-fg: var(--cds-interactive-01);
|
|
|
|
--wp-btn-tertiary-fg: var(--cds-interactive-01);
|
|
--wp-btn-tertiary-hover-fg: var(--cds-hover-primary-text);
|
|
|
|
--wp-btn-danger-fg: var(--cds-support-error);
|
|
--wp-btn-danger-border: var(--cds-support-error);
|
|
--wp-btn-danger-soft-bg: var(--wp-status-error-bg);
|
|
--wp-btn-danger-fill-bg: var(--cds-support-error);
|
|
--wp-btn-danger-fill-fg: var(--cds-text-on-color);
|
|
|
|
/* -- the second blue -------------------------------------------------------
|
|
#2563d6, not #0f62fe. Fills .sop-inherited — every field a work package
|
|
inherited from its SOP — at 7% alpha, which is why nobody has noticed a
|
|
second brand blue. Named here so it is visible; swapped at T3.5 (BL-008). */
|
|
--wp-sop-inherited-bg: rgba(37, 99, 214, .07);
|
|
}
|
|
|
|
/* Typography */
|
|
body {
|
|
font-family: var(--wp-font-sans);
|
|
color: var(--cds-text-primary);
|
|
background: var(--cds-background);
|
|
font-size: 1rem;
|
|
font-weight: 400;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
h1, h2, h3, h4, h5, h6 {
|
|
font-weight: 600;
|
|
line-height: 1.28572;
|
|
}
|
|
|
|
h1 { font-size: 2rem; }
|
|
h2 { font-size: 1.75rem; }
|
|
h3 { font-size: 1.25rem; }
|
|
h4 { font-size: 1rem; }
|
|
h5 { font-size: .875rem; }
|
|
h6 { font-size: .75rem; }
|
|
|
|
a {
|
|
color: var(--cds-link-primary);
|
|
text-decoration: none;
|
|
}
|
|
|
|
a:hover {
|
|
color: var(--cds-link-primary-hover);
|
|
text-decoration: underline;
|
|
}
|
|
|
|
button {
|
|
font-family: inherit;
|
|
cursor: pointer;
|
|
}
|
|
|
|
input, textarea, select {
|
|
font-family: inherit;
|
|
color: var(--cds-text-primary);
|
|
}
|
|
|
|
/* ============================================================================
|
|
App shell — shared "UI Shell" chrome (Prime Controls, IBM Carbon styling)
|
|
----------------------------------------------------------------------------
|
|
One dark top bar across every page so the suite reads as a single product.
|
|
The Prime Controls logo is a white-background wordmark, so it sits inside a
|
|
white "chip" on the near-black bar (reads as intentional, not a stray box).
|
|
Flip --wp-appbar-bg to a light value if a light header is ever preferred.
|
|
============================================================================ */
|
|
:root {
|
|
--wp-appbar-bg: #161616; /* near-black UI Shell bar */
|
|
--wp-appbar-fg: #ffffff;
|
|
--wp-appbar-fg-dim: #c6c6c6;
|
|
--wp-appbar-border: #6f6f6f; /* outline for ghost buttons on the bar */
|
|
--wp-appbar-hover: #353535;
|
|
--wp-appbar-height: 48px;
|
|
/* Carbon gray-90 — one step up from the bar itself. The raised surfaces that
|
|
sit ON the dark bar or the drawer: the chrome's search field, the drawer's
|
|
current row. Its two consumers both hardcoded it before T3.2. */
|
|
--wp-appbar-layer: #262626;
|
|
}
|
|
|
|
.wp-appbar {
|
|
background: var(--wp-appbar-bg);
|
|
color: var(--wp-appbar-fg);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
height: var(--wp-appbar-height);
|
|
padding: 0 16px;
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 100;
|
|
}
|
|
.wp-appbar-brand {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
height: 100%;
|
|
text-decoration: none;
|
|
color: var(--wp-appbar-fg);
|
|
}
|
|
.wp-appbar-brand:hover { text-decoration: none; opacity: .92; }
|
|
.wp-logo-chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: var(--cds-ui-01);
|
|
border-radius: 4px;
|
|
padding: 4px 8px;
|
|
}
|
|
.wp-logo-chip img { height: 24px; width: auto; display: block; }
|
|
.wp-appbar-title {
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: var(--wp-appbar-fg);
|
|
white-space: nowrap;
|
|
letter-spacing: .01em;
|
|
}
|
|
.wp-appbar-title .wp-appbar-sub { font-weight: 400; color: var(--wp-appbar-fg-dim); }
|
|
.wp-appbar-spacer { flex: 1 1 auto; }
|
|
.wp-appbar-meta { font-size: 13px; color: var(--wp-appbar-fg-dim); white-space: nowrap; }
|
|
.wp-appbar-actions { display: flex; align-items: center; gap: 8px; }
|
|
|
|
/* Buttons and links that live on the dark bar */
|
|
.wp-appbar-btn {
|
|
background: transparent;
|
|
color: var(--wp-appbar-fg);
|
|
border: 1px solid var(--wp-appbar-border);
|
|
border-radius: 0;
|
|
padding: 7px 14px;
|
|
font-size: 14px;
|
|
font-family: inherit;
|
|
line-height: 1.2;
|
|
cursor: pointer;
|
|
text-decoration: none;
|
|
white-space: nowrap;
|
|
transition: background .15s, border-color .15s;
|
|
}
|
|
.wp-appbar-btn:hover { background: var(--wp-appbar-hover); color: var(--wp-appbar-fg); text-decoration: none; }
|
|
.wp-appbar-btn.primary { background: var(--cds-interactive-01); border-color: var(--cds-interactive-01); }
|
|
.wp-appbar-btn.primary:hover { background: var(--cds-hover-primary); border-color: var(--cds-hover-primary); }
|
|
.wp-appbar-btn:focus-visible { outline: 2px solid var(--wp-appbar-fg); outline-offset: 1px; }
|
|
.wp-appbar-count { font-size: 13px; color: var(--wp-appbar-fg-dim); padding: 0 2px; white-space: nowrap; }
|
|
|
|
/* Plain text links on the dark bar (Overview / Feedback / Help, Admin, etc.) */
|
|
.wp-appbar-link { color: var(--wp-appbar-fg-dim); text-decoration: none; font-size: 14px; white-space: nowrap; }
|
|
.wp-appbar-link:hover { color: var(--wp-appbar-fg); text-decoration: none; }
|
|
|
|
@media (max-width: 720px) {
|
|
.wp-appbar { height: auto; flex-wrap: wrap; gap: 8px; padding: 8px 12px; }
|
|
.wp-appbar-actions { flex-wrap: wrap; }
|
|
.wp-appbar-meta { width: 100%; order: 5; }
|
|
}
|
|
|
|
/* ============================================================================
|
|
DRAFT STATE INDICATOR (B5 / T4.4)
|
|
----------------------------------------------------------------------------
|
|
Reports the DRAFT — what wp-autosave.js is holding for you on this device.
|
|
The sync badge in project-data.js reports the OUTBOX, which is a different
|
|
question ("has the saved record reached the project"), and its wording now
|
|
says so. Two indicators, two sentences, neither readable as the other.
|
|
|
|
Lives in theme-light.css because both form pages mount the same component,
|
|
and a second copy in a page sheet is what wave 3 spent itself removing.
|
|
============================================================================ */
|
|
.wp-draft-status {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-size: 12px;
|
|
font-weight: 400;
|
|
line-height: 1.3;
|
|
color: var(--cds-text-secondary);
|
|
white-space: nowrap;
|
|
}
|
|
.wp-draft-status.is-saved { color: var(--cds-support-success); font-weight: 600; }
|
|
.wp-draft-status.is-saving { color: var(--cds-text-secondary); }
|
|
/* A failed autosave is not a quieter version of a successful one — it means the
|
|
safety net is not there. Red, bold, and it keeps its retry. */
|
|
.wp-draft-status.is-failed {
|
|
color: var(--cds-support-error);
|
|
font-weight: 600;
|
|
white-space: normal;
|
|
}
|
|
.wp-draft-retry {
|
|
font: inherit;
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
padding: 2px 9px;
|
|
cursor: pointer;
|
|
background: var(--wp-btn-secondary-bg);
|
|
color: var(--wp-btn-danger-fg);
|
|
border: 1px solid var(--wp-btn-danger-border);
|
|
border-radius: var(--wp-radius-0);
|
|
}
|
|
.wp-draft-retry:hover { background: var(--wp-btn-danger-soft-bg); }
|
|
.wp-draft-retry:focus-visible { outline: 2px solid var(--cds-focus); outline-offset: -2px; }
|
|
|
|
/* ============================================================================
|
|
FOCUS (S12 / T4.7)
|
|
----------------------------------------------------------------------------
|
|
A keyboard user has to be able to see where they are. The suite's own sheets
|
|
removed the outline in six places and replaced it, at best, with a 3px
|
|
#edf5ff glow — a 1.05:1 edge against a white field, which is not a focus
|
|
indicator so much as a rumour of one. T3.4 fixed the wizard's three; this is
|
|
the app-wide floor underneath all of them.
|
|
|
|
:focus-visible, not :focus, so a mouse click does not leave a ring behind —
|
|
which is the reason people reach for `outline: none` in the first place.
|
|
|
|
2px of --cds-focus (#0f62fe). Against every background the app actually uses
|
|
it clears 3:1 comfortably: 8.6:1 on white, 7.8:1 on #f4f4f4, 4.9:1 on the
|
|
Carbon highlight blue. Drawn OUTSIDE the element by default (positive offset)
|
|
so it is not swallowed by a control's own border; components that need it
|
|
inset say so themselves.
|
|
|
|
This is a floor, not an override: it is a single-class-free selector list, so
|
|
any component rule with a class beats it and can draw its own ring.
|
|
============================================================================ */
|
|
:where(a, button, input, select, textarea, summary, [tabindex]:not([tabindex="-1"])):focus-visible {
|
|
outline: 2px solid var(--cds-focus);
|
|
outline-offset: 1px;
|
|
}
|
|
|
|
/* On the dark app bar and the drawer, blue-on-near-black is 2.4:1 and fails.
|
|
White is 15.9:1 against #161616 and is what wp-sidenav.css already used. */
|
|
:where(.wp-appbar, .wp-sidenav, .wp-navscrim)
|
|
:where(a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])):focus-visible {
|
|
outline-color: var(--wp-appbar-fg);
|
|
}
|
|
|
|
/* A FILLED control cannot take an inset ring: a blue ring inside a blue button
|
|
measures 1.00:1, which is not a subtle problem — it is no indicator at all.
|
|
These are every filled button in the suite (see docs/reference/tokens.md §12),
|
|
and their ring is pushed clear of the fill so it lands on the page behind them.
|
|
Class specificity, so it beats both the :where() floor above and the browser's
|
|
own default ring, which is what was winning on the creator's primary buttons. */
|
|
.btn-primary:focus-visible,
|
|
.btn-generate:focus-visible,
|
|
.use-btn:focus-visible,
|
|
.wp-nav-cta:focus-visible,
|
|
.wp-nav-cta-more:focus-visible,
|
|
.mode-btn.active:focus-visible,
|
|
.seq-del:focus-visible,
|
|
button.primary:focus-visible,
|
|
.card-button:focus-visible,
|
|
.submit-btn:focus-visible,
|
|
.comments-toggle:focus-visible,
|
|
.add-btn:focus-visible,
|
|
.ui-help-fab:focus-visible,
|
|
.wp-appbar-btn.primary:focus-visible {
|
|
outline: 2px solid var(--cds-focus);
|
|
outline-offset: 2px;
|
|
}
|