components
Same DOM in static HTML or React. Each component lists both. Pick the tab that matches your stack.
Mark
The brand glyph. SVG, scales with size, colors via currentColor.
<svg class="sn-mark sn-mark--lg" viewBox="0 0 32 32" fill="currentColor" aria-hidden="true">
<!-- crystal paths… -->
</svg>
<!-- pre-built sprite -->
<svg class="sn-mark sn-mark--lg"><use href="/icons/marks.svg#crystal"/></svg>import { MarkCrystal } from "@snowztech/ui";
<MarkCrystal size={48} />
<MarkCrystal size="lg" />
<MarkCrystal size={64} color="var(--sn-accent)" />Seven other variants live in the logo lab.
Monogram
Wraps any mark in a tile. App icons, brand stamps.
<span class="sn-monogram sn-monogram--md">
<svg class="sn-mark sn-mark--md" viewBox="0 0 32 32"><!-- mark --></svg>
</span>
<span class="sn-monogram sn-monogram--inverse">
<svg class="sn-mark sn-mark--md"><!-- mark --></svg>
</span>import { Monogram, MarkCrystal } from "@snowztech/ui";
<Monogram size={64} mark={MarkCrystal} />
<Monogram size={64} mark={MarkCrystal} bg="#f7f7f5" fg="#161616" radius={10} />Avatar
Image with initials fallback. Circle or square. Optional ring.
<span class="sn-avatar sn-avatar--lg">DR</span>
<span class="sn-avatar sn-avatar--lg sn-avatar--square">DR</span>
<span class="sn-avatar sn-avatar--lg sn-avatar--ring">DR</span>
<span class="sn-avatar sn-avatar--md">
<img src="/me.jpg" alt="">
</span>
<span class="sn-avatar-stack">
<span class="sn-avatar sn-avatar--md sn-avatar--ring">A</span>
<span class="sn-avatar sn-avatar--md sn-avatar--ring">B</span>
<span class="sn-avatar sn-avatar--md sn-avatar--ring">C</span>
</span>import { Avatar, AvatarStack } from "@snowztech/ui";
<Avatar size={48} name="DR" />
<Avatar size={48} src="/me.jpg" name="DR" />
<Avatar size={48} name="DR" shape="square" ring />
<AvatarStack overlap={10}>
<Avatar size={32} name="Ada" ring />
<Avatar size={32} name="Bo" ring />
</AvatarStack>Wordmark
<span class="sn-wordmark" style="--sn-wordmark-size: 24px">
snowztech<span class="sn-wordmark__dot">.</span>
</span>import { SnowWordmark } from "@snowztech/ui";
<SnowWordmark size={24} />Pulse
Status dot with breathing animation.
<span class="sn-pulse sn-pulse--success">
<span class="sn-pulse__dot"></span>
<span>online</span>
</span>
<span class="sn-pulse sn-pulse--warning">
<span class="sn-pulse__dot"></span>
<span>degraded</span>
</span>import { Pulse } from "@snowztech/ui";
<Pulse label="online" />
<Pulse color="var(--sn-warning)" label="degraded" />
<Pulse color="var(--sn-danger)" label="down" />Buttons
.sn-btn + a variant. Sizes via --sm / --lg.
{/* variants */}
<button className="sn-btn sn-btn--primary">Primary</button>
<button className="sn-btn sn-btn--secondary">Secondary</button>
<button className="sn-btn sn-btn--ghost">Ghost</button>
<button className="sn-btn sn-btn--accent">Accent</button>
<button className="sn-btn sn-btn--danger">Danger</button>
{/* sizes */}
<button className="sn-btn sn-btn--primary sn-btn--sm">Small</button>
<button className="sn-btn sn-btn--primary sn-btn--lg">Large</button>Badges
<span className="sn-badge">default</span>
<span className="sn-badge sn-badge--accent">accent</span>
<span className="sn-badge sn-badge--success">ok</span>
<span className="sn-badge sn-badge--warning">warn</span>
<span className="sn-badge sn-badge--danger">err</span>Input
Single-line text input. Sizes sm / md / lg. Pass invalid for error state.
<input class="sn-input" placeholder="Search…" />
<input class="sn-input sn-input--sm" placeholder="small" />
<input class="sn-input sn-input--lg" placeholder="large" />
<input class="sn-input sn-input--invalid" value="not-an-email" />
<input class="sn-input" disabled placeholder="disabled" />import { Input } from "@snowztech/ui";
<Input placeholder="Search…" />
<Input size="sm" placeholder="small" />
<Input size="lg" placeholder="large" />
<Input invalid defaultValue="not-an-email" />
<Input disabled placeholder="disabled" />Textarea
Multi-line text. Resize vertical by default.
<textarea class="sn-textarea" placeholder="Paste your text here…"></textarea>
<textarea class="sn-textarea sn-textarea--invalid">boom</textarea>import { Textarea } from "@snowztech/ui";
<Textarea placeholder="Paste your text here…" />
<Textarea invalid defaultValue="boom" />File input
Styled via ::file-selector-button — no JS, native input. Works in React too: <input type="file" className="sn-file" />.
<input type="file" className="sn-file" />
<input type="file" className="sn-file" accept=".pdf" />
<input type="file" className="sn-file" disabled />Field
Label + control + hint/error in one row. Use Field for forms; drop to Label if you need to compose manually.
<div class="sn-field">
<label class="sn-label" for="email">Email<span class="sn-label__required">*</span></label>
<input id="email" class="sn-input" type="email" placeholder="you@snowztech.com" />
<span class="sn-hint">We never share it.</span>
</div>
<div class="sn-field">
<label class="sn-label" for="user">Username</label>
<input id="user" class="sn-input sn-input--invalid" value="lucas" />
<span class="sn-error" role="alert">Already taken.</span>
</div>import { Field, Input, Textarea } from "@snowztech/ui";
<Field label="Email" hint="We never share it." htmlFor="email" required>
<Input id="email" type="email" placeholder="you@snowztech.com" />
</Field>
<Field label="Bio" htmlFor="bio">
<Textarea id="bio" placeholder="A few words…" />
</Field>
<Field label="Username" htmlFor="user" error="Already taken.">
<Input id="user" invalid defaultValue="lucas" />
</Field>Theme toggle
Tiny button that flips data-theme on <html> and persists to localStorage. React version is a client component.
<button class="sn-theme-toggle" data-sn-theme-toggle aria-label="toggle theme">
<span class="sn-theme-toggle__indicator"></span>
</button>
<!-- auto-wires [data-sn-theme-toggle] on load -->
<script type="module"
src="https://cdn.jsdelivr.net/npm/@snowztech/ui-js/dist/snowztech-ui.esm.js"></script>import { ThemeToggle } from "@snowztech/ui/client";
<ThemeToggle />
<ThemeToggle size={16} defaultTheme="dark" storageKey="theme" />Card
Flat by default. Compose with --rounded, --accent, --inset.
<div className="sn-card">…</div>
<div className="sn-card sn-card--rounded">…</div>
<div className="sn-card sn-card--accent">…</div>
<div className="sn-card sn-card--inset">…</div>
{/* compose */}
<div className="sn-card sn-card--rounded sn-card--accent">…</div>