Skip to content

Instantly share code, notes, and snippets.

@aldoyh
Last active June 7, 2026 15:32
Show Gist options
  • Select an option

  • Save aldoyh/f241b8a2ec2f005da55906b65f1ffe1e to your computer and use it in GitHub Desktop.

Select an option

Save aldoyh/f241b8a2ec2f005da55906b65f1ffe1e to your computer and use it in GitHub Desktop.
Pure CSS Glassmorphism Liquid Glass UI kit
<div class="scene" aria-hidden="true">
<div class="scene__blob scene__blob--1"></div>
<div class="scene__blob scene__blob--2"></div>
<div class="scene__blob scene__blob--3"></div>
</div>
<!-- Theme toggle -->
<button class="glass glass-btn theme-toggle-btn" id="theme-toggle" aria-label="Toggle colour scheme" title="Toggle light / dark mode">
<span class="icon-dark" aria-hidden="true">☀️</span>
<span class="icon-light" aria-hidden="true">🌙</span>
</button>
<!-- Toast region (ARIA live) -->
<div class="glass-toast-region" id="toast-region" role="region" aria-label="Notifications" aria-live="polite"></div>
<main class="page">
<!-- ─── HERO ─────────────────────────────────────────────────────────── -->
<header class="hero">
<span class="hero__kicker">Pure CSS · Glassmorphism · Liquid Glass</span>
<h1 class="hero__title">Liquid Glass<br>UI Kit</h1>
<p class="hero__sub">
Complete design token system, 15+ accessible components, light/dark mode, 12 animations — zero dependencies.
</p>
<div class="hero__cta">
<a href="#components" class="glass glass-btn glass-btn--primary">Explore Components →</a>
<a href="#animations" class="glass glass-btn glass-btn--ghost">View Animations</a>
</div>
<div class="container stats">
<div class="glass stats__item">
<div class="stats__num">15+</div>
<div class="stats__desc">Components</div>
</div>
<div class="glass stats__item">
<div class="stats__num">12</div>
<div class="stats__desc">Animations</div>
</div>
<div class="glass stats__item">
<div class="stats__num">a11y</div>
<div class="stats__desc">WCAG Compliant</div>
</div>
</div>
</header>
<div class="container page" id="components">
<!-- ─── CARDS ──────────────────────────────────────────────────────── -->
<section class="section">
<div class="section__header">
<h2 class="section__title">Glass Cards</h2>
<span class="section__subtitle">Hover to interact</span>
</div>
<div class="card-grid">
<div class="glass glass-card">
<div class="glass-card__label">Component · 01</div>
<h3 class="glass-card__title">Frosted Surface</h3>
<p class="glass-card__body">Backdrop-filter blur creates depth without obscuring the vibrant background.</p>
<div style="margin-top:20px"><span class="glass-badge glass-badge--aqua"><span class="glass-badge__dot"></span> Live</span></div>
</div>
<div class="glass glass--dark glass-card">
<div class="glass-card__label">Component · 02</div>
<h3 class="glass-card__title">Dark Glass</h3>
<p class="glass-card__body">Darker tint for contrast-rich surfaces. Ideal over bright backgrounds.</p>
<div style="margin-top:20px"><span class="glass-badge glass-badge--violet"><span class="glass-badge__dot"></span> Premium</span></div>
</div>
<div class="glass glass-card profile-card">
<div class="profile-card__avatar">🌊</div>
<div class="profile-card__name">Lyra Voss</div>
<div class="profile-card__role">UI Designer · Berlin</div>
<div class="progress-wrap" style="width:100%">
<div class="progress-label"><span>Projects</span><span>87%</span></div>
<div class="progress-bar">
<div class="progress-bar__fill" style="width:87%"></div>
</div>
</div>
<div class="progress-wrap" style="width:100%">
<div class="progress-label"><span>Clarity</span><span>94%</span></div>
<div class="progress-bar">
<div class="progress-bar__fill" style="width:94%"></div>
</div>
</div>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ─── BUTTONS ────────────────────────────────────────────────────── -->
<section class="section">
<div class="section__header">
<h2 class="section__title">Buttons</h2>
<span class="section__subtitle">Glass · Ghost · Accent · Danger · Sizes</span>
</div>
<div style="display:flex;flex-direction:column;gap:16px">
<div class="btn-row">
<button class="glass glass-btn glass-btn--primary">Primary</button>
<button class="glass glass-btn glass-btn--ghost">Ghost</button>
<button class="glass glass-btn glass-btn--accent">Accent</button>
<button class="glass glass-btn glass-btn--danger">Danger</button>
<button class="glass glass-btn glass-btn--ghost" disabled>Disabled</button>
</div>
<div class="btn-row">
<button class="glass glass-btn glass-btn--primary glass-btn--sm">Small</button>
<button class="glass glass-btn glass-btn--primary">Default</button>
<button class="glass glass-btn glass-btn--primary glass-btn--lg">Large</button>
</div>
<div class="btn-row">
<div class="glass-tooltip-wrap">
<button class="glass glass-btn glass-btn--icon glass-btn--primary" aria-label="Favourite"></button>
<div class="glass glass-tooltip" role="tooltip">Favourite</div>
</div>
<div class="glass-tooltip-wrap">
<button class="glass glass-btn glass-btn--icon glass-btn--ghost" aria-label="Refresh"></button>
<div class="glass glass-tooltip" role="tooltip">Refresh</div>
</div>
<div class="glass-tooltip-wrap">
<button class="glass glass-btn glass-btn--icon glass-btn--accent" aria-label="Upload"></button>
<div class="glass glass-tooltip" role="tooltip">Upload</div>
</div>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ─── NAVIGATION + TABS ──────────────────────────────────────────── -->
<section class="section">
<div class="section__header">
<h2 class="section__title">Navigation & Tabs</h2>
<span class="section__subtitle">Pill nav · Tab panels · Toggle</span>
</div>
<div style="display:flex;flex-direction:column;gap:var(--space-xl)">
<nav class="glass glass-nav" aria-label="Main navigation">
<span class="glass-nav__item glass-nav__item--active" tabindex="0">Discover</span>
<span class="glass-nav__item" tabindex="0">Trending</span>
<span class="glass-nav__item" tabindex="0">Collections</span>
<span class="glass-nav__item" tabindex="0">Profile</span>
</nav>
<!-- Tabs with real content panels -->
<div class="glass-tabs" id="demo-tabs">
<div class="glass glass-tab-list" role="tablist" aria-label="Demo tabs">
<button class="glass-tab" role="tab" aria-selected="true" aria-controls="tab-panel-1" id="tab-1">Overview</button>
<button class="glass-tab" role="tab" aria-selected="false" aria-controls="tab-panel-2" id="tab-2">Specs</button>
<button class="glass-tab" role="tab" aria-selected="false" aria-controls="tab-panel-3" id="tab-3">Examples</button>
</div>
<div id="tab-panel-1" class="glass glass-tab-panel is-active glass-card" role="tabpanel" aria-labelledby="tab-1">
<h3 class="glass-card__title">Overview</h3>
<p class="glass-card__body">Tab panels switch content without page navigation. Each panel is accessible via keyboard and ARIA roles, with an animated reveal on activation.</p>
</div>
<div id="tab-panel-2" class="glass glass-tab-panel glass-card" role="tabpanel" aria-labelledby="tab-2">
<h3 class="glass-card__title">Technical Specs</h3>
<p class="glass-card__body">Built on CSS custom properties with no JavaScript dependencies for pure display. The JS layer handles ARIA state updates and keyboard navigation (Arrow keys, Home, End).</p>
</div>
<div id="tab-panel-3" class="glass glass-tab-panel glass-card" role="tabpanel" aria-labelledby="tab-3">
<h3 class="glass-card__title">Example Panels</h3>
<p class="glass-card__body">Any content can live inside a tab panel — forms, data tables, cards, or rich media. The glass surface adapts to whatever's inside.</p>
</div>
</div>
<div class="glass glass-toggle" role="group" aria-label="View toggle">
<span class="glass-toggle__opt is-on" tabindex="0">Grid</span>
<span class="glass-toggle__opt" tabindex="0">List</span>
<span class="glass-toggle__opt" tabindex="0">Map</span>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ─── DROPDOWN ──────────────────────────────────────────────────── -->
<section class="section">
<div class="section__header">
<h2 class="section__title">Dropdown</h2>
<span class="section__subtitle">Custom menu · Keyboard accessible</span>
</div>
<div style="display:flex;flex-wrap:wrap;gap:var(--space-md);align-items:flex-start">
<div class="glass-dropdown" id="dd-actions">
<button class="glass glass-btn glass-btn--ghost glass-dropdown-trigger" aria-haspopup="true" aria-expanded="false">
Actions <span class="glass-dropdown-trigger-arrow"></span>
</button>
<div class="glass glass-dropdown-menu" role="menu">
<button class="glass-dropdown-item" role="menuitem">✏️ Edit</button>
<button class="glass-dropdown-item" role="menuitem">📋 Duplicate</button>
<button class="glass-dropdown-item" role="menuitem">🔗 Share</button>
<div class="glass-dropdown-divider"></div>
<button class="glass-dropdown-item glass-dropdown-item--danger" role="menuitem">🗑 Delete</button>
</div>
</div>
<div class="glass-dropdown" id="dd-sort">
<button class="glass glass-btn glass-btn--primary glass-dropdown-trigger" aria-haspopup="true" aria-expanded="false">
Sort by: Name <span class="glass-dropdown-trigger-arrow"></span>
</button>
<div class="glass glass-dropdown-menu" role="menu">
<button class="glass-dropdown-item is-active" role="menuitem">Name ↑</button>
<button class="glass-dropdown-item" role="menuitem">Name ↓</button>
<button class="glass-dropdown-item" role="menuitem">Date modified</button>
<button class="glass-dropdown-item" role="menuitem">File size</button>
</div>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ─── FORM CONTROLS ─────────────────────────────────────────────── -->
<section class="section">
<div class="section__header">
<h2 class="section__title">Form Controls</h2>
<span class="section__subtitle">Checkbox · Radio · Switch · Select · Input</span>
</div>
<div class="glass glass-form">
<div class="glass-form__row">
<div>
<label>First Name</label>
<div class="glass-input-wrap">
<input class="glass-input" placeholder="Lyra" aria-label="First name">
</div>
</div>
<div>
<label>Last Name</label>
<div class="glass-input-wrap">
<input class="glass-input" placeholder="Voss" aria-label="Last name">
</div>
</div>
</div>
<div class="glass-form__row glass-form__row--full">
<div>
<label>Email <span style="color:#f87171">*</span></label>
<div class="glass-input-wrap">
<input class="glass-input" type="email" placeholder="lyra@cosmos.io" aria-label="Email" required>
<span class="glass-input-icon"></span>
</div>
</div>
</div>
<div class="glass-form__row glass-form__row--full">
<div>
<label>Role</label>
<select class="glass-select" aria-label="Select role">
<option value="">Choose a role…</option>
<option value="design">UI Designer</option>
<option value="eng">Frontend Engineer</option>
<option value="pm">Product Manager</option>
<option value="motion">Motion Designer</option>
</select>
</div>
</div>
<div class="glass-form__row glass-form__row--full">
<div>
<label>Message</label>
<textarea class="glass-textarea" placeholder="Write something…" aria-label="Message"></textarea>
</div>
</div>
<div class="glass-form__row">
<div>
<label style="margin-bottom:var(--space-sm)">Interests</label>
<div class="glass-check-group">
<label class="glass-check-label">
<input type="checkbox" checked aria-label="Design">
<span class="glass-check-box"></span>
Design
</label>
<label class="glass-check-label">
<input type="checkbox" aria-label="Motion">
<span class="glass-check-box"></span>
Motion
</label>
<label class="glass-check-label">
<input type="checkbox" checked aria-label="Frontend">
<span class="glass-check-box"></span>
Frontend
</label>
</div>
</div>
<div>
<label style="margin-bottom:var(--space-sm)">Preferred contact</label>
<div class="glass-check-group">
<label class="glass-check-label">
<input type="radio" name="contact" value="email" checked aria-label="Email">
<span class="glass-radio-box"></span>
Email
</label>
<label class="glass-check-label">
<input type="radio" name="contact" value="slack" aria-label="Slack">
<span class="glass-radio-box"></span>
Slack
</label>
<label class="glass-check-label">
<input type="radio" name="contact" value="phone" aria-label="Phone">
<span class="glass-radio-box"></span>
Phone
</label>
</div>
</div>
</div>
<div class="glass-form__row">
<div>
<label style="margin-bottom:var(--space-sm)">Preferences</label>
<div style="display:flex;flex-direction:column;gap:var(--space-sm)">
<label class="glass-switch-label">
<input type="checkbox" checked aria-label="Email notifications">
<span class="glass-switch-track"><span class="glass-switch-thumb"></span></span>
Email notifications
</label>
<label class="glass-switch-label">
<input type="checkbox" aria-label="Dark mode">
<span class="glass-switch-track"><span class="glass-switch-thumb"></span></span>
Dark mode
</label>
<label class="glass-switch-label">
<input type="checkbox" checked aria-label="Analytics">
<span class="glass-switch-track"><span class="glass-switch-thumb"></span></span>
Analytics
</label>
</div>
</div>
<div>
<label>Blur intensity <span id="blur-val">50</span>%</label>
<input class="glass-range" type="range" min="0" max="100" value="50" aria-label="Blur intensity" oninput="document.getElementById('blur-val').textContent=this.value">
</div>
</div>
<button class="glass glass-btn glass-btn--primary" style="width:100%;justify-content:center" type="submit">
Submit Form
</button>
</div>
</section>
<div class="divider"></div>
<!-- ─── ACCORDION ─────────────────────────────────────────────────── -->
<section class="section">
<div class="section__header">
<h2 class="section__title">Accordion</h2>
<span class="section__subtitle">Click to expand</span>
</div>
<div class="glass-accordion" id="accordion">
<div class="glass glass-accordion-item is-open">
<button class="glass-accordion-trigger" aria-expanded="true" aria-controls="acc-1">
What is Liquid Glass?
<span class="glass-accordion-icon" aria-hidden="true"></span>
</button>
<div class="glass-accordion-body" id="acc-1" role="region">
<div class="glass-accordion-content">
Liquid Glass is a CSS design language built around backdrop-filter blur, translucent surfaces, and reflective gradients — creating the illusion of physical depth on digital screens.
</div>
</div>
</div>
<div class="glass glass-accordion-item">
<button class="glass-accordion-trigger" aria-expanded="false" aria-controls="acc-2">
Browser support?
<span class="glass-accordion-icon" aria-hidden="true"></span>
</button>
<div class="glass-accordion-body" id="acc-2" role="region">
<div class="glass-accordion-content">
backdrop-filter is supported in all modern browsers. A <code style="opacity:.8">@supports</code> fallback is included for older Firefox, providing a semi-transparent solid surface instead of blur.
</div>
</div>
</div>
<div class="glass glass-accordion-item">
<button class="glass-accordion-trigger" aria-expanded="false" aria-controls="acc-3">
How do I theme it?
<span class="glass-accordion-icon" aria-hidden="true"></span>
</button>
<div class="glass-accordion-body" id="acc-3" role="region">
<div class="glass-accordion-content">
All values are CSS custom properties. Toggle <code style="opacity:.8">data-theme="light"</code> on the <code style="opacity:.8">&lt;html&gt;</code> element to switch the entire palette instantly. Override individual tokens in a local scope for component-level theming.
</div>
</div>
</div>
<div class="glass glass-accordion-item">
<button class="glass-accordion-trigger" aria-expanded="false" aria-controls="acc-4">
Is it accessible?
<span class="glass-accordion-icon" aria-hidden="true"></span>
</button>
<div class="glass-accordion-body" id="acc-4" role="region">
<div class="glass-accordion-content">
Yes. All interactive elements have ARIA roles, keyboard support, and visible :focus-visible outlines. prefers-reduced-motion disables all animations. Colour contrast meets WCAG AA on both themes.
</div>
</div>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ─── STEPPER ────────────────────────────────────────────────────── -->
<section class="section">
<div class="section__header">
<h2 class="section__title">Stepper</h2>
<span class="section__subtitle">Multi-step flow · Horizontal &amp; vertical</span>
</div>
<div style="display:flex;flex-direction:column;gap:var(--space-2xl)">
<div>
<p class="section__subtitle" style="margin-bottom:var(--space-md)">Horizontal stepper</p>
<div class="glass-stepper" id="stepper-h">
<div class="glass-step is-complete">
<div class="glass-step__inner" style="display:flex;align-items:center;gap:8px">
<div class="glass-step__node" aria-label="Step 1: Account — complete"></div>
<span class="glass-step__label">Account</span>
</div>
<div class="glass-step__connector"></div>
</div>
<div class="glass-step is-complete">
<div class="glass-step__inner" style="display:flex;align-items:center;gap:8px">
<div class="glass-step__node" aria-label="Step 2: Profile — complete"></div>
<span class="glass-step__label">Profile</span>
</div>
<div class="glass-step__connector"></div>
</div>
<div class="glass-step is-active">
<div class="glass-step__inner" style="display:flex;align-items:center;gap:8px">
<div class="glass-step__node" aria-label="Step 3: Billing — current" aria-current="step">3</div>
<span class="glass-step__label">Billing</span>
</div>
<div class="glass-step__connector"></div>
</div>
<div class="glass-step">
<div class="glass-step__inner" style="display:flex;align-items:center;gap:8px">
<div class="glass-step__node" aria-label="Step 4: Review — pending">4</div>
<span class="glass-step__label">Review</span>
</div>
</div>
</div>
<div class="btn-row" style="margin-top:var(--space-md)">
<button class="glass glass-btn glass-btn--ghost glass-btn--sm" id="stepper-prev">← Prev</button>
<button class="glass glass-btn glass-btn--primary glass-btn--sm" id="stepper-next">Next →</button>
</div>
</div>
<div>
<p class="section__subtitle" style="margin-bottom:var(--space-md)">Vertical stepper</p>
<div class="glass-stepper glass-stepper--vertical">
<div class="glass-step is-complete">
<div class="glass-step__inner">
<div class="glass-step__node" aria-label="Step 1 — complete"></div>
<span class="glass-step__label">Create account</span>
</div>
<div class="glass-step__connector"></div>
</div>
<div class="glass-step is-active">
<div class="glass-step__inner">
<div class="glass-step__node" aria-label="Step 2 — current" aria-current="step">2</div>
<span class="glass-step__label">Verify email</span>
</div>
<div class="glass-step__connector"></div>
</div>
<div class="glass-step">
<div class="glass-step__inner">
<div class="glass-step__node" aria-label="Step 3 — pending">3</div>
<span class="glass-step__label">Set up workspace</span>
</div>
</div>
</div>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ─── DATA TABLE ─────────────────────────────────────────────────── -->
<section class="section">
<div class="section__header">
<h2 class="section__title">Data Table</h2>
<span class="section__subtitle">Sortable · Hover rows · Status badges</span>
</div>
<div class="glass glass-table-wrap" id="data-table-wrap">
<!-- Toolbar -->
<div class="glass-table-toolbar">
<span class="glass-table-toolbar__title">Team Members</span>
<span class="glass-table-toolbar__count" id="tbl-count">5 members</span>
<div class="glass-table-search">
<span class="glass-table-search__icon">🔍</span>
<input class="glass-table-search__input" type="search" placeholder="Search members…" id="tbl-search" aria-label="Search members">
</div>
<button class="glass glass-btn glass-btn--ghost glass-btn--sm" id="tbl-add-btn" style="white-space:nowrap">+ Invite</button>
</div>
<!-- Bulk action bar -->
<div class="glass-table-bulk" id="tbl-bulk" role="status" aria-live="polite">
<span class="glass-table-bulk__count" id="tbl-bulk-count">0 selected</span>
<button class="glass glass-btn glass-btn--ghost glass-btn--sm">📋 Export</button>
<button class="glass glass-btn glass-btn--danger glass-btn--sm">🗑 Remove</button>
<button class="glass glass-btn glass-btn--ghost glass-btn--sm" id="tbl-deselect" style="margin-left:auto">✕ Clear</button>
</div>
<!-- Table -->
<table class="glass-table" id="glass-table" aria-label="Team members">
<thead>
<tr>
<th class="col-check" scope="col">
<input type="checkbox" class="glass-table-check" id="tbl-check-all" aria-label="Select all rows">
</th>
<th scope="col" data-col="name" class="is-sorted">
Member <span class="glass-table__sort-icon"></span>
</th>
<th scope="col" data-col="role">Role</th>
<th scope="col" data-col="status">Status</th>
<th scope="col" data-col="projects" class="glass-table__cell--num">
Projects <span class="glass-table__sort-icon"></span>
</th>
<th scope="col" data-col="score" class="glass-table__cell--num">
Score <span class="glass-table__sort-icon"></span>
</th>
<th scope="col" style="width:48px"></th>
</tr>
</thead>
<tbody id="tbl-body">
<tr data-id="1">
<td class="col-check" data-label="Select">
<input type="checkbox" class="glass-table-check" aria-label="Select Lyra Voss">
</td>
<td data-label="Member">
<div class="glass-table-member">
<div class="glass-table-avatar" style="background:linear-gradient(135deg,#e8a87c,#d4845a)">👩</div>
<div class="glass-table-member__info">
<span class="glass-table-member__name">Lyra Voss</span>
<span class="glass-table-member__email">lyra@cosmos.io</span>
</div>
</div>
</td>
<td data-label="Role">UI Designer</td>
<td data-label="Status"><span class="glass-badge glass-badge--lime"><span class="glass-badge__dot"></span>Online</span></td>
<td class="glass-table__cell--num" data-label="Projects">12</td>
<td class="glass-table__cell--num" data-label="Score">
<div class="glass-table-score">
<div class="glass-table-score__bar">
<div class="glass-table-score__fill" style="width:97%"></div>
</div>
<span class="glass-table-score__num">97</span>
</div>
</td>
<td>
<button class="glass-table-action" aria-label="Actions for Lyra Voss"></button>
</td>
</tr>
<tr data-id="2">
<td class="col-check" data-label="Select">
<input type="checkbox" class="glass-table-check" aria-label="Select Marc Dubois">
</td>
<td data-label="Member">
<div class="glass-table-member">
<div class="glass-table-avatar" style="background:linear-gradient(135deg,#6baed6,#3182bd)">👨‍💻</div>
<div class="glass-table-member__info">
<span class="glass-table-member__name">Marc Dubois</span>
<span class="glass-table-member__email">marc@cosmos.io</span>
</div>
</div>
</td>
<td data-label="Role">Frontend Eng</td>
<td data-label="Status"><span class="glass-badge glass-badge--lime"><span class="glass-badge__dot"></span>Online</span></td>
<td class="glass-table__cell--num" data-label="Projects">8</td>
<td class="glass-table__cell--num" data-label="Score">
<div class="glass-table-score">
<div class="glass-table-score__bar">
<div class="glass-table-score__fill" style="width:89%"></div>
</div>
<span class="glass-table-score__num">89</span>
</div>
</td>
<td>
<button class="glass-table-action" aria-label="Actions for Marc Dubois"></button>
</td>
</tr>
<tr data-id="3">
<td class="col-check" data-label="Select">
<input type="checkbox" class="glass-table-check" aria-label="Select Sara Kim">
</td>
<td data-label="Member">
<div class="glass-table-member">
<div class="glass-table-avatar" style="background:linear-gradient(135deg,#f7b7d3,#e06fa4)">👩‍🎨</div>
<div class="glass-table-member__info">
<span class="glass-table-member__name">Sara Kim</span>
<span class="glass-table-member__email">sara@cosmos.io</span>
</div>
</div>
</td>
<td data-label="Role">Motion</td>
<td data-label="Status"><span class="glass-badge glass-badge--amber">Away</span></td>
<td class="glass-table__cell--num" data-label="Projects">5</td>
<td class="glass-table__cell--num" data-label="Score">
<div class="glass-table-score">
<div class="glass-table-score__bar">
<div class="glass-table-score__fill" style="width:84%"></div>
</div>
<span class="glass-table-score__num">84</span>
</div>
</td>
<td>
<button class="glass-table-action" aria-label="Actions for Sara Kim"></button>
</td>
</tr>
<tr data-id="4">
<td class="col-check" data-label="Select">
<input type="checkbox" class="glass-table-check" aria-label="Select Omar Reyes">
</td>
<td data-label="Member">
<div class="glass-table-member">
<div class="glass-table-avatar" style="background:linear-gradient(135deg,#9ecb8a,#4c9e3f)">👨‍🔬</div>
<div class="glass-table-member__info">
<span class="glass-table-member__name">Omar Reyes</span>
<span class="glass-table-member__email">omar@cosmos.io</span>
</div>
</div>
</td>
<td data-label="Role">Backend Eng</td>
<td data-label="Status"><span class="glass-badge glass-badge--violet">Offline</span></td>
<td class="glass-table__cell--num" data-label="Projects">9</td>
<td class="glass-table__cell--num" data-label="Score">
<div class="glass-table-score">
<div class="glass-table-score__bar">
<div class="glass-table-score__fill" style="width:91%"></div>
</div>
<span class="glass-table-score__num">91</span>
</div>
</td>
<td>
<button class="glass-table-action" aria-label="Actions for Omar Reyes"></button>
</td>
</tr>
<tr data-id="5">
<td class="col-check" data-label="Select">
<input type="checkbox" class="glass-table-check" aria-label="Select Ines Hartmann">
</td>
<td data-label="Member">
<div class="glass-table-member">
<div class="glass-table-avatar" style="background:linear-gradient(135deg,#c5b0e8,#8a63cc)">👩‍💼</div>
<div class="glass-table-member__info">
<span class="glass-table-member__name">Ines Hartmann</span>
<span class="glass-table-member__email">ines@cosmos.io</span>
</div>
</div>
</td>
<td data-label="Role">Product</td>
<td data-label="Status"><span class="glass-badge glass-badge--lime"><span class="glass-badge__dot"></span>Online</span></td>
<td class="glass-table__cell--num" data-label="Projects">14</td>
<td class="glass-table__cell--num" data-label="Score">
<div class="glass-table-score">
<div class="glass-table-score__bar">
<div class="glass-table-score__fill" style="width:99%"></div>
</div>
<span class="glass-table-score__num">99</span>
</div>
</td>
<td>
<button class="glass-table-action" aria-label="Actions for Ines Hartmann"></button>
</td>
</tr>
</tbody>
</table>
<!-- Pagination -->
<div class="glass-table-pagination">
<span class="glass-table-pagination__info" id="tbl-page-info">Showing 1–5 of 5</span>
<div class="glass-table-pagination__controls">
<button class="glass-table-page-btn" id="tbl-prev" aria-label="Previous page" disabled></button>
<button class="glass-table-page-btn is-active" aria-label="Page 1" aria-current="page">1</button>
<button class="glass-table-page-btn" aria-label="Page 2">2</button>
<button class="glass-table-page-btn" aria-label="Page 3">3</button>
<button class="glass-table-page-btn" id="tbl-next" aria-label="Next page"></button>
</div>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ─── TOAST ─────────────────────────────────────────────────────── -->
<section class="section">
<div class="section__header">
<h2 class="section__title">Toast / Snackbar</h2>
<span class="section__subtitle">Animated entry · Auto-dismiss · Click to close</span>
</div>
<div class="toast-demo-row">
<button class="glass glass-btn glass-btn--ghost" data-toast="success" data-toast-title="Saved!" data-toast-desc="Your changes have been saved.">✓ Success</button>
<button class="glass glass-btn glass-btn--ghost" data-toast="error" data-toast-title="Error" data-toast-desc="Something went wrong. Try again.">✕ Error</button>
<button class="glass glass-btn glass-btn--ghost" data-toast="warning" data-toast-title="Warning" data-toast-desc="You're running low on storage.">⚠ Warning</button>
<button class="glass glass-btn glass-btn--ghost" data-toast="info" data-toast-title="New update" data-toast-desc="v2.1.0 is available now.">ℹ Info</button>
</div>
</section>
<div class="divider"></div>
<!-- ─── BADGES & TAGS ──────────────────────────────────────────────── -->
<section class="section">
<div class="section__header">
<h2 class="section__title">Badges &amp; Tags</h2>
<span class="section__subtitle">All colour variants</span>
</div>
<div style="display:flex;flex-direction:column;gap:var(--space-md)">
<div class="btn-row">
<span class="glass-badge glass-badge--aqua"><span class="glass-badge__dot"></span>Active</span>
<span class="glass-badge glass-badge--violet"><span class="glass-badge__dot"></span>Premium</span>
<span class="glass-badge glass-badge--rose">New</span>
<span class="glass-badge glass-badge--amber">Beta</span>
<span class="glass-badge glass-badge--lime"><span class="glass-badge__dot"></span>Online</span>
<span class="glass-badge glass-badge--error">Error</span>
</div>
<div class="tags-row">
<span class="glass-tag">glassmorphism</span>
<span class="glass-tag">liquid glass</span>
<span class="glass-tag">backdrop-filter</span>
<span class="glass-tag">blur effects</span>
<span class="glass-tag">CSS animations</span>
<span class="glass-tag">zero deps</span>
<span class="glass-tag">WCAG 2.1 AA</span>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ─── NOTIFICATIONS ─────────────────────────────────────────────── -->
<section class="section">
<div class="section__header">
<h2 class="section__title">Notifications</h2>
<span class="section__subtitle">Hover to slide</span>
</div>
<div>
<div class="glass notif" role="alert">
<div class="notif__icon notif__icon--aqua">🌊</div>
<div>
<div class="notif__title">New message from Lyra</div>
<div class="notif__desc">The glass effect looks stunning on mobile…</div>
</div>
<span class="notif__time">2m ago</span>
</div>
<div class="glass notif">
<div class="notif__icon notif__icon--violet"></div>
<div>
<div class="notif__title">Build complete</div>
<div class="notif__desc">UI Kit compiled successfully — 0 errors.</div>
</div>
<span class="notif__time">8m ago</span>
</div>
<div class="glass notif">
<div class="notif__icon notif__icon--amber"></div>
<div>
<div class="notif__title">New component added</div>
<div class="notif__desc">glass-modal is ready. Check the docs.</div>
</div>
<span class="notif__time">1h ago</span>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ─── MODAL ─────────────────────────────────────────────────────── -->
<section class="section">
<div class="section__header">
<h2 class="section__title">Modal</h2>
<span class="section__subtitle">Focus trap · ARIA · Liquid entry</span>
</div>
<button class="glass glass-btn glass-btn--primary" id="modal-open">Open Modal →</button>
</section>
<div class="glass-modal-backdrop" id="modal-backdrop" role="dialog" aria-modal="true" aria-labelledby="modal-title" inert>
<div class="glass-modal">
<button class="glass-modal__close" id="modal-close" aria-label="Close dialog"></button>
<div class="glass-modal__header">
<div class="glass-card__label">Dialog · Liquid Glass</div>
<h3 class="glass-card__title" id="modal-title">Frosted Overlay</h3>
</div>
<div class="glass-modal__body">
<p class="glass-card__body">This modal uses a dedicated opaque surface — not a transparent glass panel — so text is always readable regardless of what's behind it. An accent gradient stripe marks the top edge, and the entry uses a liquid spring curve.</p>
<div style="margin-top:var(--space-md); padding:var(--space-md); border-radius:var(--radius-md); background:rgba(94,231,223,.08); border:1px solid rgba(94,231,223,.18);">
<p style="font-size:var(--text-sm); opacity:.80; line-height:1.6;">Focus is trapped inside while the modal is open. <kbd style="padding:2px 6px; border-radius:4px; font-size:.75rem; border:1px solid rgba(255,255,255,.2); background:rgba(255,255,255,.1)">Tab</kbd> cycles through buttons, <kbd style="padding:2px 6px; border-radius:4px; font-size:.75rem; border:1px solid rgba(255,255,255,.2); background:rgba(255,255,255,.1)">Esc</kbd> closes.</p>
</div>
</div>
<div class="glass-modal__footer">
<button class="glass glass-btn glass-btn--primary" id="modal-confirm">Confirm</button>
<button class="glass glass-btn glass-btn--ghost" id="modal-cancel">Cancel</button>
</div>
</div>
</div>
<div class="divider"></div>
<!-- ─── SKELETON ──────────────────────────────────────────────────── -->
<section class="section">
<div class="section__header">
<h2 class="section__title">Skeleton Loader</h2>
<span class="section__subtitle">Shimmer state for async content</span>
</div>
<div class="card-grid">
<!-- Skeleton card -->
<div class="glass skeleton-layout">
<div class="skeleton-row">
<div class="skeleton skeleton--circle" style="width:48px;height:48px;flex-shrink:0"></div>
<div class="skeleton-lines">
<div class="skeleton skeleton--text" style="width:70%"></div>
<div class="skeleton skeleton--text" style="width:45%"></div>
</div>
</div>
<div class="skeleton skeleton--text" style="width:100%"></div>
<div class="skeleton skeleton--text" style="width:88%"></div>
<div class="skeleton skeleton--text" style="width:65%"></div>
<div class="skeleton skeleton--text" style="width:40%;height:28px;border-radius:var(--radius-full)"></div>
</div>
<!-- Second skeleton -->
<div class="glass skeleton-layout">
<div class="skeleton skeleton--heading" style="width:60%"></div>
<div class="skeleton skeleton--text" style="width:100%;height:120px;border-radius:var(--radius-md)"></div>
<div class="skeleton-row" style="gap:8px">
<div class="skeleton skeleton--text" style="width:80px;height:24px;border-radius:var(--radius-full)"></div>
<div class="skeleton skeleton--text" style="width:60px;height:24px;border-radius:var(--radius-full)"></div>
</div>
</div>
<!-- Third skeleton -->
<div class="glass skeleton-layout">
<div class="skeleton skeleton--text" style="width:100%;height:160px;border-radius:var(--radius-lg)"></div>
<div class="skeleton-row" style="gap:8px">
<div class="skeleton skeleton--circle" style="width:32px;height:32px;flex-shrink:0"></div>
<div class="skeleton skeleton--circle" style="width:32px;height:32px;flex-shrink:0"></div>
<div class="skeleton skeleton--circle" style="width:32px;height:32px;flex-shrink:0"></div>
</div>
<div class="skeleton skeleton--text" style="width:50%"></div>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ─── CHIPS ─────────────────────────────────────────────────────── -->
<section class="section" id="chips">
<div class="section__header">
<h2 class="section__title">Chips</h2>
<span class="section__subtitle">Dismissible · Selectable · Dynamic</span>
</div>
<div style="display:flex;flex-direction:column;gap:var(--space-xl)">
<div>
<p class="section__subtitle" style="margin-bottom:14px">Colour variants</p>
<div class="glass-chip-group">
<span class="glass glass-chip glass-chip--aqua is-entering">
<span class="glass-chip__icon">🌊</span>
<span class="glass-chip__label">Aqua</span>
<span class="glass-chip__dismiss" role="button" aria-label="Remove Aqua"></span>
</span>
<span class="glass glass-chip glass-chip--violet is-entering" style="animation-delay:.06s">
<span class="glass-chip__icon"></span>
<span class="glass-chip__label">Violet</span>
<span class="glass-chip__dismiss" role="button" aria-label="Remove Violet"></span>
</span>
<span class="glass glass-chip glass-chip--rose is-entering" style="animation-delay:.12s">
<span class="glass-chip__icon">🌸</span>
<span class="glass-chip__label">Rose</span>
<span class="glass-chip__dismiss" role="button" aria-label="Remove Rose"></span>
</span>
<span class="glass glass-chip glass-chip--amber is-entering" style="animation-delay:.18s">
<span class="glass-chip__icon"></span>
<span class="glass-chip__label">Amber</span>
<span class="glass-chip__dismiss" role="button" aria-label="Remove Amber"></span>
</span>
</div>
</div>
<div>
<p class="section__subtitle" style="margin-bottom:14px">Selectable filter chips</p>
<div class="glass-chip-group" id="filter-chips" role="group" aria-label="Filter chips">
<span class="glass glass-chip js-selectable" role="checkbox" aria-checked="false" tabindex="0" data-value="design"><span class="glass-chip__label">Design</span></span>
<span class="glass glass-chip js-selectable is-selected" role="checkbox" aria-checked="true" tabindex="0" data-value="frontend"><span class="glass-chip__label">Frontend</span></span>
<span class="glass glass-chip js-selectable" role="checkbox" aria-checked="false" tabindex="0" data-value="motion"><span class="glass-chip__label">Motion</span></span>
<span class="glass glass-chip js-selectable" role="checkbox" aria-checked="false" tabindex="0" data-value="3d"><span class="glass-chip__label">3D</span></span>
<span class="glass glass-chip js-selectable is-selected" role="checkbox" aria-checked="true" tabindex="0" data-value="glassmorphism"><span class="glass-chip__label">Glassmorphism</span></span>
</div>
</div>
<div>
<p class="section__subtitle" style="margin-bottom:14px">Dynamic — dismiss or add new</p>
<div class="glass-chip-group" id="dynamic-chips" aria-label="Dynamic chips">
<span class="glass glass-chip" data-chip><span class="glass-chip__icon">🎨</span><span class="glass-chip__label">Illustrator</span><span class="glass-chip__dismiss" role="button" aria-label="Remove Illustrator"></span></span>
<span class="glass glass-chip" data-chip><span class="glass-chip__icon">🖥</span><span class="glass-chip__label">Figma</span><span class="glass-chip__dismiss" role="button" aria-label="Remove Figma"></span></span>
<span class="glass glass-chip" data-chip><span class="glass-chip__icon">✏️</span><span class="glass-chip__label">Sketch</span><span class="glass-chip__dismiss" role="button" aria-label="Remove Sketch"></span></span>
<button class="glass-chip-add" id="chip-add-btn" title="Add chip" aria-label="Add chip">+</button>
</div>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ─── AVATAR GROUP ──────────────────────────────────────────────── -->
<section class="section" id="avatars">
<div class="section__header">
<h2 class="section__title">Avatar Group</h2>
<span class="section__subtitle">Photos · Initials · Status · Overflow · Sizes</span>
</div>
<div style="display:flex;flex-direction:column;gap:var(--space-2xl)">
<div>
<p class="section__subtitle" style="margin-bottom:18px">Photo avatars — hover for name card</p>
<div class="glass-avatar-group">
<div class="glass-avatar glass-avatar--photo" style="--from:#e8a87c;--to:#d4845a" tabindex="0" aria-label="Lyra Voss, UI Design, online">
<div class="glass-avatar__face">👩</div>
<span class="glass-avatar__status glass-avatar__status--online"></span>
<div class="glass glass-avatar-popover" aria-hidden="true">Lyra Voss<br><span style="opacity:.5;font-size:.72rem">UI Design</span></div>
</div>
<div class="glass-avatar glass-avatar--photo" style="--from:#6baed6;--to:#3182bd" tabindex="0" aria-label="Marc Dubois, Frontend, online">
<div class="glass-avatar__face">👨‍💻</div>
<span class="glass-avatar__status glass-avatar__status--online"></span>
<div class="glass glass-avatar-popover" aria-hidden="true">Marc Dubois<br><span style="opacity:.5;font-size:.72rem">Frontend</span></div>
</div>
<div class="glass-avatar glass-avatar--photo" style="--from:#f7b7d3;--to:#e06fa4" tabindex="0" aria-label="Sara Kim, Motion, away">
<div class="glass-avatar__face">👩‍🎨</div>
<span class="glass-avatar__status glass-avatar__status--away"></span>
<div class="glass glass-avatar-popover" aria-hidden="true">Sara Kim<br><span style="opacity:.5;font-size:.72rem">Motion</span></div>
</div>
<div class="glass-avatar glass-avatar--overflow" style="z-index:5" tabindex="0" aria-label="7 more members">
<div class="glass-avatar__face">+7</div>
<div class="glass glass-avatar-popover glass-avatar-popover--list" aria-hidden="true">
<ul class="glass-avatar-popover__list">
<li class="glass-avatar-popover__item">
<div class="glass-avatar-popover__mini" style="background:linear-gradient(135deg,#5ee7df,#3b82f6)">JL</div><span>Jin Lee</span>
</li>
<li class="glass-avatar-popover__item">
<div class="glass-avatar-popover__mini" style="background:linear-gradient(135deg,#f7a8c4,#b490f5)">AP</div><span>Anya Park</span>
</li>
<li class="glass-avatar-popover__item" style="opacity:.45;font-size:.72rem;padding-left:34px">+5 more…</li>
</ul>
</div>
</div>
<span class="glass-avatar-group__meta">10 members · 3 online</span>
</div>
</div>
<div>
<p class="section__subtitle" style="margin-bottom:18px">Size variants</p>
<div style="display:flex;flex-direction:column;gap:var(--space-md);align-items:flex-start">
<div class="glass-avatar-group glass-avatar-group--sm">
<div class="glass-avatar glass-avatar--photo" style="--from:#e8a87c;--to:#d4845a">
<div class="glass-avatar__face">👩</div>
</div>
<div class="glass-avatar glass-avatar--photo" style="--from:#6baed6;--to:#3182bd">
<div class="glass-avatar__face">👨‍💻</div>
</div>
<div class="glass-avatar glass-avatar--overflow">
<div class="glass-avatar__face">+9</div>
</div>
<span class="glass-avatar-group__meta">sm · 32px</span>
</div>
<div class="glass-avatar-group">
<div class="glass-avatar glass-avatar--photo" style="--from:#e8a87c;--to:#d4845a">
<div class="glass-avatar__face">👩</div>
</div>
<div class="glass-avatar glass-avatar--photo" style="--from:#6baed6;--to:#3182bd">
<div class="glass-avatar__face">👨‍💻</div>
</div>
<div class="glass-avatar glass-avatar--overflow">
<div class="glass-avatar__face">+9</div>
</div>
<span class="glass-avatar-group__meta">default · 44px</span>
</div>
<div class="glass-avatar-group glass-avatar-group--lg">
<div class="glass-avatar glass-avatar--photo" style="--from:#e8a87c;--to:#d4845a">
<div class="glass-avatar__face">👩</div>
</div>
<div class="glass-avatar glass-avatar--photo" style="--from:#6baed6;--to:#3182bd">
<div class="glass-avatar__face">👨‍💻</div>
</div>
<div class="glass-avatar glass-avatar--overflow">
<div class="glass-avatar__face">+9</div>
</div>
<span class="glass-avatar-group__meta">lg · 56px</span>
</div>
</div>
</div>
</div>
</section>
<div class="divider"></div>
<!-- ─── BREADCRUMB ────────────────────────────────────────────────── -->
<section class="section" id="breadcrumb">
<div class="section__header">
<h2 class="section__title">Breadcrumb</h2>
<span class="section__subtitle">Collapsed · Icons · Sizes</span>
</div>
<div style="display:flex;flex-direction:column;gap:var(--space-lg);align-items:flex-start">
<nav class="glass glass-breadcrumb" aria-label="Breadcrumb">
<span class="glass-breadcrumb__crumb"><span class="glass-breadcrumb__crumb-icon"></span>Home</span>
<span class="glass-breadcrumb__sep" aria-hidden="true"></span>
<span class="glass-breadcrumb__crumb">Design System</span>
<span class="glass-breadcrumb__sep" aria-hidden="true"></span>
<span class="glass-breadcrumb__crumb">Components</span>
<span class="glass-breadcrumb__sep" aria-hidden="true"></span>
<span class="glass-breadcrumb__crumb glass-breadcrumb__crumb--active" aria-current="page"><span class="glass-breadcrumb__crumb-icon"></span>Breadcrumb</span>
</nav>
<nav class="glass glass-breadcrumb" aria-label="Collapsed breadcrumb">
<span class="glass-breadcrumb__crumb"><span class="glass-breadcrumb__crumb-icon"></span>Home</span>
<span class="glass-breadcrumb__sep" aria-hidden="true"></span>
<div class="glass-breadcrumb__ellipsis-wrap" id="bc-ellipsis-wrap">
<span class="glass-breadcrumb__ellipsis" id="bc-ellipsis" role="button" aria-expanded="false" aria-label="Show hidden breadcrumbs"></span>
<div class="glass glass-breadcrumb-dropdown" role="list">
<div class="glass-breadcrumb-dropdown__item" role="listitem" tabindex="0">🗂 Products</div>
<div class="glass-breadcrumb-dropdown__item" role="listitem" tabindex="0">📦 UI Kits</div>
<div class="glass-breadcrumb-dropdown__item" role="listitem" tabindex="0">💎 Glass Series</div>
</div>
</div>
<span class="glass-breadcrumb__sep" aria-hidden="true"></span>
<span class="glass-breadcrumb__crumb glass-breadcrumb__crumb--active" aria-current="page">Liquid Glass Kit</span>
</nav>
</div>
</section>
<div class="divider"></div>
<!-- ─── 12 ANIMATIONS ─────────────────────────────────────────────── -->
<section class="section" id="animations">
<div class="section__header">
<h2 class="section__title">12 Liquid Animations</h2>
<span class="section__subtitle">Pure CSS · Infinite loop · Respects reduced-motion</span>
</div>
<div class="anim-grid" aria-label="Animation demos">
<div class="glass anim-demo" aria-label="Liquid Morph animation">
<div class="anim-demo__orb"></div><span class="anim-demo__label">1 · Liquid Morph</span>
</div>
<div class="glass anim-demo" aria-label="Shimmer animation">
<div class="anim-demo__orb"></div><span class="anim-demo__label">2 · Shimmer</span>
</div>
<div class="glass anim-demo" aria-label="Float animation">
<div class="anim-demo__orb"></div><span class="anim-demo__label">3 · Float</span>
</div>
<div class="glass anim-demo" aria-label="Refraction animation">
<div class="anim-demo__orb"></div><span class="anim-demo__label">4 · Refraction</span>
</div>
<div class="glass anim-demo" aria-label="Ripple animation">
<div class="anim-demo__orb" style="position:relative"></div><span class="anim-demo__label">5 · Ripple</span>
</div>
<div class="glass anim-demo" aria-label="Edge Glow animation">
<div class="anim-demo__orb"></div><span class="anim-demo__label">6 · Edge Glow</span>
</div>
<div class="glass anim-demo" aria-label="Breathing Blur animation">
<div class="anim-demo__orb"></div><span class="anim-demo__label">7 · Breathing Blur</span>
</div>
<div class="glass anim-demo" aria-label="Depth Shift animation">
<div class="anim-demo__orb"></div><span class="anim-demo__label">8 · Depth Shift</span>
</div>
<div class="glass anim-demo" aria-label="Cascade Reveal animation">
<div class="anim-demo__orb"></div><span class="anim-demo__label">9 · Cascade Reveal</span>
</div>
<div class="glass anim-demo" aria-label="Prism Hue Shift animation">
<div class="anim-demo__orb"></div><span class="anim-demo__label">10 · Prism Hue Shift</span>
</div>
<div class="glass anim-demo" aria-label="Liquid Drain animation">
<div class="anim-demo__orb"></div><span class="anim-demo__label">11 · Liquid Drain</span>
</div>
<div class="glass anim-demo" aria-label="Surface Tension animation">
<div class="anim-demo__orb"></div><span class="anim-demo__label">12 · Surface Tension</span>
</div>
</div>
</section>
<footer class="footer">
<p class="footer__text">Liquid Glass UI Kit · WCAG AA · @supports fallback · prefers-reduced-motion · CSS Guidelines compliant</p>
</footer>
</div>
</main>

