@capillaryjs/capillary-ui 1.2.0 → 1.4.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.
Files changed (92) hide show
  1. package/CHANGELOG.md +152 -16
  2. package/README.md +177 -35
  3. package/colors/README.md +53 -3
  4. package/colors/gray/colors.css +4 -2
  5. package/colors/green/colors.css +4 -2
  6. package/colors/iceblue/colors.css +4 -2
  7. package/colors/ocean/colors.css +4 -2
  8. package/colors/orange/colors.css +4 -2
  9. package/colors/purple/colors.css +4 -2
  10. package/colors/red/colors.css +7 -3
  11. package/colors/yellow/colors.css +8 -4
  12. package/dist/Components/app/app.d.ts +3 -3
  13. package/dist/Components/app/app.d.ts.map +1 -1
  14. package/dist/Components/component.d.ts +13 -2
  15. package/dist/Components/component.d.ts.map +1 -1
  16. package/dist/Components/data/descriptionList.d.ts.map +1 -1
  17. package/dist/Components/data/listview/listview.d.ts.map +1 -1
  18. package/dist/Components/data/table/DataTable.d.ts +8 -0
  19. package/dist/Components/data/table/DataTable.d.ts.map +1 -1
  20. package/dist/Components/data/table/FilterPanel.d.ts.map +1 -1
  21. package/dist/Components/data/table/TableHeader.d.ts +2 -0
  22. package/dist/Components/data/table/TableHeader.d.ts.map +1 -1
  23. package/dist/Components/data/table/TableHeaderCell.d.ts +17 -0
  24. package/dist/Components/data/table/TableHeaderCell.d.ts.map +1 -1
  25. package/dist/Components/data/table/tableDataSource.d.ts +6 -0
  26. package/dist/Components/data/table/tableDataSource.d.ts.map +1 -1
  27. package/dist/Components/data/treeview/treeview.d.ts.map +1 -1
  28. package/dist/Components/layout/groupBox.d.ts.map +1 -1
  29. package/dist/Components/layout/header.d.ts.map +1 -1
  30. package/dist/Components/layout/layout.d.ts +2 -2
  31. package/dist/Components/layout/layout.d.ts.map +1 -1
  32. package/dist/Components/layout/layoutTraits.d.ts +6 -0
  33. package/dist/Components/layout/layoutTraits.d.ts.map +1 -1
  34. package/dist/Components/layout/optionGroup.d.ts.map +1 -1
  35. package/dist/Components/layout/optionsBox.d.ts +2 -1
  36. package/dist/Components/layout/optionsBox.d.ts.map +1 -1
  37. package/dist/Components/layout/splitView.d.ts +2 -0
  38. package/dist/Components/layout/splitView.d.ts.map +1 -1
  39. package/dist/Components/lineinputs/CheckableControl.d.ts +2 -2
  40. package/dist/Components/lineinputs/CheckableControl.d.ts.map +1 -1
  41. package/dist/Components/lineinputs/LabeledInputControl.d.ts +2 -2
  42. package/dist/Components/lineinputs/LabeledInputControl.d.ts.map +1 -1
  43. package/dist/Components/lineinputs/LineControl.d.ts +7 -0
  44. package/dist/Components/lineinputs/LineControl.d.ts.map +1 -0
  45. package/dist/Components/lineinputs/SelectControl.d.ts.map +1 -1
  46. package/dist/Components/lineinputs/checkbox/Checkbox.d.ts +9 -3
  47. package/dist/Components/lineinputs/checkbox/Checkbox.d.ts.map +1 -1
  48. package/dist/Components/lineinputs/checkbox/QuadCheckbox.d.ts +1 -0
  49. package/dist/Components/lineinputs/checkbox/QuadCheckbox.d.ts.map +1 -1
  50. package/dist/Components/lineinputs/checkbox/TriCheckbox.d.ts +1 -0
  51. package/dist/Components/lineinputs/checkbox/TriCheckbox.d.ts.map +1 -1
  52. package/dist/Components/lineinputs/datetime/TemporalInput.d.ts.map +1 -1
  53. package/dist/Components/lineinputs/dropdown.d.ts +4 -2
  54. package/dist/Components/lineinputs/dropdown.d.ts.map +1 -1
  55. package/dist/Components/lineinputs/radio.d.ts +9 -4
  56. package/dist/Components/lineinputs/radio.d.ts.map +1 -1
  57. package/dist/Components/lineinputs/requiredPresentation.d.ts +21 -0
  58. package/dist/Components/lineinputs/requiredPresentation.d.ts.map +1 -0
  59. package/dist/Components/lineinputs/textbox.d.ts.map +1 -1
  60. package/dist/Components/lineinputs/toggle.d.ts +4 -2
  61. package/dist/Components/lineinputs/toggle.d.ts.map +1 -1
  62. package/dist/Components/menu/button.d.ts +2 -2
  63. package/dist/Components/menu/button.d.ts.map +1 -1
  64. package/dist/Components/status/progressBar.d.ts +2 -2
  65. package/dist/Components/status/progressBar.d.ts.map +1 -1
  66. package/dist/Components/status/statusPresentation.d.ts.map +1 -1
  67. package/dist/Components/theme/stylesheetPicker.d.ts +2 -1
  68. package/dist/Components/theme/stylesheetPicker.d.ts.map +1 -1
  69. package/dist/index.d.ts +1 -1
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +2750 -2084
  72. package/dist/index.js.map +1 -1
  73. package/dist/jsx-dev-runtime.js +1 -1
  74. package/dist/{jsx-runtime-BD_q3rPg.js → jsx-runtime-Ba-uafjl.js} +595 -475
  75. package/dist/jsx-runtime-Ba-uafjl.js.map +1 -0
  76. package/dist/jsx-runtime.js +1 -1
  77. package/dist/localization.d.ts +1 -1
  78. package/dist/localization.d.ts.map +1 -1
  79. package/dist/routing/RouteOutlet.d.ts.map +1 -1
  80. package/dist/styling/theme.d.ts +1 -1
  81. package/dist/styling/theme.d.ts.map +1 -1
  82. package/docs/application-composition-guide.md +22 -5
  83. package/package.json +3 -3
  84. package/styles/structural.css +756 -161
  85. package/themes/README.md +9 -2
  86. package/themes/base.css +114 -18
  87. package/themes/capillary/theme.css +300 -0
  88. package/themes/shiny/theme.css +35 -21
  89. package/themes/soft/theme.css +37 -0
  90. package/themes/white/theme.css +52 -0
  91. package/dist/jsx-runtime-BD_q3rPg.js.map +0 -1
  92. package/themes/java/theme.css +0 -21
