/* ── Dark UI kit tokens (uiCookies) — identical in every set ──────────────────────────────
Dark first. Elevation is a lighter surface, not a shadow: surface-0 is the page, 1 cards and
panels, 2 controls, menus and dialogs, 3 hover, selected rows and tooltips.
Every colour is a light-dark() pair, so flipping color-scheme turns the whole kit light:
data-theme="dark" | "light" | "system" on any wrapper (system follows prefers-color-scheme),
or a checked .theme-pick radio inside a [data-theme] element (:has()).
Contrast, dark | light, on the worst surface (surface-3): text-1 12.2 | 15.0, text-2 6.6 | 7.3,
text-3 4.8 | 5.4, accent 5.4 | 5.1, success 7.7 | 5.0, warning 8.4 | 5.2, danger 6.8 | 5.0;
edge (control boundaries) 3.3 | 3.1 and focus 7.9 | 5.9 against every surface. */
:root{color-scheme:dark}
[data-theme="dark"]{color-scheme:dark}
[data-theme="light"]{color-scheme:light}
[data-theme="system"]{color-scheme:light dark}
[data-theme]:has(.theme-pick[value="dark"]:checked){color-scheme:dark}
[data-theme]:has(.theme-pick[value="light"]:checked){color-scheme:light}
[data-theme]:has(.theme-pick[value="system"]:checked){color-scheme:light dark}
:root{
--surface-0:light-dark(#eef1f5,#0b0d12); /* page */
--surface-1:light-dark(#ffffff,#151821); /* cards, panels, navbar, sidebar */
--surface-2:light-dark(#f6f7f9,#1d212c); /* controls, menus, dialogs, toasts */
--surface-3:light-dark(#e6e9ef,#262b38); /* hover, selected rows, tooltips */
--text-1:light-dark(#12151c,#eceef3); /* primary text */
--text-2:light-dark(#434a5a,#aab1bf); /* secondary text */
--text-3:light-dark(#555d6e,#8f96a8); /* captions, placeholders, axis labels */
--border:light-dark(#dde1e8,#2a2f3c); /* decorative 1px dividers and card edges */
--edge:light-dark(#7c8496,#737b90); /* control boundaries: fields, boxes, tracks */
--accent:light-dark(#4452d0,#8b97ff); /* the one vivid colour: links, selected, primary */
--accent-soft:light-dark(#e6e8ff,#232a4d); /* selected backgrounds; accent text keeps 5.2 | 5.3 on it */
--on-accent:light-dark(#ffffff,#0b0d12); /* text on accent fills, 6.2 | 7.4 */
--success:light-dark(#11713f,#5fd4a0); /* desaturated for dark, so they do not vibrate */
--warning:light-dark(#875500,#f0c060);
--danger:light-dark(#bb2532,#ff9790);
--focus:light-dark(#3b48c4,#b5bdff); /* 2px focus ring */
--highlight:light-dark(rgb(255 255 255 / 0),rgb(255 255 255 / .045)); /* 1px top light on dark surfaces */
--shadow-color:light-dark(rgb(16 24 40 / .16),rgb(0 0 0 / .55)); /* overlays only */
--radius:12px;--radius-sm:8px;
--mono:ui-monospace,"SF Mono","Cascadia Code",Menlo,Consolas,monospace;
}
/* No light-dark() (Chrome < 123, Safari < 17.5, Firefox < 120): the same values, chosen by attribute */
@supports not (color:light-dark(#fff,#000)){
:root,[data-theme="dark"]{--surface-0:#0b0d12;--surface-1:#151821;--surface-2:#1d212c;--surface-3:#262b38;--text-1:#eceef3;--text-2:#aab1bf;--text-3:#8f96a8;--border:#2a2f3c;--edge:#737b90;--accent:#8b97ff;--accent-soft:#232a4d;--on-accent:#0b0d12;--success:#5fd4a0;--warning:#f0c060;--danger:#ff9790;--focus:#b5bdff;--highlight:rgb(255 255 255 / .045);--shadow-color:rgb(0 0 0 / .55)}
[data-theme="light"]{--surface-0:#eef1f5;--surface-1:#ffffff;--surface-2:#f6f7f9;--surface-3:#e6e9ef;--text-1:#12151c;--text-2:#434a5a;--text-3:#555d6e;--border:#dde1e8;--edge:#7c8496;--accent:#4452d0;--accent-soft:#e6e8ff;--on-accent:#ffffff;--success:#11713f;--warning:#875500;--danger:#bb2532;--focus:#3b48c4;--highlight:rgb(255 255 255 / 0);--shadow-color:rgb(16 24 40 / .16)}
@media (prefers-color-scheme:light){[data-theme="system"]{--surface-0:#eef1f5;--surface-1:#ffffff;--surface-2:#f6f7f9;--surface-3:#e6e9ef;--text-1:#12151c;--text-2:#434a5a;--text-3:#555d6e;--border:#dde1e8;--edge:#7c8496;--accent:#4452d0;--accent-soft:#e6e8ff;--on-accent:#ffffff;--success:#11713f;--warning:#875500;--danger:#bb2532;--focus:#3b48c4;--highlight:rgb(255 255 255 / 0);--shadow-color:rgb(16 24 40 / .16)}}
}
@media (prefers-contrast:more){:root{--border:var(--edge);--text-3:var(--text-2)}}
[data-theme]{background:var(--surface-0);color:var(--text-1)}
/* Components */
:where([class^="dk-"],[class*=" dk-"]),:where([class^="dk-"],[class*=" dk-"])::before,:where([class^="dk-"],[class*=" dk-"])::after{box-sizing:border-box}
.dk-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.dk-ico{width:18px;height:18px;flex:none}
.dk-kbd{display:inline-grid;place-items:center;min-width:1.45rem;height:1.35rem;padding:0 .35rem;border:1px solid var(--border);border-bottom-width:2px;
border-radius:5px;background:var(--surface-2);color:var(--text-2);font:500 .72rem/1 var(--mono)}
.dk-link{color:var(--accent);font-weight:600;text-decoration:underline;text-decoration-color:color-mix(in srgb,var(--accent) 45%,transparent);text-underline-offset:3px}
.dk-link:hover{text-decoration-color:currentColor}
.dk-link:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:3px}
/* Card: surface-1 with a 1px border and a 1px top highlight (dark only); no shadow */
.dk-card{padding:20px;border:1px solid var(--border);border-radius:var(--radius);background:var(--surface-1);color:var(--text-1);box-shadow:inset 0 1px 0 var(--highlight)}
.dk-card h2,.dk-card h3,.dk-card p{margin:0}
.dk-card__title{font-size:1rem;font-weight:650;letter-spacing:-.01em}
.dk-card__sub{margin-top:2px;font-size:.85rem;color:var(--text-2)}
/* Buttons: one recipe, variants only swap four custom properties */
.dk-btn{--b-bg:var(--surface-2);--b-fg:var(--text-1);--b-bd:var(--border);--b-hover:var(--surface-3);
display:inline-flex;align-items:center;justify-content:center;gap:.5rem;min-height:38px;padding:.5rem 1rem;
border:1px solid var(--b-bd);border-radius:var(--radius-sm);background:var(--b-bg);color:var(--b-fg);
font-family:inherit;font-size:.875rem;font-weight:600;line-height:1.2;text-decoration:none;white-space:nowrap;cursor:pointer;
box-shadow:inset 0 1px 0 var(--highlight);transition:background-color .15s,border-color .15s,color .15s}
.dk-btn:hover{background:var(--b-hover)}
.dk-btn:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.dk-btn--primary{--b-bg:var(--accent);--b-fg:var(--on-accent);--b-bd:var(--accent);--b-hover:color-mix(in srgb,var(--accent) 86%,var(--text-1))}
.dk-btn--ghost{--b-bg:transparent;--b-bd:transparent;--b-fg:var(--text-2);box-shadow:none}
.dk-btn--ghost:hover{color:var(--text-1)}
.dk-btn--danger{--b-fg:var(--danger);--b-bd:color-mix(in srgb,var(--danger) 40%,var(--border))}
.dk-btn--sm{min-height:32px;padding:.35rem .75rem;font-size:.8rem}
.dk-btn--lg{min-height:46px;padding:.7rem 1.35rem;font-size:.95rem}
.dk-btn--block{width:100%}
.dk-btn:disabled{opacity:.5;cursor:not-allowed}
.dk-btn[aria-busy="true"]{cursor:progress}
.dk-btn:disabled:hover{background:var(--b-bg)}
.dk-btn--icon{width:38px;padding:0}
.dk-btn--icon.dk-btn--sm{width:32px}
.dk-btn--icon.dk-btn--lg{width:46px}
.dk-btn--round{border-radius:999px}
.dk-spin{width:15px;height:15px;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:dk-spin .75s linear infinite}
@keyframes dk-spin{to{transform:rotate(1turn)}}
@media (prefers-reduced-motion:reduce){.dk-btn{transition:none}.dk-spin{animation-duration:2.4s}}
/* Fields: surface-2 well, 1px edge at 3:1, accent border and focus ring on focus */
.dk-field{display:grid;gap:6px;min-width:0}
.dk-field>label,.dk-label{font-size:.82rem;font-weight:600;color:var(--text-1)}
.dk-hint{font-size:.78rem;color:var(--text-3)}
.dk-hint--error{display:flex;align-items:center;gap:.35rem;color:var(--danger)}
.dk-hint--error .dk-ico{width:15px;height:15px}
.dk-input{width:100%;min-height:40px;padding:.55rem .8rem;border:1px solid var(--edge);border-radius:var(--radius-sm);background:var(--surface-2);color:var(--text-1);
font-family:inherit;font-size:.9rem;line-height:1.4;transition:border-color .15s,background-color .15s}
.dk-input::placeholder{color:var(--text-3);opacity:1}
.dk-input:hover{border-color:var(--text-3)}
.dk-input:focus{outline:2px solid var(--focus);outline-offset:1px;border-color:var(--accent)}
.dk-input[aria-invalid="true"]{border-color:var(--danger)}
textarea.dk-input{min-height:92px;resize:vertical}
select.dk-input{appearance:none;padding-right:2.4rem;cursor:pointer;
background-image:linear-gradient(45deg,transparent 50%,var(--text-2) 50%),linear-gradient(135deg,var(--text-2) 50%,transparent 50%);
background-position:calc(100% - 18px) 52%,calc(100% - 13px) 52%;background-size:5px 5px;background-repeat:no-repeat}
.dk-input-wrap{position:relative;display:flex;align-items:center}
.dk-input-wrap>.dk-ico{position:absolute;left:.75rem;color:var(--text-3);pointer-events:none}
.dk-input-wrap>.dk-input{padding-left:2.35rem}
.dk-input-wrap>.dk-kbd{position:absolute;right:.6rem}
.dk-input-wrap>.dk-input--kbd{padding-right:3rem}
@media (prefers-reduced-motion:reduce){.dk-input{transition:none}}
/* Checkbox and radio: a visually hidden input and a drawn box; the tick is an inline SVG */
.dk-check{display:flex;align-items:flex-start;gap:.65rem;font-size:.88rem;line-height:1.4;cursor:pointer}
.dk-check small{display:block;font-size:.78rem;color:var(--text-2)}
.dk-check__box{display:grid;place-items:center;flex:none;width:18px;height:18px;margin-top:1px;border:1.5px solid var(--edge);border-radius:5px;background:var(--surface-2);color:var(--on-accent);transition:background-color .15s,border-color .15s}
.dk-check__box .dk-ico{width:13px;height:13px;opacity:0}
.dk-check input:checked+.dk-check__box{background:var(--accent);border-color:var(--accent)}
.dk-check input:checked+.dk-check__box .dk-ico{opacity:1}
.dk-check--radio .dk-check__box{border-radius:50%}
.dk-check--radio .dk-check__box::after{content:"";width:8px;height:8px;border-radius:50%;background:var(--on-accent);transform:scale(0);transition:transform .15s}
.dk-check--radio input:checked+.dk-check__box::after{transform:scale(1)}
.dk-check input:focus-visible+.dk-check__box{outline:2px solid var(--focus);outline-offset:2px}
.dk-fieldset{min-width:0;margin:0;padding:0;border:0;display:grid;gap:10px}
.dk-fieldset legend{margin-bottom:10px;padding:0;font-size:.82rem;font-weight:600}
@media (prefers-reduced-motion:reduce){.dk-check__box,.dk-check--radio .dk-check__box::after{transition:none}}
/* Range: native input, accent fill drawn with a gradient set from --val */
.dk-range{width:100%;height:22px;margin:0;background:transparent;appearance:none;-webkit-appearance:none;cursor:pointer}
.dk-range::-webkit-slider-runnable-track{height:6px;border-radius:999px;border:1px solid var(--edge);
background:linear-gradient(90deg,var(--accent) 0 var(--val,50%),var(--surface-3) var(--val,50%))}
.dk-range::-moz-range-track{height:6px;border-radius:999px;border:1px solid var(--edge);background:var(--surface-3)}
.dk-range::-moz-range-progress{height:6px;border-radius:999px;background:var(--accent)}
.dk-range::-webkit-slider-thumb{-webkit-appearance:none;width:18px;height:18px;margin-top:-7px;border:2px solid var(--accent);border-radius:50%;background:var(--text-1)}
.dk-range::-moz-range-thumb{width:14px;height:14px;border:2px solid var(--accent);border-radius:50%;background:var(--text-1)}
.dk-range:focus-visible{outline:2px solid var(--focus);outline-offset:3px;border-radius:4px}
/* Login card */
.dk-auth{width:100%;max-width:400px;margin:0 auto;padding:28px}
.dk-auth__mark{display:grid;place-items:center;width:40px;height:40px;margin-bottom:18px;border:1px solid var(--border);border-radius:10px;background:var(--surface-2);color:var(--accent)}
.dk-auth h2{font-size:1.2rem;font-weight:650;letter-spacing:-.015em}
.dk-auth__sub{margin-top:4px;font-size:.88rem;color:var(--text-2)}
.dk-auth form{display:grid;gap:16px;margin-top:22px}
.dk-auth__row{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px}
.dk-auth__or{display:flex;align-items:center;gap:12px;font-size:.75rem;color:var(--text-3)}
.dk-auth__or::before,.dk-auth__or::after{content:"";flex:1;height:1px;background:var(--border)}
.dk-auth__foot{margin-top:20px;padding-top:18px;border-top:1px solid var(--border);font-size:.85rem;color:var(--text-2);text-align:center}
.dk-out{font:600 .82rem var(--mono);color:var(--text-1)}
.dk-scale{display:flex;justify-content:space-between;font-size:.72rem;color:var(--text-3)}
JavaScript
// Range fill: keep --val (the accent part of the track) and the printed value in step with the slider.
document.querySelectorAll('input[type=range][data-fill]').forEach(r => {
const out = document.getElementById(r.dataset.fill);
const sync = () => { const p = (r.value - r.min) / (r.max - r.min) * 100; r.style.setProperty('--val', p + '%'); if (out) out.textContent = r.value + ' days'; r.setAttribute('aria-valuetext', r.value + ' days'); };
r.addEventListener('input', sync); sync();
});
/* ── Dark UI kit tokens (uiCookies) — identical in every set ──────────────────────────────
Dark first. Elevation is a lighter surface, not a shadow: surface-0 is the page, 1 cards and
panels, 2 controls, menus and dialogs, 3 hover, selected rows and tooltips.
Every colour is a light-dark() pair, so flipping color-scheme turns the whole kit light:
data-theme="dark" | "light" | "system" on any wrapper (system follows prefers-color-scheme),
or a checked .theme-pick radio inside a [data-theme] element (:has()).
Contrast, dark | light, on the worst surface (surface-3): text-1 12.2 | 15.0, text-2 6.6 | 7.3,
text-3 4.8 | 5.4, accent 5.4 | 5.1, success 7.7 | 5.0, warning 8.4 | 5.2, danger 6.8 | 5.0;
edge (control boundaries) 3.3 | 3.1 and focus 7.9 | 5.9 against every surface. */
:root{color-scheme:dark}
[data-theme="dark"]{color-scheme:dark}
[data-theme="light"]{color-scheme:light}
[data-theme="system"]{color-scheme:light dark}
[data-theme]:has(.theme-pick[value="dark"]:checked){color-scheme:dark}
[data-theme]:has(.theme-pick[value="light"]:checked){color-scheme:light}
[data-theme]:has(.theme-pick[value="system"]:checked){color-scheme:light dark}
:root{
--surface-0:light-dark(#eef1f5,#0b0d12); /* page */
--surface-1:light-dark(#ffffff,#151821); /* cards, panels, navbar, sidebar */
--surface-2:light-dark(#f6f7f9,#1d212c); /* controls, menus, dialogs, toasts */
--surface-3:light-dark(#e6e9ef,#262b38); /* hover, selected rows, tooltips */
--text-1:light-dark(#12151c,#eceef3); /* primary text */
--text-2:light-dark(#434a5a,#aab1bf); /* secondary text */
--text-3:light-dark(#555d6e,#8f96a8); /* captions, placeholders, axis labels */
--border:light-dark(#dde1e8,#2a2f3c); /* decorative 1px dividers and card edges */
--edge:light-dark(#7c8496,#737b90); /* control boundaries: fields, boxes, tracks */
--accent:light-dark(#4452d0,#8b97ff); /* the one vivid colour: links, selected, primary */
--accent-soft:light-dark(#e6e8ff,#232a4d); /* selected backgrounds; accent text keeps 5.2 | 5.3 on it */
--on-accent:light-dark(#ffffff,#0b0d12); /* text on accent fills, 6.2 | 7.4 */
--success:light-dark(#11713f,#5fd4a0); /* desaturated for dark, so they do not vibrate */
--warning:light-dark(#875500,#f0c060);
--danger:light-dark(#bb2532,#ff9790);
--focus:light-dark(#3b48c4,#b5bdff); /* 2px focus ring */
--highlight:light-dark(rgb(255 255 255 / 0),rgb(255 255 255 / .045)); /* 1px top light on dark surfaces */
--shadow-color:light-dark(rgb(16 24 40 / .16),rgb(0 0 0 / .55)); /* overlays only */
--radius:12px;--radius-sm:8px;
--mono:ui-monospace,"SF Mono","Cascadia Code",Menlo,Consolas,monospace;
}
/* No light-dark() (Chrome < 123, Safari < 17.5, Firefox < 120): the same values, chosen by attribute */
@supports not (color:light-dark(#fff,#000)){
:root,[data-theme="dark"]{--surface-0:#0b0d12;--surface-1:#151821;--surface-2:#1d212c;--surface-3:#262b38;--text-1:#eceef3;--text-2:#aab1bf;--text-3:#8f96a8;--border:#2a2f3c;--edge:#737b90;--accent:#8b97ff;--accent-soft:#232a4d;--on-accent:#0b0d12;--success:#5fd4a0;--warning:#f0c060;--danger:#ff9790;--focus:#b5bdff;--highlight:rgb(255 255 255 / .045);--shadow-color:rgb(0 0 0 / .55)}
[data-theme="light"]{--surface-0:#eef1f5;--surface-1:#ffffff;--surface-2:#f6f7f9;--surface-3:#e6e9ef;--text-1:#12151c;--text-2:#434a5a;--text-3:#555d6e;--border:#dde1e8;--edge:#7c8496;--accent:#4452d0;--accent-soft:#e6e8ff;--on-accent:#ffffff;--success:#11713f;--warning:#875500;--danger:#bb2532;--focus:#3b48c4;--highlight:rgb(255 255 255 / 0);--shadow-color:rgb(16 24 40 / .16)}
@media (prefers-color-scheme:light){[data-theme="system"]{--surface-0:#eef1f5;--surface-1:#ffffff;--surface-2:#f6f7f9;--surface-3:#e6e9ef;--text-1:#12151c;--text-2:#434a5a;--text-3:#555d6e;--border:#dde1e8;--edge:#7c8496;--accent:#4452d0;--accent-soft:#e6e8ff;--on-accent:#ffffff;--success:#11713f;--warning:#875500;--danger:#bb2532;--focus:#3b48c4;--highlight:rgb(255 255 255 / 0);--shadow-color:rgb(16 24 40 / .16)}}
}
@media (prefers-contrast:more){:root{--border:var(--edge);--text-3:var(--text-2)}}
[data-theme]{background:var(--surface-0);color:var(--text-1)}
/* Bootstrap's own variables mapped onto the kit tokens (identical in every Bootstrap set).
data-bs-theme="dark" on <html> gives Bootstrap's dark defaults; the tokens then win. */
:root,[data-bs-theme],[data-theme]{
--bs-body-bg:var(--surface-0);--bs-body-color:var(--text-1);--bs-emphasis-color:var(--text-1);
--bs-heading-color:var(--text-1);--bs-secondary-color:var(--text-2);--bs-tertiary-color:var(--text-3);
--bs-secondary-bg:var(--surface-2);--bs-tertiary-bg:var(--surface-3);
--bs-border-color:var(--border);--bs-border-color-translucent:var(--border);
--bs-border-radius:var(--radius-sm);--bs-border-radius-lg:var(--radius);--bs-border-radius-sm:6px;
--bs-link-color:var(--accent);--bs-link-hover-color:var(--accent);
--bs-focus-ring-color:var(--focus);--bs-code-color:var(--accent);
--bs-font-monospace:var(--mono);
--bs-success-text-emphasis:var(--success);--bs-warning-text-emphasis:var(--warning);--bs-danger-text-emphasis:var(--danger)}
/* Bootstrap draws the checkbox tick and select chevron as SVG images, which cannot read CSS
variables, so each theme gets its own copy */
:root,[data-theme="dark"]{--dk-check-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%230b0d12' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");--dk-select-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23aab1bf' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}
[data-theme="light"]{--dk-check-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");--dk-select-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23434a5a' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}
@media (prefers-color-scheme:light){[data-theme="system"]{--dk-check-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");--dk-select-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23434a5a' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}}
[data-theme]:has(.theme-pick[value="dark"]:checked){--dk-check-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%230b0d12' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");--dk-select-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23aab1bf' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}
[data-theme]:has(.theme-pick[value="light"]:checked){--dk-check-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");--dk-select-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23434a5a' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}
@media (prefers-color-scheme:light){[data-theme]:has(.theme-pick[value="system"]:checked){--dk-check-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23ffffff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");--dk-select-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23434a5a' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}}
@media (prefers-color-scheme:dark){[data-theme]:has(.theme-pick[value="system"]:checked){--dk-check-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%230b0d12' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");--dk-select-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23aab1bf' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}}
a{color:var(--accent)}
a:hover{color:var(--accent)}
/* Components */
.dk-ico{width:18px;height:18px;flex:none}
.dk-kbd{display:inline-grid;place-items:center;min-width:1.45rem;height:1.35rem;padding:0 .35rem;border:1px solid var(--border);border-bottom-width:2px;
border-radius:5px;background:var(--surface-2);color:var(--text-2);font:500 .72rem/1 var(--mono)}
.dk-link{color:var(--accent);font-weight:600;text-decoration-color:color-mix(in srgb,var(--accent) 45%,transparent);text-underline-offset:3px}
.dk-link:hover{color:var(--accent);text-decoration-color:currentColor}
.dk-link:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:3px;box-shadow:none}
/* .card through --bs-card-* */
.card.dk-card{--bs-card-bg:var(--surface-1);--bs-card-color:var(--text-1);--bs-card-border-color:var(--border);--bs-card-border-radius:var(--radius);
--bs-card-inner-border-radius:calc(var(--radius) - 1px);--bs-card-spacer-x:20px;--bs-card-spacer-y:20px;--bs-card-cap-bg:transparent;--bs-card-title-color:var(--text-1);
--bs-card-subtitle-color:var(--text-2);box-shadow:inset 0 1px 0 var(--highlight)}
.dk-card h2,.dk-card h3,.dk-card p{margin:0}
.dk-card .card-title{font-size:1rem;font-weight:650;letter-spacing:-.01em;margin:0}
.dk-card .card-subtitle{margin-top:2px;font-size:.85rem}
/* .btn variants through --bs-btn-* */
.btn-dk{--bs-btn-padding-x:1rem;--bs-btn-padding-y:.5rem;--bs-btn-font-size:.875rem;--bs-btn-font-weight:600;--bs-btn-line-height:1.2;--bs-btn-border-radius:var(--radius-sm);
--bs-btn-bg:var(--surface-2);--bs-btn-color:var(--text-1);--bs-btn-border-color:var(--border);
--bs-btn-hover-bg:var(--surface-3);--bs-btn-hover-color:var(--text-1);--bs-btn-hover-border-color:var(--border);
--bs-btn-active-bg:var(--surface-3);--bs-btn-active-color:var(--text-1);--bs-btn-active-border-color:var(--border);
--bs-btn-disabled-bg:var(--surface-2);--bs-btn-disabled-color:var(--text-1);--bs-btn-disabled-border-color:var(--border);--bs-btn-disabled-opacity:.5;
--bs-btn-focus-box-shadow:inset 0 1px 0 var(--highlight);
display:inline-flex;align-items:center;justify-content:center;gap:.5rem;min-height:38px;white-space:nowrap;box-shadow:inset 0 1px 0 var(--highlight)}
.btn-dk:focus-visible,.btn-check:focus-visible+.btn-dk{outline:2px solid var(--focus);outline-offset:2px}
.btn-dk-primary{--bs-btn-bg:var(--accent);--bs-btn-color:var(--on-accent);--bs-btn-border-color:var(--accent);
--bs-btn-hover-bg:color-mix(in srgb,var(--accent) 86%,var(--text-1));--bs-btn-hover-color:var(--on-accent);--bs-btn-hover-border-color:var(--accent);
--bs-btn-active-bg:color-mix(in srgb,var(--accent) 86%,var(--text-1));--bs-btn-active-color:var(--on-accent);--bs-btn-active-border-color:var(--accent);
--bs-btn-disabled-bg:var(--accent);--bs-btn-disabled-color:var(--on-accent);--bs-btn-disabled-border-color:var(--accent)}
.btn-dk-ghost{--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-color:var(--text-2);--bs-btn-hover-border-color:transparent;--bs-btn-active-border-color:transparent;box-shadow:none}
.btn-dk-danger{--bs-btn-color:var(--danger);--bs-btn-border-color:color-mix(in srgb,var(--danger) 40%,var(--border));--bs-btn-hover-color:var(--danger);
--bs-btn-hover-border-color:color-mix(in srgb,var(--danger) 40%,var(--border));--bs-btn-active-color:var(--danger)}
.btn-dk.btn-sm{--bs-btn-padding-x:.75rem;--bs-btn-padding-y:.35rem;--bs-btn-font-size:.8rem;min-height:32px}
.btn-dk.btn-lg{--bs-btn-padding-x:1.35rem;--bs-btn-padding-y:.7rem;--bs-btn-font-size:.95rem;--bs-btn-border-radius:var(--radius-sm);min-height:46px}
.btn-dk-icon{--bs-btn-padding-x:0;--bs-btn-padding-y:0;width:38px}
.btn-dk-icon.btn-sm{width:32px}.btn-dk-icon.btn-lg{width:46px}
.btn-dk.btn-dk-round{--bs-btn-border-radius:999px}
@media (prefers-reduced-motion:reduce){.btn-dk{transition:none}}
/* .form-control and .form-select through --bs-* and a few rules */
.dk-form{--bs-body-bg:var(--surface-2);--bs-border-color:var(--edge)}
.dk-form .form-label{margin-bottom:6px;font-size:.82rem;font-weight:600;color:var(--text-1)}
.dk-form .form-text{margin-top:6px;font-size:.78rem;color:var(--text-3)}
.dk-form .invalid-feedback{display:flex;align-items:center;gap:.35rem;margin-top:6px;font-size:.78rem;color:var(--danger)}
.dk-form .invalid-feedback .dk-ico{width:15px;height:15px}
.dk-form .form-control,.dk-form .form-select{min-height:40px;padding:.55rem .8rem;border-color:var(--edge);background-color:var(--surface-2);color:var(--text-1);font-size:.9rem;line-height:1.4}
.dk-form .form-select{--bs-form-select-bg-img:var(--dk-select-img);padding-right:2.4rem}
.dk-form .form-control::placeholder{color:var(--text-3);opacity:1}
.dk-form .form-control:hover,.dk-form .form-select:hover{border-color:var(--text-3)}
.dk-form .form-control:focus,.dk-form .form-select:focus{border-color:var(--accent);background-color:var(--surface-2);color:var(--text-1);box-shadow:none;outline:2px solid var(--focus);outline-offset:1px}
.dk-form .form-control.is-invalid{border-color:var(--danger);background-image:none;padding-right:.8rem}
.dk-form textarea.form-control{min-height:92px}
.dk-form .input-group-text{border-color:var(--edge);background:var(--surface-2);color:var(--text-3)}
.dk-form .dk-iconfield{position:relative}
.dk-form .dk-iconfield>.dk-ico{position:absolute;left:.75rem;top:50%;margin-top:-9px;color:var(--text-3);pointer-events:none;z-index:5}
.dk-form .dk-iconfield>.form-control{padding-left:2.35rem}
.dk-form .dk-iconfield>.dk-kbd{position:absolute;right:.6rem;top:50%;margin-top:-.7rem}
.dk-form .dk-iconfield>.dk-pad-kbd{padding-right:3rem}
@media (prefers-reduced-motion:reduce){.dk-form .form-control,.dk-form .form-select{transition:none}}
/* .form-check-input: drawn box, the tick image comes from the theme (see --dk-check-img) */
.dk-check{display:flex;align-items:flex-start;gap:.65rem;min-height:0;margin:0;padding:0;font-size:.88rem;line-height:1.4}
.dk-check .form-check-label{cursor:pointer}
.dk-check .form-check-label small{display:block;font-size:.78rem;color:var(--text-2)}
.dk-check .form-check-input{flex:none;float:none;width:18px;height:18px;margin:1px 0 0;border:1.5px solid var(--edge);border-radius:5px;background-color:var(--surface-2);cursor:pointer}
.dk-check .form-check-input[type=radio]{border-radius:50%}
.dk-check .form-check-input:focus{border-color:var(--edge);box-shadow:none}
.dk-check .form-check-input:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.dk-check .form-check-input:checked{background-color:var(--accent);border-color:var(--accent)}
.dk-check .form-check-input:checked[type=checkbox]{--bs-form-check-bg-image:var(--dk-check-img)}
.dk-check .form-check-input:checked[type=radio]{background-image:radial-gradient(circle,var(--on-accent) 0 3.5px,transparent 4px)}
.dk-fieldset{min-width:0;margin:0;padding:0;border:0;display:grid;gap:10px}
.dk-fieldset legend{float:none;width:auto;margin-bottom:10px;padding:0;font-size:.82rem;font-weight:600}
/* .form-range recoloured */
.dk-range.form-range{height:22px}
.dk-range.form-range::-webkit-slider-runnable-track{height:6px;border:1px solid var(--edge);border-radius:999px;background:linear-gradient(90deg,var(--accent) 0 var(--val,50%),var(--surface-3) var(--val,50%))}
.dk-range.form-range::-moz-range-track{height:6px;border:1px solid var(--edge);border-radius:999px;background:var(--surface-3)}
.dk-range.form-range::-moz-range-progress{height:6px;border-radius:999px;background:var(--accent)}
.dk-range.form-range::-webkit-slider-thumb{width:18px;height:18px;margin-top:-7px;border:2px solid var(--accent);border-radius:50%;background:var(--text-1);box-shadow:none}
.dk-range.form-range::-moz-range-thumb{width:14px;height:14px;border:2px solid var(--accent);border-radius:50%;background:var(--text-1)}
.dk-range.form-range:focus{box-shadow:none}
.dk-range.form-range:focus::-webkit-slider-thumb{box-shadow:none}
.dk-range.form-range:focus-visible{outline:2px solid var(--focus);outline-offset:3px;border-radius:4px}
@media (prefers-reduced-motion:reduce){.dk-range.form-range::-webkit-slider-thumb{transition:none}}
/* Login card */
.dk-auth{width:100%;max-width:400px;margin:0 auto;--bs-card-spacer-x:28px;--bs-card-spacer-y:28px}
.dk-auth__mark{display:grid;place-items:center;width:40px;height:40px;margin-bottom:18px;border:1px solid var(--border);border-radius:10px;background:var(--surface-2);color:var(--accent)}
.dk-auth h2{font-size:1.2rem;font-weight:650;letter-spacing:-.015em}
.dk-auth__sub{margin-top:4px;font-size:.88rem;color:var(--text-2)}
.dk-auth form{display:grid;gap:16px;margin-top:22px}
.dk-auth__row{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px}
.dk-auth__or{display:flex;align-items:center;gap:12px;font-size:.75rem;color:var(--text-3)}
.dk-auth__or::before,.dk-auth__or::after{content:"";flex:1;height:1px;background:var(--border)}
.dk-auth__foot{margin-top:20px;padding-top:18px;border-top:1px solid var(--border);font-size:.85rem;color:var(--text-2);text-align:center}
.dk-out{font:600 .82rem var(--mono);color:var(--text-1)}
.dk-scale{display:flex;justify-content:space-between;font-size:.72rem;color:var(--text-3)}
JavaScript
// Range fill: keep --val (the accent part of the track) and the printed value in step with the slider.
document.querySelectorAll('input[type=range][data-fill]').forEach(r => {
const out = document.getElementById(r.dataset.fill);
const sync = () => { const p = (r.value - r.min) / (r.max - r.min) * 100; r.style.setProperty('--val', p + '%'); if (out) out.textContent = r.value + ' days'; r.setAttribute('aria-valuetext', r.value + ' days'); };
r.addEventListener('input', sync); sync();
});
/* ── Dark UI kit tokens (uiCookies) — identical in every set ──────────────────────────────
Dark first. Elevation is a lighter surface, not a shadow: surface-0 is the page, 1 cards and
panels, 2 controls, menus and dialogs, 3 hover, selected rows and tooltips.
Every colour is a light-dark() pair, so flipping color-scheme turns the whole kit light:
data-theme="dark" | "light" | "system" on any wrapper (system follows prefers-color-scheme),
or a checked .theme-pick radio inside a [data-theme] element (:has()).
Contrast, dark | light, on the worst surface (surface-3): text-1 12.2 | 15.0, text-2 6.6 | 7.3,
text-3 4.8 | 5.4, accent 5.4 | 5.1, success 7.7 | 5.0, warning 8.4 | 5.2, danger 6.8 | 5.0;
edge (control boundaries) 3.3 | 3.1 and focus 7.9 | 5.9 against every surface. */
:root{color-scheme:dark}
[data-theme="dark"]{color-scheme:dark}
[data-theme="light"]{color-scheme:light}
[data-theme="system"]{color-scheme:light dark}
[data-theme]:has(.theme-pick[value="dark"]:checked){color-scheme:dark}
[data-theme]:has(.theme-pick[value="light"]:checked){color-scheme:light}
[data-theme]:has(.theme-pick[value="system"]:checked){color-scheme:light dark}
:root{
--surface-0:light-dark(#eef1f5,#0b0d12); /* page */
--surface-1:light-dark(#ffffff,#151821); /* cards, panels, navbar, sidebar */
--surface-2:light-dark(#f6f7f9,#1d212c); /* controls, menus, dialogs, toasts */
--surface-3:light-dark(#e6e9ef,#262b38); /* hover, selected rows, tooltips */
--text-1:light-dark(#12151c,#eceef3); /* primary text */
--text-2:light-dark(#434a5a,#aab1bf); /* secondary text */
--text-3:light-dark(#555d6e,#8f96a8); /* captions, placeholders, axis labels */
--border:light-dark(#dde1e8,#2a2f3c); /* decorative 1px dividers and card edges */
--edge:light-dark(#7c8496,#737b90); /* control boundaries: fields, boxes, tracks */
--accent:light-dark(#4452d0,#8b97ff); /* the one vivid colour: links, selected, primary */
--accent-soft:light-dark(#e6e8ff,#232a4d); /* selected backgrounds; accent text keeps 5.2 | 5.3 on it */
--on-accent:light-dark(#ffffff,#0b0d12); /* text on accent fills, 6.2 | 7.4 */
--success:light-dark(#11713f,#5fd4a0); /* desaturated for dark, so they do not vibrate */
--warning:light-dark(#875500,#f0c060);
--danger:light-dark(#bb2532,#ff9790);
--focus:light-dark(#3b48c4,#b5bdff); /* 2px focus ring */
--highlight:light-dark(rgb(255 255 255 / 0),rgb(255 255 255 / .045)); /* 1px top light on dark surfaces */
--shadow-color:light-dark(rgb(16 24 40 / .16),rgb(0 0 0 / .55)); /* overlays only */
--radius:12px;--radius-sm:8px;
--mono:ui-monospace,"SF Mono","Cascadia Code",Menlo,Consolas,monospace;
}
/* No light-dark() (Chrome < 123, Safari < 17.5, Firefox < 120): the same values, chosen by attribute */
@supports not (color:light-dark(#fff,#000)){
:root,[data-theme="dark"]{--surface-0:#0b0d12;--surface-1:#151821;--surface-2:#1d212c;--surface-3:#262b38;--text-1:#eceef3;--text-2:#aab1bf;--text-3:#8f96a8;--border:#2a2f3c;--edge:#737b90;--accent:#8b97ff;--accent-soft:#232a4d;--on-accent:#0b0d12;--success:#5fd4a0;--warning:#f0c060;--danger:#ff9790;--focus:#b5bdff;--highlight:rgb(255 255 255 / .045);--shadow-color:rgb(0 0 0 / .55)}
[data-theme="light"]{--surface-0:#eef1f5;--surface-1:#ffffff;--surface-2:#f6f7f9;--surface-3:#e6e9ef;--text-1:#12151c;--text-2:#434a5a;--text-3:#555d6e;--border:#dde1e8;--edge:#7c8496;--accent:#4452d0;--accent-soft:#e6e8ff;--on-accent:#ffffff;--success:#11713f;--warning:#875500;--danger:#bb2532;--focus:#3b48c4;--highlight:rgb(255 255 255 / 0);--shadow-color:rgb(16 24 40 / .16)}
@media (prefers-color-scheme:light){[data-theme="system"]{--surface-0:#eef1f5;--surface-1:#ffffff;--surface-2:#f6f7f9;--surface-3:#e6e9ef;--text-1:#12151c;--text-2:#434a5a;--text-3:#555d6e;--border:#dde1e8;--edge:#7c8496;--accent:#4452d0;--accent-soft:#e6e8ff;--on-accent:#ffffff;--success:#11713f;--warning:#875500;--danger:#bb2532;--focus:#3b48c4;--highlight:rgb(255 255 255 / 0);--shadow-color:rgb(16 24 40 / .16)}}
}
@media (prefers-contrast:more){:root{--border:var(--edge);--text-3:var(--text-2)}}
[data-theme]{background:var(--surface-0);color:var(--text-1)}
/* The tokens above as Tailwind utilities: bg-surface-0…3, text-ink-1…3, border-line, border-edge,
bg-accent, bg-accent-soft, text-on-accent, text-success|warning|danger, outline-focus, rounded-ui… */
@theme inline {
--color-surface-0: var(--surface-0); --color-surface-1: var(--surface-1);
--color-surface-2: var(--surface-2); --color-surface-3: var(--surface-3);
--color-ink-1: var(--text-1); --color-ink-2: var(--text-2); --color-ink-3: var(--text-3);
--color-line: var(--border); --color-edge: var(--edge);
--color-accent: var(--accent); --color-accent-soft: var(--accent-soft); --color-on-accent: var(--on-accent);
--color-success: var(--success); --color-warning: var(--warning); --color-danger: var(--danger);
--color-focus: var(--focus); --color-highlight: var(--highlight);
--radius-ui: var(--radius); --radius-ui-sm: var(--radius-sm);
--font-mono: var(--mono);
--shadow-overlay: 0 18px 48px -12px var(--shadow-color);
--inset-shadow-hl: inset 0 1px 0 var(--highlight);
}
/* Range track and thumb: pseudo-elements Tailwind cannot reach from markup */
@utility range-dk {
height: 22px; margin: 0; background: transparent; appearance: none; cursor: pointer;
&::-webkit-slider-runnable-track { height: 6px; border-radius: 999px; border: 1px solid var(--edge); background: linear-gradient(90deg, var(--accent) 0 var(--val, 50%), var(--surface-3) var(--val, 50%)); }
&::-moz-range-track { height: 6px; border-radius: 999px; border: 1px solid var(--edge); background: var(--surface-3); }
&::-moz-range-progress { height: 6px; border-radius: 999px; background: var(--accent); }
&::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; margin-top: -7px; border: 2px solid var(--accent); border-radius: 50%; background: var(--text-1); }
&::-moz-range-thumb { width: 14px; height: 14px; border: 2px solid var(--accent); border-radius: 50%; background: var(--text-1); }
}
JavaScript
// Range fill: keep --val (the accent part of the track) and the printed value in step with the slider.
document.querySelectorAll('input[type=range][data-fill]').forEach(r => {
const out = document.getElementById(r.dataset.fill);
const sync = () => { const p = (r.value - r.min) / (r.max - r.min) * 100; r.style.setProperty('--val', p + '%'); if (out) out.textContent = r.value + ' days'; r.setAttribute('aria-valuetext', r.value + ' days'); };
r.addEventListener('input', sync); sync();
});
About this set
Form controls for the uiCookies dark UI kit and a complete login card. The text fields card has a filled project name with hint text, a billing email in its error state with the message tied to the field by aria-describedby, a search field with a leading icon and a keyboard shortcut hint, a native select with a drawn chevron and a textarea. The choices card has a checkbox group and a radio group with descriptions, each in a fieldset with a legend, and a log retention slider whose accent fill and printed value follow the thumb. The login card puts it together: email and password with autocomplete hints, a forgot password link, a keep me signed in checkbox, a large primary sign in button, a passkey button, a single sign-on button and a link to create an account. Fields sit on surface-2 with an –edge border that keeps 3:1 against every surface, so they read as inputs in both themes, and focus adds the accent border plus a 2px ring outside the field. The checkbox tick is an inline SVG coloured by –on-accent, so it flips with the theme; in Bootstrap the tick and chevron images are swapped per theme for the same reason.
What’s included
Text, email, search, select and textarea fields with real labels
Error state with a linked message
Checkbox and radio groups in fieldsets
Range slider with an accent fill and live value
Login card with passkey and single sign-on options
Accessibility
Every control has a visible label, hints and errors are linked with aria-describedby, the invalid field has aria-invalid, and grouped options sit in a fieldset with a legend. Field borders, checkbox and radio outlines and the slider track reach 3:1 and text 4.5:1 on every surface. Focus draws a 2px ring outside the control, the slider announces its value in days through aria-valuetext, and transitions stop under prefers-reduced-motion.
Customise it
Change --surface-2 for the field fill, --edge for borders and --accent for checked and focused states. In Bootstrap the .dk-form wrapper maps them onto .form-control, .form-select and .form-check-input; in Tailwind edit bg-surface-2, border-edge and peer-checked:bg-accent.