@sonata-innovations/fiber-fbre 3.2.1 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -31,6 +31,7 @@ import { ComponentYesNoProperties } from '@sonata-innovations/fiber-types';
31
31
  import { ConditionGroup } from '@sonata-innovations/fiber-types';
32
32
  import { ConditionOperator } from '@sonata-innovations/fiber-types';
33
33
  import { ConditionRule } from '@sonata-innovations/fiber-types';
34
+ import { ConfirmationConfig } from '@sonata-innovations/fiber-types';
34
35
  import { ControlsConfig } from '@sonata-innovations/fiber-types';
35
36
  import { DateFormat } from '@sonata-innovations/fiber-types';
36
37
  import { FC } from 'react';
@@ -143,6 +144,22 @@ export { ConditionOperator }
143
144
 
144
145
  export { ConditionRule }
145
146
 
147
+ export { ConfirmationConfig }
148
+
149
+ /**
150
+ * Parent-supplied override for the terminal confirmation screen.
151
+ *
152
+ * Returned (directly or via a resolved Promise) from `onFlowComplete`. When an
153
+ * object is returned it replaces the flow's configured `confirmation` message —
154
+ * useful for content only known after submit, e.g. a server-generated
155
+ * reference number. `title`/`body` support the same `${...}` reference markup
156
+ * as the configured message.
157
+ */
158
+ export declare type ConfirmationResult = {
159
+ title?: string;
160
+ body?: string;
161
+ };
162
+
146
163
  export { ControlsConfig }
147
164
 
148
165
  export { DateFormat }
@@ -173,9 +190,16 @@ declare interface FBRECallbackProps {
173
190
  screenIndex?: number;
174
191
  context?: Record<string, string | boolean | number>;
175
192
  storeRef?: React.MutableRefObject<StoreApi<FBREStoreState> | null>;
176
- onFlowComplete: (data: any) => void | Promise<void>;
193
+ /**
194
+ * Called when the flow is submitted, with the collected {@link FlowData}.
195
+ * Return (or resolve with) a {@link ConfirmationResult} to override the
196
+ * flow's configured confirmation message — e.g. with a server-generated
197
+ * reference number. Returning a Promise keeps the submit button in its
198
+ * in-flight state until it settles; a rejection surfaces as a completion
199
+ * error and the confirmation is not shown.
200
+ */
201
+ onFlowComplete: (data: FlowData) => void | ConfirmationResult | Promise<void | ConfirmationResult>;
177
202
  onScreenChange?: (index: number, data: any) => void;
178
- onScreenValidationChange?: (index: number, data: any) => void;
179
203
  }
180
204
 
