---
url: https://docs.nemo12.com/design-system/card-patterns.md
description: >-
  Thư viện card pattern cố định cho lesson (08.10.2026): mỗi loại thông tin vẽ
  bằng đúng một pattern, khoảng cách thoáng, trình bày đa dạng.
---

# Card patterns (SRC-1298)

Chủ dự án 08.10.2026: nội dung lesson phải tách thành **các card riêng**, khoảng cách rộng cả trong
card lẫn giữa các card để đọc thoáng; cách trình bày trong card phải đa dạng; và **mọi loại thông tin
được vẽ bằng đúng một pattern** trong một thư viện cố định, không vẽ tuỳ hứng. Cùng ngày chủ dự án
nhấn thêm: nội dung kiểu "em gõ X, AI trả lời Y" (Lượt 1 / Lượt 2) phải có pattern hội thoại riêng.

Trang này là một phần của [DS-001](./index.md), đứng cạnh [Information Hierarchy](./hierarchy.md)
(thứ bậc chữ, một h1, một nút tô đầy) và [Guided Journey](./guided-journey/index.md) (cấu trúc journey).
Áp cho mọi màn AI Teen (lesson, unit) và mọi màn mới dựng nội dung học.

## 1. Luật

* **CP-1. Một loại thông tin, đúng một pattern.** Bảng §3 là bảng tra bắt buộc. Loại thông tin chưa có
  trong bảng thì thêm một dòng ở đây và một id trong `patterns.ts` trước, rồi mới vẽ.
* **CP-2. Một mapping duy nhất cho khối bài học.** `apps/learn/src/aiTeen/blockPatterns.ts` quyết định
  khối nào thành pattern nào (`BLOCK_PATTERNS` là kiểu ánh xạ trên `LessonBlock["kind"]`, nên thêm
  một `kind` mà không khai pattern là lỗi kiểu; `classifyBlock` là `switch` vét cạn). Component vẽ
  (`LessonBlockCard.tsx`) không tự quyết.
* **CP-3. File nội dung giữ chữ trơn.** Cấu trúc (lượt hội thoại, bước, cột so sánh, khung) được phân
  tích lúc vẽ từ quy ước chữ có sẵn (§4), có unit test chạy trên mọi khối thật. Tác giả không phải
  viết JSON.
* **CP-4. Nhịp khoảng cách.** Giữa các card `gap-6`, từ `sm` là `gap-8` (`STACK`); trong card `p-5
  gap-4`, từ `sm` là `p-6 gap-5` (`CARD_PAD`). Một phần đọc là một CHUỖI card, không phải một cột chữ dài.
* **CP-5. Mọi card mang `data-pattern="<id>"`** để test và audit thấy pattern nào vẽ thông tin nào.
* **CP-6. Chữ theo [hierarchy.md](./hierarchy.md):** nhãn trên đầu card là `T.meta` (không phải kicker),
  tiêu đề card là h3 `T.card`; chỉ `intro`, `next` và kết quả Kiểm tra mang h2 của bước. Card lồng tối
  đa một cấp (ô bên trong card là nền nhạt, không phải card thứ hai có bóng).
* **CP-7. Màu chỉ từ token.** Viền trái `accent` cho thứ cần chú ý, nền `muted/40` cho thứ phụ trợ,
  `destructive/5` cho cảnh báo, `success/10` cho "Thật ra". Không màu thô, không giá trị tuỳ ý.

## 2. Thư viện (18 pattern)

Mã nguồn: `apps/learn/src/components/cards/patterns.ts` (danh sách, mục đích, khoảng cách) và
`Cards.tsx` (một component mỗi pattern, cùng một vỏ `PatternCard`).