package/CHANGELOG.md CHANGED
@@ -6,8 +6,115 @@ Versioning.
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 1.4.0 - 2026-09-19
10
+
11
+ ### Fixed
12
+
13
+ - Keep DataTable header borders and column dividers visible while rows scroll,
14
+ and use the theme's table-header background and text tokens consistently.
15
+ - Show both sort directions on unsorted table columns and render a
16
+ font-independent filter funnel that remains legible at compact header sizes.
17
+ - Apply the Capillary theme's alternating table-row colors through the tokens
18
+ consumed by table rows.
19
+
20
+ ### Changed
21
+
22
+ - Correct the changelog's package/version boundaries: distinguish historical
23
+ Fray releases and identify notes already shipped in Capillary UI 1.3.0.
24
+
25
+ ## 1.3.0 - 2026-09-18
26
+
27
+ Historical correction: the following notes were shipped under `Unreleased` in
28
+ the published 1.3.0 package. They include changes introduced in earlier
29
+ Capillary UI releases; they are retained here as the shipped record, not as
30
+ new changes in the next release.
31
+
32
+ ### Fixed
33
+
34
+ - Set the default GroupBox content gap independently from island spacing, using
35
+ a compact 2px rhythm for controls inside a GroupBox.
36
+
37
+ - Render unfilled required controls with a transparent negative ✲ marker:
38
+ inside native input surfaces and beside a pseudo-element checkable outline
39
+ extension that does not change the label's layout width.
40
+ - Place checkbox and radio validation halos around the complete labelled
41
+ choice while retaining the error border on the painted shell.
42
+ - Keep busy buttons visually normal and layer only the working animation over
43
+ their ordinary chrome. Gate ProgressBar's busy animation on an emitter-backed
44
+ `FetchState.Loading` state, applying the working texture to both determinate
45
+ and indeterminate bars so literal indeterminate values remain static.
46
+ - Make ProgressBar fill its available inline width while remaining shrinkable
47
+ inside constrained layouts.
48
+ - Omit island content padding on direct app-root headers, letting their navbar
49
+ and toolbar own spacing. Preserve nested headers, other islands, outer
50
+ gutters, borders, and shadows across themes and managed/legacy layouts.
51
+ - Keep island borders and shadows visible through nested scrollports by using
52
+ existing gutter space inside the clipping boundary. Preserve surface
53
+ alignment, shared gaps, and split resizing without theme clearance tokens.
54
+ Add browser pixel checks for painted borders/shadows and clipping regressions.
55
+ - Publish only the maintained Capillary, Shiny, Soft, White, and Minimal themes.
56
+ The former Glossy, Original, Java, Dark, and Sci-fi assets and picker entries
57
+ are removed from the package and applications.
58
+ - Make named-theme surfaces, text, borders, and fills palette-derived. In
59
+ particular, Shiny progress chrome now follows the muted surface and primary
60
+ ramps instead of fixed Ice blue stops. Neutral black/white gloss and shadow
61
+ overlays remain intentionally literal.
62
+ - Replace hue-named palette status primitives with semantic negative, positive,
63
+ and neutral status colors. Error and success aliases follow the negative and
64
+ positive colors; multi-state checkbox deny, require, and prefer chrome uses
65
+ negative, positive, and neutral respectively.
66
+ - Derive shared chrome backgrounds from muted primary palette tones, so panels,
67
+ table headers, button gradients, and disabled surfaces follow palette changes.
68
+ Ice blue preserves exactly `#f5f7f8`, `#ebeff3`, and `#d7dee3`. The derivation
69
+ uses relative HSL colors and sRGB mixing; existing UI aliases remain overridable.
70
+ - Add palette-level `--palette-primary-surface-saturation`: `0` removes primary
71
+ hue from the three chrome surfaces, `1` retains their calibrated default.
72
+ - Keep unchanged DataTable headers out of row-update renders and avoid redundant
73
+ renderer marker, attribute, dataset, and style writes.
74
+ - Preserve table-part names after minification and identify header cells by column.
75
+ - Preserve table/header/body identities when loading/error messages or captions change.
76
+
77
+ - `DataTable` filterable columns no longer keep a permanent document-level
78
+ click listener: the outside-click listener that closes the filter panel is
79
+ now attached only while the panel is open. Previously every click in the
80
+ document fired it, which also flooded event traces with a
81
+ "Table header cell: click" interaction per click. The listener uses the new
82
+ `Component.listenWhile`, so it stays on the normal traced native-event path
83
+ and coalesces with other listeners observing the same click.
84
+
9
85
  ### Added
10
86
 
