@kerfjs/ui 5.0.0-beta.24 → 5.0.0-beta.26
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 +44 -23
- package/ai/application-ui-diagnostic-ids-v1.json +3 -0
- package/ai/application-ui-profile.defaults.json +1 -1
- package/ai/compile-time-contracts-v1.json +3 -11
- package/ai/component-catalog-v2-overrides.json +7 -37
- package/ai/component-catalog-v2.d.ts +1 -0
- package/ai/component-catalog-v2.json +415 -91
- package/ai/component-catalog-v2.schema.json +2 -1
- package/ai/component-catalog.json +602 -158
- package/ai/public-api-signatures-v1.md +132 -65
- package/ai/skill.md +20 -20
- package/ai/webawesome-jsx-signatures-v1.md +2 -1
- package/analyzer/cli.mjs +5 -2
- package/analyzer/index.d.mts +1 -0
- package/analyzer/index.mjs +117 -10
- package/dist/app-tab.d.ts +10 -2
- package/dist/app-tab.js +1 -1
- package/dist/browser/list-action-row.js +1 -0
- package/dist/browser/list-item.js +1 -0
- package/dist/browser/surface-scaffold.js +3 -0
- package/dist/catalog.js +5 -4
- package/dist/catalog.js.map +1 -1
- package/dist/{chunk-FEAMR7S4.js → chunk-4EF2CMYS.js} +24 -5
- package/dist/chunk-4EF2CMYS.js.map +1 -0
- package/dist/{chunk-NWSEP5RT.js → chunk-FRVS5RFM.js} +23 -5
- package/dist/chunk-FRVS5RFM.js.map +1 -0
- package/dist/{chunk-PSKN344N.js → chunk-GWEAPEZI.js} +15 -2
- package/dist/chunk-GWEAPEZI.js.map +1 -0
- package/dist/chunk-HP2B2L26.js +102 -0
- package/dist/chunk-HP2B2L26.js.map +1 -0
- package/dist/{chunk-OJWWHFH3.js → chunk-JFGXQBIP.js} +49 -23
- package/dist/chunk-JFGXQBIP.js.map +1 -0
- package/dist/chunk-OBLTAKSX.js +43 -0
- package/dist/chunk-OBLTAKSX.js.map +1 -0
- package/dist/{chunk-Y45PPBDV.js → chunk-OT6RPYAP.js} +16 -2
- package/dist/chunk-OT6RPYAP.js.map +1 -0
- package/dist/{chunk-UFZKLBCF.js → chunk-VCE4MPVE.js} +25 -3
- package/dist/chunk-VCE4MPVE.js.map +1 -0
- package/dist/{chunk-5VTB2BKP.js → chunk-WNWD54GR.js} +16 -2
- package/dist/chunk-WNWD54GR.js.map +1 -0
- package/dist/{chunk-M7JBER5U.js → chunk-X2U3QJCJ.js} +8 -2
- package/dist/chunk-X2U3QJCJ.js.map +1 -0
- package/dist/{chunk-DPDBVKNA.js → chunk-YMKH5XTF.js} +5 -3
- package/dist/chunk-YMKH5XTF.js.map +1 -0
- package/dist/collapsible-panel.d.ts +9 -1
- package/dist/collapsible-panel.js +36 -15
- package/dist/collapsible-panel.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +12 -12
- package/dist/list-action-row.d.ts +11 -1
- package/dist/list-action-row.js +2 -1
- package/dist/list-header.d.ts +15 -2
- package/dist/list-header.js +1 -1
- package/dist/list-item.d.ts +12 -1
- package/dist/list-item.js +2 -1
- package/dist/resizable-region.d.ts +19 -2
- package/dist/resizable-region.js +1 -1
- package/dist/select.d.ts +12 -1
- package/dist/select.js +1 -1
- package/dist/split-view.js +1 -1
- package/dist/styles/app-tab.css +26 -0
- package/dist/styles/collapsible-panel.css +66 -4
- package/dist/styles/list-action-row.css +64 -0
- package/dist/styles/list-header.css +32 -0
- package/dist/styles/list-item.css +39 -0
- package/dist/styles/resizable-region.css +132 -0
- package/dist/styles/select.css +68 -0
- package/dist/styles/styles.css +1 -1
- package/dist/styles/surface-scaffold.css +51 -0
- package/dist/styles/tab-bar.css +24 -0
- package/dist/styles/toolbar-control-group.css +73 -1
- package/dist/styles/toolbar-text.css +1 -1
- package/dist/styles/webawesome.css +12 -0
- package/dist/styles/workbench.css +89 -0
- package/dist/surface-scaffold.d.ts +25 -0
- package/dist/surface-scaffold.js +3 -0
- package/dist/surface-scaffold.js.map +1 -0
- package/dist/tab-bar.d.ts +11 -2
- package/dist/tab-bar.js +1 -1
- package/dist/toolbar-control-group.d.ts +16 -2
- package/dist/toolbar-control-group.js +1 -1
- package/dist/webawesome.d.ts +1 -0
- package/dist/wire-resizable-regions.js +1 -1
- package/dist/wire-sidebar.d.ts +6 -1
- package/dist/wire-sidebar.js +27 -8
- package/dist/wire-sidebar.js.map +1 -1
- package/dist/wire-token-search-fields.js +1 -1
- package/dist/workbench.d.ts +8 -0
- package/dist/workbench.js +42 -0
- package/dist/workbench.js.map +1 -1
- package/docs/accessibility.md +55 -12
- package/docs/collapsible-panel.md +7 -0
- package/docs/component-contract.md +5 -8
- package/docs/component-selection.md +63 -33
- package/docs/design/templates.md +5 -7
- package/docs/examples/component-catalog-extension-v2.json +3 -6
- package/docs/examples/component-catalog-extension.json +1 -1
- package/docs/layout.md +7 -9
- package/docs/recipes.md +8 -7
- package/docs/surface-scaffold.md +17 -0
- package/docs/type-contracts.md +6 -8
- package/docs/ui-analyzer.md +19 -1
- package/docs/ui-doctor.md +1 -1
- package/docs/ux-demo.md +6 -3
- package/docs/webawesome-theme.md +4 -1
- package/docs/workbench.md +49 -2
- package/doctor/eslint-diagnostics.d.mts +16 -0
- package/doctor/eslint-diagnostics.mjs +17 -0
- package/doctor/index.mjs +16 -1
- package/llms.txt +19 -9
- package/package.json +7 -7
- package/ux-demo/recipes/app-shell.tsx +36 -23
- package/ux-demo/recipes/collapsible-sidebar.tsx +22 -16
- package/ux-demo/recipes/composer-form.tsx +15 -6
- package/ux-demo/recipes/list-detail-dialog.tsx +31 -15
- package/ux-demo/recipes/list-workspace-states.tsx +16 -7
- package/ux-demo/recipes/loading-inspector.tsx +39 -18
- package/ux-demo/recipes/recipes.css +7 -2
- package/ux-demo/recipes/workspace-header.tsx +17 -7
- package/dist/browser/panel-header.js +0 -7
- package/dist/chunk-5VTB2BKP.js.map +0 -1
- package/dist/chunk-7XRDUG4Y.js +0 -58
- package/dist/chunk-7XRDUG4Y.js.map +0 -1
- package/dist/chunk-DPDBVKNA.js.map +0 -1
- package/dist/chunk-FEAMR7S4.js.map +0 -1
- package/dist/chunk-M7JBER5U.js.map +0 -1
- package/dist/chunk-NENBUYX5.js +0 -79
- package/dist/chunk-NENBUYX5.js.map +0 -1
- package/dist/chunk-NWSEP5RT.js.map +0 -1
- package/dist/chunk-OJWWHFH3.js.map +0 -1
- package/dist/chunk-PSKN344N.js.map +0 -1
- package/dist/chunk-UFZKLBCF.js.map +0 -1
- package/dist/chunk-Y45PPBDV.js.map +0 -1
- package/dist/panel-header.d.ts +0 -42
- package/dist/panel-header.js +0 -7
- package/dist/panel-header.js.map +0 -1
- package/dist/styles/panel-header.css +0 -64
- package/docs/design/templates/panel-header/icon-actions-dark.svg +0 -1
- package/docs/design/templates/panel-header/icon-actions.svg +0 -1
- package/docs/design/templates/panel-header/icon-summary-actions-dark.svg +0 -1
- package/docs/design/templates/panel-header/icon-summary-actions.svg +0 -1
- package/docs/design/templates/panel-header/no-icon-dark.svg +0 -1
- package/docs/design/templates/panel-header/no-icon.svg +0 -1
- package/docs/design/templates/panel-header/page-heading-dark.svg +0 -1
- package/docs/design/templates/panel-header/page-heading.svg +0 -1
- package/docs/design/templates/panel-header/title-only-dark.svg +0 -1
- package/docs/design/templates/panel-header/title-only.svg +0 -1
- package/docs/design/templates/panel-header-dark.svg +0 -1
- package/docs/design/templates/panel-header.svg +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Public API signatures for the UI authoring corpus
|
|
2
2
|
|
|
3
|
-
Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.
|
|
3
|
+
Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.26` and `kerfjs@5.0.0-beta.26`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
|
|
4
4
|
|
|
5
5
|
## `@kerfjs/ui/disclosure-arrow`
|
|
6
6
|
|
|
@@ -93,6 +93,11 @@ type ToolbarControlGroupAppearance = 'contained' | 'borderless';
|
|
|
93
93
|
type ToolbarControlGroupTone = 'default' | 'dark';
|
|
94
94
|
type ToolbarControlGroupButtonAppearance = 'plain' | 'push';
|
|
95
95
|
type ToolbarControlGroupShape = 'pill' | 'rounded';
|
|
96
|
+
type ToolbarControlGroupSize = 'default' | 'compact';
|
|
97
|
+
type ToolbarControlGroupDensity = 'comfortable' | 'tight';
|
|
98
|
+
type ToolbarControlGroupContent = 'icon' | 'text' | 'mixed' | 'avatar';
|
|
99
|
+
type ToolbarControlGroupSelectedChrome = 'raised' | 'filled' | 'outline';
|
|
100
|
+
type ToolbarControlGroupSelectedTone = 'brand' | 'neutral';
|
|
96
101
|
interface ToolbarControlGroupProps {
|
|
97
102
|
children: SafeHtml | SafeHtml[];
|
|
98
103
|
label?: string;
|
|
@@ -104,10 +109,19 @@ interface ToolbarControlGroupProps {
|
|
|
104
109
|
buttonAppearance?: ToolbarControlGroupButtonAppearance;
|
|
105
110
|
/** Corner shape: fully round `pill` (default) or a softer `rounded` rectangle. */
|
|
106
111
|
shape?: ToolbarControlGroupShape;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
112
|
+
size?: ToolbarControlGroupSize;
|
|
113
|
+
density?: ToolbarControlGroupDensity;
|
|
114
|
+
content?: ToolbarControlGroupContent;
|
|
115
|
+
selectedChrome?: ToolbarControlGroupSelectedChrome;
|
|
116
|
+
selectedTone?: ToolbarControlGroupSelectedTone;
|
|
117
|
+
/** Size a nested Web Awesome dropdown trigger as part of this group. */
|
|
118
|
+
nestedDropdown?: boolean;
|
|
119
|
+
/** Add contrast behind photo-backed avatar content. */
|
|
120
|
+
scrim?: boolean;
|
|
121
|
+
}
|
|
122
|
+
declare function ToolbarControlGroup({ children, label, className, expanded, single, appearance, tone, buttonAppearance, shape, size, density, content, selectedChrome, selectedTone, nestedDropdown, scrim, }: ToolbarControlGroupProps): SafeHtml;
|
|
123
|
+
|
|
124
|
+
export { ToolbarControlGroup, type ToolbarControlGroupAppearance, type ToolbarControlGroupButtonAppearance, type ToolbarControlGroupContent, type ToolbarControlGroupDensity, type ToolbarControlGroupProps, type ToolbarControlGroupSelectedChrome, type ToolbarControlGroupSelectedTone, type ToolbarControlGroupShape, type ToolbarControlGroupSize, type ToolbarControlGroupTone };
|
|
111
125
|
```
|
|
112
126
|
|
|
113
127
|
## `@kerfjs/ui/floating-toolbar`
|
|
@@ -153,6 +167,9 @@ type ListHeaderRootAttributes = Readonly<Record<`data-${string}`, string | undef
|
|
|
153
167
|
'data-action'?: never;
|
|
154
168
|
'data-has-badge'?: never;
|
|
155
169
|
'data-has-count'?: never;
|
|
170
|
+
'data-density'?: never;
|
|
171
|
+
'data-divider'?: never;
|
|
172
|
+
'data-indicator-tone'?: never;
|
|
156
173
|
'data-toggle'?: never;
|
|
157
174
|
}>;
|
|
158
175
|
type ListHeaderTriggerAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
|
|
@@ -164,6 +181,9 @@ type ListHeaderTriggerAttributes = Readonly<Record<`data-${string}`, string | un
|
|
|
164
181
|
}>;
|
|
165
182
|
interface ListHeaderBaseProps {
|
|
166
183
|
label: string;
|
|
184
|
+
density?: 'standard' | 'compact';
|
|
185
|
+
divider?: 'none' | 'before' | 'after' | 'both';
|
|
186
|
+
indicatorTone?: 'neutral' | 'accent' | 'danger';
|
|
167
187
|
/** Render as an unanimated loading skeleton: keep the label and action affordance, disable interaction. */
|
|
168
188
|
placeholder?: boolean;
|
|
169
189
|
rootAttributes?: ListHeaderRootAttributes;
|
|
@@ -201,13 +221,20 @@ type ListHeaderIndicatorProps = {
|
|
|
201
221
|
count: number;
|
|
202
222
|
countLabel: string;
|
|
203
223
|
badge?: never;
|
|
224
|
+
status?: never;
|
|
204
225
|
} | {
|
|
205
226
|
count?: never;
|
|
206
227
|
countLabel?: never;
|
|
207
|
-
badge
|
|
228
|
+
badge: SafeHtml;
|
|
229
|
+
status?: never;
|
|
230
|
+
} | {
|
|
231
|
+
count?: never;
|
|
232
|
+
countLabel?: never;
|
|
233
|
+
badge?: never;
|
|
234
|
+
status?: SafeHtml;
|
|
208
235
|
};
|
|
209
236
|
type ListHeaderProps = ListHeaderBaseProps & ListHeaderIndicatorProps & ListHeaderModeProps;
|
|
210
|
-
declare function ListHeader({ label, count, countLabel, badge, action, actionLabel, actionIcon, actionDisabled, disabledReason, expanded, toggle, placeholder, rootAttributes, triggerAttributes, }: ListHeaderProps): SafeHtml;
|
|
237
|
+
declare function ListHeader({ label, count, countLabel, badge, status, density, divider, indicatorTone, action, actionLabel, actionIcon, actionDisabled, disabledReason, expanded, toggle, placeholder, rootAttributes, triggerAttributes, }: ListHeaderProps): SafeHtml;
|
|
211
238
|
|
|
212
239
|
export { ListHeader, type ListHeaderProps };
|
|
213
240
|
```
|
|
@@ -223,6 +250,10 @@ type ListActionRowRootAttributes = Readonly<Record<`data-${string}`, string | un
|
|
|
223
250
|
'data-item-id'?: never;
|
|
224
251
|
'data-has-icon'?: never;
|
|
225
252
|
'data-multiline'?: never;
|
|
253
|
+
'data-density'?: never;
|
|
254
|
+
'data-divider'?: never;
|
|
255
|
+
'data-busy'?: never;
|
|
256
|
+
'data-trailing-visibility'?: never;
|
|
226
257
|
'data-state'?: never;
|
|
227
258
|
'data-selected'?: never;
|
|
228
259
|
'data-pressed'?: never;
|
|
@@ -239,6 +270,11 @@ type ListActionRowTrailingAttributes = Readonly<Record<`data-${string}`, string
|
|
|
239
270
|
interface ListActionRowProps {
|
|
240
271
|
/** Visible dormant content for the primary button. Must not contain interactive descendants. */
|
|
241
272
|
label: string | SafeHtml;
|
|
273
|
+
description?: string | SafeHtml;
|
|
274
|
+
status?: string | SafeHtml;
|
|
275
|
+
busy?: boolean;
|
|
276
|
+
density?: 'standard' | 'compact';
|
|
277
|
+
divider?: 'none' | 'before' | 'after' | 'both';
|
|
242
278
|
/** Decorative dormant content for the primary button. Must not contain interactive descendants. */
|
|
243
279
|
icon?: SafeHtml;
|
|
244
280
|
action: string;
|
|
@@ -259,12 +295,13 @@ interface ListActionRowProps {
|
|
|
259
295
|
trailingActionIcon: SafeHtml;
|
|
260
296
|
trailingActionDisabled?: boolean;
|
|
261
297
|
trailingActionTitle?: string;
|
|
298
|
+
trailingActionVisibility?: 'always' | 'interaction';
|
|
262
299
|
className?: string;
|
|
263
300
|
style?: string;
|
|
264
301
|
rootAttributes?: ListActionRowRootAttributes;
|
|
265
302
|
trailingActionAttributes?: ListActionRowTrailingAttributes;
|
|
266
303
|
}
|
|
267
|
-
declare function ListActionRow({ label, icon, action, itemId, selected, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, trailingAction, trailingActionLabel, trailingActionIcon, trailingActionDisabled, trailingActionTitle, className, style, rootAttributes, trailingActionAttributes, }: ListActionRowProps): SafeHtml;
|
|
304
|
+
declare function ListActionRow({ label, description, status, busy, density, divider, icon, action, itemId, selected, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, trailingAction, trailingActionLabel, trailingActionIcon, trailingActionDisabled, trailingActionTitle, trailingActionVisibility, className, style, rootAttributes, trailingActionAttributes, }: ListActionRowProps): SafeHtml;
|
|
268
305
|
|
|
269
306
|
export { ListActionRow, type ListActionRowProps };
|
|
270
307
|
```
|
|
@@ -280,12 +317,23 @@ type ListItemRootAttributes = Readonly<Record<`data-${string}`, string | undefin
|
|
|
280
317
|
'data-item-id'?: never;
|
|
281
318
|
'data-has-icon'?: never;
|
|
282
319
|
'data-multiline'?: never;
|
|
320
|
+
'data-density'?: never;
|
|
321
|
+
'data-divider'?: never;
|
|
322
|
+
'data-busy'?: never;
|
|
283
323
|
'data-state'?: never;
|
|
284
324
|
}>;
|
|
285
325
|
interface ListItemProps {
|
|
286
326
|
label: string | SafeHtml;
|
|
327
|
+
/** App-owned supporting text rendered in the component's stable label stack. */
|
|
328
|
+
description?: string | SafeHtml;
|
|
287
329
|
icon?: SafeHtml;
|
|
288
330
|
trailing?: SafeHtml;
|
|
331
|
+
/** Dormant status metadata rendered before trailing content. */
|
|
332
|
+
status?: string | SafeHtml;
|
|
333
|
+
/** Show a progress indicator and expose the row as busy without replacing its content. */
|
|
334
|
+
busy?: boolean;
|
|
335
|
+
density?: 'standard' | 'compact';
|
|
336
|
+
divider?: 'none' | 'before' | 'after' | 'both';
|
|
289
337
|
selected?: boolean;
|
|
290
338
|
action: string;
|
|
291
339
|
itemId?: string;
|
|
@@ -302,7 +350,7 @@ interface ListItemProps {
|
|
|
302
350
|
placeholder?: boolean;
|
|
303
351
|
rootAttributes?: ListItemRootAttributes;
|
|
304
352
|
}
|
|
305
|
-
declare function ListItem({ label, icon, trailing, selected, action, itemId, className, style, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, rootAttributes, }: ListItemProps): SafeHtml;
|
|
353
|
+
declare function ListItem({ label, description, icon, trailing, status, busy, density, divider, selected, action, itemId, className, style, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, rootAttributes, }: ListItemProps): SafeHtml;
|
|
306
354
|
|
|
307
355
|
export { ListItem, type ListItemProps };
|
|
308
356
|
```
|
|
@@ -358,53 +406,6 @@ declare function ListInsetText({ children, horizontalOnly, className, }: ListIns
|
|
|
358
406
|
export { ListInsetText, type ListInsetTextProps };
|
|
359
407
|
```
|
|
360
408
|
|
|
361
|
-
## `@kerfjs/ui/panel-header`
|
|
362
|
-
|
|
363
|
-
```ts
|
|
364
|
-
import { SafeHtml } from 'kerfjs';
|
|
365
|
-
import { HeadingLevel } from './toolbar-text.js';
|
|
366
|
-
|
|
367
|
-
interface PanelHeaderBaseProps {
|
|
368
|
-
title: string;
|
|
369
|
-
titleId: string;
|
|
370
|
-
icon?: SafeHtml;
|
|
371
|
-
iconClassName?: string;
|
|
372
|
-
actions?: SafeHtml;
|
|
373
|
-
/**
|
|
374
|
-
* Expose the title as a heading landmark (`role="heading"` + `aria-level`). Set it
|
|
375
|
-
* for a PAGE or view heading so screen-reader heading navigation works and the view
|
|
376
|
-
* has a primary heading; omit it (the default) for a dialog title, which is instead
|
|
377
|
-
* referenced via `aria-labelledby={titleId}` and needs no heading landmark.
|
|
378
|
-
*/
|
|
379
|
-
headingLevel?: HeadingLevel;
|
|
380
|
-
/** Render the title and summary as unanimated loading skeletons, keeping the icon and actions. */
|
|
381
|
-
placeholder?: boolean;
|
|
382
|
-
}
|
|
383
|
-
type PanelHeaderSummaryProps = {
|
|
384
|
-
summary: string;
|
|
385
|
-
summaryId?: string;
|
|
386
|
-
} | {
|
|
387
|
-
summary?: never;
|
|
388
|
-
summaryId?: never;
|
|
389
|
-
};
|
|
390
|
-
type PanelHeaderProps = PanelHeaderBaseProps & PanelHeaderSummaryProps;
|
|
391
|
-
/**
|
|
392
|
-
* The heading of a panel, dialog, or page: a plain `Toolbar` whose leading zone
|
|
393
|
-
* holds an optional icon control group and the title as extra-large `ToolbarText`,
|
|
394
|
-
* whose trailing zone holds the app's action controls, and with an optional
|
|
395
|
-
* subtitle on its own row, left-aligned with the title.
|
|
396
|
-
*
|
|
397
|
-
* PanelHeader overrides no Toolbar styles — it is just a Toolbar with an xl title.
|
|
398
|
-
* The only styling it adds is the icon group's fill/border color and the subtitle.
|
|
399
|
-
* When no icon is provided, the icon group is omitted entirely. The `actions` slot
|
|
400
|
-
* is passed straight into the toolbar's trailing zone; the app supplies whatever
|
|
401
|
-
* trailing controls it needs (typically a `ToolbarControlGroup`).
|
|
402
|
-
*/
|
|
403
|
-
declare function PanelHeader({ title, titleId, summary, summaryId, icon, iconClassName, actions, headingLevel, placeholder, }: PanelHeaderProps): SafeHtml;
|
|
404
|
-
|
|
405
|
-
export { PanelHeader, type PanelHeaderProps };
|
|
406
|
-
```
|
|
407
|
-
|
|
408
409
|
## `@kerfjs/ui/value-table`
|
|
409
410
|
|
|
410
411
|
```ts
|
|
@@ -443,6 +444,8 @@ type AppTabRootAttributes = Readonly<Record<`data-${string}`, string | undefined
|
|
|
443
444
|
'data-tab-dragging'?: never;
|
|
444
445
|
'data-tab-drop-position'?: never;
|
|
445
446
|
}>;
|
|
447
|
+
type AppTabPresentation = 'pill' | 'segmented' | 'icon-only';
|
|
448
|
+
type AppTabSize = 'default' | 'compact';
|
|
446
449
|
interface AppTabProps {
|
|
447
450
|
id: string;
|
|
448
451
|
name: string;
|
|
@@ -451,6 +454,12 @@ interface AppTabProps {
|
|
|
451
454
|
draggable?: boolean;
|
|
452
455
|
leading?: SafeHtml;
|
|
453
456
|
trailing?: SafeHtml;
|
|
457
|
+
/** Visual treatment within a TabBar. Icon-only tabs retain `name` as their accessible name. */
|
|
458
|
+
presentation?: AppTabPresentation;
|
|
459
|
+
/** Compact tabs use the 32px application-rail height. */
|
|
460
|
+
size?: AppTabSize;
|
|
461
|
+
/** Maximum visible label width in CSS pixels before ellipsis. */
|
|
462
|
+
labelMaxWidth?: number;
|
|
454
463
|
/** Decorative dormant content for the close button. Must not contain interactive descendants. */
|
|
455
464
|
closeIcon?: SafeHtml;
|
|
456
465
|
selectAction?: string;
|
|
@@ -460,9 +469,9 @@ interface AppTabProps {
|
|
|
460
469
|
placeholder?: boolean;
|
|
461
470
|
rootAttributes?: AppTabRootAttributes;
|
|
462
471
|
}
|
|
463
|
-
declare function AppTab({ id, name, selected, closable, draggable, leading, trailing, closeIcon, selectAction, closeAction, className, placeholder, rootAttributes, }: AppTabProps): SafeHtml;
|
|
472
|
+
declare function AppTab({ id, name, selected, closable, draggable, leading, trailing, presentation, size, labelMaxWidth, closeIcon, selectAction, closeAction, className, placeholder, rootAttributes, }: AppTabProps): SafeHtml;
|
|
464
473
|
|
|
465
|
-
export { AppTab, type AppTabProps };
|
|
474
|
+
export { AppTab, type AppTabPresentation, type AppTabProps, type AppTabSize };
|
|
466
475
|
```
|
|
467
476
|
|
|
468
477
|
## `@kerfjs/ui/tab-bar`
|
|
@@ -471,6 +480,9 @@ export { AppTab, type AppTabProps };
|
|
|
471
480
|
import { SafeHtml } from 'kerfjs';
|
|
472
481
|
|
|
473
482
|
type TabActivation = 'automatic' | 'manual';
|
|
483
|
+
type TabBarAllocation = 'intrinsic' | 'fill';
|
|
484
|
+
type TabBarPresentation = 'rail' | 'segmented' | 'inspector';
|
|
485
|
+
type TabBarTrailingPlacement = 'separate' | 'adjacent';
|
|
474
486
|
interface TabBarProps {
|
|
475
487
|
id: string;
|
|
476
488
|
label: string;
|
|
@@ -485,11 +497,17 @@ interface TabBarProps {
|
|
|
485
497
|
* selects with Enter / Space / click — use it when selecting a tab is a heavy action.
|
|
486
498
|
*/
|
|
487
499
|
activation?: TabActivation;
|
|
500
|
+
/** How available strip width is allocated across child AppTabs. */
|
|
501
|
+
allocation?: TabBarAllocation;
|
|
502
|
+
/** Named strip chrome for application rails, segmented tabs, or inspectors. */
|
|
503
|
+
presentation?: TabBarPresentation;
|
|
504
|
+
/** Keep a trailing action beside the final tab or at the far edge of the bar. */
|
|
505
|
+
trailingPlacement?: TabBarTrailingPlacement;
|
|
488
506
|
}
|
|
489
507
|
/** Render a controlled tab strip. The application owns selection, order, and persistence. */
|
|
490
|
-
declare function TabBar({ id, label, children, leading, trailing, className, activation, }: TabBarProps): SafeHtml;
|
|
508
|
+
declare function TabBar({ id, label, children, leading, trailing, className, activation, allocation, presentation, trailingPlacement, }: TabBarProps): SafeHtml;
|
|
491
509
|
|
|
492
|
-
export { type TabActivation, TabBar, type TabBarProps };
|
|
510
|
+
export { type TabActivation, TabBar, type TabBarAllocation, type TabBarPresentation, type TabBarProps, type TabBarTrailingPlacement };
|
|
493
511
|
```
|
|
494
512
|
|
|
495
513
|
## `@kerfjs/ui/wire-tab-bars`
|
|
@@ -698,6 +716,7 @@ export { Pane, type PaneContentElement, type PaneElement, type PaneProps, type P
|
|
|
698
716
|
|
|
699
717
|
```ts
|
|
700
718
|
import { SafeHtml } from 'kerfjs';
|
|
719
|
+
import { ResizableRegionSeparator, ResizableRegionCollapseMotion, ResizableRegionContentOverflow, ResizableRegionPresentation, ResizableRegionRestorePosition } from './resizable-region.js';
|
|
701
720
|
|
|
702
721
|
/** A collapsible Workbench panel — a side rail or the bottom drawer. */
|
|
703
722
|
interface WorkbenchPanel {
|
|
@@ -708,6 +727,13 @@ interface WorkbenchPanel {
|
|
|
708
727
|
size?: number;
|
|
709
728
|
/** Accessible name for the panel region. */
|
|
710
729
|
label?: string;
|
|
730
|
+
separator?: ResizableRegionSeparator;
|
|
731
|
+
collapseMotion?: ResizableRegionCollapseMotion;
|
|
732
|
+
contentOverflow?: ResizableRegionContentOverflow;
|
|
733
|
+
presentation?: ResizableRegionPresentation;
|
|
734
|
+
/** Control shown in a safe-area-aware viewport corner while collapsed. */
|
|
735
|
+
restoreControl?: SafeHtml;
|
|
736
|
+
restorePosition?: ResizableRegionRestorePosition;
|
|
711
737
|
}
|
|
712
738
|
interface WorkbenchProps {
|
|
713
739
|
id: string;
|
|
@@ -737,6 +763,7 @@ export { Workbench, type WorkbenchPanel, type WorkbenchProps };
|
|
|
737
763
|
```ts
|
|
738
764
|
import { SafeHtml } from 'kerfjs';
|
|
739
765
|
import { LucideIcon } from './lucide-icon.js';
|
|
766
|
+
import { ResizableRegionSeparator, ResizableRegionCollapseMotion, ResizableRegionContentOverflow, ResizableRegionPresentation, ResizableRegionRestorePosition } from './resizable-region.js';
|
|
740
767
|
import 'lucide';
|
|
741
768
|
|
|
742
769
|
/** Which edge a {@link CollapsiblePanel} docks to. */
|
|
@@ -786,6 +813,13 @@ interface CollapsiblePanelProps {
|
|
|
786
813
|
label?: string;
|
|
787
814
|
/** Panel content. */
|
|
788
815
|
children?: SafeHtml | readonly SafeHtml[];
|
|
816
|
+
separator?: ResizableRegionSeparator;
|
|
817
|
+
collapseMotion?: ResizableRegionCollapseMotion;
|
|
818
|
+
contentOverflow?: ResizableRegionContentOverflow;
|
|
819
|
+
presentation?: ResizableRegionPresentation;
|
|
820
|
+
/** Control shown in a safe-area-aware viewport corner while collapsed. */
|
|
821
|
+
restoreControl?: SafeHtml;
|
|
822
|
+
restorePosition?: ResizableRegionRestorePosition;
|
|
789
823
|
className?: string;
|
|
790
824
|
}
|
|
791
825
|
/**
|
|
@@ -798,7 +832,7 @@ interface CollapsiblePanelProps {
|
|
|
798
832
|
* and persistence semantics, and with `CollapsiblePanelToggle` for the standard
|
|
799
833
|
* affordance. See `docs/24-collapsible-panel.md`.
|
|
800
834
|
*/
|
|
801
|
-
declare function CollapsiblePanel({ id, side, collapsed, size, label, children, className, }: CollapsiblePanelProps): SafeHtml;
|
|
835
|
+
declare function CollapsiblePanel({ id, side, collapsed, size, label, children, separator, collapseMotion, contentOverflow, presentation, restoreControl, restorePosition, className, }: CollapsiblePanelProps): SafeHtml;
|
|
802
836
|
|
|
803
837
|
export { CollapsiblePanel, type CollapsiblePanelProps, type CollapsiblePanelSide, CollapsiblePanelToggle, type CollapsiblePanelToggleProps, collapsiblePanelToggleIcon };
|
|
804
838
|
```
|
|
@@ -836,6 +870,11 @@ interface WireSidebarOptions {
|
|
|
836
870
|
* is always inline.
|
|
837
871
|
*/
|
|
838
872
|
deviceClass?: ReadonlySignal<DeviceClass>;
|
|
873
|
+
/** Compact devices either overlay the panels (default) or hide them in favor
|
|
874
|
+
* of an application-owned responsive replacement. */
|
|
875
|
+
compactPresentation?: 'overlay' | 'hidden';
|
|
876
|
+
/** Collapse the other panels when one opens in compact overlay mode. */
|
|
877
|
+
exclusiveCompact?: boolean;
|
|
839
878
|
/** Persistence store (default `globalThis.localStorage`, if present). */
|
|
840
879
|
storage?: SidebarStorage;
|
|
841
880
|
}
|
|
@@ -846,7 +885,7 @@ interface WireSidebarOptions {
|
|
|
846
885
|
* persistence hook. The app owns each `collapsed` signal and the layout; this wire
|
|
847
886
|
* owns the interaction. Returns a disposer. See `docs/24-collapsible-panel.md`.
|
|
848
887
|
*/
|
|
849
|
-
declare function wireSidebar(root: HTMLElement, { panels, deviceClass, storage }: WireSidebarOptions): () => void;
|
|
888
|
+
declare function wireSidebar(root: HTMLElement, { panels, deviceClass, compactPresentation, exclusiveCompact, storage, }: WireSidebarOptions): () => void;
|
|
850
889
|
|
|
851
890
|
export { type SidebarStorage, type WireSidebarOptions, type WireSidebarPanel, wireSidebar };
|
|
852
891
|
```
|
|
@@ -909,6 +948,11 @@ import { SafeHtml } from 'kerfjs';
|
|
|
909
948
|
|
|
910
949
|
type ResizableRegionAxis = 'horizontal' | 'vertical';
|
|
911
950
|
type ResizableRegionEdge = 'start' | 'end';
|
|
951
|
+
type ResizableRegionSeparator = 'auto' | 'hidden';
|
|
952
|
+
type ResizableRegionCollapseMotion = 'none' | 'slide' | 'fade-slide';
|
|
953
|
+
type ResizableRegionContentOverflow = 'clip' | 'auto' | 'visible';
|
|
954
|
+
type ResizableRegionPresentation = 'inline' | 'overlay' | 'hidden';
|
|
955
|
+
type ResizableRegionRestorePosition = 'bottom-start' | 'bottom-end';
|
|
912
956
|
interface ResizableRegionProps {
|
|
913
957
|
id: string;
|
|
914
958
|
label: string;
|
|
@@ -919,15 +963,27 @@ interface ResizableRegionProps {
|
|
|
919
963
|
edge?: ResizableRegionEdge;
|
|
920
964
|
collapsed?: boolean;
|
|
921
965
|
transitioning?: boolean;
|
|
966
|
+
/** Whether the separator line is painted. The resize hit target remains available. */
|
|
967
|
+
separator?: ResizableRegionSeparator;
|
|
968
|
+
/** Keep the track change instant while optionally sliding the fixed-size content. */
|
|
969
|
+
collapseMotion?: ResizableRegionCollapseMotion;
|
|
970
|
+
/** Overflow policy for content such as an open popup inside a bottom drawer. */
|
|
971
|
+
contentOverflow?: ResizableRegionContentOverflow;
|
|
972
|
+
/** Inline layout, an edge overlay, or a responsive replacement that removes the region. */
|
|
973
|
+
presentation?: ResizableRegionPresentation;
|
|
974
|
+
/** Always-available control rendered while collapsed, outside the clipped region. */
|
|
975
|
+
restoreControl?: SafeHtml;
|
|
976
|
+
/** Safe-area-aware viewport corner for `restoreControl`. */
|
|
977
|
+
restorePosition?: ResizableRegionRestorePosition;
|
|
922
978
|
/** Decorative dormant content for the separator handle. Must not contain interactive descendants. */
|
|
923
979
|
handleIcon?: SafeHtml;
|
|
924
980
|
children: SafeHtml | SafeHtml[];
|
|
925
981
|
}
|
|
926
982
|
declare const clampRegionSize: (size: number, min: number, max: number) => number;
|
|
927
983
|
declare const resizeRegionFromPointer: (startSize: number, delta: number, edge: ResizableRegionEdge) => number;
|
|
928
|
-
declare function ResizableRegion({ id, label, size, min, max, axis, edge, collapsed, transitioning, handleIcon, children, }: ResizableRegionProps): SafeHtml;
|
|
984
|
+
declare function ResizableRegion({ id, label, size, min, max, axis, edge, collapsed, transitioning, separator, collapseMotion, contentOverflow, presentation, restoreControl, restorePosition, handleIcon, children, }: ResizableRegionProps): SafeHtml;
|
|
929
985
|
|
|
930
|
-
export { ResizableRegion, type ResizableRegionAxis, type ResizableRegionEdge, type ResizableRegionProps, clampRegionSize, resizeRegionFromPointer };
|
|
986
|
+
export { ResizableRegion, type ResizableRegionAxis, type ResizableRegionCollapseMotion, type ResizableRegionContentOverflow, type ResizableRegionEdge, type ResizableRegionPresentation, type ResizableRegionProps, type ResizableRegionRestorePosition, type ResizableRegionSeparator, clampRegionSize, resizeRegionFromPointer };
|
|
931
987
|
```
|
|
932
988
|
|
|
933
989
|
## `@kerfjs/ui/wire-resizable-regions`
|
|
@@ -1338,14 +1394,25 @@ interface SelectBaseProps<Value extends string = string> {
|
|
|
1338
1394
|
className?: string;
|
|
1339
1395
|
/** Empty-value hint text shown in the closed control (the native select placeholder). */
|
|
1340
1396
|
placeholderText?: string;
|
|
1397
|
+
/** Supporting text shown below the control and associated with its combobox. */
|
|
1398
|
+
hint?: string;
|
|
1341
1399
|
disabled?: boolean;
|
|
1342
1400
|
fitMenu?: boolean;
|
|
1343
1401
|
renderSelected?: (choice: SelectChoice<Value>) => SafeHtml;
|
|
1344
1402
|
/** Render as an unanimated loading skeleton: the label above a static, empty control box. */
|
|
1345
1403
|
placeholder?: boolean;
|
|
1404
|
+
/** Form (default), borderless toolbar, or intrinsic navigation chrome. */
|
|
1405
|
+
presentation?: 'form' | 'toolbar-borderless' | 'navigation';
|
|
1406
|
+
size?: 'default' | 'compact';
|
|
1407
|
+
/** Show only the selected choice icon while retaining the Select's accessible name. */
|
|
1408
|
+
selectedPresentation?: 'label' | 'icon-only';
|
|
1409
|
+
/** Let an enclosing ToolbarControlGroup paint the composed focus ring. */
|
|
1410
|
+
focusRingOwner?: 'select' | 'group';
|
|
1411
|
+
/** Maximum closed-control label width in CSS pixels before ellipsis. */
|
|
1412
|
+
labelMaxWidth?: number;
|
|
1346
1413
|
}
|
|
1347
1414
|
type SelectProps<Value extends string = string> = SelectBaseProps<Value> & SelectAccessibleName;
|
|
1348
|
-
declare function Select<Value extends string>({ name, value, label, ariaLabel, choices, className, placeholderText, disabled, fitMenu, renderSelected, placeholder, }: SelectProps<Value>): SafeHtml;
|
|
1415
|
+
declare function Select<Value extends string>({ name, value, label, ariaLabel, choices, className, placeholderText, hint, disabled, fitMenu, renderSelected, placeholder, presentation, size, selectedPresentation, focusRingOwner, labelMaxWidth, }: SelectProps<Value>): SafeHtml;
|
|
1349
1416
|
|
|
1350
1417
|
export { Select, type SelectChoice, type SelectProps };
|
|
1351
1418
|
```
|
package/ai/skill.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: kerf-ui
|
|
3
3
|
description: Build interfaces with kerfjs and the @kerfjs/ui production component package. Use whenever code imports @kerfjs/ui or a task asks for Kerf UI components.
|
|
4
|
-
kerf-ui-skill-version: 1.36.
|
|
4
|
+
kerf-ui-skill-version: 1.36.1
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Building with @kerfjs/ui
|
|
@@ -63,18 +63,18 @@ Choose from the need, not from visual resemblance:
|
|
|
63
63
|
|
|
64
64
|
Quick routing:
|
|
65
65
|
|
|
66
|
-
| Need | Choose | Nearest alternatives / boundary
|
|
67
|
-
| ------------------------------------- | ---------------------------------------------------------- |
|
|
68
|
-
| Navigation row | `ListItem` | Use an ordinary link or button when sidebar/menu anatomy and state do not apply. Multiline leading icons stay aligned with the first text line.
|
|
69
|
-
| Navigation row with a trailing action | `ListActionRow` | Use `ListItem` when the trailing region is dormant; never put controls inside either component's SafeHtml slots. Multiline leading icons stay aligned with the first text line.
|
|
70
|
-
| Page, panel, or dialog heading | `Toolbar`
|
|
71
|
-
| Exclusive choice | `TabBar`, `SegmentedControl`, or `Select` | Tabs switch tabpanels; segments expose a few choices; Select handles a longer value list.
|
|
72
|
-
| Status or absent content | `StateBanner` or `EmptyState` | `StateBanner` accepts an optional terse, tone-tinted `badge` beside its title for a count or compact status; do not rely on color alone. Web Awesome callouts suit ecosystem content; toasts are transient and never the only record of important state.
|
|
73
|
-
| Loading placeholder | a component's `placeholder` prop, or `Skeleton` | Set `placeholder` on a value-bearing component (`Select`, `ListHeader`, `ListItem`, `ValueTableRow`, `
|
|
74
|
-
| Adjustable or static columns | `ResizableRegion` or CSS grid | Use the component only for a user-operable controlled separator.
|
|
75
|
-
| Structured or ordinary search | `TokenSearchField` or an input | Use token search only when text and ordered filter chips form one editor.
|
|
76
|
-
| Key/value facts | `ValueTable` with `ValueTableRow` | Use rows with `icon` only when the optional 24px leading visual adds context.
|
|
77
|
-
| Command search | Application-local semantics using shared layout vocabulary | No runtime component or production recipe is exported; the app owns behavior, policy, and copy.
|
|
66
|
+
| Need | Choose | Nearest alternatives / boundary |
|
|
67
|
+
| ------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
68
|
+
| Navigation row | `ListItem` | Use an ordinary link or button when sidebar/menu anatomy and state do not apply. Multiline leading icons stay aligned with the first text line. |
|
|
69
|
+
| Navigation row with a trailing action | `ListActionRow` | Use `ListItem` when the trailing region is dormant; never put controls inside either component's SafeHtml slots. Multiline leading icons stay aligned with the first text line. |
|
|
70
|
+
| Page, panel, or dialog heading | `Toolbar` + `ToolbarText` | Use a direct extra-large `ToolbarText`, optional grouped icon, grouped trailing controls, and app-owned supporting copy below. |
|
|
71
|
+
| Exclusive choice | `TabBar`, `SegmentedControl`, or `Select` | Tabs switch tabpanels; segments expose a few choices; Select handles a longer value list. |
|
|
72
|
+
| Status or absent content | `StateBanner` or `EmptyState` | `StateBanner` accepts an optional terse, tone-tinted `badge` beside its title for a count or compact status; do not rely on color alone. Web Awesome callouts suit ecosystem content; toasts are transient and never the only record of important state. |
|
|
73
|
+
| Loading placeholder | a component's `placeholder` prop, or `Skeleton` | Set `placeholder` on a value-bearing component (`Select`, `ListHeader`, `ListItem`, `ValueTableRow`, `SegmentedControl`, `StateBanner`, `AppTab`, `ToolbarText`, `ListActionRow`) to render its real chrome with value slots as subtle **unanimated** skeletons and interactivity disabled — do not hand-rebuild an empty state. `Skeleton` is the standalone block for a custom slot. Use `LoadingSpinner` for known busy activity, not a skeleton; never animate a skeleton; prefer it over `wa-skeleton`. |
|
|
74
|
+
| Adjustable or static columns | `ResizableRegion` or CSS grid | Use the component only for a user-operable controlled separator. |
|
|
75
|
+
| Structured or ordinary search | `TokenSearchField` or an input | Use token search only when text and ordered filter chips form one editor. |
|
|
76
|
+
| Key/value facts | `ValueTable` with `ValueTableRow` | Use rows with `icon` only when the optional 24px leading visual adds context. |
|
|
77
|
+
| Command search | Application-local semantics using shared layout vocabulary | No runtime component or production recipe is exported; the app owns behavior, policy, and copy. |
|
|
78
78
|
|
|
79
79
|
For a complete shell, sidebar, workspace header, list-detail dialog, composer
|
|
80
80
|
form, list-state lifecycle, or mixed-control toolbar, start from the runnable
|
|
@@ -97,21 +97,21 @@ Hard rules:
|
|
|
97
97
|
3. Keep state, product copy, persistence, and domain mappings in the application. Do not add product-specific actions or fields to a generic component. Put ListItem/ListActionRow/ListHeader/AppTab domain `data-*` metadata in `rootAttributes`; use ListActionRow `trailingActionAttributes` and ListHeader `triggerAttributes` only for domain `data-*` or native popover target/action and `aria-controls`/`aria-haspopup`. These slots do not replace component-owned action, item/tab identity, selection, disclosure, naming, disabled, icon, or role semantics.
|
|
98
98
|
4. Wire `data-action` hooks from one stable root with `delegate()` or `delegateActions()` and retain disposers.
|
|
99
99
|
5. Use the opinionated `--kui-color-*` semantic ramps and component-level override properties. Override tokens at the narrowest useful scope and prefer equivalent props/tokens. Public-class-to-public-class selectors are supported when every Kerf class appears in the catalog entry's `publicClasses`; never target descendant tags, ids, attribute-only anatomy, or unlisted implementation classes.
|
|
100
|
-
6. `Select` is pure until the app explicitly imports `@kerfjs/ui/select/register`; do not import Web Awesome's full registration bundle. When an app writes direct `wa-*` JSX, add `import type {} from '@kerfjs/ui/webawesome'` for the catalog-supported intrinsic-element declarations, import the CSS-only `@kerfjs/ui/webawesome.css` theme once, and keep importing individual Web Awesome component modules so their JavaScript remains tree-shakeable. The type boundary emits no code and registers nothing. Pass icon-bearing `choices` and `renderSelected` content normally: `Select` preserves its slotted option icons across Kerf rerenders and keys selected content by the controlled value, so app wrappers must not add competing morph-control attributes.
|
|
100
|
+
6. `Select` is pure until the app explicitly imports `@kerfjs/ui/select/register`; do not import Web Awesome's full registration bundle. When an app writes direct `wa-*` JSX, add `import type {} from '@kerfjs/ui/webawesome'` for the catalog-supported intrinsic-element declarations, import the CSS-only `@kerfjs/ui/webawesome.css` theme once, and keep importing individual Web Awesome component modules so their JavaScript remains tree-shakeable. The type boundary emits no code and registers nothing. Pass icon-bearing `choices` and `renderSelected` content normally: `Select` preserves its slotted option icons across Kerf rerenders and keys selected content by the controlled value, so app wrappers must not add competing morph-control attributes. Give it a visible `label` or an `ariaLabel` for a visually hidden name; the nonempty visible label takes precedence and the package names the actual shadow combobox, including `renderSelected`, without adding label geometry. Use `hint` for persistent supporting text below the control and `placeholderText` only for the empty value inside the closed control; loading placeholders retain the hint.
|
|
101
101
|
7. Decorative icons are hidden; controls are named; focus is visible; state never relies on color alone; reduced motion and increased contrast remain usable. `DisclosureArrow` defaults to an 18px root-scaled visual and exposes `--kui-disclosure-arrow-size` for consumer sizing; it never becomes the interaction or accessible-name owner. Put it in an owning native control with a stable accessible name and controlled `aria-expanded`. `ListHeader` toggle mode supplies it when `actionIcon` is omitted, but the app must reveal matching content; a custom icon replaces it. Author replacement icon content facing right before its configured direction transform is applied. Direction changes take the shortest rotation path, with counterclockwise chosen for a 180-degree closed-to-open tie. Kerf `Select` separately keeps its intrinsic Web Awesome expand glyph at `--kui-disclosure-icon-scale: .5`.
|
|
102
102
|
8. `ResizableRegion` uses `wireResizableRegions()` for Arrow, Shift+Arrow, Home/End, and pointer behavior. The app owns size persistence. `handleIcon` replaces decorative dormant glyph content only.
|
|
103
103
|
9. Use controlled `SegmentedControl` for a small exclusive choice set. Select `appearance="toolbar"` when nesting it inside `ToolbarControlGroup`; use rounded or pill shapes for standalone contexts. Handle its action, update `value`, keep meaningful choice labels, and preserve every enabled native button in sequential Tab order.
|
|
104
104
|
10. Compose `AppTab` inside controlled `TabBar`; call `wireTabBars()` once and retain its disposer. It owns same-bar drag mechanics, including proximity-based horizontal edge autoscroll, while the app applies `onReorder` and owns order, selection, panels, close policy, routing, and persistence. Arrow/Home/End select on move by default and restore focus by bar/tab identity when controlled activation replaces the strip; pass `activation: 'manual'` (or `TabBar activation="manual"` per strip) so arrow keys move focus only and the user selects with Enter/Space/click — use it when selecting a tab is a heavy action. Use runtime-filtered `rootAttributes` for domain metadata and keep an optional `closeIcon` decorative and noninteractive.
|
|
105
|
-
11. Use token-controlled `TokenSearchField` when free text and removable structured filters share one editor. Editable text stays DOM-owned between token changes. The leading icon, first text line, clear action, and trailing slot share one fixed row when content wraps. The app owns parsing and suggestions; call `readTokenSearchField()` on input, empty `textContent` on clear, use `placeTokenSearchCaret()` after explicit controlled focus changes, and call `wireTokenSearchFields()` once so Enter submits without inserting a line break and keyboard chip deletion restores focus plus the text-relative caret after controlled replacement. Enable `collapsible` for an animated iconic closed state, standalone or inside `ToolbarControlGroup`; the field keeps text or tokens expanded, and `wireTokenSearchFields()` manages the transient expand/collapse/focus by default (activate to reveal + focus, Escape or empty blur to collapse). Bind the field's `expanded` to the returned handle's `expanded(id)` signal or adopt your own via `collapsible.signals`; opt a behavior out only when the app must own it. Do not hand-roll the open handler, the focusout collapse, a clear-button mousedown guard, or reopening after controlled clear — the helper keeps the replacement editor open and focused. For real-app adoption it also provides a focusout keep-open exception (`data-token-search-keep-open` regions or `collapsible.keepOpenOn(target)`, so a sibling suggestions/date/help surface does not collapse an empty field), opt-in atomic-chip keyboard (`keyboard`: Backspace/Delete remove the adjacent chip via `onRemoveToken`, ArrowRight moves the caret past a trailing chip), and an `onEdit({id, editor, event})` input callback (the `InputEvent` lets you gate on `inputType`/`data`) — reach for these instead of re-adding hand-rolled keydown/input handlers around the field.
|
|
105
|
+
11. Use token-controlled `TokenSearchField` when free text and removable structured filters share one editor. Editable text stays DOM-owned between token changes. The leading icon, first text line, clear action, and trailing slot share one fixed row when content wraps. The app owns parsing and suggestions; call `readTokenSearchField()` on input, empty `textContent` on clear, use `placeTokenSearchCaret()` after explicit controlled focus changes, and call `wireTokenSearchFields()` once so Enter submits without inserting a line break and keyboard chip deletion restores focus plus the text-relative caret after controlled replacement. Enable `collapsible` for an animated iconic closed state, standalone or inside `ToolbarControlGroup`; the field keeps text or tokens expanded, and `wireTokenSearchFields()` manages the transient expand/collapse/focus by default (activate to reveal + focus, Escape or empty blur to collapse). Bind the field's `expanded` to the returned handle's `expanded(id)` signal or adopt your own via `collapsible.signals`; opt a behavior out only when the app must own it. Do not hand-roll the open handler, the focusout collapse, a clear-button mousedown guard, or reopening after controlled clear or Select All deletion — the helper keeps the replacement editor open and focused. Deletion restoration finishes before the next keystroke, so a delayed animation frame cannot overwrite a later selection. Persist both query and tokens returned by the DOM read. For real-app adoption it also provides a focusout keep-open exception (`data-token-search-keep-open` regions or `collapsible.keepOpenOn(target)`, so a sibling suggestions/date/help surface does not collapse an empty field), opt-in atomic-chip keyboard (`keyboard`: Backspace/Delete remove the adjacent chip via `onRemoveToken`, ArrowRight moves the caret past a trailing chip), and an `onEdit({id, editor, event})` input callback (the `InputEvent` lets you gate on `inputType`/`data`) — reach for these instead of re-adding hand-rolled keydown/input handlers around the field.
|
|
106
106
|
12. Demo work uses public production component subpaths and their browser-selected CSS. Give every public visual component its own category-grouped catalog route; list themed third-party components under a clearly labeled collapsible ecosystem section, with a focused route for each. For the complete focused/composition decision, `CatalogExample` / `CatalogExampleStack` row/group nesting, exact specimen selection, geometry-overlay legend and exclusions, skip-marker behavior, and metadata ownership, follow the single [Catalog demo authoring contract](../docs/catalog.md#catalog-demo-authoring-contract), discovered machine-readably through [`catalog-authoring.json`](./catalog-authoring.json). Never infer structure from private `kui-catalog-*` classes. Project each component's deterministic repository-relative demo source and existing documentation path so the detail can expose `View demo source` and `Read guidance` links without a runtime export; also derive first-party component implementation paths from their canonical browser imports, and label Web Awesome documentation as Kerf integration guidance. Declare direct `uses` relationships so `Used by` stays derivable, and theme shell chrome through the same semantic tokens as the stage instead of drawing a substitute. Enable `wireCatalog({ revealSelection: true })` for long desktop sidebars so controlled selections remain visible without moving focus; keep its default compact media guard unless compact scrolling is an explicit product behavior, and use `revealCatalogEntry` for an initial deep link.
|
|
107
|
-
13. The Web Awesome theme makes Tooltip and Popover arrowless by default. Keep that default unless a pointer materially clarifies the anchor; opt back in with `--wa-tooltip-arrow-size`, `--kui-wa-popover-arrow-size`, or a popover's public `--arrow-size`, and use `without-arrow` when local no-arrow intent should survive theme changes. Its non-field chrome uses `--kui-wa-control-inset` (8px controls/items), `--kui-wa-surface-margin` / `--kui-wa-surface-inset` (8px around/inside Accordion, Card, Details, Callout, and Include), and `--kui-wa-container-inset` (16px Tab Panel content);
|
|
107
|
+
13. The Web Awesome theme makes Tooltip and Popover arrowless by default. Keep that default unless a pointer materially clarifies the anchor; opt back in with `--wa-tooltip-arrow-size`, `--kui-wa-popover-arrow-size`, or a popover's public `--arrow-size`, and use `without-arrow` when local no-arrow intent should survive theme changes. Its non-field chrome uses `--kui-wa-control-inset` (8px controls/items), `--kui-wa-surface-margin` / `--kui-wa-surface-inset` (8px around/inside Accordion, Card, Details, Callout, and Include), and `--kui-wa-container-inset` (16px Tab Panel content); Dialog maps its body to the 8px surface tier and its footer to the 16px container tier independently of `--spacing`. Override those tiers instead of restyling individual parts. Checkbox and Radio Group option regions, the Color Picker trigger, and Slider's complete interactive region receive the shared 8px logical inline outer inset because those controls have no bordered field shell. Known Date field captions and bordered text-like field hints align with values at the 9px border-plus-padding inset. OTP Input's label uses the same uppercase xs/650 treatment as other field labels, and both its label and hint use that 9px inset.
|
|
108
108
|
14. Treat the complete Web Awesome catalog as support coverage, not a recommendation list. Its UX sidebar marks superseded and exceptional entries `Discouraged`. Consider Popup when it replaces custom anchored positioning. Prefer Kerf `Select` over direct Dropdown/Dropdown Item/Select/Option composition, `SegmentedControl` over Button Group, `TabBar` or `SegmentedControl` over Web Awesome Tabs, `LucideIcon` over Web Awesome Icon, and `ResizableRegion` over Split Panel. Use Tree/Tree Item, Animated Image, and Comparison only for a specific required behavior; avoid Zoomable Frame.
|
|
109
109
|
15. Build sidebars, main areas, inspectors, and dialogs from `@kerfjs/ui/layout.css`: an unpadded `.kui-pane`, optional `.kui-pane__toolbar`, one scrolling `.kui-pane__content`, and optional `.kui-pane__footer`. Use `List` instead of a hand-written flex-column wrapper when rows or sections need stretch alignment, standard/custom gap, flex growth, one vertical scroll owner, or physical-edge dividers; `dividerSides` uses canonical `t`/`r`/`b`/`l` order. Add `.kui-content` for 24px major vertical separation and `.kui-content-item` for a child-owned 8px inline margin, 1px transparent-or-visible border, 8px padding, and 12px radius. Use the pill modifier for 22px. Do not pad pane shells, duplicate item geometry in wrappers, or create nested scroll owners.
|
|
110
110
|
16. Keep a visible collapsible pane's collapse control in its own toolbar. When hidden, move its restore control into the adjacent main toolbar on the corresponding logical edge: leading for an inline-start sidebar and trailing for an inline-end inspector. Collapse the pane completely; do not preserve an empty icon-only rail.
|
|
111
|
-
17. The only direct children of a `Toolbar` zone (`leading`/`center`/`trailing`) are `ToolbarText` (identity/title text) and `ToolbarControlGroup`; never drop bare buttons, inputs, links, or arbitrary markup straight into a zone. `SegmentedControl`, `Select`, a collapsible `TokenSearchField`, and Web Awesome controls all live inside a
|
|
112
|
-
18. When a recurring concept has no matching export or production recipe, keep its semantics in a thin application adapter while reusing the public layout vocabulary. The composer recipe uses one visible form surface,
|
|
111
|
+
17. The only direct children of a `Toolbar` zone (`leading`/`center`/`trailing`) are `ToolbarText` (identity/title text) and `ToolbarControlGroup`; never drop bare buttons, inputs, links, or arbitrary markup straight into a zone. Put identity/title text directly in the zone as `ToolbarText`; wrap interactive controls and icon tiles in `ToolbarControlGroup`. `SegmentedControl`, `Select`, a collapsible `TokenSearchField`, and Web Awesome controls all live inside a group. For an ordinary icon/action control inside a group, use a plain `<button>` (the group styles `> button` fully) — that is the default; reach for `wa-button` only when you need a Web Awesome feature, chiefly a `wa-dropdown` popup trigger. A popup menu in a toolbar is a `single` group wrapping a `wa-dropdown` whose `slot="trigger"` `wa-button` is the toolbar button and whose `wa-dropdown-item`s are the menu, with the dropdown kept under `data-morph-skip-children`. Compose panel/dialog/page headings with a direct extra-large `ToolbarText`, optional grouped icon, grouped trailing controls, and app-owned supporting copy below. A group remains 44px outside (`calc(2px + remify(42px))`) when its border/background are transparent; use 8px between groups and inside items. Split dormant and interactive regions: `ListHeader` fills the available inline width and keeps its label and mutually exclusive semantic count or non-count `badge` together, with an independent logical-end 44px action unless disclosure mode makes the title cluster the button. Its action visual defaults to 18px through `--kui-list-header-action-icon-size`; never shrink the target to match it. Pass every non-negative safe-integer section quantity through `count` with a localized full spoken `countLabel`; never concatenate it into `label` or put a number in `badge`. `ListItem.trailing` is dormant; use `ListActionRow` when primary and trailing actions need sibling 44px native buttons. Its `label`, `icon`, and `trailingActionIcon` slots are also dormant and cannot contain controls. Let panes relocate at narrow widths instead of shrinking targets.
|
|
112
|
+
18. When a recurring concept has no matching export or production recipe, keep its semantics in a thin application adapter while reusing the public layout vocabulary. The composer recipe uses one visible form surface, toolbar title/supporting-copy ids, shared 8px field/action gutters, 24px major rhythm, and a conditional StateBanner as its only nested semantic surface; do not turn every section into a card or double-inset intrinsically bordered controls. The application-local `../docs/examples/command-palette-adapter.tsx` is reference source for one such missing concept, not an `@kerfjs/ui` runtime export or catalog recipe. The application owns its registration, ranking, history, permissions, availability, shortcut policy, focus policy, dispatch, and copy. If a missing concept recurs across products, open an upstream component or recipe request.
|
|
113
113
|
19. Compose `ValueTable` from typed `ValueTableRow` entries instead of handwritten `dt`/`dd` wrappers. Pass `icon` for the optional 24px leading visual; the row owns 8px of root-scaled top and bottom padding, the 8px iconless or 40px icon-bearing separator start, and the common 8px right inset.
|
|
114
|
-
20. Use `
|
|
114
|
+
20. Use the standard direct `Toolbar` composition for a panel, dialog, or page heading, not a private wrapper or custom heading row. Put an optional icon in a `ToolbarControlGroup`, the title directly in the leading zone as extra-large `ToolbarText`, and actions in a trailing group. Set `headingLevel` for page/section landmarks. Supporting copy stays below the toolbar as app-owned content. The app owns modal behavior, focus, dismissal, command policy, ids, and action handling.
|
|
115
115
|
21. Space with the official five-step scale, picked by how connected two elements are — not by eye. `0` `--kui-space-none` = no separation (one unit); `4px` `--kui-space-2xs` = very minor air on a connected cluster; `8px` `--kui-space-xs` = standard, between elements within a group; `16px` `--kui-space-m` = minor, between homogeneous groups; `24px` `--kui-space-l` = major, between heterogeneous groups (the `.kui-content` rhythm). The 8px-vs-24px distinction is inside-a-group vs between-major-differing-regions. `--kui-space-s` (12px) and `--kui-space-xl` (32px) are off-scale exceptions; prefer the five canonical remify-authored tokens. See `docs/layout.md` "Spacing scale".
|
|
116
116
|
22. Pick a whole-screen layout from the opt-in, tree-shakeable subpaths by data + interaction + device, and derive responsiveness from `@kerfjs/ui/device-class`'s `deviceClass()` (`compact` = handset or portrait tablet = one pane at a time). Simple/flat → `NavStack` with one entry (single pane), plus `TabScaffold` (`@kerfjs/ui/tab-scaffold`, iOS bottom tabs, each tab its own `NavStack`) for 2–5 co-equal sections on `compact`. Drill-down → `NavStack` (`@kerfjs/ui/nav-stack`), upgrading to `SplitView` (`@kerfjs/ui/split-view`, list-detail) once both panes fit (`atLeast('tablet')` landscape); `SplitView` collapses to a `NavStack` on `compact`. Complex tool with peripheral panels → `Workbench` (`@kerfjs/ui/workbench`, collapsible rails + drawer) `atLeast('desktop')`, degrading to `NavStack`/overlays below. These are declarative (the app owns the stack/selection/collapsed/active state as signals) with disposer-returning `wire…` helpers; each ships a companion CSS import and stays out of the barrel. For a standalone collapsible rail or bottom drawer outside a full shell, use `CollapsiblePanel` + `CollapsiblePanelToggle` (`@kerfjs/ui/collapsible-panel`) with `wireSidebar` (`@kerfjs/ui/wire-sidebar`): the standard collapse animation and per-side icon convention (`PanelLeft*`/`PanelRight*`/`PanelBottom*`) plus toggle, focus, compact overlay, and persistence semantics. Dialogs pick the same inner layout, then present per device class (full-screen modal on `compact`, inline on desktop). See `docs/app-layouts.md` and `docs/collapsible-panel.md`.
|
|
117
117
|
|
|
@@ -138,7 +138,7 @@ Common mistakes:
|
|
|
138
138
|
| Let the document, pane, and list all scroll | Keep fixed chrome outside one `.kui-pane__content` scroll owner per pane |
|
|
139
139
|
| Invent an `@kerfjs/ui` command-palette export | Keep the semantics application-local, reuse public layout vocabulary, and propose recurring behavior upstream |
|
|
140
140
|
| Handwrite `ValueTable` row wrappers or compensate their padding or separators | Compose `ValueTableRow`; its root-scaled block padding, optional icon hook, and separator geometry are public contract |
|
|
141
|
-
| Rebuild a panel/dialog heading
|
|
141
|
+
| Rebuild a panel/dialog heading with bespoke CSS or a private wrapper | Compose a plain `Toolbar` with a direct xl `ToolbarText`, optional grouped icon, grouped trailing controls, and app-owned supporting copy below |
|
|
142
142
|
| Force a `width`/`height`/`padding` on a component to size or space it | Let it size to its content and tokens; a forced box leaves a halo or a stretched oval — adjust an icon-size or spacing token, not the box |
|
|
143
143
|
| Wrap a component or region in a card, border, backdrop, or outline to "contain" it | Let it sit on the surface; add a `.kui-content-item` only for a real distinction — hierarchy comes from alignment, spacing, and type first |
|
|
144
144
|
| Add another container's padding on top of a content-item's own margin | Pick one owner of the inset; a pane has no padding and its `.kui-content` children own the 8/1/8 geometry — stacking them double-insets |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Web Awesome JSX signatures for the UI authoring corpus
|
|
2
2
|
|
|
3
|
-
Generated from the emitted `@kerfjs/ui@5.0.0-beta.
|
|
3
|
+
Generated from the emitted `@kerfjs/ui@5.0.0-beta.26` declaration boundary. Import `@kerfjs/ui/webawesome` for type effects when authoring direct `wa-*` JSX. The module emits no runtime behavior and does not register custom elements.
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
6
|
import { KerfCustomElement } from 'kerfjs/jsx-runtime';
|
|
@@ -28,6 +28,7 @@ declare module 'kerfjs/jsx-runtime' {
|
|
|
28
28
|
'aria-label'?: string;
|
|
29
29
|
value?: string;
|
|
30
30
|
placeholder?: string;
|
|
31
|
+
hint?: string;
|
|
31
32
|
disabled?: boolean;
|
|
32
33
|
};
|
|
33
34
|
'wa-option': KerfCustomElement & {
|
package/analyzer/cli.mjs
CHANGED
|
@@ -24,13 +24,16 @@ const format = take('--format') ?? 'text';
|
|
|
24
24
|
const output = take('--output');
|
|
25
25
|
const profile = take('--profile');
|
|
26
26
|
const failOnReview = args.includes('--fail-on-review');
|
|
27
|
-
const
|
|
27
|
+
const adoption = args.includes('--adoption');
|
|
28
|
+
const paths = args.filter(
|
|
29
|
+
(argument) => !argument.startsWith('--') && argument !== '--adoption',
|
|
30
|
+
);
|
|
28
31
|
|
|
29
32
|
if (!['text', 'json', 'sarif'].includes(format)) {
|
|
30
33
|
console.error(`Unknown --format ${format}; expected text, json, or sarif.`);
|
|
31
34
|
process.exitCode = 2;
|
|
32
35
|
} else {
|
|
33
|
-
const report = await analyzeUiProject({ root, paths, profile });
|
|
36
|
+
const report = await analyzeUiProject({ root, paths, profile, adoption });
|
|
34
37
|
const rendered =
|
|
35
38
|
format === 'text'
|
|
36
39
|
? formatUiAnalysisText(report)
|
package/analyzer/index.d.mts
CHANGED