@capillaryjs/capillary-ui 1.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +459 -0
- package/LICENSE +55 -0
- package/NOTICE +2 -0
- package/README.md +853 -0
- package/colors/README.md +63 -0
- package/colors/gray/colors.css +16 -0
- package/colors/green/colors.css +16 -0
- package/colors/iceblue/colors.css +19 -0
- package/colors/ocean/colors.css +16 -0
- package/colors/orange/colors.css +16 -0
- package/colors/purple/colors.css +16 -0
- package/colors/red/colors.css +16 -0
- package/colors/yellow/colors.css +16 -0
- package/dist/Components/Placeholder.d.ts +15 -0
- package/dist/Components/Placeholder.d.ts.map +1 -0
- package/dist/Components/app/app.d.ts +41 -0
- package/dist/Components/app/app.d.ts.map +1 -0
- package/dist/Components/component.d.ts +256 -0
- package/dist/Components/component.d.ts.map +1 -0
- package/dist/Components/controlUtils.d.ts +23 -0
- package/dist/Components/controlUtils.d.ts.map +1 -0
- package/dist/Components/data/descriptionList.d.ts +23 -0
- package/dist/Components/data/descriptionList.d.ts.map +1 -0
- package/dist/Components/data/filterState.d.ts +38 -0
- package/dist/Components/data/filterState.d.ts.map +1 -0
- package/dist/Components/data/infoPanel.d.ts +29 -0
- package/dist/Components/data/infoPanel.d.ts.map +1 -0
- package/dist/Components/data/listview/listview.d.ts +51 -0
- package/dist/Components/data/listview/listview.d.ts.map +1 -0
- package/dist/Components/data/selectionhandler.d.ts +87 -0
- package/dist/Components/data/selectionhandler.d.ts.map +1 -0
- package/dist/Components/data/table/DataTable.d.ts +79 -0
- package/dist/Components/data/table/DataTable.d.ts.map +1 -0
- package/dist/Components/data/table/FilterPanel.d.ts +41 -0
- package/dist/Components/data/table/FilterPanel.d.ts.map +1 -0
- package/dist/Components/data/table/TableHeader.d.ts +23 -0
- package/dist/Components/data/table/TableHeader.d.ts.map +1 -0
- package/dist/Components/data/table/TableHeaderCell.d.ts +41 -0
- package/dist/Components/data/table/TableHeaderCell.d.ts.map +1 -0
- package/dist/Components/data/table/tableDataSource.d.ts +49 -0
- package/dist/Components/data/table/tableDataSource.d.ts.map +1 -0
- package/dist/Components/data/table/tableQuery.d.ts +29 -0
- package/dist/Components/data/table/tableQuery.d.ts.map +1 -0
- package/dist/Components/data/treeview/treeModel.d.ts +23 -0
- package/dist/Components/data/treeview/treeModel.d.ts.map +1 -0
- package/dist/Components/data/treeview/treeitem.d.ts +21 -0
- package/dist/Components/data/treeview/treeitem.d.ts.map +1 -0
- package/dist/Components/data/treeview/treeview.d.ts +49 -0
- package/dist/Components/data/treeview/treeview.d.ts.map +1 -0
- package/dist/Components/dialog/dialog.d.ts +46 -0
- package/dist/Components/dialog/dialog.d.ts.map +1 -0
- package/dist/Components/layout/declarativeRegion.d.ts +30 -0
- package/dist/Components/layout/declarativeRegion.d.ts.map +1 -0
- package/dist/Components/layout/groupBox.d.ts +21 -0
- package/dist/Components/layout/groupBox.d.ts.map +1 -0
- package/dist/Components/layout/header.d.ts +20 -0
- package/dist/Components/layout/header.d.ts.map +1 -0
- package/dist/Components/layout/layout.d.ts +28 -0
- package/dist/Components/layout/layout.d.ts.map +1 -0
- package/dist/Components/layout/layoutTraits.d.ts +39 -0
- package/dist/Components/layout/layoutTraits.d.ts.map +1 -0
- package/dist/Components/layout/optionGroup.d.ts +35 -0
- package/dist/Components/layout/optionGroup.d.ts.map +1 -0
- package/dist/Components/layout/optionsBox.d.ts +12 -0
- package/dist/Components/layout/optionsBox.d.ts.map +1 -0
- package/dist/Components/layout/panel.d.ts +34 -0
- package/dist/Components/layout/panel.d.ts.map +1 -0
- package/dist/Components/layout/routedSelection.d.ts +10 -0
- package/dist/Components/layout/routedSelection.d.ts.map +1 -0
- package/dist/Components/layout/sidebar.d.ts +27 -0
- package/dist/Components/layout/sidebar.d.ts.map +1 -0
- package/dist/Components/layout/splitView.d.ts +82 -0
- package/dist/Components/layout/splitView.d.ts.map +1 -0
- package/dist/Components/layout/tabpanel/tab.d.ts +15 -0
- package/dist/Components/layout/tabpanel/tab.d.ts.map +1 -0
- package/dist/Components/layout/tabpanel/tabline.d.ts +31 -0
- package/dist/Components/layout/tabpanel/tabline.d.ts.map +1 -0
- package/dist/Components/layout/tabpanel/tabpanel.d.ts +44 -0
- package/dist/Components/layout/tabpanel/tabpanel.d.ts.map +1 -0
- package/dist/Components/lineinputs/CheckableControl.d.ts +7 -0
- package/dist/Components/lineinputs/CheckableControl.d.ts.map +1 -0
- package/dist/Components/lineinputs/LabeledInputControl.d.ts +7 -0
- package/dist/Components/lineinputs/LabeledInputControl.d.ts.map +1 -0
- package/dist/Components/lineinputs/SelectControl.d.ts +7 -0
- package/dist/Components/lineinputs/SelectControl.d.ts.map +1 -0
- package/dist/Components/lineinputs/checkbox/Checkbox.d.ts +46 -0
- package/dist/Components/lineinputs/checkbox/Checkbox.d.ts.map +1 -0
- package/dist/Components/lineinputs/checkbox/QuadCheckbox.d.ts +11 -0
- package/dist/Components/lineinputs/checkbox/QuadCheckbox.d.ts.map +1 -0
- package/dist/Components/lineinputs/checkbox/TriCheckbox.d.ts +11 -0
- package/dist/Components/lineinputs/checkbox/TriCheckbox.d.ts.map +1 -0
- package/dist/Components/lineinputs/datetime/Calendar.d.ts +23 -0
- package/dist/Components/lineinputs/datetime/Calendar.d.ts.map +1 -0
- package/dist/Components/lineinputs/datetime/DatePicker.d.ts +62 -0
- package/dist/Components/lineinputs/datetime/DatePicker.d.ts.map +1 -0
- package/dist/Components/lineinputs/datetime/DateTimePicker.d.ts +52 -0
- package/dist/Components/lineinputs/datetime/DateTimePicker.d.ts.map +1 -0
- package/dist/Components/lineinputs/datetime/TimePicker.d.ts +46 -0
- package/dist/Components/lineinputs/datetime/TimePicker.d.ts.map +1 -0
- package/dist/Components/lineinputs/datetime/civilDate.d.ts +37 -0
- package/dist/Components/lineinputs/datetime/civilDate.d.ts.map +1 -0
- package/dist/Components/lineinputs/datetime/timeString.d.ts +26 -0
- package/dist/Components/lineinputs/datetime/timeString.d.ts.map +1 -0
- package/dist/Components/lineinputs/dropdown.d.ts +49 -0
- package/dist/Components/lineinputs/dropdown.d.ts.map +1 -0
- package/dist/Components/lineinputs/label.d.ts +14 -0
- package/dist/Components/lineinputs/label.d.ts.map +1 -0
- package/dist/Components/lineinputs/radio.d.ts +69 -0
- package/dist/Components/lineinputs/radio.d.ts.map +1 -0
- package/dist/Components/lineinputs/textbox.d.ts +40 -0
- package/dist/Components/lineinputs/textbox.d.ts.map +1 -0
- package/dist/Components/lineinputs/toggle.d.ts +40 -0
- package/dist/Components/lineinputs/toggle.d.ts.map +1 -0
- package/dist/Components/menu/button.d.ts +31 -0
- package/dist/Components/menu/button.d.ts.map +1 -0
- package/dist/Components/menu/toolbar.d.ts +15 -0
- package/dist/Components/menu/toolbar.d.ts.map +1 -0
- package/dist/Components/navigation/breadcrumb.d.ts +31 -0
- package/dist/Components/navigation/breadcrumb.d.ts.map +1 -0
- package/dist/Components/navigation/navigationBar.d.ts +53 -0
- package/dist/Components/navigation/navigationBar.d.ts.map +1 -0
- package/dist/Components/status/progressBar.d.ts +23 -0
- package/dist/Components/status/progressBar.d.ts.map +1 -0
- package/dist/Components/status/statusPresentation.d.ts +21 -0
- package/dist/Components/status/statusPresentation.d.ts.map +1 -0
- package/dist/Components/theme/stylesheetPicker.d.ts +42 -0
- package/dist/Components/theme/stylesheetPicker.d.ts.map +1 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7736 -0
- package/dist/index.js.map +1 -0
- package/dist/jsx-dev-runtime-BAF7C1E1.js +1368 -0
- package/dist/jsx-dev-runtime-BAF7C1E1.js.map +1 -0
- package/dist/jsx-dev-runtime.d.ts +2 -0
- package/dist/jsx-dev-runtime.d.ts.map +1 -0
- package/dist/jsx-dev-runtime.js +6 -0
- package/dist/jsx-dev-runtime.js.map +1 -0
- package/dist/jsx-runtime.d.ts +26 -0
- package/dist/jsx-runtime.d.ts.map +1 -0
- package/dist/jsx-runtime.js +9 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/localization.d.ts +76 -0
- package/dist/localization.d.ts.map +1 -0
- package/dist/routing/RouteLink.d.ts +18 -0
- package/dist/routing/RouteLink.d.ts.map +1 -0
- package/dist/routing/RouteOutlet.d.ts +38 -0
- package/dist/routing/RouteOutlet.d.ts.map +1 -0
- package/dist/routing/RouteQuery.d.ts +19 -0
- package/dist/routing/RouteQuery.d.ts.map +1 -0
- package/dist/routing/RouteScope.d.ts +18 -0
- package/dist/routing/RouteScope.d.ts.map +1 -0
- package/dist/routing/RouteValue.d.ts +19 -0
- package/dist/routing/RouteValue.d.ts.map +1 -0
- package/dist/routing/navigationAdapter.d.ts +33 -0
- package/dist/routing/navigationAdapter.d.ts.map +1 -0
- package/dist/routing/route.d.ts +64 -0
- package/dist/routing/route.d.ts.map +1 -0
- package/dist/routing/router.d.ts +109 -0
- package/dist/routing/router.d.ts.map +1 -0
- package/dist/runtime.d.ts +34 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/services.d.ts +45 -0
- package/dist/services.d.ts.map +1 -0
- package/dist/styling/styleRegistry.d.ts +15 -0
- package/dist/styling/styleRegistry.d.ts.map +1 -0
- package/dist/styling/theme.d.ts +39 -0
- package/dist/styling/theme.d.ts.map +1 -0
- package/dist/util/filterMode.d.ts +15 -0
- package/dist/util/filterMode.d.ts.map +1 -0
- package/docs/application-composition-guide.md +843 -0
- package/docs/application-layout-guide.md +553 -0
- package/package.json +90 -0
- package/styles/structural.css +2388 -0
- package/themes/README.md +99 -0
- package/themes/base.css +344 -0
- package/themes/java/theme.css +21 -0
- package/themes/minimal/theme.css +7 -0
- package/themes/shiny/theme.css +168 -0
package/README.md
ADDED
|
@@ -0,0 +1,853 @@
|
|
|
1
|
+
# Capillary UI
|
|
2
|
+
|
|
3
|
+
Capillary UI is a browser-only TypeScript component runtime built around Capillary
|
|
4
|
+
emitters. It provides TSX rendering, explicit component lifecycle, accessible
|
|
5
|
+
controls and data views, scoped services and routing, and dependency-collected
|
|
6
|
+
structural CSS. Its built-in messages and calendar display names can be
|
|
7
|
+
localized once per runtime.
|
|
8
|
+
|
|
9
|
+
Capillary UI 1.x is ESM-only and targets current evergreen browsers. Install it with
|
|
10
|
+
its Capillary peer:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm add @capillaryjs/capillary @capillaryjs/capillary-ui
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Design and ownership
|
|
17
|
+
|
|
18
|
+
Capillary UI presents application values without moving them into a second UI-specific
|
|
19
|
+
state system. Controls write ordinary Capillary emitters, components read the
|
|
20
|
+
downstream values they need, and applications retain ownership of domain
|
|
21
|
+
policy and asynchronous work.
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
application
|
|
25
|
+
domain policy, composition, services, endpoints, routes, theme selection
|
|
26
|
+
│
|
|
27
|
+
▼
|
|
28
|
+
Capillary UI
|
|
29
|
+
TSX, DOM, events, lifecycle, accessibility, structural presentation
|
|
30
|
+
│ get / subscribe / set
|
|
31
|
+
▼
|
|
32
|
+
Capillary
|
|
33
|
+
mutable values, derived values, live queries, commands, diagnostics
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The boundaries are deliberate:
|
|
37
|
+
|
|
38
|
+
| Concern | Owner |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| Domain state, validation policy, endpoint configuration, service providers, routes, page composition | Application |
|
|
41
|
+
| Translation catalogs, locale policy, application text, document `lang`/`dir` | Application |
|
|
42
|
+
| DOM structure, native events, accessible semantics, component lifetime, visual async states | Capillary UI |
|
|
43
|
+
| Capillary UI-authored message defaults and Capillary UI-owned `Intl` display names | Capillary UI, using optional runtime localization |
|
|
44
|
+
| Mutable and computed values, query execution and status, command lifecycle, optional causality | Capillary |
|
|
45
|
+
| Retrieval, wire serialization, persistence | Application-supplied handlers and adapters |
|
|
46
|
+
| Structural selectors and component layout | Capillary UI component CSS |
|
|
47
|
+
| Theme treatment, palette, application layout | Separately loaded CSS and application CSS |
|
|
48
|
+
|
|
49
|
+
Capillary UI prefers native HTML when it expresses the contract. Custom `cap-*`
|
|
50
|
+
hosts are readable light-DOM ownership and styling boundaries; they are not
|
|
51
|
+
registered custom elements and do not use Shadow DOM.
|
|
52
|
+
|
|
53
|
+
## Set up TSX
|
|
54
|
+
|
|
55
|
+
Use Capillary UI's automatic JSX runtime:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"compilerOptions": {
|
|
60
|
+
"jsx": "react-jsx",
|
|
61
|
+
"jsxImportSource": "@capillaryjs/capillary-ui"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Load the variable base, one color palette, and one theme. `CapillaryUiApp` is the
|
|
67
|
+
normal application shell: it renders a fixed `cap-app` host, applies the
|
|
68
|
+
theme canvas and typography, and has an accessible primary-content landmark by
|
|
69
|
+
default. `mountCapillaryUiApp()` collects reachable structural CSS before mounting:
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import {Emitter} from '@capillaryjs/capillary'
|
|
73
|
+
import {
|
|
74
|
+
Button,
|
|
75
|
+
CapillaryUiApp,
|
|
76
|
+
Panel,
|
|
77
|
+
PanelToolbar,
|
|
78
|
+
Textbox,
|
|
79
|
+
Toolbar,
|
|
80
|
+
createCapillaryUiRuntime,
|
|
81
|
+
mountCapillaryUiApp,
|
|
82
|
+
} from '@capillaryjs/capillary-ui'
|
|
83
|
+
|
|
84
|
+
import '@capillaryjs/capillary-ui/themes/base.css'
|
|
85
|
+
import '@capillaryjs/capillary-ui/colors/iceblue/colors.css'
|
|
86
|
+
import '@capillaryjs/capillary-ui/themes/minimal/theme.css'
|
|
87
|
+
|
|
88
|
+
class ProfileApp extends CapillaryUiApp {
|
|
89
|
+
readonly name = new Emitter('Ada')
|
|
90
|
+
|
|
91
|
+
protected override renderContent() {
|
|
92
|
+
return <Panel header="Profile">
|
|
93
|
+
<PanelToolbar><Toolbar label="Profile actions">
|
|
94
|
+
<Button label="Save" onClick={() => this.save()} />
|
|
95
|
+
</Toolbar></PanelToolbar>
|
|
96
|
+
<Textbox label="Name" valueEmitter={this.name} />
|
|
97
|
+
</Panel>
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
onDestroy() {
|
|
101
|
+
this.name.dispose()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private save() {
|
|
105
|
+
console.log(this.name.get())
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
static dependencies = [Button, Panel, PanelToolbar, Textbox, Toolbar]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const runtime = createCapillaryUiRuntime()
|
|
112
|
+
mountCapillaryUiApp(runtime, ProfileApp, document.querySelector('#app')!, {
|
|
113
|
+
sizing: 'viewport',
|
|
114
|
+
layout: 'vertical',
|
|
115
|
+
})
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`CapillaryUiApp` may also be instantiated directly with `children`. Derived apps
|
|
119
|
+
override `renderContent()`. Its `sizing` is `embedded`, `viewport-width`,
|
|
120
|
+
`viewport-height`, or `viewport`; `layout` is `horizontal` or `vertical` and
|
|
121
|
+
arranges application-owned children directly on that bounded host; `landmark`
|
|
122
|
+
is `main` (the default) or `none` for an embedded app. `static dependencies` is transitive and idempotent. It
|
|
123
|
+
declares the Capillary UI and application components whose structural CSS the root can
|
|
124
|
+
render. `CapillaryUiApp` itself registers and injects those styles whenever it
|
|
125
|
+
attaches; `mountCapillaryUiApp()` is the concise normal entry point. Applications that
|
|
126
|
+
prefer a complete static asset may import
|
|
127
|
+
`@capillaryjs/capillary-ui/styles/structural.css` instead of collecting styles.
|
|
128
|
+
|
|
129
|
+
`CapillaryUiApp` deliberately does not select a palette, theme, appearance mode,
|
|
130
|
+
services, router, routes, or domain state. Those remain application policy.
|
|
131
|
+
|
|
132
|
+
The low-level `h()` vnode factory remains exported for non-JSX integrations,
|
|
133
|
+
but TSX is the documented authoring model for applications and Capillary UI
|
|
134
|
+
components.
|
|
135
|
+
|
|
136
|
+
## Components and lifecycle
|
|
137
|
+
|
|
138
|
+
A class component has explicit phases:
|
|
139
|
+
|
|
140
|
+
1. The constructor stores props and creates local objects, without subscribing
|
|
141
|
+
or rendering.
|
|
142
|
+
2. `initialize()` runs once after Capillary UI assigns the runtime. Create subscriptions
|
|
143
|
+
or resolve declared services here.
|
|
144
|
+
3. `render()` returns TSX, a primitive, an emitter child, a component, or an
|
|
145
|
+
array of children.
|
|
146
|
+
4. `afterMount()` runs after the first DOM commit; `afterUpdate()` runs after
|
|
147
|
+
later commits.
|
|
148
|
+
5. `onDestroy()` releases resources owned by the component.
|
|
149
|
+
|
|
150
|
+
`watch()` schedules a component update when an observable changes.
|
|
151
|
+
`read(emitter)` returns its value and tracks it only for the current render.
|
|
152
|
+
`snapshot(emitter)` tracks and returns `{value, fetchState, error}`.
|
|
153
|
+
`onCleanup()` registers listeners or other cleanup functions that Capillary UI invokes
|
|
154
|
+
on destruction.
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
class Counter extends Component {
|
|
158
|
+
readonly count = new Emitter(0)
|
|
159
|
+
readonly label = this.count.map((value) => `Count: ${value}`)
|
|
160
|
+
|
|
161
|
+
render() {
|
|
162
|
+
return <Button
|
|
163
|
+
label={this.label}
|
|
164
|
+
onClick={() => this.count.set(this.count.get() + 1)}
|
|
165
|
+
/>
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
onDestroy() {
|
|
169
|
+
this.label.dispose()
|
|
170
|
+
this.count.dispose()
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
static dependencies = [Button]
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Capillary UI's synchronous keyed reconciler preserves compatible DOM and component
|
|
178
|
+
identity, focus, cursor and native input state, and event-listener cardinality.
|
|
179
|
+
Use stable `key` values for reordered siblings. Never reuse one component
|
|
180
|
+
instance under two owners.
|
|
181
|
+
|
|
182
|
+
### Custom component hosts
|
|
183
|
+
|
|
184
|
+
Wrapped components declare a host stem and render `this.Host`. The runtime maps
|
|
185
|
+
the stem to one fixed, standards-valid name by removing internal hyphens and
|
|
186
|
+
prefixing `cap-`:
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
interface BadgeProps extends ComponentProps {
|
|
190
|
+
tone?: 'neutral' | 'positive'
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
class Badge extends Component<BadgeProps> {
|
|
194
|
+
render() {
|
|
195
|
+
const Host = this.Host
|
|
196
|
+
return <Host data-tone={this.props.tone ?? 'neutral'}>
|
|
197
|
+
{this.props.children}
|
|
198
|
+
</Host>
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
static override hostName = 'badge'
|
|
202
|
+
static override css = css`
|
|
203
|
+
& { display: inline-flex; }
|
|
204
|
+
&[data-tone="positive"] { color: var(--palette-green); }
|
|
205
|
+
`
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
The `&` selector resolves against the concrete host during style collection.
|
|
210
|
+
Native-root components render their native element directly. Capillary UI-created DOM
|
|
211
|
+
has `data-cap` for diagnostics, but component styling uses the owning host,
|
|
212
|
+
native/ARIA state, fixed part elements, and meaningful traits rather than data
|
|
213
|
+
attributes as routine CSS hooks.
|
|
214
|
+
|
|
215
|
+
## Reactive templates
|
|
216
|
+
|
|
217
|
+
Capillary UI exposes four distinct reactive forms. Choose the form that matches the
|
|
218
|
+
ownership boundary.
|
|
219
|
+
|
|
220
|
+
### Tracked reads
|
|
221
|
+
|
|
222
|
+
Use `read()` when control flow or an ordinary value depends on an emitter. Use
|
|
223
|
+
`snapshot()` when loading and error state matter:
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
interface Item {
|
|
227
|
+
id: string
|
|
228
|
+
label: string
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
interface ResultsProps extends ComponentProps {
|
|
232
|
+
results: ReadableEmitter<readonly Item[] | undefined>
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
class Results extends Component<ResultsProps> {
|
|
236
|
+
render() {
|
|
237
|
+
const {value, fetchState, error} = this.snapshot(this.props.results)
|
|
238
|
+
if (fetchState === FetchState.Error) {
|
|
239
|
+
return <p role="alert">{String(error)}</p>
|
|
240
|
+
}
|
|
241
|
+
return <ul aria-busy={fetchState === FetchState.Loading}>
|
|
242
|
+
{(value ?? []).map((item) => <li key={item.id}>{item.label}</li>)}
|
|
243
|
+
</ul>
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
The surrounding component rerenders when a tracked source changes, and Capillary UI
|
|
249
|
+
reconciles the tracked source set after every render.
|
|
250
|
+
|
|
251
|
+
### Fine-grained emitter children
|
|
252
|
+
|
|
253
|
+
A readable emitter in child position updates only its owned DOM range:
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
<output>Current name: {name}</output>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
An emitter passed as a normal component prop remains the same object. Capillary UI does
|
|
260
|
+
not inspect arbitrary prop values or discover dependencies implicitly.
|
|
261
|
+
|
|
262
|
+
### One-way live properties
|
|
263
|
+
|
|
264
|
+
`live()` updates a DOM property or a component-declared live prop without
|
|
265
|
+
rerendering its parent:
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
<Button label="Submit" disabled={live(submitting)} />
|
|
269
|
+
<output title={live(summary)}>{summary}</output>
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Built-in components allowlist their live props. TypeScript and runtime checks
|
|
273
|
+
reject a binding on an undeclared prop. Value/data emitters such as
|
|
274
|
+
`valueEmitter`, `items`, and `nodes` are raw contracts and do not use `live()`.
|
|
275
|
+
|
|
276
|
+
### Two-way native bindings
|
|
277
|
+
|
|
278
|
+
`bind:value` accepts a writable string emitter and `bind:checked` accepts a
|
|
279
|
+
writable boolean emitter:
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
282
|
+
<input aria-label="Search" bind:value={search} />
|
|
283
|
+
<input type="checkbox" bind:checked={showArchived} />
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Capillary UI keeps the property synchronized in both directions and owns the renderer
|
|
287
|
+
subscription. Higher-level value controls use the same explicit
|
|
288
|
+
`valueEmitter` convention.
|
|
289
|
+
|
|
290
|
+
## Value-control convention
|
|
291
|
+
|
|
292
|
+
Stateful controls expose a public writable `valueEmitter`. Callers can supply
|
|
293
|
+
one with `valueEmitter`, supply an initial uncontrolled value with
|
|
294
|
+
`defaultValue`, or let the control create its documented fallback. `value` is
|
|
295
|
+
retained as an initial-value compatibility alias; it is not a continuously
|
|
296
|
+
controlled prop. `onChange` reports user-driven changes.
|
|
297
|
+
|
|
298
|
+
Availability and validation can be ordinary values or supported `live()`
|
|
299
|
+
bindings. Labels should be visible whenever possible; `ariaLabel` is the
|
|
300
|
+
fallback for controls without visible label content.
|
|
301
|
+
|
|
302
|
+
## Component reference
|
|
303
|
+
|
|
304
|
+
Every public component is listed below. Generic `className`, `class`, `island`,
|
|
305
|
+
`key`, and `children` come from `ComponentProps` and are omitted from the key
|
|
306
|
+
props column.
|
|
307
|
+
|
|
308
|
+
The generic controls `Dropdown`, `RadioGroup`, and `Toggle` preserve their
|
|
309
|
+
option value type through `valueEmitter` and `onChange`; the `<T>` notation in
|
|
310
|
+
the tables below denotes that TypeScript type parameter.
|
|
311
|
+
|
|
312
|
+
### Actions, inputs, and choices
|
|
313
|
+
|
|
314
|
+
| Component | Purpose | Key props and state |
|
|
315
|
+
| --- | --- | --- |
|
|
316
|
+
| `Button` | Native button with optional pressed and busy state | `label`, `type`, `disabled`, `pressed`, `busy`, `busyLabel`, `error`, `onClick`; live: `disabled`, `pressed`, `busy`, `error` |
|
|
317
|
+
| `Toolbar` | Named action group | `label`, `orientation` |
|
|
318
|
+
| `Label` | Native label for rich or live text | `text`, `htmlFor`; live: `text` |
|
|
319
|
+
| `Textbox` | Labelled native text input with validation | `label`, `valueEmitter`, `defaultValue`, `type`, `name`, `placeholder`, `disabled`, `required`, `readOnly`, `busy`, `error`, native text constraints, `inputRef`, `onInput`, `onChange`; live: availability, `busy`, and `error` |
|
|
320
|
+
| `Dropdown<T>` | Labelled native select | `options`, `label`, `valueEmitter`, `defaultValue`, `placeholder`, `disabled`, `required`, `busy`, `error`, `onChange`; `options` may be static or a readable emitter whose fetch state supplies loading/error feedback |
|
|
321
|
+
| `RadioButton` | Standalone native radio and label | `label`, `name`, `value`, `checked`, `disabled`, `required`, `busy`, `error`, `onChange`; live: state, availability, `busy`, `error` |
|
|
322
|
+
| `RadioGroup<T>` | Named native-radio fieldset owning one value | `options` as `[value, label]` tuples, `label`, `valueEmitter`, `defaultValue`, `disabled`, `required`, `busy`, `error`, `onChange`; options are ordinary render data |
|
|
323
|
+
| `Toggle<T>` | ARIA radio group rendered as toggle buttons | `options` as `[value, label]` tuples, `label`, `valueEmitter`, `defaultValue`, `disabled`, `required`, `busy`, `error`, `onChange` |
|
|
324
|
+
| `Checkbox<T>` | Configurable keyboard-operable semantic state cycle | `symbols` as `[content, value]` tuples, `label`/`ariaLabel`, `valueEmitter`, `defaultValue`, `disabled`, `required`, `busy`, `error`, `onChange` |
|
|
325
|
+
| `TriCheckbox` | Neutral/prefer/deny `FilterMode` cycle | Same public props as `Checkbox`, except fixed symbols |
|
|
326
|
+
| `QuadCheckbox` | Neutral/prefer/require/deny `FilterMode` cycle | Same public props as `Checkbox`, except fixed symbols |
|
|
327
|
+
| `DatePicker` (experimental) | Text date input with calendar dialog | value props, `label`/`ariaLabel`, `disabled`, `required`, `readOnly`, `busy`, `error`, date bounds/placeholders, input/change callbacks |
|
|
328
|
+
| `TimePicker` (experimental) | Stepped native time select | value props, `label`/`ariaLabel`, `disabled`, `required`, `busy`, `error`, time bounds/step/placeholders, input/change callbacks |
|
|
329
|
+
| `DateTimePicker` (experimental) | Combined date/time fieldset | combined value props, `label`/`ariaLabel`, `disabled`, `required`, `busy`, `error`, date/time bounds and callbacks |
|
|
330
|
+
|
|
331
|
+
`FilterMode` exports `neutral`, `prefer`, `require`, and `deny` semantic values.
|
|
332
|
+
Arrow keys move backward or forward through a multi-state checkbox; Space uses
|
|
333
|
+
the native forward cycle.
|
|
334
|
+
|
|
335
|
+
`busy` is presentational state: it sets native/ARIA busy semantics and paints
|
|
336
|
+
the theme's moving working texture without disabling an input or choice.
|
|
337
|
+
`Button` remains the exception: a busy action is unavailable until it settles.
|
|
338
|
+
When `error` is also present, error presentation wins over the animation.
|
|
339
|
+
Every error-bearing control describes its native surface with a focusable
|
|
340
|
+
`role="alert"` overlay. Its icon and initially hidden message are absolutely
|
|
341
|
+
positioned so errors do not change layout; the message opens when the icon is
|
|
342
|
+
hovered or the alert receives keyboard/tap focus. Applications still own
|
|
343
|
+
validation and the message text.
|
|
344
|
+
|
|
345
|
+
```tsx
|
|
346
|
+
const view = new Emitter<'list' | 'grid'>('list')
|
|
347
|
+
|
|
348
|
+
<RadioGroup
|
|
349
|
+
label="View"
|
|
350
|
+
options={[
|
|
351
|
+
['list', 'List'],
|
|
352
|
+
['grid', 'Grid'],
|
|
353
|
+
]}
|
|
354
|
+
valueEmitter={view}
|
|
355
|
+
/>
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Layout and navigation
|
|
359
|
+
|
|
360
|
+
| Component | Purpose | Key props and state |
|
|
361
|
+
| --- | --- | --- |
|
|
362
|
+
| `CapillaryUiApp` | Fixed `cap-app` application shell and theme-text boundary | `sizing`: `embedded`/viewport axes; `layout`: `horizontal`/`vertical`; `landmark`: `main`/`none`; content or overridden `renderContent()` |
|
|
363
|
+
| `Header` | Styled native heading surface | `level` (1–6), `headingId`, content |
|
|
364
|
+
| `GroupBox` | Labelled bordered group with a vertical header | required `header`, content |
|
|
365
|
+
| `OptionGroup` | Labelled native fieldset for related controls | `label`/`ariaLabel`, `OptionGroupHeaderEnd` and ordinary content children, `disabled`, `required`, `busy`, `error`; state props are live |
|
|
366
|
+
| `OptionsBox` | GroupBox specialization arranging option groups | required `header`, `OptionGroup` content |
|
|
367
|
+
| `Layout` | Presentation-only arrangement of arbitrary children | exactly one of `horizontal`/`vertical`; `allocation`, `scroll`, optional accessible-region configuration |
|
|
368
|
+
| `Panel` | Optional labelled, themed region composed over a Layout body | `header`, `horizontal`/`vertical`, `allocation`, `scroll`, `disabled`; `PanelToolbar` and ordinary content children; live: `disabled` |
|
|
369
|
+
| `Sidebar` | Labelled `aside` with fixed header/toolbar and scrolling content | `header`, `ariaLabel`; `SidebarToolbar` and ordinary content children |
|
|
370
|
+
| `SplitView` | Resizable two-pane layout | required `SplitPrimary` and `SplitSecondary` Layout panes; `horizontal`/`vertical`, `allocation`, initial/minimum sizes, separator label, `onResize` |
|
|
371
|
+
| `NavigationBar` | Labelled native navigation list over router-aware or external anchors | required `label`, `items`; route items accept `exact`; external items use `{kind: 'external', href}` plus disabled/link options |
|
|
372
|
+
| `Tab` | Declarative tab definition consumed by `TabPanel` | `id`, `label`, `disabled`, optional literal `route`, content |
|
|
373
|
+
| `TabLine` | Standalone keyboard-operable tab list | `tabs`, `valueEmitter`/`activeTabEmitter`, initial value, `label`, `onChange` |
|
|
374
|
+
| `TabPanel` | Tab list plus owned tabpanel sections | declarative `Tab` children or `tabs` definitions; value props, `mountPolicy`, `label`, `onChange` |
|
|
375
|
+
|
|
376
|
+
`TabLine` supports Home, End, and orientation-appropriate arrow navigation and
|
|
377
|
+
skips disabled tabs. `TabPanel` can register routed tabs when it is mounted in
|
|
378
|
+
a router-backed route scope. Its `mountPolicy` controls content lifetime while
|
|
379
|
+
keeping every semantic tabpanel shell stable:
|
|
380
|
+
|
|
381
|
+
- `eager` (the compatibility default) mounts and retains every tab's content;
|
|
382
|
+
- `lazy` mounts the selected content and retains each visited tab; and
|
|
383
|
+
- `active-only` mounts only the selected content and destroys it on leave.
|
|
384
|
+
|
|
385
|
+
During initial restoration of a direct nested URL, a routed panel preselects
|
|
386
|
+
the matching pending literal route before its first content render. An
|
|
387
|
+
`active-only` panel therefore does not briefly mount its default branch while
|
|
388
|
+
the router progressively discovers the requested child scopes.
|
|
389
|
+
|
|
390
|
+
Use `active-only` with recreatable TSX/VNodes. A prebuilt component instance
|
|
391
|
+
cannot be mounted again after destruction. Put state that must survive a view
|
|
392
|
+
instance in application-owned Capillary emitters/services, or choose a retaining
|
|
393
|
+
policy. Capillary UI does not call data-loading methods implicitly; a mounted view may
|
|
394
|
+
activate its application service/query during `initialize()`.
|
|
395
|
+
|
|
396
|
+
```tsx
|
|
397
|
+
<TabPanel id="profile" label="Profile sections" mountPolicy="active-only">
|
|
398
|
+
<Tab id="summary" label="Summary">Summary content</Tab>
|
|
399
|
+
<Tab id="details" label="Details">Details content</Tab>
|
|
400
|
+
</TabPanel>
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
`SplitView` is a resizable two-pane composition primitive. Its required named
|
|
404
|
+
Layout panes keep their roles and independent arrangement visible:
|
|
405
|
+
|
|
406
|
+
```tsx
|
|
407
|
+
<SplitView horizontal allocation="flexible" primarySize="18rem"
|
|
408
|
+
separatorLabel="Resize project navigation">
|
|
409
|
+
<SplitPrimary vertical scroll label="Projects">
|
|
410
|
+
<ProjectNavigation />
|
|
411
|
+
</SplitPrimary>
|
|
412
|
+
<SplitSecondary vertical scroll label="Details">
|
|
413
|
+
<ProjectDetails />
|
|
414
|
+
</SplitSecondary>
|
|
415
|
+
</SplitView>
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Pointer dragging and orientation-appropriate arrow keys resize the primary
|
|
419
|
+
pane. Home and End move to the configured minimum and maximum; Shift multiplies
|
|
420
|
+
the keyboard step. SplitView reports pixel sizes through `onResize`, while the
|
|
421
|
+
application owns persistence and responsive policy. Set `resizable={false}`
|
|
422
|
+
only when a fixed divider is deliberate.
|
|
423
|
+
|
|
424
|
+
`NavigationBar` uses a native `nav`, list, and anchors. It preserves
|
|
425
|
+
`RouteLink` href generation, current-route state, modified clicks, targets,
|
|
426
|
+
and downloads. An item whose `to` is `{kind: 'external', href}` renders a
|
|
427
|
+
plain anchor for destinations outside the current router or origin: the
|
|
428
|
+
router never intercepts it, no `aria-current` applies, and no router is
|
|
429
|
+
required in the runtime. It has ordinary link tab order and no tab or
|
|
430
|
+
ARIA-menu keyboard
|
|
431
|
+
model. A disabled item is rendered as a visible non-link with
|
|
432
|
+
`aria-disabled="true"`. The bar navigates only; it never locates or owns the
|
|
433
|
+
content affected by a route. `href` is application-controlled and passed to
|
|
434
|
+
the native anchor: destination trust, allowed URL schemes, availability, and
|
|
435
|
+
cross-application policy remain application responsibilities.
|
|
436
|
+
|
|
437
|
+
Its `--navigation-bar-*` and `--navigation-link-*` theme variables are
|
|
438
|
+
independent from `--button-*`. The base theme deliberately presents navigation
|
|
439
|
+
as text links with a subtle hover surface and current-route underline. Themes
|
|
440
|
+
may opt into boxed or button-like navigation without changing the component's
|
|
441
|
+
native link semantics.
|
|
442
|
+
|
|
443
|
+
### Data and record views
|
|
444
|
+
|
|
445
|
+
| Component | Purpose | Key props and state |
|
|
446
|
+
| --- | --- | --- |
|
|
447
|
+
| `DescriptionList` | Native `dl` record summary | `label`, `DescriptionItem` children |
|
|
448
|
+
| `DescriptionItem` | Native `dt`/`dd` pair | required `term`, `value` or content |
|
|
449
|
+
| `InfoPanel` | Bordered info panel with optional title and key-value fields | `title`, `label`, `InfoField` children |
|
|
450
|
+
| `InfoField` | Native `dt`/`dd` key-value pair | required `label`, `value` or content |
|
|
451
|
+
| `Placeholder` | Decorative loading placeholder | numeric `width`, clamped to 10–100 percent |
|
|
452
|
+
| `ListView<T>` | Keyed single- or multi-select ARIA listbox | `items`, `itemKey`, `label`, `placeholderCount`, `renderItem`, `multiSelect`, selected emitter |
|
|
453
|
+
| `TreeItem<T>` | Declarative tree-node marker | `id`, `label`, `textValue`, `value`, nested `TreeItem` children |
|
|
454
|
+
| `TreeView<T>` | Keyed single-select ARIA tree | `nodes` or declarative items, `label`, `placeholderCount`, selected/expanded emitters, `renderItem`, per-label class/style callbacks, `onSelect` |
|
|
455
|
+
| `FilterPanel` | Semantic filter-control fieldset | `options`, `filters`, `filterModes`, `defaultSemanticState`, `label`, `onChange` |
|
|
456
|
+
| `TableHeaderCell` | Sort/filter header-cell control | column key/label plus sort/filter state callbacks |
|
|
457
|
+
| `TableHeader` | Header row over public column definitions | `columns`, sort/filter emitters and callbacks |
|
|
458
|
+
| `DataTable<T>` | Accessible local, caller-query, or REST-backed table | `columns`, one data input, `rowKey`, caption/messages, `placeholderCount`, semantic filter options, single/multi selection |
|
|
459
|
+
|
|
460
|
+
`ListView`, `TreeView`, and `DataTable` reconcile selection by stable keys when
|
|
461
|
+
fresh item objects arrive. Supply an explicit key for application data; index
|
|
462
|
+
fallbacks are only safe for immutable ordering. `ListView.items` and
|
|
463
|
+
`TreeView.nodes` accept static arrays or readable emitters and present loading,
|
|
464
|
+
empty, and error states from the emitter snapshot.
|
|
465
|
+
|
|
466
|
+
On an empty initial/loading snapshot, all three collection views render
|
|
467
|
+
deterministic, `aria-hidden` placeholder rows; `placeholderCount` selects their
|
|
468
|
+
count. When a loading snapshot retains rows, those real rows remain semantic
|
|
469
|
+
and usable while the working texture animates over their background. Error
|
|
470
|
+
snapshots retain any available rows, add an error edge and overlay detail icon,
|
|
471
|
+
and stop the loading animation. A `DataTable` data source with `retry` also
|
|
472
|
+
renders its localized retry action.
|
|
473
|
+
|
|
474
|
+
Advanced compositions may use `BaseSelectionHandler`,
|
|
475
|
+
`SingleSelectionHandler`, `MultiSelectionHandler`, and
|
|
476
|
+
`createSelectionHandler` directly. Ordinary applications should prefer the
|
|
477
|
+
selection behavior already owned by `ListView` and `DataTable`.
|
|
478
|
+
|
|
479
|
+
`TreeView` owns keyboard navigation, expansion, typeahead, and selection. Use
|
|
480
|
+
`itemLabelClassName` and `itemLabelStyle` when only the label beside the
|
|
481
|
+
expander needs a reusable presentation trait such as `colored`.
|
|
482
|
+
|
|
483
|
+
#### DataTable inputs and ownership
|
|
484
|
+
|
|
485
|
+
`DataTable` requires exactly one data mode:
|
|
486
|
+
|
|
487
|
+
- `data`: a static array or readable emitter; the table owns the local derived
|
|
488
|
+
data source it creates.
|
|
489
|
+
- `dataSource`: a caller-owned `TableDataSource`; the caller disposes it.
|
|
490
|
+
- `rest`: convenience options for a table-owned REST-backed source.
|
|
491
|
+
|
|
492
|
+
For reusable sources, use `createLocalTableDataSource`,
|
|
493
|
+
`createQueryTableDataSource`, `createHandlerTableDataSource`, or
|
|
494
|
+
`createRestTableDataSource`. Sources expose `query`, `sortEmitter`,
|
|
495
|
+
`filtersEmitter`, optional `retry`, and `dispose()`.
|
|
496
|
+
|
|
497
|
+
`TableColumn` definitions own display and local comparison/filter functions.
|
|
498
|
+
When a column's visible `label` is rich content, supply its textual
|
|
499
|
+
`ariaLabel` for Capillary UI-generated sort and filter control names.
|
|
500
|
+
The pure `applyLocalTableState`, `serializeTableQuery`, and related table-query
|
|
501
|
+
helpers keep local behavior and remote encoding explicit. Pagination,
|
|
502
|
+
virtualization, and server-specific wire policy remain application concerns.
|
|
503
|
+
|
|
504
|
+
### Dialog, status, and presentation selection
|
|
505
|
+
|
|
506
|
+
| Component | Purpose | Key props and state |
|
|
507
|
+
| --- | --- | --- |
|
|
508
|
+
| `Dialog` | Controlled native modal with focus containment and restoration | `title`, `description`, `DialogActions` and ordinary content children, `valueEmitter`/`defaultValue`, `closeLabel`, `showCloseButton`, `initialFocusRef`, `onClose` |
|
|
509
|
+
| `ProgressBar` | Labelled native progress with visual track | required `label`, `value` or `valueEmitter`, `max`, `valueText`; `null` is indeterminate |
|
|
510
|
+
| `ThemePicker` | Select and replace a Capillary UI theme link | value props, `options`, `label`/`ariaLabel`, `disabled`, `targetDocument`, `onChange` |
|
|
511
|
+
| `ColorPicker` | Select and replace a Capillary UI color link | same contract as `ThemePicker` |
|
|
512
|
+
|
|
513
|
+
The pickers use `capillaryUiThemeOptions` and `capillaryUiColorOptions` by default. An
|
|
514
|
+
application still owns whether runtime selection is offered, which options are
|
|
515
|
+
available, and whether the selected identifier is persisted.
|
|
516
|
+
|
|
517
|
+
## Semantic filter state
|
|
518
|
+
|
|
519
|
+
Capillary UI's filter helpers keep presentation symbols separate from matching policy.
|
|
520
|
+
A `FilterState` is plain, versionable data keyed by dimension and option. A
|
|
521
|
+
`FilterDimensionDefinition` supplies the application-owned matchers.
|
|
522
|
+
|
|
523
|
+
Dimensions combine with AND. Within a dimension, deny wins, every required
|
|
524
|
+
option must match, and at least one preferred option must match when any are
|
|
525
|
+
active. Unknown persisted keys survive serialization without constraining
|
|
526
|
+
current matching.
|
|
527
|
+
|
|
528
|
+
Use `matchesFilterState` or `filterByState` for pure evaluation;
|
|
529
|
+
`deriveFilterPredicate` and `deriveFilteredItems` for reactive results; and
|
|
530
|
+
`serializeFilterState`/`parseFilterState` for deterministic versioned data.
|
|
531
|
+
|
|
532
|
+
## Localization
|
|
533
|
+
|
|
534
|
+
Capillary UI can consume the result of your existing localization system for text and
|
|
535
|
+
formatting that Capillary UI itself owns. Configure it once when creating the runtime:
|
|
536
|
+
|
|
537
|
+
```tsx
|
|
538
|
+
import {createCapillaryUiRuntime} from '@capillaryjs/capillary-ui'
|
|
539
|
+
import type {CapillaryUiMessageOverrides} from '@capillaryjs/capillary-ui'
|
|
540
|
+
|
|
541
|
+
const messages: CapillaryUiMessageOverrides = {
|
|
542
|
+
toolbarLabel: i18n.t('capillaryUi.toolbar.label'),
|
|
543
|
+
dialogCloseLabel: i18n.t('capillaryUi.dialog.close'),
|
|
544
|
+
dataTableEmpty: i18n.t('capillaryUi.table.empty'),
|
|
545
|
+
tableSortColumnLabel: (label) => i18n.t('capillaryUi.table.sort', {label}),
|
|
546
|
+
checkboxStateLabel: (label, state) =>
|
|
547
|
+
i18n.t('capillaryUi.checkbox.state', {label, state}),
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const runtime = createCapillaryUiRuntime({
|
|
551
|
+
localization: {
|
|
552
|
+
locale: i18n.locale,
|
|
553
|
+
messages,
|
|
554
|
+
},
|
|
555
|
+
})
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
`locale` is a non-empty BCP 47 tag. Capillary UI canonicalizes it and uses it for the
|
|
559
|
+
calendar's complete month/year heading, weekday names, and day numerals. The
|
|
560
|
+
calendar stays Gregorian and currently remains Sunday-first. Calendar display
|
|
561
|
+
values come from `Intl`; do not add them to the message object.
|
|
562
|
+
|
|
563
|
+
Every `CapillaryUiMessageOverrides` property is optional. Capillary UI copies supplied values
|
|
564
|
+
at runtime construction and fills omitted properties from English defaults.
|
|
565
|
+
Fixed messages are strings; messages that insert a label are typed functions,
|
|
566
|
+
so the organization's localization adapter controls word order and
|
|
567
|
+
interpolation. Explicit component props such as `Dialog.closeLabel`,
|
|
568
|
+
`Toolbar.label`, or `DataTable.emptyMessage` still take precedence.
|
|
569
|
+
|
|
570
|
+
The configuration is immutable and runtime-local. It is not a `ServiceScope`
|
|
571
|
+
service or a live locale binding. To select another language, create and mount
|
|
572
|
+
a runtime with the new localization configuration. Multiple runtimes may use
|
|
573
|
+
different locales on one page.
|
|
574
|
+
|
|
575
|
+
Capillary UI does not load catalogs, select or persist a locale, define fallbacks or
|
|
576
|
+
plural rules, translate caller-provided labels/errors/content, or set the
|
|
577
|
+
document's `lang` or `dir`. The application must set `lang` consistently with
|
|
578
|
+
the configured locale and owns RTL behavior. Localized parsing, locale-specific
|
|
579
|
+
week starts, time/number/percentage formatting, and collation are not part of
|
|
580
|
+
this contract.
|
|
581
|
+
|
|
582
|
+
`Checkbox.ariaLabel` and `TableColumn.ariaLabel` are textual alternatives for
|
|
583
|
+
rich visible labels used inside Capillary UI-generated accessibility messages. For
|
|
584
|
+
ordinary string/number labels they are unnecessary.
|
|
585
|
+
|
|
586
|
+
## Application services
|
|
587
|
+
|
|
588
|
+
Service implementations remain ordinary application TypeScript. Capillary UI provides
|
|
589
|
+
typed keys and a fixed application scope, not dependency discovery:
|
|
590
|
+
|
|
591
|
+
```tsx
|
|
592
|
+
class ProjectService {
|
|
593
|
+
readonly label = 'Projects'
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const projectService = defineService<ProjectService>('projects')
|
|
597
|
+
const services = createServiceScope([
|
|
598
|
+
provideService(projectService, () => new ProjectService()),
|
|
599
|
+
])
|
|
600
|
+
|
|
601
|
+
class ProjectTitle extends Component {
|
|
602
|
+
static requiredServices = [projectService]
|
|
603
|
+
private service!: ProjectService
|
|
604
|
+
|
|
605
|
+
initialize() {
|
|
606
|
+
this.service = this.requireService(projectService)
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
render() {
|
|
610
|
+
return <output>{this.service.label}</output>
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const runtime = createCapillaryUiRuntime({services})
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
Providers are immutable, lazy, and scope-shared. Factories can explicitly
|
|
618
|
+
resolve declared dependencies through their `ServiceResolver`; cycles and
|
|
619
|
+
missing providers fail clearly. `ServiceScope.dispose()` disposes initialized
|
|
620
|
+
services in reverse creation order. Components own the queries/results they
|
|
621
|
+
open; they do not dispose scope-shared services.
|
|
622
|
+
|
|
623
|
+
`CapillaryUiRuntime` carries one `ServiceScope`, optional router, optional static
|
|
624
|
+
localization, and isolated `StyleRegistry`. `createCapillaryUiRuntime()` is the normal
|
|
625
|
+
construction entry point; `defaultCapillaryUiRuntime` supports direct compatibility
|
|
626
|
+
mounting with English messages and the browser's default locale.
|
|
627
|
+
|
|
628
|
+
## Browser routing
|
|
629
|
+
|
|
630
|
+
Capillary UI routing binds explicit route vocabulary to ordinary writable emitters.
|
|
631
|
+
The application owns descriptors, codecs, data-dependent resolvers, and the
|
|
632
|
+
navigation adapter.
|
|
633
|
+
|
|
634
|
+
```tsx
|
|
635
|
+
const portfolioRoute = defineRoute('portfolio')
|
|
636
|
+
const registerRoute = defineRoute('register')
|
|
637
|
+
const projectRoute = defineRouteParameter('project', stringRouteCodec)
|
|
638
|
+
const selectedProject = new Emitter<string | null>(null)
|
|
639
|
+
const activeApplication = new Emitter<Key | null>('portfolio')
|
|
640
|
+
|
|
641
|
+
const router = createBrowserRouter({adapter: createHashNavigation()})
|
|
642
|
+
const runtime = createCapillaryUiRuntime({router})
|
|
643
|
+
|
|
644
|
+
<NavigationBar
|
|
645
|
+
label="Application sections"
|
|
646
|
+
items={[
|
|
647
|
+
{id: 'portfolio', label: 'Portfolio', to: routeTarget(portfolioRoute)},
|
|
648
|
+
{id: 'register', label: 'Register', to: routeTarget(registerRoute)},
|
|
649
|
+
]}
|
|
650
|
+
/>
|
|
651
|
+
<RouteOutlet
|
|
652
|
+
valueEmitter={activeApplication}
|
|
653
|
+
mountPolicy="active-only"
|
|
654
|
+
views={[{
|
|
655
|
+
id: 'portfolio',
|
|
656
|
+
route: portfolioRoute,
|
|
657
|
+
content:
|
|
658
|
+
<RouteValue
|
|
659
|
+
route={projectRoute}
|
|
660
|
+
valueEmitter={selectedProject}
|
|
661
|
+
scopeChildren={true}
|
|
662
|
+
>
|
|
663
|
+
<ProjectScreen selectedProject={selectedProject} />
|
|
664
|
+
</RouteValue>,
|
|
665
|
+
}, {
|
|
666
|
+
id: 'register',
|
|
667
|
+
route: registerRoute,
|
|
668
|
+
content: <RegisterScreen />,
|
|
669
|
+
}]}
|
|
670
|
+
/>
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
Core routing exports:
|
|
674
|
+
|
|
675
|
+
- `defineRoute`, `defineRouteParameter`, `routeParameter`, `routeTarget`, and
|
|
676
|
+
`withRouteQuery` create immutable descriptors and targets.
|
|
677
|
+
- `BrowserRouter`/`createBrowserRouter` progressively restore mounted scopes,
|
|
678
|
+
normalize locations, and expose structured issue state.
|
|
679
|
+
- `createHistoryNavigation`, `createHashNavigation`, and
|
|
680
|
+
`MemoryNavigationAdapter` decide where locations live.
|
|
681
|
+
- `RouteScope` establishes lineage; `RouteValue` binds dynamic path values;
|
|
682
|
+
`RouteQuery` binds one named query value; `RouteLink` renders a real anchor.
|
|
683
|
+
- `NavigationBar` groups native route links and external-destination anchors
|
|
684
|
+
but does not own destination DOM.
|
|
685
|
+
- `RouteOutlet` registers one sibling literal-route set against an
|
|
686
|
+
application-owned emitter and gives selected content its resolved scope.
|
|
687
|
+
- `waitForRouteValue` lets a resolver await a readable application
|
|
688
|
+
prerequisite with cancellation.
|
|
689
|
+
|
|
690
|
+
Resolvers may return `RouteRedirect` through `redirectTo()`, or throw
|
|
691
|
+
`RouteUnavailableError` when the requested value cannot be represented in the
|
|
692
|
+
mounted application state.
|
|
693
|
+
|
|
694
|
+
Explicit navigation pushes by default. Restoration never pushes; redirects,
|
|
695
|
+
fallback, canonicalization, and passive bound-state changes replace. A
|
|
696
|
+
superseding transition aborts pending resolvers. Invalid locations settle at
|
|
697
|
+
the deepest valid parent and leave accessible issue presentation to the
|
|
698
|
+
application.
|
|
699
|
+
|
|
700
|
+
The history adapter needs server fallback for direct deep requests. The hash
|
|
701
|
+
adapter reserves the fragment. The memory adapter is intended for deterministic
|
|
702
|
+
tests. The caller owns and disposes the router.
|
|
703
|
+
|
|
704
|
+
`RouteOutlet.mountPolicy` uses the same `ContentMountPolicy` values as
|
|
705
|
+
`TabPanel`: `eager`, `lazy`, and `active-only`. Immediate routes are registered
|
|
706
|
+
whether or not their content is mounted. During direct restoration, the
|
|
707
|
+
matching pending branch is selected before the first content render, so an
|
|
708
|
+
active-only default branch cannot initialize and activate unrequested work.
|
|
709
|
+
Other page regions may independently read `activeApplication`; only the outlet
|
|
710
|
+
registers that sibling route set. Application-global navigation should usually
|
|
711
|
+
use explicit `routeTarget(...)` values, while relative descriptors are suited
|
|
712
|
+
to a navigation bar inside the route scope that registered them.
|
|
713
|
+
|
|
714
|
+
## Styling contract
|
|
715
|
+
|
|
716
|
+
Load presentation in this order:
|
|
717
|
+
|
|
718
|
+
1. `@capillaryjs/capillary-ui/themes/base.css`
|
|
719
|
+
2. Collected CSS or `@capillaryjs/capillary-ui/styles/structural.css`
|
|
720
|
+
3. One `@capillaryjs/capillary-ui/colors/<name>/colors.css`
|
|
721
|
+
4. One `@capillaryjs/capillary-ui/themes/<name>/theme.css`
|
|
722
|
+
|
|
723
|
+
`base.css` declares variables and derives palette roles but contains no
|
|
724
|
+
component selectors. Color files provide anchors and endpoints. Component
|
|
725
|
+
`static css` owns selectors, layout, pseudo-elements, native states, and
|
|
726
|
+
interaction mechanics.
|
|
727
|
+
|
|
728
|
+
Theme files provide intentional overrides. Custom properties are the primary
|
|
729
|
+
instrument and belong on `:root` inside `@layer theme`, with `color-scheme` as
|
|
730
|
+
the only ordinary property in that block. A theme may also write ordinary CSS
|
|
731
|
+
rules when no variable expresses the intended difference, but those rules must
|
|
732
|
+
be placed after the `@layer theme` block: component CSS is injected as an
|
|
733
|
+
unlayered `<style>` element prepended to `<head>`, so unlayered theme rules win
|
|
734
|
+
by document order while layered ones would always lose.
|
|
735
|
+
|
|
736
|
+
`capillaryUiThemeVariableCatalog` describes the supported palette and semantic
|
|
737
|
+
variable hierarchy. `findCapillaryUiStylesheetOption`, `replaceCapillaryUiStylesheet`,
|
|
738
|
+
`setCapillaryUiAppearance`, and `getCapillaryUiAppearance` support application-controlled
|
|
739
|
+
runtime selection.
|
|
740
|
+
|
|
741
|
+
### Root sizing and typography
|
|
742
|
+
|
|
743
|
+
`CapillaryUiApp` is block-level and always applies `--application-background`,
|
|
744
|
+
`--ui-color`, `--font-family`, `--font-size`, and `--line-height`, so all of
|
|
745
|
+
its native and Capillary UI descendants inherit theme text treatment even when it is
|
|
746
|
+
embedded. Its `sizing` prop maps to `cap-fill-horizontal`,
|
|
747
|
+
`cap-fill-vertical`, or both to claim `100vw`, `100vh`, or the full viewport.
|
|
748
|
+
Its optional `layout` prop maps to the direction trait on that same host. This
|
|
749
|
+
is important for viewport shells: Flexbox only distributes an already bounded
|
|
750
|
+
size, so an auto-height intermediate wrapper does not inherit the root's
|
|
751
|
+
height constraint automatically.
|
|
752
|
+
|
|
753
|
+
The document itself remains application policy. A viewport-sized root claims
|
|
754
|
+
`100vh`, so the browser's default `body` margin would overflow it into
|
|
755
|
+
document scrollbars; dedicated app documents should remove it:
|
|
756
|
+
|
|
757
|
+
```css
|
|
758
|
+
html, body { margin: 0; }
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
The traits remain available for applications that use a plain `Component`
|
|
762
|
+
root. They now apply the same canvas, text color, and typography values. A
|
|
763
|
+
plain root without either trait remains content-sized and inherits host-page
|
|
764
|
+
text treatment.
|
|
765
|
+
|
|
766
|
+
### Native elements, traits, and component hosts
|
|
767
|
+
|
|
768
|
+
Capillary UI does not assign component or surface presentation to application-owned
|
|
769
|
+
native elements merely because of their element type. Native elements such as
|
|
770
|
+
`header`, `footer`, `main`, `section`, and `aside` retain their ordinary HTML
|
|
771
|
+
semantics.
|
|
772
|
+
|
|
773
|
+
Applications may explicitly opt native elements into Capillary UI presentation and
|
|
774
|
+
layout contracts by applying public Capillary UI traits such as `island`,
|
|
775
|
+
`cap-layout-horizontal`, `cap-layout-vertical`, `cap-size-natural`,
|
|
776
|
+
`cap-size-flexible`, and `cap-scroll`. These traits are intentionally
|
|
777
|
+
element-agnostic and may style application-owned native markup as well as
|
|
778
|
+
Capillary UI-owned hosts.
|
|
779
|
+
|
|
780
|
+
A Capillary UI component host is therefore not required merely to obtain Capillary UI layout or
|
|
781
|
+
surface treatment. Use `Layout` when no native semantic element is appropriate
|
|
782
|
+
and the container exists only to arrange children. Introduce any other component
|
|
783
|
+
when it owns a meaningful structural, behavioral, accessible, or presentation
|
|
784
|
+
contract.
|
|
785
|
+
|
|
786
|
+
In short: native element names provide semantics, Capillary UI traits provide opt-in
|
|
787
|
+
presentation and layout, and Capillary UI component hosts provide component-owned
|
|
788
|
+
contracts.
|
|
789
|
+
|
|
790
|
+
### Reusable traits
|
|
791
|
+
|
|
792
|
+
The allocation traits are structural and independently composable:
|
|
793
|
+
|
|
794
|
+
- `cap-layout-horizontal` and `cap-layout-vertical` arrange direct children
|
|
795
|
+
and stretch them across the other axis;
|
|
796
|
+
- `cap-size-natural` keeps a direct child's application/content allocation;
|
|
797
|
+
- `cap-size-flexible` shares remaining main-axis space and supplies zero
|
|
798
|
+
logical minimums so nested content can shrink;
|
|
799
|
+
- `cap-scroll` makes a bounded node the explicit overflow owner.
|
|
800
|
+
|
|
801
|
+
`Header`, `Layout`, `NavigationBar`, `Panel`, `Sidebar`, `SplitView`, and
|
|
802
|
+
`Toolbar` accept
|
|
803
|
+
`allocation="natural" | "flexible"` and map it to their outer host. `Panel`
|
|
804
|
+
uses `horizontal` or `vertical` for its inner Layout body rather than its
|
|
805
|
+
generated header and toolbar. SplitView applies direction to the relationship
|
|
806
|
+
between its two panes; each pane independently arranges its own children.
|
|
807
|
+
Application-owned elements may use the classes directly.
|
|
808
|
+
|
|
809
|
+
`Layout`, `Panel`, and `SplitView` reject simultaneous `horizontal` and
|
|
810
|
+
`vertical` modifiers. Layout requires one explicitly. Panel and SplitView retain
|
|
811
|
+
their former vertical and horizontal defaults, respectively, while the legacy
|
|
812
|
+
`orientation` and `direction` spellings remain compatibility aliases.
|
|
813
|
+
|
|
814
|
+
Flexible siblings have equal growth shares only when their box decoration is
|
|
815
|
+
equivalent. Application CSS may override ratios, sizes, and gaps. Flexible
|
|
816
|
+
allocation does not imply scrolling, and an island does not select the scroll
|
|
817
|
+
owner; filled-root island overflow remains a compatibility fallback. Rules use
|
|
818
|
+
no `!important`, so later application CSS can refine them. Capillary UI does not yet
|
|
819
|
+
provide breakpoint variants.
|
|
820
|
+
|
|
821
|
+
`island` marks one deliberate themeable surface boundary. Pass
|
|
822
|
+
`island={true}` to a wrapped component or use the class on application-owned
|
|
823
|
+
native markup. Capillary UI rejects nested component islands; application markup must
|
|
824
|
+
preserve the same one-layer invariant.
|
|
825
|
+
|
|
826
|
+
`colored` consumes an explicit `--c1`, `--c2`, `--c3` triplet for the shared
|
|
827
|
+
gradient and `--colored-shadow` treatment. It does not choose semantic colors
|
|
828
|
+
for the application.
|
|
829
|
+
|
|
830
|
+
## Accessibility and browser support
|
|
831
|
+
|
|
832
|
+
Capillary UI components use native controls and landmarks where possible, expose
|
|
833
|
+
accessible names, preserve focus during keyed updates, and render loading,
|
|
834
|
+
empty, and error messages outside collection semantics. The browser matrix
|
|
835
|
+
covers pinned Chromium, Firefox, and WebKit builds, including keyboard flows,
|
|
836
|
+
200% text, forced colors, and automated accessibility checks.
|
|
837
|
+
|
|
838
|
+
Applications remain responsible for meaningful labels, heading hierarchy,
|
|
839
|
+
domain validation messages, color contrast introduced by application CSS,
|
|
840
|
+
focus order across composed screens, and manual assistive-technology testing.
|
|
841
|
+
|
|
842
|
+
Capillary UI does not support SSR, hydration, Shadow DOM, registered Web Components,
|
|
843
|
+
legacy browsers, or a concurrent rendering scheduler.
|
|
844
|
+
|
|
845
|
+
## Further reference
|
|
846
|
+
|
|
847
|
+
- [Public API surface](../../docs/API_SURFACE.md)
|
|
848
|
+
- [Architecture](../../docs/architecture.md)
|
|
849
|
+
- [Application composition guide](docs/application-composition-guide.md) — design screens, component boundaries, state lifetimes, and layouts.
|
|
850
|
+
- [Repository layout guide](docs/application-layout-guide.md) — organize application source by responsibility and feature.
|
|
851
|
+
- [Theme contract](themes/README.md)
|
|
852
|
+
- [Color palette contract](colors/README.md)
|
|
853
|
+
- [Release history](CHANGELOG.md)
|