Survey UI
Build a custom survey component styled to your app's design system.
By default, sunboard init wires the built-in <Sunboard.Survey /> — a centered
modal that shows every question at once. /sunboard.survey-ui replaces it with a
custom component that matches your design system, rendered (by default) as a
dedicated /onboarding page with one question — or a small group — per step. The
useSurvey() hook owns the data, step navigation, and submit; the skill owns the
visual layer. It's the survey counterpart to
Checklist UI.
When to use it
- The built-in modal doesn't match your look.
- You want onboarding to read as a full "set up your account" page rather than a popup to dismiss.
What happens
- Inventories your design system — Tailwind, shadcn, MUI, CSS modules, your button/radio/checkbox/progress primitives, and how dark mode works.
- Confirms placement — default is a dedicated
/onboardingroute, multi-step with a progress bar; a modal or single-screen layout if you prefer. - Writes the component at
components/sunboard/onboarding-survey.tsx, rendering the current step fromuseSurvey()and reusing your own primitives. - Mounts it in
sunboard-provider.tsx, replacing<Sunboard.Survey />(checklist, tours, and hotspots stay as they are). - Previews it against your real app.
The hook contract
Everything you render comes from useSurvey() — copy is already
token-interpolated, and the hook drives step navigation and submission:
const {
visible, // owed + not yet submitted — render nothing when false
title, // pre-interpolated heading
steps, // SurveyViewStep[] — authored groups, or one per question
currentStep, // render this step's questions
progress, // 0..100, by step
isLastStep,
canAdvance, // current step's required questions answered
next, // advance, or submit on the last step
back,
answers, // Record<string, unknown>
setAnswer, // (key, value) — single-select / text
toggleAnswer, // (key, value) — one value of a multi-select
submit, // posts answers, then re-bootstraps
} = useSurvey();Don't reach past the hook
Render from currentStep and let next/back/progress drive the flow —
don't hand-roll a step index or per-step validation. The hook's field names are
a stable, append-only contract. For imperative work, use useSunboard()
separately.