diff --git a/apps/playground-e2e/playwright.config.ts b/apps/playground-e2e/playwright.config.ts index 12115dd..3ebf88d 100644 --- a/apps/playground-e2e/playwright.config.ts +++ b/apps/playground-e2e/playwright.config.ts @@ -3,11 +3,11 @@ import { nxE2EPreset } from '@nx/playwright/preset'; import { workspaceRoot } from '@nx/devkit'; /* - * Dedicated port: 4200 is the default `nx serve playground` port, so a - * developer's already-running dev server (of this app or any other) would be - * silently reused and the suite would screenshot the wrong application. + * Dedicated e2e port, distinct from common local dev-server ports: an + * already-running dev server (of this app or any other) would be silently + * reused and the suite would screenshot the wrong application. */ -const baseURL = process.env['BASE_URL'] || 'http://localhost:4300'; +const baseURL = process.env['BASE_URL'] || 'http://localhost:4310'; /** * Read environment variables from file. @@ -28,8 +28,8 @@ export default defineConfig({ }, /* Run your local dev server before starting the tests */ webServer: { - command: 'pnpm exec nx run playground:serve --port 4300', - url: 'http://localhost:4300', + command: 'pnpm exec nx run playground:serve --port 4310', + url: 'http://localhost:4310', reuseExistingServer: true, cwd: workspaceRoot, }, diff --git a/apps/playground-e2e/src/parity.spec.ts-snapshots/input-default-focused-chromium-darwin.png b/apps/playground-e2e/src/parity.spec.ts-snapshots/input-default-focused-chromium-darwin.png index d6c4b6f..673d6b7 100644 Binary files a/apps/playground-e2e/src/parity.spec.ts-snapshots/input-default-focused-chromium-darwin.png and b/apps/playground-e2e/src/parity.spec.ts-snapshots/input-default-focused-chromium-darwin.png differ diff --git a/apps/playground-e2e/src/parity.spec.ts-snapshots/input-default-focused-chromium-linux.png b/apps/playground-e2e/src/parity.spec.ts-snapshots/input-default-focused-chromium-linux.png index 127c4e9..a3cd7ba 100644 Binary files a/apps/playground-e2e/src/parity.spec.ts-snapshots/input-default-focused-chromium-linux.png and b/apps/playground-e2e/src/parity.spec.ts-snapshots/input-default-focused-chromium-linux.png differ diff --git a/apps/playground/src/app/themes/_flydocs-theme-dark.scss b/apps/playground/src/app/themes/_flydocs-theme-dark.scss index 1dee954..320aa49 100644 --- a/apps/playground/src/app/themes/_flydocs-theme-dark.scss +++ b/apps/playground/src/app/themes/_flydocs-theme-dark.scss @@ -19,8 +19,10 @@ --ff-color-surface: #161826; --ff-color-border: #363b4e; - /* Same alpha-drop reasoning as the light theme, over the dark ring hue. */ --ff-color-border-focus: #6378ff; + /* Focus ring, consumed by ff-input as the wrapper's focus box-shadow + (translucent brand-blue halo, not a hard-edged outline). */ + --ff-input-focus-ring: rgba(99, 120, 255, 0.3); --ff-color-on-surface: #f6f7fa; --ff-text-primary: #f6f7fa; diff --git a/apps/playground/src/app/themes/_flydocs-theme.scss b/apps/playground/src/app/themes/_flydocs-theme.scss index 2be60a6..5b2ee52 100644 --- a/apps/playground/src/app/themes/_flydocs-theme.scss +++ b/apps/playground/src/app/themes/_flydocs-theme.scss @@ -27,10 +27,11 @@ /* ---- Surfaces / structure ---- */ --ff-color-surface: #ffffff; --ff-color-border: #e1e4ec; - /* Focus ring hue only: Flydocs models focus as a translucent ring - (rgba brand-blue at 25%); ff consumes it as a solid 2px outline, - so the hue is kept and the alpha dropped. */ + /* Focus border hue, consumed by ff-input as a solid 2px border-color. */ --ff-color-border-focus: #3b59f5; + /* Focus ring, consumed by ff-input as the wrapper's focus box-shadow + (translucent brand-blue halo, not a hard-edged outline). */ + --ff-input-focus-ring: rgba(59, 89, 245, 0.25); /* ---- Text-on-surface roles ---- `on-surface` has no named Flydocs counterpart; it plays the same diff --git a/docs/flydocs-parity-report.md b/docs/flydocs-parity-report.md index 6239050..0f65b6c 100644 --- a/docs/flydocs-parity-report.md +++ b/docs/flydocs-parity-report.md @@ -44,7 +44,7 @@ zoom**. | 4 | `ff-panel` | alert warning/danger, default with heading/footer | ✅ Pass | | 4 | `ff-card` | basic, md shadow | ✅ Pass | | 4 | `ff-tab-bar` | underline/pills, active not-first, badge+icon tabs | ✅ Pass | -| 5 | `ff-input` | rest / hover / **focus** / error / disabled | ⚠️ Pass with one flagged delta (focus-ring rendering) | +| 5 | `ff-input` | rest / hover / **focus** / error / disabled | ✅ Pass (focus-ring delta resolved, see FIR-318) | | 5 | `ff-checkbox` | unchecked / checked / disabled-checked | ✅ Pass | | 5 | `ff-radio` | group with selected option | ✅ Pass | @@ -52,21 +52,23 @@ zoom**. Per the gate's contract, components that fail the comparison are **not** fixed inside FF-CAT-20 — each perceptible delta generates its own issue. -One delta is flagged, borderline-perceptible and pre-documented in the -equivalence doc: - -1. **Input focus ring: solid outline vs translucent halo.** Flydocs models - focus as a translucent box-shadow ring (brand blue at 25% alpha); - `ff-input` consumes `--ff-color-border-focus` as a solid 2px outline on - the field wrapper (`:focus-within`, after the FIR-289 refactor moved it - off the native control). The hue matches; the rendering mechanism does - not. At 100% zoom the difference is visible on direct comparison - (hard edge vs soft glow). Changing it means teaching the component a - ring-style focus treatment — a component change, out of this gate's - scope. +No delta is currently flagged. ## Resolved deltas +- **Input focus ring: solid outline vs translucent halo — resolved (FIR-318).** + `ff-input`'s field wrapper already rendered its focus indicator through a + dedicated component token, `--ff-input-focus-ring` (falling back to + `--ff-color-border-focus` when unset). The Flydocs theme now pins that + token to the product's own translucent halo value + (`--hub-sys-focus-ring-color`, `rgba(59, 89, 245, 0.25)` in light, + `rgba(99, 120, 255, 0.3)` in dark), instead of leaving it to fall back to + the solid focus border color. The wrapper's `border-color` (driven by + `--ff-color-border-focus`) still changes on focus alongside the ring, so + the focus indicator carries two independent visual cues (border color + step-up + halo), keeping it perceptible at the reduced ring alpha. See + `docs/flydocs-theme-token-equivalence.md` for the full reasoning. + - **Neutral badge chip runs cool, Flydocs' runs warm — resolved (FIR-317).** `ff-badge`'s neutral chip background now resolves through its own component token, `--ff-badge-neutral-bg` (falling back to diff --git a/docs/flydocs-theme-token-equivalence.md b/docs/flydocs-theme-token-equivalence.md index d76eee6..adae51e 100644 --- a/docs/flydocs-theme-token-equivalence.md +++ b/docs/flydocs-theme-token-equivalence.md @@ -69,7 +69,8 @@ published theme. |---|---|---|---|---| | `--ff-color-surface` | Default component chrome background (card, header, nav, panel) | `--hub-sys-color-surface-default` (= `--hub-ref-color-neutral-0`) | `#ffffff` | Direct | | `--ff-color-border` | Default hairline border (cards, tables, dividers) | `--hub-sys-border-color-default` (= `--hub-ref-color-neutral-200`) | `#e1e4ec` | Direct | -| `--ff-color-border-focus` | Solid 2px focus outline | `--hub-sys-focus-ring-color` (`rgba(59,89,245,.25)`, i.e. brand blue) | `#3b59f5` | Derived — Flydocs models focus as a translucent ring meant for `box-shadow`; ff consumes it as a solid `outline`. The hue (brand blue = `--hub-ref-color-blue-500`) is kept, the alpha dropped so the stroke stays visible as a hard outline. | +| `--ff-color-border-focus` | Solid 2px focus border-color on the input wrapper | `--hub-sys-focus-ring-color` (`rgba(59,89,245,.25)`, i.e. brand blue) | `#3b59f5` | Derived — the border-color role only ever needs the ring's hue (brand blue = `--hub-ref-color-blue-500`); the alpha channel is not applicable to a `border-color` and is carried instead by `--ff-input-focus-ring` below. | +| `--ff-input-focus-ring` (component-scoped, `ff-input`) | Translucent focus halo on the input wrapper's `box-shadow` | `--hub-sys-focus-ring-color` | `rgba(59, 89, 245, 0.25)` (dark: `rgba(99, 120, 255, 0.3)`, `--hub-sys-focus-ring-color` re-pinned in `_theme-dark.scss`) | Direct — `ff-input` already renders its focus indicator as a `box-shadow` ring (`var(--ff-input-focus-ring, var(--ff-color-border-focus))`), so the full rgba value (hue + alpha) is ported as-is instead of being lossily collapsed into a solid color. | | `--ff-color-on-primary` | Text/icon color atop a primary-filled surface (button label, checked radio dot, checkbox mark) | `--hub-sys-color-primary-on` / `-on-default` | `#ffffff` | Direct | | `--ff-color-on-surface` | Text/icon color atop the default surface (menu item text) | no named counterpart | `#161826` | Derived — no Flydocs token is called "on-surface"; the role (primary reading text over the default surface) is identical to `text-primary`, so it takes that value. |