Pure CSS Glassmorphism Liquid Glass UI kit

Complete design token system, 15+ accessible components, light/dark mode, 12 animations — zero dependencies. 1. Custom Properties — Complete Design Token System 2. Reset & Base 3. Light Theme Tokens 4. @supports — Graceful Degradation 5. prefers-reduced-motion 6. Animated Background Scene 7. Glass Utility Classes 8. Focus Visible 9. Component: Card 10. Component: Button 11. Component: Badge & Tag 12. Component: Input 13. Component: Checkbox & Radio 14. Component: Switch 15. Component: Navigation 16. Component: Tabs + Panels 17. Component: Accordion 18. Component: Modal / Dialog 19. Component: Toast / Snackbar 20. Component: Tooltip 21. Component: Dropdown / Select 22. Component: Chip 23. Component: Avatar Group 24. Component: Breadcrumb 25. Component: Skeleton Loader 26. Component: Stepper 27. Component: Data Table 28. 12 Liquid Glass Animations 29. Page Layout & Hero 30. Theme Toggle Button 31. Responsive

A Pen by Margarita on CodePen.

License.

/**
* liquidGlassKit.js
* ─────────────────────────────────────────────────────────────────────────
* Class-based component architecture with custom events.
* Each component:
* - Has an init() static factory method
* - Emits custom 'glass:*' events on its host element
* - Is keyboard accessible
* - Does not rely on global state
* ─────────────────────────────────────────────────────────────────────────
*/
(function liquidGlassKit() {
"use strict";
/* ======================================================================
UTILITIES
====================================================================== */
function emit(el, name, detail) {
el.dispatchEvent(
new CustomEvent("glass:" + name, { bubbles: true, detail: detail || {} })
);
}
function getFocusable(container) {
return Array.from(
container.querySelectorAll(
"a[href], button:not([disabled]), input:not([disabled]), " +
"select:not([disabled]), textarea:not([disabled]), " +
'[tabindex]:not([tabindex="-1"])'
)
);
}
function trapFocus(container, evt) {
var focusable = getFocusable(container);
if (!focusable.length) return;
var first = focusable[0];
var last = focusable[focusable.length - 1];
if (evt.key === "Tab") {
if (evt.shiftKey && document.activeElement === first) {
evt.preventDefault();
last.focus();
} else if (!evt.shiftKey && document.activeElement === last) {
evt.preventDefault();
first.focus();
}
}
}
/* ======================================================================
GlassTheme
====================================================================== */
var GlassTheme = {
init: function () {
var btn = document.getElementById("theme-toggle");
var root = document.documentElement;
var key = "glass-theme";
var saved = localStorage.getItem(key);
if (saved) root.setAttribute("data-theme", saved);
if (btn) {
btn.addEventListener("click", function () {
var next =
root.getAttribute("data-theme") === "light" ? "dark" : "light";
root.setAttribute("data-theme", next);
localStorage.setItem(key, next);
emit(root, "theme:change", { theme: next });
});
}
}
};
/* ======================================================================
GlassModal
====================================================================== */
function GlassModal(backdropEl) {
this.backdrop = backdropEl;
this.modal = backdropEl.querySelector(".glass-modal");
this._onKey = this._handleKey.bind(this);
this._bindTriggers();
}
GlassModal.prototype._bindTriggers = function () {
var self = this;
/* Open triggers — any [data-modal-open] or #modal-open */
document
.querySelectorAll("[data-modal-open], #modal-open")
.forEach(function (el) {
el.addEventListener("click", function () {
self.open();
});
});
/* Close triggers */
this.backdrop
.querySelectorAll(
'[id^="modal-close"], [id^="modal-confirm"], [id^="modal-cancel"]'
)
.forEach(function (el) {
el.addEventListener("click", function () {
self.close();
});
});
/* Backdrop click */
this.backdrop.addEventListener("click", function (evt) {
if (evt.target === self.backdrop) self.close();
});
};
GlassModal.prototype.open = function () {
this._lastFocus = document.activeElement;
this.backdrop.removeAttribute("inert");
this.backdrop.classList.add("is-open");
document.addEventListener("keydown", this._onKey);
/* Focus first focusable after animation */
var self = this;
setTimeout(function () {
var focusable = getFocusable(self.modal);
if (focusable.length) focusable[0].focus();
}, 50);
emit(this.backdrop, "modal:open");
};
GlassModal.prototype.close = function () {
this.backdrop.classList.remove("is-open");
this.backdrop.setAttribute("inert", "");
document.removeEventListener("keydown", this._onKey);
if (this._lastFocus) this._lastFocus.focus();
emit(this.backdrop, "modal:close");
};
GlassModal.prototype._handleKey = function (evt) {
if (evt.key === "Escape") {
this.close();
return;
}
trapFocus(this.modal, evt);
};
GlassModal.init = function () {
var backdrop = document.getElementById("modal-backdrop");
if (backdrop) new GlassModal(backdrop);
};
/* ======================================================================
GlassTabs
====================================================================== */
function GlassTabs(containerEl) {
this.container = containerEl;
this.tabs = Array.from(containerEl.querySelectorAll(".glass-tab"));
this.panels = Array.from(containerEl.querySelectorAll(".glass-tab-panel"));
this._bind();
}
GlassTabs.prototype._bind = function () {
var self = this;
this.tabs.forEach(function (tab, i) {
tab.addEventListener("click", function () {
self.activate(i);
});
tab.addEventListener("keydown", function (evt) {
if (evt.key === "ArrowRight") {
evt.preventDefault();
self.activate((i + 1) % self.tabs.length);
self.tabs[(i + 1) % self.tabs.length].focus();
}
if (evt.key === "ArrowLeft") {
evt.preventDefault();
var prev = (i - 1 + self.tabs.length) % self.tabs.length;
self.activate(prev);
self.tabs[prev].focus();
}
if (evt.key === "Home") {
evt.preventDefault();
self.activate(0);
self.tabs[0].focus();
}
if (evt.key === "End") {
evt.preventDefault();
self.activate(self.tabs.length - 1);
self.tabs[self.tabs.length - 1].focus();
}
});
});
};
GlassTabs.prototype.activate = function (index) {
var self = this;
this.tabs.forEach(function (t, i) {
var on = i === index;
t.setAttribute("aria-selected", String(on));
t.setAttribute("tabindex", on ? "0" : "-1");
});
this.panels.forEach(function (p, i) {
if (i === index) {
p.classList.add("is-active");
} else {
p.classList.remove("is-active");
}
});
emit(this.container, "tabs:change", { index: index });
};
GlassTabs.init = function () {
document.querySelectorAll(".glass-tabs").forEach(function (el) {
new GlassTabs(el);
});
};
/* ======================================================================
GlassAccordion
====================================================================== */
function GlassAccordion(containerEl) {
this.container = containerEl;
this._bind();
}
GlassAccordion.prototype._bind = function () {
var self = this;
this.container
.querySelectorAll(".glass-accordion-trigger")
.forEach(function (trigger) {
trigger.addEventListener("click", function () {
var item = trigger.closest(".glass-accordion-item");
if (!item) return;
var isOpen = item.classList.contains("is-open");
/* Allow multiple open — remove this block to make exclusive */
item.classList.toggle("is-open", !isOpen);
trigger.setAttribute("aria-expanded", String(!isOpen));
emit(item, "accordion:toggle", { open: !isOpen });
});
trigger.addEventListener("keydown", function (evt) {
if (evt.key === "Enter" || evt.key === " ") {
evt.preventDefault();
trigger.click();
}
});
});
};
GlassAccordion.init = function () {
document.querySelectorAll(".glass-accordion").forEach(function (el) {
new GlassAccordion(el);
});
};
/* ======================================================================
GlassDropdown
====================================================================== */
function GlassDropdown(containerEl) {
this.container = containerEl;
this.trigger = containerEl.querySelector(".glass-dropdown-trigger");
this.menu = containerEl.querySelector(".glass-dropdown-menu");
this._onOutside = this._closeOnOutside.bind(this);
this._bind();
}
GlassDropdown.prototype._bind = function () {
var self = this;
if (this.trigger) {
this.trigger.addEventListener("click", function (evt) {
evt.stopPropagation();
self.toggle();
});
this.trigger.addEventListener("keydown", function (evt) {
if (evt.key === "ArrowDown") {
evt.preventDefault();
self.open();
self._focusFirst();
}
if (evt.key === "Escape") {
self.close();
}
});
}
if (this.menu) {
this.menu
.querySelectorAll(".glass-dropdown-item")
.forEach(function (item) {
item.addEventListener("click", function () {
self.close();
});
item.addEventListener("keydown", function (evt) {
if (evt.key === "Escape") {
self.close();
self.trigger && self.trigger.focus();
}
if (evt.key === "ArrowDown") {
evt.preventDefault();
var next = item.nextElementSibling;
while (next && next.classList.contains("glass-dropdown-divider"))
next = next.nextElementSibling;
if (next) next.focus();
}
if (evt.key === "ArrowUp") {
evt.preventDefault();
var prev = item.previousElementSibling;
while (prev && prev.classList.contains("glass-dropdown-divider"))
prev = prev.previousElementSibling;
if (prev) prev.focus();
else self.trigger && self.trigger.focus();
}
});
});
}
};
GlassDropdown.prototype.open = function () {
this.container.classList.add("is-open");
this.trigger && this.trigger.setAttribute("aria-expanded", "true");
document.addEventListener("click", this._onOutside);
document.addEventListener(
"keydown",
(this._onKey = function (evt) {
if (evt.key === "Escape") this.close();
}.bind(this))
);
emit(this.container, "dropdown:open");
};
GlassDropdown.prototype.close = function () {
this.container.classList.remove("is-open");
this.trigger && this.trigger.setAttribute("aria-expanded", "false");
document.removeEventListener("click", this._onOutside);
emit(this.container, "dropdown:close");
};
GlassDropdown.prototype.toggle = function () {
this.container.classList.contains("is-open") ? this.close() : this.open();
};
GlassDropdown.prototype._focusFirst = function () {
var first = this.menu && this.menu.querySelector(".glass-dropdown-item");
if (first)
setTimeout(function () {
first.focus();
}, 20);
};
GlassDropdown.prototype._closeOnOutside = function (evt) {
if (!this.container.contains(evt.target)) this.close();
};
GlassDropdown.init = function () {
document.querySelectorAll(".glass-dropdown").forEach(function (el) {
new GlassDropdown(el);
});
};
/* ======================================================================
GlassToast
====================================================================== */
var GlassToast = {
ICONS: { success: "✓", error: "✕", warning: "⚠", info: "ℹ" },
LABELS: {
success: "Success",
error: "Error",
warning: "Warning",
info: "Info"
},
show: function (type, title, desc, duration) {
var region = document.getElementById("toast-region");
if (!region) return;
var toast = document.createElement("div");
toast.className = "glass glass-toast glass-toast--" + type;
toast.setAttribute("role", "status");
toast.setAttribute("aria-live", "polite");
toast.innerHTML =
'<span class="glass-toast__icon" aria-hidden="true">' +
(this.ICONS[type] || "ℹ") +
"</span>" +
'<div class="glass-toast__body">' +
'<div class="glass-toast__title">' +
(title || this.LABELS[type]) +
"</div>" +
(desc ? '<div class="glass-toast__desc">' + desc + "</div>" : "") +
"</div>" +
'<span class="glass-toast__close" aria-label="Dismiss">✕</span>';
region.appendChild(toast);
emit(region, "toast:show", { type: type, title: title });
var dismiss = function () {
toast.classList.add("is-exiting");
toast.addEventListener(
"animationend",
function () {
toast.remove();
},
{ once: true }
);
};
toast
.querySelector(".glass-toast__close")
.addEventListener("click", dismiss);
toast.addEventListener("click", dismiss);
if (duration !== 0) setTimeout(dismiss, duration || 4000);
},
init: function () {
document.addEventListener("click", function (evt) {
var btn = evt.target.closest("[data-toast]");
if (!btn) return;
GlassToast.show(
btn.dataset.toast,
btn.dataset.toastTitle,
btn.dataset.toastDesc
);
});
}
};
/* ======================================================================
GlassStepper
====================================================================== */
var GlassStepper = {
init: function () {
var stepper = document.getElementById("stepper-h");
var prevBtn = document.getElementById("stepper-prev");
var nextBtn = document.getElementById("stepper-next");
if (!stepper || !prevBtn || !nextBtn) return;
var steps = Array.from(stepper.querySelectorAll(".glass-step"));
var current = steps.findIndex(function (s) {
return s.classList.contains("is-active");
});
function update(idx) {
steps.forEach(function (step, i) {
step.classList.toggle("is-complete", i < idx);
step.classList.toggle("is-active", i === idx);
step.classList.toggle("is-pending", i > idx);
var node = step.querySelector(".glass-step__node");
if (node) {
if (i < idx) node.textContent = "✓";
else node.textContent = String(i + 1);
}
var nodeEl = step.querySelector(".glass-step__node");
if (nodeEl) {
if (i === idx) {
nodeEl.setAttribute("aria-current", "step");
} else {
nodeEl.removeAttribute("aria-current");
}
}
});
current = idx;
prevBtn.disabled = idx === 0;
nextBtn.disabled = idx === steps.length - 1;
emit(stepper, "stepper:change", { step: idx });
}
prevBtn.addEventListener("click", function () {
if (current > 0) update(current - 1);
});
nextBtn.addEventListener("click", function () {
if (current < steps.length - 1) update(current + 1);
});
update(current >= 0 ? current : 0);
}
};
/* ======================================================================
GlassNavTabs (pill nav)
====================================================================== */
var GlassNavTabs = {
init: function () {
document.querySelectorAll(".glass-nav").forEach(function (nav) {
nav.addEventListener("click", function (evt) {
var item = evt.target.closest(".glass-nav__item");
if (!item) return;
nav.querySelectorAll(".glass-nav__item").forEach(function (el) {
el.classList.remove("glass-nav__item--active");
});
item.classList.add("glass-nav__item--active");
});
nav.addEventListener("keydown", function (evt) {
var item = evt.target.closest(".glass-nav__item");
if (!item) return;
if (evt.key === "Enter" || evt.key === " ") {
evt.preventDefault();
item.click();
}
});
});
}
};
/* ======================================================================
GlassToggle
====================================================================== */
var GlassToggle = {
init: function () {
document.querySelectorAll(".glass-toggle").forEach(function (toggle) {
toggle.addEventListener("click", function (evt) {
var opt = evt.target.closest(".glass-toggle__opt");
if (!opt) return;
toggle.querySelectorAll(".glass-toggle__opt").forEach(function (el) {
el.classList.remove("is-on");
});
opt.classList.add("is-on");
});
});
}
};
/* ======================================================================
GlassRipple
====================================================================== */
var GlassRipple = {
create: function (el, evt) {
/* Limit DOM nodes: skip if too many ripples present */
if (el.querySelectorAll("[data-ripple]").length > 3) return;
var ripple = document.createElement("span");
ripple.setAttribute("data-ripple", "");
var rect = el.getBoundingClientRect();
var size = Math.max(rect.width, rect.height) * 1.2;
var x = evt.clientX - rect.left - size / 2;
var y = evt.clientY - rect.top - size / 2;
Object.assign(ripple.style, {
position: "absolute",
left: x + "px",
top: y + "px",
width: size + "px",
height: size + "px",
borderRadius: "50%",
background: "rgba(255,255,255,0.18)",
transform: "scale(0)",
pointerEvents: "none",
animation: "ripple 600ms ease-out forwards",
zIndex: "999"
});
el.appendChild(ripple);
ripple.addEventListener(
"animationend",
function () {
ripple.remove();
},
{ once: true }
);
},
init: function () {
document.addEventListener("click", function (evt) {
var target = evt.target.closest(".glass-btn, .glass-card, .notif");
if (target) GlassRipple.create(target, evt);
});
}
};
/* ======================================================================
GlassParallax (mouse tilt on cards)
====================================================================== */
var GlassParallax = {
init: function () {
/* Skip on touch devices */
if (window.matchMedia("(hover: none)").matches) return;
/* Respect reduced motion */
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
document.querySelectorAll(".glass-card").forEach(function (card) {
card.addEventListener("mousemove", function (evt) {
var rect = card.getBoundingClientRect();
var dx =
(evt.clientX - (rect.left + rect.width / 2)) / (rect.width / 2);
var dy =
(evt.clientY - (rect.top + rect.height / 2)) / (rect.height / 2);
card.style.transform =
"translateY(-6px) scale(1.01) rotateX(" +
-dy * 4 +
"deg) rotateY(" +
dx * 4 +
"deg)";
card.style.transition = "none";
});
card.addEventListener("mouseleave", function () {
card.style.transform = "";
card.style.transition = "";
});
});
}
};
/* ======================================================================
GlassChips
====================================================================== */
var GlassChips = {
init: function () {
/* Dismiss */
document.addEventListener("click", function (evt) {
var btn = evt.target.closest(".glass-chip__dismiss");
if (!btn) return;
var chip = btn.closest(".glass-chip");
if (!chip) return;
chip.classList.add("is-exiting");
chip.addEventListener(
"animationend",
function () {
chip.remove();
},
{ once: true }
);
emit(chip, "chip:dismiss");
});
/* Selectable */
var filterGroup = document.getElementById("filter-chips");
if (filterGroup) {
function toggleChip(chip) {
var on = !chip.classList.contains("is-selected");
chip.classList.toggle("is-selected", on);
chip.setAttribute("aria-checked", String(on));
emit(chip, "chip:select", {
value: chip.dataset.value,
selected: on
});
}
filterGroup.addEventListener("click", function (evt) {
var chip = evt.target.closest(".js-selectable");
if (chip) toggleChip(chip);
});
filterGroup.addEventListener("keydown", function (evt) {
var chip = evt.target.closest(".js-selectable");
if (chip && (evt.key === "Enter" || evt.key === " ")) {
evt.preventDefault();
toggleChip(chip);
}
});
}
/* Dynamic add */
var addBtn = document.getElementById("chip-add-btn");
var dynGroup = document.getElementById("dynamic-chips");
if (!addBtn || !dynGroup) return;
var activeInputWrap = null;
function commitChip() {
if (!activeInputWrap) return;
var val =
(activeInputWrap.querySelector(".glass-chip-input") || {}).value ||
"";
var label = val.trim();
cancelInput();
if (!label) return;
var chip = document.createElement("span");
chip.className = "glass glass-chip is-entering";
chip.setAttribute("data-chip", "");
chip.innerHTML =
'<span class="glass-chip__label">' +
label.replace(/</g, "&lt;").replace(/>/g, "&gt;") +
'</span><span class="glass-chip__dismiss" role="button" aria-label="Remove ' +
label +
'">✕</span>';
dynGroup.insertBefore(chip, addBtn);
chip.addEventListener(
"animationend",
function () {
chip.classList.remove("is-entering");
},
{ once: true }
);
emit(dynGroup, "chip:add", { label: label });
}
function cancelInput() {
if (!activeInputWrap) return;
activeInputWrap.remove();
activeInputWrap = null;
addBtn.style.display = "";
}
function openInput() {
if (activeInputWrap) return;
addBtn.style.display = "none";
var wrap = document.createElement("div");
wrap.className = "glass-chip-input-wrap";
wrap.innerHTML =
'<input class="glass-chip-input" type="text" placeholder="Tag name…" maxlength="24" autocomplete="off" aria-label="New chip name"><button class="glass-chip-input-confirm" aria-label="Add chip">✓</button>';
activeInputWrap = wrap;
dynGroup.insertBefore(wrap, addBtn);
var inputEl = wrap.querySelector(".glass-chip-input");
var confirmEl = wrap.querySelector(".glass-chip-input-confirm");
inputEl.focus();
inputEl.addEventListener("keydown", function (evt) {
if (evt.key === "Enter") {
evt.preventDefault();
commitChip();
}
if (evt.key === "Escape") {
cancelInput();
}
});
confirmEl.addEventListener("click", function (evt) {
evt.stopPropagation();
commitChip();
});
setTimeout(function () {
document.addEventListener("click", function onOut(evt) {
if (!wrap.contains(evt.target) && evt.target !== addBtn) {
document.removeEventListener("click", onOut);
cancelInput();
}
});
}, 0);
}
addBtn.addEventListener("click", function (evt) {
evt.stopPropagation();
openInput();
});
}
};
/* ======================================================================
GlassAvatarGroups
====================================================================== */
var GlassAvatarGroups = {
init: function () {
document
.querySelectorAll(".glass-avatar-group")
.forEach(function (group) {
var avatars = Array.from(group.querySelectorAll(".glass-avatar"));
avatars.forEach(function (av, i) {
av.style.zIndex = i + 1;
av.addEventListener("mouseenter", function () {
av.style.zIndex = avatars.length + 10;
});
av.addEventListener("mouseleave", function () {
av.style.zIndex = i + 1;
});
});
});
}
};
/* ======================================================================
GlassBreadcrumb
====================================================================== */
var GlassBreadcrumb = {
init: function () {
var wrap = document.getElementById("bc-ellipsis-wrap");
var btn = document.getElementById("bc-ellipsis");
if (!wrap || !btn) return;
function close() {
wrap.classList.remove("is-open");
btn.setAttribute("aria-expanded", "false");
}
btn.addEventListener("click", function (evt) {
evt.stopPropagation();
var open = wrap.classList.toggle("is-open");
btn.setAttribute("aria-expanded", String(open));
});
wrap.querySelector(".glass-breadcrumb-dropdown") &&
wrap
.querySelector(".glass-breadcrumb-dropdown")
.addEventListener("click", close);
document.addEventListener("click", function (evt) {
if (!wrap.contains(evt.target)) close();
});
document.addEventListener("keydown", function (evt) {
if (evt.key === "Escape") close();
});
}
};
/* ======================================================================
GlassTable — sort, search, row selection, bulk actions
====================================================================== */
function GlassTable(wrapEl) {
this.wrap = wrapEl;
this.table = wrapEl.querySelector(".glass-table");
this.tbody = wrapEl.querySelector("#tbl-body");
this.searchEl = wrapEl.querySelector("#tbl-search");
this.countEl = wrapEl.querySelector("#tbl-count");
this.bulkBar = wrapEl.querySelector("#tbl-bulk");
this.bulkCount = wrapEl.querySelector("#tbl-bulk-count");
this.deselect = wrapEl.querySelector("#tbl-deselect");
this.checkAll = wrapEl.querySelector("#tbl-check-all");
this.pageInfo = wrapEl.querySelector("#tbl-page-info");
this.sortCol = "name";
this.sortDir = "asc";
/* Cache all original rows as data objects */
this._rows = Array.from(this.tbody.querySelectorAll("tr")).map(function (
tr
) {
return {
el: tr,
id: tr.dataset.id,
name: tr.querySelector(".glass-table-member__name")
? tr.querySelector(".glass-table-member__name").textContent
: "",
role: tr.cells[2] ? tr.cells[2].textContent.trim() : "",
projects: parseInt(
tr.cells[4] ? tr.cells[4].textContent.trim() : "0",
10
),
score: parseInt(
tr.querySelector(".glass-table-score__num")
? tr.querySelector(".glass-table-score__num").textContent.trim()
: "0",
10
),
selected: false
};
});
this._filtered = this._rows.slice();
this._bind();
this._refreshCount();
}
GlassTable.prototype._bind = function () {
var self = this;
/* Column sort */
this.table.querySelectorAll("thead th[data-col]").forEach(function (th) {
th.addEventListener("click", function () {
var col = th.dataset.col;
if (self.sortCol === col) {
self.sortDir = self.sortDir === "asc" ? "desc" : "asc";
} else {
self.sortCol = col;
self.sortDir = "asc";
}
self._applySort();
self._updateSortUI();
});
});
/* Search */
if (this.searchEl) {
this.searchEl.addEventListener("input", function () {
self._applyFilter();
});
}
/* Row checkboxes */
this.tbody.addEventListener("change", function (evt) {
if (!evt.target.classList.contains("glass-table-check")) return;
var tr = evt.target.closest("tr");
var row = self._rows.find(function (r) {
return r.el === tr;
});
if (row) row.selected = evt.target.checked;
tr.classList.toggle("is-selected", evt.target.checked);
self._refreshBulk();
});
/* Select all */
if (this.checkAll) {
this.checkAll.addEventListener("change", function () {
var checked = self.checkAll.checked;
self._filtered.forEach(function (row) {
row.selected = checked;
row.el.classList.toggle("is-selected", checked);
var cb = row.el.querySelector(".glass-table-check");
if (cb) cb.checked = checked;
});
self._refreshBulk();
});
}
/* Deselect all */
if (this.deselect) {
this.deselect.addEventListener("click", function () {
self._rows.forEach(function (row) {
row.selected = false;
row.el.classList.remove("is-selected");
var cb = row.el.querySelector(".glass-table-check");
if (cb) cb.checked = false;
});
if (self.checkAll) self.checkAll.checked = false;
self._refreshBulk();
});
}
};
GlassTable.prototype._applyFilter = function () {
var query = this.searchEl ? this.searchEl.value.trim().toLowerCase() : "";
this._filtered = this._rows.filter(function (row) {
return (
!query ||
row.name.toLowerCase().includes(query) ||
row.role.toLowerCase().includes(query)
);
});
/* Show/hide rows */
this._rows.forEach(function (row) {
row.el.style.display = "none";
});
this._filtered.forEach(function (row) {
row.el.style.display = "";
});
this._refreshCount();
};
GlassTable.prototype._applySort = function () {
var col = this.sortCol;
var dir = this.sortDir;
this._filtered.sort(function (a, b) {
var av = a[col],
bv = b[col];
if (typeof av === "string") av = av.toLowerCase();
if (typeof bv === "string") bv = bv.toLowerCase();
if (av < bv) return dir === "asc" ? -1 : 1;
if (av > bv) return dir === "asc" ? 1 : -1;
return 0;
});
/* Reorder DOM */
var tbody = this.tbody;
this._filtered.forEach(function (row) {
tbody.appendChild(row.el);
});
/* Stagger re-entry */
this._filtered.forEach(function (row, i) {
row.el.style.animation = "none";
row.el.offsetHeight; /* force reflow */
row.el.style.animation = "cascade-in 200ms ease both";
row.el.style.animationDelay = i * 30 + "ms";
});
};
GlassTable.prototype._updateSortUI = function () {
var col = this.sortCol;
var dir = this.sortDir;
this.table.querySelectorAll("thead th[data-col]").forEach(function (th) {
var isSorted = th.dataset.col === col;
th.classList.toggle("is-sorted", isSorted);
th.classList.toggle("sort-desc", isSorted && dir === "desc");
var icon = th.querySelector(".glass-table__sort-icon");
if (icon) {
if (!isSorted) {
icon.textContent = "↕";
} else {
icon.textContent = dir === "asc" ? "↑" : "↓";
}
}
});
};
GlassTable.prototype._refreshBulk = function () {
var count = this._rows.filter(function (r) {
return r.selected;
}).length;
if (this.bulkBar) this.bulkBar.classList.toggle("is-visible", count > 0);
if (this.bulkCount) this.bulkCount.textContent = count + " selected";
/* Sync indeterminate state on select-all */
if (this.checkAll) {
var total = this._filtered.length;
this.checkAll.indeterminate = count > 0 && count < total;
this.checkAll.checked = count > 0 && count === total;
}
emit(this.wrap, "table:select", { count: count });
};
GlassTable.prototype._refreshCount = function () {
if (this.countEl) {
this.countEl.textContent =
this._filtered.length +
" member" +
(this._filtered.length !== 1 ? "s" : "");
}
if (this.pageInfo) {
this.pageInfo.textContent =
"Showing 1–" + this._filtered.length + " of " + this._rows.length;
}
};
GlassTable.init = function () {
var wrap = document.getElementById("data-table-wrap");
if (wrap) new GlassTable(wrap);
};
function boot() {
GlassTheme.init();
GlassModal.init();
GlassTabs.init();
GlassAccordion.init();
GlassDropdown.init();
GlassToast.init();
GlassStepper.init();
GlassNavTabs.init();
GlassToggle.init();
GlassRipple.init();
GlassParallax.init();
GlassChips.init();
GlassAvatarGroups.init();
GlassBreadcrumb.init();
GlassTable.init();
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", boot);
} else {
boot();
}
})();
/* ==========================================================================
LIQUID GLASS UI KIT
Following CSS Guidelines (cssguidelin.es) — BEM methodology,
single responsibility, progressive enhancement.
==========================================================================
TABLE OF CONTENTS
-----------------
1. Custom Properties — Complete Design Token System
2. Reset & Base
3. Light Theme Tokens
4. @supports — Graceful Degradation
5. prefers-reduced-motion
6. Animated Background Scene
7. Glass Utility Classes
8. Focus Visible
9. Component: Card
10. Component: Button
11. Component: Badge & Tag
12. Component: Input
13. Component: Checkbox & Radio
14. Component: Switch
15. Component: Navigation
16. Component: Tabs + Panels
17. Component: Accordion
18. Component: Modal / Dialog
19. Component: Toast / Snackbar
20. Component: Tooltip
21. Component: Dropdown / Select
22. Component: Chip
23. Component: Avatar Group
24. Component: Breadcrumb
25. Component: Skeleton Loader
26. Component: Stepper
27. Component: Data Table
28. 12 Liquid Glass Animations
29. Page Layout & Hero
30. Theme Toggle Button
31. Responsive
========================================================================== */
/* --------------------------------------------------------------------------
1. CUSTOM PROPERTIES — Complete Design Token System
-------------------------------------------------------------------------- */
:root {
/* ── Glass surface tints ── */
--glass-white: rgba(255, 255, 255, 0.12);
--glass-white-md: rgba(255, 255, 255, 0.2);
--glass-white-lg: rgba(255, 255, 255, 0.3);
--glass-dark: rgba(0, 0, 0, 0.18);
--glass-dark-md: rgba(0, 0, 0, 0.28);
/* ── Borders ── */
--glass-border: rgba(255, 255, 255, 0.25);
--glass-border-subtle: rgba(255, 255, 255, 0.12);
--glass-border-bright: rgba(255, 255, 255, 0.5);
/* ── Blur values ── */
--blur-sm: blur(8px);
--blur-md: blur(18px);
--blur-lg: blur(32px);
--blur-xl: blur(60px);
/* ── Shadows ── */
--shadow-glass: 0 8px 32px rgba(0, 0, 0, 0.28),
inset 0 1px 0 rgba(255, 255, 255, 0.3);
--shadow-float: 0 20px 60px rgba(0, 0, 0, 0.4), 0 4px 16px rgba(0, 0, 0, 0.2);
--shadow-glow: 0 0 40px rgba(120, 200, 255, 0.35);
/* ── Reflections ── */
--reflection-top: linear-gradient(
135deg,
rgba(255, 255, 255, 0.4) 0%,
rgba(255, 255, 255, 0) 50%
);
--reflection-inner: inset 0 1px 1px rgba(255, 255, 255, 0.45),
inset 0 -1px 1px rgba(0, 0, 0, 0.1);
/* ── Colour palette ── */
--accent-aqua: #5ee7df;
--accent-violet: #b490f5;
--accent-rose: #f7a8c4;
--accent-amber: #ffd27f;
--accent-lime: #a8f08a;
/* ── Semantic surface / text ── */
--color-bg: #0b0e1a;
--color-surface: rgba(255, 255, 255, 0.04);
--color-text: #ffffff;
--color-text-muted: rgba(255, 255, 255, 0.55);
--color-text-subtle: rgba(255, 255, 255, 0.35);
/* ── Spacing scale ── */
--space-2xs: 4px;
--space-xs: 8px;
--space-sm: 12px;
--space-md: 16px;
--space-lg: 24px;
--space-xl: 32px;
--space-2xl: 48px;
--space-3xl: 64px;
--space-4xl: 96px;
/* ── Type scale ── */
--text-2xs: 0.68rem;
--text-xs: 0.75rem;
--text-sm: 0.82rem;
--text-md: 0.9rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.3rem;
--text-2xl: 1.6rem;
--text-3xl: 2rem;
--text-4xl: 2.8rem;
/* ── Radius scale ── */
--radius-xs: 6px;
--radius-sm: 10px;
--radius-md: 14px;
--radius-lg: 20px;
--radius-xl: 24px;
--radius-2xl: 32px;
--radius-full: 9999px;
/* ── Z-index scale ── */
--z-base: 1;
--z-raised: 10;
--z-overlay: 100;
--z-modal: 500;
--z-toast: 800;
--z-tooltip: 900;
/* ── Easing tokens ── */
--ease-glass: cubic-bezier(0.22, 0.68, 0, 1.2);
--ease-liquid: cubic-bezier(0.34, 1.56, 0.64, 1);
--ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
/* ── Duration tokens ── */
--dur-fast: 180ms;
--dur-mid: 360ms;
--dur-slow: 600ms;
/* ── Typography ── */
--font-display: "Italiana", serif;
--font-body: "DM Sans", sans-serif;
}
/* --------------------------------------------------------------------------
3. LIGHT THEME TOKENS
-------------------------------------------------------------------------- */
[data-theme="light"] {
/* ── Surfaces ── */
--glass-white: rgba(255, 255, 255, 0.62);
--glass-white-md: rgba(255, 255, 255, 0.78);
--glass-white-lg: rgba(255, 255, 255, 0.9);
--glass-dark: rgba(0, 0, 0, 0.06);
--glass-dark-md: rgba(15, 23, 42, 0.08);
/* ── Borders — dark on light bg (white borders are invisible) ── */
--glass-border: rgba(15, 23, 42, 0.14);
--glass-border-subtle: rgba(15, 23, 42, 0.09);
--glass-border-bright: rgba(15, 23, 42, 0.22);
/* ── Shadows ── */
--shadow-glass: 0 8px 32px rgba(0, 0, 0, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.9);
--shadow-float: 0 20px 60px rgba(0, 0, 0, 0.16),
0 4px 16px rgba(0, 0, 0, 0.08);
/* ── Background ── */
--color-bg: #e8f0fa;
--color-surface: rgba(255, 255, 255, 0.55);
--color-text: #0f172a;
--color-text-muted: rgba(15, 23, 42, 0.58);
--color-text-subtle: rgba(15, 23, 42, 0.38);
/* ── Reflections ── */
--reflection-top: linear-gradient(
135deg,
rgba(255, 255, 255, 0.65) 0%,
rgba(255, 255, 255, 0) 50%
);
--reflection-inner: inset 0 1px 1px rgba(255, 255, 255, 0.95),
inset 0 -1px 1px rgba(0, 0, 0, 0.04);
/* ── Accent colours — darkened for WCAG AA on light backgrounds ── */
--accent-aqua: #0891b2; /* cyan-600 — was #5ee7df */
--accent-violet: #6d28d9; /* violet-700 — was #b490f5 */
--accent-rose: #be185d; /* pink-700 — was #f7a8c4 */
--accent-amber: #b45309; /* amber-700 — was #ffd27f */
--accent-lime: #15803d; /* green-700 — was #a8f08a */
}
/* --------------------------------------------------------------------------
4. @supports — Graceful Degradation
When backdrop-filter is unsupported (older Firefox / Edge),
fall back to a solid-ish semi-transparent surface.
-------------------------------------------------------------------------- */
@supports not (backdrop-filter: blur(1px)) {
.glass {
background: rgba(20, 25, 45, 0.88) !important;
border-color: rgba(255, 255, 255, 0.15) !important;
}
[data-theme="light"] .glass {
background: rgba(255, 255, 255, 0.92) !important;
border-color: rgba(15, 23, 42, 0.18) !important;
}
.glass-modal-backdrop {
background: rgba(0, 0, 0, 0.7) !important;
}
}
/* --------------------------------------------------------------------------
5. prefers-reduced-motion — legal requirement in many markets
-------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
.scene__blob {
animation: none !important;
}
.hero__title,
.hero__kicker,
.hero__sub,
.hero__cta,
.stats,
.section {
animation: none !important;
opacity: 1 !important;
transform: none !important;
}
}
/* --------------------------------------------------------------------------
2. RESET & BASE
-------------------------------------------------------------------------- */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth;
-webkit-font-smoothing: antialiased;
}
body {
font-family: var(--font-body);
font-weight: 300;
color: var(--color-text);
background: var(--color-bg);
overflow-x: hidden;
min-height: 100vh;
transition: background var(--dur-mid) var(--ease-smooth),
color var(--dur-mid) var(--ease-smooth);
}
/* --------------------------------------------------------------------------
6. ANIMATED BACKGROUND SCENE
-------------------------------------------------------------------------- */
.scene {
position: fixed;
inset: 0;
z-index: 0;
overflow: hidden;
pointer-events: none;
}
.scene__blob {
position: absolute;
border-radius: 50%;
filter: blur(80px);
opacity: 0.55;
animation: blob-drift var(--dur, 18s) ease-in-out infinite alternate;
}
[data-theme="light"] .scene__blob {
opacity: 0.3;
}
.scene__blob--1 {
width: 700px;
height: 700px;
background: radial-gradient(circle, #5ee7df, #3b82f6);
top: -200px;
left: -150px;
--dur: 22s;
}
.scene__blob--2 {
width: 600px;
height: 600px;
background: radial-gradient(circle, #b490f5, #ec4899);
bottom: -200px;
right: -100px;
--dur: 17s;
animation-delay: -8s;
}
.scene__blob--3 {
width: 400px;
height: 400px;
background: radial-gradient(circle, #ffd27f, #f7a8c4);
top: 40%;
left: 50%;
--dur: 25s;
animation-delay: -13s;
}
@keyframes blob-drift {
0% {
transform: translate(0, 0) scale(1);
}
33% {
transform: translate(60px, -40px) scale(1.08);
}
66% {
transform: translate(-40px, 60px) scale(0.94);
}
100% {
transform: translate(30px, 30px) scale(1.04);
}
}
/* --------------------------------------------------------------------------
7. GLASS UTILITY CLASSES
-------------------------------------------------------------------------- */
.glass {
backdrop-filter: var(--blur-md);
-webkit-backdrop-filter: var(--blur-md);
background: var(--glass-white);
border: 1px solid var(--glass-border);
box-shadow: var(--shadow-glass);
position: relative;
overflow: hidden;
}
.glass::before {
content: "";
position: absolute;
inset: 0;
background: var(--reflection-top);
pointer-events: none;
border-radius: inherit;
z-index: 1;
}
.glass--dark {
background: var(--glass-dark-md);
border-color: rgba(255, 255, 255, 0.1);
}
.glass--bright {
background: var(--glass-white-lg);
border-color: var(--glass-border-bright);
}
.glass--frosted {
backdrop-filter: var(--blur-lg);
-webkit-backdrop-filter: var(--blur-lg);
background: rgba(255, 255, 255, 0.08);
}
.glass--liquid {
backdrop-filter: var(--blur-xl);
-webkit-backdrop-filter: var(--blur-xl);
background: rgba(255, 255, 255, 0.06);
border-color: rgba(255, 255, 255, 0.18);
}
/* --------------------------------------------------------------------------
8. FOCUS VISIBLE — Keyboard accessibility
-------------------------------------------------------------------------- */
:focus {
outline: none;
}
:focus-visible {
outline: 2px solid var(--accent-aqua);
outline-offset: 3px;
border-radius: var(--radius-xs);
}
.glass-btn:focus-visible,
.glass-nav__item:focus-visible,
.glass-toggle__opt:focus-visible {
outline: 2px solid var(--accent-aqua);
outline-offset: 2px;
}
.glass-input:focus-visible {
outline: none; /* handled by box-shadow below */
}
/* --------------------------------------------------------------------------
9. COMPONENT: CARD
-------------------------------------------------------------------------- */
.glass-card {
border-radius: var(--radius-xl);
padding: var(--space-xl);
transition: transform var(--dur-mid) var(--ease-glass),
box-shadow var(--dur-mid) var(--ease-glass), background var(--dur-mid);
}
.glass-card:hover {
transform: translateY(-6px) scale(1.01);
box-shadow: var(--shadow-float);
background: var(--glass-white-md);
}
.glass-card::after {
content: "";
position: absolute;
inset: -1px;
border-radius: inherit;
background: linear-gradient(
135deg,
rgba(255, 255, 255, 0.22) 0%,
rgba(255, 255, 255, 0) 40%,
rgba(255, 255, 255, 0.08) 100%
);
opacity: 0;
transition: opacity var(--dur-mid);
pointer-events: none;
z-index: 2;
}
.glass-card:hover::after {
opacity: 1;
}
.glass-card__label {
font-size: var(--text-2xs);
letter-spacing: 0.14em;
text-transform: uppercase;
opacity: 0.55;
margin-bottom: var(--space-xs);
}
.glass-card__title {
font-family: var(--font-display);
font-size: clamp(1.4rem, 2.5vw, 2rem);
line-height: 1.15;
margin-bottom: var(--space-sm);
}
.glass-card__body {
font-size: var(--text-md);
line-height: 1.65;
opacity: 0.75;
}
/* --------------------------------------------------------------------------
10. COMPONENT: BUTTON
-------------------------------------------------------------------------- */
.glass-btn {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-sm) var(--space-lg);
border-radius: var(--radius-full);
border: none;
cursor: pointer;
font-family: var(--font-body);
font-size: var(--text-sm);
font-weight: 500;
letter-spacing: 0.04em;
color: var(--color-text);
text-decoration: none;
transition: transform var(--dur-fast) var(--ease-liquid),
box-shadow var(--dur-fast), background var(--dur-fast);
}
.glass-btn::before {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
background: var(--reflection-top);
pointer-events: none;
}
.glass-btn:active {
transform: scale(0.96);
}
.glass-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
pointer-events: none;
}
.glass-btn--primary {
background: linear-gradient(
135deg,
rgba(94, 231, 223, 0.5) 0%,
rgba(59, 130, 246, 0.5) 100%
);
border: 1px solid rgba(94, 231, 223, 0.45);
box-shadow: 0 4px 24px rgba(94, 231, 223, 0.3), var(--reflection-inner);
}
.glass-btn--primary:hover {
background: linear-gradient(
135deg,
rgba(94, 231, 223, 0.7) 0%,
rgba(59, 130, 246, 0.7) 100%
);
box-shadow: 0 8px 36px rgba(94, 231, 223, 0.5), var(--reflection-inner);
transform: translateY(-2px);
}
.glass-btn--ghost {
background: var(--glass-white);
border: 1px solid var(--glass-border);
box-shadow: var(--shadow-glass);
}
.glass-btn--ghost:hover {
background: var(--glass-white-md);
transform: translateY(-2px);
}
.glass-btn--accent {
background: linear-gradient(
135deg,
rgba(180, 144, 245, 0.5) 0%,
rgba(247, 168, 196, 0.5) 100%
);
border: 1px solid rgba(180, 144, 245, 0.45);
box-shadow: 0 4px 24px rgba(180, 144, 245, 0.3), var(--reflection-inner);
}
.glass-btn--accent:hover {
transform: translateY(-2px);
box-shadow: 0 8px 40px rgba(180, 144, 245, 0.5), var(--reflection-inner);
}
.glass-btn--danger {
background: linear-gradient(
135deg,
rgba(239, 68, 68, 0.45) 0%,
rgba(220, 38, 38, 0.45) 100%
);
border: 1px solid rgba(239, 68, 68, 0.4);
box-shadow: 0 4px 24px rgba(239, 68, 68, 0.25), var(--reflection-inner);
}
.glass-btn--danger:hover {
transform: translateY(-2px);
box-shadow: 0 8px 36px rgba(239, 68, 68, 0.45), var(--reflection-inner);
}
.glass-btn--icon {
width: 48px;
height: 48px;
padding: 0;
justify-content: center;
line-height: 1;
}
.glass-btn--sm {
padding: var(--space-2xs) var(--space-md);
font-size: var(--text-xs);
}
.glass-btn--lg {
padding: var(--space-md) var(--space-xl);
font-size: var(--text-base);
}
/* --------------------------------------------------------------------------
11. COMPONENT: BADGE & TAG
-------------------------------------------------------------------------- */
.glass-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 14px;
border-radius: var(--radius-full);
font-size: var(--text-2xs);
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.glass-badge--aqua {
background: rgba(94, 231, 223, 0.2);
border: 1px solid rgba(94, 231, 223, 0.4);
color: var(--accent-aqua);
box-shadow: 0 0 12px rgba(94, 231, 223, 0.18);
}
.glass-badge--violet {
background: rgba(180, 144, 245, 0.2);
border: 1px solid rgba(180, 144, 245, 0.4);
color: var(--accent-violet);
box-shadow: 0 0 12px rgba(180, 144, 245, 0.18);
}
.glass-badge--rose {
background: rgba(247, 168, 196, 0.2);
border: 1px solid rgba(247, 168, 196, 0.4);
color: var(--accent-rose);
}
.glass-badge--amber {
background: rgba(255, 210, 127, 0.2);
border: 1px solid rgba(255, 210, 127, 0.4);
color: var(--accent-amber);
}
.glass-badge--lime {
background: rgba(168, 240, 138, 0.2);
border: 1px solid rgba(168, 240, 138, 0.4);
color: var(--accent-lime);
}
.glass-badge--error {
background: rgba(239, 68, 68, 0.2);
border: 1px solid rgba(239, 68, 68, 0.4);
color: #f87171;
}
.glass-badge__dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
animation: pulse-badge 2s ease infinite;
}
@keyframes pulse-badge {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.4;
transform: scale(0.7);
}
}
.glass-tag {
display: inline-block;
padding: 6px 16px;
border-radius: var(--radius-sm);
font-size: var(--text-xs);
font-weight: 400;
background: var(--glass-white);
border: 1px solid var(--glass-border-subtle);
backdrop-filter: var(--blur-sm);
-webkit-backdrop-filter: var(--blur-sm);
transition: transform var(--dur-fast) var(--ease-liquid),
background var(--dur-fast);
cursor: default;
}
.glass-tag:hover {
background: var(--glass-white-md);
transform: translateY(-2px);
}
/* --------------------------------------------------------------------------
12. COMPONENT: INPUT
-------------------------------------------------------------------------- */
.glass-input-wrap {
position: relative;
display: flex;
align-items: center;
}
.glass-input {
width: 100%;
padding: var(--space-sm) var(--space-md);
border-radius: var(--radius-md);
border: 1px solid var(--glass-border-subtle);
background: var(--glass-white);
backdrop-filter: var(--blur-sm);
-webkit-backdrop-filter: var(--blur-sm);
color: var(--color-text);
font-family: var(--font-body);
font-size: var(--text-md);
outline: none;
transition: border-color var(--dur-fast), box-shadow var(--dur-fast),
background var(--dur-fast);
}
.glass-input::placeholder {
opacity: 0.45;
}
.glass-input:focus {
border-color: rgba(94, 231, 223, 0.6);
background: var(--glass-white-md);
box-shadow: 0 0 0 3px rgba(94, 231, 223, 0.15), var(--shadow-glass);
}
.glass-input[aria-invalid="true"] {
border-color: rgba(239, 68, 68, 0.6);
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.15);
}
.glass-input-icon {
position: absolute;
right: 16px;
opacity: 0.5;
pointer-events: none;
font-size: 1rem;
}
/* Textarea */
.glass-textarea {
width: 100%;
padding: var(--space-sm) var(--space-md);
border-radius: var(--radius-md);
border: 1px solid var(--glass-border-subtle);
background: var(--glass-white);
backdrop-filter: var(--blur-sm);
-webkit-backdrop-filter: var(--blur-sm);
color: var(--color-text);
font-family: var(--font-body);
font-size: var(--text-md);
outline: none;
resize: vertical;
min-height: 100px;
transition: border-color var(--dur-fast), box-shadow var(--dur-fast),
background var(--dur-fast);
}
.glass-textarea::placeholder {
opacity: 0.45;
}
.glass-textarea:focus {
border-color: rgba(94, 231, 223, 0.6);
background: var(--glass-white-md);
box-shadow: 0 0 0 3px rgba(94, 231, 223, 0.15), var(--shadow-glass);
}
/* Select */
.glass-select {
width: 100%;
padding: var(--space-sm) 40px var(--space-sm) var(--space-md);
border-radius: var(--radius-md);
border: 1px solid var(--glass-border-subtle);
background: var(--glass-white);
backdrop-filter: var(--blur-sm);
-webkit-backdrop-filter: var(--blur-sm);
color: var(--color-text);
font-family: var(--font-body);
font-size: var(--text-md);
outline: none;
appearance: none;
-webkit-appearance: none;
cursor: pointer;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='rgba(255,255,255,0.5)' stroke-width='2' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 14px center;
transition: border-color var(--dur-fast), box-shadow var(--dur-fast),
background-color var(--dur-fast);
}
.glass-select:focus {
border-color: rgba(94, 231, 223, 0.6);
box-shadow: 0 0 0 3px rgba(94, 231, 223, 0.15), var(--shadow-glass);
}
.glass-select option {
background: #1a1f35;
color: #fff;
}
[data-theme="light"] .glass-select option {
background: #dce8f5;
color: #0f172a;
}
[data-theme="light"] .glass-select {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='rgba(15,23,42,0.5)' stroke-width='2' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
}
.glass-field-error {
font-size: var(--text-xs);
color: #f87171;
margin-top: var(--space-2xs);
display: flex;
align-items: center;
gap: 4px;
}
/* --------------------------------------------------------------------------
13. COMPONENT: CHECKBOX & RADIO
-------------------------------------------------------------------------- */
.glass-check-group {
display: flex;
flex-direction: column;
gap: var(--space-sm);
}
.glass-check-label {
display: inline-flex;
align-items: center;
gap: var(--space-sm);
font-size: var(--text-sm);
cursor: pointer;
user-select: none;
color: var(--color-text);
}
/* Hide native input */
.glass-check-label input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
/* Custom box */
.glass-check-box {
width: 20px;
height: 20px;
border-radius: var(--radius-xs);
border: 1px solid var(--glass-border);
background: var(--glass-white);
backdrop-filter: var(--blur-sm);
-webkit-backdrop-filter: var(--blur-sm);
display: grid;
place-items: center;
flex-shrink: 0;
transition: background var(--dur-fast), border-color var(--dur-fast),
box-shadow var(--dur-fast), transform var(--dur-fast) var(--ease-liquid);
}
.glass-check-box::after {
content: "✓";
font-size: 0.75rem;
color: var(--accent-aqua);
opacity: 0;
transform: scale(0.5);
transition: opacity var(--dur-fast),
transform var(--dur-fast) var(--ease-liquid);
}
.glass-check-label input:checked + .glass-check-box {
background: rgba(94, 231, 223, 0.25);
border-color: rgba(94, 231, 223, 0.65);
box-shadow: 0 0 14px rgba(94, 231, 223, 0.22), var(--reflection-inner);
}
.glass-check-label input:checked + .glass-check-box::after {
opacity: 1;
transform: scale(1);
}
.glass-check-label input:focus-visible + .glass-check-box {
outline: 2px solid var(--accent-aqua);
outline-offset: 2px;
}
.glass-check-label:hover .glass-check-box {
transform: scale(1.08);
}
/* Radio variant */
.glass-radio-box {
width: 20px;
height: 20px;
border-radius: 50%;
border: 1px solid var(--glass-border);
background: var(--glass-white);
backdrop-filter: var(--blur-sm);
-webkit-backdrop-filter: var(--blur-sm);
display: grid;
place-items: center;
flex-shrink: 0;
transition: background var(--dur-fast), border-color var(--dur-fast),
box-shadow var(--dur-fast), transform var(--dur-fast) var(--ease-liquid);
}
.glass-radio-box::after {
content: "";
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--accent-aqua);
opacity: 0;
transform: scale(0.3);
transition: opacity var(--dur-fast),
transform var(--dur-fast) var(--ease-liquid);
}
.glass-check-label input:checked + .glass-radio-box {
background: rgba(94, 231, 223, 0.18);
border-color: rgba(94, 231, 223, 0.65);
box-shadow: 0 0 14px rgba(94, 231, 223, 0.22);
}
.glass-check-label input:checked + .glass-radio-box::after {
opacity: 1;
transform: scale(1);
}
.glass-check-label input:focus-visible + .glass-radio-box {
outline: 2px solid var(--accent-aqua);
outline-offset: 2px;
}
/* --------------------------------------------------------------------------
14. COMPONENT: SWITCH
-------------------------------------------------------------------------- */
.glass-switch-label {
display: inline-flex;
align-items: center;
gap: var(--space-sm);
cursor: pointer;
user-select: none;
font-size: var(--text-sm);
color: var(--color-text);
}
.glass-switch-label input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.glass-switch-track {
position: relative;
width: 44px;
height: 24px;
border-radius: var(--radius-full);
border: 1px solid var(--glass-border);
background: var(--glass-white);
backdrop-filter: var(--blur-sm);
-webkit-backdrop-filter: var(--blur-sm);
transition: background var(--dur-fast), border-color var(--dur-fast),
box-shadow var(--dur-fast);
flex-shrink: 0;
}
.glass-switch-thumb {
position: absolute;
top: 3px;
left: 3px;
width: 16px;
height: 16px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.65);
border: 1px solid rgba(255, 255, 255, 0.35);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
transition: transform var(--dur-fast) var(--ease-liquid),
background var(--dur-fast);
}
.glass-switch-label input:checked + .glass-switch-track {
background: rgba(94, 231, 223, 0.3);
border-color: rgba(94, 231, 223, 0.65);
box-shadow: 0 0 14px rgba(94, 231, 223, 0.22), var(--reflection-inner);
}
.glass-switch-label input:checked + .glass-switch-track .glass-switch-thumb {
transform: translateX(20px);
background: var(--accent-aqua);
}
.glass-switch-label input:focus-visible + .glass-switch-track {
outline: 2px solid var(--accent-aqua);
outline-offset: 2px;
}
/* --------------------------------------------------------------------------
15. COMPONENT: NAVIGATION
-------------------------------------------------------------------------- */
.glass-nav {
display: flex;
align-items: center;
gap: 4px;
padding: var(--space-xs);
border-radius: var(--radius-full);
}
.glass-nav__item {
padding: 9px 22px;
border-radius: var(--radius-full);
font-size: var(--text-sm);
font-weight: 400;
opacity: 0.65;
cursor: pointer;
transition: background var(--dur-fast), opacity var(--dur-fast),
transform var(--dur-fast) var(--ease-liquid);
}
.glass-nav__item:hover {
opacity: 1;
transform: translateY(-1px);
}
.glass-nav__item--active {
background: var(--glass-white-md);
border: 1px solid var(--glass-border);
opacity: 1;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);
}
/* --------------------------------------------------------------------------
16. COMPONENT: TABS + PANELS
-------------------------------------------------------------------------- */
.glass-tabs {
display: flex;
flex-direction: column;
gap: var(--space-md);
}
.glass-tab-list {
display: flex;
align-items: center;
gap: 4px;
padding: var(--space-2xs);
border-radius: var(--radius-full);
overflow-x: auto;
-ms-overflow-style: none;
scrollbar-width: none;
}
.glass-tab-list::-webkit-scrollbar {
display: none;
}
.glass-tab {
padding: 9px 22px;
border-radius: var(--radius-full);
font-size: var(--text-sm);
font-weight: 400;
opacity: 0.65;
cursor: pointer;
border: none;
background: transparent;
color: var(--color-text);
font-family: var(--font-body);
white-space: nowrap;
transition: background var(--dur-fast), opacity var(--dur-fast),
transform var(--dur-fast) var(--ease-liquid);
}
.glass-tab:hover {
opacity: 1;
transform: translateY(-1px);
background: var(--glass-white);
}
.glass-tab[aria-selected="true"] {
background: var(--glass-white-md);
border: 1px solid var(--glass-border);
opacity: 1;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);
}
.glass-tab-panel {
display: none;
animation: cascade-in var(--dur-mid) var(--ease-glass) both;
}
.glass-tab-panel.is-active {
display: block;
}
/* --------------------------------------------------------------------------
17. COMPONENT: ACCORDION
-------------------------------------------------------------------------- */
.glass-accordion {
display: flex;
flex-direction: column;
gap: var(--space-xs);
}
.glass-accordion-item {
border-radius: var(--radius-lg);
overflow: hidden;
transition: box-shadow var(--dur-fast);
}
.glass-accordion-trigger {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-md) var(--space-lg);
font-family: var(--font-body);
font-size: var(--text-sm);
font-weight: 500;
color: var(--color-text);
cursor: pointer;
border: none;
background: transparent;
text-align: left;
transition: background var(--dur-fast);
}
.glass-accordion-trigger:hover {
background: var(--glass-white);
}
.glass-accordion-icon {
width: 24px;
height: 24px;
border-radius: 50%;
border: 1px solid var(--glass-border);
background: var(--glass-white);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.65rem;
line-height: 1;
flex-shrink: 0;
transition: transform var(--dur-mid) var(--ease-liquid),
background var(--dur-fast);
}
.glass-accordion-item.is-open .glass-accordion-icon {
transform: rotate(180deg);
background: rgba(94, 231, 223, 0.2);
border-color: rgba(94, 231, 223, 0.5);
}
.glass-accordion-body {
overflow: hidden;
max-height: 0;
transition: max-height var(--dur-mid) var(--ease-smooth),
padding var(--dur-mid) var(--ease-smooth);
}
.glass-accordion-item.is-open .glass-accordion-body {
max-height: 400px;
}
.glass-accordion-content {
padding: 0 var(--space-lg) var(--space-md);
font-size: var(--text-sm);
line-height: 1.7;
opacity: 0.75;
}
/* --------------------------------------------------------------------------
18. COMPONENT: MODAL / DIALOG
-------------------------------------------------------------------------- */
.glass-modal-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
display: grid;
place-items: center;
z-index: var(--z-modal);
opacity: 0;
pointer-events: none;
transition: opacity var(--dur-mid);
padding: var(--space-md);
}
.glass-modal-backdrop.is-open {
opacity: 1;
pointer-events: all;
}
/* The modal itself gets its own opaque tinted surface — NOT using .glass
directly so it isn't transparent like other glass panels. */
.glass-modal {
border-radius: var(--radius-2xl);
padding: 0;
width: min(500px, 90vw);
transform: translateY(24px) scale(0.96);
transition: transform var(--dur-slow) var(--ease-liquid),
opacity var(--dur-slow);
opacity: 0;
overflow: hidden;
/* Dark: deep navy with enough opacity to read clearly */
background: rgba(16, 20, 40, 0.92);
border: 1px solid rgba(255, 255, 255, 0.16);
box-shadow: 0 32px 80px rgba(0, 0, 0, 0.55), 0 8px 24px rgba(0, 0, 0, 0.35),
inset 0 1px 0 rgba(255, 255, 255, 0.12);
backdrop-filter: var(--blur-lg);
-webkit-backdrop-filter: var(--blur-lg);
position: relative;
}
/* Top accent bar — thin gradient stripe across the header */
.glass-modal::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(
90deg,
var(--accent-aqua) 0%,
var(--accent-violet) 50%,
var(--accent-rose) 100%
);
z-index: 10;
pointer-events: none;
}
.glass-modal-backdrop.is-open .glass-modal {
transform: translateY(0) scale(1);
opacity: 1;
}
/* Header area — slightly lighter strip */
.glass-modal__header {
padding: var(--space-xl) var(--space-xl) var(--space-md);
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
background: rgba(255, 255, 255, 0.04);
}
/* Body area */
.glass-modal__body {
padding: var(--space-xl);
}
/* Footer actions */
.glass-modal__footer {
padding: var(--space-md) var(--space-xl) var(--space-xl);
display: flex;
gap: var(--space-sm);
}
.glass-modal__close {
position: absolute;
top: var(--space-md);
right: var(--space-md);
width: 32px;
height: 32px;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.15);
background: rgba(255, 255, 255, 0.08);
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.8rem;
line-height: 1;
z-index: 20;
transition: background var(--dur-fast), color var(--dur-fast),
transform var(--dur-fast) var(--ease-liquid);
}
.glass-modal__close:hover {
background: rgba(255, 255, 255, 0.16);
color: #fff;
transform: rotate(90deg) scale(1.1);
}
/* Light mode modal overrides */
[data-theme="light"] .glass-modal-backdrop {
background: rgba(15, 23, 42, 0.45);
}
[data-theme="light"] .glass-modal {
background: rgba(255, 255, 255, 0.96);
border-color: rgba(15, 23, 42, 0.12);
box-shadow: 0 32px 80px rgba(0, 0, 0, 0.18), 0 8px 24px rgba(0, 0, 0, 0.1),
inset 0 1px 0 rgba(255, 255, 255, 0.95);
}
[data-theme="light"] .glass-modal__header {
border-bottom-color: rgba(15, 23, 42, 0.08);
background: rgba(15, 23, 42, 0.02);
}
[data-theme="light"] .glass-modal__close {
border-color: rgba(15, 23, 42, 0.14);
background: rgba(15, 23, 42, 0.06);
color: rgba(15, 23, 42, 0.55);
}
[data-theme="light"] .glass-modal__close:hover {
background: rgba(15, 23, 42, 0.12);
color: #0f172a;
}
/* --------------------------------------------------------------------------
19. COMPONENT: TOAST / SNACKBAR
-------------------------------------------------------------------------- */
.glass-toast-region {
position: fixed;
bottom: var(--space-lg);
right: var(--space-lg);
z-index: var(--z-toast);
display: flex;
flex-direction: column;
gap: var(--space-xs);
pointer-events: none;
width: min(360px, 90vw);
}
.glass-toast {
border-radius: var(--radius-lg);
padding: var(--space-md) var(--space-lg);
display: flex;
align-items: center;
gap: var(--space-sm);
pointer-events: all;
cursor: pointer;
animation: toast-in var(--dur-mid) var(--ease-liquid) both;
transition: transform var(--dur-fast) var(--ease-liquid),
opacity var(--dur-fast);
}
.glass-toast:hover {
transform: translateX(-4px);
}
.glass-toast.is-exiting {
animation: toast-out var(--dur-mid) var(--ease-glass) forwards;
}
@keyframes toast-in {
from {
opacity: 0;
transform: translateX(60px) scale(0.95);
}
to {
opacity: 1;
transform: translateX(0) scale(1);
}
}
@keyframes toast-out {
from {
opacity: 1;
transform: translateX(0) scale(1);
max-height: 80px;
}
to {
opacity: 0;
transform: translateX(60px) scale(0.9);
max-height: 0;
padding: 0;
margin: 0;
}
}
.glass-toast__icon {
font-size: 1.2rem;
flex-shrink: 0;
}
.glass-toast__body {
flex: 1;
}
.glass-toast__title {
font-size: var(--text-sm);
font-weight: 500;
}
.glass-toast__desc {
font-size: var(--text-xs);
opacity: 0.65;
margin-top: 2px;
}
.glass-toast__close {
opacity: 0.5;
font-size: 0.75rem;
flex-shrink: 0;
transition: opacity var(--dur-fast);
}
.glass-toast:hover .glass-toast__close {
opacity: 1;
}
.glass-toast--success {
border-color: rgba(74, 222, 128, 0.4) !important;
box-shadow: 0 4px 24px rgba(74, 222, 128, 0.2) !important;
}
.glass-toast--error {
border-color: rgba(239, 68, 68, 0.4) !important;
box-shadow: 0 4px 24px rgba(239, 68, 68, 0.2) !important;
}
.glass-toast--warning {
border-color: rgba(251, 191, 36, 0.4) !important;
box-shadow: 0 4px 24px rgba(251, 191, 36, 0.2) !important;
}
.glass-toast--info {
border-color: rgba(94, 231, 223, 0.4) !important;
box-shadow: 0 4px 24px rgba(94, 231, 223, 0.2) !important;
}
/* Toast demo buttons */
.toast-demo-row {
display: flex;
flex-wrap: wrap;
gap: var(--space-sm);
}
/* --------------------------------------------------------------------------
20. COMPONENT: TOOLTIP
-------------------------------------------------------------------------- */
.glass-tooltip-wrap {
position: relative;
display: inline-flex;
}
.glass-tooltip {
position: absolute;
bottom: calc(100% + 10px);
left: 50%;
transform: translateX(-50%) translateY(6px);
padding: 8px 14px;
border-radius: var(--radius-sm);
font-size: var(--text-xs);
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity var(--dur-fast),
transform var(--dur-fast) var(--ease-liquid);
z-index: var(--z-tooltip);
}
.glass-tooltip::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 5px solid transparent;
border-top-color: rgba(255, 255, 255, 0.22);
}
.glass-tooltip-wrap:hover .glass-tooltip {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
/* --------------------------------------------------------------------------
21. COMPONENT: DROPDOWN / SELECT (Custom)
-------------------------------------------------------------------------- */
.glass-dropdown {
position: relative;
display: inline-block;
}
.glass-dropdown-menu {
position: absolute;
top: calc(100% + 8px);
left: 0;
min-width: 200px;
border-radius: var(--radius-lg);
padding: var(--space-2xs);
pointer-events: none;
opacity: 0;
transform: translateY(-8px) scale(0.97);
transition: opacity var(--dur-fast) var(--ease-smooth),
transform var(--dur-fast) var(--ease-liquid);
z-index: var(--z-overlay);
overflow: hidden;
}
.glass-dropdown.is-open .glass-dropdown-menu {
opacity: 1;
transform: translateY(0) scale(1);
pointer-events: all;
}
.glass-dropdown-item {
display: flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-sm) var(--space-md);
border-radius: var(--radius-sm);
font-size: var(--text-sm);
cursor: pointer;
transition: background var(--dur-fast);
color: var(--color-text);
font-family: var(--font-body);
border: none;
background: transparent;
width: 100%;
text-align: left;
}
.glass-dropdown-item:hover {
background: var(--glass-white-md);
}
.glass-dropdown-item.is-active {
background: rgba(94, 231, 223, 0.18);
color: var(--accent-aqua);
}
.glass-dropdown-item--danger {
color: #f87171;
}
.glass-dropdown-item--danger:hover {
background: rgba(239, 68, 68, 0.15);
}
.glass-dropdown-divider {
height: 1px;
background: var(--glass-border-subtle);
margin: var(--space-2xs) 0;
}
.glass-dropdown-trigger {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
}
.glass-dropdown-trigger-arrow {
font-size: 0.65rem;
opacity: 0.55;
transition: transform var(--dur-fast) var(--ease-liquid);
}
.glass-dropdown.is-open .glass-dropdown-trigger-arrow {
transform: rotate(180deg);
}
/* --------------------------------------------------------------------------
22. COMPONENT: CHIP
-------------------------------------------------------------------------- */
.glass-chip {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
padding: 7px 14px 7px 10px;
border-radius: var(--radius-full);
font-size: var(--text-sm);
font-weight: 400;
letter-spacing: 0.02em;
cursor: pointer;
user-select: none;
transition: background var(--dur-fast), border-color var(--dur-fast),
box-shadow var(--dur-fast), transform var(--dur-fast) var(--ease-liquid),
opacity var(--dur-fast), max-width var(--dur-mid) var(--ease-liquid),
padding var(--dur-mid) var(--ease-liquid);
}
.glass-chip__icon {
width: 22px;
height: 22px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.85rem;
line-height: 1;
flex-shrink: 0;
background: rgba(255, 255, 255, 0.15);
border: 1px solid rgba(255, 255, 255, 0.2);
overflow: hidden;
}
.glass-chip__icon img {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
}
.glass-chip__label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.glass-chip__dismiss {
width: 18px;
height: 18px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.12);
border: 1px solid rgba(255, 255, 255, 0.18);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.6rem;
line-height: 1;
flex-shrink: 0;
cursor: pointer;
transition: background var(--dur-fast),
transform var(--dur-fast) var(--ease-liquid);
}
.glass-chip__dismiss:hover {
background: rgba(255, 255, 255, 0.28);
transform: scale(1.2) rotate(90deg);
}
.glass-chip:hover {
background: var(--glass-white-md);
transform: translateY(-2px);
}
.glass-chip.is-selected {
background: linear-gradient(
135deg,
rgba(94, 231, 223, 0.28) 0%,
rgba(180, 144, 245, 0.28) 100%
);
border-color: rgba(94, 231, 223, 0.55);
box-shadow: 0 0 16px rgba(94, 231, 223, 0.22), var(--reflection-inner);
}
.glass-chip--aqua {
background: rgba(94, 231, 223, 0.14);
border: 1px solid rgba(94, 231, 223, 0.3);
color: var(--accent-aqua);
}
.glass-chip--violet {
background: rgba(180, 144, 245, 0.14);
border: 1px solid rgba(180, 144, 245, 0.3);
color: var(--accent-violet);
}
.glass-chip--rose {
background: rgba(247, 168, 196, 0.14);
border: 1px solid rgba(247, 168, 196, 0.3);
color: var(--accent-rose);
}
.glass-chip--amber {
background: rgba(255, 210, 127, 0.14);
border: 1px solid rgba(255, 210, 127, 0.3);
color: var(--accent-amber);
}
@keyframes chip-exit {
0% {
opacity: 1;
transform: scale(1);
max-width: 220px;
}
40% {
opacity: 0;
transform: scale(0.85);
}
100% {
opacity: 0;
transform: scale(0.8);
max-width: 0;
padding: 0;
margin: 0;
}
}
@keyframes chip-enter {
0% {
opacity: 0;
transform: scale(0.72) translateY(4px);
}
100% {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.glass-chip.is-exiting {
animation: chip-exit var(--dur-mid) var(--ease-glass) forwards;
pointer-events: none;
overflow: hidden;
}
.glass-chip.is-entering {
animation: chip-enter var(--dur-mid) var(--ease-liquid) both;
}
.glass-chip-group {
display: flex;
flex-wrap: wrap;
gap: var(--space-xs);
align-items: center;
}
.glass-chip-add {
width: 34px;
height: 34px;
border-radius: 50%;
border: 1px dashed var(--glass-border);
background: transparent;
color: var(--color-text);
display: grid;
place-items: center;
font-size: 1rem;
cursor: pointer;
opacity: 0.45;
transition: opacity var(--dur-fast), background var(--dur-fast),
transform var(--dur-fast) var(--ease-liquid);
}
.glass-chip-add:hover {
opacity: 1;
background: var(--glass-white);
transform: rotate(90deg) scale(1.1);
}
.glass-chip-input-wrap {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
padding: 5px 10px 5px 14px;
border-radius: var(--radius-full);
border: 1px solid rgba(94, 231, 223, 0.5);
background: rgba(94, 231, 223, 0.1);
box-shadow: 0 0 14px rgba(94, 231, 223, 0.18), var(--reflection-inner);
animation: chip-enter var(--dur-mid) var(--ease-liquid) both;
}
.glass-chip-input {
background: none;
border: none;
outline: none;
color: var(--color-text);
font-family: var(--font-body);
font-size: var(--text-sm);
width: 90px;
caret-color: var(--accent-aqua);
}
.glass-chip-input::placeholder {
opacity: 0.4;
}
.glass-chip-input-confirm {
width: 20px;
height: 20px;
border-radius: 50%;
background: rgba(94, 231, 223, 0.25);
border: 1px solid rgba(94, 231, 223, 0.45);
color: var(--accent-aqua);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.6rem;
line-height: 1;
cursor: pointer;
flex-shrink: 0;
transition: background var(--dur-fast),
transform var(--dur-fast) var(--ease-liquid);
}
.glass-chip-input-confirm:hover {
background: rgba(94, 231, 223, 0.45);
transform: scale(1.15);
}
/* --------------------------------------------------------------------------
23. COMPONENT: AVATAR GROUP
-------------------------------------------------------------------------- */
.glass-avatar-group {
display: flex;
align-items: center;
padding: 70px 8px 16px;
margin-top: -60px;
}
.glass-avatar {
position: relative;
width: var(--avatar-size, 44px);
height: var(--avatar-size, 44px);
border-radius: 50%;
border: 2.5px solid rgba(255, 255, 255, 0.3);
background: linear-gradient(135deg, var(--from, #5ee7df), var(--to, #b490f5));
display: grid;
place-items: center;
font-size: calc(var(--avatar-size, 44px) * 0.38);
font-weight: 500;
overflow: visible;
cursor: pointer;
flex-shrink: 0;
margin-left: calc(var(--avatar-size, 44px) * -0.25);
transition: transform var(--dur-fast) var(--ease-liquid),
box-shadow var(--dur-fast);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35),
inset 0 1px 0 rgba(255, 255, 255, 0.35);
}
.glass-avatar:first-child {
margin-left: 0;
}
.glass-avatar__face {
width: 100%;
height: 100%;
border-radius: 50%;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
font-size: inherit;
line-height: 1;
}
.glass-avatar__face img {
width: 100%;
height: 100%;
object-fit: cover;
}
.glass-avatar:hover {
transform: translateY(-8px) scale(1.1);
z-index: 10;
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45),
inset 0 1px 0 rgba(255, 255, 255, 0.45);
}
.glass-avatar--overflow {
background: var(--glass-white-md);
backdrop-filter: var(--blur-sm);
-webkit-backdrop-filter: var(--blur-sm);
border: 2px solid var(--glass-border);
font-size: calc(var(--avatar-size, 44px) * 0.3);
font-weight: 500;
letter-spacing: -0.02em;
color: rgba(255, 255, 255, 0.75);
}
.glass-avatar--overflow:hover {
background: var(--glass-white-lg);
}
.glass-avatar--photo .glass-avatar__face {
font-size: calc(var(--avatar-size, 44px) * 0.6);
}
.glass-avatar-popover {
position: absolute;
bottom: calc(100% + 10px);
left: 50%;
transform: translateX(-50%) translateY(6px);
min-width: 120px;
border-radius: var(--radius-md);
padding: 10px 14px;
pointer-events: none;
opacity: 0;
white-space: nowrap;
font-size: var(--text-xs);
transition: opacity var(--dur-fast),
transform var(--dur-fast) var(--ease-liquid);
z-index: 50;
}
.glass-avatar:hover .glass-avatar-popover {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
.glass-avatar-popover--list {
min-width: 180px;
padding: 10px;
white-space: normal;
}
.glass-avatar-popover__list {
list-style: none;
display: flex;
flex-direction: column;
gap: 6px;
}
.glass-avatar-popover__item {
display: flex;
align-items: center;
gap: 8px;
font-size: var(--text-xs);
}
.glass-avatar-popover__mini {
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.6rem;
line-height: 1;
flex-shrink: 0;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.glass-avatar-group--sm .glass-avatar {
--avatar-size: 32px;
}
.glass-avatar-group--lg .glass-avatar {
--avatar-size: 56px;
}
.glass-avatar-group__meta {
margin-left: 14px;
font-size: var(--text-xs);
opacity: 0.55;
white-space: nowrap;
}
.glass-avatar__status {
position: absolute;
bottom: 1px;
right: 1px;
width: 10px;
height: 10px;
border-radius: 50%;
border: 2px solid var(--color-bg);
z-index: 2;
}
.glass-avatar__status--online {
background: #4ade80;
}
.glass-avatar__status--away {
background: var(--accent-amber);
}
.glass-avatar__status--offline {
background: rgba(255, 255, 255, 0.28);
}
/* --------------------------------------------------------------------------
24. COMPONENT: BREADCRUMB
-------------------------------------------------------------------------- */
.glass-breadcrumb {
display: flex;
align-items: center;
padding: 6px 8px;
border-radius: var(--radius-full);
flex-wrap: nowrap;
max-width: 100%;
overflow: visible;
}
.glass-breadcrumb__crumb {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 14px;
border-radius: var(--radius-full);
font-size: var(--text-sm);
white-space: nowrap;
cursor: pointer;
transition: background var(--dur-fast), color var(--dur-fast),
transform var(--dur-fast) var(--ease-liquid);
}
.glass-breadcrumb__crumb:hover {
background: rgba(255, 255, 255, 0.12);
transform: translateY(-1px);
}
.glass-breadcrumb__crumb--active {
background: var(--glass-white-md);
border: 1px solid var(--glass-border);
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);
cursor: default;
pointer-events: none;
}
.glass-breadcrumb__crumb--active:hover {
transform: none;
}
.glass-breadcrumb__crumb-icon {
font-size: 0.85rem;
opacity: 0.7;
}
.glass-breadcrumb__sep {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
flex-shrink: 0;
opacity: 0.25;
font-size: 0.65rem;
pointer-events: none;
user-select: none;
}
.glass-breadcrumb__ellipsis-wrap {
position: relative;
z-index: 300;
}
.glass-breadcrumb__ellipsis {
padding: 6px 10px;
border-radius: var(--radius-full);
font-size: var(--text-xs);
cursor: pointer;
opacity: 0.5;
transition: background var(--dur-fast), opacity var(--dur-fast);
}
.glass-breadcrumb__ellipsis:hover {
background: rgba(255, 255, 255, 0.12);
opacity: 1;
}
.glass-breadcrumb-dropdown {
position: absolute;
top: calc(100% + 8px);
left: 0;
min-width: 190px;
border-radius: var(--radius-lg);
padding: var(--space-2xs);
pointer-events: none;
opacity: 0;
transform: translateY(-6px) scale(0.97);
transition: opacity var(--dur-fast),
transform var(--dur-fast) var(--ease-liquid);
z-index: 400;
}
.glass-breadcrumb__ellipsis-wrap.is-open .glass-breadcrumb-dropdown {
opacity: 1;
transform: translateY(0) scale(1);
pointer-events: all;
}
.glass-breadcrumb-dropdown__item {
display: flex;
align-items: center;
gap: var(--space-xs);
padding: 9px 12px;
border-radius: var(--radius-sm);
font-size: var(--text-sm);
cursor: pointer;
transition: background var(--dur-fast);
}
.glass-breadcrumb-dropdown__item:hover {
background: rgba(255, 255, 255, 0.12);
}
.glass-breadcrumb--sm .glass-breadcrumb__crumb {
font-size: var(--text-2xs);
padding: 4px 10px;
}
.glass-breadcrumb--lg .glass-breadcrumb__crumb {
font-size: var(--text-base);
padding: 8px 18px;
}
/* --------------------------------------------------------------------------
25. COMPONENT: SKELETON LOADER
-------------------------------------------------------------------------- */
.skeleton {
position: relative;
overflow: hidden;
border-radius: var(--radius-sm);
background: var(--glass-white);
}
.skeleton::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.18) 40%,
rgba(255, 255, 255, 0.28) 50%,
rgba(255, 255, 255, 0.18) 60%,
rgba(255, 255, 255, 0) 100%
);
background-size: 200% auto;
animation: skeleton-shimmer 1.8s linear infinite;
}
@keyframes skeleton-shimmer {
0% {
background-position: -200% center;
}
100% {
background-position: 200% center;
}
}
.skeleton--text {
height: 16px;
}
.skeleton--heading {
height: 28px;
border-radius: var(--radius-xs);
}
.skeleton--circle {
border-radius: 50%;
}
.skeleton--card {
border-radius: var(--radius-xl);
}
.skeleton-layout {
display: flex;
flex-direction: column;
gap: var(--space-md);
padding: var(--space-xl);
border-radius: var(--radius-xl);
}
.skeleton-row {
display: flex;
align-items: center;
gap: var(--space-md);
}
.skeleton-lines {
flex: 1;
display: flex;
flex-direction: column;
gap: var(--space-xs);
}
/* --------------------------------------------------------------------------
26. COMPONENT: STEPPER
-------------------------------------------------------------------------- */
.glass-stepper {
display: flex;
align-items: center;
gap: 0;
}
.glass-step {
display: flex;
align-items: center;
gap: var(--space-xs);
flex: 1;
}
.glass-step:last-child {
flex: none;
}
.glass-step__connector {
flex: 1;
height: 1px;
margin: 0 var(--space-2xs);
background: var(--glass-border-subtle);
transition: background var(--dur-mid);
}
.glass-step.is-complete .glass-step__connector {
background: rgba(94, 231, 223, 0.5);
}
.glass-step__node {
width: 36px;
height: 36px;
border-radius: 50%;
border: 2px solid var(--glass-border);
background: var(--glass-white);
backdrop-filter: var(--blur-sm);
-webkit-backdrop-filter: var(--blur-sm);
display: flex;
align-items: center;
justify-content: center;
font-size: var(--text-xs);
font-weight: 500;
line-height: 1;
flex-shrink: 0;
transition: background var(--dur-mid) var(--ease-liquid),
border-color var(--dur-mid), box-shadow var(--dur-mid),
transform var(--dur-mid) var(--ease-liquid);
}
.glass-step.is-complete .glass-step__node {
background: rgba(94, 231, 223, 0.25);
border-color: rgba(94, 231, 223, 0.65);
box-shadow: 0 0 16px rgba(94, 231, 223, 0.25);
transform: scale(1.05);
}
.glass-step.is-active .glass-step__node {
background: rgba(180, 144, 245, 0.25);
border-color: rgba(180, 144, 245, 0.65);
box-shadow: 0 0 20px rgba(180, 144, 245, 0.3);
animation: step-pulse 2s ease infinite;
}
@keyframes step-pulse {
0%,
100% {
box-shadow: 0 0 12px rgba(180, 144, 245, 0.25);
}
50% {
box-shadow: 0 0 24px rgba(180, 144, 245, 0.5);
}
}
.glass-step__label {
font-size: var(--text-xs);
opacity: 0.55;
white-space: nowrap;
}
.glass-step.is-active .glass-step__label {
opacity: 1;
font-weight: 500;
}
.glass-step.is-complete .glass-step__label {
opacity: 0.7;
}
.glass-stepper--vertical {
flex-direction: column;
align-items: flex-start;
}
.glass-stepper--vertical .glass-step {
flex-direction: column;
align-items: flex-start;
flex: auto;
width: 100%;
}
.glass-stepper--vertical .glass-step__connector {
width: 2px;
height: 32px;
flex: none;
margin: var(--space-2xs) 0 var(--space-2xs) 17px;
}
.glass-stepper--vertical .glass-step__inner {
display: flex;
align-items: center;
gap: var(--space-sm);
}
/* --------------------------------------------------------------------------
27. COMPONENT: DATA TABLE
-------------------------------------------------------------------------- */
.glass-table-wrap {
overflow-x: auto;
border-radius: var(--radius-xl);
-webkit-overflow-scrolling: touch;
}
/* ── Table toolbar ── */
.glass-table-toolbar {
display: flex;
align-items: center;
gap: var(--space-md);
padding: var(--space-md) var(--space-lg);
border-bottom: 1px solid var(--glass-border-subtle);
flex-wrap: wrap;
}
.glass-table-toolbar__title {
font-family: var(--font-display);
font-size: var(--text-lg);
flex: 1;
min-width: 120px;
}
.glass-table-toolbar__count {
font-size: var(--text-xs);
opacity: 0.45;
white-space: nowrap;
}
.glass-table-search {
position: relative;
width: min(220px, 100%);
}
.glass-table-search__icon {
position: absolute;
left: 12px;
top: 50%;
transform: translateY(-50%);
opacity: 0.4;
font-size: 0.85rem;
pointer-events: none;
}
.glass-table-search__input {
width: 100%;
padding: 8px 12px 8px 34px;
border-radius: var(--radius-full);
border: 1px solid var(--glass-border-subtle);
background: var(--glass-white);
backdrop-filter: var(--blur-sm);
-webkit-backdrop-filter: var(--blur-sm);
color: var(--color-text);
font-family: var(--font-body);
font-size: var(--text-xs);
outline: none;
transition: border-color var(--dur-fast), box-shadow var(--dur-fast);
}
.glass-table-search__input::placeholder {
opacity: 0.4;
}
.glass-table-search__input:focus {
border-color: rgba(94, 231, 223, 0.55);
box-shadow: 0 0 0 3px rgba(94, 231, 223, 0.12);
}
[data-theme="light"] .glass-table-search__input {
background: rgba(255, 255, 255, 0.7);
border-color: rgba(15, 23, 42, 0.14);
color: #0f172a;
}
[data-theme="light"] .glass-table-search__input:focus {
border-color: rgba(8, 145, 178, 0.45);
box-shadow: 0 0 0 3px rgba(8, 145, 178, 0.12);
}
/* ── Table core ── */
.glass-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
font-size: var(--text-sm);
}
.glass-table thead th {
padding: var(--space-sm) var(--space-md);
text-align: left;
font-size: var(--text-2xs);
font-weight: 500;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--color-text-muted);
border-bottom: 1px solid var(--glass-border-subtle);
white-space: nowrap;
cursor: pointer;
user-select: none;
transition: color var(--dur-fast);
position: relative;
}
.glass-table thead th:hover {
color: var(--color-text);
}
.glass-table thead th.is-sorted {
color: var(--accent-aqua);
}
[data-theme="light"] .glass-table thead th.is-sorted {
color: #0891b2;
}
.glass-table__sort-icon {
margin-left: 5px;
font-size: 0.6rem;
opacity: 0.45;
display: inline-block;
transition: opacity var(--dur-fast),
transform var(--dur-fast) var(--ease-liquid);
}
.glass-table thead th.is-sorted .glass-table__sort-icon {
opacity: 1;
}
.glass-table thead th.is-sorted.sort-desc .glass-table__sort-icon {
transform: rotate(180deg);
}
/* Checkbox column */
.glass-table th.col-check,
.glass-table td.col-check {
width: 44px;
padding-right: 0;
text-align: center;
}
/* Inline checkbox */
.glass-table-check {
width: 16px;
height: 16px;
border-radius: 4px;
border: 1px solid var(--glass-border);
background: var(--glass-white);
cursor: pointer;
appearance: none;
-webkit-appearance: none;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: background var(--dur-fast), border-color var(--dur-fast);
vertical-align: middle;
position: relative;
}
.glass-table-check::after {
content: "✓";
font-size: 0.6rem;
color: var(--accent-aqua);
opacity: 0;
transform: scale(0.4);
transition: opacity var(--dur-fast),
transform var(--dur-fast) var(--ease-liquid);
position: absolute;
}
.glass-table-check:checked {
background: rgba(94, 231, 223, 0.22);
border-color: rgba(94, 231, 223, 0.65);
}
.glass-table-check:checked::after {
opacity: 1;
transform: scale(1);
}
[data-theme="light"] .glass-table-check {
border-color: rgba(15, 23, 42, 0.22);
background: rgba(255, 255, 255, 0.7);
}
[data-theme="light"] .glass-table-check:checked {
background: rgba(8, 145, 178, 0.18);
border-color: rgba(8, 145, 178, 0.55);
}
[data-theme="light"] .glass-table-check:checked::after {
color: #0891b2;
}
/* Row states */
.glass-table tbody tr {
transition: background var(--dur-fast);
}
.glass-table tbody tr:hover {
background: var(--glass-white);
}
.glass-table tbody tr.is-selected {
background: rgba(94, 231, 223, 0.07);
}
.glass-table tbody tr.is-selected:hover {
background: rgba(94, 231, 223, 0.11);
}
[data-theme="light"] .glass-table tbody tr:hover {
background: rgba(255, 255, 255, 0.55);
}
[data-theme="light"] .glass-table tbody tr.is-selected {
background: rgba(8, 145, 178, 0.07);
}
[data-theme="light"] .glass-table tbody tr.is-selected:hover {
background: rgba(8, 145, 178, 0.12);
}
.glass-table tbody td {
padding: var(--space-sm) var(--space-md);
border-bottom: 1px solid var(--glass-border-subtle);
vertical-align: middle;
}
.glass-table tbody tr:last-child td {
border-bottom: none;
}
[data-theme="light"] .glass-table tbody td,
[data-theme="light"] .glass-table thead th {
border-color: rgba(15, 23, 42, 0.08);
}
/* Cell types */
.glass-table__cell--num {
text-align: right;
font-variant-numeric: tabular-nums;
}
/* Member cell with avatar */
.glass-table-member {
display: flex;
align-items: center;
gap: var(--space-sm);
}
.glass-table-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 1rem;
line-height: 1;
border: 1.5px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.22);
}
.glass-table-member__info {
display: flex;
flex-direction: column;
gap: 1px;
}
.glass-table-member__name {
font-weight: 500;
font-size: var(--text-sm);
}
.glass-table-member__email {
font-size: var(--text-2xs);
opacity: 0.45;
}
/* Score mini bar */
.glass-table-score {
display: flex;
align-items: center;
gap: var(--space-xs);
justify-content: flex-end;
}
.glass-table-score__num {
font-size: var(--text-sm);
font-variant-numeric: tabular-nums;
font-weight: 500;
min-width: 28px;
text-align: right;
}
.glass-table-score__bar {
width: 60px;
height: 4px;
border-radius: var(--radius-full);
background: rgba(255, 255, 255, 0.12);
overflow: hidden;
flex-shrink: 0;
}
[data-theme="light"] .glass-table-score__bar {
background: rgba(15, 23, 42, 0.1);
}
.glass-table-score__fill {
height: 100%;
border-radius: var(--radius-full);
background: linear-gradient(90deg, var(--accent-aqua), var(--accent-violet));
transform-origin: left;
animation: progress-grow 0.8s var(--ease-liquid) both;
}
/* Action button */
.glass-table-action {
width: 28px;
height: 28px;
border-radius: var(--radius-sm);
border: 1px solid var(--glass-border-subtle);
background: transparent;
color: var(--color-text);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
opacity: 0.45;
transition: opacity var(--dur-fast), background var(--dur-fast),
border-color var(--dur-fast);
}
.glass-table-action:hover {
opacity: 1;
background: var(--glass-white);
border-color: var(--glass-border);
}
/* ── Bulk action bar (shown when rows selected) ── */
.glass-table-bulk {
display: flex;
align-items: center;
gap: var(--space-sm);
padding: var(--space-sm) var(--space-lg);
border-bottom: 1px solid rgba(94, 231, 223, 0.2);
background: rgba(94, 231, 223, 0.06);
font-size: var(--text-xs);
opacity: 0;
height: 0;
overflow: hidden;
transition: opacity var(--dur-fast), height var(--dur-fast);
}
.glass-table-bulk.is-visible {
opacity: 1;
height: 44px;
}
[data-theme="light"] .glass-table-bulk {
background: rgba(8, 145, 178, 0.06);
border-color: rgba(8, 145, 178, 0.2);
}
.glass-table-bulk__count {
font-weight: 500;
color: var(--accent-aqua);
}
[data-theme="light"] .glass-table-bulk__count {
color: #0891b2;
}
/* ── Pagination ── */
.glass-table-pagination {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-sm) var(--space-lg);
border-top: 1px solid var(--glass-border-subtle);
font-size: var(--text-xs);
color: var(--color-text-muted);
flex-wrap: wrap;
gap: var(--space-sm);
}
[data-theme="light"] .glass-table-pagination {
border-top-color: rgba(15, 23, 42, 0.08);
}
.glass-table-pagination__info {
opacity: 0.55;
}
.glass-table-pagination__controls {
display: flex;
align-items: center;
gap: 4px;
}
.glass-table-page-btn {
width: 28px;
height: 28px;
border-radius: var(--radius-sm);
border: 1px solid var(--glass-border-subtle);
background: transparent;
color: var(--color-text);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: var(--text-xs);
font-family: var(--font-body);
transition: background var(--dur-fast), border-color var(--dur-fast),
color var(--dur-fast);
}
.glass-table-page-btn:hover:not(:disabled) {
background: var(--glass-white);
border-color: var(--glass-border);
}
.glass-table-page-btn.is-active {
background: rgba(94, 231, 223, 0.18);
border-color: rgba(94, 231, 223, 0.45);
color: var(--accent-aqua);
}
.glass-table-page-btn:disabled {
opacity: 0.25;
cursor: not-allowed;
}
[data-theme="light"] .glass-table-page-btn.is-active {
background: rgba(8, 145, 178, 0.14);
border-color: rgba(8, 145, 178, 0.4);
color: #0891b2;
}
[data-theme="light"] .glass-table-page-btn:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.65);
}
/* ── Responsive stacked cards ── */
@media (max-width: 640px) {
.glass-table thead {
display: none;
}
.glass-table tbody tr {
display: block;
border: 1px solid var(--glass-border-subtle);
border-radius: var(--radius-md);
margin-bottom: var(--space-sm);
}
.glass-table tbody td {
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: none;
border-top: 1px solid var(--glass-border-subtle);
padding: 10px var(--space-md);
}
.glass-table tbody td:first-child {
border-top: none;
}
.glass-table tbody td::before {
content: attr(data-label);
font-size: var(--text-2xs);
letter-spacing: 0.06em;
text-transform: uppercase;
opacity: 0.45;
}
.glass-table-member {
flex-direction: row;
}
.glass-table-score {
justify-content: flex-end;
}
.glass-table-pagination {
justify-content: center;
}
.glass-table-toolbar {
flex-direction: column;
align-items: flex-start;
}
}
/* --------------------------------------------------------------------------
28. TWELVE LIQUID GLASS ANIMATIONS
-------------------------------------------------------------------------- */
@keyframes liquid-morph {
0% {
border-radius: 60% 40% 30% 70%/60% 30% 70% 40%;
}
25% {
border-radius: 30% 60% 70% 40%/50% 60% 30% 60%;
}
50% {
border-radius: 50% 60% 30% 60%/40% 40% 70% 60%;
}
75% {
border-radius: 60% 40% 60% 30%/60% 70% 40% 50%;
}
100% {
border-radius: 60% 40% 30% 70%/60% 30% 70% 40%;
}
}
@keyframes shimmer {
0% {
background-position: -200% center;
}
100% {
background-position: 200% center;
}
}
@keyframes float {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-12px);
}
}
@keyframes refraction {
0%,
100% {
opacity: 0.15;
transform: rotate(0deg) scale(1);
}
50% {
opacity: 0.35;
transform: rotate(180deg) scale(1.2);
}
}
@keyframes ripple {
0% {
transform: scale(0);
opacity: 0.5;
}
100% {
transform: scale(2.5);
opacity: 0;
}
}
@keyframes edge-glow {
0%,
100% {
box-shadow: 0 0 0 0 rgba(94, 231, 223, 0);
}
50% {
box-shadow: 0 0 30px 6px rgba(94, 231, 223, 0.35);
}
}
@keyframes breathing-blur {
0%,
100% {
backdrop-filter: blur(10px);
background: rgba(255, 255, 255, 0.1);
}
50% {
backdrop-filter: blur(30px);
background: rgba(255, 255, 255, 0.22);
}
}
@keyframes depth-shift {
0%,
100% {
transform: perspective(400px) rotateY(0deg);
}
50% {
transform: perspective(400px) rotateY(8deg);
}
}
@keyframes cascade-in {
0% {
opacity: 0;
transform: translateY(24px) scale(0.97);
filter: blur(8px);
}
100% {
opacity: 1;
transform: translateY(0) scale(1);
filter: blur(0);
}
}
@keyframes hue-shift {
0% {
filter: hue-rotate(0deg) brightness(1);
}
50% {
filter: hue-rotate(40deg) brightness(1.1);
}
100% {
filter: hue-rotate(0deg) brightness(1);
}
}
@keyframes liquid-drain {
0% {
clip-path: inset(0 0 0 0 round 20px);
opacity: 1;
}
100% {
clip-path: inset(0 0 100% 0 round 20px);
opacity: 0;
}
}
@keyframes tension {
0%,
100% {
transform: scaleX(1) scaleY(1);
}
30% {
transform: scaleX(1.06) scaleY(0.94);
}
60% {
transform: scaleX(0.95) scaleY(1.05);
}
}
@keyframes progress-grow {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
.anim-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: var(--space-md);
}
.anim-demo {
border-radius: var(--radius-lg);
padding: 28px 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: var(--space-md);
cursor: pointer;
min-height: 160px;
text-align: center;
}
.anim-demo__orb {
width: 60px;
height: 60px;
border-radius: 50%;
background: linear-gradient(135deg, var(--accent-aqua), var(--accent-violet));
border: 1px solid rgba(255, 255, 255, 0.4);
box-shadow: 0 4px 20px rgba(94, 231, 223, 0.3);
}
.anim-demo__label {
font-size: var(--text-xs);
letter-spacing: 0.08em;
text-transform: uppercase;
opacity: 0.6;
}
.anim-demo:nth-child(1) .anim-demo__orb {
animation: liquid-morph 5s ease-in-out infinite;
}
.anim-demo:nth-child(2) .anim-demo__orb {
background: linear-gradient(
90deg,
rgba(94, 231, 223, 0) 0%,
rgba(255, 255, 255, 0.6) 50%,
rgba(94, 231, 223, 0) 100%
);
background-size: 200% auto;
animation: shimmer 2s linear infinite;
}
.anim-demo:nth-child(3) .anim-demo__orb {
animation: float 3s ease-in-out infinite;
}
.anim-demo:nth-child(4) .anim-demo__orb {
background: conic-gradient(
var(--accent-aqua),
var(--accent-violet),
var(--accent-rose),
var(--accent-aqua)
);
animation: refraction 4s ease-in-out infinite;
}
.anim-demo:nth-child(5) {
position: relative;
overflow: hidden;
}
.anim-demo:nth-child(5) .anim-demo__orb {
cursor: pointer;
position: relative;
}
.anim-demo:nth-child(5) .anim-demo__orb::after {
content: "";
position: absolute;
inset: 0;
border-radius: 50%;
background: rgba(94, 231, 223, 0.5);
animation: ripple 2s ease-out infinite;
}
.anim-demo:nth-child(6) .anim-demo__orb {
animation: edge-glow 2.5s ease-in-out infinite;
}
.anim-demo:nth-child(7) {
animation: breathing-blur 4s ease-in-out infinite;
}
.anim-demo:nth-child(8) {
animation: depth-shift 4s ease-in-out infinite;
}
.anim-demo:nth-child(9) {
animation: cascade-in var(--dur-slow) var(--ease-glass) both;
animation-delay: 0.4s;
}
.anim-demo:nth-child(10) {
animation: hue-shift 6s ease-in-out infinite;
}
.anim-demo:nth-child(11) .anim-demo__orb {
animation: liquid-drain 2.5s ease-in-out infinite alternate;
}
.anim-demo:nth-child(12) {
animation: tension 2s ease-in-out infinite;
}
/* --------------------------------------------------------------------------
29. PAGE LAYOUT & HERO
-------------------------------------------------------------------------- */
.page {
position: relative;
z-index: 10;
}
.section {
padding: var(--space-3xl) 0;
animation: cascade-in var(--dur-slow) var(--ease-glass) both;
}
.section:nth-child(1) {
animation-delay: 0.05s;
}
.section:nth-child(2) {
animation-delay: 0.15s;
}
.section:nth-child(3) {
animation-delay: 0.25s;
}
.section:nth-child(4) {
animation-delay: 0.35s;
}
.section:nth-child(5) {
animation-delay: 0.45s;
}
.section:nth-child(6) {
animation-delay: 0.55s;
}
.section:nth-child(7) {
animation-delay: 0.65s;
}
.container {
width: min(1100px, 92vw);
margin-inline: auto;
}
.section__header {
display: flex;
align-items: baseline;
gap: var(--space-md);
margin-bottom: var(--space-xl);
}
.section__title {
font-family: var(--font-display);
font-size: clamp(1.6rem, 3vw, 2.2rem);
letter-spacing: 0.01em;
}
.section__subtitle {
font-size: var(--text-xs);
opacity: 0.45;
letter-spacing: 0.06em;
text-transform: uppercase;
}
.hero {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 80px 24px;
gap: var(--space-lg);
}
.hero__kicker {
font-size: var(--text-xs);
letter-spacing: 0.2em;
text-transform: uppercase;
opacity: 0.55;
animation: cascade-in 0.7s var(--ease-glass) 0.1s both;
}
.hero__title {
font-family: var(--font-display);
font-size: clamp(3rem, 9vw, 7rem);
line-height: 1;
letter-spacing: -0.02em;
background: linear-gradient(
135deg,
#fff 30%,
var(--accent-aqua) 65%,
var(--accent-violet) 100%
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
animation: cascade-in 0.8s var(--ease-glass) 0.2s both;
}
[data-theme="light"] .hero__title {
background: linear-gradient(135deg, #0f172a 20%, #0369a1 60%, #7c3aed 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero__sub {
max-width: 480px;
line-height: 1.7;
opacity: 0.65;
font-size: var(--text-base);
animation: cascade-in 0.8s var(--ease-glass) 0.3s both;
}
.hero__cta {
display: flex;
gap: var(--space-sm);
flex-wrap: wrap;
justify-content: center;
animation: cascade-in 0.8s var(--ease-glass) 0.4s both;
}
.stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-md);
margin-top: var(--space-3xl);
animation: cascade-in 0.8s var(--ease-glass) 0.5s both;
}
.stats__item {
border-radius: var(--radius-lg);
padding: var(--space-lg);
text-align: center;
}
.stats__num {
font-family: var(--font-display);
font-size: 2.2rem;
background: linear-gradient(135deg, #fff, var(--accent-aqua));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
[data-theme="light"] .stats__num {
background: linear-gradient(135deg, #0f172a, #0369a1);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.stats__desc {
font-size: var(--text-xs);
opacity: 0.5;
letter-spacing: 0.04em;
margin-top: 4px;
}
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: var(--space-md);
}
.btn-row {
display: flex;
flex-wrap: wrap;
gap: var(--space-sm);
align-items: center;
}
.tags-row {
display: flex;
flex-wrap: wrap;
gap: var(--space-xs);
}
.profile-card {
border-radius: var(--radius-xl);
padding: var(--space-xl);
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-md);
text-align: center;
}
.profile-card__avatar {
width: 72px;
height: 72px;
border-radius: 50%;
border: 2px solid var(--glass-border);
background: linear-gradient(135deg, var(--accent-violet), var(--accent-aqua));
display: flex;
align-items: center;
justify-content: center;
font-size: 1.8rem;
line-height: 1;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.25);
}
.profile-card__name {
font-family: var(--font-display);
font-size: 1.3rem;
}
.profile-card__role {
font-size: var(--text-sm);
opacity: 0.5;
}
.progress-wrap {
margin-top: var(--space-sm);
width: 100%;
}
.progress-bar {
height: 6px;
border-radius: var(--radius-full);
background: rgba(255, 255, 255, 0.12);
overflow: hidden;
margin-top: 6px;
}
.progress-bar__fill {
height: 100%;
border-radius: var(--radius-full);
background: linear-gradient(90deg, var(--accent-aqua), var(--accent-violet));
animation: progress-grow 1.2s var(--ease-liquid) 0.5s both;
transform-origin: left;
}
.progress-label {
display: flex;
justify-content: space-between;
font-size: var(--text-xs);
opacity: 0.55;
}
.glass-form {
border-radius: var(--radius-2xl);
padding: var(--space-2xl);
}
.glass-form__row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-md);
margin-bottom: var(--space-md);
}
.glass-form__row--full {
grid-template-columns: 1fr;
}
label {
display: block;
font-size: var(--text-xs);
letter-spacing: 0.06em;
text-transform: uppercase;
opacity: 0.55;
margin-bottom: var(--space-xs);
color: var(--color-text);
}
.notif {
border-radius: var(--radius-lg);
padding: 18px 22px;
display: flex;
align-items: center;
gap: var(--space-md);
margin-bottom: var(--space-sm);
transition: transform var(--dur-fast) var(--ease-liquid);
}
.notif:hover {
transform: translateX(6px);
}
.notif__icon {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.1rem;
line-height: 1;
flex-shrink: 0;
}
.notif__icon--aqua {
background: rgba(94, 231, 223, 0.2);
}
.notif__icon--violet {
background: rgba(180, 144, 245, 0.2);
}
.notif__icon--amber {
background: rgba(255, 210, 127, 0.2);
}
.notif__title {
font-size: var(--text-md);
font-weight: 500;
margin-bottom: 3px;
}
.notif__desc {
font-size: var(--text-xs);
opacity: 0.55;
}
.notif__time {
margin-left: auto;
font-size: var(--text-2xs);
opacity: 0.4;
white-space: nowrap;
}
.divider {
height: 1px;
background: linear-gradient(
90deg,
transparent,
var(--glass-border),
transparent
);
margin: var(--space-3xl) 0;
}
.footer {
padding: var(--space-2xl) 0 var(--space-3xl);
text-align: center;
}
.footer__text {
font-size: var(--text-xs);
opacity: 0.35;
letter-spacing: 0.06em;
}
.glass-toggle {
display: flex;
border-radius: var(--radius-full);
padding: 4px;
gap: 4px;
}
.glass-toggle__opt {
padding: 8px 20px;
border-radius: var(--radius-full);
font-size: var(--text-sm);
cursor: pointer;
transition: background var(--dur-fast), box-shadow var(--dur-fast);
}
.glass-toggle__opt.is-on {
background: var(--glass-white-md);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
}
.glass-range {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 6px;
border-radius: var(--radius-full);
background: rgba(255, 255, 255, 0.15);
outline: none;
cursor: pointer;
}
.glass-range::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: linear-gradient(135deg, var(--accent-aqua), var(--accent-violet));
border: 2px solid rgba(255, 255, 255, 0.5);
box-shadow: 0 2px 10px rgba(94, 231, 223, 0.4);
cursor: pointer;
transition: transform var(--dur-fast) var(--ease-liquid);
}
.glass-range::-webkit-slider-thumb:active {
transform: scale(1.3);
}
/* --------------------------------------------------------------------------
30. THEME TOGGLE
-------------------------------------------------------------------------- */
.theme-toggle-btn {
position: fixed;
top: var(--space-md);
right: var(--space-md);
z-index: var(--z-toast);
width: 48px;
height: 48px;
border-radius: 50%;
border: none;
cursor: pointer;
font-size: 1.2rem;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
transition: transform var(--dur-fast) var(--ease-liquid),
box-shadow var(--dur-fast);
}
.theme-toggle-btn span {
display: flex;
align-items: center;
justify-content: center;
line-height: 1;
}
.theme-toggle-btn:hover {
transform: scale(1.1) rotate(15deg);
}
.theme-toggle-btn .icon-dark {
display: block;
}
.theme-toggle-btn .icon-light {
display: none;
}
[data-theme="light"] .theme-toggle-btn .icon-dark {
display: none;
}
[data-theme="light"] .theme-toggle-btn .icon-light {
display: block;
}
/* --------------------------------------------------------------------------
31. RESPONSIVE
-------------------------------------------------------------------------- */
@media (max-width: 768px) {
.glass-form__row {
grid-template-columns: 1fr;
}
.glass-stepper:not(.glass-stepper--vertical) .glass-step__label {
display: none;
}
}
@media (max-width: 640px) {
.stats {
grid-template-columns: 1fr;
}
.hero__title {
font-size: 3.2rem;
}
}
/* ==========================================================================
LIGHT THEME — Component-level overrides
Accent colour tokens cascade, but any component that hardcoded
rgba() with the old dark-palette values needs explicit overrides.
========================================================================== */
/* ── Glass surface dark variant (used on dark cards) ── */
[data-theme="light"] .glass--dark {
background: rgba(15, 23, 42, 0.06);
border-color: rgba(15, 23, 42, 0.14);
}
/* ── Badges — replace rgba() tints built from dark-theme accents ── */
[data-theme="light"] .glass-badge--aqua {
background: rgba(8, 145, 178, 0.12);
border-color: rgba(8, 145, 178, 0.35);
}
[data-theme="light"] .glass-badge--violet {
background: rgba(109, 40, 217, 0.1);
border-color: rgba(109, 40, 217, 0.3);
box-shadow: none;
}
[data-theme="light"] .glass-badge--rose {
background: rgba(190, 24, 93, 0.1);
border-color: rgba(190, 24, 93, 0.3);
}
[data-theme="light"] .glass-badge--amber {
background: rgba(180, 83, 9, 0.1);
border-color: rgba(180, 83, 9, 0.3);
}
[data-theme="light"] .glass-badge--lime {
background: rgba(21, 128, 61, 0.1);
border-color: rgba(21, 128, 61, 0.3);
}
[data-theme="light"] .glass-badge--error {
background: rgba(185, 28, 28, 0.1);
border-color: rgba(185, 28, 28, 0.3);
color: #b91c1c;
}
/* ── Tags ── */
[data-theme="light"] .glass-tag {
background: rgba(255, 255, 255, 0.7);
border-color: rgba(15, 23, 42, 0.14);
color: #0f172a;
}
[data-theme="light"] .glass-tag:hover {
background: rgba(255, 255, 255, 0.88);
}
/* ── Nav + Tabs ── */
[data-theme="light"] .glass-nav__item--active,
[data-theme="light"] .glass-tab[aria-selected="true"] {
background: rgba(255, 255, 255, 0.78);
border-color: rgba(15, 23, 42, 0.16);
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}
[data-theme="light"] .glass-tab:hover {
background: rgba(255, 255, 255, 0.55);
}
[data-theme="light"] .glass-toggle__opt.is-on {
background: rgba(255, 255, 255, 0.78);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
}
/* ── Inputs ── */
[data-theme="light"] .glass-input,
[data-theme="light"] .glass-textarea,
[data-theme="light"] .glass-select {
background: rgba(255, 255, 255, 0.72);
border-color: rgba(15, 23, 42, 0.16);
color: #0f172a;
}
[data-theme="light"] .glass-input:focus,
[data-theme="light"] .glass-textarea:focus,
[data-theme="light"] .glass-select:focus {
border-color: rgba(8, 145, 178, 0.55);
box-shadow: 0 0 0 3px rgba(8, 145, 178, 0.14), var(--shadow-glass);
}
[data-theme="light"] .glass-input::placeholder,
[data-theme="light"] .glass-textarea::placeholder {
color: rgba(15, 23, 42, 0.38);
opacity: 1;
}
/* ── Checkboxes / Radios ── */
[data-theme="light"] .glass-check-box,
[data-theme="light"] .glass-radio-box {
background: rgba(255, 255, 255, 0.72);
border-color: rgba(15, 23, 42, 0.22);
}
[data-theme="light"] .glass-check-label input:checked + .glass-check-box,
[data-theme="light"] .glass-check-label input:checked + .glass-radio-box {
background: rgba(8, 145, 178, 0.15);
border-color: rgba(8, 145, 178, 0.55);
box-shadow: 0 0 10px rgba(8, 145, 178, 0.18);
}
[data-theme="light"] .glass-check-box::after {
color: #0891b2;
}
/* ── Switch ── */
[data-theme="light"] .glass-switch-track {
background: rgba(255, 255, 255, 0.72);
border-color: rgba(15, 23, 42, 0.22);
}
[data-theme="light"] .glass-switch-thumb {
background: rgba(15, 23, 42, 0.45);
}
[data-theme="light"] .glass-switch-label input:checked + .glass-switch-track {
background: rgba(8, 145, 178, 0.2);
border-color: rgba(8, 145, 178, 0.55);
box-shadow: 0 0 10px rgba(8, 145, 178, 0.15);
}
[data-theme="light"]
.glass-switch-label
input:checked
+ .glass-switch-track
.glass-switch-thumb {
background: #0891b2;
}
/* ── Buttons ── */
[data-theme="light"] .glass-btn--ghost {
background: rgba(255, 255, 255, 0.65);
border-color: rgba(15, 23, 42, 0.18);
}
[data-theme="light"] .glass-btn--ghost:hover {
background: rgba(255, 255, 255, 0.84);
border-color: rgba(15, 23, 42, 0.25);
}
[data-theme="light"] .glass-btn--primary {
background: linear-gradient(
135deg,
rgba(8, 145, 178, 0.55) 0%,
rgba(37, 99, 235, 0.55) 100%
);
border-color: rgba(8, 145, 178, 0.5);
color: #fff;
}
[data-theme="light"] .glass-btn--primary:hover {
background: linear-gradient(
135deg,
rgba(8, 145, 178, 0.75) 0%,
rgba(37, 99, 235, 0.75) 100%
);
}
[data-theme="light"] .glass-btn--accent {
background: linear-gradient(
135deg,
rgba(109, 40, 217, 0.45) 0%,
rgba(190, 24, 93, 0.45) 100%
);
border-color: rgba(109, 40, 217, 0.4);
color: #fff;
}
/* ── Modal ── */
/* ── Modal dismiss (handled in modal block above) ── */
/* ── Accordion ── */
[data-theme="light"] .glass-accordion-trigger:hover {
background: rgba(255, 255, 255, 0.55);
}
[data-theme="light"] .glass-accordion-icon {
background: rgba(255, 255, 255, 0.65);
border-color: rgba(15, 23, 42, 0.18);
}
[data-theme="light"] .glass-accordion-item.is-open .glass-accordion-icon {
background: rgba(8, 145, 178, 0.15);
border-color: rgba(8, 145, 178, 0.45);
}
/* ── Dropdown ── */
[data-theme="light"] .glass-dropdown-item:hover {
background: rgba(15, 23, 42, 0.06);
}
[data-theme="light"] .glass-dropdown-item.is-active {
background: rgba(8, 145, 178, 0.12);
color: #0891b2;
}
[data-theme="light"] .glass-dropdown-item--danger {
color: #b91c1c;
}
[data-theme="light"] .glass-dropdown-item--danger:hover {
background: rgba(185, 28, 28, 0.08);
}
[data-theme="light"] .glass-dropdown-divider {
background: rgba(15, 23, 42, 0.1);
}
/* ── Toast ── */
[data-theme="light"] .glass-toast--success {
border-color: rgba(21, 128, 61, 0.35) !important;
box-shadow: 0 4px 24px rgba(21, 128, 61, 0.14) !important;
}
[data-theme="light"] .glass-toast--error {
border-color: rgba(185, 28, 28, 0.35) !important;
box-shadow: 0 4px 24px rgba(185, 28, 28, 0.14) !important;
}
[data-theme="light"] .glass-toast--warning {
border-color: rgba(180, 83, 9, 0.35) !important;
box-shadow: 0 4px 24px rgba(180, 83, 9, 0.14) !important;
}
[data-theme="light"] .glass-toast--info {
border-color: rgba(8, 145, 178, 0.35) !important;
box-shadow: 0 4px 24px rgba(8, 145, 178, 0.14) !important;
}
/* ── Chips ── */
[data-theme="light"] .glass-chip {
color: #0f172a;
}
[data-theme="light"] .glass-chip:hover {
background: rgba(255, 255, 255, 0.75);
}
[data-theme="light"] .glass-chip--aqua {
background: rgba(8, 145, 178, 0.1);
border-color: rgba(8, 145, 178, 0.3);
color: #0891b2;
}
[data-theme="light"] .glass-chip--violet {
background: rgba(109, 40, 217, 0.1);
border-color: rgba(109, 40, 217, 0.28);
color: #6d28d9;
}
[data-theme="light"] .glass-chip--rose {
background: rgba(190, 24, 93, 0.1);
border-color: rgba(190, 24, 93, 0.28);
color: #be185d;
}
[data-theme="light"] .glass-chip--amber {
background: rgba(180, 83, 9, 0.1);
border-color: rgba(180, 83, 9, 0.28);
color: #b45309;
}
[data-theme="light"] .glass-chip__dismiss {
background: rgba(15, 23, 42, 0.1);
border-color: rgba(15, 23, 42, 0.18);
}
[data-theme="light"] .glass-chip__dismiss:hover {
background: rgba(15, 23, 42, 0.2);
}
[data-theme="light"] .glass-chip.is-selected {
background: linear-gradient(
135deg,
rgba(8, 145, 178, 0.18) 0%,
rgba(109, 40, 217, 0.18) 100%
);
border-color: rgba(8, 145, 178, 0.45);
}
[data-theme="light"] .glass-chip-input-wrap {
border-color: rgba(8, 145, 178, 0.45);
background: rgba(8, 145, 178, 0.08);
}
[data-theme="light"] .glass-chip-add {
border-color: rgba(15, 23, 42, 0.25);
color: #0f172a;
}
[data-theme="light"] .glass-chip-add:hover {
background: rgba(255, 255, 255, 0.65);
}
[data-theme="light"] .glass-chip-input {
color: #0f172a;
}
[data-theme="light"] .glass-chip-input-confirm {
background: rgba(8, 145, 178, 0.2);
border-color: rgba(8, 145, 178, 0.4);
color: #0891b2;
}
/* ── Table ── */
[data-theme="light"] .glass-table thead th {
color: #0f172a;
}
[data-theme="light"] .glass-table thead th.is-sorted {
color: #0891b2;
}
[data-theme="light"] .glass-table tbody tr:hover {
background: rgba(255, 255, 255, 0.55);
}
[data-theme="light"] .glass-table tbody td,
[data-theme="light"] .glass-table thead th {
border-color: rgba(15, 23, 42, 0.1);
}
/* ── Stepper ── */
[data-theme="light"] .glass-step__node {
background: rgba(255, 255, 255, 0.72);
border-color: rgba(15, 23, 42, 0.22);
color: #0f172a;
}
[data-theme="light"] .glass-step.is-complete .glass-step__node {
background: rgba(8, 145, 178, 0.18);
border-color: rgba(8, 145, 178, 0.55);
box-shadow: 0 0 12px rgba(8, 145, 178, 0.18);
}
[data-theme="light"] .glass-step.is-active .glass-step__node {
background: rgba(109, 40, 217, 0.18);
border-color: rgba(109, 40, 217, 0.55);
box-shadow: 0 0 16px rgba(109, 40, 217, 0.2);
}
[data-theme="light"] .glass-step__connector {
background: rgba(15, 23, 42, 0.15);
}
[data-theme="light"] .glass-step.is-complete .glass-step__connector {
background: rgba(8, 145, 178, 0.4);
}
/* ── Skeleton ── */
[data-theme="light"] .skeleton {
background: rgba(15, 23, 42, 0.08);
}
[data-theme="light"] .skeleton::after {
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.55) 40%,
rgba(255, 255, 255, 0.75) 50%,
rgba(255, 255, 255, 0.55) 60%,
rgba(255, 255, 255, 0) 100%
);
}
/* ── Breadcrumb ── */
[data-theme="light"] .glass-breadcrumb__crumb:hover {
background: rgba(15, 23, 42, 0.07);
}
[data-theme="light"] .glass-breadcrumb__crumb--active {
background: rgba(255, 255, 255, 0.78);
border-color: rgba(15, 23, 42, 0.16);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
[data-theme="light"] .glass-breadcrumb-dropdown__item:hover {
background: rgba(15, 23, 42, 0.07);
}
/* ── Avatar ── */
[data-theme="light"] .glass-avatar {
border-color: rgba(255, 255, 255, 0.8);
}
[data-theme="light"] .glass-avatar--overflow {
background: rgba(255, 255, 255, 0.7);
border-color: rgba(15, 23, 42, 0.18);
color: rgba(15, 23, 42, 0.65);
}
[data-theme="light"] .glass-avatar-popover {
color: #0f172a;
}
/* ── Notifications ── */
[data-theme="light"] .notif__icon--aqua {
background: rgba(8, 145, 178, 0.14);
}
[data-theme="light"] .notif__icon--violet {
background: rgba(109, 40, 217, 0.12);
}
[data-theme="light"] .notif__icon--amber {
background: rgba(180, 83, 9, 0.12);
}
/* ── Range slider ── */
[data-theme="light"] .glass-range {
background: rgba(15, 23, 42, 0.15);
}
/* ── Tooltip ── */
[data-theme="light"] .glass-tooltip {
color: #0f172a;
}
[data-theme="light"] .glass-tooltip::after {
border-top-color: rgba(15, 23, 42, 0.18);
}
/* ── Divider ── */
[data-theme="light"] .divider {
background: linear-gradient(
90deg,
transparent,
rgba(15, 23, 42, 0.14),
transparent
);
}
/* ── Modal backdrop (handled in modal block above) ── */
/* ── Section subtitle ── */
[data-theme="light"] .section__subtitle {
color: rgba(15, 23, 42, 0.45);
opacity: 1;
}
[data-theme="light"] .glass-card__label {
color: rgba(15, 23, 42, 0.45);
opacity: 1;
}
[data-theme="light"] .glass-card__body {
color: rgba(15, 23, 42, 0.75);
opacity: 1;
}
[data-theme="light"] .footer__text {
color: rgba(15, 23, 42, 0.38);
opacity: 1;
}
[data-theme="light"] .hero__sub {
color: rgba(15, 23, 42, 0.65);
opacity: 1;
}
[data-theme="light"] .stats__desc {
color: rgba(15, 23, 42, 0.52);
opacity: 1;
}
/* ── Profile card ── */
[data-theme="light"] .profile-card__role {
color: rgba(15, 23, 42, 0.52);
opacity: 1;
}
[data-theme="light"] .progress-bar {
background: rgba(15, 23, 42, 0.12);
}
[data-theme="light"] .progress-label {
color: rgba(15, 23, 42, 0.55);
opacity: 1;
}
[data-theme="light"] .notif__desc {
color: rgba(15, 23, 42, 0.55);
opacity: 1;
}
[data-theme="light"] .notif__time {
color: rgba(15, 23, 42, 0.4);
opacity: 1;
}
[data-theme="light"] .glass-avatar-group__meta {
color: rgba(15, 23, 42, 0.5);
opacity: 1;
}
[data-theme="light"] .glass-accordion-content {
color: rgba(15, 23, 42, 0.72);
opacity: 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment