SniceSnice

Accordion

Expandable content sections

What is Snice?

A TypeScript web component library with decorators, differential rendering, and separation of concerns.

How do I install it?

Run npm install snice to get started. Components are tree-shakeable.

Is it accessible?

Yes! All components follow WCAG guidelines with keyboard navigation and ARIA attributes.

html <snice-accordion> <snice-accordion-item item-id="faq-1" open> <span slot="header">Question?</span> <p>Answer content here.</p> </snice-accordion-item> </snice-accordion>
Elevated variant
Surface elevation

Items float with shadows instead of borders. Expanded items get higher elevation.

Standalone cards

Each item is a standalone card with gap spacing between them.

Smooth transitions

Shadow depth transitions smoothly on expand/collapse.

html <snice-accordion variant="elevated"> <snice-accordion-item item-id="elev-1" open> <span slot="header">Surface elevation</span> <p>Items float with shadows.</p> </snice-accordion-item> </snice-accordion>

Action Bar

Positioned, animated container for contextual actions

Hover me
html <div style="position: relative" @mouseenter=${() => bar.show()} @mouseleave=${() => bar.hide()}> Hover me <snice-action-bar position="bottom" variant="pill"> <snice-button variant="text" circle size="small"> <svg slot="icon" viewBox="0 0 24 24"><!-- pencil --></svg> </snice-button> <snice-button variant="text" circle size="small"> <svg slot="icon" viewBox="0 0 24 24"><!-- clipboard --></svg> </snice-button> <snice-button variant="danger" circle size="small"> <svg slot="icon" viewBox="0 0 24 24"><!-- trash --></svg> </snice-button> </snice-action-bar> </div>
Always visible
html <snice-action-bar no-animation position="top-right" size="small" > <snice-button variant="text" circle size="small"> <svg slot="icon" viewBox="0 0 24 24"><!-- pencil --></svg> </snice-button> <snice-button variant="danger" circle size="small"> <svg slot="icon" viewBox="0 0 24 24"><!-- trash --></svg> </snice-button> </snice-action-bar>

Activity Feed

Chronological stream of user activities with filtering

javascript <snice-activity-feed group-by="date" ></snice-activity-feed> feed.activities = [ { id: '1', actor: { name: 'Alice' }, action: 'created', target: 'Project Alpha', type: 'create', timestamp: new Date().toISOString() } ];

Alert

Contextual feedback messages

This is an informational message. Use it to provide helpful context. Your changes have been saved successfully. Please review your input before continuing. An error occurred. Please try again.
html <snice-alert variant="info" title="Information" dismissible> This is an informational message. </snice-alert> <snice-alert variant="success" title="Success"> Your changes have been saved. </snice-alert> <snice-alert variant="error" title="Error"> An error occurred. </snice-alert>

App Tiles

App launcher grid like Google's app drawer or a phone home screen

material icons
html <snice-app-tiles columns="3"> <snice-app-tile name="Mail" icon="mail" color="rgb(220 38 38)"></snice-app-tile> <snice-app-tile name="Videos" icon="play_circle" badge="5"></snice-app-tile> </snice-app-tiles>
letter fallback
html <!-- No icon = colored letter --> <snice-app-tiles columns="3"> <snice-app-tile name="Slack" color="rgb(97 31 105)"> </snice-app-tile> <snice-app-tile name="AWS" color="rgb(255 153 0)" badge="!"> </snice-app-tile> </snice-app-tiles>
list variant
html <snice-app-tiles columns="1" variant="list"> <snice-app-tile name="Dashboard" icon="dashboard" color="rgb(37 99 235)"> </snice-app-tile> <snice-app-tile name="Messages" icon="chat" badge="8"> </snice-app-tile> </snice-app-tiles>
compact variant
html <snice-app-tiles columns="2" variant="compact"> <snice-app-tile name="Dashboard" icon="dashboard" color="rgb(37 99 235)"> </snice-app-tile> <snice-app-tile name="Messages" icon="chat" badge="8"> </snice-app-tile> </snice-app-tiles>
small
large
xl
2xl
html <!-- size: sm, md, lg, xl, 2xl --> <snice-app-tiles columns="2" size="xl"> <snice-app-tile name="Photos" icon="photo_library"> </snice-app-tile> </snice-app-tiles> <!-- Custom size via CSS variable --> <snice-app-tiles columns="2" style="--snice-app-tile-icon-size:7rem"> <snice-app-tile name="Gallery" icon="collections"> </snice-app-tile> </snice-app-tiles>
image icons
js tiles.columns = 3; tiles.tiles = [ { id: '1', name: 'Chrome', icon: 'https://cdn.../chrome.svg' }, { id: '2', name: 'Slack', icon: 'https://cdn.../slack.svg' }, ]; tiles.addEventListener('tile-click', e => console.log(e.detail.tile));

Approval Flow

Visual approval chain with approver avatars, status indicators, and action buttons

Horizontal
Vertical
javascript <snice-approval-flow id="flow" orientation="horizontal" current-step="step-3" ></snice-approval-flow> <script> flow.steps = [ { id: 'step-1', approver: 'Alice Chen', role: 'Manager', status: 'approved', timestamp: 'Feb 25, 10:00 AM' }, { id: 'step-2', approver: 'Bob Smith', role: 'Director', status: 'approved', comment: 'Looks good' }, { id: 'step-3', approver: 'Carol Wu', role: 'VP Finance', status: 'pending' } ]; flow.addEventListener('step-approve', e => console.log(e.detail)); </script>

Audio Recorder

Voice recording (requests mic permission on record)

html <snice-audio-recorder show-controls show-visualizer show-timer show-playback ></snice-audio-recorder>

Availability

Weekly availability grid with click/drag selection, presets, and configurable time slots

javascript <snice-availability id="avail" granularity="60" start-hour="8" end-hour="20" format="12h" ></snice-availability> <script> // Pre-fill with business hours avail.value = [ { day: 0, start: '09:00', end: '17:00' }, { day: 1, start: '09:00', end: '17:00' }, { day: 2, start: '09:00', end: '17:00' }, { day: 3, start: '09:00', end: '17:00' }, { day: 4, start: '09:00', end: '17:00' } ]; avail.addEventListener('availability-change', e => console.log(e.detail.value)); </script>

Avatar Group

Stacked avatar list with overflow indicator

html <snice-avatar-group id="group" max="4" size="medium"> </snice-avatar-group> <script> group.avatars = [ { name: 'Alice Chen', src: 'assets/avatars/pravatar-80-1.jpg' }, { name: 'Bob Rivera' }, { name: 'Carol Kim', src: 'assets/avatars/pravatar-80-3.jpg' }, { name: 'Dave Patel' }, { name: 'Eve Martinez' }, { name: 'Frank Lee' } ]; </script> <!-- Sizes: small, medium, large --> <!-- max: visible count before +N -->

Avatar

User profile images with fallback initials

html <snice-avatar name="Alice Johnson" size="large"> </snice-avatar> <snice-avatar src="avatar.jpg" name="User" ></snice-avatar>
html <!-- Shapes --> <snice-avatar shape="circle" name="Circle"></snice-avatar> <snice-avatar shape="square" name="Square"></snice-avatar> <snice-avatar shape="rounded" name="Rounded"></snice-avatar> <!-- Auto-generated colors from name --> <snice-avatar name="Alice"></snice-avatar> <snice-avatar name="Bob"></snice-avatar> <!-- Custom fallback colors --> <snice-avatar name="Red" fallback-background="#dc2626" fallback-color="#fff"> </snice-avatar>

Badge

Notification indicators and count displays

Messages Online Updates
html <snice-badge count="5"> <snice-button>Messages</snice-button> </snice-badge> <!-- Live status — pulsing dot --> <snice-badge dot pulse variant="success"> <snice-button>Online</snice-button> </snice-badge> <!-- Overflow: shows 99+ when count > max --> <snice-badge count="150" max="99" variant="primary"> <snice-button>Updates</snice-button> </snice-badge>
Status Priority Release
html <snice-badge inline content="Active" variant="success" ></snice-badge> <!-- Default (neutral) for non-semantic labels --> <snice-badge inline content="New"></snice-badge>

Banner

Full-width notification banners

html <snice-banner variant="info" message="New version available!" action-text="Update Now" dismissible ></snice-banner> <!-- Variants: info, success, warning, error -->

Binpack

Draggable bin-packing layout — items fill gaps like Tetris. Drag widgets to rearrange.

💰 Revenue
$48.2k
▲ 12.5% vs last month
Users
3,842
▲ 8%
CPU
74%
📦 Today's Orders
284
processed
82%
12%
6%
Mem
61%
🌐 Traffic Sources
Organic45%
Social28%
Direct18%
Other9%
Conv.
3.2%
Goal: 4%
Uptime
99.9%
30d avg
🔔 Alerts
⚠DB latency spike2m ago
✓Deploy complete14m ago
ℹScaling up +231m ago
Disk
71%
html <snice-binpack draggable gap="8px" column-width="80" row-height="80"> <div style="width:168px;height:168px"> Revenue widget... </div> <div style="width:80px;height:80px"> Users widget... </div> <!-- Items pack tightly, drag to rearrange --> </snice-binpack> <!-- Attributes --> <snice-binpack draggable <!-- enable drag-to-reorder --> gap="8px" <!-- spacing between items --> column-width="80" <!-- grid snap width --> row-height="80" <!-- grid snap height --> horizontal <!-- pack left-to-right --> stagger="40" <!-- cascade transition delay --> ></snice-binpack>

Book

CSS-only page-flip book with 3D transforms, based on the Foundation book preview CodePen

Web Components

Jane Developer

Contents

Chapter ICustom Elements3
Chapter IIShadow DOM15
Chapter IIITemplates27

Chapter I: Custom Elements

Web Components are a set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web pages and web apps.

Custom Elements let you define your own HTML elements with custom behavior and styling using the CustomElementRegistry.

The Custom Elements specification lays the foundation for designing and using new types of DOM elements that are fully-featured and conforming.

Chapter I (continued)

A custom element's name must contain a hyphen. This requirement ensures forward compatibility with future HTML elements and avoids naming collisions.

Elements can observe attribute changes via attributeChangedCallback, enabling reactive property updates from HTML attributes alone.

The lifecycle callbacks — connected, disconnected, adopted, and attribute-changed — give full control over the element's behavior in the DOM.

Chapter II: Shadow DOM

Shadow DOM provides encapsulation for DOM and CSS. A shadow tree is a hidden subtree attached to an element in the regular DOM tree.

Styles defined inside a shadow root do not leak out, and external styles do not bleed in. This is true encapsulation at the platform level.

Slots allow composition — the host element's light DOM children are projected into designated positions within the shadow tree.

Chapter III: Templates

The <template> element holds HTML content that is not rendered when the page loads, but can be instantiated later using JavaScript.

Template content is parsed but inert — scripts don't run, images don't load, and styles don't apply until the content is cloned into the live DOM.

Combined with custom elements and shadow DOM, templates provide the final piece for building fully self-contained, reusable components.

html <snice-book title="Web Components" author="Jane Developer" cover-image="cover.jpg" > <snice-book-page> <div slot="front">Title page</div> <div slot="back">Contents</div> </snice-book-page> <snice-book-page> <div slot="front">Chapter 1...</div> </snice-book-page> </snice-book> <script> book.nextPage(); book.goToPage(1); book.addEventListener('page-turn', e => console.log(e.detail)); </script>

Booking

