@explorer02/cfm-survey-sdk 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/dist/cli/index.js +140 -33
  2. package/dist/cli/index.mjs +140 -33
  3. package/dist/index.js +1 -1
  4. package/dist/index.mjs +1 -1
  5. package/package.json +8 -1
  6. package/postinstall.js +8 -5
  7. package/templates/AGENT.md +60 -19
  8. package/templates/deploy-wizard-to-s3.sh +52 -0
  9. package/templates/docs/00-integration/agent-execution-flow.md +281 -0
  10. package/templates/docs/00-integration/agent-operating-contract.md +260 -0
  11. package/templates/docs/00-integration/apply-ui-config.md +365 -0
  12. package/templates/docs/00-integration/canonical-survey-fixtures.md +94 -0
  13. package/templates/docs/00-integration/client-integration-guide.md +9 -7
  14. package/templates/docs/00-integration/client-lib-folder.md +115 -0
  15. package/templates/docs/00-integration/component-checklist.md +88 -62
  16. package/templates/docs/00-integration/constraints.md +13 -5
  17. package/templates/docs/00-integration/custom-field-logic-and-navigation.md +56 -0
  18. package/templates/docs/00-integration/progress.md +4 -2
  19. package/templates/docs/00-integration/setup.md +12 -0
  20. package/templates/docs/00-integration/ui-customization-wizard.md +144 -0
  21. package/templates/docs/00-integration/useSurveySDK.md +35 -1
  22. package/templates/docs/00-integration/wizard-api.md +134 -0
  23. package/templates/docs/00-integration/wizard-config-handoff.md +246 -0
  24. package/templates/docs/00-integration/wizard-preview-build-guide.md +400 -0
  25. package/templates/docs/00-integration/wizard-question-type-styling.md +195 -0
  26. package/templates/docs/01-components/01-survey-page.md +18 -8
  27. package/templates/docs/01-components/02-question.md +2 -2
  28. package/templates/docs/01-components/03-rating-scale.md +12 -18
  29. package/templates/docs/01-components/05-csat-matrix-scale.md +18 -26
  30. package/templates/docs/01-components/06-likert-matrix-scale.md +19 -39
  31. package/templates/docs/01-components/07-slider-matrix-scale.md +48 -33
  32. package/templates/docs/01-components/09-custom-slider-track.md +15 -25
  33. package/templates/docs/01-components/10-header-footer.md +23 -4
  34. package/templates/docs/01-components/11-progress-bar.md +14 -6
  35. package/templates/docs/01-components/19-survey-sticky-chrome.md +79 -0
  36. package/templates/docs/01-components/README.md +2 -2
  37. package/templates/docs/02-reference/question-types/10-slider-matrix.md +38 -26
  38. package/templates/docs/02-reference/routing-table.md +1 -0
  39. package/templates/docs/02-reference/value-derivation.md +1 -1
  40. package/templates/docs/03-ui-specs/00-question-shell.md +10 -0
  41. package/templates/docs/03-ui-specs/01-rating.md +18 -0
  42. package/templates/docs/03-ui-specs/02-radio.md +16 -1
  43. package/templates/docs/03-ui-specs/03-text.md +12 -1
  44. package/templates/docs/03-ui-specs/04-csat.md +84 -50
  45. package/templates/docs/03-ui-specs/06-slider.md +3 -1
  46. package/templates/docs/03-ui-specs/07-matrix-cfm.md +71 -48
  47. package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +48 -18
  48. package/templates/docs/03-ui-specs/09-slider-matrix.md +59 -32
  49. package/templates/docs/03-ui-specs/10-file-upload.md +11 -0
  50. package/templates/docs/03-ui-specs/11-text-and-media.md +4 -0
  51. package/templates/docs/03-ui-specs/12-survey-chrome.md +54 -5
  52. package/templates/docs/03-ui-specs/13-heatmap.md +12 -1
  53. package/templates/docs/03-ui-specs/14-rank-order.md +11 -0
  54. package/templates/docs/03-ui-specs/README.md +4 -2
  55. package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +1 -1
  56. package/templates/docs/MANIFEST.json +162 -6
  57. package/templates/docs/index.md +116 -117
  58. package/templates/docs/templates/CsatMatrixScale.tsx +637 -0
  59. package/templates/docs/templates/FileUploadScale.tsx +262 -0
  60. package/templates/docs/templates/HeatmapScale.tsx +114 -0
  61. package/templates/docs/templates/LikertMatrixScale.tsx +414 -0
  62. package/templates/docs/templates/Question.tsx +3 -0
  63. package/templates/docs/templates/RatingScale.tsx +129 -0
  64. package/templates/docs/templates/SliderMatrixScale.tsx +248 -0
  65. package/templates/docs/templates/SurveyStickyChrome.tsx +24 -0
  66. package/templates/docs/templates/customFieldValues.ts +36 -0
  67. package/templates/docs/templates/implementation_plan.md +56 -9
  68. package/templates/docs/templates/labelStyles.ts +33 -0
  69. package/templates/docs/templates/selectionStyles.ts +100 -0
  70. package/templates/docs/templates/surveyUiIcons.tsx +11 -0
  71. package/templates/docs/templates/surveyUiScaleUtils.ts +51 -0
  72. package/templates/docs/templates/verify-agent-build.sh +119 -0
  73. package/templates/preview-harness/preview-bridge.inline.js +201 -0
  74. package/templates/preview-harness/previewPages.js +108 -0
  75. package/templates/preview-harness/previewPages.ts +298 -0
  76. package/templates/preview-harness/vite-app/src/PreviewConfigContext.tsx +67 -0
  77. package/templates/preview-harness/vite-app/src/QuestionPreview.tsx +122 -0
  78. package/templates/preview-harness/vite-app/src/SurveyPagePreview.tsx +75 -0
  79. package/templates/preview-harness/vite-app/src/fixtures/questions.ts +311 -0
  80. package/templates/preview-harness/vite-app/src/globals.css +16 -0
  81. package/templates/preview-harness/vite-app/src/mount.tsx +9 -0
  82. package/templates/preview-harness/vite-app/src/preview-live-overrides.css +101 -0
  83. package/templates/preview-harness/vite-app/stubs/next-dynamic.tsx +25 -0
  84. package/templates/preview-harness/vite-app/stubs/next-image.tsx +29 -0
  85. package/templates/previewBridge.ts +153 -0
  86. package/templates/survey-theme.css +295 -0
  87. package/templates/survey-ui-config.schema.json +213 -0
  88. package/templates/wizard-dist/assets/PreviewMock-DgHfrVeb.js +1 -0
  89. package/templates/wizard-dist/assets/TypePanel-CFVC3Ptn.js +1 -0
  90. package/templates/wizard-dist/assets/index-BhWM50Yu.css +1 -0
  91. package/templates/wizard-dist/assets/index-DYK3X1e5.js +34 -0
  92. package/templates/wizard-dist/assets/vendor-BwkXDkd3.js +17 -0
  93. package/templates/wizard-dist/index.html +20 -0
  94. package/templates/wizard.html +13 -1129
