videorc/ui

Disabled Reason

Says why a control is disabled, on hover and on keyboard focus. A focusable wrapper carries the tooltip, and the reason stays the control's description.

Installation

$ pnpm dlx shadcn@latest add @videorc/disabled-reason

Usage

import { DisabledReason } from "@/components/ui/disabled-reason";
<DisabledReason
  reason={canGoLive ? null : "Add a destination before you go live."}
>
  <Button disabled={!canGoLive}>Go Live…</Button>
</DisabledReason>

Pass the reason only while the control is disabled. With no reason the control renders exactly as it is, with no extra tab stop.

Examples

In a menu

A menu or select item is reachable while disabled: Base UI keeps it in the arrow-key order as aria-disabled, and the highlight lands on it. Pass focusable={false} and the item itself takes the hover and the focus and is described, with no wrapper and no second stop in the list.

Theming

Token or attributeMeaning
rounded-chip, focus-visible:ring-3The wrapper's focus ring, around the control
data-disabled:pointer-events-autoAdded to a menu or select item with focusable={false}, so it takes the hover
data-slot="disabled-reason"The focusable wrapper
data-slot="disabled-reason-text"The hidden description

Keyboard

KeyAction
TabFocuses the wrapper of a disabled control and shows the reason
↑ ↓In a menu, highlight a disabled item and show its reason
EscHides the reason

Accessibility

A disabled control takes no pointer events and no focus, so a title on it never shows. The wrapper is a tab stop with the reason as its aria-describedby, kept in the DOM, hidden, not only while the tooltip shows. Use label when the control's own name isn't enough to say what the wrapper stands for. Never pass focusable={false} for a native disabled <button>: it gets no pointer events and no focus, so only the wrapper works.

API reference

Prop

Type