Skip to content

Launcher

The Launcher component provides a powerful command palette for keyboard-driven navigation in your Astro projects. It follows the WAI-ARIA combobox pattern and includes full keyboard support, instant search, and proper ARIA attributes. The component supports navigation links, action buttons with toggle states, and automatic integration with preference toggles from accessible-astro-components.

Use the Launcher component when you need to:

  • Provide keyboard-driven navigation
  • Create a command palette (like VS Code’s Cmd/Ctrl+K)
  • Allow quick searching through site pages
  • Toggle preferences (dark mode, high contrast, reduced motion)
  • Provide power users with efficient navigation
  • Add a search-style trigger in your header
  • Accessible by default: WAI-ARIA combobox pattern with proper roles
  • Keyboard navigation: Open with Cmd/Ctrl + K, navigate with arrow keys
  • Screen reader support: Live region announcements for results
  • Quick search: Instant client-side fuzzy search
  • Navigation items: Link to any page with custom icons
  • Action items: Toggle preferences with LED-style indicators
  • Preference sync: Automatic sync with accessible-astro-components toggles
  • Dark mode: Automatic light/dark theming via light-dark() CSS
  • Customizable: Extensive styling through CSS custom properties
  • i18n ready: All text labels customizable via props
  • Zero dependencies: Pure Astro components
  • TypeScript: Full type support
Terminal window
npm install accessible-astro-launcher
---
import {
Launcher,
LauncherTrigger,
LauncherList,
LauncherGroup,
LauncherItem,
} from 'accessible-astro-launcher'
---
<LauncherTrigger launcherId="site-launcher" />
<Launcher id="site-launcher">
<LauncherList>
<LauncherGroup label="Navigation">
<LauncherItem type="navigation" href="/" label="Home" />
<LauncherItem type="navigation" href="/about" label="About" />
<LauncherItem type="navigation" href="/contact" label="Contact" />
</LauncherGroup>
<LauncherGroup label="Preferences">
<LauncherItem type="action" onAction="toggle-dark-mode" label="Dark mode" />
</LauncherGroup>
</LauncherList>
</Launcher>

The Launcher system consists of five components that work together:

The main dialog component containing the search input and results.

<Launcher
id="my-launcher"
labels={{
placeholder: "Search...",
noResults: "No results found",
close: "Close"
}}
>
<!-- LauncherList with items goes here -->
</Launcher>

A button that opens the launcher. Can be placed anywhere in your layout.

<!-- Full trigger with placeholder -->
<LauncherTrigger launcherId="my-launcher" />
<!-- Compact trigger -->
<LauncherTrigger launcherId="my-launcher" compact />
<!-- Icon only trigger -->
<LauncherTrigger launcherId="my-launcher" iconOnly />
<!-- With gradient border effect -->
<LauncherTrigger launcherId="my-launcher" gradientBorder />

Wrapper for items with proper ARIA listbox role.

<LauncherList>
<!-- LauncherGroup or LauncherItem components -->
</LauncherList>

Optional grouping wrapper for organizing items.

<LauncherGroup label="Navigation">
<LauncherItem type="navigation" href="/" label="Home" />
<LauncherItem type="navigation" href="/about" label="About" />
</LauncherGroup>

Individual items - either navigation links or action buttons.

<!-- Navigation item -->
<LauncherItem
type="navigation"
href="/dashboard"
label="Dashboard"
keywords={["admin", "panel"]}
/>
<!-- Action item (toggle) -->
<LauncherItem
type="action"
onAction="toggle-dark-mode"
label="Dark mode"
pressed={false}
/>
<!-- Navigation item with custom icon -->
<LauncherItem type="navigation" href="/settings" label="Settings">
<svg slot="icon" viewBox="0 0 24 24"><!-- icon SVG --></svg>
</LauncherItem>
PropTypeDefaultDescription
idstringRequiredUnique identifier (must match launcherId on triggers)
labelsLauncherLabels{}i18n labels object for all UI text
classstring''Additional CSS classes
PropertyTypeDefaultDescription
placeholderstring'Search or use commands...'Search input placeholder
noResultsstring'No results found'Text shown when no results match
endOfResultsstring'End of results'Text shown at end of results
resultsCountstring'{count} results'Template for results count
closestring'Close'Close button label
clearstring'Clear'Clear button label
toSelectstring'to select'Hint text for Enter key
toNavigatestring'to navigate'Hint text for arrow keys
toClosestring'to close'Hint text for Escape key
PropTypeDefaultDescription
launcherIdstringRequiredID of the launcher to open
idstringundefinedOptional trigger element ID
placeholderstring'Search or use commands...'Placeholder text
shortcutKeystring'K'Keyboard shortcut key to display
compactbooleanfalseCompact mode without placeholder
iconOnlybooleanfalseIcon-only mode
gradientBorderbooleanfalseAnimated gradient border effect
classstring''Additional CSS classes
PropTypeDefaultDescription
classstring''Additional CSS classes
PropTypeDefaultDescription
labelstringRequiredGroup heading text
classstring''Additional CSS classes
PropTypeDefaultDescription
type'navigation' | 'action'RequiredItem type
labelstringRequiredDisplay text
hrefstringundefinedURL for navigation items
onActionstringundefinedAction identifier for action items
pressedbooleanfalseInitial pressed state for toggles
keywordsstring[][]Additional search keywords
typeLabelstring'Go to' / 'Run'Label for type indicator
classstring''Additional CSS classes

