Skip to content

Viết tài liệu

Site này là VitePress trong thư mục docs/, build cùng monorepo và deploy từ main. Tài liệu viết theo kiểu docs-as-code: ví dụ là file chạy thật, bảng API sinh từ source, CI chặn khi tài liệu lệch code (RFC 0003).

Xem trước

bash
pnpm turbo run dev --filter=docs      # http://localhost:5173/v2/ — turbo build @tasco/* trước
pnpm turbo run build --filter=docs    # như CI: báo link hỏng, thiếu TSDoc

Không có bản xem trước theo MR — người review tự chạy lệnh trên.

Trang đặt ở đâu

KhuThư mụcNgười đọcLoại nội dung
Bắt đầudocs/start/Team sản phẩm mớiHọc: làm theo từng bước
Hướng dẫndocs/guide/Team sản phẩmLàm: giải quyết một việc cụ thể
Componentdocs/components/Team sản phẩmTra cứu + ví dụ cho từng component
UI/UXdocs/design/Designer, BA, devHiểu: quy tắc giao diện
Tra cứudocs/reference/Mọi ngườiTra cứu: bảng, chữ ký, không giải thích dài
Phát triển coredocs/core/Team coreQuy trình và kiến trúc nội bộ

Mỗi trang một việc. Trang vừa hướng dẫn vừa liệt kê đủ mọi tuỳ chọn thì tách phần liệt kê sang khu Tra cứu và dẫn link.

Thêm trang mới: thêm mục vào sidebar trong docs/.vitepress/config.ts. Đổi hoặc xoá URL đã phát hành: thêm dòng chuyển hướng vào docs/deploy/_redirects — link cũ trong app, CLAUDE.md của sản phẩm và bookmark phải còn vào được.

Giọng văn

  • Tiếng Việt, câu ngắn, chủ động, nói thẳng việc cần làm: "Đặt NUXT_SESSION_SECRET ở production", không "Bạn nên cân nhắc việc thiết lập…".
  • Viết cho người đang làm việc: câu đầu trang nói trang giúp làm gì; bảng cho thông tin tra cứu; checklist cho việc cần rà soát.
  • Nói vì sao khi quy tắc không hiển nhiên — người đọc làm đúng lâu hơn khi hiểu lý do.
  • Không viết điều code không làm. Chưa chắc thì đọc source hoặc chạy thử trước khi viết.
  • Thuật ngữ: giữ tên API và khái niệm kỹ thuật phổ biến bằng tiếng Anh trong code (runtimeConfig, middleware, slot); dùng từ Việt cho khái niệm chung (phân quyền, phiên đăng nhập, bộ lọc, trang danh sách).

Cấu trúc trang

  • Một # tiêu đề trang; mục ##, ###. Tiêu đề là danh từ hoặc việc cần làm, không đánh số.
  • Mỗi mục ##/### đọc được khi đứng riêng: search_docs của @tasco/mcp trả cho AI từng mục, không kèm phần trên của trang. Tiêu đề mục nói rõ chủ đề ("Chặn nút theo quyền", không phải "Cách 2"); câu đầu không viết "như trên".
  • Trang kết thúc bằng mục Liên quan, 2–4 link kèm một câu vì sao nên đọc.
  • Link trong site viết đường dẫn tuyệt đối, không đuôi .md: [Chặn theo quyền](/guide/auth/guard). Không thêm /v2/ — VitePress tự thêm base.
  • Ghi chú nổi bật dùng khối của VitePress: ::: info, ::: tip, ::: warning, ::: danger. Tối đa một, hai khối mỗi trang — nhiều khối thì không khối nào nổi bật.

Trang component

