# GoodGo Frontend Accessibility Issues - Code Fixes Required **Date**: 2026-04-10 **Scope**: apps/web (GoodGo Frontend) **Status**: ACTIONABLE ITEMS - Ready for Implementation --- ## Summary Found **4 specific accessibility issues** that require code fixes across the GoodGo frontend. Below are the exact file paths, line numbers, problematic code snippets, and required fixes. --- ## ISSUE 1: Form Inputs Missing aria-label or Associated Labels ### 1.1 File Upload Input Without aria-label **File**: `apps/web/components/listings/image-upload.tsx` **Line**: 118 **Problem**: Hidden file input has no aria-label or associated label element **Current Code**: ```tsx { if (e.target.files) addFiles(e.target.files); e.target.value = ''; }} /> ``` **Fix Required**: Add `aria-label` ```tsx { if (e.target.files) addFiles(e.target.files); e.target.value = ''; }} /> ``` --- ### 1.2 Search Save Dialog - Text Input Without aria-label **File**: `apps/web/app/[locale]/(public)/search/page.tsx` **Line**: 189 **Problem**: Text input for saving search name has no associated label or aria-label **Current Code**: ```tsx setSaveName(e.target.value)} placeholder="Tên tìm kiếm (VD: Chung cư Q7 dưới 3 tỷ)" className="mb-3 w-full rounded-md border bg-background px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-primary" maxLength={100} onKeyDown={(e) => e.key === 'Enter' && handleSaveSearch()} /> ``` **Fix Required**: Add `aria-label` ```tsx setSaveName(e.target.value)} placeholder="Tên tìm kiếm (VD: Chung cư Q7 dưới 3 tỷ)" aria-label="Tên bộ lọc tìm kiếm" className="mb-3 w-full rounded-md border bg-background px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-primary" maxLength={100} onKeyDown={(e) => e.key === 'Enter' && handleSaveSearch()} /> ``` --- ### 1.3 Admin Moderation - Select All Checkbox Without aria-label **File**: `apps/web/app/[locale]/(admin)/admin/moderation/page.tsx` **Line**: 222 **Problem**: Table header checkbox for "select all" has no aria-label **Current Code**: ```tsx 0} onChange={toggleSelectAll} className="rounded border-input" /> ``` **Fix Required**: Add `aria-label` ```tsx 0} onChange={toggleSelectAll} className="rounded border-input" /> ``` --- ### 1.4 Admin Moderation - Row Checkboxes Without aria-label **File**: `apps/web/app/[locale]/(admin)/admin/moderation/page.tsx` **Line**: 242 **Problem**: Individual row checkboxes in table have no aria-label **Current Code**: ```tsx toggleSelect(item.listingId)} className="rounded border-input" /> ``` **Fix Required**: Add `aria-label` with dynamic content ```tsx toggleSelect(item.listingId)} className="rounded border-input" /> ``` --- ## ISSUE 2: Mock Image Component Missing alt Attribute ### 2.1 Test Mock Image Component **File**: `apps/web/app/[locale]/(public)/search/__tests__/search.spec.tsx` **Line**: 46 **Problem**: Mock Image component spreads all props including missing alt attribute **Current Code**: ```tsx default: (props: Record) => , ``` **Fix Required**: Ensure alt is always included in mock or add default ```tsx default: (props: Record) => {props.alt, ``` OR better approach - require alt in mock setup: ```tsx default: (props: Record) => { if (!props.alt) { console.warn('Missing alt attribute in Image mock:', props); } return {props.alt; }, ``` --- ## ISSUE 3: Hidden File Input Needs Better Accessibility ### 3.1 Image Upload Drag-Drop Area Needs Better Labeling **File**: `apps/web/components/listings/image-upload.tsx` **Lines**: 86-128 **Problem**: The clickable div that triggers file input has descriptive text but no label element linking to the hidden input **Current Implementation**: ```tsx
inputRef.current?.click()} className={cn(...)} > ...

Kéo thả ảnh vào đây hoặc nhấp để chọn

JPG, PNG, WebP - Tối đa {maxFiles} ảnh, mỗi ảnh 10MB

{...}} />
``` **Recommended Enhancement**: Add proper label or role ```tsx
inputRef.current?.click()} role="button" tabIndex={0} aria-label="Khu vực kéo thả hoặc nhấp để tải ảnh lên" onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { inputRef.current?.click(); } }} className={cn(...)} > {/* ... rest of content ... */}
``` --- ## ISSUE 4: Verification of Properly Implemented Accessibility ### ✅ CORRECT - Image Components with alt attributes The following files already have proper alt attributes and require NO changes: - `apps/web/components/listings/image-gallery.tsx` - All Image components have alt (lines 34, 77) - `apps/web/components/listings/image-upload.tsx` - All img tags have alt (line 135-138) - `apps/web/components/search/property-card.tsx` - Image has alt (line 44) - `apps/web/app/[locale]/(dashboard)/listings/page.tsx` - All Images have alt (lines 192, 272) - `apps/web/app/[locale]/(dashboard)/dashboard/page.tsx` - Image has alt (line 252) - `apps/web/app/[locale]/(admin)/admin/kyc/page.tsx` - All Images have alt (lines 102, 116, 130) ### ✅ CORRECT - Icon-only Buttons with aria-label The following files already have proper aria-labels and require NO changes: - `apps/web/components/listings/image-gallery.tsx` - Navigation buttons have aria-labels (lines 47, 54) - `apps/web/app/[locale]/(public)/layout.tsx` - Mobile menu button has aria-label (line 91) ### ✅ CORRECT - Dialogs with Semantic Titles The following dialogs already have proper DialogTitle elements and require NO changes: - `apps/web/app/[locale]/(dashboard)/dashboard/subscription/page.tsx` - DialogTitle present (line 327-329) - `apps/web/app/[locale]/(admin)/admin/kyc/page.tsx` - Both dialogs have DialogTitle (approval and rejection dialogs) ### ✅ CORRECT - Checkbox with Associated Label - `apps/web/app/[locale]/(public)/search/page.tsx` (line 199) - Checkbox has associated `