Skip to main content

UI Components

Virtual List

Render huge datasets efficiently with DOM recycling — only items visible in the viewport are in the DOM:

js
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():

js
allItems.push(newItem);
refresh();

Cleanup

js
destroy(); // Remove event listeners and DOM nodes

Responsive Images

Lazy Loading

Enhance images with lazy loading via IntersectionObserver:

html
<img data-wl-src="/photos/hero.jpg" alt="Hero" width="800" height="400" />
js
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:

js
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:

js
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.

js
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:

js
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:

js
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:

js
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

FunctionAnimation
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:

js
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:

js
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:

js
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:

js
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:

js
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:

js
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:

js
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

html
<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>
js
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:

RuleDescription
requiredField must not be empty
emailMust be a valid email address
min:nMinimum length of n characters
max:nMaximum length of n characters
pattern:regexMust match the given regex
match:fieldMust 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.

js
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

js
// 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.

js
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.

js
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

FunctionOutputOptions
bar(el, opts)Vertical bar chartwidth, height, color, labels
line(el, opts)Line chart with optional area fillwidth, height, color, fill, dots
pie(el, opts)Pie/donut chartsize, donut, colors
sparkline(el, opts)Inline sparklinewidth, height, color, strokeWidth

Toast Notifications Pro

Accessible notification system with auto-dismiss, stacking, and theming. Announces to screen readers via aria-live region.

js
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

TypeUsage
'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

html
<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

js
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.

js
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'.