Bộ hướng dẫn này phù hợp cho cả các dự án mới và các dự án hiện có.
1# Hướng dẫn Chung cho các Dự án React / Next.js23> Mục đích: Các quy tắc chung để phát triển nhiều loại dự án khác nhau với React + TypeScript, Next.js + TypeScript và Tailwind CSS.4> Cách sử dụng: Đặt tệp này vào thư mục gốc của một dự án mới dưới tên `AGENTS.md`, `CLAUDE.md` hoặc `PROJECT_RULES.md`, hoặc sử dụng nó làm tập hướng dẫn cơ sở cho một tác tử AI (AI agent).5> Quan trọng: Các hướng dẫn này không chứa quy tắc đặc thù của từng sản phẩm. Hãy lưu giữ mọi thứ liên quan đến một dự án riêng lẻ trong một tệp `PROJECT_RULES.md` tách biệt.67---89# 1. Nguyên tắc Cốt lõi1011Xây dựng một ứng dụng sẵn sàng cho môi trường thực tế (production-ready), chứ không phải một tập hợp các thành phần (components) rời rạc.1213Luôn tuân theo trình tự sau:14151. Xem xét cấu trúc dự án hiện tại, `package.json`, cơ chế định tuyến (routing), các nguyên hàm giao diện (UI primitives), các kho lưu trữ trạng thái (stores), hooks, lược đồ (schemas) và quy tắc dự án.162. Tìm kiếm các hành động (actions), hàm bổ trợ (helpers), lược đồ và các thành phần hiện có có thể tái sử dụng được.173. Xác định thay đổi nhỏ nhất cần thiết cho tác vụ.184. Bảo toàn hành vi hiện có.195. Triển khai từng tính năng mới trọn vẹn từ đầu đến cuối (end to end): mô hình dữ liệu, xác thực, giao diện, lưu trữ/nhập/xuất, các trường hợp biên và bước xác minh.206. Chạy các bước kiểm tra có liên quan và báo cáo kết quả một cách trung thực.2122Không thêm các thư viện phụ thuộc (dependencies), lớp trừu tượng, store toàn cục hoặc tầng kiến trúc trừ khi chúng thực sự cần thiết.23Sử dụng `shadcn/ui` theo mặc định cho công việc giao diện. Không thêm một bộ UI kit khác chồng lên trên nó nếu không có lý do rõ ràng.2425---2627# 2. Lựa chọn giữa React và Next.js2829Sử dụng Next.js khi dự án cần:3031- định tuyến (routing);32- tối ưu hóa công cụ tìm kiếm (SEO);33- SSR / Server Components;34- Server Actions;35- Route Handlers / các tuyến API (API routes);36- xác thực người dùng (authentication);37- truy cập cơ sở dữ liệu;38- các biến môi trường riêng tư;39- xuất bản nội dung.4041Sử dụng React + Vite khi:4243- ứng dụng chạy hoàn toàn ở phía trình khách (client-side);44- không yêu cầu SEO;45- đó là một công cụ cục bộ, bảng điều khiển (dashboard), trình soạn thảo, trang quản trị (admin panel) hoặc giao diện giống ứng dụng máy tính để bàn;46- máy chủ đã tồn tại như một dịch vụ riêng biệt.4748Đừng chọn Next.js chỉ vì nó phổ biến. Không thêm Redux, Zustand, React Query, thư viện biểu mẫu hoặc một bộ UI kit khác nếu không có lý do cụ thể.4950---5152# 3. Bộ công nghệ Mặc định và Các bước Kiểm tra5354Sử dụng các công nghệ sau theo mặc định:5556- React;57- TypeScript ở chế độ nghiêm ngặt (strict mode);58- Tailwind CSS;59- `shadcn/ui` làm phương pháp tiếp cận giao diện bắt buộc để có thiết kế gọn gàng và phát triển giao diện nhanh chóng;60- Lucide React hoặc thư viện biểu tượng được sử dụng bởi cấu hình shadcn hiện tại;61- ESLint;62- hàm bổ trợ `cn()` dùng chung;63- xác thực thời gian chạy (runtime validation) đối với dữ liệu bên ngoài;64- các phần tử HTML hỗ trợ khả năng tiếp cận (accessible HTML elements).6566Sử dụng `shadcn/ui` làm nguồn chính cung cấp các nguyên hàm giao diện: nút bấm, ô nhập liệu, ô chọn (selects), hộp thoại (dialogs), bảng trượt (sheets), menu thả xuống (dropdowns), chú giải công cụ (tooltips), các tab, băng chuyền (carousels), thẻ (cards), huy hiệu (badges), khung xương tải trang (skeletons), vùng cuộn (scroll areas) và các thành phần cần thiết khác. Chỉ tạo các nguyên hàm tùy chỉnh khi shadcn không cung cấp thành phần phù hợp hoặc khi dự án đã có sẵn một nguyên hàm cục bộ ổn định.6768Đối với một bản MVP, hãy bắt đầu với dữ liệu giả lập (mock)/JSON/localStorage và xác thực các luồng người dùng cục bộ trước tiên. Thêm phần backend, cơ sở dữ liệu, thanh toán, xác thực và các tích hợp bên ngoài vào sau cùng, một khi giao diện, các mô hình dữ liệu và các luồng thao tác đã rõ ràng.6970Ở mức tối thiểu, hãy chạy các lệnh này sau khi thay đổi mã nguồn:7172```bash73npm run typecheck74npm run lint75npm run build76```7778Đừng tuyên bố rằng dự án hoạt động tốt nếu các lệnh này chưa được chạy hoặc chạy xong nhưng có lỗi.7980---8182# 4. Kiến trúc8384Đối với các dự án Next.js dự kiến sẽ phát triển lớn hơn, hãy giữ mã nguồn bên trong `src/` theo mặc định: `src/app`, `src/components`, `src/lib`, `src/data`, `src/hooks` và `src/features`. Giữ các thư mục và tệp hỗ trợ cấp gốc (`public`, các tệp cấu hình, tệp khóa phiên bản lockfiles và README) ở thư mục gốc của dự án.8586Đối với các dự án nhỏ, cấu trúc sau đây có thể chấp nhận được:8788```text89src/90 app/ or pages/91 components/92 features/93 lib/94 shared/95```9697Đối với các dự án vừa và lớn, hãy sử dụng phương pháp tiếp cận tương tự FSD (Feature-Sliced Design):9899```text100src/101 app/ # bootstrap, providers, layouts, routes102 views/ # page-level composition103 widgets/ # large UI blocks104 features/ # user workflows105 entities/ # domain model106 shared/ # generic helpers, config, thin wrappers around shadcn/ui107```108109Chiều hướng import:110111```text112app/views -> widgets -> features -> entities -> shared113```114115Tuyệt đối không:116117- import `widgets` vào trong `features`;118- đặt logic nghiệp vụ vào trong `shared`;119- biến `shared/lib` thành nơi chứa tạp nham các hàm không liên quan với nhau;120- sao chép trùng lặp logic thay đổi dữ liệu (mutation logic) trên nhiều thành phần giao diện;121- sử dụng cách import sâu vào cấu trúc nội bộ của một mô-đun khác khi mô-đun đó đã cung cấp một API công khai (public API).122123---124125# 5. API Công khai (Public API)126127Mỗi thư mục tính năng (feature), thực thể (entity) hoặc giao diện dùng chung nên cung cấp một API công khai rõ ràng thông qua tệp `index.ts` khi mô-đun đó được sử dụng từ bên ngoài. Đối với các nguyên hàm shadcn, API công khai thường đã nằm sẵn trong `components/ui/*` hoặc tầng giao diện cục bộ của dự án.128129Tốt:130131```ts132import { createTask } from "@/features/create-task";133```134135Kém:136137```ts138import { createTask } from "@/features/create-task/model/createTask";139```140141Ngoại lệ: mã nguồn nội bộ bên trong cùng một tính năng hoặc thực thể.142143---144145# 6. TypeScript146147Bắt buộc:148149- bật `strict: true`;150- không sử dụng `any` ngoại trừ trong phần mã tương tác hệ thống (interoperability code) đã được cô lập;151- không che giấu lỗi kiểu dữ liệu bằng phép ép kiểu `as`;152- sử dụng kiểu hợp phân biệt (discriminated unions) cho trạng thái phức tạp;153- xác thực dữ liệu JSON lúc chạy (runtime) bằng một lược đồ (schema);154- không tạo nhiều kiểu dữ liệu giống hệt nhau nếu không có lý do có ý nghĩa.155156Ví dụ về kiểu trạng thái:157158```ts159type LoadState<T> =160 | { status: "idle" }161 | { status: "loading" }162 | { status: "success"; data: T }163 | { status: "error"; message: string };164```165166---167168# 7. Trạng thái (State) và Hiệu ứng (Effects) trong React169170Lưu trữ trạng thái ở nơi nó thực sự thuộc về:171172| Loại trạng thái | Nơi lưu trữ |173| ----------------------------- | ----------------------------------------------------------------------- |174| Giao diện cục bộ (Local UI) | `useState`, `useReducer` |175| Trạng thái URL | các tham số route/search |176| Trạng thái máy chủ | kết xuất phía máy chủ (server rendering) hoặc tầng cache/query |177| Trạng thái biểu mẫu | hook/thư viện quản lý biểu mẫu |178| Giao diện toàn cục | một store nhỏ khi cần thiết |179| Trạng thái miền (Domain state)| thực thể/store khi trạng thái được chia sẻ qua nhiều luồng thao tác |180181Không đặt những thứ sau đây vào store toàn cục:182183- trạng thái di chuột (hover state);184- trạng thái đóng/mở của một menu thả xuống đơn lẻ;185- giá trị nháp của một ô nhập liệu đơn lẻ;186- trạng thái của một hộp thoại (modal) đơn lẻ;187- tab đang chọn tạm thời của một thành phần.188189Sử dụng `useEffect` để đồng bộ hóa với các hệ thống bên ngoài:190191- các API của trình duyệt;192- bộ hẹn giờ (timers);193- các đăng ký lắng nghe sự kiện (subscriptions);194- các store bên ngoài;195- các tích hợp DOM.196197Không sử dụng `useEffect` cho các giá trị dẫn xuất (derived values).198199Kém:200201```tsx202const [fullName, setFullName] = useState("");203204useEffect(() => {205 setFullName(` `);206}, [firstName, lastName]);207```208209Tốt:210211```tsx212const fullName = ` `;213```214215---216217# 8. Ranh giới trong Next.js218219Trong App Router, các thành phần là Server Components theo mặc định.220221Chỉ thêm `"use client"` ở những nơi bạn cần:222223- các trình xử lý sự kiện (event handlers);224- trạng thái cục bộ;225- các hiệu ứng (effects);226- `window`, `document` hoặc `localStorage`;227- kéo và thả (drag and drop);228- `contenteditable`;229- các thư viện chỉ chạy ở phía trình khách (client-only libraries).230231Đừng biến toàn bộ một bố cục (layout) thành Client Component nếu không có nhu cầu rõ ràng.232233Mã nguồn chỉ chạy trên máy chủ (Server-only code) bao gồm:234235- truy cập cơ sở dữ liệu;236- xác thực người dùng;237- các trình khách API riêng tư;238- các biến môi trường bí mật;239- webhooks;240- kiểm tra quyền truy cập.241242Không bao giờ import một mô-đun chỉ dành cho máy chủ vào trong một Client Component.243244---245246# 9. Xác thực Thời gian chạy và Di chuyển dữ liệu (Migrations)247248Xác thực toàn bộ dữ liệu bên ngoài ngay tại ranh giới tiếp nhận:249250- phần thân yêu cầu (request bodies);251- dữ liệu biểu mẫu;252- các tham số URL/search;253- các tệp được tải lên;254- dữ liệu JSON được nhập vào;255- dữ liệu trong localStorage/IndexedDB;256- các phản hồi từ API bên ngoài.257258Khi thêm một trường mới vào mô hình dữ liệu, hãy cập nhật toàn bộ vòng đời của nó:2592601. Kiểu dữ liệu TypeScript.2612. Lược đồ kiểm tra thời gian chạy (Runtime schema).2623. Các giá trị khởi tạo (factory)/mặc định.2634. Trình phân tích cú pháp/chuyển đổi cho dữ liệu cũ (legacy data).2645. Các hàm bổ trợ chuẩn hóa dữ liệu (Normalization helpers).2656. Tính năng nhập/xuất (Import/export).2667. Đánh chỉ mục tìm kiếm/lọc, nếu trường đó cần có khả năng tìm kiếm được.2678. Bản chụp hoàn tác/làm lại (Undo/redo snapshots), nếu người dùng có thể chỉnh sửa trường đó.2689. Giao diện để tạo, chỉnh sửa và xóa giá trị trường đó.26910. Các trường hợp biên và bước kiểm tra.270271Ví dụ:272273```ts274return {275 ...item,276 status: item.status ?? "active",277 tags: normalizeTags(item.tags),278 dueDate: normalizeDate(item.dueDate),279};280```281282Đừng chỉ thêm một trường mô hình dữ liệu ở mỗi tầng giao diện.283284---285286# 10. Biểu mẫu (Forms)287288Mọi biểu mẫu phải bao gồm:289290- một lược đồ xác thực (validation schema);291- hiển thị lỗi của từng trường;292- trạng thái đang gửi/đang tải (submitting/loading);293- nút gửi bị vô hiệu hóa trong khi đang gửi;294- cơ chế bảo vệ chống gửi trùng lặp nhiều lần;295- trạng thái báo lỗi;296- hành vi khi gửi thành công;297- hành vi đặt lại (reset)/lưu nháp, khi áp dụng.298299Một biểu mẫu chưa được coi là hoàn chỉnh nếu nó chỉ hoạt động khi yêu cầu gửi đi thành công mỹ mãn.300301---302303# 11. shadcn/ui và Giao diện Dùng chung304305Sử dụng `shadcn/ui` theo mặc định để xây dựng các giao diện sạch sẽ, nhất quán một cách nhanh chóng.306307Quy tắc:308309- trước tiên hãy kiểm tra xem thành phần cần thiết có tồn tại trong kho đăng ký (registry) của shadcn hay không;310- thêm các thành phần shadcn thông qua CLI hoặc phương thức cục bộ đã được thiết lập của dự án;311- không tự tạo mới Button, Input, Modal, Dropdown, Tooltip, Tabs hoặc Card tùy chỉnh nếu shadcn đã đáp ứng được trường hợp sử dụng đó;312- điều chỉnh các thành phần shadcn thông qua `className`, các biến thể (variants) và sự kết hợp (composition) thay vì sao chép ra các thành phần tương tự;313- giữ các thành phần nghiệp vụ tách biệt khỏi các nguyên hàm giao diện: `components/marketplace`, `features/*/ui`, `widgets/*` hoặc `entities/*/ui`;314- chỉ giữ các nguyên hàm shadcn và các lớp bọc mỏng có thể tái sử dụng trong `components/ui` hoặc `shared/ui`;315- không đặt các thành phần nghiệp vụ đặc thù của sản phẩm vào đó;316- nếu shadcn không cung cấp một thành phần nào đó, hãy tạo một lớp bọc cục bộ tối giản nhất quán với cấu hình shadcn hiện tại.317318Tập hợp cơ sở các thành phần shadcn dành cho giao diện hiệu suất công việc (productivity interfaces):319320```text321button322input323select324textarea325checkbox326switch327dialog328sheet329dropdown-menu330popover331tooltip332tabs333card334badge335avatar336separator337scroll-area338skeleton339carousel340accordion341collapsible342hover-card343```344345Đối với các luồng chợ thương mại (marketplace), trò chuyện (chat) và hỗ trợ khách hàng, hãy lên kế hoạch sử dụng thêm các thành phần shadcn mới hơn sau đây:346347```text348message349message-scroller350attachment351marker352```353354Luôn sử dụng hàm `cn()`:355356```ts357export function cn(...values: Array<string | false | null | undefined>) {358 return values.filter(Boolean).join(" ");359}360```361362---363364# 12. Chọn Bề mặt Giao diện Phù hợp365366Trước khi thêm một công cụ mới, hãy chọn bề mặt hiển thị phù hợp:367368| Quy mô tính năng | Vị trí đặt | Ví dụ |369| -------------------------------------------- | ---------------------------------------- | -------------------------------------------- |370| 1-5 cài đặt nhanh | menu ngữ cảnh / dropdown / popover | trạng thái, ngày đến hạn, thẻ gắn (tags) |371| 5-12 cài đặt được phân nhóm | popover chia mục, có thể cuộn | thuộc tính thực thể, bộ lọc thu gọn |372| tập dữ liệu lớn hoặc thao tác hàng loạt | thanh bên (sidebar) / ngăn kéo (drawer) | bộ lọc, bảng công cụ |373| biểu mẫu phức tạp hoặc hành động nguy hiểm | hộp thoại (modal) | nhập/xuất dữ liệu, xác nhận xóa |374| không gian làm việc cố định | trang/khung nhìn/widget chuyên dụng | bảng điều khiển, lịch, trình soạn thảo |375376Quy tắc:377378> Nếu một điều khiển chỉ được sử dụng thỉnh thoảng, hãy giữ nó trong một menu.379> Nếu một điều khiển được sử dụng liên tục, hãy giữ nó hiển thị trên bề mặt chính.380> Nếu một điều khiển phức tạp và dài dòng, hãy chuyển nó sang thanh bên hoặc hộp thoại modal.381382Đừng biến một nhóm nhỏ các điều khiển thành một thẻ (card) lớn chiếm diện tích trên trang. Trong các giao diện làm việc tập trung vào năng suất, điều này làm lãng phí không gian quý giá.383384---385386# 13. Giao diện Thu gọn cho Trình soạn thảo, Bảng điều khiển và Không gian làm việc387388Trong các ứng dụng hiệu suất công việc, nội dung chính phải luôn là tâm điểm chú ý.389390Bắt buộc:391392- tiêu đề, phần thân, bảng công việc hoặc trình soạn thảo không được bị đẩy tụt xuống dưới bởi các điều khiển phụ;393- các thuộc tính của thực thể nhìn chung nên được mở từ một nút biểu tượng nằm cạnh tiêu đề;394- các nút cài đặt phải có thuộc tính `aria-label`;395- một trạng thái quan trọng có thể được hiển thị dưới dạng một huy hiệu (badge) nhỏ;396- các hành động tạo/thêm mới phải xuất hiện trong một ngữ cảnh rõ ràng;397- các luồng phụ thuộc nhiều vào thanh bên phải bao gồm một menu hoặc bộ chuyển đổi thân thiện với thiết bị di động;398- đừng làm cho một công cụ làm việc trông giống như một trang đích quảng cáo (landing page).399400Kém:401402```tsx403404 <Select>Status</Select>405 <Select>Task</Select>406 <Input>Date</Input>407 <Input>Tags</Input>408</LargePropertiesCard>409```410411Tốt:412413```tsx414415 <TitleInput />416 <PropertiesMenu />417</TitleRow>418```419420---421422# 14. Lớp phủ (Overlays), Menu thả xuống (Dropdowns), Popovers và Menu Ngữ cảnh423424Mọi menu phải hoạt động như một lớp phủ (overlay) thực thụ.425426Quy tắc:427428- nếu một menu có thể vượt ra ngoài khung chứa của nó, hãy kết xuất nó thông qua `createPortal(..., document.body)`;429- sử dụng `position: fixed` hoặc một công cụ định vị đáng tin cậy;430- đặt giá trị `z-index` tường minh;431- sử dụng `backgroundColor` đặc (không trong suốt);432- không chỉ dựa vào nền bán trong suốt `bg-black/50` hoặc hiệu ứng làm mờ (blur);433- thêm đường viền (border), vòng viền (ring) hoặc bóng đổ (shadow);434- đặt `max-height` và `overflow-y-auto`;435- đóng lại khi nhấn phím `Escape`;436- đóng lại khi nhấp chuột/chạm ra bên ngoài;437- ngăn văn bản trên trang hiển thị xuyên qua hoặc đè lên trên menu;438- trạng thái di chuột (hover) và kích hoạt (active) không được làm thay đổi kích thước của mục chọn.439440Kiểu dáng lớp phủ tối thiểu:441442```tsx443<div444 role="menu"445 className="rounded-2xl border p-2 shadow-2xl"446 style="#151a21",447 boxShadow: "0 24px 70px rgb(0 0 0 / 78%)",448 isolation: "isolate",449 zIndex: 1000,450>451 ...452</div>453```454455Nếu nền menu không hiển thị đúng hoặc nội dung bên dưới xuất hiện đè lên trên nó, hãy kiểm tra:456457- portal;458- `position`;459- `z-index`;460- ngữ cảnh xếp chồng (stacking contexts) của phần tử cha;461- `isolation`;462- độ mờ đục (opacity)/màu nền;463- thuộc tính tràn (overflow)/cắt xén (clipping) của phần tử cha.464465---466467# 15. Danh sách Tùy chọn trong Menu468469Danh sách các tác vụ, dự án, người dùng, thẻ gắn hoặc các tùy chọn khác trong một menu không được trông giống như một bức tường văn bản dày đặc.470471Đối với một mục hiển thị hai dòng:472473- sử dụng `min-height` từ 40-44px;474- bao gồm khoảng cách `gap` giữa biểu tượng, văn bản và dấu tích chọn;475- sử dụng khoảng đệm dọc chẳng hạn như `py-1.5`;476- cung cấp chiều cao dòng (line heights) khác nhau cho tiêu đề và siêu dữ liệu (metadata);477- thêm `mt-0.5` giữa tiêu đề và siêu dữ liệu;478- áp dụng `min-w-0` cho phần tử cha chứa văn bản;479- áp dụng `truncate` cho tiêu đề và siêu dữ liệu;480- áp dụng `shrink-0` cho dấu tích chọn và các biểu tượng.481482Ví dụ:483484```tsx485<button className="flex min-h-11 items-center gap-2.5 rounded-lg px-2.5 py-1.5">486 <span className="min-w-0 flex-1">487 <span className="block truncate font-medium leading-5"></span>488 <span className="mt-0.5 block truncate text-xs leading-4 text-muted">489 {meta}490 </span>491 </span>492 {isActive ? <Check className="shrink-0" /> : null}493</button>494```495496---497498# 16. Văn bản Dài và Hiện tượng Tràn khung (Overflow)499500Bất kỳ văn bản nào do người dùng cung cấp cũng có thể chứa một từ rất dài không có khoảng trắng.501502Đối với các trình soạn thảo, phần tử `contenteditable`, Markdown, tiêu đề thẻ (card titles) và bình luận:503504- sử dụng `min-w-0` trên các phần tử con của flex/grid;505- sử dụng các lớp tiện ích (utilities) hiện hành của Tailwind để ngắt dòng các từ dài;506- trong các phiên bản Tailwind mới hơn, `break-words` có thể được viết là `wrap-break-word`;507- kiểm tra tài liệu của phiên bản Tailwind hiện tại trong dự án trước khi sử dụng các lớp ngắt dòng, tràn khung, text-wrap, grid, khoảng cách hoặc giá trị tùy ý (arbitrary-value);508- nếu một phần tử nằm bên trong khung chứa flex và văn bản dài làm vỡ chiều rộng của nó, hãy kiểm tra xem `wrap-anywhere` có phù hợp hay không;509- sử dụng `truncate` cho các dòng ngắn trong thẻ;510- ngắt dòng văn bản phần thân thay vì để xảy ra hiện tượng tràn ngang;511- văn bản không được hiển thị đè lên trên menu, popover hoặc hộp thoại modal;512- kiểm thử bằng một chuỗi ký tự dài không chứa khoảng trắng.513514Đối với một khối có thể chỉnh sửa:515516```tsx517className = "min-w-0 wrap-break-word whitespace-pre-wrap";518```519520Nếu dự án sử dụng phiên bản Tailwind cũ hơn chưa có `wrap-break-word`, hãy kiểm tra phiên bản Tailwind đã cài đặt cùng tài liệu chính thức hoặc ghi chú phiên bản, sau đó sử dụng cách tương đương được hỗ trợ: `break-words`, một giá trị tùy ý hoặc thuộc tính CSS thuần.521522Đối với một huy hiệu (badge):523524```tsx525className = "inline-flex whitespace-nowrap";526```527528Một huy hiệu không được ép nén văn bản theo chiều dọc. Nếu không đủ chỗ, hãy chuyển nó xuống dòng mới hoặc sử dụng `truncate` với chiều rộng tường minh, dễ hiểu.529530---531532# 17. Tailwind CSS: Xác minh Tên Lớp (Class Names) Hiện hành533534Tác tử AI phải kiểm tra phiên bản Tailwind được cài đặt trong dự án trước khi sử dụng các lớp mới hoặc có khả năng phụ thuộc vào phiên bản.535536Quy trình:5375381. Kiểm tra `package.json` và tệp lockfile.5392. Xác định phiên bản chính (major version) của Tailwind.5403. Nếu một lớp có thể khác nhau giữa các phiên bản, hãy kiểm tra tài liệu chính thức cho đúng phiên bản đó.5414. Không thay thế các lớp một cách máy móc mà không xác minh.5425. Khi sử dụng một giá trị tùy ý, hãy xác nhận rằng nó được đưa vào kết quả bản dựng (build output).543544Đặc biệt chú ý đến:545546- `break-words` / `wrap-break-word` / `wrap-anywhere`;547- `text-wrap`, `text-balance` và `text-pretty`;548- `overflow-*`;549- `size-*`;550- các màu sắc tùy ý chẳng hạn như `bg-[#151a21]`;551- bóng đổ tùy ý;552- các mẫu lưới (grid templates) tùy ý;553- tên lớp được tạo động.554555Không tạo các lớp Tailwind động theo cách này:556557```tsx558const color = "red";559return <div className={`bg--500`} />;560```561562Tailwind có thể không phát hiện ra lớp đó trong quá trình dựng (build). Hãy sử dụng một bảng ánh xạ (map):563564```tsx565const colorClassName = {566 danger: "bg-red-500",567 success: "bg-emerald-500",568};569```570571Nếu một màu nền lớp phủ quan trọng không được phép phụ thuộc vào đầu ra bản dựng của Tailwind, việc sử dụng thuộc tính nội tuyến `style.backgroundColor` là có thể chấp nhận được.572573---574575# 18. Bố cục và Thu gọn Thanh bên (Sidebar Collapse)576577Việc thu gọn thanh bên hoặc ngăn kéo không được làm thay đổi chiều cao trang hoặc để lại một khối trống.578579Quy tắc:580581- khung vỏ ứng dụng (app shell): `h-dvh min-h-dvh overflow-hidden`;582- các vùng bên trong: `flex min-h-0 flex-1 overflow-hidden`;583- chỉ bật cuộn trên vùng phù hợp bằng `overflow-y-auto`;584- khi thu gọn, hãy thay đổi chiều rộng/flex-basis thay vì chiều cao;585- thanh bên đã thu gọn phải có chiều rộng ổn định;586- cung cấp một nút điều khiển rõ ràng để khôi phục lại thanh bên;587- các hành động mang tính phá hủy hoặc tạo mới không được nằm trơ trọi như những nút bấm cô lập không có ngữ cảnh;588- các tùy chọn giao diện của người dùng có thể được lưu giữ trong localStorage.589590Ví dụ:591592```tsx593<main className="flex h-dvh min-h-dvh flex-col overflow-hidden">594 <div className="flex min-h-0 flex-1 overflow-hidden">595 <Sidebar className="h-full min-h-0 shrink-0" />596 <section className="min-h-0 flex-1 overflow-y-auto" />597 </div>598</main>599```600601---602603# 19. Các API Trình duyệt và localStorage604605Trong Next.js, các API trình duyệt chỉ khả dụng bên trong Client Components.606607Quy tắc:608609- một tệp sử dụng `localStorage`, `window`, `document`, kéo và thả hoặc `contenteditable` bắt buộc phải có khai báo `"use client"`;610- không đọc `localStorage` trong một Server Component;611- không gây ra lỗi đồng bộ hóa (hydration errors) do các giá trị khởi tạo khác nhau giữa máy chủ và trình duyệt;612- bọc các thao tác lưu trữ trong khối `try/catch`;613- lỗi lưu trữ không được làm hỏng giao diện;614- xác minh các tùy chọn giao diện đã lưu sau khi tải lại trang;615- quá trình dựng (build) không được thất bại với lỗi `window is not defined`.616617Ví dụ:618619```tsx620const toggle = useCallback(() => {621 setIsCollapsed((current) => {622 const next = !current;623624 try {625 window.localStorage.setItem(KEY, next ? "true" : "false");626 } catch {627 // UI still works without browser storage.628 }629630 return next;631 });632}, []);633```634635Xác minh rằng:636637- trạng thái mặc định hoạt động tốt khi bộ nhớ trống;638- việc tải lại trang vẫn giữ nguyên trạng thái;639- chế độ ẩn danh (private mode) hoặc lỗi bộ nhớ không làm hỏng màn hình;640- quá trình dựng không bị lỗi `window is not defined`.641642---643644# 20. Mối quan hệ Giữa các Công cụ645646Nếu một thực thể được liên kết với một thực thể khác, mối quan hệ đó phải là thật:647648- lưu trữ nó trong mô hình dữ liệu;649- hiển thị nó trên giao diện;650- nhấp vào nó sẽ mở ra thực thể được liên kết;651- khi tạo thực thể có liên quan, hãy lưu mối quan hệ đó ngay lập tức;652- bảo toàn mối quan hệ trong quá trình nhập/xuất dữ liệu;653- đưa mối quan hệ vào hành vi tìm kiếm/lọc khi hữu ích;654- nếu thực thể được liên kết bị xóa, hãy hiển thị trạng thái dự phòng (fallback) trên giao diện.655656Đừng tạo một nút "Liên kết" chỉ mang tính trang trí nếu mối quan hệ đó không được lưu trữ bền vững.657658---659660# 21. Thống nhất các Thao tác Miền (Domain Operations)661662Mỗi thao tác của người dùng phải có một nguồn chân lý duy nhất (single source of truth).663664Tuyệt đối không:665666- tạo một thực thể theo một kiểu từ menu lệnh gạch chéo (slash menu);667- tạo nó theo một kiểu khác từ thanh công cụ;668- bỏ qua bước xác thực khi gọi từ bảng lệnh (command palette);669- sao chép trùng lặp logic thay đổi dữ liệu trong menu ngữ cảnh.670671Thay vào đó:672673- giữ thao tác miền ở một nơi duy nhất;674- để các thành phần giao diện gọi tới thao tác đó;675- sử dụng cùng một quy trình xác thực và các ràng buộc cho mọi điểm truy cập.676677---678679# 23. Khả năng Tiếp cận (Accessibility)680681Bắt buộc:682683- sử dụng `<button>` cho các hành động;684- sử dụng `<a>` cho việc điều hướng;685- thêm `aria-label` cho các nút chỉ có biểu tượng;686- cung cấp nhãn (labels) cho các ô nhập liệu;687- hiển thị trạng thái tiêu điểm (focus state) rõ ràng;688- hỗ trợ điều hướng bằng bàn phím;689- đóng hộp thoại modal và popover khi nhấn `Escape`;690- đóng popover khi nhấp chuột ra bên ngoài;691- sử dụng bẫy tiêu điểm (focus trap) trong các hộp thoại modal;692- không sử dụng màu sắc làm cách duy nhất để truyền đạt ý nghĩa;693- không thay thế `<button>` bằng ``.694695---696697# 24. Trạng thái Đang tải, Trống và Lỗi698699Các màn hình hiển thị theo dữ liệu phải tính đến:700701- trạng thái đang tải (loading);702- trạng thái thành công (success);703- trạng thái trống (empty state);704- từ chối quyền truy cập (permission denied);705- lỗi mạng (network error);706- lỗi máy chủ (server error);707- thao tác thử lại (retry).708709Một màn hình trắng trơn không có lời giải thích nào là một lỗi (bug).710711---712713# 25. Bảo mật714715Bắt buộc:716717- chỉ giữ các thông tin bí mật (secrets) ở phía máy chủ;718- sử dụng xác thực thời gian chạy (runtime validation);719- thực thi kiểm soát quyền truy cập ở phía máy chủ;720- xác thực kiểu MIME và kích thước của tệp tải lên;721- làm sạch (sanitize) mã HTML do người dùng cung cấp;722- không sử dụng `dangerouslySetInnerHTML` mà không có bộ làm sạch mã (sanitizer);723- không ghi nhật ký (log) các mã thông báo (tokens) hoặc dữ liệu cá nhân;724- không tin tưởng các giá trị `role` hoặc `userId` do trình duyệt gửi lên.725726---727728# 26. Hiệu năng729730Đo lường trước, tối ưu hóa sau.731732Sử dụng:733734- import động (dynamic imports) cho các mô-đun nặng như trình soạn thảo, biểu đồ, bản đồ và PDF;735- tối ưu hóa hình ảnh;736- ảo hóa (virtualization) cho các danh sách lớn;737- cơ chế hủy/bảo vệ chống yêu cầu cũ (abort/stale-request protection) cho tính năng tìm kiếm;738- các bộ chọn (selectors) để giảm thiểu việc kết xuất lại (rerenders).739740Không thêm các kỹ thuật ghi nhớ tạm (memoization) nếu không có lý do rõ ràng.741742---743744# 27. Kiểm thử Thiết kế với Nội dung Thực tế745746Để có thêm hướng dẫn về chất lượng giao diện, bạn có thể tham khảo:747748- https://jakub.kr/skills/make-interfaces-feel-better749750Tài nguyên này rất hữu ích khi trau chuốt nghệ thuật chữ (typography), trạng thái di chuột, bóng đổ, đường viền, khoảng cách, căn chỉnh thị giác, tương tác vi mô (micro-interactions) và cảm giác tổng thể của giao diện.751752Trước khi hoàn thành một tác vụ giao diện, hãy kiểm thử nó với:753754- một từ dài không có khoảng trắng;755- một tiêu đề tiếng Nga dài;756- một tiêu đề ngắn;757- một tiêu đề để trống;758- nhiều thẻ gắn (tags);759- một tên danh sách/danh mục dài;760- nhiều tùy chọn trong một menu thả xuống;761- trạng thái đang hoạt động và không hoạt động;762- trường hợp có ngày tháng và thiếu ngày tháng.763764Xác minh rằng:765766- không có phần tử nào bị đè lên nhau;767- các lớp phủ che kín nội dung bên dưới;768- văn bản không bị nhìn xuyên qua các menu;769- các huy hiệu không ép nén văn bản theo chiều dọc;770- các phần tử không chen chúc sát vào nhau;771- thanh cuộn không che khuất văn bản quan trọng;772- trạng thái di chuột (hover) và tiêu điểm (focus) dễ đọc;773- cả chiều rộng màn hình máy tính để bàn và thiết bị di động đều hiển thị chuẩn xác.774775---776777# 28. Kiểm tra Sau khi Thay đổi778779Sau khi thay đổi mã nguồn, hãy chạy:780781```bash782npm run typecheck783npm run lint784npm run build785```786787Nếu giao diện bị thay đổi:788789- mở trang trong trình duyệt;790- thực hiện trọn vẹn luồng người dùng chính;791- kiểm thử tương tác bằng cả bàn phím và chuột;792- kiểm thử hành vi nhấn `Escape` và nhấp chuột ra bên ngoài;793- kiểm thử việc tải lại trang;794- kiểm thử văn bản dài;795- kiểm thử chiều rộng khung nhìn thiết bị di động;796- chụp ảnh màn hình nếu tầng hiển thị trực quan có thay đổi.797798Nếu không thể xác minh bằng trình duyệt, hãy nêu rõ điều đó. Đừng trình bày kết quả `typecheck` như là bước xác minh trực quan giao diện.799800---801802# 29. Git và Cây thư mục Làm việc (Working Tree)803804Trước khi thực hiện thay đổi, hãy kiểm tra trạng thái hiện tại:805806```bash807git status --short808```809810Quy tắc:811812- không hoàn tác (revert) các thay đổi của người khác nếu không có yêu cầu rõ ràng;813- không sử dụng các câu lệnh mang tính phá hủy nếu không có sự cho phép rõ ràng;814- không thực hiện việc tái cấu trúc mã (refactoring) không liên quan;815- không tự động tạo commit trừ khi người dùng yêu cầu;816- không thay đổi ký tự xuống dòng (line endings) hoặc định dạng lại toàn bộ dự án một cách không cần thiết.817818---819820# 30. Báo cáo Cuối cùng821822Trong phản hồi cuối cùng, hãy nêu rõ:823824- những gì đã thay đổi;825- những tệp nào là quan trọng;826- những bước kiểm tra nào đã được chạy;827- những gì không thể xác minh được;828- những rủi ro nào vẫn còn tồn đọng.829830Giữ cho báo cáo ngắn gọn và trung thực.831