UI Components
Virtual List
Render huge datasets efficiently with DOM recycling — only items visible in the viewport are in the DOM:
import { virtualList } from 'weblisk/ui/virtual-list.js'; const { refresh, destroy } = virtualList('#item-list', { items: () => allItems(), // Signal or array of items itemHeight: 48, // Fixed row height in px overscan: 5, // Extra rows above/below viewport render: (item, index) => ` <div class="item"> <span>${item.name}</span> <span>${item.email}</span> </div> `, });
Updating
When the data source changes (if using a signal), the list auto-updates. For arrays, call
refresh():
allItems.push(newItem); refresh();
Cleanup
destroy(); // Remove event listeners and DOM nodes
Responsive Images
Lazy Loading
Enhance images with lazy loading via IntersectionObserver:
<img data-wl-src="/photos/hero.jpg" alt="Hero" width="800" height="400" />
import { lazyImages } from 'weblisk/ui/img.js'; const cleanup = lazyImages({ selector: '[data-wl-src]', rootMargin: '200px', // Start loading 200px before visible fade: true, // CSS fade-in effect });
Srcset Generation
Generate responsive srcset from a URL pattern:
import { srcset } from 'weblisk/ui/img.js'; const responsive = srcset('/images/hero-{w}.jpg', [320, 640, 1024, 1920]); // → "/images/hero-320.jpg 320w, /images/hero-640.jpg 640w, ..."
Blur-Up Placeholders
Show a tiny blurred thumbnail while the full image loads:
import { blurUp } from 'weblisk/ui/img.js'; blurUp('#hero-container', '/images/hero-thumb.jpg', '/images/hero-full.jpg');
The thumbnail is displayed with a CSS blur filter, then crossfaded to the full image when it loads.
Clipboard
Read and write the system clipboard with async support and a reactive lastCopied signal.
Falls back to execCommand('copy') in older browsers.
import { writeText, readText, lastCopied } from 'weblisk/ui/clipboard.js'; // Copy text to clipboard await writeText('Hello, world!'); // Read text from clipboard const text = await readText(); // Reactive signal — updates whenever writeText() is called console.log(lastCopied()); // → 'Hello, world!'
Rich Content
Copy and read arbitrary MIME types via ClipboardItem:
import { write, read } from 'weblisk/ui/clipboard.js'; await write([new ClipboardItem({ 'text/html': blob })]); const items = await read();
Resize Observer
Track element dimensions reactively with ResizeObserver. Returns live signals for width and height:
import { observeResize, onResize } from 'weblisk/ui/resize.js'; // Reactive signals — update automatically on resize const { width, height, cleanup } = observeResize('#panel'); console.log(width(), height()); // Callback-based alternative const stop = onResize('#panel', (entry) => { console.log('Resized:', entry.contentRect); });
Animations
Imperative Web Animations API wrapper with sequencing, parallel execution, and preset helpers:
import { animate, fadeIn, slideIn, sequence } from 'weblisk/ui/animate.js'; // Custom animation animate('#box', [{ transform: 'scale(0)' }, { transform: 'scale(1)' }], { duration: 300, easing: 'ease-out' } ); // Presets fadeIn('#toast', 200); slideIn('#sidebar', { from: 'left', duration: 400 }); // Sequential: each waits for the previous to finish await sequence([ ['#a', [{ opacity: 0 }, { opacity: 1 }], 200], ['#b', [{ opacity: 0 }, { opacity: 1 }], 200], ]);
Presets
| Function | Animation |
|---|---|
fadeIn(el, ms?) | Opacity 0 → 1 |
fadeOut(el, ms?) | Opacity 1 → 0 |
slideIn(el, opts?) | Slide from left/right/top/bottom |
scaleIn(el, ms?) | Scale 0.8 → 1 with fade |
Dialog & Popover
Native <dialog> element wrapper with reactive open state and focus trap integration.
Also provides Popover API support with manual fallback:
import { dialog, popover } from 'weblisk/ui/dialog.js'; // Modal dialog with focus trap and backdrop close const d = dialog('#my-dialog'); d.showModal(); // Opens as modal console.log(d.open()); // Reactive signal → true d.close(); // Native popover const p = popover('#tooltip', { type: 'auto' }); p.toggle(); // Show/hide popover
Fullscreen
Toggle fullscreen mode with a reactive isFullscreen signal:
import { toggleFullscreen, isFullscreen } from 'weblisk/ui/fullscreen.js'; btn.addEventListener('click', () => toggleFullscreen('#video')); // Reactive signal — use in effects or bindings console.log(isFullscreen()); // → true/false
Web Share
Share content via the native share sheet with a clipboard fallback for unsupported browsers:
import { share, canShare } from 'weblisk/ui/share.js'; if (canShare()) { await share({ title: 'Check out Weblisk', text: 'Zero-dependency JS framework', url: 'https://weblisk.dev', }); }
On desktop browsers without Web Share API, the URL is copied to the clipboard as a fallback.
Drag & Drop
Simplified drag and drop with reactive state and CSS class management:
import { draggable, dropZone, fileDropZone } from 'weblisk/ui/drag.js'; // Make an element draggable const cleanup = draggable('#card', { type: 'task', data: 'task-42', }); // Create a drop zone with reactive isOver signal const { isOver } = dropZone('#column', { type: 'task', onDrop: (data, e) => moveTask(data), }); // File drop zone — great for upload areas fileDropZone('#upload', (files) => handleFiles(files));
Media Session
Control OS-level media notifications (lock screen, notification tray) via the MediaSession API:
import { setMetadata, setPlayback, setActionHandlers } from 'weblisk/ui/media-session.js'; setMetadata({ title: 'My Track', artist: 'Artist', album: 'Album', artwork: [{ src: '/images/cover.jpg', sizes: '512x512' }], }); setPlayback('playing'); const cleanup = setActionHandlers({ play: () => audio.play(), pause: () => audio.pause(), previoustrack: () => prev(), nexttrack: () => next(), });
Wake Lock
Prevent the screen from dimming or locking. Automatically re-acquires when the page becomes visible again:
import { requestWakeLock, releaseWakeLock, wakeLockActive } from 'weblisk/ui/wakelock.js'; await requestWakeLock(); console.log(wakeLockActive()); // → true // Release when done await releaseWakeLock();
Geolocation
Reactive geolocation with one-shot and continuous watch modes:
import { geolocation, getCurrentPosition } from 'weblisk/ui/geo.js'; // Reactive — updates as position changes const { position, error, cleanup } = geolocation({ watch: true }); const pos = position(); if (pos) console.log(pos.latitude, pos.longitude); // Promise-based one-shot const loc = await getCurrentPosition();
Form Validation Pro
Declarative validation via data-validate attributes with async submission, loading states, and inline error display.
Setup
<form id="signup" action="/api/signup"> <input name="email" data-validate="required|email" /> <input name="password" type="password" data-validate="required|min:8" /> <button type="submit">Sign Up</button> </form>
import { enhance } from 'weblisk/pro/form.js'; const { submitting, errors } = enhance('#signup', { format: 'json', onSuccess: (data) => console.log('Signed up!', data), onError: (err) => console.error(err), }); // submitting() → reactive boolean // errors() → reactive { fieldName: 'error message' }
Validation Rules
Chain rules with the pipe | separator in data-validate:
| Rule | Description |
|---|---|
required | Field must not be empty |
email | Must be a valid email address |
min:n | Minimum length of n characters |
max:n | Maximum length of n characters |
pattern:regex | Must match the given regex |
match:field | Must match another field's value |
Data Tables Pro
Interactive sortable, filterable, paginated tables from any data source. Renders with ARIA attributes for accessibility, includes CSV export.
import { table, exportCSV } from 'weblisk/pro/table.js'; const t = table('#users', { data: users, pageSize: 20, filterable: true, columns: [ { key: 'name', label: 'Name', sortable: true }, { key: 'email', label: 'Email', sortable: true }, { key: 'role', label: 'Role' }, ], });
CSV Export
// Export the current filtered data as a CSV download exportCSV(t.rows(), columns, 'users.csv');
Reactive State
All table state is reactive — page, sort, filter, and rows are signals. Update them programmatically and the table re-renders automatically.
t.setPage(3); // Jump to page 3 t.setSort({ key: 'name', dir: 'desc' }); t.setFilter('admin'); // Filter rows containing 'admin' t.setData(newUsers); // Replace data source
SVG Charts Pro
Pure DOM SVG chart generation — bar, line, pie, and sparkline. No canvas, no external deps, accessible by default with <title> elements on every data point.
import { bar, line, pie, sparkline } from 'weblisk/pro/chart.js'; // Bar chart bar('#revenue', { data: [ { label: 'Jan', value: 420 }, { label: 'Feb', value: 380 }, { label: 'Mar', value: 510 }, ], width: 400, height: 250, color: '#6366f1', }); // Line chart with area fill line('#traffic', { data: dailyViews, fill: true, dots: true, color: '#10b981', });
Chart Types
| Function | Output | Options |
|---|---|---|
bar(el, opts) | Vertical bar chart | width, height, color, labels |
line(el, opts) | Line chart with optional area fill | width, height, color, fill, dots |
pie(el, opts) | Pie/donut chart | size, donut, colors |
sparkline(el, opts) | Inline sparkline | width, height, color, strokeWidth |
Toast Notifications Pro
Accessible notification system with auto-dismiss, stacking, and theming. Announces to screen readers via aria-live region.
import { toast } from 'weblisk/pro/toast.js'; // Simple notification toast('Settings saved successfully', { type: 'success' }); // With custom duration and action button const { dismiss } = toast('Item deleted', { type: 'warning', duration: 8000, action: 'Undo', onAction: () => restoreItem(), position: 'top-right', }); // Programmatic dismiss dismiss();
Toast Types
| Type | Usage |
|---|---|
'info' | General information (default) |
'success' | Operation completed successfully |
'warning' | Caution — action may have side effects |
'error' | Operation failed |
Positioning
Six positions are supported: top-right, top-left, top-center, bottom-right (default), bottom-left, bottom-center.
Accessible Tabs Pro
ARIA-compliant tablist/tab/tabpanel pattern with keyboard navigation (Arrow keys, Home, End).
HTML Structure
<div data-tabs> <div role="tablist"> <button role="tab" aria-controls="panel-1">Overview</button> <button role="tab" aria-controls="panel-2">Details</button> </div> <div role="tabpanel" id="panel-1">Overview content</div> <div role="tabpanel" id="panel-2">Details content</div> </div>
Initialize
import { tabs } from 'weblisk/pro/tabs.js'; const { activate, active } = tabs('[data-tabs]', { defaultIndex: 0, onChange: (index, tab, panel) => { console.log('Switched to tab', index); }, }); // Programmatic activation activate(1); console.log(active()); // → 1
Payments Pro
Wraps the browser Payment Request API for native checkout UI, with fallback handling. Works with any payment backend.
import { pay, payButton, status } from 'weblisk/pro/pay.js'; // One-line payment button payButton('#pay-btn', { currency: 'USD', amount: 1999, // $19.99 in cents label: 'Pro License', endpoint: '/api/charge', }); // Or call pay() directly for custom flows const result = await pay({ currency: 'USD', amount: 4999, label: 'Enterprise Plan', endpoint: '/api/charge', requestEmail: true, }); if (result.success) { toast('Payment complete!', { type: 'success' }); }
Status Signal
The reactive status signal tracks the payment lifecycle: 'idle' → 'pending' → 'success' or 'error'.