@net-advantage/jo-ui-button
v2.1.0
Published
Publishable single-button primitive package.
Downloads
1,515
Readme
@net-advantage/jo-ui-button
Publishable single-button primitive package.
The button follows a strict MVVM split. The model normalizes variant, size, loading label, and min width. The view-model owns the click guard and the stable loading width. The view renders the view-model.
MVVM
flowchart LR
Props["ButtonProps"] --> ViewModel["useButtonViewModel"]
Model["button.model.ts<br/>normalize variant, size, icon-only name, loading label, and min width"] --> ViewModel
ViewModel --> View["Button.tsx"]
View -->|"onClick"| ViewModel
ViewModel -->|"onClick"| Host["Host application"]| Layer | File | Responsibility |
| --- | --- | --- |
| Contract | button.types.ts | Props, variant, size, loading label, min width, and view-model |
| Model | button.model.ts | Default missing variant to secondary and size to md. Require an accessible name for iconOnly. Normalize loadingLabel and minWidth, and derive the stable label state |
| View-model | button.view-model.ts | Framework-free button observable, including loading width and the click callback |
| View | button.use-view-model.ts | useButtonViewModel reads that observable |
| View | Button.tsx | Render the button from the view-model, including the stacked labels |
Models
flowchart TD
Props["ButtonProps"] --> Variant["ButtonVariant"]
Props --> Size["ButtonSize"]
Props --> IconOnly["iconOnly"]
Props --> LoadingLabel["loadingLabel"]
Props --> MinWidth["minWidth"]
Variant --> ViewModel["ButtonViewModel"]
Size --> ViewModel
IconOnly --> ViewModel
LoadingLabel --> ViewModel
MinWidth --> ViewModel
Props --> ViewModelButtonVariantisprimary,secondary,ghost,primary-modal,text-muted,destructive, oroverlay. A missing variant becomessecondary.- Corner radius comes from
--jo-radius-control(12px).text-mutedstays square. overlayis for controls on top of media. It uses--jo-color-scrimand--jo-color-on-scrim.ButtonSizeisxs,sm,md, orlg. A missing size becomesmd.xsis the compact year-bar size. Height, type size, padding, radius, and the icon-only square come from--jo-ui-button-xs-height,--jo-ui-button-xs-font-size,--jo-ui-button-xs-padding-inline,--jo-ui-button-xs-radius, and--jo-ui-button-xs-icon-size. Text is 11px and 25px tall. An icon-only button is 26×26, with the glyph at--jo-ui-button-xs-icon-font-size(14px) and--jo-ui-button-xs-icon-font-weight(400).- The button font is
--jo-ui-button-font-family, which follows--jo-ui-control-font-familyand otherwise--jo-font-family. It does not keep the browser's Arial button face. - A
.jo-ui-year-barancestor remaps a secondary button onto the year-bar theme tokens: surface, text, border, and hover. The button does not carry those colours as raw hex. iconOnlydraws the square form. It requires a non-emptybuttonProps['aria-label'], because the visible children are the icon.disabledblocks interaction.loadingkeeps the button's width. The resting label stays in the layout withvisibility: hidden, and a spinner is overlaid in that box. The button setsaria-busyandaria-live="polite".loadingLabelis optional. The resting label and the loading label share one grid cell, so the button is as wide as the wider label in both states. While loading, the loading label is the visible text. At rest, the loading label is hidden but still reserves its width.minWidthis an optional CSS length, such as12remorvar(--jo-size-button), for buttons whose label changes outside the loading state.
Variants
primarysecondaryghostprimary-modal(matches New Journey Type "Next" styling)text-muted(matches New Journey Type "Cancel" styling)destructive(reddish emphasis action)overlay(translucent scrim with light text, for controls on media)
Structure
- src: runtime component, model, view-model, and types.
- stories: Storybook stories for documentation and visual testing.
Publish footprint
This package publishes only:
- dist
- README.md
Stories and tests are excluded from the published artifact by design.
