i18n lo phần dịch thuật.
KDF lo phần thiết kế.
Cả hai đều nằm gọn trong JSON.
Một lớp điều phối thiết kế bằng JSON dành cho web app server-side và UI do AI hỗ trợ. Các quyết định lặp lại — layout, khoảng cách, typography, component style — giờ đều nằm chung một chỗ để agent đọc được, thay vì mò mẫm qua từng file, từng trang hay lục lại các cuộc trò chuyện cũ.
{ "$layout": ["hero", "footer"], "hero": { "wrapper": "mx-auto max-w-6xl px-6 py-20", "title": "@typography.h1", "cta-primary": "@button.cta" } }
Một nguồn chân lý duy nhất giúp giữ cho mọi trang, mọi component, và mọi phiên làm việc của agent luôn đồng nhất với nhau.
JSON định nghĩa · code render · data-kdf map trở lại
Thiết kế bị lệch pha
trong các component.
Khi tên class chỉ nằm chết trong các file .tsx, các trang dần sẽ lệch pha nhau. Kiểu đó thì ổn nếu chỉ có mỗi con người code — nhưng sẽ rất mong manh ngay khi AI nhảy vào can thiệp giao diện.
<section className="mx-auto max-w-6xl px-6 py-20"> <h1 className="text-5xl font-semibold tracking-tight"> // …and again, slightly different, on the next page
Cùng một cái nút mà tự nhiên đẻ ra 5 phiên bản. Khoảng cách thì mỗi trang mỗi kiểu. Mỗi lần mở phiên mới là agent lại phải học lại luật từ đầu.
- — Agent tự phăng ra đủ thứ màu sắc, khoảng cách, typography và layout.
- — Lần nào người dùng cũng phải hì hục sửa lại giao diện thông qua chat.
- — Mỗi lần thay đổi là một lần bới tung các file component lên.
- — Chat qua nhiều phiên cũng rơi rụng dần ý đồ thiết kế ban đầu.
→ những vòng lặp "to lên tí" · "xanh hơn xíu" · "kéo qua trái" không hồi kết
Làm cho thiết kế trở nên rành mạch.
KDF làm với thiết kế y hệt cách i18n làm với chữ: bốc các quyết định lặp lại ra khỏi component rồi nhét vào JSON. UI lúc render vẫn dùng CSS bình thường — cái thay đổi ở đây là quyền kiểm soát thuộc về ai.
{ "hero": { "title": "text-5xl font-semibold tracking-tight" } }
const d = getDesign("homepage"); <h1 data-kdf="hero.title" className={d("hero.title")}>
Thư viện thì bảo đó là gì.
KDF thì bảo đó là cái nào.
Thư viện thiết kế quăng cho bạn cái Button. KDF thì cho bạn biết cái đó là homepage.hero.cta-primary, được render thành Button, và xài style của token này. Nó lấp đầy một chỗ trống quan trọng mà các thư viện hay bỏ sót — map từ element đến đúng quyết định thiết kế.
Sáu ký hiệu.
Một ngữ pháp.
KDF chỉ lưu tên class, các reference dùng chung, thứ tự section và CSS custom properties. Tuyệt đối không chứa business logic, event handler, lấy dữ liệu, quyền hạn hay tính năng accessibility.
Mỗi element xài token đều cõng theo một path tương ứng. Các DOM node truy ngược thẳng về JSON — cực tiện cho bot quét, test và cho agent kiểm tra lại.
Phân giải path thành string className. Còn d.css() sẽ trả về cục CSS custom properties.
Trỏ đến một token xài lại được trong thư mục shared/. Các ref có thể nối tiếp nhau và mở rộng bằng cách chèn class.
Một array chứa các key của section. Nằm trong list thì render theo thứ tự; không có thì app sẽ tự động giấu đi.
Metadata dành cho agent và tool của host — báo cho biết component nào đang render token này, kèm theo các hint về size và biến thể.
Các giá trị không thể gom thành class dùng lại sẽ được nhét vào inline style variable thông qua hàm d.css().
Mặc định dùng chung.
Override từng trang.
Design token sống ở hai nơi: các token mặc định có thể xài lại nằm ở shared/, và một phần để override cục bộ cho những chỗ cần thiết. Có thêm hai file CSS riêng biệt lo phần first paint và giải quyết các case hóc búa.
kdf/ shared/ button.json ← reusable defaults card.json color.json typography.json homepage.json ← page composition konde-server.css ← critical, first paint konde.css ← non-critical tweaks
Một cái reference kiểu @button.cta sẽ resolve từ thư mục shared/ của trang, rồi lội lên shared/ của thư mục cha, sau đó mới đến các token của trang. Template chỉ ghi đè đúng thứ nó cần và kế thừa mọi thứ còn lại.
Thiết kế được resolve ngay trên server; browser chỉ việc nhận chuỗi class đã đóng gói. CSS thiết yếu nhất (critical) sẽ chốt đơn ngay first paint, phần còn lại load sau — đảm bảo máy yếu không phải nai lưng ra chịu tải lúc render.
Được app import vào để các biến thiết kế và các bản override chống chớp giao diện (FOUC) có mặt ngay từ lần paint đầu tiên — trước khi màn hình kịp nhấp nháy.
/* konde-server.css */ :root { --kdf-primary: #1F8F47; } [data-kdf="hero.slider"] { display: none; }
Load sau CSS của framework và app, dùng cho mấy trò tweak, test thử và các ca khó nhằn — tinh chỉnh thoải mái mà không bao giờ chặn quá trình paint.
/* konde.css */ [data-kdf="hero.title"] { letter-spacing: -0.02em; } [data-kdf="hero.wrapper"] { gap: 3rem; }
→ KDF chỉ tạo cả hai file này đúng một lần và không bao giờ ghi đè. Plugin sẽ ném đường dẫn thông qua biến môi trường (env) - app của bạn tự import nhé; không có trò tự động tiêm mã lén lút đâu.
Đổi giao diện
dễ như đổi ngôn ngữ.
Cách i18n đổi ngôn ngữ sao thì KDF đổi template thiết kế y chang vậy. Vẫn app đó, chung component đó, xài cùng bộ code — chỉ cần trỏ KDF_DIR qua thư mục thiết kế khác là cái UI lột xác liền.
designs/ lander/ shared/ homepage.json newlander/ shared/ homepage.json
// next.config.ts withKDF({ dir: "./designs/lander" })(nextConfig);
Resolve trên server.
Ném string xuống dưới.
KDF core đọc thẳng JSON từ ổ đĩa nên nó chạy bên server-side ráo: Next.js Server Components, Astro render, Hono handler. Cứ resolve class ở đó, xong xuôi thì quăng string thuần xuống cho client component xài.
import { getDesign, cn } from "@kondeio/kdf"; const d = getDesign("homepage"); <button data-kdf="hero.cta" className={cn(d("hero.cta"), isActive)}>Start</button>
→ chuỗi className đã resolve
→ object CSS custom-properties
Nối class theo điều kiện, vứt các giá trị falsy, và dedupe class — mặc định không quan tâm ngữ nghĩa.
Lên production thì luôn cache; còn lúc dev thì nó revalidate dựa vào thời gian (mtime) & dung lượng file.
Nằm gọn bên trên
stack của bạn.
STYLING — CHỈ LƯU TÊN CLASS, KHÔNG LƯU CSS
KDF đâu phải CSS engine. Token chỉ chứa chuỗi class, hệ thống styling của bạn xài thế nào thì tùy. Nếu framework của bạn có vụ scan source file, cứ mạnh dạn trỏ nó tới luôn mấy file JSON: @source "../kdf/**/*.json".
RUNTIME — SERVER-SIDE JAVASCRIPT
Cài một lần.
Init tự lo nốt phần còn lại.
Lúc chạy lệnh cài đặt, tool sẽ sinh luôn thư mục kdf/ mẫu nếu nó chưa có. Nếu file đã tồn tại thì cứ yên tâm là không bao giờ bị đè lên.
import withKDF from "@kondeio/kdf/plugin"; export default withKDF({ dir: "./designs" })(nextConfig);