Breath
Live
Rendered live with React 19.2.7
import './breath-button.css';
import type { ComponentPropsWithoutRef } from 'react';
// The React dialect over the stylesheet — same markup and class names as the html
// tab. The sheen is a background-position transition on the button itself, so
// there is no overlay element to render.
interface BreathButtonProps extends ComponentPropsWithoutRef<'button'> {
size?: 'sm' | 'md' | 'lg';
loading?: boolean;
}
export function BreathButton({
size = 'md',
loading = false,
disabled,
children,
...props
}: BreathButtonProps) {
return (
<button
className={size === 'md' ? 'breath-btn' : `breath-btn breath-btn--${size}`}
type="button"
disabled={disabled || loading}
aria-busy={loading || undefined}
{...props}
>
{loading && <span className="breath-btn__spinner" aria-hidden="true" />}
<span className="breath-btn__label">{children}</span>
</button>
);
}
react@19.2.7the dialect you are reading
Built on Astro: the stage mounts the exhibit's actual React or Vue file as a live island. Pick the framework and the stage, the code and the dependency list agree.
The calm end of the range. A rounded fill with almost no contrast in it, one highlight parked off the edge, and on approach that highlight walks across the face over nine hundred milliseconds while the whole thing rises by two percent. The press is the only moment it moves down.
The sheen is a background position, not a keyframe animation — which is why its Tailwind dialect is the same design rather than an approximation of it. A variant that needs an @keyframes block needs a stylesheet, and a stylesheet is the one thing the utility version promises you do not need.