@sonata-innovations/fiber-types 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -40,6 +40,7 @@ npx ajv-cli validate --spec=draft2020 \
40
40
  - **Options must be `{ label, value }` objects**, not bare strings. `value` may be a string or a number.
41
41
  - **Option-bearing components do not default to their first option.** Initial value is `null` unless `properties.defaultValue` matches an option's `value`.
42
42
  - **Never set runtime-only fields** (`value`, `valid`, `addedComponents`) in Flow JSON.
43
+ - **`theme.fontFamily` may be an object, not just a string.** `{ family, src }` names a font the renderer loads itself; a bare string assumes the host page already has it. Narrow before treating it as a stack.
43
44
  - **`Component` is a discriminated union.** Narrow on `component.type` and `properties` narrows with it; use `ComponentOfType<"inputText">` to name one variant.
44
45
  - **If an AI/LLM generated the flow**, run `normalizeConditionalOrder(flow)` before handing it to FBTL — it reorders conditionals to sit after their triggers, structurally only, and is idempotent.
45
46
 
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  All notable changes to the Fiber schema types. Dates are release dates.
4
4
 
5
+ ## 2.5.0 — 2026-08-20
6
+
7
+ ### Added
8
+
9
+ - **`ThemeConfig.fontFamily` now also accepts a loadable font config** — `{ family, src?, faces?, stack? }`, with the new `FontFamilyConfig`, `FontFaceConfig` and `FontSource` types exported. A plain family string still works and still means "the host page already loads this font"; the object form carries the sources a renderer needs to register the face itself. This is what makes a brand font work inside a shadow root, where an `@font-face` declared in the root's stylesheet is never registered at all. The schema (`flow-schema.json`) accepts either shape.
10
+
11
+ Widening the type is source-compatible for anything that *sets* `fontFamily`, but code that *reads* it as a `string` needs to narrow. `isFontFamilyConfig` and `fontFamilyStack` are exported from `@sonata-innovations/fiber-fbre` for that.
12
+
13
+ ## 2.4.0 — 2026-07-11 (never published)
14
+
15
+ > The version bump landed in the repo but was never tagged, so npm went 2.3.0 → 2.5.0. The change below ships as part of 2.5.0.
16
+
17
+
18
+ ### Added
19
+
20
+ - `ComponentInputType` (`"text" | "email" | "tel" | "url" | "password"`) is now exported from the package index. It is the declared type of the `inputText` component's `inputType` property and was the only property-level union not re-exported — every sibling (`ComponentWidth`, `DateFormat`, `ComponentCalloutVariant`, `SignatureMode`, …) already was. Consumers can now type a helper against it directly instead of restating the string union. No runtime or schema change: the `inputType` values are unchanged and were already documented in `flow-schema.json`.
21
+
5
22
  ## 2.3.0 — 2026-07-10
6
23
 
7
24
  ### Added
