The attribute open changed the moment you pressed. What you just saw was only paint.
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
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
- DetailsReveal and height transition for
<details>. - DialogBackdrop, or the modal itself appearing from the effect.
- ImageThe picture bleeds through the ink as it loads.
- Progress and meterValue changes that run like ink.
- SwapCovered, swapped, revealed: tabs and pages.
- ExpiryA time limit that burns the element away.
- Popover
popoverelements andpopovertargetbuttons. - Renderer and effectsBuilt-in effects, your own GLSL, TSL.
Principles
- Native first. Never reimplement what the browser already does: focus trap, Esc,
inert, roles, focus restore. - WebGL is decoration. The canvas is
aria-hidden, takes no focus or pointer events, holds no content, and exists only while an animation runs. - 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.
- Functions, not classes. Small composable functions, no global state.
- 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.