87
+ - Add a live `readOnly` state to Dropdown, checkbox variants, RadioButton,
88
+ RadioGroup, Toggle, and stylesheet pickers. Read-only value controls stay
89
+ focusable and readable, expose native `readonly` or `aria-readonly` as
90
+ appropriate, and reject attempted user changes without becoming disabled.
91
+ - Add palette-derived base read-only chrome tokens for inputs, dropdowns,
92
+ checkables, and toggles. Themes can override the tokens but no longer need to
93
+ reproduce disabled styling to represent a fixed value.
94
+ - Add inherited `islands` layout mode to `CapillaryUiApp` and `Layout`, with
95
+ native `cap-island-layout` / `cap-island-layout-off` traits. One outer inset
96
+ and shared gutters replace accumulated island margins inside opt-in scopes;
97
+ ordinary island content and legacy layouts keep their existing spacing.
98
+ The `--island-gap` and `--island-inset` tokens support theme/application
99
+ overrides, RouteOutlet carries the mode, and SplitView puts its accessible
100
+ separator in one gutter, including zero-gap and RTL resizing.
101
+ - Add the light `capillary` theme: bright rounded cards, restrained neutral
102
+ elevation, compact controls, and palette-primary selected states.
103
+ - Add an overridable instance diagnostic label and a named table-body consumer.
104
+ Consumer facts report render triggers/passes and own renderer DOM-write counts,
105
+ distinguishing execution from DOM work without changing update scheduling.
106
+ - Add `Component.listenWhile(target, type, listener, options)`: like
107
+ `listen`, but returns a function that detaches the listener, for listeners
108
+ that are only active some of the time. It runs through the same traced
109
+ native-event coalescing as `listen` and is removed on destroy if still
110
+ attached.
111
+ - Add a `filterable` column flag to `DataTable`: a column marked
112
+ `filterable` derives its header filter options from the field's distinct
113
+ values across the source's unfiltered rows, so options stay stable while
114
+ a filter is applied. Explicit `filterOptions` still take precedence.
115
+ Local and caller-query data sources expose their pre-filter rows through
116
+ the new `TableDataSource.sourceRows`; remote sources fall back to the
117
+ displayed rows.
11
118
  - Add diagnostic ownership scopes and stable diagnostic labels, automatic
12
119
  native interaction roots, component/read/watch and function-consumer facts,
13
120
  and reactive child, live-property, and native-binding endpoints. Synchronous
@@ -16,6 +123,29 @@ Versioning.
16
123
 
17
124
  ### Changed
18
125
 
126
+ - Use flexbox for single-axis component stacks and simple centered chrome,
127
+ retaining grid only where its track model is needed. This does not change the
128
+ public DOM or layout contract.
129
+ - Give all single-line controls the same minimum row footprint with symmetric
130
+ breathing room, including checkbox/radio variants, temporal inputs, and
131
+ ProgressBar. Add `--control-row-min-height` and `--control-row-padding-block`
132
+ without changing painted control shapes or Capillary's `6px 5px` padding.
133
+ Only OptionsBox retains compact rows and owns a fixed 2px vertical option
134
+ rhythm across themes; RadioGroup no longer adds a default gap on top of the
135
+ common row spacing elsewhere.
136
+ - Empty required text, temporal, and dropdown controls now have a subtle
137
+ themeable dashed required-value outline. The same outline appears on an
138
+ unchecked two-state `Checkbox`; tri- and quad-state checkboxes intentionally
139
+ do not use native required validity because no single semantic state is the
140
+ required value.
141
+ - Retained `Loading` results in `DataTable`, `ListView`, and `TreeView` now
142
+ show a non-blocking translucent animated working texture while their existing
143
+ rows remain visible.
144
+
145
+ ## 1.2.0 - 2026-09-15
146
+
147
+ ### Changed
148
+
19
149
  - Make DataTable, ListView, and TreeView shrink and own overflow before a
20
150
  bounded flex layout container must scroll. DataTable header cells now remain
21
151
  sticky within the table's scrollport.
@@ -42,8 +172,8 @@ Versioning.
42
172
  `cap-header:has(+ cap-toolbar)` shadow override together with its
43
173
  `--section-header-with-toolbar-shadow` variable.
44
174
  - Normalize line-control sizing and text centering in structural CSS: 24px
45
- default textbox, dropdown, toggle, and button bodies at 12px UI text, with
46
- compact checkbox/radio rows. Remove Shiny's label/select text offsets and
175
+ default textbox, dropdown, toggle, and button bodies at 12px UI text.
176
+ Remove Shiny's label/select text offsets and
47
177
  prevent toggle border/selection changes from shifting segment text.
48
178
  - Refine Shiny's input depth, toolbar/table-header chrome, navigation-to-toolbar
49
179
  seam, and loading BlockGraph treatment without moving structural layout
@@ -89,7 +219,13 @@ Versioning.
89
219
  CSS/data namespaces to the `cap-*` prefix. Existing released entries below
90
220
  retain their historical names.
91
221
 
92
- ## 1.4.0 - 2026-09-13
222
+ ## Historical Fray releases
223
+
224
+ The entries below describe released `@sylwellsoftware/fray` versions before the
225
+ package was renamed to `@capillaryjs/capillary-ui`. They are retained as
226
+ historical release notes and are not Capillary UI release versions.
227
+
228
+ ## Fray 1.4.0 - 2026-09-13
93
229
 
94
230
  ### Added
95
231
 
@@ -235,7 +371,7 @@ Versioning.
235
371
  `RadioGroup`, `DatePicker`, `DateTimePicker`, and `TimePicker`, which were
236
372
  missing from the stylesheet manifest.
237
373
 
238
- ## 1.3.1 - 2026-09-08
374
+ ## Fray 1.3.1 - 2026-09-08
239
375
 
240
376
  ### Fixed
241
377
 
@@ -245,7 +381,7 @@ Versioning.
245
381
  - Published npm packages now include this changelog alongside their release
246
382
  history.
247
383
 
