yksa-web-kit/yksa_web/static/yksa/css/kit.css
2026-08-17 22:42:04 +08:00

326 lines
8.3 KiB
CSS

/* =============================================================================
YKSA UI kit. Ships with yksa-web-kit; see ecosystem/ui-kit.md.
Requires Bootstrap >= 5.3: the state pills below are built on the
--bs-*-bg-subtle / --bs-*-text-emphasis / --bs-*-border-subtle families, and
text-body-secondary is a 5.3 utility. On 5.2.3 every rule here still parses
and the page renders wrong -- muted text is not muted, tinted backgrounds are
transparent. The bundled bootstrap.min.css is the version this is written for;
do not point a service at its own older copy.
Two rules govern the whole kit:
1. Typography carries meaning through weight, colour and size only. No
small-caps, no thin/semibold, no letter-spacing, no .display-*. Weights
are 400 and 700.
2. Colour is never the only signal. Every state renders an icon and a word
alongside its hue.
Anything Bootstrap already provides is used as-is. The classes below exist
only where it has no equivalent.
============================================================================= */
:root {
--navbar-height: 44px;
}
/* -----------------------------------------------------------------------------
Navbar and footer -- layout-identical across every service
-------------------------------------------------------------------------- */
.custom-navbar {
height: var(--navbar-height);
padding-top: 0;
padding-bottom: 0;
z-index: 1002;
border: none;
background-color: var(--bs-body-bg) !important;
}
.navbar {
z-index: 1002;
}
.navbar-brand {
margin-right: 1em;
}
/* Nav links fill the bar's height and butt against one another, so the active
item reads as a selected tab rather than a highlighted word. */
.nav-full-height {
display: flex;
align-items: center;
height: 100%;
}
.nav-full-height.nav-link {
color: inherit;
padding-left: 1rem !important;
padding-right: 1rem !important;
padding-top: 12px;
background-color: var(--bs-body-bg);
margin-right: -1px;
}
.nav-full-height.nav-link:hover,
.nav-full-height.nav-link.active {
color: #fff !important;
background-color: var(--bs-primary);
}
/* -----------------------------------------------------------------------------
State -- the sanctioned rendering of status (ui/_state.html)
-------------------------------------------------------------------------- */
.yksa-state {
display: inline-flex;
align-items: center;
gap: .25rem;
font-size: .75rem;
font-weight: 700;
padding: .05rem .4rem;
white-space: nowrap;
border: 1px solid transparent;
}
/* health */
.yksa-state-ok,
.yksa-state-complete {
color: var(--bs-success-text-emphasis);
background: var(--bs-success-bg-subtle);
border-color: var(--bs-success-border-subtle);
}
.yksa-state-warning,
.yksa-state-forced {
color: var(--bs-warning-text-emphasis);
background: var(--bs-warning-bg-subtle);
border-color: var(--bs-warning-border-subtle);
}
.yksa-state-failed {
color: var(--bs-danger-text-emphasis);
background: var(--bs-danger-bg-subtle);
border-color: var(--bs-danger-border-subtle);
}
.yksa-state-running,
.yksa-state-in-progress {
color: var(--bs-primary-text-emphasis);
background: var(--bs-primary-bg-subtle);
border-color: var(--bs-primary-border-subtle);
}
.yksa-state-skipped,
.yksa-state-unknown,
.yksa-state-not-started {
color: var(--bs-secondary-color);
background: var(--bs-secondary-bg-subtle);
border-color: var(--bs-border-color);
}
/* Neutral metadata chip. Not a coloured badge, because it carries no status. */
.yksa-chip {
display: inline-flex;
align-items: center;
gap: .25rem;
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);
}
/* -----------------------------------------------------------------------------
Typography helpers
-------------------------------------------------------------------------- */
.yksa-label {
font-size: .75rem;
font-weight: 700;
color: var(--bs-secondary-color);
}
.yksa-hint {
font-size: .75rem;
font-weight: 400;
color: var(--bs-secondary-color);
}
/* Tabular numerals in the body face. Monospace is reserved for verbatim machine
text -- hex dumps, TLE lines, command mnemonics. */
.yksa-num {
font-variant-numeric: tabular-nums;
}
.yksa-readout {
font-variant-numeric: tabular-nums;
font-weight: 700;
line-height: 1;
}
.yksa-utc {
font-variant-numeric: tabular-nums;
}
.yksa-utc .yksa-local {
color: var(--bs-secondary-color);
font-size: .8em;
}
/* -----------------------------------------------------------------------------
Layout
-------------------------------------------------------------------------- */
/* A record in a list is a row with a left status marker, not a card. */
.yksa-row {
display: flex;
align-items: center;
gap: .75rem;
padding: .5rem;
border-left: 4px solid var(--bs-border-color);
}
.yksa-row:hover {
background: var(--bs-tertiary-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); }
/* 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);
}
/* Empty state: a sentence plus, where there is one, the action that resolves
it -- beside the sentence, not under it. Never a blank card. */
.yksa-empty {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: .75rem;
padding: 1.25rem;
color: var(--bs-secondary-color);
}
/* Reading column for forms and reference pages that are worse for being wide. */
.yksa-narrow {
max-width: 920px;
}
/* Save bar pinned to the viewport bottom, plus a spacer so it never covers the
last row of the form it belongs to. */
.yksa-actionbar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 1030;
}
.yksa-actionbar-spacer {
height: 5rem;
}
.form-error-bar ul {
margin-bottom: 0;
}
/* -----------------------------------------------------------------------------
Cookie banner
-------------------------------------------------------------------------- */
.cookie-banner {
position: sticky;
bottom: 0;
left: 0;
right: 0;
z-index: 1030;
background-color: #1f2937;
color: #f8fafc;
}
.cookie-banner a {
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. */
.nav-full-height.nav-link {
padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);
margin-right: 0;
border: none !important;
}
.custom-navbar {
height: auto;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
}
/* -----------------------------------------------------------------------------
Chip filters (yksa/includes/chip_filters.html + src/chip-filters.js)
-------------------------------------------------------------------------- */
.filter-chip {
cursor: pointer;
user-select: none;
transition: background-color 0.12s ease;
}
.filter-chip:hover {
filter: brightness(110%);
}
.filter-chip.active {
background-color: var(--bs-primary) !important;
color: #fff;
}
/* -----------------------------------------------------------------------------
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.
-------------------------------------------------------------------------- */
.text-content {
overflow: auto;
hyphens: auto;
}
.text-content img {
max-width: 100%;
height: auto !important;
}
.text-content p:last-child,
.text-content ul:last-child,
.text-content ol:last-child,
td p:last-child,
td ul:last-child {
margin-bottom: 0 !important;
}
/* Django's messages framework emits `error`; Bootstrap only ships `danger`. */
.alert-error {
--bs-alert-color: #fff;
--bs-alert-bg: #c42526;
--bs-alert-border-color: #c42526;
}
.btn-outline-bordered {
border-color: rgba(0, 0, 0, 0.066);
}