- Add `SkipLink` component that appears on focus and jumps to main content.
- Update `en.ts` and `de.ts` with "Skip to content" translation.
- Add `id="main-content"`, `tabIndex={-1}`, and `outline: 'none'` to `Home`, `About`, and `Contact` pages to ensure proper focus management.
- Add tests for `SkipLink`.
- Document Skip Link pattern in `.Jules/palette.md`.
This improves accessibility for keyboard and screen reader users by allowing them to bypass navigation.
Co-authored-by: ragusa-it <196988693+ragusa-it@users.noreply.github.com>
2.1 KiB
2024-05-24 - Accessible Input Validation
Learning: React 19 renders aria-invalid={false} as aria-invalid="false", unlike older versions which might have omitted it. Explicitly handling this in tests is crucial. Also, ensuring DOM cleanup (cleanup()) in afterEach is vital when testing similar components with same labels across tests to avoid "finding the wrong element" false positives/negatives.
Action: Always include afterEach(() => cleanup()) in vitest setup for DOM tests, and expect aria-invalid="false" (or explicitly handle undefined if omission is desired) when testing valid states in React 19.
2024-05-24 - Accessible Loading Buttons
Learning: Replacing button text with a spinner destroys the accessible name.
Action: Use aria-busy="true", keep children in DOM (visually hidden via opacity/class), and overlay spinner absolutely. Ensure wrapper element replicates flex layout (gap/alignment) to prevent layout shifts.
2024-05-24 - Enhancing Inputs Safely
Learning: Adding features like character counters to generic input components must handle both controlled and uncontrolled states. Assuming a component is controlled (using props.value) can break uncontrolled usage by showing stale data (e.g., sticking at 0/100).
Action: When enhancing generic components, detect uncontrolled state (e.g., props.value === undefined) and either implement internal state tracking or gracefully degrade (hide the feature) to avoid misleading UX.
2025-05-24 - Skip Link Focus Management
Learning: For a "Skip to content" link to effectively move focus in an SPA, the target container (e.g., <main>) must be programmatically focusable. Simply having an ID is not enough; tabIndex={-1} is required. Additionally, standard browser behavior might show a focus ring on the container, which is often undesirable for non-interactive wrappers, so outline: 'none' should be applied inline or via CSS.
Action: Always add tabIndex={-1} and outline: 'none' to the target of a Skip Link (#main-content) to ensure correct focus behavior without visual clutter.