@@ -0,0 +1,400 @@
1
+ # Wizard Live Preview — Client Build Guide
2
+
3
+ > **Read during Phase 5 (BUILD) — mandatory.** Wizard **run** (Phase 6b) is optional; this contract is required for every agent build.
4
+ > Pipeline: [`agent-execution-flow.md`](agent-execution-flow.md).
5
+
6
+ Related docs:
7
+
8
+ - [`ui-customization-wizard.md`](ui-customization-wizard.md) — wizard flow and CLI
9
+ - [`apply-ui-config.md`](apply-ui-config.md) — applying final `survey-ui-config.json` after wizard
10
+ - [`wizard-api.md`](wizard-api.md) — EC2 `/wizard/*` API
11
+ - [`03-ui-specs/12-survey-chrome.md`](../03-ui-specs/12-survey-chrome.md) — chrome layout spec
12
+
13
+ ---
14
+
15
+ ## Why client code matters for live preview
16
+
17
+ The wizard does **not** re-render your React app. It loads **static preview pages** built from your real `src/components/` (`npx cfm-sdk export-previews`), then sends token updates into each preview iframe via `postMessage`.
18
+
19
+ ```mermaid
20
+ sequenceDiagram
21
+ participant Agent as Agent_build
22
+ participant CLI as export_previews
23
+ participant S3 as S3_previews
24
+ participant Wizard as Wizard_SPA
25
+ participant Iframe as Preview_iframe
26
+
27
+ Agent->>Agent: Build components with token contract
28
+ Agent->>CLI: customize-ui / export-previews
29
+ CLI->>S3: Upload HTML + JS + CSS
30
+ Wizard->>Iframe: Load chrome / MCQ_single / etc.
31
+ Wizard->>Iframe: postMessage SNAPSHOT + DELTA
32
+ Iframe->>Iframe: Apply --cfm-* vars + content patches
33
+ ```
34
+
35
+ If components use **hardcoded** colors (`bg-[#e20074]`) with no `var(--cfm-*)` hooks, the preview **looks correct initially** but **does not change** when the client drags color pickers or edits identity fields (unless a harness override happens to match your class names).
36
+
37
+ **The same token contract is used after the wizard** when you apply `survey-ui-config.json` to production (Phase 6c). Building for live preview = building for accurate customization end-to-end.
38
+
39
+ ---
40
+
41
+ ## Two preview modes (know which you get)
42
+
43
+ | Mode | CLI | Preview source | Live updates | Fidelity |
44
+ |------|-----|----------------|--------------|----------|
45
+ | **Agent components** (default) | `export-previews` | Copies your `src/components/` + Vite build | Full **only if** token contract below is followed | Matches real survey UI |
46
+ | **Template harness only** | `export-previews --use-templates-only` | SDK static HTML harness | Always live | Generic SDK chrome — not your implementation |
47
+
48
+ **Default for production wizard:** agent components. Use templates-only for SDK smoke tests, not client delivery.
49
+
50
+ ---
51
+
52
+ ## Required project artifacts (Phase 5)
53
+
54
+ Create these **before** the client opens the wizard:
55
+
56
+ | File | Purpose |
57
+ |------|---------|
58
+ | `src/styles/survey-theme.css` | All `--cfm-*` CSS custom properties (defaults from mockup) |
59
+ | `survey-ui-config.json` (draft) | Seeds wizard form fields from agent branding |
60
+ | `src/lib/previewBridge.ts` | Live bridge in **local dev** (`npx cfm-sdk customize`) — copy from `templates/previewBridge.ts` |
61
+ | `src/components/PreviewBridgeInit.tsx` | Mount bridge once in `layout.tsx` (local dev only) |
62
+
63
+ Import theme in `globals.css`:
64
+
65
+ ```css
66
+ @import "tailwindcss";
67
+ @import "../styles/survey-theme.css";
68
+ ```
69
+
70
+ Wire bridge in root layout (client app only — not uploaded to wizard):
71
+
72
+ ```tsx
73
+ import { PreviewBridgeInit } from '@/components/PreviewBridgeInit';
74
+
75
+ export default function RootLayout({ children }) {
76
+ return (
77
+ <html lang="en">
78
+ <body>
79
+ <PreviewBridgeInit />
80
+ {children}
81
+ </body>
82
+ </html>
83
+ );
84
+ }
85
+ ```
86
+
87
+ **Wizard iframes** embed `preview-bridge.inline.js` at export time — you do not ship `previewBridge.ts` inside preview HTML.
88
+
89
+ ---
90
+
91
+ ## The token contract (two layers)
92
+
93
+ ### Layer 1 — CSS variables (`--cfm-*`)
94
+
95
+ Wizard sends CSS custom properties on `:root` (SNAPSHOT on tab load, DELTA during drag).
96
+
97
+ **Rule:** Any brand color, radius, spacing, or chrome dimension the client can edit in the wizard must be consumed via `var(--cfm-…)` in components or shared CSS — **not** hardcoded hex in Tailwind arbitrary values.
98
+
99
+ Copy the full default set from [`templates/survey-theme.css`](../../survey-theme.css) and extend with per-type vars your survey uses.
100
+
101
+ **Good — responds to wizard color changes:**
102
+
103
+ ```tsx
104
+ <div
105
+ className="h-3 rounded-full transition-all"
106
+ style={{
107
+ width: `${progress}%`,
108
+ backgroundColor: 'var(--cfm-progress-fill, var(--cfm-primary))',
109
+ }}
110
+ />
111
+ ```
112
+
113
+ **Avoid for wizard-controlled colors:**
114
+
115
+ ```tsx
116
+ <div className="bg-[#e20074] h-3 rounded-full" />
117
+ ```
118
+
119
+ **Tailwind + tokens pattern** (when you prefer classes):
120
+
121
+ ```tsx
122
+ <button
123
+ className="rounded-md px-6 py-2 text-sm font-semibold hover:opacity-90"
124
+ style={{
125
+ backgroundColor: 'var(--cfm-primary)',
126
+ color: 'var(--cfm-button-text)',
127
+ borderRadius: 'var(--cfm-border-radius)',
128
+ }}
129
+ >
130
+ {nextLabel}
131
+ </button>
132
+ ```
133
+
134
+ Or define utilities in `survey-theme.css`:
135
+
136
+ ```css
137
+ .cfm-btn-primary {
138
+ background-color: var(--cfm-primary);
139
+ color: var(--cfm-button-text);
140
+ border-radius: var(--cfm-border-radius);
141
+ }
142
+ ```
143
+
144
+ ### Layer 2 — Content hooks (`data-cfm-*`)
145
+
146
+ Wizard sends a **content patch** for text, logo URL, layout toggles, and footer links. The preview bridge updates DOM nodes matching these attributes.
147
+
148
+ | Attribute | Wizard source | Apply to |
149
+ |-----------|---------------|----------|
150
+ | `data-cfm-logo` | Logo step / `global.logo` | `<img>` in Header, Footer — **image logo mode** |
151
+ | `data-cfm-logo-text` | Identity → company name (when no image) | Text fallback inside logo slot — **text logo mode** |
152
+ | `data-cfm-company` | Identity → company name | Header company text (separate from logo slot) |
153
+ | `data-cfm-survey-title` | Identity → survey title | Main heading, chrome preview |
154
+ | `data-cfm-thank-you` | Identity → thank-you message | End page message |
155
+ | `data-cfm-copyright` | Footer step | Footer copyright line |
156
+ | `data-cfm-footer-links` | Footer step | Footer `<nav>` container (links injected) |
157
+ | `data-cfm-btn-next` | Layout → button labels | Next / Submit button |
158
+ | `data-cfm-btn-back` | Layout → button labels | Back button |
159
+ | `data-cfm-progress` | Layout → show progress bar | Wrapper around `ProgressBar` |
160
+
161
+ **Image logo (file in `public/`):**
162
+
163
+ ```tsx
164
+ <div className="cfm-header-logo-slot flex shrink-0 items-center justify-center">
165
+ <img
166
+ data-cfm-logo
167
+ src="/telekom-logo.svg"
168
+ alt="Company"
169
+ className="object-contain"
170
+ style={{
171
+ padding: 'var(--cfm-header-logo-padding)',
172
+ width: '100%',
173
+ height: '100%',
174
+ }}
175
+ />
176
+ {/* Hidden until wizard has no uploaded image — bridge toggles visibility */}
177
+ <span data-cfm-logo-text className="hidden font-semibold">
178
+ Company Name
179
+ </span>
180
+ </div>
181
+ ```
182
+
183
+ **Text-only logo (CSS-built brand, no `public/` image):**
184
+
185
+ ```tsx
186
+ <div className="cfm-header-logo-slot flex shrink-0 items-center justify-center">
187
+ <span
188
+ data-cfm-logo-text
189
+ className="font-semibold"
190
+ style={{
191
+ color: 'var(--cfm-header-company-color)',
192
+ fontSize: 'var(--cfm-header-company-size)',
193
+ fontWeight: 'var(--cfm-header-company-weight)',
194
+ }}
195
+ >
196
+ Company Name
197
+ </span>
198
+ </div>
199
+ ```
200
+
201
+ Do **not** add unrelated `public/logo_*` files during agent build — the CLI seeds the wizard from `Header.tsx` (`data-cfm-logo` src) or `data-cfm-logo-text`, not the newest file in `public/`. After the client uploads a logo in the wizard, `survey-ui-config.json` stores `global.logo.fileName`; the next wizard session uses **only that saved file**.
202
+
203
+ **Example SurveyPage nav buttons:**
204
+
205
+ ```tsx
206
+ <button data-cfm-btn-back type="button" onClick={onBack}>
207
+ Back
208
+ </button>
209
+ <button
210
+ data-cfm-btn-next
211
+ type="button"
212
+ style={{ backgroundColor: 'var(--cfm-primary)', color: 'var(--cfm-button-text)' }}
213
+ onClick={onNext}
214
+ >
215
+ Next
216
+ </button>
217
+ ```
218
+
219
+ ---
220
+
221
+ ## Per-component checklist
222
+
223
+ ### Chrome (always)
224
+
225
+ | Component | CSS vars | data-cfm-* |
226
+ |-----------|----------|------------|
227
+ | `Header.tsx` | `--cfm-header-*`, `--cfm-primary` (logo box) | `data-cfm-logo`, `data-cfm-company` |
228
+ | `Footer.tsx` | `--cfm-footer-*` | `data-cfm-logo`, `data-cfm-copyright`, `data-cfm-footer-links` |
229
+ | `ProgressBar.tsx` | `--cfm-progress-*` | wrap parent with `data-cfm-progress` |
230
+ | `SurveyStickyChrome.tsx` | header + progress vars | same as Header / progress |
231
+ | `IntroPage.tsx` | page bg, card, primary button | `data-cfm-survey-title` on main heading |
232
+ | `EndPage.tsx` | same | `data-cfm-thank-you` on thank-you copy |
233
+ | `SurveyPage.tsx` | `--cfm-max-width`, layout toggles | `data-cfm-btn-next`, `data-cfm-btn-back`; conditional render for `showHeader`, `showFooter`, `showProgressBar` from config at apply time |
234
+
235
+ ### Question scales (per type in survey)
236
+
237
+ Full per-type wizard control mapping: [`wizard-question-type-styling.md`](wizard-question-type-styling.md).
238
+
239
+ Use vars from `survey-theme.css` for cosmetic tokens the wizard exposes per type. See [`apply-ui-config.md`](apply-ui-config.md) token → file mapping.
240
+
241
+ | Type | Key vars | Component |
242
+ |------|----------|-----------|
243
+ | MCQ | `--cfm-mcq-*` | `Question.tsx` MCQ branch / `OptionItem` |
244
+ | NPS_SCALE | `--cfm-nps-*` | `RatingScale.tsx` |
245
+ | CFM_MATRIX | `--cfm-matrix-*` | `LikertMatrixScale.tsx` |
246
+ | CSAT_MATRIX | `--cfm-csat-*` | `CsatMatrixScale.tsx` |
247
+ | RATING_MATRIX | `--cfm-star-*`, `--cfm-badge-*` | `RatingScale.tsx` / `CsatMatrixScale.tsx` |
248
+ | SLIDER_MATRIX | `--cfm-slider-*` | `SliderMatrixScale.tsx`, `CustomSliderTrack.tsx` |
249
+ | FILE_UPLOAD | `--cfm-upload-*` | `FileUploadScale.tsx` |
250
+ | HEATMAP | `--cfm-heatmap-*` | `HeatmapScale.tsx` |
251
+ | RANK_ORDER | `--cfm-rank-*` | `RankOrderScale.tsx` |
252
+
253
+ **Do not change** dispatch logic, answer shapes, or SDK visibility helpers for wizard compatibility.
254
+
255
+ ---
256
+
257
+ ## Draft `survey-ui-config.json` (wizard seed)
258
+
259
+ After branding from the client prompt, write a **draft** `survey-ui-config.json` at project root so `customize-ui` seeds the wizard with your build — not generic Sprinklr defaults.
260
+
261
+ CLI resolution order:
262
+
263
+ 1. `./survey-ui-config.json` — including wizard-saved `global.logo.fileName` (re-open uses uploaded logo only)
264
+ 2. Parsed `src/styles/survey-theme.css`
265
+ 3. `implementation_plan.md` branding hints
266
+ 4. `Header.tsx` — `data-cfm-logo` image `src` (e.g. `/telekom-logo.svg`) or `data-cfm-logo-text` for text-only builds
267
+ 5. SDK defaults
268
+
269
+ **Logo seed rules for agents:**
270
+
271
+ - **Image logo:** put file in `public/`, reference exact path on `<img data-cfm-logo src="/your-logo.svg" />`. CLI base64-encodes that file for the wizard preview.
272
+ - **Text logo:** use `<span data-cfm-logo-text>` in the logo slot (CSS shapes optional). Omit `data-cfm-logo` img. Wizard shows company text until client uploads an image.
273
+ - **After wizard upload:** `global.logo.fileName` in saved config takes precedence — do not fall back to `Header.tsx` or scan `public/logo_*`.
274
+
275
+ Debug without opening wizard:
276
+
277
+ ```bash
278
+ npx cfm-sdk resolve-ui-config --write
279
+ ```
280
+
281
+ Keep draft JSON and `survey-theme.css` **in sync** (same primary color, title, company name).
282
+
283
+ ---
284
+
285
+ ## How `export-previews` uses your code
286
+
287
+ When the client opts in, `customize-ui` runs `export-previews`:
288
+
289
+ 1. Copies `src/components/`, `src/lib/`, `public/`, `survey-theme.css` into a temp Vite harness
290
+ 2. Stubs `next/image` and `next/dynamic` for static build
291
+ 3. Builds one HTML page per preview key (`header`, `chrome`, `MCQ_single`, …) with canonical fixtures
292
+ 4. Injects `preview-bridge.inline.js` into each `index.html`
293
+ 5. Uploads each preview page to S3 via EC2 presigned PUTs
294
+ 6. Prints the **deployed wizard link** for the client:
295
+
296
+ ```
297
+ http://43.204.26.213:3000/wizard-app/index.html?session=<uuid>&secret=<hex>
298
+ ```
299
+
300
+ 7. Wizard iframes load previews via EC2 proxy (`GET /wizard/preview/:sessionId/:component/...`)
301
+
302
+ **Agent handoff:** after step 5–6, copy the printed link and send it to the client. The client customizes on EC2 only — not localhost.
303
+
304
+ Preview keys match wizard steps — see [`ui-customization-wizard.md`](ui-customization-wizard.md) step table.
305
+
306
+ **Implication:** Wizard previews are a **snapshot of your component source at export time**. Re-run `customize-ui` (or `export-previews` + re-upload) after you change component structure.
307
+
308
+ ---
309
+
310
+ ## Live update protocol (wizard ↔ iframe)
311
+
312
+ | Message | When | Payload |
313
+ |---------|------|---------|
314
+ | `CFM_UI_CONFIG_SNAPSHOT` | Iframe load, tab change, text debounce | Full `cssVars` + `contentPatch` |
315
+ | `CFM_UI_CONFIG_DELTA` | Color sliders, drag (~60fps) | Partial `cssVars` only |
316
+
317
+ Content patches apply to `[data-cfm-*]` nodes. CSS vars apply to `:root`. Components must **read** those vars (Layer 1) for visual updates.
318
+
319
+ ---
320
+
321
+ ## Agent build checklist (blocking for full live preview)
322
+
323
+ Add to `implementation_plan.md` §5 and verify before Phase 6b:
324
+
325
+ - [ ] `src/styles/survey-theme.css` exists with full `--cfm-*` set for types in survey
326
+ - [ ] Draft `survey-ui-config.json` matches theme + identity from mockup
327
+ - [ ] `previewBridge.ts` + `PreviewBridgeInit` wired for local `customize` dev
328
+ - [ ] **No hardcoded brand hex** in chrome or scale components for wizard-controlled tokens
329
+ - [ ] Header: `data-cfm-logo`, `data-cfm-company` + header CSS vars
330
+ - [ ] Footer: `data-cfm-copyright`, `data-cfm-footer-links`, footer CSS vars
331
+ - [ ] ProgressBar: `--cfm-progress-fill` / `--cfm-progress-track`; parent `data-cfm-progress`
332
+ - [ ] SurveyPage buttons: `data-cfm-btn-next`, `data-cfm-btn-back`
333
+ - [ ] Intro/End: `data-cfm-survey-title`, `data-cfm-thank-you` where applicable
334
+ - [ ] Per-type scale vars wired per `apply-ui-config.md` mapping
335
+ - [ ] `npx cfm-sdk export-previews` succeeds from project root
336
+ - [ ] Grep: no `not yet implemented` stubs
337
+
338
+ Quick self-test (local):
339
+
340
+ ```bash
341
+ npx cfm-sdk export-previews
342
+ # Open out/previews/chrome/index.html in browser; in devtools console:
343
+ # postMessage({ type: 'CFM_UI_CONFIG_SNAPSHOT', cssVars: { '--cfm-primary': '#0066cc' }, contentPatch: { surveyTitle: 'Test Title' }}, '*')
344
+ # Title and primary-colored surfaces should update
345
+ ```
346
+
347
+ ---
348
+
349
+ ## Common mistakes
350
+
351
+ | Mistake | Symptom in wizard | Fix |
352
+ |---------|-------------------|-----|
353
+ | Hardcoded `bg-[#brand]` everywhere | Preview frozen on theme step | Use `var(--cfm-primary)` or `.cfm-btn-primary` |
354
+ | `survey-theme.css` missing or minimal | Seed config wrong; few vars update | Copy full template `survey-theme.css` |
355
+ | No `data-cfm-survey-title` | Identity edits don't change preview text | Add attribute to visible headings |
356
+ | Logo `<Image>` without `data-cfm-logo` | Logo step doesn't update preview | Add `data-cfm-logo` to img |
357
+ | Only Phase 6c tokenization | Worked after wizard, not during | Tokenize in Phase 5 (this guide) |
358
+ | Skipped `export-previews` after component edits | Wizard shows old UI | Re-run `customize-ui` |
359
+
360
+ ---
361
+
362
+ ## Phase flow summary
363
+
364
+ | Phase | Agent action |
365
+ |-------|----------------|
366
+ | **5 BUILD** | Implement components per this guide + UI specs; draft config + theme |
367
+ | **6 VERIFY** | `npm run build`, verify script, localhost preview |
368
+ | **6b WIZARD** (optional) | Client says yes → `customize-ui` → **wait** for submit → read `.diff.md` |
369
+ | **6c APPLY** | Apply **all** paths from `survey-ui-config.final.json` per [`wizard-config-handoff.md`](wizard-config-handoff.md) + [`apply-ui-config.md`](apply-ui-config.md) → `confirm-ui-config` |
370
+ | **7 DEPLOY** | Full static build upload (separate from wizard preview files) |
371
+
372
+ ---
373
+
374
+ ## Copy-from-template shortcut
375
+
376
+ Fastest path to full live preview: start from SDK portable templates in `templates/docs/templates/`, then customize branding using **tokens from day one** rather than hardcoded mockup hex. Extend tokens as you deviate from defaults.
377
+
378
+ Templates to prioritize for token wiring:
379
+
380
+ - `Header.tsx`, `Footer.tsx`, `ProgressBar.tsx`, `SurveyStickyChrome.tsx`
381
+ - Scale components listed in [`component-checklist.md`](component-checklist.md)
382
+
383
+ ---
384
+
385
+ ## CLI reference
386
+
387
+ ```bash
388
+ # Build preview pages only (from project root)
389
+ npx cfm-sdk export-previews
390
+
391
+ # Template harness smoke test (not client UI)
392
+ npx cfm-sdk export-previews --use-templates-only
393
+
394
+ # Full AWS wizard session
395
+ export CFM_INSTANCE_ID="..." CFM_DEPLOY_API_BASE="http://your-ec2:3000"
396
+ npx cfm-sdk customize-ui
397
+
398
+ # Inspect seed config
399
+ npx cfm-sdk resolve-ui-config --write
400
+ ```
@@ -0,0 +1,195 @@
1
+ # Wizard Question-Type Styling
2
+
3
+ > **Read during Phase 5 (BUILD) — mandatory.** Alongside [`wizard-preview-build-guide.md`](wizard-preview-build-guide.md) and [`apply-ui-config.md`](apply-ui-config.md).
4
+ > Pipeline: [`agent-execution-flow.md`](agent-execution-flow.md).
5
+
6
+ **Do not change** `Question.tsx` dispatch logic, answer shapes, SDK visibility helpers, or matrix routing algorithms — styling only.
7
+
8
+ ---
9
+
10
+ ## Shared selection pattern (Question card theme)
11
+
12
+ Wizard **Theme & Colors → Question card** exposes two tokens used across radio/scale selections:
13
+
14
+ | Wizard field | CSS variable | Visual role |
15
+ |--------------|--------------|-------------|
16
+ | **Cell selected color** | `--cfm-matrix-selected` | Fill **between** outer ring and inner dot (annulus); also solid numbered/star cell background when selected |
17
+ | **Focus ring** | `--cfm-input-focus-ring` | Outer circle **border** + inner **dot** color |
18
+
19
+ Also mapped (for MCQ compatibility): `--cfm-mcq-selected-accent` ← cell selected color.
20
+
21
+ Copy [`templates/selectionStyles.ts`](../templates/selectionStyles.ts) → `src/lib/surveyUi/selectionStyles.ts` and use in scale components:
22
+
23
+ ```tsx
24
+ import {
25
+ scaleRadioRingStyle,
26
+ scaleRadioDotStyle,
27
+ matrixRadioRingStyle,
28
+ matrixRadioDotStyle,
29
+ cellSelectedVar,
30
+ focusRingVar,
31
+ } from '@/lib/surveyUi/selectionStyles';
32
+ ```
33
+
34
+ **Selected radio cell structure:**
35
+
36
+ ```
37
+ ┌─ focus ring (border) ─────────┐
38
+ │ cell selected (annulus) │
39
+ │ ● focus ring (dot) │
40
+ └─────────────────────────────┘
41
+ ```
42
+
43
+ **Numbered badge when selected:** border = focus ring, background = cell selected, text = white.
44
+ **Unselected numbered badges:** keep traffic-light / per-number colors from `--cfm-number-*` vars (do not override with theme tokens).
45
+
46
+ ---
47
+
48
+ ## Shared label helpers
49
+
50
+ Copy [`templates/labelStyles.ts`](../templates/labelStyles.ts) → `src/lib/surveyUi/labelStyles.ts`.
51
+
52
+ | Helper | CSS vars | Wizard control |
53
+ |--------|----------|----------------|
54
+ | `hintLabelStyle()` | `--cfm-hint-label-color`, `--cfm-hint-label-bg` | Hint labels (min/max anchors) |
55
+ | `matrixColumnLabelStyle()` | `--cfm-matrix-column-label-*` | CFM matrix column header items |
56
+ | `csatColumnLabelStyle()` | `--cfm-csat-column-label-*` | CSAT/Rating column header items |
57
+ | `sliderTickLabelStyle()` | `--cfm-slider-tick-label-*` | Slider matrix tick label items |
58
+
59
+ **CFM matrix:** column headers are **label items** only — not hint labels, not number labels.
60
+ **CSAT/Rating:** per-format controls differ — see per-type table below.
61
+
62
+ ---
63
+
64
+ ## Per-type wizard controls → CSS vars → components
65
+
66
+ ### MCQ (`MCQ_single`, `MCQ_multiple`)
67
+
68
+ | Wizard section | Config path | CSS vars | Apply in |
69
+ |----------------|-------------|----------|----------|
70
+ | Option style | `questionTypes.MCQ.optionStyle` | `data-cfm-mcq-style` on `:root` | `Question.tsx` MCQ branch |
71
+ | Option gap / radius / padding | `questionTypes.MCQ.*` | `--cfm-mcq-option-gap`, `--cfm-mcq-border-radius`, `--cfm-mcq-card-padding` | MCQ option cards |
72
+ | Selection | Theme → Question card | `--cfm-input-focus-ring`, `--cfm-matrix-selected`, `--cfm-mcq-selected-bg` | Radio: ring border + annulus + dot; checkbox: ring border + fill |
73
+
74
+ Single-select radio: outer border = focus ring, annulus = cell selected, inner dot = focus ring.
75
+
76
+ ### TEXTFIELD (`TEXTFIELD_short`, `TEXTFIELD_long`)
77
+
78
+ | Wizard section | CSS vars |
79
+ |----------------|----------|
80
+ | Input radius, border, placeholder | `--cfm-input-radius`, `--cfm-input-border`, `--cfm-input-placeholder` |
81
+ | Focus | `--cfm-input-focus-ring` on focus outline |
82
+
83
+ ### NPS_SCALE (`NPS_SCALE`)
84
+
85
+ | Wizard section | Config path | CSS vars | Apply in |
86
+ |----------------|-------------|----------|----------|
87
+ | Button style | `questionTypes.NPS_SCALE.buttonStyle` | `data-cfm-nps-button-style` | `RatingScale.tsx` |
88
+ | Track | `selectedFill`, `trackBar`, `trackHighlight` | `--cfm-nps-selected-fill`, `--cfm-nps-track-bar`, `--cfm-nps-track-highlight` | Track bar background |
89
+ | Hint labels | `hintMinText`, `hintMaxText` + hint colors | `--cfm-hint-label-*` | Min/max anchor row |
90
+ | Number labels | `numberLabelMode`, per-index colors | `--cfm-number-label-mode`, `--cfm-number-mono-color`, `--cfm-number-color-{n}` | Badge row above radios |
91
+ | Selection | Theme → Question card | `--cfm-matrix-selected`, `--cfm-input-focus-ring` | `scaleRadioRingStyle` + `scaleRadioDotStyle` |
92
+
93
+ ### CFM_MATRIX (`CFM_matrix`, `CFM_dropdown`, `CFM_transpose`, `CFM_likert`, `CFM_bipolar`)
94
+
95
+ | Wizard section | CSS vars | Apply in |
96
+ |----------------|----------|----------|
97
+ | Row label width | `--cfm-matrix-row-width` | `LikertMatrixScale.tsx` |
98
+ | Bipolar column width | `--cfm-matrix-bipolar-width` | Bipolar anchor column |
99
+ | Label items | `--cfm-matrix-column-label-color`, `--cfm-matrix-column-label-bg` | Column header row only |
100
+ | Selection | `--cfm-matrix-selected`, `--cfm-input-focus-ring` | `matrixRadioRingStyle` / custom cells; dropdown option ring |
101
+
102
+ **No** per-type accent/ring overrides in CFM — use Question card tokens only.
103
+
104
+ ### CSAT_MATRIX (`CSAT_carousel`, `CSAT_dropdown`, `CSAT_emoji`, `CSAT_graphics`, `CSAT_numbered`, `CSAT_star`)
105
+
106
+ | Format | Wizard sections | Key styling |
107
+ |--------|-----------------|-------------|
108
+ | **emoji** | Emoji size, unselected opacity, label items | `emojiSizeStyle()`, `unselectedOpacityStyle()`, emoji ring = focus ring on select |
109
+ | **star** | Emoji size, unselected opacity, label items | Stars: selected color = `cellSelectedVar` |
110
+ | **numbered** | Number labels, label items | Selected cell: border focus ring, fill cell selected |
111
+ | **radio** (grid) | Label items | Horizontal radio grid; `matrixRadioRingStyle` |
112
+ | **graphics** (slider) | Label items, tick labels | `CustomSliderTrack` thumb: ring + annulus + dot pattern |
113
+ | **carousel** | Label items | Dot indicators: cell selected when active |
114
+ | **dropdown / selectbox** | Label items | `MatrixDropdown` + list radio pattern |
115
+
116
+ **Removed from wizard UI (do not wire):** per-type accent, selected ring, badge color, hint labels section on CSAT formats that use label items only.
117
+
118
+ ### RATING_MATRIX (`RATING_radio`, `RATING_emoji`, `RATING_graphics`, `RATING_slider`, `RATING_star`, `RATING_numbered`)
119
+
120
+ Same CSAT patterns where formats overlap. **Radio format:** horizontal grid (not vertical list). Use CSAT matrix component paths with star/numbered/emoji/graphics variants.
121
+
122
+ ### SLIDER_MATRIX (`SLIDER_matrix`)
123
+
124
+ | Wizard section | CSS vars | Apply in |
125
+ |----------------|----------|----------|
126
+ | Track / thumb / tick | `--cfm-slider-track`, `--cfm-slider-thumb`, `--cfm-slider-tick` | `SliderMatrixScale`, `CustomSliderTrack` |
127
+ | Row band | `--cfm-slider-row-band` | Statement row background |
128
+ | Tick label items | `--cfm-slider-tick-label-*` | Header tick labels |
129
+ | Tick number labels | `--cfm-number-*` | Colored number badges |
130
+ | Thumb selection | `--cfm-matrix-selected`, `--cfm-input-focus-ring` | Knob: border + annulus + center dot |
131
+
132
+ **No** hint labels section in wizard for slider matrix — use tick label items.
133
+
134
+ ### FILE_UPLOAD (`FILE_UPLOAD`)
135
+
136
+ | Wizard section | CSS vars | Apply in |
137
+ |----------------|----------|----------|
138
+ | Dropzone border style | `--cfm-upload-border-style`, `data-cfm-dropzone-style` | `FileUploadScale.tsx` |
139
+ | Border color | `--cfm-upload-border-color` | Dropzone border (not hardcoded `#d1d5db`) |
140
+ | Fill / padding | `--cfm-upload-fill`, `--cfm-upload-padding` | Dropzone background |
141
+ | Focus/hover | `--cfm-input-focus-ring`, `--cfm-mcq-selected-bg` | Focus border + drag-active bg |
142
+
143
+ ### HEATMAP (`HEATMAP`)
144
+
145
+ | Wizard section | CSS vars |
146
+ |----------------|----------|
147
+ | Pin color / border / size | `--cfm-heatmap-pin`, pin border config, `--cfm-heatmap-pin-size` |
148
+
149
+ ### RANK_ORDER (`RANK_drag`, `RANK_dropdown`)
150
+
151
+ | Variant | Wizard controls | CSS vars |
152
+ |---------|-----------------|----------|
153
+ | Drag | Item bg, handle, badge, gap, padding | `--cfm-rank-item-bg`, `--cfm-rank-handle`, `--cfm-rank-badge`, `--cfm-rank-item-gap`, `--cfm-rank-item-padding` |
154
+ | Dropdown | Item bg, select border, badge | `--cfm-rank-select-border`, etc. |
155
+ | Focus on inputs | Theme focus ring | `--cfm-input-focus-ring` on focus |
156
+
157
+ **Rank drag:** ensure `interactionMode: 'dragAndDrop'` in fixture/survey — do not change dispatch.
158
+
159
+ ### TEXT_AND_MEDIA
160
+
161
+ Use `--cfm-media-max-width`, `--cfm-media-radius` from theme.
162
+
163
+ ---
164
+
165
+ ## Live preview re-render (export-previews harness)
166
+
167
+ Components that read CSS vars at render time should import `useLiveThemeTick` from preview harness or bump state when wizard sends `CFM_UI_CONFIG_SNAPSHOT` / `DELTA`. The **export-previews** Vite harness includes this; production client apps only need it if running local `npx cfm-sdk customize`.
168
+
169
+ For production builds after wizard: syncing `survey-theme.css` is sufficient — no `previewLiveTick` required.
170
+
171
+ ---
172
+
173
+ ## Agent checklist (per survey type)
174
+
175
+ For each type in survey inventory:
176
+
177
+ - [ ] Scale component uses `var(--cfm-*)` for every wizard-exposed token (no hardcoded brand hex)
178
+ - [ ] Radio/scale selections use `selectionStyles.ts` pattern (focus ring + cell selected)
179
+ - [ ] Matrix column headers use `labelStyles.ts` (label items — not conflated with hint/number labels)
180
+ - [ ] NPS/numbered formats use `resolveNumberLabelColor` from `surveyUiScaleUtils.ts` for **unselected** badges only
181
+ - [ ] File upload dropzone uses `--cfm-upload-border-color`
182
+ - [ ] `Question.tsx` dispatch unchanged; cosmetic inline styles only
183
+ - [ ] `npx cfm-sdk export-previews` succeeds after styling wiring
184
+
185
+ ---
186
+
187
+ ## Related files
188
+
189
+ | File | Purpose |
190
+ |------|---------|
191
+ | [`templates/selectionStyles.ts`](../templates/selectionStyles.ts) | Copy to client `src/lib/surveyUi/` |
192
+ | [`templates/labelStyles.ts`](../templates/labelStyles.ts) | Copy to client `src/lib/surveyUi/` |
193
+ | [`templates/survey-theme.css`](../../survey-theme.css) | Full `--cfm-*` defaults |
194
+ | [`wizard-preview-build-guide.md`](wizard-preview-build-guide.md) | Chrome + bridge contract |
195
+ | [`apply-ui-config.md`](apply-ui-config.md) | Post-wizard apply workflow |
@@ -1,7 +1,8 @@
1
1
  # Survey Page Orchestrator Architectural Blueprint
