Phong cách chung: màu đặc, nét rõ, không dùng màu chuyển sắc và bóng mờ. Hệ chỉ có một màu hành động: cta bằng chính primary.
1. Màu
Bảng màu
Nguồn duy nhất là design-system/tokens.json ở gốc kho. Bảng dưới đây in ra đúng token của bản dựng này — sửa màu thì sửa tokens.json rồi chạy npm run sync:tokens ở cả ba app trong cùng một PR, đừng sửa theme/tokens.css.
| Token | Mã màu | Dùng cho |
|---|---|---|
--c-primary | #1c3179 | Nút chính, liên kết, tab đang chọn, viền focus. Nền này chỉ cõng chữ trắng. |
--c-primary-deep | #111d47 | Đỉnh dải màu đầu trang, nền các màn phủ kín xanh. |
--c-primary-mid | #2e4fb0 | Đáy dải màu đầu trang — vẫn đủ tương phản cho chữ trắng. |
--c-primary-soft | #2e4fb0 | Chỉ để trang trí: vòng tròn, nét vẽ, đoạn đang chạy của thanh tiến trình. Không cõng chữ. |
--c-primary-veil | #c2cdf2 | Thẻ và pill sáng nổi trên nền xanh. Chữ trên nó là primaryDeep, không phải trắng. |
--c-on-primary | #ffffff | Chữ trên nền primary / primaryDeep / primaryMid. |
--c-on-primary-soft | #dce3fa | Chữ phụ trên nền primary. |
--c-line-on-primary | #9db2f2 | Đường kẻ trên nền primary. |
--c-cta | #e8b23a | Nền nút gọi hành động. Mini App chỉ có một màu hành động nên cta bằng primary; bề mặt máy tính — Website và Web quản trị — có màu riêng (amber), khai ở desktopSurface. |
--c-on-cta | #1c3179 | Chữ trên nút gọi hành động. |
--c-link | #1c3179 | Màu chữ của liên kết trên nền xám — primary không đủ tương phản ở đó. |
--c-accent | #c2352b | Chỉ lỗi, từ chối, huỷ. Không dùng để nhấn mạnh. |
--c-success | #0b7a43 | Đã duyệt, voucher còn hạn. Luôn kèm chữ và icon. |
--c-success-bright | #19a35c | Dấu thành công đặt trên nền xanh đậm — `success` ở đó chỉ tương phản 2,20, chìm hẳn. |
--c-warn-bg | #f4efe3 | Nền nhãn chờ xử lý. |
--c-warn-ink | #5b4a1f | Chữ trên nhãn chờ xử lý. |
--c-reward | #5b4a1f | Chữ điểm thưởng trên thẻ quiz, kết quả quiz. |
--c-ink | #1a1b22 | Chữ chính. |
--c-ink-soft | #444857 | Chữ phụ. |
--c-muted | #555a6b | Chữ mờ, chú thích. |
--c-note | #6b5a2a | Chữ của nội dung chưa thật. |
--c-tag-text | #5b4a1f | Chữ trên nhãn trung tính. |
--c-surface | #ffffff | Nền thẻ. |
--c-surface-alt | #f4efe3 | Nền trang, nền thẻ phụ. |
--c-surface-tint | #dce3fa | Nền mục đang mở, đáp án đã chọn. |
--c-tile-violet | #f4efe3 | Ô số liệu. |
--c-tile-mint | #dce3fa | Ô số liệu. |
--c-border | #e3e5ee | Viền thẻ, đường kẻ mảnh. |
--c-track | #ddd3bc | Nền thanh tiến trình. |
Độ tương phản chữ trên nền (tính từ token, theo WCAG 2.1)
| Chữ | Nền | Tỉ lệ |
|---|---|---|
onPrimary | primary | 11.90AAA |
onPrimary | primaryDeep | 16.27AAA |
onPrimarySoft | primaryDeep | 12.72AAA |
onPrimary | primaryMid | 7.34AAA |
primaryDeep | primaryVeil | 10.31AAA |
onCta | cta | 6.15AA |
ink | surface | 17.16AAA |
inkSoft | surface | 9.09AAA |
muted | surface | 6.86AA |
link | surface | 11.90AAA |
link | surfaceAlt | 10.37AAA |
note | surfaceAlt | 5.86AA |
accent | surface | 5.47AA |
onPrimary | accent | 5.47AA |
success | surface | 5.41AA |
warnInk | warnBg | 7.49AAA |
lineOnPrimary | primaryDeep | 7.81AAA |
Quy tắc màu
- Nền
primarychỉ cõng chữ trắng.primarySoftkhông cõng chữ nào, và trên nềnprimarythì nó gần như biến mất — nét vẽ trang trí nằm trên nền xanh dùngctahoặcaccent. - Liên kết nằm trên nền trắng hoặc nền xám dùng
link, không dùngprimary:primarychỉ đủ tương phản khi làm nền hoặc viền. - Dải màu đầu trang đi từ
primaryDeepxuốngprimaryMid, và chỉ một dải đó. Đậm ở trên vì thanh trạng thái của máy nằm chồng lên đỉnh dải; cả hai bậc đều cõng được chữ trắng. accenttrên Website là màu nhận diện kiểu Đông Hồ: dải doanh nghiệp, số thứ tự bước, bóng đổ sau khung điện thoại. Không dùng nó làm màu chữ của liên kết — việc đó là củalink. Trong Mini App,accentchỉ dành cho lỗi, từ chối và huỷ.- Màu hành động của bề mặt máy tính là vàng nghệ:
ctalà#e8b23a, chữ trên nút làonCta— navy, không bao giờ trắng. Mỗi tầm mắt chỉ một nút vàng; việc phụ đi bằng nút viền. primaryVeillà pill sáng nổi trên nền xanh — chữ trên nó làprimaryDeep, không bao giờ là trắng.- Đổi mã màu trong
tokens.jsonthì đọc lại bảng tương phản ở trên — bảng tự tính lại, cặp nào tụt xuống “không đạt” là chặn.
2. Chữ
| Token | Font | Cỡ chữ (máy tính → điện thoại) | Độ đậm | Khoảng cách dòng |
|---|---|---|---|---|
display-1 | Phudu | clamp(2.4rem, 5.2vw, 4.4rem) | 800 | 1.18 |
display-2 | Phudu | clamp(2rem, 3.4vw, 3rem) | 800 | 1.18 |
display-3 | Phudu | 26px | 800 | 1.15 |
heading | Be Vietnam Pro | 18–22px | 600–700 | 1.35 |
lead | Be Vietnam Pro | clamp(1.05rem, 1.5vw, 1.25rem) | 400 | 1.6 |
body | Be Vietnam Pro | 16px | 400 | 1.6 |
button-lg | Be Vietnam Pro | 17px | 700 | 1 |
button | Be Vietnam Pro | 15px | 600–700 | 1 |
small | Be Vietnam Pro | 14px | 400–600 | 1.5 |
caption | Be Vietnam Pro | 13px | 400 | 1.5 |
micro | Be Vietnam Pro | 11–12px | 400–600 | 1.3 |
- Khoảng cách dòng của tiêu đề tối thiểu là 1.18. Thấp hơn thì chữ có hai dấu chồng nhau (
ộ,ặ) bị cắt dấu và dễ đọc sai. - Không viết hoa toàn bộ chữ trong nhãn hay tiêu đề.
- Mỗi dòng chữ dài tối đa khoảng 40em.
- File font lưu cùng trang, không tải từ Google Fonts. Chỉ nạp các độ đậm có trong bảng, cắt gọn còn bộ ký tự
vietnamesevàlatin.
3. Bo góc
Bản thiết kế đang dùng 11 mức bo góc khác nhau. Khi chuyển sang code, gộp lại còn 6 mức:
| Token | Giá trị | Dùng cho | Gộp từ các giá trị trong bản thiết kế |
|---|---|---|---|
--radius-xs | 6px | Logo, nhãn nhỏ trong khung điện thoại, góc nhọn của bóng chat | 4, 6 |
--radius-md | 16px | Thẻ việc làm, thẻ trong điện thoại, thông báo, ô vuông icon | 10, 14, 16 |
--radius-lg | 24px | Thẻ bước, thẻ đối tác, khối doanh nghiệp, ảnh tin, bóng chat, ô QR | 18, 20, 22, 24 |
--radius-xl | 32px | Khung chat AI, khối “Mở Mini App” (24px ở ≤760px) | 28, 32 |
--radius-device | 34px | Chỉ khung điện thoại minh hoạ | 34 |
--radius-pill | 9999px | Mọi nút, chip lọc, tag | 999 |
- Khối lồng nhau: bo góc bên trong nhỏ hơn bên ngoài một bậc.
- Bóng chat:
--radius-lgở ba góc,--radius-xsở góc phía người nói. - Mọi nút đều bo tròn hai đầu (
--radius-pill), không có nút góc vuông.
4. Khoảng cách và bố cục
| Token | Giá trị |
|---|---|
| Container tối đa | 1240px, căn giữa |
| Lề ngang | 24px (mọi kích thước màn hình) |
| Section, desktop | 72–88px trên/dưới |
| Section, mobile ≤760px | 52–60px trên/dưới |
| Khoảng cách trong thẻ | 20–32px padding |
| Thang gap | 8 · 10 · 12 · 14 · 18 · 24 · 36 · 48 · 56 |
| Điểm chuyển bố cục | Chỉ một điểm: max-width: 760px |
Bố cục dùng flex-wrap và flex-basis, nên các khối tự xếp thành một cột trên điện thoại mà không cần thêm điểm chuyển.
5. Viền, bóng, focus
| Token | Giá trị | Dùng cho |
|---|---|---|
--border-border | 1.5px solid var(--c-border) | Thẻ trong khung điện thoại minh hoạ |
--border-control | 2px solid | Nút phụ, chip lọc |
--shadow-offset | 18px 18px 0 var(--c-accent) | Chỉ dùng cho khung điện thoại ở đầu trang |
--focus-ring | 3px solid var(--c-primary) | Mọi phần tử bấm được (:focus-visible) |
Trang không dùng bóng mờ. Chỉ có một bóng đổ màu accent đặc, đặt sau khung điện thoại ở đầu trang để tạo điểm nhấn. Dùng bóng này ở nhiều chỗ thì nó không còn nổi bật.
6. Nút
Mọi nút bo tròn hai đầu (--radius-pill), cao tối thiểu 44px, dùng font Be Vietnam Pro, không thêm mũi tên “→” ở cuối nhãn.
| Biến thể | Nền | Chữ | Viền | Cỡ chữ · chiều cao | Ví dụ |
|---|---|---|---|---|---|
| Primary | cta | on-cta, 700 | không | 17px · 52px | “Mở Mini App trên Zalo” |
| Primary-sm | cta | on-cta, 700 | không | 15px · 44px | Header, “Đăng tin tuyển dụng” |
| Secondary-on-dark | trong suốt | on-primary, 600 | 2px line-on-primary | 17px · 52px | “Xem việc làm mới” |
| Outline-light | surface | link, 600 | 2px primary | 15–16px · 44px | Nút tạm dừng, nút “Việc làm” ở thanh đáy |
| On-accent | surface | accent, 700 | không | 16px · 48px | “Liên hệ hợp tác” |
| Text link | không | link, 700, gạch chân | không | 17px | “Xem tất cả tin trên Mini App” |
| Icon | không | theo nền | không | 44×44 | Nút đóng thông báo (bắt buộc có aria-label) |
Trạng thái
- Rê chuột lên link: chữ chuyển sang màu đỏ.
- Focus: viền
--focus-ring. - Đang chọn hoặc đang bật: đặt
aria-pressed="true"và đổi giao diện theo trạng thái này. - Chưa nối chức năng: nút vẫn bấm được và hiện thông báo “Chưa hoàn thiện”. Không làm mờ nút và không để nút bấm mà không có phản hồi.
7. Component
Chip lọc
Bo tròn hai đầu, viền 2px màu primary. Chưa chọn: nền surface, chữ link. Đang chọn: nền primary, chữ on-primary, aria-pressed="true". Trên điện thoại, các chip nằm trên một hàng, vuốt ngang để xem tiếp, không xuống dòng.
Tag / nhãn
| Loại | Nền | Chữ | Ví dụ |
|---|---|---|---|
| Trạng thái | surface-alt | tag-text, 600, 14px, bo tròn hai đầu | “Sắp ra mắt” |
| Loại việc | surface (đặt trên nền surface-alt) | ink, 600, 13px, bo tròn hai đầu | “Toàn thời gian” |
| Minh hoạ | surface-alt | tag-text, 11px, --radius-xs | “Minh họa” |
Thẻ việc làm
Nền surface-alt, bo góc --radius-md, khoảng đệm 20px trên dưới và 22px hai bên. Tên công việc 18px, độ đậm 600. Nơi làm việc 14px màumuted. Nhãn loại việc nằm bên phải, xuống dòng khi trên điện thoại.
Thẻ bước (hành trình điểm thưởng)
Nền surface đặt trên nền surface-alt, bo góc --radius-lg, khoảng đệm 24px (trên điện thoại là 18px trên dưới, 20px hai bên). Số thứ tự bước dùng font Phudu cỡ 40px (28px trên điện thoại), màu accent. Bước đang chạy được đánh dấu bằng đồng xu nảy lên, không đổi nền. Dưới cùng là thanh tiến trình 3 đoạn: phần chưa chạy màu track, phần đã chạy màu accent.
Khối nền tối
Nền primary-deep, chữ on-primary. Ô thông tin bên cạnh bo góc --radius-lg, khối “Mở Mini App” bo góc --radius-xl.
Bóng chat (trợ lý AI)
Câu hỏi của người dùng: nền primary, chữ on-primary, căn phải. Câu trả lời: nền surface, chữ ink, căn trái. Cả hai nằm trong khối nền primary-deep bo góc --radius-xl.
Thông báo “Chưa hoàn thiện”
Nền primary-deep, bo góc --radius-md, nằm giữa, cách đáy màn hình 24px. Trên điện thoại cách đáy 84px để không bị thanh thao tác che. Dòng tiêu đề màu on-primary-soft, độ đậm 700; dòng lý do màu on-primary, cỡ 15px. Nút đóng 44×44px. Thông báo tự ẩn sau 4 giây và nằm trong vùng role="status" aria-live="polite".
Thanh thao tác ở đáy (chỉ hiện khi màn hình rộng từ 760px trở xuống)
Nền trắng, đường kẻ phía trên màu --color-border. Có hai nút cao 48px: “Việc làm” (kiểu Outline-light, chiếm 1 phần chiều ngang) và “Mở Mini App trên Zalo” (kiểu Primary, chiếm 2 phần). Thêm khoảng đệm env(safe-area-inset-bottom) ở dưới.
Icon
Icon là SVG dạng nét, đặt thẳng trong mã, nét dày 1.6–2.4, màu theo currentColor. Icon chỉ để trang trí thì đặt aria-hidden="true". Không dùng emoji làm icon.
Hình trang trí
Có hai hình: bông hoa tròn (một vòng tròn với 8 nét toả ra) và đường lượn sóng. Vẽ bằng nét primary-soft hoặc line-on-primary, chỉ đặt trên nền primary-deep. Hai hình này chỉ để trang trí, luôn đặt aria-hidden.
8. Chuyển động
| Token | Giá trị |
|---|---|
| Một vòng hành trình điểm thưởng | 9 giây, mỗi bước 3 giây, chạy lặp lại |
| Thông báo hiện ra | 0.2 giây, ease-out, trượt lên 12px |
| Thông báo tự ẩn sau | 4 giây |
| Cuộn tới mục | scroll-behavior: smooth |
Chỉ tạo chuyển động trên transform, opacity và background. Mỗi trang chỉ có một chỗ chuyển động chính, và không có khối nào tự trượt vào khi cuộn. Khi người dùng bật prefers-reduced-motion, tắt mọi chuyển động: hành trình hiện tĩnh, thanh tiến trình hiện đầy, cuộn trang nhảy thẳng tới vị trí.
9. CSS tokens
Không dán tay đoạn nào vào theme/tokens.css — file đó sinh ra từ design-system/tokens.json. Đoạn dưới đây là bản in của token đang hiệu lực, để đối chiếu:
/* theme/tokens.css — FILE SINH RA từ design-system/tokens.json */
:root {
--c-primary: #1c3179;
--c-primary-deep: #111d47;
--c-primary-mid: #2e4fb0;
--c-primary-soft: #2e4fb0;
--c-primary-veil: #c2cdf2;
--c-on-primary: #ffffff;
--c-on-primary-soft: #dce3fa;
--c-line-on-primary: #9db2f2;
--c-cta: #e8b23a;
--c-on-cta: #1c3179;
--c-link: #1c3179;
--c-accent: #c2352b;
--c-success: #0b7a43;
--c-success-bright: #19a35c;
--c-warn-bg: #f4efe3;
--c-warn-ink: #5b4a1f;
--c-reward: #5b4a1f;
--c-ink: #1a1b22;
--c-ink-soft: #444857;
--c-muted: #555a6b;
--c-note: #6b5a2a;
--c-tag-text: #5b4a1f;
--c-surface: #ffffff;
--c-surface-alt: #f4efe3;
--c-surface-tint: #dce3fa;
--c-tile-violet: #f4efe3;
--c-tile-mint: #dce3fa;
--c-border: #e3e5ee;
--c-track: #ddd3bc;
--r-xs: 6px;
--r-md: 16px;
--r-lg: 24px;
--r-xl: 32px;
}
/* app/globals.css nối chúng vào Tailwind, thêm vài hằng số riêng của Website */
@theme {
--color-primary: var(--c-primary); /* … đủ 28 vai trò */
--radius-md: var(--r-md);
}
:root {
--radius-device: 34px; /* khung điện thoại ở đầu trang */
--radius-pill: 9999px;
--container: 1240px;
--gutter: 24px;
--tap-min: 44px;
--border-control: 2px;
--shadow-offset: 18px 18px 0 var(--c-accent);
--focus-ring: 3px solid var(--c-primary);
--journey-cycle: 9s;
--notice-hide-after: 4s;
--lh-display: 1.18; /* thấp hơn mức này thì dấu tiếng Việt bị cắt */
--lh-body: 1.6;
}10. Nên và không nên
Nên
- Để nút nền đặc cho việc chính của màn, nút viền cho việc phụ.
- Đặt nhãn “Minh họa” hoặc “Dữ liệu mẫu” cạnh mọi nội dung chưa phải thật.
- Cho nút chưa nối chức năng hiện thông báo “Chưa hoàn thiện”.
- Kiểm tra mọi tiêu đề có dấu chồng (
ộ,ặ,ữ) ở cỡ lớn nhất.
Không nên
- Màu chuyển sắc, bóng mờ, hiệu ứng phát sáng, hình 3D.
- Đặt chữ lên nền
primary-soft, hoặc liên kết màuprimarytrên nền xám. - Khoảng cách dòng của tiêu đề dưới 1.18, hoặc viết hoa toàn bộ.
- Dùng mức bo góc nằm ngoài 6 mức ở mục 3.
- Tự vẽ lại huy hiệu Đoàn thay vì dùng file gốc.
- Dùng emoji làm icon.