Signature Pad
fandhe-frontend-headless-ui の signature_pad mod が提供する手書き署名入力の unstyled 部品です。Root / Label(label 要素)/ Control / Segment(svg)/ SegmentPath / Guide / ClearTrigger / HiddenInput の 8 anatomy パーツを持ち、canvas を一切使わず、ストローク(座標列)を SVG path 文字列へ変換する決定的な純粋関数のみで描画します。ポインタイベントの収集自体は本部品の責務外です。
/themes/ 側の SignaturePad(Chakra ライクな variant/size を持つスタイル層)と異なり、CSS を一切持ちません。
root は disabled/empty(strokes が空か)に応じて data-disabled/data-empty を付与します。label/guide は data-disabled を反映します(label のタグはネイティブ <label> 要素です)。control は role="application"・aria-roledescription="signature pad" を常時付与し、disabled に応じて非 disabled 時のみ tabindex="0"、disabled 時のみ aria-disabled="true" を切り替えます。control のアクセシブルネーム(aria-label/aria-labelledby)は偽の説明文を捏造しない fail-closed 方針により本モジュールが自動生成することはなく、呼び出し側が attrs で渡す契約です。clear-trigger はネイティブ disabled + data-disabled を反映します。SignaturePad::control メソッド経由でのみ data-readonly が付与されます(自由関数 control にはこの属性はありません)。
キーボード操作
| キー | 対象 | 効果 |
|---|---|---|
| Tab | Control | tabindex="0"(非 disabled 時のみ)によりフォーカスを移します。 |
| Enter / Space | ClearTrigger | フォーカスがネイティブ <button> 上にあるとき、ブラウザ既定の活性化操作で全ストロークを削除します。 |
参考サイト(ark-ui docs / zag.js signature-pad.connect.ts)に Keyboard Interactions 表・onKeyDown ハンドラは存在しません。描画自体はポインタ専用のままで、undo(直前ストロークの取り消し)を含むアクションに既定のキー割り当てはありません。
参考サイトとの差分
ark-ui docs(Anatomy / Data Attributes 表)と zag.js(signature-pad.connect.ts/signature-pad.anatomy.ts)を一次情報として突合し、label のタグ(div → label)・data-disabled(label/guide)・control の role/aria-roledescription/tabindex/aria-disabled を是正しました。一方、以下は意図的に合わせていません。
labelのdata-required・for(hidden input への関連付け): 本モジュールに「必須」概念が無く、forは呼び出し側attrsに委ねます。controlのアクセシブルネーム自動生成: 偽の説明文を捏造しない fail-closed 方針により、呼び出し側がaria-label/aria-labelledbyを渡す契約のままとします。clear-triggerのネイティブhidden(ストローク空 or 描画中): author CSS(display)との干渉を避けるため持ち込まず、root[data-empty]を使う自前 CSS(下記参照)で代替します。data-empty(root)・data-readonly(control)・segmentのrole="img": 本フレームワーク固有の拡張として維持します。- hidden-input の
type="hidden"(参照はtype="text" hidden readOnly required): フォーム送信用途としてtype="hidden"のまま維持します。
スタイル済みの表示例は Signature Pad を参照してください。
関連 API: fandhe-frontend-headless-ui API
自前 CSS の最小例
Themes 版を使わず本部品を直接使う場合、[data-scope="signature-pad"][data-part="..."] セレクタでスタイルを当てます。以下は control の枠線とフォーカスリング、segment-path の線描画、guide のベースライン、root[data-empty] による clear-trigger の非表示(参照の hidden 相当)の最小例です。
[data-scope="signature-pad"][data-part="control"] {
display: inline-block;
border: 1px solid #ccc;
border-radius: 6px;
}
[data-scope="signature-pad"][data-part="control"]:focus-visible {
outline: 2px solid #06c;
outline-offset: 2px;
}
[data-scope="signature-pad"][data-part="control"][data-disabled] {
opacity: 0.5;
}
[data-scope="signature-pad"][data-part="segment-path"] {
fill: none;
stroke: currentColor;
stroke-width: 2;
}
[data-scope="signature-pad"][data-part="guide"] {
border-bottom: 1px dashed #999;
}
[data-scope="signature-pad"][data-part="root"][data-empty] [data-part="clear-trigger"] {
display: none;
}Demo
枠線・余白は docs サイト側のデモ枠であり、fandhe-frontend-headless-ui 自体はスタイルを持ちません。
Features
- Root / Label(
label要素)/ Control / Segment(svg)/ SegmentPath(ストロークごとのpath)/ Guide / ClearTrigger / HiddenInput の 8 anatomy パーツを提供する。canvas を一切使わず、ストローク(座標列)を SVG path 文字列へ変換する決定的な純粋関数(stroke_path_d)が中核である。 stroke_path_dは同一座標列から常に同一のd属性値を生成する(固定小数点 2 桁・round half away from zero・指数表記なし)。ポインタイベントの収集自体は本モジュールの責務外(fandhe-frontend-wasm-fullが座標列を正規化して dispatch する)。hidden_inputは全ストロークのd文字列を;結合した値をフォーム送信する。points 数・ストローク数には上限(MAX_POINTS_PER_STROKE/MAX_STROKES)があり、改ざんされた dispatch payload・hydration 属性による無制限メモリ確保を防ぐ。- 参照突合(イシュー #1620):
controlにrole="application"・aria-roledescription="signature pad"・非 disabled 時tabindex="0"を追加し zag.js のgetControlPropsと一致させた。呼び出し側attrsからのこれら固定属性・data-disabledの偽装は fail-closed に除去する。
Anatomy
root
label
control
guide
segment
segment-path
clear-trigger
hidden-inputAPI Reference
Arguments
| Name | Type | Default | Description |
|---|---|---|---|
root(disabled) | bool | — | data-disabled を反映する。 |
root(empty) | bool | — | strokes が空かどうか。data-empty として反映する(本フレームワーク固有の拡張)。 |
label(disabled) | bool | — | data-disabled を反映する。タグはネイティブ <label> 要素(イシュー #1620)。 |
control(disabled) | bool | — | data-disabled・非 disabled 時 tabindex="0"・disabled 時 aria-disabled="true" を反映する。role="application"/aria-roledescription="signature pad" は常時固定。 |
segment(width, height) | u32, u32 | — | viewBox="0 0 {width} {height}" として描画領域寸法を出力する。 |
segment(aria_label_text) | Option<&str> | — | Some のとき aria-label を付与する。未指定時は role="img" のみ(偽の説明文を捏造しない fail-closed 方針)。 |
segment_path(stroke) | &Stroke | — | d 属性値は stroke_path_d(stroke) の内部生成文字列のみ(fill/stroke は付与せず headless 中立を保つ)。 |
guide(disabled) | bool | — | data-disabled を反映する(イシュー #1620、ark docs Data Attributes 表の Guide 行と一致)。 |
clear_trigger(disabled) | bool | — | ネイティブ disabled + data-disabled を反映する。 |
hidden_input(name, value) | &str, &str | — | フォーム送信名と、全ストロークを ; 結合した値。 |
Data Attributes
| Part | Attribute | Observed Values |
|---|---|---|
clear-trigger | data-disabled | (値なし) |
control | data-disabled | (値なし) |
control | data-readonly | (値なし) |
guide | data-disabled | (値なし) |
hidden-input | data-disabled | (値なし) |
label | data-disabled | (値なし) |
root | data-disabled | (値なし) |
root | data-empty | (値なし) |
Examples
Empty, disabled
ストロークが 1 本もない無効化状態(root(empty: true, disabled: true))。guide のみが描画される。
Read-only
SignaturePad::new(strokes, false, true) の read-only 状態。data-readonly は SignaturePad::control メソッド経由でのみ付与される(自由関数 control にはこの属性はない)。
Custom CSS
headless-ui はスタイルレスです。data-scope/data-part/data-* をセレクタに使い、segment-path の fill/stroke・control の枠線と :focus-visible リング・guide のベースライン・root[data-empty] による clear-trigger の非表示を自前 CSS で当てられます。
headless-ui はスタイルレスです。data-scope/data-part/data-* をセレクタに使い、以下のような CSS を自前で当てられます。
[data-scope="signature-pad"][data-part="control"] {
display: inline-block;
border: 1px solid #ccc;
border-radius: 6px;
}
[data-scope="signature-pad"][data-part="control"]:focus-visible {
outline: 2px solid #06c;
outline-offset: 2px;
}
[data-scope="signature-pad"][data-part="control"][data-disabled] {
opacity: 0.5;
}
[data-scope="signature-pad"][data-part="segment-path"] {
fill: none;
stroke: currentColor;
stroke-width: 2;
}
[data-scope="signature-pad"][data-part="guide"] {
border-bottom: 1px dashed #999;
}
[data-scope="signature-pad"][data-part="root"][data-empty] [data-part="clear-trigger"] {
display: none;
}Accessibility
Keyboard Interactions
| Key | Description |
|---|---|
| Tab | control(tabindex="0"、非 disabled 時のみ)へフォーカスを移動する。参照(ark-ui docs / zag.js)に Keyboard Interactions 表は存在せず、描画自体はポインタ専用のまま(undo 等のアクションに既定キー割り当てはない)。 |
| Enter / Space | フォーカスが clear-trigger(ネイティブ <button>)上にあるとき、ブラウザ既定の活性化操作で全ストロークを削除する。 |
WAI-ARIA
| Attribute | Description |
|---|---|
| role="application" (control) | 固定付与。zag.js getControlProps と一致(イシュー #1620)。 |
| aria-roledescription="signature pad" (control) | 固定付与。 |
| aria-disabled="true" (control) | disabled が true のときのみ付与する(非 disabled 時は tabindex="0" を付与する)。 |
| aria-label / aria-labelledby (control) | 偽の説明文を捏造しない fail-closed 方針により本モジュールは自動付与しない。呼び出し側が attrs 経由でアクセシブルネームを渡す契約。 |
| role="img" (segment) | aria_label_text が None のときのみ role="img" のみを出力し、偽の説明文を作らない(aria_label_text が Some のときは aria-label を併せて付与する)。 |