@peripheries/ui
v0.1.2
Published
Headless behaviour shared by the host's chrome and every component bundle: dismissal, focus, live regions, icons, safe markdown and the composer. No CSS; styling hooks are data-part, data-state and ARIA.
Readme
@peripheries/ui
Behaviour, without a look, shared by the host's chrome and every component bundle: behaviour is standard, appearance is themed.
The host serves one copy per page through the import map
(/_shared/peripheries-ui.js, like react), so a bundle leaves
@peripheries/ui external and declares it in peerDependencies
(^0.1.0). There is no CSS here: style the data-part, data-state,
data-md and ARIA hooks with tokens (--panel-*, --icon-*,
--focus-ring-*, --tap-min in @stack/core).
| Export | What it does |
|---|---|
| Panel, PanelHeader | One panel anatomy: back ("Back to "), title, tools, close ("Close "). modal declares a dialog that traps focus and locks the page's scroll; every panel closes on Escape and returns focus. |
| useDismiss | Escape closes the topmost open surface only; a press outside closes it when outside is set; closeProps for the close button. |
| useFocusTrap, useFocusReturn, useScrollLock, useSurfaceFocus | Focus for surfaces that keep their own markup. |
| announce, useLiveRegion | One polite (and one assertive) live region per page. |
| Icon, IconSet, registerIcons | <Icon name size label /> looks up the nearest IconSet, then the page's registry (the host registers lucide; an org its own set), then a few built-in glyphs (x, arrow-left, chevron-*, menu). Sized by --icon-size-sm|md|lg, stroked by --icon-stroke, decorative unless labelled. |
| Markdown, renderMarkdown, parseMarkdown | micromark + GFM → mdast → React nodes. Raw HTML renders as text; links pass safeLink (in-app paths through onNavigate, http(s) and mailto with rel="noopener noreferrer" in a new tab, anything else as its label); images are not loaded (alt text instead). streaming holds back half-arrived markers. |
| useComposer | Enter sends, Shift+Enter adds a line, IME-safe, never blank; while busy the send button stops the turn. fieldProps for the textarea, sendProps for the button. |
| Dialog, Sheet, Menu, Popover | Base UI 1.8 (pinned), re-exported so the page has one copy. |
Model output is untrusted: markdown.test.tsx checks that <script> and
<img onerror> in the source render as text, and that no file here writes HTML
from a string.
Versioning: its own line, starting at 0.1.0; bump it with the release that
changes it. Built and published with the other packages
(npm run build:packages, npm run publish:packages). The published
renderer doesn't depend on it: the host lends it to the runtime
(installRuntime(globalThis, { '@peripheries/ui': … })).
