Skip to content

Dọn màu mentors.conan.school về token: 09.09.2026 ​

Site thứ sáu về 0. 53 → 0. Toàn bộ 53 chỗ nằm trong một file: app/globals.css.

mentors là kiểu vi phạm thứ ba, và là kiểu khó thấy nhất trong sáu lượt.

Màu đúng, tên sai ​

Ba kiểu đã gặp cho tới nay:

SiteKiểu
adminhệ thiết kế chưa từng được nạp, và bảng màu tự khai sai giá trị
playhệ thiết kế đã nạp, token vẫn ở đó, không ai dùng
mentorshệ thiết kế chưa nạp, nhưng bảng màu tự khai ĐÚNG giá trị, chỉ dưới tên riêng

globals.css mở đầu bằng một khối :root của riêng nó:

css
:root {
  --ink: #111111;   --muted: #6b6b6b;   --accent: #d94f2b;
  --accent-soft: #fff7ed;  --success: #10b981;  --amber: #f59e0b;
  --rose: #f43f5e;  --cyan: #06b6d4;  --sidebar: #111111;  --line: #e8e8e8;  --soft: #f7f7f7;
}

Đối chiếu với theme.css: 9 trên 11 giá trị trùng chính xác. Ai viết file này rõ ràng đã đọc hệ thiết kế và chép đúng.

Nhưng vì chúng là bản sao dưới tên khác, đổi --color-primary trong theme.css vẫn không lan tới mentors. Đó chính xác là điều LUẬT MÀU sinh ra để chặn, chỉ ở dạng khó phát hiện nhất: nhìn qua thì đúng thương hiệu, thậm chí trông có kỷ luật hơn mã dùng slate-500, mà vẫn là một hòn đảo màu riêng.

Bài học: "màu trông đúng" không phải bằng chứng token đang được dùng. Phép thử duy nhất là grep xem file có @import theme.css không.

Cách sửa: nối tên riêng thành bí danh, giữ 250 chỗ dùng nguyên vẹn ​

Đổi tên --ink → --color-foreground ở cả 297 dòng là một diff khổng lồ và rủi ro. Thay vào đó, nạp nguồn dùng chung rồi biến các tên cũ thành bí danh:

css
@import "../../packages/design-system/theme.css";

:root {
  --ink: var(--color-foreground);        --muted: var(--color-muted-foreground);
  --line: var(--color-border);           --soft: var(--color-muted);
  --accent: var(--color-primary);        --accent-soft: var(--color-accent);
  --sidebar: var(--color-surface-inverse);  --on-dark: var(--color-surface-inverse-foreground);
  --success: var(--color-emerald);       --amber: var(--color-amber);
  --rose: var(--color-rose);             --cyan: var(--color-cyan);
}

Mọi chỗ dùng var(--accent) giữ nguyên; giá trị giờ đến từ theme.css. Diff gọn, và nguồn sự thật đã nối lại.

Thêm hai bí danh mới cho những chỗ trước đây viết cứng: --accent-ink (chữ trên nền accent) và --on-dark (chữ trên bề mặt nghịch đảo).

Đổi màu thật, nói cho đúng ​

Đo bằng cách biên dịch CSS thật trước và sau, rồi phân giải hết chuỗi var():

biếncũmới
--ink, --accent, --accent-soft, --sidebar, --muted, --success, --amber, --rose, --cyan9 giá trị giống hệt
--line#e8e8e8#f2f2f2đường kẻ nhạt đi một chút
--soft#f7f7f7#f2f2f2nền phụ xám hơn một chút

Chỉ hai thay đổi, cả hai đều là dịch về phía token dùng chung.

Còn 42 mã màu viết cứng ngoài khối :root ​