248
- ## 1.3.0 - 2026-09-08
384
+ ## Fray 1.3.0 - 2026-09-08
249
385
 
250
386
  ### Added
251
387
 
@@ -269,7 +405,7 @@ Versioning.
269
405
  - Shiny now presents `NavigationBar` as a light, text-link navigation strip
270
406
  beneath application chrome instead of a dark action-bar surface.
271
407
 
272
- ## 1.2.0 - 2026-09-08
408
+ ## Fray 1.2.0 - 2026-09-08
273
409
 
274
410
  ### Added
275
411
 
@@ -307,7 +443,7 @@ Versioning.
307
443
  the published text color as well as typography.
308
444
  - RadioGroup basic rendering adjusted
309
445
 
310
- ## 1.1.1 - 2026-09-07
446
+ ## Fray 1.1.1 - 2026-09-07
311
447
 
312
448
  ### Changed
313
449
 
@@ -318,7 +454,7 @@ Versioning.
318
454
  semantics, meaningful HTML/CSS separation, and the complementary intent
319
455
  behind the Glue and Fray names.
320
456
 
321
- ## 1.1.0 - 2026-09-07
457
+ ## Fray 1.1.0 - 2026-09-07
322
458
 
323
459
  ### Added
324
460
 
@@ -351,7 +487,7 @@ Versioning.
351
487
  `--font-size`, and `--line-height` tokens, preventing inherited controls and
352
488
  native content from falling back to the browser's serif defaults.
353
489
 
354
- ## 1.0.0 - 2026-09-06
490
+ ## Fray 1.0.0 - 2026-09-06
355
491
 
356
492
  ### Added
357
493
 
@@ -433,7 +569,7 @@ Versioning.
433
569
  - Multi-state Checkbox variants synchronize the native checked property after
434
570
  every semantic transition.
435
571
 
436
- ## 0.7.0 - 2026-09-04
572
+ ## Fray 0.7.0 - 2026-09-04
437
573
 
438
574
  ### Added
439
575
 
@@ -449,7 +585,7 @@ Versioning.
449
585
  inherit their mounted route lineage without changing Glue or unrouted
450
586
  component behavior.
451
587
 
452
- ## 0.6.0 - 2026-09-04
588
+ ## Fray 0.6.0 - 2026-09-04
453
589
 
454
590
  ### Added
455
591
 
@@ -467,7 +603,7 @@ Versioning.
467
603
  - Theme/color root attributes and public custom properties are prefix-free;
468
604
  see the semantic-markup and theming migration guidance.
469
605
 
470
- ## 0.5.0 - 2026-09-03
606
+ ## Fray 0.5.0 - 2026-09-03
471
607
 
472
608
  ### Changed
473
609
 
@@ -475,7 +611,7 @@ Versioning.
475
611
  the checked state, so native checkbox interactions remain correct for
476
612
  bindings such as `visible`/`hidden`.
477
613
 
478
- ## 0.4.0 - 2026-09-03
614
+ ## Fray 0.4.0 - 2026-09-03
479
615
 
480
616
  ### Added
481
617
 
@@ -487,7 +623,7 @@ Versioning.
487
623
  - Application services are registered at the composition root and inherited by
488
624
  nested class components without service prop-drilling.
489
625
 
490
- ## 0.3.0 - 2026-09-03
626
+ ## Fray 0.3.0 - 2026-09-03
491
627
 
492
628
  ### Added
493
629
 
@@ -507,7 +643,7 @@ Versioning.
507
643
  - `DataTable` uses one of `data`, `dataSource`, or `rest` instead of legacy
508
644
  query/REST props.
509
645
 
510
- ## 0.2.0 - 2026-09-03
646
+ ## Fray 0.2.0 - 2026-09-03
511
647
 
512
648
  ### Added
513
649
 
@@ -520,7 +656,7 @@ Versioning.
520
656
  colors, and published package checks validate CSS subpaths.
521
657
  - Fray's Glue peer range follows the compatible Glue `0.2.x` line.
522
658
 
523
- ## 0.1.0-alpha.1 - 2026-09-02
659
+ ## Fray 0.1.0-alpha.1 - 2026-09-02
524
660
 
525
661
  ### Added
526
662
 
package/README.md CHANGED
@@ -149,6 +149,14 @@ A class component has explicit phases:
149
149
  `onCleanup()` registers listeners or other cleanup functions that Capillary UI invokes
150
150
  on destruction.
151
151
 
152
+ Diagnostic labels default to static `diagnosticLabel`, `hostName`, then the class
153
+ name. Override the instance `diagnosticLabel` getter when instances need distinct
154
+ names; identity labels are captured at first observation. Table diagnostics expose
155
+ `Table header`, column-specific header cells, and `Table body`. Unchanged header
156
+ props skip parent-driven rendering, while sort/filter subscriptions remain active.
157
+ Consumer events distinguish render passes from own renderer DOM writes; see the
158
+ [diagnostics guide](../../docs/diagnostics.md) for the exact counting boundary.
159
+
152
160
  ```tsx
153
161
  class Counter extends Component {
154
162
  readonly count = new Emitter(0)
@@ -197,7 +205,7 @@ class Badge extends Component<BadgeProps> {
197
205
  static override hostName = 'badge'
198
206
  static override css = css`
199
207
  & { display: inline-flex; }
200
- &[data-tone="positive"] { color: var(--palette-green); }
208
+ &[data-tone="positive"] { color: var(--palette-status-positive); }
201
209
  `
202
210
  }
203
211
  ```
@@ -313,11 +321,11 @@ the tables below denotes that TypeScript type parameter.
313
321
  | `Toolbar` | Named action group | `label`, `orientation` |
314
322
  | `Label` | Native label for rich or live text | `text`, `htmlFor`; live: `text` |
315
323
  | `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` |