package/README.md CHANGED
@@ -46,7 +46,7 @@ npx ajv-cli validate --spec=draft2020 \
46
46
  import type {
47
47
  Flow, FlowScreen, Component, ComponentProperties,
48
48
  FlowMetadata, FlowConfiguration, ThemeConfig, NavigationConfig,
49
- ControlsConfig, ConfirmationConfig,
49
+ ControlsConfig, ConfirmationConfig, FontFamilyConfig,
50
50
  FlowData, ScreenData, ComponentData, CalculationData,
51
51
  FileUploadData, RangeValue,
52
52
  FlowConditionConfig, ConditionRule, ConditionOperator,
@@ -66,6 +66,12 @@ import {
66
66
  } from "@sonata-innovations/fiber-types";
67
67
  ```
68
68
 
69
+ Those are the headline types. Every **per-component property shape**
70
+ (`ComponentInputProperties`, `ComponentOptionProperties`, `ComponentRepeaterProperties`, …)
71
+ and every **property-level union** (`ComponentWidth`, `DateFormat`, `ComponentInputType`,
72
+ `ComponentCalloutVariant`, `SignatureMode`, `OptionItem`, `TableColumn`, …) is exported too —
73
+ import them when you need to type a helper against a single property rather than a whole component.
74
+
69
75
  `Component` is a **discriminated union keyed on `type`** — narrow on `component.type` and `component.properties` narrows with it:
70
76
 
71
77
  ```ts
@@ -76,6 +82,7 @@ if (component.type === "inputText") {
76
82
 
77
83
  ## Gotchas
78
84
 
85
+ - **`ThemeConfig.fontFamily` is `string | FontFamilyConfig`.** A plain string is a CSS stack and means "the host page already loads this font"; `{ family, src }` carries the sources a renderer registers itself. Code that reads the knob as a `string` has to narrow — `isFontFamilyConfig` / `fontFamilyStack` are exported from `@sonata-innovations/fiber-fbre` for that.
79
86
  - **30 component types.** Display types (`header`, `text`, `divider`, `callout`, `table`) never appear in FlowData; `computed` does.
80
87
  - **`pattern` validation rules take `params.regex`**, not `params.pattern`. A `pattern` key is silently ignored and the rule always passes.
81
88
  - **Options must be `{ label, value }` objects**, not bare strings. `value` may be a string or a number.
package/dist/flow.d.ts CHANGED
@@ -14,6 +14,49 @@ export type FlowMetadata = Record<string, string> & {
14
14
  * `darkMode: boolean`. Light is the default. A future "auto" (follow the
15
15
  * viewer's `prefers-color-scheme`) is an additive option, not yet supported. */
16
16
  export type ColorScheme = "light" | "dark";
17
+ /** One `url(...)` entry inside a face's `src`. `format` is the CSS
18
+ * `format()` hint — "woff2", "woff", "truetype". */
19
+ export type FontSource = {
20
+ url: string;
21
+ format?: string;
22
+ };
23
+ /** A single `@font-face`: one weight/style of a family. */
24
+ export type FontFaceConfig = {
25
+ src: FontSource[];
26
+ /** CSS `font-weight` — "400", "700", or a variable range like "100 900". */
27
+ weight?: string;
28
+ style?: "normal" | "italic" | "oblique";
29
+ display?: "auto" | "block" | "swap" | "fallback" | "optional";
30
+ unicodeRange?: string;
31
+ };
32
+ /**
33
+ * A brand font the renderer can actually load, rather than a family name it
34
+ * can only hope the host page already has.
35
+ *
36
+ * Naming a family in a theme does nothing on its own: `fontFamily` maps to a
37
+ * CSS family *string*, and if no `@font-face` for it is registered in the
38
+ * owning document the form falls back to `system-ui` and looks approximately
39
+ * fine, so nobody reports it. It matters most exactly where the host cannot
40
+ * patch around it — a form mounted in a shadow root, since `@font-face`
41
+ * rules inside a shadow stylesheet are not registered at all.
42
+ *
43
+ * Give the family sources here and the renderer registers the faces in the
44
+ * owning document itself (`ensureFontLoaded` in `@sonata-innovations/fiber-fbre`),
45
+ * so the family string and the loaded face cannot drift apart.
46
+ */
47
+ export type FontFamilyConfig = {
48
+ /** CSS family name. Used both for the `@font-face` and the token value. */
49
+ family: string;
50
+ /** Shorthand for a single regular face. Merged with `faces`. */
51
+ src?: FontSource[];
52
+ /** Additional faces — a second weight, an italic. */
53
+ faces?: FontFaceConfig[];
54
+ /**
55
+ * The full stack written to `--fbre-font`. Defaults to the family plus a
56
+ * system fallback, so a face that fails to load still renders sensibly.
57
+ */
58
+ stack?: string;
59
+ };
17
60
  /**
18
61
  * Visual theme settings. Two layers:
19
62
  * - `colorScheme` + `style` pick built-in presets (the seed palette + shape).
@@ -40,8 +83,12 @@ export type ThemeConfig = {
40
83
  border?: string;
41
84
  /** Corner radius, any CSS length (`--fbre-radius`), e.g. `"3px"`. */
42
85
  radius?: string;
43
- /** Font family stack (`--fbre-font`). */
44
- fontFamily?: string;
86
+ /**
87
+ * Font family (`--fbre-font`). A plain CSS stack string uses whatever the
88
+ * host page already provides; a `FontFamilyConfig` additionally carries the
89
+ * sources the renderer needs to register the face itself.
90
+ */
91
+ fontFamily?: string | FontFamilyConfig;
45
92
  /** Error state color (`--fbre-error`). */
46
93
  error?: string;
47
94
  /** Success state color (`--fbre-success`). */
@@ -1 +1 @@
1
- {"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../src/flow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../src/flow.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { Flow, FlowScreen, FlowConfiguration, ThemeConfig, ColorScheme, NavigationConfig, ControlsConfig, ConfirmationConfig, FlowMetadata, Component, ComponentOfType, ComponentProperties, ScreenTransitionType, FlowStyleType, ControlsLayout, StepperStyle, FlowModeType, } from "./flow.js";
1
+ export type { Flow, FlowScreen, FlowConfiguration, ThemeConfig, ColorScheme, NavigationConfig, ControlsConfig, FontSource, FontFaceConfig, FontFamilyConfig, ConfirmationConfig, FlowMetadata, Component, ComponentOfType, ComponentProperties, ScreenTransitionType, FlowStyleType, ControlsLayout, StepperStyle, FlowModeType, } from "./flow.js";
2
2
  export type { ComponentType, ComponentPropertiesByType, } from "./component-variants.js";
3
3
  export { COMPONENT_VARIANT_TYPES, assertManifestTypeMapParity, } from "./component-variants.js";
4
4
  export type { ConditionAction, ConditionLogic, ConditionOperator, ConditionRule, ConditionRuleSource, ConditionGroup, FlowConditionConfig, } from "./conditions.js";
@@ -13,5 +13,5 @@ export type { PresetData, TemplateData } from "./preset-template-data.js";
13
13
  export type { CalculationFormat, FlowCalculation, } from "./calculation.js";
14
14
  export type { ComponentCategory, ComponentManifestEntry, } from "./component-manifest.js";
15
15
  export { COMPONENT_MANIFEST, COMPONENT_TYPES, DISPLAY_ONLY_TYPES, CATEGORY_LABELS, getComponentManifestEntry, getManifestByCategory, } from "./component-manifest.js";
16
- export type { ComponentWidth, DateFormat, ComponentDisplayProperties, ComponentDividerProperties, ComponentInputProperties, OptionItem, ComponentOptionProperties, ComponentSliderProperties, ComponentSwitchProperties, ComponentRatingProperties, ComponentFileUploadProperties, ComponentGroupProperties, ComponentRepeaterProperties, ComponentDateProperties, ComponentTimeProperties, ComponentDateTimeProperties, ComponentDateRangeProperties, ComponentTimeRangeProperties, ComponentDateTimeRangeProperties, ComponentYesNoProperties, ComponentConfirmProperties, ComponentColorPickerProperties, CardSelectOption, ComponentCardSelectProperties, ComponentCalloutVariant, ComponentCalloutProperties, TableColumn, TableRow, ComponentTableProperties, SignatureMode, ComponentSignatureProperties, ComponentComputedProperties, } from "./properties.js";
16
+ export type { ComponentWidth, DateFormat, ComponentDisplayProperties, ComponentDividerProperties, ComponentInputType, ComponentInputProperties, OptionItem, ComponentOptionProperties, ComponentSliderProperties, ComponentSwitchProperties, ComponentRatingProperties, ComponentFileUploadProperties, ComponentGroupProperties, ComponentRepeaterProperties, ComponentDateProperties, ComponentTimeProperties, ComponentDateTimeProperties, ComponentDateRangeProperties, ComponentTimeRangeProperties, ComponentDateTimeRangeProperties, ComponentYesNoProperties, ComponentConfirmProperties, ComponentColorPickerProperties, CardSelectOption, ComponentCardSelectProperties, ComponentCalloutVariant, ComponentCalloutProperties, TableColumn, TableRow, ComponentTableProperties, SignatureMode, ComponentSignatureProperties, ComponentComputedProperties, } from "./properties.js";
17
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,IAAI,EACJ,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACb,cAAc,EACd,YAAY,EACZ,YAAY,GACb,MAAM,WAAW,CAAC;AAEnB,YAAY,EACV,aAAa,EACb,yBAAyB,GAC1B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EACV,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,mBAAmB,EACnB,cAAc,EACd,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EACV,kBAAkB,EAClB,cAAc,EACd,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,YAAY,EACV,QAAQ,EACR,UAAU,EACV,aAAa,EACb,eAAe,EACf,cAAc,EACd,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,GACX,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAE7E,OAAO,EACL,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE1E,YAAY,EACV,iBAAiB,EACjB,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EACV,cAAc,EACd,UAAU,EACV,0BAA0B,EAC1B,0BAA0B,EAC1B,wBAAwB,EACxB,UAAU,EACV,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,6BAA6B,EAC7B,wBAAwB,EACxB,2BAA2B,EAC3B,uBAAuB,EACvB,uBAAuB,EACvB,2BAA2B,EAC3B,4BAA4B,EAC5B,4BAA4B,EAC5B,gCAAgC,EAChC,wBAAwB,EACxB,0BAA0B,EAC1B,8BAA8B,EAC9B,gBAAgB,EAChB,6BAA6B,EAC7B,uBAAuB,EACvB,0BAA0B,EAC1B,WAAW,EACX,QAAQ,EACR,wBAAwB,EACxB,aAAa,EACb,4BAA4B,EAC5B,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,IAAI,EACJ,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACb,cAAc,EACd,YAAY,EACZ,YAAY,GACb,MAAM,WAAW,CAAC;AAEnB,YAAY,EACV,aAAa,EACb,yBAAyB,GAC1B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EACV,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,aAAa,EACb,mBAAmB,EACnB,cAAc,EACd,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EACV,kBAAkB,EAClB,cAAc,EACd,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,YAAY,EACV,QAAQ,EACR,UAAU,EACV,aAAa,EACb,eAAe,EACf,cAAc,EACd,oBAAoB,EACpB,gBAAgB,EAChB,UAAU,GACX,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAE7E,OAAO,EACL,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE1E,YAAY,EACV,iBAAiB,EACjB,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,yBAAyB,EACzB,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EACV,cAAc,EACd,UAAU,EACV,0BAA0B,EAC1B,0BAA0B,EAC1B,kBAAkB,EAClB,wBAAwB,EACxB,UAAU,EACV,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,yBAAyB,EACzB,6BAA6B,EAC7B,wBAAwB,EACxB,2BAA2B,EAC3B,uBAAuB,EACvB,uBAAuB,EACvB,2BAA2B,EAC3B,4BAA4B,EAC5B,4BAA4B,EAC5B,gCAAgC,EAChC,wBAAwB,EACxB,0BAA0B,EAC1B,8BAA8B,EAC9B,gBAAgB,EAChB,6BAA6B,EAC7B,uBAAuB,EACvB,0BAA0B,EAC1B,WAAW,EACX,QAAQ,EACR,wBAAwB,EACxB,aAAa,EACb,4BAA4B,EAC5B,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,428 @@
1
+ ---
2
+ title: Custom Presets & Templates (FBT)
3
+ applies-to:
4
+ - "@sonata-innovations/fiber-fbt@^2.2"
5
+ - "@sonata-innovations/fiber-types@^2.2"
6
+ read-when: "Extending FBT's pool with custom presets/templates: data-based vs factory definitions, icon catalog, collision rules, server-stored definitions."
7
+ ---
8
+
9
+ <!-- Generated from the Fiber repo's docs/ tree by project/scripts/sync-package-docs.mjs. Do not edit here. -->
10
+ # Custom Presets & Templates
11
+
12
+ Parent applications extend FBT's pool with custom **presets** and **templates** via the optional `customPresets` and `customTemplates` props. This is the deep-dive companion to the short "Custom Presets & Templates" section in `docs/integration/fbt.md`.
13
+
14
+ ## Concepts
15
+
16
+ - A **preset** is one or more pre-configured components. Dropping it onto the stage batch-adds those components to the **current screen** (at the drop position if dropped over an existing component, otherwise appended).
17
+ - A **template** is a complete `Flow`. Dropping it **merges** into the current flow: its screens are **appended** to the existing screen list via `mergeFlow`, and the first appended screen becomes the active screen. Nothing is replaced — the existing screens, metadata, and config are untouched, and the template's own `metadata`/`config` are ignored by the merge (only its screens are ingested).
18
+
19
+ Whether a definition is treated as a preset or a template is determined by **which prop array it arrives in** (`customPresets` vs `customTemplates`), not by its `type` string.
20
+
21
+ ## Two definition kinds, one set of props
22
+
23
+ Both props accept a union type:
24
+
25
+ ```ts
26
+ // fbt/src/types/custom.ts
27
+ export type AnyPresetDefinition = CustomPresetDefinition | PresetData;
28
+ export type AnyTemplateDefinition = CustomTemplateDefinition | TemplateData;
29
+ ```
30
+
31
+ - **Data-based** (`PresetData` / `TemplateData`, exported from `@sonata-innovations/fiber-types`) — plain serializable JSON. **Preferred**: it can be stored in a database, sent over the wire, and edited without code. FBT regenerates UUIDs automatically on every drop.
32
+ - **Factory-based** (`CustomPresetDefinition` / `CustomTemplateDefinition`, exported from `@sonata-innovations/fiber-fbt`) — a `factory()` function called on every drop. Kept for backward compatibility.
33
+
34
+ FBT distinguishes the two kinds structurally (`"factory" in definition`), so you can mix both kinds in the same array.
35
+
36
+ ## Data-based definitions (primary)
37
+
38
+ ### Shapes
39
+
40
+ ```ts
41
+ // @sonata-innovations/fiber-types
42
+ export type PresetData = {
43
+ type: string; // unique identifier (see collision rules below)
44
+ label: string; // display name in the pool
45
+ icon: string; // key into FBT's ICON_MAP (see icon catalog below)
46
+ components: Component[];
47
+ };
48
+
49
+ export type TemplateData = {
50
+ type: string;
51
+ label: string;
52
+ icon: string;
53
+ flow: Flow;
54
+ };
55
+ ```
56
+
57
+ ### UUID regeneration semantics
58
+
59
+ The UUIDs you write into a data-based definition are **placeholders**. On every drop, FBT calls `regenerateComponentUUIDs` (presets) or `regenerateFlowUUIDs` (templates) from `@sonata-innovations/fiber-types`:
60
+
61
+ - Every component gets a fresh UUID; `properties` are deep-cloned (`structuredClone`), and nested `components` arrays (groups) are regenerated recursively.
62
+ - For templates, the flow UUID and every screen UUID are also replaced.
63
+ - The original definition object is never mutated, so the same definition can be dropped any number of times without duplicate-UUID bugs.
64
+
65
+ This means placeholder UUIDs only need to be unique **within** the definition (so group nesting is unambiguous); any string works.
66
+
67
+ ### Full preset example
68
+
69
+ `Component` is a discriminated union keyed on `type` — `properties` is checked against the shape for that `type` (e.g. `label` is required for `"inputText"`, `value` for `"header"`, `options` for `"dropDown"`). Group children live on the component's top-level `components` field, not inside `properties`.
70
+
71
+ ```ts
72
+ import type { PresetData } from "@sonata-innovations/fiber-types";
73
+
74
+ const ssnPreset: PresetData = {
75
+ type: "custom-ssn",
76
+ label: "SSN",
77
+ icon: "ssn",
78
+ components: [
79
+ {
80
+ uuid: "ssn-field",
81
+ type: "inputText",
82
+ properties: {
83
+ label: "Social Security Number",
84
+ placeholder: "XXX-XX-XXXX",
85
+ validation: {
86
+ rules: [
87
+ { type: "required" },
88
+ // NOTE: the param key is `regex`, not `pattern`
89
+ { type: "pattern", params: { regex: "^\\d{3}-\\d{2}-\\d{4}$" } },
90
+ ],
91
+ },
92
+ },
93
+ },
94
+ ],
95
+ };
96
+
97
+ const emergencyContactPreset: PresetData = {
98
+ type: "custom-emergencyContact",
99
+ label: "Emergency Contact",
100
+ icon: "emergencyContact",
101
+ components: [
102
+ {
103
+ uuid: "ec-group",
104
+ type: "group",
105
+ properties: { label: "Emergency Contact", showLabel: true },
106
+ components: [
107
+ {
108
+ uuid: "ec-name",
109
+ type: "inputText",
110
+ properties: {
111
+ label: "Contact Name",
112
+ width: "half",
113
+ validation: { rules: [{ type: "required" }] },
114
+ },
115
+ },
116
+ {
117
+ uuid: "ec-phone",
118
+ type: "inputText",
119
+ properties: {
120
+ label: "Contact Phone",
121
+ placeholder: "(555) 555-5555",
122
+ inputType: "tel",
123
+ width: "half",
124
+ validation: { rules: [{ type: "required" }, { type: "phone" }] },
125
+ },
126
+ },
127
+ {
128
+ uuid: "ec-relationship",
129
+ type: "dropDown",
130
+ properties: {
131
+ label: "Relationship",
132
+ options: [
133
+ { label: "Spouse", value: "spouse" },
134
+ { label: "Parent", value: "parent" },
135
+ { label: "Sibling", value: "sibling" },
136
+ { label: "Friend", value: "friend" },
137
+ { label: "Other", value: "other" },
138
+ ],
139
+ validation: { rules: [{ type: "required" }] },
140
+ },
141
+ },
142
+ ],
143
+ },
144
+ ],
145
+ };
146
+ ```
147
+
148
+ ### Full template example
149
+
150
+ ```ts
151
+ import type { TemplateData } from "@sonata-innovations/fiber-types";
152
+
153
+ const onboardingTemplate: TemplateData = {
154
+ type: "custom-employeeOnboarding",
155
+ label: "Employee Onboarding",
156
+ icon: "employeeOnboarding",
157
+ flow: {
158
+ uuid: "onboarding-flow",
159
+ metadata: { name: "Employee Onboarding" },
160
+ config: {},
161
+ screens: [
162
+ {
163
+ uuid: "onboarding-screen-1",
164
+ label: "Personal Info",
165
+ components: [
166
+ {
167
+ uuid: "ob-header-1",
168
+ type: "header",
169
+ properties: { value: "Employee Information" },
170
+ },
171
+ {
172
+ uuid: "ob-first-name",
173
+ type: "inputText",
174
+ properties: {
175
+ label: "First Name",
176
+ width: "half",
177
+ validation: { rules: [{ type: "required" }] },
178
+ },
179
+ },
180
+ {
181
+ uuid: "ob-last-name",
182
+ type: "inputText",
183
+ properties: {
184
+ label: "Last Name",
185
+ width: "half",
186
+ validation: { rules: [{ type: "required" }] },
187
+ },
188
+ },
189
+ {
190
+ uuid: "ob-email",
191
+ type: "inputText",
192
+ properties: {
193
+ label: "Email Address",
194
+ placeholder: "name@company.com",
195
+ inputType: "email",
196
+ validation: { rules: [{ type: "required" }, { type: "email" }] },
197
+ },
198
+ },
199
+ ],
200
+ },
201
+ {
202
+ uuid: "onboarding-screen-2",
203
+ label: "Employment Details",
204
+ components: [
205
+ {
206
+ uuid: "ob-header-2",
207
+ type: "header",
208
+ properties: { value: "Employment Details" },
209
+ },
210
+ {
211
+ uuid: "ob-job-title",
212
+ type: "inputText",
213
+ properties: {
214
+ label: "Job Title",
215
+ validation: { rules: [{ type: "required" }] },
216
+ },
217
+ },
218
+ {
219
+ uuid: "ob-employment-type",
220
+ type: "dropDown",
221
+ properties: {
222
+ label: "Employment Type",
223
+ options: [
224
+ { label: "Full-Time", value: "full-time" },
225
+ { label: "Part-Time", value: "part-time" },
226
+ { label: "Contract", value: "contract" },
227
+ { label: "Intern", value: "intern" },
228
+ ],
229
+ validation: { rules: [{ type: "required" }] },
230
+ },
231
+ },
232
+ ],
233
+ },
234
+ ],
235
+ },
236
+ };
237
+ ```
238
+
239
+ Remember: dropping this template does **not** overwrite the builder's current flow — its two screens are appended after the existing ones, and "Personal Info" becomes the active screen. The `metadata.name` above documents the template but is not merged into the current flow.
240
+
241
+ ### Passing to FBT
242
+
243
+ ```tsx
244
+ import { FBT } from "@sonata-innovations/fiber-fbt";
245
+ import "@sonata-innovations/fiber-fbt/styles";
246
+ import type { AnyPresetDefinition, AnyTemplateDefinition } from "@sonata-innovations/fiber-fbt";
247
+
248
+ const MY_PRESETS: AnyPresetDefinition[] = [ssnPreset, emergencyContactPreset];
249
+ const MY_TEMPLATES: AnyTemplateDefinition[] = [onboardingTemplate];
250
+
251
+ function App() {
252
+ return (
253
+ <FBT
254
+ flow={existingFlow}
255
+ onFlowChange={handleFlowChange}
256
+ customPresets={MY_PRESETS}
257
+ customTemplates={MY_TEMPLATES}
258
+ />
259
+ );
260
+ }
261
+ ```
262
+
263
+ Both props are optional; if omitted, FBT shows only its built-in items. Keep the arrays referentially stable (module constants or memoized) — the context memoizes its lookup maps on the array identities.
264
+
265
+ ## Factory-based definitions (back-compat)
266
+
267
+ ```ts
268
+ // @sonata-innovations/fiber-fbt
269
+ export type CustomPresetDefinition = {
270
+ type: string;
271
+ label: string;
272
+ icon: string;
273
+ factory: () => Component[];
274
+ };
275
+
276
+ export type CustomTemplateDefinition = {
277
+ type: string;
278
+ label: string;
279
+ icon: string;
280
+ factory: () => Flow;
281
+ };
282
+ ```
283
+
284
+ With a factory, **you** are responsible for returning fresh UUIDs on every call — FBT does not regenerate UUIDs for factory-based definitions. You would still use a factory when the definition must be computed at drop time (e.g. injecting the current date, user data, or environment-dependent options):
285
+
286
+ ```ts
287
+ import type { CustomPresetDefinition } from "@sonata-innovations/fiber-fbt";
288
+
289
+ const visitDatePreset: CustomPresetDefinition = {
290
+ type: "custom-visitDate",
291
+ label: "Visit Date",
292
+ icon: "date",
293
+ factory: () => [
294
+ {
295
+ uuid: crypto.randomUUID(),
296
+ type: "date",
297
+ properties: {
298
+ label: "Visit Date",
299
+ min: new Date().toISOString().slice(0, 10), // today, computed at drop
300
+ validation: { rules: [{ type: "required" }] },
301
+ },
302
+ },
303
+ ],
304
+ };
305
+ ```
306
+
307
+ For anything static, prefer `PresetData`/`TemplateData`.
308
+
309
+ ## How resolution works
310
+
311
+ 1. **Context injection** — the FBT provider builds `presetMap` / `templateMap` (`Map<string, () => Component[] | Flow>`) from the prop arrays, keyed by `type`. Factory definitions map to their `factory`; data definitions map to a wrapper that regenerates UUIDs. If two custom definitions share a `type`, the later one in the array wins (plain `Map.set` overwrite).
312
+ 2. **Kind by prop array** — items from `customPresets` are tagged `kind: "preset"`, items from `customTemplates` are tagged `kind: "template"` when the pool renders them. The `type` string plays no part in this.
313
+ 3. **Built-in-first lookup at drop** — the DnD handler resolves the dropped `type` against the **built-in** definitions first, and only falls back to the custom map if that returns nothing:
314
+
315
+ ```ts
316
+ // fbt/src/ui/dnd/dnd-wrapper.tsx
317
+ const flow = createTemplate(item.type) ?? templateMap.get(item.type)?.();
318
+ const components = createPreset(item.type) ?? presetMap.get(item.type)?.();
319
+ ```
320
+
321
+ ### Collision rules
322
+
323
+ There is **no required prefix** on custom `type` strings — FBT accepts any string. But because built-in lookup runs first, a custom definition whose `type` equals a built-in key is **silently shadowed**: your pool entry shows your label and icon, but dropping it produces the built-in content.
324
+
325
+ Built-in keys to avoid — presets: `preset-phone`, `preset-email`, `preset-date`, `preset-url`, `preset-fullName`, `preset-dollarAmount`, `preset-password`, `preset-addressBlock`, `preset-contactInfo`, `preset-nameFields`, `preset-agreement`; templates: `template-contactForm`, `template-signUp`, `template-patientIntake`, `template-feedbackSurvey`.
326
+
327
+ **Recommendation:** use a distinct namespace such as `custom-*`. No built-in uses it, and it matches what the Fiber server enforces for stored definitions (see below).
328
+
329
+ ## Icon catalog
330
+
331
+ `icon` is a key into FBT's `ICON_MAP` (`fbt/src/lib/icons.tsx`) — 46 built-in SVG icons. Icon keys are their own namespace: they often mirror component types but are not the same set (e.g. the paragraph-text icon is `descriptionText` while the component type is `text`; `select` is the dropdown icon). If the key is unknown, the item renders with an empty icon slot — no error, graceful fallback.
332
+
333
+ ### Display & structure
334
+
335
+ | Key | Glyph |
336
+ |-----|-------|
337
+ | `header` | Heading lines |
338
+ | `descriptionText` | Paragraph lines |
339
+ | `divider` | Horizontal rule |
340
+ | `callout` | Info box |
341
+ | `table` | Grid |
342
+
343
+ ### Text & number inputs
344
+
345
+ | Key | Glyph |
346
+ |-----|-------|
347
+ | `textInput` | Single-line field |
348
+ | `textArea` | Multi-line field |
349
+ | `numberInput` | Field with `#` |
350
+ | `dollarAmount` | Field with `$` |
351
+ | `phone` | Phone handset/device |
352
+ | `email` | Envelope |
353
+ | `url` | Chain link |
354
+ | `password` | Padlock |
355
+
356
+ ### Selection
357
+
358
+ | Key | Glyph |
359
+ |-----|-------|
360
+ | `select` | Dropdown |
361
+ | `multiSelect` | Checked list |
362
+ | `checkbox` | Checked box |
363
+ | `radio` | Radio dot |
364
+ | `toggleSwitch` | Switch |
365
+ | `yesNo` | Check/cross pair |
366
+ | `confirm` | Checked box (shares the `checkbox` glyph) |
367
+ | `cardSelect` | Card grid |
368
+
369
+ ### Date & time
370
+
371
+ | Key | Glyph |
372
+ |-----|-------|
373
+ | `date` | Calendar |
374
+ | `time` | Clock |
375
+ | `dateTime` | Calendar + clock |
376
+ | `dateRange` | Two calendars |
377
+ | `timeRange` | Two clocks |
378
+ | `dateTimeRange` | Two calendar+clock pairs |
379
+
380
+ ### Interactive & special
381
+
382
+ | Key | Glyph |
383
+ |-----|-------|
384
+ | `fileUpload` | Upload arrow |
385
+ | `rating` | Star |
386
+ | `slider` | Slider track |
387
+ | `colorPicker` | Color wheel |
388
+ | `signature` | Signature stroke |
389
+ | `repeater` | Stacked dashed rows |
390
+ | `calculated` | `fx` in a box |
391
+
392
+ ### People & preset-flavored
393
+
394
+ | Key | Glyph |
395
+ |-----|-------|
396
+ | `fullName` | Person |
397
+ | `addressBlock` | Map pin |
398
+ | `contactInfo` | Contact card |
399
+ | `nameFields` | Paired fields |
400
+ | `agreement` | Document with check |
401
+ | `ssn` | Masked field (`***`) |
402
+ | `emergencyContact` | Person with plus |
403
+
404
+ ### Template-flavored
405
+
406
+ | Key | Glyph |
407
+ |-----|-------|
408
+ | `signUpForm` | Form with avatar |
409
+ | `patientIntake` | Bulleted form |
410
+ | `feedbackSurvey` | Form with star |
411
+ | `contactForm` | Form lines |
412
+ | `employeeOnboarding` | Document lines |
413
+
414
+ ## Server-stored definitions
415
+
416
+ The Fiber server (private package, not published) provides tenant-scoped storage for data-based definitions:
417
+
418
+ - CRUD endpoints at `/api/v1/presets` and `/api/v1/templates`, storing `PresetData` / `TemplateData` JSON per tenant.
419
+ - The server **rejects** any stored definition whose `type` does not start with `custom-` — this prefix guarantees no collision with built-in keys.
420
+ - The portal provides management pages for presets and templates, and its flow editor fetches the tenant's custom definitions on mount.
421
+
422
+ From FBT's perspective there is nothing special about server-stored definitions: the parent app fetches them and passes them through the same `customPresets` / `customTemplates` props as data-based definitions.
423
+
424
+ ## UI behavior
425
+
426
+ - When either prop array is non-empty, the corresponding pool tab (Presets or Templates) appends a **CUSTOM** section after the built-in sections, containing one item per definition.
427
+ - Custom items carry a **star badge** in place of the colored accent that built-in preset/template items get; search results label them with a "Custom" badge.
428
+ - Items are drag-and-drop onto the stage. Icon resolution is `ICON_MAP[item.icon]` guarded by `{Icon && <Icon />}` — an unknown key renders nothing.
@@ -2,9 +2,11 @@
2
2
  title: Fiber Concepts
3
3
  applies-to:
4
4
  - "@sonata-innovations/fiber-types@^2.2"
5
+ - "@sonata-innovations/fiber-shared@^1.0"
5
6
  - "@sonata-innovations/fiber-fbre@^3.3"
6
7
  - "@sonata-innovations/fiber-fbt@^2.2"
7
8
  - "@sonata-innovations/fiber-fbtl@^2.2"
9
+ - "@sonata-innovations/fiber-theme-editor@^1.0"
8
10
  read-when: "First contact with Fiber: the Flow/Screen/Component model, FlowData, builders vs render engine, conditions/validation/calculations concepts."
9
11
  ---
10
12
 
@@ -22,7 +24,7 @@ read-when: "First contact with Fiber: the Flow/Screen/Component model, FlowData,
22
24
  Fiber is a system for building and rendering data-collection forms. It splits authoring from rendering and offers two authoring surfaces targeted at different audiences:
23
25
 
24
26
  - **FBT (Fiber Tool)** — A visual drag-and-drop builder for power users / form designers. Full feature surface: screens, multi-rule conditions, validation, calculations, reference markup, the complete 30-type component palette. See the [FBT integration guide](@sonata-innovations/fiber-fbt/docs/integration/fbt.md).
25
- - **FBTL (Fiber Tool Lite)** — A stripped-down, end-user-friendly builder designed to be embedded in parent apps so non-technical end users (clinicians, teachers, etc.) can author their own forms. Five question types + Information Screens + single-rule conditions. Preserves advanced properties from loaded flows without editing them. See the [FBTL integration guide](@sonata-innovations/fiber-fbtl/docs/integration/fbtl.md).
27
+ - **FBTL (Fiber Tool Lite)** — A stripped-down, end-user-friendly builder designed to be embedded in parent apps so non-technical end users (clinicians, teachers, etc.) can author their own forms. Seven question types + Information Screens + single-rule conditions. Preserves advanced properties from loaded flows without editing them. See the [FBTL integration guide](@sonata-innovations/fiber-fbtl/docs/integration/fbtl.md).
26
28
  - **FBRE (Fiber Render Engine)** — A render engine that consumes Flow JSON, renders the interactive form for end users, and outputs collected data ("FlowData") back to the parent application. See the [FBRE integration guide](@sonata-innovations/fiber-fbre/docs/integration/fbre.md).
27
29
 
28
30
  A fourth library, the **Theme Editor**, is a plug-and-play widget that lets end users visually customize a flow's theme (color scheme, style, palette knobs) and emits the resulting `ThemeConfig`. See the [Theme Editor integration guide](@sonata-innovations/fiber-theme-editor/docs/integration/theme-editor.md).
@@ -92,7 +94,7 @@ The optional `config` object controls runtime behavior, organized into semantic
92
94
  | `theme` | `colorScheme` | Built-in palette preset: `"light"` (default) or `"dark"` (replaces the former `darkMode` boolean) |
93
95
  | `theme` | `style` | Visual style (`"clean"`, `"outlined"`, …) |
94
96
  | `theme` | `background` / `surface` / `text` / `border` | Palette knobs overriding the preset tokens |
95
- | `theme` | `radius` / `fontFamily` | Corner radius and font family knobs |
97
+ | `theme` | `radius` / `fontFamily` | Corner radius and font family knobs. `fontFamily` also accepts `{ family, src }`, which FBRE loads itself rather than assuming the host page already has the font |
96
98
  | `theme` | `error` / `success` / `warning` | Semantic state color knobs |
97
99
  | `navigation` | `transition` | Screen transition animation type |
98
100
  | `navigation` | `allowInvalidTransition` | Allow navigating past screens with validation errors |
@@ -291,11 +293,11 @@ The formula engine lives in `@sonata-innovations/fiber-shared`, so calculations
291
293
  ### Authoring Phase (FBT or FBTL)
292
294
 
293
295
  1. A form author opens **FBT** (power users) or **FBTL** (non-technical end users) in a parent application
294
- 2. They assemble the flow — FBT via multi-screen drag-and-drop with the full component palette; FBTL via a flat, one-question-per-screen list limited to five question types plus Information Screens
296
+ 2. They assemble the flow — FBT via multi-screen drag-and-drop with the full component palette; FBTL via a flat, one-question-per-screen list limited to seven question types plus Information Screens
295
297
  3. The builder produces a Flow JSON object via the `onFlowChange` callback (FBT also exposes `exportFlow()` on the store)
296
298
  4. The parent application saves the Flow JSON (to a database, file, API, etc.)
297
299
 
298
- FBTL emits a flow with one component per screen (conversational mode) and promotes component-level conditions to screen-level conditions on emit. Loading a multi-screen flow into FBTL flattens it into the one-per-screen shape on the next save, while preserving all advanced properties (calculations, reference markup, multi-rule conditions, etc.) that FBTL itself cannot edit. The resulting JSON remains fully interoperable with FBT.
300
+ FBTL uses a **page-break model**: each stage card carries a break-after flag, so a freshly authored flow is one question per screen until the author merges cards onto a shared screen. Screen structure round-trips a loaded multi-screen flow's boundaries become break flags rather than being flattened away — and the host can switch the builder to a single-screen model entirely (`options.screenModel`). A component-level condition is promoted to a screen-level condition only when its screen ends up holding exactly one component; otherwise it stays on the component as an in-place show/hide. Advanced properties FBTL cannot edit (calculations, reference markup, multi-rule conditions, and so on) are preserved untouched, and the resulting JSON remains fully interoperable with FBT.
299
301
 
300
302
  ### Rendering Phase (FBRE)
301
303
 
@@ -386,6 +388,8 @@ The markup → HTML converter lives in `@sonata-innovations/fiber-shared`, share
386
388
 
387
389
  `${...}` tokens interpolate live values into text. A token is resolved in order against: **calculations** (by UUID) → **component values** (by UUID) → **context** (by key, from FBRE's `context` prop). Unresolvable references render as empty. This powers dynamic text like "Your total is ${calc-uuid}" or "Welcome back, ${userName}".
388
390
 
391
+ Writing one means knowing a UUID, so builders offer a picker. `resolvableReferences(flow)` from `@sonata-innovations/fiber-shared` returns every reference a flow can resolve, in that same order — use it rather than re-walking the flow, so a picker cannot drift from what the renderer will actually resolve. See [Confirmation Screen → Building your own picker](@sonata-innovations/fiber-fbre/docs/features/confirmation-screen.md#building-your-own-picker).
392
+
389
393
  ---
390
394
 
391
395
  ## 11. Extension Points
@@ -406,7 +410,7 @@ A template is likewise data — a `TemplateData` object (`{ type, label, icon, f
406
410
 
407
411
  Presets and templates being plain data (rather than code) is what enables server-side storage: the hosted platform stores tenant-scoped custom presets and templates in the database and serves them to FBT at load time. Older factory-function definitions (functions returning fresh `Component`/`Flow` objects) are still accepted for back-compat, but data-based definitions are the current model.
408
412
 
409
- See [`custom-presets-and-templates.md`](@sonata-innovations/fiber-fbt/docs/features/custom-presets-and-templates.md) for the full authoring guide.
413
+ See [`custom-presets-and-templates.md`](features/custom-presets-and-templates.md) for the full authoring guide.
410
414
 
411
415
  ---
412
416
 
@@ -37,7 +37,7 @@ Flow
37
37
  | Field | Type | Default | Description |
38
38
  |-------|------|---------|-------------|
39
39
  | `mode` | `FlowModeType` | `"standard"` | `"standard"` or `"conversational"` — conversational mode centers content, auto-advances on selection, and Enter-advances on text inputs |
40
- | `theme` | `ThemeConfig` | — | `{ color?, colorScheme?, style?, background?, surface?, text?, border?, radius?, fontFamily?, error?, success?, warning? }` — visual theme settings (palette knobs + preset) |
40
+ | `theme` | `ThemeConfig` | — | `{ color?, colorScheme?, style?, background?, surface?, text?, border?, radius?, fontFamily?, error?, success?, warning? }` — visual theme settings (palette knobs + preset). `fontFamily` is a CSS stack string **or** `{ family, src }` for a font FBRE loads itself |
41
41
  | `navigation` | `NavigationConfig` | — | `{ transition?, allowInvalidTransition? }` — screen navigation settings |
42
42
  | `controls` | `ControlsConfig` | — | `{ show?, layout?, showStepper?, stepperStyle? }` — navigation controls settings |
43
43
  | `confirmation` | `ConfirmationConfig` | — | `{ show?, title?, body? }` — terminal thank-you screen (see [Confirmation Screen](@sonata-innovations/fiber-fbre/docs/integration/fbre.md#confirmation-screen)) |
@@ -70,8 +70,11 @@
70
70
  "description": "Corner radius as a CSS length (--fbre-radius), e.g. '3px'."
71
71
  },
72
72
  "fontFamily": {
73
- "type": "string",
74
- "description": "Font family stack (--fbre-font)."
73
+ "description": "Font family (--fbre-font). A plain CSS stack string uses whatever font the host page already provides; an object additionally carries the sources FBRE registers itself via ensureFontLoaded, which is what makes a brand font work inside a shadow root.",
74
+ "oneOf": [
75
+ { "type": "string" },
76
+ { "$ref": "#/$defs/FontFamilyConfig" }
77
+ ]
75
78
  },
76
79
  "error": {
77
80
  "type": "string",
@@ -93,6 +96,73 @@
93
96
  }
94
97
  },
95
98
 
99
+ "FontSource": {
100
+ "type": "object",
101
+ "additionalProperties": false,
102
+ "required": ["url"],
103
+ "properties": {
104
+ "url": {
105
+ "type": "string",
106
+ "description": "Where the font file is served from."
107
+ },
108
+ "format": {
109
+ "type": "string",
110
+ "description": "CSS format() hint, e.g. 'woff2'."
111
+ }
112
+ }
113
+ },
114
+ "FontFaceConfig": {
115
+ "type": "object",
116
+ "description": "One @font-face — a single weight/style of the family.",
117
+ "additionalProperties": false,
118
+ "required": ["src"],
119
+ "properties": {
120
+ "src": {
121
+ "type": "array",
122
+ "items": { "$ref": "#/$defs/FontSource" },
123
+ "minItems": 1
124
+ },
125
+ "weight": {
126
+ "type": "string",
127
+ "description": "CSS font-weight: '400', '700', or a variable range like '100 900'."
128
+ },
129
+ "style": {
130
+ "type": "string",
131
+ "enum": ["normal", "italic", "oblique"]
132
+ },
133
+ "display": {
134
+ "type": "string",
135
+ "enum": ["auto", "block", "swap", "fallback", "optional"]
136
+ },
137
+ "unicodeRange": { "type": "string" }
138
+ }
139
+ },
140
+ "FontFamilyConfig": {
141
+ "type": "object",
142
+ "description": "A brand font the renderer can load, rather than a family name it can only hope the host page already has.",
143
+ "additionalProperties": false,
144
+ "required": ["family"],
145
+ "properties": {
146
+ "family": {
147
+ "type": "string",
148
+ "description": "CSS family name, used for both the @font-face and the token value."
149
+ },
150
+ "src": {
151
+ "type": "array",
152
+ "items": { "$ref": "#/$defs/FontSource" },
153
+ "description": "Shorthand for a single regular face. Merged with faces."
154
+ },
155
+ "faces": {
156
+ "type": "array",
157
+ "items": { "$ref": "#/$defs/FontFaceConfig" },
158
+ "description": "Additional faces — a second weight, an italic."
159
+ },
160
+ "stack": {
161
+ "type": "string",
162
+ "description": "Full CSS stack written to --fbre-font. Defaults to the family plus a system fallback."
163
+ }
164
+ }
165
+ },
96
166
  "NavigationConfig": {
97
167
  "type": "object",
98
168
  "description": "Screen navigation settings.",
@@ -136,7 +206,7 @@
136
206
 
137
207
  "ConfirmationConfig": {
138
208
  "type": "object",
139
- "description": "Terminal 'thank you' / confirmation screen shown after the flow is submitted. Presentation-only, so it lives on the config rather than as a Screen. Renders only when 'show' is not false and 'title' or 'body' has content (or the parent supplies an override via the onFlowComplete return value).",
209
+ "description": "Terminal 'thank you' / confirmation screen shown after the flow is submitted. Presentation-only, so it lives on the config rather than as a Screen. In local and remote modes it renders only when 'show' is not false and 'title' or 'body' has content (or the parent supplies an override via the onFlowComplete return value); server-driven mode falls back to a generic 'Thank you' when nothing is configured. An explicit 'show': false renders nothing in every mode.",
140
210
  "properties": {
141
211
  "show": {
142
212
  "type": "boolean",
@@ -250,7 +320,7 @@
250
320
  ]
251
321
  },
252
322
  "properties": {
253
- "description": "Type-specific properties. JSON schema is the open ComponentProperties shape below; TypeScript narrows it per-type via the variant map in fiber-types/src/component-variants.ts.",
323
+ "description": "Type-specific properties. JSON schema is the open ComponentProperties shape below; TypeScript narrows it per-type via the variant map in types/src/component-variants.ts.",
254
324
  "$ref": "#/$defs/ComponentProperties"
255
325
  },
256
326
  "conditions": { "$ref": "#/$defs/FlowConditionConfig" },
@@ -66,11 +66,40 @@ The theme has two layers: `colorScheme` + `style` pick built-in presets (the see
66
66
  | `text` | `string` | No | Primary text color (`--fbre-text`); derives secondary/placeholder/label |
67
67
  | `border` | `string` | No | Border/rule color (`--fbre-border`); derives hover/light/subtle |
68
68
  | `radius` | `string` | No | Corner radius, any CSS length (`--fbre-radius`), e.g. `"3px"` |
69
- | `fontFamily` | `string` | No | Font family stack (`--fbre-font`) |
69
+ | `fontFamily` | `string \| FontFamilyConfig` | No | Font family (`--fbre-font`). See [FontFamilyConfig](#fontfamilyconfig) |
70
70
  | `error` | `string` | No | Error state color (`--fbre-error`) |
71
71
  | `success` | `string` | No | Success state color (`--fbre-success`) |
72
72
  | `warning` | `string` | No | Warning state color (`--fbre-warning`) |
73
73
 
74
+ #### FontFamilyConfig
75
+
76
+ A plain string is a CSS stack and nothing more: the form renders in that family only if the host page already loaded it, and falls back to `system-ui` otherwise — silently, and to something that looks approximately fine. Passing an object instead gives the renderer the sources, and FBRE registers the faces in the owning document itself.
77
+
78
+ That matters most where the host cannot patch around it. FBRE is designed to mount inside a shadow root, and `@font-face` rules declared inside a shadow root's stylesheet are never registered — faces resolve at document level only.
79
+
80
+ | Field | Type | Required | Description |
81
+ | -------- | ------------------ | -------- | ------------------------------------------------------------------ |
82
+ | `family` | `string` | Yes | CSS family name, used for both the `@font-face` and the token value |
83
+ | `src` | `FontSource[]` | No | Shorthand for a single regular face. Merged with `faces` |
84
+ | `faces` | `FontFaceConfig[]` | No | Additional faces — a second weight, an italic |
85
+ | `stack` | `string` | No | Full stack written to `--fbre-font`. Defaults to the family plus a system fallback |
86
+
87
+ `FontSource` is `{ url, format? }`; `FontFaceConfig` is `{ src, weight?, style?, display?, unicodeRange? }`.
88
+
89
+ ```json
90
+ {
91
+ "fontFamily": {
92
+ "family": "Brand Sans",
93
+ "src": [{ "url": "https://cdn.example.com/brand.woff2", "format": "woff2" }],
94
+ "faces": [
95
+ { "src": [{ "url": "https://cdn.example.com/brand-bold.woff2", "format": "woff2" }], "weight": "700" }
96
+ ]
97
+ }
98
+ }
99
+ ```
100
+
101
+ See [FBRE Theming Guide → Loading a brand font](@sonata-innovations/fiber-fbre/docs/features/fbre-theming.md#loading-a-brand-font).
102
+
74
103
  #### Style Types
75
104
 
76
105
  **Standard mode styles** (6):
@@ -113,7 +142,7 @@ Each style has a default stepper visual (see `stepperStyle`). When switching for
113
142
 
114
143
  ### ConfirmationConfig
115
144
 
116
- A terminal "thank you" screen shown after the flow is submitted (once `onFlowComplete` resolves). It is presentation-only — not a data-collection `Screen` — so it lives on the config. The screen renders only when `show` is not `false` **and** `title` or `body` has content. When shown, it replaces the final screen and the navigation controls/stepper are hidden.
145
+ A terminal "thank you" screen shown after the flow is submitted (once `onFlowComplete` resolves). It is presentation-only — not a data-collection `Screen` — so it lives on the config. In local and remote modes the screen renders only when `show` is not `false` **and** `title` or `body` has content; server-driven mode falls back to a generic "Thank you" when nothing is configured, and renders nothing on an explicit `show: false` (see [Per-mode behavior](@sonata-innovations/fiber-fbre/docs/features/confirmation-screen.md#per-mode-behavior)). When shown, it replaces the final screen and the navigation controls/stepper are hidden.
117
146
 
118
147
  `title` and `body` support the same `${...}` reference markup as display components — references resolve against collected field values, calculations, and external `context` values (by name). A parent application can also override the configured message at runtime by returning (or resolving with) a `{ title?, body? }` object from `onFlowComplete` — useful for post-submit data such as a server-generated reference number.
119
148
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sonata-innovations/fiber-types",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "description": "TypeScript type definitions for the Fiber form builder system",
5
5
  "keywords": [
6
6
  "fiber",