298 lines
12 KiB
CSS
298 lines
12 KiB
CSS
/* ════════════════════════════════════════════════════════════════════════
|
|
Janitorial QC — Global UI Theme
|
|
────────────────────────────────────────────────────────────────────────
|
|
Purpose: unify every signed-in page onto one polished design system by
|
|
retuning Bootstrap's own components. UI/UX only — no markup/route/JS change.
|
|
|
|
Design tokens (matching the app's premium pages: inspections/view,
|
|
templates/form_editor, etc.):
|
|
Font DM Sans
|
|
Accent #2563eb / hover #1d4ed8 / soft #eff6ff
|
|
Slate text #0f172a · heading #374151 · muted #64748b / #94a3b8
|
|
border #e2e8f0 / #cbd5e1 · surface #f8fafc / #f1f5f9 · page #eef0f4
|
|
Chrome navbar #1a1d23 (matches the dark card-headers on premium pages)
|
|
Green #16a34a Amber #f59e0b Danger #dc2626
|
|
Shape radius .85rem cards / .5rem controls · soft shadows
|
|
|
|
Load order: AFTER bootstrap.min.css, BEFORE page-level {% block extra_css %}
|
|
so any page's own styles still win.
|
|
════════════════════════════════════════════════════════════════════════ */
|
|
|
|
/* ── 1. Design tokens (retune Bootstrap CSS variables) ───────────────────── */
|
|
:root {
|
|
--bs-primary: #2563eb;
|
|
--bs-primary-rgb: 37, 99, 235;
|
|
--bs-success: #16a34a;
|
|
--bs-success-rgb: 22, 163, 74;
|
|
--bs-warning: #f59e0b;
|
|
--bs-warning-rgb: 245, 158, 11;
|
|
--bs-danger: #dc2626;
|
|
--bs-danger-rgb: 220, 38, 38;
|
|
|
|
--bs-link-color: #2563eb;
|
|
--bs-link-color-rgb: 37, 99, 235;
|
|
--bs-link-hover-color: #1d4ed8;
|
|
--bs-link-hover-color-rgb: 29, 78, 216;
|
|
|
|
--bs-body-color: #0f172a;
|
|
--bs-body-color-rgb: 15, 23, 42;
|
|
--bs-secondary-color: rgba(100, 116, 139, 1);
|
|
--bs-body-bg: #eef0f4;
|
|
--bs-border-color: #e2e8f0;
|
|
|
|
--bs-border-radius: .55rem;
|
|
--bs-border-radius-sm: .4rem;
|
|
--bs-border-radius-lg: .85rem;
|
|
--bs-border-radius-xl: 1rem;
|
|
|
|
--bs-body-font-family: 'DM Sans', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
|
|
--jqc-ink: #0f172a;
|
|
--jqc-heading: #374151;
|
|
--jqc-muted: #64748b;
|
|
--jqc-faint: #94a3b8;
|
|
--jqc-border: #e2e8f0;
|
|
--jqc-border-2: #cbd5e1;
|
|
--jqc-surface: #f8fafc;
|
|
--jqc-surface-2: #f1f5f9;
|
|
--jqc-accent: #2563eb;
|
|
--jqc-accent-700: #1d4ed8;
|
|
--jqc-accent-50: #eff6ff;
|
|
--jqc-navbar: #1a1d23;
|
|
--jqc-shadow: 0 1px 2px rgba(15, 23, 42, .06), 0 1px 3px rgba(15, 23, 42, .05);
|
|
--jqc-shadow-md: 0 4px 24px rgba(15, 23, 42, .10);
|
|
}
|
|
|
|
/* ── 2. Base typography ──────────────────────────────────────────────────── */
|
|
body {
|
|
font-family: var(--bs-body-font-family);
|
|
background-color: var(--bs-body-bg);
|
|
color: var(--jqc-ink);
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
h1, h2, h3, h4, h5, h6,
|
|
.h1, .h2, .h3, .h4, .h5, .h6 {
|
|
font-family: var(--bs-body-font-family);
|
|
color: var(--jqc-ink);
|
|
font-weight: 700;
|
|
letter-spacing: -.01em;
|
|
}
|
|
.text-muted { color: var(--jqc-muted) !important; }
|
|
a { text-underline-offset: 2px; }
|
|
|
|
/* ── 3. Navbar — dark slate chrome (matches premium dark card-headers) ────── */
|
|
.navbar.bg-primary {
|
|
background-color: var(--jqc-navbar) !important;
|
|
border-bottom: 1px solid rgba(255, 255, 255, .06);
|
|
box-shadow: 0 1px 0 rgba(15, 23, 42, .04);
|
|
}
|
|
.navbar .navbar-brand {
|
|
font-weight: 700;
|
|
letter-spacing: -.01em;
|
|
}
|
|
.navbar-dark .navbar-nav .nav-link {
|
|
color: rgba(255, 255, 255, .80);
|
|
border-radius: 6px;
|
|
padding-inline: .7rem;
|
|
transition: background-color .15s, color .15s;
|
|
}
|
|
.navbar-dark .navbar-nav .nav-link:hover { color: #fff; }
|
|
.navbar-dark .navbar-nav .nav-link.active {
|
|
background-color: rgba(37, 99, 235, .22) !important;
|
|
box-shadow: inset 0 -2px 0 var(--jqc-accent) !important;
|
|
color: #fff !important;
|
|
}
|
|
|
|
/* ── 4. Cards ────────────────────────────────────────────────────────────── */
|
|
.card {
|
|
border: 1px solid var(--jqc-border);
|
|
border-radius: var(--bs-border-radius-lg);
|
|
box-shadow: var(--jqc-shadow);
|
|
}
|
|
.card.shadow-sm { box-shadow: var(--jqc-shadow) !important; }
|
|
.card.shadow,
|
|
.card.shadow-lg { box-shadow: var(--jqc-shadow-md) !important; }
|
|
|
|
.card-header {
|
|
background-color: var(--jqc-surface);
|
|
border-bottom: 1px solid var(--jqc-border);
|
|
color: var(--jqc-heading);
|
|
font-weight: 600;
|
|
padding: .7rem 1rem;
|
|
}
|
|
.card-header.bg-light { background-color: var(--jqc-surface) !important; }
|
|
.card-header.bg-white { background-color: #fff !important; }
|
|
|
|
/* Colored stat cards (dashboard) — flatten + soften corners */
|
|
.card.text-white { border: none; }
|
|
.card.text-white .text-white-50 { color: rgba(255, 255, 255, .72) !important; }
|
|
|
|
/* ── 5. Buttons ──────────────────────────────────────────────────────────── */
|
|
.btn {
|
|
font-weight: 500;
|
|
border-radius: var(--bs-border-radius);
|
|
--bs-btn-focus-box-shadow: 0 0 0 .2rem rgba(37, 99, 235, .25);
|
|
}
|
|
.btn-primary {
|
|
--bs-btn-bg: var(--jqc-accent);
|
|
--bs-btn-border-color: var(--jqc-accent);
|
|
--bs-btn-hover-bg: var(--jqc-accent-700);
|
|
--bs-btn-hover-border-color: var(--jqc-accent-700);
|
|
--bs-btn-active-bg: var(--jqc-accent-700);
|
|
--bs-btn-active-border-color: var(--jqc-accent-700);
|
|
--bs-btn-disabled-bg: var(--jqc-accent);
|
|
--bs-btn-disabled-border-color: var(--jqc-accent);
|
|
}
|
|
.btn-outline-primary {
|
|
--bs-btn-color: var(--jqc-accent);
|
|
--bs-btn-border-color: var(--jqc-accent);
|
|
--bs-btn-hover-bg: var(--jqc-accent);
|
|
--bs-btn-hover-border-color: var(--jqc-accent);
|
|
--bs-btn-active-bg: var(--jqc-accent);
|
|
--bs-btn-active-border-color: var(--jqc-accent);
|
|
}
|
|
.btn-success {
|
|
--bs-btn-bg: var(--bs-success); --bs-btn-border-color: var(--bs-success);
|
|
--bs-btn-hover-bg: #15803d; --bs-btn-hover-border-color: #15803d;
|
|
--bs-btn-active-bg: #15803d; --bs-btn-active-border-color: #15803d;
|
|
--bs-btn-disabled-bg: var(--bs-success); --bs-btn-disabled-border-color: var(--bs-success);
|
|
}
|
|
.btn-danger {
|
|
--bs-btn-bg: var(--bs-danger); --bs-btn-border-color: var(--bs-danger);
|
|
--bs-btn-hover-bg: #b91c1c; --bs-btn-hover-border-color: #b91c1c;
|
|
--bs-btn-active-bg: #b91c1c; --bs-btn-active-border-color: #b91c1c;
|
|
--bs-btn-disabled-bg: var(--bs-danger); --bs-btn-disabled-border-color: var(--bs-danger);
|
|
}
|
|
.btn-warning {
|
|
--bs-btn-bg: var(--bs-warning); --bs-btn-border-color: var(--bs-warning);
|
|
--bs-btn-color: #422006; --bs-btn-hover-color: #422006;
|
|
--bs-btn-hover-bg: #d97706; --bs-btn-hover-border-color: #d97706;
|
|
--bs-btn-active-bg: #d97706; --bs-btn-active-border-color: #d97706;
|
|
}
|
|
.btn-outline-secondary {
|
|
--bs-btn-color: var(--jqc-muted);
|
|
--bs-btn-border-color: var(--jqc-border-2);
|
|
--bs-btn-hover-bg: var(--jqc-surface);
|
|
--bs-btn-hover-color: var(--jqc-accent);
|
|
--bs-btn-hover-border-color: var(--jqc-accent);
|
|
--bs-btn-active-bg: var(--jqc-surface-2);
|
|
--bs-btn-active-color: var(--jqc-ink);
|
|
--bs-btn-active-border-color: var(--jqc-border-2);
|
|
}
|
|
.btn-link { --bs-btn-color: var(--jqc-accent); --bs-btn-hover-color: var(--jqc-accent-700); text-decoration: none; }
|
|
|
|
/* ── 6. Forms ────────────────────────────────────────────────────────────── */
|
|
.form-label {
|
|
color: var(--jqc-heading);
|
|
font-weight: 500;
|
|
margin-bottom: .35rem;
|
|
}
|
|
.form-control, .form-select {
|
|
border-color: var(--jqc-border);
|
|
color: var(--jqc-ink);
|
|
border-radius: var(--bs-border-radius);
|
|
}
|
|
.form-control::placeholder { color: var(--jqc-faint); }
|
|
.form-control:focus, .form-select:focus {
|
|
border-color: var(--jqc-accent);
|
|
box-shadow: 0 0 0 .2rem rgba(37, 99, 235, .18);
|
|
}
|
|
.input-group-text {
|
|
background-color: var(--jqc-surface);
|
|
border-color: var(--jqc-border);
|
|
color: var(--jqc-muted);
|
|
}
|
|
.form-check-input:checked {
|
|
background-color: var(--jqc-accent);
|
|
border-color: var(--jqc-accent);
|
|
}
|
|
.form-check-input:focus {
|
|
border-color: var(--jqc-accent);
|
|
box-shadow: 0 0 0 .2rem rgba(37, 99, 235, .18);
|
|
}
|
|
|
|
/* ── 7. Tables ───────────────────────────────────────────────────────────── */
|
|
.table {
|
|
--bs-table-color: var(--jqc-ink);
|
|
--bs-table-border-color: var(--jqc-border);
|
|
border-color: var(--jqc-border);
|
|
margin-bottom: 0;
|
|
}
|
|
.table > thead th,
|
|
.table-light, .table-light > th, .table-light > td {
|
|
background-color: var(--jqc-surface-2);
|
|
color: var(--jqc-heading);
|
|
font-weight: 600;
|
|
border-bottom-color: var(--jqc-border);
|
|
}
|
|
.table > thead th {
|
|
text-transform: none;
|
|
font-size: .82rem;
|
|
letter-spacing: .01em;
|
|
padding-block: .6rem;
|
|
}
|
|
.table > tbody > tr { border-color: var(--jqc-border); }
|
|
.table-hover > tbody > tr:hover > * {
|
|
background-color: var(--jqc-surface);
|
|
--bs-table-bg-state: var(--jqc-surface);
|
|
}
|
|
/* Cards that wrap a flush table: clip corners so the table follows the radius */
|
|
.card > .table-responsive:last-child,
|
|
.card > .card-body.p-0 > .table-responsive,
|
|
.card > .card-body.p-0 > .table {
|
|
border-bottom-left-radius: var(--bs-border-radius-lg);
|
|
border-bottom-right-radius: var(--bs-border-radius-lg);
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* ── 8. Badges ───────────────────────────────────────────────────────────── */
|
|
.badge {
|
|
font-weight: 600;
|
|
letter-spacing: .01em;
|
|
border-radius: .4rem;
|
|
padding: .35em .6em;
|
|
}
|
|
|
|
/* ── 9. Alerts ───────────────────────────────────────────────────────────── */
|
|
.alert {
|
|
border-radius: var(--bs-border-radius);
|
|
border-width: 1px;
|
|
}
|
|
|
|
/* ── 10. Dropdowns ───────────────────────────────────────────────────────── */
|
|
.dropdown-menu {
|
|
border: 1px solid var(--jqc-border);
|
|
border-radius: var(--bs-border-radius);
|
|
box-shadow: var(--jqc-shadow-md);
|
|
padding: .35rem;
|
|
--bs-dropdown-link-active-bg: var(--jqc-accent);
|
|
}
|
|
.dropdown-item {
|
|
border-radius: var(--bs-border-radius-sm);
|
|
padding: .45rem .7rem;
|
|
color: var(--jqc-ink);
|
|
}
|
|
.dropdown-item:hover, .dropdown-item:focus {
|
|
background-color: var(--jqc-surface);
|
|
color: var(--jqc-accent);
|
|
}
|
|
.dropdown-divider { border-top-color: var(--jqc-border); }
|
|
|
|
/* ── 11. Misc components ─────────────────────────────────────────────────── */
|
|
.page-link { color: var(--jqc-accent); }
|
|
.page-item.active .page-link {
|
|
background-color: var(--jqc-accent);
|
|
border-color: var(--jqc-accent);
|
|
}
|
|
.nav-tabs .nav-link.active { color: var(--jqc-accent); }
|
|
.list-group-item-action:hover { background-color: var(--jqc-surface); }
|
|
.modal-content { border: 1px solid var(--jqc-border); border-radius: var(--bs-border-radius-lg); }
|
|
.spinner-border.text-primary { color: var(--jqc-accent) !important; }
|
|
hr { color: var(--jqc-border-2); }
|
|
|
|
/* Notification dropdown (base.html) — align accent + hover */
|
|
.notif-item.unread { border-left-color: var(--jqc-accent) !important; background-color: var(--jqc-accent-50) !important; }
|
|
.notif-item:hover { background-color: var(--jqc-surface) !important; }
|
|
|
|
/* ── 12. Focus visibility (accessibility) ────────────────────────────────── */
|
|
:focus-visible { outline: 2px solid rgba(37, 99, 235, .45); outline-offset: 1px; } |