Dust. Fragment shaders as a design primitive.

Every component surface is a live GPU shader. Per-pixel lighting, cursor tracking, composable effects — each component renders to its own canvas via a shared WebGL context. Standard Custom Elements. Zero runtime dependencies.

~25 kB Brotli total
1 GL Context
0 Dependencies

Base Shaders

Every component accepts a shader attribute. These are the built-in surfaces — the visual material behind your DOM content. Mouse interaction is continuous: the shader knows where the cursor is, how fast it's moving, and how long it's been there.

dust/solid
dust/bevel
dust/gradient — move pointer

Prismatic facets — wide format.

dust/crystalline

Northern lights colour shift.

dust/aurora

Radial pulse rings from center.

dust/pulse-ring
dust/ripple

The Compose System

Effects are composable layers that stack on any base. Use -fx shader variants and the effect attribute. The system generates one uber-shader per combination — no runtime branching, no multi-pass overhead. Control each effect's weight and parameters via CSS custom properties.

Single Effects

effect="shimmer" — hover to activate
effect="hoverglow" — hover for flare
effect="crystalline"
no effects — just the bevel base

Stacked Effects

effect="hoverglow noisefog"
effect="hoverglow shimmer"
effect="ripple"

Triple Composition

Bevel + Shimmer + Aurora + Noise Fog

fog 0.4 / aurora 0.6 / shimmer on hover

CSS partial weights: --dust-fx-noisefog: 0.4

Gradient + Aurora + Pulse Ring + Noise Fog

all at full weight

different base, same effects

Solid + Lens Flare + Ripple + Shimmer

all hover-activated — move pointer

3 hover effects stacked

CSS Effect Control

Effect parameters are CSS custom properties. Change a property and the shader responds on the next frame. Theme entire component trees with a single class.

--dust-fx-shimmer: 0.7 / speed: 3.0

Solid + Noise Fog + Lens Flare

--dust-color: #1a1a2e

Gradient + Pulse Ring

--dust-inner / --dust-outer

Class-Driven Theming

Define a CSS class with custom properties. Apply it to any component. The shader picks up the new values automatically.

