Files
Project-SDE-WP-Suite/html/work-package-suite-styles.css
n.siegfried ce6fb840f0 T3.5 - A5: one button system, and green goes back to being a status
251 buttons across the 7 pages, counted in the browser with every wizard step,
creator section and tool panel forced visible. Two thirds of them are
display:none at load, so a static grep sees about eighty and misses the rest.

FOUR ROLES, defined once in theme-light.css as --wp-btn-*, and no fifth:

  primary    the one action the screen exists for. Filled accent.
  secondary  every other real action. White, --border-strong, accent on hover.
  tertiary   navigating or undoing. No fill, no border, accent text.
  danger     destructive. Outlined red; filled red only where the control is too
             small for an outline to read - the 28px x on a sequence row.

Every button class is mapped to a role in docs/reference/tokens.md section 12.
No value is new: these are the fills the sheets already rendered, given one
definition so that "primary" means one thing.

GREEN IS A STATUS COLOUR AND NO LONGER FILLS A BUTTON. A5 names two green action
buttons; there are four. .use-btn and the launcher's completed-SOP card button
never render green in the default fixture, so the review could not have seen
them - the SOP has to be finished and a suggested value has to be offered first.

  .nav-btn.primary            "SOP complete"    wizard
  .btn.btn-generate           "Save & view"     creator
  .use-btn                                      creator
  .card.complete .card-button                   launcher

The green did not go anywhere. .cstatus button.on-cleared, .toggle-btn.enabled,
.wp-nav-dot.ok, .rb-ready, .badge-R and the launcher card's own left border and
status line all still carry it, and every one of those is a state rather than an
action. The launcher card in particular still says "complete" twice after this
change; it just no longer says it on the button.

SENTENCE CASE, applied to buttons and field labels only, which is the scope A5
sets. First word capitalised, the rest lowercased, acronyms and external proper
nouns left alone (SOP, QC, WP, UPN, PM/APM/CM/QM, PDF, JSON, CSV, BIM, MIMO,
Excel, Acumatica).

  ~30 button labels across launcher, wizard, creator, admin and two scripts
  46 field labels
  text-transform:uppercase removed from 4 rules - .btn and .add-btn (creator
  buttons), label and .cmt-namebar label (creator field labels)

Labels carrying markup - a .req asterisk, a .help-tip chip - had only their text
nodes transformed, so the markup survives and "first word" means the first word
of the label rather than of each fragment. The creator's mono face, 10px size and
tracking are its idiom and are untouched; only the forced uppercase goes.

help.js was updated too. It names "Load Sample" and "SOP Complete" in prose, so
renaming the buttons without it would have left the help centre describing
controls that no longer exist. That coupling is the only place in the app where
button text is referenced by name.

Verified by re-running the inventory: 0 green action buttons, 0 uppercase button
labels, 251 buttons still present - nothing was lost in the rename.

console.css card headers are unchanged, confirmed by diff: the only six lines
this task touches in that file are token substitutions on button/button.primary/
button.danger, none of them within twenty lines of .card h2.

f_items 5 FIXED / F6 REPRODUCES. browser_check 71/71.

Left alone and logged: .step-tab is still uppercase (BL-015) - it is a stepper
tab, neither a button nor a field label, and A4/S9 rebuild the stepper. Table
headers, section eyebrows and headings keep their case throughout. BL-008 and
BL-009 were re-targeted from T3.5 to wave 9: both are colour merges on a field
fill and a status pill, and this task is scoped to buttons.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 19:11:56 -05:00

702 lines
20 KiB
CSS

