Actionable
The bare interactive primitive. A button, link or focusable div with the focus ring and disabled handling, nothing else.
Last updated
Show codeHide 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
Show codeHide 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 codeHide 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 codeHide 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 overas="div". as="div"is focusable but has no role. Addroleand key handling yourself, or use a button.- Disabled links lose their
hrefso 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
insetFocuswhen 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.
Related
ButtonTriggers an action. Renders a <button>, or an <a> when href is set. Hue, fill and height are three separate props.
LinkAn inline text link — for a link that should look like a button, use Button with href instead.
CardA surface container for grouped content. Compose freely inside — heading, text, button, an optional media block.