.danger { --dust-color: #e74c3c }
.ocean { --dust-fx-noisefog-color: ... }
.mint { --dust-color: #1abc9c }

Works Everywhere

Each component renders to its own canvas — shaders work inside any container, regardless of background colour or stacking context. No z-index hacks needed.

shader visible inside dark container
Crystal Chip
chip in opaque container
+
fab in opaque container

Component Library

Standard Custom Elements that drop into any HTML page. Every component is a regular DOM node — flexbox, CSS sizing, accessibility tree, event listeners. The shader is just the surface.

Buttons & Actions

Save
dust-button
Submit
--dust-color
Delete
--dust-color
Deep
--dust-depth: 0.3
Disabled
disabled
+
dust-fab
+
green
+
disabled
Default
dust-chip
Selected
selected
Purple
custom color

Inputs

dust-switch
checked
dust-checkbox
checked
indeterminate
dust-radio
checked
grouped
dust-slider
min
max
dust-textfield
email
password
dust-select
dust-combobox

Display

Card

Shader background with header, body, and footer slots.

Footer area
dust-card

Fog Card

Any shader works as the card background.

with noisefog effect

Aurora Card

Aurora shader running behind content slots.

Action
with nested dust-button
dust-avatar
purple
small
dust-badge (dot)
value: 3
max: 50
circular 70%
indeterminate
linear 40%
indeterminate

Shader Showcase — Crystalline

One effect applied across every component type. The shader adapts to each component's shape, size, and interaction model. Adjust the speed to see how CSS properties drive shader parameters in real time.

1
Crystal
chip
+
fab
switch
checkbox
radio
slider
badge
avatar
circular
linear
textfield
select
combobox

Reactive Field

Components share a physics simulation. Click anywhere — ripples propagate through every component in the field. Each component receives the wave as a shader effect, creating a unified surface that spans multiple DOM elements.

Bevel Solid Chip +

Outer Reactive Field

The water surface renders between components. Waves flow around obstacles, bounce off edges, and create caustic highlights. The simulation runs on the CPU; the rendering is a single fullscreen shader pass. 1

Bevel Solid Crystalline Aurora
Chip +
Shimmer

Infinitely Expandable

Bring your own shaders, compose your own effects, extend your own components. The system is open at every layer.

Custom Shader

Register a fragment shader. Use it on any component.

// Register your shader
registerShader('my/plasma', `
  vec2 uv = gl_FragCoord.xy / u_resolution;
  float t = u_time * 0.4;

  // Two overlapping sine waves = plasma
  float v = sin(uv.x*8.0+t) * cos(uv.y*6.0-t*0.7)
          + sin(length(uv-0.5)*12.0-t*1.5) * 0.5;

  float w = v * 0.5 + 0.5;
  vec3 col = mix(u_color.rgb, u_color.rgb + vec3(0.3, 0.1, 0.4), w);

  // Lighten on hover — u_hover is 0.0 or 1.0
  col += u_hover * 0.12;

  // Rounded-rect SDF — clips to component shape
  float rad = u_radius * min(u_resolution.x, u_resolution.y) * 0.5;
  vec2 q = abs(gl_FragCoord.xy - u_resolution*0.5) - u_resolution*0.5 + rad;
  float d = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - rad;
  float a = 1.0 - smoothstep(-1.0, 0.5, d);
  fragColor = vec4(clamp(col, 0.0, 1.0) * a, a);
`);

// Use it — works on any component, any shape
<dust-button shader="my/plasma" animated>Go</dust-button>
<dust-fab shader="my/plasma" animated>+</dust-fab>
Custom plasma shader
Button + Chip

Custom Component

Extend DustElement. Inherit shaders, CSS bridge, pointer tracking, and all effects. Click the panels to cycle palettes.

import { DustElement } from '@dust/components';

const PALETTES = [
  [0.18, 0.20, 0.45], // blue
  [0.45, 0.12, 0.18], // crimson
  [0.12, 0.38, 0.22], // emerald
  [0.42, 0.28, 0.12], // amber
];

class PaletteCycle extends DustElement {
  private idx = 0;

  connectedCallback() {
    this.setAttribute('shader', 'my/plasma');
    this.setAttribute('animated', '');
    super.connectedCallback();
    this.addEventListener('click', this.cycle);
  }

  private cycle = () => {
    this.idx = (this.idx + 1) % PALETTES.length;
    const c = PALETTES[this.idx];
    setComponentUniform(this.comp.id, 'u_color', c);
  };
}
customElements.define('palette-cycle', PaletteCycle);
Click to cycle palette
Button
+
Chip

The Full Engine

Dust is a modular WebGL engine. This demo ships Dust WebComponents — one layer. The full stack includes sprite rendering, MSDF text, scene graph, animation, and more. Every module is tree-shakeable; import only what you use.

Engine Modules

Module
Lines
WebComponents — this demo
8,006
Scene Graph
2,377
Render Pipeline
1,743
MSDF Text
1,364
Animation
890
Mote — WebGL bootstrap
773
Layout
634
Input
616
Engine Core — pulse, timing
548
Texture
415
Viewport
340
UI
339
Total
18,045

WebComponent Internals

Component
Lines
Inputs — switch, slider, radio, ...
1,691
Shaders — GLSL fragments
1,247
Core — WebGL engine, FBO pool
1,109
Compose — shader composition
1,228
Field — reactive physics sim
602
Display — card, badge, avatar, ...
586
Base — element, button, CSS bridge
561
Buttons — chip, FAB
270
Web — projector canvas
144
Headless — offscreen surface
89

Zero runtime dependencies. Tree-shaking drops what you don't import. This demo transfers ~25 KB brotli over the wire.