316
- | `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 |
317
- | `RadioButton` | Standalone native radio and label | `label`, `name`, `value`, `checked`, `disabled`, `required`, `busy`, `error`, `onChange`; live: state, availability, `busy`, `error` |
318
- | `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 |
319
- | `Toggle<T>` | ARIA radio group rendered as toggle buttons | `options` as `[value, label]` tuples, `label`, `valueEmitter`, `defaultValue`, `disabled`, `required`, `busy`, `error`, `onChange` |
320
- | `Checkbox<T>` | Configurable keyboard-operable semantic state cycle | `symbols` as `[content, value]` tuples, `label`/`ariaLabel`, `valueEmitter`, `defaultValue`, `disabled`, `required`, `busy`, `error`, `onChange` |
324
+ | `Dropdown<T>` | Labelled native select | `options`, `label`, `valueEmitter`, `defaultValue`, `placeholder`, `disabled`, `required`, `readOnly`, `busy`, `error`, `onChange`; `readOnly` retains focusability and restores the selected value after attempted changes; `options` may be static or a readable emitter whose fetch state supplies loading/error feedback |
325
+ | `RadioButton` | Standalone native radio and label | `label`, `name`, `value`, `checked`, `disabled`, `required`, `readOnly`, `busy`, `error`, `onChange`; live: state, availability, `busy`, `error` |
326
+ | `RadioGroup<T>` | Named native-radio fieldset owning one value | `options` as `[value, label]` tuples, `label`, `valueEmitter`, `defaultValue`, `disabled`, `required`, `readOnly`, `busy`, `error`, `onChange`; options are ordinary render data |
327
+ | `Toggle<T>` | ARIA radio group rendered as toggle buttons | `options` as `[value, label]` tuples, `label`, `valueEmitter`, `defaultValue`, `disabled`, `required`, `readOnly`, `busy`, `error`, `onChange` |
328
+ | `Checkbox<T>` | Configurable keyboard-operable semantic state cycle | `symbols` as `[content, value]` tuples, `label`/`ariaLabel`, `valueEmitter`, `defaultValue`, `disabled`, `required`, `readOnly`, `busy`, `error`, `onChange` |
321
329
  | `TriCheckbox` | Neutral/prefer/deny `FilterMode` cycle | Same public props as `Checkbox`, except fixed symbols |
322
330
  | `QuadCheckbox` | Neutral/prefer/require/deny `FilterMode` cycle | Same public props as `Checkbox`, except fixed symbols |
323
331
  | `DatePicker` (experimental) | Native `<input type="date">` | `CivilDate` value props, `label`/`ariaLabel`, `name`, `autoComplete`, `disabled`, `required`, `readOnly`, `busy`, `error`, `min`/`max`/`step`, `inputRef`, input/change callbacks |
@@ -330,7 +338,12 @@ the native forward cycle.
330
338
 
331
339
  `busy` is presentational state: it sets native/ARIA busy semantics and paints
332
340
  the theme's moving working texture without disabling an input or choice.
333
- `Button` remains the exception: a busy action is unavailable until it settles.
341
+ `Button` remains the exception: a busy action is unavailable until it settles,
342
+ but keeps its normal button colors, border, and elevation with the working
343
+ texture layered on top. `ProgressBar` only paints that working texture when it
344
+ has a `valueEmitter` whose fetch state is `Loading`; this applies to both
345
+ determinate and indeterminate bars. A literal `value={null}` is simply an
346
+ unfilled indeterminate bar.
334
347
  When `error` is also present, error presentation wins over the animation.
335
348
  Every error-bearing control describes its native surface with a focusable
336
349
  `role="alert"` overlay. Its icon and initially hidden message are absolutely
@@ -355,12 +368,12 @@ const view = new Emitter<'list' | 'grid'>('list')
355
368
 
356
369
  | Component | Purpose | Key props and state |
357
370
  | --- | --- | --- |
358
- | `CapillaryUiApp` | Fixed `cap-app` application shell and theme-text boundary | `sizing`: `embedded`/viewport axes; `layout`: `horizontal`/`vertical`; `landmark`: `main`/`none`; content or overridden `renderContent()` |
371
+ | `CapillaryUiApp` | Fixed `cap-app` application shell and theme-text boundary | `sizing`: `embedded`/viewport axes; `layout`: `horizontal`/`vertical`; `islands`: inherited surface spacing; `landmark`: `main`/`none`; content or overridden `renderContent()` |
359
372
  | `Header` | Styled native heading surface | `level` (1–6), `headingId`, content |
360
- | `GroupBox` | Labelled group; defaults to a bordered vertical-header group and may be a section or column | required `header`, content; optional `variant`: `section` or `column` |
361
- | `OptionGroup` | Labelled native fieldset for related controls | `label`/`ariaLabel`, `OptionGroupHeaderEnd` and ordinary content children, `disabled`, `required`, `busy`, `error`; state props are live |
362
- | `OptionsBox` | GroupBox specialization arranging option groups | required `header`, `OptionGroup` content |
363
- | `Layout` | Presentation-only arrangement of arbitrary children | exactly one of `horizontal`/`vertical`; `allocation`, `scroll`, optional accessible-region configuration |
373
+ | `GroupBox` | Labelled group; defaults to a bordered vertical-header group and may be a section or column | required `header`, direct content arranged vertically (`section` arranges direct content horizontally); optional `variant`: `section` or `column` |
374
+ | `OptionGroup` | Labelled native fieldset for related controls | `label`/`ariaLabel`, `OptionGroupHeaderEnd` and ordinary content children, `disabled`, `required`, `busy`, `error`; state props are live. Use for related direct checkboxes or multiple distinct controls. |
375
+ | `OptionsBox` | GroupBox specialization arranging compact choice groups | required `header`, direct `OptionGroup` and/or `RadioGroup` content |
376
+ | `Layout` | Presentation-only arrangement of arbitrary children | exactly one of `horizontal`/`vertical`; `allocation`, `scroll`, inherited `islands` spacing, optional accessible-region configuration |
364
377
  | `Panel` | Optional labelled, themed region composed over a Layout body | `header`, `horizontal`/`vertical`, `allocation`, `scroll`, `disabled`; `PanelToolbar` and ordinary content children; live: `disabled` |
