304 lines
26 KiB
HTML
304 lines
26 KiB
HTML
<!--
|
||
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_<PROJECT> · 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>
|