feat(portal-shell): thin full-width footer with copyright + a11y links (#88)
## Summary
- Re-add a **40 px** (h-10) footer pinned to the bottom of the shell, spanning the full viewport width below header + sidebar + main.
- Hosts the **`© <year> APF France handicap`** copyright on the left and the FR + EN accessibility statement links on the right, separated by a thin divider.
- **Move the accessibility links out of the sidebar bottom.** Putting legal / compliance links in the footer matches universal web convention and keeps the sidebar focused on product navigation.
## Why footer rather than the help menu
The header's `circle-help` icon will eventually open a help menu (FAQ, keyboard shortcuts, contact support) — that's **product help**, not **legal / compliance**. Auditors (RGAA 4.1, EN 301 549) look for the accessibility statement in the footer; hiding it inside a help dropdown would hurt discoverability. The footer is the canonical home.
## Why both FR + EN stay visible
There is no language toggle yet — `@angular/localize` and its ADR are a separate chantier. Until then, exposing both languages prevents a francophone user from landing on the EN page (or vice versa) via a stale favourite. Once the locale switcher lands, the footer drops the link that doesn't match the active locale.
## Layout
```
:host flex column, h-100vh
├── app-header shrink-0, h-16
├── div.shell-body flex-1, flex row
│ ├── app-sidebar w-{64|16}, h-full (== shell-body)
│ └── main.shell-main flex-1, overflow-y-auto
└── app-footer shrink-0, h-10
```
The sidebar now sits *between* header and footer, so the collapse toggle stays flush above the footer at every viewport size. shell-main still owns its own vertical scroll so long content does not push the footer off-screen.
## What this PR reserves for later (placeholder)
- **Language toggle (FR / EN)** — lands once `@angular/localize` is in.
- **Dev-only version badge** — lands once `environment.ts` per ADR-0018 is wired up.
Both belong in the footer; the layout already has slots for them (center / right groupings).
## Accessibility
- `<footer aria-label="Page footer">` landmark.
- Inline text links inside `<nav aria-label="Legal">` with hover underline + visible focus ring (brand primary, 4 px offset). Inline-link exception applies to the 44×44 touch target (ADR-0016).
- Dark mode: white → `dark:bg-gray-900`, gray-500 text → `dark:text-gray-400`, brand-primary-500 hover → `dark:hover:text-brand-primary-300`.
## Test plan
- [x] `pnpm exec nx run-many -t lint test build --projects=portal-shell` — green (**36 / 36 specs**, +3 for the footer).
- [x] Production build: **121 kB gzip initial** (unchanged from main).
- [ ] Manual: footer pinned at the bottom in every viewport size; sidebar height tracks shell-body so the collapse button sits just above it.
- [ ] Manual: both `/accessibility` and `/accessibilite` links navigate correctly and get `aria-current="page"` when active.
- [ ] Manual: dark mode → footer surface flips with the rest of the shell.
- [ ] Manual: keyboard — Tab into the footer reaches each link, focus ring is visible, Shift+Tab walks back out.
---------
Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #88
This commit was merged in pull request #88.
This commit is contained in:
@@ -10,7 +10,7 @@ describe('App', () => {
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it('renders the layout shell — skip link, header, sidebar, main', async () => {
|
||||
it('renders the layout shell — skip link, header, sidebar, main, footer', async () => {
|
||||
const fixture = TestBed.createComponent(App);
|
||||
await fixture.whenStable();
|
||||
const root = fixture.nativeElement as HTMLElement;
|
||||
@@ -18,5 +18,6 @@ describe('App', () => {
|
||||
expect(root.querySelector('app-header')).not.toBeNull();
|
||||
expect(root.querySelector('app-sidebar')).not.toBeNull();
|
||||
expect(root.querySelector('main#main-content')).not.toBeNull();
|
||||
expect(root.querySelector('app-footer')).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user