181
205
  export declare interface FBRELocalProps extends FBRECallbackProps {
@@ -248,7 +272,7 @@ export declare type FBREStoreState = {
248
272
  getValidationErrors: (uuid: string) => string[];
249
273
  updateContext: (context: Record<string, string | boolean | number>) => void;
250
274
  evaluateFlowValidation: () => boolean;
251
- getMaxScreenCount: () => number;
275
+ getMaxScreenIndex: () => number;
252
276
  getCalculationResult: (uuid: string) => number | null;
253
277
  };
254
278
 
@@ -0,0 +1,125 @@
1
+ ---
2
+ title: Confirmation ("Thank You") Screen
3
+ applies-to:
4
+ - "@sonata-innovations/fiber-fbre@^3.3"
5
+ - "@sonata-innovations/fiber-fbt@^2.2"
6
+ read-when: "Configuring the post-submit thank-you screen: config.confirmation, dynamic ${...} content, and the onFlowComplete return contract."
7
+ ---
8
+
9
+ <!-- Generated from the Fiber repo's docs/ tree by project/scripts/sync-package-docs.mjs. Do not edit here. -->
10
+ <!-- Spec: consumed by FBRE (fbre/src/ui/confirmation), FBT (editor-settings), fiber-types (ConfirmationConfig) -->
11
+
12
+ # Confirmation ("Thank You") Screen
13
+
14
+ A terminal screen shown **after** a form is submitted — a centered, formatted thank-you message. It is a flow-level setting, **not** a data-collection screen, so it never appears in the FBT/FBTL stage editor and never collects input.
15
+
16
+ - **Where it lives:** `flow.config.confirmation` (a serializable config group, alongside `theme` / `navigation` / `controls` / `summary`).
17
+ - **When it shows:** once the flow is submitted and `onFlowComplete` settles.
18
+ - **What it renders:** `title` + `body`, centered, with the controls and stepper hidden.
19
+
20
+ ## Quick start
21
+
22
+ ```ts
23
+ const flow = {
24
+ ...myFlow,
25
+ config: {
26
+ ...myFlow.config,
27
+ confirmation: {
28
+ show: true,
29
+ title: "Thank you!",
30
+ body: "Your response has been submitted.",
31
+ },
32
+ },
33
+ };
34
+
35
+ <FBRE flow={flow} onFlowComplete={handleComplete} />;
36
+ ```
37
+
38
+ It renders **only** when `show` is not `false` **and** `title` or `body` has content. There is no built-in default copy — an enabled-but-empty confirmation shows nothing and the flow behaves as before.
39
+
40
+ ### `ConfirmationConfig`
41
+
42
+ | Field | Type | Description |
43
+ | ------- | --------- | -------------------------------------------------------------- |
44
+ | `show` | `boolean` | Explicit off-switch. Content presence is the positive gate. |
45
+ | `title` | `string` | Heading. Supports `${...}` references and text formatting. |
46
+ | `body` | `string` | Message. Supports `${...}` references and text formatting. |
47
+
48
+ ## Dynamic content
49
+
50
+ `title` and `body` run through the same `${...}` reference markup as `text` / `header` / `callout` components. References resolve, in order, against:
51
+
52
+ 1. **Calculations** — `${<calculationUuid>}`
53
+ 2. **Collected field values** — `${<componentUuid>}` (what the user just entered)
54
+ 3. **External context** — `${<contextKey>}` (values the parent passed via the `context` prop, by name)
55
+
56
+ ```ts
57
+ // config.confirmation.body — mixes a collected field and a context value
58
+ "Thanks ${q_name}! A copy is on its way to ${q_email}. Our team at ${support_email} will follow up.";
59
+ ```
60
+
61
+ ```tsx
62
+ // q_name / q_email are component UUIDs; support_email comes from context
63
+ <FBRE flow={flow} context={{ support_email: "support@acme.com" }} onFlowComplete={handleComplete} />
64
+ ```
65
+
66
+ > In FBT, the reference picker inserts field/calculation UUIDs for you. Context keys aren't in the picker yet — type `${key}` by hand for those. Resolved values are HTML-escaped (safe against injection).
67
+
68
+ ## Deciding *when* it shows: the `onFlowComplete` contract
69
+
70
+ The confirmation appears when `onFlowComplete` settles. Its return value drives the behavior:
71
+
72
+ ```ts
73
+ onFlowComplete: (data: FlowData) =>
74
+ void | ConfirmationResult | Promise<void | ConfirmationResult>;
75
+ ```
76
+
77
+ | You return… | What happens |
78
+ | --- | --- |
79
+ | `void` | Confirmation shows immediately (optimistic). |
80
+ | `Promise<void>` | Submit button stays in its **in-flight / loading** state until the promise settles. Resolve → confirmation shows. **Reject → a completion error renders and the confirmation is _not_ shown.** |
81
+ | `ConfirmationResult` (`{ title?, body? }`), synchronously or as the resolved value of a Promise | **Overrides** the configured message. Use for content only known after submit — e.g. a server reference number. |
82
+
83
+ ### Wait for the backend, then confirm with its reference number
84
+
85
+ ```tsx
86
+ import type { FlowData, ConfirmationResult } from "@sonata-innovations/fiber-fbre";
87
+
88
+ const handleComplete = async (data: FlowData): Promise<ConfirmationResult> => {
89
+ const { referenceId } = await saveToBackend(data); // rejects → FBRE shows the error, no confirmation
90
+ return { title: "All done!", body: `Your reference number is ${referenceId}.` };
91
+ };
92
+
93
+ <FBRE flow={flow} onFlowComplete={handleComplete} />;
94
+ ```
95
+
96
+ If you don't need a runtime override, just author the message in `config.confirmation` and return `void` (or a `Promise<void>` to get the in-flight state while your save runs).
97
+
98
+ ## Authoring in FBT
99
+
100
+ Open **Flow Settings → Confirmation Screen**, toggle it on, and fill in the title and message. This writes `config.confirmation` — it is intentionally separate from the stage/question editor. The message fields accept `${...}` references.
101
+
102
+ ## FBTL
103
+
104
+ FBTL doesn't edit this config, but it **carries it through untouched** on load and export. A flow authored with a confirmation in FBT (or by hand) keeps it when round-tripped through FBTL, and FBTL's live FBRE preview renders it.
105
+
106
+ ## Try it
107
+
108
+ FBRE playground → **Confirmation Demo** fixture:
109
+
110
+ ```bash
111
+ cd fbre && npm run dev:playground
112
+ ```
113
+
114
+ Fill in name + email and submit: the demo simulates a ~1.2s server round-trip (showing the in-flight submit state), then renders the confirmation with the field values and a context value interpolated in.
115
+
116
+ ## Exports & schema
117
+
118
+ - Types: `ConfirmationConfig`, `ConfirmationResult` — exported from `@sonata-innovations/fiber-fbre` (and `@sonata-innovations/fiber-types`).
119
+ - Schema: [`flow-schema.md` → ConfirmationConfig](@sonata-innovations/fiber-types/docs/schema/flow-schema.md#confirmationconfig).
120
+ - Integration reference: [FBRE Integration Guide → Confirmation Screen](../integration/fbre.md#confirmation-screen).
121
+
122
+ ## Edge cases
123
+
124
+ - A non-empty `ConfirmationResult` returned from `onFlowComplete` forces the confirmation to render even when `config.confirmation.show` is `false` (the override path bypasses the config gate). An empty object (`{}`) is ignored and does not count as an override.
125
+ - The confirmation screen exists only in local and remote modes. In **server-driven mode** it is not rendered — the parent should present its own terminal state from `onFlowComplete`.
@@ -0,0 +1,264 @@
1
+ ---
2
+ title: FBRE Theming Guide
3
+ applies-to:
4
+ - "@sonata-innovations/fiber-fbre@^3.3"
5
+ read-when: "Theming FBRE-rendered forms: colorScheme presets, palette knobs, raw --fbre-* token overrides, precedence model, full token catalog."
6
+ ---
7
+
8
+ <!-- Generated from the Fiber repo's docs/ tree by project/scripts/sync-package-docs.mjs. Do not edit here. -->
9
+ # FBRE Theming Guide
10
+
11
+ How to theme a form rendered by **FBRE** (`@sonata-innovations/fiber-fbre`) — from a one-line accent change to a full custom brand palette, light or dark.
12
+
13
+ > This is the canonical theming reference. It ships inside the `@sonata-innovations/fiber-fbre` package (`docs/features/fbre-theming.md`), so the copy you are reading matches the version you installed. `fbre/README.md` carries a condensed summary; this document is authoritative.
14
+
15
+ ---
16
+
17
+ ## The mental model
18
+
19
+ FBRE's appearance is driven entirely by **CSS custom properties** (the `--fbre-*` tokens). Everything you see — every fill, border, label, and state color — resolves from one of these tokens. Theming is the act of supplying values for them.
20
+
21
+ There are **two independent axes**:
22
+
23
+ | Axis | Controls | Set via |
24
+ | --- | --- | --- |
25
+ | **`style`** (FlowStyleType) | the *shape*: borders vs. underlines, fills, spacing, radius rhythm | `theme.style` (e.g. `"clean"`, `"defined-outlined"`) |
26
+ | **palette** | the *colors*: ground, surfaces, text, borders, accent, state colors | `colorScheme` + the palette knobs, or raw `--fbre-*` overrides |
27
+
28
+ `style` and palette are orthogonal: any style works with any palette. This guide is about the **palette** axis. (For the list of styles, see the [Flow Schema](@sonata-innovations/fiber-types/docs/schema/flow-schema.md#style-types).)
29
+
30
+ ---
31
+
32
+ ## The precedence chain
33
+
34
+ Palette values resolve through four layers. Each layer overrides the ones above it, so you only specify what you want to change:
35
+
36
+ ```
37
+ 1. Light defaults base.css .fbre-container (lowest)
38
+ 2. Preset: colorScheme: "dark" base.css .fbre-container[data-mode="dark"]
39
+ 3. Knobs: theme.{surface,text,…} inline custom properties on the container
40
+ 4. Raw CSS: your own --fbre-* rules your stylesheet
41
+ ```
42
+
43
+ - **Layer 1–2 (presets)** seed the *entire* token set with a coherent light or dark palette. Picking `colorScheme` swaps which one is active.
44
+ - **Layer 3 (knobs)** are written as **inline** custom properties on the container, so they beat any stylesheet rule targeting the same element — no selector can out-specify an inline declaration.
45
+ - **Layer 4 (raw CSS)** beats the presets and is the escape hatch for every token no knob touches. For a token a knob *has* set, a stylesheet rule on `.fbre-container` loses to the inline knob — to override a knob-set token you need `!important`, or set the token on a **descendant** element so it takes effect lower in the tree.
46
+
47
+ This is why you can set `colorScheme: "dark"` and then override just `surface` and `text` — the rest of the form stays coherent with the dark preset, and your two knobs take effect on top.
48
+
49
+ ---
50
+
51
+ ## 1. Color scheme (presets)
52
+
53
+ `theme.colorScheme` selects the built-in starter palette. Default is `"light"`.
54
+
55
+ ```tsx
56
+ // Via the theme prop (takes precedence over flow config)
57
+ <FBRE flow={flow} theme={{ colorScheme: "dark" }} onFlowComplete={done} />
58
+
59
+ // Via the flow config
60
+ const flow = { ...base, config: { ...base.config, theme: { colorScheme: "dark" } } };
61
+ ```
62
+
63
+ It applies `data-mode="light" | "dark"` on the `.fbre-container` and seeds all `--fbre-*` tokens with light- or dark-appropriate values.
64
+
65
+ > **Migration:** `colorScheme` replaces the former `darkMode: boolean`. `darkMode: true` → `colorScheme: "dark"`; `darkMode: false`/absent → `colorScheme: "light"`. There is no runtime fallback — pre-migrate stored flows.
66
+
67
+ There is intentionally **no `"auto"`** scheme yet (following the viewer's `prefers-color-scheme`). It is an additive future option; today a flow renders one chosen scheme for every viewer.
68
+
69
+ ---
70
+
71
+ ## 2. Palette knobs
72
+
73
+ Knobs live on `theme` and override individual tokens **on top of** the active preset. Set any subset; unset knobs fall through.
74
+
75
+ ```tsx
76
+ <FBRE
77
+ flow={flow}
78
+ theme={{
79
+ colorScheme: "dark",
80
+ color: "#bcd3cd", // accent
81
+ background: "#1a2a3a", // form ground
82
+ surface: "#243244", // input fills / cards
83
+ text: "#e8ede9",
84
+ border: "#2e4158",
85
+ radius: "3px",
86
+ fontFamily: '"Inter", system-ui, sans-serif',
87
+ }}
88
+ onFlowComplete={done}
89
+ />
90
+ ```
91
+
92
+ Each knob sets a **primary token** and **derives** the related tokens from it, so you get a coherent result from a single value:
93
+
94
+ | Knob | Primary token | Also derives |
95
+ | --- | --- | --- |
96
+ | `color` | `--fbre-theme-color` | `--fbre-theme-light`, `--fbre-theme-dark` |
97
+ | `background` | `--fbre-bg` | — |
98
+ | `surface` | `--fbre-surface` | `--fbre-surface-hover/-alt/-subtle`, `--fbre-control-surface`, `--fbre-input-bg`, `--fbre-input-bg-focus` |
99
+ | `text` | `--fbre-text` | `--fbre-text-secondary/-placeholder/-disabled`, `--fbre-label` |
100
+ | `border` | `--fbre-border` | `--fbre-border-hover/-light/-subtle` |
101
+ | `radius` | `--fbre-radius` | — |
102
+ | `fontFamily` | `--fbre-font` | — |
103
+ | `error` | `--fbre-error` | `--fbre-error-light` |
104
+ | `success` | `--fbre-success` | `--fbre-success-bg`, `--fbre-success-border` |
105
+ | `warning` | `--fbre-warning` | `--fbre-warning-bg`, `--fbre-warning-border` |
106
+
107
+ ### How derivation works
108
+
109
+ Knobs are applied as inline custom properties on the container; derived tokens are emitted as `color-mix()` expressions that reference other tokens rather than literal colors — e.g. `--fbre-text-secondary` is roughly `color-mix(in srgb, var(--fbre-text) 64%, var(--fbre-bg))`. Because the inputs are *tokens*, derivations stay **mode-aware**: if you set only `surface`, its derived hover/alt surfaces still mix against the active scheme's `--fbre-bg`/`--fbre-text`. A knob's derived tokens are emitted **only when that knob is set**, so an unset knob leaves the preset's values untouched. The mapping lives in one place: `buildThemeVars()` in `fbre/src/lib/theme-vars.ts`.
110
+
111
+ > Knobs are *colors and a few scalars*, not shape. To change borders-vs-underlines, fills, or spacing rhythm, choose a different `theme.style` — see the orthogonal-axes note above.
112
+
113
+ ---
114
+
115
+ ## 3. Raw CSS escape hatch
116
+
117
+ For anything not exposed as a knob — or for surgical control — override the `--fbre-*` tokens directly in your own stylesheet. Scope to your wrapper so you out-specify the engine's defaults regardless of load order:
118
+
119
+ ```css
120
+ .my-form-wrapper .fbre-container {
121
+ --fbre-surface: #243244;
122
+ --fbre-star-filled: #bcd3cd; /* not a knob — set it raw */
123
+ --fbre-shadow-color: rgba(0, 0, 0, 0.4);
124
+ }
125
+ ```
126
+
127
+ Knobs are applied inline on the container, so a knob and a raw stylesheet rule targeting the *same* token on the same element will see the knob win regardless of selector specificity — override a knob-set token with `!important` or by setting it on a descendant element. In practice: use knobs for the common palette, raw CSS for the long tail of tokens no knob touches.
128
+
129
+ ---
130
+
131
+ ## Token catalog
132
+
133
+ Every themeable value, with light and dark preset defaults. Tokens marked **(derived)** are produced from a knob when that knob is set; you can also set any of them raw.
134
+
135
+ ### Accent
136
+
137
+ | Token | Light | Dark | Knob |
138
+ | --- | --- | --- | --- |
139
+ | `--fbre-theme-color` | `#1976d2` | `#42a5f5` | `color` |
140
+ | `--fbre-theme-light` | `#e3f2fd` | `#1a2332` | (derived) |
141
+ | `--fbre-theme-dark` | `#1565c0` | `#1e88e5` | (derived) |
142
+ | `--fbre-on-primary` | `#fff` | `#fff` | — |
143
+
144
+ ### Ground & surfaces
145
+
146
+ | Token | Light | Dark | Knob |
147
+ | --- | --- | --- | --- |
148
+ | `--fbre-bg` | `#fff` | `#121212` | `background` |
149
+ | `--fbre-surface` | `#fff` | `#1e1e1e` | `surface` |
150
+ | `--fbre-surface-hover` | `#f5f5f5` | `#2a2a2a` | (derived) |
151
+ | `--fbre-surface-alt` | `#e0e0e0` | `#333` | (derived) |
152
+ | `--fbre-surface-subtle` | `#e8e8e8` | `#2a2a2a` | (derived) |
153
+ | `--fbre-control-surface` | `#fff` | `#e0e0e0` | (derived) |
154
+ | `--fbre-input-bg` | `var(--fbre-surface)` | `var(--fbre-surface)` | (derived) |
155
+ | `--fbre-input-bg-focus` | `var(--fbre-bg)` | `var(--fbre-bg)` | (derived) |
156
+
157
+ > Note: some style variants override tokens inside their `[data-style]` block: `defined-outlined` overrides `--fbre-input-bg` and `--fbre-label`, `soft-outlined` overrides `--fbre-input-bg`, and `airy-clean` overrides `--fbre-label`; the outlined variants also adjust `--fbre-layout-gap`. A knob still wins over all of these, because knobs are inline. (Their dark-mode blocks reset input-bg/label back to surface-derived values.)
158
+
159
+ ### Text
160
+
161
+ | Token | Light | Dark | Knob |
162
+ | --- | --- | --- | --- |
163
+ | `--fbre-text` | `#212121` | `#e0e0e0` | `text` |
164
+ | `--fbre-text-secondary` | `#666` | `#9e9e9e` | (derived) |
165
+ | `--fbre-text-placeholder` | `#aaa` | `#666` | (derived) |
166
+ | `--fbre-text-disabled` | `#bbb` | `#555` | (derived) |
167
+ | `--fbre-label` | `var(--fbre-text-secondary)` | `var(--fbre-text-secondary)` | (derived) |
168
+
169
+ ### Borders
170
+
171
+ | Token | Light | Dark | Knob |
172
+ | --- | --- | --- | --- |
173
+ | `--fbre-border` | `#ccc` | `#444` | `border` |
174
+ | `--fbre-border-hover` | `#999` | `#666` | (derived) |
175
+ | `--fbre-border-light` | `#ddd` | `#444` | (derived) |
176
+ | `--fbre-border-subtle` | `#eee` | `#333` | (derived) |
177
+
178
+ ### State colors
179
+
180
+ | Token | Light | Dark | Knob |
181
+ | --- | --- | --- | --- |
182
+ | `--fbre-error` | `#d32f2f` | `#ef5350` | `error` |
183
+ | `--fbre-error-light` | `#fdecea` | `#2c1b1b` | (derived) |
184
+ | `--fbre-success` | `#2d6a28` | `#a8e6a0` | `success` |
185
+ | `--fbre-success-bg` | `#ddffda` | `#1a3318` | (derived) |
186
+ | `--fbre-success-border` | `#b8e6b4` | `#2d5a28` | (derived) |
187
+ | `--fbre-warning` | `#7a5900` | `#ffe082` | `warning` |
188
+ | `--fbre-warning-bg` | `#fff8e1` | `#332b00` | (derived) |
189
+ | `--fbre-warning-border` | `#ffe082` | `#665500` | (derived) |
190
+
191
+ ### Controls
192
+
193
+ | Token | Light | Dark | Knob |
194
+ | --- | --- | --- | --- |
195
+ | `--fbre-toggle-track` | `#ccc` | `#555` | — |
196
+ | `--fbre-star-empty` | `#ddd` | `#444` | — |
197
+ | `--fbre-star-filled` | `#ffc107` | `#ffca28` | — |
198
+ | `--fbre-slider-track` | `#ddd` | `#444` | — |
199
+ | `--fbre-shadow-color` | `rgba(0,0,0,.12)` | `rgba(0,0,0,.4)` | — |
200
+
201
+ ### Shape & motion
202
+
203
+ | Token | Default | Knob |
204
+ | --- | --- | --- |
205
+ | `--fbre-radius` | `4px` | `radius` |
206
+ | `--fbre-font` | `"Segoe UI", system-ui, -apple-system, sans-serif` | `fontFamily` |
207
+ | `--fbre-layout-gap` | `4px` (varies by style) | — |
208
+ | `--fbre-transition-duration` | `250ms` | — |
209
+ | `--fbre-transition-easing` | `cubic-bezier(0.4, 0, 0.2, 1)` | — |
210
+
211
+ The control and motion tokens above have **no dedicated knob** — set them raw if you need to. (Tokens are defined in `fbre/src/styles/base.css`; transition tokens in `transitions.css`.)
212
+
213
+ ---
214
+
215
+ ## Worked example: a dark brand form
216
+
217
+ Goal: a sage-on-navy intake form using the `defined-outlined` style. Two equivalent ways to express it.
218
+
219
+ **With knobs (recommended)** — the engine derives the supporting tokens and keeps the dark scheme coherent:
220
+
221
+ ```tsx
222
+ <FBRE
223
+ flow={flow}
224
+ theme={{
225
+ style: "defined-outlined",
226
+ colorScheme: "dark",
227
+ color: "#bcd3cd",
228
+ background: "#1a2a3a",
229
+ surface: "#243244",
230
+ text: "#e8ede9",
231
+ border: "#2e4158",
232
+ radius: "3px",
233
+ fontFamily: '"Inter", system-ui, sans-serif',
234
+ }}
235
+ onFlowComplete={done}
236
+ />
237
+ ```
238
+
239
+ **With raw CSS** — equivalent for these tokens, and the only option for non-knob tokens (e.g. recoloring rating stars):
240
+
241
+ ```css
242
+ .brand .fbre-container {
243
+ --fbre-bg: #1a2a3a;
244
+ --fbre-surface: #243244;
245
+ --fbre-input-bg: #243244;
246
+ --fbre-text: #e8ede9;
247
+ --fbre-text-secondary: #8fa899;
248
+ --fbre-label: #8fa899;
249
+ --fbre-border: #2e4158;
250
+ --fbre-theme-color: #bcd3cd;
251
+ --fbre-radius: 3px;
252
+ --fbre-font: "Inter", system-ui, sans-serif;
253
+ }
254
+ ```
255
+
256
+ The knob form needs no `data-mode` gymnastics and no per-element selector overrides — input fills, labels, and focus states all follow the tokens.
257
+
258
+ ---
259
+
260
+ ## See also
261
+
262
+ - [Flow Schema — ThemeConfig](@sonata-innovations/fiber-types/docs/schema/flow-schema.md) — the `theme` object's fields and types
263
+ - [FBRE Integration Guide](../integration/fbre.md) — embedding FBRE in a parent app
264
+ - `fbre/README.md` — FBRE props and a condensed theming summary