/* ===== uiCookies Glassmorphism kit: design tokens (identical in every set) =====
Retheme the whole kit here. Light scene on :root, dark scene on [data-scene="dark"]
(or any [data-scene] holding a checked [data-scene-switch]). Selectors use :where()
so the fallbacks below always win. */
:where(:root,[data-scene="light"]){
color-scheme:light;
/* background scene: the colour the glass frosts */
--scene-base:linear-gradient(155deg,#eceffe 0%,#fbeef7 52%,#e6f6fb 100%);
--scene-1:linear-gradient(140deg,#7c3aed,#c026d3);
--scene-2:linear-gradient(140deg,#fb7185,#f97316);
--scene-3:linear-gradient(140deg,#22d3ee,#3b82f6);
--scene-4:linear-gradient(140deg,#fde047,#fb923c);
/* glass */
--glass-tint:255 255 255;
--glass-border:rgb(255 255 255 / .8);
--glass-edge:rgb(36 32 88 / .9);
--glass-highlight:inset 0 1px 0 rgb(255 255 255 / .85);
--glass-shadow:0 18px 40px -18px rgb(37 30 102 / .45);
--glass-ink:#16133a;
--glass-ink-soft:#3b3862;
--glass-accent:#4338ca;
--glass-accent-bg:rgb(67 56 202 / .92);
--glass-on-accent:#fff;
--glass-check-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='%23fff' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
--glass-focus:#1e1b4b;
--glass-good:#14532d;--glass-good-bg:rgb(220 252 231 / .9);
--glass-bad:#991b1b;--glass-bad-bg:rgb(254 226 226 / .9);
--glass-warn:#7c2d12;--glass-warn-bg:rgb(254 243 199 / .9);
--glass-scrim:rgb(22 19 58 / .3);
}
:where([data-scene="dark"]),:where([data-scene]:has([data-scene-switch]:checked)){
color-scheme:dark;
--scene-base:linear-gradient(155deg,#0a0c22 0%,#170c2c 52%,#06171f 100%);
--scene-1:linear-gradient(140deg,#6d28d9,#a21caf);
--scene-2:linear-gradient(140deg,#e11d48,#ea580c);
--scene-3:linear-gradient(140deg,#0891b2,#2563eb);
--scene-4:linear-gradient(140deg,#ca8a04,#c2410c);
--glass-tint:16 18 44;
--glass-border:rgb(255 255 255 / .2);
--glass-edge:rgb(214 220 255 / .72);
--glass-highlight:inset 0 1px 0 rgb(255 255 255 / .16);
--glass-shadow:0 18px 40px -18px rgb(0 0 0 / .75);
--glass-ink:#f5f6ff;
--glass-ink-soft:#d0d3ee;
--glass-accent:#a5b4fc;
--glass-accent-bg:rgb(165 180 252 / .96);
--glass-on-accent:#14123a;
--glass-check-icon:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='%2314123a' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
--glass-focus:#c7d2fe;
--glass-good:#bbf7d0;--glass-good-bg:rgb(20 83 45 / .7);
--glass-bad:#fecaca;--glass-bad-bg:rgb(127 29 29 / .7);
--glass-warn:#fde68a;--glass-warn-bg:rgb(120 53 15 / .7);
--glass-scrim:rgb(0 0 0 / .5);
}
:where(:root){
--glass-alpha:.6; /* panels and cards */
--glass-alpha-strong:.82; /* menus, dialogs, toasts: they cover unknown content */
--glass-alpha-control:.5; /* buttons and fields sitting on a panel */
--glass-blur:18px;
--glass-saturate:150%;
--glass-radius:20px;
}
/* derived: recomputed inside every scene so a dark scene gets dark glass */
:where(:root,[data-scene]){
--glass-bg:rgb(var(--glass-tint) / var(--glass-alpha));
--glass-bg-strong:rgb(var(--glass-tint) / var(--glass-alpha-strong));
--glass-bg-control:rgb(var(--glass-tint) / var(--glass-alpha-control));
}
/* no backdrop-filter: frosting can't happen, so the glass turns nearly solid */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
:where(:root){--glass-alpha:.94;--glass-alpha-strong:.97;--glass-alpha-control:.9}
}
@media (prefers-reduced-transparency:reduce){
:where(:root){--glass-alpha:.93;--glass-alpha-strong:.97;--glass-alpha-control:.9;--glass-blur:8px}
}
@media (prefers-contrast:more){
:where(:root){--glass-alpha:.96;--glass-alpha-strong:.98;--glass-alpha-control:.96}
:where(:root,[data-scene]){--glass-border:var(--glass-ink);--glass-edge:var(--glass-ink)}
}
/* ===== end of tokens ===== */
/* Tailwind theme: utilities such as bg-glass, border-glass-edge, backdrop-blur-glass and text-ink read the tokens above */
@theme inline {
--color-glass: var(--glass-bg);
--color-glass-strong: var(--glass-bg-strong);
--color-glass-control: var(--glass-bg-control);
--color-glass-border: var(--glass-border);
--color-glass-edge: var(--glass-edge);
--color-ink: var(--glass-ink);
--color-ink-soft: var(--glass-ink-soft);
--color-accent: var(--glass-accent);
--color-accent-glass: var(--glass-accent-bg);
--color-on-accent: var(--glass-on-accent);
--color-focus: var(--glass-focus);
--color-good: var(--glass-good);
--color-good-glass: var(--glass-good-bg);
--color-bad: var(--glass-bad);
--color-bad-glass: var(--glass-bad-bg);
--color-warn: var(--glass-warn);
--color-warn-glass: var(--glass-warn-bg);
--color-scrim: var(--glass-scrim);
--blur-glass: var(--glass-blur);
--radius-glass: var(--glass-radius);
--shadow-glass: var(--glass-shadow);
--inset-shadow-glass: var(--glass-highlight);
--animate-drift: g-drift 32s ease-in-out infinite alternate;
}
@keyframes g-drift { to { transform: translate3d(3vw,4vh,0) scale(1.06); } }
@theme { --animate-pop: g-pop .22s ease-out; }
@keyframes g-pop { from { opacity: 0; transform: translateY(10px) scale(.98); } }
JavaScript
// Dialog: open as a real modal (focus trap, Escape, ::backdrop). Toasts: dismiss. Tooltips and menus: Escape closes.
document.querySelectorAll('[data-dialog-open]').forEach(btn => btn.addEventListener('click', () => {
const d = document.getElementById(btn.dataset.dialogOpen); if (!d) return;
if (d.open) d.close(); d.showModal();
}));
document.querySelectorAll('[data-toast-dismiss]').forEach(btn => btn.addEventListener('click', () => btn.closest('[data-toast]')?.remove()));
document.addEventListener('keydown', e => {
if (e.key !== 'Escape') return;
document.querySelectorAll('[data-tip]').forEach(w => { w.dataset.dismissed = ''; });
document.querySelectorAll('details[data-menu][open]').forEach(d => { d.open = false; d.querySelector('summary').focus(); });
});
document.querySelectorAll('[data-tip]').forEach(w => ['mouseleave', 'focusout'].forEach(ev => w.addEventListener(ev, () => delete w.dataset.dismissed)));
document.addEventListener('click', e => document.querySelectorAll('details[data-menu][open]').forEach(d => { if (!d.contains(e.target)) d.open = false; }));
About this set
Overlays and feedback for the uiCookies glassmorphism UI kit: a modal dialog, three toasts, an inline alert, tooltips and a dropdown menu. The dialog asks to archive a project, with an icon, a heading, a description and Cancel and Archive buttons; it is shown in place on the demo page and opens as a true modal from the button above it. The toasts confirm a sent invoice, report a failed upload and announce a new comment, each with a dismiss button. The alert warns that a trial is ending and links to billing. Three icon buttons carry tooltips, and a Project actions menu offers Rename, Duplicate, Move to team and Delete. Overlays use the stronger –glass-bg-strong tint because they cover content you do not control. The dialog is a native dialog element opened with showModal(), so focus is trapped and Escape closes it, and its backdrop blurs the page. The menu is a details element, so it opens without JavaScript. About a dozen lines of vanilla JavaScript open the dialog, dismiss toasts and close tooltips and the menu with Escape. The Bootstrap version feeds the tokens into Bootstrap’s own component variables, and the Tailwind version maps them to theme utilities such as bg-glass, border-glass-edge, backdrop-blur-glass and text-ink.
What’s included
Native dialog with a blurred backdrop, opened with showModal()
Success, error and info toasts with dismiss buttons
Inline warning alert with a link
Tooltips that open on hover and keyboard focus and close with Escape
Dropdown menu built on details and summary
Accessibility
The dialog is labelled by its heading and described by its text, traps focus as a modal and closes with Escape. Toasts sit in a role="status" region and their dismiss buttons name the toast. Tooltips use role="tooltip" with aria-describedby, open on focus as well as hover, stay open while hovered and close with Escape. All text was measured at 4.5:1, focus shows a 3px outline, and the pop-in animation stops under prefers-reduced-motion.
Customise it
Change --glass-alpha-strong for how opaque overlays are, --glass-scrim for the dialog backdrop and the --glass-good, --glass-bad and --glass-warn pairs for status colours. In Bootstrap they feed --bs-modal-*, --bs-toast-*, --bs-alert-*, --bs-tooltip-* and --bs-dropdown-* variables; in Tailwind edit bg-glass-strong and backdrop:bg-scrim.