| Rule | Correct | Wrong | |------|---------|-------| | First rule | Use native HTML element | Add ARIA to
| | Labels | aria-labelledby on interactive element | aria-label on | | Live regions | aria-live="polite" for updates | role="alert" everywhere | | Hidden | aria-hidden="true" on decorative SVG | display:none + ARIA role | | Disabled | HTML disabled attribute | aria-disabled on |
// Sanitize on input to prevent XSS in error displays input.addEventListener('input', () => { input.setAttribute('aria-invalid', 'false'); error.textContent = ''; // Strip any HTML if dynamically setting error text });
2. CSS Architecture — BEM + ITCSS + Cascade Layers
Custom Properties & Design Tokens
/ @layer order declaration — lowest priority first /
@layer reset, base, layout, components, utilities;
flowchart TD
A["Design component"] --> B{"Needs layout change?"}
B -- No --> C["Fluid: clamp(), %, vw"]
B -- Yes --> D{"Change at viewport or container context?"}
D -- Container --> E["@container query"]
D -- Viewport --> F{"Breakpoint needed?"}
F -- "640px+" --> G["@media ≥40em (small tablet)"]
F -- "1024px+" --> H["@media ≥64em (desktop)"]
F -- "1280px+" --> I["@media ≥80em (wide)"]
E --> J["Set container-type on parent"]
G --> K["Mobile-first: min-width"]
H --> K
I --> K
> Key rule: With CSP, avoid style-src 'unsafe-inline'. Use CSS custom properties and classes instead of inline style="" attributes. If inline styles are unavoidable, use a nonce: style-src 'nonce-abc123' and .
XSS Prevention — Never Use innerHTML
// ❌ DANGEROUS — opens XSS vector even with CSP
element.innerHTML = userInput;
// ✅ Safe alternatives: element.textContent = userInput; // plain text element.replaceChildren(...nodes); // structured DOM
// ✅ Sanitize when HTML is required import { sanitize } from 'dompurify'; element.setHTML(sanitize(userInput, { ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'], ALLOWED_ATTR: ['href'], FORBID_ATTR: ['style', 'onerror', 'onload'] // never allow style/events }));
// ✅ Template literals for safe construction const card = document.createElement('article'); card.className = 'card'; const title = document.createElement('h2'); title.className = 'card__title'; title.textContent = data.title; // auto-escaped card.append(title);