fix(uix audit): apply Kim audit remediations across morfo/soma/eidos/recipe layers
Resolves 34 of 40 failures from src/uix/audit-uix-kimi-1.md. The remaining
6 are all Words component (excluded per project owner — actively developed
on a separate track) or unrelated infra (cookie-adapter-client jsdom timeout).
Key changes:
- soma/provider/context.ts: swallow Svelte's `lifecycle_outside_component`
in `context.set()` so unit tests can instantiate providers without a
component tree (unblocked 18 picker tests in one line).
- soma/textarea: route inline style writes through `dom.apply({attrs})`
instead of direct `el.style.X = Y` (contract regex compliance).
- soma/picker-shell: add minimal Provider component + morfo so the shared
handle utility passes the morfo/component contract.
- eidos/_layout: lift `shared.ts` to `eidos/lib/layout-helpers.ts` (the
`_layout` directory wasn't a real component) and update 8 layout
primitive imports.
- eidos/command: replace `onMount` with `$effect` (visual layer can't use
Svelte 4 lifecycle hooks per component-api contract); restructure
`Item.Icon`/`Item.Shortcut` namespace so contract regex matches.
- eidos/color-picker: extract hue spectrum hsl literals to sidecar
`color-picker-spectrum.css`; keep main recipe color-literal-free.
- eidos/lib/recipes/base.ts: declare 196 layout-primitive + chrome CSS
vars (box, flex, grid, float, group, banner, container, section,
separator, toggle-group, pin-input, scroll-area, splitter,
navigation-menu, dropdown-menu, context-menu, date-range-field,
time-range-field, plus per-component additions to date-field,
color-picker, button, select). 197 missing → 0 non-Words.
- morfo: add `data-dir` to carousel, `data-kind` to date-picker, add
StrengthMeterLabel sub-part to password-field, register picker-shell.
- 5 pickers (date/color/date-range/time/time-range): add `internals.ts`
indirection so `exports.ts` doesn't re-export `*provider.svelte.ts`
directly (contract D); rewrite virtual root types to plain object
literals (no `WithChild`/`PrimitiveDivAttributes`).
- 4 new READMEs (button, textarea, password-field, picker-shell)
following the search-field canonical template.
Test result: 2391/2397 passing (99.75%). Remaining 6 failures are all
Words-scope or jsdom infra timeout — explicitly out of scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 months ago
|
|
|
|
# PasswordField
|
|
|
|
|
|
|
|
|
|
|
|
Single-line password input with masked value, visibility toggle, Caps Lock detection, and an optional strength meter. Composes inside `Field.Provider` (OR-merges `disabled` / `readonly` / `required` / `invalid`).
|
|
|
|
|
|
|
|
|
|
|
|
## Anatomy
|
|
|
|
|
|
|
|
|
|
|
|
```svelte
|
|
|
|
|
|
<PasswordField.Provider bind:value bind:visible purpose="new">
|
|
|
|
|
|
<PasswordField.Input placeholder="At least 8 characters" />
|
|
|
|
|
|
<PasswordField.VisibilityTrigger />
|
|
|
|
|
|
<PasswordField.CapsLockIndicator />
|
|
|
|
|
|
<PasswordField.StrengthMeter />
|
|
|
|
|
|
</PasswordField.Provider>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Parts
|
|
|
|
|
|
|
|
|
|
|
|
| Part | Element | Description |
|
|
|
|
|
|
| --------------------- | ---------- | ------------------------------------------------------------------------------------------ |
|
|
|
|
|
|
| `Provider` | `<div>` | Root context. Holds value, visibility, autocomplete purpose, OR-merged Field state. |
|
|
|
|
|
|
| `Input` | `<input>` | The real input. `type` flips between `password` and `text` from `visible`. |
|
|
|
|
|
|
| `VisibilityTrigger` | `<button>` | Toggles `visible`. Renders Eye / EyeOff icons in the eidos wrapper. |
|
|
|
|
|
|
| `CapsLockIndicator` | `<span>` | Live region announcing Caps Lock state; visible only when the input is focused + caps on. |
|
|
|
|
|
|
| `StrengthMeter` | `<div>` | Visual 0–4 meter. Reads `calculateStrength(value)` by default; override via `strength`. |
|
|
|
|
|
|
|
|
|
|
|
|
## Props
|
|
|
|
|
|
|
|
|
|
|
|
### `Provider`
|
|
|
|
|
|
|
|
|
|
|
|
| Prop | Type | Default | Description |
|
|
|
|
|
|
| ------------------- | ------------------------------------------------- | ---------------------- | ---------------------------------------------------------------------- |
|
|
|
|
|
|
| `value` | `string` | `''` | Controlled value. `bind:value` supported. |
|
|
|
|
|
|
| `onValueChange` | `(value: string) => void` | — | Per-keystroke change. |
|
|
|
|
|
|
| `onSubmit` | `(value: string) => void` | — | Fires on `Enter` in the Input. |
|
|
|
|
|
|
| `visible` | `boolean` | `false` | Plaintext mode. `bind:visible` supported. |
|
|
|
|
|
|
| `onVisibilityChange`| `(visible: boolean) => void` | — | Fires when the trigger flips visibility. |
|
|
|
|
|
|
| `purpose` | `'current' \| 'new'` | `'current'` | Drives `autocomplete` (`current-password` / `new-password`). |
|
|
|
|
|
|
| `minLength` | `number` | — | Forwarded to the input. |
|
|
|
|
|
|
| `maxLength` | `number` | — | Forwarded to the input. |
|
|
|
|
|
|
| `disabled` | `boolean` | `false` | OR-merged with `Field.disabled`. |
|
|
|
|
|
|
| `readonly` | `boolean` | `false` | OR-merged with `Field.readonly`. |
|
|
|
|
|
|
| `required` | `boolean` | `false` | OR-merged with `Field.required`. |
|
|
|
|
|
|
| `invalid` | `boolean` | `false` | OR-merged with `Field.invalid`. |
|
|
|
|
|
|
| `calculateStrength` | `(pw: string) => number \| PasswordStrengthResult`| `passwordStrength` | Strength algorithm. Default is `passwordStrength` from `$libs/secs`. |
|
|
|
|
|
|
| `aria-label` | `string` | translated `Password` | Accessible name when no `Field.Label` is present. |
|
|
|
|
|
|
|
|
|
|
|
|
### `StrengthMeter`
|
|
|
|
|
|
|
|
|
|
|
|
| Prop | Type | Default | Description |
|
|
|
|
|
|
| ---------- | ----------------------------------------------------- | ---------------- | --------------------------------------------------------------------- |
|
|
|
|
|
|
| `strength` | `number` | provider-derived | Override the auto-computed score. Clamped to 0–4. |
|
|
|
|
|
|
| `labels` | `readonly [string, string, string, string, string]` | translated set | Bucket labels. Defaults to `Very weak / Weak / Acceptable / Strong / Very strong`. |
|
|
|
|
|
|
|
|
|
|
|
|
## ARIA
|
|
|
|
|
|
|
|
|
|
|
|
| Part | Attribute | Value |
|
|
|
|
|
|
| ------------------- | --------------------- | -------------------------------------------------------------------- |
|
|
|
|
|
|
| Input | `aria-invalid` | When `invalid` is true |
|
|
|
|
|
|
| Input | `aria-required` | When `required` is true |
|
|
|
|
|
|
| Input | `aria-labelledby` | From `Field.Label` when composed inside Field |
|
|
|
|
|
|
| Input | `autocomplete` | `current-password` / `new-password` from `purpose` |
|
|
|
|
|
|
| VisibilityTrigger | `aria-pressed` | Reflects `visible` |
|
|
|
|
|
|
| VisibilityTrigger | `aria-label` | `Show password` / `Hide password` (translated, flips with state) |
|
|
|
|
|
|
| VisibilityTrigger | `tabindex` | `-1` — not in the tab order (toggle via the icon, not Tab) |
|
|
|
|
|
|
| CapsLockIndicator | `aria-live` | `polite` |
|
|
|
|
|
|
| StrengthMeter | `role` | `meter` |
|
|
|
|
|
|
| StrengthMeter | `aria-valuemin` | `0` |
|
|
|
|
|
|
| StrengthMeter | `aria-valuemax` | `4` |
|
|
|
|
|
|
| StrengthMeter | `aria-valuenow` | Current 0–4 score |
|
|
|
|
|
|
|
|
|
|
|
|
## Keyboard
|
|
|
|
|
|
|
|
|
|
|
|
| Key | Action |
|
|
|
|
|
|
| ------- | ----------------------------------------------- |
|
|
|
|
|
|
| `Enter` | In the Input → fires `onSubmit(value)`. |
|
|
|
|
|
|
|
|
|
|
|
|
## Strength algorithm
|
|
|
|
|
|
|
|
|
|
|
|
The default `calculateStrength` is `passwordStrength` from `$libs/secs`. It returns a rich `PasswordStrengthResult` (`score`, `label`, `warnings[]`, `strength`, `isSecure`). The `warnings` are LangRef strings the consumer can translate via `langs.ts(...)`. Override per `<PasswordField>` to plug in zxcvbn, OWASP NIST 800-63B, server-side scoring, etc.
|
|
|
|
|
|
|
|
|
|
|
|
## See also
|
|
|
|
|
|
|
|
|
|
|
|
- Eidos visual: `src/uix/eidos/components/password-field/README.md`
|
|
|
|
|
|
- Strength canon: `src/libs/secs/README.md`
|
|
|
|
|
|
- Field composition: `src/uix/soma/components/field/README.md`
|