Slots:

SlotDescription
iconCustom icon for navigation items

Accessibility isn’t an afterthought - it’s built into the core of this component through the WAI-ARIA combobox pattern. The Launcher component implements:

KeyAction
Cmd/Ctrl + KOpen launcher
Arrow Up/DownNavigate items
EnterSelect item
EscapeClose launcher
TabMove between header elements
  • Dialog with aria-modal="true" and proper labeling
  • Input with role="combobox", aria-controls, aria-activedescendant
  • List with role="listbox"
  • Items with role="option", aria-selected
  • Groups with role="group", aria-labelledby
  • Live region for results count announcements
  • Focus moves to input when launcher opens
  • Virtual focus via aria-activedescendant for smooth navigation
  • Focus returns to trigger element when launcher closes
  • Clear focus indicators on all interactive elements
  • LED-style indicators for toggle action states
  • Clear selected item highlighting
  • Visible focus outlines
  • Proper color contrast in light and dark modes

The Launcher component dispatches custom events that you can listen for:

Dispatched when an action item is selected.

document.addEventListener('launcher:action', (event) => {
switch (event.detail.action) {
case 'toggle-dark-mode':
window.darkMode?.toggle()
break
case 'toggle-high-contrast':
window.highContrast?.toggle()
break
case 'toggle-reduced-motion':
window.reducedMotion?.toggle()
break
case 'logout':
window.location.href = '/logout'
break
}
})

Dispatched when the launcher opens. Useful for syncing preference states.

document.addEventListener('launcher:open', () => {
// Sync preference states when launcher opens
console.log('Launcher opened')
})

Make the Launcher your own while maintaining its accessibility features.

The Launcher uses CSS custom properties for easy theming:

:root {
/* Base theme colors */
--launcher-theme-light: #fff;
--launcher-theme-dark: #090b0f;
/* These are auto-generated from theme colors */
--launcher-text-color: /* auto light/dark */;
--launcher-subtle-text-color: /* auto light/dark */;
--launcher-outer-border-color: /* auto light/dark */;
--launcher-inner-border-color: /* auto light/dark */;
--launcher-main-body-color: /* auto light/dark */;
--launcher-action-bar-color: /* auto light/dark */;
--launcher-kbd-color: /* auto light/dark */;
--launcher-interaction-color: /* auto light/dark */;
--launcher-backdrop-color: rgba(0 0 0 / 0.3);
}
/* Custom purple theme */
:root {
--launcher-theme-light: #faf5ff;
--launcher-theme-dark: #1a0a2e;
}
/* Larger launcher */
:root {
--launcher-width: min(95vw, 800px);
--launcher-height: min(70vh, 600px);
}

Here’s a full implementation showing all features:

---
import {
Launcher,
LauncherTrigger,
LauncherList,
LauncherGroup,
LauncherItem,
} from 'accessible-astro-launcher'
import { DarkMode, HighContrast, ReducedMotion } from 'accessible-astro-components'
---
<header>
<nav>
<a href="/">My Site</a>
<LauncherTrigger launcherId="main-launcher" gradientBorder />
<DarkMode />
</nav>
</header>
<Launcher
id="main-launcher"
labels={{
placeholder: "Search pages, actions...",
noResults: "Nothing found. Try a different search.",
}}
>
<LauncherList>
<LauncherGroup label="Pages">
<LauncherItem type="navigation" href="/" label="Home" keywords={["start", "main"]} />
<LauncherItem type="navigation" href="/about" label="About Us" keywords={["team", "company"]} />
<LauncherItem type="navigation" href="/blog" label="Blog" keywords={["articles", "posts"]} />
<LauncherItem type="navigation" href="/contact" label="Contact" keywords={["email", "form"]} />
</LauncherGroup>
<LauncherGroup label="Preferences">
<LauncherItem type="action" onAction="toggle-dark-mode" label="Toggle dark mode" />
<LauncherItem type="action" onAction="toggle-high-contrast" label="Toggle high contrast" />
<LauncherItem type="action" onAction="toggle-reduced-motion" label="Toggle reduced motion" />
</LauncherGroup>
<LauncherGroup label="Account">
<LauncherItem type="navigation" href="/settings" label="Settings" />
<LauncherItem type="action" onAction="logout" label="Log out" />
</LauncherGroup>
</LauncherList>
</Launcher>
<!-- Handle custom actions -->
<script>
document.addEventListener('launcher:action', (event) => {
if (event.detail.action === 'logout') {
window.location.href = '/logout'
}
})
</script>

The package includes full TypeScript definitions. Import types as needed:

import type {
LauncherProps,
LauncherTriggerProps,
LauncherListProps,
LauncherGroupProps,
LauncherItemProps,
LauncherLabels,
LauncherActionEventDetail,
} from 'accessible-astro-launcher'

Integration with accessible-astro-components

Section titled “Integration with accessible-astro-components”

The Launcher automatically syncs with preference toggles from accessible-astro-components:

EventAction ID
darkmode:changetoggle-dark-mode
highcontrast:changetoggle-high-contrast
reducemotion:changetoggle-reduced-motion

When you use these action IDs, the launcher automatically:

  • Updates the LED indicator state when preferences change
  • Listens for preference change events
  • Syncs state when the launcher opens