@sylwellsoftware/fray 0.7.0 → 1.1.0
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/README.md +282 -207
- package/colors/README.md +27 -23
- package/colors/gray/colors.css +1 -42
- package/colors/green/colors.css +1 -42
- package/colors/iceblue/colors.css +5 -43
- package/colors/ocean/colors.css +1 -42
- package/colors/orange/colors.css +1 -42
- package/colors/purple/colors.css +1 -42
- package/colors/red/colors.css +1 -42
- package/colors/yellow/colors.css +1 -42
- package/dist/Components/Placeholder.d.ts +1 -2
- package/dist/Components/Placeholder.d.ts.map +1 -1
- package/dist/Components/component.d.ts +15 -7
- package/dist/Components/component.d.ts.map +1 -1
- package/dist/Components/data/descriptionList.d.ts +3 -1
- package/dist/Components/data/descriptionList.d.ts.map +1 -1
- package/dist/Components/data/listview/listview.d.ts +1 -2
- package/dist/Components/data/listview/listview.d.ts.map +1 -1
- package/dist/Components/data/table/DataTable.d.ts +1 -1
- package/dist/Components/data/table/DataTable.d.ts.map +1 -1
- package/dist/Components/data/table/FilterPanel.d.ts +2 -4
- package/dist/Components/data/table/FilterPanel.d.ts.map +1 -1
- package/dist/Components/data/table/TableHeader.d.ts +2 -0
- package/dist/Components/data/table/TableHeader.d.ts.map +1 -1
- package/dist/Components/data/table/TableHeaderCell.d.ts +1 -1
- package/dist/Components/data/table/TableHeaderCell.d.ts.map +1 -1
- package/dist/Components/data/treeview/treeitem.d.ts +1 -0
- package/dist/Components/data/treeview/treeitem.d.ts.map +1 -1
- package/dist/Components/data/treeview/treeview.d.ts +5 -1
- package/dist/Components/data/treeview/treeview.d.ts.map +1 -1
- package/dist/Components/dialog/dialog.d.ts +6 -2
- package/dist/Components/dialog/dialog.d.ts.map +1 -1
- package/dist/Components/layout/groupPanel.d.ts +21 -0
- package/dist/Components/layout/groupPanel.d.ts.map +1 -0
- package/dist/Components/layout/header.d.ts +19 -0
- package/dist/Components/layout/header.d.ts.map +1 -0
- package/dist/Components/layout/panel.d.ts +7 -3
- package/dist/Components/layout/panel.d.ts.map +1 -1
- package/dist/Components/layout/sidebar.d.ts +4 -0
- package/dist/Components/layout/sidebar.d.ts.map +1 -1
- package/dist/Components/layout/splitView.d.ts +1 -1
- package/dist/Components/layout/splitView.d.ts.map +1 -1
- package/dist/Components/layout/tabpanel/tab.d.ts +1 -0
- package/dist/Components/layout/tabpanel/tab.d.ts.map +1 -1
- package/dist/Components/layout/tabpanel/tabline.d.ts +1 -2
- package/dist/Components/layout/tabpanel/tabline.d.ts.map +1 -1
- package/dist/Components/layout/tabpanel/tabpanel.d.ts +1 -1
- package/dist/Components/layout/tabpanel/tabpanel.d.ts.map +1 -1
- 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 +11 -5
- package/dist/Components/lineinputs/checkbox/Checkbox.d.ts.map +1 -1
- package/dist/Components/lineinputs/checkbox/QuadCheckbox.d.ts +1 -2
- package/dist/Components/lineinputs/checkbox/QuadCheckbox.d.ts.map +1 -1
- package/dist/Components/lineinputs/checkbox/TriCheckbox.d.ts +1 -2
- package/dist/Components/lineinputs/checkbox/TriCheckbox.d.ts.map +1 -1
- package/dist/Components/lineinputs/dropdown.d.ts +7 -6
- package/dist/Components/lineinputs/dropdown.d.ts.map +1 -1
- 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 +20 -7
- package/dist/Components/lineinputs/radio.d.ts.map +1 -1
- package/dist/Components/lineinputs/textbox.d.ts +7 -6
- package/dist/Components/lineinputs/textbox.d.ts.map +1 -1
- package/dist/Components/lineinputs/toggle.d.ts +10 -3
- package/dist/Components/lineinputs/toggle.d.ts.map +1 -1
- package/dist/Components/menu/button.d.ts +6 -3
- package/dist/Components/menu/button.d.ts.map +1 -1
- package/dist/Components/menu/toolbar.d.ts +1 -1
- package/dist/Components/menu/toolbar.d.ts.map +1 -1
- package/dist/Components/status/progressBar.d.ts +1 -1
- package/dist/Components/status/progressBar.d.ts.map +1 -1
- package/dist/Components/theme/stylesheetPicker.d.ts +6 -7
- package/dist/Components/theme/stylesheetPicker.d.ts.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2613 -1825
- package/dist/index.js.map +1 -1
- package/dist/{jsx-dev-runtime-Kf1Dhu-y.js → jsx-dev-runtime-8s_xIag_.js} +501 -484
- package/dist/jsx-dev-runtime-8s_xIag_.js.map +1 -0
- package/dist/jsx-dev-runtime.js +1 -1
- package/dist/jsx-runtime.js +1 -1
- package/dist/routing/RouteLink.d.ts +1 -0
- package/dist/routing/RouteLink.d.ts.map +1 -1
- package/dist/routing/RouteQuery.d.ts +1 -0
- package/dist/routing/RouteQuery.d.ts.map +1 -1
- package/dist/routing/RouteScope.d.ts +1 -0
- package/dist/routing/RouteScope.d.ts.map +1 -1
- package/dist/routing/RouteValue.d.ts +1 -0
- package/dist/routing/RouteValue.d.ts.map +1 -1
- package/dist/runtime.d.ts +5 -13
- package/dist/runtime.d.ts.map +1 -1
- package/dist/styling/styleRegistry.d.ts +0 -4
- package/dist/styling/styleRegistry.d.ts.map +1 -1
- package/dist/styling/theme.d.ts +14 -2
- package/dist/styling/theme.d.ts.map +1 -1
- package/package.json +5 -6
- package/styles/structural.css +943 -399
- package/themes/README.md +30 -72
- package/themes/base.css +313 -0
- package/themes/java/theme.css +14 -423
- package/themes/minimal/theme.css +3 -419
- package/themes/shiny/theme.css +121 -508
- package/dist/jsx-dev-runtime-Kf1Dhu-y.js.map +0 -1
- package/dist/styling/baseStyleDefinitions.d.ts +0 -75
- package/dist/styling/baseStyleDefinitions.d.ts.map +0 -1
- package/themes/dark.css +0 -98
- package/themes/light.css +0 -98
package/README.md
CHANGED
|
@@ -27,10 +27,12 @@ Presentation should be equally direct. Native HTML already defines buttons,
|
|
|
27
27
|
inputs, tables, lists, progress, dialogs, and landmarks, so Fray uses those
|
|
28
28
|
elements when their semantics match. Components that need another boundary use
|
|
29
29
|
readable light-DOM host names rather than framework identity classes.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
Within a component host, Fray uses classes for owned parts, purely visual
|
|
31
|
+
states with no semantic equivalent, and meaningful reusable traits; those
|
|
32
|
+
classes do not identify components. Native and ARIA state attributes remain the
|
|
33
|
+
sole semantic state source and are targeted directly by CSS. Renderer markers,
|
|
34
|
+
diagnostics, interoperability, and actual data use `data-*`; Fray does not use
|
|
35
|
+
`data-*` as its routine component-CSS hook.
|
|
34
36
|
|
|
35
37
|
## Design model
|
|
36
38
|
|
|
@@ -92,15 +94,17 @@ Responsibility stays at the narrowest layer that understands it:
|
|
|
92
94
|
|
|
93
95
|
## Set up a browser application
|
|
94
96
|
|
|
95
|
-
Fray ships ESM, TypeScript declarations, automatic/classic JSX runtimes,
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
Fray ships ESM, TypeScript declarations, automatic/classic JSX runtimes,
|
|
98
|
+
variable defaults, replaceable theme treatments, and replaceable color
|
|
99
|
+
palettes. Glue is a peer dependency. Applications collect structural CSS from
|
|
100
|
+
the component classes they actually declare.
|
|
98
101
|
|
|
99
102
|
```ts
|
|
100
103
|
import {Emitter} from '@sylwellsoftware/glue'
|
|
101
104
|
import {
|
|
102
105
|
Button,
|
|
103
106
|
Component,
|
|
107
|
+
Header,
|
|
104
108
|
Panel,
|
|
105
109
|
Sidebar,
|
|
106
110
|
Textbox,
|
|
@@ -108,7 +112,7 @@ import {
|
|
|
108
112
|
createFrayRuntime,
|
|
109
113
|
h,
|
|
110
114
|
} from '@sylwellsoftware/fray'
|
|
111
|
-
import '@sylwellsoftware/fray/
|
|
115
|
+
import '@sylwellsoftware/fray/themes/base.css'
|
|
112
116
|
import '@sylwellsoftware/fray/colors/iceblue/colors.css'
|
|
113
117
|
import '@sylwellsoftware/fray/themes/minimal/theme.css'
|
|
114
118
|
|
|
@@ -118,7 +122,10 @@ class App extends Component {
|
|
|
118
122
|
static dependencies = [Button, Panel, Textbox, Toolbar]
|
|
119
123
|
|
|
120
124
|
render() {
|
|
121
|
-
return h(Panel, {
|
|
125
|
+
return h(Panel, {
|
|
126
|
+
className: 'fray-fill-horizontal fray-fill-vertical',
|
|
127
|
+
header: 'Profile',
|
|
128
|
+
},
|
|
122
129
|
h(Textbox, {label: 'Name', valueEmitter: name}),
|
|
123
130
|
h(Toolbar, {label: 'Profile actions'},
|
|
124
131
|
h(Button, {label: 'Save', onClick: () => save(name.get())})))
|
|
@@ -126,6 +133,7 @@ class App extends Component {
|
|
|
126
133
|
}
|
|
127
134
|
|
|
128
135
|
const runtime = createFrayRuntime()
|
|
136
|
+
runtime.registerStyles(App).injectStyles(document)
|
|
129
137
|
runtime.mount(runtime.create(App), document.querySelector('#app')!)
|
|
130
138
|
|
|
131
139
|
function save(value: string) {
|
|
@@ -133,6 +141,17 @@ function save(value: string) {
|
|
|
133
141
|
}
|
|
134
142
|
```
|
|
135
143
|
|
|
144
|
+
Root sizing is application-owned. Add `fray-fill-horizontal` to a rendered
|
|
145
|
+
application root to claim exactly `100vw`, `fray-fill-vertical` to claim
|
|
146
|
+
exactly `100vh`, or both for a fullscreen application. Each axis supplies its
|
|
147
|
+
own root overflow fallback and zero minimum; descendant islands are bounded
|
|
148
|
+
and scroll on only the opted-in axes. Other components retain their intrinsic
|
|
149
|
+
minimums and their own structural overflow contracts. Either modifier also
|
|
150
|
+
establishes the theme's `--font-family`, `--font-size`, and `--line-height` on
|
|
151
|
+
the application root so native content and Fray controls inherit the published
|
|
152
|
+
typography without application CSS. A root with neither modifier keeps its
|
|
153
|
+
ordinary embedded/content-sized behavior, including its host page's typography.
|
|
154
|
+
|
|
136
155
|
The prebuilt structural file targets Fray's default `fray-` hosts. Applications
|
|
137
156
|
with custom components or configured host names may instead register their root
|
|
138
157
|
dependencies and call `runtime.injectStyles(document)`; collection remains
|
|
@@ -380,11 +399,11 @@ not a framework identity class. The default application runtime therefore
|
|
|
380
399
|
produces DOM such as:
|
|
381
400
|
|
|
382
401
|
```html
|
|
383
|
-
<fray-panel class="
|
|
384
|
-
<fray-textbox data-fray-component="textbox">
|
|
385
|
-
<input type="text">
|
|
402
|
+
<fray-panel data-fray class="island" data-fray-component="panel">
|
|
403
|
+
<fray-textbox data-fray data-fray-component="textbox">
|
|
404
|
+
<input data-fray type="text">
|
|
386
405
|
</fray-textbox>
|
|
387
|
-
<button data-fray-component="button">Save</button>
|
|
406
|
+
<button data-fray data-fray-component="button">Save</button>
|
|
388
407
|
</fray-panel>
|
|
389
408
|
```
|
|
390
409
|
|
|
@@ -393,48 +412,44 @@ Native semantics remain native: `Button` renders `button`, `Toggle` renders
|
|
|
393
412
|
`thead`/`th`. `Tab` is a declarative child consumed by `TabPanel` and has no
|
|
394
413
|
independent root. The
|
|
395
414
|
`data-fray-component` keeps diagnostics unambiguous. It is not a structural or
|
|
396
|
-
theme selector. Fray may merge public presentation traits such as `
|
|
397
|
-
with an application-supplied `class`/`className`; those traits
|
|
398
|
-
reusable capability, not component identity.
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
+
theme selector. Fray may merge public presentation traits such as `island`
|
|
416
|
+
and `colored` with an application-supplied `class`/`className`; those traits
|
|
417
|
+
describe a reusable capability, not component identity.
|
|
418
|
+
|
|
419
|
+
Pass `island` to a fixed-host component when that surface should be visually
|
|
420
|
+
separated from the page. Fray adds the reusable `island` class to its host;
|
|
421
|
+
applications may use the same class on deliberate native surface boundaries.
|
|
422
|
+
The component-owned rule consumes `--island-*` variables, so an island theme
|
|
423
|
+
can add gutters, an edge, and elevation while a flat theme can leave the
|
|
424
|
+
modifier visually neutral. Islands are one surface layer: nesting an `island`
|
|
425
|
+
component below another island is rejected. Application-authored native island
|
|
426
|
+
classes must follow the same no-nesting invariant. A surface island never
|
|
427
|
+
creates a nested theme or palette scope.
|
|
428
|
+
|
|
429
|
+
Apply `colored` only to an element that supplies an ordered dark/base/light
|
|
430
|
+
triplet through `--c1`, `--c2`, and `--c3`. The shared component rule paints
|
|
431
|
+
that triplet as a gradient; named themes can add depth through
|
|
432
|
+
`--colored-shadow`. Shiny supplies its glossy shadow while the base and other
|
|
433
|
+
themes remain flat.
|
|
434
|
+
|
|
435
|
+
Built-in host names are fixed public DOM: Fray adds its one required custom
|
|
436
|
+
element hyphen and removes internal word separators from the component stem.
|
|
437
|
+
For example, `Panel`, `ListView`, and `ThemePicker` render as `<fray-panel>`,
|
|
438
|
+
`<fray-listview>`, and `<fray-themepicker>`. They cannot be prefixed, renamed,
|
|
439
|
+
or made prefix-free at runtime. This lets distributed stylesheets target hosts
|
|
440
|
+
directly and predictably.
|
|
441
|
+
|
|
442
|
+
Every element created by Fray's renderer also receives the boolean `data-fray`
|
|
443
|
+
attribute. Themes can therefore target native Fray output without affecting
|
|
444
|
+
other UI libraries: `button[data-fray]`, `input[data-fray]`, and
|
|
445
|
+
`dialog[data-fray]`. The marker is renderer-owned and cannot be removed through
|
|
446
|
+
component props. `data-fray-component` remains diagnostic metadata, not an
|
|
447
|
+
ordinary styling selector.
|
|
415
448
|
|
|
416
|
-
const selectedOverrides = createFrayRuntime({
|
|
417
|
-
elementNames: {
|
|
418
|
-
prefix: null,
|
|
419
|
-
overrides: {'panel': 'change-panel'},
|
|
420
|
-
},
|
|
421
|
-
})
|
|
422
|
-
// <change-panel>, with standalone names for the other components
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
HTML custom-element names must contain a hyphen, so prefixless mode uses each
|
|
426
|
-
component's standards-valid standalone name rather than invalid names such as
|
|
427
|
-
`<panel>` or `<listview>`. Prefixes and overrides must be lowercase kebab-case;
|
|
428
|
-
an exact override must itself be a non-reserved custom-element name.
|
|
429
|
-
|
|
430
|
-
Each runtime owns its element-name mapping and structural-style registry.
|
|
431
449
|
Create the runtime once at application startup, then create and mount the root
|
|
432
|
-
through that runtime.
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
Styles still live in the document's global cascade because these hosts are
|
|
436
|
-
deliberately unregistered light-DOM elements, not Web Components or Shadow DOM
|
|
437
|
-
boundaries.
|
|
450
|
+
through that runtime. Styles live in the document's global cascade because the
|
|
451
|
+
hosts are deliberately unregistered light-DOM elements, not Web Components or
|
|
452
|
+
Shadow DOM boundaries.
|
|
438
453
|
|
|
439
454
|
## Reactive templates
|
|
440
455
|
|
|
@@ -449,8 +464,8 @@ canonical vnode operation, but it is not the built-in component authoring
|
|
|
449
464
|
format. The workspace lint gate rejects new `h()` templates under
|
|
450
465
|
`packages/fray/src/Components`.
|
|
451
466
|
|
|
452
|
-
|
|
453
|
-
|
|
467
|
+
Custom hosts are available inside a TSX component through its protected `Host`
|
|
468
|
+
template component:
|
|
454
469
|
|
|
455
470
|
```tsx
|
|
456
471
|
interface BadgeProps extends ComponentProps {
|
|
@@ -466,12 +481,11 @@ class Badge extends Component<BadgeProps> {
|
|
|
466
481
|
}
|
|
467
482
|
|
|
468
483
|
static override hostName = 'badge'
|
|
469
|
-
static override standaloneHostName = 'ui-badge'
|
|
470
484
|
}
|
|
471
485
|
```
|
|
472
486
|
|
|
473
|
-
At runtime that template produces `<fray-badge
|
|
474
|
-
|
|
487
|
+
At runtime that template produces `<fray-badge>`. `hostName` is a stable
|
|
488
|
+
lowercase kebab-case component identity; Fray derives the fixed host spelling.
|
|
475
489
|
Native-root components such as `Button` use their native tag directly in TSX
|
|
476
490
|
instead of `Host`.
|
|
477
491
|
|
|
@@ -481,7 +495,7 @@ Glue values have explicit behavior at each template boundary:
|
|
|
481
495
|
| --- | --- |
|
|
482
496
|
| `{emitter}` | Render the current value and patch only that child range on emission. |
|
|
483
497
|
| `<Child source={emitter} />` | Pass the emitter object unchanged; the child owns how it consumes it. |
|
|
484
|
-
| `prop={live(emitter)}` | Subscribe a
|
|
498
|
+
| `prop={live(emitter)}` | Subscribe a DOM property or a component-declared live prop one way to the emitter's current value. |
|
|
485
499
|
| `<input bind:value={emitter} />` | Bind a writable string emitter and native `value` two ways. |
|
|
486
500
|
| `<input bind:checked={emitter} />` | Bind a writable boolean emitter and native `checked` two ways. |
|
|
487
501
|
| `this.read(emitter)` | Read during `render()` and rerender the component while that dependency is used. |
|
|
@@ -494,6 +508,16 @@ The component still owns and disposes emitters it creates. Direct rendering
|
|
|
494
508
|
uses only an emitter's value; use `snapshot()` when loading and error state
|
|
495
509
|
must affect the markup.
|
|
496
510
|
|
|
511
|
+
Component props do not implicitly unwrap emitters. A raw emitter prop passes
|
|
512
|
+
the emitter object to the component, while `live(emitter)` passes its current
|
|
513
|
+
value and subscribes at the renderer boundary. Every class component has an
|
|
514
|
+
explicit live-prop contract and rejects `live()` outside its allowlist in both
|
|
515
|
+
typed templates and at runtime. `live()` is reserved for small render-time
|
|
516
|
+
state such as availability, validation, and busy/pressed state. Identity,
|
|
517
|
+
callbacks, initial values, structural collections, and emitter ownership are
|
|
518
|
+
ordinary props. A data prop documented as accepting a readable emitter is a
|
|
519
|
+
separate input-source contract, not a `live()` prop.
|
|
520
|
+
|
|
497
521
|
This complete example uses mutable and derived emitters, direct emitter
|
|
498
522
|
children, raw emitter props, native two-way bindings, a one-way live property,
|
|
499
523
|
conditional tracked state, and a `LiveQuery` passed to a child:
|
|
@@ -619,7 +643,7 @@ class ChangeApp extends Component {
|
|
|
619
643
|
static dependencies = [Button, Panel, Preview, Results, Textbox]
|
|
620
644
|
}
|
|
621
645
|
|
|
622
|
-
const runtime = createFrayRuntime(
|
|
646
|
+
const runtime = createFrayRuntime()
|
|
623
647
|
runtime.registerStyles(ChangeApp).injectStyles(document)
|
|
624
648
|
runtime.mount(runtime.create(ChangeApp), document.querySelector('#app')!)
|
|
625
649
|
```
|
|
@@ -690,8 +714,10 @@ or `defaultValue` in new code.
|
|
|
690
714
|
|
|
691
715
|
## Stable component reference
|
|
692
716
|
|
|
693
|
-
All components also accept `children`, `className` (`class` is an alias),
|
|
694
|
-
sibling-local `key` through the common
|
|
717
|
+
All components also accept `children`, `className` (`class` is an alias), an
|
|
718
|
+
`island` surface modifier, and a sibling-local `key` through the common
|
|
719
|
+
component props. The modifier is static presentation input, not a `live()`
|
|
720
|
+
property.
|
|
695
721
|
|
|
696
722
|
| Component | Important props | User callback | State/emitter behavior |
|
|
697
723
|
| --- | --- | --- | --- |
|
|
@@ -699,23 +725,56 @@ sibling-local `key` through the common component props.
|
|
|
699
725
|
| `Toolbar` | `label`, `orientation`, `id`, `children` | None | Stateless named toolbar; orientation is horizontal or vertical. |
|
|
700
726
|
| `Textbox` | `label` or `ariaLabel`, value props, `disabled`, `required`, `readOnly`, `error`, native text constraints, `inputRef` | `onInput(value, event)`, `onChange(value, event)` | String `valueEmitter`; external emitter changes patch the native input without replacing it. |
|
|
701
727
|
| `Dropdown<T>` | `options`, `label` or `ariaLabel`, value props, `disabled`, `required`, `error`, `placeholder`, `name` | `onChange(value, event)` | Typed string/number `valueEmitter`; `options` may be an array or readable emitter. |
|
|
702
|
-
| `RadioButton` | `label`, `name`, `value`, `checked`, `disabled`, `required` | `onChange(checked, event)` | Native radio input with a
|
|
703
|
-
| `RadioGroup<T>` | `options`, `label` or `ariaLabel`, value props, `name`, `disabled`, `required` | `onChange(value, event)` | Native radio inputs with one selected `valueEmitter`
|
|
704
|
-
| `Toggle<T>` | `options`, `label` or `ariaLabel`, value props, `disabled`, `required` | `onChange(value, event)` | One selected value;
|
|
705
|
-
| `Checkbox<T>` | `symbols`, `label`, value props, `disabled`, `required`, `name` | `onChange(value, event)` | Two-state semantic value by default;
|
|
728
|
+
| `RadioButton` | `label`, `name`, `value`, `checked`, `disabled`, `required`, `error` | `onChange(checked, event)` | Native radio input with a labelled fixed shell; `checked`, `disabled`, `required`, and `error` support `live()`. |
|
|
729
|
+
| `RadioGroup<T>` | Plain-array `options`, `label` or `ariaLabel`, value props, `name`, `disabled`, `required`, `error` | `onChange(value, event)` | Native radio inputs with one selected `valueEmitter`; `disabled`, `required`, and `error` support `live()`. |
|
|
730
|
+
| `Toggle<T>` | `options`, `label` or `ariaLabel`, value props, `disabled`, `required`, `error` | `onChange(value, event)` | One selected value; `disabled`, `required`, and `error` support `live()`. |
|
|
731
|
+
| `Checkbox<T>` | `symbols`, `label`, value props, `disabled`, `required`, `error`, `name` | `onChange(value, event)` | Two-state semantic value by default; `disabled`, `required`, and `error` support `live()`. |
|
|
706
732
|
| `TriCheckbox` | Checkbox props except `symbols` | `onChange(value, event)` | Cycles deny → neutral → prefer using `FilterMode`. |
|
|
707
733
|
| `QuadCheckbox` | Checkbox props except `symbols` | `onChange(value, event)` | Cycles deny → neutral → prefer → require using `FilterMode`. |
|
|
708
|
-
| `
|
|
709
|
-
| `
|
|
710
|
-
| `
|
|
734
|
+
| `Header` | `id`, `headingId`, `level`, `children` | None | Styled heading surface using a native `h1`–`h6`; level defaults to `2`. |
|
|
735
|
+
| `GroupPanel` | `header`, `id`, `children` | None | Labelled bordered control group with a full-height vertical Header. |
|
|
736
|
+
| `Panel` | `header`, `toolbar`, `orientation`, `disabled`, `id`, `children` | None | Stateless labelled section that composes Header when header content exists; `disabled` describes the region but does not mutate descendant controls. |
|
|
737
|
+
| `Sidebar` | `header`, `toolbar`, `ariaLabel`, `id`, `children` | None | Fixed `fray-sidebar` host containing a native complementary region with fixed header/toolbar parts and independently scrolling content. |
|
|
738
|
+
| `SplitView` | `primary`, `secondary`, `direction`, `primarySize`, pane labels | None | Stateless, non-resizable two-pane flex layout with explicit overflow ownership and keyboard-focusable panes; `primarySize` is a flex-basis value. |
|
|
711
739
|
| `DescriptionList` / `DescriptionItem` | list `label`; item `term`, `value` or children | None | Native `dl`/`dt`/`dd` record summary with responsive term/value wrapping. |
|
|
712
|
-
| `ProgressBar` | `label`, `value` or `valueEmitter`, `max`, `valueText` | None |
|
|
740
|
+
| `ProgressBar` | `label`, `value` or `valueEmitter`, `max`, `valueText` | None | Native progress semantics with a clipped-label visual surface; a null value is indeterminate. |
|
|
713
741
|
| `ThemePicker` | `label` or `ariaLabel`, theme `options`, value props, `targetDocument`, `disabled` | `onChange(value, option, event)` | String `valueEmitter`; replaces only the theme stylesheet link. |
|
|
714
742
|
| `ColorPicker` | `label` or `ariaLabel`, color `options`, value props, `targetDocument`, `disabled` | `onChange(value, option, event)` | String `valueEmitter`; replaces only the color stylesheet link. |
|
|
715
743
|
| `Tab` | `id`, `label`, `disabled`, optional literal `route`, `children` | None | Declarative content marker consumed by `TabPanel`; a route annotation binds tab activation to the current route scope. |
|
|
716
744
|
| `TabLine` | `tabs`, `label`, `baseId`, value props | `onChange(id, event)` | Active-tab `valueEmitter`; arrow keys skip disabled tabs, with Home/End support. |
|
|
717
745
|
| `TabPanel` | `tabs` or `Tab` children, `label`, `id`, value props | `onChange(id, event)` | Owns or consumes the active-tab emitter, wires the selected tabpanel, and contextually registers annotated tabs when a router is present. |
|
|
718
746
|
|
|
747
|
+
### Live binding and data-source contracts
|
|
748
|
+
|
|
749
|
+
Use `live()` only for the props listed here. All other component props reject
|
|
750
|
+
`live()` in TSX, `h()`, and at runtime. Form-control errors render an alert,
|
|
751
|
+
set invalid state, and associate the control or group with that message.
|
|
752
|
+
|
|
753
|
+
| Component | `live()` props | Dedicated reactive input/state props |
|
|
754
|
+
| --- | --- | --- |
|
|
755
|
+
| `Button` | `disabled`, `pressed`, `busy` | None |
|
|
756
|
+
| `Textbox` | `disabled`, `required`, `readOnly`, `error` | `valueEmitter` |
|
|
757
|
+
| `Dropdown` | `disabled`, `required`, `error` | `options` may be an array or readable emitter; `valueEmitter` |
|
|
758
|
+
| `RadioButton` | `checked`, `disabled`, `required`, `error` | None |
|
|
759
|
+
| `RadioGroup` | `disabled`, `required`, `error` | `valueEmitter`; `options` is always an ordinary array |
|
|
760
|
+
| `Toggle` | `disabled`, `required`, `error` | `valueEmitter`; `options` is always an ordinary array |
|
|
761
|
+
| `Checkbox`, `TriCheckbox`, `QuadCheckbox` | `disabled`, `required`, `error` | `valueEmitter` |
|
|
762
|
+
| `ThemePicker`, `ColorPicker` | `disabled` | `valueEmitter` |
|
|
763
|
+
| `Dialog` | `showCloseButton` | `valueEmitter` controls open state |
|
|
764
|
+
| `Panel` | `disabled` | None |
|
|
765
|
+
| `ProgressBar` | None | `valueEmitter` |
|
|
766
|
+
| `ListView` | None | `items` may be an array or readable emitter; selection emitters are outputs |
|
|
767
|
+
| `TreeView` | None | `nodes` may be an array or readable emitter; selected/expanded emitters are outputs; `itemLabelClassName` and `itemLabelStyle` decorate each block label beside its expander |
|
|
768
|
+
| `DataTable` | None | `data` may be an array or readable emitter; `dataSource`/`rest` and selection emitters are explicit source/state contracts |
|
|
769
|
+
| `FilterPanel` | None | `options` may be an array or readable emitter |
|
|
770
|
+
| Layout, tab, description, toolbar, placeholder, and routing components | None | Their ordinary structural/configuration props require an owner rerender when changed |
|
|
771
|
+
|
|
772
|
+
Readable data sources carry their own fetch state and error through Glue's
|
|
773
|
+
`getFetchState()` and `getError()` APIs. `ListView`, `DataTable`, `FilterPanel`,
|
|
774
|
+
and `TreeView` surface those source failures as data-loading errors. This is
|
|
775
|
+
separate from a control's `error` prop, which represents validation or other
|
|
776
|
+
application-level input feedback.
|
|
777
|
+
|
|
719
778
|
Invalid option arrays, duplicate tab IDs, unsupported orientations, malformed
|
|
720
779
|
emitters, and non-function callbacks fail with descriptive errors.
|
|
721
780
|
|
|
@@ -770,6 +829,80 @@ h(Toggle, {
|
|
|
770
829
|
})
|
|
771
830
|
```
|
|
772
831
|
|
|
832
|
+
### RadioGroup
|
|
833
|
+
|
|
834
|
+
`RadioGroup` deliberately has a narrow reactive contract. Its selectable
|
|
835
|
+
`options` are ordinary structural input, its `valueEmitter` is a stable raw
|
|
836
|
+
writable state channel, and only `disabled`, `required`, and `error` accept one-way
|
|
837
|
+
`live()` bindings.
|
|
838
|
+
|
|
839
|
+
| Prop | Role | `live()` support |
|
|
840
|
+
| --- | --- | --- |
|
|
841
|
+
| `options` | Selectable value/label tuples supplied by the owner | No |
|
|
842
|
+
| `disabled` | Current availability state | Yes, with a readable boolean emitter |
|
|
843
|
+
| `required` | Current form-requirement state | Yes, with a readable boolean emitter |
|
|
844
|
+
| `error` | Current validation message and invalid state | Yes, with a readable error emitter |
|
|
845
|
+
| `valueEmitter` | Stable writable selected-value channel | Pass the emitter raw; do not wrap it |
|
|
846
|
+
| `value`, `defaultValue`, `initialValue` | Initial value when no `valueEmitter` is supplied | No |
|
|
847
|
+
| `id`, `label`, `ariaLabel`, `name` | Ordinary identity and presentation input | No `live()` binding |
|
|
848
|
+
| `onChange` | Action callback | No |
|
|
849
|
+
|
|
850
|
+
Static and live availability state can be combined without changing the option
|
|
851
|
+
contract:
|
|
852
|
+
|
|
853
|
+
```tsx
|
|
854
|
+
const selectedView = new Emitter<'list' | 'grid'>('list')
|
|
855
|
+
const unavailable = new Emitter(false)
|
|
856
|
+
const mustChoose = new Emitter(true)
|
|
857
|
+
|
|
858
|
+
<RadioGroup
|
|
859
|
+
label="View"
|
|
860
|
+
options={[
|
|
861
|
+
['list', 'List'],
|
|
862
|
+
['grid', 'Grid'],
|
|
863
|
+
]}
|
|
864
|
+
valueEmitter={selectedView}
|
|
865
|
+
disabled={live(unavailable)}
|
|
866
|
+
required={live(mustChoose)}
|
|
867
|
+
/>
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
`live(optionsEmitter)` and a raw options emitter are both unsupported. When an
|
|
871
|
+
application genuinely owns a changing option vocabulary, its owning class
|
|
872
|
+
component must make that structural rerender explicit:
|
|
873
|
+
|
|
874
|
+
```tsx
|
|
875
|
+
interface ViewChooserProps extends ComponentProps {
|
|
876
|
+
options: ReadableEmitter<readonly RadioOption[]>
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
class ViewChooser extends Component<ViewChooserProps> {
|
|
880
|
+
render() {
|
|
881
|
+
return <RadioGroup
|
|
882
|
+
label="View"
|
|
883
|
+
options={this.read(this.props.options)}
|
|
884
|
+
/>
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
Calling `optionsEmitter.get()` directly in `render()` only reads a snapshot and
|
|
890
|
+
does not subscribe. `this.read(optionsEmitter)` rerenders the owner when the
|
|
891
|
+
array changes; normal vnode reconciliation then supplies the new ordinary
|
|
892
|
+
array prop to `RadioGroup`. The owner remains responsible for deciding what an
|
|
893
|
+
option removal means for its selected-value emitter. Fray does not silently
|
|
894
|
+
select, clear, or otherwise rewrite that state.
|
|
895
|
+
|
|
896
|
+
`label` is a `FrayChild`, so it may still contain an emitter that is rendered
|
|
897
|
+
and subscribed as child content. That fine-grained child behavior is distinct
|
|
898
|
+
from making the `label` property itself a `live()` binding. The same is true of
|
|
899
|
+
an individual `RadioOption` label: its rendered content may be reactive without
|
|
900
|
+
making the option array or its selectable values live.
|
|
901
|
+
|
|
902
|
+
Because `live()` subscriptions belong to a parent render record, pass live
|
|
903
|
+
props through JSX or `h()`. Direct `new RadioGroup(...)` construction accepts
|
|
904
|
+
resolved booleans and ordinary arrays only.
|
|
905
|
+
|
|
773
906
|
### Checkbox variants
|
|
774
907
|
|
|
775
908
|
```ts
|
|
@@ -781,6 +914,18 @@ h(QuadCheckbox, {label: 'Required tags', defaultValue: FilterMode.Require})
|
|
|
781
914
|
`FilterMode` values are `Deny`, `Neutral`, `Prefer`, and `Require`. The basic
|
|
782
915
|
checkbox uses neutral/prefer, while the variants expose the additional states.
|
|
783
916
|
|
|
917
|
+
### Header
|
|
918
|
+
|
|
919
|
+
```tsx
|
|
920
|
+
<Header id="portfolio-header" headingId="portfolio-title" level={2}>
|
|
921
|
+
Portfolio summary
|
|
922
|
+
</Header>
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
`level` is an integer from `1` through `6` and defaults to `2`. Header children
|
|
926
|
+
must be valid native heading content. The custom host owns presentation while
|
|
927
|
+
the nested native heading owns document semantics.
|
|
928
|
+
|
|
784
929
|
### Panel
|
|
785
930
|
|
|
786
931
|
```ts
|
|
@@ -803,10 +948,10 @@ h(Sidebar, {
|
|
|
803
948
|
}, h('ul', null, h('li', null, 'Release automation')))
|
|
804
949
|
```
|
|
805
950
|
|
|
806
|
-
The surrounding grid or flex layout must bound the Sidebar's height. Its
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
`header`.
|
|
951
|
+
The surrounding grid or flex layout must bound the Sidebar's height. Its native
|
|
952
|
+
complementary region is inside the fixed Sidebar host; Header and toolbar remain
|
|
953
|
+
fixed while the dedicated content part owns vertical scrolling and is keyboard-
|
|
954
|
+
focusable. Supply `ariaLabel` when there is no visible `header`.
|
|
810
955
|
|
|
811
956
|
### Declarative tabs
|
|
812
957
|
|
|
@@ -837,149 +982,79 @@ content and IDs.
|
|
|
837
982
|
|
|
838
983
|
## Styling and accessibility
|
|
839
984
|
|
|
840
|
-
Fray
|
|
841
|
-
|
|
842
|
-
|
|
|
843
|
-
| --- | --- | --- |
|
|
844
|
-
|
|
|
845
|
-
|
|
|
846
|
-
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
and
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
CSS custom properties are Fray's primary theme integration protocol:
|
|
985
|
+
A Fray application loads four ordered styling inputs:
|
|
986
|
+
|
|
987
|
+
| Order | Input | Responsibility |
|
|
988
|
+
| --- | --- | --- |
|
|
989
|
+
| 1 | `themes/base.css` | Default palette anchors, palette-ramp derivation, and semantic custom-property defaults only |
|
|
990
|
+
| 2 | Runtime-collected component CSS | Rules from the `static css` declarations of the component classes the application declares |
|
|
991
|
+
| 3 | `colors/<name>/colors.css` | Palette anchors and endpoints only |
|
|
992
|
+
| 4 | `themes/<name>/theme.css` | Intentional custom-property overrides; `color-scheme` is the only ordinary declaration allowed |
|
|
993
|
+
|
|
994
|
+
Application layout remains in application-owned CSS and is not a fifth Fray
|
|
995
|
+
layer. Named color and theme files never import the base file; load order is
|
|
996
|
+
explicit at the application entry point.
|
|
997
|
+
|
|
998
|
+
Component classes own every selector and every declaration that consumes a
|
|
999
|
+
Fray variable. Shared rules live in meaningful abstract component classes when
|
|
1000
|
+
those components share DOM or behavior. For example, `Dropdown` inherits the
|
|
1001
|
+
labeled-input contract and then the select-shell contract. The collector walks
|
|
1002
|
+
that class chain base-to-derived, resolves each class's own `&` selectors
|
|
1003
|
+
against the concrete host, and then traverses dependencies declared at every
|
|
1004
|
+
level. The older named `baseStyles` recipe system has been removed.
|
|
1005
|
+
|
|
1006
|
+
A component's `static css` should contain the absolute minimum needed for its
|
|
1007
|
+
contract. Every selector, declaration, pseudo-element, and variable must have a
|
|
1008
|
+
specific purpose that cannot be expressed more simply. A base-class edit must
|
|
1009
|
+
be reviewed against all descendants because its CSS is compiled into each
|
|
1010
|
+
concrete descendant that is used.
|
|
1011
|
+
|
|
1012
|
+
The checked-in `styles/structural.css` is a complete generated artifact for
|
|
1013
|
+
non-tree-shaken/diagnostic use. Normal applications should register their root
|
|
1014
|
+
component and inject the resulting usage-specific stylesheet:
|
|
871
1015
|
|
|
872
|
-
```
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
▼
|
|
877
|
-
theme.css
|
|
878
|
-
global UI roles (font, spacing, shape, surface)
|
|
879
|
-
│
|
|
880
|
-
▼
|
|
881
|
-
generic families (header, button, input, panel, selection)
|
|
882
|
-
│
|
|
883
|
-
▼
|
|
884
|
-
optional variants (table header, tab button, toggle button,
|
|
885
|
-
dropdown trigger, dialog header)
|
|
886
|
-
│
|
|
887
|
-
▼
|
|
888
|
-
structural CSS and custom components
|
|
1016
|
+
```ts
|
|
1017
|
+
const runtime = createFrayRuntime()
|
|
1018
|
+
runtime.registerStyles(App).injectStyles(document)
|
|
1019
|
+
runtime.mount(runtime.create(App), root)
|
|
889
1020
|
```
|
|
890
1021
|
|
|
891
|
-
|
|
892
|
-
toward its generic family. A theme can therefore change all header-like or
|
|
893
|
-
button-like elements with a few assignments, then override only the variants
|
|
894
|
-
that should look different. `frayThemeVariableCatalog` exports this contract in
|
|
895
|
-
machine-readable form, including every variable's layer, family, value kind,
|
|
896
|
-
purpose, and optional fallback.
|
|
1022
|
+
### Custom properties
|
|
897
1023
|
|
|
898
|
-
|
|
899
|
-
|
|
1024
|
+
`base.css` derives full primary, secondary, and neutral ramps from each
|
|
1025
|
+
palette's `500` anchor plus light/dark mix endpoints. A color file therefore
|
|
1026
|
+
sets anchors and endpoints, while a theme maps or overrides semantic families
|
|
1027
|
+
such as `--button-*`, `--input-*`, `--panel-*`, and `--selection-*`.
|
|
1028
|
+
`frayThemeVariableCatalog` exposes the supported vocabulary.
|
|
900
1029
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
1030
|
+
`--application-background` owns the canvas behind an axis-filling application
|
|
1031
|
+
root and its islands. Its base value is the white palette endpoint, which
|
|
1032
|
+
Shiny and Minimal both retain.
|
|
1033
|
+
|
|
1034
|
+
The explicit `island` modifier consumes `--island-margin`,
|
|
1035
|
+
`--island-padding`, `--island-background`, `--island-border`,
|
|
1036
|
+
`--island-radius`, and `--island-shadow`. Base and Minimal keep its layout and
|
|
1037
|
+
elevation neutral; Shiny uses it for Bank2-style separated surfaces. Themes do
|
|
1038
|
+
not infer island boundaries from component type or nesting, and islands cannot
|
|
1039
|
+
contain other islands.
|
|
907
1040
|
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
`toolbarlike`; the panel and toolbar families also expose
|
|
913
|
-
`panelshell`/`panelinner` and `toolbarshell`/`toolbarinner`. Wrappers are not
|
|
914
|
-
introduced solely to carry a split trait.
|
|
915
|
-
|
|
916
|
-
Themes directly target native elements, public traits, native pseudo-parts,
|
|
917
|
-
and native/ARIA state. They never target `data-fray-component` or `data-part`.
|
|
918
|
-
Every theme seeds inherited variables with a zero-specificity
|
|
919
|
-
`:root`/`[data-theme]` boundary rule. Presentation selectors use `@scope` with
|
|
920
|
-
nested theme roots and `[data-theme-exclude]` limits, a named cascade layer,
|
|
921
|
-
and low-specificity `:where()` selectors. Shiny's highlights and
|
|
922
|
-
select/progress decoration follow that contract and yield to native
|
|
923
|
-
representation under forced colors.
|
|
924
|
-
|
|
925
|
-
`coloredlike` defaults to the primary palette. Components can provide
|
|
926
|
-
`--colored-base`, `--colored-light`, `--colored-dark`, and
|
|
927
|
-
`--colored-contrast`; the active theme decides whether those inputs become a
|
|
928
|
-
flat color, gradient, other polish, or no special treatment.
|
|
1041
|
+
The reusable `colored` trait consumes a required `--c1`/`--c2`/`--c3`
|
|
1042
|
+
dark/base/light triplet and `--colored-shadow`. Its base treatment is the
|
|
1043
|
+
shared three-stop gradient; Shiny adds depth without changing the
|
|
1044
|
+
application-owned triplet.
|
|
929
1045
|
|
|
930
1046
|
### Runtime selection
|
|
931
1047
|
|
|
932
1048
|
`replaceFrayStylesheet` maintains one
|
|
933
1049
|
`link[data-fray-stylesheet="theme"]` and one
|
|
934
1050
|
`link[data-fray-stylesheet="colors"]`. Replacing either link also sets the
|
|
935
|
-
corresponding
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
The default option catalogs resolve URLs against Fray's published package
|
|
940
|
-
layout for direct ESM/CDN use. A bundled application should ask its bundler to
|
|
941
|
-
emit each selectable CSS file as an asset and supply those resulting URLs:
|
|
942
|
-
|
|
943
|
-
```tsx
|
|
944
|
-
import {ColorPicker, Component, ThemePicker} from '@sylwellsoftware/fray'
|
|
945
|
-
import iceblueHref from '@sylwellsoftware/fray/colors/iceblue/colors.css?url'
|
|
946
|
-
import purpleHref from '@sylwellsoftware/fray/colors/purple/colors.css?url'
|
|
947
|
-
import minimalHref from '@sylwellsoftware/fray/themes/minimal/theme.css?url'
|
|
948
|
-
import shinyHref from '@sylwellsoftware/fray/themes/shiny/theme.css?url'
|
|
949
|
-
|
|
950
|
-
const themes = [
|
|
951
|
-
{value: 'shiny', label: 'Shiny', href: shinyHref},
|
|
952
|
-
{value: 'minimal', label: 'Minimal', href: minimalHref},
|
|
953
|
-
]
|
|
954
|
-
const colors = [
|
|
955
|
-
{value: 'iceblue', label: 'Ice blue', href: iceblueHref},
|
|
956
|
-
{value: 'purple', label: 'Purple', href: purpleHref},
|
|
957
|
-
]
|
|
958
|
-
|
|
959
|
-
class AppearanceControls extends Component {
|
|
960
|
-
render() {
|
|
961
|
-
return <aside aria-label="Appearance">
|
|
962
|
-
<ThemePicker label="Theme" options={themes} defaultValue="shiny" />
|
|
963
|
-
<ColorPicker label="Colors" options={colors} defaultValue="iceblue" />
|
|
964
|
-
</aside>
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
static dependencies = [ColorPicker, ThemePicker]
|
|
968
|
-
}
|
|
969
|
-
```
|
|
970
|
-
|
|
971
|
-
The `?url` syntax above is supported by Vite; use the equivalent emitted-asset
|
|
972
|
-
mechanism for another bundler. The structural stylesheet is not replaced.
|
|
973
|
-
|
|
974
|
-
See [`themes/README.md`](themes/README.md) for the complete architecture and
|
|
975
|
-
variable families, and [`colors/README.md`](colors/README.md) for the palette
|
|
976
|
-
contract.
|
|
1051
|
+
corresponding root data attribute. `ThemePicker` and `ColorPicker` expose the
|
|
1052
|
+
same operation through the normal value-control contract. The base file and
|
|
1053
|
+
runtime-injected structural stylesheet stay loaded while those two links are
|
|
1054
|
+
replaced.
|
|
977
1055
|
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
operation, labelled roles, reduced motion, 200% configured text sizing, and
|
|
981
|
-
forced-colors focus visibility. A formal manual screen-reader pass is still a
|
|
982
|
-
release-candidate requirement; automated checks are not a substitute for it.
|
|
1056
|
+
See [the theme contract](themes/README.md) and
|
|
1057
|
+
[the palette contract](colors/README.md) for authoring details.
|
|
983
1058
|
|
|
984
1059
|
## Data workflows
|
|
985
1060
|
|
|
@@ -1043,8 +1118,8 @@ For a tree derived from domain state, a callback updates that real source; the
|
|
|
1043
1118
|
tree derivation then rebuilds and `deriveTreeNode` resolves the fresh node.
|
|
1044
1119
|
`updateTreeNode` is the equivalent pure path-copy operation.
|
|
1045
1120
|
|
|
1046
|
-
Generic `FilterState` keeps `neutral`, `prefer`, `require`, and `deny`
|
|
1047
|
-
values separate from glyphs and transport. `filterByState`,
|
|
1121
|
+
Generic `FilterState` keeps `neutral`, `prefer`, `require`, and `deny`
|
|
1122
|
+
semantic values separate from glyphs and transport. `filterByState`,
|
|
1048
1123
|
`deriveFilterPredicate`, and `deriveFilteredItems` consume caller-supplied
|
|
1049
1124
|
dimension matchers. `serializeFilterState`/`parseFilterState` round-trip
|
|
1050
1125
|
validated version-1 plain data without owning URL or storage access. Unknown
|
|
@@ -1063,5 +1138,5 @@ React/Vue adapters, a broad design system, a virtualized production data grid,
|
|
|
1063
1138
|
legacy-browser support, or a stable `1.0` API. See the [workspace
|
|
1064
1139
|
overview](../../README.md), [API surface](../../docs/API_SURFACE.md),
|
|
1065
1140
|
[architecture overview](../../docs/architecture.md),
|
|
1066
|
-
[changelog](
|
|
1141
|
+
[changelog](CHANGELOG.md), [contribution guide](../../CONTRIBUTING.md),
|
|
1067
1142
|
and [security policy](../../SECURITY.md).
|