# Lovable Accessibility

> Legal + engineering playbook for making Lovable.dev apps accessible under Israeli law (ת"י 5568 / תקנה 35) and the EU European Accessibility Act (EN 301 549), including Hebrew/RTL correctness, the shadcn-Radix defect list, a self-built accessibility toolbar spec, and the הצהרת נגישות page. Trigger whenever the user mentions נגישות, accessibility, WCAG, תקן 5568, תקנה 35, EAA, EN 301 549, הצהרת נגישות, accessibility statement, סרגל/תוסף נגישות, accessibility widget/overlay, screen reader, keyboard navigation, color contrast, or RTL correctness in a Lovable project.

- Skill: `gidila91/lovable-accessibility` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add gidila91/lovable-accessibility`
- Raw SKILL.md: https://api.skillmd.com/api/skills/gidila91/lovable-accessibility/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: gidila91 (https://skillmd.com/u/gidila91)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/gidila91/lovable-accessibility

---


# Lovable Accessibility — Israel (ת"י 5568) + EU (EAA)

Lovable output starts from a good base (Radix primitives ship real ARIA, focus management, and keyboard handling) and then loses it in the generated app layer: custom `<div onClick>` controls, English `aria-label`s on Hebrew buttons, no focus management on route change, decorative heading levels, and default token contrast that misses AA. The gap is always in the code Lovable wrote around the components, not in the components.

**This is engineering guidance, not legal advice.** For a business over ~5M ₪ turnover, a public body, or anyone already served with a demand letter, the deliverable is a survey by a certified מורשה נגישות שירות — this skill gets the code and the paperwork to the point where that survey passes.

## Two rules that override everything else

1. **Conformance lives in the code. A toolbar is a convenience feature, never the compliance strategy.** Never install a third-party overlay script (accessiBe / UserWay / EqualWeb auto-fix widgets) as the fix. The FTC fined accessiBe $1M in April 2025 over its compliance claims, the European Commission has stated overlays are not a valid path to conformance, and roughly a quarter of US web-accessibility suits in 2024 targeted sites that had an overlay installed. If the client already pays for one, whether to keep it is their business call — fix the code either way, and never let the widget be cited as the reason the site is compliant.
2. **Automated scores lie.** A Lovable-built site scored 100% in the built-in Axe check while failing a real screen-reader user on menus, focus, headings, and language switching. Axe/Lighthouse catch roughly a third of real barriers. The manual protocol below is not optional.

## Step 0 — scope triage (ask before building anything)

Do not start work until these are answered; they change the entire deliverable:

1. **Average annual turnover over the last 3 years (₪)?** — sets the Israeli obligation tier.
2. **How many employees?** — 25+ triggers a mandatory רכז נגישות whose details go in the statement.
3. **Does it serve or sell to consumers in the EU (incl. Greece)?** — triggers the EAA, which has no turnover-based content exemption, only a micro-enterprise carve-out for *services*.
4. **Is the site public, or closed to registered users?** — a closed site capped at 500 registered users has a narrower content obligation, but its structure and navigation must still be accessible.
5. **Is there recorded video/audio?** — captions become mandatory above ~5M ₪ turnover or for a public body.

Then read `references/legal-il-eu.md` for the exact thresholds, the sub-regulations, the exemption wording, and the enforcement exposure on each side. **An exemption never removes the הצהרת נגישות** — an exempt business publishes a statement that says it is exempt and how to get help.

## Build target

**WCAG 2.1 AA is the build target for every project.** It satisfies ת"י 5568 (anchored on WCAG 2.0 AA, with the 2020/2023 editions aligned to 2.1 — 2.1 AA is a strict superset, so hitting it clears both readings) and it is exactly what EN 301 549 v3.2.1 requires today.

Add these four WCAG 2.2 AA criteria on new work — they are cheap in Tailwind and EN 301 549 v4.1.1 (draft published Nov 2025, expected in the Official Journal around Oct 2026) moves the EU baseline to 2.2:

- **2.4.11 Focus Not Obscured** — sticky headers/toolbars must not cover the focused element.
- **2.5.8 Target Size (Minimum)** — 24×24 CSS px minimum for interactive targets (`h-6 w-6` floor, `h-9 w-9` for icon buttons).
- **3.3.7 Redundant Entry** — don't ask for the same data twice in a flow; autofill it.
- **3.3.8 Accessible Authentication** — no cognitive-function test (puzzle/memory) as the only login path; allow paste into OTP fields.

## The five deliverables

A Lovable project is "accessible" when all five exist, in this order:

1. **Conformant application code** — the defect list in `references/code-audit.md`, cleared and re-verified.
2. **Structure & focus** — skip link, one `<h1>`, no skipped heading levels, `<header>/<nav>/<main id="main">/<footer>` landmarks, and focus moved to the page heading on every SPA route change.
3. **Hebrew/RTL correctness** — see below. This is where Israeli Lovable projects fail most often and it is invisible to English-language linters.
4. **Accessibility toolbar** — self-built, in-repo, per the spec below. Israeli users expect the icon; regulators don't accept it as a substitute.
5. **`/accessibility` statement page** in Hebrew, linked from the footer of *every* page, written last so it describes what is actually true.

## Workflow

Follow `lovable-rigor` throughout — the Lovable agent will claim accessibility fixes it did not make, and accessibility claims are legally consequential.

1. **Audit in the code first.** Read the actual files through the Lovable MCP (`list_files` / `read_file`) and build the defect list yourself. Do not ask Lovable "is this site accessible?" — it answers optimistically.
2. **Fix in ordered batches, one concern per message** — semantics/landmarks → keyboard & focus → contrast tokens → RTL/lang → components → toolbar → statement. A single "make the site accessible" prompt produces `aria-label` spray and new bugs.
3. **Re-verify every claimed fix in source**, then in the live preview, then on the published domain — a fix that is only in preview is not shipped.
4. **Run the manual protocol** below before telling the user anything is done.
5. **Only then write the statement**, and only claim the level you actually verified.

## Manual verification protocol (required — automation is not enough)

- **Keyboard only**, no mouse: Tab through the whole page. Every interactive element reachable, visible focus ring at every stop, order matches visual order, no trap, Escape closes every overlay, focus returns to the trigger on close. Skip link appears on first Tab.
- **Screen reader spot check** — VoiceOver (⌘F5) on the primary flow. Listen for: content behind an open modal still being announced, menu buttons that never announce expanded/collapsed, route changes that announce nothing, live regions that will not stop talking.
- **200% browser zoom and a 320px-wide viewport** — no horizontal scroll, no content clipped, toolbar does not cover the page.
- **Contrast** — check real rendered pairs, including placeholder text, muted/secondary text, disabled states, and the focus ring itself (3:1 non-text minimum).
- **`prefers-reduced-motion: reduce`** enabled at OS level — carousels stop auto-advancing, entrance animations are suppressed.
- **Hebrew reality check** — read one page with the screen reader in Hebrew; English `aria-label`s on Hebrew buttons will be obvious immediately.

## Hebrew / RTL rules

These are Israel-specific and no automated tool will catch them:

- `<html lang="he" dir="rtl">` set at the document root, not on a wrapper `<div>`. Mixed-language content gets its own `lang` on the element (`<span lang="en">`), otherwise the screen reader reads English in a Hebrew voice.
- **`aria-label` must match the visible Hebrew text** (WCAG 2.5.3 Label in Name). An English `aria-label` on a Hebrew-labelled button breaks voice control and is a straight AA failure. If a label is needed for an icon-only button, write it in Hebrew.
- **Logical Tailwind utilities only** — `ms-*`/`me-*`, `ps-*`/`pe-*`, `start-*`/`end-*`, `text-start`/`text-end`, `rounded-s-*`, `border-s-*`. Physical `left-*`/`right-*`/`ml-*`/`text-right` breaks the moment the site adds English or Greek.
- **`dir="ltr"` on inputs holding LTR data** — email, URL, phone, IBAN, numeric codes — otherwise the caret and punctuation jump.
- **Mirror directional icons** (back/forward arrows, chevrons) with `rtl:rotate-180`; never mirror logos, media controls (play stays pointing right), or numbers.
- The **הצהרת נגישות must be in Hebrew** (plain, simple Hebrew). Add English/Greek versions if the EAA applies.
- The **language switcher** must announce the current language and set `lang` — a switcher that announces only "en button / he button" is a real reported failure.

## Accessibility toolbar spec

Build it as a component in the repo (`src/components/AccessibilityToolbar.tsx`), never as an injected third-party script.

**The toolbar has its own fixed design system — it never inherits the host app's theme.** This is a firm rule, not a one-off preference for one client: whatever project this gets dropped into, the toolbar's own colors, icon, and type are constant across every project and must ignore that project's tokens (Tailwind theme vars, shadcn CSS custom properties, brand palette) entirely — hardcode the toolbar's own values, don't derive them from `var(--primary)` or equivalent. The reasoning: a visitor scanning for "the accessibility button" is pattern-matching on a near-universal blue/navy circular icon and a consistent look learned from other sites — reskinning it to match each host site's own brand makes it *harder* to recognize, not more cohesive.

**Trigger icon and color.** Use the universal, instantly-recognized accessibility symbol (person-in-a-circle, ISO-style — a client reference example is `references/toolbar-trigger-example.png`, icon only, no caption). Fixed dark navy `#080F44` for the trigger and the panel header background, **with white header/title text always** — never derived from the host app's foreground token. A subtle hover animation on the trigger (small scale/glow pulse) matches the validated reference and is worth keeping as the default. Ask the client for a preferred hex if they have one; `#080F44` (navy bg + white text) is the solid default otherwise.

**Must have:** own trigger button with a Hebrew `aria-label` and `aria-expanded`; `aria-pressed` on every toggle; Escape closes and returns focus to the trigger; fully keyboard operable; preferences persisted in `localStorage` and reapplied before first paint; a Reset-all button; a link to `/accessibility`; must not cover content at 320px or 200% zoom.

**Position control — a preset picker, not free drag.** Real commercial widgets (and the client reference) offer a small sub-panel with radio options — "שמאל" (left) / "ימין" (right) / "הסתר" (hide/minimize) — not literal mouse-drag positioning. Prefer this: it is simpler, fully keyboard-operable, has no viewport-clamping edge cases, and works identically on touch. Persist the choice in the same `localStorage` blob as the other preferences.

**Minimize, never remove.** The "הסתר" position option collapses it to a small keyboard-reachable edge handle — not a way to delete it. There must always be at least two routes back: `Alt+Shift+A` from anywhere (a modifier combo, so WCAG 2.1.4 never applies), and a permanent restore button on `/accessibility`, which is reachable from the footer of every page. Reset clears the minimized state too. A visitor who can lose the accessibility trigger and not find it again is worse off than one who never had it.

**Responsive — the panel must adapt to device, not just reflow.** On desktop/tablet, a docked side panel works. On a narrow/mobile viewport the same fixed-width side panel is cramped and can itself cover content — use a bottom sheet or full-screen panel below a breakpoint (matching whatever mobile-sheet pattern the project's own component library already uses, e.g. shadcn `Sheet`/`Drawer`, rather than inventing a new one), with the same feature set and the same `localStorage` state either way. Tap targets get the touch-sized floor (WCAG 2.5.8, 24×24 CSS px minimum) regardless of viewport.

**Feature set** — validated against a real commercial reference (a well-known "one-click accessibility"-style widget), broader than a minimal build:
- **Text size** — drive `document.documentElement.style.fontSize` so Tailwind's rem scale follows — audit for hardcoded `px` first, or the control does nothing.
- **Contrast modes** (ניגודיות+) — override the shadcn CSS custom properties on a root class (mirror whatever pattern the project's own dark-mode class already uses, e.g. a sibling to `.dark`) — that is the clean hook; do **not** use `filter` on `<html>`, it breaks `position: fixed`.
- **Link highlighting** (הדגשת קישורים) — visible underline + strong outline on every `<a>`.
- **Text spacing** (ריווח טקסט) and **line height** (גובה שורה) — WCAG 1.4.12, letter/word-spacing and line-height, as separate toggles if the reference splits them, or one combined control if that reads more simply — judge per project.
- **Stop/disable animations** (ביטול הנפשות) — same effect as `prefers-reduced-motion: reduce`, but as an explicit toggle; never overrides the OS setting when the OS already asks for reduced motion, only adds the same effect for a visitor whose OS doesn't.
- **Hide images** (הסתרת תמונות) — visually hides decorative imagery for visitors who find visual clutter distracting; never hide an image whose `alt` text carries information needed to use the page (form diagrams, charts) — audit before wiring this one.
- **Cursor emphasis** (סמן) — a larger/higher-contrast cursor mode; desktop/pointer-device only, hide the control on touch.
- **Readable-font / letter-spacing mode**, offered plainly rather than marketed as a "dyslexia font" (the reference calls this "תמיכה בדיסלקסיה" — fine to label it that way for recognizability if the client wants parity with a familiar pattern, but the underlying evidence for special dyslexia *typefaces* specifically is weak; what actually helps is spacing, so make sure the toggle's real effect is spacing/readability, not just a font-family swap dressed up as a fix).
- **Saturation control** (רוויה) — a desaturate/grayscale toggle for visual sensitivity.
- **Text alignment** (יישור טקסט) — cheap to offer; lower priority for a Hebrew RTL-only project than for a multi-language one, but harmless to include for parity.
- **Reset-all** — one button, clears every toggle and the minimized/position state together.

**Must not:** be the only way to reach any function; claim in its UI that the site is now accessible/compliant; override the user's OS `prefers-reduced-motion` (respect it by default, the toggle only adds); or trap focus.

## Reference files

- `references/legal-il-eu.md` — thresholds, exemptions, statement contents, enforcement exposure, Israel vs EU vs Greece comparison, sources.
- `references/code-audit.md` — the concrete Lovable/shadcn defect checklist with how-to-spot and the fix for each, plus the shadcn component gap table and tooling.
- `references/lovable-prompts.md` — ready-to-send prompts for the Lovable agent (audit, baseline fix, focus management, RTL pass, toolbar, statement page) and the Hebrew הצהרת נגישות template.

