The bare interactive primitive. A button, link or focusable div with the focus ring and disabled handling, nothing else.

Last updated

A link
Show code
<Actionable class="rounded-lg border border-neutral p-4 hover:bg-neutral-faded-highlighted">
Click me
</Actionable>
<Actionable href="#" class="rounded-lg border border-neutral p-4 hover:bg-neutral-faded-highlighted">
A link
</Actionable>

Add

npx @ava-ui/apogee add actionable

No Apogee dependencies.

Usage

Wrap any custom clickable surface. You bring the look; Actionable brings the semantics, the focus ring and the disabled state.

---
import Actionable from '@/components/apogee/Actionable/Actionable.astro'

<JsNote fallback="The div is still focusable and clickable; only Enter and Space activation is missing. Prefer as='button' when you can.">
  A div rendered as a button (`as="div"`) activates like a native button: Enter on key press, Space on key release.
</JsNote>
---
<Actionable href="/plans/pro" class="rounded-lg border border-neutral p-4">
  <strong>Pro</strong>, $12/month
</Actionable>

API

@composable. Any other native attribute passes through to the rendered element.

Prop Type Default Description
href string — Renders an <a>. Dropped while disabled.
as 'button' | 'div' 'button' Element when there is no href. A div gets tabindex="0".
disabled boolean false disabled on a button; aria-disabled and no href otherwise.
fullWidth boolean false Stretches to the container width.
type 'button' | 'submit' | 'reset' 'button' Native button type (ignored with href or as="div").
insetFocus boolean false Focus ring inside the edge, for tiles that touch their neighbours.
command 'show-modal' | 'close' | 'request-close' | 'show-popover' | 'hide-popover' | 'toggle-popover' — Invoker command sent to the commandfor target; works with no JS.
commandfor string — id of the dialog or popover the command targets.
class string — Extra classes, merged last.

Variants

Link
Div
Disabled link
Show code
<Actionable>Button</Actionable>
<Actionable href="#">Link</Actionable>
<Actionable as="div">Div</Actionable>
<Actionable disabled>Disabled</Actionable>
<Actionable href="#" disabled>Disabled link</Actionable>

Inset focus

Show code
<Actionable insetFocus class="rounded-md border border-neutral p-4">Tile</Actionable>

States

Focus shows the primary outline. Disabled sets the disabled text colour and a not-allowed cursor. Hover and active are yours to add via class.

Examples

A whole card as one link. insetFocus keeps the ring inside the card edge:

Show code
<Actionable href="#" insetFocus class="block max-w-sm rounded-lg">
<Card><p class="font-semibold">Quarterly report</p><p class="text-sm text-neutral-faded">Revenue, churn and the plan for Q4.</p></Card>
</Actionable>

Accessibility

  • A real <button type="button"> or <a> by default; prefer those over as="div".
  • as="div" is focusable but has no role. Add role and key handling yourself, or use a button.
  • Disabled links lose their href so they drop out of the tab order, as a disabled button does.

Tokens

Token Used for
--outline-color-focus Focus outline

Do / Don’t

  • Do use Actionable to make a whole tile one target: a card, a row, a media block.
  • Do set insetFocus when tiles touch, so the ring is not clipped by a neighbour.
  • Don’t nest links or buttons inside it. A click target inside a click target confuses pointer and screen-reader users.
  • Don’t use it where Button or Link fits; it has no visual style of its own.

Content

  • The tile’s heading names the destination; screen readers read the whole tile as the link text, so keep the text short.