` with `@onclick` handlers. Screen readers cannot identify them as interactive. Keyboard users cannot Tab to them or activate with Enter/Space.
**Example (CafeDesktop.razor):**
```razor
AddToCart(product)">
...
```
---
### 3. No ARIA Labels on Interactive Icons — WCAG 2.1 §4.1.2
**Files:** `Components/Auth/AuthButton.razor`, `Components/Auth/AuthInput.razor` (line 37), all layout files
**Impact:** Icon-only buttons (close, toggle, back) have no accessible names — screen readers announce nothing or raw Unicode.
**Example (AuthInput.razor ~line 37):**
```razor
```
---
### 4. Error/Success Messages Missing `role="alert"` — WCAG 2.1 §4.1.3
**Files:** `Pages/Auth/LoginAdmin.razor` (lines 28–40), `Pages/Pos/Cafe/CafeDesktop.razor`
**Impact:** Dynamic status changes (login error, payment success, cart update) are not announced to screen readers.
**Example (LoginAdmin.razor ~line 29):**
```razor
@_errorMessage
@_errorMessage
```
---
### 5. Hardcoded Vietnamese Strings in POS UI — Localization Failure
**File:** `Pages/Pos/Cafe/CafeDesktop.razor` and all POS vertical pages
**Impact:** English-language users see Vietnamese text. App cannot support international merchants.
**Examples found (CafeDesktop.razor):**
| Line | Hardcoded String | Should Be |
|------|-----------------|-----------|
| 24 | `"Đang tải..."` | `@L["Common_Loading"]` |
| 25 | `"Không thể tải dữ liệu"` | `@L["Common_LoadError"]` |
| 66 | `"Đơn hàng"` | `@L["Pos_OrderPanel_Title"]` |
| 67 | `"món"` | `@L["Pos_CartItem_Unit"]` |
| 89 | `"Nhập mã voucher..."` | `@L["Pos_Voucher_Placeholder"]` |
| 97 | `"Giảm giá"` | `@L["Pos_Discount_Label"]` |
| 107 | `"Tổng cộng"` | `@L["Pos_Total_Label"]` |
| 117 | `"Đang tạo đơn..."` | `@L["Pos_CreatingOrder"]` |
| 122 | `"Thanh toán"` | `@L["Pos_Checkout_Button"]` |
Also found in layouts:
- `AdminLayout.razor` (line ~160): `"Có lỗi xảy ra"`, `"Vui lòng thử lại"`, `"Tải lại"`
- `StaffLayout.razor` (line 148): Same error boundary text
- `PosLayout.razor` (lines 29, 65, 115): `"GoodGo POS"`, `"Menu"`, `"Đơn hàng"`
---
### 6. Hardcoded Color Values Instead of CSS Variables
**Files:** `Pages/Pos/Cafe/CafeDesktop.razor`, `Pages/Auth/LoginAdmin.razor`, multiple admin pages
**Impact:** Theme overrides are impossible. Colors are not consistent with the design token system.
**Examples:**
```razor
style="background:rgba(139,92,246,0.1);color:#8B5CF6;"
style="background:rgba(239,68,68,0.12);color:#EF4444;"
style="background-color:rgba(139,92,246,0.125);"
class="status-purple"
class="status-danger"
```
**Hardcoded colors to replace:**
| Value | Should Be |
|-------|-----------|
| `#16A34A` | `var(--color-success)` |
| `#EF4444` | `var(--color-danger)` |
| `#8B5CF6` | `var(--color-purple)` |
| `rgba(139,92,246,0.1)` | `var(--color-purple-subtle)` |
| `rgba(239,68,68,0.12)` | `var(--color-danger-subtle)` |
---
## Warnings
Issues that create significant technical debt or UX inconsistency.
### 7. 2,316 Inline Style Attributes
Inline styles are scattered across all pages — especially POS desktop views, admin shop pages, and dashboard. This makes CSS maintenance impossible and prevents design system enforcement.
**Top offending patterns:**
```razor
style="display:flex;flex-direction:column;gap:24px;"
style="padding:6px 12px;border-radius:8px;border:none;"
style="position:absolute;left:@{X}px;top:@{Y}px;"
```
**Strategy:** Create CSS utility classes and component classes. Move inline styles to scoped `.razor.css` files.
---
### 8. Inconsistent Spacing Scale
The design uses 10+ different spacing values without a defined scale:
- Gaps: 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 48px
- Padding: 6, 8, 10, 12, 16, 20, 24px
- Border radius: 6, 8, 10, 12, 14, 20, 24px
**Recommended scale:**
```css
/* Spacing: 4-point scale */
--space-1: 4px; --space-2: 8px; --space-3: 12px;
--space-4: 16px; --space-5: 20px; --space-6: 24px;
--space-8: 32px; --space-12: 48px;
/* Border radius: 3 values */
--radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px;
```
---
### 9. Missing Contrast Ratio for Secondary Text — WCAG 2.1 §1.4.3
**File:** `wwwroot/css/admin.css`
```css
/* Current — MAY fail WCAG AAA */
--admin-text-secondary: #ADADB0; /* ratio ~5.08:1 on #1A1A1D — FAILS AAA (7:1) */
--admin-text-tertiary: #8B8B90; /* ratio ~4.13:1 on #1A1A1D — FAILS AA (4.5:1) */
```
`--admin-text-tertiary` likely **fails WCAG AA** for normal text. Needs contrast validation.
---
### 10. Cart Items Use `
` Instead of Semantic List
**File:** `Pages/Pos/Cafe/CafeDesktop.razor`
Cart items rendered as generic `
` elements. Should be `
/- ` structure for screen readers to announce list count and items.
---
### 11. No `.razor.css` Scoped Stylesheets Per Component
All 8 reusable components (`AuthButton`, `AuthInput`, `OtpInput`, etc.) have zero scoped CSS files. Styles are defined globally in `auth.css`. This creates unintended style leakage and makes component refactoring dangerous.
---
### 12. OTP Input Missing ARIA Group Label
**File:** `Components/Auth/OtpInput.razor` (lines 12–19)
The 6-digit OTP input is a group of individual `` elements but lacks `role="group"` and `aria-label="One-time password"`. Screen readers will announce 6 separate unlabeled inputs.
---
### 13. No Focus Trap in Modal Overlays
**Files:** `Layout/PosLayout.razor`, admin dialog pages
When mobile overlays or modals open, focus is not trapped within them. Users pressing Tab will cycle through background content, violating WCAG 2.1 §2.1.2.
---
## Improvements
Recommendations that would improve UX quality and developer experience.
### A. Create Component-Scoped CSS Files
For each component in `Components/`, create a matching `.razor.css` file:
```
Components/Auth/AuthButton.razor.css
Components/Auth/AuthInput.razor.css
Components/Pos/ResponsiveOrderPanel.razor.css
```
### B. Add Password Strength Indicator
**File:** `Pages/Auth/Register.razor`
Registration form has no visual password strength feedback. Add a 4-step strength bar (weak → fair → good → strong) using the existing `PasswordStrengthCalculator` pattern from Swift app.
### C. Standardize Icon Sizes to 3 Tiers
Currently uses: 12, 14, 16, 18, 20, 24, 28, 32px. Reduce to:
```css
--icon-sm: 16px; /* Inline, label */
--icon-md: 20px; /* Button, nav */
--icon-lg: 24px; /* Header, feature */
```
### D. Add `aria-expanded` to Expandable Sections
**File:** `Pages/Admin/Shop/ShopRecipes.razor`
Accordion-style expand/collapse sections lack `aria-expanded` and `aria-controls`. Screen readers cannot determine collapsed state.
### E. Add `aria-busy` to Async Loading States
Async data-fetch loading spinners don't set `aria-busy="true"` on the container, so screen readers don't know content is loading.
### F. Introduce Design Token Documentation
Create a living style guide page at `/admin/design-system` (dev only) that shows all color tokens, spacing, typography, and component variants. Reference: MudBlazor Theme Manager pattern.
### G. Validate Table Semantics
Admin tables (staff management, inventory, orders) should verify `
` on all headers. MudBlazor's `MudTable` typically handles this, but custom HTML tables may not.
---
## Action Items
Prioritized next steps.
| # | Priority | Effort | Item | Files |
|---|----------|--------|------|-------|
| 1 | 🔴 Critical | Small | Add `:focus-visible` styles globally | `wwwroot/css/app.css` |
| 2 | 🔴 Critical | Large | Replace clickable `` with ` |