Utilities
The shared helpers every component builds on, and how to use them in your own code.
Last updated
npx @ava-ui/apogee add utils copies components/apogee/utils/. Every
component that needs one imports from there; so can you.
| Helper | Used by |
|---|---|
island |
Every component with enhancement JS |
uid |
Every component that renders an id (fields, menus, Tabs, Tooltip …) |
hotkey |
CommandPalette |
roving |
Tabs, ToggleButtonGroup |
fade |
ScrollArea, Table, Tabs |
scroll |
None yet; for your own overlays |
theme |
Theme, the docs toggle |
panel / menu |
Combobox, ContextMenu, Dropdown, Menubar, Popover, Select, Tooltip |
responsive |
Box, Carousel, Dialog, Grid, Stack, Steps, Text |
field / radius |
Text controls (Input, Select, Textarea …); Menubar, SegmentedControl, Tabs |
clipboard |
Code |
files |
FileUpload |
table-enhance |
DataGrid, Table |
island
island(selector, init) runs init once per matching element, now and after
every Astro View Transition. Document-level listeners stay outside init.
import { island } from '@/components/apogee/utils/island.ts'
island('.my-widget', (root) => { /* wire it */ })
uid
uid(Astro.locals, prefix?) returns a deterministic id per request, so builds
reproduce and two components on one page never collide.
hotkey
hotkey(combo, fn, { scope?, ignoreInputs? }) binds a shortcut and returns an
unbind. mod is Meta on macOS and Control elsewhere; typing targets (inputs,
textareas, contenteditable) are ignored unless ignoreInputs: false.
CommandPalette uses it.
import { hotkey } from '@/components/apogee/utils/hotkey.ts'
const off = hotkey('mod+k', () => palette.showModal())
parseHotkey('shift+/') and matchesHotkey(event, 'mod+k') are exported for
your own key handling. On macOS, Option changes the key value, so
alt+<letter> combos do not match there; prefer mod and shift.
roving
stepIndex(key, current, last, rtl?) returns the index an arrow, Home or End
moves to (wrapping), or null. Tabs uses it.
rovingTabindex(root, { selector, loop? }) wires a whole row: one tab stop,
arrows move it. ToggleButtonGroup uses it.
import { rovingTabindex } from '@/components/apogee/utils/roving.ts'
rovingTabindex(toolbar, { selector: 'button' })
fade
edgeFade(el, axis = 'x') sets data-fade="left right" (or top bottom) on a
scrolling box as it scrolls and resizes. Pair it with the mask utilities:
data-[fade~=left]:mask-l-from-85% data-[fade~=right]:mask-r-from-85%.
Tabs, Table and ScrollArea use it. fadeState / fadeStateY are the pure
functions behind it.
scroll
Modal dialogs lock the page through one rule in the tokens file
(body:has(dialog:modal)). For a custom overlay call lockScroll() and
unlockScroll(); locks are counted, so nested overlays release in order.
theme
getTheme(), setTheme(mode, { persist? }), toggleTheme() and
resolveInverted(root). See Theme.
panel and menu
panelStyle({ anchor, placement, gap, width, maxHeight }) returns the inline
anchor-positioning declarations every floating panel uses; panelClass,
panelSurface, menuClass and the item tables style them.
menuKeydown(event, menu) handles arrows, Home and End inside a
role="menu"; restoreFocus(panel, trigger) returns focus when a nested
popover closes. See Popover and Dropdown.
responsive
responsive(value, table) turns a Responsive<T> prop ('row' or
{ base: 'column', md: 'row' }) into the class list from a generated table.
Box, Carousel, Dialog, Grid, Stack, Steps and Text use it.
The tables live in utils/responsive-tables.ts, generated by
npm run build:responsive; edit the script, not the file.
field and radius
fieldSizes, fieldVariants, fieldBase are the shared text-control tables;
innerRadius['md-1'] gives a chip inside a rounded tray a concentric corner.
clipboard
copyText(text) copies and resolves true on success. It uses the Clipboard
API where the page is a secure context and falls back to a selected hidden
textarea elsewhere (plain-HTTP pages, some in-app browsers). Code uses it.
files
acceptsFile(accept, file) applies an accept list (.pdf, image/*,
image/png) to a { name, type }, the rule the native picker uses, so
dropped files get the same check. readableSize(bytes) gives 1.5 KB.
FileUpload uses both.