Naming Convention — Quy Tắc Đặt Tên Toàn Hệ Thống
Dành cho: Developer, Designer
Mục tiêu: Thống nhất cách đặt tên từ token → CSS class → Figma frame
Thời gian đọc: ~15 phút
1. Tổng Quan — 1 Naming Convention, 5 Contexts
Prisma dùng 1 bộ quy tắc nhất quán cho tất cả:
| Context |
Format |
Ví dụ |
| CSS Token |
--{tier}-{property}-{variant} |
--group-padding-default |
| CSS Class |
{layer}-{role}__{element}--{modifier} |
card-stat__value--positive |
| DS Component |
.{component}--{variant} |
.btn--primary, .input--error |
| Figma Frame |
{Layer} / {Component} / {Variant} |
Card / Stat / Default |
| Figma Sub-frame |
_{element} hoặc {slot} |
_icon, _value, header |
2. CSS Token Naming
Format
--{tier}-{property}-{variant}
Tier (4 cấp)
| Tier |
Prefix |
Dùng cho |
| Page |
--page- |
Background, main content area |
| Section |
--section- |
Content regions, groups of cards |
| Group |
--group- |
Cards, panels, visual containers |
| Comp |
--comp- |
Buttons, inputs, leaf elements |
Property Types
| Property |
Dạng token |
Ví dụ |
| Padding |
{tier}-padding-{size} |
--group-padding-default |
| Stack gap |
{tier}-stack-gap-{size} |
--section-stack-gap-large |
| Inline gap |
{tier}-inline-gap-{size} |
--comp-inline-gap-default |
| Grid gap |
{tier}-grid-{direction}-gap |
--section-grid-row-gap |
| Radius |
{tier}-radius-{variant} |
--group-radius-default |
| Margin |
{tier}-margin-{size} |
--page-margin-default |
| Height |
{tier}-height-{size} |
--comp-height-default |
Size Variants
xlarge > large > default > small > xsmall
Rule: Luôn dùng default trước. Chỉ dùng large/small khi cần size khác biệt rõ ràng.
Ví Dụ Đầy Đủ: Stat Card
.card-stat {
/* ✅ Tier tokens — group level */
padding: var(--group-padding-default); /* --group-padding-default */
gap: var(--group-stack-gap-default); /* --group-stack-gap-default */
border-radius: var(--group-radius-default); /* --group-radius-default */
/* ✅ Color tokens — card layer */
background: var(--card);
color: var(--card-foreground);
border: 1px solid var(--card-border);
}
3. CSS Class Naming (Elevation BEM)
Format
.[layer]-[role]__[element]--[modifier]
| Part |
Bắt buộc? |
Ý nghĩa |
Ví dụ |
layer |
✅ |
Elevation layer: ground, card, surface |
card- |
role |
✅ |
UI role (semantic) |
-stat, -nav, -form |
__element |
❌ |
Child element bên trong |
__value, __icon, __label |
--modifier |
❌ |
Trạng thái hoặc variant |
--active, --positive, --compact |
Quy tắc đặt tên role
| ✅ Đúng (UI role) |
❌ Sai (business data) |
card-stat |
balance-card |
card-product |
loan-card |
card-form |
registration-card |
surface-nav |
menu-bar |
ground-content |
main-area |
Ví Dụ Đầy Đủ: Dashboard Stat Card
card-stat → Component root (group tier)
├── card-stat__header → Header area
│ ├── card-stat__label → "Revenue" text
│ └── card-stat__icon → Trend icon
├── card-stat__value → "$12,450" main number
│ └── card-stat__value--positive → Green variant
└── card-stat__trend → "+12% from last month"
<article class="card-stat">
<div class="card-stat__header">
<span class="card-stat__label">Revenue</span>
<i class="card-stat__icon" data-lucide="trending-up"></i>
</div>
<span class="card-stat__value card-stat__value--positive">$12,450</span>
<span class="card-stat__trend">+12% from last month</span>
</article>
.card-stat { ... }
.card-stat__header { display: flex; justify-content: space-between; align-items: center; }
.card-stat__label { font: var(--text-style-label-default-font-weight) var(--text-style-label-default-font-size)/var(--text-style-label-default-line-height) inherit; }
.card-stat__value { font: var(--text-style-heading-2-font-weight) var(--text-style-heading-2-font-size)/var(--text-style-heading-2-line-height) inherit; }
.card-stat__value--positive { color: var(--success); }
.card-stat__value--negative { color: var(--destructive); }
.card-stat__trend { font: var(--text-style-caption-font-weight) var(--text-style-caption-font-size)/var(--text-style-caption-line-height) inherit; color: var(--muted-foreground); }
4. DS Component Naming (Shared Components)
DS components (.btn, .input, .badge, v.v.) KHÔNG dùng layer prefix — chúng là layer-agnostic.
Format
Ví dụ
| Component |
Variant |
Class |
| Button |
primary |
.btn--primary |
| Button |
secondary |
.btn--secondary |
| Button |
ghost |
.btn--ghost |
| Input |
error |
.input--error |
| Badge |
success |
.badge--success |
| Card |
elevated |
.card-elevated |
| Tabs |
segment |
.tabs--segment |
Sub-component (BEM Element)
| Component |
Sub-element |
Class |
| Tab |
icon |
.tab__icon |
| Card |
header |
.card-header |
| Card |
body |
.card-body |
| Card |
footer |
.card-footer |
| Card |
title |
.card-title |
| Alert |
icon |
.alert__icon |
| Alert |
content |
.alert__content |
Note: DS card component dùng - thay vì __ do legacy convention (.card-header thay vì .card__header). Showcase card classes dùng __ (.card-stat__header).
5. Page-Level Naming — Từ Page đến Comp
Ví Dụ Đầy Đủ: Finance Dashboard
LEVEL 0 — PAGE
ground-app → Body / root container
├── surface-topbar → Top navigation bar
│ ├── surface-topbar__brand → Logo + app name
│ ├── surface-topbar__search → Search input
│ └── surface-topbar__actions → User avatar, notifications
│
├── surface-sidebar → Left sidebar
│ ├── surface-sidebar__nav → Navigation links
│ └── surface-sidebar__footer → Collapse toggle
│
└── ground-content → Main scrollable area
LEVEL 1 — SECTION
├── ground-content__overview → Stats section
│
│ LEVEL 2 — GROUP
│ ├── card-stat → Revenue card
│ │ ├── card-stat__label → "Revenue"
│ │ ├── card-stat__value → "$12,450"
│ │ └── card-stat__trend → "+12%"
│ │
│ ├── card-stat → Users card
│ ├── card-stat → Orders card
│ └── card-stat → Growth card
│
├── ground-content__analytics → Chart section
│ └── card-chart → Chart container
│ ├── card-chart__header → Title + filter buttons
│ │
│ │ LEVEL 3 — COMP (no prefix)
│ │ ├── btn--ghost → "Weekly" toggle
│ │ ├── btn--ghost → "Monthly" toggle
│ │ └── btn--primary → "Yearly" toggle
│ │
│ └── card-chart__canvas → Chart canvas area
│
└── ground-content__transactions → Table section
└── card-table → Table container (slotted)
├── .slot-header → Title + "Export" button
├── .slot-body-flush → <table> full-width
└── .slot-footer-between → Pagination
CSS tương ứng
/* LEVEL 0 — Page */
.ground-app {
display: grid;
grid-template-columns: auto 1fr;
grid-template-rows: auto 1fr;
min-height: 100vh;
background: var(--canvas);
}
.ground-content {
padding: var(--page-padding-default);
display: flex;
flex-direction: column;
gap: var(--page-stack-gap-default);
overflow-y: auto;
}
/* LEVEL 1 — Section */
.ground-content__overview {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--section-grid-row-gap) var(--section-grid-column-gap);
}
.ground-content__analytics,
.ground-content__transactions {
display: flex;
flex-direction: column;
gap: var(--section-stack-gap-default);
}
/* LEVEL 2 — Group */
.card-stat {
background: var(--card);
color: var(--card-foreground);
border: 1px solid var(--card-border);
border-radius: var(--group-radius-default);
padding: var(--group-padding-default);
display: flex;
flex-direction: column;
gap: var(--group-stack-gap-small);
}
/* LEVEL 3 — Comp (NO layer prefix) */
.card-stat__value {
font-size: var(--text-style-heading-2-font-size);
font-weight: var(--text-style-heading-2-font-weight);
}
6. Figma Frame Naming
Frame Hierarchy
Figma frames mirror CSS naming nhưng dùng / separator thay vì - / __:
Page / Section / Component / Variant / State
Ví dụ
| Figma Frame Name |
CSS Class |
Card / Stat / Default |
.card-stat |
Card / Stat / Default / Hover |
.card-stat:hover |
Card / Chart / With Filter |
.card-chart.card-chart--filtered |
Surface / Topbar / Default |
.surface-topbar |
Surface / Sidebar / Collapsed |
.surface-sidebar--collapsed |
Ground / Content / Overview |
.ground-content__overview |
Sub-frame Naming
Figma sub-frames (auto-layout children) dùng prefix _ để phân biệt structural frame vs semantic element:
| Sub-frame |
Ý nghĩa |
CSS |
_header |
Structural container |
.card-stat__header |
_body |
Content area |
.card-stat__body |
_footer |
Footer area |
.card-stat__footer |
_icon |
Icon slot |
.card-stat__icon |
_actions |
Button group |
.card-stat__actions |
Label |
Text content (no _) |
.card-stat__label |
Value |
Text content (no _) |
.card-stat__value |
Figma Component Variants
Component Name: Card / Stat
Properties:
├── Size: sm | md | lg
├── State: default | hover | active | disabled
├── Trend: positive | negative | neutral
└── Has Icon: true | false
Rule: Sub-frames bắt đầu bằng _ → structural (frame/layout).
Text/icon nodes KHÔNG có _ → content elements.
7. Mapping Table — CSS ↔ Figma ↔ Token
| Level |
CSS Class |
Figma Frame |
Token Tier |
| Page |
.ground-app |
Ground / App |
--page-* |
| Section |
.ground-content__overview |
Ground / Content / Overview |
--section-* |
| Group |
.card-stat |
Card / Stat / Default |
--group-* |
| Sub-element |
.card-stat__value |
Value (text node) |
--comp-* |
| Modifier |
.card-stat__value--positive |
Property: Trend = positive |
(conditional) |
| DS Component |
.btn--primary |
Button / Primary / Default |
--comp-* |
8. Anti-Patterns
❌ Tên theo business data
/* ❌ SAI */
.balance-display { ... }
.loan-interest-rate { ... }
.customer-profile-card { ... }
/* ✅ ĐÚNG */
.card-stat__value { ... }
.card-detail__rate { ... }
.card-profile { ... }
❌ Thiếu layer prefix
/* ❌ SAI — không biết element thuộc layer nào */
.dashboard-header { ... }
.stats-container { ... }
/* ✅ ĐÚNG */
.surface-topbar { ... }
.ground-content__overview { ... }
❌ Quá nhiều nesting levels
/* ❌ SAI — quá sâu, khó maintain */
.card-stat__header__icon__wrapper { ... }
/* ✅ ĐÚNG — flatten, tối đa 1 level `__` */
.card-stat__icon { ... }
❌ Figma frame không match CSS
/* ❌ SAI */
Figma: "Stats Card" → CSS: .card-stat
Figma: "stat_card_v2" → CSS: .card-stat
/* ✅ ĐÚNG — đồng bộ */
Figma: "Card / Stat" → CSS: .card-stat
9. Quick Reference — Naming Cheat Sheet
TOKEN: --{tier}-{property}-{variant}
--group-padding-default
--section-stack-gap-large
--comp-radius-capsule
CLASS: {layer}-{role}__{element}--{modifier}
card-stat
card-stat__value
card-stat__value--positive
surface-topbar__actions
COMP: {component}--{variant}
btn--primary
input--error
badge--success
FIGMA: {Layer} / {Component} / {Variant}
Card / Stat / Default
Surface / Topbar / Default
Button / Primary / Hover
SUB: _{structural} | {Content}
_header | Label
_body | Value
_actions | Icon
Quy tắc vàng: Nếu bạn không thể đọc tên class và biết ngay nó thuộc layer nào + làm gì → tên chưa đủ tốt.