| Pattern | Mục đích | Cấu tạo | Màu, nhấn | Đáp ứng (375px → desktop) |
| --- | --- | --- | --- | --- |
| `intro` | Câu hỏi dẫn dắt của lesson và một câu tóm tắt | h2 câu hỏi + đoạn tóm tắt | viền trái accent, nền `accent/5` | một cột |
| `outcomes` | Lesson / unit outcomes | h3 + danh sách có dấu tick | trơn, tick accent | một cột |
| `output` | Thứ em cầm ra khỏi lesson | nhãn "Em sẽ cầm ra" + một câu chữ lớn + dòng meta | nền `muted/40` | một cột |
| `concepts` | Key concept của lesson | h3 + chip (bấm mở giải thích) | trơn | chip xuống dòng |
| `prose` | Chữ đọc | h3 tuỳ chọn + đoạn ngắn; dòng `- ` thành gạch đầu dòng | trơn | một cột, `max` đo theo cột chính |
| `example` | Ví dụ kể bằng lời | nhãn "Ví dụ" + h3 + đoạn | nền `muted/40` | một cột |
| `chat` | Hội thoại em gõ / AI trả lời, một hay nhiều lượt | nhãn "Ví dụ hỏi đáp" + h3; mỗi lượt một ô có nhãn "Lượt n"; bong bóng em (phải, nền `accent/10`, "Em gõ") và AI (trái, viền, "AI" + nhãn "Minh hoạ do Nemo viết"); danh sách đánh số trong câu AI thành `<ol>` | accent cho nhãn lượt | lượt xếp chồng; từ `md` hai lượt đứng cạnh nhau (`data-variant="compare"`) |
| `compare` | Hai bản đặt cạnh nhau | h3 + 2 ô có nhãn | ô nền `background` có viền | xếp chồng; từ `md` hai cột |
| `steps` | Quy trình, chuỗi vòng | h3 + dòng thời gian đánh số tròn | số trên nền `accent/10` | một cột |
| `framework` | Khung có 2-4 phần có tên (vd 4 phần của một prompt) | h3 + ô gạch, mỗi ô nhãn + một câu | ô nền `accent/5` | một cột; từ `sm` lưới 2 cột |
| `prompt` | Prompt mẫu để chép | nhãn "Prompt mẫu" + h3 + khối chữ đơn cách + nút Chép | khối nền `muted/40` | khối xuống dòng, không cuộn ngang |
| `callout` | Mẹo / cảnh báo / giới hạn | biểu tượng + nhãn ("Mẹo", "Cẩn thận", "Giới hạn") + đoạn | tip: viền accent · warning: viền + nền destructive · limit: nền muted | một cột |
| `misconception` | Hiểu lầm và sự thật | hai ô "Hiểu lầm" (X) → "Thật ra" (tick) | `destructive/5` và `success/10` | xếp chồng; từ `md` hai cột |
| `reflection` | Câu hỏi để em tự nghĩ | biểu tượng + "Câu hỏi cho em" + câu hỏi + ô ghi chú | nền `muted/40` | một cột |
| `checklist` | Bài làm tay có danh sách tự kiểm | nhãn "Câu n · Thực hành" + đề + gợi ý + ô tick | trơn | một cột |
| `quiz` | Câu hỏi có phương án (cổng đọc, Kiểm tra, tự chấm rubric) | nhãn + câu hỏi + các nút chọn | chọn: viền primary | một cột; rubric 5 mức thành lưới từ `lg` |
| `result` | Điểm, kết quả kiểm tra, giải thích, bằng chứng | h2/nhãn + điểm + việc tiếp | đạt: viền accent | một cột |
| `next` | Đường đi tiếp (lesson sau, Practice) | h2 + meta + nút | trơn | nút xuống dòng ở 320px |

## 3. Bảng tra: loại thông tin → pattern bắt buộc

| Loại thông tin (AI Teen lesson và unit) | Pattern |
| --- | --- |
| Câu hỏi dẫn dắt + tóm tắt lesson (Bắt đầu · Câu hỏi) | `intro` |
| Lesson Outcomes, Unit Outcomes dạng danh sách | `outcomes` |
| "Em sẽ cầm ra", bước Đúc kết · Sản phẩm, "Em sẽ làm ra sản phẩm nào?" của unit | `output` |
| Concept chip của lesson, "Em sẽ học được gì?" của unit | `concepts` |
| Khối `text` thường | `prose` |
| Khối `example` kể bằng lời | `example` |
| Khối `example` theo quy ước "Em gõ / AI có thể trả lời" (Lượt 1/2 …) | `chat` |
| Khối `example` có hai đầu mục VIẾT HOA (TRƯỚC / SAU …) | `compare` |
| Khối `text` / `example` có ≥3 dòng "1." / "Bước n" / "Vòng n" liên tiếp; danh sách activity khi lesson chưa có bài | `steps` |
| Khối `text` có 2-4 dòng liên tiếp "Nhãn ngắn: nội dung" (khung 4 phần của prompt …) | `framework` |
| Khối `prompt` | `prompt` |
| Khối `tip` (mẹo / cảnh báo / giới hạn tự nhận theo từ khoá) | `callout` |
| Khối có "Hiểu lầm: … / Thật ra: …" | `misconception` |
| "Câu hỏi cho em: …" sau một hội thoại, khối `text` tiêu đề "Em tự rút ra …", câu Reflect của unit, ô "Sản phẩm của em" ở Assess | `reflection` |
| Bài thực hành có checklist (Practice) | `checklist` |
| Câu kiểm tra nhanh của cổng đọc, câu Kiểm tra, tự chấm rubric ở Assess | `quiz` |
| Tổng điểm cổng đọc, giải thích sau khi qua, "Em đúng k/N câu", thẻ "Cần ôn", bằng chứng tự chấm, "Unit đã xong" | `result` |
| Lesson tiếp theo / trước, sang Practice, thông báo "không có câu kiểm tra" | `next` |

Nút "Xong bước này" và thanh "Sang …" cuối mỗi bước là điều hướng của Guided Journey, không phải nội
dung, nên đứng ngoài card ([guided-journey.md](./guided-journey/index.md)).

## 4. Quy ước chữ mà bộ phân tích đọc

| Quy ước trong `body` | Thành |
| --- | --- |
| Dòng `LƯỢT n` / `Lượt n` đứng riêng | nhãn một lượt mới |
| Dòng bắt đầu `Em gõ:` hoặc `Prompt:` | bong bóng của em (bỏ ngoặc kép bọc ngoài) |
| Dòng bắt đầu `AI có thể trả lời` (có thể kèm `kiểu như`, `(…)`) | bong bóng AI; các dòng ngay sau nối vào đến dòng trống |
| Dòng `(… minh hoạ …)` đứng riêng | bỏ, thay bằng nhãn "Minh hoạ do Nemo viết" trên mỗi câu AI |
| Dòng bắt đầu `Câu hỏi cho em:` | card `reflection` riêng ngay sau hội thoại |

Test giữ: `apps/learn/src/aiTeen/blockPatterns.test.ts` (mọi kind có pattern, mọi khối thật rơi vào
pattern kind ấy cho phép, mọi khối dùng quy ước hội thoại thành `chat` không sót dấu). E2E
`aiTeen.spec.ts` kiểm `data-pattern`, khoảng cách giữa card và 375px không tràn ngang.
