glass-button
A real <button> under a WebGL2 render of thick glass with a procedural effect inside: fire or water, in four status colours. Hover, press, tab to it. Everything below is the same component at different sizes and settings.
Status modes
The status attribute recolours the effect, the glass rim and the floor glow and shows a matching icon; unknown is the default. Changing it crossfades the palette. icon="none" hides the icon; slot="icon" supplies your own.
Water
effect="water" swaps the combustion for a lit liquid: a waving surface with a specular crest, caustics, rising bubbles and a faint mist. Status palettes tint it; without a status it is aqua. Switching effects crossfades. Hover raises the waves; pressing sends a ripple across.
States
Idle burns low. Hovering raises the effect, the lower glass illumination and the rim brightness over a few hundred milliseconds. Moving the pointer slides the key light across the glass. Pressing compresses the pill slightly and pulses the effect, then relaxes with no bounce. Use the controls under the hero button above to hold these states.
Sizes
The shader works in units of pill height, so rims, refraction and effect scale stay proportional. The effect buffer keeps a fixed texel density per pill height instead of stretching.
Mobile width
An explicit width on the host stretches the pill; the effect still scales with the height.
Reduced motion
With prefers-reduced-motion: reduce the effect is frozen in place and the renderer only draws while a hover or press state is easing. The design stays intact. The toggle below forces it via the reducedMotion property.
CSS fallback
When WebGL2 is unavailable the element sets data-renderer="css" and the shadow stylesheet paints a simplified glass and glow. Setting that attribute yourself forces the fallback, as here.
Usage
<script type="module" src="glass-button.js"></script>
<glass-button level="1.2" bloom="0.8">
Take Action →
</glass-button>
<script type="module">
const el = document.querySelector('glass-button');
el.addEventListener('click', () => console.log('clicked'));
el.intensity = 1.3; // property ↔ attribute
el.params = { turbulence: 1.4 }; // merge several at once
</script>