Comment cleanup

This commit is contained in:
ThePetrovich 2026-08-21 22:09:17 +08:00
parent c6cbc8fe70
commit f18276ab01
7 changed files with 336 additions and 97 deletions

View file

@ -1,5 +1,5 @@
/* =============================================================================
YKSA UI kit. Ships with yksa-web-kit; see ecosystem/ui-kit.md.
YKSA UI kit.
Requires Bootstrap >= 5.3: the state pills below are built on the
--bs-*-bg-subtle / --bs-*-text-emphasis / --bs-*-border-subtle families, and
@ -34,6 +34,23 @@
--yksa-z-sticky: 1020;
--yksa-z-chrome: 1030;
--yksa-z-floating: 1035;
/* One hover treatment for the whole estate: the surface's ground goes one
step darker and nothing else moves. Every hoverable thing -- table rows,
list rows, step rows, catalog cards, disclosures -- uses this token, so a
page cannot end up with three ways of saying "you are pointing at this".
Borders, shadows and colour shifts are all reserved for *state*, which is
a property of the record; hover is a property of the pointer. */
--yksa-hover-bg: var(--bs-tertiary-bg);
}
/* Bootstrap's own hoverable components, pointed at the same token. */
.table {
--bs-table-hover-bg: var(--yksa-hover-bg);
}
.list-group {
--bs-list-group-action-hover-bg: var(--yksa-hover-bg);
}
/* Bootstrap declares --bs-dropdown-zindex on .dropdown-menu, not on :root, so it
@ -43,10 +60,6 @@
--bs-dropdown-zindex: var(--yksa-z-floating);
}
/* -----------------------------------------------------------------------------
Navbar and footer -- layout-identical across every service
-------------------------------------------------------------------------- */
.custom-navbar {
height: var(--navbar-height);
padding-top: 0;
@ -96,32 +109,66 @@
object-fit: contain;
}
/* An open dropdown is as much "the item you are on" as an active one. Without
this the toggle dropped back to the resting colours the moment its own menu
appeared, which reads as the click having missed. */
.nav-full-height.nav-link:hover,
.nav-full-height.nav-link.active {
.nav-full-height.nav-link.active,
.nav-full-height.nav-link.show {
color: #fff !important;
background-color: var(--bs-primary);
}
/* -----------------------------------------------------------------------------
State -- the sanctioned rendering of status (ui/_state.html)
-------------------------------------------------------------------------- */
/* Muted text inside a highlighted nav item takes the item's colour.
Bootstrap's text utilities set `color` with `!important`, so a muted span --
a countdown, a hint -- kept its resting grey when the item turned solid primary
underneath it: grey on blue, the one combination the theme has no contrast for.
`inherit` needs `!important` here for the same reason the utility does.
Deliberately only the muted utility, not every descendant. A badge and a state
pill are separate surfaces carrying their own background, and flattening them
to the parent's colour would erase what they are for. */
.nav-full-height.nav-link:hover .text-body-secondary,
.nav-full-height.nav-link.active .text-body-secondary,
.nav-full-height.nav-link.show .text-body-secondary,
/* Same problem, same fix, one row down: a dropdown item with a muted hint or
sub-line under its label -- the outcome menu on the pass console, the duty
menu in the header -- highlights on hover, focus and keyboard selection, and
the muted line stayed grey against it. Any menu that explains its options has
this shape, so the rule belongs on `.dropdown-item` rather than on each one. */
.dropdown-item:hover .text-body-secondary,
.dropdown-item:focus .text-body-secondary,
.dropdown-item:active .text-body-secondary,
.dropdown-item.active .text-body-secondary {
color: inherit !important;
}
/* `inline-block`, not `inline-flex`, and this is the whole of the table-alignment
fix. An inline-flex box's baseline is its last flex item's baseline, and its
padding and border hang *below* that -- so in a table cell the pill sits low,
and no amount of `vertical-align` corrects it without also fighting the line
height it inherits. An inline-block shares the row's baseline with the text
beside it and straddles it symmetrically, which is why Bootstrap's own `.badge`
has always looked right in a table. The icon is centred by bootstrap-icons'
`vertical-align: -.125em`, the same way it is in running text.
Nothing here sets `line-height`: the pill takes the surface's, so it matches
the row it sits in rather than being a second type size. */
.yksa-state {
display: inline-flex;
align-items: center;
gap: .25rem;
display: inline-block;
font-size: .75rem;
font-weight: 700;
padding: .05rem .4rem;
white-space: nowrap;
border: 1px solid transparent;
/* An inline-flex box baseline-aligns on its own last line box, which in a
table cell or a run of text leaves it sitting a few pixels low. It is a
self-contained box; align it on the middle instead. */
vertical-align: middle;
}
/* health */
.yksa-state > .bi,
.yksa-chip > .bi {
margin-right: .25rem;
}
.yksa-state-ok,
.yksa-state-complete {
color: var(--bs-success-text-emphasis);
@ -157,23 +204,51 @@
border-color: var(--bs-border-color);
}
/* Neutral metadata chip. Not a coloured badge, because it carries no status. */
/* Neutral metadata chip. Not a coloured badge, because it carries no status.
Same box model as .yksa-state, for the same reason. */
.yksa-chip {
display: inline-flex;
align-items: center;
gap: .25rem;
display: inline-block;
font-size: .75rem;
padding: 0 .35rem;
white-space: nowrap;
color: var(--bs-secondary-color);
background: var(--bs-tertiary-bg);
border: 1px solid var(--bs-border-color);
vertical-align: middle;
}
/* -----------------------------------------------------------------------------
Typography helpers
-------------------------------------------------------------------------- */
/* A borderless icon button for dense rows: a table row's remove control, an
overflow `...` toggle, a field's clear. Bordered buttons in those places draw a
box around every row and the boxes become the pattern the eye follows.
This exists because the alternative was being spelled out by hand, differently,
in five templates -- `btn btn-sm btn-link p-0 text-body-secondary`, sometimes
with `border-0`, sometimes with `px-1`, once with an `fs-5` on the glyph. It is
a button, not a link: `.btn-link` is reserved for navigation, and a control
styled as a link but wired to a POST invites a middle-click that silently
does nothing. */
.yksa-btn-icon {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 1.75rem;
min-height: 1.75rem;
padding: 0 .25rem;
color: var(--bs-secondary-color);
background: none;
border: none;
line-height: 1;
}
.yksa-btn-icon:hover,
.yksa-btn-icon:focus-visible {
color: var(--bs-body-color);
background: var(--yksa-hover-bg);
}
.yksa-btn-icon.is-danger:hover,
.yksa-btn-icon.is-danger:focus-visible {
color: var(--bs-danger);
}
.yksa-label {
font-size: .75rem;
@ -207,6 +282,36 @@
margin-inline: 0.3rem;
}
/* A run of metadata separated by rules. Use this rather than dropping bare
`.yksa-sep`s between text nodes.
The margins above are symmetric, but the *source* around them is not: a
template that puts the separator at the start of a line contributes a collapsed
space on its left and none on its right, so the rule sits visibly off-centre
between the two items it divides. Nobody sees that while writing the template
and everybody sees it on the page.
A flex container fixes it at the root: whitespace-only text nodes never become
flex items, so indentation stops existing, and the gap is the only spacing.
Each item goes in its own element -- an unwrapped text node *would* become an
anonymous flex item, and then the whole run is one item and the gap does
nothing. That is the reason for the spans. */
.yksa-meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0 .5rem;
}
.yksa-meta-tight {
gap: 0 .3rem;
}
/* Inside the run the gap does the spacing; the rule's own margins would double it. */
.yksa-meta > .yksa-sep {
margin-inline: 0;
}
/* Tabular numerals in the body face. Monospace is reserved for verbatim machine
text -- hex dumps, TLE lines, command mnemonics. */
.yksa-num {
@ -228,10 +333,6 @@
font-size: .8em;
}
/* -----------------------------------------------------------------------------
Containers
-------------------------------------------------------------------------- */
/* A container that supplies its own padding must not also inherit the trailing
margin of whatever it ends with. Bootstrap does this for `p` in a few places
and not at all for `dl`, `ul` or `table`, so the fix was being applied by hand
@ -248,16 +349,20 @@
margin-bottom: 0;
}
/* -----------------------------------------------------------------------------
Alerts
/* A definition list ends with a `dd`, which carries a bottom margin of its own.
`mb-0` on the `dl` does not reach it, so the gap survives the very utility
applied to remove it -- which is how several fact lists ended up looking
bottom-heavy inside an otherwise even card. */
dl:last-child > dd:last-child {
margin-bottom: 0;
}
The estate theme paints alerts in solid colour, not the tinted panel Bootstrap
ships by default. That changes what belongs inside one: a utility text colour
is computed against the page ground and is unreadable on a saturated one, and
an outline button drawn in `--bs-secondary` all but vanishes.
-------------------------------------------------------------------------- */
/* Alerts here paint in solid colour, not Bootstrap's default tinted panel: a
utility text colour computed against the page ground is unreadable on a
saturated one, and an outline button drawn in `--bs-secondary` all but
vanishes -- hence the overrides below.
/* Muted text inside an alert is the alert's own colour, softened. All three of
Muted text inside an alert is the alert's own colour, softened. All three of
these resolve to --bs-secondary-color, which is computed against the page. */
.alert .text-body-secondary,
.alert .yksa-label,
@ -269,26 +374,51 @@
/* Outline buttons take their colour from the alert rather than from the palette,
and invert to the alert's ground on hover. The semantic distinction between
`-secondary` and `-danger` is lost here, which is correct: the alert already
says which of those it is, and a red outline on a red ground says nothing. */
says which of those it is, and a red outline on a red ground says nothing.
Every value comes from `--bs-alert-color` / `--bs-alert-bg`, which the alert
variant declares and the button inherits, so one rule covers all six variants
in both text directions. Not `currentColor`: inside `:hover` that resolves to
the *hover* colour, so a hover background of `currentColor` paints the button
the colour it is about to be and the hover state disappears. */
.alert .btn-outline-primary,
.alert .btn-outline-secondary,
.alert .btn-outline-success,
.alert .btn-outline-danger,
.alert .btn-outline-warning {
--bs-btn-color: inherit;
--bs-btn-border-color: currentColor;
--bs-btn-color: var(--bs-alert-color);
--bs-btn-border-color: var(--bs-alert-color);
--bs-btn-hover-color: var(--bs-alert-bg);
--bs-btn-hover-bg: currentColor;
--bs-btn-hover-border-color: currentColor;
--bs-btn-hover-bg: var(--bs-alert-color);
--bs-btn-hover-border-color: var(--bs-alert-color);
--bs-btn-active-color: var(--bs-alert-bg);
--bs-btn-active-bg: currentColor;
--bs-btn-active-border-color: currentColor;
--bs-btn-active-bg: var(--bs-alert-color);
--bs-btn-active-border-color: var(--bs-alert-color);
--bs-btn-disabled-color: var(--bs-alert-color);
--bs-btn-disabled-border-color: var(--bs-alert-color);
--bs-btn-focus-shadow-rgb: 255, 255, 255;
}
/* -----------------------------------------------------------------------------
Layout
-------------------------------------------------------------------------- */
/* A filled button on a solid alert is the alert's own colour inverted -- the same
pair, the other way round. `btn-light` was doing this by hand and only happened
to be right on the four dark variants. */
.alert .btn-contrast {
--bs-btn-color: var(--bs-alert-bg);
--bs-btn-bg: var(--bs-alert-color);
--bs-btn-border-color: var(--bs-alert-color);
--bs-btn-hover-color: var(--bs-alert-bg);
--bs-btn-hover-bg: var(--bs-alert-color);
--bs-btn-hover-border-color: var(--bs-alert-color);
--bs-btn-active-color: var(--bs-alert-bg);
--bs-btn-active-bg: var(--bs-alert-color);
--bs-btn-active-border-color: var(--bs-alert-color);
--bs-btn-focus-shadow-rgb: 255, 255, 255;
opacity: 1;
}
.alert .btn-contrast:hover {
opacity: .9;
}
/* A record in a list is a row with a left status marker, not a card. */
.yksa-row {
@ -300,39 +430,107 @@
}
.yksa-row:hover {
background: var(--bs-tertiary-bg);
background: var(--yksa-hover-bg);
}
.yksa-row.is-in-progress { border-left-color: var(--bs-primary); }
.yksa-row.is-complete { border-left-color: var(--bs-success); }
.yksa-row.is-pending { border-left-color: var(--bs-warning); }
.yksa-row.is-failed { border-left-color: var(--bs-danger); }
/* Withdrawn/skipped: still a record, but nothing here is work. The border
stays the resting grey and the row's text steps back, which is the same
thing `skipped` says in the state vocabulary. */
.yksa-row.is-skipped { color: var(--bs-secondary-color); }
/* The one saturated element allowed on a surface: the active shift, the pass in
contact, the source currently polling. Two on a page means one is wrong. */
.yksa-row.is-active {
border-left-color: var(--bs-success);
background: var(--bs-success-bg-subtle);
/* A table row that *is* a record: the row is the link to it. Sanctioned where a
card's equivalent is not -- a row is one target, while a card is a container
of several. `.stretched-link` goes on the cell that names the record; this
class is only here to give that anchor something to stretch against.
Sibling links in the row are lifted above it, the same lift `.card-footer`
needed and for the same reason: the stretched anchor covers the whole row, so
without this a press on the station's own link silently opens the record
instead. A row with no sibling links needs no lift and pays nothing for it. */
.yksa-row-link {
position: relative;
}
/* Catalog card: a card summarising one record, where the whole card is the link
to it. The hover treatment is defined once, here, because three services had a
grid of these and none of them said what hovering meant -- the cursor changed
over the title only, and the padding around it was dead. The card carries this
class and the title anchor carries .stretched-link. */
.yksa-row-link a:not(.stretched-link) {
position: relative;
z-index: 2;
}
/* The one emphasised element allowed on a surface: the active shift, the pass in
contact, the source currently polling. Two on a page means one is wrong.
It is a heavier border in the accent colour, not a tint. A row already carries
a coloured left border and usually a state pill; adding a filled ground makes
three ways of saying one thing, and the strongest of the three drowns the row's
own content. Emphasis is the *weight* of the existing signal, not a new one. */
.yksa-row.is-active {
border-left-color: var(--bs-success);
border-left-width: 6px;
padding-left: calc(.5rem - 2px);
}
/* Catalog card: a card summarising one record. The card is a container, not a
control: its title is the link and nothing else in it moves under the pointer.
Two earlier versions made the whole card hot -- one recoloured the border and
title, one painted the hover ground -- and both put a card-sized reaction
behind a card-sized target that also held its own footer buttons, so the page
read as a grid of giant buttons and a press near a footer control was a coin
toss. Hover is reserved for rows, which are one target each.
`position: relative` stays so a card can still host absolutely-positioned
corner chrome; it no longer backs a .stretched-link. */
.yksa-card-link {
position: relative;
}
.yksa-card-link:hover {
border-color: var(--bs-primary);
background: var(--bs-tertiary-bg);
/* The title is a link and says so in the link colour, the way odms has always
drawn it. `.text-decoration-none` on the anchor suppresses the underline at
rest so a grid of forty titles is not forty underlines; hover brings it back,
which is this card's one hover affordance. Both rules have to out-specify that
utility, hence the element-qualified selectors. */
.yksa-card-link .card-title a,
.yksa-card-link h2 a,
.yksa-card-link h3 a {
color: var(--bs-link-color);
}
/* The title is already the link; underlining it on hover would be the only
moving part in a grid of forty. The border carries the affordance instead. */
.yksa-card-link:hover .stretched-link {
color: var(--bs-primary);
.yksa-card-link .card-title a:hover,
.yksa-card-link h2 a:hover,
.yksa-card-link h3 a:hover,
.yksa-card-link .card-title a:focus-visible,
.yksa-card-link h2 a:focus-visible,
.yksa-card-link h3 a:focus-visible {
color: var(--bs-link-hover-color);
text-decoration: underline;
}
/* Row chrome that appears under the pointer: the retract on one entry of a long
append-only stream, where forty always-visible trash icons would be the loudest
thing on the page. It keeps its box at rest, so revealing it never reflows the
row. Focus counts as pointing -- without that clause the control exists only
for mouse users. */
.yksa-reveal {
opacity: 0;
}
.yksa-reveal-host:hover .yksa-reveal,
.yksa-reveal:focus-visible,
.yksa-reveal-host:focus-within .yksa-reveal {
opacity: 1;
}
/* The fact grid inside a catalog card: two columns of `.yksa-label` + figure.
It was `.meta-col` in odms.css, defined against `.sat-row-card`, which is why
ops built its own with a `dl.row` instead and got a different rhythm and a
stray bottom margin. One card, one grid. */
.yksa-card-meta {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: .25rem .75rem;
}
/* Empty state: a sentence plus, where there is one, the action that resolves
@ -369,10 +567,6 @@
margin-bottom: 0;
}
/* -----------------------------------------------------------------------------
Cookie banner
-------------------------------------------------------------------------- */
.cookie-banner {
position: sticky;
bottom: 0;
@ -387,10 +581,6 @@
color: #bfdbfe;
}
/* -----------------------------------------------------------------------------
Responsive
-------------------------------------------------------------------------- */
@media (max-width: 991px) {
/* Collapsed, the nav is a stacked list; the full-height tab treatment would
put a border around every row. */
@ -407,33 +597,40 @@
}
}
/* -----------------------------------------------------------------------------
Chip filters (yksa/includes/chip_filters.html + src/chip-filters.js)
-------------------------------------------------------------------------- */
/* A filter chip is a toggle, so it looks like one at both ends of its state: an
outlined chip at rest, a filled one when it is on. It used to be `badge
bg-secondary` at rest, which made an unselected filter a solid grey block --
as loud as the selected one and easy to read as already applied. The class is
self-contained now; callers need no `badge bg-*` alongside it. */
.filter-chip {
display: inline-block;
font-size: .75rem;
line-height: 1.5;
padding: .05rem .5rem;
color: var(--bs-body-color);
background-color: var(--bs-body-bg);
border: 1px solid var(--bs-border-color);
cursor: pointer;
user-select: none;
transition: background-color 0.12s ease;
}
.filter-chip:hover {
filter: brightness(110%);
background-color: var(--yksa-hover-bg);
}
.filter-chip.active {
background-color: var(--bs-primary) !important;
color: #fff;
background-color: var(--bs-primary);
border-color: var(--bs-primary);
}
/* -----------------------------------------------------------------------------
Prose and tables
Long-form text rendered from the database (policies, descriptions) and the
table cells that hold it. Everything else that used to live in main.css was
qsl-only and did not come across.
-------------------------------------------------------------------------- */
.filter-chip.active:hover {
background-color: var(--bs-primary);
}
/* Long-form text rendered from the database (policies, descriptions) and the
table cells that hold it. */
.text-content {
overflow: auto;
hyphens: auto;
@ -459,6 +656,10 @@ td ul:last-child {
--bs-alert-border-color: #c42526;
}
/* A quiet border for grouped buttons that sit inside another bordered surface --
a catalog card's footer -- where the button group's own outline would read as a
second box around the first. `--bs-border-color` rather than a black alpha: the
alpha approximated exactly this colour on white and disappeared on anything else. */
.btn-outline-bordered {
border-color: rgba(0, 0, 0, 0.066);
border-color: var(--bs-border-color);
}