/* Names only. Every value comes from theme-light.css, which this page loads
first — this sheet declares nothing of its own (T3.2 / S5 / C3). The names
stay because work-package-suite-app.js reads ten of them from JavaScript,
where a rename fails silently. See docs/reference/tokens.md section 9. */
:root {
--primary: var(--cds-interactive-01);
--primary-light: var(--cds-highlight);
--success: var(--cds-support-success);
--warning: var(--wp-status-warning-text);
--warning-bg: var(--wp-status-warning-bg);
--danger: var(--cds-support-error);
--text: var(--cds-text-primary);
--text-light: var(--cds-text-secondary);
--text-dim: var(--cds-ui-04);
--border: var(--cds-border-subtle);
--border-strong: var(--cds-border-strong);
--bg: var(--cds-background);
--bg-card: var(--cds-layer);
--appbar: var(--wp-appbar-bg);
--shadow: var(--wp-shadow-none);
/* Neutral, where the creator's --shadow-lg is cool-tinted. Same geometry in
both — 0 4px 16px — so the two can be merged later without moving anything;
merging is still a rendered change, so not here. tokens.md section 8-F. */
--shadow-lg: var(--wp-shadow-lg-neutral);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: var(--wp-font-sans);
color: var(--text);
background: var(--bg);
line-height: 1.5;
}
.app-container {
display: flex;
flex-direction: column;
min-height: 100vh;
}
/* HEADER — dark UI Shell bar
Four groups share this bar, not two: .header-left and .header-right are in the
markup, and wp-chrome.js injects the project switcher and search between them
while auth-guard.js appends the user menu after. At 1024px that is more than
fits on one 48px line, and a fixed height meant the overflow had nowhere to go
but on top of its neighbours. min-height + wrap lets the bar grow instead.
One row still measures exactly 48px, so nothing moves at desk width. */
.header {
background: var(--appbar);
color: var(--wp-appbar-fg);
padding: 0 16px;
min-height: 48px;
display: flex;
flex-wrap: wrap;
row-gap: 6px;
justify-content: space-between;
align-items: center;
}
/* Sizes to its content and never narrower than the logo.
This was `flex: 1; min-width: 0`, which put it in a dead tie with the chrome
wp-chrome.js injects as its sibling (`flex: 1 1 auto; min-width: 0`): both
claimed the same run of the bar, both were allowed to shrink to nothing, and
the chrome's wider content won every time. .header-left computed to 0 while
its `flex-shrink: 0` logo kept its 106px and overflowed — so the project
switcher rendered straight across the logo (F3).
Now the chrome is the only one that grows, and `min-width: auto` restores the
content-based floor, which is the logo plus the gap: the inner title block
keeps its own `min-width: 0`, so the project name still gives way first, by
the ellipsis .header-subtitle already carries. Truncation policy itself is
B2's, not this task's. */
.header-left {
flex: 0 1 auto;
display: flex;
align-items: center;
gap: 14px;
min-width: auto;
}
/* Prime logo (white-background wordmark) sits in a white chip on the dark bar */
.logo {
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--cds-ui-01);
padding: 4px 8px;
border-radius: 4px;
text-decoration: none;
transition: opacity 0.2s;
flex-shrink: 0;
}
.logo:hover { opacity: 0.92; }
.logo img { height: 24px; width: auto; display: block; }
.logo-icon {
width: 36px;
height: 36px;
background: var(--primary-light);
border-radius: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-weight: 700;
}
.header-title {
font-size: 15px;
font-weight: 600;
margin-bottom: 0;
color: var(--wp-appbar-fg);
white-space: nowrap;
}
.header-subtitle {
font-size: 12px;
color: var(--wp-appbar-fg-dim);
min-height: 16px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Holds its natural size. Left to the default `flex-shrink: 1` it was squeezed
narrower than its buttons, so "Load Sample" ran underneath "Feedback". The
chrome between the two groups is the only flexible one, which is what gives
the three a defined order of giving way: right keeps its buttons, left keeps
its logo, the middle absorbs the difference. */
.header-right {
display: flex;
align-items: center;
gap: 8px;
flex: 0 0 auto;
}
.header-button {
padding: 7px 14px;
background: transparent;
color: var(--wp-appbar-fg);
border: 1px solid var(--wp-appbar-border);
border-radius: 0;
cursor: pointer;
font-size: 14px;
font-weight: 400;
transition: background 0.15s, border-color 0.15s;
}
.header-button:hover {
background: var(--wp-appbar-hover);
border-color: var(--wp-appbar-border);
}
.step-counter {
background: transparent;
border: 1px solid var(--wp-appbar-border);
color: var(--wp-appbar-fg-dim);
padding: 4px 10px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
}
/* MAIN NAVIGATION — underline tabs */
.main-nav {
display: flex;
gap: 0;
padding: 0 16px;
background: var(--bg-card);
border-bottom: 1px solid var(--border);
}
.nav-tab {
padding: 13px 18px;
background: none;
border: none;
border-bottom: 3px solid transparent;
border-radius: 0;
cursor: pointer;
font-size: 15px;
font-weight: 400;
color: var(--text-light);
display: flex;
align-items: center;
gap: 0.5rem;
transition: background 0.15s, color 0.15s;
}
.nav-tab:hover {
background: var(--bg);
color: var(--text);
}
.nav-tab.active {
background: none;
color: var(--text);
border-bottom-color: var(--primary);
font-weight: 600;
}
.tab-icon { font-size: 16px; }
/* CONTENT AREA
The SOP wizard reads better with a bound on line length, but 1000px on a 1920
screen wasted half the display — and it also squeezed the embedded Work Package
Creator (an iframe living in here) into a ~930px column with its own scrollbar
inside the page's. Wider cap for the wizard; the embedded tools go full-bleed
(see .content-area.embed-full below). */
.content-area {
flex: 1;
padding: 2rem;
max-width: 1700px;
margin: 0 auto;
width: 100%;
}
/* Work Package Creation / Dashboard: the iframe fills the window below the app
chrome and owns the only scrollbar, so the creator's sticky save bar and
navigator drawer position against a real viewport instead of scrolling away. */
.content-area.embed-full {
/* `flex: none` matters: .content-area is a column flex item with `flex: 1`, whose
flex-basis:0% overrides `height` and leaves the used height INDEFINITE — so a
child's `height:100%` resolves to auto and the iframe collapses to its 150px
default. Opting out of flex sizing makes the height definite. */
flex: none;
max-width: none;
padding: 0;
height: calc(100vh - var(--wp-chrome-h, 96px));
overflow: hidden;
display: flex;
flex-direction: column;
}
.content-area.embed-full > .tool.active {
flex: 1 1 auto;
min-height: 0; /* let it shrink instead of overflowing the shell */
height: 100%;
}
#wp-frame {
width: 100%;
border: 0;
min-height: calc(100vh - 200px);
}
#wp-frame.fill {
display: block;
height: 100%;
min-height: 0;
}
/* No page scrollbar while a full-bleed tool is open — the iframe scrolls. */
body.embed-full { overflow: hidden; }
.tool {
display: none;
}
.tool.active {
display: block;
}
/* STEP NAV */
.step-nav {
margin-bottom: 2rem;
overflow-x: auto;
}
.steps-container {
display: flex;
gap: 0.5rem;
min-width: min-content;
padding: 0.5rem;
}
.step-item {
padding: 0.6rem 0.9rem;
border-radius: 0;
background: var(--bg-card);
border: 1px solid var(--border);
color: var(--text-light);
cursor: pointer;
font-size: 12px;
font-weight: 500;
white-space: nowrap;
transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.step-item:hover { background: var(--bg); border-color: var(--border-strong); color: var(--text); }
.step-item.active { background: var(--primary); color: var(--cds-text-on-color); border-color: var(--primary); font-weight: 600; }
/* STEP CONTENT */
.step-content {
background: var(--bg-card);
padding: 2rem;
border: 1px solid var(--border);
border-radius: 0;
margin-bottom: 2rem;
}
.step { display: none; }
.step h2 {
font-size: 22px;
font-weight: 400;
letter-spacing: -0.01em;
margin-bottom: 0.75rem;
color: var(--text);
}
.notice {
font-size: 13px;
color: var(--text-light);
background: var(--primary-light);
padding: 0.75rem 1rem;
border-radius: 0;
margin-bottom: 1.5rem;
border-left: 4px solid var(--primary);
}
/* FIELDS
The wizard's fields were one per row, which looked right in a 1000px column but
stretches a text input across the screen now that the content area is wide. Flow
them into as many ~340px columns as fit; `.col1` still forces a single column for
the fields that genuinely want the width (long text, textareas). */
.field-grid {
display: grid;
gap: 1.5rem;
grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
}
.field-grid.col1 { grid-template-columns: 1fr; }
.field {
display: flex;
flex-direction: column;
}
.field label {
font-size: 13px;
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--text);
}
/* An editable field is white on --border-strong, which is what an editable field
is on every other page in the suite. It used to be filled with var(--bg) — this
sheet's PAGE BACKGROUND, #f4f4f4 — so an empty required field was indistinguishable
from a locked one and people did not type in them (F5/A3).
The cause was the wizard declaring its own token set and therefore never seeing
--cds-field, which theme-light.css had been supplying to this page all along.
T1.5 reached past the local tokens with a fallback; T3.2 removed the local tokens
entirely, so the fallback is now dead code and the canonical token is simply
consumed. No field-specific colour is declared on this page. */
.field input,
.field select,
.field textarea {
padding: 0.75rem;
border: 1px solid var(--border-strong);
border-radius: 0;
font-size: 14px;
font-family: inherit;
color: var(--text);
background: var(--cds-field);
transition: border-color 0.2s;
}
/* Editable fields being white means genuinely locked ones have to say so, or the
signal inverts rather than being fixed. --cds-field-02 is the theme's own
secondary field surface and matches .locked-field in the creator.
The border is deliberately the same on both states: identical --border-strong is
what makes a field read as a field, and the fill is what carries locked versus
editable. Verified as #ffffff vs #f4f4f4 against an identical border. */
.field input:disabled,
.field select:disabled,
.field textarea:disabled,
.field input[readonly],
.field textarea[readonly] {
background: var(--cds-field-02);
color: var(--text-light);
cursor: not-allowed;
}
/* One focus ring for every control on this page (BL-002).
All three focus rules here removed the outline. Two replaced it with a 3px
var(--primary-light) glow — #edf5ff against a #ffffff field, which is a 1.05:1
edge: a faint halo on the card, invisible on the field itself. The third,
.seq-step input.seq-label, removed the outline with no replacement at all.
CLAUDE.md allows outline:none only with a replacement of at least equal
visibility, so two were marginal and one was a straight violation.
This is Carbon's ring and the one console.css:69 and wp-chrome.css:206 already
draw — 2px of --cds-focus inset over the control's own edge — so it is the app's
existing idiom rather than a fourth. border-color stays as a second cue and as
the fallback if outline is ever suppressed. */
.field input:focus,
.field select:focus,
.field textarea:focus,
.user-pick:focus,
.seq-step input.seq-label:focus {
outline: 2px solid var(--cds-focus);
outline-offset: -2px;
border-color: var(--primary);
}
/* Helper text under a field, by either of the two names the markup uses: <small>
inside a .field, and .field-hint anywhere. They were two rules with byte-identical
declarations — the duplication that produced the bug this block used to carry a
comment about, in miniature. One rule now, so they cannot drift apart again.
The colour is deliberately left as --text-dim. #8d8d8d on white measures 3.3:1,
under the 4.5:1 floor, and console.css:103 already rejected it for exactly this
job — but propagating that fix is T4.6, which re-measures rather than inheriting
either published figure. Changing it here would be a rendered change in a wave
that must produce none, and would take T4.6's decision away from it. */
.field small,
.field-hint {
font-size: 12px;
color: var(--text-dim);
margin-top: 0.25rem;
}
.field-hint strong { color: var(--text-light); }
/* The sign-off name pickers sit outside .field, so they got no form styling at all.
They carried the F5/A3 defect too, and were missed because the review counted
inputs inside .field: --bg fill on a --border hairline is the grey-on-grey that
made empty required fields read as locked. These are enabled selects and now look
like every other enabled control in the suite — --cds-field on --border-strong.
Focus is the shared ring above. */
.user-pick {
padding: 0.75rem; border: 1px solid var(--border-strong); border-radius: 0;
font-size: 14px; font-family: inherit; color: var(--text); background: var(--cds-field);
}
/* ROLES */
.required-roles {
display: flex;
flex-direction: column;
gap: 1rem;
}
.role-required {
display: flex;
align-items: center;
padding: 1rem;
background: var(--bg);
border-radius: 0;
border: 1px solid var(--border);
}
.role-checkbox {
display: flex;
align-items: center;
gap: 0.5rem;
}
.role-checkbox input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
.role-checkbox label {
margin: 0;
font-weight: 600;
cursor: pointer;
}
/* WP TYPES */
#wp-types-table { overflow-x: auto; }
.wp-type-row {
display: grid;
grid-template-columns: 1.1fr 80px 1.3fr 1.6fr 1.2fr;
gap: 0.85rem;
align-items: center;
padding: 0.75rem 1rem;
background: var(--bg);
border-radius: 0;
margin-bottom: 0.5rem;
border: 1px solid var(--border);
}
.wp-types-header {
display: grid;
grid-template-columns: 1.1fr 80px 1.3fr 1.6fr 1.2fr;
gap: 0.85rem;
padding: 0.5rem 1rem;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--text-light);
margin-bottom: 0.5rem;
}
/* BUTTONS — roles are documented in docs/reference/tokens.md section 12 (A5) */
/* primary */
.add-btn {
padding: 0.7rem 1.25rem;
background: var(--wp-btn-primary-bg);
color: var(--wp-btn-primary-fg);
border: none;
border-radius: 0;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
.add-btn:hover { background: var(--wp-btn-primary-hover); }
/* secondary */
.nav-btn {
padding: 0.7rem 1.4rem;
background: var(--wp-btn-secondary-bg);
border: 1px solid var(--wp-btn-secondary-border);
border-radius: 0;
font-size: 14px;
font-weight: 600;
color: var(--wp-btn-secondary-fg);
cursor: pointer;
transition: all 0.15s;
}
.nav-btn:hover { border-color: var(--wp-btn-secondary-hover-fg); color: var(--wp-btn-secondary-hover-fg); background: var(--bg); }
/* A5: this was green — the loudest control in the suite, on a button whose job
is to save a form. The completion it announced is still announced, on the
launcher card's border and status line, which is where a status belongs. */
.nav-btn.primary {
background: var(--wp-btn-primary-bg);
color: var(--wp-btn-primary-fg);
border-color: var(--wp-btn-primary-bg);
}
.nav-btn.primary:hover { background: var(--wp-btn-primary-hover); border-color: var(--wp-btn-primary-hover); color: var(--wp-btn-primary-fg); }
.nav-btn:disabled { opacity: 0.5; cursor: not-allowed; }
/* NAVIGATION */
.step-navigation {
display: flex;
gap: 1rem;
justify-content: space-between;
padding: 1.5rem;
background: var(--bg-card);
border-radius: 0;
box-shadow: var(--shadow);
}
/* COMMENTS */
.comments-section {
margin-top: 2rem;
padding-top: 1.5rem;
border-top: 2px solid var(--border);
}
.comments-toggle {
padding: 0.5rem 1rem;
background: var(--primary-light);
color: var(--primary);
border: 1px solid var(--primary);
border-radius: 0;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.comments-toggle:hover { background: var(--primary); color: var(--cds-text-on-color); }
/* CONSTRUCTION SEQUENCE (drag & drop) */
#sequence-list { display: flex; flex-direction: column; gap: 8px; }
.seq-step {
display: flex; align-items: center; gap: 12px; padding: 11px 14px;
background: var(--bg-card); border: 1px solid var(--border); border-radius: 0;
box-shadow: var(--shadow); transition: border-color .12s, box-shadow .12s, opacity .12s;
}
.seq-step:hover { border-color: var(--primary); }
.seq-step.dragging { opacity: .35; border-style: dashed; }
.seq-step.drag-over { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-light); }
.seq-handle { cursor: grab; color: var(--text-dim); font-size: 16px; line-height: 1; user-select: none; flex-shrink: 0; }
.seq-handle:active { cursor: grabbing; }
.seq-num {
width: 24px; height: 24px; flex-shrink: 0; border-radius: 50%; background: var(--primary-light); color: var(--primary);
font-size: 11px; font-weight: 600; display: flex; align-items: center; justify-content: center;
}
.seq-step input.seq-label {
border: 1px solid transparent; background: transparent; font-size: 14px; padding: 5px 8px; color: var(--text); flex: 1; border-radius: 4px;
}
/* Fill only — the ring itself is the shared rule above, which this used to opt out
of entirely with a bare `outline: none`. */
.seq-step input.seq-label:focus { background: var(--bg); }
/* danger, filled: a 28px square is too small for an outline to read */
.seq-del {
background: var(--wp-btn-danger-fill-bg); color: var(--wp-btn-danger-fill-fg); border: none; border-radius: 4px;
width: 28px; height: 28px; cursor: pointer; font-weight: 600; flex-shrink: 0;
}
.seq-arrow { text-align: center; color: var(--text-dim); font-size: 13px; line-height: .4; margin: -2px 0; }
.seq-step.gate { border-color: var(--warning); background: var(--warning-bg); border-style: dashed; }
.seq-step.gate .seq-label { color: var(--warning); font-weight: 500; }
.seq-gate-badge {
flex-shrink: 0; padding: 3px 9px; border-radius: 20px; background: var(--warning); color: var(--cds-text-on-color);
font-size: 9px; font-weight: 600; letter-spacing: .08em; white-space: nowrap;
}
/* STEP COMMENTS DROPDOWN */
.comments-dropdown {
position: fixed;
top: 80px;
right: 2rem;
width: 360px;
max-width: calc(100vw - 2rem);
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 0;
box-shadow: var(--shadow-lg);
padding: 1.25rem;
z-index: 1200;
}
.comments-dropdown-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.comments-dropdown-close {
background: none;
border: none;
font-size: 16px;
cursor: pointer;
color: var(--text-light);
line-height: 1;
}
.comments-dropdown-close:hover { color: var(--text); }
/* MODAL */
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--wp-scrim-neutral);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background: var(--bg-card);
border-radius: 0;
padding: 2rem;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
box-shadow: var(--shadow-lg);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--border);
padding-bottom: 1rem;
}
.modal-header h3 { font-size: 18px; margin: 0; }
.modal-close {
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: var(--text-dim);
}
.modal-close:hover { color: var(--text); }
/* UTILITY */
.sub-heading {
font-size: 14px;
font-weight: 700;
color: var(--text);
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* RESPONSIVE */
@media (max-width: 768px) {
.header { height: auto; flex-direction: column; align-items: stretch; text-align: center; gap: 0.75rem; padding: 12px 16px; }
.main-nav { flex-wrap: wrap; }
.content-area { padding: 1rem; }
.step-content { padding: 1rem; }
.wp-type-row { grid-template-columns: 1fr; }
.wp-types-header { display: none; }
.step-navigation { flex-direction: column; }
}