/* 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; } /* ---------- Leaflet map (dark) ---------- */ #map { height: 360px; border-radius: var(--tblr-border-radius); border: 1px solid var(--tblr-border-color); } .leaflet-container { background: #14171c; } /* Scoped under .leaflet-popup so these beat Leaflet's own defaults, which load after app.css (leaflet.css is pulled in per-page in the map card body). */ .leaflet-popup .leaflet-popup-content-wrapper, .leaflet-popup .leaflet-popup-tip { 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)); } .leaflet-popup .leaflet-popup-content { color: var(--tblr-body-color); } .leaflet-container .leaflet-bar a, .leaflet-container .leaflet-control-zoom a { background: var(--tblr-bg-surface); color: var(--tblr-body-color); border-bottom-color: var(--tblr-border-color); } .leaflet-container .leaflet-bar a:hover { background: var(--tblr-bg-surface-secondary); } /* Base-layer (Dark/Light) switcher: match the dark UI so the native radios render correctly. On the default white box + dark color-scheme, an unselected radio shows as a black dot; a dark box fixes that, and accent-color tints the selected one. Scoped under .leaflet-container to beat leaflet.css (loaded after app.css). */ .leaflet-container .leaflet-control-layers { background: var(--tblr-bg-surface); color: var(--tblr-body-color); border-color: var(--tblr-border-color); box-shadow: var(--tblr-box-shadow, 0 1px 2px rgba(0, 0, 0, 0.4)); } .leaflet-container .leaflet-control-layers-list label { margin: 0; color: var(--tblr-body-color); } .leaflet-container .leaflet-control-layers-selector { accent-color: var(--tblr-primary); } /* 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; } }