Vitra · Docs v1.11.0

Vitra CSS

Premium glassmorphism, directional particles, ambient scenery and smooth motion — CSS-only core, optional JS, zero dependencies.

v1.11.0 CSS-only core 0 dependencies

Introduction

Vitra is a lightweight, zero-dependency CSS framework built around glassmorphism, directional particles, ambient scenery, and smooth motion. The core is pure CSS: themes, glass surfaces, layout, components, and utilities all work from a single stylesheet.

JavaScript is optional and progressive. A small script (~6 kB min) adds interactive behavior — theme persistence, modals, tooltips, toasts, dropdowns, particles — but nothing in the CSS depends on it. If the script never loads, every component still renders and the page stays usable.

This page is built with Vitra itself: the glass header and sidebar, the badges, the tabs below, the toast you'll see when you copy a snippet. Switch the theme from the header dropdown and the whole page re-colors. For a full visual tour, see the interactive demo.

Installation

Add the stylesheet to <head>:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/DesvoSoft/Vitra@v1.11.0/dist/vitra.min.css" crossorigin="anonymous">

Optionally add the script before </body> — it enables modals, tooltips, toasts, dropdowns, particles, and theme persistence:

<script src="https://cdn.jsdelivr.net/gh/DesvoSoft/Vitra@v1.11.0/dist/vitra.min.js" crossorigin="anonymous" defer></script>

Always pin a version tag (@v1.11.0) in production. Subresource-integrity hashes for each release are generated at build time and published in dist/SRI.txt.

Which file do I need?

FileUse
vitra.min.cssEverything visual. Required.
vitra.min.jsInteractive components and theme persistence. Optional.
vitra.esm.jsSame as above for bundlers, tree-shakeable imports.

Theming

Themes are applied with a single attribute on the root element. Every --vitra-* token — accent hue, backgrounds, surfaces, borders, text — updates instantly.

<html data-theme="dark">

Built-in themes

Seven presets, each with its own accent hue and tinted neutrals so every surface carries a trace of the accent:

light dark auto pastel neon ocean emerald

auto follows the operating system's prefers-color-scheme and re-resolves live when it changes.

Persistence

With the optional script, initialize once and the visitor's choice is stored in localStorage and restored on every page that calls init:

Vitra.theme.init({ defaultTheme: 'auto', persist: true });

This docs page and the demo share that stored choice — pick a theme here and the demo opens in it.

Creating a custom theme

A theme is a [data-theme] block that overrides tokens. Start from the accent and the base surfaces:

html[data-theme="sunset"] {
  --vitra-color-accent-h: 18;
  --vitra-color-accent-s: 85%;
  --vitra-color-accent-l: 58%;
  --vitra-color-bg: hsl(18deg 30% 8%);
  --vitra-color-surface: hsl(18deg 40% 90% / 6%);
  --vitra-color-surface-hover: hsl(18deg 40% 90% / 10%);
  --vitra-color-border: hsl(18deg 40% 90% / 12%);
  --vitra-color-text-primary: hsl(18deg 20% 95%);
  --vitra-color-text-secondary: hsl(18deg 15% 72%);
}

Set it with data-theme="sunset" directly. Note that Vitra.theme.set() only accepts the built-in names — apply custom themes via the attribute, or keep managing them yourself.

Components

Every example below is live — rendered by the same stylesheet you install. Components marked CSS-only need no JavaScript; needs JS means the optional script drives the behavior.

Buttons CSS-only

<button class="vitra-btn vitra-btn-solid">Solid</button>
<button class="vitra-btn vitra-btn-glass">Glass</button>
<button class="vitra-btn vitra-btn-outline">Outline</button>

Cards & Glass CSS-only

Glass card

Backdrop blur with a solid-color fallback when backdrop-filter is unsupported.

<div class="vitra-card vitra-glass">Hello, Vitra!</div>

Forms CSS-only

<input type="text" class="vitra-input" placeholder="Your name">

Tabs

