JavaScript Action Sheet
@lemonadejs/actionsheet · ✓ 15 contract checks · framework-agnostic · zero dependencies
<Actionsheet /> — iOS-style action sheet on the Modal primitive.
Faithful port of @lemonadejs/actionsheet (v5): a bottom sheet over a dimmed backdrop, listing GROUPS of actions — each group a white rounded card, each action a full-width button; action: ‘cancel’ renders red. Built ON Modal (headerless, position bottom, backdrop) the way every v5 floating surface was built on @lemonadejs/modal.
v5 → v6 mapping: visible → bind (the open state, two-way); show()/hide() → api.open()/close()/toggle() + isOpened(); actions keeps its name and its shape ([{ options: [{ title, action, className, onclick }] }]) and is LIVE — swap the array, the sheet re-renders (v5 show(options) merged properties; in v6 you write the state instead). Per-option onclick still receives the option object. The sheet does NOT auto-close on a pick — exactly like v5, closing is the consumer’s call.
Added: closable (backdrop click closes — v5 shipped no close affordance at all; Escape ALWAYS closes regardless: the backdrop Modal traps Tab inside the sheet, so the keyboard must keep an exit); title/message header card (v5 shipped the CSS for .jactionsheet-title/-message but never rendered them — resurrected). Dropped: the v5 slide-bottom-out exit animation (it gated closing on animationend; v6 closes immediately, the slide-IN stays, pure CSS). onclose(origin): ‘backdrop’ | ‘escape’ | ‘api’.
Example
import { html, ref } from 'lemonadejs';
import Actionsheet from '@lemonadejs/actionsheet';
const App = (props, { state }) => {
const sheet = ref();
const picked = state('');
const pick = (option) => {
picked.value = option.title;
sheet.current.close();
};
const actions = [
{ options: [
{ title: 'Share photo', onclick: pick },
{ title: 'Add to album', onclick: pick },
{ title: 'Delete photo', onclick: pick },
] },
{ options: [{ title: 'Cancel', action: 'cancel', onclick: pick }] },
];
return html`<div>
<button onclick="${() => sheet.current.open()}">Photo options</button>
<p>Picked: <b>${() => picked.value || 'nothing yet'}</b></p>
<${Actionsheet} ref="${sheet}" actions="${actions}" closable
title="IMG_2048.jpg" message="Taken 12 Aug 2026, Lisbon" />
</div>`;
};Installation
npm install @lemonadejs/actionsheet
import Actionsheet from '@lemonadejs/actionsheet';
import '@lemonadejs/actionsheet/style.css';
Three deployment forms, one component:
html`<${Actionsheet} />` // by value (no registration)
setComponents({ Actionsheet }); // then <Actionsheet /> by name anywhere
createWebComponent(Actionsheet); // <lm-actionsheet> in plain HTML/any framework
Props
Every declared prop arrives as a live state — pass a value for a snapshot or a state for a two-way live wire. Attribute strings are coerced to the declared type.
| Prop | Type | Default | Description |
|---|---|---|---|
bind | boolean | — | Two-way bound value. .set() fires onchange; plain assignment is silent. open state (v5: visible) |
actions | array | — | ActionsheetGroup[] — live (v5: actions) |
title | string | '' | optional header card title (v5 CSS, resurrected) |
message | string | '' | optional header card message (v5 CSS, resurrected) |
label | string | "Actions" | accessible name when there is no title (aria-label) |
closable | boolean | false | backdrop click closes the sheet (Escape always closes) |
Events
All event names are lowercase (the platform convention — LJS-305 warns otherwise).
onopen— sheet openedonclose— sheet closed (origin)
API
import { ref } from 'lemonadejs';
const actionsheet = ref();
html`<${Actionsheet} ref="${actionsheet}" />`;
// actionsheet.current.open(...) · actionsheet.current.close(...) · actionsheet.current.toggle(...) · actionsheet.current.isOpened(...)
open()close()toggle()isOpened()
Styling
All classes follow the lm-actionsheet-* convention; visual variants are data-*
attributes on the root. Override freely — there is no styling engine to fight.
Contract
The machine-readable schema ships with the package:
import contract from '@lemonadejs/actionsheet/contract.json';
verify.json carries the conformance proof produced by verify(Actionsheet).
Looking for the v5 plugin? See the archived v5 documentation.