Icons Pack
Material Symbols Outlined variable icon font. Context-aware weight and optical size. Attractor animations via data-vb-attract.
| Type | Full (theme + effects + JS) |
| Theme CSS | packs/icons.theme.css |
| Effects CSS | packs/icons.effects.css |
| Effects JS | packs/icons.effects.js |
| Full bundle | packs/icons.full.css + packs/icons.full.js |
| Font weight | ~3.8MB (full set) — subset for production |
Coexistence with icon-wc
This pack coexists with the existing <icon-wc> SVG icon system. Use whichever fits your needs:
<icon-wc name="menu"> — SVG icons (lucide etc.), unchanged
<span class="vb-icon">menu</span> — Material Symbols font icons (when pack loaded)
Loading
<link rel="stylesheet" href="/cdn/packs/icons.full.css">
<script type="module" src="/cdn/packs/icons.full.js"></script>
<!-- Icons without attractor animations -->
<link rel="stylesheet" href="/cdn/packs/icons.theme.css">
Basic Usage
<!-- Inline with text -->
<p>Upload your files <span class="vb-icon">upload</span> to get started.</p>
<!-- Button with fill-on-hover -->
<button data-vb-icon-fill="hover">
<span class="vb-icon">favorite</span> Save
</button>
<!-- Standalone large icon -->
<span class="vb-icon" data-size="xl">notifications</span>
<!-- Always filled -->
<span class="vb-icon filled">star</span>
Context-Aware Sizing
Icons automatically adjust weight and optical size based on their context:
| Context | Weight | Optical Size |
h1, h2 | 600 | 48 |
h3, h4 | 500 | 40 |
button | Inherits text weight | 24 |
small, caption | 400 | 20 |
Icon Axes (Custom Properties)
| Property | Range | Default | Effect |
--vb-icon-fill | 0–1 | 0 | Outline to filled |
--vb-icon-weight | 100–700 | 400 | Stroke weight |
--vb-icon-grad | −50–200 | 0 | Grade (weight without spacing shift) |
--vb-icon-opsz | 20–48 | 24 | Optical size |
Attractor Animations
Add data-vb-attract to animate an icon. Animations respect prefers-reduced-motion.
<!-- Pulse: fill breathes 0 → 1 → 0 -->
<span class="vb-icon" data-vb-attract="pulse">notifications</span>
<!-- Beat: weight surges like a heartbeat -->
<span class="vb-icon" data-vb-attract="beat">favorite</span>
<!-- Bounce: vertical nudge, 3 times -->
<span class="vb-icon" data-vb-attract="bounce">arrow_downward</span>
<!-- Wiggle: rotation shake, suggests error -->
<span class="vb-icon" data-vb-attract="wiggle">warning</span>
<!-- Breathe: slow continuous fill oscillation -->
<span class="vb-icon" data-vb-attract="breathe">mic</span>
| Type | Meaning | Iterations |
pulse | Something happened here | 3 |
beat | New/unread content | 3 |
bounce | Call to action | 3 |
wiggle | Error/warning | 3 |
breathe | Active/live signal | Infinite |
Declarative Triggers
Use data-vb-attract-on for event-driven attractors without JS:
<!-- Animate when scrolled into view -->
<span class="vb-icon" data-vb-attract="pulse"
data-vb-attract-on="visible">star</span>
<!-- Animate on hover -->
<span class="vb-icon" data-vb-attract="beat"
data-vb-attract-on="hover">favorite</span>
JS API
import { attract, attractRandom, enableWhimsy } from 'vanilla-breeze/icons-js';
// Trigger a specific attractor
attract(document.querySelector('.vb-icon'), 'pulse');
// Random weighted attractor
attractRandom(iconElement);
// Idle whimsy mode (opt-in)
const { stop } = enableWhimsy(45000);
Whimsy can also be enabled declaratively: <body data-vb-whimsy>