@sursaut/ui
v0.1.0
Published
Headless UI primitives for Sursaut applications
Maintainers
Readme
@sursaut/ui
Headless UI primitives for Sursaut applications.
@sursaut/ui owns behavior, accessibility, reactive bindings, and low-level interaction logic. It does not own styling. Adapters consume these models and render the actual markup and classes.
What this package exports
@sursaut/ui currently exposes four kinds of API:
Headless models
buttonModel,checkboxModel,radioModel,switchModelcheckButtonModel,radioButtonModelaccordionModelselectModel,comboboxModelmultiselectModelmenuModel,menuItemModel,menuBarModelprogressModelstarsModelbadgeModel,pillModel,chipModelheadingModel,textModel,linkModelthemeToggleModeldialogModel,drawerModel,toastModelstackModel,inlineModel,gridModel,containerModel,appShellModelwithOverlaysModel
Components
DockviewInfiniteScroll
Directives
badge,intersect,loading,pointer,resize,scroll,sizeable,tail
Utilities
uiComponentoptionsIcon- overlay helpers from
./overlays
Architecture
@sursaut/ui is the headless layer. Adapters sit on top and decide the visual language.
import { buttonModel } from '@sursaut/ui'
export const Button = (props) => {
const model = buttonModel(props)
return (
<button class={`btn btn-${props.variant ?? 'secondary'}`} {...model.button} {...props.el}>
{model.icon && <span {...model.icon.span}>{model.icon.element}</span>}
{props.children}
</button>
)
}Model pattern
Models return plain objects of lazy getters.
- No styling
- No adapter-specific markup opinions
- Reactive reads happen inside getters, not at model construction time
- Element props are grouped by target (
model.button,model.input,model.details, ...)
This makes models safe to construct in component bodies while preserving reactivity when adapters spread the returned groups.
Quick start
import { buttonModel, checkboxModel } from '@sursaut/ui'
function Demo(props) {
const button = buttonModel(props.button)
const checkbox = checkboxModel(props.checkbox)
return (
<div>
<button {...button.button}>
{button.icon && <span {...button.icon.span}>{button.icon.element}</span>}
{props.button.children}
</button>
<label style="display: inline-flex; align-items: center; gap: 8px;">
<input {...checkbox.input} />
{props.checkbox.children}
</label>
</div>
)
}Public surface by category
Models
See docs/models.md.
Components
See docs/components.md.
Directives
See docs/directives.md.
Palette
The palette APIs are published because they are useful in Sursaut applications today, but they should be considered experimental. The command/editor surface is still evolving and the tool is not finished enough to treat as a stable design target.
Topic guides
Utilities
uiComponent
Curried helper for adapter authors. It narrows variant, adds dot-syntax variant accessors, and validates variants in development.
import { uiComponent } from '@sursaut/ui'
const picoComponent = uiComponent(['primary', 'secondary', 'danger'] as const)options
Global mutable configuration for the package.
Today the main hook is options.iconFactory, used by <Icon> and by models that resolve string icon names.
Icon
Thin wrapper around options.iconFactory. Falls back to a plain <span data-icon> if no icon factory is installed.
Development
Run the demo
pnpm demoThe UI demo server runs on port 5270.
Run tests
pnpm test
pnpm test:e2eBuild
pnpm buildDocumentation map
- Button
- Checkbox / Radio / Switch
- Select / Combobox
- Overlays
- Directives
- Models catalogue
- Components catalogue
- Model recipe
License
MIT