Mọi trang trong docs/components/<tên-kebab>/index.md theo cùng thứ tự:

  1. Mở đầu — một câu component làm gì; Dùng khi / Không dùng khi (kèm component nên dùng thay).
  2. Ví dụ — mỗi trường hợp một mục có demo: cơ bản, từng biến thể chính, từng trạng thái (disabled, loading, rỗng, lỗi), ít nhất một ví dụ ghép thực tế (useTable, CForm, phân quyền).
  3. API<ApiTable component="CTên" />.
  4. Giao diện — biến CSS đang dùng (<TokenTable prefix="…" />), class BEM được phép ghi đè.
  5. Khả năng tiếp cận — bàn phím, nhãn, focus.
  6. Lưu ý — lỗi hay gặp, lỗi [@tasco/ui] component tự ném khi dùng sai.
  7. Liên quan.

Trang xong khi: bảng API không còn ô "Thiếu mô tả"; demo chỉ dùng C* và composable công khai; một người không viết trang làm theo được.

Claude Code: skill /doc-component <Tên> đọc source, test, story rồi soạn nháp trang và demo theo đúng thứ tự trên. Nháp là điểm bắt đầu — người mở MR đọc lại từng câu và chạy thử từng demo.

API mới trong một major

Docs /v2/ build từ main, nên mô tả cả API đã merge mà chưa phát hành — người đọc còn ở bản cũ hơn sẽ gọi hàm không tồn tại. Mục mô tả API thêm trong một bản minor gắn nhãn phiên bản ở heading, hoặc ở cuối dòng bảng khi API chỉ chiếm một dòng:

md
### Bằng `ref` <Badge type="tip" text="từ 2.1" />
  • Số phiên bản là bản minor sẽ chứa thay đổi (bản đang phát hành cộng một minor), không đổi sau khi phát hành.
  • Chỉ gắn cho API người dùng framework gọi trực tiếp; thay đổi nội bộ và trang khu Phát triển core thì không.
  • Anchor của heading không đổi vì VitePress bỏ qua thẻ khi sinh anchor; @tasco/mcp đưa chữ trong nhãn vào tên mục để AI biết API cần bản nào.

Demo chạy thật

md
<demo src="./demos/closable.vue" />

Plugin demo-plugin.ts render file .vue chạy thật và hiện code đọc từ chính file đó, nên code hiển thị không thể lệch code chạy.

