Skip to content
fandhe-frontendcore v0.4.3
GitHub

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 / SpaceVisibilityTriggerネイティブ <button type="button"> のため、フォーカス時の Enter/Space によるクリック相当の発火はブラウザ標準操作として成立します。クリックから表示切替への dispatch("toggle")配線自体は fandhe-frontend-wasm-full 側の責務です。
TabInput → 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 時の減光の最小例です。

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;
}

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
      indicator

API Reference

Arguments

NameTypeDefaultDescription
root/control/input/visibility_trigger/indicator(visible)bool—表示中かどうか(data-state/type/aria-pressed を決める)。
PasswordInputProps.id&str—ベース id。input の id("{id}-input")の導出元。
PasswordInputProps.disabledbool—input/visibility_trigger にネイティブ disabled + data-disabled を付与する。
PasswordInputProps.readonlybool—input にネイティブ readonly + data-readonly を、6 パーツ全てに data-readonly を付与する。visibility_trigger にはネイティブ disabled を合成しない。
PasswordInputProps.invalidbool—root/control/input/label/indicator に data-invalid を、input に aria-invalid="true" を付与する。
PasswordInputProps.requiredbool—label/input にネイティブ required(input のみ)+ data-required を付与する。
PasswordInputProps.autocompletePasswordAutocomplete—CurrentPassword(autocomplete="current-password")または NewPassword("new-password")。
indicator(props)&PasswordInputProps—disabled/invalid/readonly の状態反映(aria-hidden="true" 固定、意味論は visibility_trigger が担う)。

Data Attributes

PartAttributeObserved Values
controldata-disabled(値なし)
controldata-invalid(値なし)
controldata-readonly(値なし)
controldata-statehidden, visible
indicatordata-disabled(値なし)
indicatordata-invalid(値なし)
indicatordata-readonly(値なし)
indicatordata-statehidden, visible
inputdata-disabled(値なし)
inputdata-invalid(値なし)
inputdata-readonly(値なし)
inputdata-required(値なし)
inputdata-statehidden, visible
labeldata-disabled(値なし)
labeldata-invalid(値なし)
labeldata-readonly(値なし)
labeldata-required(値なし)
rootdata-disabled(値なし)
rootdata-invalid(値なし)
rootdata-readonly(値なし)
rootdata-statehidden, visible
visibility-triggerdata-disabled(値なし)
visibility-triggerdata-readonly(値なし)
visibility-triggerdata-statehidden, 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 を自前で当てられます。

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

KeyDescription
Enter / Spacevisibility_trigger はネイティブ <button type="button"> のため、フォーカス時の Enter/Space によるクリック相当の発火はブラウザ標準操作として成立する。クリックから表示切替への dispatch 配線("toggle")は fandhe-frontend-wasm-full 側の責務。
Tabinput → visibility_trigger の順にフォーカスが移動する。トリガーには tabindex を付与しないため tab 順序から除外されない(モジュール doc「参考サイトとの意図的な差分」節参照)。

WAI-ARIA

AttributeDescription
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 が担うため重複読み上げを防ぐ)。