Password Input
fandhe-frontend-headless-ui の password_input mod が提供する表示切替トリガー付きパスワード入力の unstyled 部品です。Root / Label / Control / Input / VisibilityTrigger / Indicator の 6 anatomy パーツで構成され、表示切替(visible)状態機械のみを持ちます。パスワード値そのものは一切保持・出力しません(input は value 引数自体を持たない設計です)。
/themes/ 側の PasswordInput(Chakra ライクな variant/size を持つスタイル層)と異なり、CSS を一切持ちません。
PasswordInputProps(disabled/readonly/invalid/required)は次のように各パーツへ反映されます。root/control/indicator は data-disabled/data-invalid/data-readonly を、label はこれら 3 つに加えて data-required を、input はこれら 4 つ全てを付与します(root/control/input の data-state("visible"/"hidden")と input の data-required は ark-ui/zag には無い superset ですが、後方互換のため維持しています)。readonly: true のとき input にはネイティブ readonly 存在属性も付きますが、visibility_trigger にはネイティブ disabled を合成しません(表示切替は値を変更しない操作のため、読み取り専用でもキーボード操作での表示確認を封じない安全側の判断です)。input にはあわせて autocapitalize="off"・spellcheck="false" を固定付与し、表示中のパスワード文字列がブラウザのリモートスペルチェック・自動大文字化サービスへ送られる経路を減らします。
キーボード操作
| キー | 対象 | 効果 |
|---|---|---|
| Enter / Space | VisibilityTrigger | ネイティブ <button type="button"> のため、フォーカス時の Enter/Space によるクリック相当の発火はブラウザ標準操作として成立します。クリックから表示切替への dispatch("toggle")配線自体は fandhe-frontend-wasm-full 側の責務です。 |
| Tab | Input → VisibilityTrigger | この順にフォーカスが移動します。VisibilityTrigger には tabindex を付与しないため、tab 順序から除外されません(下記「参考サイトとの差分」参照)。 |
参考サイトとの差分
ark-ui/zag は VisibilityTrigger へ aria-expanded(表示中で true)+ tabIndex: -1(tab 順序から除外しポインタ操作専用にする)を採用しますが、本コンポーネントは意図的にこれへ追随していません。表示切替はトグルボタンパターン(WAI-ARIA APG)であり input は開閉する別領域ではないため aria-pressed を維持し、キーボード操作のみの利用者が表示切替できるよう tabindex を付与せず tab 順序に残しています(Radix Primitives の Password Toggle Field も「キーボード操作時はトリガーへフォーカス維持」としており、この判断と整合します)。
ポインタ・キーボード操作の実際の DOM 配線(fandhe-frontend-wasm-full)は本部品のスコープ外です。本部品は SSR 静的マークアップと dispatch 契約のみを提供します。
スタイル済みの表示例は Password Input を参照してください。
関連 API: fandhe-frontend-headless-ui API
自前 CSS の最小例
Themes 版を使わず本部品を直接使う場合、[data-scope="password-input"][data-part="..."] セレクタでスタイルを当てます。以下は control の枠線、表示中トリガーの強調、invalid 時の input 枠線、disabled/readonly 時の減光の最小例です。
[data-scope="password-input"][data-part="control"] {
display: inline-flex;
align-items: center;
border: 1px solid #ccc;
}
[data-scope="password-input"][data-part="visibility-trigger"][data-state="visible"] {
color: #0a7;
}
[data-scope="password-input"][data-part="input"][data-invalid] {
border-color: #d33;
}
[data-scope="password-input"][data-part="root"][data-disabled],
[data-scope="password-input"] [data-readonly] {
opacity: 0.6;
}Demo
枠線・余白は docs サイト側のデモ枠であり、fandhe-frontend-headless-ui 自体はスタイルを持ちません。
Features
- Root / Label / Control / Input / VisibilityTrigger / Indicator の 6 anatomy パーツを提供する。表示切替(
visible)状態機械のみを持ち、パスワード値そのものは一切保持しない(inputはvalue引数自体を持たない)。 inputはvisibleの bool からtype="password"/type="text"を決定的に導出する(呼び出し側文字列を type スロットへ通す経路がない)。data-state(root/controlと同じ visible/hidden 語彙)・autocapitalize="off"・spellcheck="false"も固定付与する(ark-ui/zag 準拠、イシュー #1614)。readonly(PasswordInputProps)でinputにネイティブreadonlyを、6 パーツ全てにdata-readonlyを付与する。visibility_triggerへはネイティブdisabledを合成しない(表示切替は値を変更しない操作のため、readonly でもキーボード操作での表示確認を封じない)。- パーツ別
data-*分布は ark-ui/zag に合わせる:root/control/indicatorはdisabled/invalid/readonly、labelはdisabled/invalid/readonly/required、inputはdisabled/invalid/readonly/required(requiredは当方の superset。root/controlのdata-stateも同様に superset として維持し、後方互換を壊さない)。 visibility_triggerはbutton type="button"+aria-pressed(トグルボタンパターン)+aria-controlsで意味論を担う。aria-labelは呼び出し側が付与する(国際化はアプリ側の責務)。zag はaria-expanded/tabIndex: -1を採用するが、本コンポーネントは意図的に追随しない(aria-pressedは input が展開/折り畳み対象領域でないため、tab 順序は Radix の「キーボード操作時はトリガーへフォーカス維持」と整合させ tabindex を付与しない。詳細はモジュール doc「参考サイトとの意図的な差分」節参照)。PasswordInputPropsからinputの id("{id}-input")を決定的に導出し、label/visibility_triggerへも同じ値を一貫伝播する。name属性が必要な場合はinputのattrsへ呼び出し側が付与する(reserved キーではない)。Defaultは Hidden(パスワードを隠す方が安全側の既定)。hydration 属性data-hydrate-visibleは fail-closed(未知値をHydrateErrorで拒否)。- ark-ui/zag の
ignorePasswordManagers相当(data-1p-ignore等のパスワードマネージャ連携抑止属性)は本コンポーネントでは提供しない(機能プロップでありデータ属性の状態語彙ではないため、イシュー #1614 でも見送り)。
Anatomy
root
label
control
input
visibility-trigger
indicatorAPI Reference
Arguments
| Name | Type | Default | Description |
|---|---|---|---|
root/control/input/visibility_trigger/indicator(visible) | bool | — | 表示中かどうか(data-state/type/aria-pressed を決める)。 |
PasswordInputProps.id | &str | — | ベース id。input の id("{id}-input")の導出元。 |
PasswordInputProps.disabled | bool | — | input/visibility_trigger にネイティブ disabled + data-disabled を付与する。 |
PasswordInputProps.readonly | bool | — | input にネイティブ readonly + data-readonly を、6 パーツ全てに data-readonly を付与する。visibility_trigger にはネイティブ disabled を合成しない。 |
PasswordInputProps.invalid | bool | — | root/control/input/label/indicator に data-invalid を、input に aria-invalid="true" を付与する。 |
PasswordInputProps.required | bool | — | label/input にネイティブ required(input のみ)+ data-required を付与する。 |
PasswordInputProps.autocomplete | PasswordAutocomplete | — | CurrentPassword(autocomplete="current-password")または NewPassword("new-password")。 |
indicator(props) | &PasswordInputProps | — | disabled/invalid/readonly の状態反映(aria-hidden="true" 固定、意味論は visibility_trigger が担う)。 |
Data Attributes
| Part | Attribute | Observed Values |
|---|---|---|
control | data-disabled | (値なし) |
control | data-invalid | (値なし) |
control | data-readonly | (値なし) |
control | data-state | hidden, visible |
indicator | data-disabled | (値なし) |
indicator | data-invalid | (値なし) |
indicator | data-readonly | (値なし) |
indicator | data-state | hidden, visible |
input | data-disabled | (値なし) |
input | data-invalid | (値なし) |
input | data-readonly | (値なし) |
input | data-required | (値なし) |
input | data-state | hidden, visible |
label | data-disabled | (値なし) |
label | data-invalid | (値なし) |
label | data-readonly | (値なし) |
label | data-required | (値なし) |
root | data-disabled | (値なし) |
root | data-invalid | (値なし) |
root | data-readonly | (値なし) |
root | data-state | hidden, visible |
visibility-trigger | data-disabled | (値なし) |
visibility-trigger | data-readonly | (値なし) |
visibility-trigger | data-state | hidden, visible |
Examples
Invalid + new-password
登録フォーム向けに autocomplete="new-password" を指定し、invalid/required を立てた状態。aria-invalid="true" が input に付与される。
Read-only
readonly: true の状態。input にネイティブ readonly が、6 パーツ全てに data-readonly が付く。表示切替トリガーはキーボード操作のまま無効化されない。
Custom CSS
headless-ui はスタイルレスであるため、[data-scope]/[data-part]/[data-state]/data-* 状態属性セレクタを利用者が自前 CSS で装飾する最小例です。
headless-ui はスタイルレスです。data-scope/data-part/data-state/data-* をセレクタに使い、以下のような CSS を自前で当てられます。
[data-scope="password-input"][data-part="control"] {
display: inline-flex;
align-items: center;
border: 1px solid #ccc;
}
[data-scope="password-input"][data-part="visibility-trigger"][data-state="visible"] {
color: #0a7;
}
[data-scope="password-input"][data-part="input"][data-invalid] {
border-color: #d33;
}
[data-scope="password-input"][data-part="root"][data-disabled],
[data-scope="password-input"] [data-readonly] {
opacity: 0.6;
}Accessibility
Keyboard Interactions
| Key | Description |
|---|---|
| Enter / Space | visibility_trigger はネイティブ <button type="button"> のため、フォーカス時の Enter/Space によるクリック相当の発火はブラウザ標準操作として成立する。クリックから表示切替への dispatch 配線("toggle")は fandhe-frontend-wasm-full 側の責務。 |
| Tab | input → visibility_trigger の順にフォーカスが移動する。トリガーには tabindex を付与しないため tab 順序から除外されない(モジュール doc「参考サイトとの意図的な差分」節参照)。 |
WAI-ARIA
| Attribute | Description |
|---|---|
| aria-pressed (visibility_trigger) | トグルボタンパターン。表示中(visible == true)で "true"。 |
| aria-controls (visibility_trigger) | input の id を指す。 |
| aria-label (visibility_trigger) | 呼び出し側が attrs へ付与する(固定文言は持たない)。推奨文言は表示中 "Hide password"、非表示中 "Show password"(ark-ui/zag の既定翻訳に準拠)。 |
| aria-invalid (input) | invalid が true のときのみ "true" を出力する。 |
| aria-hidden (indicator) | 常に "true"(装飾専用、意味論は visibility_trigger の aria-pressed が担うため重複読み上げを防ぐ)。 |