2
2
 
3
3
  > **UI spec (authoritative):** `03-ui-specs/12-survey-chrome.md`
4
- > **Reference implementation:** `apps/client/src/components/SurveyPage.tsx`
4
+ > **Portable template:** implement from `docs/templates/` + [`agent-execution-flow.md`](../00-integration/agent-execution-flow.md) Phase 5
5
+ > **Monorepo snapshot (do not copy):** `apps/client/src/components/SurveyPage.tsx`
5
6
  > **Target Component**: `SurveyPage`
6
7
  > **Role**: Wires `useSurveySDK` to the UI — loading/error states, phase routing, pagination, submission.
7
8
 
@@ -13,9 +14,9 @@ Act as the **phase state machine** for the survey. Never render questions direct
13
14
 
14
15
  ```tsx
15
16
  import type { CustomFieldValueMap } from '@explorer02/cfm-survey-sdk';
17
+ import { CUSTOM_FIELD_VALUES } from '@/lib/customFieldValues';
16
18
 
17
19
  const SURVEY_PLACEHOLDERS = { FIRST_NAME: 'Customer' }; // Must be outside component!
18
- const CUSTOM_FIELD_VALUES: CustomFieldValueMap = {}; // Populate when survey uses custom-field logic
19
20
 
20
21
  export default function SurveyPage() {
21
22
  const [selectedLanguage, setSelectedLanguage] = useState<string | undefined>();
@@ -46,6 +47,14 @@ export default function SurveyPage() {
46
47
 
47
48
  See [`client-integration-guide.md`](../00-integration/client-integration-guide.md) for lifecycle effects (`SCROLL`, `RECORD_QUESTION_VIEW`).
48
49
 
50
+ When fetched survey contains any `_c_…` custom field id in logic rules:
51
+
52
+ - [ ] Copy [`templates/customFieldValues.ts`](../templates/customFieldValues.ts) → `src/lib/customFieldValues.ts`
53
+ - [ ] Populate map from CRM/transaction payload
54
+ - [ ] Import `CUSTOM_FIELD_VALUES` in `SurveyPage` (do not define inline empty `{}`)
55
+
56
+ See [`client-lib-folder.md`](../00-integration/client-lib-folder.md) and [`custom-field-logic-and-navigation.md`](../00-integration/custom-field-logic-and-navigation.md).
57
+
49
58
  ## Render Order (MANDATORY)
50
59
 
51
60
  Handle in this order:
@@ -73,12 +82,13 @@ See `14-intro-page.md`, `15-end-page.md`, `16-paused-page.md`.
73
82
 
74
83
  ### 3. Active content (`phase.kind === 'content'`)
75
84
 
76
- 1. `Header`
77
- 2. `ProgressBar` (`state.progressPercentage`) — see `00-integration/progress.md`
78
- 3. `LanguageSelector`
79
- 4. Question map (below)
80
- 5. Navigation buttons (below)
81
- 6. `Footer`
85
+ 1. `SurveyStickyChrome` — sticky `Header` + `ProgressBar` (`state.progressPercentage`); see `19-survey-sticky-chrome.md` and `00-integration/progress.md`
86
+ 2. `main`: `LanguageSelector`
87
+ 3. Question map (below)
88
+ 4. Navigation buttons (below)
89
+ 5. `Footer`
90
+
91
+ Use standalone `Header` (no sticky chrome) on intro, end, paused, loading, and terminal phases.
82
92
 
83
93
  ## The Question Map
84
94
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **UI spec (authoritative):** `03-ui-specs/00-question-shell.md`
4
4
  > **Canonical template:** [`templates/Question.tsx`](../templates/Question.tsx) — copy verbatim, then create imported components
5
- > **Monorepo reference:** `apps/client/src/components/Question.tsx`
5
+ > **Monorepo snapshot (illustrative only):** `apps/client/src/components/Question.tsx`
6
6
  > **Target Component**: `Question`
7
7
 
8
8
  ## Start Here (CRITICAL)
@@ -77,7 +77,7 @@ Store matrix cell values as column **`id`** via `matrixColumnStoredValue(col)`
77
77
  ### MCQ (`QUESTION_TYPE.MCQ`)
78
78
 
79
79
  - [ ] Filter options with `getVisibleMcqOptionsForAnswers` before render
80
- - [ ] Store `option.id`; support `isMultiSelect` array shape when configured
80
+ - [ ] Store `option.id`; use `selectionMode` (`single` | `multiple`) — not `isMultiSelect`
81
81
  - [ ] onAction CHANGE: `{ questionId, answerValue: optionId | optionId[] }`
82
82
 
83
83
  ### TEXTFIELD (`QUESTION_TYPE.TEXTFIELD`)