<arc-sheet> Overview
Guidelines
When to use
- Use the bottom sheet for mobile-friendly contextual panels like filters or share menus
- Use the right sheet for desktop detail panes, settings forms, or property inspectors
- Provide a descriptive `heading` so screen readers announce the dialog purpose via `aria-label`
- Listen to `arc-close` to reset your local open state and perform any necessary cleanup
- Populate the `footer` slot with primary and secondary action buttons for task-oriented sheets
When not to use
- Do not nest a Sheet inside another Sheet -- use a Modal for layered overlays instead
- Do not use Sheet for brief confirmations or alerts -- use Modal or Toast for those patterns
- Do not set both `side="bottom"` and `side="right"` -- only one placement is active at a time
- Do not place critical navigation inside a Sheet -- it is dismissible and should contain optional content
- Avoid overloading the sheet body with too many form fields -- consider a full page for complex forms
Features
- Two placement modes: `bottom` (default) and `right`, controlled by the `side` prop
- Backdrop overlay with `backdrop-filter: blur(4px)` that dismisses the sheet on click
- Bottom variant includes a rounded drag-handle bar for mobile touch affordance
- Structured layout with header, scrollable body, and sticky footer zones
- Named slots for `header` and `footer` allow full customisation of chrome areas
- Automatic scroll locking on `document.body` when open, restored on close
- Escape key dismissal with focus auto-moved to the close button on open
- Fires `arc-open` and `arc-close` custom events for lifecycle synchronisation
Preview
Status
Category
Sort by
acme-dashboard
Last deployed 2 hours ago
Framework
Next.js 14
Region
US East (iad1)
Status
Live
Team
3 members
Usage
This component requires JavaScript. No pure HTML/CSS version is available — use the Web Component directly or a framework wrapper.
<!-- Bottom sheet for filters -->
<arc-button id="open-filters" variant="secondary">Filters</arc-button>
<arc-sheet id="filter-sheet" heading="Filter Results" side="bottom">
<div style="display:flex;flex-direction:column;gap:16px;">
<div>
<strong style="font-size:13px;">Status</strong>
<div style="display:flex;gap:6px;margin-top:6px;">
<arc-chip selected>Active</arc-chip>
<arc-chip>Archived</arc-chip>
<arc-chip>Draft</arc-chip>
</div>
</div>
<arc-select placeholder="All categories" style="width:100%;"></arc-select>
</div>
<div slot="footer">
<arc-button variant="ghost" onclick="this.closest('arc-sheet').open = false">Reset</arc-button>
<arc-button variant="primary" onclick="this.closest('arc-sheet').open = false">Apply</arc-button>
</div>
</arc-sheet>
<!-- Right sheet for detail pane -->
<arc-button id="open-details" variant="secondary">Details</arc-button>
<arc-sheet id="detail-sheet" heading="Project Details" side="right">
<div style="display:flex;flex-direction:column;gap:12px;font-size:13px;">
<div style="display:flex;justify-content:space-between;">
<span style="color:var(--text-tertiary);">Framework</span>
<span style="font-weight:500;">Next.js 14</span>
</div>
<div style="display:flex;justify-content:space-between;">
<span style="color:var(--text-tertiary);">Status</span>
<arc-badge variant="success">Live</arc-badge>
</div>
</div>
<div slot="footer">
<arc-button variant="primary">Open Project</arc-button>
</div>
</arc-sheet>
<script>
document.querySelector('#open-filters').addEventListener('click', () => {
document.querySelector('#filter-sheet').open = true;
});
document.querySelector('#open-details').addEventListener('click', () => {
document.querySelector('#detail-sheet').open = true;
});
</script> import { Sheet, Button, Chip, Select, Badge } from '@arclux/arc-ui-react';
import { useState } from 'react';
function FilterSheet() {
const [open, setOpen] = useState(false);
return (
<>
<Button variant="secondary" onClick={() => setOpen(true)}>Filters</Button>
<Sheet heading="Filter Results" side="bottom" open={open} onArcClose={() => setOpen(false)}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
<div>
<strong style={{ fontSize: 13 }}>Status</strong>
<div style={{ display: 'flex', gap: 6, marginTop: 6 }}>
<Chip selected>Active</Chip>
<Chip>Archived</Chip>
<Chip>Draft</Chip>
</div>
</div>
<Select placeholder="All categories" />
</div>
<div slot="footer">
<Button variant="ghost" onClick={() => setOpen(false)}>Reset</Button>
<Button variant="primary" onClick={() => setOpen(false)}>Apply</Button>
</div>
</Sheet>
</>
);
}
function DetailSheet() {
const [open, setOpen] = useState(false);
return (
<>
<Button variant="secondary" onClick={() => setOpen(true)}>Details</Button>
<Sheet heading="Project Details" side="right" open={open} onArcClose={() => setOpen(false)}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 12, fontSize: 13 }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span style={{ color: 'var(--text-tertiary)' }}>Framework</span>
<span style={{ fontWeight: 500 }}>Next.js 14</span>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span style={{ color: 'var(--text-tertiary)' }}>Status</span>
<Badge variant="success">Live</Badge>
</div>
</div>
<div slot="footer">
<Button variant="primary">Open Project</Button>
</div>
</Sheet>
</>
);
} <script setup>
import { ref } from 'vue';
import { Sheet, Button, Chip, Badge } from '@arclux/arc-ui-vue';
const filtersOpen = ref(false);
const detailsOpen = ref(false);
</script>
<template>
<Button variant="secondary" @click="filtersOpen = true">Filters</Button>
<Sheet heading="Filter Results" side="bottom" :open="filtersOpen" @arc-close="filtersOpen = false">
<div style="display:flex;flex-direction:column;gap:16px;">
<div>
<strong style="font-size:13px;">Status</strong>
<div style="display:flex;gap:6px;margin-top:6px;">
<Chip selected>Active</Chip>
<Chip>Archived</Chip>
</div>
</div>
</div>
<template #footer>
<Button variant="ghost" @click="filtersOpen = false">Reset</Button>
<Button variant="primary" @click="filtersOpen = false">Apply</Button>
</template>
</Sheet>
<Button variant="secondary" @click="detailsOpen = true">Details</Button>
<Sheet heading="Project Details" side="right" :open="detailsOpen" @arc-close="detailsOpen = false">
<div style="display:flex;flex-direction:column;gap:12px;font-size:13px;">
<div style="display:flex;justify-content:space-between;">
<span>Framework</span>
<span style="font-weight:500;">Next.js 14</span>
</div>
<div style="display:flex;justify-content:space-between;">
<span>Status</span>
<Badge variant="success">Live</Badge>
</div>
</div>
<template #footer>
<Button variant="primary">Open Project</Button>
</template>
</Sheet>
</template> <script>
import { Sheet, Button, Chip, Badge } from '@arclux/arc-ui-svelte';
let filtersOpen = $state(false);
let detailsOpen = $state(false);
</script>
<Button variant="secondary" onclick={() => filtersOpen = true}>Filters</Button>
<Sheet heading="Filter Results" side="bottom" open={filtersOpen} on:arc-close={() => filtersOpen = false}>
<div style="display:flex;flex-direction:column;gap:16px;">
<strong style="font-size:13px;">Status</strong>
<div style="display:flex;gap:6px;">
<Chip selected>Active</Chip>
<Chip>Archived</Chip>
</div>
</div>
<div slot="footer">
<Button variant="ghost" onclick={() => filtersOpen = false}>Reset</Button>
<Button variant="primary" onclick={() => filtersOpen = false}>Apply</Button>
</div>
</Sheet>
<Button variant="secondary" onclick={() => detailsOpen = true}>Details</Button>
<Sheet heading="Project Details" side="right" open={detailsOpen} on:arc-close={() => detailsOpen = false}>
<div style="font-size:13px;display:flex;justify-content:space-between;">
<span>Status</span>
<Badge variant="success">Live</Badge>
</div>
<div slot="footer">
<Button variant="primary">Open Project</Button>
</div>
</Sheet> import { Component } from '@angular/core';
import { Sheet, Button, Chip, Badge } from '@arclux/arc-ui-angular';
@Component({
imports: [Sheet, Button, Chip, Badge],
template: `
<Button variant="secondary" (click)="filtersOpen = true">Filters</Button>
<Sheet heading="Filter Results" side="bottom" [open]="filtersOpen" (arcClose)="filtersOpen = false">
<div style="display:flex;flex-direction:column;gap:16px;">
<strong style="font-size:13px;">Status</strong>
<div style="display:flex;gap:6px;">
<Chip [selected]="true">Active</Chip>
<Chip>Archived</Chip>
</div>
</div>
<div slot="footer">
<Button variant="ghost" (click)="filtersOpen = false">Reset</Button>
<Button variant="primary" (click)="filtersOpen = false">Apply</Button>
</div>
</Sheet>
`,
})
export class FilterComponent {
filtersOpen = false;
} import { createSignal } from 'solid-js';
import { Sheet, Button, Chip, Badge } from '@arclux/arc-ui-solid';
function FilterSheet() {
const [open, setOpen] = createSignal(false);
return (
<>
<Button variant="secondary" onClick={() => setOpen(true)}>Filters</Button>
<Sheet heading="Filter Results" side="bottom" open={open()} onArcClose={() => setOpen(false)}>
<div style="display:flex;flex-direction:column;gap:16px;">
<strong style="font-size:13px;">Status</strong>
<div style="display:flex;gap:6px;">
<Chip selected>Active</Chip>
<Chip>Archived</Chip>
</div>
</div>
<div slot="footer">
<Button variant="ghost" onClick={() => setOpen(false)}>Reset</Button>
<Button variant="primary" onClick={() => setOpen(false)}>Apply</Button>
</div>
</Sheet>
</>
);
} import { useState } from 'preact/hooks';
import { Sheet, Button, Chip, Badge } from '@arclux/arc-ui-preact';
function FilterSheet() {
const [open, setOpen] = useState(false);
return (
<>
<Button variant="secondary" onClick={() => setOpen(true)}>Filters</Button>
<Sheet heading="Filter Results" side="bottom" open={open} onArcClose={() => setOpen(false)}>
<div style="display:flex;flex-direction:column;gap:16px;">
<strong style="font-size:13px;">Status</strong>
<div style="display:flex;gap:6px;">
<Chip selected>Active</Chip>
<Chip>Archived</Chip>
</div>
</div>
<div slot="footer">
<Button variant="ghost" onClick={() => setOpen(false)}>Reset</Button>
<Button variant="primary" onClick={() => setOpen(false)}>Apply</Button>
</div>
</Sheet>
</>
);
} API
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Controls whether the sheet is visible. Reflected as an attribute and toggleable programmatically. |
side | 'bottom' | 'right' | 'bottom' | Which edge the panel slides in from. Bottom sheets have a max-height of 80vh; right sheets are 400px wide. |
heading | string | '' | Text displayed in the header row. Also used as the `aria-label` for the dialog panel. |
Events
| Event | Description |
|---|---|
arc-open | Fired when the sheet opens |
arc-close | Fired when the sheet closes |
See Also
- Drawer Slide-out panel with backdrop overlay, keyboard dismissal, and left/right positioning for off-canvas navigation, filters, and detail views.
- Modal General-purpose focus-trapping overlay with backdrop blur, slide-up animation, and ESC-to-close behavior for forms, settings, and rich content that needs full user attention.
- Sidebar Collapsible navigation sidebar with grouped sections, heading labels, and active link highlighting. Ideal for documentation sites, admin panels, and any layout that needs persistent vertical navigation.