From 0ce91b30edef941d897cbc46a4d5621e8190606b Mon Sep 17 00:00:00 2001 From: Luke Fretwell Date: Wed, 29 Jul 2026 07:32:43 -0700 Subject: [PATCH] Fix table header wrap, sync scangov.css light-mode fixes Indicators/Standard column headers wrap onto two lines with their sort icon - added text-nowrap. --- _includes/table_header.html | 4 +- public/css/scangov.css | 222 +++++++--- scripts/bundle.css | 790 ++++++++++++++++++------------------ 3 files changed, 554 insertions(+), 462 deletions(-) diff --git a/_includes/table_header.html b/_includes/table_header.html index d58a172d..2386cbd3 100644 --- a/_includes/table_header.html +++ b/_includes/table_header.html @@ -1,7 +1,7 @@ - Indicators - Standard + Indicators + Standard Why Guidance diff --git a/public/css/scangov.css b/public/css/scangov.css index bf810aba..e62bac04 100644 --- a/public/css/scangov.css +++ b/public/css/scangov.css @@ -92,6 +92,7 @@ --bs-card-shadow: var(--bs-shadow); --bs-box-shadow: var(--bs-shadow); --bs-box-shadow-md: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.5); + --bs-box-shadow-hover: 0 0.25rem 0.5rem rgba(0, 0, 0, 1); --bs-secondary: var(--bs-body-color); --bs-text-secondary: var(--bs-secondary); --bs-badge-bg: var(--bs-primary); @@ -179,10 +180,11 @@ --bs-nav-link-color: var(--bs-body-color); --bs-nav-link-hover-color: var(--bs-link-color); --bs-border-color: #d5dce8; - --bs-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.06); + --bs-shadow: .25rem 0.25rem 0 0 rgba(0, 0, 0, 0.20); --bs-card-shadow: var(--bs-shadow); --bs-box-shadow: var(--bs-shadow); - --bs-box-shadow-md: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.15); + --bs-box-shadow-md: .25rem 0.25rem 0 0 rgba(0, 0, 0, 0.15); + --bs-box-shadow-hover: .25rem 0.25rem 0 0 rgba(0, 0, 0, 1); --bs-secondary: var(--bs-body-color); --bs-text-secondary: var(--bs-secondary); --bs-badge-bg: var(--bs-primary); @@ -215,6 +217,7 @@ --bs-bg-success-rgb: 127, 177, 53; --bs-border-success: #7fb135; --bs-success-bg-subtle: #c5ee93; + --bs-danger-bg-subtle: #f5c2cb; } /* Status badge overrides */ @@ -494,7 +497,7 @@ button:not(.card *) { } .navbar-org { - font-family: var(--bs-font-monospace); + font-family: "JetBrains Mono ExtraBold", monospace !important; color: var(--bs-body-color) !important; text-decoration: none; margin-left: 0.5rem; @@ -537,10 +540,6 @@ a.list-group-item:hover { background-color: var(--bs-body-secondary-bg); } -.border-end { - box-shadow: var(--bs-shadow); -} - /* Social links */ .social a, .social a:visited { @@ -690,6 +689,31 @@ button.btn-primary-outline { transition: filter 0.25s ease, box-shadow 0.25s ease; } +a.btn-secondary-outline, +button.btn-secondary-outline { + background-color: var(--bs-body-bg); + color: var(--bs-body-color); + border-radius: var(--bs-border-radius-sm); + border: 1px solid var(--bs-border-color); + box-shadow: var(--bs-shadow); + cursor: pointer; + font-family: var(--bs-font-sans-serif); + padding: 0.5rem 0.875rem; + transition: filter 0.25s ease, box-shadow 0.25s ease, background-color 0.25s ease, color 0.25s ease; +} + +.btn-secondary-outline.btn-sm { + padding: 0.3rem 0.6rem; +} + +.btn-secondary-outline:hover, +.btn-secondary-outline:focus, +.btn-secondary-outline:active { + background-color: var(--bs-secondary) !important; + color: var(--bs-body-bg) !important; + border-color: var(--bs-secondary) !important; +} + .btn.btn-outline-primary { --bs-btn-color: var(--bs-body-color); --bs-btn-border-color: var(--bs-border-color); @@ -699,7 +723,9 @@ button.btn-primary-outline { --bs-btn-active-color: var(--bs-body-bg); --bs-btn-active-bg: var(--bs-body-color); --bs-btn-active-border-color: var(--bs-body-color); + box-shadow: var(--bs-shadow); padding: 0.5rem 0.875rem; + transition: filter 0.25s ease, box-shadow 0.25s ease; } .btn.btn-outline-primary.btn-lg { @@ -765,6 +791,7 @@ button.btn-primary-outline { pointer-events: none; visibility: hidden; background-color: var(--bs-body-bg) !important; + border: 1px solid var(--bs-border-color); box-shadow: var(--bs-shadow); cursor: pointer; position: fixed; @@ -857,7 +884,6 @@ button.btn-primary-outline { .nav-pills .nav-link { border: 1px solid var(--bs-border-color); - box-shadow: var(--bs-shadow); font-family: "Public Sans Regular", sans-serif !important; color: var(--bs-body-color); background-color: var(--bs-body-bg); @@ -1011,7 +1037,6 @@ button.btn-primary-outline { background-color: var(--bs-primary) !important; color: var(--bs-primary-text) !important; border-color: var(--bs-primary) !important; - box-shadow: var(--bs-shadow); } .nav-tabs.filter .nav-link:not(.active):hover i, @@ -1110,58 +1135,43 @@ button.btn-primary-outline { border-color: var(--bs-body-color) !important; } -/* Form submit buttons only - other .btn-primary uses (links styled as buttons, - feedback CTA, etc.) keep the brand blue. */ +/* Form submit buttons and the modal copy button - other .btn-primary uses (links + styled as buttons, feedback CTA, etc.) keep the brand blue. */ [data-bs-theme=light] button[type="submit"].btn-primary, [data-bs-theme=light] button[type="submit"].btn-primary:hover, [data-bs-theme=light] button[type="submit"].btn-primary:focus, -[data-bs-theme=light] button[type="submit"].btn-primary:active { +[data-bs-theme=light] button[type="submit"].btn-primary:active, +[data-bs-theme=light] .btn-primary.js-copy, +[data-bs-theme=light] .btn-primary.js-copy:hover, +[data-bs-theme=light] .btn-primary.js-copy:focus, +[data-bs-theme=light] .btn-primary.js-copy:active { background-color: var(--bs-body-color) !important; border-color: var(--bs-body-color) !important; } -/* Per-icon colors (dark mode default) */ -.nav-tabs.topnav .nav-link:not(.active) .fa-bars-progress { +/* Per-icon colors (dark mode only) - explicitly theme-gated so light mode isn't + beaten by this rule's specificity and falls through to the general + .fa-solid:not([class*="text-"]) inherit rule instead. */ +[data-bs-theme=dark] .nav-tabs.topnav .nav-link:not(.active) .fa-bars-progress { color: var(--bs-primary) !important; } -.nav-tabs.topnav .nav-link:not(.active) .fa-certificate { +[data-bs-theme=dark] .nav-tabs.topnav .nav-link:not(.active) .fa-certificate { color: #ffbe2e !important; } -.nav-tabs.topnav .nav-link:not(.active) .fa-diagram-project { +[data-bs-theme=dark] .nav-tabs.topnav .nav-link:not(.active) .fa-diagram-project { color: #7efbe1 !important; } -.nav-tabs.topnav .nav-link:not(.active) .fa-file-lines { +[data-bs-theme=dark] .nav-tabs.topnav .nav-link:not(.active) .fa-file-lines { color: #a8f2ff !important; } -.nav-tabs.topnav .nav-link:not(.active) .fa-database { +[data-bs-theme=dark] .nav-tabs.topnav .nav-link:not(.active) .fa-database { color: #a8f5d5 !important; } -/* Per-icon colors (light mode) */ -[data-bs-theme=light] .nav-tabs.topnav .nav-link:not(.active) .fa-bars-progress { - color: #8a4f00 !important; -} - -[data-bs-theme=light] .nav-tabs.topnav .nav-link:not(.active) .fa-certificate { - color: #8a6500 !important; -} - -[data-bs-theme=light] .nav-tabs.topnav .nav-link:not(.active) .fa-diagram-project { - color: #0d7a6e !important; -} - -[data-bs-theme=light] .nav-tabs.topnav .nav-link:not(.active) .fa-file-lines { - color: #1a6fa0 !important; -} - -[data-bs-theme=light] .nav-tabs.topnav .nav-link:not(.active) .fa-database { - color: #0d6b52 !important; -} - /* Hover overrides for per-icon colors — must come after default icon rules, with :hover for higher specificity */ .nav-tabs.topnav .nav-link:not(.active):hover .fa-bars-progress, .nav-tabs.topnav .nav-link:not(.active):hover .fa-certificate, @@ -1239,7 +1249,6 @@ th.sortable { } .table:not(.table-sm) { - box-shadow: var(--bs-box-shadow); font-size: 0.95rem; } @@ -1393,7 +1402,6 @@ code.language-plaintext { .card { border: 1px solid var(--bs-border-color); - box-shadow: var(--bs-shadow); width: 100%; transition: filter 0.25s ease, box-shadow 0.25s ease; } @@ -1408,31 +1416,27 @@ a.card { text-decoration: none; } -.card:not(.text-bg-success):not(.text-bg-warning):not(.text-bg-danger):hover { - filter: brightness(1.3); - box-shadow: var(--bs-box-shadow-md); -} - -[data-bs-theme=light] .card:not(.text-bg-success):not(.text-bg-warning):not(.text-bg-danger):hover { - filter: brightness(0.97); -} - .card-body i+h3 { margin-top: 0.75rem; margin-bottom: 0.5rem; } .card:has(.stretched-link) { - border-color: rgba(var(--bs-link-color-rgb), 0.9) !important; + border-color: var(--bs-border-color) !important; + box-shadow: var(--bs-shadow); opacity: 0.9; transition: border-color 0.2s ease, box-shadow 0.25s ease, opacity 0.2s ease; } -.card:has(.stretched-link):hover { - border-color: rgba(var(--bs-link-color-rgb), 1) !important; - box-shadow: var(--bs-box-shadow-md); +.card:has(.stretched-link):not(.text-bg-success):not(.text-bg-warning):not(.text-bg-danger):hover { + border-color: var(--bs-border-color) !important; + box-shadow: var(--bs-box-shadow-hover); opacity: 1; - filter: none; + filter: brightness(1.3); +} + +[data-bs-theme=light] .card:has(.stretched-link):not(.text-bg-success):not(.text-bg-warning):not(.text-bg-danger):hover { + filter: brightness(0.97); } .card-header>a, @@ -1476,7 +1480,7 @@ a.card { .text-bg-danger.card:hover, .text-bg-info.card:hover { filter: brightness(1.3); - box-shadow: var(--bs-box-shadow-md); + box-shadow: var(--bs-box-shadow-hover); } [data-bs-theme=light] .text-bg-success.card:hover, @@ -1530,9 +1534,25 @@ a.card { .card-grade, .card-grade-sm { + box-shadow: var(--bs-shadow); margin-bottom: 0; } +/* Explicit hover, independent of the stretched-link/status-color routing other + card hover rules use - guarantees every .card-grade/.card-grade-sm card gets + the same hover shadow regardless of whether it happens to be a link or carry + a text-bg-* status color. */ +.card-grade:hover, +.card-grade-sm:hover { + filter: brightness(1.3); + box-shadow: var(--bs-box-shadow-hover); +} + +[data-bs-theme=light] .card-grade:hover, +[data-bs-theme=light] .card-grade-sm:hover { + filter: brightness(0.97); +} + .card-grade .card-body { text-align: center; padding-top: 2.5rem; @@ -1867,7 +1887,6 @@ iframe { background-color: var(--bs-body-bg); border: var(--bs-border-width) solid var(--bs-border-color); border-radius: var(--bs-border-radius); - box-shadow: 0.05em 0.2em 0.6em rgba(0, 0, 0, 0.2); text-shadow: none; } @@ -2624,6 +2643,68 @@ a .fa-up-right-from-square { fill: var(--bs-body-color) !important; } +/* Icons inside a plain link (sidebar nav, table cells, card links, breadcrumbs, etc.) + should read as body text color, not link-blue - "inherit" above only works when the + surrounding text is already body-colored, and anchors default to link-blue. Buttons + are excluded since a .btn's icon should keep matching its own (often white-on-color) + button text via .btn-primary i/.btn-primary svg further down. .active is excluded too - + selected/active nav states (nav-tabs, nav-pills, list-group) deliberately keep + var(--bs-primary-text) on their own colored fill, handled by their own rules. */ +[data-bs-theme=light] a:not(.btn):not(.active) .fa-solid:not([class*="text-"]), +[data-bs-theme=light] a:not(.btn):not(.active) .fa-regular:not([class*="text-"]), +[data-bs-theme=light] a:not(.btn):not(.active) .fa-brands:not([class*="text-"]) { + color: var(--bs-body-color) !important; + fill: var(--bs-body-color) !important; +} + +/* Icons rendered via the sprite pattern (as opposed to font-glyph + icons): currentColor can resolve against either the calling or the hidden + definition depending on browser engine (see comment above on symbol + coverage), and the symbol itself carries the same fa-* class too - so any icon + used this way needs its own direct, theme-scoped override rather than relying + solely on the wrapper matching .fa-solid or the anchor-context rule above. Add + new icon classes here as the ... pattern picks + up more icons (get-profile-000siteslug-details.mjs, get-docs.mjs, etc). */ +[data-bs-theme=light] .fa-universal-access:not([class*="text-"]), +[data-bs-theme=light] .fa-bolt:not([class*="text-"]), +[data-bs-theme=light] .fa-circle-question:not([class*="text-"]), +[data-bs-theme=light] .fa-certificate:not([class*="text-"]), +[data-bs-theme=light] .fa-scroll:not([class*="text-"]), +[data-bs-theme=light] .fa-robot:not([class*="text-"]), +[data-bs-theme=light] .fa-hand-pointer:not([class*="text-"]), +[data-bs-theme=light] .fa-shield-halved:not([class*="text-"]), +[data-bs-theme=light] .fa-truck-ramp-box:not([class*="text-"]), +[data-bs-theme=light] .fa-circle-info:not([class*="text-"]), +[data-bs-theme=light] .fa-user-group:not([class*="text-"]), +[data-bs-theme=light] .fa-spray-can-sparkles:not([class*="text-"]), +[data-bs-theme=light] .fa-timeline:not([class*="text-"]), +[data-bs-theme=light] .fa-circle-nodes:not([class*="text-"]), +[data-bs-theme=light] .fa-scale-balanced:not([class*="text-"]), +[data-bs-theme=light] .fa-link:not([class*="text-"]), +[data-bs-theme=light] .fa-concierge-bell:not([class*="text-"]), +[data-bs-theme=light] .fa-table:not([class*="text-"]), +[data-bs-theme=light] .fa-circle-play:not([class*="text-"]), +[data-bs-theme=light] .fa-download:not([class*="text-"]), +[data-bs-theme=light] .fa-bullhorn:not([class*="text-"]), +[data-bs-theme=light] .fa-filter:not([class*="text-"]), +[data-bs-theme=light] .fa-graduation-cap:not([class*="text-"]), +[data-bs-theme=light] .fa-chart-simple:not([class*="text-"]), +[data-bs-theme=light] .fa-photo-film:not([class*="text-"]), +[data-bs-theme=light] .fa-crown:not([class*="text-"]), +[data-bs-theme=light] .fa-user-shield:not([class*="text-"]), +[data-bs-theme=light] .fa-wave-square:not([class*="text-"]), +[data-bs-theme=light] .fa-ranking-star:not([class*="text-"]), +[data-bs-theme=light] .fa-book-open:not([class*="text-"]), +[data-bs-theme=light] .fa-file-lines:not([class*="text-"]), +[data-bs-theme=light] .fa-bars-progress:not([class*="text-"]), +[data-bs-theme=light] .fa-star:not([class*="text-"]), +[data-bs-theme=light] .fa-arrow-right-to-bracket:not([class*="text-"]), +[data-bs-theme=light] .fa-check-circle:not([class*="text-"]), +[data-bs-theme=light] .fa-square-check:not([class*="text-"]) { + color: var(--bs-body-color) !important; + fill: var(--bs-body-color) !important; +} + /* Exceptions to the inherit-text-color rule above: these keep a fixed, meaningful color regardless of theme rather than blending into surrounding text. Selectors here need to match the blanket rule's specificity (three class-tier selectors) to win - a plain @@ -2633,6 +2714,19 @@ a .fa-up-right-from-square { color: #e41d3d !important; } +/* Status icons (pass/warning) keep their semantic color in light mode instead of + blending into body text - matches how .text-success/.text-danger utility icons + are already excluded from the blanket rule above. */ +[data-bs-theme=light] .fa-circle-check:not([class*="text-"]) { + color: var(--bs-success) !important; + fill: var(--bs-success) !important; +} + +[data-bs-theme=light] .fa-triangle-exclamation:not([class*="text-"]) { + color: var(--bs-danger) !important; + fill: var(--bs-danger) !important; +} + [data-bs-theme=light] .bg-secondary-subtle { color: var(--bs-body-color) !important; background-color: #ffffff !important; @@ -2788,7 +2882,6 @@ select.js-subfilters:hover { background-color: var(--bs-primary) !important; color: var(--bs-primary-text) !important; border-color: var(--bs-primary) !important; - box-shadow: var(--bs-shadow) !important; border-radius: 0 !important; } @@ -2817,6 +2910,17 @@ th a:focus i, th a:focus svg { text-decoration: none; } +/* Same treatment for td - icons wrapped in a table-cell link should read as + text color, not link color, unless they already carry their own text-* utility. */ +td a i:not([class*="text-"]), td a svg:not([class*="text-"]), +td a:link i:not([class*="text-"]), td a:link svg:not([class*="text-"]), +td a:visited i:not([class*="text-"]), td a:visited svg:not([class*="text-"]), +td a:hover i:not([class*="text-"]), td a:hover svg:not([class*="text-"]), +td a:focus i:not([class*="text-"]), td a:focus svg:not([class*="text-"]) { + color: var(--bs-body-color) !important; + fill: var(--bs-body-color) !important; +} + /* Sortable tables (tablesort library, public/js/tablesort-vendor.js): the icon is driven entirely by the aria-sort attribute the library sets on the sorted - a plain CSS rule, not a second piece of JS guessing at sort state. */ @@ -3129,7 +3233,6 @@ dialog::backdrop { } background-color: var(--bs-body-bg); color: var(--bs-body-color); border: 1px solid var(--bs-border-color); - box-shadow: var(--bs-box-shadow); } .tooltip[data-popper-placement^="top"] .tooltip-arrow::before { @@ -3216,6 +3319,7 @@ button.btn.btn-primary[disabled] { } .btn { + box-shadow: var(--bs-shadow); text-wrap: nowrap; } diff --git a/scripts/bundle.css b/scripts/bundle.css index 737040a1..289ec934 100644 --- a/scripts/bundle.css +++ b/scripts/bundle.css @@ -307,6 +307,7 @@ code.language-plaintext { --bs-card-shadow: var(--bs-shadow); --bs-box-shadow: var(--bs-shadow); --bs-box-shadow-md: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.5); + --bs-box-shadow-hover: 0 0.25rem 0.5rem rgba(0, 0, 0, 1); --bs-secondary: var(--bs-body-color); --bs-text-secondary: var(--bs-secondary); --bs-badge-bg: var(--bs-primary); @@ -336,11 +337,11 @@ code.language-plaintext { --bs-form-control-focus-color: var(--bs-body-color); --bs-card-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.25); /* Status colors */ - --bs-success: #70e17b; - --bs-success-rgb: 112, 225, 123; - --bs-bg-success: #70e17b; - --bs-bg-success-rgb: 112, 225, 123; - --bs-border-success: #70e17b; + --bs-success: #7e9c1d; + --bs-success-rgb: 126, 156, 29; + --bs-bg-success: #7e9c1d; + --bs-bg-success-rgb: 126, 156, 29; + --bs-border-success: #7e9c1d; --bs-warning: #face00; --bs-warning-rgb: 250, 206, 0; --bs-text-warning: #000000; @@ -394,10 +395,11 @@ code.language-plaintext { --bs-nav-link-color: var(--bs-body-color); --bs-nav-link-hover-color: var(--bs-link-color); --bs-border-color: #d5dce8; - --bs-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.06); + --bs-shadow: .25rem 0.25rem 0 0 rgba(0, 0, 0, 0.20); --bs-card-shadow: var(--bs-shadow); --bs-box-shadow: var(--bs-shadow); - --bs-box-shadow-md: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.15); + --bs-box-shadow-md: .25rem 0.25rem 0 0 rgba(0, 0, 0, 0.15); + --bs-box-shadow-hover: .25rem 0.25rem 0 0 rgba(0, 0, 0, 1); --bs-secondary: var(--bs-body-color); --bs-text-secondary: var(--bs-secondary); --bs-badge-bg: var(--bs-primary); @@ -422,15 +424,25 @@ code.language-plaintext { --bs-form-control-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.25); --bs-form-control-focus-color: var(--bs-body-color); --bs-card-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.25); - /* font vars, status colors inherited from :root */ + /* font vars inherited from :root; danger/warning status colors also inherited + since they don't change between themes - only success differs here */ + --bs-success: #7fb135; + --bs-success-rgb: 127, 177, 53; + --bs-bg-success: #7fb135; + --bs-bg-success-rgb: 127, 177, 53; + --bs-border-success: #7fb135; + --bs-success-bg-subtle: #c5ee93; + --bs-danger-bg-subtle: #f5c2cb; } /* Status badge overrides */ -/* WCAG 2.2 AA: #13171f on #70e17b = 8.19:1 ✓ */ +/* WCAG 2.2 AA: #13171f on #7e9c1d (dark) = 5.69:1 ✓, #13171f on #7fb135 (light) = 4.65:1 ✓ + background-color pulls from the theme's --bs-success-rgb (same source .bg-success uses) + so the plain and text utilities never drift apart across themes. */ .text-bg-success { color: #13171f !important; - background-color: #70e17b !important; + background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important; } /* WCAG 2.2 AA: #000 on #face00 = 15.18:1 ✓ */ @@ -439,10 +451,12 @@ code.language-plaintext { background-color: #face00 !important; } -/* WCAG 2.2 AA: #fff on #e41d3d = 4.73:1 ✓ */ +/* WCAG 2.2 AA: #fff on #e41d3d = 4.73:1 ✓ - rgba(...) here matches the progress + bar's plain .bg-danger, which already uses Bootstrap's own --bs-danger-rgb-based + background rather than a flat hex, so card and progress-bar reds stay identical. */ .text-bg-danger { color: #ffffff !important; - background-color: #e41d3d !important; + background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-primary { @@ -664,9 +678,7 @@ button:not(.card *) { } .navbar { - box-shadow: var(--bs-shadow); - transition: box-shadow 0.25s ease; - --bs-navbar-padding-y: 0.5rem; + --bs-navbar-padding-y: 0.25rem; --bs-navbar-padding-x: 0; --bs-nav-link-padding-y: 0.5rem; --bs-nav-link-padding-x: 1rem; @@ -679,6 +691,7 @@ button:not(.card *) { display: flex; align-items: center; gap: 0.25rem; + margin-right: 0; } .navbar-brand>svg { @@ -693,6 +706,24 @@ button:not(.card *) { fill: var(--bs-link-color) !important; } +.navbar-brand-container { + display: flex; + align-items: center; +} + +.navbar-org { + font-family: "JetBrains Mono ExtraBold", monospace !important; + color: var(--bs-body-color) !important; + text-decoration: none; + margin-left: 0.5rem; + padding-left: 0.5rem; + border-left: 1px solid var(--bs-border-color); +} + +.navbar-org:hover { + color: var(--bs-link-color) !important; +} + .navbar-toggler-icon { color: var(--bs-body-color) !important; } @@ -724,10 +755,6 @@ a.list-group-item:hover { background-color: var(--bs-body-secondary-bg); } -.border-end { - box-shadow: var(--bs-shadow); -} - /* Social links */ .social a, .social a:visited { @@ -772,6 +799,10 @@ a.list-group-item:hover { text-decoration: none; } +.bc-domain-name { + font-family: "JetBrains Mono ExtraBold", monospace !important; +} + .breadcrumb-profile .breadcrumb-item, .breadcrumb-profile .breadcrumb-item a, .breadcrumb-profile .breadcrumb-item a:visited { @@ -815,6 +846,12 @@ a.list-group-item:hover { color: var(--bs-body-color); } +.breadcrumb-item a.bc-domain:hover, +[data-bs-theme=light] .breadcrumb-item a.bc-domain:hover { + color: var(--bs-link-color) !important; + text-decoration: none; +} + /* Default (Bootstrap-style) breadcrumb — no bold, standard colors */ .breadcrumb-default .breadcrumb-item, .breadcrumb-default .breadcrumb-item a, @@ -867,6 +904,31 @@ button.btn-primary-outline { transition: filter 0.25s ease, box-shadow 0.25s ease; } +a.btn-secondary-outline, +button.btn-secondary-outline { + background-color: var(--bs-body-bg); + color: var(--bs-body-color); + border-radius: var(--bs-border-radius-sm); + border: 1px solid var(--bs-border-color); + box-shadow: var(--bs-shadow); + cursor: pointer; + font-family: var(--bs-font-sans-serif); + padding: 0.5rem 0.875rem; + transition: filter 0.25s ease, box-shadow 0.25s ease, background-color 0.25s ease, color 0.25s ease; +} + +.btn-secondary-outline.btn-sm { + padding: 0.3rem 0.6rem; +} + +.btn-secondary-outline:hover, +.btn-secondary-outline:focus, +.btn-secondary-outline:active { + background-color: var(--bs-secondary) !important; + color: var(--bs-body-bg) !important; + border-color: var(--bs-secondary) !important; +} + .btn.btn-outline-primary { --bs-btn-color: var(--bs-body-color); --bs-btn-border-color: var(--bs-border-color); @@ -876,7 +938,9 @@ button.btn-primary-outline { --bs-btn-active-color: var(--bs-body-bg); --bs-btn-active-bg: var(--bs-body-color); --bs-btn-active-border-color: var(--bs-body-color); + box-shadow: var(--bs-shadow); padding: 0.5rem 0.875rem; + transition: filter 0.25s ease, box-shadow 0.25s ease; } .btn.btn-outline-primary.btn-lg { @@ -941,17 +1005,13 @@ button.btn-primary-outline { opacity: 0; pointer-events: none; visibility: hidden; - background-color: var(--bs-primary); - color: var(--bs-primary-text) !important; - border-radius: var(--bs-border-radius); - border: 1px solid var(--bs-primary); + background-color: var(--bs-body-bg) !important; + border: 1px solid var(--bs-border-color); box-shadow: var(--bs-shadow); cursor: pointer; - font-family: var(--bs-font-bold); position: fixed; bottom: 1rem; - left: 50%; - transform: translateX(-50%); + right: 1rem; z-index: 1000; text-decoration: none; transition: opacity 0.25s ease, visibility 0.25s ease, filter 0.25s ease, box-shadow 0.25s ease; @@ -963,14 +1023,17 @@ button.btn-primary-outline { visibility: visible; } -.btn-primary i, .btn-primary svg, -.feedback i, .feedback svg { +.btn-primary i, .btn-primary svg { color: inherit !important; transition: filter 0.25s ease; } -.btn-primary:hover, .btn-primary:focus, .btn-primary:active, -.feedback:hover, .feedback:focus, .feedback:active { +.btn.feedback i, .btn.feedback svg { + color: var(--bs-primary) !important; + transition: filter 0.25s ease; +} + +.btn-primary:hover, .btn-primary:focus, .btn-primary:active { filter: brightness(1.3); box-shadow: var(--bs-box-shadow-md); background-color: var(--bs-primary) !important; @@ -978,6 +1041,17 @@ button.btn-primary-outline { border-color: var(--bs-primary) !important; } +.feedback:hover, .feedback:focus, .feedback:active { + background-color: var(--bs-body-color) !important; + box-shadow: var(--bs-box-shadow-md); +} + +.btn.feedback:hover i, .btn.feedback:hover svg, +.btn.feedback:focus i, .btn.feedback:focus svg, +.btn.feedback:active i, .btn.feedback:active svg { + color: var(--bs-btn-hover-color) !important; +} + .btn-primary-outline:not(.active):hover, .btn-primary-outline:not(.active):focus, .btn-primary-outline:not(.active):active, @@ -1025,7 +1099,6 @@ button.btn-primary-outline { .nav-pills .nav-link { border: 1px solid var(--bs-border-color); - box-shadow: var(--bs-shadow); font-family: "Public Sans Regular", sans-serif !important; color: var(--bs-body-color); background-color: var(--bs-body-bg); @@ -1179,7 +1252,6 @@ button.btn-primary-outline { background-color: var(--bs-primary) !important; color: var(--bs-primary-text) !important; border-color: var(--bs-primary) !important; - box-shadow: var(--bs-shadow); } .nav-tabs.filter .nav-link:not(.active):hover i, @@ -1245,46 +1317,74 @@ button.btn-primary-outline { filter: brightness(1.3); } -/* Per-icon colors (dark mode default) */ -.nav-tabs.topnav .nav-link:not(.active) .fa-bars-progress { - color: var(--bs-primary) !important; +/* Light mode: active/selected nav states use body text color (black) instead of the + link/primary blue - sidebar, nav-pills, and tabs all read as "selected" via a solid + black fill rather than brand color. Text/icon color stays var(--bs-primary-text) + (white in light mode), which still contrasts fine against black. */ +[data-bs-theme=light] .nav-pills .nav-link.active, +[data-bs-theme=light] .nav-pills .nav-link.active:focus, +[data-bs-theme=light] .nav-pills .nav-link.active:hover, +[data-bs-theme=light] .nav-pills a.btn-primary-outline.active, +[data-bs-theme=light] .nav-pills a.btn-primary-outline.active:focus, +[data-bs-theme=light] .nav-pills a.btn-primary-outline.active:hover { + background-color: var(--bs-body-color) !important; + border-color: var(--bs-body-color) !important; } -.nav-tabs.topnav .nav-link:not(.active) .fa-certificate { - color: #ffbe2e !important; +[data-bs-theme=light] .list-group-item.active, +[data-bs-theme=light] .list-group-item.active:focus, +[data-bs-theme=light] .list-group-item.active:hover { + background-color: var(--bs-body-color) !important; + border-color: var(--bs-body-color) !important; } -.nav-tabs.topnav .nav-link:not(.active) .fa-diagram-project { - color: #7efbe1 !important; +[data-bs-theme=light] .nav-tabs.filter .nav-link.active, +[data-bs-theme=light] .nav-tabs.filter .nav-link.active:focus { + background-color: var(--bs-body-color) !important; + border-color: var(--bs-body-color) !important; } -.nav-tabs.topnav .nav-link:not(.active) .fa-file-lines { - color: #a8f2ff !important; +[data-bs-theme=light] .nav-tabs.topnav .nav-link.active, +[data-bs-theme=light] .nav-tabs.topnav .nav-link.active:focus { + background-color: var(--bs-body-color) !important; + border-color: var(--bs-body-color) !important; } -.nav-tabs.topnav .nav-link:not(.active) .fa-database { - color: #a8f5d5 !important; +/* Form submit buttons and the modal copy button - other .btn-primary uses (links + styled as buttons, feedback CTA, etc.) keep the brand blue. */ +[data-bs-theme=light] button[type="submit"].btn-primary, +[data-bs-theme=light] button[type="submit"].btn-primary:hover, +[data-bs-theme=light] button[type="submit"].btn-primary:focus, +[data-bs-theme=light] button[type="submit"].btn-primary:active, +[data-bs-theme=light] .btn-primary.js-copy, +[data-bs-theme=light] .btn-primary.js-copy:hover, +[data-bs-theme=light] .btn-primary.js-copy:focus, +[data-bs-theme=light] .btn-primary.js-copy:active { + background-color: var(--bs-body-color) !important; + border-color: var(--bs-body-color) !important; } -/* Per-icon colors (light mode) */ -[data-bs-theme=light] .nav-tabs.topnav .nav-link:not(.active) .fa-bars-progress { - color: #8a4f00 !important; +/* Per-icon colors (dark mode only) - explicitly theme-gated so light mode isn't + beaten by this rule's specificity and falls through to the general + .fa-solid:not([class*="text-"]) inherit rule instead. */ +[data-bs-theme=dark] .nav-tabs.topnav .nav-link:not(.active) .fa-bars-progress { + color: var(--bs-primary) !important; } -[data-bs-theme=light] .nav-tabs.topnav .nav-link:not(.active) .fa-certificate { - color: #8a6500 !important; +[data-bs-theme=dark] .nav-tabs.topnav .nav-link:not(.active) .fa-certificate { + color: #ffbe2e !important; } -[data-bs-theme=light] .nav-tabs.topnav .nav-link:not(.active) .fa-diagram-project { - color: #0d7a6e !important; +[data-bs-theme=dark] .nav-tabs.topnav .nav-link:not(.active) .fa-diagram-project { + color: #7efbe1 !important; } -[data-bs-theme=light] .nav-tabs.topnav .nav-link:not(.active) .fa-file-lines { - color: #1a6fa0 !important; +[data-bs-theme=dark] .nav-tabs.topnav .nav-link:not(.active) .fa-file-lines { + color: #a8f2ff !important; } -[data-bs-theme=light] .nav-tabs.topnav .nav-link:not(.active) .fa-database { - color: #0d6b52 !important; +[data-bs-theme=dark] .nav-tabs.topnav .nav-link:not(.active) .fa-database { + color: #a8f5d5 !important; } /* Hover overrides for per-icon colors — must come after default icon rules, with :hover for higher specificity */ @@ -1364,7 +1464,6 @@ th.sortable { } .table:not(.table-sm) { - box-shadow: var(--bs-box-shadow); font-size: 0.95rem; } @@ -1518,7 +1617,6 @@ code.language-plaintext { .card { border: 1px solid var(--bs-border-color); - box-shadow: var(--bs-shadow); width: 100%; transition: filter 0.25s ease, box-shadow 0.25s ease; } @@ -1533,31 +1631,27 @@ a.card { text-decoration: none; } -.card:not(.text-bg-success):not(.text-bg-warning):not(.text-bg-danger):hover { - filter: brightness(1.3); - box-shadow: var(--bs-box-shadow-md); -} - -[data-bs-theme=light] .card:not(.text-bg-success):not(.text-bg-warning):not(.text-bg-danger):hover { - filter: brightness(0.97); -} - .card-body i+h3 { margin-top: 0.75rem; margin-bottom: 0.5rem; } .card:has(.stretched-link) { - border-color: rgba(var(--bs-link-color-rgb), 0.9) !important; + border-color: var(--bs-border-color) !important; + box-shadow: var(--bs-shadow); opacity: 0.9; transition: border-color 0.2s ease, box-shadow 0.25s ease, opacity 0.2s ease; } -.card:has(.stretched-link):hover { - border-color: rgba(var(--bs-link-color-rgb), 1) !important; - box-shadow: var(--bs-box-shadow-md); +.card:has(.stretched-link):not(.text-bg-success):not(.text-bg-warning):not(.text-bg-danger):hover { + border-color: var(--bs-border-color) !important; + box-shadow: var(--bs-box-shadow-hover); opacity: 1; - filter: none; + filter: brightness(1.3); +} + +[data-bs-theme=light] .card:has(.stretched-link):not(.text-bg-success):not(.text-bg-warning):not(.text-bg-danger):hover { + filter: brightness(0.97); } .card-header>a, @@ -1581,7 +1675,7 @@ a.card { /* Status cards — consistent across themes */ .text-bg-success.card { - border-color: #70e17b !important; + border-color: var(--bs-success) !important; } .text-bg-warning.card { @@ -1589,14 +1683,26 @@ a.card { } .text-bg-danger.card { - border-color: #e41d3d !important; + border-color: var(--bs-danger) !important; +} + +.text-bg-info.card { + border-color: #0dcaf0 !important; } .text-bg-success.card:hover, .text-bg-warning.card:hover, -.text-bg-danger.card:hover { +.text-bg-danger.card:hover, +.text-bg-info.card:hover { filter: brightness(1.3); - box-shadow: var(--bs-box-shadow-md); + box-shadow: var(--bs-box-shadow-hover); +} + +[data-bs-theme=light] .text-bg-success.card:hover, +[data-bs-theme=light] .text-bg-warning.card:hover, +[data-bs-theme=light] .text-bg-danger.card:hover, +[data-bs-theme=light] .text-bg-info.card:hover { + border-color: var(--bs-border-color) !important; } /* Icons and links inside status cards match card text color */ @@ -1614,13 +1720,22 @@ a.card { fill: #ffffff !important; } +/* Padding/font applies to every grade-style card header, colored or not (e.g. the + uncolored "Add new site" card on /sites), so they all line up consistently. The + tinted background/border-bottom below stays scoped to colored variants only, since + it's a white overlay meant to sit on top of a color. */ +.card-grade .card-header, +.card-grade-sm .card-header { + padding: 0.75rem 1rem; + font-family: var(--bs-font-regular); +} + .text-bg-success .card-header, .text-bg-warning .card-header, -.text-bg-danger .card-header { +.text-bg-danger .card-header, +.text-bg-info .card-header { background-color: rgba(255, 255, 255, 0.15) !important; border-bottom: 1px solid rgba(255, 255, 255, 0.2) !important; - padding: 0.75rem 1rem; - font-family: var(--bs-font-regular); --bs-card-cap-bg: rgba(255, 255, 255, 0.15); } @@ -1634,15 +1749,37 @@ a.card { .card-grade, .card-grade-sm { + box-shadow: var(--bs-shadow); margin-bottom: 0; } +/* Explicit hover, independent of the stretched-link/status-color routing other + card hover rules use - guarantees every .card-grade/.card-grade-sm card gets + the same hover shadow regardless of whether it happens to be a link or carry + a text-bg-* status color. */ +.card-grade:hover, +.card-grade-sm:hover { + filter: brightness(1.3); + box-shadow: var(--bs-box-shadow-hover); +} + +[data-bs-theme=light] .card-grade:hover, +[data-bs-theme=light] .card-grade-sm:hover { + filter: brightness(0.97); +} + .card-grade .card-body { text-align: center; padding-top: 2.5rem; padding-bottom: 2.5rem; } +.card-grade .card-body .display-1, +.card-body.text-center .display-1 { + line-height: 1; + margin-bottom: 0; +} + .card-grade-sm .card-body { text-align: center; padding-top: 1.25rem; @@ -1650,6 +1787,7 @@ a.card { } .card-grade-sm .card-body .display-2 { + line-height: 1; margin-bottom: 0; } @@ -1964,7 +2102,6 @@ iframe { background-color: var(--bs-body-bg); border: var(--bs-border-width) solid var(--bs-border-color); border-radius: var(--bs-border-radius); - box-shadow: 0.05em 0.2em 0.6em rgba(0, 0, 0, 0.2); text-shadow: none; } @@ -2320,6 +2457,13 @@ td a:hover .fa-circle-info { color: var(--bs-body-color) !important; } +/* Sidenav link hover comes before the icon-color rules below so those rules - + same specificity, but later in source - keep winning on hover, instead of the + icons flashing link-color like the surrounding link text does. */ +.js-sidenav a:hover { + color: var(--bs-link-color) !important; +} + .js-sidenav a .fa-circle-question, .js-sidenav a:hover .fa-circle-question { color: #97d4ea !important; @@ -2687,341 +2831,135 @@ a .fa-up-right-from-square { color: #ffbc78 !important; } -/* Icons — light mode overrides (WCAG 2.2 AA compliant, min 4.5:1 on #fff) */ - -[data-bs-theme=light] .fa-arrow-right-to-bracket, -[data-bs-theme=light] .fa-arrows-rotate, -[data-bs-theme=light] .fa-compact-disc, -[data-bs-theme=light] .fa-building, -[data-bs-theme=light] .fa-check-circle, -[data-bs-theme=light] .fa-circle-check, -[data-bs-theme=light] .fa-circle-info, -[data-bs-theme=light] .fa-circle-plus, -[data-bs-theme=light] .fa-puzzle-piece, -[data-bs-theme=light] .fa-right-from-bracket, -[data-bs-theme=light] .fa-rotate, -[data-bs-theme=light] .fa-seedling, -[data-bs-theme=light] .fa-toggle-on { - /* → #2d7a3a = 5.31:1 ✓ */ - color: #2d7a3a !important; -} - -[data-bs-theme=light] .fa-bell, -[data-bs-theme=light] .fa-copy, -[data-bs-theme=light] .fa-diagram-project, -[data-bs-theme=light] .fa-envelope-open-text, -[data-bs-theme=light] .fa-graduation-cap, -[data-bs-theme=light] .fa-link, -[data-bs-theme=light] .fa-ranking-star, -[data-bs-theme=light] .fa-table, -[data-bs-theme=light] .fa-table-columns, -[data-bs-theme=light] .fa-up-right-from-square { - /* → #0d7a6e = 5.22:1 ✓ */ - color: #0d7a6e !important; -} - -[data-bs-theme=light] .fa-expand { - /* → #0a6ebd = 5.28:1 ✓ */ - color: #0a6ebd !important; -} - -[data-bs-theme=light] .fa-terminal { - /* → #147a5f = 5.28:1 ✓ */ - color: #147a5f !important; -} - -[data-bs-theme=light] .fa-bell-concierge, -[data-bs-theme=light] .fa-brain, -[data-bs-theme=light] .fa-bug, -[data-bs-theme=light] .fa-code-branch, -[data-bs-theme=light] .fa-concierge-bell, -[data-bs-theme=light] .fa-face-frown, -[data-bs-theme=light] .fa-life-ring, -[data-bs-theme=light] .fa-map, -[data-bs-theme=light] .fa-server { - /* → #8a5e2d = 5.65:1 ✓ */ - color: #8a5e2d !important; -} - -[data-bs-theme=light] .fa-bolt, -[data-bs-theme=light] .fa-broom, -[data-bs-theme=light] .fa-certificate, -[data-bs-theme=light] .fa-check, -[data-bs-theme=light] .fa-crown, -[data-bs-theme=light] .fa-dolly, -[data-bs-theme=light] .fa-dove, -[data-bs-theme=light] .fa-face-grin-hearts, -[data-bs-theme=light] .fa-face-sad-tear, -[data-bs-theme=light] .fa-face-smile, -[data-bs-theme=light] .fa-file-contract, -[data-bs-theme=light] .fa-hand, [data-bs-theme=light] .fa-hands, -[data-bs-theme=light] .fa-key, -[data-bs-theme=light] .fa-hands-clapping, -[data-bs-theme=light] .fa-handshake, -[data-bs-theme=light] .fa-handshake-simple, -[data-bs-theme=light] .fa-handshake-angle, -[data-bs-theme=light] .fa-hand-peace, -[data-bs-theme=light] .fa-hand-point-up, -[data-bs-theme=light] .fa-hand-point-down, -[data-bs-theme=light] .fa-hand-point-right, -[data-bs-theme=light] .fa-hand-point-left, -[data-bs-theme=light] .fa-highlighter, -[data-bs-theme=light] .fa-lightbulb, -[data-bs-theme=light] .fa-message, -[data-bs-theme=light] .fa-magnifying-glass, -[data-bs-theme=light] .fa-magnifying-glass-chart, -[data-bs-theme=light] .fa-plug-circle-bolt, -[data-bs-theme=light] .fa-robot, -[data-bs-theme=light] .fa-scale-balanced, -[data-bs-theme=light] .fa-scroll, -[data-bs-theme=light] .fa-star, -[data-bs-theme=light] .fa-stop-circle, -[data-bs-theme=light] .fa-wand-magic-sparkles, -[data-bs-theme=light] .fa-wand-sparkles { - /* → #8a6500 = 5.33:1 ✓ */ - color: #8a6500 !important; -} - -[data-bs-theme=light] .fa-arrow-right-arrow-left, -[data-bs-theme=light] .fa-asterisk { - /* → #8a6500 on #fff = 5.33:1 ✓ */ - color: #8a6500 !important; -} - -[data-bs-theme=light] .fa-bars-progress, -[data-bs-theme=light] .fa-chart-simple, -[data-bs-theme=light] .fa-chart-line, -[data-bs-theme=light] .fa-circle-exclamation, -[data-bs-theme=light] .fa-dashboard, -[data-bs-theme=light] .fa-fire, -[data-bs-theme=light] .fa-folder-tree, -[data-bs-theme=light] .fa-gauge, -[data-bs-theme=light] .fa-gauge-simple, -[data-bs-theme=light] .fa-gauge-high, -[data-bs-theme=light] .fa-rss, -[data-bs-theme=light] .fa-screwdriver-wrench, -[data-bs-theme=light] .fa-sitemap { - /* → #8a4f00 = 6.56:1 ✓ */ - color: #8a4f00 !important; -} - -[data-bs-theme=light] .btn.btn-outline-primary .fa-sitemap { - color: #8a4f00 !important; -} - -[data-bs-theme=light] .btn.btn-outline-primary:hover .fa-sitemap, -[data-bs-theme=light] .btn.btn-outline-primary:focus .fa-sitemap { - color: inherit !important; -} - -[data-bs-theme=light] .btn.btn-outline-primary .fa-wand-sparkles { - color: #8a6500 !important; -} +/* Icons — light mode: inherit surrounding text color instead of individual accent colors. + Excludes icons that carry their own Bootstrap text-color utility (e.g. the sidebar's + pass/fail status dots, .text-success/.text-danger up/down changelog arrows) - those are + semantic status color, not decorative icon color, and must keep winning. */ -[data-bs-theme=light] .btn.btn-outline-primary:hover .fa-wand-sparkles, -[data-bs-theme=light] .btn.btn-outline-primary:focus .fa-wand-sparkles { +/* Font-glyph icons: color is all that's needed to render the glyph, and "inherit" + (rather than a hardcoded value) lets icons inside colored contexts - like a button's + own white text - pick up their parent's color instead of being forced black. */ +[data-bs-theme=light] .fa-solid:not([class*="text-"]), +[data-bs-theme=light] .fa-regular:not([class*="text-"]), +[data-bs-theme=light] .fa-brands:not([class*="text-"]) { color: inherit !important; } -[data-bs-theme=light] .btn.btn-outline-primary .fa-file-lines { - color: #1a6fa0 !important; -} - -[data-bs-theme=light] .btn.btn-outline-primary:hover .fa-file-lines, -[data-bs-theme=light] .btn.btn-outline-primary:focus .fa-file-lines { - color: inherit !important; -} - -[data-bs-theme=light] .btn.btn-outline-primary .fa-rocket { - color: #5b4ea0 !important; -} - -[data-bs-theme=light] .btn.btn-outline-primary:hover .fa-rocket, -[data-bs-theme=light] .btn.btn-outline-primary:focus .fa-rocket { - color: inherit !important; -} - -[data-bs-theme=light] .fa-arrow-right, -[data-bs-theme=light] .fa-arrow-right-long, -[data-bs-theme=light] .fa-book-open, -[data-bs-theme=light] .fa-book-open-reader, -[data-bs-theme=light] .fa-truck-ramp-box, -[data-bs-theme=light] .fa-calendar, -[data-bs-theme=light] .fa-circle-nodes, -[data-bs-theme=light] .fa-download, -[data-bs-theme=light] .fa-envelope, -[data-bs-theme=light] .fa-file, -[data-bs-theme=light] .fa-file-lines, -[data-bs-theme=light] .fa-file-arrow-down, -[data-bs-theme=light] .fa-file-csv, -[data-bs-theme=light] .fa-file-code, -[data-bs-theme=light] .fa-markdown, -[data-bs-theme=light] .fa-linkedin, -[data-bs-theme=light] .fa-linkedin-in, -[data-bs-theme=light] .fa-x-twitter, -[data-bs-theme=light] .fa-paper-plane, -[data-bs-theme=light] .fa-percent, -[data-bs-theme=light] .fa-list-check, -[data-bs-theme=light] .fa-satellite-dish, -[data-bs-theme=light] .fa-square-check, -[data-bs-theme=light] .fa-universal-access { - /* → #1a6fa0 = 5.49:1 ✓ */ - color: #1a6fa0 !important; -} - -[data-bs-theme=light] .fa-binoculars, -[data-bs-theme=light] .fa-circle-play, -[data-bs-theme=light] .fa-eye { - /* → #1a7a6a = 5.21:1 ✓ */ - color: #1a7a6a !important; -} - -[data-bs-theme=light] .fa-bluesky, -[data-bs-theme=light] .fa-code, -[data-bs-theme=light] .fa-bullhorn, -[data-bs-theme=light] .fa-discord, -[data-bs-theme=light] .fa-github, -[data-bs-theme=light] .fa-user-group, -[data-bs-theme=light] .fa-wave-square { - /* → #4a6bb5 = 5.17:1 ✓ */ - color: #4a6bb5 !important; -} - -[data-bs-theme=light] .fa-comment, -[data-bs-theme=light] .fa-comments, -[data-bs-theme=light] .fa-filter, -[data-bs-theme=light] .fa-print, -[data-bs-theme=light] .fa-rocket { - /* → #5b4ea0 = 6.90:1 ✓ */ - color: #5b4ea0 !important; -} - -[data-bs-theme=light] .feedback .fa-rocket, -[data-bs-theme=light] .feedback .fa-message { - color: inherit !important; -} - -[data-bs-theme=light] .fa-circle-notch { - /* → #5a5a5a = 6.90:1 ✓ */ - color: #5a5a5a !important; -} - -[data-bs-theme=light] .fa-circle-question { - /* → #1a6fa0 = 5.49:1 ✓ */ - color: #1a6fa0; -} - -[data-bs-theme=light] a .fa-circle-question, -[data-bs-theme=light] a:hover .fa-circle-question, -[data-bs-theme=light] a .fa-circle-info, -[data-bs-theme=light] a:hover .fa-circle-info { - color: inherit !important; -} - -[data-bs-theme=light] .js-sidenav a .fa-circle-question, -[data-bs-theme=light] .js-sidenav a:hover .fa-circle-question { - color: #1a6fa0 !important; -} - -[data-bs-theme=light] .js-sidenav a .fa-circle-info, -[data-bs-theme=light] .js-sidenav a:hover .fa-circle-info { - color: #2d7a3a !important; -} - -[data-bs-theme=light] .fa-triangle-exclamation { - /* → #b03030 = 5.62:1 ✓ */ - color: #b03030; -} - -[data-bs-theme=light] .fa-box-archive, -[data-bs-theme=light] .fa-file-pdf, -[data-bs-theme=light] .fa-filter-circle-xmark, -[data-bs-theme=light] .fa-rectangle-list, -[data-bs-theme=light] .fa-share-from-square, -[data-bs-theme=light] .fa-user-astronaut { - /* → #b5476a = 5.15:1 ✓ */ - color: #b5476a !important; -} - -[data-bs-theme=light] .fa-circle-down, -[data-bs-theme=light] .fa-circle-xmark, -[data-bs-theme=light] .fa-flag { - /* → #b52040 = 6.47:1 ✓ */ - color: #b52040 !important; -} - -[data-bs-theme=light] .fa-circle-up { - /* → #1a7a2a = 5.10:1 ✓ */ - color: #1a7a2a !important; -} - -[data-bs-theme=light] .fa-building-columns, -[data-bs-theme=light] .fa-circle-dot, -[data-bs-theme=light] .fa-gear, -[data-bs-theme=light] .fa-gears, -[data-bs-theme=light] .fa-network-wired, -[data-bs-theme=light] .fa-newspaper { - /* → #1a5e9e = 6.69:1 ✓ */ - color: #1a5e9e !important; -} - -[data-bs-theme=light] .fa-heart, -[data-bs-theme=light] .fa-heart-crack, -[data-bs-theme=light] .fa-lock, -[data-bs-theme=light] .fa-shield-halved { - /* → #c0392b = 5.44:1 ✓ */ - color: #c0392b !important; +/* svg/use icons (as opposed to font-glyph icons) render from a defined + once in the hidden sprite sheet (svgsprites.mjs) and cloned via at each call + site. Browsers differ on whether currentColor for that clone resolves against the + symbol's own class list or the call site's wrapping , so + both are set explicitly (not "inherit", an actual value) to cover either resolution + path regardless of engine. */ +[data-bs-theme=light] symbol[data-prefix="fas"]:not([class*="text-"]), +[data-bs-theme=light] symbol[data-prefix="far"]:not([class*="text-"]), +[data-bs-theme=light] symbol[data-prefix="fab"]:not([class*="text-"]) { + color: var(--bs-body-color) !important; + fill: var(--bs-body-color) !important; } -[data-bs-theme=light] .fa-house { - /* → #7a2ea0 = 7.70:1 ✓ */ - color: #7a2ea0 !important; +/* Icons inside a plain link (sidebar nav, table cells, card links, breadcrumbs, etc.) + should read as body text color, not link-blue - "inherit" above only works when the + surrounding text is already body-colored, and anchors default to link-blue. Buttons + are excluded since a .btn's icon should keep matching its own (often white-on-color) + button text via .btn-primary i/.btn-primary svg further down. .active is excluded too - + selected/active nav states (nav-tabs, nav-pills, list-group) deliberately keep + var(--bs-primary-text) on their own colored fill, handled by their own rules. */ +[data-bs-theme=light] a:not(.btn):not(.active) .fa-solid:not([class*="text-"]), +[data-bs-theme=light] a:not(.btn):not(.active) .fa-regular:not([class*="text-"]), +[data-bs-theme=light] a:not(.btn):not(.active) .fa-brands:not([class*="text-"]) { + color: var(--bs-body-color) !important; + fill: var(--bs-body-color) !important; } -[data-bs-theme=light] .fa-laptop { - /* → #1a6e6a = 6.03:1 ✓ */ - color: #1a6e6a !important; +/* Icons rendered via the sprite pattern (as opposed to font-glyph + icons): currentColor can resolve against either the calling or the hidden + definition depending on browser engine (see comment above on symbol + coverage), and the symbol itself carries the same fa-* class too - so any icon + used this way needs its own direct, theme-scoped override rather than relying + solely on the wrapper matching .fa-solid or the anchor-context rule above. Add + new icon classes here as the ... pattern picks + up more icons (get-profile-000siteslug-details.mjs, get-docs.mjs, etc). */ +[data-bs-theme=light] .fa-universal-access:not([class*="text-"]), +[data-bs-theme=light] .fa-bolt:not([class*="text-"]), +[data-bs-theme=light] .fa-circle-question:not([class*="text-"]), +[data-bs-theme=light] .fa-certificate:not([class*="text-"]), +[data-bs-theme=light] .fa-scroll:not([class*="text-"]), +[data-bs-theme=light] .fa-robot:not([class*="text-"]), +[data-bs-theme=light] .fa-hand-pointer:not([class*="text-"]), +[data-bs-theme=light] .fa-shield-halved:not([class*="text-"]), +[data-bs-theme=light] .fa-truck-ramp-box:not([class*="text-"]), +[data-bs-theme=light] .fa-circle-info:not([class*="text-"]), +[data-bs-theme=light] .fa-user-group:not([class*="text-"]), +[data-bs-theme=light] .fa-spray-can-sparkles:not([class*="text-"]), +[data-bs-theme=light] .fa-timeline:not([class*="text-"]), +[data-bs-theme=light] .fa-circle-nodes:not([class*="text-"]), +[data-bs-theme=light] .fa-scale-balanced:not([class*="text-"]), +[data-bs-theme=light] .fa-link:not([class*="text-"]), +[data-bs-theme=light] .fa-concierge-bell:not([class*="text-"]), +[data-bs-theme=light] .fa-table:not([class*="text-"]), +[data-bs-theme=light] .fa-circle-play:not([class*="text-"]), +[data-bs-theme=light] .fa-download:not([class*="text-"]), +[data-bs-theme=light] .fa-bullhorn:not([class*="text-"]), +[data-bs-theme=light] .fa-filter:not([class*="text-"]), +[data-bs-theme=light] .fa-graduation-cap:not([class*="text-"]), +[data-bs-theme=light] .fa-chart-simple:not([class*="text-"]), +[data-bs-theme=light] .fa-photo-film:not([class*="text-"]), +[data-bs-theme=light] .fa-crown:not([class*="text-"]), +[data-bs-theme=light] .fa-user-shield:not([class*="text-"]), +[data-bs-theme=light] .fa-wave-square:not([class*="text-"]), +[data-bs-theme=light] .fa-ranking-star:not([class*="text-"]), +[data-bs-theme=light] .fa-book-open:not([class*="text-"]), +[data-bs-theme=light] .fa-file-lines:not([class*="text-"]), +[data-bs-theme=light] .fa-bars-progress:not([class*="text-"]), +[data-bs-theme=light] .fa-star:not([class*="text-"]), +[data-bs-theme=light] .fa-arrow-right-to-bracket:not([class*="text-"]), +[data-bs-theme=light] .fa-check-circle:not([class*="text-"]), +[data-bs-theme=light] .fa-square-check:not([class*="text-"]) { + color: var(--bs-body-color) !important; + fill: var(--bs-body-color) !important; } -[data-bs-theme=light] .fa-landmark { - /* → #1a5e9e = 6.69:1 ✓ */ - color: #1a5e9e !important; +/* Exceptions to the inherit-text-color rule above: these keep a fixed, meaningful color + regardless of theme rather than blending into surrounding text. Selectors here need to + match the blanket rule's specificity (three class-tier selectors) to win - a plain + type selector or combinator isn't enough to beat it, since specificity compares the + class/attribute tier first regardless of how many type selectors are added. */ +[data-bs-theme=light] .fa-solid.fa-heart { + color: #e41d3d !important; } -[data-bs-theme=light] .fa-share-nodes, -[data-bs-theme=light] .fa-check-to-slot, -[data-bs-theme=light] .fa-timeline { - /* → #9e2060 = 7.42:1 ✓ */ - color: #9e2060 !important; +/* Status icons (pass/warning) keep their semantic color in light mode instead of + blending into body text - matches how .text-success/.text-danger utility icons + are already excluded from the blanket rule above. */ +[data-bs-theme=light] .fa-circle-check:not([class*="text-"]) { + color: var(--bs-success) !important; + fill: var(--bs-success) !important; } -[data-bs-theme=light] .fa-database { - /* → #0d6b52 = 7.3:1 ✓ */ - color: #0d6b52 !important; +[data-bs-theme=light] .fa-triangle-exclamation:not([class*="text-"]) { + color: var(--bs-danger) !important; + fill: var(--bs-danger) !important; } -[data-bs-theme=light] .fa-arrow-pointer, -[data-bs-theme=light] .fa-hand-pointer, -[data-bs-theme=light] .fa-mastodon, -[data-bs-theme=light] .fa-photo-film, -[data-bs-theme=light] .fa-spray-can-sparkles { - /* → #6b3fa0 = 7.38:1 ✓ */ - color: #6b3fa0 !important; +[data-bs-theme=light] .bg-secondary-subtle { + color: var(--bs-body-color) !important; + background-color: #ffffff !important; } -[data-bs-theme=light] .fa-user-shield { - /* → #8a4e00 = 6.62:1 ✓ */ - color: #8a4e00 !important; +[data-bs-theme=light] .breadcrumb-item .fa-solid:not([class*="text-"]) { + color: var(--bs-body-color) !important; } -[data-bs-theme=light] .fa-window-maximize { - /* → #a03050 = 6.92:1 ✓ */ - color: #a03050 !important; +/* Light-mode-specific card colors - the Tasks/info card differs from its dark-mode + value here, unlike red/yellow/green (green now via --bs-success-rgb above) which + stay consistent through variables rather than a separate override block. */ +[data-bs-theme=light] .text-bg-info { + background-color: #58b4ff !important; } -[data-bs-theme=light] .fa-youtube { - /* → #c0392b = 5.44:1 ✓ */ - color: #c0392b !important; +[data-bs-theme=light] .text-bg-info.card { + border-color: #58b4ff !important; } /* Status card icons — must come last to beat FA light mode overrides */ @@ -3159,7 +3097,6 @@ select.js-subfilters:hover { background-color: var(--bs-primary) !important; color: var(--bs-primary-text) !important; border-color: var(--bs-primary) !important; - box-shadow: var(--bs-shadow) !important; border-radius: 0 !important; } @@ -3188,6 +3125,58 @@ th a:focus i, th a:focus svg { text-decoration: none; } +/* Same treatment for td - icons wrapped in a table-cell link should read as + text color, not link color, unless they already carry their own text-* utility. */ +td a i:not([class*="text-"]), td a svg:not([class*="text-"]), +td a:link i:not([class*="text-"]), td a:link svg:not([class*="text-"]), +td a:visited i:not([class*="text-"]), td a:visited svg:not([class*="text-"]), +td a:hover i:not([class*="text-"]), td a:hover svg:not([class*="text-"]), +td a:focus i:not([class*="text-"]), td a:focus svg:not([class*="text-"]) { + color: var(--bs-body-color) !important; + fill: var(--bs-body-color) !important; +} + +/* Sortable tables (tablesort library, public/js/tablesort-vendor.js): the icon is + driven entirely by the aria-sort attribute the library sets on the sorted - + a plain CSS rule, not a second piece of JS guessing at sort state. */ +table[data-sortable] th[role="columnheader"] { + cursor: pointer; +} + +th[aria-sort="ascending"] .fa-sort { + --fa: "\f0de" !important; +} + +th[aria-sort="descending"] .fa-sort { + --fa: "\f0dd" !important; +} + +/* Same aria-sort-driven icon convention, expressed as three sibling icons instead + of one - needed on the Eleventy static sites, where @11ty/font-awesome rewrites + every into an inline SVG sprite at build time. A sprite's + shape is fixed by which it references, so it can't be repainted via a + CSS content/custom-property swap the way a font glyph can; toggling which of + three fixed icons is visible works regardless of that rewrite. */ +th .sort-asc, +th .sort-desc { + display: none; +} + +th[aria-sort="ascending"] .sort-neutral, +th[aria-sort="ascending"] .sort-desc, +th[aria-sort="descending"] .sort-neutral, +th[aria-sort="descending"] .sort-asc { + display: none; +} + +th[aria-sort="ascending"] .sort-asc { + display: inline; +} + +th[aria-sort="descending"] .sort-desc { + display: inline; +} + /* Docs */ #post h2 { margin-top: 2.5rem; @@ -3211,10 +3200,6 @@ th a:focus i, th a:focus svg { top: 1rem; } -.js-sidenav a:hover { - color: var(--bs-link-color) !important; -} - /* Actions list: separate boxes — restore left border + full radius on every item */ .actions.list-group .list-group-item { padding: 0; @@ -3264,10 +3249,6 @@ th a:focus i, th a:focus svg { color: #ffbe2e; } -[data-bs-theme=light] .fa-signs-post { - color: #8a6500 !important; -} - /* Lunr search results */ #lunrsearchresults { padding-top: 0.2rem; @@ -3467,7 +3448,6 @@ dialog::backdrop { } background-color: var(--bs-body-bg); color: var(--bs-body-color); border: 1px solid var(--bs-border-color); - box-shadow: var(--bs-box-shadow); } .tooltip[data-popper-placement^="top"] .tooltip-arrow::before { @@ -3516,6 +3496,7 @@ body[data-bs-theme='dark'] ul.navbar-nav img { .card-footer { color: inherit !important; + font-family: var(--bs-font-monospace); } a.cardlink { @@ -3553,6 +3534,7 @@ button.btn.btn-primary[disabled] { } .btn { + box-shadow: var(--bs-shadow); text-wrap: nowrap; } @@ -3591,6 +3573,12 @@ td.padded-cell { --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e") } +[data-bs-theme=light] .form-select.js-subfilters { + background-color: var(--bs-body-color); + color: var(--bs-body-bg); + --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e") +} + blockquote .lead { margin: 2rem 0; }