Barazo default frontend barazo.forum

feat(pages): migrate accessibility page to CMS (#159)

* feat(pages): migrate accessibility page to CMS

- Update footer link from /accessibility to /p/accessibility
- Remove hardcoded accessibility page and test

* fix(ci): remove /accessibility from Lighthouse URLs

The hardcoded /accessibility page is replaced by the CMS page at
/p/accessibility which requires the API backend. Since the Lighthouse CI
only runs the Next.js standalone server, CMS pages can't be tested.
Accessibility coverage for CMS pages is handled by vitest-axe tests.

authored by

Guido X Jansen and committed by
GitHub
8ad9ad1b e1aa3589

+2 -218
+1 -2
lighthouserc.json
··· 5 5 "http://localhost:3000/", 6 6 "http://localhost:3000/search/", 7 7 "http://localhost:3000/admin/", 8 - "http://localhost:3000/settings/", 9 - "http://localhost:3000/accessibility/" 8 + "http://localhost:3000/settings/" 10 9 ], 11 10 "settings": { 12 11 "onlyCategories": ["accessibility"],
-78
src/app/accessibility/page.test.tsx
··· 1 - /** 2 - * Tests for accessibility statement page. 3 - * @see specs/prd-web.md Section M14 4 - */ 5 - 6 - import { describe, it, expect, vi } from 'vitest' 7 - import { render, screen } from '@testing-library/react' 8 - import { axe } from 'vitest-axe' 9 - import AccessibilityPage from './page' 10 - 11 - // Mock next/navigation 12 - vi.mock('next/navigation', () => ({ 13 - usePathname: () => '/accessibility', 14 - useRouter: () => ({ push: vi.fn() }), 15 - })) 16 - 17 - // Mock next-themes 18 - vi.mock('next-themes', () => ({ 19 - useTheme: () => ({ theme: 'dark', setTheme: vi.fn() }), 20 - ThemeProvider: ({ children }: { children: React.ReactNode }) => children, 21 - })) 22 - 23 - // Mock useAuth hook 24 - vi.mock('@/hooks/use-auth', () => ({ 25 - useAuth: () => ({ 26 - user: null, 27 - isAuthenticated: false, 28 - isLoading: false, 29 - getAccessToken: () => null, 30 - login: vi.fn(), 31 - logout: vi.fn(), 32 - setSessionFromCallback: vi.fn(), 33 - authFetch: vi.fn(), 34 - }), 35 - })) 36 - 37 - describe('AccessibilityPage', () => { 38 - it('renders page heading', async () => { 39 - const page = await AccessibilityPage() 40 - render(page) 41 - expect(screen.getByRole('heading', { name: /accessibility/i, level: 1 })).toBeInTheDocument() 42 - }) 43 - 44 - it('states WCAG 2.2 AA conformance target', async () => { 45 - const page = await AccessibilityPage() 46 - render(page) 47 - expect(screen.getByText(/wcag.*2\.2/i)).toBeInTheDocument() 48 - expect(screen.getByText(/level aa/i)).toBeInTheDocument() 49 - }) 50 - 51 - it('lists testing methods', async () => { 52 - const page = await AccessibilityPage() 53 - render(page) 54 - expect(screen.getByText(/automated testing/i)).toBeInTheDocument() 55 - expect(screen.getByText(/keyboard navigation/i)).toBeInTheDocument() 56 - expect(screen.getByText(/screen reader/i)).toBeInTheDocument() 57 - }) 58 - 59 - it('includes contact information', async () => { 60 - const page = await AccessibilityPage() 61 - render(page) 62 - expect(screen.getByRole('heading', { name: /contact/i })).toBeInTheDocument() 63 - expect(screen.getByRole('link', { name: /github issue tracker/i })).toBeInTheDocument() 64 - }) 65 - 66 - it('renders breadcrumbs', async () => { 67 - const page = await AccessibilityPage() 68 - render(page) 69 - expect(screen.getByText('Home')).toBeInTheDocument() 70 - }) 71 - 72 - it('passes axe accessibility check', async () => { 73 - const page = await AccessibilityPage() 74 - const { container } = render(page) 75 - const results = await axe(container) 76 - expect(results).toHaveNoViolations() 77 - }) 78 - })
-137
src/app/accessibility/page.tsx
··· 1 - /** 2 - * Accessibility statement page. 3 - * URL: /accessibility 4 - * Describes WCAG 2.2 AA conformance, testing methods, and contact info. 5 - * @see specs/prd-web.md Section M14 6 - */ 7 - 8 - import type { Metadata } from 'next' 9 - import { getPublicSettings } from '@/lib/api/client' 10 - import { ForumLayout } from '@/components/layout/forum-layout' 11 - import { Breadcrumbs } from '@/components/breadcrumbs' 12 - 13 - export const metadata: Metadata = { 14 - title: 'Accessibility Statement', 15 - description: 16 - 'Barazo is committed to WCAG 2.2 Level AA accessibility. Learn about our testing, standards, and how to report issues.', 17 - alternates: { 18 - canonical: '/accessibility', 19 - }, 20 - } 21 - 22 - export default async function AccessibilityPage() { 23 - let publicSettings = null 24 - try { 25 - publicSettings = await getPublicSettings() 26 - } catch { 27 - // silently degrade 28 - } 29 - 30 - return ( 31 - <ForumLayout publicSettings={publicSettings}> 32 - <div className="mx-auto max-w-2xl space-y-8"> 33 - <Breadcrumbs items={[{ label: 'Home', href: '/' }, { label: 'Accessibility' }]} /> 34 - 35 - <h1 className="text-2xl font-bold text-foreground">Accessibility statement</h1> 36 - 37 - <section className="space-y-3"> 38 - <h2 className="text-lg font-semibold text-foreground">Our commitment</h2> 39 - <p className="text-sm leading-relaxed text-muted-foreground"> 40 - Barazo is committed to ensuring digital accessibility for people with disabilities. We 41 - continually improve the user experience for everyone and apply the relevant 42 - accessibility standards. 43 - </p> 44 - </section> 45 - 46 - <section className="space-y-3"> 47 - <h2 className="text-lg font-semibold text-foreground">Conformance status</h2> 48 - <p className="text-sm leading-relaxed text-muted-foreground"> 49 - We aim to conform to the{' '} 50 - <strong>Web Content Accessibility Guidelines (WCAG) 2.2 Level AA</strong>. These 51 - guidelines explain how to make web content more accessible to people with a wide range 52 - of disabilities. 53 - </p> 54 - </section> 55 - 56 - <section className="space-y-3"> 57 - <h2 className="text-lg font-semibold text-foreground">Testing methods</h2> 58 - <p className="text-sm leading-relaxed text-muted-foreground"> 59 - We test accessibility through a combination of methods: 60 - </p> 61 - <ul className="list-inside list-disc space-y-2 text-sm text-muted-foreground"> 62 - <li> 63 - <strong>Automated testing</strong> using axe-core and ESLint accessibility rules in 64 - our continuous integration pipeline. 65 - </li> 66 - <li> 67 - <strong>Keyboard navigation</strong> testing to ensure all interactive elements are 68 - reachable and operable without a mouse. 69 - </li> 70 - <li> 71 - <strong>Screen reader</strong> testing with VoiceOver to verify content is properly 72 - announced and navigable. 73 - </li> 74 - <li> 75 - <strong>Lighthouse audits</strong> targeting an accessibility score of 95 or higher on 76 - all page types. 77 - </li> 78 - </ul> 79 - </section> 80 - 81 - <section className="space-y-3"> 82 - <h2 className="text-lg font-semibold text-foreground">Accessibility features</h2> 83 - <ul className="list-inside list-disc space-y-2 text-sm text-muted-foreground"> 84 - <li>Semantic HTML with proper heading hierarchy and landmark regions.</li> 85 - <li>Skip links for jumping to main content and the reply editor.</li> 86 - <li>Keyboard-accessible controls with visible focus indicators.</li> 87 - <li>ARIA attributes for dynamic content, dialogs, and tab patterns.</li> 88 - <li>Color contrast meeting WCAG AA requirements in both light and dark themes.</li> 89 - <li>Pagination as the default for content lists (no infinite scroll).</li> 90 - <li>Respects reduced motion preferences via prefers-reduced-motion.</li> 91 - </ul> 92 - </section> 93 - 94 - <section className="space-y-3"> 95 - <h2 className="text-lg font-semibold text-foreground">Known limitations</h2> 96 - <p className="text-sm leading-relaxed text-muted-foreground"> 97 - While we strive for full accessibility, some areas may have limitations: 98 - </p> 99 - <ul className="list-inside list-disc space-y-2 text-sm text-muted-foreground"> 100 - <li> 101 - User-generated content may not always meet accessibility standards (e.g., images 102 - without alt text in posts). 103 - </li> 104 - <li>Third-party embeds and plugins may have their own accessibility limitations.</li> 105 - </ul> 106 - </section> 107 - 108 - <section className="space-y-3"> 109 - <h2 className="text-lg font-semibold text-foreground">Contact us</h2> 110 - <p className="text-sm leading-relaxed text-muted-foreground"> 111 - If you encounter accessibility barriers on Barazo, please contact us. We take 112 - accessibility feedback seriously and will work to address issues promptly. 113 - </p> 114 - <p className="text-sm leading-relaxed text-muted-foreground"> 115 - You can report accessibility issues through our{' '} 116 - <a 117 - href="https://github.com/barazo-forum/barazo-web/issues" 118 - className="text-primary underline hover:text-primary/80" 119 - target="_blank" 120 - rel="noopener noreferrer" 121 - > 122 - GitHub issue tracker 123 - </a> 124 - . Please include the page URL, a description of the issue, and the assistive technology 125 - you are using. 126 - </p> 127 - </section> 128 - 129 - <section className="space-y-3"> 130 - <p className="text-xs text-muted-foreground"> 131 - This statement was last updated on February 2026. 132 - </p> 133 - </section> 134 - </div> 135 - </ForumLayout> 136 - ) 137 - }
+1 -1
src/components/layout/forum-layout.tsx
··· 135 135 <nav aria-label="Footer"> 136 136 <ul className="flex gap-4"> 137 137 <li> 138 - <Link href="/accessibility" className="transition-colors hover:text-foreground"> 138 + <Link href="/p/accessibility" className="transition-colors hover:text-foreground"> 139 139 Accessibility 140 140 </Link> 141 141 </li>