> ## Documentation Index
> Fetch the complete documentation index at: https://constanza101-borrissol-28.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Accessibility implementation: WCAG, ARIA, and keyboard nav

> How Borrissol achieves Lighthouse Accessibility 97: skip link, ARIA labels, heading hierarchy, touch targets, focus-visible outlines, and reduced-motion.

Borrissol scores **Lighthouse Accessibility 97** — high enough to demonstrate real commitment to inclusive design while reflecting a few pragmatic trade-offs inherent in a marketing site with embedded third-party content (Google Maps, Google Analytics). This page documents every accessibility feature built into the site and the rules contributors must follow to keep that score.

## Accessibility features overview

| Feature           | Implementation                                                           |
| ----------------- | ------------------------------------------------------------------------ |
| Skip link         | First child of `<body>`, visually hidden by default, visible on `:focus` |
| Section labels    | Every `<section>` has `aria-labelledby` pointing to its heading          |
| Nav label         | `<nav aria-label="…">` on all navigation elements                        |
| Footer role       | `<footer role="contentinfo">`                                            |
| Focus outline     | `focus-visible` CSS outline on all interactive elements                  |
| Touch targets     | Minimum 44 × 44 px on all buttons and links                              |
| Hamburger button  | `aria-expanded` attribute toggled by JS                                  |
| Heading hierarchy | Never skips levels (H1 → H2 → H3, no jumps)                              |
| Reduced motion    | `prefers-reduced-motion` respected in scroll reveal animations           |
| FAQ               | Native `<details>` / `<summary>` — keyboard accessible without JS        |

## Skip link

The skip link is defined in `Layout.astro` as the **first child of `<body>`**. It allows keyboard and screen reader users to jump past the navigation directly to the main content without tabbing through every nav item.

The link is visually hidden by default and becomes visible when it receives focus:

```html theme={null}
<!-- First child of <body> in Layout.astro -->
<a href="#main-content" class="skip-link">Skip to main content</a>
```

The CSS pattern for the skip link uses `position: absolute` with a large negative offset at rest, returning to a visible position on `:focus-visible`:

```css theme={null}
.skip-link {
  position: absolute;
  top: -9999px;
  left: 1rem;
}
.skip-link:focus-visible {
  top: 1rem;
  z-index: 9999;
}
```

## ARIA section pattern

Every `<section>` on the page uses `aria-labelledby` to associate it with its visible heading. This allows screen readers to announce the section name as users navigate by landmark.

```html theme={null}
<section aria-labelledby="offer-heading">
  <h2 id="offer-heading">Els nostres serveis</h2>
  <!-- section content -->
</section>
```

<Note>
  The `id` on the heading and the `aria-labelledby` value on the section must always match exactly. When renaming or refactoring a section, update both attributes together.
</Note>

## Navigation and landmarks

```html theme={null}
<!-- Primary navigation -->
<nav aria-label="Primary navigation">
  <!-- nav links -->
</nav>

<!-- Footer landmark -->
<footer role="contentinfo">
  <!-- footer content -->
</footer>
```

All navigation elements carry an explicit `aria-label` so that when a page has more than one `<nav>` (e.g. primary nav and a footer nav), screen readers can distinguish them by name.

## Focus management

Every interactive element on the site has a visible `:focus-visible` outline. This is defined globally in `theme.css` and must not be overridden with `outline: none` in any component stylesheet.

The WhatsApp Floating Action Button (FAB) requires particular attention:

* It must have an accessible label (e.g. `aria-label="Contacta'ns per WhatsApp"`)
* It must have a clearly visible `:focus-visible` state
* It must meet the 44 × 44 px minimum touch target size

## Touch target sizes

Minimum touch target size is **44 × 44 px** per [WCAG Success Criterion 2.5.8](https://www.w3.org/WAI/WCAG22/Understanding/target-size-minimum.html). This applies to:

* All CTA buttons (`.btn-primary`, `.btn-secondary`, `.btn-tertiary`)
* All navigation links in the navbar and mobile menu
* The WhatsApp FAB
* The scroll-to-top button
* Carousel arrow controls

<Warning>
  Small touch targets are the most common accessibility regression introduced during UI tweaks. After resizing any button or link, verify it still meets 44 × 44 px in DevTools' device emulation mode.
</Warning>

## Hamburger menu

The mobile hamburger button uses `aria-expanded` to communicate its open/closed state to assistive technologies. JavaScript toggles the attribute when the menu opens or closes:

```html theme={null}
<button class="hamburger" aria-expanded="false" aria-controls="mobile-menu">
  <!-- icon -->
</button>
```

```js theme={null}
hamburgerBtn.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
```

## Heading hierarchy

Heading levels are never skipped anywhere on the site. The document outline follows a strict H1 → H2 → H3 progression:

* **H1** — the main page headline (one per page, always present)
* **H2** — section headings (one per `<section>`)
* **H3** — sub-items within a section (workshop cards, FAQ items, etc.)

<Warning>
  Never use an `<h3>` or `<h4>` if there is no parent `<h2>` in the same section. Never use a heading tag purely for its visual size — use the appropriate utility class (`.text-h2`, `.text-h3`) on a `<p>` or `<span>` if you need the look without the semantic weight.
</Warning>

## Reduced motion

The scroll reveal animations driven by `IntersectionObserver` in `Layout.astro` check the `prefers-reduced-motion` media query at runtime. If the user has enabled reduced motion in their OS settings, content is displayed immediately without transitions or slide-in effects:

```js theme={null}
const prefersReducedMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches;

if (!prefersReducedMotion) {
  // attach IntersectionObserver and add animation classes
}
```

## FAQ accessibility

The FAQ section uses native HTML `<details>` and `<summary>` elements. This means:

* Fully keyboard navigable with no JavaScript
* Screen readers announce the expanded/collapsed state natively via the browser
* Works with JavaScript disabled

## Rules for contributors

The following rules apply to all contributors — human and AI — when adding or modifying interactive content:

1. **Never skip heading levels.** Every heading must have a parent heading exactly one level up.
2. **Always provide `alt` text for images.** Use a descriptive string for meaningful images. Use `alt=""` for purely decorative images so screen readers skip them.
3. **Always use semantic HTML.** Use `<button>` for actions that trigger behaviour; use `<a>` for navigation to a URL. Never use `<div>` or `<span>` as interactive elements.
4. **Always test keyboard navigation** after adding interactive elements. Tab through the page and confirm focus order is logical and every interactive element is reachable and operable.
5. **New interactive elements must have a `focus-visible` outline.** Never suppress the default outline without providing an equal or better replacement.
6. **Maintain ARIA section pattern.** Every new `<section>` must have `aria-labelledby` pointing to its heading, and that heading must have a matching `id`.