Khối :root chỉ là 11 dòng; 42 chỗ còn lại nằm rải trong 297 dòng CSS. Ba nhóm:

  • rgba(255,255,255,X) trên nền tối (24 chỗ) → color-mix(in srgb, var(--on-dark) N%, transparent). Đã đối chiếu 1:1 từng độ mờ: .1→10%, .42→42%, .85→85%… Chỉ một chỗ lệch, .025→2% (0,5% của trắng trên nền đen, không nhìn thấy được).
  • Viền "đậm" của ô nhập (#d8d8d8, #dcdcdc, #dedede, #d6d3d1) → không quy thẳng về --color-border (#f2f2f2) vì như thế ô nhập gần như mất viền. Dùng color-mix(in srgb, var(--ink) 15%, transparent), giữ đúng sức nặng thị giác và phái sinh từ token.
  • Sắc phái sinh: #a93d21 (hover nút chính) → opacity: .9; #ff744f (số trên thẻ tối) → color-mix(var(--accent) 72%, var(--on-dark)); #047857/#ecfdf5 (trạng thái thành công) → pha từ --success.

Và rồi: mentors chưa bao giờ deploy được ​

Định dựng site để xem kết quả thì phát hiện npm run build không chạy, và không phải vì thay đổi của lượt này. deploy-mentors chỉ từng chạy hai lượt (11.08.2026 và 03.09.2026), và cả hai đều failure, luôn cùng một lỗi. Site này chưa bao giờ lên production từ CI.

Hai nguyên nhân độc lập, cả hai đều là loại "đọc mã thì không thấy":

1. .gitignore nuốt một file NGUỒN ​

vite.config.ts mở đầu bằng:

ts
import { sites } from "./build/sites-vite-plugin";

mentors.conan.school/build/sites-vite-plugin.ts là một plugin Vite viết tay (44 dòng, đóng gói metadata và migration sau khi build). Nhưng .gitignore của monorepo có luật chung:

# Build outputs
dist/
build/          ← nuốt luôn mentors.conan.school/build/

Nên file nguồn đó chưa bao giờ vào git. Máy người viết có nó nên mọi thứ chạy; CI checkout sạch thì không, và chết ngay ở bước nạp config:

[UNRESOLVED_IMPORT] Could not resolve './build/sites-vite-plugin' in vite.config.ts

Đã mở ngoại lệ trong .gitignore và đưa file vào git. Bài học: một luật ignore theo TÊN thư mục sẽ bắt nhầm thư mục nguồn trùng tên. Khi thêm một app vào monorepo, thử git status trên bản clone sạch chứ đừng tin thư mục làm việc của mình.

2. Năm chỗ JSX viết template literal trong thuộc tính nháy kép ​

jsx
<article className="recommendation-card ${open ? "expanded" : ""}">

Cú pháp này không phải JSX, nó là chuỗi template viết nhầm vào thuộc tính chuỗi thường. Năm chỗ trong MentorIntelligenceApp.tsx, chia hai loại:

  • Hai chỗ có nháy kép bên trong ("expanded", "single") → nháy đóng sớm, lỗi cú pháp, build chết.
  • Ba chỗ không có ("disclosure ${className}", "sidebar ${mobileOpen ? …}", "nav-group ${isActive ? …}") → parse được, nhưng in nguyên văn chuỗi ${...} vào thuộc tính class. Nghĩa là .nav-group.active, .sidebar.mobile-open, .recommendation-grid.single
    • những style mà chính lượt dọn màu này vừa quy về token, chưa bao giờ được áp dụng.

Loại thứ hai nguy hiểm hơn: không lỗi, không cảnh báo, chỉ là trạng thái "đang chọn" không bao giờ sáng lên. Đã sửa cả năm thành className={\…`}`.

Sửa hai thứ này nằm ngoài phạm vi "dọn màu", nhưng màu không thể tới người dùng nếu site không dựng được, và một trong hai lỗi làm chết đúng những lớp CSS vừa dọn.

Kiểm chứng ​

mentors không có job nào trên CI (giống play, mam), nên phải tự kiểm kỹ hơn:

  • Đường dẫn @import phân giải thật, và giống hệt cách bốn site kia khai (../../packages/design-system/theme.css).
  • Biên dịch CSS thật bằng @tailwindcss/node: 34.724 byte, không lỗi, chuỗi @import chạy.
  • Phân giải hết chuỗi var() trong đầu ra: 9 giá trị khớp, 2 dịch như bảng trên.
  • 0 mã hex, 0 rgba(255,255,255,…) còn lại trong file.
  • npm ci && npm run build (vinext / Next 16) chạy trót lọt lần đầu tiên sau khi gỡ hai lỗi chặn ở trên. CSS đã dựng: 0 mã hex tự chế, đủ token primary/foreground/surface-inverse/ emerald/amber/rose/cyan.
  • Xem bằng mắt trên bản dựng (vinext start): sidebar rgb(17,17,17), eyebrow rgb(217,79,43) = primary, glance card rgb(255,247,237) = accent; chuỗi bí danh phân giải đúng (--accent → --color-primary → #d94f2b).
  • 0 phần tử nào còn ${ trong class, và .nav-group.active đã tồn tại, hai lỗi JSX đã hết.
  • Cả bảy gác ở gốc repo xanh.

Hậu kiểm sau khi merge: domain KHÔNG trỏ vào Worker vừa deploy ​

deploy-mentors thành công lần đầu tiên sau khi gỡ hai lỗi chặn, đúng như mong đợi. Nhưng:

URL--accenthex tự chế
site-creator-vinext-starter.dac2205.workers.dev (Worker vừa deploy)#d94f2b ✅0 ✅
mentors.conan.school#cc4e2d ❌14 ❌

Lượt dọn màu đã lên Worker đúng; mentors.conan.school thì không trỏ vào đó. Và vì deploy-mentors chưa từng thành công trước hôm nay, nội dung sau domain đó không thể đến từ CI, nó là tàn dư của một lượt deploy tay, với --accent: #cc4e2d, tức cũ hơn cả globals.css hiện tại trong git.

Đây là lần thứ hai trong hai ngày, sau play. Chi tiết và cách kiểm: known-risks. Sửa chỗ gắn domain phải làm trên dashboard Cloudflare.

Trạng thái ​

65 chỗ tồn đọng toàn repo, từ 3.445 hôm qua. Sáu site khoá ở 0: www, admin, com, play, mam, mentors. Còn: packages 32 (DebugFooter của admin-shared, cố ý) · docs 12 · video 11 · api 10.

Tài liệu nội bộ nền tảng Conan School.