パスワード強度メーター
stimeo--password-strength
軽量なヒューリスティックでパスワード強度を算定し、メーターとラベルを駆動する。
stimeo--password-strength コントローラは、ゼロ依存のヒューリスティック(長さのマイルストーンと文字種の多様性。単純な繰り返しは弱に丸める)でパスワード強度を推定し、Password Reveal と対でサインアップ体験を補完します。専用の APG パターンは無く、メーター表示は Meter に準じます。入力ごとにメーターの aria-valuenow を同期し、レベルのトークンを data-strength に、0–1 の塗りを --stimeo-password-strength に反映し、レベルを polite な aria-live ラベルへ書き込みます(入力中の読み上げ氾濫を避けるためデバウンス)。推定は辞書/zxcvbn 級ではありません。必要なら利用側でより強力な推定器に差し替えてください。ライブラリは挙動のみを提供し、見た目はこの Playground が持ちます。
入力すると強度が更新されます。判定は軽量なヒューリスティック(長さと文字種の多様性)であり辞書照合ではないため、目安として扱ってください。メーターの値とラベルは即時に更新され、読み上げの割り込みを避けるため音声ラベルだけデバウンスされます。
<%# Markup for the password-strength demo.
The controller scores the field with a lightweight zero-dependency heuristic and
drives the meter (aria-valuenow), the data-strength token, and the 0–1 fill on the
--stimeo-password-strength custom property. The input sits inside the controller so
its data-action input->...#evaluate binds directly. Level labels are localized via
the levels value; demo.css colors the bar by the locale-independent data-strength band,
so the look never depends on the (translated) label text. The library writes no CSS. %>
<% levels = t("components.password_strength.demo.levels").to_json %>
<div
class="password-strength"
data-controller="stimeo--password-strength"
data-stimeo--password-strength-levels-value="<%= levels %>">
<label class="password-strength__label" for="password-strength-demo-input">
<%= t("components.password_strength.demo.label") %>
</label>
<input
class="password-strength__input"
id="password-strength-demo-input"
type="password"
autocomplete="new-password"
aria-describedby="password-strength-demo-readout"
data-stimeo--password-strength-target="input"
data-action="input->stimeo--password-strength#evaluate">
<div
class="password-strength__meter"
role="meter"
aria-valuemin="0"
aria-valuemax="4"
aria-valuenow="0"
aria-label="<%= t('components.password_strength.demo.meter_label') %>"
data-stimeo--password-strength-target="meter">
<span class="password-strength__bar"></span>
</div>
<span
class="password-strength__readout"
id="password-strength-demo-readout"
aria-live="polite"
data-stimeo--password-strength-target="label"></span>
</div>
<p class="password-strength__hint"><%= t("components.password_strength.demo.hint") %></p>
/*
* Presentation-only styles for the password-strength demo.
* The bar width comes from --stimeo-password-strength (0–1); its color comes from
* the data-strength band (weak…strong), which is locale-independent — so the look
* holds even though the visible level label is localized via the levels value.
*/
.password-strength {
display: flex;
flex-direction: column;
gap: 0.5rem;
max-width: 24rem;
}
.password-strength__label {
font-weight: 600;
}
.password-strength__input {
padding: 0.5rem 0.75rem;
border: 1px solid var(--border-strong);
border-radius: 0.375rem;
background: var(--bg);
font: inherit;
color: var(--fg);
}
.password-strength__input:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 1px;
}
.password-strength__meter {
height: 0.5rem;
border-radius: 0.375rem;
background: var(--surface-subtle);
overflow: hidden;
}
.password-strength__bar {
display: block;
height: 100%;
border-radius: inherit;
width: calc(var(--stimeo-password-strength, 0) * 100%);
background: var(--border-interactive);
transition:
width 0.2s ease,
background-color 0.2s ease;
}
/* Color bands keyed on the locale-independent data-strength token (weak → strong). */
.password-strength[data-strength="weak"] .password-strength__bar {
background: var(--danger-500);
}
.password-strength[data-strength="fair"] .password-strength__bar {
background: var(--amber-500);
}
.password-strength[data-strength="good"] .password-strength__bar {
background: var(--amber-500);
}
.password-strength[data-strength="strong"] .password-strength__bar {
background: var(--leaf-500);
}
.password-strength__readout {
min-height: 1.25rem;
font-size: 0.85rem;
color: var(--muted);
}
.password-strength__hint {
margin: 0.25rem 0 0;
max-width: 32rem;
font-size: 0.85rem;
line-height: 1.5;
color: var(--muted);
}
@media (prefers-reduced-motion: reduce) {
.password-strength__bar {
transition: none;
}
}
このデモに固有の消費側 JS はありません(挙動はコントローラが担います)。
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。 共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--password-strength"
ターゲット
| 名前 | 説明 | 属性 |
|---|---|---|
input
必須
|
強度を算定する対象のパスワード入力。 | data-stimeo--password-strength-target="input" |
meter
必須
|
aria-valuenow がレベルを反映するメーター要素。 | data-stimeo--password-strength-target="meter" |
label
|
レベルのトークンを書き込む polite ライブリージョン。 | data-stimeo--password-strength-target="label" |
値(Values)
| 名前 | 説明 | 属性 |
|---|---|---|
minScore
|
これ未満を不合格扱いとするスコア。data-below-min フックと change の meetsMin=false で通知。0(既定)はゲート無効。 |
data-stimeo--password-strength-min-score-value |
levels
|
昇順の表示ラベル(label と change の level に入る)。要素数が段階数を決める。翻訳可。data-strength は別系統の固定バンド(既定 weak, fair, good, strong)。 |
data-stimeo--password-strength-levels-value |
アクション
| 名前 | 説明 | アクション |
|---|---|---|
evaluate
|
入力を再採点する。入力欄の input イベントにバインドする。 |
stimeo--password-strength#evaluate |
イベント
| 名前 | 説明 | イベント |
|---|---|---|
change
|
evaluate ごとに発火。detail.score・detail.level・detail.max・detail.meetsMin を伴う。 |
stimeo--password-strength:change |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
aria-valuenow |
meter ターゲット | 強度レベル(0 = 空 … レベルの数)。 |
data-strength |
ルート要素 | ロケール非依存の固定バンド(weak / fair / good / strong)。levels を翻訳しても不変で CSS フックに使える。空のときは付かない。 |
data-below-min |
ルート要素 | score が minScore 未満のとき付与(既定 minScore=0 では付かない)。 |
--stimeo-password-strength |
ルート要素 | 利用側 CSS がバー幅に変換する 0–1 の塗り。 |