Files
Project-SDE-WP-Suite/html/work-package-suite-styles.css
n.siegfried 2485ca7b61 T3.3 - S5: fix .field-hint properly and retire the comment it left behind
The comment at work-package-suite-styles.css:395 recorded the consequence of the
four token systems: .field-hint's only rule lived in wp-creation-styles.css,
which the wizard does not link, so the class rendered as unstyled body text. The
rule was later added to the wizard sheet, but as a SECOND rule with declarations
byte-identical to the .field small rule four lines above it - the same
duplication that caused the original bug, in miniature.

Merged into one rule under both selectors, so they cannot drift apart again, and
deleted the comment. It described a fixed problem, and its account of where the
class is used was wrong anyway: .field-hint appears once on the wizard, not in
"step 2's CM hint, the team-member notices".

The colour is deliberately untouched. --text-dim is #8d8d8d, which measures
3.3:1 on white and which console.css:103 already rejected for exactly this job -
but propagating that fix is T4.6, which is told to re-measure rather than inherit
either published figure. Fixing it here would change a rendered value in a wave
that must produce none, and would take the decision away from T4.6.

THE HUNT for the same class of failure elsewhere, which is the rest of the task.

Statically, per page, over every stylesheet that page actually links plus its own
inline <style>: every bare var(--x) reference where --x is declared by no sheet
the page loads. That is the .field-hint failure exactly, and it is the one that
renders nothing rather than rendering wrong.

  login 32 refs, launcher 54, sop 67, creator 92, admin 100, users 100, field 56
  unresolvable: 0 of 501

Zero, and structurally zero from here on: after T3.2 every token resolves from
theme-light.css, which all seven pages link first. The failure mode is no longer
possible rather than merely absent.

In the browser, on all seven pages: every class on a real element that no rule in
any loaded stylesheet targets, including the sheets help.js and wp-creation-app.js
inject at runtime. One candidate, and it is not a defect - .team-pick on the
wizard's four project-team selects is a JS hook that sits beside data-team, and
the selects are inside .field, so .field select styles them. Its sibling
.user-pick needs its own rule only because those two selects sit OUTSIDE .field,
which the comment there already explains.

Stated as a limitation rather than left implicit: the browser half sees each page
in its load state, so a class applied only on interaction would not appear in it.
The static half has no such gap - it covers every rule in every sheet regardless
of what is on screen - and it is the half that catches this particular bug.

Verification: token_check reports every token and all 3,500 elements identical to
wave 2 on all 7 pages.

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

676 lines
19 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);
}
/* INTERIM (F5/A3). T3.4 removes the duplicate token underneath this; the fix here
is only to stop editable fields looking disabled.
These were filled with var(--bg) — this sheet's PAGE BACKGROUND, #f4f4f4 — on a
#e0e0e0 border, so an empty required field was indistinguishable from a locked
one and users did not type in them. The wizard redeclares its own tokens and so
never saw --cds-field: #ffffff, which theme-light.css has been supplying to this
page all along. Consume that instead of the local override, and take the same
--border-strong the creator's inputs use, so a field looks like a field in both
places. No new value is introduced here. */
.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, var(--bg-card));
transition: border-color 0.2s;
}
/* Now that editable fields are white, genuinely locked ones need to say so — there
was no disabled rule at all, so they would have gone white too and the signal
would have inverted rather than been fixed. --cds-field-02 is the theme's own
secondary field surface, and matches .locked-field in the creator.
The border is deliberately left alone: the same --border-strong on both states
is what makes a field read as a field, and the fill is what carries locked vs
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, var(--bg));
color: var(--text-light);
cursor: not-allowed;
}
.field input:focus,
.field select:focus,
.field textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px var(--primary-light);
}
/* 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. */
.user-pick {
padding: 0.75rem; border: 1px solid var(--border); border-radius: 0;
font-size: 14px; font-family: inherit; color: var(--text); background: var(--bg);
}
.user-pick:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-light); }
/* 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 */
.add-btn {
padding: 0.7rem 1.25rem;
background: var(--primary);
color: var(--cds-text-on-color);
border: none;
border-radius: 0;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
.add-btn:hover { background: var(--cds-hover-primary); }
.nav-btn {
padding: 0.7rem 1.4rem;
background: var(--bg-card);
border: 1px solid var(--border-strong);
border-radius: 0;
font-size: 14px;
font-weight: 600;
color: var(--text);
cursor: pointer;
transition: all 0.15s;
}
.nav-btn:hover { border-color: var(--primary); color: var(--primary); background: var(--bg); }
.nav-btn.primary {
background: var(--success);
color: var(--cds-text-on-color);
border-color: var(--success);
}
.nav-btn.primary:hover { background: var(--wp-hover-success); border-color: var(--wp-hover-success); color: var(--cds-text-on-color); }
.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;
}
.seq-step input.seq-label:focus { background: var(--bg); border-color: var(--primary); outline: none; }
.seq-del {
background: var(--danger); color: var(--cds-text-on-color); 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; }
}