326 lines
8.3 KiB
CSS
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);
|
|
}
|