Custom elements
Every primitive as framework-agnostic Web Components, with their parts, state attributes and events, in Vue, Svelte, plain HTML and any framework.
@audio-ui/elements provides every primitive as a set of unstyled, accessible custom elements, built on @audio-ui/core: their parts, keyboard interactions, ARIA semantics and data-* state attributes. The elements work in plain HTML and in any framework that renders HTML, such as Vue, Svelte, Angular or Lit. Every element has a page with its attributes, its parts, a live preview and its usage in your framework.
Installation
pnpm add @audio-ui/elements
Each primitive is a separate entry point, @audio-ui/elements/<primitive>, which registers its elements when imported. The package root registers every primitive. Import an entry once, before the elements are used:
import "@audio-ui/elements/knob";Registration is a no-op on the server: server-rendered markup upgrades once the module loads in the browser.
Usage
<audio-ui-knob min="20" max="20000" default-value="1000" scale="log">
<audio-ui-knob-label>Cutoff</audio-ui-knob-label>
<audio-ui-knob-control>
<audio-ui-knob-track></audio-ui-knob-track>
<audio-ui-knob-indicator></audio-ui-knob-indicator>
<audio-ui-knob-thumb></audio-ui-knob-thumb>
</audio-ui-knob-control>
<audio-ui-knob-value></audio-ui-knob-value>
</audio-ui-knob>
<script type="module">
import "@audio-ui/elements/knob";
const knob = document.querySelector("audio-ui-knob");
knob.addEventListener("input", (event) => setCutoff(event.detail.value));
</script>Value and events
Every control follows the same contract:
| Member | Kind | Description |
|---|---|---|
input | Event | Fires on every change. event.detail holds the new value and the reason for the change, such as "drag", "keyboard", "wheel", "reset" or "input-change". Calling preventDefault() rejects the change. |
change | Event | Fires once when an interaction that changed the value ends. Use it to commit: start a note, record an undo step, save. |
value | Property | Reads the current value, and sets it. While input is dispatched it reads the value proposed, as a native input's does, so event.target.value is the new one. Framework bindings (v-model in Vue, value={} in Svelte) read and write this property. During a drag, the control follows the pointer even when the bound value lags behind. |
name | Attribute | Submits the value with the enclosing form, through the control's native range input. |
A part that holds a value of its own fires events prefixed with its name, for example clipinput and clipchange on a timeline clip. These never collide with the control's own input and change, and they bubble, so a single listener on the root can handle every part.
Styling
The elements apply only the styles their behavior depends on: the display of each box, the positioning of placed parts, touch-action on interactive surfaces. These rules are wrapped in :where(), so they carry no specificity and any rule of yours takes precedence. No color, size or spacing is set. Style the parts by tag and by the same state attributes as the React parts, with a stylesheet or with classes. Parts drawn as SVG use currentColor.
The examples on every element's page do both, and the Styling choice of the customizer says which one they show. They read the theme's variables. The CSS examples carry the default theme's values as fallbacks, so they work as pasted; to change the look, define the variables once in your own stylesheet. The Tailwind examples need them, named for their classes:
:root {
--background: oklch(1 0 0);
--border: oklch(0.922 0 0);
--card: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--input: oklch(0.922 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--popover: oklch(1 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--radius: 0.625rem;
--ring: oklch(0.708 0 0);
}
.dark {
--background: oklch(0.145 0 0);
--border: oklch(1 0 0 / 10%);
--card: oklch(0.205 0 0);
--foreground: oklch(0.985 0 0);
--input: oklch(1 0 0 / 15%);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--popover: oklch(0.205 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--ring: oklch(0.556 0 0);
}The Accent, Radius and Font of the customizer restyle every preview, and Copy CSS gives this stylesheet with your choices.
Elements
Each primitive is a set of elements, with a page of its attributes, its parts and a live preview:
| Element | What it is |
|---|---|
| Knob | <audio-ui-knob> is the root of a rotary control. It owns the value and renders its parts from it. |
| Fader | <audio-ui-fader> is the root of a linear control, with audio defaults: vertical, from -60 to +6. It has role="group", labelled by its label part. |
| Pad | <audio-ui-pad> is a grid of velocity-sensitive pads with role="group". A pad sounds while held, by pointer or with Enter and Space, and each pointer holds its own pad. Velocity comes from pen or touch pressure, or from the vertical press position. The pads share one tab stop and are navigated with the arrow keys. |
| Piano | <audio-ui-piano> is a playable keyboard with role="group". A key sounds while pressed, by pointer or with Enter and Space. Dragging glides across keys, and each pointer holds its own key, so chords can be played on touch screens. The keys share one tab stop and are navigated with the arrow keys, by octave with Shift. |
| XY pad | <audio-ui-xypad> controls a two-dimensional value { x, y }. A press moves the point to the pointer and a drag follows it; the wheel, the trackpad and the arrow keys move it; a double click or Delete resets it. The position is exposed as --xy-pad-x (from the left) and --xy-pad-y (from the top), as percentages. |
| Meter | <audio-ui-meter> displays one or more level channels. Levels come from the value and peak attributes or properties, one per channel, or from a live source property, { getSnapshot, subscribe }, whose frames are written directly to the parts. A click clears clip indicators latched by a live source. |
| Sequencer | <audio-ui-sequencer> is a step grid with one row per sound and role="group". Pressing a cell toggles it, and dragging applies the new state to every cell crossed. The cells share one tab stop and are navigated with the arrow keys. Rows and steps are generated from templates: |
| Envelope | <audio-ui-envelope> is a breakpoint envelope editor with role="group". Points are dragged to move, added with a double click on the graph, and removed with a double click, Alt + click or Delete. Each segment has a curve handle at its midpoint, and Alt + drag on the graph bends the segment under the pointer. The points share one tab stop: Page Up and Page Down (or Home and End) move between them, and the arrow keys move the focused point. Points and segment handles are generated, from a <template> when one is provided: |
| Plot | <audio-ui-plot> defines two axes and lays out everything drawn on them in axis units, for response curves, filter displays and transfer functions. Each drawing part renders its own SVG covering the plot, in currentColor, with a constant stroke width. Curves take their function and traces their live source as properties: |
| Waveform | <audio-ui-waveform> displays audio and, with a control part, seeks through it: a press seeks to the pointer, a drag scrubs, and the keyboard steps. The shape comes from the peaks property or a live levels source; the position from value or a live source, exposed as --waveform-position: |
| Timeline | <audio-ui-timeline> is the root of an arrangement: a ruler that sets the position, lanes of clips that move, trim and fade, loop ranges, and a viewport that scrolls and zooms. Units are arbitrary: seconds, beats or steps. |
| Piano roll | <audio-ui-piano-roll> is a note editor placed inside an <audio-ui-timeline>, which provides its length, zoom, grid and position. Its entry point also registers the timeline and piano elements. Notes are set through the value property as { id, pitch, start, length, velocity?, chance?, muted? }[], and the selection through the selection property. Rows, notes and velocity bars are generated from them: |
Data attributes
Parts reflect the state of their control:
Data attributes · 9
data-disableddata-draggingdata-drag-modedrag-mode, for cursor styling.data-orientationhorizontal or vertical.data-activedata-ondata-currentdata-paintingdata-blockedThe package
pnpm add @audio-ui/elements
import "@audio-ui/elements";| Export | Kind | What it is |
|---|---|---|
ENVELOPE_TAGS | const | The names the envelope's elements are registered under, frozen: a page cannot rename them by accident. |
EnvelopeChangeEvent | type | Fired once an interaction that changed the points ends. |
EnvelopeElement | class | The root of a breakpoint envelope editor, with role="group". |
EnvelopeElementEventMap | interface | The events an envelope fires, as addEventListener types them. |
EnvelopeEventDetail | interface | What an envelope's input and change events carry, besides the envelope itself as their target. |
EnvelopeFillElement | class | The area under the curve, in currentColor. |
EnvelopeGraphElement | class | The drawing surface, filling the envelope (position: absolute; inset: 0) and containing the line and the fill. |
EnvelopeInputEvent | type | Fired on every change. |
EnvelopeLineElement | class | The curve through the points, in currentColor, with a stroke width that stays constant as the graph scales. |
EnvelopePointElement | class | A draggable handle for the point at index, positioned with left and bottom. |
EnvelopePointsElement | class | Generates an <audio-ui-envelope-point> per point, in x order, from its <template> when present. |
EnvelopeSegmentElement | class | A curve handle at the midpoint of the segment that starts at point index, a role="slider" from -1 to 1. |
EnvelopeSegmentsElement | class | Generates an <audio-ui-envelope-segment> per segment, from its <template> when present. |
FADER_TAGS | const | The names the fader's elements are registered under, frozen: a page cannot rename them by accident. |
FaderChangeEvent | type | Fired once a gesture ends with a new value. |
FaderControlElement | class | The interactive area. |
FaderElement | class | The root of a linear control, with audio defaults: vertical, from -60 to +6. |
FaderElementEventMap | interface | The events a fader fires, as addEventListener types them. |
FaderEventDetail | interface | What a fader's input and change events carry, besides the fader itself as their target. |
FaderIndicatorElement | class | The filled range of the track, from min to the value, or between anchor and the value. |
FaderInputEvent | type | Fired on every change. |
FaderLabelElement | class | The accessible label. |
FaderThumbElement | class | The draggable thumb, positioned at the value. |
FaderTrackElement | class | The full range of travel, containing the indicator and the thumb. |
FaderValueElement | class | Renders the value, formatted with the fader's format and locale. |
KNOB_TAGS | const | The names the knob's elements are registered under, frozen: a page cannot rename them by accident. |
KnobChangeEvent | type | Fired once a gesture ends with a new value. |
KnobControlElement | class | The interactive surface. |
KnobElement | class | The root of a rotary control. |
KnobElementEventMap | interface | The events a knob fires, as addEventListener types them. |
KnobEventDetail | interface | What a knob's input and change events carry, besides the knob itself as their target. |
KnobIndicatorElement | class | The arc from anchor to the value, rendered over the track. |
KnobInputEvent | type | Fired on every change. |
KnobLabelElement | class | The accessible label. |
KnobThumbElement | class | Rotates with the value, clockwise from 12 o'clock, through rotate. |
KnobTrackElement | class | The full sweep of the dial, rendered as an SVG arc in currentColor. |
KnobValueElement | class | Renders the value, formatted with the knob's format and locale. |
METER_TAGS | const | The names the meter's elements are registered under, frozen: a page cannot rename them by accident. |
MeterChannelElement | class | A channel with role="meter", for its index, labelled by aria-label or an <audio-ui-meter-label>. |
MeterChannelsElement | class | Generates an <audio-ui-meter-channel> per level from its <template>, regenerated when the channel count changes. |
MeterElement | class | The root of a level meter, with role="group". |
MeterIndicatorElement | class | The lit range of a channel, scaled to the level along the meter's length. |
MeterLabelElement | class | The accessible label of its channel. |
MeterMarkElement | class | A scale graduation at value, such as -18 dB, placed along the meter's length from its minimum. |
MeterPeakElement | class | The peak-hold marker at the channel's peak level. |
MeterSnapshot | interface | What the meter's parts draw from. |
MeterSource | interface | A live signal, in the shape of an external store: subscribe calls back on each new frame, getSnapshot returns the latest. |
MeterTrackElement | class | The full range of a channel, behind the indicator. |
MeterValueElement | class | Renders the channel's level, formatted with the meter's format and locale. |
PAD_TAGS | const | The names the pad's elements are registered under, frozen: a page cannot rename them by accident. |
PadElement | class | The root of a velocity-sensitive pad grid, with role="group". |
PadElementEventMap | interface | The events a pad grid fires, as addEventListener types them. |
PadItemElement | class | A pad, a button that sounds while held, by pointer or with Enter and Space. |
PadOffDetail | interface | What a padoff event carries: the pad released, and why. |
PadOffEvent | type | Fired when a pad stops sounding. |
PadOnDetail | interface | What a padon event carries: the pad struck, how hard, and how. |
PadOnEvent | type | Fired when a pad is struck. |
PIANO_TAGS | const | The names the piano's elements are registered under, frozen: a page cannot rename them by accident. |
PianoElement | class | The root of a playable keyboard, with role="group". |
PianoElementEventMap | interface | The events a piano fires, as addEventListener types them. |
PianoKeyElement | class | A key, positioned for its pitch. |
PianoKeyLabelElement | class | The name of its key, such as "C4". |
PianoKeysElement | class | Generates an <audio-ui-piano-key> per pitch, lowest first, filled from its <template>, such as with an <audio-ui-piano-key-label>. |
PianoNoteOffDetail | interface | What a noteoff event carries: the pitch released, and why. |
PianoNoteOffEvent | type | Fired when a key stops sounding. |
PianoNoteOnDetail | interface | What a noteon event carries: the pitch played, how hard, and how. |
PianoNoteOnEvent | type | Fired when a key starts sounding. |
PIANO_ROLL_TAGS | const | The names the piano roll's elements are registered under, frozen: a page cannot rename them by accident. |
PianoRollElement | class | The root of a note editor, placed inside an <audio-ui-timeline>, which provides its length, zoom, grid and position. |
PianoRollElementEventMap | interface | The events a piano roll fires, as addEventListener types them. |
PianoRollEventDetail | interface | What a roll's input and change events carry, besides the roll itself as their target. |
PianoRollGridElement | class | The note surface: a focusable timeline lane with role="group", with the rows stacked vertically. |
PianoRollKeysElement | class | Lays out the <audio-ui-piano> it contains on the roll's rows, low end at the bottom, and lights the sounding pitches. |
PianoRollMarqueeElement | class | The selection rectangle during a marquee drag. |
PianoRollNoteElement | class | A note, positioned at its start in timeline units and on its pitch's row. |
PianoRollNoteHandleElement | class | Resizes its note from its edge (start or end): the end changes the length, the start trims from the front with the end fixed. |
PianoRollNotesElement | class | Generates an <audio-ui-piano-roll-note> per note in reading order (by start, then highest pitch first), which is also the tab order, from its <template>, such as with <audio-ui-piano-roll-note-handle>s. |
PianoRollRowElement | class | The band of a pitch across the grid, for row shading, scale highlighting and octave lines. |
PianoRollRowsElement | class | Generates an <audio-ui-piano-roll-row> per row, highest pitch first, from its <template> when present. |
PianoRollSelectionSpanElement | class | The time range of the selected notes, spanning the height of its container: the grid, the velocity lane or the ruler. |
PianoRollStretchHandleElement | class | An edge of the selection span. |
PianoRollVelocitiesElement | class | Generates an <audio-ui-piano-roll-velocity> per note, from its <template> when present. |
PianoRollVelocityElement | class | A note's velocity, or chance in a level="chance" lane, rendered as a bar sized by --piano-roll-level. |
PianoRollVelocityLaneElement | class | A timeline lane of per-note bars for velocity or, with level="chance", probability, each at its note's start. |
PLOT_TAGS | const | The names the plot's elements are registered under, frozen: a page cannot rename them by accident. |
PlotBandElement | class | The range of one axis between from and to, spanning the other axis, such as a sweep range or a level above a threshold. |
PlotCursorElement | class | Tracks a mouse or pen over the plot, for crosshairs and readouts. |
PlotCursorReadoutElement | class | Renders the value under the pointer as text, inside an <audio-ui-plot-cursor>. |
PlotCurveElement | class | Plots y = fn(x) across the plot in axis units, from the fn property, such as a frequency response in dB. |
PlotDotElement | class | A non-interactive point, such as the input level on a compressor curve. |
PlotDotSource | type | A live point, in the axes' units. |
PlotElement | class | The root of a plot. |
PlotGridElement | class | Grid lines and labels at round values along the axes. |
PlotHandleChangeEvent | type | Fired once a gesture that moved a handle ends, and after each key or input change. |
PlotHandleElement | class | A draggable point in axis units, such as an equalizer band or a filter's cutoff and resonance. |
PlotHandleElementEventMap | interface | The events a handle fires, as addEventListener types them. |
PlotHandleEventDetail | interface | What a handle's input and change events carry, besides the handle itself as their target. |
PlotHandleInputEvent | type | Fired on every move of a handle. |
PlotLineElement | class | A straight line at value: vertical on the x axis (axis="x", the default), such as a cutoff, or horizontal on the y axis, such as 0 dB. |
PlotMarkElement | class | A label at value on one axis, such as "1k" or "+12", positioned with left (x, the default) or top (y). |
PlotSnapshot | interface | What every part draws from: the plot's two axes, and whether it is disabled. |
PlotTraceElement | class | A live line from the y values of the source property, such as a spectrum, spread evenly across the x axis and redrawn on each change. |
PlotTraceSource | type | A live list of y values, spread evenly across the x axis. |
SEQUENCER_TAGS | const | The names the sequencer's elements are registered under, frozen: a page cannot rename them by accident. |
SequencerCellElement | class | A step: a toggle button, pressed while the step is on (data-on). |
SequencerChangeEvent | type | Fired when a press, a paint or a key press that changed the pattern ends. |
SequencerElement | class | The root of a step sequencer, one row per sound, with role="group". |
SequencerElementEventMap | interface | The events a sequencer fires, as addEventListener types them. |
SequencerEventDetail | interface | What a sequencer's input and change events carry. |
SequencerInputEvent | type | Fired while cells are flipped. |
SequencerRowElement | class | A row of steps with role="group", for its index from the top. |
SequencerRowsElement | class | Generates an <audio-ui-sequencer-row> per pattern row from its <template>. |
SequencerSource | interface | A live value followed without rendering: here, the step being played. |
SequencerStepElement | class | A generated step, for its index. |
SequencerStepsElement | class | Generates an <audio-ui-sequencer-step> per step of its row from its <template>: a cell, a velocity bar, or both. |
SequencerVelocityChangeEvent | type | Fired when a drag, a key press or a reset that changed a velocity ends. |
SequencerVelocityDetail | interface | What a sequencer's velocityinput and velocitychange events carry. |
SequencerVelocityElement | class | A step's velocity, rendered as a bar sized by --sequencer-velocity. |
SequencerVelocityInputEvent | type | Fired while a velocity is drawn or nudged. |
TimelineClipElement | class | A region of a source on a lane, such as a recording or a pattern. |
TimelineClipElementEventMap | interface | The events a clip fires, as addEventListener types them. |
TimelineClipEventDetail | interface | What a clip's events carry, besides the clip itself as their target. |
TimelineClipFadeElement | class | Renders the fade at its edge (in or out) over the clip: the gain ramp as a line in currentColor, with the attenuated area shaded by --clip-fade-shade (the line color by default). |
TimelineClipFadeHandleElement | class | Sets the length of the fade at its edge (in or out) by drag, off the grid, or through the native <input type="range"> it contains, which carries the accessible name. |
TimelineClipHandleElement | class | Trims the clip's edge (start or end) by drag, on the grid unless Cmd or Ctrl is held, or through the native <input type="range"> it contains, which carries the accessible name. |
TimelineClipHeaderElement | class | The clip's title bar. |
TimelineLaneElement | class | A lane for placed parts, such as a track's clips or a row of markers. |
TimelineLaneElementEventMap | interface | The events a selectable lane or ruler fires, as addEventListener types them. |
TimelineMarkElement | class | A part placed at value on a lane, such as a bar number, a cue or a grid line, labelled by its content. |
TimelineMarkElementEventMap | interface | The events a mark fires, as addEventListener types them. |
TimelineNavigatorElement | class | An overview bar of the whole timeline, showing the visible range as <audio-ui-timeline-navigator-window>. |
TimelineNavigatorHandleElement | class | An edge of the navigator window (start or end). |
TimelineNavigatorWindowElement | class | The visible range of the timeline. |
TimelinePlayheadElement | class | A marker at the playback position on its lane, following it through --timeline-offset. |
TimelineRulerElement | class | The lane that sets the position: a press seeks to the pointer and a drag scrubs, on the grid unless Cmd or Ctrl is held, four times finer with Shift. |
TimelineViewportElement | class | The scrolling container of the timeline. |
TimelineRangeElement | class | A loop or selection on its lane, from value ("start end"; the whole timeline by default). |
TimelineRangeElementEventMap | interface | The events a range fires, as addEventListener types them. |
TimelineRangeEventDetail | interface | What a range's events carry: which edge moved, or "both", and the range, [start, end] in the timeline's units. |
TimelineRangeHandleElement | class | Resizes the edge (start or end) of its range by drag, on the grid unless Cmd or Ctrl is held, or through the native <input type="range"> it contains, which carries the accessible name. |
TIMELINE_TAGS | const | The names the timeline's elements are registered under, frozen: a page cannot rename them by accident. |
TimelineChangeEvent | type | Fired once a gesture that moved the position ends. |
TimelineDescribe | type | Returns how a position is announced. |
TimelineElement | class | The root of a timeline. |
TimelineElementEventMap | interface | The events a timeline fires, as addEventListener types them. |
TimelineEventDetail | interface | What a timeline's input and change events carry, besides the timeline itself as their target. |
TimelineInputEvent | type | Fired on every move of the position. |
TimelineSettings | interface | What the timeline's parts measure and step by, as its attributes set them. |
TimelineZoomEvent | type | Fired when the zoom changes, already clamped. |
ViewportMetrics | interface | How the viewport is scrolled and zoomed, as last laid out. |
TimelineTrackElement | class | A track: a row with a header and a lane. |
TimelineTrackElementEventMap | interface | The events a track fires, as addEventListener types them: its height, in pixels. |
TimelineTrackHeaderElement | class | The header beside a track's lane, for its name and controls. |
TimelineTrackResizeHandleElement | class | Sets the track's height by drag, or through the native <input type="range"> it contains, which carries the accessible name: the arrow keys step (Down is taller), Page Up and Page Down take larger steps, and Home and End go to the minimum and maximum. |
WAVEFORM_TAGS | const | The names the waveform's elements are registered under, frozen: a page cannot rename them by accident. |
WaveformBarElement | class | A bar of <audio-ui-waveform-bars>, for its index. |
WaveformBarsElement | class | Renders the waveform as count <audio-ui-waveform-bar> elements (24) for CSS-styled visualizers. |
WaveformCanvasElement | class | Renders the waveform on a canvas aligned to the device pixel grid, so it stays sharp at any density. |
WaveformChangeEvent | type | Fired once a seek that moved the position ends. |
WaveformControlElement | class | The seekable surface: a press seeks to the pointer and a drag scrubs. |
WaveformControlElementEventMap | interface | The events a selectable waveform control fires, as addEventListener types them. |
WaveformDescribe | type | Returns how a position is announced. |
WaveformElement | class | The root of a waveform. |
WaveformElementEventMap | interface | The events a waveform fires, as addEventListener types them. |
WaveformEventDetail | interface | What a waveform's input and change events carry, besides the waveform itself as their target. |
WaveformHoverElement | class | Shown at the hovered position while the pointer is over the control. |
WaveformInputEvent | type | Fired on every seek. |
WaveformLevelsSource | type | A live signal: the current levels, 0…1 each, or raw samples for the scope variant. |
WaveformMarkerElement | class | A cue or chapter marker at value, labelled by its content. |
WaveformPlayheadElement | class | A non-interactive marker at the playback position. |
WaveformPositionSource | type | A live playback position, in duration units. |
WaveformRangeDetail | interface | What a selection's and a region's events carry: the stretch, [start, end] in duration units. |
WaveformRegionElement | class | A loop or selection over the waveform, from value ("start end" in duration units; the whole audio by default). |
WaveformRegionElementEventMap | interface | The events a region fires, as addEventListener types them. |
WaveformRegionHandleElement | class | Resizes the edge (start or end) of its region by drag or through the native <input type="range"> it contains, which receives its aria-label. |
WaveformThumbElement | class | A handle centered on the playback position, through --waveform-position. |
XYPAD_TAGS | const | The names the XY pad's elements are registered under, frozen: a page cannot rename them by accident. |
XYPadChangeEvent | type | Fired once a gesture ends with a new point. |
XYPadControlElement | class | The interactive surface. |
XYPadElement | class | The root of a two-dimensional control. |
XYPadElementEventMap | interface | The events an XY pad fires, as addEventListener types them. |
XYPadEventDetail | interface | What an XY pad's input and change events carry, besides the pad itself as their target. |
XYPadInputEvent | type | Fired on every change. |
XYPadLabelElement | class | The accessible label. |
XYPadThumbElement | class | Marks the point, positioned with left and top and centered with translate. |
XYPadValueElement | class | Renders the point as "x, y", formatted with the pad's format and locale. |