Skip to content

fix(BBBToggle): show keyboard focus and disabled state; add tooltip - #118

Merged
Arthurk12 merged 4 commits into
bigbluebutton:developfrom
Arthurk12:fix/toggle-focus-disabled
Nov 12, 2026
Merged

Arthurk12 merged 4 commits into
bigbluebutton:developfrom
Arthurk12:fix/toggle-focus-disabled

Conversation

@Arthurk12

Copy link
Copy Markdown
Member

What does this PR do?

BBBToggle now shows keyboard focus and a dimmed disabled state, and gains a tooltip, matching the recent BBBCheckbox changes.

  • Keyboard focus draws a 2px brand-colored outline around the switch, like BBButton and BBBCheckbox.
  • When disabled, the label and helper text are dimmed and show a not-allowed cursor.
  • A disabled toggle in the on state is now dimmed too, instead of keeping its full brand color.
  • New tooltipLabel and tooltipPlacement (top by default, bottom, left, right) props show a tooltip on hover and keyboard focus.
  • A disabled toggle with a tooltipLabel stays reachable with Tab, so keyboard users can read why it is unavailable. It is announced as unavailable, can't be toggled, and is left out of form submission and validation, like a natively disabled toggle.
  • Toggles that are enabled and have no tooltip render as before.
  • Storybook controls, stories and the README now cover disabled, tooltipLabel and tooltipPlacement.

Motivation

Keyboard focus is only a faint halo on the thumb, a disabled toggle's text still looks clickable, and a disabled toggle that is on looks enabled. A disabled toggle also has no way to tell users why it is unavailable.

More

  • Added/updated documentation

When BBBToggle is focused from the keyboard, MUI's focus ripple only
tints the small thumb. That faint halo is the only focus cue, which
falls short of WCAG 2.4.7 (Focus Visible).

Draw a 2px colorBrand1 outline, offset by 2px, around the switch while
it holds `.Mui-focusVisible`, the same treatment BBButton and
BBBCheckbox use, so mouse clicks don't show it. The outline goes on
the switch root because the track is clipped by the root's overflow.
The root gets the track's border radius only while focused: a
permanent radius would clip the track's anti-aliased edge a second
time and slightly change how an unfocused toggle renders.
The component does not pass `disabled` to its text. With `disabled`,
MUI dims the switch, but the label and helper text keep their regular
colors and the text keeps `cursor: pointer`, so they look and point as
if the toggle could still be changed.

Destructure `disabled`, pass it on to the MUI switch and, as a
transient `$disabled` prop, to the text wrapper, the label and the
helper text. Disabled text gets `cursor: not-allowed` and
colorNeutral3, the same color BBButton and BBBCheckbox use for
disabled text.

Also give the Storybook `disabled` control its default value, reword
it and the README row to mention the dimmed text, and add a Disabled
story.
A disabled BBBToggle in the on state looks enabled: its track stays at
full brand color. The checked track's `opacity: 1` override outranks
the opacity MUI applies to a disabled track, so only the unchecked
track is dimmed.

Restate MUI's disabled track opacity (0.12 in the light theme, 0.2 in
the dark one) for a checked, disabled switch, so both states dim the
same way. Add a DisabledChecked story.
@Arthurk12
Arthurk12 force-pushed the fix/toggle-focus-disabled branch from 7ec5fd8 to 6fa1e48 Compare November 10, 2026 01:20
@Arthurk12 Arthurk12 mentioned this pull request Nov 10, 2026
2 tasks done

@germanocaumo germanocaumo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Focus ring, disabled styling, the dimmed checked track and the new tooltip triggers check out on MUI 6.4.9, 6.5.0 and 7.3.4 in Chromium, Firefox and WebKit (the tooltip stays up from text to switch and skips the empty full-width row). The tooltip has the same three issues #117 just fixed; the same changes apply.

Comment thread src/components/Toggle/component.tsx Resolved
Comment thread src/components/Toggle/component.tsx Resolved
Comment thread src/components/Toggle/component.tsx Resolved

@germanocaumo germanocaumo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

verified and working

BBBToggle has no way to show a tooltip. When a toggle is disabled,
users get no explanation of why they can't change it. BBButton,
BBBAccordion and BBBCheckbox already offer this through
`tooltipLabel` and `tooltipPlacement`.

Add the same two props, backed by Tippy and wired as in BBBCheckbox.
Unlike BBBCheckbox's container, the toggle's wrapper spans the whole
row, so it can't trigger or anchor the tooltip: the tooltip would open
over empty space and center on the row. The text and the switch root
trigger it instead, and it points at the text, or at the switch when
there is no text. A 100ms hide delay bridges the gap between the text
and the switch, so crossing it doesn't hide and reshow the tooltip.
Tippy is rendered beside the wrapper and pointed at it through
`reference`, instead of wrapping it: wrapping would change the root
element whenever `tooltipLabel` is added or removed, and React would
remount the toggle, dropping its focus and resetting an uncontrolled
toggle to `defaultChecked`.

The switch root is looked up through MUI's `switchClasses.root`: the
Switch ref points at its inner button, which a disabled switch hides
from the pointer with `pointer-events: none`. Passing a ref through
`slotProps.root` was discarded, as it could override a consumer's own
root slot props.

The tooltip opens on `mouseenter` and `focusin`, because `focusin`
bubbles up from the input and Tippy's `focus` trigger does not. The
tooltip text is also kept in a `hidden` element that the input's
`aria-describedby` points to, after the helper text or
`ariaDescribedBy`, since Tippy only mounts its content while the
tooltip is shown. A visually hidden copy was discarded: it is still
page text, so screen readers would read the tooltip twice in browse
mode.

When nothing else names the toggle (no `label`, `ariaLabel`,
`ariaLabelledBy` or `helperText`), the tooltip text names it through
`aria-labelledby` instead of describing it, so a toggle with only a
tooltip still has an accessible name.

A natively disabled input leaves the tab order, so keyboard users
could never reach the tooltip that explains why it is disabled. When
`disabled` and `tooltipLabel` are both set, the input gets
`aria-disabled="true"` instead of the native `disabled` attribute and
stays focusable:
- the MUI switch still receives `disabled`, so it keeps its disabled
  look;
- an `onClick` with `preventDefault()` on the switch cancels mouse
  clicks, label clicks and Space, and MUI skips `onChange` for a
  prevented click. It sits on the switch because MUI drops event
  handlers passed through `slotProps.input`, except `onChange`;
- `name` and `required` are dropped from the input, so it stays out
  of form submission and validation, as a natively disabled input
  would;
- MUI never sets `Mui-focusVisible` while disabled, so the focus
  outline also matches `.Mui-disabled input:focus-visible`.

Without a tooltip, a disabled toggle keeps the native `disabled`
attribute and leaves the tab order, as before.
@Arthurk12
Arthurk12 force-pushed the fix/toggle-focus-disabled branch from 0180879 to 5de4152 Compare November 12, 2026 18:22
@Arthurk12
Arthurk12 merged commit 46f98da into bigbluebutton:develop Nov 12, 2026
2 checks passed
@Arthurk12
Arthurk12 deleted the fix/toggle-focus-disabled branch November 12, 2026 22:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants