Copied!

## Colors

Brand palette, semantic aliases, and text hierarchy. Click any swatch to copy the CSS variable.

Brand
Semantic Aliases
Text & Surface
WCAG Contrast Preview
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.

Type Scale
Font Families
Manrope — body
The quick brown fox
Body text, labels, inputs
Unbounded — display
The quick brown fox
Headings, KPI numbers, logo
Weight Ladder
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.

No shadow
--shadow
--shadow-h
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.

No blur
Frosted glass
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.

Variants
Disabled state
Loading state
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.

Text input (default)
Text input (error) This field is required
Number
Select
Textarea
Disabled
File upload
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.

Variants
Учитель Активен Завершено Новый Ученик
Sizes
Micro Regular Large
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.

KPI chip
Students
128
Top score
96%
Filter 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.

Surface card
Uses `var(--surface)` background with shadow.
Glass card
Frosted backdrop-filter on gradient bg.
Top-bar card
`::before` colored bar at top.
Sessions this week
1,248
+12% vs last week
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.

Line skeleton
Card skeleton
Row skeleton (violet shimmer variant)
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`.

No data yet
Create your first item to get started.
Something went wrong
We couldn't load the data. Check your connection.
No results
Try adjusting your search or filters.
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.

Name-hashed colors
Role variants
AD
Admin
Gradient
TR
Teacher
Cyan-green
ST
Student
Subtle violet
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.

Total sessions
1,248
+12% this week
Avg. score
78%
Stable
Active classes
24
+3 new
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>

## Sidebar Nav

`.sb-link` with active state indicator. Collapses to icon-only at 62px.

Dashboard Students 3 Library Settings
Code snippet

    <a class="sb-link active" href="/dashboard">
      <span class="sb-icon"><i data-lucide="layout-dashboard"></i></span>
      <span class="sb-lbl">Dashboard</span>
    </a>
    <a class="sb-link" href="/students">
      <span class="sb-icon"><i data-lucide="users"></i></span>
      <span class="sb-lbl">Students</span>
      <span class="sb-badge">3</span>
    </a>

## 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.

Good morning, Maxim
Subjects
8
Students
128
Avg. score
78%

## Bento Grid

3-column masonry-like grid with spanning cells for dashboard layouts.

Wide cell (span 2)
grid-column: span 2
Tall cell (span 2 rows)
grid-row: span 2
Cell A
Cell B
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.

Simulate prefers-reduced-motion
Spring
--ease-spring
0.22s
Ease-out
--ease-out
0.22s
Slow
--duration-slow
0.40s
Shimmer
.ls-sk continuous
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).

Focus ring (Tab through these)
Link C
Touch target 44x44px (dashed outline = target area)
Nav link

## Contrast Checker

Compute WCAG 2.1 contrast ratio live. AA requires 4.5:1 (normal text), 3:1 (large text).

Text color (hex)
Background color (hex)
Sample text on background
Design system contrast checks

## 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.

Don't
`color: #9B5DE5;`
Hardcoded hex bypasses theming
Do
`color: var(--violet);`
Use CSS token — survives palette changes
Don't
`style="display:none"`
Inline style overrides specificity
Do
`class="hidden"`
Utility class, toggled with JS
Don't
`style="background:#9B5DE5;color:#fff;border-radius:999px;..."`
Re-inventing existing components
Do
`<button class="btn-primary">`
Reuse design system component
Don't
`style="padding:12px"`
Hardcoded spacing breaks scale
Do
`class="p-3"`
Token-backed utility (= 12px = --space-3)