Xây dựng Toast Notification Accessible trong React: Cursor AI Giúp Được Gì?
Hướng dẫn chi tiết xây dựng hệ thống toast notification accessible trong React với ARIA live regions, keyboard navigation, và focus management — kết hợp Cursor AI để tăng tốc độ mà không bỏ sót WCAG compliance.
Toast Notification "nhỏ mà không nhỏ"
Hầu hết các dev đều đã implement toast notification ít nhất một lần. Cái gì đó pop lên góc màn hình, hiện thông báo, rồi biến mất. Simple, right? Nhưng nếu bạn đang build một product thực sự cần accessible — hỗ trợ screen reader, keyboard navigation, WCAG 2.1 — thì câu chuyện phức tạp hơn nhiều.
Bài viết này sẽ walk through cách build một Toast Notification System accessible trong React, và quan trọng hơn — cách dùng Cursor AI để đẩy nhanh quá trình mà không bỏ sót accessibility.
Vấn đề với Toast Notifications thông thường
Implementation điển hình trông như thế này:
// Cách thường gặp — thiếu accessibility
function Toast({ message }) {
return (
<div className="toast">
{message}
</div>
);
}
Vấn đề? Screen reader sẽ không announce nội dung này. Người dùng dùng keyboard không có cách dismiss. Không có focus management. Color contrast có thể fail WCAG AA.
Giải pháp: ARIA Live Regions
Để screen reader announce toast, bạn cần dùng ARIA live regions:
// Accessible Toast với ARIA
import { useEffect, useRef } from 'react';
interface ToastProps {
message: string;
type: 'success' | 'error' | 'info' | 'warning';
onDismiss: () => void;
}
function Toast({ message, type, onDismiss }: ToastProps) {
const toastRef = useRef<HTMLDivElement>(null);
useEffect(() => {
toastRef.current?.focus();
}, []);
return (
<div
ref={toastRef}
role="alert"
aria-live="polite"
aria-atomic="true"
tabIndex={-1}
className={`toast toast--${type}`}
onKeyDown={(e) => e.key === 'Escape' && onDismiss()}
>
<span className="sr-only">{type}: </span>
{message}
<button aria-label="Dismiss notification" onClick={onDismiss}>x</button>
</div>
);
}
Những key points:
role="alert"+aria-live="polite": Screen reader sẽ đọc khi toast xuất hiệnaria-atomic="true": Đọc toàn bộ nội dung, không đọc từng phầntabIndex={-1}+ auto-focus: Keyboard users có thể interact ngay- Escape key để dismiss: Standard keyboard pattern
Custom Hook — useToast
// useToast.ts
import { createContext, useContext, useState, useCallback } from 'react';
type ToastType = 'success' | 'error' | 'info' | 'warning';
interface ToastItem {
id: string;
message: string;
type: ToastType;
}
const ToastContext = createContext<{
addToast: (message: string, type?: ToastType) => void;
} | null>(null);
export function ToastProvider({ children }: { children: React.ReactNode }) {
const [toasts, setToasts] = useState<ToastItem[]>([]);
const addToast = useCallback((message: string, type: ToastType = 'info') => {
const id = crypto.randomUUID();
setToasts(prev => [...prev, { id, message, type }]);
setTimeout(() => {
setToasts(prev => prev.filter(t => t.id !== id));
}, 5000);
}, []);
return (
<ToastContext.Provider value={{ addToast }}>
{children}
<ToastContainer toasts={toasts} />
</ToastContext.Provider>
);
}
export const useToast = () => {
const ctx = useContext(ToastContext);
if (!ctx) throw new Error('useToast must be used within ToastProvider');
return ctx;
};
Cursor AI giúp được gì ở đây?
Khi mình đang build phần này, Cursor với Composer mode đã làm được một thứ khá ấn tượng: mình chỉ cần describe "build accessible toast system with ARIA live regions, keyboard dismiss, and focus trap" — nó generate ra cả file với TypeScript types, custom hook, và ToastContainer đầy đủ.
Điều mình thích ở Cursor là nó hiểu context của cả project. Nó thấy mình đang dùng Tailwind, tự generate class names phù hợp. Thấy project có TypeScript strict mode, không generate any type nào.
Tip thực tế: Trong Cursor, thêm file ACCESSIBILITY.md vào project rules với các WCAG guidelines bạn cần follow. Cursor sẽ tự reference khi generate accessible components — không cần nhắc lại mỗi lần.
Kết luận
Accessibility không phải là "nice to have" — với nhiều markets, đó là legal requirement và là dấu hiệu của product quality thực sự. Với AI tools như Cursor ($20/tháng), chi phí implement accessible components đã giảm đáng kể. Không còn lý do gì để skip nữa.
Nếu bạn chưa thử Cursor và vẫn đang code theo kiểu truyền thống, đây là lúc tốt để thay đổi workflow.
Admin
Cal.com
Open source scheduling — tự host booking system, thay thế Calendly. Free & privacy-first.
Bình luận (0)
Đăng nhập để bình luận
Chưa có bình luận nào. Hãy là người đầu tiên!