diff --git a/README.md b/README.md index 75b64c4..603e294 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ across sites. | `weirsoe-theme.css` | Tokens (`--color-*`) + base (glow, selection, transitions) + `.wt-card/.wt-btn/.wt-input/.wt-tag` + `.prose` + theme-toggle styles | | `weirsoe-theme.js` | Persisted dark⇄light toggle + a no-flash snippet | | `adapters/bulma.css` | Maps Bulma classes → tokens (for Bulma sites) | +| `adapters/bootstrap.css` | Maps Bootstrap 5 classes → tokens (for Bootstrap sites) | | `examples/demo.html` | Standalone preview | ## Quick start (vanilla) @@ -51,6 +52,21 @@ Load the adapter **after** Bulma and **after** `weirsoe-theme.css`: Your existing `.box`, `.button`, `.input`, `.tag`, `.navbar`… are now dark and card-styled. `wish.weircon.dk` (Christmas-Community fork) consumes it this way. +## Theming an existing Bootstrap site + +Load the adapter **after** Bootstrap and **after** `weirsoe-theme.css`, and mirror +`data-theme` onto Bootstrap’s `data-bs-theme` on `` so the two stay in sync: + +```html + + + +``` + +Your existing `.card`, `.btn`, `.form-control`, `.alert`, `.table`, `.navbar`, +`.badge`… become dark and card-styled. `email-manager.weircon.dk` consumes it +this way. + ## Tokens `--color-bg --color-surface --color-surface-2 --color-surface-3 --color-border diff --git a/adapters/bootstrap.css b/adapters/bootstrap.css new file mode 100644 index 0000000..5bc2405 --- /dev/null +++ b/adapters/bootstrap.css @@ -0,0 +1,173 @@ +/* ============================================================================ + weirsoe-theme — Bootstrap 5 adapter. + Maps Bootstrap's component classes onto the weirsoe-theme tokens so an + existing Bootstrap site gets the dark, card-based look without rewriting + markup. Load AFTER bootstrap(.min).css and AFTER weirsoe-theme.css (which + defines the --color-* tokens). Mirror of adapters/bulma.css. + + Keep Bootstrap's own dark mode in sync by mirroring data-theme onto + data-bs-theme on (dark for "dark"/"terminal", light for "light"). + ========================================================================== */ + +/* ---------- page surfaces ---------- */ +body, +body.bg-light { background-color: var(--color-bg) !important; color: var(--color-text); } +.bg-white, .bg-body, .bg-body-tertiary, .bg-body-secondary { background-color: var(--color-surface) !important; } +.bg-light { background-color: var(--color-surface-2) !important; } + +/* ---------- typography ---------- */ +h1, h2, h3, h4, h5, h6, +.h1, .h2, .h3, .h4, .h5, .h6, +strong, b, label, .form-label { color: var(--color-text); } +.text-muted, .text-secondary { color: var(--color-muted) !important; } +.text-body, .text-dark, .text-black { color: var(--color-text) !important; } +.text-primary { color: var(--color-accent) !important; } +.text-success { color: var(--color-good) !important; } +.text-danger { color: var(--color-bad) !important; } +.text-warning { color: #ffd479 !important; } +a { color: var(--color-accent); } +a:hover { color: var(--color-accent-2); } +h1, .h1, h2, .h2, h3, .h3 { letter-spacing: -0.02em; } + +/* ---------- header / navbar (translucent dark, blurred) ---------- */ +.navbar, +.navbar.navbar-gradient { + background: color-mix(in srgb, var(--color-bg) 70%, transparent) !important; + -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); + border-bottom: 1px solid color-mix(in srgb, var(--color-border) 70%, transparent); + box-shadow: none !important; +} +.navbar .navbar-brand, +.navbar .nav-link, +.navbar .navbar-text { color: var(--color-text) !important; } +.navbar .nav-link { + border-radius: 0.5rem; padding: 0.375rem 0.75rem; + transition: color .2s, background-color .2s; +} +.navbar .nav-link:hover, +.navbar .nav-link.active, +.navbar .nav-link[aria-current="page"] { background: var(--color-surface-2); color: var(--color-text) !important; } +.navbar .navbar-brand { font-weight: 700; letter-spacing: -0.02em; } +.navbar-toggler { border: 1px solid var(--color-border) !important; } +.navbar-toggler:focus { box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 22%, transparent); } + +/* ---------- cards (the card-based look) ---------- */ +.card { + background: color-mix(in srgb, var(--color-surface) 75%, transparent); + border: 1px solid var(--color-border); + border-radius: var(--wt-radius); + box-shadow: var(--wt-shadow); + color: var(--color-text); + transition: border-color .16s ease, transform .16s ease, box-shadow .16s ease; +} +.card:hover { border-color: color-mix(in srgb, var(--color-accent) 35%, var(--color-border)); } +.card-header, +.card-footer { + background: color-mix(in srgb, var(--color-surface-2) 60%, transparent); + border-color: var(--color-border); + color: var(--color-text); +} +.card .card { box-shadow: none; } +.list-group-item { + background: color-mix(in srgb, var(--color-surface) 70%, transparent); + border-color: var(--color-border); + color: var(--color-text); +} +.list-group-item-action:hover { background: var(--color-surface-2); color: var(--color-text); } + +/* ---------- buttons ---------- */ +.btn { border-radius: var(--wt-radius-sm); transition: filter .15s ease, background-color .15s ease, border-color .15s ease; } +.btn-primary, +.btn-gradient { + background: var(--color-accent) !important; border-color: transparent !important; + color: var(--color-accent-ink) !important; font-weight: 650; +} +.btn-primary:hover, +.btn-gradient:hover { filter: brightness(1.08); color: var(--color-accent-ink) !important; } +.btn-secondary, .btn-light, +.btn-outline-secondary, .btn-outline-light { + background: var(--color-surface-2) !important; border-color: var(--color-border) !important; color: var(--color-text) !important; +} +.btn-secondary:hover, .btn-light:hover, +.btn-outline-secondary:hover { background: var(--color-surface-3) !important; color: var(--color-text) !important; } +.btn-outline-primary { background: transparent !important; border-color: var(--color-accent) !important; color: var(--color-accent) !important; } +.btn-outline-primary:hover { background: var(--color-accent) !important; color: var(--color-accent-ink) !important; } +.btn-danger { background: #3a1620 !important; border-color: #5b2230 !important; color: var(--color-bad) !important; } +.btn-danger:hover { filter: brightness(1.12); } +.btn-success { background: #10341f !important; border-color: #1d5a32 !important; color: var(--color-good) !important; } +.btn-warning { background: #3a2c10 !important; border-color: #5b471d !important; color: #ffd479 !important; } +.btn-link { color: var(--color-accent) !important; text-decoration: none; } +.btn-link:hover { color: var(--color-accent-2) !important; } +.btn-close { filter: invert(1) grayscale(1) brightness(1.7); } + +/* ---------- form controls ---------- */ +.form-control, .form-select, .input-group-text { + background: var(--color-surface-2) !important; border-color: var(--color-border) !important; + color: var(--color-text) !important; border-radius: var(--wt-radius-sm); +} +.form-control::placeholder { color: color-mix(in srgb, var(--color-muted) 80%, transparent); } +.form-control:focus, .form-select:focus { + border-color: var(--color-accent) !important; + box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 22%, transparent) !important; +} +.form-check-input { background-color: var(--color-surface-2); border-color: var(--color-border); } +.form-check-input:checked { background-color: var(--color-accent); border-color: var(--color-accent); } +.form-check-input:focus { border-color: var(--color-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 22%, transparent); } +.form-label { font-weight: 600; } +.form-text { color: var(--color-muted); } + +/* ---------- alerts (notification style) ---------- */ +.alert { border-radius: var(--wt-radius); border: 1px solid var(--color-border); } +.alert-success { background: #10341f; color: #c8f7d6; border-color: #1d5a32; } +.alert-danger, .alert-error { background: #3a1620; color: #ffd0d9; border-color: #5b2230; } +.alert-warning { background: #3a2c10; color: #ffe1a8; border-color: #5b471d; } +.alert-info, .alert-primary { background: #10243a; color: #cfe6ff; border-color: #1d3a5a; } + +/* ---------- tables ---------- */ +.table { + color: var(--color-text); + --bs-table-bg: transparent; --bs-table-color: var(--color-text); + --bs-table-border-color: var(--color-border); + --bs-table-striped-bg: color-mix(in srgb, var(--color-surface-2) 35%, transparent); + --bs-table-striped-color: var(--color-text); + --bs-table-hover-bg: color-mix(in srgb, var(--color-accent) 8%, transparent); + --bs-table-hover-color: var(--color-text); +} +.table > :not(caption) > * > * { border-bottom-color: var(--color-border); } +.table thead th, .em-table thead th { + background: var(--color-surface-2) !important; color: var(--color-text) !important; + border-bottom: 1px solid var(--color-border) !important; +} + +/* ---------- badges ---------- */ +.badge.bg-primary, .badge.text-bg-primary { background: var(--color-accent) !important; color: var(--color-accent-ink) !important; } +.badge.bg-secondary, .badge.text-bg-secondary { background: var(--color-surface-3) !important; color: var(--color-text) !important; } +.badge.bg-success, .badge.text-bg-success { background: #10341f !important; color: var(--color-good) !important; } +.badge.bg-danger, .badge.text-bg-danger { background: #3a1620 !important; color: var(--color-bad) !important; } +.badge.bg-warning, .badge.text-bg-warning { background: #3a2c10 !important; color: #ffd479 !important; } +.badge.bg-info, .badge.text-bg-info { background: #10243a !important; color: #cfe6ff !important; } + +/* ---------- dropdowns / modals / offcanvas ---------- */ +.dropdown-menu, .modal-content, .offcanvas { + background: var(--color-surface); border: 1px solid var(--color-border); + color: var(--color-text); border-radius: var(--wt-radius-sm); box-shadow: var(--wt-shadow); +} +.dropdown-item { color: var(--color-text); } +.dropdown-item:hover, .dropdown-item:focus { background: var(--color-surface-2); color: var(--color-accent); } +.dropdown-divider, .modal-header, .modal-footer { border-color: var(--color-border); } + +/* ---------- pagination ---------- */ +.page-link { background: var(--color-surface-2); border-color: var(--color-border); color: var(--color-text); } +.page-link:hover { background: var(--color-surface-3); color: var(--color-accent); } +.page-item.active .page-link { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-accent-ink); } +.page-item.disabled .page-link { background: var(--color-surface); color: var(--color-muted); } + +/* ---------- borders / dividers / misc ---------- */ +.border, .border-top, .border-bottom, .border-start, .border-end { border-color: var(--color-border) !important; } +hr { border-top-color: var(--color-border); color: var(--color-border); opacity: 1; } +.shadow-sm, .shadow, .shadow-lg { box-shadow: var(--wt-shadow) !important; } +.nav-tabs { border-bottom-color: var(--color-border); } +.nav-tabs .nav-link.active { background: var(--color-surface); border-color: var(--color-border) var(--color-border) var(--color-surface); color: var(--color-text); } +.progress { background: var(--color-surface-2); } +.progress-bar { background: var(--color-accent); } +code, kbd { color: var(--color-accent-2); }