Appointment booking widget with date picker, time slot selection, and confirmation form

javascript <snice-booking id="bk" duration="30" variant="stepper" ></snice-booking> <script> // Generate available dates const dates = []; for (let i = 1; i <= 14; i++) { const d = new Date(); d.setDate(d.getDate() + i); if (d.getDay() !== 0 && d.getDay() !== 6) dates.push(d.toISOString().slice(0,10)); } bk.availableDates = dates; bk.availableSlots = dates.flatMap( date => ['09:00','10:00','11:00', '14:00','15:00','16:00'].map( time => ({ date, time, duration: 30 }) )); bk.fields = [ { name: 'name', label: 'Full Name', type: 'text', required: true }, { name: 'email', label: 'Email', type: 'email', required: true }, { name: 'notes', label: 'Notes', type: 'textarea' } ]; bk.addEventListener('booking-confirm', e => console.log(e.detail)); </script>

Breadcrumbs

Navigation path indicators with separators, icons, sizes, and collapsible paths

Default
With Icons
Collapsible (max-items=3)
Small
Large
html <!-- Separators: / > » • | --> <snice-breadcrumbs separator="/"> <snice-crumb href="/" label="Home"></snice-crumb> <snice-crumb href="/docs" label="Docs"></snice-crumb> <snice-crumb label="API" active></snice-crumb> </snice-breadcrumbs> <!-- With icons --> <script> el.items = [ { label: 'Home', href: '/', icon: '🏠' }, { label: 'Products', href: '/products', icon: '📦' }, { label: 'Details' } ]; </script> <!-- Collapse long paths --> <snice-breadcrumbs max-items="3">...</snice-breadcrumbs> <!-- Sizes: small, medium, large --> <snice-breadcrumbs size="small">...</snice-breadcrumbs>

Button

All variants, sizes, shapes, and states

Default Primary Success Warning Danger Text
html <snice-button variant="primary">Primary</snice-button> <snice-button variant="success">Success</snice-button> <snice-button variant="danger" outline>Danger</snice-button> <snice-button variant="primary" pill>Pill</snice-button> <snice-button variant="primary" circle>+</snice-button>
Outline Success Danger Warning
html <snice-button variant="primary" outline>Outline</snice-button> <snice-button variant="success" outline>Success</snice-button>
Small Medium Large Pill + ✓ ×
html <snice-button variant="primary" size="small"> Small </snice-button> <snice-button variant="primary" size="large"> Large </snice-button> <snice-button variant="primary" pill>Pill</snice-button> <snice-button variant="primary" circle>+</snice-button>
Icon Start Icon End Search Delete
html <snice-button variant="primary"> <svg slot="icon" viewBox="0 0 24 24" fill="currentColor"> <path d="M14.6 1.6 3.75 12.75h6L8.75 22.4 20.25 9.75h-7.27L14.6 1.6Z"/> </svg> Icon Start </snice-button> <snice-button icon-placement="end"> <svg slot="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="m4.5 12.75 6 6 9-13.5"/> </svg> Icon End </snice-button>
Loading Disabled
html <snice-button variant="primary" loading>Loading</snice-button> <snice-button variant="primary" disabled>Disabled</snice-button>

Calendar

Date picker calendar with events and scheduled slots

javascript <snice-calendar id="cal" highlight-today ></snice-calendar> <script> cal.events = [ { id: 1, title: 'Sprint Review', start: inDays(1), color: '#3b82f6', avatar: { src: 'assets/avatars/pravatar-40-alice.jpg?v=80938d7', name: 'Dana Ives' } }, { id: 2, title: 'Conference', start: inDays(3), end: inDays(9), color: '#22c55e', tooltip: 'Berlin, hall 2' }, { id: 3, title: 'Planning', start: inDays(4), color: '#f59e0b' } ]; </script>

Camera Annotate

Image annotation with camera capture, freehand drawing, and labeled annotations

Open Camera Annotate
html <snice-camera-annotate></snice-camera-annotate>
Without Labels Panel
html <!-- Hide the labels sidebar --> <snice-camera-annotate show-labels-panel="false" ></snice-camera-annotate> <!-- Disable auto color rotation --> <snice-camera-annotate auto-rotate-colors="false" ></snice-camera-annotate>

Camera

Webcam capture (requires permission)

Open Camera
html <snice-camera show-controls controls-position="bottom" auto-start="false" ></snice-camera>

Camera Capture

Close

Candlestick

SVG-based OHLC candlestick chart for financial data