Tab styling is CSS; the few lines that switch panels are yours to write (this page's switcher is ~15 lines — see the install section above working).

<div class="vitra-tabs">
  <div class="vitra-tabs-nav">
    <button class="vitra-tabs-tab vitra-tabs-tab-active" data-tab="tab1">One</button>
    <button class="vitra-tabs-tab" data-tab="tab2">Two</button>
  </div>
  <div class="vitra-tabs-content">
    <div class="vitra-tabs-panel" id="tab1">First panel</div>
    <div class="vitra-tabs-panel" id="tab2" style="display: none;">Second panel</div>
  </div>
</div>

Modals needs JS

Vitra.modal.open('my-modal');  // focus trap, Esc + overlay close, scroll lock

Tooltips needs JS

<button data-vitra-tooltip="Click to save" data-vitra-tooltip-position="top">Save</button>

Toasts needs JS

Vitra.toast.show('Saved successfully!', { type: 'success', duration: 3000 });

Dropdowns needs JS

<div class="vitra-dropdown">
  <button data-vitra-dropdown-toggle>Options</button>
  <div class="vitra-dropdown-menu">
    <button class="vitra-dropdown-item">Edit</button>
  </div>
</div>

Badges CSS-only

Default Primary Success Info Small
<span class="vitra-badge vitra-badge-primary">Primary</span>

Progress ring CSS-only

75%
<div class="vitra-progress-ring" style="--vitra-progress-ring-value: 75;">
  <div class="vitra-progress-ring-inner">75%</div>
</div>

Avatars CSS-only

JD
AB
<div class="vitra-avatar">JD</div>

Particles needs JS

Vitra.particles.spawn(10, { direction: 'down' });  // capped at 40 desktop / 15 mobile

JavaScript API

The script exposes a single global, Vitra, with one submodule per feature. ES module builds export the same modules for tree-shaking. Every module is optional — use only what you call.

Vitra.theme

MethodDoes
get()Current theme name ('auto' if unset).
set(name)Applies a built-in theme; returns false and warns on unknown names.
toggle()Switches light ↔ dark (resolves auto first); returns the new theme.
init(options)Restores stored choice; { defaultTheme: 'auto', persist: true }.
getEffective()Resolves auto to 'light' or 'dark'.
getValidThemes()Array of the seven built-in names.
clear()Removes the stored preference.

Vitra.particles

Vitra.particles.spawn(10, {
  container: 'body',
  size: 4,
  direction: 'down'   // 'down' | 'down-left' | 'down-right' | angle in degrees | omit for ambient bob
});
Vitra.particles.destroy();   // all, or destroy(n)
Vitra.particles.limits();    // { active, available, max }

Hard caps: 40 particles on desktop, 15 on mobile. spawn beyond the cap is ignored with a console warning.

Vitra.reveal

Vitra.reveal.init({
  selector: '.vitra-reveal',
  threshold: 0.1,
  rootMargin: '0px 0px 10% 0px',
  stagger: 100
});

Also count() (revealed so far) and reset() (re-arm everything). With prefers-reduced-motion, elements reveal immediately.

Vitra.modal

Vitra.modal.open('my-modal', { closeOnOverlay: true, closeOnEsc: true });
Vitra.modal.close();

open accepts an id, a selector, or an element. Any child with data-vitra-modal-close closes it.

Vitra.tooltip

Vitra.tooltip.show('#save-btn', 'Saves your draft', { position: 'top' });
Vitra.tooltip.hide();
Vitra.tooltip.init();   // activates all [data-vitra-tooltip] elements

Vitra.toast

Vitra.toast.show('Saved successfully!', { type: 'success', duration: 3000 });

Types: success, error, info, default. Markup is created for you; messages are rendered as text, never HTML.

Vitra.dropdown / Vitra.ripple / Vitra.spotlight

Behavior modules with init() / destroy(). They initialize automatically (see data-config): dropdowns from [data-vitra-dropdown-toggle], ripple from .vitra-ripple elements, spotlight cursor-tracking from .vitra-spotlight.

Vitra.destroyAll()

Tears down every module — removes all listeners and injected nodes. Useful in SPAs on route teardown.

data-config

Declarative initialization without writing a line of JS: put a JSON data-config attribute on any element (conventionally <body>) and Vitra parses it on DOMContentLoaded.

<body data-config='{
  "theme": { "default": "auto", "persist": true },
  "reveal": { "stagger": 100 },
  "particles": { "count": 12, "direction": "down" },
  "dropdown": false
}'>

Defaults

Since v1.10.3, ripple, tooltip, dropdown, and spotlight initialize by default — with or without a data-config element. Setting a module to false opts out. theme, particles, and reveal are opt-in: they only run when their key is present.

KeyDefaultValue
themeoffTheme name, or init() options object.
particlesofftrue, or spawn() options with count.
revealofftrue, or init() options object.
rippleonfalse to disable.
tooltiponfalse to disable.
dropdownonfalse to disable.
spotlightonfalse to disable.

Malformed JSON logs a [Vitra] console warning and falls back to the defaults above — it never throws.

Accessibility & Browser Support

Reduced motion

Every animation layer — particles, scenery, reveals, cinematic effects — ships inside @media (prefers-reduced-motion: reduce) guards. When the OS setting is on, ambient motion stops and reveal elements appear immediately. No configuration needed.

Graceful degradation

  • backdrop-filter (glass) is wrapped in @supports — unsupported browsers get a solid translucent surface instead.
  • Modern features (oklch(), Popover API, @starting-style, container queries) all carry fallbacks; nothing breaks below Tier 1.
  • Without JavaScript, all styling works; modals, toasts, dropdowns, and theme persistence simply don't activate.

Keyboard & screen readers

Modals trap focus and restore it on close; dropdowns close on Escape and refocus their toggle; theme changes are announced via an aria-live region; tooltips set aria-describedby.

Browser support

TierBrowsersWhat you get
Tier 1Chrome/Edge 117+, Firefox 129+, Safari 17.4+Full feature set, including Popover API and @starting-style.
Tier 2Chrome/Edge 88+, Firefox 97+, Safari 15.4+Everything core; advanced extras fall back (JS dropdown toggle, HSL colors, transition-based entries).
LegacyIE 11 and olderNot supported.