Files
sys-analysis-design/.claude/skills/ba-design/templates/components.html
2026-09-11 23:01:18 +07:00

304 lines
26 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!--
components.html — component sheet của DS_<PROJECT>, sinh bởi skill ba-design (activity ds). Tự chứa, mở bằng trình duyệt.
QUY TẮC: <style> = tokens.css (khối :root) + ds-components.css NGUYÊN VĂN. Mỗi component có data-component và data-uiconv (khớp UICONV §11).
Đủ trạng thái: default · hover (mô tả) · focus · disabled (+ tooltip lý do) · loading · error. Không mã màu thô ngoài biến --ds-*.
Kiểm: node .claude/skills/ba-design/scripts/design-check.mjs --tokens tokens.json --css tokens.css --html components.html
-->
<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>DS — <PROJECT> — Components</title>
<style>
:root{
--ds-color-bg-canvas:#F6F7F9;
--ds-color-bg-surface:#FFFFFF;
--ds-color-bg-subtle:#EEF1F5;
--ds-color-border-default:#D5DAE1;
--ds-color-border-strong:#767F8C;
--ds-color-text-primary:#1B1F24;
--ds-color-text-secondary:#5B6472;
--ds-color-text-disabled:#9AA3AF;
--ds-color-text-inverse:#FFFFFF;
--ds-color-brand-primary:#2456A6;
--ds-color-brand-primary-hover:#1C468A;
--ds-color-brand-primary-subtle:#E7EEF9;
--ds-color-status-danger:#B42318;
--ds-color-status-danger-subtle:#FDECEA;
--ds-color-status-success:#1E7B45;
--ds-color-status-success-subtle:#E6F4EC;
--ds-color-status-warning:#B7791F;
--ds-color-status-warning-subtle:#FFF4DE;
--ds-color-focus-ring:#2456A6;
--ds-font-family-base:"Segoe UI","Roboto","Helvetica Neue","Arial",sans-serif;
--ds-font-family-mono:"SFMono-Regular","Consolas","Menlo",monospace;
--ds-font-size-xs:12px;
--ds-font-size-sm:14px;
--ds-font-size-md:16px;
--ds-font-size-lg:20px;
--ds-font-size-xl:24px;
--ds-font-size-2xl:32px;
--ds-font-weight-regular:400;
--ds-font-weight-semibold:600;
--ds-font-line-height-tight:1.25;
--ds-font-line-height-normal:1.5;
--ds-space-1:4px;
--ds-space-2:8px;
--ds-space-3:12px;
--ds-space-4:16px;
--ds-space-6:24px;
--ds-space-8:32px;
--ds-space-12:48px;
--ds-radius-sm:4px;
--ds-radius-md:8px;
--ds-radius-pill:999px;
--ds-shadow-sm:0px 1px 2px 0px #1B1F2414;
--ds-shadow-md:0px 4px 12px 0px #1B1F2426;
--ds-size-control-desktop:36px;
--ds-size-control-touch:44px;
--ds-size-icon:20px;
--ds-size-content-max:1200px;
--ds-breakpoint-tablet:768px;
--ds-breakpoint-desktop:1024px;
}
/* ds-components v1.1 — BEGIN
CSS component của DS_<PROJECT>. Chỉ dùng biến --ds-* (tokens.css); cấm mã màu/px thô ngoài khối này.
Từ vựng lớp = cột "Kiểu trình bày" của UICONV §11 (xem DS §3). Khối này được chép NGUYÊN VĂN vào
components.html và mọi HIFI_*.html — kiểm khớp bằng scripts/design-check.mjs. */
*,*::before,*::after{box-sizing:border-box}
html{font-family:var(--ds-font-family-base);font-size:var(--ds-font-size-sm);line-height:var(--ds-font-line-height-normal);color:var(--ds-color-text-primary);background:var(--ds-color-bg-canvas);-webkit-font-smoothing:antialiased}
body{margin:0}
h1,h2,h3{margin:0;line-height:var(--ds-font-line-height-tight);font-weight:var(--ds-font-weight-semibold)}
h1{font-size:var(--ds-font-size-xl)}h2{font-size:var(--ds-font-size-lg)}h3{font-size:var(--ds-font-size-md)}
a,.ds-link{color:var(--ds-color-brand-primary);text-decoration:underline;text-underline-offset:2px;cursor:pointer}
a:hover,.ds-link:hover{color:var(--ds-color-brand-primary-hover)}
:focus-visible{outline:2px solid var(--ds-color-focus-ring);outline-offset:2px}
.ds-muted{color:var(--ds-color-text-secondary)}
.ds-small{font-size:var(--ds-font-size-xs)}
.ds-icon{width:var(--ds-size-icon);height:var(--ds-size-icon);flex:none;stroke:currentColor;fill:none;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round}
.ds-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
/* Layout */
.ds-page{max-width:var(--ds-size-content-max);margin:0 auto;padding:var(--ds-space-6) var(--ds-space-4);display:grid;gap:var(--ds-space-4)}
.ds-row{display:flex;gap:var(--ds-space-2);align-items:center;flex-wrap:wrap}
.ds-row--between{justify-content:space-between}.ds-row--end{justify-content:flex-end}
.ds-stack{display:grid;gap:var(--ds-space-3)}
.ds-cols{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,1fr);gap:var(--ds-space-4);align-items:start}
.ds-header{position:sticky;top:0;z-index:5;background:var(--ds-color-bg-surface);border-bottom:1px solid var(--ds-color-border-default);padding:var(--ds-space-3) var(--ds-space-4);display:flex;align-items:center;gap:var(--ds-space-4);min-height:56px}
.ds-breadcrumb{display:flex;gap:var(--ds-space-2);align-items:center;font-size:var(--ds-font-size-xs);color:var(--ds-color-text-secondary)}
.ds-breadcrumb a{text-decoration:none}
/* Nút — UICONV §11: nút chính / nút phụ / nút nguy hiểm / liên kết */
.ds-btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--ds-space-2);height:var(--ds-size-control-desktop);padding:0 var(--ds-space-4);border-radius:var(--ds-radius-sm);border:1px solid transparent;font:inherit;font-weight:var(--ds-font-weight-semibold);cursor:pointer;white-space:nowrap;text-decoration:none;transition:background .12s}
.ds-btn--primary{background:var(--ds-color-brand-primary);color:var(--ds-color-text-inverse)}
.ds-btn--primary:hover{background:var(--ds-color-brand-primary-hover)}
.ds-btn--secondary{background:var(--ds-color-bg-surface);color:var(--ds-color-text-primary);border-color:var(--ds-color-border-strong)}
.ds-btn--secondary:hover{background:var(--ds-color-bg-subtle)}
.ds-btn--danger{background:var(--ds-color-status-danger);color:var(--ds-color-text-inverse)}
.ds-btn--ghost{background:transparent;color:var(--ds-color-brand-primary)}
.ds-btn--icon{width:var(--ds-size-control-desktop);padding:0}
.ds-btn[aria-disabled="true"],.ds-btn:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}
.ds-btn.is-loading{pointer-events:none}
.ds-spinner{width:16px;height:16px;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:ds-spin .8s linear infinite;flex:none}
@keyframes ds-spin{to{transform:rotate(360deg)}}
/* Field — UICONV §4: nhãn trên field, * sau nhãn, lỗi dưới field thay hint */
.ds-field{display:grid;gap:var(--ds-space-1)}
.ds-field__label{font-size:var(--ds-font-size-xs);font-weight:var(--ds-font-weight-semibold);color:var(--ds-color-text-primary)}
.ds-field__label .ds-req{color:var(--ds-color-status-danger);margin-left:2px}
.ds-field__hint{font-size:var(--ds-font-size-xs);color:var(--ds-color-text-secondary)}
.ds-field__error{font-size:var(--ds-font-size-xs);color:var(--ds-color-status-danger);display:flex;gap:var(--ds-space-1);align-items:center}
.ds-input,.ds-select,.ds-textarea{width:100%;height:var(--ds-size-control-desktop);padding:0 var(--ds-space-3);border:1px solid var(--ds-color-border-strong);border-radius:var(--ds-radius-sm);background:var(--ds-color-bg-surface);color:var(--ds-color-text-primary);font:inherit}
.ds-textarea{height:auto;min-height:88px;padding:var(--ds-space-2) var(--ds-space-3)}
.ds-input::placeholder{color:var(--ds-color-text-secondary)}
.ds-input:hover,.ds-select:hover{border-color:var(--ds-color-text-secondary)}
.ds-input:focus,.ds-select:focus{border-color:var(--ds-color-brand-primary);outline:2px solid var(--ds-color-focus-ring);outline-offset:0}
.ds-input.is-invalid,.ds-select.is-invalid{border-color:var(--ds-color-status-danger)}
.ds-input[disabled],.ds-select[disabled]{background:var(--ds-color-bg-subtle);color:var(--ds-color-text-disabled);cursor:not-allowed}
.ds-input[readonly]{border-color:transparent;background:transparent;padding-left:0}
.ds-input-wrap{position:relative}
.ds-input-wrap .ds-icon{position:absolute;left:var(--ds-space-3);top:50%;transform:translateY(-50%);color:var(--ds-color-text-secondary)}
.ds-input-wrap .ds-input{padding-left:calc(var(--ds-space-3) + var(--ds-size-icon) + var(--ds-space-2))}
.ds-select{appearance:none;background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' stroke='%235B6472' stroke-width='1.75'><path d='m4 6 4 4 4-4'/></svg>");background-repeat:no-repeat;background-position:right var(--ds-space-3) center;padding-right:calc(var(--ds-space-3) + 24px)}
.ds-check{display:inline-flex;align-items:center;gap:var(--ds-space-2);cursor:pointer;min-height:var(--ds-size-control-desktop)}
.ds-check input{width:18px;height:18px;margin:0;accent-color:var(--ds-color-brand-primary)}
.ds-switch{position:relative;width:40px;height:22px;border-radius:var(--ds-radius-pill);background:var(--ds-color-border-strong);display:inline-block;flex:none}
.ds-switch::after{content:"";position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:var(--ds-color-bg-surface)}
.ds-switch[aria-checked="true"]{background:var(--ds-color-brand-primary)}
.ds-switch[aria-checked="true"]::after{left:20px}
.ds-stepper{display:inline-flex;border:1px solid var(--ds-color-border-strong);border-radius:var(--ds-radius-sm);height:var(--ds-size-control-desktop);overflow:hidden}
.ds-stepper button{width:var(--ds-size-control-desktop);border:0;background:var(--ds-color-bg-surface);font:inherit;cursor:pointer;color:var(--ds-color-text-primary)}
.ds-stepper input{width:48px;text-align:center;border:0;border-left:1px solid var(--ds-color-border-default);border-right:1px solid var(--ds-color-border-default);font:inherit}
/* Bảng / Card — UICONV §3, §11 */
.ds-table-wrap{overflow-x:auto;border:1px solid var(--ds-color-border-default);border-radius:var(--ds-radius-md);background:var(--ds-color-bg-surface)}
.ds-table{width:100%;border-collapse:collapse;font-size:var(--ds-font-size-sm)}
.ds-table th,.ds-table td{padding:var(--ds-space-3) var(--ds-space-4);text-align:left;border-bottom:1px solid var(--ds-color-border-default);vertical-align:middle}
.ds-table th{background:var(--ds-color-bg-subtle);font-weight:var(--ds-font-weight-semibold);font-size:var(--ds-font-size-xs);color:var(--ds-color-text-secondary);white-space:nowrap}
.ds-table th[aria-sort]{cursor:pointer}
.ds-table tbody tr:hover{background:var(--ds-color-bg-canvas)}
.ds-table tbody tr[aria-selected="true"]{background:var(--ds-color-brand-primary-subtle)}
.ds-table tbody tr:last-child td{border-bottom:0}
.ds-table td.ds-num{text-align:right;font-variant-numeric:tabular-nums}
.ds-card{background:var(--ds-color-bg-surface);border:1px solid var(--ds-color-border-default);border-radius:var(--ds-radius-md);padding:var(--ds-space-4);display:grid;gap:var(--ds-space-2)}
.ds-cards{display:grid;gap:var(--ds-space-3)}
.ds-badge{display:inline-flex;align-items:center;height:22px;padding:0 var(--ds-space-2);border-radius:var(--ds-radius-pill);font-size:var(--ds-font-size-xs);font-weight:var(--ds-font-weight-semibold);background:var(--ds-color-bg-subtle);color:var(--ds-color-text-primary)}
.ds-badge--info{background:var(--ds-color-brand-primary-subtle)}
.ds-badge--success{background:var(--ds-color-status-success-subtle);color:var(--ds-color-status-success)}
.ds-badge--warning{background:var(--ds-color-status-warning-subtle);color:var(--ds-color-status-warning)}
.ds-badge--danger{background:var(--ds-color-status-danger-subtle);color:var(--ds-color-status-danger)}
.ds-tabs{display:flex;gap:var(--ds-space-4);border-bottom:1px solid var(--ds-color-border-default)}
.ds-tab{padding:var(--ds-space-2) 0;border-bottom:2px solid transparent;color:var(--ds-color-text-secondary);cursor:pointer;font-weight:var(--ds-font-weight-semibold)}
.ds-tab[aria-selected="true"]{color:var(--ds-color-brand-primary);border-bottom-color:var(--ds-color-brand-primary)}
.ds-pagination{display:flex;gap:var(--ds-space-2);align-items:center;font-size:var(--ds-font-size-xs);color:var(--ds-color-text-secondary)}
.ds-price{font-variant-numeric:tabular-nums;font-weight:var(--ds-font-weight-semibold)}
.ds-thumb{width:64px;height:64px;border-radius:var(--ds-radius-sm);background:var(--ds-color-bg-subtle);border:1px solid var(--ds-color-border-default);display:grid;place-items:center;color:var(--ds-color-text-secondary);flex:none}
/* Phản hồi hệ thống — UICONV §5, §6 */
.ds-toast{position:fixed;top:var(--ds-space-4);right:var(--ds-space-4);z-index:20;min-width:280px;max-width:420px;padding:var(--ds-space-3) var(--ds-space-4);border-radius:var(--ds-radius-md);background:var(--ds-color-text-primary);color:var(--ds-color-text-inverse);box-shadow:var(--ds-shadow-md);display:flex;gap:var(--ds-space-3);align-items:flex-start}
.ds-toast--error{background:var(--ds-color-status-danger)}
.ds-banner{display:flex;gap:var(--ds-space-3);align-items:flex-start;padding:var(--ds-space-3) var(--ds-space-4);border-radius:var(--ds-radius-md);border:1px solid var(--ds-color-border-default);background:var(--ds-color-bg-subtle)}
.ds-banner--error{background:var(--ds-color-status-danger-subtle);border-color:var(--ds-color-status-danger);color:var(--ds-color-status-danger)}
.ds-banner--warning{background:var(--ds-color-status-warning-subtle);border-color:var(--ds-color-status-warning)}
.ds-empty{display:grid;justify-items:center;text-align:center;gap:var(--ds-space-3);padding:var(--ds-space-12) var(--ds-space-4);color:var(--ds-color-text-secondary)}
.ds-empty__illus{width:120px;height:88px;color:var(--ds-color-border-strong)}
.ds-empty--filter{padding:var(--ds-space-8) var(--ds-space-4)}
.ds-skeleton{height:14px;border-radius:var(--ds-radius-sm);background:linear-gradient(90deg,var(--ds-color-bg-subtle),var(--ds-color-bg-canvas),var(--ds-color-bg-subtle));background-size:200% 100%;animation:ds-shimmer 1.2s infinite}
@keyframes ds-shimmer{to{background-position:-200% 0}}
.ds-modal-backdrop{position:fixed;inset:0;background:#1B1F2480;display:grid;place-items:center;z-index:10;padding:var(--ds-space-4)}
.ds-modal{width:100%;max-width:520px;background:var(--ds-color-bg-surface);border-radius:var(--ds-radius-md);box-shadow:var(--ds-shadow-md);display:grid}
.ds-modal__head{display:flex;justify-content:space-between;align-items:center;padding:var(--ds-space-4) var(--ds-space-6);border-bottom:1px solid var(--ds-color-border-default)}
.ds-modal__body{padding:var(--ds-space-6);display:grid;gap:var(--ds-space-4)}
.ds-modal__foot{display:flex;justify-content:flex-end;gap:var(--ds-space-2);padding:var(--ds-space-4) var(--ds-space-6);border-top:1px solid var(--ds-color-border-default)}
.ds-status-page{display:grid;justify-items:center;gap:var(--ds-space-3);padding:var(--ds-space-12);text-align:center}
/* Trạng thái ẩn/khoá/read-only — W7 */
[data-vis="hidden"]{display:none!important}
[data-vis="disabled"]{opacity:.45;pointer-events:none}
[data-vis="readonly"] .ds-input{border-color:transparent;background:transparent}
/* Responsive — breakpoint theo UICONV §1; body[data-view] ép khung nhìn khi xem trong HIFI */
@media (max-width:1023px){.ds-cols{grid-template-columns:1fr}}
@media (max-width:767px){
.ds-page{padding:var(--ds-space-4) var(--ds-space-3)}
.ds-btn{height:var(--ds-size-control-touch)}
.ds-input,.ds-select{height:var(--ds-size-control-touch)}
.ds-table-wrap{display:none}.ds-cards{display:grid}
.ds-hide-m{display:none!important}
}
@media (min-width:768px){.ds-cards{display:none}.ds-hide-d{display:none!important}}
body[data-view="mobile"] .ds-cols{grid-template-columns:1fr}
body[data-view="mobile"] .ds-table-wrap{display:none}body[data-view="mobile"] .ds-cards{display:grid}
body[data-view="mobile"] .ds-hide-m{display:none!important}body[data-view="mobile"] .ds-hide-d{display:revert}
body[data-view="mobile"] .ds-btn,body[data-view="mobile"] .ds-input,body[data-view="mobile"] .ds-select{height:var(--ds-size-control-touch)}
body[data-view="mobile"] .ds-page{padding:var(--ds-space-4) var(--ds-space-3)}
body[data-view="tablet"] .ds-cols{grid-template-columns:1fr}
/* Tiện ích xếp chồng ở mobile: phần tử trong .ds-row chiếm trọn hàng (nhóm nút thao tác của một dòng danh sách/CartItem…) */
.ds-m-full{}
@media (max-width:767px){.ds-m-full{flex-basis:100%;min-width:0}}
body[data-view="mobile"] .ds-m-full{flex-basis:100%;min-width:0}
/* ds-components v1.1 — END */
</style>
</head>
<body>
<header class="ds-header">
<h1 style="font-size:var(--ds-font-size-md)">DS_&lt;PROJECT&gt; · Component sheet v1.0</h1>
<span class="ds-muted ds-small">Mỗi component = một dòng UICONV §11 · đủ trạng thái default / hover / focus / disabled / loading / error · chỉ dùng biến <code>--ds-*</code></span>
</header>
<main class="ds-page">
<section class="ds-card" data-component="Nút" data-uiconv="Nút chính · Nút phụ · Nút nguy hiểm · Liên kết">
<h2>Nút</h2>
<div class="ds-row">
<button class="ds-btn ds-btn--primary">Tạo mới</button>
<button class="ds-btn ds-btn--secondary">Huỷ</button>
<button class="ds-btn ds-btn--danger">Xoá</button>
<button class="ds-btn ds-btn--ghost">Xoá lọc</button>
<a class="ds-link" href="#">Chi tiết</a>
</div>
<div class="ds-row">
<button class="ds-btn ds-btn--primary" aria-disabled="true" title="Chọn ít nhất 1 dòng để bật">Disabled + tooltip lý do (UICONV §7)</button>
<button class="ds-btn ds-btn--primary is-loading"><span class="ds-spinner"></span>Đang lưu…</button>
<button class="ds-btn ds-btn--secondary ds-btn--icon" aria-label="Đóng"><svg class="ds-icon" viewBox="0 0 24 24"><path d="M6 6l12 12M18 6 6 18"/></svg></button>
</div>
<p class="ds-small ds-muted">Hover: nền tối hơn một bậc (<code>--ds-color-brand-primary-hover</code>). Focus: viền 2px <code>--ds-color-focus-ring</code>. Chiều cao 36px desktop / 44px mobile.</p>
</section>
<section class="ds-card" data-component="Field" data-uiconv="Ô nhập · Ô nhập số · Chọn 1 · Chọn nhiều · Chọn ngày · Công tắc">
<h2>Field</h2>
<div class="ds-cols" style="grid-template-columns:1fr 1fr">
<label class="ds-field"><span class="ds-field__label">Mã <b class="ds-req">*</b></span><input class="ds-input" placeholder="3–20 ký tự, A–Z 0–9 -"><span class="ds-field__hint">Không trùng với mã đã có</span></label>
<label class="ds-field"><span class="ds-field__label">Mã <b class="ds-req">*</b></span><input class="ds-input is-invalid" value="ab"><span class="ds-field__error"><svg class="ds-icon" style="width:14px;height:14px" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 8v5m0 3h.01"/></svg>Mã phải có từ 3 đến 20 ký tự.</span></label>
<label class="ds-field"><span class="ds-field__label">Loại</span><select class="ds-select"><option>Thường</option></select></label>
<label class="ds-field"><span class="ds-field__label">Tên (read-only)</span><input class="ds-input" readonly value="Cửa hàng A"></label>
<label class="ds-field"><span class="ds-field__label">Tên (disabled)</span><input class="ds-input" disabled value="Cửa hàng A"></label>
<div class="ds-field"><span class="ds-field__label">Tìm kiếm</span><div class="ds-input-wrap"><svg class="ds-icon" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg><input class="ds-input" placeholder="Nhập mã hoặc tên…"></div></div>
</div>
<div class="ds-row">
<label class="ds-check"><input type="checkbox" checked> Chọn nhiều</label>
<label class="ds-check"><input type="radio" name="r" checked> Chọn 1 (≤ 7 lựa chọn)</label>
<span class="ds-row"><span class="ds-switch" role="switch" aria-checked="true"></span> Công tắc</span>
<span class="ds-stepper"><button aria-label="Giảm">−</button><input value="2" aria-label="Số lượng"><button aria-label="Tăng">+</button></span>
</div>
</section>
<section class="ds-card" data-component="Bảng / Card" data-uiconv="Bảng · Card · Badge trạng thái">
<h2>Bảng (desktop) → Card (mobile)</h2>
<div class="ds-table-wrap"><table class="ds-table">
<thead><tr><th aria-sort="ascending">Mã ▲</th><th>Tên</th><th>Trạng thái</th><th class="ds-num">Số tiền</th><th></th></tr></thead>
<tbody>
<tr><td>STR-001</td><td>Cửa hàng A</td><td><span class="ds-badge">Nháp</span></td><td class="ds-num ds-price">1.234.567 ₫</td><td><a class="ds-link">Chi tiết</a></td></tr>
<tr aria-selected="true"><td>STR-002</td><td>Cửa hàng B</td><td><span class="ds-badge ds-badge--success">Đã duyệt</span></td><td class="ds-num ds-price">890.000 ₫</td><td><a class="ds-link">Chi tiết</a></td></tr>
<tr><td>STR-003</td><td>Cửa hàng C</td><td><span class="ds-badge ds-badge--danger">Từ chối</span></td><td class="ds-num ds-price">—</td><td><a class="ds-link">Chi tiết</a></td></tr>
</tbody></table></div>
<div class="ds-cards" style="display:grid"><div class="ds-card"><div class="ds-row ds-row--between"><b>STR-001</b><span class="ds-badge">Nháp</span></div><div>Cửa hàng A</div><div class="ds-small ds-muted">30/08/2026</div></div></div>
<div class="ds-row"><span class="ds-badge">Mặc định</span><span class="ds-badge ds-badge--info">Thông tin</span><span class="ds-badge ds-badge--success">Thành công</span><span class="ds-badge ds-badge--warning">Cảnh báo</span><span class="ds-badge ds-badge--danger">Lỗi</span></div>
<div class="ds-row ds-row--between"><div class="ds-tabs"><span class="ds-tab" aria-selected="true">Tất cả</span><span class="ds-tab">Nháp</span><span class="ds-tab">Đã duyệt</span></div><div class="ds-pagination"><button class="ds-btn ds-btn--secondary ds-btn--icon" aria-label="Trang trước">‹</button><span>1 / 7</span><button class="ds-btn ds-btn--secondary ds-btn--icon" aria-label="Trang sau">›</button></div></div>
</section>
<section class="ds-card" data-component="Trạng thái chuẩn" data-uiconv="Skeleton · Toast · Banner · Trang trạng thái">
<h2>Trạng thái chuẩn (UICONV §6) — hai trạng thái rỗng phải trông khác nhau</h2>
<div class="ds-cols" style="grid-template-columns:1fr 1fr">
<div class="ds-card"><b class="ds-small">Đang tải</b><div class="ds-skeleton"></div><div class="ds-skeleton" style="width:85%"></div><div class="ds-skeleton" style="width:60%"></div></div>
<div class="ds-card"><b class="ds-small">Lỗi tải (banner trong vùng)</b><div class="ds-banner ds-banner--error"><svg class="ds-icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 8v5m0 3h.01"/></svg><span>Không tải được dữ liệu.</span><button class="ds-btn ds-btn--secondary" style="margin-left:auto;height:32px">Thử lại</button></div></div>
<div class="ds-card"><b class="ds-small">Rỗng — chưa có dữ liệu (minh hoạ + nút chính)</b><div class="ds-empty"><svg class="ds-empty__illus" viewBox="0 0 120 88" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="16" y="20" width="88" height="56" rx="6"/><path d="M16 36h88M32 52h28M32 62h44"/></svg><span>Chưa có cửa hàng nào.</span><button class="ds-btn ds-btn--primary">Tạo mới</button></div></div>
<div class="ds-card"><b class="ds-small">Rỗng — bộ lọc không khớp (không minh hoạ + nút phụ)</b><div class="ds-empty ds-empty--filter"><span>Không tìm thấy kết quả phù hợp với bộ lọc.</span><button class="ds-btn ds-btn--secondary">Xoá lọc</button></div></div>
</div>
<div class="ds-row">
<div class="ds-toast" style="position:static"><svg class="ds-icon" viewBox="0 0 24 24"><path d="m5 12 4 4L19 6"/></svg><span>Đã lưu cửa hàng STR-004.</span><button class="ds-btn ds-btn--ghost ds-btn--icon" aria-label="Đóng" style="color:inherit;height:24px;width:24px;margin-left:auto">✕</button></div>
<div class="ds-toast ds-toast--error" style="position:static"><svg class="ds-icon" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 8v5m0 3h.01"/></svg><span>Không lưu được. Thử lại sau.</span></div>
</div>
</section>
<section class="ds-card" data-component="Modal" data-uiconv="Modal · Drawer · Bottom-sheet">
<h2>Modal xác nhận (UICONV §5) — nút chính bên phải, hành động nguy hiểm dùng nút nguy hiểm</h2>
<div class="ds-modal" style="box-shadow:var(--ds-shadow-sm)">
<div class="ds-modal__head"><h3>Xoá cửa hàng STR-001?</h3><button class="ds-btn ds-btn--ghost ds-btn--icon" aria-label="Đóng"><svg class="ds-icon" viewBox="0 0 24 24"><path d="M6 6l12 12M18 6 6 18"/></svg></button></div>
<div class="ds-modal__body"><p style="margin:0">Hành động này không hoàn tác được.</p><label class="ds-field"><span class="ds-field__label">Lý do <b class="ds-req">*</b></span><textarea class="ds-textarea" placeholder="Tối đa 200 ký tự"></textarea></label></div>
<div class="ds-modal__foot"><button class="ds-btn ds-btn--secondary">Huỷ</button><button class="ds-btn ds-btn--danger">Xoá</button></div>
</div>
</section>
<section class="ds-card" data-component="Icon" data-uiconv="—">
<h2>Icon — inline SVG 24 grid, stroke 1.75, không emoji</h2>
<div class="ds-row" style="gap:var(--ds-space-4)">
<svg class="ds-icon" viewBox="0 0 24 24" aria-label="search"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
<svg class="ds-icon" viewBox="0 0 24 24" aria-label="plus"><path d="M12 5v14M5 12h14"/></svg>
<svg class="ds-icon" viewBox="0 0 24 24" aria-label="close"><path d="M6 6l12 12M18 6 6 18"/></svg>
<svg class="ds-icon" viewBox="0 0 24 24" aria-label="chevron-down"><path d="m6 9 6 6 6-6"/></svg>
<svg class="ds-icon" viewBox="0 0 24 24" aria-label="back"><path d="M19 12H5m7-7-7 7 7 7"/></svg>
<svg class="ds-icon" viewBox="0 0 24 24" aria-label="check"><path d="m5 12 4 4L19 6"/></svg>
<svg class="ds-icon" viewBox="0 0 24 24" aria-label="alert"><circle cx="12" cy="12" r="9"/><path d="M12 8v5m0 3h.01"/></svg>
<svg class="ds-icon" viewBox="0 0 24 24" aria-label="trash"><path d="M4 7h16M9 7V4h6v3m-8 0 1 13h8l1-13"/></svg>
<svg class="ds-icon" viewBox="0 0 24 24" aria-label="filter"><path d="M3 5h18l-7 8v6l-4-2v-4z"/></svg>
<svg class="ds-icon" viewBox="0 0 24 24" aria-label="cart"><path d="M3 4h2l2.4 11h11.2L21 7H6"/><circle cx="9" cy="19" r="1.5"/><circle cx="17" cy="19" r="1.5"/></svg>
<svg class="ds-icon" viewBox="0 0 24 24" aria-label="user"><circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/></svg>
<svg class="ds-icon" viewBox="0 0 24 24" aria-label="menu"><path d="M4 7h16M4 12h16M4 17h16"/></svg>
</div>
</section>
</main>
</body>
</html>