html + js <snice-candlestick id="chart" show-volume y-axis-format="currency" ></snice-candlestick> <script> chart.data = [ { date: '2024-01-02', open: 185.09, high: 188.44, low: 183.89, close: 186.01, volume: 5820000 }, { date: '2024-01-03', open: 186.50, high: 187.10, low: 183.42, close: 184.25, volume: 4910000 }, // ...more candles ]; </script>
html <!-- Minimal: no volume, default format --> <snice-candlestick id="chart"> </snice-candlestick> <script> chart.data = [ { date: '2024-03-01', open: 170, high: 175, low: 168, close: 173 }, { date: '2024-03-04', open: 173, high: 178, low: 171, close: 176 }, ]; </script>

Card

Content containers with header, footer, and image slots

TECHNOLOGY

The Future of Web Components

Exploring emerging standards and the rise of framework-free development...

5 min read Mar 15, 2025
DESIGN

UI/UX Best Practices

Essential design principles for intuitive, beautiful interfaces...

8 min read Mar 14, 2025
DEVELOPMENT

Building Scalable Apps

Architect applications that grow with your business needs...

12 min read Mar 13, 2025
SECURITY

Auth Patterns in 2025

Modern authentication strategies for web applications...

7 min read Mar 12, 2025

Total Revenue

$48.2k

↑ 12.5% from last month

Active Users

2,847

↑ 8.1% from last week

Conversion Rate

3.24%

↓ 2% from last month

Avg. Response

142ms

↑ 15% faster

html <!-- Blog card with gradient image slot --> <snice-card variant="elevated" clickable> <div slot="image" style="background:linear-gradient(...)"> <svg><!-- code icon --></svg> </div> <h4>Title</h4> <p>Description...</p> <div slot="footer">5 min read</div> </snice-card> <!-- Dashboard stat card --> <snice-card variant="flat"> <p>Total Revenue</p> <p style="font-size:1.5rem">$48.2k</p> <p style="color:green">↑ 12.5%</p> </snice-card>

Carousel

Image and content slider with controls, autoplay, and multi-slide views

with controls Slide 1 Slide 2 Slide 3
autoplay Auto 1 Auto 2 Auto 3
multi-slide 1 2 3 4 5 6
html <!-- With controls --> <snice-carousel show-controls show-indicators loop> <img src="photo1.jpg?v=80938d7"> <img src="photo2.jpg?v=80938d7"> </snice-carousel> <!-- Autoplay --> <snice-carousel autoplay autoplay-interval="2000" loop >...</snice-carousel> <!-- Multiple slides per view --> <snice-carousel slides-per-view="3" space-between="12" >...</snice-carousel>

Cart

Shopping cart with line items, quantity controls, coupon field, and checkout summary

javascript <snice-cart id="cart" tax-rate="8.5" ></snice-cart> <script> cart.items = [ { id: '1', name: 'Wireless Headphones', price: 99.99, quantity: 1, image: 'headphones.jpg' }, { id: '2', name: 'Yoga Mat Premium', price: 49.99, quantity: 2, variant: 'Sage Green' } ]; cart.addEventListener('checkout', e => console.log(e.detail)); </script>

Chart & Sparkline

Line, bar, pie charts and inline mini charts

Revenue
Users
Errors
html <snice-chart id="chart" type="line" height="200"> </snice-chart> <snice-sparkline type="line" color="success" width="80" height="24" ></snice-sparkline> <script> chart.labels = ['Jan', 'Feb', 'Mar']; chart.datasets = [{ label: 'Sales', data: [30, 45, 60] }]; </script>

Chat

Slack-style messaging — theming, markdown, bubble layouts, declarative messages

html <!-- Default --> <snice-chat current-user="You"></snice-chat> <script> chat.messages = [ { id:'1', type:'text', author:'Alice', content:'Hey! How’s it going?', timestamp:new Date() } ]; </script>
html <!-- Auto color per author --> <snice-chat color-authors></snice-chat> <!-- or explicit --> chat.authorColors = { Alice: '#e11d48', Bob: '#2563eb' };
html <!-- Render bodies as markdown --> <snice-chat markdown></snice-chat> chat.addMessage({ type:'text', author:'Alice', format:'markdown', content:'**Release 5.3** 🎉\n\n- colors\n- bubbles\n\n`npm i snice@latest`', timestamp:new Date() });
html <!-- Mobile-OS aligned bubbles --> <snice-chat layout="bubbles" current-user="You" ></snice-chat>
Hey, you around? yep, one sec check the **PR** then
html <!-- Author messages as children --> <snice-chat current-user="Me" color-authors> <snice-chat-message slot="messages" author="Alice">Hey, you around?</snice-chat-message> <snice-chat-message slot="messages" author="Me">yep, one sec</snice-chat-message> <snice-chat-message slot="messages" author="Alice" format="markdown">check the **PR**</snice-chat-message> </snice-chat>

Checkbox

Toggle selections with checked, indeterminate, and loading states

html <snice-checkbox label="Remember me" checked> </snice-checkbox> <snice-checkbox label="Indeterminate" indeterminate> </snice-checkbox> <!-- Sizes --> <snice-checkbox label="Small" size="small"></snice-checkbox> <snice-checkbox label="Large" size="large"></snice-checkbox> <!-- States --> <snice-checkbox label="Disabled" disabled></snice-checkbox> <snice-checkbox label="Loading" loading></snice-checkbox> <snice-checkbox label="Invalid" invalid></snice-checkbox>

Chip

Tags, filters, and selections

html <!-- Status chips: pick a semantic variant --> <snice-chip label="Active" variant="success"></snice-chip> <snice-chip label="Pending" variant="warning"></snice-chip> <snice-chip label="Failed" variant="error"></snice-chip> <snice-chip label="Archived"></snice-chip>
html <!-- Filter chips: click-to-toggle, neutral by default --> <snice-chip label="All" selectable selected></snice-chip> <snice-chip label="Open" selectable></snice-chip> <snice-chip label="Closed" selectable></snice-chip> <snice-chip label="Mine" selectable></snice-chip>
html <!-- Tag-input style: X to remove --> <snice-chip label="typescript" removable></snice-chip> <snice-chip label="web-components" removable></snice-chip> <snice-chip label="design-systems" removable></snice-chip>
html <!-- Sizes --> <snice-chip label="Small" size="small"></snice-chip> <snice-chip label="Large" size="large"></snice-chip> <!-- Shapes --> <snice-chip label="Pill"></snice-chip> <!-- default --> <snice-chip label="Rounded" shape="rounded"></snice-chip> <snice-chip label="Square" shape="square"></snice-chip>

Code Block

Syntax-highlighted code display with formatting

import { element, property, render, dispatch } from 'snice'; @element('my-counter') export class MyCounter extends HTMLElement { @property({ type: Number }) count = 0; @property({ type: Number }) step = 1; @property({ type: Number }) min = 0; @property({ type: Number }) max = 100; increment() { this.count = Math.min(this.count + this.step, this.max); this.emitChange(); } decrement() { this.count = Math.max(this.count - this.step, this.min); this.emitChange(); } reset() { this.count = 0; this.emitChange(); } @dispatch('count-change', { bubbles: true }) private emitChange() { return { count: this.count, step: this.step }; } @render() template() { const pct = ((this.count - this.min) / (this.max - this.min)) * 100; return html` <div class="counter"> <button @click="${this.decrement}">-</button> <span class="value">${this.count}</span> <button @click="${this.increment}">+</button> </div> <div class="bar" style="width:${pct}%"></div> `; } }
html <snice-code-block language="snice" grammar="grammars/snice.json?v=80938d7" format="pretty" show-line-numbers copyable filename="example.ts" > const x = 42; </snice-code-block> <!-- Custom formatter (same pattern as setHighlighter) --> <script> cb.setFormatter((code, language) => { // format → then highlight runs automatically return myPrettier.format(code, { parser: language }); }); </script>

Color Display

Color swatch with label

html <snice-color-display value="#3b82f6" label="Primary" show-swatch show-label ></snice-color-display>

Color Picker

Interactive color selection

html <snice-color-picker value="#3b82f6" label="Theme Color" show-presets ></snice-color-picker>
html <snice-color-picker value="#3b82f6" loading></snice-color-picker>

Command Palette

Keyboard-driven command search (Ctrl/Cmd+K)

Open Command Palette or press Ctrl+K
html <snice-command-palette id="cmd" placeholder="Type a command..." ></snice-command-palette> <script> cmd.commands = [ { id: 'save', label: 'Save', shortcut: 'Ctrl+S' } ]; </script>

Comments

Threaded comment section with nested replies, likes, avatars, and timestamps

Love how clean the component API is. The threaded replies make conversations easy to follow! Totally agree. The like/unlike toggle is a nice touch too. Try typing a comment above and clicking Post. You can also reply to, like, or delete your own comments!
html <snice-comments current-user="Alice" allow-replies allow-likes> <snice-comment author="Bob" avatar="bob.jpg" timestamp="2025-06-15T10:30:00Z" likes="3"> Great post! <snice-comment author="Alice" timestamp="2025-06-15T11:00:00Z"> Thanks Bob! </snice-comment> </snice-comment> <snice-comment author="Charlie" timestamp="2025-06-15T12:00:00Z"> Very informative. </snice-comment> </snice-comments>
javascript el.comments = [ { id: '1', author: 'Bob', avatar: 'avatar.jpg', text: 'Great post!', timestamp: new Date().toISOString(), likes: 3, liked: false, replies: [ { id: '2', author: 'Alice', text: 'Thanks!', timestamp: new Date().toISOString() } ] } ]; el.addEventListener('comment-add', e => console.log(e.detail));

Countdown

Countdown timer to a target date with flip, circular, and simple variants

html <snice-countdown target="2026-12-31T00:00:00Z" format="dhms" variant="flip" ></snice-countdown> <snice-countdown variant="circular" format="hms" ></snice-countdown> <script> cd.addEventListener('countdown-complete', () => alert('Done!')); </script>

Cropper

Image cropping with drag-to-resize, rotation, zoom, and aspect ratio lock

html <snice-cropper src="/photo.jpg?v=80938d7" aspect-ratio="1.777" output-type="jpeg" ></snice-cropper> <script> // Get cropped image const blob = await cropper.crop(); // Rotate and zoom cropper.rotate(90); cropper.zoom(1.5); cropper.reset(); </script>

Data Card

Key-value detail panels with grouped fields and inline editing

Customer Details
html <snice-data-card id="card"> <span slot="title">Customer Details</span> </snice-data-card> <script> card.fields = [ { label: 'Name', value: 'Alice Johnson', group: 'Contact' }, { label: 'Email', value: 'alice@acme.co', type: 'link', href: 'mailto:alice@acme.co', group: 'Contact' }, { label: 'Status', value: 'Active', type: 'badge', badgeVariant: 'success', group: 'Account' }, { label: 'Balance', value: '$12,450', type: 'currency', group: 'Account' } ]; </script>

Date Picker

Date selection with calendar, year picker, formats, and states

html <snice-date-picker label="Select Date" clearable ></snice-date-picker> <!-- Formats --> <snice-date-picker format="mm/dd/yyyy"></snice-date-picker> <snice-date-picker format="dd/mm/yyyy"></snice-date-picker> <snice-date-picker format="yyyy-mm-dd"></snice-date-picker> <snice-date-picker format="mmmm dd, yyyy"></snice-date-picker>
html <!-- Sizes --> <snice-date-picker size="small"></snice-date-picker> <snice-date-picker size="large"></snice-date-picker> <!-- Variants --> <snice-date-picker variant="filled"></snice-date-picker> <snice-date-picker variant="underlined"></snice-date-picker> <!-- States --> <snice-date-picker disabled></snice-date-picker> <snice-date-picker readonly></snice-date-picker> <snice-date-picker loading></snice-date-picker> <snice-date-picker required helper-text="Required"></snice-date-picker> <snice-date-picker invalid error-text="Invalid"></snice-date-picker>
Ready
html <form> <snice-date-picker name="delivery-date" value="2026-03-15" format="dd/mm/yyyy" min="2026-03-10" max="2026-03-20" required ></snice-date-picker> </form> <!-- FormData: delivery-date=2026-03-15 -->

Date Range Picker

Select a date range with calendar, presets, and year picker

html <snice-date-range-picker label="Date Range" clearable ></snice-date-range-picker> <snice-date-range-picker label="Dual Column" columns="2" clearable ></snice-date-range-picker>
html <snice-date-range-picker id="preset-picker" label="With Presets" columns="2" clearable ></snice-date-range-picker> <script> const picker = document.getElementById('preset-picker'); const today = new Date(); picker.presets = [ { label: 'Last 7 days', start: new Date(Date.now() - 7 * 86400000), end: today }, { label: 'Last 30 days', start: new Date(Date.now() - 30 * 86400000), end: today }, { label: 'This month', start: new Date(today.getFullYear(), today.getMonth(), 1), end: new Date(today.getFullYear(), today.getMonth() + 1, 0) }, ]; </script>
html <!-- Sizes --> <snice-date-range-picker size="small"></snice-date-range-picker> <snice-date-range-picker size="large"></snice-date-range-picker> <!-- States --> <snice-date-range-picker disabled></snice-date-range-picker> <snice-date-range-picker readonly></snice-date-range-picker> <snice-date-range-picker loading></snice-date-range-picker> <snice-date-range-picker required helper-text="Required"></snice-date-range-picker> <snice-date-range-picker invalid error-text="Invalid"></snice-date-range-picker>
Ready
html <form> <snice-date-range-picker name="booking" start="2026-03-10" end="2026-03-20" min="2026-03-01" max="2026-03-31" clearable required ></snice-date-range-picker> <button type="submit">Submit</button> <button type="reset">Reset</button> </form>

Date Time Picker

Combined date and time selection with calendar, time columns, and year picker

html <snice-date-time-picker label="Event Start" placeholder="Select date and time" ></snice-date-time-picker> <!-- 12h + US date format --> <snice-date-time-picker time-format="12h" date-format="mm/dd/yyyy" ></snice-date-time-picker> <!-- Long date format --> <snice-date-time-picker date-format="mmmm dd, yyyy" ></snice-date-time-picker> <!-- With seconds --> <snice-date-time-picker show-seconds></snice-date-time-picker> <!-- Clearable --> <snice-date-time-picker value="2026-03-15T10:30" clearable ></snice-date-time-picker>
html <!-- Sizes --> <snice-date-time-picker size="small"></snice-date-time-picker> <snice-date-time-picker size="large"></snice-date-time-picker> <!-- States --> <snice-date-time-picker disabled></snice-date-time-picker> <snice-date-time-picker readonly></snice-date-time-picker> <snice-date-time-picker loading></snice-date-time-picker> <snice-date-time-picker required helper-text="Required"></snice-date-time-picker> <snice-date-time-picker invalid error-text="Invalid"></snice-date-time-picker>
Ready
html <form> <snice-date-time-picker name="appointment" value="2026-03-10T14:05" min="2026-03-01T00:00" max="2026-03-31T23:59" show-seconds clearable required ></snice-date-time-picker> <button type="submit">Submit</button> <button type="reset">Reset</button> </form>

Diff

Code and text diff viewer with unified and split modes

html <snice-diff mode="unified" line-numbers context-lines="3" ></snice-diff> <script> diff.oldText = 'function hello() {\n' + ' return "hello";\n}'; diff.newText = 'function hello() {\n' + ' return "hello world";\n}'; </script>

Divider

Content separators in horizontal and vertical orientations

Home Products About
html <snice-divider text="OR"></snice-divider> <snice-divider orientation="vertical"></snice-divider> <snice-divider variant="dashed"></snice-divider>

Doc

Rich text document editor with formatting toolbar, headings, lists, images, links, tables, and download as HTML/Markdown/Text

html <snice-doc icons="material"></snice-doc> <script> editor.setHTML(` <h1>Welcome!</h1> <p>Rich text with <b>bold</b>, <i>italic</i>, <u>underline</u></p> <ul> <li>Headings (H1, H2, H3)</li> <li>Bullet and numbered lists</li> <li>Images, links, and tables</li> </ul> `); // Export as markdown const md = editor.getMarkdown(); // Download as file editor.downloadAs('markdown', 'my-doc.md'); </script>

Drawer

Slide-out panels from any edge with push, persist, and size options

Push Content

This drawer pushes sibling content.

Navigation
Dashboard Projects Settings
Details Panel

Additional information and actions can go here.

Notification Bar

You have 3 new notifications

Quick Actions
Save Cancel
Left Right Top Bottom Push Content
html <!-- Positions --> <snice-drawer position="left">...</snice-drawer> <snice-drawer position="right">...</snice-drawer> <snice-drawer position="top">...</snice-drawer> <snice-drawer position="bottom">...</snice-drawer> <!-- Sizes --> <snice-drawer size="small"></snice-drawer> <snice-drawer size="large"></snice-drawer> <snice-drawer size="full"></snice-drawer> <!-- Push content with target --> <snice-drawer id="my-drawer" push-content></snice-drawer> <snice-drawer-target for="my-drawer"> <main>Content moves when drawer opens</main> </snice-drawer-target> <!-- Features --> <snice-drawer no-backdrop></snice-drawer> <snice-drawer persistent></snice-drawer>

Inline Mode

Sits in the document flow as a persistent sidebar. Use breakpoint to auto-switch between inline and overlay.

Sidebar
Home Explore Settings

Main Content

The drawer sits inline alongside the content, no overlay or backdrop.

html <!-- Always inline --> <snice-drawer inline position="left" size="small"> <span slot="title">Sidebar</span> ... </snice-drawer> <!-- Responsive: inline above 768px, overlay below --> <snice-drawer breakpoint="768" position="left" size="small"> <span slot="title">Sidebar</span> ... </snice-drawer>

Empty State

Placeholder for empty content areas

html <snice-empty-state title="No results found" description="Try adjusting your search." > <svg slot="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"> <circle cx="11" cy="11" r="8"/> <line x1="21" y1="21" x2="16.65" y2="16.65"/> </svg> </snice-empty-state>

Estimate

Quote/estimate with optional line items, accept/decline actions, and expiry date

javascript <snice-estimate id="est" estimate-number="EST-0042" date="Feb 27, 2026" expiry-date="Mar 15, 2026" status="sent" currency="$" tax-rate="8" ></snice-estimate> <script> est.from = { name: 'Design Studio' }; est.to = { name: 'Client Corp' }; est.items = [ { description: 'Brand Identity', quantity: 1, unitPrice: 5000 }, { description: 'Social Media Kit', quantity: 1, unitPrice: 2000, optional: true } ]; est.addEventListener('estimate-accept', e => console.log(e.detail)); </script>

File Gallery

File management with previews and upload progress

html <snice-file-gallery accept="image/*" multiple view="grid" show-add-button allow-delete ></snice-file-gallery> <script> gallery.addEventListener( 'request/file-gallery-upload', (e) => { const { file, onProgress } = e.detail; e.preventDefault(); // Simulate upload progress let p = 0; const iv = setInterval(() => { p = Math.min(1, p + 0.05); onProgress(p); if (p >= 1) clearInterval(iv); }, 100); }); </script>

File Upload

Drag-and-drop file selection

html <snice-file-upload label="Upload files" accept="image/*,.pdf" multiple drag-drop show-preview max-files="5" ></snice-file-upload>

Flip Card

3D card flip with front and back faces, click or programmatic control

Click to flip
Back side
Vertical flip
Other side
html <snice-flip-card direction="horizontal" style="width:240px;height:160px" > <div slot="front">Front content</div> <div slot="back">Back content</div> </snice-flip-card> <script> card.flip(); card.flipTo('back'); card.addEventListener('flip-change', e => console.log(e.detail.side)); </script>

Flow

Node-based flow/diagram editor with draggable nodes, ports, and bezier curve edges

html + js <snice-flow id="flow" snap-to-grid minimap> </snice-flow> <script> const flow = document.getElementById('flow'); flow.nodes = [ { id: 'start', x: 50, y: 100, label: 'Start', outputs: [{ id: 'out', label: 'Out' }] }, { id: 'process', x: 300, y: 80, label: 'Process', inputs: [{ id: 'in', label: 'In' }], outputs: [{ id: 'out', label: 'Out' }] }, { id: 'end', x: 550, y: 100, label: 'End', inputs: [{ id: 'in', label: 'In' }] }, ]; flow.edges = [ { id: 'e1', source: 'start', target: 'process', sourcePort: 'out', targetPort: 'in' }, { id: 'e2', source: 'process', target: 'end', sourcePort: 'out', targetPort: 'in' }, ]; </script>

Form Layout

Responsive form grid with configurable columns and label positions

html <snice-form-layout columns="2" gap="medium"> <snice-input label="First Name"></snice-input> <snice-input label="Last Name"></snice-input> <snice-input label="Email"></snice-input> <snice-input label="Phone"></snice-input> </snice-form-layout> <!-- Label positions: top (default), left, right --> <snice-form-layout label-position="left"> ... </snice-form-layout> <!-- Gaps: small, medium (default), large --> <!-- Variants: default, compact, card --> <snice-form-layout columns="3" gap="small" variant="compact" ></snice-form-layout>

Funnel

SVG-based funnel chart for conversion tracking and pipeline visualization

html + js <snice-funnel id="funnel" animation></snice-funnel> <script> funnel.data = [ { label: 'Visitors', value: 12000 }, { label: 'Signups', value: 5200 }, { label: 'Trials', value: 2800 }, { label: 'Paid', value: 1100 }, { label: 'Enterprise', value: 340 } ]; </script>
html + js <snice-funnel variant="gradient" orientation="horizontal" animation ></snice-funnel> <script> funnel.data = [ { label: 'Impressions', value: 50000, color: 'rgb(99 102 241)' }, { label: 'Clicks', value: 18000, color: 'rgb(79 70 229)' }, { label: 'Leads', value: 6400, color: 'rgb(67 56 202)' }, { label: 'Sales', value: 1800, color: 'rgb(55 48 163)' } ]; </script>

Gantt

Project timeline with draggable task bars, zoom levels, and task groups

html <snice-gantt zoom="week"></snice-gantt> <script> gantt.tasks = [ { id: '1', name: 'Design', start: '2026-03-01', end: '2026-03-07', progress: 80, group: 'Phase 1' }, { id: '2', name: 'Development', start: '2026-03-05', end: '2026-03-15', progress: 30, group: 'Phase 1' } ]; gantt.addEventListener('task-click', e => console.log(e.detail.task)); </script>

Gauge

SVG-based gauge/meter for dashboards

html <snice-gauge value="75" variant="primary" label="CPU"></snice-gauge> <snice-gauge value="42" variant="success" label="Memory"></snice-gauge> <snice-gauge value="90" variant="error" label="Disk"></snice-gauge> <snice-gauge value="58" variant="warning" label="Network"></snice-gauge>
html <snice-gauge value="30" size="small" variant="info" label="Small"></snice-gauge> <snice-gauge value="60" size="medium" variant="primary" label="Medium"></snice-gauge> <snice-gauge value="85" size="large" variant="success" label="Large"></snice-gauge>

Grid

Coordinate-based grid layout — place items at explicit positions with spanning. Drag widgets to rearrange with snap-to-grid.

💰 Revenue
$48.2k
▲ 12.5% vs last month
Users
3,842
▲ 8%
CPU
74%
📦 Orders
284
processed
82%
12%
🌐 Traffic Sources
Organic45%
Social28%
Direct18%
Mem
61%
🔔 Alerts
⚠DB latency spike2m ago
✓Deploy complete14m ago
Uptime
99.9%
30d avg
html <snice-grid draggable gap="8px" column-width="80" row-height="80"> <!-- Place items at explicit grid coordinates --> <div grid-col="0" grid-row="0" grid-colspan="2" grid-rowspan="2"> Revenue (2x2 at column 0, row 0) </div> <div grid-col="2" grid-row="0"> Users (1x1 at column 2, row 0) </div> <div grid-col="2" grid-row="1" grid-colspan="2"> Orders (2x1 at column 2, row 1) </div> </snice-grid> <!-- Attributes --> <snice-grid draggable <!-- enable drag with snap --> gap="8px" <!-- spacing between cells --> column-width="80" <!-- cell width in px --> row-height="80" <!-- cell height in px --> columns="6" <!-- fixed column count --> rows="4" <!-- fixed row count --> stagger="40" <!-- cascade animation delay --> ></snice-grid>

Heatmap

GitHub-style contribution heatmap

html + js <snice-heatmap id="heatmap"></snice-heatmap> <script> const heatmap = document.getElementById('heatmap'); const data = []; const now = new Date(); for (let i = 0; i < 365; i++) { const d = new Date(now); d.setDate(d.getDate() - i); if (Math.random() > 0.3) { data.push({ date: d.toISOString().split('T')[0], value: Math.floor(Math.random() * 10) }); } } heatmap.data = data; </script>
html <snice-heatmap color-scheme="blue" weeks="26" ></snice-heatmap> <snice-heatmap color-scheme="purple" weeks="26" ></snice-heatmap>

Icons

A built-in Heroicons set every component falls back to. Pass a name to any icon property — no icon font, no extra request

Search Delete Download

69 icons ship built in — open Showcase for the full catalogue.

html <!-- A name from the built-in set --> <snice-button icon="magnifying-glass">Search</snice-button> <!-- Or your own image / emoji --> <snice-button icon="/icons/custom.svg">Custom</snice-button> <snice-button icon="🚀">Launch</snice-button> <!-- Or slot an element to override entirely --> <snice-button> <span slot="icon" class="material-symbols-outlined">home</span> Home </snice-button>

Image

Lazy-loaded images with variants

html <snice-image src="assets/photos/sample-200.jpg?v=80938d7" alt="Photo" variant="circle" size="medium" lazy ></snice-image>

Input

Text fields with types, sizes, variants, and validation

html <snice-input label="Email" type="email" placeholder="Enter your email"></snice-input> <snice-input label="Password" type="password" password placeholder="Enter password"></snice-input> <snice-input size="small" label="Small"></snice-input> <snice-input variant="filled" label="Filled"></snice-input> <snice-input invalid error-text="Invalid email"></snice-input>
html <snice-input label="Search" placeholder="Search..."> <svg slot="prefix-icon" viewBox="0 0 24 24"> <!-- magnifying-glass --> </svg> </snice-input> <snice-input label="Email"> <svg slot="prefix-icon" viewBox="0 0 24 24"> <!-- envelope --> </svg> <svg slot="suffix-icon" viewBox="0 0 24 24"> <!-- check-circle --> </svg> </snice-input>
html <snice-input label="Loading" placeholder="Fetching data..." loading></snice-input>

Invoice

Professional invoice with line items, tax/discount calculations, and status tracking

javascript <snice-invoice id="inv" invoice-number="INV-2026-001" date="Feb 27, 2026" due-date="Mar 27, 2026" status="sent" currency="USD" tax-rate="10" discount="5" ></snice-invoice> <script> inv.from = { name: 'Acme Corp', address: '123 Business St' }; inv.to = { name: 'Client Inc', address: '456 Market Ave' }; inv.items = [ { description: 'Web Development', quantity: 40, unitPrice: 150 }, { description: 'UI Design', quantity: 20, unitPrice: 120 } ]; </script>
Templates - click a sheet to zoom

Kanban

Drag-and-drop kanban board

html <snice-kanban id="kb" allow-drag-drop show-card-count ></snice-kanban> <script> kb.columns = [ { id: 'todo', title: 'To Do', cards: [ { id: '1', title: 'Task', labels: ['Design'] } ]} ]; </script>

Key Value

Key-value pair editor for HTTP headers, env vars, and config entries — with edit and view modes

html <!-- Declarative --> <snice-key-value label="Headers"> <snice-kv-pair key="Content-Type" value="application/json" ></snice-kv-pair> <snice-kv-pair key="Authorization" value="Bearer token" ></snice-kv-pair> </snice-key-value> <!-- Imperative --> <snice-key-value id="kv" label="Env Vars" ></snice-key-value> <script> const kv = document.getElementById('kv'); kv.setItems([ { key: 'NODE_ENV', value: 'production' }, { key: 'PORT', value: '3000' } ]); kv.addEventListener('kv-change', e => console.log(e.detail.items)); </script>
html <!-- View mode (read-only display) --> <snice-key-value label="Response Headers" mode="view" ></snice-key-value> <!-- Fixed rows --> <snice-key-value rows="3" label="Fixed Rows" ></snice-key-value>
Ready
html <form> <snice-key-value name="metadata" value='[{"key":"region", "value":"us-east-1", "description":"Primary region"}]' show-description required ></snice-key-value> <button type="submit">Submit</button> <button type="reset">Reset</button> </form>

KPI

Key performance indicators with trends, sparklines, and sentiment colors

html <snice-kpi label="Revenue" value="$127,450" trend-value="+22.4%" sentiment="up" ></snice-kpi> <script> kpi.trendData = [30, 45, 35, 50, 40, 60, 55, 70]; </script> <!-- Sizes: small, medium, large --> <snice-kpi label="Small" value="1,234" size="small" sentiment="up"></snice-kpi> <!-- color-value applies sentiment to value --> <snice-kpi label="Sales" value="$12,345" sentiment="up" color-value></snice-kpi>

Layouts

Twelve layout shells for common application shells: dashboards, landing pages, split views, and more.

Dashboard · header + toolbar + sidebar + main + rail

snice-layout-dashboard
Header
Toolbar
Sidebar
Main
Right

Sidebar · persistent left nav, overlays on mobile

snice-layout-sidebar
Header
Sidebar
Main

Split · two panes side-by-side

snice-layout-split
Left
Right

Centered · auth card, brand above, links below

snice-layout-centered
Header
Content

Minimal · bare main, no chrome

snice-layout-minimal
Main

Landing · hero + stacked sections

snice-layout-landing
Header
Hero

Blog · centred measure, optional rail

snice-layout-blog
Header
Article
Meta

Card grid · responsive columns

snice-layout-card
Card

Fullscreen · layered canvas, overlay + controls

snice-layout-fullscreen
Fullscreen canvas

Master detail · list beside the selected item

snice-layout-master-detail
Header
List
Detail

Docs · nav tree, prose, on-this-page rail

snice-layout-docs
Header
Nav
Prose
TOC

Auth split · form beside a brand panel

snice-layout-auth-split
Form
Panel
html <!-- Each layout consumes placards from the Snice router --> <snice-layout-dashboard></snice-layout-dashboard> <snice-layout-sidebar></snice-layout-sidebar> <snice-layout-split></snice-layout-split> <snice-layout-centered></snice-layout-centered> <snice-layout-minimal></snice-layout-minimal> <snice-layout-landing></snice-layout-landing> <snice-layout-blog></snice-layout-blog> <snice-layout-card></snice-layout-card> <snice-layout-fullscreen></snice-layout-fullscreen> <snice-layout-master-detail></snice-layout-master-detail> <snice-layout-docs></snice-layout-docs> <snice-layout-auth-split></snice-layout-auth-split>

Leaderboard

Ranked list with podium variant, avatars, change indicators, and dual API (declarative children or imperative setter)

html <!-- Declarative (child elements) --> <snice-leaderboard variant="podium" title="Top Players"> <snice-leaderboard-entry rank="1" name="Alice" score="2,500" change="3" highlighted> </snice-leaderboard-entry> <snice-leaderboard-entry rank="2" name="Bob" score="2,100" change="-1"> </snice-leaderboard-entry> </snice-leaderboard> <!-- Imperative (setEntries) --> <script> lb.setEntries([ { rank: 1, name: 'Alice', score: 2500, change: 3 }, { rank: 2, name: 'Bob', score: 2100, change: -1 }, ]); </script>

Link Preview

Social media-style link preview cards

html <snice-link-preview title="Snice - Beautiful Decorators" description="Write clean, reactive web components..." image="assets/photos/link-preview.jpg" site-name="snice.dev" url="https://snice.dev" ></snice-link-preview> <snice-link-preview variant="horizontal" size="small" title="Getting Started Guide" description="Learn to build elements step by step" site-name="snice.dev" url="https://snice.dev/guide" ></snice-link-preview>

Link

Styled anchor elements with variants

Default Link Primary Secondary Underlined External Link Disabled
html <snice-link href="#" variant="primary">Primary</snice-link> <snice-link href="#" underline>Underlined</snice-link> <snice-link href="#" external>External</snice-link> <snice-link href="#" disabled>Disabled</snice-link>

List

Structured lists with dividers, search, before/after slots, and rich content

With Icons & Dividers 2.4 MB 5.1 MB 876 KB
Searchable
html <snice-list dividers> <snice-list-item heading="Document.pdf" description="Updated 2h ago"> <svg slot="before"><!-- document icon --></svg> <span slot="after">2.4 MB</span> </snice-list-item> </snice-list> <!-- With search --> <snice-list searchable dividers> <snice-list-item heading="Alice" description="alice@example.com"> <span slot="before">...</span> </snice-list-item> </snice-list>

Location

Address and coordinate display

html <snice-location name="Eiffel Tower" address="5 Avenue Anatole France" city="Paris" country="France" show-icon clickable ></snice-location>

Login

Authentication forms with card, minimal, and default variants, configurable fields

Sign in to your account

html <snice-login variant="card" title="Welcome Back" action-text="Sign In" show-remember-me show-forgot-password > <p slot="subtitle">Sign in to your account</p> <div slot="footer"> <a href="#">Sign up here</a> </div> </snice-login> <!-- Variants: default, card, minimal --> <!-- Sizes: small, medium, large -->

Map

Interactive tile-based map with markers, popups, and zoom controls

html <snice-map zoom="13"></snice-map> <script> map.center = { lat: 51.505, lng: -0.09 }; map.markers = [ { id: '1', lat: 51.505, lng: -0.09, label: 'London', popup: 'Hello London!' } ]; map.addEventListener('marker-click', e => console.log(e.detail.marker)); map.fitBounds(); </script>

Markdown

GFM-compatible markdown renderer with sanitization and syntax highlighting

## Project Status The build pipeline is **fully operational**. Here's what shipped: ### Features - [x] GFM tables and task lists - [x] Code block syntax highlighting - [ ] Image lazy loading ### Code Example ```js const greeting = (name) => { return `Hello, ${name}!`; }; ``` > *Markdown is rendered safely with built-in sanitization.* | Component | Status | Size | |-----------|--------|------| | Button | Stable | 2 KB | | Modal | Stable | 4 KB | | Table | Beta | 12 KB |
html <snice-markdown theme="github"> ## Hello World **Bold** and *italic* text. - Item 1 - Item 2 | Col A | Col B | |-------|-------| | One | Two | </snice-markdown>
javascript md.setContent('## Dynamic\n\n' + '- Item 1\n- Item 2\n\n' + '**Bold** and *italic*'); md.addEventListener('link-click', e => console.log(e.detail.href));

Masonry

Pinterest-style grid layout with variable height items

Mountain cabin
Mountain Retreat
Minimalist cabin design in the Alps
Sunset colors
Sunset Palette
Color inspiration for your next interior project
Garden
Urban Garden Tips
Dark UI
Dark Mode UI Kit
50+ components designed for dark interfaces
Food bowl
Breakfast Bowl
Healthy smoothie recipe with granola topping
Ceramics
Ceramic Collection
Handmade pottery from Japanese artisans
Typography
Typography Trends 2026
Coastal room
Coastal Living Room
Light and airy beach house inspiration
Workspace
Workspace Setup
Developer desk organization ideas
html <snice-masonry columns="3" gap="0.75rem"> <div class="pin-card"> <img src="photo.jpg?v=80938d7" class="pin-img"> <div class="pin-body"> <div class="pin-title">Mountain Retreat</div> <div class="pin-desc">Minimalist cabin design</div> </div> </div> <!-- ... more cards --> </snice-masonry> <!-- Auto-responsive columns --> <snice-masonry columns="0" min-column-width="250px"> <div class="pin-card">...</div> </snice-masonry>

Menu

Dropdown menus with keyboard navigation

Actions Menu Edit Duplicate Delete More Options Download Share Archive
html <snice-menu> <snice-button slot="trigger" variant="primary"> Actions Menu </snice-button> <snice-menu-item>Edit</snice-menu-item> <snice-menu-item>Duplicate</snice-menu-item> <snice-menu-divider></snice-menu-divider> <snice-menu-item variant="danger">Delete</snice-menu-item> </snice-menu>
Sarah Chen
Profile Settings Billing Sign Out
html <!-- Avatar dropdown --> <snice-menu> <div slot="trigger" style="display:inline-flex; align-items:center;gap:0.5rem"> <snice-avatar name="Sarah Chen" size="small" shape="circle"></snice-avatar> <span>Sarah Chen</span> <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"> <polyline points="6 9 12 15 18 9"></polyline> </svg> </div> <snice-menu-item>Profile</snice-menu-item> <snice-menu-item>Settings</snice-menu-item> <snice-menu-divider></snice-menu-divider> <snice-menu-item variant="danger">Sign Out</snice-menu-item> </snice-menu>

Message Strip

Inline status messages with optional dismiss

This is an informational message. Changes saved successfully. Your session will expire in 5 minutes. Unable to connect to the server.
html <snice-message-strip variant="info"> This is an informational message. </snice-message-strip> <snice-message-strip variant="success" dismissible> Changes saved successfully. </snice-message-strip> <snice-message-strip variant="warning" dismissible> Your session will expire in 5 minutes. </snice-message-strip> <snice-message-strip variant="danger"> Unable to connect to the server. </snice-message-strip>

Modal

Dialog overlays for focused interactions

Open Modal Form Modal
html <snice-modal id="my-modal" label="Confirm"> <h2 slot="header">Confirm Action</h2> <p>Are you sure you want to proceed?</p> <div slot="footer"> <snice-button variant="default" onclick="this.closest('snice-modal').close()"> Cancel</snice-button> <snice-button variant="primary" onclick="this.closest('snice-modal').close()"> Confirm</snice-button> </div> </snice-modal>

Confirm Action

Are you sure you want to proceed? This action cannot be undone.

Cancel Confirm

Edit Profile

Cancel Save Changes

Music Player

Audio playback with playlist. Tracks: Piñaita — used with permission.

html <snice-music-player show-playlist show-controls show-volume show-artwork show-track-info ></snice-music-player> <script> player.tracks = [ { id: '1', title: 'Track', artist: 'Artist', src: 'url' } ]; </script>

Nav

Application navigation with flat, hierarchical, and grouped variants in horizontal or vertical orientations

javascript // Flat navigation nav.update(placards, appContext, 'dashboard'); // Hierarchical (parent-child) nav.update([ { name: 'products', title: 'Products', icon: 'inventory_2' }, { name: 'products/list', parent: 'products', title: 'Product List' }, ], ctx, 'products/list'); // Grouped (by category) nav.update([ { name: 'users', title: 'Users', group: 'Management', icon: 'group' }, ], ctx, 'settings'); // Variants: flat, hierarchical, grouped // Orientation: horizontal, vertical // active-style="text" = color-only highlight

Network Graph

Force-directed network/relationship graph visualization

html + js <snice-network-graph id="graph" show-labels ></snice-network-graph> <script> graph.data = { nodes: [ { id: 'api', label: 'API Gateway', group: 'infra' }, { id: 'web', label: 'Web App', group: 'frontend' }, { id: 'db', label: 'Database', group: 'data' }, ], edges: [ { source: 'web', target: 'api' }, { source: 'api', target: 'db' }, ], }; </script>
html <!-- Circular layout with weighted edges --> <snice-network-graph id="graph" layout="circular" show-labels ></snice-network-graph> <script> graph.data = { nodes: [ { id: 'alice', label: 'Alice', group: 'eng' }, { id: 'bob', label: 'Bob', group: 'eng' }, { id: 'carol', label: 'Carol', group: 'design' }, ], edges: [ { source: 'alice', target: 'bob', weight: 3 }, { source: 'bob', target: 'carol', weight: 1 }, ], }; </script>

Notification Center

Bell icon with unread badge and dropdown notification panel

html <snice-notification-center icon="notifications"> </snice-notification-center> <script> nc.notifications = [ { id: '1', title: 'New comment', message: 'Alice commented on your PR', timestamp: '2 min ago', icon: 'chat_bubble' }, { id: '2', title: 'Deploy complete', message: 'Build v2.4 succeeded', timestamp: '10 min ago', icon: 'check_circle', read: true } ]; </script>

Order Tracker

Order status timeline with tracking info, horizontal and vertical orientations

javascript <snice-order-tracker id="tracker" tracking-number="1Z999AA10123456784" carrier="UPS" ></snice-order-tracker> <script> tracker.steps = [ { label: 'Ordered', status: 'completed', timestamp: 'Feb 20, 2:30 PM' }, { label: 'Shipped', status: 'completed', timestamp: 'Feb 21, 9:15 AM' }, { label: 'In Transit', status: 'current', description: 'Package in transit' }, { label: 'Delivered', status: 'pending' } ]; </script>

Org Chart

Organizational hierarchy with collapsible nodes and avatar placeholders

html <snice-org-chart direction="top-down" ></snice-org-chart> <script> chart.data = { id: '1', name: 'CEO', title: 'Chief Executive', children: [ { id: '2', name: 'CTO', title: 'Technology', children: [...] }, { id: '3', name: 'CFO', title: 'Finance' } ] }; </script>

Pagination

Navigation for paged content

html <snice-pagination current="1" total="10"> </snice-pagination> <snice-pagination current="5" total="20" siblings="1"></snice-pagination> <snice-pagination current="3" total="5" size="small" variant="rounded"> </snice-pagination>

Draw + Paint

Self-contained drawing canvas with built-in toolbar. Uses <snice-draw> under the hood — use it standalone for a bare canvas with your own UI.

html <!-- All-in-one with built-in toolbar --> <snice-paint></snice-paint> <!-- With 2 custom color pickers --> <snice-paint color-selects="2"></snice-paint> <!-- Custom tools via slots --> <snice-paint> <button slot="tools">🔲 Rect</button> <button slot="toolbar-end">💾 Save</button> </snice-paint> <!-- Bare canvas only (bring your own toolbar) --> <snice-draw width="800" height="600" color="#3b82f6" stroke-width="3" ></snice-draw> <script> paint.addEventListener('color-select', (e) => { console.log('Custom color:', e.detail.color); }); </script>

PDF Viewer

Document viewer with page navigation, zoom, fit modes, and print/download

html <snice-pdf-viewer src="/documents/report.pdf" fit="width" zoom="1" ></snice-pdf-viewer> <script> viewer.addEventListener('pdf-loaded', e => console.log( 'Pages:', e.detail.totalPages)); // Navigation viewer.nextPage(); viewer.goToPage(5); viewer.print(); viewer.download(); </script>

Permission Matrix

Role-permission grid with toggleable checkboxes, grouped permissions, and read-only mode

javascript <snice-permission-matrix id="pm" ></snice-permission-matrix> <script> pm.roles = [ { id: 'admin', name: 'Admin' }, { id: 'editor', name: 'Editor' }, { id: 'viewer', name: 'Viewer' } ]; pm.permissions = [ { id: 'read', name: 'Read', group: 'Content' }, { id: 'write', name: 'Write', group: 'Content' }, { id: 'delete', name: 'Delete', group: 'Content' }, { id: 'manage', name: 'Manage Users', group: 'Admin' } ]; pm.matrix = { admin: ['read','write','delete','manage'], editor: ['read', 'write'], viewer: ['read'] }; pm.addEventListener('permission-toggle', e => console.log(e.detail)); </script>

Podcast Player

Podcast playback with RSS feed support, episode list, speed control, and sleep timer

html <!-- Direct source --> <snice-podcast-player src="episode.mp3" title="Episode Title" show="Podcast Name" artwork="cover.jpg" skip-forward="30" skip-back="15" ></snice-podcast-player> <!-- RSS feed mode --> <snice-podcast-player from-rss="https://feed.example.com/rss" ></snice-podcast-player> <script> player.addEventListener('podcast-play', e => console.log(e.detail)); player.setPlaybackRate(1.5); </script>

Popover

Click-triggered interactive panel anchored to a trigger

Open popover
Popover panel Interactive content lives here. Click outside or press Escape to dismiss.
Placement: bottom
Centered under the trigger.
No outside dismiss
Only Escape or the trigger closes this.
html <snice-popover placement="bottom-start"> <snice-button slot="trigger"> Open popover </snice-button> <div>Interactive panel content</div> </snice-popover> <snice-popover placement="right" no-outside-dismiss> ... </snice-popover>

Pricing Table

Pricing tier comparison with cards/table variants, billing toggle, and highlighted plans

declarative (cards)
table variant
html <snice-pricing-table> <snice-plan name="Free" price="0" cta="Get Started"> <snice-feature name="1 user" included></snice-feature> <snice-feature name="API"></snice-feature> </snice-plan> <snice-plan name="Pro" price="29" annual-price="24" highlighted badge="Popular" cta="Start Trial"> <snice-feature name="10 users" included></snice-feature> <snice-feature name="API" included></snice-feature> </snice-plan> </snice-pricing-table>
javascript pt.plans = [ { name: 'Free', price: 0, features: [ { name: '1 user', included: true }, { name: 'API', included: false } ], cta: 'Get Started' }, { name: 'Pro', price: 29, annualPrice: 24, features: [ { name: '10 users', included: true }, { name: 'API', included: true } ], cta: 'Start Trial', highlighted: true, badge: 'Popular' } ]; pt.addEventListener('plan-select', e => console.log(e.detail));

Product Card

E-commerce product display with image gallery, ratings, variant selectors, badges, favorites, and add-to-cart. Six variants: vertical, horizontal, compact, featured, minimal, and grid.

javascript <snice-product-card variant="featured" name="MacBook Pro M4" price="2499.00" sale-price="2199.00" badge="SALE" badge-variant="sale" rating="4.5" review-count="1247" stock-count="12" in-stock ></snice-product-card> <script> card.images = ['hero1.jpg', 'hero2.jpg']; card.variants = [ { type: 'Color', options: ['#1d1d1f','#f5f5f7','#b8860b'] }, { type: 'Storage', options: ['512GB','1TB','2TB'] } ]; card.addEventListener('favorite', e => console.log(e.detail.favorited)); card.addEventListener('quick-view', () => console.log('quick view')); </script>
html <!-- Vertical with badge + low stock --> <snice-product-card name="Wireless Headphones" price="149.99" sale-price="99.99" badge="NEW" badge-variant="new" stock-count="3" in-stock ></snice-product-card> <!-- Minimal: clean fashion look --> <snice-product-card variant="minimal" name="Cashmere Sweater" price="245.00" ></snice-product-card> <!-- Compact: small inline card --> <snice-product-card variant="compact" name="USB-C Cable 2m" price="19.99" ></snice-product-card>
html <!-- Grid variant: optimized for product grids --> <div style="display:grid;grid-template-columns: repeat(auto-fill,minmax(180px,1fr));gap:1rem"> <snice-product-card variant="grid" name="Running Shoes Pro" price="129.99" sale-price="89.99" badge="SALE" badge-variant="sale" ></snice-product-card> <snice-product-card variant="grid" name="Yoga Mat Premium" price="49.99" ></snice-product-card> </div>
html <!-- Skeleton loading state --> <snice-product-card loading> </snice-product-card> <snice-product-card loading variant="horizontal"> </snice-product-card>

Progress Ring

Circular progress indicators with customizable size and color

html <snice-progress-ring value="25" show-value size="small" ></snice-progress-ring> <snice-progress-ring value="60" show-value ></snice-progress-ring> <snice-progress-ring value="85" show-value size="large" color="#22c55e" ></snice-progress-ring> <snice-progress-ring value="100" show-value size="large" label="Done" color="#6366f1" ></snice-progress-ring>

Progress & Spinner

Linear progress bars, circular progress, and spinners

Download Progress
Upload Complete
Storage Warning
Processing
html <snice-progress value="65" show-label></snice-progress> <snice-progress value="100" color="success"> </snice-progress> <snice-progress indeterminate></snice-progress> <snice-spinner size="large"></snice-spinner> <snice-progress variant="circular" value="75" show-label ></snice-progress>

QR Code

Generate QR codes with colors, logos, and dot styles

html <!-- Logo overlay --> <snice-qr-code value="https://snice.dev" size="120" margin="6" include-image image-url="logo.png" image-size="30" fg-color="#2196f3" bg-color="#e3f2fd"> </snice-qr-code> <!-- Text with independent color --> <snice-qr-code value="WIFI:T:WPA;S:MyNetwork;P:secret;;" center-text="WiFi" text-fill-color="#fff" fg-color="#ff9800"> </snice-qr-code>

QR Reader

QR code scanner (requires camera permission)

Open QR Scanner
html <snice-qr-reader auto-start="false" ></snice-qr-reader>

Scan QR Code

Close

Radio

Single-selection radio buttons with inline and block variants

html <snice-radio name="plan" label="Free" value="free" checked> </snice-radio> <snice-radio name="plan" label="Pro" value="pro"> </snice-radio> <!-- Sizes --> <snice-radio label="Small" size="small"></snice-radio> <snice-radio label="Large" size="large"></snice-radio> <!-- States --> <snice-radio label="Disabled" disabled></snice-radio> <snice-radio label="Loading" loading></snice-radio>
Free forever $12/mo Contact us
html <snice-radio variant="block" name="pricing" value="free" label="Free Plan" description="For individuals and side projects" checked > <span slot="suffix">Free forever</span> </snice-radio> <snice-radio variant="block" name="pricing" value="pro" label="Pro Plan" description="For growing teams" > <span slot="suffix">$12/mo</span> </snice-radio>

Range Slider

Dual-handle slider for selecting a value range

Price Range ($0 - $1000)
Age Filter (18 - 65)
Disabled
html <snice-range-slider min="0" max="1000" value-low="200" value-high="800" step="50" show-tooltip show-labels ></snice-range-slider> <snice-range-slider min="18" max="65" value-low="25" value-high="45" show-tooltip show-labels ></snice-range-slider> <snice-range-slider disabled></snice-range-slider>

Rating

Star rating input with full and half precision, custom icons, and keyboard navigation

html <snice-rating value="3.5" max="5" precision="half" size="large" ></snice-rating> <snice-rating value="4" readonly> </snice-rating> <script> rating.addEventListener('rating-change', e => console.log(e.detail.value)); </script>

Receipt

Transaction receipt with merchant info, line items, totals, and payment method

javascript <snice-receipt id="rcpt" receipt-number="RCT-48291" date="Feb 27, 2026 3:42 PM" tax="4.76" payment-method="Visa ending in 4242" ></snice-receipt> <script> rcpt.merchant = { name: 'Coffee Shop', address: '789 Main St' }; rcpt.items = [ { name: 'Latte', quantity: 2, price: 5.50 }, { name: 'Muffin', quantity: 1, price: 3.95 } ]; </script>
Templates - click a sheet to zoom

Recipe

Interactive recipe card with ingredient scaling, step-by-step progress, and nutrition facts

javascript recipe.title = 'Pasta Carbonara'; recipe.prepTime = 10; recipe.cookTime = 20; recipe.servings = 4; recipe.difficulty = 'medium'; recipe.cuisine = 'Italian'; recipe.ingredients = [ { name: 'Spaghetti', amount: 400, unit: 'g' }, { name: 'Pancetta', amount: 200, unit: 'g' }, { name: 'Eggs', amount: 4, unit: '' }, { name: 'Parmesan', amount: 100, unit: 'g' } ]; recipe.steps = [ { text: 'Boil pasta in salted water.', time: 10 }, { text: 'Fry pancetta until crispy.' }, { text: 'Mix eggs and parmesan.' }, { text: 'Combine off heat.' } ]; recipe.nutrition = { calories: 650, protein: 28, carbs: 72, fat: 24 };

Sankey

Flow diagram showing quantities between categories

html + js <snice-sankey id="sankey" show-labels show-values animation> </snice-sankey> <script> sankey.data = { nodes: [ { id: 'organic', label: 'Organic' }, { id: 'paid', label: 'Paid Ads' }, { id: 'social', label: 'Social' }, { id: 'landing', label: 'Landing' }, { id: 'blog', label: 'Blog' }, { id: 'pricing', label: 'Pricing' }, { id: 'signup', label: 'Sign Up' }, { id: 'download', label: 'Download' }, { id: 'purchase', label: 'Purchase' } ], links: [ { source: 'organic', target: 'landing', value: 3000 }, { source: 'organic', target: 'blog', value: 2000 }, { source: 'paid', target: 'landing', value: 4000 }, { source: 'paid', target: 'pricing', value: 1500 }, { source: 'social', target: 'landing', value: 1000 }, { source: 'social', target: 'blog', value: 800 }, { source: 'landing', target: 'signup', value: 5000 }, { source: 'landing', target: 'download', value: 2000 }, { source: 'blog', target: 'signup', value: 1500 }, { source: 'blog', target: 'download', value: 1000 }, { source: 'pricing', target: 'purchase', value: 1500 } ] }; </script>
html + js <snice-sankey show-labels show-values></snice-sankey> <script> sankey.data = { nodes: [ { id: 'solar', label: 'Solar', color: 'rgb(250 204 21)' }, { id: 'wind', label: 'Wind', color: 'rgb(56 189 248)' }, { id: 'gas', label: 'Gas', color: 'rgb(251 146 60)' }, { id: 'grid', label: 'Grid', color: 'rgb(163 163 163)' }, { id: 'home', label: 'Home', color: 'rgb(74 222 128)' }, { id: 'ev', label: 'EV', color: 'rgb(129 140 248)' } ], links: [ { source: 'solar', target: 'grid', value: 120 }, { source: 'wind', target: 'grid', value: 80 }, { source: 'gas', target: 'grid', value: 200 }, { source: 'grid', target: 'home', value: 300 }, { source: 'grid', target: 'ev', value: 100 } ] }; </script>

Segmented Control

Mutually exclusive option selector with sliding indicator

javascript <snice-segmented-control value="weekly" ></snice-segmented-control> control.options = [ { label: 'Daily', value: 'daily' }, { label: 'Weekly', value: 'weekly' }, { label: 'Monthly', value: 'monthly' } ];

Select

Searchable, multi-select, and custom dropdowns

United States United Kingdom Canada Australia Germany France Japan Engineering Design Marketing Sales
JavaScript TypeScript React Vue Node.js Python Rust Go
Option A Option B Option C Locked Value Option X Option Y Option Z
html <snice-select label="Country" searchable clearable> <snice-option value="us" icon="flags/us.png">United States</snice-option> </snice-select> <snice-select label="Skills" multiple searchable> <snice-option value="js">JavaScript</snice-option> </snice-select> <!-- Sizes: small, medium (default), large --> <snice-select size="small">...</snice-select> <snice-select disabled>...</snice-select>
Red Green Blue Locked Value
html <!-- Loading state --> <snice-select label="Loading" loading></snice-select> <!-- Lazy-load options on first open --> <snice-select id="lazy" label="Lazy-loaded"></snice-select> <script> const el = document.getElementById('lazy'); el.addEventListener('select-open', async () => { if (el.options.length) return; el.loading = true; const data = await fetch('/api/options').then(r => r.json()); el.options = data; el.loading = false; }); </script> <!-- Free text entry (type anything, not just listed options) --> <snice-select searchable allow-free-text clearable> <snice-option value="red">Red</snice-option> </snice-select>
html <!-- Editable: text input trigger instead of button --> <snice-select editable label="Fruit" placeholder="Type or select..." ></snice-select> <script> select.options = [ { value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }, { value: 'cherry', label: 'Cherry' } ]; </script> <!-- Editable with custom typed values --> <snice-select editable allow-free-text label="Custom values" placeholder="Type anything..." ></snice-select> <!-- Sizes: small, medium (default), large --> <snice-select editable size="small"></snice-select> <snice-select editable disabled></snice-select>

Skeleton

Loading placeholders

html <snice-skeleton variant="circular" width="48px" height="48px" ></snice-skeleton> <snice-skeleton variant="text" width="40%"> </snice-skeleton> <snice-skeleton variant="rectangular" height="200px"> </snice-skeleton>

Slider

Range input with variants, ticks, and labels

html <snice-slider label="Volume" value="75" show-value ></snice-slider> <snice-slider label="Rating (1-5)" min="1" max="5" step="1" show-value show-ticks variant="warning" ></snice-slider>
html <snice-slider label="Loading" value="50" show-value loading></snice-slider>

Sortable

Drag-and-drop reorderable list with animated transitions

Design mockups
Write API endpoints
Set up CI pipeline
Code review
Deploy to staging
html <snice-sortable> <div>Item 1</div> <div>Item 2</div> <div>Item 3</div> </snice-sortable> <!-- With drag handle --> <snice-sortable handle=".handle"> <div> <span class="handle">&#9776;</span> Item A </div> </snice-sortable> <script> sortable.addEventListener('sort-change', e => console.log( e.detail.oldIndex, e.detail.newIndex)); </script>

Split Button

Primary action with a dropdown of secondary actions

javascript <snice-split-button label="Save" variant="primary" ></snice-split-button> button.actions = [ { label: 'Save as Draft', value: 'draft' }, { label: 'Save & Close', value: 'save-close' }, { label: 'Discard', value: 'discard' } ];

Split Pane

Resizable split layouts

Left Panel

Drag the divider to resize

Right Panel

Content adjusts automatically

html <snice-split-pane direction="horizontal" primary-size="40" > <div slot="primary">Left</div> <div slot="secondary">Right</div> </snice-split-pane>

Spotlight

Guided tour overlay that highlights elements with step-by-step instructions

Search bar
Notifications
Settings
Start Tour
html <snice-spotlight></snice-spotlight> <script> spotlight.steps = [ { target: '#search', title: 'Search', description: 'Find anything', position: 'bottom' }, { target: '#notifs', title: 'Notifications', description: 'Stay updated' }, { target: '#settings', title: 'Settings', description: 'Customize your experience' } ]; spotlight.start(); </script>

Stat Group

Coordinated row of KPI stat cards with trends

html <snice-stat-group id="stats" variant="card"> </snice-stat-group> <script> stats.stats = [ { label: 'Revenue', value: '$48.2K', trend: 'up', trendValue: '+12.5%' }, { label: 'Users', value: '2,340', trend: 'up', trendValue: '+8.1%' }, { label: 'Bounce', value: '24.3%', trend: 'down', trendValue: '-3.2%' }, { label: 'Sessions', value: '18.7K', trend: 'neutral', trendValue: '0%' } ]; </script> <!-- Variants: card, minimal, bordered -->

Step Input

Numeric stepper with increment/decrement buttons

Quantity
Temperature
Wrapping (1-12)
Disabled
Readonly
html <snice-step-input value="1" min="0" max="99" ></snice-step-input> <!-- Decimal step --> <snice-step-input value="20" min="-10" max="50" step="0.5" ></snice-step-input> <!-- Wrap: max rolls to min and vice versa --> <snice-step-input value="1" min="1" max="12" wrap ></snice-step-input> <!-- Sizes: small, medium (default), large --> <snice-step-input size="small"></snice-step-input> <snice-step-input disabled></snice-step-input>

Stepper

Multi-step process indicator with horizontal/vertical orientations, panels, error states, and clickable navigation

Horizontal
Previous Next
html <snice-stepper id="stepper" current-step="1" clickable ></snice-stepper> <script> stepper.steps = [ { label: 'Account', description: 'Create account' }, { label: 'Profile', description: 'Setup profile' }, { label: 'Settings', description: 'Configure' }, { label: 'Complete', description: 'All done!' } ]; </script>
Vertical
html <snice-stepper orientation="vertical" current-step="1" clickable ></snice-stepper> <!-- Error state --> <script> stepper.steps = [ { label: 'Upload', status: 'completed' }, { label: 'Validate', status: 'error' }, { label: 'Process', status: 'pending' } ]; </script>
With Panels

Step 1: Account Info

Enter your email and choose a password.

Step 2: Profile

Tell us about yourself.

Step 3: Preferences

Customize your experience.

Email notifications Weekly digest

All Done!

Your account is ready.

Get Started
Previous Next
html <snice-stepper id="wizard" clickable> <snice-stepper-panel> <h4>Step 1: Account Info</h4> <snice-input placeholder="Email"></snice-input> <snice-input placeholder="Password" type="password"></snice-input> </snice-stepper-panel> <snice-stepper-panel> <h4>Step 2: Profile</h4> <snice-input placeholder="Full Name"> </snice-input> </snice-stepper-panel> <!-- Panels auto-show/hide with step --> </snice-stepper>
Error State
javascript stepper.steps = [ { label: 'Upload', status: 'completed' }, { label: 'Validate', status: 'error' }, { label: 'Process', status: 'pending' }, { label: 'Complete', status: 'pending' } ]; stepper.currentStep = 1;

Switch

Toggle switches with on/off labels, sizes, and states

Notifications Dark Mode Disabled
html <snice-switch checked>Notifications</snice-switch> <snice-switch>Dark Mode</snice-switch> <snice-switch disabled>Disabled</snice-switch> <!-- Sizes --> <snice-switch size="small" label="Small"></snice-switch> <snice-switch size="large" label="Large"></snice-switch> <!-- On/Off labels inside the track --> <snice-switch label-on="ON" label-off="OFF"> Power </snice-switch> <!-- States --> <snice-switch loading>Loading</snice-switch> <snice-switch invalid>Invalid</snice-switch>

Table

Data tables with 24 built-in cell formats, grouping, aggregation, sorting, filtering, selection, editing, virtualization, and remote data

javascript table.columns = [ { key: 'product', label: 'Product', sortable: true }, { key: 'revenue', label: 'Revenue', type: 'currency', currencyFormat: { currency: 'USD', decimals: 0 } }, { key: 'growth', label: 'Growth', type: 'percent' }, { key: 'rating', label: 'Rating', type: 'rating' }, { key: 'progress', label: 'Completion', type: 'progress' }, { key: 'trend', label: 'Trend', type: 'sparkline' }, { key: 'status', label: 'Status', type: 'tag' } ]; // 24 built-in formats: text, number, currency, accounting, // scientific, fraction, percentage, date, boolean, rating, // progress, sparkline, duration, filesize, tags, status, // email, phone, link, color, image, JSON, location, actions

Tabs

Tabbed content with transitions and four placement directions

placement: top Overview Features Getting Started

Component Library

Snice ships 64 production-ready web components built entirely with TypeScript. Each component uses Shadow DOM for style encapsulation and works standalone or as part of a larger application.

Framework agnostic — works with React, Vue, Angular, or vanilla JS.

What's Included

✓ Differential rendering
✓ TypeScript decorators
✓ Client-side routing
✓ Dark & light themes
✓ Form validation
✓ Accessibility built-in

Quick Start

Install from npm and import only what you need. Each component can also be loaded as a standalone script with zero configuration.

npm install snice
placement: bottom Design Develop Deploy

Design System

Every component follows a consistent design language with customizable CSS properties. Swap color palettes, adjust spacing, or override individual parts.

Supports ::part() selectors for deep customization.

Developer Experience

TypeScript decorators handle boilerplate: @element registers the component, @property creates reactive attributes, @render defines the template. Less code, fewer bugs.

Production Ready

Tree-shakeable ESM builds, standalone IIFE scripts, and zero runtime dependencies. Each component is under 50 KB minified. Ship only what you use.

placement: start Basics Props Events

Import the component and add it to your HTML. Attributes map directly to properties.

Use placement, transition, and active to control behavior.

Listen for tab-change to respond when the active tab switches.

placement: end v4.5 v4.4 v4.3

Added component import validation, build improvements, and new standalone output formats.

Duplicate element registration now warns instead of erroring. Improved dev ergonomics.

Website improvements, release automation, and CI pipeline for automatic publishing.

html <snice-tabs transition="fade"> <snice-tab slot="nav">Overview</snice-tab> <snice-tab slot="nav">Features</snice-tab> <snice-tab-panel> <h4>Component Library</h4> <p>64 production-ready components...</p> </snice-tab-panel> <snice-tab-panel> <p>Differential rendering, routing...</p> </snice-tab-panel> </snice-tabs> <!-- Bottom placement --> <snice-tabs placement="bottom" transition="slide"> ... </snice-tabs> <!-- Vertical --> <snice-tabs placement="start">...</snice-tabs> <snice-tabs placement="end">...</snice-tabs>

Tag Input

Tag/pill input with autocomplete suggestions and keyboard navigation

html <snice-tag-input label="Technologies" placeholder="Add a tag..." max-tags="5" ></snice-tag-input> <script> input.suggestions = [ 'JavaScript', 'TypeScript', 'Python', 'Rust', 'Go' ]; input.value = ['JavaScript']; input.addEventListener('tag-add', e => console.log(e.detail.tag)); input.addEventListener('tag-remove', e => console.log(e.detail.tag)); </script>

Tag

Display-only tags and tokens with variants, sizes, and removable option

Default Primary Success Warning Danger Info Outline Pill Removable Small Large
html <snice-tag variant="primary">Primary</snice-tag> <snice-tag variant="success">Success</snice-tag> <snice-tag variant="danger">Danger</snice-tag> <!-- Outline style --> <snice-tag variant="primary" outline>Outline</snice-tag> <!-- Pill shape --> <snice-tag variant="success" pill>Pill</snice-tag> <!-- Removable (emits tag-remove) --> <snice-tag variant="danger" removable> Removable </snice-tag> <!-- Sizes: small, medium, large --> <snice-tag size="small">Small</snice-tag> <snice-tag size="large">Large</snice-tag>

Terminal

Interactive command-line interface

html <snice-terminal id="term" prompt="$ "></snice-terminal> <script> term.writeln('Welcome!', 'info'); term.addEventListener('terminal-command', (e) => { term.writeln('You typed: ' + e.detail.command); }); </script>

Testimonial

Quote card with author info, avatar, star rating, and multiple variants

html <snice-testimonial quote="This framework transformed our workflow." author="Sarah Chen" role="CTO" company="Nexus Labs" avatar="avatar.jpg" rating="5" variant="card" ></snice-testimonial> <!-- Variants: card, minimal, featured --> <snice-testimonial quote="Outstanding experience." author="Marcus Rivera" variant="featured" ></snice-testimonial>

Textarea

Multi-line text input with auto-grow and character count

html <snice-textarea label="Message" rows="3" placeholder="Type your message..."></snice-textarea> <snice-textarea label="Limited" maxlength="100"> </snice-textarea> <snice-textarea auto-grow rows="2" label="Auto-grow"></snice-textarea>
html <snice-textarea label="Loading" placeholder="Fetching content..." rows="3" loading></snice-textarea>

Time Picker

Time selection with scrollable hour/minute columns

html <snice-time-picker label="Meeting Time" value="09:30" step="15" ></snice-time-picker> <!-- 12-hour format with AM/PM --> <snice-time-picker label="12-Hour Format" value="14:00" format="12h" ></snice-time-picker> <!-- Constrained time range --> <snice-time-picker min-time="08:00" max-time="18:00" step="30" ></snice-time-picker> <!-- Clearable --> <snice-time-picker clearable value="12:00"></snice-time-picker>
html <!-- Sizes --> <snice-time-picker size="small"></snice-time-picker> <snice-time-picker size="large"></snice-time-picker> <!-- States --> <snice-time-picker disabled></snice-time-picker> <snice-time-picker readonly></snice-time-picker> <snice-time-picker loading></snice-time-picker> <snice-time-picker required helper-text="Required"></snice-time-picker> <snice-time-picker invalid error-text="Invalid"></snice-time-picker>
Ready
html <form> <snice-time-picker name="appointment" value="14:05:10" format="12h" step="5" min-time="09:00:00" max-time="17:00:00" show-seconds clearable required ></snice-time-picker> <button type="submit">Submit</button> <button type="reset">Reset</button> </form>

Time Range Picker

Vertically stacked time slot picker with click-and-drag range selection

html <snice-time-range-picker start-time="08:00" end-time="18:00" granularity="30" value='[{"start":"09:00","end":"11:00"}]' ></snice-time-range-picker>
html <!-- 12h format, hourly, multiple selection --> <snice-time-range-picker start-time="06:00" end-time="22:00" granularity="60" format="12h" multiple disabled-ranges='[{"start":"12:00","end":"13:00"}]' ></snice-time-range-picker>

Timeline

Chronological event display with multiple positions and orientations

left
alternate
right
horizontal
javascript <!-- Vertical positions --> <snice-timeline position="left"></snice-timeline> <snice-timeline position="alternate"></snice-timeline> <snice-timeline position="right"></snice-timeline> <!-- Horizontal --> <snice-timeline orientation="horizontal"> </snice-timeline> timeline.items = [ { title: 'Started', timestamp: 'Jan', variant: 'info' }, { title: 'Testing', timestamp: 'Mar', variant: 'warning' }, { title: 'Launched', timestamp: 'Apr', variant: 'success' } ];

Timer

Countdown and stopwatch timers

html <snice-timer mode="stopwatch"></snice-timer> <snice-timer mode="timer" initial-time="60"> </snice-timer>

Toast

Temporary notification messages

Success Error Warning Info
html <snice-toast-container id="toasts" position="bottom-right"> </snice-toast-container> <script> toasts.show('Saved!', { type: 'success' }); </script>

Tooltip

Contextual information on hover or click

Top Right Bottom Left Click Trigger
html <snice-tooltip content="Tooltip on top" position="top"> <snice-button>Top</snice-button> </snice-tooltip> <snice-tooltip content="Click me!" trigger="click"> <snice-button variant="primary"> Click Trigger </snice-button> </snice-tooltip>

Intrinsic Tooltips

Attribute-based — no wrapper element needed

Top Right Bottom Left Click Trigger
html <button tooltip="Tooltip on top">Top</button> <button tooltip="Tooltip on right" style="--tooltip-position: right"> Right </button> <button tooltip="Click me!" style="--tooltip-trigger: click"> Click Trigger </button> <!-- Tooltip observer activates automatically when tooltip component loads -->

Tree

Hierarchical data with expand/collapse, checkboxes, keyboard navigation, and lazy loading

File System
Checkboxes (cascading)
javascript tree.nodes = [ { id: 'src', label: 'src', icon: '\ud83d\udcc1', expanded: true, children: [ { id: 'index.ts', label: 'index.ts', icon: '\ud83d\udcc4' }, { id: 'app.ts', label: 'app.ts', icon: '\ud83d\udcc4' } ] } ]; // Checkboxes with cascading selection <snice-tree show-checkboxes selection-mode="multiple" ></snice-tree> // API: expandAll(), collapseAll(), checkNode(id), // getCheckedNodes(), selectNode(id)

Treemap

Hierarchical data as nested rectangles with drill-down navigation

html + js <snice-treemap id="treemap" show-labels show-values> </snice-treemap> <script> treemap.data = { label: 'Portfolio', value: 0, children: [ { label: 'Tech', value: 0, children: [ { label: 'AAPL', value: 4200 }, { label: 'MSFT', value: 3800 }, { label: 'GOOG', value: 2900 }, { label: 'NVDA', value: 2100 } ]}, { label: 'Finance', value: 0, children: [ { label: 'JPM', value: 1800 }, { label: 'BAC', value: 1200 } ]}, { label: 'Health', value: 0, children: [ { label: 'JNJ', value: 1600 }, { label: 'PFE', value: 900 } ]} ] }; </script>
html + js <snice-treemap show-labels color-scheme="cool" ></snice-treemap> <script> treemap.data = { label: 'Disk Usage', value: 0, children: [ { label: 'Documents', value: 42 }, { label: 'Photos', value: 28 }, { label: 'Videos', value: 18 }, { label: 'Music', value: 8 }, { label: 'Other', value: 4 } ] }; </script>

User Card

Profile cards with avatar, contact info, and social links

html <snice-user-card name="Sarah Chen" avatar="avatar.png" role="Lead Engineer" company="Acme Inc" email="sarah@acme.io" location="San Francisco, CA" status="online" ></snice-user-card> <snice-user-card name="James Wilson" role="Designer" status="away" variant="compact" ></snice-user-card>

Video Player

Full-featured player with custom controls, keyboard shortcuts, PiP, and fullscreen

html <snice-video-player src="video.mp4" poster="poster.jpg" controls ></snice-video-player> <script> player.play(); player.seekTo(30); player.setPlaybackRate(1.5); player.requestFullscreen(); // Keyboard: Space=play/pause, // F=fullscreen, M=mute, // Arrow keys=seek/volume </script>

Virtual Scroller

Efficiently renders thousands of items by virtualizing the DOM — only visible items are rendered

Top Middle Bottom 5,000 items — only ~15 rendered
javascript scroller.items = Array.from({ length: 5000 }, (_, i) => ({ id: i, data: { name: `User ${i + 1}`, email: `user${i + 1}@example.com` } })); scroller.renderItem = (item) => ` <div style="display:flex;align-items:center; gap:10px;padding:12px 16px; border-bottom:1px solid rgba(128,128,128,0.15)"> <div class="avatar">${item.data.name[0]}</div> <div> <div>${item.data.name}</div> <div style="opacity:0.5">${item.data.email}</div> </div> </div>`; scroller.scrollToIndex(2500); // jump to any index

Waterfall

Bridge chart showing cumulative effect of sequential positive and negative values

javascript chart.data = [ { label: 'Revenue', value: 1000, type: 'total' }, { label: 'Product Sales', value: 420 }, { label: 'Services', value: 250 }, { label: 'Refunds', value: -80 }, { label: 'Operating Costs', value: -350 }, { label: 'Tax', value: -120 }, { label: 'Net Profit', value: 1120, type: 'total' } ]; chart.addEventListener('bar-click', e => console.log(e.detail.item));

Weather

Weather display card with current conditions, details, and multi-day forecast

javascript weather.unit = 'celsius'; weather.variant = 'full'; weather.data = { temp: 22, condition: 'Partly Cloudy', humidity: 65, wind: 12, forecast: [ { day: 'Mon', high: 24, low: 18, condition: 'Sunny' }, { day: 'Tue', high: 20, low: 15, condition: 'Rain' }, { day: 'Wed', high: 22, low: 16, condition: 'Cloudy' } ] };

Work Order

Service work order with task checklist, parts list, time tracking, and cost summary

javascript <snice-work-order id="wo" wo-number="WO-2026-0184" priority="high" status="in-progress" labor-rate="85" ></snice-work-order> <script> wo.customer = { name: 'Office Park LLC', phone: '(555) 123-4567' }; wo.tasks = [ { description: 'Inspect units', assignee: 'Mike', hours: 2, completed: true }, { description: 'Replace filters', assignee: 'Mike', hours: 3 } ]; wo.parts = [ { name: 'HVAC Filter 20x25', partNumber: 'HF-2025', quantity: 8, unitCost: 24.99 } ]; </script>
Templates - click a sheet to zoom