Colors
Brand palette, semantic aliases, and text hierarchy. Click any swatch to copy the CSS variable.
Code snippet
/* Brand */
--violet: #9B5DE5;
--cyan: #06D6E0;
--green: #06D664;
--pink: #F15BB5;
--amber: #FFB347;
/* Semantic */
--success: var(--green);
--warning: var(--amber);
--danger: var(--pink);
--info: var(--cyan);Typography
Type scale, font families, and weight ladder. Unbounded: display/headings/KPI numbers. Manrope: everything else.
Code snippet
--text-xs: 0.72rem; /* 11.5px */
--text-sm: 0.82rem; /* 13px */
--text-base: 0.92rem; /* 14.7px */
--text-md: 1.02rem; /* 16.3px */
--text-lg: 1.18rem; /* 18.9px */
--text-xl: 1.5rem; /* 24px */
--text-2xl: 2rem; /* 32px */
--text-3xl: 2.6rem; /* 41.6px */
--fw-regular: 400;
--fw-medium: 500;
--fw-semibold: 600;
--fw-bold: 700;
--fw-extrabold: 800;Spacing
4px base scale. Use tokens instead of hardcoded values for consistent rhythm.
Code snippet
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-8: 32px;
--space-10: 40px;
--space-12: 48px;
--space-16: 64px;Radii
Border-radius ladder from sharp to pill. Click to copy.
Code snippet
--r-xs: 4px;
--r-sm: 8px;
--r-md: 12px;
--r-lg: 20px;
--r-xl: 24px;
--r-pill: 999px;Shadows
Two-layer shadows: crisp ambient + lifted. Use --shadow-h on hover.
Code snippet
--shadow: 0 2px 8px rgba(15,23,42,0.08), 0 8px 40px rgba(15,23,42,0.10);
--shadow-h: 0 4px 16px rgba(15,23,42,0.12), 0 16px 56px rgba(15,23,42,0.13);Blur
--blur: blur(20px) — used with a semi-transparent background for glass surfaces.
Code snippet
--blur: blur(20px);
/* Usage */
.glass-panel {
background: rgba(238,242,255,0.55);
backdrop-filter: var(--blur);
border: 1.5px solid rgba(255,255,255,0.4);
}Buttons
All buttons meet WCAG 2.5.5 44px touch target. Primary has shimmer on hover.
Code snippet
<button class="btn-primary">Primary</button>
<button class="btn-ghost">Ghost</button>
<button class="btn-danger">Danger</button>
<button class="icon-btn" aria-label="Settings">
<i data-lucide="settings"></i>
</button>Form Inputs
All inputs use .form-input. Focus ring uses violet at 15% opacity.
Code snippet
<input class="form-input" type="text" placeholder="Enter value...">
<input class="form-input error" type="text">
<select class="form-input">...</select>
<textarea class="form-input" rows="3"></textarea>Badges
5 brand-color variants, pill-shaped, suitable for status labels.
Code snippet
<span class="badge badge-violet">Учитель</span>
<span class="badge badge-cyan">Активен</span>
<span class="badge badge-green">Завершено</span>
<span class="badge badge-pink">Новый</span>
<span class="badge badge-amber">Ученик</span>Chips
Interactive filter chips, KPI chips, and status chips.
Code snippet
<button class="chip active">All subjects</button>
<button class="chip">Mathematics
<span class="chip-x" aria-label="Remove">...</span>
</button>Cards
Surface, glass, top-bar, and hero card variants.
var(--surface) background with shadow.Code snippet
<div class="ds-surface-card">...</div>
<!-- glass: wrap in gradient div, then -->
<div style="backdrop-filter:var(--blur);background:rgba(255,255,255,0.35)">...</div>
<!-- top-bar: ::before height:3px; background: gradient -->Modal
Uses LS.modal() from api.js. Focus-trapped, dismissible via Escape or overlay click.
Code snippet
const m = LS.modal({
title: 'Modal title',
content: '<p>Body content here</p>',
size: 'md', // 'sm' | 'md' | 'lg'
actions: [
{ label: 'Cancel', onClick: () => m.close() },
{ label: 'Confirm', primary: true, onClick: async () => { ... } },
],
});Toast
4 variants via LS.toast(message, type). Auto-dismiss in 3.5s.
Code snippet
LS.toast('Action completed!', 'success');
LS.toast('Review your input.', 'warn');
LS.toast('An error occurred.', 'error');
LS.toast('Session saved.', 'info');Skeleton
Shimmer placeholders for loading states. Use .ls-skeleton as base.
Code snippet
<!-- Neutral shimmer -->
<div class="ls-skeleton ls-skeleton-line"></div>
<!-- Violet shimmer (existing) -->
<div class="ls-sk" style="height:36px"></div>Empty State
Zero-data, error, and no-results variants using .rich-empty.
Code snippet
<div class="rich-empty">
<div class="rich-empty-svg"><!-- Lucide icon --></div>
<div class="rich-empty-title">No data yet</div>
<div class="rich-empty-sub">Description text</div>
<button class="rich-empty-btn">Action</button>
</div>Avatar
Initials-based pills with HSL color derived from name hash. Role-colored variants.
Code snippet
function avatarColor(name) {
let h = 0;
for (const c of name) h = (h * 31 + c.charCodeAt(0)) % 360;
return `hsl(${h}, 60%, 48%)`;
}
// initials
const initials = name.split(' ').slice(0,2).map(w=>w[0]).join('').toUpperCase();Stat Card
KPI cards with colored top bar and optional delta indicator.
Code snippet
<div class="stat-card vc">
<div class="stat-label">Total sessions</div>
<div class="stat-val">1,248</div>
<div class="stat-delta">+12% this week</div>
</div>
/* .vc = violet-cyan bar, .gc = green-cyan, .ac = amber-pink */Data Table
Sticky header, hover highlight, row actions revealed on hover.
| Student | Subject | Score | Date | Status | |
|---|---|---|---|---|---|
| Anna K. | Mathematics | 92% | May 20 | Passed | |
| Dmitri P. | Physics | 74% | May 19 | Review | |
| Maria S. | Chemistry | 88% | May 18 | Graded | |
| Ivan B. | History | 61% | May 17 | Failed | |
| Elena V. | Biology | 95% | May 16 | Top score |
Code snippet
<div class="ds-table-wrap">
<table class="ds-table">
<thead><tr><th>Name</th>...</tr></thead>
<tbody>
<tr>
<td>Anna K.</td>...
<td><div class="row-actions">...</div></td>
</tr>
</tbody>
</table>
</div>Search + Filter Bar
Search input paired with filter chips pattern.
Code snippet
<div class="search-bar">
<div class="search-wrap">
<!-- Lucide search icon -->
<input class="form-input" type="search" placeholder="Search...">
</div>
<button class="chip active">All</button>
<button class="chip">Active</button>
</div>Tabs
Pill-tabs with white active indicator. Wraps in a muted violet track.
Code snippet
<div class="ds-tabs">
<button class="ds-tab active">Overview</button>
<button class="ds-tab">Sessions</button>
<button class="ds-tab">Students</button>
</div>Hero Header
Greeting + KPI chip row used at the top of dashboard pages.
Bento Grid
3-column masonry-like grid with spanning cells for dashboard layouts.
Code snippet
<div class="bento">
<div class="bento-cell wide">Span 2 cols</div>
<div class="bento-cell tall">Span 2 rows</div>
<div class="bento-cell">Normal</div>
<div class="bento-cell">Normal</div>
</div>Hover Row Actions
Action buttons hidden by default, revealed on row hover via display:flex toggle.
| Name | Role | Last active | |
|---|---|---|---|
| Anna K. | Teacher | 2 min ago | |
| Dmitri P. | Student | 1 hour ago | |
| Maria S. | Admin | Just now |
Hover a row to see action buttons.
Motion
Spring and ease-out transitions. Hover cards to see. Toggle prefers-reduced-motion simulation.
0.22s
0.22s
0.40s
Code snippet
--ease-out: cubic-bezier(0.16, 1, 0.3, 1);
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--duration-fast: 0.12s;
--duration-base: 0.22s;
--duration-slow: 0.40s;
/* Usage */
.card { transition: transform var(--duration-base) var(--ease-spring); }
.card:hover { transform: translateY(-6px) scale(1.02); }
/* Respect user preference */
@media (prefers-reduced-motion: reduce) {
.card { transition: none; }
}Accessibility
Focus rings use :focus-visible. All interactive elements meet 44px touch target (WCAG 2.5.5).
Contrast Checker
Compute WCAG 2.1 contrast ratio live. AA requires 4.5:1 (normal text), 3:1 (large text).
Icons (Lucide)
Top 50 Lucide icons used in LearnSpace. Click to copy <i data-lucide="..."> tag.
Code snippet
<!-- Include once in <head> -->
<script src="https://cdn.jsdelivr.net/npm/lucide@0.469.0/dist/umd/lucide.min.js"></script>
<!-- Use icon -->
<i data-lucide="users" style="width:18px;height:18px"></i>
<!-- Initialize (call once after DOM) -->
<script>lucide.createIcons();</script>Anti-Patterns
Common mistakes and their correct counterparts in LearnSpace.
color: #9B5DE5;
color: var(--violet);
style="display:none"
class="hidden"
style="background:#9B5DE5;color:#fff;border-radius:999px;..."
<button class="btn-primary">
style="padding:12px"
class="p-3"