WebGL UI

Accessible-by-default WebGL effects for native HTML elements. The element keeps its state, its keyboard and its place in the accessibility tree; WebGL only paints the surface.

Live example: a native <details> with the ink effect
Open me

The attribute open changed the moment you pressed. What you just saw was only paint.

Install

Not on npm yet. The library is plain TypeScript with no dependencies: copy src/ into your project, or import it from the repository.

Quick start

<details>
  <summary>Shipping</summary>
  <div>Orders leave the warehouse within two days.</div>
</details>
import { createRenderer, createDetailsFx, inkEffect } from 'webgl-ui'

// One renderer per page: one canvas and one WebGL context, shared by every element.
const renderer = createRenderer()

document.querySelectorAll('details').forEach(d => createDetailsFx(d, { renderer, effect: inkEffect }))

That is all. Keyboard, open, the toggle event, find-in-page and screen readers keep working as they do natively.

Components

Principles

  1. Native first. Never reimplement what the browser already does: focus trap, Esc, inert, roles, focus restore.
  2. WebGL is decoration. The canvas is aria-hidden, takes no focus or pointer events, holds no content, and exists only while an animation runs.
  3. Progressive enhancement. No WebGL2, a lost context, or a preference for reduced motion, forced colors, more contrast or reduced transparency: the element behaves exactly like the native one.
  4. Functions, not classes. Small composable functions, no global state.
  5. CSS can follow. While animating, elements expose [data-animating] and --p, the same progress the shader uses.

Every component has a parity table against MDN: native parity. For the inner workings, see architecture.