365
378
  | `Sidebar` | Labelled complementary region with fixed header/toolbar and scrolling content | `header`, `ariaLabel`; `SidebarToolbar` and ordinary content children |
366
379
  | `SplitView` | Resizable two-pane layout | required `SplitPrimary` and `SplitSecondary` Layout panes; `horizontal`/`vertical`, `allocation`, initial/minimum sizes, separator label, `onResize` |
@@ -475,12 +488,13 @@ range while retaining selections outside that range.
475
488
  On an initial snapshot, or a loading snapshot with no result, all three
476
489
  collection views render deterministic, `aria-hidden` placeholder rows;
477
490
  `placeholderCount` selects their count. A loading snapshot with retained rows
478
- keeps those rows in place and marks the collection busy, avoiding flicker during
479
- sort and filter refinements. Application renderers never receive dummy values,
480
- and refreshes preserve valid keyed selections and expansions. Error snapshots
481
- retain any available rows, add an error edge and overlay detail icon, and stop
482
- the loading animation. A `DataTable` data source with `retry` also renders its
483
- localized retry action.
491
+ keeps those rows in place, marks the collection busy, and displays a thin
492
+ animated translucent working texture, avoiding flicker during sort and filter
493
+ refinements.
494
+ Application renderers never receive dummy values, and refreshes preserve valid
495
+ keyed selections and expansions. Error snapshots retain any available rows,
496
+ add an error edge and overlay detail icon, and stop the loading animation. A
497
+ `DataTable` data source with `retry` also renders its localized retry action.
484
498
 
485
499
  Advanced compositions may use `BaseSelectionHandler`,
486
500
  `SingleSelectionHandler`, `MultiSelectionHandler`, and
@@ -503,13 +517,21 @@ expander needs a reusable presentation trait such as `colored`.
503
517
  For reusable sources, use `createLocalTableDataSource`,
504
518
  `createQueryTableDataSource`, `createHandlerTableDataSource`, or
505
519
  `createRestTableDataSource`. Sources expose `query`, `sortEmitter`,
506
- `filtersEmitter`, optional `retry`, and `dispose()`. Both direct data and
507
- query-shaped sources pass their rows through an emitter-derived local table
508
- view, so their sort and filter state always changes the rendered rows.
520
+ `filtersEmitter`, optional `retry`, optional `sourceRows`, and `dispose()`.
521
+ Both direct data and query-shaped sources pass their rows through an
522
+ emitter-derived local table view, so their sort and filter state always
523
+ changes the rendered rows. `sourceRows` exposes the rows before local
524
+ sort/filter when the source can provide them — local and caller-query
525
+ sources do; remote sources leave it absent because filtering is
526
+ server-side.
509
527
 
510
528
  `TableColumn` definitions own display and local comparison/filter functions.
511
529
  When a column's visible `label` is rich content, supply its textual
512
530
  `ariaLabel` for Capillary UI-generated sort and filter control names.
531
+ Set a column's `filterable` flag to derive its header filter options from
532
+ the field's distinct values across `sourceRows` (or the displayed rows when
533
+ the source cannot expose them), so the options stay stable while a filter
534
+ is applied. Explicit `filterOptions` take precedence over `filterable`.
513
535
  The pure `applyLocalTableState`, `serializeTableQuery`, and related table-query
514
536
  helpers keep local behavior and remote encoding explicit. Pagination,
515
537
  virtualization, and server-specific wire policy remain application concerns.
@@ -519,7 +541,7 @@ virtualization, and server-specific wire policy remain application concerns.
519
541
  | Component | Purpose | Key props and state |
520
542
  | --- | --- | --- |
521
543
  | `Dialog` | Controlled native modal with focus containment and restoration | `title`, `description`, `DialogActions` and ordinary content children, `valueEmitter`/`defaultValue`, `closeLabel`, `showCloseButton`, `initialFocusRef`, `onClose` |
522
- | `ProgressBar` | Labelled native progress with visual track | required `label`, `value` or `valueEmitter`, `max`, `valueText`; `null` is indeterminate |
544
+ | `ProgressBar` | Labelled native progress with visual track | required `label`, `value` or `valueEmitter`, `max`, `valueText`; fills its available width; `null` is indeterminate; emitter-backed busy chrome (including determinate bars) requires `FetchState.Loading` |
523
545
  | `ThemePicker` | Select and replace a Capillary UI theme link | value props, `options`, `label`/`ariaLabel`, `disabled`, `targetDocument`, `onChange` |
524
546
  | `ColorPicker` | Select and replace a Capillary UI color link | same contract as `ThemePicker` |
525
547
 
@@ -738,6 +760,14 @@ component selectors. Color files provide anchors and endpoints. Component
738
760
  `static css` owns selectors, layout, pseudo-elements, native states, and
739
761
  interaction mechanics.
740
762
 