Quy tắcVì sao
File đặt trong demos/ cạnh trang, tên kebab-caseDemo.vue nạp mọi **/demos/*.vue
Chỉ dùng C*, primitive và composable export từ @tasco/ui, @tasco/utilsESLint của docs chặn ant-design-vue như ở app sản phẩm
Không dùng API của Nuxt (useApi, definePageMeta, navigateTo)VitePress không có runtime Nuxt — ví dụ cần Nuxt viết thành code block
Comment đầu file nói demo minh hoạ điều gìNgười đọc bấm "Xem code" thấy ngay ý chính
Ví dụ dài hơn 10 dòng là file demo, không viết trong markdownĐược vue-tsc và ESLint kiểm
Component thương hiệu dùng trong demo phải có trong docs/.vitepress/env.d.tsvue-tsc kiểm demo như code app

Demo render trong <ClientOnly> vì antdv render phía server kém; chữ và code block của trang vẫn render tĩnh nên tìm kiếm không bị ảnh hưởng.

Component dùng được trong markdown

Cú phápHiển thị
<demo src="./demos/x.vue" />Demo chạy thật + nút xem code
<ApiTable component="CTag" />Props, sự kiện, slot từ component-meta.json
<TokenTable prefix="color-success" />Token theo tiền tố; :prefixes="['card-', 'form-']" nhiều tiền tố; on="page" đo tương phản trên nền trang
<ContrastPairs />Bảng cặp chữ/nền từ contrastPairs

Component C* cũng dùng thẳng được trong markdown (bọc <ClientOnly>), như ở trang Tổng quan bộ C*.

Phần tự sinh — sửa nguồn, không sửa trang

Nội dungNguồn cần sửa
Bảng API componentJSDoc của prop, defineEmits, defineSlots trong packages/ui/src/components/*.vue
Trang Tra cứu package TSTSDoc trong packages/{composables,utils,theme,ai}/src
Bảng token, cặp tương phảnpackages/theme/src/tokens.ts, contrast-pairs.ts
ChangelogChangeset của từng MR
Quy ước code, Nợ kỹ thuậtCLAUDE.md
Dữ liệu search_docs, read_doc, get_component của @tasco/mcpChính trang docs và JSDoc — sinh lại khi build package

Nhúng một đoạn của CLAUDE.md (hoặc file markdown khác) vào trang: bọc đoạn đó bằng <!-- #region ten-vung --><!-- #endregion ten-vung --> trong file nguồn, rồi đặt dòng include trong trang, đường dẫn tính từ file trang:

md
<!--@include: ../../duong-dan/file-nguon.md#ten-vung-->

Include chạy cả trong code block

VitePress thay mọi dòng @include trước khi đọc markdown, kể cả dòng nằm trong code block. Muốn hiển thị cú pháp include như ví dụ trên thì dùng đường dẫn không tồn tại — VitePress giữ nguyên dòng khi không đọc được file.

Bẫy khi viết markdown cho VitePress

Trang markdown được biên dịch thành template Vue:

  • {{ … }} bị hiểu là biểu thức Vue ở mọi chỗ ngoài code block — kể cả trong code inline. Không có biến trùng tên thì trang hiện rỗng, có ký tự lạ thì build hỏng. Bọc đoạn văn đó bằng khối ::: v-pre:::.
  • Thẻ lạ ngoài code (<Tên>, <đối tượng>) bị hiểu là component. Viết trong `code` hoặc bỏ dấu <>.
  • Bảng có ký tự | trong code: viết \|.

Quy tắc nội dung — site công khai

Site docs không đặt sau đăng nhập, nên:

  • Không bao giờ đưa token, secret, mật khẩu, cookie thật, kể cả đã hết hạn.
  • Host IAM, gateway, backend trong ví dụ dùng example.com (https://iam.example.com/cop). Chỉ ghi host thật khi người đọc bắt buộc phải cấu hình đúng host đó — hiện chỉ có registry GitLab.
  • Không đưa tên người, số điện thoại, dữ liệu khách hàng thật vào ví dụ và ảnh chụp.

CI kiểm tra gì

Kiểm traFail khi
pnpm lint (docs)Demo import ant-design-vue, vi phạm rule Vue
pnpm typecheck (docs)Demo sai kiểu, dùng component chưa khai trong env.d.ts
pnpm build (docs)Link nội bộ hỏng, export package TS thiếu TSDoc, template Vue trong markdown lỗi
pnpm test (docs)Script sinh changelog hoặc trang API sai
docs-coverage --strictComponent thiếu trang hoặc thiếu mô tả; khoá runtimeConfig chưa có trong trang tra cứu
check-mcp-linksLink docs trong dữ liệu @tasco/mcp trỏ tới trang hoặc mục không có trong bản build

Link tới mục trong trang (#ten-muc) không được kiểm — bấm thử trước khi mở MR. Mã mục là tiêu đề bỏ dấu viết thường nối gạch ngang, riêng chữ đ giữ nguyên; tiêu đề có đ thì đặt mã riêng: ## Quyền đóng góp {#quyen-dong-gop}.

Checklist MR tài liệu

Mục "Tài liệu" và "Ảnh chụp" trong template MR (.gitlab/merge_request_templates/Default.md) là bản rút gọn của danh sách này — MR đổi API công khai chỉ xong khi có JSDoc, trang docs, demo, changeset và mục Migration nếu breaking.

  • [ ] Trang ở đúng khu, có trong sidebar; URL đổi thì có _redirects.
  • [ ] Đã đọc source hoặc chạy thử điều được viết.
  • [ ] Demo chạy ở cả chế độ sáng và tối; MR đổi giao diện có ảnh chụp trước/sau.
  • [ ] Mục ##/### đọc được khi đứng riêng.
  • [ ] Không có host thật (trừ registry), token, dữ liệu cá nhân.
  • [ ] pnpm turbo run build --filter=docs không lỗi.

Liên quan