Skip to content
fandhe-frontendcore v0.4.3
GitHub

Questionnaire

単一選択・複数選択・自由記述・スキップ可の質問を多段で提示する shadcn/ui Questionnaire 相当の部品です(参照軸はイシュー #2001)。fandhe-frontend-headless-ui の questionnaire mod が構造・アクセシビリティ(WAI-ARIA)・表示状態(data-*)のみを提供する unstyled 部品で、Root / Progress / Question / Prompt / Description / Options / Freeform / Actions / Back / Next / Skip の 11 anatomy パーツと、count(全質問数)・step(現在位置)から質問の表示状態を導出する状態機械 Questionnaire を持ちます。

責務境界

回答値の保持・検証(必須判定)・分岐(次にどの質問へ進むか)・送信はアプリケーション側の責務であり、本部品は持ちません。部品が担うのは「現在位置に応じて質問の表示状態を切り替え、前へ / 次へ / スキップのトリガーを出す」までです。next / skip の data-disabled は、必須判定の結果(真偽)だけをアプリから引数として受け取ります。

状態モデル

question の index(0-origin)と step の比較で 3 状態(data-state)を導出します。

条件data-state
index < stepcompleted
index == stepactive
index > stepupcoming

active 以外の質問には hidden 属性が付き、タブ操作・支援技術の双方から除外されます。step == count は「全質問完了」を表し、root に data-complete が付き、next / skip は無条件で無効化されます。

Skip は状態遷移としては Next と同一(min(step + 1, count) へ進む)です。「どの質問をスキップしたか」は本部品に保持せず、QuestionProps::skipped として呼び出し側が渡します。

QuestionProps

question(index, props, attrs, children) の props(QuestionProps)は 4 つの存在属性フラグを持ち、いずれも既定 false です。

フィールド出力
answereddata-answered
skippeddata-skipped
requireddata-required
invaliddata-invalid + aria-invalid="true"(invalid が true のときのみ)

Options / Freeform スロット

options / freeform は純スロットです。選択肢は options へ Radio Group / Checkbox Group のパーツを、自由記述は freeform へ Field の textarea パーツを入れ子にする契約とします。両者の data-scope は questionnaire scope と独立して残ります(回答ロジック自体は入れ子にした部品・アプリ側が担います)。

Progress

progress(label, attrs, children) は role="progressbar" を持ち、aria-valuemin / aria-valuemax / aria-valuenow / aria-valuetext({percent}% complete)を出力します。label が空文字でないときのみ aria-label を付与します。

アクセシビリティ

  • question は fieldset 要素です。prompt(legend)を先頭子に置く契約とし、ネイティブなグループ名が id 配管なしで付きます。
  • back / next / skip はネイティブ button(type="button")です。境界(step == 0 / step == count)または呼び出し側の判定結果で disabled + data-disabled を出力します。
  • キーボード操作は Tab / Shift+Tab(フォーカス移動)・Enter / Space(ボタン押下)のみで、矢印キー等の独自ハンドリングは持ちません。options / freeform のキー操作は入れ子にした部品のものを継承します。

back / next / skip の click から dispatch("prev" / "next" / "skip")への実際の DOM 配線は fandhe-frontend-wasm-full 0.18.0 以降、Runtime::mount / Runtime::hydrate が自動配線します。クリックで DOM 上の data-step ・各 question の data-state / hidden ・progress の aria-valuenow / aria-valuetext が更新され、アプリ状態へは実際に遷移した場合のみ "questionnaire:prev" / "questionnaire:next" / "questionnaire:skip"(payload は遷移前の step)が通知されます。回答値の保持・必須判定・分岐(次にどの質問へ進むか)はこの通知を受けたアプリ側の再描画で行う契約です。back / next / skip の disabled は境界(step == 0 / step == count)が変化したときのみ wasm-full が付け外しし、非境界位置でアプリが付けた disabled(必須判定結果)は保存されます。詳細は docs/design/wasm-full-architecture.md §27 を参照してください。

参考サイトとの差分

shadcn/ui の Questionnaire は回答状態管理・バリデーション・キーボードショートカット・失敗時のフォーカス移動を内包しますが、本部品はこれらを持ちません。

  • QuestionnaireError / QuestionnaireSubmit: 非採用です。検証結果の表示は呼び出し側が Field のエラー表示パーツを question 内へ入れ子にし、送信ボタンは actions スロットへ通常の button を置きます。
  • QuestionnaireChoice / QuestionnaireInput / 英数字キーによるショートカット / required / multiple の回答ロジック: 非採用です。選択肢は Radio Group / Checkbox Group、自由記述は Field の textarea の再利用で賄います。
  • items 配列からの一括描画・回答状態管理・バリデーション・失敗時のフォーカス移動: 非採用です(アプリケーションロジック・クライアント DOM 操作の関心)。

スタイル済みの表示例(recipe・golden テスト)は Questionnaire を参照してください。

関連 API: fandhe-frontend-headless-ui API

自前 CSS の最小例

CSS
[data-scope="questionnaire"][data-part="question"] {
  display: block;
}
[data-scope="questionnaire"][data-part="question"][hidden] {
  display: none;
}
[data-scope="questionnaire"][data-part="question"][data-invalid] {
  border-left: 2px solid #b91c1c;
  padding-left: 0.75rem;
}
[data-scope="questionnaire"][data-part="next"][data-disabled],
[data-scope="questionnaire"][data-part="skip"][data-disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}
[data-scope="questionnaire"][data-part="progress"] {
  height: 0.25rem;
  background: #e5e7eb;
}
[data-scope="questionnaire"][data-part="progress"][data-complete] {
  background: #16a34a;
}

Demo

枠線・余白は docs サイト側のデモ枠であり、fandhe-frontend-headless-ui 自体はスタイルを持ちません。

Do you like Rust?
Pick one.
What could we improve?
Thanks for completing the survey!

Features

  • Root / Progress / Question / Prompt / Description / Options / Freeform / Actions / Back / Next / Skip の 11 anatomy パーツと、count/step から質問の表示状態を導出する状態機械 Questionnaire を提供する(crate::steps::Steps と同型の正規化・dispatch 契約)。
  • 回答値の保持・検証(必須判定)・分岐・送信はアプリケーション責務であり本クレートは持たない。next/skip の data-disabled は、必須判定の結果(真偽)だけを引数として受け取る(.claude/rules/coding-rust.md §UI 部品の責務境界 規則 1)。
  • question の data-state(active/completed/upcoming)は count/step/index から導出する。非 active な質問は hidden 属性で隠す(crate::steps::Steps::content と同型契約)。
  • 選択肢は options スロットへ radio_group/checkbox_group のパーツを、自由記述は freeform スロットへ field::textarea を入れ子にする契約(両者の data-scope は questionnaire scope と独立して残る)。
  • Skip は状態遷移としては Next と同一(min(step + 1, count))であり、「どの質問をスキップしたか」は QuestionProps::skipped として呼び出し側が保持する。

Anatomy

root
  progress
  question
    prompt
    description
    options
    actions
      back
      next
      skip
    freeform

API Reference

Arguments

NameTypeDefaultDescription
new(count, step, orientation)usize, usize, Orientation—count.max(1)・step.min(count) で fail-closed 正規化する。
progress(label)&str""空文字でないときのみ aria-label を付与する(shadcn「named progressbar」)。
question(index)usize—0-origin の質問インデックス(data-index・data-state 導出に使う)。
question(props)QuestionPropsQuestionProps::default()answered/skipped/required/invalid の 4 存在属性。invalid は aria-invalid="true" も付与する。
back(disabled)boolfalsestep == 0 との OR で disabled+data-disabled を決める。
next/skip(disabled)boolfalsestep == count との OR で disabled+data-disabled を決める(責務境界規則 1: 必須判定の結果だけを渡す)。

Data Attributes

PartAttributeObserved Values
nextdata-disabled(値なし)
progressdata-complete(値なし)
questiondata-answered(値なし)
questiondata-index1
questiondata-invalid(値なし)
questiondata-required(値なし)
questiondata-stateactive
rootdata-complete(値なし)
rootdata-orientationhorizontal
rootdata-step1, 3
skipdata-disabled(値なし)

Examples

Single choice question

options スロットへ radio_group を入れ子にした単一選択の質問。

Do you like Rust?
Pick one.

Freeform question (invalid)

freeform スロットへ field::textarea を入れ子にし、QuestionProps::invalid で data-invalid/aria-invalid="true" を露出する。

What could we improve?

Accessibility

Keyboard Interactions

KeyDescription
Tab / Shift+Tabback/next/skip はネイティブ button のため既定のタブ操作を継承する。非 active な質問は hidden 属性により Tab 到達不能。
Enter / Spaceフォーカス中の back/next/skip ボタンを押下する(ネイティブ button の既定動作)。
(実 DOM 配線)back/next/skip の click から dispatch("prev"/"next"/"skip")への実配線は fandhe-frontend-wasm-full 側の責務(本クレートのスコープ外)。0.18.0 以降 Runtime::mount/Runtime::hydrate が自動配線し、境界での disabled 付け外し・"questionnaire:*" 通知まで行う(イシュー #2118)。

WAI-ARIA

AttributeDescription
role="progressbar" (progress)aria-valuemin/aria-valuemax/aria-valuenow/aria-valuetext を併せて出力する。
aria-invalid (question)QuestionProps::invalid が true のときのみ "true" を出力する(valid のときは属性自体を省略する)。
hidden (question)非 active な質問へ付与し、支援技術・タブ操作の双方から除外する。
disabled / data-disabled (back/next/skip)境界(step == 0/step == count)または呼び出し側の判定結果で活性化を抑止する。