763
+ The three chrome backgrounds (`--ui-primary-bg-color`, `--ui-medium-bg-color`,
764
+ and `--ui-dark-bg-color`) alias muted primary surface tones derived from the
765
+ palette's primary anchor and light endpoint. Ice blue retains the exact legacy
766
+ colors `#f5f7f8`, `#ebeff3`, and `#d7dee3`; other palettes recolor these surfaces.
767
+ See the [palette contract](colors/README.md#muted-primary-surfaces) for the
768
+ relative-HSL/sRGB calibration, including the `0`–`1`
769
+ `--palette-primary-surface-saturation` palette control and override points.
770
+
741
771
  Theme files provide intentional overrides. Custom properties are the primary
742
772
  instrument and belong on `:root` inside `@layer theme`, with `color-scheme` as
743
773
  the only ordinary property in that block. A theme may also write ordinary CSS
@@ -746,6 +776,11 @@ be placed after the `@layer theme` block: component CSS is injected as an
746
776
  unlayered `<style>` element prepended to `<head>`, so unlayered theme rules win
747
777
  by document order while layered ones would always lose.
748
778
 
779
+ The built-in theme options are Capillary, Shiny, Soft, White, and Minimal.
780
+ Capillary is a bright, compact card treatment with restrained elevation and
781
+ palette-primary selections. Minimal is adaptive; the other options are fixed
782
+ light treatments.
783
+
749
784
  `capillaryUiThemeVariableCatalog` describes the supported palette and semantic
750
785
  variable hierarchy. `findCapillaryUiStylesheetOption`, `replaceCapillaryUiStylesheet`,
751
786
  `setCapillaryUiAppearance`, and `getCapillaryUiAppearance` support application-controlled
@@ -753,10 +788,23 @@ runtime selection.
753
788
 
754
789
  ### Line-control sizing
755
790
 
756
- Textbox, Dropdown, Toggle, and Button use `--control-min-height: 2em` by
757
- default: a 24px border-box minimum at the default 12px `--ui-font-size`.
758
- This minimum is independent of general UI padding and the text line height.
759
- Their labels and native controls inherit the font family and share a unitless
791
+ All single-line controls reserve the same vertical row space: Textbox,
792
+ Dropdown (including theme/color pickers), Toggle, Button, checkbox variants,
793
+ RadioButton, date/time controls, and ProgressBar. Their existing painted
794
+ bodies remain centered within that row; small checkbox/radio shapes do not
795
+ grow to look like text fields.
796
+
797
+ `--control-min-height: 2em` sets the body minimum (24px at the default 12px
798
+ `--ui-font-size`). The row uses the greater of that minimum and
799
+ `--control-row-min-height` (default `0px`), plus
800
+ `--control-row-padding-block: .25em` on each side. Default rows are therefore
801
+ 30px with 3px above and below the body. Capillary supplies a `2.75em` row
802
+ floor (39px including padding) to accommodate its roomier chrome and native
803
+ temporal inputs while retaining its `6px 5px` control padding. Additional
804
+ layout `gap` is optional and adds to this built-in breathing room.
805
+
806
+ These are minimum sizes: multiline/rich content can grow without clipping.
807
+ Labels and native controls inherit the font family and share a unitless
760
808
  1.2 authored text line height. Native single-line inputs may clamp the used
761
809
  line-height to platform font metrics (notably Firefox on Linux); their bodies
762
810
  and text remain centered. Larger content can increase the minimum-sized body.
@@ -765,6 +813,29 @@ minimum-width/shrinking behavior. Field and button inline padding is 5px;
765
813
  toggle segments use 6px. General `--space-xs`/`--space-sm` no longer determine
766
814
  these line controls' padding.
767
815
 
816
+ `OptionsBox` deliberately keeps compact descendants, including through nested
817
+ `OptionGroup` and `Layout` wrappers: it removes the shared row floor and
818
+ block padding while retaining each control's natural body size. Its checkbox
819
+ and radio rows are normally 1.2em, or the theme's larger painted-shell size.
820
+ OptionsBox owns a fixed 2px vertical option rhythm—between nested groups,
821
+ checkboxes, and radio options—regardless of theme. Ordinary GroupBox and
822
+ Toolbar do not opt into compact spacing. RadioGroup elsewhere adds no
823
+ inter-option gap by default, so the same number of radio and checkbox rows
824
+ occupies the same height (excluding any group legend). Set
825
+ `--radio-group-gap` to add spacing explicitly outside OptionsBox.
826
+
827
+ An `OptionGroup` and a labelled `RadioGroup` both produce a native
828
+ `fieldset`/`legend`. For one mutually exclusive choice, put the `RadioGroup`
829
+ directly in an `OptionsBox` and give it the one visible label. Use an
830
+ `OptionGroup` for a collection of direct checkboxes (or when its legend adds a
831
+ separate, meaningful concept around several controls). Do not wrap a single
832
+ labelled `RadioGroup` in an `OptionGroup` merely for layout; that creates
833
+ redundant legends and nested fieldsets.
834
+
835
+ Maintained themes add chrome without text offsets. Shadows do not participate
836
+ in centering. Toggle borders and selected overlap are painted independently
837
+ of segment layout, so selection does not move text.
838
+
768
839
  Horizontal form `GroupBox` children use their natural content size: GroupBox
769
840
  does not contribute a synthetic preferred width or an artificial size floor.
770
841
  Intrinsic label/body tracks distinguish preferred field widths from the
@@ -794,14 +865,6 @@ their normal inset. In this composition, omit a `DataTable` `caption`: the
794
865
  labelled Panel header supplies the surrounding data-region name. Application
795
866
  data-surface components can opt in by declaring the same static trait.
796
867
 
797
- Checkbox variants and RadioButton retain compact 1.2em label rows with 1em
798
- squares/circles. They center within stretched horizontal hosts without making
799
- vertical lists as tall as text fields. Date/time controls consume the same
800
- shared sizing rules. Themes may deliberately override the minimum (Java does),
801
- but Shiny uses the structural defaults and adds chrome without text offsets.
802
- Shadows do not participate in centering. Toggle borders and selected overlap
803
- are painted independently of segment layout, so selection does not move text.
804
-
805
868
  ### Root sizing and typography
806
869
 
807
870
  `CapillaryUiApp` is block-level and always applies `--application-background`,
@@ -837,7 +900,7 @@ semantics.
837
900
  Applications may explicitly opt native elements into Capillary UI presentation and
838
901
  layout contracts by applying public Capillary UI traits such as `island`,
839
902
  `cap-layout-horizontal`, `cap-layout-vertical`, `cap-size-natural`,
840
- `cap-size-flexible`, and `cap-scroll`. These traits are intentionally
903
+ `cap-size-flexible`, `cap-scroll`, and `cap-island-layout`. These traits are intentionally
841
904
  element-agnostic and may style application-owned native markup as well as
842
905
  Capillary UI-owned hosts.
843
906
 
@@ -893,6 +956,85 @@ provide breakpoint variants.
893
956
  native markup. Capillary UI rejects nested component islands; application markup must
894
957
  preserve the same one-layer invariant.
895
958
 
959
+ ### Composing islands without margin doubling
960
+
961
+ Enable `islands` once on the app or the Layout that contains the composition:
962
+
963
+ ```tsx
964
+ <CapillaryUiApp sizing="viewport" islands>
965
+ <Header island>Application header</Header>
966
+ <Layout horizontal allocation="flexible">
967
+ <Sidebar island header="Navigation">...</Sidebar>
968
+ <Layout vertical allocation="flexible">
969
+ <Panel island header="Overview">...</Panel>
970
+ <Panel island header="Results" allocation="flexible">...</Panel>
971
+ </Layout>
972
+ </Layout>
973
+ <footer className="island cap-size-natural">Status</footer>
974
+ </CapillaryUiApp>
975
+ ```
976
+
977
+ `islands` is a **layout mode**; `island` still marks each **surface**. The
978
+ outermost enabled container gets one perimeter inset. Participating layouts
979
+ then place a single shared gutter between their children, with no island
980
+ margins to add up. Nested Layouts inherit the mode automatically, including
981
+ through ordinary wrappers and `RouteOutlet`. Repeating `islands` on an already
982
+ enabled nested Layout does not add another inset. An app with `islands` and no
983
+ explicit `layout` defaults to vertical; Layout still requires an axis.
984
+
985
+ The public `CapillaryUiIslandLayoutProps` contract is static `islands?: boolean`:
986
+
987
+ - `true` enables managed composition;
988
+ - omitted or `undefined` inherits the surrounding mode;
989
+ - `false` stops inheritance and restores legacy spacing in that subtree.
990
+
991
+ Surface boundaries stop managed layout gaps from reaching controls inside
992
+ them. Panel padding, toolbar gaps, and other internal spacing keep their own
993
+ contracts. A later explicit enabled scope below an opt-out starts a fresh
994
+ perimeter inset. Neither this mode nor nested layouts permit nested islands.
995
+
996
+ Use `--island-gap` for gutters and `--island-inset` for the outer inset. Base
997
+ defaults both to `1rem`; White uses zero. Override these on the composition,
998
+ and optionally `--island-gap` on a nested Layout for a local gutter.
999
+ `--island-padding` remains the padding **inside** each surface, except a direct
1000
+ app-root header: `cap-app > header.island` and `cap-app > cap-header.island`
1001
+ have zero padding. These shell headers delegate internal spacing to their
1002
+ navbar, toolbar, and branding rows. Their border, shadow, outer inset, and
1003
+ gutter remain unchanged. Nested headers and non-island headings are unaffected;
1004
+ this rule also applies outside managed island mode. Use a native `<header
1005
+ className="island">` for a shell containing navigation/toolbars; `Header`
1006
+ remains the component for a native heading.
1007
+
1008
+ `SplitView`
1009
+ inherits the mode and places its separator in one shared gutter; a zero gutter
1010
+ retains an overlapping drag target and keyboard resizing.
1011
+
1012
+ Scrolling layouts automatically include surrounding gutter space in their
1013
+ scrollport, so an island's border and shadow can paint there. Equal padding
1014
+ and negative margin extend the clip by half a gutter without moving the
1015
+ surfaces or increasing their separation. Nested scrollports reuse this space;
1016
+ RouteOutlet and SplitView leave clipping to their content/panes. Themes only
1017
+ declare their ordinary borders and shadows: no clearance token is required.
1018
+ Surface contents and opt-outs retain ordinary scrolling behavior. A zero
1019
+ gutter deliberately leaves no paint space between adjacent surfaces.
1020
+
1021
+ Native markup can use `cap-island-layout` and `cap-island-layout-off`. Combine
1022
+ them with a direction trait, or with your own `display: grid` rules. A wrapper
1023
+ around one region needs no new trait. A native wrapper arranging multiple
1024
+ regions must opt into a layout: inheritance does not turn arbitrary elements
1025
+ into flex/grid containers. Axes, dimensions, responsive wrapping, allocation,
1026
+ and scroll ownership remain explicit application choices.
1027
+
1028
+ Migration is opt-in. Existing layouts outside managed scopes keep
1029
+ `--island-margin` and their former behavior. When enabling a scope, remove
1030
+ margin-cancellation selectors and redundant gap rules, and move any intended
1031
+ spacing override to `--island-gap`/`--island-inset` on the layout. A direct CSS
1032
+ `margin` or `gap` override still wins through the normal cascade; the framework
1033
+ does not measure or rewrite application styles. `islands={false}` can isolate
1034
+ a legacy region during incremental migration.
1035
+
1036
+ ### Semantic category color
1037
+
896
1038
  `colored` consumes an explicit `--c1`, `--c2`, `--c3` triplet for the shared
897
1039
  gradient and `--colored-shadow` treatment. It does not choose semantic colors
898
1040
  for the application.