/* 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; }