- Show the focus ring only when the keyboard moved the focus. Radix focuses
whatever the pointer is over, so the browser cannot answer that question
here: Chromium treats every focus after a key press as keyboard-driven,
Firefox treats no programmatic focus as keyboard-driven. The menu records
which modality arrived, watched at the document while it is open, since the
first arrow key lands on the menu itself rather than on anything we render.
- Suppress the browser's own ring on menu items, so there is one answer to
that question rather than two.
- Drop readOnly from the device radios. Compound paints a read-only control
muted, and that rule comes after the checked rule, so it overrode the accent
fill marking the selection and the menu stopped matching settings.
- Make the decorative radio inert rather than aria-hidden. A negative tabindex
inside an interactive control stays reachable to assistive technology even
when hidden, which axe rejects.
- Give each section a labelled group and mark its heading decorative. A menu
may contain only items, separators and groups, and the headings were direct
children of it. The list wrappers are role="none" for the same reason.
- Run axe over the open menu, which is what found both of the above.
Spec: FEATURES_SPEC/2026-09_Quick_Audio_Menu.md — AC23, AC24, AC25
- Bound the scrolling device list by the measured height of the call area.
It was capped with a viewport unit and a Radix variable, both of which
measure the window: as a component in a corner of a host's page, the menu
would have been sized against the whole page. The menu is portalled outside
the root, so no container query reaches it and it has to be measured.
- Leave a single divider between the two lists, as the design has. Compound
underlines every menu heading, so the headings drop theirs, and the line
closing the microphone section goes: the section wrapper already bounds the
meter's stickiness without one.
- Cover device persistence, hot-plug, fallback when the device in use is
removed, fallback to a default when a remembered device is gone, numbered
labels before permission, "Default" listed as its own entry, one device kind
not disturbing another, and camera parity. Their acceptance criteria named
check commands for tests that did not exist, which makes the criteria
unenforceable and blocks the next drift check.
Spec: FEATURES_SPEC/2026-09_Quick_Audio_Menu.md — AC3, AC4, AC5, AC8, AC9,
AC10, AC11, AC19, AC20
- Draw the input level beneath the microphone list as 32 discrete segments,
so the level reads by count and not by hue alone.
- Expose it through role="meter" with aria-valuenow and aria-valuetext, so a
screen reader announces it.
- Give denied permission and no input device their own message and next
action, rather than a flat meter that reads as silence.
- Read the level only while the menu is open, so nothing holds a second
capture of the microphone for the length of a call.
- Read the device the picker has selected, so the meter re-points as soon as
the selection changes.
- Ignore anything below a noise floor: a quiet room is never digitally
silent, and its hiss otherwise lights the first segments permanently, which
reads as "it can hear me" when nobody is speaking.
- Quantise loudness with a square root above that floor: amplitude puts
ordinary speech near the bottom of the range, where a linear meter barely
moves.
- Withhold the output select callback where the platform cannot route audio
to a chosen device, which is what renders the speaker section disabled.
Safari and most Firefox builds land here.
- Track the in-flight device by kind as well as id: Chrome names both an
input and an output "default", so the spinner appeared on the wrong row.
- Disable every device in the menu while a selection is settling, so a second
request cannot overtake the first.
The meter opens its own short-lived capture rather than tapping the call's
audio track. That is what lets it follow the picker instantly, and it avoids
the pre-join track, which is deliberately frozen to the device selected when
the screen mounted. The cost is a second capture of the same device while the
menu is open. See the spec's notes sidecar.
Spec: FEATURES_SPEC/2026-09_Quick_Audio_Menu.md — AC1, AC6, AC7, AC13, AC14,
AC15, AC17, AC18, AC21, AC22
- Mark the selection with a radio control instead of a trailing check icon
beside a device glyph, matching the design and the settings modal, so both
device pickers read the same way.
- Take the visual from Compound's RadioInput rather than restyling a span of
our own. RadioControl is wrapped in a Radix form control and needs a Form
ancestor, which a dropdown menu has no business providing; RadioInput does
not.
- Keep rows as menuitemradio with aria-checked, rendered through
MenuItem as="div" so an input is never nested inside a button.
- Render the radio aria-hidden and not focusable: Radix owns focus inside the
menu, and aria-checked carries the state.
- Leave the activating spinner unchanged.
- Keep every scope.behavior call inside the function that owns the scope,
which no-observablescope-leak requires.
- Cover the speaker section, an output that cannot be chosen, and a lone
device shown disabled, with unit tests and stories for each.
Spec: FEATURES_SPEC/2026-09_Quick_Audio_Menu.md — AC1, AC7
* add MeidaMuteAndSwitchButton
* User button in footer
* Add tests
* update styling (dark bg on menu open + chevron white + chevron up)
* fix tests
* add storybook to CI
only add storybook with storybook label
test names
another env name test
TestName
new default name
remove label condition
Update pr-deploy.yaml
* Update pr-deploy.yaml
* add toggle example to default component
* hook up footer select actions
* fix video audio button (swapped) and lable in story