mirror of
https://github.com/MeshTender/MeshTender.git
synced 2026-09-02 10:03:43 +00:00
308 lines
14 KiB
CSS
308 lines
14 KiB
CSS
/* MeshTender app-specific styles layered on top of Tabler.
|
|
Everything generic (cards, forms, buttons, tables, badges, dropdowns) comes
|
|
from Tabler; this file only covers things Tabler has no component for: the
|
|
live console/event log, the Leaflet map, and the public-key code block. */
|
|
|
|
/* ---------- Text selection ----------
|
|
The dark theme's default selection highlight is nearly invisible inside form
|
|
controls; force a high-contrast highlight everywhere, inputs included. */
|
|
::selection {
|
|
background-color: var(--tblr-primary);
|
|
color: #fff;
|
|
}
|
|
input::selection,
|
|
textarea::selection {
|
|
background-color: var(--tblr-primary);
|
|
color: #fff;
|
|
}
|
|
|
|
/* ---------- Back links (icon + text, plain link — not a button) ----------
|
|
These are navigational "← All organizations" links. Styling them as a plain
|
|
link (rather than .btn .btn-link) avoids the button's inline-flex box being
|
|
icon-height tall but text-width wide, which made the focus/hover state show
|
|
asymmetric padding (flush left/right, padded top/bottom). */
|
|
.back-link {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
color: var(--tblr-link-color);
|
|
text-decoration: none;
|
|
}
|
|
/* a.back-link beats Tabler's `a:hover:has(.icon){text-decoration:none}`. */
|
|
a.back-link:hover {
|
|
color: var(--tblr-link-hover-color);
|
|
text-decoration: underline;
|
|
}
|
|
.back-link .icon {
|
|
width: 1.25rem;
|
|
height: 1.25rem;
|
|
}
|
|
|
|
/* ---------- Public-key / monospace code blocks ---------- */
|
|
code.pk,
|
|
code.pubkey {
|
|
display: block;
|
|
word-break: break-all;
|
|
background: var(--tblr-bg-surface-secondary);
|
|
border: 1px solid var(--tblr-border-color);
|
|
border-radius: var(--tblr-border-radius);
|
|
padding: 0.5rem 0.625rem;
|
|
font-size: 0.8125rem;
|
|
/* gray-400, not --tblr-secondary (#6b7280): the latter is only 3.67:1 on the
|
|
near-black panel bg and fails WCAG AA for this key/data text. */
|
|
color: var(--tblr-gray-400);
|
|
}
|
|
|
|
/* ---------- Collapse toggle label (Show/Hide) ---------- */
|
|
.collapse-hide {
|
|
display: none;
|
|
}
|
|
[aria-expanded="true"] .collapse-show {
|
|
display: none;
|
|
}
|
|
[aria-expanded="true"] .collapse-hide {
|
|
display: inline;
|
|
}
|
|
|
|
/* A row's expanded panel (account page credential rows) lines up with the row's
|
|
heading rather than with the icon gutter to its left: one .icon (1.25rem) plus
|
|
one g-3 grid gutter (1rem). Hardcoded rather than read from --tblr-icon-size,
|
|
which Tabler redefines per component context. */
|
|
.row-panel {
|
|
padding-left: 2.25rem;
|
|
}
|
|
|
|
/* A chevron on a collapse toggle: points down when the panel is closed, up when
|
|
it's open. Bootstrap maintains aria-expanded on the toggle itself, so the same
|
|
attribute the Show/Hide labels key off drives this too — no JS of our own. */
|
|
.collapse-chevron {
|
|
transition: transform 0.2s ease;
|
|
}
|
|
[aria-expanded="true"] .collapse-chevron {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
/* ---------- Live event / console log ---------- */
|
|
.evlog {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0.75rem;
|
|
background: var(--tblr-bg-surface-secondary);
|
|
border: 1px solid var(--tblr-border-color);
|
|
border-radius: var(--tblr-border-radius);
|
|
max-height: 240px;
|
|
overflow-y: auto;
|
|
font-family: var(--tblr-font-monospace);
|
|
font-size: 0.8125rem;
|
|
}
|
|
.evlog:empty { display: none; }
|
|
.evlog.console { max-height: 320px; }
|
|
.ev { padding: 0.15rem 0; border-bottom: 1px solid var(--tblr-border-color-translucent); }
|
|
.ev:last-child { border-bottom: none; }
|
|
/* Colors chosen for WCAG AA (≥4.5:1) on the near-black log background:
|
|
--tblr-secondary (#6b7280) and --tblr-danger (#d63939) only reach ~3.7:1,
|
|
so info uses the lighter gray-400 and errors the lighter danger emphasis. */
|
|
.ev-info { color: var(--tblr-gray-400); }
|
|
.ev-confirmed { color: var(--tblr-success); font-weight: 600; }
|
|
.ev-error, .ev-timeout { color: var(--tblr-danger-text-emphasis); }
|
|
.ev-warning, .ev-denied, .ev-noreply { color: var(--tblr-warning); }
|
|
.ev-debug { color: var(--tblr-info); word-break: break-all; }
|
|
.ev-sent { color: var(--tblr-azure); }
|
|
.ev-reply { color: var(--tblr-body-color); white-space: pre-wrap; }
|
|
|
|
/* ---------- Badge contrast (dark) ----------
|
|
Tabler's light "-lt" badges set their text to the full-strength brand color
|
|
over a 10%-opacity tint, which is too dark for this small status text on the dark
|
|
theme. The list below is now maintained by TestContrastMeetsWCAGAA rather than by
|
|
eye — an earlier by-hand pass asserted "azure/success/warning/yellow already pass",
|
|
and azure (4.15:1) and teal (4.13:1) in fact did not.
|
|
|
|
Where Tabler defines a dark-theme *-text-emphasis value, use it; the rest are mixed
|
|
toward white against their own palette variable, so they stay tied to the palette
|
|
instead of becoming hand-picked hexes. */
|
|
.badge.bg-purple-lt { color: #d98fe6 !important; }
|
|
.badge.bg-secondary-lt { color: var(--tblr-secondary-text-emphasis) !important; }
|
|
.badge.bg-blue-lt { color: var(--tblr-primary-text-emphasis) !important; }
|
|
.badge.bg-azure-lt { color: color-mix(in srgb, var(--tblr-azure) 80%, #fff) !important; }
|
|
.badge.bg-teal-lt { color: color-mix(in srgb, var(--tblr-teal) 80%, #fff) !important; }
|
|
|
|
/* Tabler's danger button pairs its off-white foreground (#f9fafb) with the danger red,
|
|
which measures 4.46:1 — just under the 4.5:1 floor. Pure white reaches 4.66:1 without
|
|
touching the button's color. */
|
|
.btn-danger {
|
|
color: #fff;
|
|
}
|
|
|
|
/* ---------- Contrast corrections (WCAG AA on the dark theme) ----------
|
|
Found by internal/e2e TestContrastMeetsWCAGAA, which measures rendered pixels. All
|
|
three point at Tabler's own dark-theme *-text-emphasis values rather than
|
|
hand-picked colors, so they stay tied to the palette.
|
|
|
|
Tabler's dark theme lightens --tblr-link-hover-color but NOT --tblr-link-color, which
|
|
leaves every link at 3.55:1 on the page background and 2.94:1 on a card — below the
|
|
4.5:1 AA floor for body text. Emphasis blue measures 7.07:1 and 5.85:1. .back-link
|
|
already reads --tblr-link-color, so it's fixed by this too. */
|
|
:root {
|
|
--tblr-link-color: var(--tblr-primary-text-emphasis);
|
|
}
|
|
|
|
/* Inactive tab labels use --tblr-muted (#6b7280 → 3.67:1). Emphasis gray is 7.65:1 and
|
|
still clearly dimmer than the active tab, which uses the full body color. */
|
|
.nav-tabs .nav-link {
|
|
color: var(--tblr-secondary-text-emphasis);
|
|
}
|
|
|
|
/* ---------- Members list filter (org members page) ----------
|
|
The members page filters rows by role + search in JS (text search can't be
|
|
pure CSS). Rows carry `.d-flex` (display:flex !important), so the JS toggles
|
|
this `!important` hide class rather than an inline style, which `.d-flex`
|
|
would otherwise win over. */
|
|
#member-list .member-hidden {
|
|
display: none !important;
|
|
}
|
|
|
|
/* ---------- Step indicator (.steps) ----------
|
|
Tabler ships two unrelated components under the same `.steps` class: the
|
|
horizontal step indicator (ul.steps > li.step-item) we use on the add/added
|
|
wizards, and a numbered documentation list (div.steps > h3). The latter's
|
|
rule sets border-left + a left margin/padding, which also lands on the step
|
|
indicator — drawing a stray vertical line and an empty gap before the first
|
|
step. Undo those just for the indicator usage. */
|
|
.steps:has(> .step-item) {
|
|
margin-left: 0;
|
|
padding-left: 0;
|
|
border-left: 0;
|
|
}
|
|
|
|
/* ---------- MapLibre map (dark) ---------- */
|
|
#map {
|
|
height: 360px;
|
|
border-radius: var(--tblr-border-radius);
|
|
border: 1px solid var(--tblr-border-color);
|
|
}
|
|
/* Painted behind the WebGL canvas, so it shows while tiles are still in flight
|
|
rather than a white flash on a dark page. */
|
|
.maplibregl-map { background: #14171c; }
|
|
/* Scoped under .maplibregl-popup so these beat MapLibre's own defaults, which load
|
|
after app.css (maplibre-gl.css is pulled in per-page in the map card body). */
|
|
.maplibregl-popup .maplibregl-popup-content {
|
|
background: var(--tblr-bg-surface);
|
|
color: var(--tblr-body-color);
|
|
box-shadow: var(--tblr-box-shadow, 0 1px 2px rgba(0, 0, 0, 0.4));
|
|
}
|
|
.maplibregl-popup-anchor-top .maplibregl-popup-tip { border-bottom-color: var(--tblr-bg-surface); }
|
|
.maplibregl-popup-anchor-bottom .maplibregl-popup-tip { border-top-color: var(--tblr-bg-surface); }
|
|
.maplibregl-popup-anchor-left .maplibregl-popup-tip { border-right-color: var(--tblr-bg-surface); }
|
|
.maplibregl-popup-anchor-right .maplibregl-popup-tip { border-left-color: var(--tblr-bg-surface); }
|
|
.maplibregl-popup-close-button { color: var(--tblr-body-color); }
|
|
|
|
/* Controls: the zoom buttons, plus our own two button groups (the Dark/Light
|
|
basemap switch and the region-editor draw tools, both built in JS — see
|
|
basemap.js and regionmap.js). MapLibre's default control is a white box, which
|
|
is wrong on every page we render.
|
|
|
|
Everything here is scoped under .maplibregl-map purely to win the cascade:
|
|
maplibre-gl.css is pulled in per-page in the map card body, so it loads *after*
|
|
app.css and an equally specific rule of ours loses to it. The tell when this is
|
|
missing is subtle — the buttons we style by state still look right, and only the
|
|
unpressed ones stay white. */
|
|
.maplibregl-map .maplibregl-ctrl-group {
|
|
background: var(--tblr-bg-surface);
|
|
box-shadow: var(--tblr-box-shadow, 0 1px 2px rgba(0, 0, 0, 0.4));
|
|
}
|
|
.maplibregl-map .maplibregl-ctrl-group button { color: var(--tblr-body-color); }
|
|
/* The zoom glyphs are background-image SVGs with a hard-coded #333 fill — fine on
|
|
MapLibre's white control, invisible on our dark one, and not restyleable because
|
|
the color lives inside the data: URI. Inverting the icon is the way to reach it. */
|
|
.maplibregl-map .maplibregl-ctrl-group button .maplibregl-ctrl-icon { filter: invert(1); }
|
|
.maplibregl-map .maplibregl-ctrl-group button + button { border-top-color: var(--tblr-border-color); }
|
|
.maplibregl-map .maplibregl-ctrl-group button:not(:disabled):hover { background: var(--tblr-bg-surface-secondary); }
|
|
/* MapLibre's icon buttons are square and unlabeled; ours carry text, so they need
|
|
width and horizontal padding or the label is clipped to a 29px box. */
|
|
.maplibregl-map .mesh-basemap-ctrl button,
|
|
.maplibregl-map .mesh-draw-ctrl button {
|
|
width: auto;
|
|
min-width: 4.5rem;
|
|
padding: 0 0.5rem;
|
|
font-size: 0.75rem;
|
|
line-height: 1.75rem;
|
|
}
|
|
/* aria-pressed is the state, so it is also what styles the active button — there
|
|
is no second class to keep in sync. */
|
|
.maplibregl-map .mesh-basemap-ctrl button[aria-pressed="true"],
|
|
.maplibregl-map .mesh-draw-ctrl button[aria-pressed="true"] {
|
|
background: var(--tblr-primary);
|
|
color: #fff;
|
|
}
|
|
.maplibregl-map .mesh-draw-ctrl button:disabled { opacity: 0.45; }
|
|
/* The attribution CARTO require stays visible; only its chrome is themed. It sits
|
|
over the map, not the page, so it is deliberately NOT painted in theme tokens:
|
|
the same strip has to stay legible over both the near-black Dark Matter basemap
|
|
and the near-white Voyager one, which a token that only knows the page theme
|
|
cannot do. A dark scrim with near-white text reads on both. */
|
|
.maplibregl-map .maplibregl-ctrl.maplibregl-ctrl-attrib {
|
|
/* Both class names, because upstream's own rule is .maplibregl-ctrl.maplibregl-
|
|
ctrl-attrib — matching only one of them ties on specificity and loses, which
|
|
shows up as white-on-white text rather than as nothing happening. */
|
|
background: rgba(0, 0, 0, 0.7);
|
|
color: #f4f6fa;
|
|
}
|
|
.maplibregl-map .maplibregl-ctrl-attrib a,
|
|
.maplibregl-map .maplibregl-ctrl-attrib a:hover { color: #f4f6fa; }
|
|
|
|
/* Profile list on the org config page: a long list scrolls inside its card
|
|
instead of pushing the location-preview map off the page. Rows are compact —
|
|
Tabler's default list-group padding plus a full-size icon button makes a ~4.5rem
|
|
row, so only a couple fit before scrolling. Both are driven by CSS variables, so
|
|
turn those down here rather than overriding the padding itself. */
|
|
.profile-list {
|
|
max-height: 22rem;
|
|
overflow-y: auto;
|
|
}
|
|
.profile-list .list-group-item {
|
|
--tblr-list-group-item-padding-y: 0.375rem;
|
|
}
|
|
.profile-list .btn-icon {
|
|
--tblr-btn-padding-y: 0.25rem;
|
|
--tblr-btn-icon-size: 1.25rem;
|
|
}
|
|
|
|
/* Active region block in the config editor: highlight the block currently being
|
|
drawn on the shared region map. */
|
|
.region-block.region-active {
|
|
border-color: var(--tblr-primary) !important;
|
|
box-shadow: 0 0 0 1px var(--tblr-primary);
|
|
}
|
|
|
|
/* Footer on the centered auth layout (authbase). footerbody right-packs its two
|
|
columns, which suits a full-width page but reads as misaligned under a narrow
|
|
centered card, so center them here instead. */
|
|
.footer-centered .row {
|
|
justify-content: center;
|
|
}
|
|
|
|
/* Undo Tabler's scrollbar compensation, which shifts the whole page sideways.
|
|
@media (min-width:992px) it sets `:root { margin-left: calc(100vw - 100%) }` —
|
|
100vw includes the scrollbar, 100% doesn't, so a page long enough to scroll gets a
|
|
left margin the width of the scrollbar. The intent is to stop layout shifting when
|
|
a scrollbar appears, but the result is that every scrollable page sits ~15px right
|
|
of the window with an unpainted strip down the left edge, navbar included (it was
|
|
visible on the org Repeaters and Configuration tabs, and absent on the shorter Home
|
|
and Members tabs, which is what made it look page-specific).
|
|
|
|
scrollbar-gutter reserves the space on the correct side instead: no shift when the
|
|
scrollbar appears, no gutter on the left. Where it isn't supported the declaration
|
|
is ignored, which lands on the plain browser behavior rather than back on the
|
|
sideways shift. Overlay scrollbars (macOS, mobile) take no space at all, so the
|
|
gutter is a no-op there.
|
|
|
|
Overriding here rather than editing tabler.min.css keeps that file byte-identical
|
|
to the audited upstream artifact (internal/licenses pins its hash). */
|
|
:root {
|
|
scrollbar-gutter: stable;
|
|
}
|
|
@media (min-width: 992px) {
|
|
:root {
|
|
margin-left: 0;
|
|
}
|
|
}
|