TechViet UI kit
Các phần của trang
Trên trang này

UI kit v1.3

Mọi thứ trên trang này chỉ dùng class của kit.css và biến của tokens.base.css.
Mọi thứ trên trang này chỉ dùng class của kit.css và biến của tokens.base.css. Ba ô chọn ở thanh trên đổi bộ token: Dáng (bo góc, bóng), Màu (bảng màu) và Font ghép tự do với nhau, mỗi bảng màu đã qua check-palette.mjs. Mặc định (base) là dáng mềm, màu ink, font Inter Display. Desktop, Mobile và Tất cả ở thanh trên chọn màn hình: Desktop và Mobile mỗi bên chỉ hiện phần dùng được ở màn đó (phần dùng chung hiện ở cả hai). Bên trái xếp theo năm lớp: Nền tảng, Thành phần, Mẫu thiết kế, Bố cục trang, Hướng dẫn (xem phần Quy ước ở cuối trang). Giao diện: app không phải màn hình mà là kiểu vẽ khác cho cùng các class (nút viên thuốc, viền thay bóng, chữ 16px, điều khiển 52px khi chạm); bật nó thì thêm vài phần chỉ có ở kiểu app.

Tokens

Màu nền, chữ, trạng thái.

bgnền trang
surfacethẻ, ô nhập
surface-2đầu bảng
sunkvô hiệu, lõm
primarynút chính
notice-bgthông báo
okthành công
warncảnh báo
dangerlỗi
infothông tin
xs 12sm 13md 14 base 15lg 17xl 202xl 24

Icon

Biểu tượng Phosphor (MIT) trong icons.svg, màu theo chữ xung quanh.
Phosphor (MIT) trong icons.svg: 168 icon nét thường và 14 icon đặc (hậu tố -fill, dùng cho mục đang chọn). .icon là 20 px, .icon-sm 16 px, .icon-lg 24 px; màu theo chữ xung quanh.
sm 16 20 lg 24
app-window
archive
arrow-clockwise
arrow-counter-clockwise
arrow-down
arrow-left
arrow-right
arrow-square-out
arrow-up
arrows-clockwise
arrows-down-up
at
bank
barcode
bell
bell-ringing
book-open
bookmark
buildings
calendar
calendar-blank
calendar-check
calendar-plus
camera
caret-double-left
caret-double-right
caret-down
caret-left
caret-right
caret-up
caret-up-down
chart-bar
chart-line
chart-pie
chat-circle
chat-circle-text
chats
check
check-circle
circle
circle-dashed
clipboard-text
clock
clock-countdown
clock-counter-clockwise
cloud-arrow-up
coins
columns
compass
copy
copy-simple
credit-card
crop
currency-dollar
database
diamond
dots-nine
dots-three
dots-three-vertical
download-simple
envelope
eye
eye-slash
file
file-csv
file-image
file-pdf
file-text
file-video
file-xls
film-strip
fingerprint
flag
floppy-disk
folder
folder-open
funnel
gauge
gear
gear-six
globe
grid-four
hand-coins
heart
hourglass
house
identification-card
image
images
info
invoice
kanban
key
layout
lifebuoy
lightbulb
link
list
list-bullets
lock
magnifying-glass
map-pin
map-trifold
megaphone
microphone
minus
money
music-note
note-pencil
package
paint-brush
palette
paper-plane-tilt
paperclip
password
pause
pencil-simple
percent
phone
play
play-circle
plus
presentation-chart
printer
prohibit
push-pin
qr-code
question
receipt
receipt-x
rows
seal-check
shield
shield-check
shopping-bag
shopping-cart
sidebar
sign-in
sign-out
sliders
sliders-horizontal
sort-ascending
sort-descending
speaker-high
spinner
squares-four
star
storefront
table
tag
target
timer
trash
trend-down
trend-up
truck
upload-simple
user
user-circle
user-minus
user-plus
users
video-camera
wallet
warehouse
warning
warning-circle
wifi-high
wifi-slash
wrench
x
x-circle
x-square
book-open-fill
buildings-fill
calendar-check-fill
chart-bar-fill
clipboard-text-fill
file-text-fill
gear-fill
house-fill
map-pin-fill
package-fill
receipt-fill
seal-check-fill
sliders-fill
squares-four-fill
storefront-fill
truck-fill
user-fill
users-fill
wallet-fill
warehouse-fill
wifi-high-fill
wrench-fill
arrows-left-right

Cỡ chạm: --tap và data-tap

Dưới ngón tay mọi điều khiển cao ít nhất 44px; dưới chuột kit giữ cỡ gọn, trừ khi trang bật data-tap.
Mặc định: @media (pointer: coarse) nâng nút nhỏ, chip, mục .seg, nút lịch, nút xóa của chip, nút liên kết trong banner nháp… lên --tap (44px, tenant đặt lại được) và chữ ô nhập lên 1rem. Chuột (pointer: fine) giữ cỡ gọn. Tenant nào muốn mục tiêu 44px cả khi dùng chuột (máy tính tiền cảm ứng, laptop cảm ứng, hoặc thiết kế muốn 44px khắp nơi) đặt <html data-tap="all">: cùng danh sách cỡ của khối chạm, trừ cỡ chữ ô nhập (cỡ đó chỉ để iOS không phóng to). Không đặt thì không có gì đổi. Trong kiểu app, look-app.css giữ nút nhỏ ở 44px thay vì 40px. Ô .daterange: ô ngày cao hết khung và bấm vào lề hoặc dấu gạch ngang của khung thì focus ô ngày gần nhất. Không dùng cho liên kết nằm trong đoạn chữ (.btn-link trần), cũng như khối chạm.
Chip
D10
Có bản nháp chưa lưu từ 09:41

Button

Nút: sáu kiểu, ba cỡ, nút chỉ có biểu tượng và trạng thái đang tải.
.btn + -primary -danger -danger-solid -ghost -link, cỡ -sm -lg, -icon, -block, -group. Trạng thái: :disabled, aria-busy="true".

Nút có biểu tượng

Nút có biểu tượng đứng trước hoặc sau chữ.
Biểu tượng đứng trước hoặc sau chữ, khoảng cách do .btn giữ. Trong .btn-sm biểu tượng tự nhỏ còn 16 px. Nút chỉ có biểu tượng dùng .btn-icon và bắt buộc có aria-label. Khi aria-busy="true" vòng quay thay biểu tượng.

Form

Biểu mẫu: ô nhập, ô chọn, công tắc và báo lỗi.
.field > .label + .input .select .textarea + .field-hint / .field-error; .form-grid, .check, .switch, .form-actions. Lỗi dùng aria-invalid="true".
Đúng như trên hợp đồng.
  • Phiếu nhập
  • Phiếu xuất
Nhập số tiền hợp lệ.

Cột có độ rộng khác nhau

.form-cols là lưới 12 cột; mỗi .field lấy .c-3, .c-4, .c-6 hoặc .c-8 cột (không có lớp: cả hàng). Khi bản thân biểu mẫu hẹp dưới 36rem, trong hộp thoại hẹp cũng như trên điện thoại, mọi ô chiếm cả hàng. Dùng .form-grid khi các ô bằng nhau.

Trên: đủ rộng, các ô theo số cột. Dưới: cùng kiểu trong khung 28rem, mỗi ô một hàng.

Số trong khoảng ngắn

.number-stepper[data-stepper] do kit.js điều khiển, cho số nguyên trong khoảng ngắn (chọn thay cho một hàng chip khi chỉ có vài giá trị). data-min (mặc định 0), data-max (mặc định không giới hạn) và data-step-by (mặc định 1) đặt trên khung. Gõ số rồi rời ô thì số được kẹp vào khoảng; chữ không phải số thì giữ giá trị cũ. Nút ra ngoài khoảng có aria-disabled="true" nhưng vẫn giữ tiêu điểm. ↑ ↓ trong ô tăng giảm, Home và End về hai đầu. Giá trị ở data-value; ô bắn input và change.

Dropdown with search

Ô nhập lọc danh sách (không cần gõ dấu) để chọn một mục.
.combo bọc input.input[role="combobox"] và danh sách ul.combo-list[role="listbox"][popover="manual"] gồm các li.combo-option[role="option"] (.combo-sub cho mã phụ, aria-disabled="true" cho mục khóa, .combo-empty khi không khớp). Gõ để lọc, không cần gõ dấu ("phu thanh" khớp "Phú Thành"); mũi tên chọn, Enter lấy, Esc đóng; rời ô thì chữ trở về mục đã chọn. Danh sách là popover nên bảng hay hộp thoại không cắt nó: đặt nó trong cùng hộp thoại với ô nhập. Dùng khi danh sách dài hơn khoảng 10 mục; ngắn hơn thì dùng .select. Kit lo hình thức, tenant lo lọc, chọn và vị trí (mẫu ở cuối trang).
  • Công ty Phú ThànhNCC-012
  • Cửa hàng Minh SơnNCC-007
  • Hợp tác xã Nga ThanhNCC-031
  • Nguyễn Văn AnNCC-044
  • Trần Thị BìnhNCC-052
  • Công ty Thanh NhànNCC-060
  • Đại lý Hòa AnNCC-018
  • Nhà phân phối Đông ÁNCC-025
  • Cơ khí Đại PhátNCC-071
  • Xưởng bao bì Thành CôngNCC-081
  • Cửa hàng Ánh DươngNCC-090
  • Công ty Việt HưngNCC-094
  • Kho 1
  • Kho 2
  • Kho 3 (đang kiểm kê)
  • Kho vật tư

Ô chọn hai dòng, nhóm và dòng thêm mới

Ba thêm cho ô chọn có tìm kiếm: giá trị đã chọn hiện hai dòng (tên, rồi mã · điện thoại · công nợ), danh sách có nhóm kèm mô tả, và dòng “+ Thêm khách mới” ở cuối.
data-combo-two-line trên .combo cùng div.combo-value[aria-hidden="true"] (có .combo-value-main và .combo-value-sub rỗng) đặt sau ô nhập: input[role="combobox"] vẫn là ô điều khiển (gõ, aria-*, giá trị là dòng chính; dòng phụ là aria-describedby của nó), còn kit.js chỉ phủ hai dòng lên ô khi danh sách đóng và đã chọn mục. Dòng phụ lấy từ .combo-sub của mục. Ô cao bằng hai dòng dù có chọn hay chưa (không nhảy khi chọn), mỗi dòng cắt bằng “…”. Trong danh sách, dòng phụ nằm dưới tên. li.combo-group[role="presentation"] (.combo-group-title, tùy chọn .combo-group-desc) đứng trước các mục của nhóm: không phải mục chọn, phím mũi tên bỏ qua, và bị ẩn khi bộ lọc không còn mục nào của nhóm; tiêu đề nhóm chỉ để nhìn, nên đặt tên mục sao cho đọc riêng vẫn rõ. li.combo-option.combo-add[role="option"] cuối danh sách (biểu tượng plus rồi chữ): mũi tên với tới được, không bao giờ là giá trị, luôn hiện, dính ở đáy khi danh sách cuộn; Enter hoặc bấm thì danh sách đóng và .combo phát sự kiện combo-add (nổi bọt, detail.query là chữ đã gõ) để tenant mở form thêm.

Hai dòng, đã chọn một khách

  • Nguyễn Văn HùngKH-0012 · 0905 111 222 · Công nợ 12.400.000 đ
  • Công ty TNHH Cơ khí Nam Việt Nha Trang chi nhánh Diên KhánhKH-0007 · 0258 3822 100 · Công nợ 0 đ
  • Trần Thị MaiKH-0031 · 0912 345 678 · Công nợ 3.050.000 đ
  • Lê Quốc BảoKH-0044 · 0977 654 321 · Công nợ 860.000 đ

Ô đóng hiện tên và dòng phụ. Bấm hoặc gõ thì ô trở về một dòng chữ để lọc (gõ “0905” hay “nam viet” đều khớp); dòng quá dài bị cắt bằng “…” (xem ô hẹp bên dưới).

Tên dài trong ô hẹp

  • Nguyễn Văn HùngKH-0012 · 0905 111 222 · Công nợ 12.400.000 đ
  • Công ty TNHH Cơ khí Nam Việt Nha Trang chi nhánh Diên KhánhKH-0007 · 0258 3822 100 · Công nợ 0 đ
  • Trần Thị MaiKH-0031 · 0912 345 678 · Công nợ 3.050.000 đ
  • Lê Quốc BảoKH-0044 · 0977 654 321 · Công nợ 860.000 đ

Khung 15 rem: cả hai dòng đều bị cắt bằng “…”, chiều cao ô không đổi. Đọc đủ ở danh sách (dòng phụ xuống dòng) và ở trình đọc màn hình (tên là giá trị của ô, dòng phụ là mô tả).

Hai dòng, chưa chọn

  • Nguyễn Văn HùngKH-0012 · 0905 111 222 · Công nợ 12.400.000 đ
  • Công ty TNHH Cơ khí Nam Việt Nha Trang chi nhánh Diên KhánhKH-0007 · 0258 3822 100 · Công nợ 0 đ
  • Trần Thị MaiKH-0031 · 0912 345 678 · Công nợ 3.050.000 đ
  • Lê Quốc BảoKH-0044 · 0977 654 321 · Công nợ 860.000 đ

Chưa chọn thì chỉ có gợi ý, nhưng ô vẫn cao bằng hai dòng nên biểu mẫu không nhảy khi chọn.

Nhóm có tiêu đề và mô tả

  • Quỹ tiền mặt cửa hàngTM-01
  • Két sắt văn phòngTM-02
  • Vietcombank Nha TrangVCB-0071
  • BIDV Khánh HòaBIDV-2210
  • Sacombank Diên KhánhSTB-0418
  • Quỹ dự phòngDP-01

Ba nhóm; mũi tên bỏ qua dòng tiêu đề. Gõ “bidv” thì chỉ còn nhóm Ngân hàng, hai nhóm kia ẩn theo.

Dòng thêm mới ở cuối

  • Nguyễn Văn HùngKH-0012
  • Trần Thị MaiKH-0031
  • Lê Quốc BảoKH-0044
  • Thêm khách mới

Chưa bấm “Thêm khách mới”. Gõ “Hoàng” (không khớp ai) rồi bấm dòng đó hoặc Enter sau khi tới nó bằng mũi tên: sự kiện combo-add mang chữ đã gõ.

Tìm không phân biệt dấu: TvKit.fold

Hàm bỏ dấu tiếng Việt của kit, dùng cho danh sách tự lọc mà không cần ô chọn có tìm kiếm.
TvKit.fold(chuỗi) trả về chuỗi viết thường, bỏ dấu thanh và dấu mũ, đ thành d ("Đặng Thị Hương" thành "dang thi huong"). Đây chính là hàm ô chọn có tìm kiếm (.combo) vẫn dùng, nay mở cho trang tự lọc danh sách: TvKit.fold(tên).indexOf(TvKit.fold(từ khóa)) >= 0. Kit không lọc hộ: trang tự ẩn và hiện dòng, tự báo số kết quả.

10 người

  • Nguyễn Văn Đạt
  • Đặng Thị Hương
  • Trần Minh Chiến
  • Lê Thị Thu Hà
  • Phạm Ngọc Vinh
  • Bùi Đức Thịnh
  • Hoàng Quốc Việt
  • Đỗ Thị Ánh Tuyết
  • Vũ Thành Long
  • Ngô Quang Hải

Một input thường lọc một ul bằng TvKit.fold: gõ "dang" ra Đặng, "thinh" ra Thịnh, "d" ra mọi tên có chữ Đ hoặc D.

Ô nhập số: tiền và số lượng

Gõ tới đâu hiện dấu chấm nghìn tới đó, số tiền bằng chữ ngay dưới ô, và nút nhanh 000, 00, Xóa cho ngón tay cái.
input.input[data-money] do kit.js điều khiển: chỉ giữ chữ số, thêm dấu chấm giữa các nghìn khi gõ (1250000 hiện 1.250.000), giữ con trỏ ở chỗ người dùng đang gõ hay xóa (Backspace và Delete nhảy qua dấu chấm sang chữ số bên cạnh), dán chữ thì chỉ lấy chữ số, ô trống vẫn trống. Số thô nằm ở data-value; ô bắn input mỗi lần đổi và change khi rời ô hoặc nhấn Enter, nên lắng nghe change. data-max là số chữ số tối đa (chữ số vượt quá bị từ chối). data-words="id" ghi số tiền bằng chữ tiếng Việt vào phần tử đó (.field-hint hoặc p, tự có aria-live="polite"); chỉ cần có data-words là giới hạn mặc định 12 chữ số (999 tỷ). Lỗi là aria-invalid của biểu mẫu kèm .field-error; required cũng của biểu mẫu. .input-lg là cỡ lớn (2,25rem, cao 3rem, căn phải) cho nhập trên điện thoại. .money-keys chứa button.btn với data-append="000", data-append="00" và data-clear, nối với ô bằng data-money-keys="id" hoặc đứng cùng .field; nút cao và rộng 48px, giữ tiêu điểm trong ô.

Ô thường

đ

Chỉ gõ chữ số; tối đa 12 chữ số. Số thô: (trống)

Thử: gõ 1250000, đặt con trỏ giữa số rồi gõ hoặc xóa, dùng Backspace và Delete cạnh dấu chấm, dán "1.250.000 đ" hay chữ lẫn số.

Ô lớn có đơn vị, số bằng chữ và nút nhanh

đ

Số tiền bằng chữ hiện ở đây.

.input-lg trong .input-affix với data-words và .money-keys (cùng .field nên không cần data-money-keys). Bấm nút, tiêu điểm vẫn ở trong ô.

Ô báo lỗi

đ

Số tiền chi phải lớn hơn 0.

Ô bị khóa

đ

Ô số lượng thập phân

Số lượng kiểu Việt Nam (kg, mét, giờ): dấu phẩy, tối đa 3 số lẻ, và bước 0,5 cho nút tăng giảm.
input.input[data-number] do kit.js điều khiển (inputmode="decimal"): chỉ giữ chữ số và một dấu thập phân; gõ dấu phẩy hay dấu chấm đều được và luôn hiện dấu phẩy. Một dấu chấm hay dấu phẩy đơn lẻ luôn là dấu thập phân ở mọi nơi: "4.162" là 4,162, không phải bốn nghìn. data-decimals (mặc định 3) là số chữ số lẻ tối đa: chữ số thừa bị từ chối (không làm tròn), ô có aria-invalid="true" và phần tử nêu ở data-error hiện ra ("Tối đa 3 số lẻ") cho tới lần sửa hợp lệ tiếp theo hoặc khi rời ô. Khi rời ô hoặc nhấn Enter: bỏ số 0 đầu và dấu phẩy thừa ở cuối (007 thành 7, "12," thành 12, ",5" thành 0,5), còn các số lẻ đã gõ thì giữ ("12,50" vẫn là "12,50"). Số ở dạng dấu chấm nằm ở data-value ("12.50"); ô bắn input và change. Số âm bị cấm trừ khi có data-allow-negative. .input-lg làm ô lớn; đơn vị đặt trong nhãn hoặc .affix. .number-stepper[data-stepper] nhận data-decimals thì data-step-by, data-min, data-max viết được 0,5; ô hiện dấu phẩy, giá trị làm tròn tới data-decimals nên 0,1 + 0,2 vẫn hiện 0,3; không có data-decimals thì như cũ, chỉ số nguyên. Một .affix sau ô cho đơn vị.

Ô thường và ô lớn có đơn vị

Dấu phẩy hoặc dấu chấm đều là dấu thập phân. Số thô: (trống)

kg

Thử: gõ 4.162 (hiện 4,162), 12,50 rồi rời ô (vẫn 12,50), 007 (thành 7), ",5" (thành 0,5), thêm số lẻ thứ tư (bị từ chối), dán "3,25 kg".

Báo vượt số chữ số lẻ

Tối đa 3 số lẻ

Trạng thái ngay sau khi chữ số thứ tư sau dấu phẩy bị từ chối: ô giữ "4,162", aria-invalid và lời báo hiện ra, mất đi khi sửa hợp lệ hoặc rời ô. Với data-allow-negative thì cho phép dấu trừ đứng đầu.

Nút tăng giảm số thập phân, 0 đến 12, bước 0,5

g là giờ. Số thô: 2.5

.number-stepper[data-decimals="1" data-step-by="0,5"]: ↑ ↓ và hai nút bước 0,5; gõ "3.5" hay "3,5" đều được và hiện "3,5". Hai ô nguyên ở mục "Nhập liệu" (số ảnh tối thiểu, số người) không đổi.

Date picker, range và điều hướng kỳ

Ô gõ ngày/tháng/năm kèm lịch, không phụ thuộc ngôn ngữ của trình duyệt.
.datepicker[data-datepicker] chứa input.input (gõ dd/mm/yyyy, hoặc 8 chữ số liền như 05102026), button.datepicker-btn (aria-haspopup="dialog") và .calendar[popover="manual"][role="dialog"]: .calendar-head (tháng trước, chọn tháng, chọn năm, tháng sau), table.calendar-grid[role="grid"] với td[role="gridcell"] chứa .calendar-day, .calendar-foot (Hôm nay, Xóa). Ngày được giữ ở data-value dạng yyyy-mm-dd; data-min, data-max tắt các ngày ngoài khoảng, data-min-input và data-max-input lấy giới hạn từ ô ngày khác (khoảng từ – đến). Ô tự bắn input và change khi ngày đổi; nên nghe change. Trong lịch: ← → ↑ ↓ đổi ngày hoặc tuần, Home và End về đầu và cuối tuần, Page Up và Page Down đổi tháng (thêm Shift thì đổi năm), Enter chọn, Esc đóng. Gõ sai hoặc ngoài khoảng thì ô có aria-invalid="true"; thông báo lỗi là việc của tenant (data-error trỏ tới phần tử lỗi). Khoảng ngày bằng một lịch: .daterange[data-daterange] chứa hai input.daterange-input (từ, đến, mỗi ô giữ data-value), .range-sep, button.datepicker-btn và .calendar.calendar-range: .calendar-presets (các nút .calendar-preset[data-preset]: today, yesterday, 7d, 30d, month, lastmonth, quarter, year; tenant bỏ bớt tùy ý), .calendar-months với hai .calendar-month (dưới 48rem chỉ một tháng, nút chọn nhanh thành hàng), .calendar-foot (gợi ý, Xóa). Bấm ngày đầu rồi ngày cuối (đảo thứ tự cũng được), các ngày giữa được tô; rê chuột hoặc dùng phím mũi tên sẽ xem trước. Chỉ khi chọn xong ngày cuối thì hai ô mới đổi và mới bắn input, change (ô nào đổi thì bắn ô đó, sau khi cả hai đã có giá trị mới). Esc bỏ lần chọn dở. Nút chọn nhanh trùng khoảng đang có thì aria-pressed="true"; nằm ngoài data-min, data-max thì aria-disabled. Gõ vào từng ô cũng được: đầu đến trước đầu từ thì ô đó aria-invalid="true".
Khoảng ngày
Khoảng ngày, một lịch

Một ô, một lịch: bấm ngày bắt đầu rồi ngày kết thúc, hoặc chọn nhanh ở bên trái. Ô giới hạn đến 31/12/2026 (data-max). Trên: hai ô ngày ghép nhau, hợp khi mỗi đầu có nhãn và việc riêng.

Giờ trong ngày

Giờ 24, dạng 14:00

input.input-time[data-time] do kit.js điều khiển: gõ 14:00, 1400, 930, 9h30 hoặc 14 đều thành HH:MM khi rời ô hoặc bấm Enter. Giờ chuẩn nằm ở data-value; giờ quá 23 hoặc phút quá 59 thì ô có aria-invalid="true" và hiện phần tử data-error. Ô tự bắn input và change.

Tháng

.datepicker[data-monthpicker] do kit.js điều khiển: gõ 11/2026, 11-2026 hoặc 112026, hoặc bấm nút để chọn trong 12 tháng. Tháng được giữ ở data-value dạng yyyy-mm; data-min, data-max cùng dạng, tắt các tháng ngoài khoảng (ô thứ hai chỉ nhận năm 2026). Ô bắn input và change. Trong bảng chọn: ← → đổi tháng, ↑ ↓ đổi ba tháng, Page Up và Page Down đổi năm, Home và End về đầu và cuối năm, Enter chọn, Esc đóng.

Điều hướng kỳ: ngày, tháng, quý, năm

Hai nút và nhãn để lùi hoặc tiến từng ngày, tháng, quý hoặc năm, có chặn ở hai đầu; kèm bộ chọn quý, chọn nhanh “Tuần này”, “Từ đầu” và bộ chuyển ngày dính trên đầu.
.period-nav[data-period-nav] (role="group" kèm aria-label) gồm button.btn.btn-icon[data-period="-1"] (mũi tên trái, aria-label “Tháng trước”), span.period-nav-label[aria-live="polite"][aria-atomic="true"] và button.btn.btn-icon[data-period="1"]. Trong nhãn có span.period-nav-text (kit.js viết “Tháng 09/2026”, “Quý 3/2026”, “Năm 2026”, “Thứ Tư, 30/09/2026”) và span.tag.tag-info.period-nav-now[hidden] (“Hôm nay”, hiện khi kỳ chứa ngày hôm nay). Thuộc tính: data-unit (day, month, quarter, year), data-value (yyyy-mm-dd, yyyy-mm, yyyy-Qn, yyyy; để trống thì lấy kỳ chứa hôm nay), data-min và data-max (kỳ đầu và kỳ cuối được phép, cùng các dạng đó; today là hôm nay, hợp với “không mở ngày tương lai”). Ở đầu hoặc cuối nút không đi tiếp được có aria-disabled="true", không phải disabled, nên bàn phím vẫn dừng ở nút và đọc được “không khả dụng”; nút mờ theo luật chung của .btn. Nhãn là chữ thường trong vùng thông báo lịch sự, không có role: role="status" là sai cho một nhãn, và nếu trang biến nhãn thành nút chọn thì chuyển aria-live sang một .visually-hidden bên cạnh. Mỗi lần đổi kỳ, phần tử bắn change (nổi bọt) với detail.value, detail.start, detail.end (ngày đầu và ngày cuối của kỳ, yyyy-mm-dd). Chỉ dùng nút và phím Tab, Enter hoặc Space; không có phím mũi tên. Nút button.btn.btn-link.btn-sm.period-nav-today[data-period-today] (“Hôm nay”) về kỳ chứa hôm nay và tự ẩn khi đang ở đó hoặc khi hôm nay ngoài khoảng cho phép; .period-nav-sticky làm thanh dính đầu khung cuộn (top: var(--sticky-top, 0), nền --surface, đường kẻ dưới). Bộ chọn quý: .datepicker[data-quarterpicker] dùng đúng khung của bộ chọn tháng (đầu bảng là năm với hai mũi tên), .month-grid.month-grid-q có bốn nút .month-btn xếp hai hàng hai cột; gõ Q3/2026, 3/2026, Q3-2026 hoặc Quý 3/2026; giá trị ở data-value="2026-Q3", data-min và data-max cùng dạng. Chọn nhanh của khoảng ngày: data-preset="week" (thứ Hai đến Chủ nhật của tuần này) và data-preset="all" (từ data-all-from, hoặc data-min, đến hôm nay) chạy sẵn; trang đăng ký thêm bằng TvKit.presets.register('fy', 'Năm tài chính', function (today, box) { return { from: '2026-04-01', to: today }; }) rồi viết nút data-preset="fy" trống (nhãn lấy từ đăng ký) hoặc có chữ riêng.

Tháng, chặn hai đầu

Tháng 04/2026
Tháng 07/2026
Tháng 09/2026

Sổ bắt đầu từ 04/2026 và có số liệu đến 09/2026 (data-min="2026-04", data-max="2026-09"): ở tháng đầu nút lùi mờ, ở tháng cuối nút tiến mờ, cả hai vẫn nhận phím Tab. Bấm thử để đổi kỳ.

Quý

Quý 2/2026
Quý 3/2026
Quý 4/2026

Năm

Năm 2024
Năm 2025
Năm 2026

Ngày, không mở ngày tương lai

Thứ Tư, 01/04/2026
Thứ Hai, 21/09/2026
Thứ Hai, 02/11/2026Hôm nay

Sổ công việc bắt đầu từ 01/04/2026 và không mở ngày chưa tới (data-max="today"). Ví dụ thứ ba để trống data-value, nên mở ở hôm nay: nhãn có thẻ “Hôm nay”, nút tiến mờ. Nhãn dài nhất (“Chủ Nhật, 30/08/2026”) vẫn nằm trong một dòng ở khổ 390 px.

Đặt từ script và ngày giả cho mockup

TvKit.periodNav.set(el, value, { unit }) đặt kỳ từ script: value là yyyy-mm-dd, yyyy-mm, yyyy-Qn, yyyy hoặc "today" (làm tròn về kỳ, bị chặn bởi min/max); bỏ trống thì giữ kỳ đang xem. { unit } đổi đơn vị (nhãn nút trước/sau do kit viết đổi theo). Hàm không bắn change: người gọi đã biết; đọc data-value khi cần. data-today="2026-09-30" trên khung cố định hôm nay cho mockup: data-max="today", thẻ “Hôm nay” và nút “Hôm nay” đều theo ngày đó thay vì đồng hồ máy (data-max cũng nhận ngày yyyy-mm-dd trực tiếp). Ngày giả chỉ áp cho khung có thuộc tính, không áp cho lịch hay ô ngày.

Thanh chuyển ngày dính trên đầu

Thứ Hai, 21/09/2026
  • Cắt tôn tấm lớn
    Tổ cắt · 8 giờ
  • Hàn khung cửa sổ
    Tổ hàn 1 · 8 giờ
  • Sơn lót lô 14
    Tổ sơn · 4 giờ
  • Lắp đặt công trình Hải Châu
    Tổ lắp đặt · 8 giờ
  • Vệ sinh xưởng cuối ca
    Tổ cắt · 8 giờ
  • Kiểm vật tư tồn
    Tổ hàn 1 · 8 giờ
  • Chấm công tổ hàn
    Tổ sơn · 4 giờ
  • Giao hàng đơn 0231
    Tổ lắp đặt · 8 giờ

.period-nav.period-nav-sticky trong khung cuộn 15 rem (khung có tabindex="0"): thanh nằm ở đầu khung, các dòng trượt dưới nền --surface, có nút “Hôm nay” (.period-nav-today) vì ngày đang xem không phải hôm nay. Bấm “Hôm nay” thì nút ẩn đi, thẻ “Hôm nay” hiện ra và tiêu điểm chuyển sang nút lùi.

Thứ Hai, 02/11/2026Hôm nay
  • Cắt tôn tấm lớn
    Tổ cắt · 8 giờ
  • Hàn khung cửa sổ
    Tổ hàn 1 · 8 giờ
  • Sơn lót lô 14
    Tổ sơn · 4 giờ
  • Lắp đặt công trình Hải Châu
    Tổ lắp đặt · 8 giờ
  • Vệ sinh xưởng cuối ca
    Tổ cắt · 8 giờ
  • Kiểm vật tư tồn
    Tổ hàn 1 · 8 giờ
  • Chấm công tổ hàn
    Tổ sơn · 4 giờ
  • Giao hàng đơn 0231
    Tổ lắp đặt · 8 giờ

Cùng thanh ở ngày hôm nay: thẻ “Hôm nay” trong nhãn, không có nút “Hôm nay”, nút tiến mờ.

Bộ chọn quý

.datepicker[data-quarterpicker] do kit.js điều khiển, theo mẫu bộ chọn tháng: gõ Q3/2026, 3/2026, q3-2026 hoặc Quý 3/2026, hoặc bấm nút để chọn trong bốn quý. Giá trị ở data-value dạng 2026-Q3; data-min, data-max cùng dạng (ô thứ hai chỉ nhận năm 2026). Ô bắn input và change. Trong bảng chọn: ← → đổi quý, ↑ ↓ đổi hai quý, Page Up và Page Down đổi năm, Home và End về đầu và cuối năm, Enter chọn, Esc đóng.

Khoảng ngày: chọn nhanh Tuần này, Từ đầu và một mục do trang đăng ký

Khoảng ngày với chọn nhanh Tuần này, Từ đầu và Năm tài chính

Ba mục chọn nhanh mới: “Tuần này” (thứ Hai đến Chủ nhật), “Từ đầu” (từ data-all-from="2026-04-01" đến hôm nay) và “Năm tài chính” (từ 01/04 đến hôm nay), do trang mẫu đăng ký bằng TvKit.presets.register('fy', 'Năm tài chính', fn); nút của nó trong mã nguồn để trống, chữ lấy từ lần đăng ký. Mục nằm ngoài data-max (31/12/2026) thì mờ và gạch ngang.

Tệp và ảnh

Vùng thả tệp có ô chọn tệp thật bên trong, kèm danh sách tệp đã chọn.
label.dropzone là cả vùng thả: ô input type="file" nằm trong nó và ẩn bằng .visually-hidden nên bàn phím và hộp chọn tệp của trình duyệt vẫn chạy, nhãn của ô là tiêu đề cộng dòng gợi ý. Tenant đặt data-over lúc đang kéo tệp qua, aria-invalid="true" trên ô khi từ chối tệp và lý do trong .field-error. Tệp đã chọn nằm dưới trong ul.file-list: mỗi tệp một li.file-item với data-state="done|uploading|error"; trạng thái luôn có chữ ở dòng phụ (không chỉ màu), và đang tải thì thêm .bar.
  • don-hang-thang-10.xlsx1,2 MB · Đã tải lên
  • khach-hang.csv340 KB · Đang tải lên 60%
  • ton-kho-cu.xlsx18,5 MB · Quá 10 MB, không tải lên được

Thử kéo một tệp .xlsx vào vùng trên, hoặc một tệp khác loại (.pdf) để xem lỗi: tệp đạt vào danh sách là đã chọn, tệp bị từ chối có lý do bằng chữ và ô báo lỗi. Ở đây không có gì rời khỏi trang; tenant tự tải lên và tự đổi data-state theo tiến độ.

Chọn một tệp Excel để nhập.

Hai trạng thái còn lại: báo lỗi (viền đỏ, lý do ở dưới) và không dùng được.

Ô thêm ảnh và lưới tệp

Ô gạch đứt ở cuối hàng ảnh để chụp hoặc chọn ảnh từ thư viện, và lưới tệp đính kèm có huy hiệu loại tệp bằng chữ.
li.thumb-add là mục cuối của .thumb-list: một label bọc input[type="file"][accept="image/*"][multiple] ẩn (.visually-hidden), biểu tượng và chữ. Có capture="environment" thì điện thoại mở camera sau (biểu tượng camera, chữ “Chụp ảnh”); không có thì mở thư viện ảnh, thêm .thumb-add-pick (biểu tượng image, chữ “Thêm ảnh”): hình dạng giống nhau. Cả ô là nhãn nên cả ô là vùng chạm (rộng bằng một ảnh, cao ít nhất --tap); vòng lấy nét hiện quanh ô khi dùng bàn phím. Không có script: muốn xem trước ảnh vừa chọn thì trang tự nghe change của ô nhập và chèn một li.thumb trước ô này. ul.file-grid > li > a.file-tile (hoặc div.file-tile): svg.file-tile-icon, .file-tile-type (PDF, XLS, DOC, JPG: chữ, không chỉ màu), .file-tile-name (tối đa hai dòng rồi “…”), .file-tile-foot với .file-tile-size và nút xóa tùy chọn. Nút không nằm được trong a, nên ô có nút xóa là div.file-tile và đường dẫn đặt trên tên (a.file-tile-name, trải kín ô). Cột tối thiểu 8 rem: hai cột ở điện thoại, nhiều hơn khi khung rộng.

Hai ảnh, ô chụp và ô chọn từ thư viện

  • Ảnh 1 · nhận 14:32
  • Ảnh 2 · nhận 14:35

Ô chụp (capture="environment") và ô thư viện (.thumb-add-pick) nằm cuối hàng, cùng dáng khung ảnh và thẳng hàng với ảnh. Bấm hoặc Tab tới rồi Enter/Space để mở; thử phím Tab để thấy vòng lấy nét.

Chưa có ảnh nào

Danh sách trống chỉ có ô thêm; cùng cỡ một ảnh nên chỗ của nó không đổi khi ảnh đầu tiên đến.

Cỡ nhỏ, bốn ô một hàng

Với .thumb-list-sm (5 rem) ô thêm vẫn cao hơn 44 px.

Lưới tệp: bốn loại, tên rất dài

Cả ô là đường dẫn (a.file-tile). Tên dài bị cắt ở hai dòng bằng “…” và ô cao bằng nhau trong một hàng; loại tệp là chữ ở góc.

Có nút xóa

div.file-tile với a.file-tile-name trải kín ô và button xóa nằm cuối, có tên riêng cho từng tệp (“Xóa tệp …”).

Trong khung hẹp

Khung 15 rem chỉ đủ một cột; tên vẫn hai dòng, không tràn ra ngoài.

Chọn nhanh: nút chọn một và thẻ quỹ

Nút chọn một trong vài mục cho ngón tay cái: các ô chia đều bề rộng, mục đang chọn mang màu riêng (Thu, Chi, Chuyển; Đi làm, Nghỉ phép; Xong, Có vấn đề).
.seg.seg-block là .seg kéo dài hết bề rộng của khung chứa: các .seg-item chia đều, cao ít nhất --tap (kiểu app: cao hơn 4 px), nhãn một dòng và bị cắt bằng “…” nếu không vừa, nên giữ mỗi nhãn khoảng mười ký tự (bốn mục vừa một thẻ 358 px ở 390 px). Biểu tượng đứng trước nhãn là svg.icon trong mục. data-tone="ok|warn|bad|info" trên một mục cho trạng thái đang chọn màu riêng: nền nhạt của tông (-bg), chữ màu tông và viền 2 px quanh mục, không phải nền đặc. Màu không đứng một mình: mục chọn khác mục còn lại bởi viền, nền và chữ đậm hơn, và chính chữ của mục nói nó là gì. Không có data-tone thì giữ dáng cũ. Vai trò như .seg: role="radiogroup" có tên, role="radio" và aria-checked trên từng mục, một điểm dừng Tab (tabindex="0" ở mục đang chọn), phím mũi tên đổi mục (gallery có script, bỏ qua mục bị khóa). Mục bị khóa dùng disabled; nói lý do ở dòng cạnh nhóm.

Loại phiếu: Chi, Thu, Chuyển

Loại phiếu

Ba mục, ba tông: data-tone="warn", "ok", "info". Mỗi mục có biểu tượng riêng; bấm hoặc dùng phím mũi tên để đổi.

Điểm danh: bốn mục trong thẻ 358 px

Nguyễn Văn An, thứ Hai 2/11

Bốn nhãn, dài nhất “Không phép”, nằm một dòng trong thẻ rộng 358 px đệm 12 px (điện thoại 390 px), cả ở chữ 14 px của kiểu app. Thẻ đệm 20 px chỉ còn 316 px cho nhóm: nhãn dài nhất sẽ bị cắt bằng “…”, nên giữ đệm hẹp hoặc rút nhãn. Không biểu tượng, vì nhãn đã đủ rõ.

Kết quả công việc: có biểu tượng

Kết quả

Biểu tượng đứng trước nhãn; mục “Đang làm” đang chọn với tông info.

Hai mục, không màu

Nhập hay xuất

Không có data-tone: mục chọn nổi trên nền chìm như .seg, chỉ rộng hết dòng.

Một mục bị khóa

Chuyển quỹ

Quỹ dự phòng chỉ kế toán chọn được.

Mục bị khóa mờ đi và phím mũi tên bỏ qua nó; lý do nằm ở dòng chữ bên dưới.

Chọn quỹ: dải cuộn, thẻ gọn, cặp Từ / Đến

Ba cách chọn quỹ khi có nhiều quỹ: dải thẻ cuộn ngang (quỹ âm tô đỏ), thẻ chọn gọn có biểu tượng và báo lỗi khi rút quá số dư, và cặp “Từ” / “Đến” có nút đổi chiều.
.kpi-tabs.kpi-tabs-scroll là .kpi-tabs cuộn ngang (mỗi thẻ rộng theo nội dung, tối thiểu --tab-w 9rem) và cho phép data-tone="ok|warn|bad": thẻ nhuộm màu, con số theo màu của tone. Màu không đứng một mình: số có dấu trừ (.num-neg) và dòng chân có chữ “Âm” (.tag.tag-bad). kit.js cuộn thẻ đang chọn vào khung khi thẻ đổi. .choice-cards.choice-cards-compact: thẻ từ 7,5rem, svg.icon + .choice-title + .choice-sub (số dư); aria-invalid="true" trên input của thẻ cho viền và nền đỏ, lời nhắn ở p.field-error dưới nhóm (nối bằng aria-describedby); nhóm vẫn là role="radiogroup", bàn phím là của ô radio. .pair-pick[data-pair-swap]: hai ô và button.btn.btn-icon.pair-swap[aria-label="Đổi chiều"] ở giữa; dưới 28rem hai ô xếp dọc và mũi tên xoay 90°. kit.js đổi giá trị của hai ô (select, .combo, thẻ chọn radio, ô chữ) và gọi input, change trên cả hai; hai ô phải có cùng danh sách lựa chọn.

Dải quỹ cuộn ngang, một quỹ âm

Sổ quỹ của Tiền mặt: danh sách phiếu thu, chi và chuyển của quỹ này.

Bảy quỹ không vừa khung nên dải cuộn ngang, thẻ bám mép đầu khi cuộn; Techcombank là quỹ âm: thẻ nhuộm đỏ, số có dấu trừ và có nhãn “Âm”. Chọn một thẻ ở xa (hoặc bấm mũi tên, End) thì dải tự cuộn thẻ đó vào khung.

Thẻ chọn quỹ gọn

Rút từ quỹ

Thẻ 7,5rem, biểu tượng, tên quỹ và một dòng số dư. Tab vào nhóm, mũi tên đổi quỹ.

Thẻ có một quỹ không hợp lệ

Rút từ quỹ

Rút quá số dư: còn 2.400.000 đ

Số tiền rút lớn hơn số dư của quỹ đã chọn: thẻ viền đỏ, biểu tượng đỏ và câu báo lỗi dưới nhóm nói rõ lý do. Bỏ aria-invalid khi người dùng đổi quỹ.

Cặp Từ / Đến có nút đổi chiều

  • Tiền mặt
  • Vietcombank ·· 6789
  • Techcombank ·· 1234
  • BIDV ·· 4455
  • Tiền mặt
  • Vietcombank ·· 6789
  • Techcombank ·· 1234
  • BIDV ·· 4455

Rộng: hai ô cạnh nhau, nút đổi chiều ngang hàng với ô. Bấm nút (hoặc Tab tới rồi Enter, Space) thì hai quỹ đổi chỗ; ở đây là hai ô chọn có tìm kiếm.

Cặp Từ / Đến trong khung hẹp

Khung dưới 28rem: hai ô xếp dọc, nút nằm giữa và mũi tên xoay thành lên xuống; đây là hai ô select thường.

Cặp thẻ chọn quỹ

Từ
Đến

Mỗi bên là một nhóm thẻ gọn (hai nhóm radio có tên khác nhau, cùng giá trị value); nút đổi chiều đổi quỹ đã chọn của hai bên.

Chip và tag

Chip là nút nhỏ bấm được (bộ lọc, chọn một, đường dẫn); tag là nhãn chỉ đọc nói trạng thái hoặc mức thay đổi.
.chip bấm được: bật tắt một bộ lọc (aria-pressed), chọn một trong nhóm (role="radio" trong .chip-group[role="radiogroup"]), hoặc dẫn đi (a.chip); thêm .chip-count, .chip-group-scroll, .chip-add, .chip-value + .chip-x cho giá trị đang lọc. .tag chỉ đọc, không bấm: -ok -warn -bad -info, -draft nét đứt, -solid nền đặc, -bare không nền, -lg cỡ lớn, .delta cho mức tăng giảm. Tag luôn có chữ (và thường biểu tượng) đi kèm, màu không đứng một mình. Dòng đầu bảng có chip mã (.code-chip) và bộ lọc .filter-active xem ở phần Panel, bảng và Filter bar. Hai kiểu khác của cùng tag: -solid (đặc) cho một trạng thái cần nặng hơn các trạng thái còn lại trong cột, ví dụ phiếu đã phát hành; -bare (không nền, không viền) cho cột mà dòng nào cũng có nhãn thì bảng rối, ví dụ kết luận đặt cạnh trạng thái; nó là cùng nhãn, cùng cỡ chữ. Nhãn kết quả khác nhau bằng hình chứ không chỉ bằng màu: tròn có dấu tích (đạt), vuông có dấu X (không đạt), tròn nét đứt (chưa đủ số liệu), thoi (không đánh giá), tròn gạch chéo (đã hủy). Màu của trạng thái chờ do tenant chọn: mặc định -warn và clock, nhưng tenant có hai trạng thái cần chú ý (chờ duyệt và bị trả lại) có thể cho chờ duyệt -info và bị trả lại -warn, rồi giữ nguyên ở mọi nơi.

Chip: chọn một và tag lớn

Tất cảChờ duyệtĐã duyệt
Tag lớn

Tag: nhãn trạng thái, nhãn đặc, huy hiệu tăng giảm

Đã duyệtChờ duyệtBị từ chốiThông tinBản nhápĐã hủyĐạtKhông đạtChưa đủ số liệuKhông đánh giáNhãn trạng thái: .tag + -ok -warn -bad -info, .tag-draft nét đứt, icon đứng trước chữ. Bốn nhãn cuối là .tag-bare: cùng nhãn, cùng cỡ chữ, bỏ nền và viền, mỗi kết quả một hình riêng
Đã phát hànhĐạtCần xem lạiKhông đạtChờ duyệtNhãn đặc: .tag-solid, một mình là màu mực
Tăng 24,6%Giảm 7,1%Không đổi 0,0%Huy hiệu tăng giảm: .tag.delta

Chip có số đếm, hàng cuộn, chip thêm và chip đường dẫn

Bốn thứ nhỏ cho bộ lọc trên điện thoại: số đếm trong chip, hàng chip cuộn ngang một dòng, chip “+ Thêm tổ” và chip dẫn đến danh sách đã lọc.
.chip-count đứng sau nhãn trong .chip: chữ số đều bề rộng, nét nhẹ hơn nhãn, cùng màu với chip nên đọc được cả trên chip đang chọn; khi số cần một từ cho trình đọc màn hình thì đặt .visually-hidden trong đó (“20 việc đang làm”). .chip-group.chip-group-scroll giữ cả nhóm trên một dòng và cuộn ngang (role="radiogroup" hoặc role="group" kèm aria-label); chip cuối trong tầm nhìn nên bị mép cắt để thấy hàng còn dài. Đệm quanh hàng giữ cho vòng focus của chip đầu và cuối không bị cắt, margin âm trả lại chỗ (hàng nhô ra 8 px mỗi bên, nên đặt trong lề trang hoặc đệm của thẻ). Hàng chip có thể focus (radio, nút, liên kết) đã vào được bằng bàn phím và tự cuộn theo chip đang focus; chỉ thêm tabindex="0" cho hàng khi không chip nào nhận được focus. Đưa chip đang chọn vào tầm nhìn khi lựa chọn đổi từ nơi khác (scrollIntoView({ block: "nearest", inline: "nearest" })) là việc của tenant. .chip.chip-add là chip làm một việc (“+ Thêm tổ”): nét đứt, icon dấu cộng, màu liên kết; là button type="button", không có aria-checked hay role="radio", nên khi đứng cuối hàng thì hàng là role="group" chứa .chip-group[role="radiogroup"] rồi đến nút. Khác .chip-dashed là chip giá trị. a.chip là chip dẫn đi đâu đó: cùng dáng, cùng chiều cao --tap khi chạm, không gạch chân. Mọi chip mới đều mang lớp .chip nên đã nằm trong quy tắc cao --tap của con trỏ thô.

Bộ lọc trạng thái có số đếm

Tất cả24 ngườiĐang làm20 ngườiĐã nghỉ4 người

.chip-count sau nhãn; chữ ẩn “người” cho trình đọc màn hình. Bấm hoặc dùng phím mũi tên để đổi.

Hàng chip cuộn ngang một dòng

Tất cả24Tổ hàn 15Tổ hàn 24Tổ cắt3Tổ lắp ráp3Tổ sơn2Tổ điện2Tổ kho2Tổ vận chuyển1Tổ bảo trì2

Mười chip trên một dòng trong khung 32 rem (hẹp hơn nữa ở điện thoại): cuộn ngang, thanh cuộn mảnh. Chọn chip bằng phím mũi tên thì hàng tự cuộn tới chip đó.

Chip thêm ở cuối hàng

Tất cảTổ hàn 1Tổ hàn 2Tổ cắtTổ lắp rápTổ sơnTổ điệnTổ khoTổ vận chuyểnTổ bảo trì

.chip-add là nút (không phải radio) ở cuối hàng: nét đứt, dấu cộng, màu liên kết. Cuộn đến cuối hàng để thấy nó; các chip radio nằm trong .chip-group[role="radiogroup"] riêng.

Chip là đường dẫn

a.chip với biểu tượng cảnh báo và số đếm: cùng dáng với chip thường, không gạch chân, cao --tap khi chạm. Biểu tượng và chữ nói đây là việc có vấn đề, màu không đứng một mình.

Checklist: dòng việc có nút, nút tick, ảnh

Danh sách việc để tích, nút tròn 56px cho tay ướt và ảnh minh chứng có trạng thái.
Dòng việc là li.work-row: phần chữ là liên kết mở dòng, nút tích nằm riêng ở mép phải ở mọi độ rộng. Việc cần ảnh hiện nút máy ảnh thay cho nút tích, để người làm không gặp nút bấm mà không có tác dụng. Mỗi ảnh nói trạng thái bằng chữ, không chỉ bằng màu viền.

Dòng việc có nút

Mép trái có vạch màu theo data-tone (Trễ, Chưa đạt); thẻ trạng thái luôn nói bằng chữ. Nút tích không co lại khi thẻ hẹp.

Nút tick

Chưa tích, đã tích (aria-pressed="true"), chụp ảnh (.tick-primary) và bị khóa (aria-disabled="true").

Ảnh thu nhỏ

  • Ảnh 1 · nhận 14:32
  • Ảnh 2 · đang gửi
  • Chưa gửi được

Đã nhận, đang gửi (thanh tiến độ .bar) và lỗi (viền đỏ, chữ nói rõ và nút Thử lại). Ảnh thật là <img> trong .thumb-img.

.thumb-list-sm: bốn ảnh trên một hàng ở điện thoại.

Ảnh vuốt ngang

Ảnh 1 trên 3 · nhận 14:32
Ảnh 2 trên 3 · nhận 14:33
Ảnh 3 trên 3 · nhận 14:35

.media-strip: mỗi ảnh rộng 86% hàng (tối đa 26rem) nên ảnh kế hiện ở mép; vuốt bằng tay, hoặc Tab vào hàng rồi dùng phím mũi tên. Chữ dưới ảnh nói đó là ảnh nào và nhận lúc nào.

Thẻ đóng mở

Khu vực tầng 1

4 việc, đã xong 3

Lau quầy, quét sàn, đổ rác, kiểm tra nhà vệ sinh.

Khu vực tầng 2

6 việc, chưa bắt đầu

Sắp xếp bàn ghế, lau cửa kính, hút bụi hành lang.

details.panel.panel-fold không cần script: summary.panel-head giữ tiêu đề, dòng phụ .panel-sub và biểu tượng .panel-caret ở cuối; .panel-body là phần mở ra. Thêm open để mở sẵn. Đóng thì đầu thẻ không có đường kẻ dưới; mở thì mũi tên quay lên.

KPI card và số liệu tổng quan

Một con số kèm tên, mức thay đổi so với kỳ trước và một dòng giải thích, cho đầu trang tổng quan.
.kpi-row xếp nhiều thẻ trên lưới (mỗi thẻ từ 13rem). Trong .kpi chỉ có ba phần: .kpi-head (.kpi-label và .kpi-icon nếu cần), .kpi-value (số lớn, .kpi-unit cho đơn vị) và .kpi-foot (huy hiệu .tag.delta, chữ ngữ cảnh, thanh .bar nếu có tiến độ). Ba phần này giúp các thẻ cùng hàng thẳng hàng với nhau. Thẻ là <a> khi bấm vào mở danh sách sau con số. data-tone="ok|warn|danger" thêm vạch ở mép trái, dày bằng vạch của notice; chữ ở .kpi-foot nói cùng điều đó nên màu không đứng một mình. Số luôn nằm trên một dòng: từ 100.000 trở lên viết gọn (k nghìn, M triệu, B tỷ, một số lẻ, dấu phẩy), số đầy đủ để trong .visually-hidden cho trình đọc màn hình và title cho chuột. Một hàng viết cùng một kiểu, nên cả hàng viết gọn khi có một thẻ cần. Nếu thẻ là cái chuyển nội dung bên dưới, dùng .kpi-tabs. .kpi-value đọc --font-figure (kiểu chữ riêng cho con số của tenant); không đặt thì dùng chữ của trang.
Doanh thu tháng 10

128.450.000 đồng

Tăng 12,4% so với tháng 9

Phiếu chờ duyệt

6

3 phiếu đã chờ quá 2 ngày

Công nợ quá hạn

34.200.000 đồng

Giảm 7,1% 4 khách quá 30 ngày

Mẫu đạt trong tháng

94,2%

Mục tiêu 95%, còn thiếu 0,8 điểm

Thẻ thứ hai và thứ ba là liên kết (rê chuột đổi nền) và có vạch màu; thẻ đầu và thẻ cuối không phải liên kết. Số tiền từ 100.000 trở lên viết gọn nên không bị xuống dòng. Huy hiệu tăng giảm dùng .tag.delta của kit: ở thẻ công nợ, giảm là tốt nên huy hiệu để màu xanh.

Tiền mặt tại quỹ cửa hàng chính

845.200₫

Giảm 4,2% so với tháng 9, thấp nhất từ đầu năm

Mẫu đạt

94,2%

Đạt mục tiêu

Hai thẻ cùng hàng, tên thẻ đầu dài hai dòng và dòng chân dài hơn: thẻ cao bằng nhau, số và dòng chân của hai thẻ vẫn thẳng hàng. Số dưới 100.000 viết đủ.

Chỗ đặt biểu đồ hoặc bảng doanh thu theo ngày của tháng 10.

.kpi-tabs: các thẻ là bộ chuyển cho phần bên dưới. Thẻ đang chọn nổi lên trên nền chìm, như .seg; bấm hoặc dùng phím mũi tên, Home, End để đổi. Thẻ là <button role="tab"> nên ba phần bên trong viết bằng <span>, và không dùng data-tone ở đây.

Thẻ trạng thái có nút

Đã vào ca

7:18

Ca sáng 7:00 – 15:00, đã làm 3 giờ 12 phút

Giờ làm hôm nay

0giờ

Chưa vào ca

Mạng đang dùng

Wifi quán

Chưa khớp mạng cửa hàng đã lưu, nên chưa thể vào ca

.kpi-action đặt sau .kpi-foot, chứa một hoặc hai .btn. Thẻ có nút là div.kpi, không phải a.kpi. Trong một hàng, thẻ không có nút vẫn cao bằng thẻ có nút và số vẫn thẳng hàng, chỉ thừa chỗ dưới dòng chân.

Con số đủ đồng, vừa thẻ

Sổ tiền cần số đầy đủ ("549.500.000 đ"), nên con số thu nhỏ cho vừa thẻ thay vì viết gọn hoặc tràn.
.kpi-value.kpi-value-fit: con số vẫn một dòng, cỡ chữ tự co theo bề rộng của chính thẻ (container query, không script) từ --fs-3xl xuống tối thiểu --fs-md. Mặc định tính cho 14 ký tự (chữ số, dấu chấm và đơn vị); đặt --fit-chars trên .kpi-row (không đặt trên từng thẻ, để các thẻ cùng cỡ) khi số tiền ngắn hơn hoặc dài hơn: 12 cho "549.500.000 đ", 16 cho nghìn tỷ. Thẻ cần có bề rộng riêng (một ô của .kpi-row hoặc đặt width). .kpi-row-pair trên hàng đúng hai thẻ giữ hai thẻ cạnh nhau tới điện thoại hẹp nhất, nơi hàng thẻ bình thường xuống một thẻ một dòng. Số quá dài so với thẻ ở cỡ nhỏ nhất thì viết gọn: bộ k / tr / tỷ cho người dùng Việt (k nghìn, tr triệu, tỷ), xem COMPONENTS.md.

Hai thẻ cạnh nhau ở bề rộng điện thoại

Khách còn nợ mình

549.500.000đ

6 khách còn nợ

Mình còn nợ NCC

270.600.000đ

5 nhà cung cấp

Hàng 22rem (điện thoại) với .kpi-row-pair: hai thẻ vẫn ngang hàng, số vừa khít và không xuống dòng. Thiếu .kpi-row-pair thì dưới 27rem mỗi thẻ một dòng.

Hai thẻ ở bề rộng vừa và rộng

Quỹ tiền mặt

549.500.000đ

Sau phiếu hôm nay

Quỹ Vietcombank

1.270.600.000đ

Sau phiếu hôm nay

Quỹ tiền mặt

549.500.000đ

Sau phiếu hôm nay

Quỹ Vietcombank

1.270.600.000đ

Sau phiếu hôm nay

Cùng hai con số ở hàng 30rem và 44rem: càng rộng càng gần cỡ đầy đủ. Hai số dài ngắn khác nhau trong cùng hàng có cùng cỡ chữ, nên không lệch nhau.

Một thẻ lẻ, và số dài hơn mặc định

Tổng quỹ

1.549.500.000đ

Một thẻ lẻ rộng 22rem

Thẻ lẻ ở điện thoại còn chỗ rộng nên giữ cỡ đầy đủ. --fit-chars:16 báo trước số dài hơn mặc định để cỡ chữ không vượt thẻ; --fit-chars:12 trên hàng đầu tiên cho số 9 chữ số to hơn mức mặc định.

Bộ rút gọn tiếng Việt, khi chỉ cần liếc

Doanh thu quý 3

549.500.000 đồng

tr = triệu

Doanh thu năm

2.340.000.000 đồng

tỷ = tỷ đồng

Bảng báo cáo, không phải sổ quỹ: số viết gọn bằng k (nghìn), tr (triệu), tỷ, kèm số đầy đủ cho trình đọc màn hình. Kit không tự định dạng số.

KPI: delta trung tính, thẻ nhấn mạnh, khối chờ tải

Ba bổ sung cho thẻ KPI: mũi tên delta không tốt không xấu (chi tăng 9,6%), thẻ cần nhấn mạnh hơn các thẻ khác, và khối chờ tải có chiều cao.
.tag.delta.delta-neutral: huy hiệu xu hướng chữ --muted trên nền --sunk, không sắc xanh hay đỏ, cho con số mà tăng hay giảm đều không tốt không xấu; mũi tên và chữ ẩn “Tăng ” / “Giảm ” vẫn có. .kpi[data-emphasis] (chỉ cần thuộc tính): thanh 4 px màu --primary ở đỉnh thẻ và con số lớn gấp 1,25 lần (cả giao diện app), nền vẫn --surface; là trọng lượng chứ không phải trạng thái nên không cần chữ đi kèm; chỉ nên một thẻ nhấn mạnh trong một hàng; có thể đi cùng data-tone. Màu được kiểm: chỉ --text và --primary trên --surface. span.skel-block là bản khối của .skel: --skel-w (mặc định 100%) và --skel-h (mặc định 3rem), nhịp nháy và màu của kit, tắt nhịp khi prefers-reduced-motion. Khối là trang trí: aria-hidden="true" trên từng khối, aria-busy="true" trên phần chứa chúng và một dòng .visually-hidden[role="status"] nói đang tải gì. Cách xếp khung chờ của một thẻ (ba phần của .kpi, các dòng của bảng) là việc của trang: đặt khối vào đúng thẻ hoặc bảng thật.

Delta tốt, xấu và trung tính

Doanh thu

128,5tr

Tăng 12,4%so với tháng 9

Nợ phải trả

86,0tr

Tăng 7,2%so với tháng 9

Tổng chi

66,1tr

Tăng 9,6%so với tháng 9

Doanh thu tăng là tốt (xanh), nợ phải trả tăng là xấu (đỏ), chi tăng thì chưa biết tốt hay xấu (xám: .delta-neutral). Mũi tên và chữ ẩn nói hướng ở cả ba.

Thẻ nhấn mạnh

Lãi gộp tạm tính

119,5tr

của 6 đơn giao, chưa gồm nhân công

Đơn nhận

9

trong quý 3

Quá hạn

2

Cần xemđơn chậm giao

Thẻ giữa là thẻ thường để so. Thẻ nhấn mạnh có thanh --primary ở đỉnh và số lớn hơn 1,25 lần; thẻ thứ ba có cả data-tone="danger" (thanh đỏ ở mép đầu, kèm chữ “Cần xem”).

Thẻ nhấn mạnh với số giữ nguyên cả số tiền (.kpi-value-fit)

Tiền mặt

549.500.000đ

cuối ngày 30/09

Ngân hàng

312.080.000đ

cuối ngày 30/09

Số vẫn vừa một dòng: kích thước tối đa của thẻ nhấn mạnh lớn hơn, nhưng không bao giờ rộng hơn thẻ.

Khối chờ tải: thẻ KPI và bảng

Đang tải số liệu
Đang tải danh sách phiếu

Khối trong khung thẻ KPI (ba khối cao 1, 2,25 và 1 rem) và ba khối cao 3 rem xếp như dòng bảng, khối cuối ngắn hơn (--skel-w: 70%). Nhịp nháy dừng khi người dùng chọn giảm chuyển động.

Dải tóm tắt

Dải phẳng hai đến bốn ô trong một panel: tên mờ phía trên, con số phía dưới, ngăn nhau bằng đường kẻ mảnh; không biểu tượng, không bóng đổ. Ba ô vẫn một hàng ở phone.
dl.stat-strip > div.stat-strip-item > dt (tên, mờ, được xuống dòng) + dd (con số: --font-figure, số cùng độ rộng, không bao giờ xuống dòng; đặt .num-in, .num-out, .num-neg trên span bên trong, dấu viết trong chữ với dấu trừ thật; một dd được chứa hai con số, chúng xuống dòng ở giữa). Hai hoặc ba ô luôn nằm một hàng: ô co lại, dt xuống hai dòng, cỡ chữ con số tự giảm theo bề rộng của dải (container query, không script) từ --fs-lg xuống --fs-sm, không nhỏ hơn; --fit-chars báo độ dài khác 14 ký tự. Bốn ô xếp hai nhân hai khi dải hẹp hơn 24rem. .stat-strip-item-total cho ô quan trọng nhất: con số đậm hơn, dt màu chữ chính. Dải là container nên cần bề rộng riêng: là khối trong panel, không phải phần tử flex co theo nội dung. Màu: chỉ --text, --muted, --ok, --danger trên --surface, đã được kiểm.

Thu, chi, chênh lệch (ô cuối đậm, màu theo dấu)

Quý 3 năm 2026

Thu
+ 108.600.000đ
Chi
− 66.072.000đ
Chênh lệch
+ 42.528.000đ

Ba ô một hàng ở mọi bề rộng. Chênh lệch dương dùng .num-in (xanh, có dấu “+”).

Chênh lệch âm, số dài nhất

Tháng 10 năm 2026

Thu
+ 24.800.000đ
Chi
− 37.200.000đ
Chênh lệch
− 12.400.000đ

“− 12.400.000 đ” còn nguyên một dòng trong ô khoảng 100 px ở phone nhờ cỡ chữ tự giảm. Số âm dùng .num-neg (đỏ); dấu trừ vẫn nằm trong chữ.

Đầu kỳ, vào / ra, cuối kỳ (số thường, một ô có hai số)

Tiền mặt ngày 30/09

Đầu 29/09
5.380.000đ
Vào / ra
+29,6tr −16,5tr
Cuối 30/09
18.450.000đ

Số không có dấu thì không cần lớp. Ô “Vào / ra” có hai số, mỗi số không xuống dòng, hai số có thể xuống dòng ở giữa khi ô hẹp.

Bốn ô (hai nhân hai khi hẹp hơn 24rem) và hai ô

Đơn hàng DH-2026-0031

Giá trị
225.000.000đ
Đã thu
+ 135.000.000đ
Còn phải thu
90.000.000đ
Lãi gộp tạm tính
+ 91.702.000đ

Bốn ô: một hàng khi dải rộng từ 24rem, hai nhân hai khi hẹp hơn (như ở phone).

Tổng chi Thuê ngoài gia công
− 62.400.000đ
Số phiếu
14phiếu

Hai ô, tên dài xuống hai dòng, con số vẫn nằm sát đáy ô nên các số thẳng hàng.

Trong khung hẹp: 21rem còn một hàng, 15rem thành từng dòng

Thu
+ 108.600.000đ
Chi
− 66.072.000đ
Chênh lệch
+ 42.528.000đ

Khung 21rem (dải khoảng 18rem): ba ô vẫn một hàng, con số giảm tới cỡ nhỏ nhất (--fs-xs).

Thu
+ 108.600.000đ
Chi
− 66.072.000đ
Chênh lệch
+ 42.528.000đ

Khung 15rem: dải hẹp hơn 17,5rem thì ba con số không còn đủ chỗ cạnh nhau, mỗi ô thành một dòng (tên ở đầu, số ở cuối) thay vì chồng lên nhau.

Ô có tone: số liệu có ngưỡng

Kiểm tra dữ liệu nhập

Đạt
284
Cảnh báo
12
Lỗi
3

data-tone="ok|warn|bad" trên .stat-strip-item tô màu con số (như tone của thẻ KPI); tên ô vẫn nói nghĩa bằng chữ (Đạt, Cảnh báo, Lỗi) nên màu không đứng một mình.

Số và mã chứng từ

Thu, chi, số dư âm và số của phiếu đã hủy, để mắt đọc nghĩa của con số ngay trong sổ.
.num-in (xanh: tiền hoặc hàng vào), .num-out (màu chữ: ra), .num-neg (đỏ: số dư dưới 0), .num-void (mờ, gạch ngang: phiếu đã hủy) đứng trên td.num, trên span trong dòng danh sách hoặc trên .kpi-value. Dấu viết trong chữ (+ 1.250.000, − 350.000 với dấu trừ thật U+2212 và dấu cách không ngắt sau dấu) và loại phiếu nằm ở tiêu đề cột hoặc trong một chữ .visually-hidden, nên màu không phải dấu hiệu duy nhất. .num-unit là đơn vị sau con số (đ, kg), nhỏ hơn một bậc. Gõ --font-figure trong tokens.json của tenant nếu con số dùng kiểu chữ hẹp hơn chữ thường. Font Barlow không có ký hiệu ₫: viết đ.

Sổ quỹ tiền mặt

Quý 3 · 2026
Diễn giảiNgàyThuChiSố dư
Khách Tuấn Phát trả đợt 230/09/2026+ 12.450.000đ18.450.000đ
Mua que hàn, đá cắt30/09/2026− 350.000đ6.000.000đ
Ứng lương tháng 9 (vượt số dư)29/09/2026− 7.200.000đ− 1.200.000đ
Mua nhầm ống 40 Đã hủy28/09/2026− 90.000đ

Trong bảng dạng thẻ (.tbl-cards, đọc được trên điện thoại): tiêu đề cột Thu và Chi nói loại phiếu, dấu +/− nằm trong số. Dòng đã hủy dùng .num-void cho từng số khi dòng không phải tr.row-void.

Quỹ Vietcombank

− 1.200.000đ

Đã chi quá số dư 2 phiếu

Quỹ tiền mặt

+ 18.450.000đ

Còn sau phiếu hôm nay

Trên thẻ KPI: .num-neg đi với data-tone="danger" và câu chữ nói rõ, nên màu không đứng một mình.

Mã chứng từ

Số phiếu hoặc mã đơn viết bằng chữ đơn cách, không xuống dòng, có bản nền đậm cho số ở đầu phiếu.
.doc-code: chữ --font-mono, đậm, số cùng độ rộng, một dòng, không khung, để mã đọc ra là mã khi nằm trong tiêu đề, ô bảng hay dòng danh sách. .doc-code-solid: cùng mã đó trên tấm nền đậm (nền --heading, chữ --surface, cặp màu đã được check-palette.mjs kiểm đạt AA ở cả 7 bảng màu) cho số ở đầu phiếu. Không phải .code-chip (khung nhỏ cho mã 2–4 ký tự). Khi in, tấm nền thành khung viền cùng màu chữ vì trình duyệt thường không in nền.

Trong tiêu đề

Phiếu chi PC-2026-0412

Đơn hàng DH-2026-0087

Cỡ chữ theo tiêu đề quanh nó. .doc-code-solid cho số ở đầu phiếu, .doc-code khi mã chỉ là một phần của dòng.

Trong ô bảng

Số phiếuNgàyDiễn giảiSố tiền
PC-2026-041230/09/2026Mua que hàn, đá cắt− 350.000đ
PT-2026-019830/09/2026Khách Tuấn Phát trả đợt 2+ 12.450.000đ

Trong dòng danh sách việc

Ở bảng thẻ (.tbl-cards) mã đứng ở ô đầu th[scope="row"]; trong dòng việc nó đứng trước tên, trên một dòng, không bị cắt.

Avatar

Avatar chữ cái đầu hoặc ảnh, chấm trạng thái, nhóm và ghép với tên.
.avatar (-xs -sm -lg -xl, -square, màu -primary -ok -warn -bad -info) chứa chữ cái đầu hoặc <img>; .avatar-dot báo trạng thái trực tuyến (ghi chữ trong .visually-hidden, vì màu một mình không nói gì); .avatar-group chồng nhiều người; .avatar-label ghép avatar với tên và một dòng phụ. Cạnh tên thì avatar chỉ để trang trí (aria-hidden="true", alt=""); đứng một mình cần role="img" và aria-label. Chữ cái đầu do tenant tính.
xs 24, sm 32, mặc định 40, lg 48, xl 64
màu, vuông, ảnh
trực tuyến, vắng, bận, ngoại tuyến
+3 Nhóm: .avatar-group
Nhân viênVai tròTrạng thái
Nguyễn Văn Anan.nguyen@email.vnKế toánĐang làm
Trần Thị Bìnhbinh.tran@email.vnThủ khoĐang làm
Lê Văn Cườngcuong.le@email.vnQuản đốcNghỉ việc

Biểu đồ và xếp hạng

Cột nhóm Thu/Chi theo tháng vẽ bằng HTML và CSS (không SVG, chữ không co theo khung): chú giải, trục, vạch cùng kỳ năm trước; trên điện thoại biểu đồ cuộn ngang trong khung riêng, bảng số liệu đi kèm là nội dung thật.
figure.chart-cols (tên khác .chart của giao diện app, nên hai bên không đụng nhau) gồm figcaption.cc-title, ul.cc-legend (mỗi mục là span.cc-swatch[data-series="1"] cùng một chữ: Thu, Chi; mục cuối .cc-swatch-prev “Cùng kỳ năm trước”), rồi .cc-scroll > .cc-body > ol.cc-axis[aria-hidden] (mỗi li là một mốc, style="--t:.5" là vị trí từ 0 đến 1 của giá trị lớn nhất) và .cc-plot > .cc-group (một tháng) > .cc-cols > .cc-col[data-series][style="--v:.78"] (span, hoặc a khi cột là đường dẫn; bên trong có span.visually-hidden đọc giá trị và, nếu có, span.cc-mark[style="--v:.62"] là vạch cùng kỳ năm trước), kèm .cc-label (“T9”). --v là tỉ lệ so với giá trị lớn nhất của trục (0 đến 1); --cc-rows (mặc định 4) là số khoảng của đường kẻ ngang, phải khớp với các mốc của trục; --cc-h, --cc-group-w, --cc-col-max chỉnh chiều cao, bề rộng tối thiểu của nhóm và bề rộng tối đa của cột. Màu lấy từ --series-1 đến --series-4 và --series-prev của bảng màu (mọi bảng màu đặt đủ, qua cổng 3:1 trên --surface và --bg); màu không đứng một mình: chuỗi 1 đặc, chuỗi 2 sọc chéo, chuỗi 3 chấm, chuỗi 4 sọc dọc, chú giải lặp lại cả mẫu lẫn chữ. Hỗ trợ đọc: lời của biểu đồ nằm ở .cc-plot[role="img"][aria-label] (đọc cả chuỗi bằng chữ), không ở figure, vì khung cuộn phải focus được (tabindex="0", role="region", aria-label; thêm khi biểu đồ có thể cuộn, tức nhiều nhóm hơn mức vừa khổ hẹp nhất) mà bên trong role="img" không được có phần tử focus. Khi cột là đường dẫn thì .cc-plot là role="group" và mỗi liên kết có tên từ chữ .visually-hidden của nó. Nội dung thật là bảng số liệu: luôn đặt một table (hiện, hoặc .visually-hidden) ngay sau biểu đồ. Dưới ngón tay cột liên kết rộng ít nhất --tap; trục dính ở mép đầu khi cuộn, không bao giờ cuộn cả trang. Biểu đồ trống không có .cc-group, chỉ có p.cc-empty.

Thu và chi 12 tháng, kèm vạch cùng kỳ năm trước

Thu và chi theo tháng, năm 2026 (triệu đồng)
  • Thu
  • Chi
  • Cùng kỳ năm trước
Thu và chi theo tháng năm 2026, triệu đồng
ThángThu 2026Thu 2025Chi 2026Chi 2025
T1248230201190
T2262251219205
T3291274236221
T4305288262240
T5338310274255
T6347320290270
T7352,4340281,3268
T8421,5380322,5300
T9312,5330238,9255
T10396370301285
T11408385318298
T12452420361330

Hai chuỗi, mười hai nhóm: ở khổ điện thoại biểu đồ cuộn ngang trong khung của nó (khung có tabindex="0" để cuộn bằng bàn phím), trục tung dính ở mép trái, chữ nhãn vẫn 12 px. Vạch đậm trên mỗi cột là cùng kỳ năm trước. Bảng bên dưới là nội dung thật.

Ba tháng, cột là đường dẫn

Quý 3, ba nhóm: ở khổ rộng vừa khít, ở khổ điện thoại cuộn nhẹ trong khung (có tabindex), vì mỗi cột liên kết rộng ít nhất 44 px. Mỗi cột là a.cc-col (rê chuột thì nền cột đậm lên, Tab thì có khung focus; dưới ngón tay mỗi cột rộng ít nhất 44 px). .cc-plot là role="group", mỗi liên kết có tên “Thu tháng 7: 352,4 triệu, cùng kỳ năm trước 340 triệu”.

Chưa có số liệu

Thu và chi tháng 11 năm 2026 (triệu đồng)
  • Thu
  • Chi
Thu và chi tháng 11 năm 2026
ThángThuChi
Chưa có số liệu trong kỳ.

Kỳ chưa có phiếu: giữ trục và chú giải, trong vùng vẽ có một câu p.cc-empty và aria-label nói rõ chưa có số liệu; bảng bên dưới cũng nói như vậy.

Một chuỗi

Chi vật tư theo tháng, quý 2 và quý 3 (triệu đồng)
  • Chi vật tư
  • Cùng kỳ năm trước
Chi vật tư theo tháng, triệu đồng
ThángNăm 2026Năm 2025
T4120110
T5140150
T6160140
T7155160
T8172150
T9138145

Một chuỗi (chi vật tư) với vạch cùng kỳ năm trước; chú giải vẫn có chữ, trục tối đa 200 triệu. Sáu nhóm vẫn có khung cuộn có tên, vì ở khổ hẹp chúng không vừa.

Bốn chuỗi: bốn kiểu tô

Chi theo nhóm, quý 3 năm 2026 (triệu đồng)
  • Vật tư
  • Lương
  • Thuê ngoài
  • Khác
Chi theo nhóm, quý 3 năm 2026, triệu đồng
ThángVật tưLươngThuê ngoàiKhác
T7150802031
T8180903517
T9120851816

Cả bốn mẫu tô: đặc, sọc chéo, chấm, sọc dọc; kể cả khi in đen trắng hay với người khó phân biệt màu, chú giải và mẫu tô vẫn cho biết cột nào là chuỗi nào.

Danh sách xếp hạng có thanh

Các khoản lớn nhất xếp từ trên xuống: tên và dòng phụ, một thanh ngang, con số bên phải; trên điện thoại thanh nằm dưới tên. Cả dòng là một liên kết.
ul.rank-list (hoặc ol khi thứ tự quan trọng) > li > a.rank-row (div.rank-row khi dòng không dẫn đi đâu) gồm span.rank-no (tùy chọn: hạng, mờ, số căn đều; aria-hidden="true"), span.rank-name (đậm, được xuống dòng), span.rank-sub (tùy chọn, mờ: “Trả nhà cung cấp · 42 phiếu”), span.bar.rank-bar[aria-hidden="true"] (chính là .bar của bộ kit, bề rộng lấy từ --v: phần trăm so với dòng lớn nhất) và span.rank-val (con số kèm .num-unit, không xuống dòng, dùng --font-figure). Cả dòng là liên kết: cao ít nhất --tap, rê chuột thì nền --surface-hover, khung focus của bộ kit vẽ bên trong dòng; chữ vẫn là --text và --muted, hai màu đã qua cổng trên nền khi rê chuột. Màu thanh theo data-tone="ok|warn|bad" trên dòng, mặc định --primary; thanh chỉ là trang trí và lặp lại con số, nên trạng thái màu phải có chữ (trong .rank-sub hoặc một .visually-hidden trong tên). Khi danh sách rộng dưới 28 rem (@container của chính danh sách, không phải của cửa sổ) thanh xuống dưới tên và con số vẫn ở cuối dòng tên. Danh sách là container-type: inline-size, nên cần có bề rộng của cột chứa nó.

Tám khoản chi nhiều nhất, có hạng và dòng phụ

Mỗi dòng là liên kết tới các phiếu tạo ra con số (thanh là tỉ lệ so với khoản lớn nhất, 386,2 triệu). Hạng nằm trong .rank-no (aria-hidden, vì danh sách là ol).

Có liên kết và không có liên kết

Có liên kết

Không có liên kết

  • Đơn 0231 – Cổng sắt Hải Châu96 giờ
  • Đơn 0228 – Khung nhà xe72 giờ
  • Đơn 0235 – Lan can inox55 giờ
  • Đơn 0219 – Mái tôn kho31 giờ

Trái: a.rank-row, rê chuột hoặc Tab thì thấy nền và khung focus. Phải: div.rank-row, không có nền khi rê chuột, không có .rank-no và không có dòng phụ.

Màu thanh theo trạng thái

data-tone “ok”, “warn”, “bad” đổi màu thanh; trạng thái vẫn được nói bằng chữ trong dòng phụ (“Vượt định mức 12%”) và trong chữ ẩn của tên. Thanh “bad” đầy vì đã tới 100% bề rộng, con số ghi 112%.

Cột hẹp: thanh xuống dưới tên

Ngay trên máy tính, một cột rộng 20 rem (dưới 28 rem) đã dùng cách xếp của điện thoại: thanh nằm dưới tên, con số vẫn ở cuối dòng tên. Quy tắc theo bề rộng của danh sách, nên cột bên của một trang rộng cũng được.

Một tên rất dài

Tên dài thì xuống dòng và không đẩy con số hay thanh ra ngoài; con số luôn trên một dòng.

Thanh tỉ lệ (100 %)

Các phần của một tổng nằm cạnh nhau trên một thanh: xếp loại của một đơn vị, trạng thái phiếu của một kỳ. Mỗi dòng bảng một thanh, các thanh tự thẳng hàng; có dòng tổng, đoạn là liên kết và trạng thái chưa có đánh giá.
div.share-bar (role="img" và một câu đủ số ở aria-label, hoặc role="group" khi đoạn là liên kết; data-tip-title là dòng đầu tooltip) > span.share-seg hoặc a.share-seg với style="--v:.034" (tỉ lệ thô, không phải số đã làm tròn), màu theo data-grade="1..5", data-tone hoặc data-series, data-tip là dòng của đoạn trong tooltip. .share-bar-lg cao 24px với span.share-lab trong đoạn; .share-bar-empty > span.share-empty khi mẫu số là 0. Thang xếp loại --grade-1 (A+, xanh đậm, chữ trắng) đến --grade-5 (D, đỏ đậm); --grade-2 và --grade-3 là màu nhạt, dưới 3:1 trên nền trắng: được phép vì đoạn có chữ cái hoặc bảng có số, khe 2px tách hai đoạn và chú giải gọi tên.

Một thanh có chữ trong đoạn

Trạm cấp cứu mỏ Hạ Long, Tháng 9/2026

  • A+
  • A
  • B
  • C

58 người được đánh giá: A+ 3, A 47, B 5, C 3. .share-bar-lg cao 24px, chữ cái nằm trong đoạn (màu chữ là cặp --on-grade-n); đoạn nào hẹp hơn chữ cộng 4px mỗi bên thì kit.js ẩn chữ (ở khổ điện thoại đoạn A+ và C chỉ còn màu), tooltip và chú giải vẫn nói. Rê chuột lên một đoạn: “Trạm cấp cứu mỏ Hạ Long · C · 3 người · 5,2 %”.

Mỗi đơn vị một dòng, có dòng tổng

Xếp loại Tháng 9/2026 theo đơn vị
Đơn vịĐược đánh giáA+ABCTỷ lệ
Ban Giám đốcBGĐ300,0 %3100,0 %00,0 %00,0 %
Phòng Tổ chức - Hành chínhP.TCHC1516,7 %1386,7 %16,7 %00,0 %
Phòng Kế toán - Tài chínhP.KTTC7114,3 %685,7 %00,0 %00,0 %
Phòng Kiểm định - Thử nghiệm và Bảo dưỡng thiết bị cứu hộ mỏP.KĐTN800,0 %675,0 %225,0 %00,0 %
Trạm cấp cứu mỏ Hạ LongTrạm HL5835,2 %4781,0 %58,6 %35,2 %
Trạm cấp cứu mỏ Cẩm PhảTrạm CP6046,7 %5083,3 %58,3 %11,7 %
Tổ xeTổ xe1417,1 %1178,6 %214,3 %00,0 %
Đội cứu hộ Vàng Danh (mới thành lập)Đội VD0––––
Toàn trung tâm165106,1 %13682,4 %159,1 %42,4 %

Thanh nằm trong td nên các thanh của một cột tự thẳng hàng; dòng tổng ở tfoot dùng .share-bar-lg. P.TCHC in ra 6,7 + 86,7 + 6,7 = 100,1 % nhưng độ rộng theo tỉ lệ thô (--v) nên vẫn đúng. Đội Vàng Danh chưa có đánh giá: .share-bar-empty với chữ, không NaN, không thanh rỗng. Trong bảng thanh mang aria-hidden="true" vì các ô đã đọc đủ số; thanh đứng riêng thì là role="img" với câu đầy đủ. Ở 390px bảng cuộn ngang trong khung của nó.

Đoạn là liên kết: tiến độ kỳ

Tiến độ Tháng 10/2026

198 phiếu
  • Đã đánh giá
  • Chờ đánh giá
  • Đã giao, chưa gửi
  • Nháp
  • Không đánh giá

Mỗi đoạn là a.share-seg có aria-label riêng, nên thanh là role="group"; Tab đi qua từng đoạn và khung focus hiện cùng tooltip với khi rê chuột. Màu là tone của kit (data-tone: ok, warn, info, draft có gạch chéo, muted), chữ trong đoạn là --text. Dưới ngón tay thanh có liên kết cao ít nhất 24px.

Đoạn rất nhỏ và chưa có đánh giá

Chờ đánh giá 1 trên 196 phiếu (0,51 %)
Đơn vị chưa có phiếu nào được đánh giá

Một đoạn trên 0 luôn rộng ít nhất 3px, nên 0,51 % vẫn thấy (rộng hơn tỉ lệ thật một chút, tooltip và bảng ghi đúng số). Mẫu số bằng 0: đường viền đứt và một câu, ở cả hai cỡ.

Thanh trọng số so với mốc

Trọng số các tiêu chí trên một thang tuyệt đối, vạch mốc 100, phần vượt có gạch đỏ; sửa trọng số thì thanh cập nhật ngay.
.share-bar.share-bar-abs: rãnh --sunk là cả thang do tác giả chọn (tổng 105 thì thang 105); --v của đoạn là phần của thang. span.share-target[style="--at:.952"] > span.share-target-lab “100”: vạch 2px --text nhô 4px trên và dưới thanh, nhãn ở trên. .share-seg.share-over: phần vượt mốc, --danger gạch chéo --danger-line. Khe 2px giữa các đoạn là viền --share-gap (mặc định --surface; đặt bằng nền của dòng khi dòng có màu).

Trọng số 105 trên thang 105, mốc 100

Bản giao Tháng 10/2026: Đinh Thị Hằng

105 % · thừa 5
Trọng số từng tiêu chí
TTTiêu chíTrọng số %
TC1Tổ chức, cán bộ của phòng30
TC2Đào tạo, huấn luyện cán bộ20
TC3Bảo hiểm, chế độ chính sách15
TC4Tỷ lệ hoàn thành kế hoạch đào tạo15
TC5Đánh giá KPI cho nhân viên phụ trách15
TC6Tuân thủ nội quy lao động, giờ giấc làm việc; đăng ký, cập nhật, tự đánh giá KPI tuần, tháng10
Tổng105

Thang là 105 nên mốc 100 nằm ở --at:.952; mỗi đoạn là trọng số chia 105. TC6 có 10, nửa đầu vừa dưới mốc, nửa sau là .share-over (đỏ, gạch chéo) và không có chữ: mốc và dòng “105 % · thừa 5” bên cạnh đã nói. Chữ “TC6 10” không vừa đoạn của nó nên bị ẩn; tooltip vẫn có.

Sửa trọng số, thanh cập nhật ngay

100 % · đủ 100

Gõ số vào một ô: --v của từng đoạn và --at của mốc đổi theo, bề rộng trượt trong --dur-2 (không trượt khi bật giảm chuyển động); tổng trên 100 thì thang giãn ra và phần vượt hiện gạch đỏ, dưới 100 thì phần còn lại của rãnh --sunk để trống.

Cột có tương tác và cột chồng

Biểu đồ cột nhóm có tooltip, dải nền khi rê, chọn một nhóm và vạch ngưỡng; cột chồng có tổng ở tooltip. Bật thêm, không đổi hình của biểu đồ cột hiện có.
Bật bằng data-chart (và data-select) trên figure.chart-cols; số của tooltip lấy từ data-value trên từng .cc-col (hoặc phần sau dấu hai chấm của chữ ẩn), data-value trên .cc-mark thêm dòng cùng kỳ. .chart-cols.cc-stacked: cột chồng. div.cc-ref[style="--t:.8"] > span.cc-ref-lab: vạch tham chiếu trong .cc-plot. Không có data-chart thì không gì thay đổi.

Tooltip, dải nền khi rê, chọn một tháng, vạch ngưỡng

Điểm trung bình tháng, P.TCHC (điểm)
  • Tự đánh giá
  • Cấp trên

Chưa chọn tháng nào. Bấm một nhóm cột, hoặc Tab tới vùng vẽ rồi ←/→ và Enter.

Điểm trung bình tháng, P.TCHC
Loại điểmT1T2T3T4T5T6T7T8T9
Tự đánh giá99,597,898,999,298,197,695,4100,698,7
Cấp trên98,396,497,698,097,396,894,299,197,8

data-chart trên figure.chart-cols bật tooltip từng nhóm (số đọc từ data-value của mỗi cột, định dạng theo data-decimals và data-unit), dải nền --surface-hover sau nhóm đang rê, chuyển động lúc vào và, với data-select, chọn một nhóm: cột đậm hơn, nhãn đậm, aria-pressed="true" và sự kiện tvkit:chart-select (dòng ngay dưới biểu đồ in lại nó). Vùng vẽ cột thường thành một điểm dừng Tab (←/→, Home, End, Enter, Esc). div.cc-ref là vạch ngưỡng có nhãn ở mép cuối, nét đứt, không nhầm với đường kẻ. Không có data-chart thì .chart-cols giữ nguyên như phần trên.

Cột chồng, có vạch ngân sách

Chi phí theo nhóm, quý 2 và quý 3 năm 2026 (triệu đồng)
  • Vật tư
  • Nhân công
  • Vận chuyển
  • Khác
Chi phí theo nhóm, triệu đồng
NhómT4T5T6T7T8T9
Vật tư120132110145138150
Nhân công858890929597
Vận chuyển404638524955
Khác221825202421
Tổng267284263309306323

.cc-stacked chồng các cột của một nhóm lên nhau (cột đầu ở dưới), mỗi --v là phần của giá trị lớn nhất của trục; khe 2px giữa các khúc, chỉ khúc trên cùng bo góc, mẫu tô từng chuỗi như cột nhóm. Tooltip ghi tổng ở dòng đầu (“Tháng 7 · Tổng 309 tr”).

Biểu đồ đường

Một đến bốn chuỗi theo kỳ cùng kỳ trước, vạch ngưỡng có nhãn, tháng thiếu thành khoảng trống, vùng nhạt cho một chuỗi; tooltip, vạch dọc, bàn phím, chạm, chú giải bật tắt và chọn kỳ.
figure.chart-line[data-chart-table="…"] > figcaption.chart-title, sau đó là bảng số (hiện, hoặc .visually-hidden): ô tiêu đề sau ô đầu là các kỳ, mỗi tbody tr một chuỗi có tên ở th, data-series="1..4|prev" chọn màu; data-value (dấu chấm thập phân, chưa làm tròn) thắng chữ trong ô; data-label trên ô tiêu đề là tên dài (“Tháng 7”). Trên figure: data-y-min, data-y-max, data-y-step, data-refs="80:B;90:A", data-unit, data-decimals, data-curve="linear", data-area, data-legend-toggle, data-select, data-series-in="cols", data-empty. kit.js dựng chú giải, vùng vẽ svg theo pixel thật (chữ không co theo khung) và vùng đọc; đường cong đơn điệu không vọt quá số thật.

Ba đường, vạch ngưỡng, tháng thiếu, chú giải bật tắt, chọn tháng

Điểm theo tháng, Lý Văn Quý (nhân viên P.TCHC), năm 2026

Chưa chọn tháng nào.

Điểm theo tháng, Lý Văn Quý, năm 2026
ChuỗiT1T2T3T4T5T6T7T8T9T10T11T12
Cấp trên92,597,0108,096,598,598,0102,588,5
Tự đánh giá94,098,0108,597,099,099,5103,090,096,5
Cùng kỳ năm 202595,093,597,099,096,5101,094,092,598,097,599,5103,5

Số lấy từ bảng ngay dưới (data-chart-table): ô trống là tháng thiếu, đường đứt ra chứ không về 0 (tháng 4 không đánh giá; tháng 10 cấp trên chưa chấm, tháng 11 và 12 chưa tới). Điểm trên 100 là bình thường, trục không cắt. Chuỗi 1 nét liền, chuỗi 2 nét gạch dài, cùng kỳ nét gạch ngắn màu --series-prev; chú giải vẽ đúng nét đó. Rê chuột: vạch dọc bám tháng gần nhất, chấm trên mọi đường, tooltip ghi số trước tên. Tab tới vùng vẽ: ←/→, Home, End, Enter chọn, Esc ẩn; vùng aria-live đọc “Tháng 7: Cấp trên 98,0 điểm; …”. Rê lên một mục chú giải thì các đường khác mờ còn 25 %, bấm thì ẩn hoặc hiện (luôn còn một đường, trục không đổi). Nút “Đổi số” sửa bảng rồi gọi TvKit.chart.render(figure): cùng tháng nên số trượt trong 350 ms. “Đang tải lại” đặt aria-busy="true": vùng vẽ mờ một nửa, khung giữ nguyên.

Một chuỗi có vùng nhạt

Điểm trung bình toàn trung tâm, năm 2026
Điểm trung bình toàn trung tâm, năm 2026
ChuỗiT1T2T3T4T5T6T7T8T9T10T11T12
Điểm trung bình96,196,897,296,597,097,495,997,897,3

data-area: một lớp nhạt 10 % màu chuỗi dưới đường, chỉ khi biểu đồ có một chuỗi. Trục tự chọn bậc đẹp (1, 2, 2,5, 5 × 10ⁿ, bốn đến sáu đường) và không bắt đầu từ 0 khi số nằm sát nhau. Ở đây bảng nằm trong div.visually-hidden (đặt lớp lên chính table thì bảng không co về 1px và làm trang tràn ngang): trình đọc màn hình vẫn đọc được.

Bốn chuỗi và cùng kỳ, tên dài, một điểm đứng riêng

Điểm trung bình theo đơn vị, năm 2026
Điểm trung bình theo đơn vị, năm 2026
ChuỗiT1T2T3T4T5T6T7T8T9T10T11T12
Phòng Tổ chức - Hành chính96,497,098,197,596,997,895,098,697,497,998,298,8
Phòng Kế toán - Tài chính98,097,296,898,499,198,097,699,398,598,999,499,0
Trạm cấp cứu mỏ Hạ Long93,594,295,094,495,896,192,795,594,995,696,096,4
Trạm cấp cứu mỏ Cẩm Phả95,296,897,196,597,397,9
Toàn trung tâm năm 202595,195,695,996,296,096,695,296,496,196,897,097,5

Bốn chuỗi cùng năm, cùng thang điểm nên cùng là nét liền, phân biệt bằng màu, chú giải (rê chuột vào một mục làm mờ các đường khác), tooltip và điểm cuối; chỉ cùng kỳ năm trước là nét gạch ngắn vì là hệ quy chiếu khác. Trạm Cẩm Phả có số tháng 6 đứng riêng (tháng 5 và 7 trống): điểm không có hàng xóm luôn có chấm. Mười hai tháng ở 390px: nhãn tháng thưa ra (cách một, cách hai), tháng đầu và tháng cuối luôn hiện; cao 12,5rem khi biểu đồ hẹp hơn 40rem.

Chưa có số liệu

Điểm theo tháng, năm 2027
Điểm theo tháng, năm 2027
ChuỗiT1T2T3T4T5T6T7T8T9T10T11T12
Cấp trên

Không có số nào: một câu p.chart-empty trong vùng vẽ (chữ của data-empty), không vẽ trục.

Biểu đồ tròn rỗng (donut)

Phần của một tổng cho 2 đến 5 phần: số tổng ở giữa, chú giải có tỉ lệ, rê hoặc chọn thì phần đó nhô ra; quá năm phần thì gộp thành “Khác”.
figure.chart-donut[data-chart-table] (data-unit, data-decimals, data-total-label, data-select) > figcaption.chart-title, sau đó là bảng một chuỗi. kit.js vẽ div.cd-body (svg và số tổng ở giữa) và ul.cd-legend. Màu: --series-1..4 theo thứ tự, hoặc data-series, data-grade, data-tone trên ô tiêu đề (hay dòng) của phần đó; “Khác” là --series-prev. --donut-size đặt cỡ vòng (12rem).

Bốn phần, có tổng ở giữa, chọn một phần

Doanh thu quý 3 theo kênh

Chưa chọn phần nào.

Doanh thu quý 3 theo kênh, triệu đồng
KênhCửa hàngĐại lýBán onlineBán sỉ
Triệu đồng412,5268,0154,396,2

Vòng dày 22 % bán kính, khe 2px giữa các phần, màu theo thứ tự chuỗi của bảng (không sắp lại theo độ lớn). Chú giải dưới vòng: mỗi phần một cột, ô màu, tên, tỉ lệ chữ lớn; bốn phần không đủ chỗ thì xếp 2 × 2, không bao giờ 3 + 1. Rê hoặc Tab tới vòng rồi ←/→: phần đó nhô ra 4px và tooltip ghi số trước, tỉ lệ sau; Enter (hoặc bấm) giữ nó nhô ra và gửi tvkit:chart-select.

Trạng thái phiếu, màu theo tone, bảng xếp dọc

Phiếu Tháng 10/2026 theo trạng thái
Phiếu Tháng 10/2026 theo trạng thái
Trạng tháiSố phiếu
Đã đánh giá96
Chờ đánh giá85
Đã giao, chưa gửi13
Nháp1
Không đánh giá3

Bảng một cột số, mỗi dòng một phần (kit.js nhận ra cả hai chiều); data-tone trên ô đầu dòng (th) chọn màu tone thay cho màu chuỗi. Tone là màu nhạt: chữ, chú giải và bảng mang số, khe 2px tách các phần. Phần “Nháp” chỉ 0,5 % nên mỏng như một vạch, tooltip vẫn đọc được.

Tám khoản: danh sách bên cạnh vòng, “Khác” mở ra được

Chi phí tháng 9 theo khoản
Chi phí tháng 9 theo khoản, triệu đồng
KhoảnTriệu đồng
Vật tư145
Nhân công92
Vận chuyển52
Điện nước21
Sửa chữa14
Văn phòng phẩm9
Tiếp khách6
Quảng cáo4

Hơn năm phần thì bốn phần đầu giữ nguyên, phần còn lại gộp vào “Khác” (màu --series-prev). Từ năm phần chú giải là danh sách dọc (ô màu, tên, giá trị, tỉ lệ canh phải), nằm cạnh vòng khi hình rộng từ 34rem, dưới vòng trên điện thoại; bấm dòng “Khác” để xem từng khoản đã gộp với số của nó. Không dùng carousel: chú giải phải thấy hết một lần. Donut cho 2 đến 5 phần của một tổng; so các phần giữa nhiều dòng thì dùng thanh tỉ lệ.

Chưa có số liệu

Doanh thu quý 4 theo kênh
KênhCửa hàngĐại lý
Triệu đồng00

Tổng bằng 0: vòng xám --sunk và một câu ở giữa, không có chú giải.

Sparkline

Đường xu hướng nhỏ không trục trong thẻ KPI hoặc ô bảng, điểm cuối nổi bật; nghĩa nằm ở chữ và một câu cho trình đọc màn hình.
span.spark[data-values="72,5 81 79 96,3"][aria-hidden="true"]: các số cách nhau bằng dấu cách, dấu phẩy thập phân, “-” cho tháng thiếu; đặt trong .kpi-foot, ngay sau .kpi-value, hoặc trong ô bảng. kit.js vẽ svg rộng 100 %, cao --spark-h; gọi TvKit.chart.render(span) sau khi đổi data-values.

Trong thẻ KPI

Điểm trung bình toàn trung tâm

97,3điểm

Tháng 9, giảm 0,5 so với tháng 8. Xu hướng 9 tháng: từ 96,1 lên 97,3, thấp nhất 95,9 ở tháng 7

Phiếu quá hạn

12

12 phiếu đã giao chưa gửi tự đánh giá. Xu hướng 9 tháng: từ 18 xuống 12, cao nhất 21 ở tháng 7

Tỷ lệ xếp loại A trở lên

89,2%

Tháng 9/2026. Xu hướng 9 tháng: từ 88,0 lên 89,2, tháng 3 không có số

Đường 2px theo tông của thẻ (--primary, hoặc màu data-tone của .kpi: thẻ giữa là warn); các tháng trước nhạt hơn, đoạn cuối và điểm cuối đậm. Rê chuột hoặc chạm: một chấm chạy theo tháng gần nhất và tooltip nói tháng (data-labels) và giá trị. Thẻ đầu đặt sparkline ngay sau con số, hai thẻ sau đặt trong .kpi-foot (sparkline lên trước chữ). Thẻ thứ ba thiếu tháng 3 (“-”): hai bên nối bằng nét chấm mờ. Nghĩa của hình nằm ở chữ trong thẻ và một câu .visually-hidden do tác giả viết.

Trong ô bảng

Điểm trung bình tháng 9 và xu hướng 9 tháng theo đơn vị
Đơn vịTháng 9Xu hướng 9 tháng
Phòng Tổ chức - Hành chính97,4từ 96,4 lên 97,4
Phòng Kế toán - Tài chính98,5từ 98,0 lên 98,5
Trạm cấp cứu mỏ Hạ Long94,9từ 93,5 lên 94,9

Trong ô bảng sparkline rộng bằng ô (ít nhất 4rem), cao --spark-h (2rem); câu ẩn trong ô nói xu hướng.

Bảng tô màu theo giá trị

Ô số tô một tông, nhạt ở số nhỏ nhất, đậm dần đến số lớn nhất; số vẫn là thông tin chính, màu chỉ giúp.
table.tbl[data-heat="col|row|table"], th[data-heat="off"], --heat-strong, .heat-key

Mỗi cột một thang (mặc định)

Thu và chi theo tháng, triệu đồng
ThángThu 2026Thu 2025Chi 2026Chi 2025
T1248230201190
T2262251219205
T3291274236221
T4305288262240
T5338310274255
T6347320290270
T7352,4340281,3268
T8421,5380322,5300
T9312,5330238,9255
T10396370301285
T11408385318298
T12452420361330
Cả năm4.133,93.8983.303,73.117

Nhỏ nhấtLớn nhất, trong từng cột

table.tbl[data-heat]: mỗi cột so với chính nó, nên Thu và Chi cùng có tháng đậm nhất (T12) dù số khác nhau. Dòng tổng ở tfoot không tô. Ô đậm nhất vẫn đủ tương phản cho chữ (đã kiểm trên 7 bảng màu).

Một thang cho cả bảng

Thu và chi theo tháng, triệu đồng
ThángThu 2026Thu 2025Chi 2026Chi 2025
T1248230201190
T2262251219205
T3291274236221
T4305288262240
T5338310274255
T6347320290270
T7352,4340281,3268
T8421,5380322,5300
T9312,5330238,9255
T10396370301285
T11408385318298
T12452420361330
Cả năm4.133,93.8983.303,73.117

data-heat="table": mọi ô so với nhau, nên cột Thu đậm hơn hẳn cột Chi. data-heat="row" so trong từng dòng; th[data-heat="off"] bỏ một cột ra.

Có cả số âm và số dương

Lãi lỗ và chênh lệch so với cùng kỳ theo tháng, triệu đồng
ThángLãi / lỗSo với cùng kỳ
T112,5−3,0
T28,01,2
T3−4,5−8,4
T415,26,0
T5−12,8−2,1
T63,19,5
T722,44,4
T8−0,6−15,0
T96,92,2
T1018,011,3
T11−7,20
T1225,67,8

Âm nhiềuDương nhiều

Khi một thang có cả số âm và số dương, 0 là điểm giữa (nền trắng): số dương đậm dần màu xanh, số âm đậm dần màu đỏ, hai phía chung một độ dài nên −12,8 và 12,8 đậm như nhau. Dấu trừ vẫn nằm trong số. Tự bật, không cần thuộc tính; data-heat-mid="off" giữ thang nhạt đến đậm.

Tông khác

Thu và chi theo tháng, triệu đồng
ThángThu 2026Thu 2025Chi 2026Chi 2025
T1248230201190
T2262251219205
T3291274236221
T4305288262240
T5338310274255
T6347320290270
T7352,4340281,3268
T8421,5380322,5300
T9312,5330238,9255
T10396370301285
T11408385318298
T12452420361330
Cả năm4.133,93.8983.303,73.117

Đặt --heat-strong trên bảng để đổi tông (ở đây là ok); giữ mức pha để chữ vẫn đọc được.

Ô số liệu là liên kết tới danh sách, có dòng chênh lệch có dấu và màu theo chiều tốt; dải nhận đến sáu ô.
Ô liên kết: div.stat-strip-item > dt > a.stat-strip-link[href] (tên và svg.icon.icon-sm.stat-strip-caret “caret-right”) + dd. Chênh lệch: dd > span.stat-delta[data-dir="up|down|flat"][data-good="up|down"] > icon mũi tên + số có dấu + kỳ so sánh. Dải nhận 5 và 6 ô.

Sáu ô là liên kết, có chênh lệch

Kỳ Tháng 10/2026

Đã đánh giá
96+12 so với hôm qua
Chờ đánh giá
85−12 so với hôm qua
Đã giao, chưa gửi
13+1 · 12 quá hạn
Nháp
1không đổi
Không đánh giá
3không đổi

Cả ô là vùng bấm (liên kết nằm trong dt và phủ cả ô, vì dl chỉ chứa div > dt + dd): rê chuột thì nền --surface-hover, Tab thì khung focus nằm trong ô, mũi tên ở mép cuối màu --muted, chữ vẫn là màu chữ. Dòng chênh lệch có mũi tên, số có dấu và kỳ so sánh; xanh khi đi đúng chiều tốt (data-good), đỏ khi ngược, xám khi không đổi. “Chờ đánh giá −12” là tốt nên xanh; “Đã giao +1” là xấu nên đỏ.

Năm ô

Xếp loại Tháng 9/2026

A+
12+2 so với T8
A
161−3 so với T8
B
17+1 so với T8
C
4−2 so với T8

Năm và sáu ô nằm một hàng khi đủ chỗ; dải hẹp hơn 40rem (điện thoại) thì xuống ba ô một hàng: sáu thành 3 + 3, năm thành 3 + 2.

Bảng cơ bản

Panel, bảng cơ bản, trạng thái dòng và dòng số dư đầu kỳ; nhãn và chip có phần riêng, bảng cuộn dính và bảng rộng ở Bảng nâng cao.
.panel (-head -body -foot -flush), .tbl-wrap > .tbl (-compact -dense, cột số dùng .num), .tag (-ok -warn -bad -info -draft). Nhãn .tag và chip xem ở phần Chip và tag; trong bảng mọi nhãn cao hơn một bước (--fs-sm) cho khớp chữ của bảng. Chip mã đứng trước tên là .code-chip trong .cell-lead.

Phiếu gần đây

5 phiếu gần nhất

Số phiếuKhách hàngTrạng tháiKiểm hàngSố lượng (kg)Thành tiền (₫)Thao tác
PN-0012Ớt bột
Công ty Phú ThànhĐã duyệtĐạt1.250,50012.450.000
PN-0013Tiêu xay
Nguyễn Văn AnChờ duyệtĐạt4,1623.280.000
PN-0014Muối tinh
Cửa hàng Minh SơnBị từ chốiKhông đạt0,350890.000
PN-0015Bao bì 500 g
Trần Thị BìnhBản nhápChưa đủ số liệu12,0001.020.000
PN-0016Ớt bột
Hợp tác xã Nga ThanhĐã hủyKhông đánh giá0,0000

Cột đầu có chip mã (.code-chip trong .cell-lead): mã mặt hàng ngắn, hộp viền chữ đơn cách, để quét cột theo loại; tên mặt hàng vẫn nằm ở dòng phụ nên chip là aria-hidden. Trạng thái là nhãn có nền, kiểm hàng là nhãn không nền (.tag-bare): hai cột cạnh nhau vẫn đọc được, không thành hai hàng nhãn viên thuốc.

Tiêu đề cột dài

CỡSai số KLChảy nhỏ nhất (MPa)Bền nhỏ nhất (MPa)A5 nhỏ nhất (%)
D6CB240-T−1,6 … −0,9 %± 8 %311,0≥ 240455,8≥ 38025,3≥ 20
D10CB300-V−1,1 … −0,4 %± 6 %318,5≥ 300459,9≥ 45022,0≥ 19

.tbl-th-wrap: tiêu đề cột được xuống dòng, nên bảng có tiêu đề dài (kèm đơn vị) trên số ngắn vừa một cột hẹp mà không phải cuộn ngang. Ô số dạng khoảng giữ một dòng bằng .nowrap.

Trạng thái dòng: ngừng dùng, đã hủy, tô theo tone, vừa thêm

Một dòng bảng hoặc dòng danh sách có thể mờ, bị gạch, được tô màu hoặc đánh dấu là vừa thêm; chữ trong dòng luôn nói thêm điều đó.
tr.row-void (đã có): mờ và gạch ngang, nghĩa là đã hủy. .row-off trên tr, trên li hoặc .work-item của .work-list: chữ mờ, không gạch, cho thứ ngừng dùng, tài khoản bị khóa, người đã nghỉ việc; hãy thêm một .tag (“Ngừng dùng”) vì mờ một mình dễ bị đọc là dòng phụ. tr[data-tone="ok|warn|bad|info"] tô cả dòng bằng màu -bg của tone qua --row-bg (ô dính theo màu); hover và dòng đang chọn vẫn thắng màu tô, .cell-fill giữ màu riêng, và dòng vẫn phải có chữ nói trạng thái (tag, ô). .row-new: dòng vừa thêm, nền như dòng được chọn cộng một vạch ở đầu dòng và nên có chữ “Mới”; tenant gỡ lớp này sau ít giây, không có hiệu ứng. Dòng danh sách: .work-row[data-state="void"] hoặc .row-void làm dòng mờ, tên và mã bị gạch, con số như .num-void, tag “Đã hủy” vẫn rõ và vạch tone bị bỏ; .row-off và .row-new dùng như ở bảng. Trong bảng dạng thẻ (.tbl-cards) thẻ mang cùng màu và vạch.

Bảng: từng trạng thái

Vật tư và kho

Rê chuột vào một dòng để thấy hover (thắng màu tô)
Vật tưTrạng tháiSố lượng (kg)
VT-0101Thép tấm 5 lyBình thường1.250,500
VT-0102Que hàn 3.2 (phiếu đã hủy)Đã hủy− 90,000
VT-0103Sơn lót cũ (ngừng nhập)Ngừng dùng12,000
VT-0104Ống 40 (đã đồng bộ kho)Đã đồng bộ+ 40,000
VT-0105Đá cắt 100 (chưa đồng bộ)Chưa đồng bộ250,000
VT-0106Bulong M10 (lỗi đồng bộ)Lỗi đồng bộ− 1.200,000
VT-0107Tồn làm tròn của hệ thốngHệ thống0,350
VT-0108Tôn lạnh 0.4 (vừa thêm)Mới800,000
VT-0109Dây hàn lõi thuốc (đang chọn)Đã chọn35,000

Từ trên xuống: bình thường, row-void, row-off, bốn tone ok warn bad info, row-new, aria-selected. Dòng cuối đang chọn có cùng nền với dòng mới nhưng không có vạch.

Danh sách: đã hủy, ngừng dùng, vừa thêm

Dòng 2 .row-off, dòng 3 .work-row[data-state="void"], dòng 4 .row-void kèm data-tone="warn" (vạch bị bỏ), dòng 5 .row-new.

Khung hẹp: bảng thành thẻ

Vật tư và kho

Vật tưTrạng tháiSố lượng (kg)
VT-0105Đá cắt 100 (chưa đồng bộ)Chưa đồng bộ250,000
VT-0103Sơn lót cũ (ngừng nhập)Ngừng dùng12,000
VT-0102Que hàn 3.2 (phiếu đã hủy)Đã hủy− 90,000
VT-0108Tôn lạnh 0.4 (vừa thêm)Mới800,000
VT-0106Bulong M10 (lỗi đồng bộ)Lỗi đồng bộ− 1.200,000

Thẻ tô warn, thẻ row-off, thẻ row-void, thẻ row-new (có vạch ở mép trái), thẻ tô bad.

Dòng số dư đầu kỳ

Dòng đầu của sổ cái cho biết số còn lại trước khi các khoản phát sinh bắt đầu: nhạt hơn dòng thường, chỉ số dư là chữ đậm.
tr.row-open là dòng đầu tiên của tbody (“Số dư đầu kỳ 01/07”, “Tồn đầu kỳ”). Nền --surface-2, chữ mờ, đường kẻ dưới đậm hơn để tách khỏi các dòng phát sinh; không gạch, không nghiêng; chỉ ô số dư (td.num cuối dòng) là chữ đậm. Nhãn là ô tiêu đề dòng th scope="row": gộp cột (colspan) qua các cột trước số dư, hoặc giữ một ô và để trống các ô còn lại (nên làm vậy trong bảng .tbl-sticky-col, kẻo ô đầu rộng đè lên các ô đang cuộn). Con số nằm ở đúng cột của nó. Hover và dòng được chọn chạy như mọi dòng. Trong .tbl-sticky đầu bảng vẫn nằm trên dòng này khi cuộn. Trong .tbl-cards nó thành một thẻ cùng vẻ nhạt, nhãn là tiêu đề thẻ, các ô trống được bỏ. Số phát sinh dùng .num-in, .num-out, .num-neg.

Sổ quỹ có dòng đầu kỳ và dòng tổng

Sổ quỹ tiền mặt

Quý 3 · 2026
Diễn giảiNgàyThuChiSố dư
Số dư đầu kỳ 01/07/20266.200.000đ
Khách Tuấn Phát trả đợt 103/07/2026+ 12.450.000đ18.650.000đ
Mua que hàn, đá cắt05/07/2026− 350.000đ18.300.000đ
Ứng lương tháng 710/07/2026− 7.200.000đ11.100.000đ
Khách Hòa Bình trả tiền hàng14/07/2026+ 4.800.000đ15.900.000đ
Tiền điện xưởng tháng 618/07/2026− 3.150.000đ12.750.000đ
Mua thép tấm 5 ly (vượt số dư)22/07/2026− 14.000.000đ− 1.250.000đ
Khách Tuấn Phát trả đợt 228/07/2026+ 12.450.000đ11.200.000đ
Cộng phát sinh+ 29.700.000đ− 24.700.000đ11.200.000đ

Dòng đầu kỳ: th[colspan="4"] cho nhãn, số dư ở cột cuối. Dòng cuối là tfoot (tổng phát sinh và số dư cuối kỳ).

Khung hẹp: thẻ

Sổ quỹ tiền mặt

Diễn giảiNgàyThuChiSố dư
Số dư đầu kỳ 01/07/20266.200.000đ
Khách Tuấn Phát trả đợt 103/07/2026+ 12.450.000đ18.650.000đ
Mua que hàn, đá cắt05/07/2026− 350.000đ18.300.000đ
Ứng lương tháng 710/07/2026− 7.200.000đ11.100.000đ
Cộng phát sinh+ 29.700.000đ− 24.700.000đ11.200.000đ

Cùng bảng ở khung dưới 36rem: thẻ đầu kỳ nhạt, tiêu đề là nhãn, chỉ có ô số dư (ô trống được bỏ); tfoot thành thẻ cuối có viền đậm, các ô số của nó cần data-label như ô thường.

Bảng dính đầu, cuộn

Diễn giảiNgàyThuChiSố dư
Số dư đầu kỳ 01/07/20266.200.000đ
Khách Tuấn Phát trả đợt 103/07/2026+ 12.450.000đ18.650.000đ
Mua que hàn, đá cắt05/07/2026− 350.000đ18.300.000đ
Ứng lương tháng 710/07/2026− 7.200.000đ11.100.000đ
Khách Hòa Bình trả tiền hàng14/07/2026+ 4.800.000đ15.900.000đ
Tiền điện xưởng tháng 618/07/2026− 3.150.000đ12.750.000đ
Mua thép tấm 5 ly (vượt số dư)22/07/2026− 14.000.000đ− 1.250.000đ
Khách Tuấn Phát trả đợt 228/07/2026+ 12.450.000đ11.200.000đ
Cộng phát sinh+ 29.700.000đ− 24.700.000đ11.200.000đ

.tbl-sticky trong .tbl-scroll: đầu bảng và dòng tổng dính, dòng đầu kỳ cuộn dưới đầu bảng như mọi dòng. Cuộn bảng để thấy đầu bảng ở trên dòng đầu kỳ.

Cột đầu dính, các ô trống giữ nguyên

Diễn giảiNgàyThuChiSố dư
Số dư đầu kỳ01/07/20266.200.000đ
Khách Tuấn Phát trả đợt 103/07/2026+ 12.450.000đ18.650.000đ
Mua que hàn, đá cắt05/07/2026− 350.000đ18.300.000đ
Ứng lương tháng 710/07/2026− 7.200.000đ11.100.000đ
Khách Hòa Bình trả tiền hàng14/07/2026+ 4.800.000đ15.900.000đ
Tiền điện xưởng tháng 618/07/2026− 3.150.000đ12.750.000đ
Mua thép tấm 5 ly (vượt số dư)22/07/2026− 14.000.000đ− 1.250.000đ
Khách Tuấn Phát trả đợt 228/07/2026+ 12.450.000đ11.200.000đ

Trong bảng .tbl-sticky-col không dùng colspan: nhãn ở ô đầu, các ô khác của dòng đầu kỳ để trống.

Bảng nâng cao

Bảng cuộn: dính đầu và cột, chọn dòng, sắp xếp, tổng, phân trang.
Thêm vào .tbl: .tbl-sticky (đầu và dòng tổng dính, cần .tbl-scroll có chiều cao), .tbl-sticky-col (cột đầu dính; thêm .tbl-select thì cột chọn và cột sau nó cùng dính), .tbl-grid (kẻ ô), .tbl-zebra, tfoot (tổng), .th-sort + aria-sort, tr[aria-selected="true"] + .cell-check, .row-void, td.trunc; trong ô: .cell-sub (dòng phụ), .cell-metric với .bar (thanh tỷ lệ) hoặc .tag.delta (tăng giảm), td.nowrap.tnum (ngày giờ), td.cell-fill (ô tô màu); quanh bảng: .tbl-tools, .bulk-bar, .pager. Bảng dưới cuộn được cả hai chiều: thử chọn dòng, bấm tiêu đề cột để sắp xếp, bấm dấu ⋮ cuối dòng để mở menu thao tác, đổi kiểu bảng bằng các nút bên dưới. Cột Số lượng có thanh so với phiếu nhập nhiều nhất, cột Đơn giá có mức tăng giảm so với lần nhập trước (màu theo ý nghĩa, mũi tên theo chiều: giá nhập tăng là xấu nên đỏ, giảm là tốt nên xanh), cột Nhà cung cấp có mã ở dòng phụ.
GọnChữ nhỏKẻ ôSọc
Tất cảChờ duyệtĐã duyệt
EmailKhoTrạng tháiKiểm hàngĐơn giá (₫)so với lần nhập trướcGhi chúThao tác
PN-010101/10/2026 08:30Công ty Phú ThànhNCC-012lienhe@phuthanh.vnKho 1Đã duyệtĐạt1.250,50018.500Tăng 3,4%23.134.250Hàng đợt 1, đã kiểm đủ số lượng theo hợp đồng số 24/HĐ-2026
PN-010202/10/2026 09:15Cửa hàng Minh SơnNCC-007info@minhson.vnKho 2Đã duyệtĐạt4,16295.000Giảm 3,1%395.390Giao tại kho
PN-010303/10/2026 14:00Hợp tác xã Nga ThanhNCC-031ketoan@ngathanh.vnKho 1Chờ duyệtChờ kết quả320,00042.000Giảm 4,5%13.440.000Chờ phiếu kiểm nghiệm
PN-010404/10/2026 10:45Nguyễn Văn AnNCC-044an.nguyen@email.vnKho 1Đã duyệtĐạt0,3501.200.000Tăng 4,3%420.000
PN-010505/10/2026 16:20Trần Thị BìnhNCC-052binh.tran@email.vnKho 2Bản nhápChưa đủ số liệu75,25031.000Giảm 6,1%2.332.750Chưa chốt giá
PN-010606/10/2026 08:05Công ty Phú ThànhNCC-012lienhe@phuthanh.vnKho 1Đã duyệtĐạt980,00018.500Không đổi 0,0%18.130.000Hàng đợt 2
PN-010707/10/2026 11:30Cửa hàng Minh SơnNCC-007info@minhson.vnKho 2Đã hủyKhông đạt12,00095.000Không đổi 0,0%1.140.000Nhập nhầm kho, đã lập lại phiếu PN-0108
PN-010807/10/2026 13:10Cửa hàng Minh SơnNCC-007info@minhson.vnKho 1Đã duyệtĐạt12,00095.000Tăng 3,3%1.140.000
PN-010908/10/2026 09:40Công ty Thanh NhànNCC-060kinhdoanh@thanhnhan.vnKho 2Chờ duyệtCần xem lại2.100,75015.200Giảm 5,6%31.931.400Giá theo bảng giá tháng 10
PN-011009/10/2026 15:25Hợp tác xã Nga ThanhNCC-031ketoan@ngathanh.vnKho 1Đã duyệtĐạt640,50042.000Tăng 3,7%26.901.000
PN-011110/10/2026 10:10Nguyễn Văn AnNCC-044an.nguyen@email.vnKho 2Đã duyệtKhông đạt1,0002.500.000Giảm 5,7%2.500.000Thiết bị đo
PN-011212/10/2026 17:05Trần Thị BìnhNCC-052binh.tran@email.vnKho 1Bản nhápChưa đủ số liệu18,12531.000Tăng 5,1%561.875
Tổng11 phiếu, không tính phiếu hủy5.402,637120.886.665

Cột Kiểm hàng là ô tô màu như cột Status của một bảng việc: td.cell-fill + -ok -warn -bad -info (không thêm màu là xám), bên trong .cell-fill-label chứa icon và chữ. Cả ô lấy màu, chữ ở giữa, một viền màu nền của dòng giữ các ô cách nhau; ô vẫn giữ màu khi dòng sọc, được rê chuột hoặc được chọn, còn dòng đã hủy bỏ màu để chữ gạch ngang đọc được.

Sheet cells

Bảng nhập liệu kiểu Excel: ô đang chọn, đã sửa, lỗi, chỉ đọc.
.tbl-grid + td.cell-edit chứa .cell-input. Trạng thái: ô đang chọn td[aria-selected="true"], ô đã sửa .cell-dirty, ô lỗi td[aria-invalid="true"], ô chỉ đọc (công thức) .cell-ro. Kit chỉ lo hình thức; phím mũi tên, dán nhiều dòng và lưu do tenant xử lý. Bấm vào một ô để thấy ô đang chọn.
STTMã hàngTên hàngĐVSố lượngĐơn giá (₫)Thành tiền (₫)
16.266.000
27.350.000
3—
42.760.000
5—

Skeleton and empty table

Bảng đang tải và bảng trống.
.skel thay chữ trong lúc tải (đặt aria-busy="true" lên bảng); bảng trống là một dòng .tbl-empty chứa .empty.
Số phiếuNhà cung cấpTrạng tháiThành tiền (₫)
Số phiếuNhà cung cấpTrạng tháiThành tiền (₫)
Không có phiếu nào khớp

Bỏ bớt bộ lọc hoặc tạo phiếu mới.

Bảng cây đơn vị, đổi thứ tự

Danh sách đơn vị nhiều cấp trong bảng: thụt theo cấp, đường nối từ đơn vị cha, gập được, nút lên/xuống đổi thứ tự trong cùng cấp.
table.tbl.tbl-tree[data-row-sort], mỗi dòng tr[data-level="0".."3"] theo thứ tự cây, ô đầu th.tree-cell > (button.tree-toggle[aria-expanded]) + svg.icon (loại đơn vị: buildings, tree-structure, users) + tên. Cột cuối td.row-moves > button[data-row-up], button[data-row-down] (và nút sửa). Cột viết tắt dùng span.doc-code. kit.js vẽ đường nối, gập các dòng con, chuyển một dòng cùng toàn bộ dòng con của nó trong cùng cấp, làm mờ nút ở đầu và cuối, đọc vị trí mới và gửi change với detail { row, from, to } để tenant lưu.

Đơn vị

Thứ tự ở đây là thứ tự dòng trên các bảng tổng hợp
Đơn vị theo cây, đổi được thứ tự
Tên đơn vịViết tắtNhân viênTrạng tháiThứ tự
Trung tâm Kỹ thuật Nam ViệtTT112Đang dùng
Ban Giám đốcBGĐ3Đang dùng
Phòng Tổ chức - Hành chínhP.TCHC12Đang dùng
Phòng Kế toán - Tài chínhP.KTTC7Đang dùng
Trạm BắcTrạm B46Đang dùng
Ban chỉ huy trạmBCH Trạm B4Đang dùng
Tổ 1T114Đang dùng
Tổ 2T214Đang dùng
Tổ 3T314Đang dùng
Trạm NamTrạm N44Đang dùng
Ban chỉ huy trạmBCH Trạm N4Đang dùng
Tổ 1T120Đang dùng
Tổ 2T220Đang dùng

Mỗi cấp thụt vào 1,75rem (3rem khi bảng có nút gập, để nút nằm giữa đường nối và icon); tên không xuống dòng; đường kẻ mảnh đậm hơn đường kẻ dòng một bậc (để không lẫn với lưới) đi từ icon của đơn vị cha xuống rồi cong vào icon của dòng con (như đường cây ở sidebar), dừng ở con cuối. Trạm Bắc đang mở, Trạm Nam đang gập (mũi tên quay ngang, dòng con ẩn). Nút lên/xuống chỉ đổi chỗ trong cùng cấp và kéo theo dòng con: đưa Trạm Nam lên thì cả bốn dòng của nó đi cùng. Nút đầu (lên) của dòng đầu và nút cuối (xuống) của dòng cuối mờ đi. Đơn vị gốc không có nút. Khi in, nút gập và nút thứ tự ẩn.

Tiêu đề bảng hai tầng

Dòng nhóm trên dòng tiêu đề cho số liệu đi theo nhóm (Tồn đầu, Nhập, Xuất, Tồn cuối; mỗi nhóm hai cột SL và Giá trị); cả hai dòng đều dính khi cuộn.
.tbl-head-group đặt trên thead: dòng đầu là các th[scope="colgroup"][colspan] căn giữa, mỗi nhóm có đường kẻ ngắn dưới phần của mình; cột đầu là một th[rowspan="2"]. Dòng hai là các th[scope="col"] như thường. Trong .tbl-sticky hai dòng cùng dính, dòng hai ngay dưới dòng một: dòng nhóm cao --tbl-head-row1 (mặc định tính từ đệm ô của bộ kit) và dòng hai dính ở độ lệch đó; đặt lại --tbl-head-row1 khi tên nhóm xuống dòng. colspan ở thead dính không sao (chỉ dính phía trên); với .tbl-sticky-col, ô đầu của dòng nhóm không được có colspan. Với .tbl-cards cả đầu bảng ẩn, nên mỗi td mang data-label gồm tên nhóm (“Nhập · SL”); ô trống ở tfoot bị bỏ khỏi thẻ tổng.

Phiếu tồn kho: bốn nhóm

Nhập xuất tồn quý 3

01/07 – 30/09/2026
Vật tưTồn đầuNhậpXuấtTồn cuối
SLGiá trịSLGiá trịSLGiá trịSLGiá trị
Thép phi 10kg1.20021.600.000đ80014.400.000đ1.45026.100.000đ5509.900.000đ
Xi măng PCB40bao24022.080.000đ60055.200.000đ54049.680.000đ30027.600.000đ
Cát vàngm³185.580.000đ4012.400.000đ4614.260.000đ123.720.000đ
Cộng giá trị49.260.000đ81.980.000đ90.040.000đ41.220.000đ

Bảng thường: nhóm ở dòng trên, mỗi nhóm có đường kẻ riêng; “Vật tư” là ô rowspan="2" chiếm cả hai dòng. Dòng cuối là tổng giá trị (cột SL để trống vì đơn vị khác nhau). Bảng này dùng .tbl-cards nên dưới 36rem nó thành thẻ (xem ví dụ cuối).

Hai dòng đầu cùng dính khi cuộn

Vật tưTồn đầuNhậpXuấtTồn cuối
SLGiá trịSLGiá trịSLGiá trịSLGiá trị
Thép phi 10kg1.20021.600.000đ80014.400.000đ1.45026.100.000đ5509.900.000đ
Xi măng PCB40bao24022.080.000đ60055.200.000đ54049.680.000đ30027.600.000đ
Cát vàngm³185.580.000đ4012.400.000đ4614.260.000đ123.720.000đ
Cộng giá trị49.260.000đ81.980.000đ90.040.000đ41.220.000đ

Hộp cuộn thấp: kéo xuống thì hai dòng đầu nằm chồng khít (không hở, không đè nhau) và dòng tổng dính ở đáy. Cuộn ngang thì cả hai dòng cuộn cùng cột.

Có cột đầu dính

Vật tưTồn đầuNhậpXuấtTồn cuối
SLGiá trịSLGiá trịSLGiá trịSLGiá trị
Thép phi 10kg1.20021.600.000đ80014.400.000đ1.45026.100.000đ5509.900.000đ
Xi măng PCB40bao24022.080.000đ60055.200.000đ54049.680.000đ30027.600.000đ
Cát vàngm³185.580.000đ4012.400.000đ4614.260.000đ123.720.000đ
Cộng giá trị49.260.000đ81.980.000đ90.040.000đ41.220.000đ

Cùng bảng với .tbl-sticky-col: cột “Vật tư” đứng yên khi cuộn ngang; ô “SL” đầu tiên của dòng hai không bị dính theo.

Khung hẹp: thẻ

Vật tưTồn đầuNhậpXuấtTồn cuối
SLGiá trịSLGiá trịSLGiá trịSLGiá trị
Thép phi 10kg1.20021.600.000đ80014.400.000đ1.45026.100.000đ5509.900.000đ
Xi măng PCB40bao24022.080.000đ60055.200.000đ54049.680.000đ30027.600.000đ
Cát vàngm³185.580.000đ4012.400.000đ4614.260.000đ123.720.000đ
Cộng giá trị49.260.000đ81.980.000đ90.040.000đ41.220.000đ

Với .tbl-cards dòng nhóm ẩn cùng đầu bảng; mỗi ô nêu nhóm trong data-label (“Nhập · SL”, “Nhập · Giá trị”). Các ô SL trống của tfoot bị bỏ khỏi thẻ tổng.

Ma trận phân quyền

Bảng vai trò theo quyền: mỗi giao điểm là một ô tích; có hàng nhóm quyền, ô khóa của Quản trị và ô vừa đổi chưa lưu; đầu bảng và cột quyền cùng dính.
.tbl.tbl-matrix.tbl-sticky.tbl-sticky-col trong .tbl-scroll. Cột đầu là th[scope="row"] (tên quyền, rộng --matrix-name-w 10rem), đầu bảng là th[scope="col"] mỗi vai trò. Ô tích là td.cell-check > label > input[type="checkbox"] với aria-label nêu cả quyền lẫn vai trò; label phủ kín ô nên vùng bấm cả ô và không nhỏ hơn 44 px. Mỗi nhóm quyền là một tbody mở đầu bằng tr.grp > th[scope="rowgroup"] (chỉ ô đầu có chữ, không dùng colspan vì ô đầu dính) và các td trống: nền --surface-2, chữ đậm, không đổi màu khi rê chuột. Ô khóa: td.cell-check[data-locked][aria-disabled="true"] có title, input thật là disabled checked và biểu tượng khóa; câu “Quản trị luôn có quyền này” cho trình đọc màn hình qua aria-describedby. Ô vừa đổi chưa lưu: td.cell-dirty (nền và vạch trái của ô đã sửa) thêm một chấm ở góc, và “đã đổi, chưa lưu” trong aria-label. Đếm “3 thay đổi chưa lưu” và lưu là việc của trang.

Năm vai trò, ba nhóm quyền

Quản trị luôn có quyền này

QuyềnQuản trịKế toánThủ khoQuản đốcCông nhân
Quỹ
Xem sổ quỹ, số dư quỹ
Lập phiếu thu, chi, chuyển quỹ
Khóa sổ quỹ
Kho
Xem tồn kho, thẻ kho
Lập phiếu nhập, xuất kho
Kiểm kê kho
Công việc
Xem sổ công việc
Chấm công, nhập ngày làm
Duyệt bảng công

Cột Quản trị bị khóa (hộp tích mờ, có khóa; rê chuột để thấy “Quản trị luôn có quyền này”). Hai ô của Quản đốc vừa được bật nhưng chưa lưu: nền vàng nhạt, vạch trái và chấm ở góc. Cuộn xuống thì tên vai trò vẫn ở trên, cuộn ngang thì tên quyền vẫn ở bên trái.

Ô chấm công và chú giải

Bảng công theo tháng: mỗi ô tô nhạt theo trạng thái (đủ công, nửa công, phép, không phép), cột Chủ nhật nền xám, ô “chưa vào làm” gạch chéo, số tăng ca ở góc ô, kèm chú giải.
td.cell-mark với -ok, -info, -bad, -muted (tô nhạt cả ô từ --ok-bg, --info-bg, --danger-bg, --sunk), -na (gạch chéo, chưa vào làm). Ký hiệu (“Đ”, “½”, “P”, “KP”) ở giữa ô, màu --text, không dùng màu của tone nên chữ mới là dấu hiệu chính. Ô có thể là liên kết: td.cell-mark > a, cả ô là vùng bấm không nhỏ hơn 44 px, vòng lấy nét nằm trong ô; đặt aria-label nêu đủ nghĩa (“Ngày 5, Hùng: đủ công, tăng ca 2 giờ”). td[data-off] (và th[data-off]) tô nền ngày nghỉ; đầu cột vẫn ghi “CN”. .cell-corner là số nhỏ 0,75rem ở góc trên bên phải ô, aria-hidden vì aria-label của ô đã nói đủ. Chú giải: ul.legend > li.legend-item > span.legend-swatch + chữ; ô mẫu -ok -info -bad -muted -off -na trùng ô chấm công, -solid và -line cho biểu đồ (màu theo --legend-color). Chú giải tự xuống dòng và không phải là điều khiển; .cc-legend của biểu đồ cột giữ nguyên.

Bảng công tháng 10, bốn công nhân

  • Đủ công
  • Nửa công
  • Nghỉ có phép
  • Nghỉ không phép
  • Chủ nhật
  • Chưa vào làm
  • Số nhỏ ở góc ô: giờ tăng ca
Công nhân1T52T63T74CN5T26T37T48T59T610T711CN12T213T314T415T516T617T718CN19T220T321T422T523T624T725CN26T227T328T429T530T631T7
Nguyễn Văn HùngTổ hàn 1ĐĐĐĐĐĐPĐĐĐĐĐ½ĐĐĐĐĐĐĐĐĐĐĐĐĐĐ
Trần Thị LanTổ hàn 1ĐĐĐĐĐĐĐKPĐĐĐĐĐĐĐĐĐĐ½ĐĐĐĐĐĐĐĐ
Lê Quang MinhTổ lắp dựngĐ½ĐĐĐĐĐĐĐĐĐĐĐĐĐĐĐĐĐĐĐĐĐĐPĐĐ
Phạm Văn TàiTổ lắp dựngĐĐĐĐĐPĐĐĐĐĐĐĐĐĐĐĐĐĐ

Ba mươi mốt cột ngày, cột tên dính khi cuộn ngang. Chủ nhật (4, 11, 18, 25) nền xám cả cột; Tài vào làm từ ngày 10 nên các ô trước đó gạch chéo; ba ô có số tăng ca ở góc (ngày 6 của Hùng 2 giờ, ngày 13 của Lan 1,5 giờ, ngày 20 của Minh 3 giờ). Mỗi ô là một liên kết, Tab tới thì vòng lấy nét nằm trong ô.

Ô giờ số: một dòng đủ mọi trạng thái ngày

Bảng giờ số dùng .tbl-marks: ô 2,25rem dưới chuột, chứa được “8,5”, “7,5P”, “5K”, “1.200”; thứ Bảy, Chủ nhật, hôm nay, ngày chưa tới, trước khi vào làm, sau khi nghỉ, chưa chấm, thiếu giờ và dấu góc trên cùng một dòng.
.tbl-marks trên bảng: ô td.cell-mark rộng --mark-w (2,25rem) cao --mark-h (1,875rem) khi dùng chuột, về lại --tap khi chạm hoặc data-tap="all"; số dài làm rộng cột chứ không tràn. Mới: .cell-mark-warn (thiếu giờ), .cell-mark-missing (chưa chấm: vòng đứt rỗng, chữ ẩn “Chưa chấm”), .cell-auto (tự tính, sửa được: chữ nghiêng và sao ở góc), .cell-corner-start, .cell-corner-bottom (góc trên-đầu, dưới-cuối, nhận svg.icon). Cột: data-off="half" (thứ Bảy), th[aria-current="date"] + td[data-today] (hôm nay, cả cột), data-future (chưa tới, mờ cả đầu cột). Trước ngày vào làm và sau ngày nghỉ đều là .cell-mark-na.
Công nhân1T52T63T74CN5T26T37T48T59T610T711CN12T213T314T415T516T617T718CN19T220T321T422T523T624T725CN26T227T328T429T530T631T7
Trần Thị LanVào làm 03/10, nghỉ việc từ 28/1048,586,587,5P40,58Chưa chấm81.200485K888

Ngày 1–2 (chưa vào làm) và 28–31 (đã nghỉ việc) gạch chéo; thứ Bảy (3, 10, 17, 24, 31) nền nhạt hơn Chủ nhật; hôm nay (23) có vạch dưới đầu cột và hai đường dọc cả cột; từ 24 là ngày chưa tới, đầu cột mờ; ngày 14 chưa chấm (vòng đứt, trình đọc màn hình nghe “Chưa chấm”). Ngày 13 có ba dấu: sao (tự tính, góc trên-đầu), số tăng ca (góc trên-cuối), vân tay (góc dưới-cuối).

Lưu ô: đang lưu, lỗi lưu, xung đột, đang sửa

Ô giữ giá trị vừa gõ; thông báo nổi trong top layer, neo vào ô, không bị khung cuộn cắt.
td[data-save="saving"] (chấm nhấp nháy góc trên-đầu, thêm aria-busy), "error" (viền đỏ liền, “Chưa lưu · Thử lại”), "conflict" (viền vàng đứt, “vừa được sửa bởi …, Tải lại ô”). Thông báo là div.cell-pop[popover="manual"] nằm trong ô: kit.js mở nó khi trạng thái đổi sang lỗi hoặc xung đột, khi ô nhận focus hoặc được bấm; đặt dưới ô (hoặc trên nếu hết chỗ), đi theo ô khi cuộn, ẩn khi ô khuất; Esc hoặc nút Đóng đóng lại và trả focus về ô; nút trong thông báo đi ngay sau ô khi nhấn Tab. Ô đang sửa: td.cell-mark > input.cell-input phủ lên ô, rộng ít nhất 5rem.
Công nhân1T52T63T74T25T36T4
Lê Quang Minh88,54

Ô này vừa được sửa bởi Kế toán Hoa (10:42). Giá trị trên máy chủ là 8.

Giá trị đã gõ: 4, vẫn giữ trên ô.

7,58
Phạm Văn Tài884888

Bấm vào ô ngày 2 hoặc 3 (hay Tab tới) để mở thông báo; cuộn khung hoặc trang thì thông báo đi theo ô, ô khuất thì nó ẩn. Ngày 5 đang sửa: ô gõ rộng 5rem phủ lên ô bên cạnh. Ngày 6 tự tính (chữ nghiêng, sao).

Ô hai dòng: xem trước vân tay

Giờ quẹt ở dòng trên, kết quả ở dòng dưới; giờ quẹt bị bỏ qua thì gạch ngang.
td.cell-mark.cell-mark-2l > .cell-mark-sub (giờ quẹt, chữ nhỏ) + .cell-mark-main (kết quả, ghi chú trong <small>). Giờ quẹt không dùng viết bằng s.cell-mark-sub: gạch ngang, lý do ở dòng dưới (“chấm tay · giữ”, “bỏ qua”).
Công nhân05/10T206/10T307/10T408/10T5
Nguyễn Văn HùngMáy số 1407:28–17:058 giờ07:30–19:108 giờtăng ca 207:31–16:027,5 giờthiếu 0,507:29!1 lần quẹt
Trần Thị LanMáy số 2207:40–17:008 giờchấm tay · giữ07:12–07:14—bỏ quakhông quẹt07:25–17:018 giờ

Ô vàng: thiếu giờ hoặc một lần quẹt (chữ nói rõ). Hàng của Lan: ngày 5 giữ giá trị chấm tay, giờ quẹt gạch ngang; ngày 6 bỏ qua hai lần quẹt sát nhau.

Ô xếp loại theo tháng

Bảng người × 12 tháng: ô tô theo hạng (A+ tốt nhất … D kém nhất), chữ cái là tín hiệu chính.
td.cell-mark[data-grade="1".."5"]: nền --grade-N, chữ --on-grade-N (chưa có token thì lùi về --ok, --ok-line, --warn-line, --danger, --danger-hover). Trạng thái tháng: nhập từ file (đầu cột ghi “Từ file”, không có liên kết vì không có phiếu), chưa chốt (data-state="open": chữ nghiêng trong khung đứt; đầu cột ghi “Chưa chốt”), chưa tới (data-future, trống), không đánh giá (.cell-mark-muted với chữ “Không ĐG”). Khác với ô “chưa chấm” của bảng công: chưa chấm là vòng đứt rỗng không có giá trị, chưa chốt là giá trị nằm trong khung đứt.
  • Xuất sắc
  • Tốt
  • Khá
  • Trung bình
  • Kém
  • Chưa chốt
  • Không đánh giá
Họ tênT1Từ fileT2Từ fileT3Từ fileT4Từ fileT5Từ fileT6Từ fileT7Từ fileT8Từ fileT9Đã chốtT10Chưa chốtT11 T12 Trung bình
Vũ Thị LanTrưởng phòngA+AAAAABAA+A96,3
Đặng Văn BìnhKỹ sưABBACBBAAB88,1
Hoàng Văn NamKỹ thuật viênBCB–CDCBCC79,4
Lý Thu TrangKế toánAAA+AAA+AAAA+97,0

Tháng 1–8 nhập từ file năm (không bấm được); tháng 9 đã chốt (bấm mở phiếu); tháng 10 chưa chốt (khung đứt, chữ nghiêng); tháng 11–12 chưa tới. Hoàng Văn Nam không được đánh giá tháng 4.

Chú giải cho các trạng thái mới

  • Số giờ làm
  • Thiếu giờ
  • Nghỉ phép
  • Nghỉ không phép
  • Chưa chấm
  • Chưa vào làm, đã nghỉ việc
  • Chủ nhật (CN)
  • Thứ Bảy (T7)
  • Hôm nay
  • Chưa tới
  • Tự tính, sửa được
  • Từ máy vân tay

Mỗi ô mẫu vẽ đúng như ô thật: -warn, -missing (vòng đứt), -half, -future, -today, -open, [data-grade] và một svg.icon trong ô mẫu cho dấu ở góc.

Chú giải cho biểu đồ

  • Công thực tế
  • Công tăng ca
  • Kế hoạch

-solid là khối màu, -line là nét kẻ; màu lấy từ --legend-color (mặc định --series-1). Đường kẻ chấm không phải điều khiển; chữ luôn đi kèm.

Bảng công tháng: nhiều cột dính, khối theo người

Đầu bảng, bốn cột trái, cột Thực lĩnh bên phải và ba dòng tổng cùng đứng yên; mỗi người một khối nhiều dòng; dòng nhóm theo tổ; thanh công thức, vùng chọn và toàn màn hình.
.tbl-sticky-cols với data-sticky="1".."6" trên mọi ô của cột trái (số là vị trí cột, nên dòng 2..n của một khối vẫn dính đúng chỗ) và data-sticky="end" cho cột cuối; kit.js đo bề rộng (ResizeObserver) và ghi --sticky-x2..6, không có kit.js thì đặt --sticky-w1..6. Khung cuộn hẹp hơn 48rem thì cột phải thôi dính. tfoot.tbl-foot-rows: nhiều dòng tổng dính đáy, mỗi dòng cao --tbl-foot-row (chỉ CSS), ô rowspan phủ cả ba dòng. tbody.tbl-block: một khối mỗi người, kẻ đậm giữa các khối, rê chuột sáng cả khối. tr.grp: dòng nhóm dùng được ở mọi bảng. td[data-range="top start"]: vùng chọn khi dán hay điền. .tbl-formula: thanh công thức. button[data-fullscreen]: toàn màn hình, Esc để thoát.
  • Số giờ làm
  • Thiếu giờ
  • Nghỉ phép
  • Nghỉ không phép
  • Chưa chấm
  • Chưa vào làm, đã nghỉ việc
  • Chủ nhật (CN)
  • Thứ Bảy (T7)
  • Hôm nay
  • Chưa tới
  • Tự tính, sửa được
  • Từ máy vân tay

Bảng chấm công tháng 10/2026

5 người · hôm nay 23/10 · Tạm tính
Nguyễn Văn Hùng · Giờ làm · ngày 167,5P

Đang sửa: 7,5 giờ làm, phần còn lại của ca là nghỉ phép (P). Enter để lưu, Esc để bỏ.

STTMã thẻHọ tênHạng mục1T52T63T74CN5T26T37T48T59T610T711CN12T213T314T415T516T617T718CN19T220T321T422T523T624T725CN26T227T328T429T530T631T7TổngĐơn giáThành tiềnThực lĩnh
Tổ hàn 1 · 3 người
1CN-014Nguyễn Văn HùngChính thứcGiờ làm giờ88,5488887,5P488Chưa chấm848888820445.0009.180.0009.640.000 đ
Tăng ca giờ220,526,560.000390.000
Suất ăn suất111325.00075.000
2CN-022Trần Thị LanKhoánGiờ làm giờ4886,5884888Chưa chấm8485K888148,541.0006.088.5006.120.500 đ
Tăng ca giờ0,50,5160.00060.000
Suất ăn suất1125.00025.000
3CN-031Lê Quang MinhChính thứcGiờ làm giờ8848888848,57

Ô này vừa được sửa bởi Kế toán Hoa (10:42). Giá trị trên máy chủ là 8.

Giá trị đã gõ: 7, vẫn giữ trên ô.

88848888819645.0008.820.0008.915.000 đ
Tăng ca giờ11260.000120.000
Suất ăn suất025.0000
Tổ lắp dựng · 2 người
4CN-040Phạm Văn TàiKhoánGiờ làm giờ8P48888848888848888820036.0007.200.0007.402.000 đ
Tăng ca giờ1,51,51,51,51726.000182.000
Suất ăn suất2210.00020.000
5CN-047Đỗ Thị MaiKhoánGiờ làm giờ8848K886,54888884P80,5K8176,520.0003.530.000Âm 1.250.000 đ
Tăng ca giờ026.0000
Suất ăn suất025.0000
Sản lượng kg8508508501.2008508508508501.2008508508501.2008508508501.20085085085016.950——
Tổng giờ cả xưởng340334172340334344338338,51723383403423443381723443383403423448.214553.814.491 đ
Tổng tăng ca91011891011128101112891012891011196,5
Tổng suất ăn67575675656756767567118

Cuộn khung theo cả hai chiều: đầu bảng, bốn cột (STT, Mã thẻ, Họ tên, Hạng mục), cột Thực lĩnh và ba dòng tổng đứng yên. Hùng ngày 16 đang sửa (ô gõ 5rem), ngày 23 đang lưu; Minh ngày 12 lỗi lưu và 13 xung đột (bấm vào ô để xem thông báo, cuộn để thấy nó đi theo ô); Tài ngày 5–8 là một vùng chọn 2 × 4. Mai có bốn dòng (thêm Sản lượng, “1.200” kg) và thực lĩnh âm (chữ “Âm”). Bấm “Toàn màn hình” để bảng phủ cả cửa sổ; Esc để thoát.

Danh sách dòng

Danh sách việc cần làm, mỗi việc một dòng thay cho một bảng.
ul.work-list > li > a.work-item (là div khi dòng không phải liên kết; cả dòng bấm được, rê chuột tô nền): một dấu đầu dòng (.code-chip, icon), .work-main và .work-end. .work-main có .work-title (.work-code đậm rồi .work-name), .work-sub (một dòng mờ, dài thì cắt bằng dấu ba chấm) và .work-reason (icon và một câu màu cảnh báo: vì sao dòng này cần xem). .work-end có trạng thái (.tag, một con số) và .work-meta (ngày, giờ). Đặt trong .panel-flush để các dòng chạy sát mép thẻ, ngăn nhau bằng một vạch. Dưới 26rem bề ngang của chính nó, phần cuối chuyển xuống dưới chữ. Dùng cho hàng đợi ở trang tổng quan: vài dòng, mỗi dòng một việc. Khi người xem cần so sánh các cột giữa nhiều dòng thì dùng bảng.

Thẻ đủ rộng: trạng thái và thời gian nằm bên phải. Dòng có .work-reason là dòng lý do.

Thẻ hẹp (dưới 26rem): phần cuối nằm dưới chữ.

Tiêu đề nhóm trong danh sách

Dòng tiêu đề đứng trên một nhóm dòng: tên nhóm, số lượng và số liệu của nhóm (tổng ròng của ngày, “4/5 đã chấm”), có bản dính khi cuộn.
h3.group-head (hoặc h4) gồm .group-head-title (đậm, được xuống dòng), .group-head-count (mờ: “· 4” kèm đơn vị trong .visually-hidden, dấu chấm giữa để aria-hidden) và .group-head-sum ở cuối dòng, không bao giờ xuống dòng: số liệu có .num-in, .num-out, .num-neg bên trong, hoặc chữ như “4/5 đã chấm”. Nó là thẻ tiêu đề thật nên trình đọc màn hình nhảy được từ nhóm sang nhóm; đặt nó trên danh sách của nhóm (ul.work-list, ul, .menu, một div các dòng): mỗi nhóm là một tiêu đề và một danh sách. Nền --surface-2 giữa hai đường kẻ, đọc như dòng tổng của nhóm; lề hai bên lấy từ --group-head-px (mặc định --sp-4, bằng đệm của dòng bên dưới). .group-head-sticky dính ở đầu khung cuộn (--sticky-top là chiều cao thanh bên trên) trên nền --surface-2 có đường kẻ dưới để các dòng trượt qua phía dưới; bọc mỗi nhóm (tiêu đề và danh sách) trong một div thì tiêu đề sau đẩy tiêu đề trước ra thay vì đè lên. Không có nút gập nhóm.

Sổ quỹ nhóm theo ngày, kèm thu chi ròng

Mỗi ngày là một nhóm: tên ngày, số phiếu và tổng ròng của ngày (+ 2.350.000 đ xanh, − 3.550.000 đ đỏ; dấu đi cùng số nên màu không đứng một mình). Dòng ngày nằm trên nền --surface-2 giữa hai đường kẻ, đọc như dòng tổng của các phiếu bên dưới. Icon nói nguồn tiền: money tiền mặt, bank ngân hàng.

Nhóm công nhân theo tổ, số liệu bằng chữ

Số liệu có thể là chữ: “4/5 đã chấm”. Cùng lớp, cùng chỗ; không có dấu hay màu nào của sổ quỹ.

Tên nhóm dài, danh sách là các div

Danh mục chi phí vận hành của cơ sở sản xuất và các khoản phải trả định kỳ theo tháng3 mục

Điện− 2.870.000đ
Nước− 310.000đ

Tên nhóm quá dài thì xuống dòng, số đếm vẫn nằm liền một khối. Nhóm không có .group-head-sum chỉ còn tên và số đếm; các dòng bên dưới có thể là bất cứ thứ gì (ở đây là div).

Bản dính trong khung cuộn ngắn

Khung cắt ngang giữa một dòng phiếu (không cắt ngang tiêu đề), nên nhìn là biết còn nữa; cuộn thì tiêu đề ngày dính ở đầu, dòng trượt dưới nền --surface-2, ngày sau đẩy ngày trước đi. Khung có tabindex="0" để dùng được bàn phím.

Sortable rows

  • Vệ sinh cửa hàng đầu caHằng ngày · cần 2 ảnh
  • Kiểm tra nhiệt độ tủ lạnhHằng ngày
  • Chụp ảnh quầy pha chếThứ hai, thứ năm · cần 1 ảnh
  • Đóng cửa và khóa kétCuối ca

ul.sort-list[data-sortable]: kéo tay cầm (chuột hoặc ngón tay), hoặc bấm nút mũi tên lên xuống, là cách không cần kéo (bàn phím, đọc màn hình). kit.js dời dòng, đọc vị trí mới (“Vệ sinh cửa hàng đầu ca: vị trí 2 trên 4”) và bắn change với detail là { item, from, to }. Dòng đầu mất nút lên, dòng cuối mất nút xuống (aria-disabled).

Pager

Phân trang đầy đủ, có số dòng, gọn và Trước/Sau; tự gọn trên điện thoại.
nav.pager với aria-label: nút trước và sau (.btn-icon.btn-sm, aria-label), .pager-pages chứa các nút số (trang hiện tại aria-current="page") và .pager-gap cho dấu "…", .pager-status ("Trang 3 / 12") thay các nút số khi màn dưới 40rem hoặc khi thêm .pager-compact. .pager-center canh giữa khi đứng riêng; trong bảng, .pager-size và .pager-range nằm bên trái. Dãy số luôn đủ 7 chỗ (1 … 4 5 6 … 12) nên thanh không nhảy khi đổi trang; tenant tính dãy này, hàm mẫu ở cuối trang. Thu nhỏ cửa sổ dưới 40rem để xem hai kiểu đầu thu về "Trang x / y".

Notice, toast, empty state

Thông báo trong trang (bốn mức, hai mật độ) và toast góc trên phải.
.notice (-ok -warn -danger -info, -compact một dòng) gồm svg.notice-icon, .notice-body (.notice-title, nội dung, .notice-actions) và nút .notice-close. Mỗi mức có icon riêng (thông tin, thành công, cảnh báo tam giác, lỗi) và vạch màu bên trái, nên không chỉ dựa vào màu. Lỗi nên có role="alert", thành công role="status". Toast: .toast-host > .toast (-ok -warn -bad, mặc định là thông tin) gồm .toast-icon, .toast-body (.toast-title, .toast-text), .toast-action ("Hoàn tác") và .toast-close; nằm góc trên bên phải dưới thanh trên (điện thoại: cả chiều ngang) để không che phân trang; .empty.
Thông báo chung
Kỳ báo cáo tháng này chưa khóa.
Đã lưu
Phiếu PN-0017 đã được lưu.
Sắp đến hạn
3 phiếu cần duyệt trước thứ Sáu.
Mẹo
Dán nhiều dòng từ Excel vào bảng để nhập nhanh.
Kỳ báo cáo chưa khóa.Còn 2 phiếu chờ duyệt.
Nút đóng chỉ ẩn thông báo trong trang mẫu này.

Toast

Đã lưu phiếu PN-0017
Kho và công nợ đã cập nhật.
Không lưu được phiếu
Máy chủ không phản hồi. Thử lại sau ít phút.
Phiếu sắp quá hạn duyệt
3 phiếu cần duyệt trước thứ Sáu.
Đã đồng bộ 12 phiếu mới
Toast thành công tự ẩn sau 5 giây; cảnh báo và lỗi ở lại đến khi đóng.
Toast thành công và thông tin tự biến mất sau 5 giây, cảnh báo và lỗi ở lại đến khi đóng; rê chuột hoặc focus vào thì giữ lại. Mới nhất ở trên, tối đa ba, không bỏ toast lỗi để nhường chỗ.
Chưa có phiếu nào

Tạo phiếu đầu tiên để bắt đầu theo dõi.

Icon lấy từ icons.svg: .empty-ic tự lấy màu chữ nhạt và cỡ 3rem, có hoặc không có .icon.

Chưa có việc nào hôm nay

Quản lý chưa giao việc cho ca này.

Neutral notice

Thông báo xám có biểu tượng khóa, cho trạng thái đã khóa, bị chặn, chỉ xem.
.notice.notice-neutral dùng đúng cấu trúc của các mức khác (svg.notice-icon với icons.svg#lock, .notice-body với .notice-title và .notice-actions, button.notice-close; -compact cũng dùng được). Nền --surface-2, viền --panel-border, biểu tượng và vạch --muted, tiêu đề --text: các cặp màu này đã nằm trong bộ kiểm tra palette. Đây là trạng thái của trang, không phải tin báo gấp: dùng role="status" hoặc không có role, không bao giờ role="alert". Màu xám không phải dấu hiệu: biểu tượng khóa và câu chữ mới nói điều đó. Biểu tượng là phần markup như các mức khác, bỏ svg.notice-icon thì hộp vẫn đúng.
Kỳ tháng 9 đã khóa
Không sửa được phiếu trong kỳ này.

Có tiêu đề và một dòng giải thích.

Kỳ đã khóa, không sửa được.

Một dòng (-compact), không role.

Tài khoản bị khóa
Tài khoản này không đăng nhập được từ 28/10.

Có liên kết hành động trong .notice-actions.

Bạn chỉ có quyền xem
Liên hệ quản lý kho để được cấp quyền sửa phiếu kho.

Có nút đóng (.notice-close); trang chủ ẩn hộp khi đóng.

Phiếu đã chốt
Phiếu này đã vào sổ và chỉ để xem.

Không có biểu tượng (bỏ svg.notice-icon khỏi markup): vẫn là một hộp đúng, chữ nói nghĩa.

Tiến độ

Một tập tiêu chí, mỗi dòng có kết quả riêng bằng nhãn trạng thái.
Đặt trong .panel sau .panel-head (tên bên trái, số lượng bên phải bằng .muted). ul.progress-list > li.progress-item > .progress-row (span, hoặc a / button khi bấm dòng để mở chỉ tiêu đó) > .progress-no (mã do tenant đặt: "1/.", "A.2") + .progress-label + một .tag kết quả, cùng nhãn trạng thái với bảng: tag-ok đạt, tag-warn cần xem lại, tag-bad không đạt, tag-draft chưa có. Kết quả là chữ trong nhãn nên không phụ thuộc màu. Dòng đang xem (aria-current="true") có nền tô và vạch bên trái như dòng được chọn của bảng, chữ in đậm. Khác thanh bước (.stepper): thanh bước là các bước làm lần lượt, còn đây là các mục độc lập, mỗi mục chỉ báo kết quả của nó.

Chỉ tiêu

8

Hồ sơ nghiệm thu

4
  • 1/.Biên bản nghiệm thuĐạt
  • 2/.Bản vẽ hoàn côngCần xem lại
  • 3/.Chứng nhận xuất xưởngKhông đạt
  • 4/.Hóa đơn đầu vàoChưa kiểm

Trái: bấm dòng để chuyển dòng đang xem. Phải: dòng không bấm được, đủ bốn kết quả.

Thanh tiến độ báo trạng thái

Thanh tiến độ là nguồn duy nhất của một trạng thái (đã ghi 120/300 dòng): có vai trò thanh tiến độ, giá trị và câu nói cho trình đọc màn hình, kèm dòng chữ số; có bản chưa biết độ dài.
.bar nhỏ sau một con số vẫn là trang trí (aria-hidden="true", con số đứng cạnh nói thay). Khi thanh là nguồn duy nhất thì nó là thanh tiến độ: div.bar[role="progressbar"] với aria-valuenow, aria-valuemin, aria-valuemax, aria-valuetext (“Ghi 120/300 dòng”: câu mà trình đọc nói thay cho “40%”) và tên bằng aria-label hoặc aria-labelledby (“Ghi sổ”). Độ đầy là --v (từ 0% đến 100%), trang giữ cho khớp với aria-valuenow. Thanh rộng hết khung (--bar-w: 100%; quy tắc nào đặt bề rộng riêng, như thanh trong ảnh thu nhỏ, thì thắng) và dày 0,5rem khi đứng ngay dưới .bar-label (--bar-h đổi độ dày). data-tone="ok|warn|bad" đổi màu phần đầy (cùng bộ màu với .bar-ok, .bar-warn, .bar-bad); màu không bao giờ đứng một mình nên tình trạng phải có trong chữ. .bar-label là dòng chữ số đi kèm, ở trên hoặc bên cạnh: mờ, --font-figure, chữ số thẳng hàng; hai span trong đó nằm ở hai đầu. Nhãn lặp lại aria-valuetext nên khi tĩnh thì đặt aria-hidden="true"; khi trang đổi nhãn theo tiến độ thì dùng role="status" (polite) và để nó đọc được, vì thanh tiến độ không tự được đọc lại khi giá trị đổi. .bar[data-indeterminate] cho việc chưa biết độ dài: không có aria-valuenow (giữ aria-valuetext, “Đang ghi…”), phần đầy là sọc chéo chạy khắp thanh; khi người dùng chọn giảm chuyển động thì sọc đứng yên. Phần demo cập nhật bên dưới (nút “Ghi 10 dòng”) chỉ là script của thư viện mẫu.

0%, 40% và 100% với tone

Mỗi thanh có aria-label và aria-valuetext; dòng chữ số phía trên chỉ lặp lại nên có aria-hidden="true". Tone nằm cả trong chữ (“xong”, “Gần hết hạn mức”, “Dừng vì lỗi”).

Chưa biết độ dài (data-indeterminate)

Không có aria-valuenow. Sọc chéo chạy khắp thanh; dừng lại khi hệ điều hành bật “giảm chuyển động”.

Cập nhật trực tiếp (demo của thư viện mẫu)

Ghi 0/300 dòng0%

Mỗi lần bấm: aria-valuenow, aria-valuetext, --v và nhãn cùng đổi; nhãn là role="status" nên trình đọc màn hình nói câu mới. Đủ 300 dòng thì thanh sang tone ok và nút tắt.

Chú thích và tooltip

Giữ một dòng phụ đề; lời giải thích dài ẩn đến khi bấm hoặc rê chuột.
.subtext là <details>: summary chứa .subtext-line (một dòng, cắt bằng "…" khi dài) và svg.subtext-caret, nội dung ẩn nằm trong .subtext-more. .tip là bong bóng chữ của nút .tip-btn (aria-controls, aria-describedby, aria-expanded), đặt cạnh nhãn hoặc tiêu đề trong .tip-row; mở khi rê chuột hoặc focus, bấm để giữ mở (trên điện thoại là chạm), Esc hoặc bấm ra ngoài thì đóng. Điều người dùng phải đọc mới điền được (định dạng bắt buộc, lỗi) không phải chú thích: để hiện sẵn bằng .field-hint hoặc .field-error.

So sánh: nhiều chữ và gọn

Thêm khách hàng

Nhập thông tin khách hàng mới. Thông tin này được dùng để lập phiếu bán, theo dõi công nợ và in giấy báo. Các trường có dấu * là bắt buộc.

Nhập đúng tên như trên hợp đồng hoặc hóa đơn, không viết tắt.

Gồm 10 số, bắt đầu bằng 0.

Tổng tiền khách được nợ cùng lúc. Vượt hạn mức thì phiếu bán mới cần quản lý duyệt. Để 0 nếu khách trả ngay.

Thêm khách hàng

Dùng cho phiếu bán, công nợ và giấy báo.
Nhập thông tin khách hàng mới. Thông tin này được dùng để lập phiếu bán, theo dõi công nợ và in giấy báo. Các trường có dấu * là bắt buộc.

Gồm 10 số, bắt đầu bằng 0.

Trái: lời giải thích hiện sẵn dưới mọi ô, màn hình dài và rối. Phải: một dòng phụ đề, giải thích đặt sau nút ⓘ; riêng định dạng số điện thoại cần để điền đúng nên vẫn hiện sẵn.

Tiêu đề có nút giải thích

Công nợ phải thu

Tính đến hết ngày 29/10/2026, 6 khách quá hạn.
Số liệu cập nhật mỗi đêm lúc 00:30. Khách quá hạn là khách có phiếu bán chưa thu quá 30 ngày kể từ ngày giao. Phiếu nháp và phiếu đã hủy không được tính.

Tooltip

Tên của nút chỉ có biểu tượng, hiện khi rê chuột một lúc hoặc khi tới bằng bàn phím.
Nút vẫn có aria-label làm tên; thêm data-tooltip để hiện chính chữ đó (hoặc chữ trong data-tooltip="…", ví dụ kèm phím tắt). Tenant giữ một .tooltip[popover="manual"] có aria-hidden="true" (tên đã nằm ở nút, trình đọc màn hình không đọc hai lần), hiện nó phía trên nút hoặc phía dưới khi hết chỗ, đóng khi chuột rời, nút mất tiêu điểm, cuộn trang hoặc nhấn Esc; chuột được phép di sang chính nó. Vì là popover nên không bị khung cuộn của bảng hay hộp thoại cắt. Là gợi ý, không phải chỗ cho thứ người dùng bắt buộc phải đọc: màn hình cảm ứng không có, nên biểu tượng khó đoán thì cần nhãn chữ. Không dùng cho nút disabled. Khác với .tip (chữ giải thích đứng cạnh nhãn hoặc tiêu đề, bấm để giữ).
Số phiếuKhách hàngThao tác
PN-0012Công ty Phú Thành
PN-0013Nguyễn Văn An

Hàng trên: bốn nút, nút cuối có chữ riêng kèm phím tắt. Bảng dưới: nút trong hàng của bảng mở tooltip mà khung cuộn của bảng không cắt được, vì nó nằm ở lớp trên cùng của trang.

Modal và drawer

Hộp thoại mở bằng showModal(), có sẵn lớp phủ, khóa focus và phím Esc.
<dialog class="modal"> (-sm -lg) với .modal-head, .modal-body, .modal-foot; mở bằng showModal(), trình duyệt lo lớp phủ, khóa focus và phím Esc. Trên điện thoại nó thành tấm trượt từ dưới lên.

Drawer

Ngăn trượt từ phải hoặc trái cho chi tiết bản ghi và bộ lọc.
<dialog class="drawer"> (-lg rộng 560 px, -left trượt từ trái) với .drawer-head, .drawer-body, .drawer-foot; mở bằng showModal() như hộp thoại, nên có lớp phủ, khóa focus và phím Esc, bấm nền thì đóng. Dùng cho chi tiết một bản ghi hoặc biểu mẫu lọc mà người dùng không muốn rời trang; việc cần quyết định (xác nhận hủy) vẫn là hộp thoại. Trên điện thoại nó chiếm gần hết chiều ngang và chừa một dải lớp phủ.

Phiếu nhập PN-0013

Nguyễn Văn An · 03/10/2026 14:00

Chờ duyệt
Nhà cung cấp
Nguyễn Văn An NCC-044
Kho nhập
Kho 1
Số lượng
4,162 kg
Thành tiền
3.280.000 ₫
Ghi chú
Chờ phiếu kiểm nghiệm
Mặt hàngSố lượngThành tiền (₫)
Ớt bột loại 12,5001.300.000
Tiêu xay1,6621.980.000
Phiếu chờ kế toán duyệt trước khi cộng vào kho.

Bộ lọc phiếu

Kết quả cập nhật khi bấm Áp dụng.

Trạng thái
Tất cảChờ duyệtĐã duyệt
  • Công ty Phú ThànhNCC-012
  • Cửa hàng Minh SơnNCC-007
  • Hợp tác xã Nga ThanhNCC-031
  • Nguyễn Văn AnNCC-044
  • Trần Thị BìnhNCC-052
  • Công ty Thanh NhànNCC-060
  • Đại lý Hòa AnNCC-018
  • Nhà phân phối Đông ÁNCC-025
  • Cơ khí Đại PhátNCC-071
  • Xưởng bao bì Thành CôngNCC-081
  • Cửa hàng Ánh DươngNCC-090
  • Công ty Việt HưngNCC-094

Tabs và menu

Thẻ chuyển (tabs) và nút chọn kiểu viên thuốc.
.tabs (role="tablist") > .tab (role="tab", aria-selected, .tab-count); nội dung là .tab-panel. Kiểu viên thuốc: .tabs-pill. Phím mũi tên, Home, End do tenant gắn (mẫu ở cuối trang).

24 phiếu trong tháng này.

Menu

Menu mở từ nút: thao tác, chọn một, bật tắt mục, tài khoản.
.menu là popover mở từ một nút aria-haspopup="menu" có aria-controls trỏ tới menu. Mục là .menu-item (role="menuitem"); chọn một trong nhiều là role="menuitemradio", bật tắt từng mục là role="menuitemcheckbox", cả hai dùng aria-checked và .menu-check để hiện dấu tích; nhóm có role="group" với .menu-label; đầu menu tài khoản là .menu-head chứa .avatar-label. Nút có chữ dùng data-menu-align="start" để canh mép trái. Bấm Esc hoặc ra ngoài thì đóng, mũi tên, Home, End di chuyển, focus về nút khi đóng. Danh sách chọn có tìm kiếm ở mục "Dropdown có tìm kiếm".
Thao tác, chọn một, bật tắt cột, tài khoản
Nút trông như ô nhập; bấm hoặc nhấn Ctrl K để mở hộp tìm trong mọi bản ghi.
.search-btn (có .search-btn-text và kbd.kbd gợi ý phím) mở dialog.modal.modal-search: .search-box chứa input.search-input[role="combobox"], .search-results[role="listbox"] gồm các .search-group và .search-item[role="option"] (.search-item-icon, -title, -sub, -meta), .search-empty và .search-foot. Dưới 40rem hoặc với .search-btn-compact nút chỉ còn icon, chữ vẫn nằm trong trang cho trình đọc màn hình. Trên .topbar nút tự đổi sang màu header. Tenant tự lấy kết quả (gọi API, nhóm theo loại), đặt data-active khi bấm ↑ ↓ và mở mục khi bấm Enter; gallery chỉ lọc vài mục mẫu, không cần gõ dấu.

Trên thanh trên cùng

TechViet
Khách hàng
Công ty Phú ThànhNCC-012 · 0912 345 678Nợ 12.450.000 ₫
Nguyễn Văn AnNCC-044 · 0988 112 233Nợ 3.280.000 ₫
Hợp tác xã Nga ThanhNCC-031 · 0905 667 788Hết nợ
Phiếu
PN-0101Công ty Phú Thành · 01/10/202612.450.000 ₫
PN-0103Hợp tác xã Nga Thanh · 03/10/2026890.000 ₫
Mặt hàng
Kẹo dẻo trái cây 500gSP-0231Tồn 1.250,500 kg
Bim bim cua 20gSP-0045Tồn 320 thùng
↑ ↓ chọnEnter mởEsc đóng

Mục lục trang

Danh sách liên kết tới các phần của một trang dài, tô phần đang xem.
nav.toc[aria-label] > ul > li > a; phần đang xem có aria-current="location" (đậm, vạch bên trái đổi màu chính, không chỉ đổi màu). Một danh sách đặt sau liên kết là mục con. .toc-label là tiêu đề nhóm (chữ, không phải liên kết) và có danh sách riêng. Không thêm class thì mục lục giữ kiểu mặc định này (vạch trái); các ví dụ dưới đây dùng kiểu Vibe. Dùng cho trang dài có nhiều phần: cài đặt, hướng dẫn, biểu mẫu chia mục. Giữ nó trong tầm nhìn bằng position: sticky ở phần bọc, và chuyển dấu khi cuộn; cột bên phải của chính trang này là mục lục dựng từ component này (từ 90rem trở lên nó nằm bên phải; hẹp hơn nó gập thành "Trên trang này" ở đầu trang). Trang ngắn hoặc đã có thẻ chuyển (.tabs) thì không cần.

Kiểu Vibe: .toc-pill (bỏ vạch trái, mục đang xem là viên xám đậm chữ), .toc-card + .toc-title (thẻ có tiêu đề), details.toc-float (thu thành nút icon, mở ra bên dưới). Mục con là một danh sách đặt sau liên kết. Cột phải của gallery là chính thẻ này, kèm mục con. Cột trái là thanh bên của kit: mỗi nhóm một details.side-group, mỗi phần một mục con.

Dải đường dẫn và tiêu đề phần

Dải liên kết ngang tới các phần của một trang, cuộn ngang trong chính nó ở phone, ẩn bớt mục theo vai trò; và tiêu đề của một phần có đường kẻ dưới và chỗ cho nút ở cuối.
nav.toc.toc-strip[aria-label] > ul > li > a: một cấp, không có .toc-label. Ở mọi bề rộng các liên kết nằm một hàng và cuộn ngang bên trong dải (không bao giờ cuộn cả trang), bám vào mép đầu; mỗi liên kết cao ít nhất --tap, chữ không xuống dòng. Phần đang xem đánh dấu aria-current="true" (aria-current="location" cũng được): chữ đậm, màu --text và thanh 2 px --primary ở dưới, nên dấu hiệu không chỉ là màu. Trang tự chuyển dấu khi cuộn. Muốn ẩn một mục theo vai trò thì đặt thuộc tính hidden trên li (hoặc a): CSS không phải làm gì, các mục còn lại khép lại. Mọi liên kết đều focus được nên người dùng bàn phím tới được mục ngoài màn hình (trình duyệt tự cuộn). .section-head (div hoặc header) > h2 (hoặc h3) + .section-head-actions (tùy chọn: nút “Excel”, một liên kết) có đường kẻ 1 px dưới --panel-border; không cần panel. Tiêu đề lấy chỗ còn lại, các nút nằm ở cuối; khi tiêu đề hẹp hơn khoảng 28rem thì các nút xuống dưới tiêu đề. Màu: --text, --muted và --primary (thanh, không phải chữ) trên --bg và --surface, đã được kiểm.

Dải sáu liên kết, một mục ẩn theo vai trò

Mục “Thu chi” đang xem (aria-current="true", thanh dưới). “Lãi gộp” có hidden (chỉ Quản trị thấy) nên không chiếm chỗ. Ở phone dải cuộn ngang trong chính nó; trang không cuộn ngang. Bấm một liên kết để thấy dấu nhảy về đúng phần dưới.

Tiêu đề phần: có nút Excel và không có nút

Thu chi trong quý 3

Phần nội dung của “Thu chi”.

Công nợ

Phần nội dung của “Công nợ”: tiêu đề không có nút.

Tiêu đề thứ nhất có .section-head-actions; khi chỗ hẹp hơn khoảng 28rem (như ở phone) nút xuống dưới tiêu đề. Tiêu đề thứ hai chỉ có đường kẻ.

Khung hẹp 18rem: nút xuống dưới tiêu đề

Nhân công và chấm công tháng mười

Tiêu đề dài xuống dòng, nút ở dòng dưới; đường kẻ nằm dưới cả hai.

Page head and columns

Đường dẫn, đầu trang với tiêu đề, nhãn và nút, rồi hai cột có cột phụ dính.
Khung của một trang trong .container: .stack (các khối xếp dọc, --stack-gap) > nav.crumbs[aria-label] > ol > li (trang hiện tại aria-current="page"), rồi header.page-head gồm khối tiêu đề (h1, .page-lead mô tả, .page-meta một dòng nhãn và dữ kiện) và .page-actions ở bên phải (rơi xuống dưới tiêu đề khi hẹp), rồi .page-cols > .page-main + .page-side (thêm .stack khi có nhiều thẻ). Hai cột nằm cạnh nhau khi cột chính còn ít nhất --main-min (36rem) và cột phụ rộng --side-w (20rem), tính theo chỗ trang có nên chạy được cả khi có thanh bên; không đủ chỗ thì cột phụ xuống dưới cột chính, rộng hết hàng. Cột phụ giữ trong tầm nhìn khi cuộn (cửa sổ cao từ 40rem), bên dưới --sticky-top (chiều cao thanh trên và một khoảng); nên để cột phụ ngắn hơn cột chính, và đặt nút chính của trang ở đó. --container-w đổi bề rộng tối đa của .container (72rem). Đổi cửa sổ hẹp dưới khoảng 60rem để xem cột phụ xuống dưới. Hàng đợi hai cột đều nhau dùng .page-cols.page-cols-even (xem ví dụ thứ hai). Cột phụ dính dưới thanh trên cùng ở --topbar-real-h (chiều cao thật của thanh: nút + đệm + viền, 61px ở kiểu chuẩn, 65px ở kiểu app); --topbar-h chỉ là chiều cao tối thiểu.

TH-D3-15 · Thép tròn trơn và thép thanh vằn

Đường D3 nối dài – gói thầu XL-02 · Cống hộp K0+350

Chờ duyệtĐạt · 10 cỡLấy mẫu 24/10/2026Bùi Đức Thịnh

Thông tin trên phiếu

Đơn vị yêu cầu
Công ty CP Xây dựng Cầu đường Thành Đạt
Công trình
Đường D3 nối dài – gói thầu XL-02
Hạng mục
Cống hộp K0+350
Tư vấn giám sát
Công ty TNHH Tư vấn Giám sát Sao Việt

Lịch sử sửa từ đầu

3
  1. Gửi duyệt lại

    Bùi Đức Thịnh

  2. v8

    Sai số KL D22: Không đạt → Đạt

    Đổi kết luậnBùi Đức Thịnh

    Lý do: thử lại theo TCVN 1651: mẫu 2, 3 rớt thật

  3. Gửi duyệt lần 1

    Kết luận lúc đó: Không đạt

Ghi chú

Cột bên phải giữ nút phát hành trong tầm nhìn khi cuộn qua các thẻ dài như thế này.

Khung này lấy chỗ của riêng nó: cửa sổ rộng thì hai cột, hẹp thì cột phải xuống dưới. Cuộn trang để thấy cột phải đi theo.

Hai cột đều nhau

Nháp của tôi 2

TH-D3-14 · Cát vàng

TH-D3-16 · Đá 1×2

Sắp có kết quả 1

XM-D3-02 · Xi măng, đủ 28 ngày vào mai

Chờ duyệt 3

TH-D3-15 · Thép tròn trơn

TH-D3-12 · Bê tông M300

TH-D3-11 · Gạch

Không đạt trong 30 ngày 0

Không có kết quả không đạt.

.page-cols.page-cols-even > hai (hoặc ba) khối con, mỗi khối thường là .stack của vài thẻ: các cột rộng bằng nhau khi mỗi cột còn ít nhất --col-min (28rem), không đủ chỗ thì xếp dọc. Không có cột dính.

Thẻ dòng hàng, dòng kiểm kê và nút thêm gạch đứt

Dòng hàng của phiếu kho trên điện thoại là một thẻ (tên, mã · đơn vị, hai ô lớn số lượng và đơn giá, thành tiền, lỗi riêng), cuối danh sách có nút “+ Thêm vật tư”; dòng kiểm kê có ô nhập số ngay trong dòng.
li.line-card (trong ul.line-card-list, cách nhau --sp-3): .line-card-head (.line-card-name đậm, .line-card-sub mờ “VT-0101 · kg”, nút xóa .btn.btn-ghost.btn-icon ở cuối, có tên theo dòng), .line-card-body (hai .field cạnh nhau, nhãn trên ô: input[data-number] cho số lượng, input[data-money] cho đơn giá; chữ lớn hơn cỡ ô thường, số căn phải, inputmode thập phân; ghi đơn vị trong nhãn), .line-card-total (.line-card-total-label “Thành tiền” và output.line-card-total-figure, không xuống dòng, dùng --font-figure; output tự là vùng thông báo lịch sự) và p.field-error[aria-live="polite"] luôn có trong thẻ, trống cho đến khi dòng sai: có chữ thì thẻ viền đỏ, ô lỗi có aria-invalid="true" và aria-describedby trỏ vào lời lỗi. Tenant tự tính thành tiền (demo này có một đoạn script của riêng trang mẫu). button.btn.btn-dashed.btn-block ở cuối là nút thêm dòng: là .btn với viền đứt, đủ các trạng thái của .btn. li.count-row trong ul.count-list: .count-row-main (tên, mã · đơn vị), .count-row-book (“Sổ: 120,5”), .count-row-field với nhãn .visually-hidden và input[data-number] hẹp, và .count-row-diff bên dưới: .num-out/.num-in kèm chữ “Lệch” và dấu, hoặc chữ mờ “Khớp”; trống thì không chiếm chỗ. .work-act giờ cũng chứa được một .input (rộng 6 rem, số căn phải).

Hai thẻ dòng hàng, một thẻ có lỗi, và nút thêm

  • Thép hộp 40×40×1,4VT-0101 · kg
    Thành tiền481.250đ

  • Que hàn 3,2 mm (hộp 5 kg)VT-0214 · hộp
    Thành tiền468.000đ

    Số lượng 9 hộp lớn hơn tồn kho 8 hộp. Giảm số lượng hoặc đổi sang kho khác.

Sửa số lượng hoặc đơn giá thì thành tiền đổi theo (script của trang mẫu). Thẻ thứ hai có lời lỗi: viền đỏ, lời lỗi nằm trong thẻ và trình đọc màn hình đọc nó khi nó xuất hiện. Cỡ 24 rem là điện thoại 390 px trừ lề.

Danh sách kiểm kê: lệch, khớp, chưa đếm

  • Thép hộp 40×40×1,4VT-0031 · cây
    Sổ: 120,5
    Lệch − 2,5
  • Thép tấm 3 ly (cuộn)VT-0044 · kg
    Sổ: 386,4
    Khớp
  • Ống kẽm Ø 27 dài 6 mVT-0052 · cây
    Sổ: 45

Dòng 1 đếm ít hơn sổ (.num-out, chữ “Lệch” và dấu trừ), dòng 2 khớp (chữ mờ), dòng 3 chưa đếm: ô trống và không có dòng lệch.

Ô nhập trong dòng việc

.input trong .work-act: ô hẹp 6 rem, số căn phải, nhãn ẩn nêu tên dòng.

Offline draft

Biểu mẫu giữ bản nháp trên máy và hỏi có khôi phục không; báo khi mất mạng.
form[data-draft="khóa"] (khóa riêng cho từng biểu mẫu, ví dụ phieu-chi:new; data-draft-ttl là số giờ, mặc định 72). kit.js chỉ lưu vào localStorage (tvkit:draft:khóa) và không gửi gì qua mạng: lưu thật và gửi lại khi có mạng là việc của tenant. Sau 400 ms không gõ nữa, các ô có name được lưu: ô chữ, số, ngày, giờ, số tiền (như đang hiện, “1.250.000”), textarea, select, checkbox, radio, ô dropdown của kit (chữ của mục đang chọn) và [role="radiogroup"][data-name] như .seg (data-value của mục, hoặc chữ của mục). Không bao giờ lưu: mật khẩu, tệp, ô ẩn, nút, ô khóa hoặc chỉ đọc, và gì nằm trong [data-draft-skip]. Mở lại biểu mẫu mà có bản nháp còn hạn và khác giá trị ban đầu thì đầu biểu mẫu có .notice.notice-warn.draft-banner[role="status"] với “Khôi phục” (điền lại từng ô, bắn input và change nên ô số tiền, dropdown, lịch hiện đúng; focus về ô đầu tiên được điền; bỏ thanh) và “Bỏ nháp” (xóa nháp và thanh). Trong lúc thanh còn hiện, kit không lưu đè lên bản nháp đang chờ quyết định. TvKit.draft.clear(form hoặc khóa) gọi sau khi lưu thành công (biểu mẫu reset cũng xóa), TvKit.draft.save() lưu ngay, TvKit.draft.has(khóa) hỏi có nháp không. Khi trình duyệt mất mạng (offline), biểu mẫu thêm dòng .draft-offline[role="status"] (đầu .action-bar nếu có, không thì cuối biểu mẫu) và bắn draft-offline; khi có mạng lại bắn draft-online với detail.hasDraft, để tenant gửi lại. Câu chữ là tiếng Việt mặc định, đổi bằng data-draft-msg-found|restore|discard|offline|yesterday hoặc TvKit.draft.messages. Nếu bộ nhớ trình duyệt bị chặn hay đầy thì không có nháp và không có lỗi.
đ

Số tiền bằng chữ hiện ở đây.

  • Công ty Phú ThànhNCC-012
  • Cửa hàng Minh SơnNCC-007
  • Nguyễn Văn AnNCC-044
Loại phiếu

Ô mật khẩu không bao giờ vào bản nháp.

Gõ vài ô, đợi nửa giây rồi tải lại trang: đầu biểu mẫu có thanh “Có bản nháp chưa lưu từ …”. “Lưu phiếu” giả lập lưu thành công và gọi TvKit.draft.clear; “Nhập lại” (reset) cũng xóa nháp.

Thử nghiệm

Chưa có sự kiện nào.

“Giả lập mất mạng” bắn sự kiện offline rồi online lên window, không cắt mạng thật: biểu mẫu hiện dòng .draft-offline và bắn draft-offline, rồi draft-online kèm detail.hasDraft.

Chấm công theo ngày

Mỗi người một dòng: nút nhanh, ô giờ và tăng ca, suất ăn; chỉ ngày khác thường mới cần chạm.
ul.entry-list > li.entry-row: .entry-who (tên, mã, đơn giá, giờ quẹt), .entry-quick (một .seg với data-tone: Đủ giờ, Nửa ngày, P, K), .entry-field.entry-hours và .entry-ot (.input-affix có đơn vị), .entry-toggles (chip bật tắt, .mark-auto “tự” khi phần mềm tự bật), p.entry-note (.entry-note-warn cho cảnh báo). data-state="changed": nền vàng nhạt và vạch trái khi đã sửa chưa lưu. Danh sách là container: từ 72rem một dòng, 36–72rem hai dòng, dưới 36rem xếp dọc với nút nhanh chiếm cả chiều ngang.

Tổ hàn 1· 4 người3/4 đã chấm

  • Nguyễn Văn HùngCN-014 · 45.000 đ/giờ · 8 giờ/ngày · 07:28–17:05
    giờ
    giờ TC
  • Trần Thị Lan, đã sửa, chưa lưuCN-022 · Khoán · 8 giờ/ngày · 07:31
    giờ
    giờ TC

    Máy vân tay chỉ có một lần quẹt (07:31). Cơm tăng ca tự bật vì tăng ca trên 1,5 giờ.

  • Lê Quang MinhCN-031 · 45.000 đ/giờ · 8 giờ/ngày
    giờ
    giờ TC

    Nghỉ nên bữa trưa tự bỏ; bật lại được nếu vẫn ăn.

  • Phạm Văn TàiCN-040 · Khoán · 8 giờ/ngày · nghỉ việc từ 28/10
    giờ

    Số giờ làm không quá 24.

    giờ TC

Hùng: ngày thường, không chạm gì. Lan: đã sửa chưa lưu (nền vàng, vạch trái), dòng cảnh báo và “Cơm TC tự”. Minh: nghỉ phép (P), ô tăng ca và Ca đêm khóa. Tài: số giờ sai, lỗi nằm ngay dưới ô. Thu hẹp cửa sổ để thấy dòng xuống hai dòng (tablet) rồi xếp dọc (điện thoại).

Filter bar

Bộ lọc nhiều ô trên một danh sách, kèm dòng "Đang lọc" và dòng kết quả.
.list-filters xếp ba phần cách nhau: .panel chứa .filter-bar, hàng .filter-active và .result-bar. Trong .filter-bar mỗi .field có bề rộng riêng (--field-w, mặc định 13rem), .filter-grow cho ô tìm lấy phần còn lại. Ô tìm có icon là .input-icon; khoảng ngày là .range (hai ô quanh .range-sep, nhóm có role="group" và aria-labelledby). Mỗi bộ lọc đang bật là một .chip.chip-value với nút .chip-x (aria-label nói rõ bỏ bộ lọc nào), cuối hàng là .btn.btn-link "Xóa lọc". .seg là nhóm radio dạng nút liền (role="radiogroup", .seg-item[role="radio"]) để đổi cách sắp xếp hay xem. Gallery nối sẵn các ô với chip; khi bỏ một chip, focus chuyển sang chip kế tiếp.
  • Tất cả vật liệu
  • Bê tông
  • Thép
  • Xi măng
  • Mọi trạng thái trừ Hủy
  • Bản nháp
  • Chờ duyệt
  • Đã phát hành
  • Đã hủy
  • Mọi kết luận
  • Đạt
  • Không đạt
Ngày lấy mẫu
Đang lọc:

62 mẫu · 9 nháp · 6 chờ duyệt · 46 đã phát hành · 1 đã hủy

Table as cards

Bảng nhiều cột thành danh sách thẻ khi khung hẹp, để đọc từ trên xuống thay vì cuộn ngang.
Thêm .tbl-cards vào .tbl-wrap và data-label (tên cột) vào mỗi td. Khung rộng dưới 36rem thì mỗi hàng thành một thẻ: ô đầu (th scope="row") là tiêu đề, các ô còn lại xếp hai cột với nhãn trên giá trị, nút của hàng (td.actions) nằm dưới cùng, ô chọn lên góc. Rộng hơn thì vẫn là bảng. Khung bọc là container nên một bảng trong cột hẹp cũng đổi, không chỉ trên điện thoại. Dùng cho bảng thường: không kèm -sticky, -select hay .tbl-scroll. Hàng tiêu đề vẫn còn cho trình đọc màn hình.

Khung hẹp (điện thoại)

Phiếu gần đây

Số phiếuKhách hàngTrạng tháiSố lượng (kg)Thành tiền (₫)Thao tác
PN-0012Ớt bột
Công ty Phú ThànhĐã duyệt1.250,50012.450.000
PN-0013Tiêu xay
Nguyễn Văn AnChờ duyệt4,1623.280.000
PN-0014Muối tinh
Cửa hàng Minh SơnBị từ chối0,350890.000

Khung rộng

Phiếu gần đây

Số phiếuKhách hàngTrạng tháiSố lượng (kg)Thành tiền (₫)Thao tác
PN-0012Ớt bột
Công ty Phú ThànhĐã duyệt1.250,50012.450.000
PN-0013Tiêu xay
Nguyễn Văn AnChờ duyệt4,1623.280.000
PN-0014Muối tinh
Cửa hàng Minh SơnBị từ chối0,350890.000

Cùng một bảng, hai độ rộng khung: dưới 36rem là thẻ, trên đó là bảng. Ô td.actions không có data-label nên không có nhãn; ô th đầu là tiêu đề của thẻ.

Ít cột số: --cards-cols: 3

Sổ nhập kho

PhiếuSố lượngĐơn giáThành tiền
PN-0012 · Ớt bột12,586k1,08tr
PN-0013 · Tiêu xay4,162120k499k
PN-0014 · Muối tinh0,3512k4k

--cards-cols: 3 trên .tbl-cards (mặc định 2) xếp ba ô cạnh nhau dưới tiêu đề thẻ: bảng ít cột số ngắn thấp hơn gần một nửa. Mỗi con số tối đa chín ký tự (hoặc viết gọn k, tr).

Bảng công trên điện thoại

Mỗi người một thẻ; mở thẻ thì 31 ngày cuộn ngang trong thẻ, cột hạng mục đứng yên, trang không cuộn ngang.
Thẻ là details.row-fold; trong thân là .tbl-wrap.tbl-scroll > .tbl.tbl-sticky-cols.tbl-marks với một cột dính data-sticky="1" và --sticky-w1: 8.5rem (chỉ CSS, không cần kit.js). Khi chạm, ô ngày về --tap. Năm của một người (xếp loại 12 tháng) là ol.mark-strip: 12 ô nhỏ dưới tên, mỗi ô có tháng và hạng bằng chữ cho trình đọc màn hình.
  • Trần Thị Lan148,5 giờ · 1 giờ TC · 6.120.500 đ
    Hạng mục1T52T63T74CN5T26T37T48T59T610T711CN12T213T314T415T516T617T718CN19T220T321T422T523T624T725CN26T227T328T429T530T631T7
    Giờ làm giờ4886,5884888Chưa chấm8485K888
    Tăng ca giờ0,50,5
    Suất ăn suất1
  • Nguyễn Văn Hùng204 giờ · 6,5 giờ TC · 9.640.000 đ
    Mở để xem 31 ngày.

Kéo ngang bảng trong thẻ: cột “Hạng mục” rộng 8,5rem đứng yên, trang không cuộn ngang ở 390px.

Năm của một người

Vũ Thị Lan
Trưởng phòng
96,3
8/9 tháng ≥ A
  1. Tháng 1: A+
  2. Tháng 2: A
  3. Tháng 3: A
  4. Tháng 4: A
  5. Tháng 5: A
  6. Tháng 6: A
  7. Tháng 7: B
  8. Tháng 8: A
  9. Tháng 9: A+
  10. Tháng 10: A, chưa chốt
  11. Tháng 11: chưa tới
  12. Tháng 12: chưa tới

Tháng 10 chưa chốt (khung đứt, chữ nghiêng), tháng 11–12 chưa tới (ô trống).

Chi tiết bản ghi

Danh sách nhãn và giá trị của một bản ghi, và dòng lịch sử các lần sửa.
dl.dl > div > dt + dd. Mỗi hàng đặt nhãn và giá trị cạnh nhau khi đủ chỗ, và nhãn nằm trên giá trị khi hẹp (cột bên, điện thoại), nên không cần điểm ngắt riêng. -lines kẻ một vạch giữa các hàng, -tight là cỡ nhỏ cho cột bên; --dl-key đổi độ rộng nhãn, --dl-val độ rộng tối thiểu của giá trị. Dùng thay bảng hai cột cho thông tin của một bản ghi (phiếu, công trình, khách). Lịch sử: ol.timeline > li.timeline-item > .timeline-mark (phiên bản, ví dụ v9, trong một viên; dòng không có thì có một chấm) và .timeline-body > .timeline-title (điều đã đổi), .timeline-meta (ai và lúc nào, span và time cách nhau một dấu chấm giữa; nhãn .tag đứng đầu nếu có), .timeline-note (lý do, trong hộp nền nhạt). Dòng mới nhất là aria-current="true" (viên hoặc chấm tô màu chính). Dòng không được bỏ sót có data-tone="warn|danger|ok|info" (viên hoặc chấm và hộp lý do cùng sắc); sắc không phải dấu hiệu duy nhất, chính dòng phải nói điều gì đã đổi, và nên kèm một nhãn .tag. Khác thanh bước dọc (.stepper-vertical): thanh bước là tiến độ các bước làm lần lượt, còn đây là nhật ký các lần sửa, không có bước xong hay chưa. .timeline-feed là dạng dòng hoạt động quanh một bản ghi: mỗi dòng là ngày (time.timeline-date, chấm trên đường), hàng bo tròn .timeline-card (.timeline-ic + .timeline-text + nút mở ở cuối) và tùy chọn .timeline-detail (nhãn, giá trị, nút).

Thông tin trên phiếu

Đơn vị yêu cầu
Công ty CP Xây dựng Cầu đường Thành Đạt
Công trình
Đường D3 nối dài – gói thầu XL-02
Hạng mục
Cống hộp K0+350
Ngày ký
27/10/2026 (ngày thí nghiệm cuối)
Tư vấn giám sát
Công ty TNHH Tư vấn Giám sát Sao Việt

Ảnh chụp lúc phát hành

Phát hành
28/10/2026 10:06 · Lê Minh Tuấn
Phiên bản
v9 (xét v1–v9)
Bộ tính
Thép, phiên bản 1
Lần sửa
3

Lịch sử sửa từ đầu

5
  1. Gửi duyệt lại

    Bùi Đức Thịnh

  2. v9

    Uốn D25 mẫu 6: (trống) → Không nứt

    Bùi Đức Thịnh

    Lý do: bổ sung nhận xét theo số thí nghiệm

  3. v8

    Sai số KL D22: Không đạt → Đạt (thử lại 4 mẫu)

    Đổi kết luậnBùi Đức Thịnh

    Lý do: thử lại theo TCVN 1651: mẫu 2, 3 rớt thật

  4. v7

    D22 mẫu 1 khối lượng 1.685,0 → 1.785,0 g

    Bùi Đức Thịnh

    Lý do: gõ nhầm số cân, số cân ghi 1.785,0 g

  5. Gửi duyệt lần 1

    Kết luận lúc đó: Không đạt

Bốn sắc

  1. v12

    Đã phát hành phiếu 486

    Lê Minh Tuấn

  2. v11

    Chuyển cho Trưởng phòng

    Bùi Đức Thịnh

  3. v10

    Trả lại: thiếu ảnh chụp mẫu

    Lê Minh Tuấn

    Lý do: chưa có ảnh mẫu D22 sau khi thử lại

  4. v9

    Đổi kết luận D22

    Bùi Đức Thịnh

Hoạt động gần đây

  1. Phiếu TH-D3-15 đổi kết luận Đạt bởi Bùi Đức Thịnh

  2. Phiếu 486 phát hành cho Công ty CP Xây dựng Cầu đường Thành Đạt

    Số phiếu
    486/LAS-XD 1222
  3. Mẫu D22 Không đạt sai số khối lượng, chờ thử lại

  4. Phiếu TH-D3-07 được duyệt bởi Lê Minh Tuấn

Trái đến phải: .dl-lines ở cột chính, .dl-tight ở cột hẹp (nhãn trên giá trị khi không đủ chỗ), lịch sử kiểu nhật ký phiên bản (.timeline: viên số phiên bản .timeline-mark hoặc chấm, tiêu đề, dòng người và giờ, lý do trong hộp), bốn sắc của viên và hộp lý do, rồi dòng hoạt động (.timeline-feed: ngày, hàng bo tròn có icon và câu có nhãn, nút mở ở cuối, dòng chi tiết bên dưới).

Hiển thị đổi trước và sau

Một lần sửa, từng trường một: tên trường, giá trị cũ gạch ngang, mũi tên, giá trị mới đậm; nhiều trường xếp chồng, chữ vẫn đủ nghĩa khi không có màu.
dl.diff (hoặc div.diff) > .diff-row > dt.diff-field (tên trường, mờ) + dd.diff-values gồm del.diff-old (gạch ngang, --muted), svg.icon.diff-arrow[aria-hidden="true"] và ins.diff-new (không gạch chân, --text, đậm 600, nền --sunk). Trình duyệt và trình đọc màn hình đọc del và ins không đồng đều nên chữ phải tự đủ nghĩa: trong mỗi cái có span.visually-hidden “Trước: ” và “Sau: ”, người dùng trình đọc nghe “Trước: 350.000 đ, Sau: 380.000 đ”; nét gạch và mũi tên chỉ là dấu hiệu thứ hai. Giá trị cũ rỗng là del.diff-old.diff-empty viết “(trống)”, giá trị mới rỗng là ins.diff-new.diff-empty viết “(đã xóa)”: mờ, không gạch, không đậm (chữ viết trong markup, theo ngôn ngữ của khách). Số bọc trong span.num để chữ số thẳng hàng với --font-figure. Các trường cách nhau bằng đường kẻ mảnh. Giá trị dài xuống dòng, không bao giờ làm rộng khung. Khi danh sách hẹp hơn 28rem (container query, không script) tên trường lên trên giá trị, cũ trên mới và mũi tên quay xuống. Danh sách là container nên cần bề rộng riêng. Màu: --text và --muted trên --surface và --sunk, đã được kiểm.

Sửa phiếu chi bốn trường, khung rộng 36rem

Phiếu PC-2026-0412, sửa lúc 30/09 09:30

Người chi
Trước: Cây xăng PetrolimexSau: Cây xăng Phú Mỹ
Số tiền
Trước: 350.000 đSau: 380.000 đ
Ngày
Trước: 28/09/2026Sau: 29/09/2026
Ghi chú
Trước: Đổ xăng xe tải 51C-123.45Sau: Đổ xăng xe tải 51C-123.45 và xe cẩu 5 tấn đi lắp kèo thử tại công trình Bình Chánh, đã có hóa đơn đỏ kèm theo phiếu giấy

Chữ, số tiền (span.num), ngày và một ghi chú dài (xuống dòng ở cả cũ và mới). Mỗi trường là một hàng có đường kẻ mảnh.

Cùng bốn trường trong khung hẹp 20rem

Phiếu PC-2026-0412

Người chi
Trước: Cây xăng PetrolimexSau: Cây xăng Phú Mỹ
Số tiền
Trước: 350.000 đSau: 380.000 đ
Ngày
Trước: 28/09/2026Sau: 29/09/2026
Ghi chú
Trước: Đổ xăng xe tải 51C-123.45Sau: Đổ xăng xe tải 51C-123.45 và xe cẩu 5 tấn đi lắp kèo thử tại công trình Bình Chánh, đã có hóa đơn đỏ kèm theo phiếu giấy

Khung hẹp hơn 28rem: tên trường nằm trên giá trị, cũ ở trên mới, mũi tên quay xuống.

Giá trị cũ rỗng và giá trị mới rỗng

Phiếu PC-2026-0398, hủy

Lý do hủy
Trước: (trống)Sau: Ghi trùng với PC-2026-0397
Số hóa đơn
Trước: HĐ-0042Sau: (đã xóa)

“(trống)” và “(đã xóa)” mờ, không gạch và không đậm; người đọc màn hình vẫn nghe “Trước: (trống)” và “Sau: (đã xóa)”.

Cùng nội dung, khung hẹp

Lý do hủy
Trước: (trống)Sau: Ghi trùng với PC-2026-0397
Số hóa đơn
Trước: HĐ-0042Sau: (đã xóa)

Panel có tone, trạng thái và dòng gập

Panel nói được mình đang ở tình trạng nào: thanh 4 px ở mép đầu theo tone, viền nét đứt khi chưa lưu, nền xám khi chỉ đọc; và dòng danh sách gập thành nội dung, đủ nhẹ để dùng hai chục lần trong một danh sách.
.panel[data-tone="ok|warn|bad|info"]: thanh 4 px ở mép đầu (chính là viền của panel, không phần nào bên trong che được; cả ở giao diện app). Thanh không bao giờ đứng một mình: để chữ trong phần đầu, một .tag cùng tone trong .panel-state (“Có vấn đề”, “Đã duyệt”). .panel[data-state="unsaved"]: viền nét đứt --field-border, không bóng, kèm .tag.tag-warn “Chưa lưu” trong .panel-state. .panel[data-state="readonly"]: nền --surface-2, chữ mờ, kèm .tag “Chỉ đọc” có biểu tượng khóa. Cả hai trạng thái không vô hiệu hóa gì: ô nhập bên trong vẫn đọc được, vì thế markup vẫn đặt readonly hoặc disabled trên chúng. Tone và trạng thái đi cùng nhau được (thẻ chưa lưu có vấn đề giữ thanh trên viền đứt). .panel-state là chỗ của chữ đó trong .panel-head, đẩy về cuối, trước .panel-actions. details.row-fold > summary (dòng: tối thiểu --tap; bên trong dùng các phần của .work-row như .work-main, .work-reason, .work-end, .avatar, hoặc span.row-fold-main cho chữ thường; phần cuối là svg.icon.row-fold-caret[aria-hidden] quay khi mở) + .row-fold-body (thụt vào theo --row-fold-indent, mặc định 2,25rem; 2,75rem khi có .avatar-sm; có đường kẻ nhạt phía trên). Các dòng cách nhau bằng đường kẻ 1 px (anh em liền kề, hoặc li trong ul.row-fold-list). data-tone trên details vẽ thanh 4 px như .work-row, chữ nói tình trạng nằm trong summary. Bàn phím là của phần tử gốc (Enter, Space), không script; không có điểm đánh dấu mặc định, summary có vòng lấy nét của kit vẽ bên trong dòng. Màu: các cặp chữ trên --surface và --surface-2 mà kit đã kiểm.

Bốn tone, mỗi panel một chữ trong phần đầu

Phiếu PC-2026-0398

Đã duyệt
Quản trị đã xem và duyệt lúc 09:30.

Đơn DH-2026-0033

Cần xem
Còn thiếu một phiếu chi cho nhà thầu phụ.

Thẻ Trần Văn Bình

Có vấn đề
Một việc báo “Có vấn đề”: thiếu thép tấm.

Ghi chú của kế toán

Thông tin
Đối chiếu sổ phụ ngân hàng vào cuối tháng.

Thanh ở mép đầu là cue thứ hai; cue thứ nhất là chữ có biểu tượng trong .panel-state (tag cùng tone).

Chưa lưu và chỉ đọc

Lê Văn Cường

Chưa lưu
Giữ trên máy này, chưa gửi lên.

Phạm Thị Dung

Có vấn đềChưa lưu
Chưa lưu và có vấn đề: thanh đỏ nằm trên viền nét đứt.

Tháng 9 năm 2026

Chỉ đọc
Kỳ đã khóa từ 01/10, không sửa được.

Hai nhãn trong một phần đầu thì chỉ nhãn đầu có margin-inline-start: auto chia chỗ; ô nhập vẫn đọc được vì readonly nằm ngay trên ô, không do panel.

Danh sách bốn dòng gập (ul.row-fold-list)

  • Trần Văn Bình
    CN-002 · Tổ hàn
    Có vấn đề: thiếu thép tấm 5 ly
    Có vấn đề

    DH-2026-0031 · Hàn giằng kèo trục A–B: báo “Có vấn đề” lúc 14:20.

    Anh Hùng đã nhận, chờ vật tư về.

  • Mai Xuân Vinh
    CN-014 · Tổ hàn
    Đi làm

    Đủ ngày, tăng ca 1,5 giờ. Lưu 16:58 · anh Hùng.

  • Hủy phiếu · PC-2026-0398
    Phạm Thị Lan · 30/09 09:30
    Thu chi
    Trạng thái
    Trước: Hiệu lựcSau: Đã hủy
    Lý do hủy
    Trước: (trống)Sau: Ghi trùng với PC-2026-0397
  • Sửa số tiền · PT-2026-0087
    Nguyễn Thị Hoa · 29/09 16:12
    Thu chi
    Số tiền
    Trước: 45.000.000 đSau: 54.000.000 đ

Dòng đầu có thanh đỏ và nói bằng chữ (“Có vấn đề”); dòng thứ ba mở sẵn (open), mũi tên quay lên, nội dung có đường kẻ nhạt phía trên và là một dl.diff (trước → sau). Tab tới dòng, Enter hoặc Space mở / đóng.

Dòng chữ thường, anh em liền kề (không ul)

Khi nào chốt sổ cuối tháng?

Ngày làm việc cuối cùng của tháng, sau khi kế toán đối chiếu sổ phụ.

Sửa phiếu đã chốt thế nào, và ai được sửa, và sửa đến khi nào thì phải xin quản trị mở lại kỳ?

Chỉ Quản trị mở lại kỳ; mọi thay đổi được ghi vào nhật ký.

Câu hỏi dài xuống dòng, mũi tên giữ nguyên ở cuối, dòng vẫn cao tối thiểu --tap.

Tiêu đề panel: h2, h3 hoặc h4

Tiêu đề h2

Tiêu đề h3

Tiêu đề h4

.panel-head cho cùng một cỡ chữ với h2, h3 và h4, để thứ bậc tiêu đề của trang đúng mà panel không đổi dáng.

Stepper, form nhiều bước và phần có số

Thanh bước ngang hoặc dọc, hiện bằng bước hoặc thanh tiến độ, và khung nhập liệu chia bước có kiểm tra từng bước.
ol.stepper > li.step[data-state="done|current|error|todo"] > .step-btn (là button khi người dùng được quay lại hoặc tới bước đó, là span khi chưa, bước hiện tại có aria-current="step") > .step-mark (số, dấu tích khi xong, "!" khi lỗi) + .step-label. Bốn kiểu bày cùng một danh sách: vòng tròn .stepper-circles (theo MultiStepIndicator của Vibe: vòng tròn nối nhau bằng vạch mảnh, chữ ở bên cạnh và có thể thêm .step-sub trong .step-text; bước hiện tại là vòng đặc có viền ngoài, bước xong là vòng đặc có dấu tích, bước chưa tới là vòng rỗng, bước lỗi giữ màu lỗi; .stepper-compact thu nhỏ vòng và chỉ giữ nhãn; dưới 40rem chỉ bước hiện tại còn chữ), ngang (kiểu mặc định, các ô trên nền xám như .tabs-pill, ô trắng là bước hiện tại; dưới 40rem chỉ bước hiện tại còn nhãn), dọc .stepper-vertical (cột nối bằng vạch, vạch tô màu thành công khi bước xong; mỗi bước có thể có .step-sub mô tả trong .step-text cùng nhãn và .step-meta bên phải cho ngày giờ), thanh tiến độ .stepper-bars (mỗi bước là một đoạn, nhãn nằm dưới, không có số). Bước xong màu thành công, bước lỗi màu lỗi, chữ cùng cỡ chữ thân trang; trạng thái luôn có chữ hoặc dấu hiệu ngoài màu (tích, "!", chữ ẩn "(đã xong)", "(có lỗi)"). Tiến độ chung: .wizard-progress gồm "Bước 2/4", một .bar (--v là phần đã đi) và phần trăm. Biểu mẫu chia bước: trong hộp thoại (.wizard-steps dưới phần đầu, chân có .modal-foot-start cho nút Hủy) hoặc trên trang (.wizard-page: thẻ bước dọc bên trái, thẻ biểu mẫu bên phải có tiến độ ở đầu, Quay lại và Tiếp tục ở chân; dưới 48rem các bước nằm ngang phía trên). Mỗi bước là một .wizard-panel (chỉ bước hiện tại hiện, có h3 để chuyển focus tới). Bấm Tiếp tục kiểm tra các ô required của bước đó: ô thiếu có aria-invalid và thông báo .field-error, bước chuyển sang lỗi và focus vào ô đầu tiên; Quay lại hoặc bấm bước đã qua thì không kiểm tra. Trang xác nhận ở bước cuối liệt kê các ô đánh dấu data-sum. Đóng hộp thoại là mất dữ liệu đã nhập; tenant nên hỏi xác nhận khi đã nhập dở.

Ngang, hiện bước

  1. Thời hạnNgày hiệu lực
  2. Xác nhậnXem lại và gửi
  1. Thời hạn
  2. Xác nhận
  1. Thời hạnNgày hiệu lực
  2. Xác nhậnXem lại và gửi

Kiểu Vibe, .stepper-circles. Trên: nhãn và mô tả. Giữa: gọn, .stepper-compact. Dưới: thanh hẹp, chỉ bước hiện tại giữ chữ. Bước đã xong và bước lỗi bấm được để quay lại.

Ngang, hiện thanh tiến độ

  1. Đang làm
  2. Đang duyệt
  3. Hoàn tất
Bước 2/450%

Dọc

Tiến trình giao hàng

  1. Đang vận chuyểnĐang ở trung tâm khu vực18/11 09:15
  2. Đã giaoHàng sẽ giao tận nơi
  1. Địa chỉ
  2. Xác nhận

Cùng dạng vòng tròn với bản ngang. Trái: có mô tả và ngày giờ. Phải: chỉ nhãn, để làm cột bước bên cạnh biểu mẫu.

Trong hộp thoại (ngang)

Hộp thoại bốn bước: thử bỏ trống ô có dấu *

    Trên trang (dọc, có tiến độ)

      Bước 1/425%

      Hồ sơ

      • Bán lẻ
      • Bán buôn
      • Dịch vụ

      Numbered parts, choice cards

      Biểu mẫu dài chia thành các phần có số, chọn vật liệu bằng thẻ, chip nét đứt, ô có hậu tố cố định và hộp xác nhận.
      Mỗi phần là một .panel có .panel-head.panel-head-part: .part-num (số trong vòng tròn; .part-num-ok với icon dấu tích khi phần đã đủ), tiêu đề, và .panel-meta ở cuối dòng (đã nhập bao nhiêu). Nhóm chọn dạng thẻ là .choice-cards > label.choice-card chứa input[type="radio"] thật, nên bàn phím và trình đọc màn hình dùng như nhóm radio thường. .chip-dashed đổi viền chip sang nét đứt cho giá trị khác loại. .input-affix > .input + .affix giữ chữ cố định trong khung ô. label.confirm là hộp tích xác nhận: màu hổ phách cho đến khi tích, rồi xanh lá.
      1

      Vật liệu

      Chọn một
      Đã đủ

      Kích cỡ

      3 cỡ
      D10D12Ø16 tròn trơn
      3

      Phát hành

      0/2
      /LAS-XD 1222
      Phần sau dấu gạch chéo tự thêm.

      Phần 1 và 2: số trong vòng tròn, phần đã đủ đổi sang dấu tích xanh. Cỡ Ø16 tròn trơn có viền nét đứt vì khác loại với hai cỡ vằn. Hộp xác nhận ở phần 3 tích được: thử bấm.

      Khung điện thoại

      Thanh tab dưới, đầu trang con có nút quay lại, thanh nút ở đáy và toast ở đáy cho ứng dụng chạy trên điện thoại.
      Thanh tab dành cho các trang chính và thanh nút cho trang con, nên hai thanh không chung một màn hình. Chỉ hiện thanh tab ở bố cục điện thoại; ở màn hình rộng các liên kết đó nằm trong sidebar hoặc thanh trên. Thêm .has-tabbar hoặc .has-action-bar vào trang để dòng cuối không bị thanh che. Toast ở đáy (.toast-host-bottom) tự nâng lên trên thanh.

      Trang chính: .tabbar với mục hiện tại aria-current="page", icon -fill và số đếm .side-badge; toast hiện ngay trên thanh.

      Pha trà nền buổi chiều

      Thứ Bảy 31/10 · Ca chiều 14:00

      Còn thiếu 1 ảnh.Chụp thêm ảnh thùng trà đã đậy nắp.

      Pha 12 lít trà nền, để nguội trong thùng có nắp. Ghi giờ pha lên nhãn.

      Gửi đủ 2 ảnh để bấm Xong

      Trang con: .topbar-back, .topbar-title (tên và dòng phụ cắt bằng “…”) và .action-bar với ghi chú vì sao nút đang khóa.

      Số đếm đứng riêng

      .side-badge nằm trong dòng chữ vẫn giữ dạng viên thuốc; thêm chữ ẩn để đọc được ý nghĩa của số.

      Đánh giá 9 chưa duyệt · Chấm công 1 chờ duyệt

      Đầu trang điện thoại cấp một

      Đầu trang của màn chính (trang chủ, sổ quỹ, kho): dấu hiệu hoặc biểu tượng, tiêu đề kèm dòng ngày, một hai nút biểu tượng, không có nút quay lại.
      .topbar có phần tử đầu là .brand-mark hoặc svg.icon[aria-hidden] được bố cục như đầu trang con: một dòng, cùng chiều cao, .topbar-title-text (là h1 của trang, để mọi trang điện thoại đều có tiêu đề) và .topbar-title-sub bị cắt bằng “…”, .topbar-actions ở cuối với một hai nút .btn.btn-ghost.btn-icon có aria-label (cao và rộng ít nhất --tap). Chỉ dấu hiệu hoặc biểu tượng là phần tử đầu tiên mới tính, nên thanh trên của màn hình rộng (.brand và .nav), thanh mỏng của sidebar và đầu trang con giữ nguyên.

      Cơ khí Trần Minh Chiến

      Thứ Tư, 30/09/2026

      Đầu trang cấp 1: .brand-mark thay nút quay lại, tiêu đề là h1.topbar-title-text, dòng ngày, một nút làm mới.

      Biểu tượng svg.icon làm phần đầu, hai nút ở cuối: tìm kiếm và menu ⋯.

      Công ty TNHH Cơ khí và Xây dựng Trần Minh Chiến

      Thứ Tư, 30/09/2026 · Nha Trang, Khánh Hòa

      Tiêu đề và dòng phụ dài bị cắt bằng “…”, hai nút ở cuối không bị đẩy ra ngoài.

      Thanh nút đáy có ô tổng, bản dính trên máy tính

      Thanh nút ở đáy điện thoại có chỗ cho con số tổng (“Chi từ Tiền mặt, − 350.000 đ”), và trên màn hình rộng cùng thanh đó thành thẻ dính ở cuối cột biểu mẫu.
      .action-bar-sum là khối đứng trước các nút (sau ghi chú nếu có): .action-bar-sum-label một dòng (bị cắt bằng “…” khi không vừa) và .action-bar-sum-figure mang lớp .num-in, .num-out hoặc .num-neg với dấu trong chữ. Con số không bao giờ xuống dòng và ô tổng không hẹp hơn nó; nhãn nhường chỗ, các nút chia phần còn lại. Tổng + một nút: một dòng ở 390 px (số 12 chữ số vẫn đủ chỗ). Tổng + hai nút: hai dòng, ô tổng chiếm cả dòng trên, hai nút chia dòng dưới (--action-bar-extra giữ .has-action-bar và toast ở trên thanh). Nút ⋯ (.btn-icon) không tính là nút thứ hai. Màn có ba việc: nút chính cộng nút ⋯ mở .menu, không thêm nút thứ ba. .action-bar-sticky: từ 64rem, cùng markup nhưng không dính mép cửa sổ mà là thẻ dính (position: sticky; bottom: var(--sp-4)) ở cuối cột biểu mẫu; đặt nó cuối cột, cột là khối thường, không phải lưới. Dưới 64rem vẫn là thanh cố định. Khóa nút bằng aria-disabled, .action-bar-note và .has-action-bar giữ nguyên.

      Phiếu chi mới

      Mã phiếu được cấp khi lưu

      Chi từ Tiền mặt

      − 350.000đ

      Tổng và một nút: một dòng, tổng bên trái, nút chính lấy phần còn lại.

      Phiếu thu mới

      Mã phiếu được cấp khi lưu

      Phiếu giữ trên máy, gửi lại khi có sóng

      Thu vào VCB ·· 6789

      + 45.000.000đ

      Tổng và hai nút: hai dòng. Ô tổng chiếm dòng trên, hai nút chia dòng dưới; ghi chú nằm trên cùng.

      Phiếu chi mới

      Mã phiếu được cấp khi lưu

      Nhập số tiền để bấm Lưu

      Chi từ Tiền mặt

      0đ

      Nút chính khóa bằng aria-disabled="true", ghi chú nói vì sao; tổng vẫn hiện.

      Phiếu chi mới

      Mã phiếu được cấp khi lưu

      Chi từ Vietcombank

      − 123.456.789.012đ

      Số 12 chữ số vẫn một dòng, nút chính còn đủ rộng.

      Phiếu chi mới

      Mã phiếu được cấp khi lưu

      Chi từ Tiền mặt

      − 350.000đ

      Màn có ba việc: nút chính và nút ⋯ mở .menu, không thêm nút thứ ba, vì thanh sẽ xuống dòng.

      Bản dính trên máy tính

      Phiếu chi

      Quỹ tiền mặt
      Chi từ Tiền mặt

      − 350.000đ

      Sau phiếu này

      Quỹ tiền mặt
      18.450.000 đ
      Còn lại
      18.100.000 đ

      Từ 64rem, .action-bar.action-bar-sticky là thẻ ở cuối cột biểu mẫu, dính cách đáy cửa sổ 16 px khi cột còn dài; cùng nội dung với bản điện thoại. Thẻ đủ rộng nên tổng và hai nút nằm cùng một dòng. Dưới 64rem khung này hiện thanh cố định ở đáy khung. Trong ảnh chụp cả mục, thẻ đang dính giữa cột; cuộn trang để thấy nó trượt xuống cuối cột.

      Nút nổi, bottom sheet, danh sách trong sheet

      Nút tròn cho việc chính của trang có thanh tab, và tấm trượt từ đáy để chọn một thao tác hoặc đi tới một mục.
      .fab là nút tròn primary cao --tap + 12px, bóng --sh-3, cố định ở mép cuối phía trên thanh tab (--fab-bottom đổi chỗ khi không có thanh tab); chỉ có icon thì cần aria-label. .fab-ext thêm chữ cạnh icon, thành hình viên thuốc, chữ là tên nên không cần aria-label; a.fab cho nút chuyển trang. Trang thêm .has-fab cạnh .has-tabbar để dòng cuối không bị che; toast ở đáy (.toast-host-bottom) tự nâng lên trên nút. Không dùng cùng trang có .action-bar. <dialog class="modal modal-sheet"> mở bằng showModal(): trượt từ đáy ở mọi bề ngang, giữa màn hình và rộng tối đa 32rem, dưới 40rem thì rộng hết; vạch ở đầu chỉ là dấu hiệu, đóng bằng nút đóng, bấm nền hoặc Esc. Bên trong: .sheet-list > li > .sheet-item (button hoặc a, cao tối thiểu 48px) với .sheet-ic (đĩa icon) và nhãn; a.sheet-item có thêm svg.icon mũi tên làm con cuối, tự đẩy về cuối dòng; .sheet-item-danger cho thao tác xóa, nhãn đã nói rõ việc xóa nên màu đỏ chỉ là tín hiệu thứ hai. Kiểu app tinh chỉnh bóng, bo góc và cỡ đĩa icon.

      Nút nổi

      Lập phiếu

      .fab chỉ có icon (có aria-label), .fab.fab-ext có chữ, a.fab.fab-ext là liên kết. Ở đây đặt position: static để xem riêng; trên trang thật nó cố định.

      Trong khung điện thoại

      .fab trên .tabbar, ở mép cuối; toast ở đáy tự nâng lên trên nút.

      a.sheet-item: icon, nhãn và mũi tên. Nhãn dài xuống dòng, mũi tên giữ ở cuối.

      Sổ ca

      Thứ Bảy 31/10

      button.sheet-item.sheet-item-danger: nhãn nói rõ việc xóa, màu đỏ chỉ là tín hiệu thứ hai.

      Mở bản thật

      Thu hẹp cửa sổ dưới 40rem để thấy sheet rộng hết bề ngang.

      Ô thao tác lớn

      Vài ô to chạm bằng ngón cái: icon trong đĩa tròn đặt trên một nhãn ngắn, dùng trong sheet “Lập mới” và ở hàng nút nhanh của trang chủ.
      .action-tiles > a.action-tile hoặc button.action-tile > span.action-tile-ic[aria-hidden] (svg.icon) và nhãn là chữ nên không cần aria-label. .action-tile-primary tô màu chủ đạo cho một ô (việc thường làm nhất). Lưới tự đếm bề ngang của chính nó (container query) nên dùng được trong sheet lẫn trên trang: từ 18rem là 3 ô một hàng, dưới đó là 2; đúng 2 hoặc 4 ô thì chia đôi hàng, một ô lẻ thì chiếm cả hàng. Ô cao tối thiểu 5.5rem, nhãn xuống dòng và không bao giờ làm ô rộng ra. Nên dừng ở sáu ô.

      Lưới 3 × 2 trong sheet “Lập mới”

      Trang chủ

      Thứ Bảy 31/10

      Quỹ hôm nay

      Tiền mặt 12.450.000 đ

      Sáu ô trong .modal-sheet: ba ô một hàng, Phiếu chi là ô chính.

      Hàng ba ô trên trang, một ô chính

      Trang chủ: Phiếu chi (.action-tile-primary), Phiếu thu, Chuyển quỹ.

      Hàng hai ô

      Đúng hai ô thì chia đôi hàng.

      Nhãn dài

      Nhãn dài xuống dòng trong ô, ô không rộng ra và các ô cùng hàng cao bằng nhau.

      Hẹp hơn 18rem: hai ô một hàng

      Khung rộng 16rem: lưới đếm bề ngang của nó chứ không đếm cửa sổ.

      Bản button

      button.action-tile trông giống hệt bản liên kết; dùng khi ô làm một việc tại chỗ thay vì đi tới trang khác.

      Sidebar

      Thanh bên thu gọn về icon và trượt ra trên điện thoại.
      .app-shell > .sidebar + .app-main. Mục là .side-item (aria-current="page", .side-label, .side-badge), nhóm có .side-heading, nhóm gập được (kiểu cây của Vibe) là details.side-group > summary.side-item (icon, .side-label, .side-group-caret) + .side-sub chứa các .side-item chỉ có chữ, chân thanh bên là .sidebar-foot. data-collapsed="true" thu về icon. Dưới 48rem thanh bên trượt ra trên trang (data-open="true" và .sidebar-scrim). Khung dưới đây thu nhỏ trình duyệt để xem cả hai.

      Tổng quan

      Thanh bên
      Nút thứ hai thu gọn menu về icon; ở màn hẹp, nút đầu mở menu.

      Số liệu có màu trên thanh bên

      Màu theo trường hợp: xanh đã xong, vàng chờ xử lý, đỏ quá hạn, xanh dương mới.
      .side-badge (xám) và -ok -warn -bad -info -primary, cùng cặp màu với .tag. Màu chỉ nói thêm mức độ; con số vẫn là nội dung chính, và khi màu có ý nghĩa thì ghi thêm chữ trong .visually-hidden (ví dụ " quá hạn"). Tenant giới hạn con số ("99+"). Khi thanh bên thu về icon, huy hiệu bị ẩn như trước.

      Customer logo

      Logo khách gửi qua đặt trong thanh trên cùng, thanh bên và trang đăng nhập.
      Trong .brand, hoặc một img.brand-logo đứng một mình (file đã có tên khách trong đó, logo dài; alt là tên khách), hoặc một img[alt=""] trong .brand-mark (file vuông hoặc chỉ có biểu tượng) kèm tên bằng chữ bên cạnh. Chiều cao cố định (--logo-h, 2.25rem), chiều rộng theo file đến giới hạn --logo-w (11rem), không cắt và không kéo giãn. .brand-logo-lg cho trang đăng nhập. Logo vẽ cho nền sáng mà đặt trên thanh tối thì thêm .brand-logo-plate (nền đệm màu bề mặt). Thanh bên thu gọn: logo dài được nhìn qua khung vuông ở đầu trái, nơi thường có biểu tượng; file chỉ có chữ thì dùng kiểu logo vuông cạnh tên. Chưa có file hoặc file lỗi thì tenant hiện .brand-mark chữ viết tắt. Ảnh mẫu trong samples/ là thương hiệu giả "Nam Việt" của bộ kit.

      Logo dài thay cho chữ

      Logo vuông cạnh tên

      Chưa có logo

      Hai dòng, số đếm và tài khoản

      Thương hiệu hai dòng (.brand-text > .brand-name + .brand-sub, dòng dưới cắt bằng dấu … khi dài), số đếm là .side-badge trong .nav-item, tài khoản là .btn chứa .avatar-label (mở một .menu). Nút chính .btn-primary giữ màu nền trên thanh trên.

      Thanh trên thu gọn

      Thêm .topbar-dense vào thanh trên khi vai có nhiều mục (từ sáu mục trở lên): dòng dưới của thương hiệu mất, tài khoản chỉ còn avatar (chữ vẫn cho trình đọc màn hình). Không cần thêm gì ở cửa sổ dưới 89rem (1424px): thanh tự thu gọn như vậy. Hẹp hơn nữa thì thanh xuống hai hàng.

      Trên nền tối

      Trái: logo chữ đen mất hút trên nền tối. Phải: thêm .brand-logo-plate.

      Thanh bên

      Mở rộng, thu gọn với logo dài (chỉ thấy đầu trái), thu gọn với logo vuông. Thu gọn chỉ thấy trên màn rộng; trên điện thoại thanh bên luôn mở đủ.

      Trang đăng nhập

      Đăng nhập để tiếp tục

      Sign-in

      Thẻ hẹp ở giữa trang với thương hiệu, tiêu đề, biểu mẫu và dòng trợ giúp.
      main.login căn giữa cả hai chiều một .panel.login-card (rộng --login-w, mặc định 25rem) và một dòng .caption nhỏ bên dưới; --login-h là chiều cao trang (mặc định cả cửa sổ). Trong thẻ: .panel-body chứa .login-brand (một .brand, logo khách hoặc chữ), một div có h1 và .page-lead, rồi biểu mẫu .stack kết thúc bằng .btn.btn-primary.btn-lg.btn-block; .panel-foot chứa dòng trợ giúp. Màn chia đôi (một khung tối bên trái, biểu mẫu bên phải) là main.login-split > .login-aside + .login: khung trái chứa .brand, một div có h1 và p, hình vẽ svg.login-art (tô bằng màu của tenant, hoặc currentColor) và .login-facts (các cặp strong + chữ); thẻ bên phải phẳng, không viền. Ô mật khẩu có nút Hiện/Ẩn nằm trong ô là .input-action > .input + .btn.btn-ghost.btn-sm[data-reveal] (ô chừa --action-w, 5rem; tenant đổi type, chữ, aria-label và icon eye / eye-slash như script của trang này). Khung tối đổi bằng --aside-bg, --aside-fg, --aside-muted. Dưới 60rem khung trái thành dải phía trên biểu mẫu, bỏ hình vẽ và các cặp thông tin. Hình vẽ là của tenant: kit chỉ cho chỗ đặt.

      Chia đôi

      .login-split làm cả trang (cao --login-h, ở đây 32rem); trên trang thật nó nằm trong main và cao cả cửa sổ. Thu hẹp cửa sổ dưới 60rem để xem khung trái thành dải trên.

      Thẻ thường có hình

      svg.login-art là phần tử đầu của .panel-body, đứng trên .login-brand: nằm trong luồng, rộng --login-art-w (4rem), cùng mép với thương hiệu và mang màu chính (svg dùng currentColor). Hình chỉ để trang trí nên có aria-hidden="true".

      Giấy in A4 và A5

      Tờ A4 như khi in, cho phiếu khách cũng điền trên giấy.
      .paper-stage (bàn xám đặt các tờ giấy, cuộn ngang khi màn hình hẹp) > .paper (.paper-landscape xoay ngang) > nội dung, cùng .paper-no là số trang ở chân tờ. Tờ luôn trắng chữ đen, không theo palette, vì bản in cũng vậy. Tenant đặt font tài liệu và lề cần dùng trên .paper. Khi in, bàn và bóng đổ bị bỏ, mỗi tờ ngắt trang. Tiêu đề giấy theo khách: header.paper-head > .paper-org (img.paper-logo + .paper-org-text có .paper-org-name và các dòng .paper-org-line: địa chỉ, MST, điện thoại) + p.paper-doc bên phải (mẫu số, số liên); logo cao cố định 14 mm (A5: 11 mm), rộng theo file tối đa 50 mm, không cắt không méo, in đúng màu của logo. Logo ngang có chữ hay logo vuông cạnh tên đều được; tên pháp lý luôn viết ra bằng chữ nên logo để alt="". Không có logo thì chỉ còn phần chữ. Ví dụ dưới cắt ngắn tờ (min-height: 0) cho vừa trang gallery.

      Công ty CP Kiểm định Nam Việt

      Phòng thí nghiệm LAS-XD 1222 · 45 Lê Hồng Phong, Nha Trang

      MST 4201234567 · ĐT 0258 3 812 345

      Mẫu BM-07
      Lần ban hành 03

      Phiếu kết quả thí nghiệm

      Số 486/LAS-XD 1222 · Ngày phát hành 30/10/2026

      Công trình: Đường D3 nối dài, hạng mục cống hộp K0+350.
      Mẫu: TH-D3-15, thép vằn D10–D16, lấy ngày 24/10/2026.

      Chỉ tiêuYêu cầuKết quảKết luận
      Giới hạn chảy (MPa)≥ 400452Đạt
      Giới hạn bền (MPa)≥ 570641Đạt

      Người thí nghiệmTrưởng phòng

      1/1

      A5 paper, signature row, no-print

      Tờ A5 cho phiếu cửa hàng in và giữ lại, hàng chữ ký và lớp ẩn khi in.
      .paper.paper-a5 (148×210 mm) và .paper.paper-a5-land (210×148 mm) đặt trong .paper-stage như tờ A4; lề bằng 0,7 lần lề A4, chữ 10pt. Kit chưa có @page cho A4, nên A5 tự mang trang riêng: @page a5 và @page a5-land (có tên, lề 0, lề thật là padding của tờ), chọn bằng thuộc tính page trên tờ. Trang có tên chỉ áp cho tờ gọi nó, nên tờ A4 trong cùng tài liệu vẫn theo trang mặc định của tài liệu (trang nên tự đặt @page { size: A4; margin: 0 } nếu máy in không mặc định A4). Hàng chữ ký: .paper-sign (ba cột đều; .paper-sign-2 cho hai cột) > .paper-sign-slot > .paper-sign-role (chức danh, đậm) + .paper-sign-hint (nghiêng, “(Ký, ghi rõ họ tên)”), chừa 18 mm để ký (--sign-space), cả hàng không bị cắt qua hai trang. Số tiền trên giấy dùng .num; .num-in, .num-out, .num-neg, .num-void trong tờ luôn đen (giấy in đơn sắc, dấu và chữ mang nghĩa; .num-void vẫn gạch ngang). .no-print ẩn phần tử khi in và chỉ khi in: dùng cho nút In, Quay lại quanh tờ. Các tờ dưới cắt ngắn (min-height: 0) cho vừa trang gallery.
      Nút này mang .no-print: hiện trên màn hình, mất khi in.

      Cửa hàng Vật liệu Minh Chiến

      12 Nguyễn Trãi, Nha Trang · ĐT 0258 3 888 888

      Mẫu số 02-TT
      Liên 1: lưu

      PHIẾU CHI

      Số PC-2026-0412 · Ngày 02/11/2026

      Người nhận tiền: Nguyễn Văn An
      Lý do chi: Mua vật tư sửa kho 2

      Nội dungSố tiền (đ)
      Xi măng PCB40, 20 bao3.100.000
      Phí vận chuyển250.000

      Cộng3.350.000đ

      Bằng chữ: Ba triệu ba trăm năm mươi nghìn đồng.

      Người lập phiếu(Ký, ghi rõ họ tên)
      Thủ quỹ(Ký, ghi rõ họ tên)
      Người nhận tiền(Ký, ghi rõ họ tên)

      A5 đứng, ba chữ ký (mặc định). Số tiền in đen: “Cộng” dùng .num-out nhưng trong tờ không có màu.

      Cửa hàng Vật liệu Minh Chiến

      PHIẾU THU

      Đã nhận của Công ty Thành Đạt: + 1.250.000đ (Một triệu hai trăm năm mươi nghìn đồng).

      Người nộp tiền(Ký, ghi rõ họ tên)
      Người thu tiền(Ký, ghi rõ họ tên)

      .paper-a5-land với .paper-sign-2: hai chữ ký. .num-in trong tờ cũng đen; dấu “+” trong chữ nói nghĩa.

      Bảng dài nhiều trang, khổ A3, in đen trắng

      Bảng chảy qua nhiều trang: đầu bảng lặp lại mỗi trang, khối của một người không bị cắt, hàng chữ ký đi cùng phần cuối; ô chấm công in đen trắng.
      .paper-flow thêm vào tờ (A4, A4 ngang, A3 .paper-a3, A3 ngang .paper-a3-land): trên màn hình là một tờ dài lề 10 mm; khi in, lề chuyển sang trang (trang tên a4-flow, a4-land-flow, a3-flow, a3-land-flow), thead lặp mỗi trang, mỗi tbody (một khối) và mỗi dòng không bị cắt, tfoot in một lần ở cuối, .paper-sign không đứng một mình đầu trang. Bảng .tbl-marks trong tờ: kẻ đen mảnh, không tô màu; P và K đậm, thiếu giờ gạch chân, chưa chấm gạch dày, chủ nhật cột xám có chữ “CN”, chưa vào làm gạch thưa, tự tính chữ nghiêng. Chữ 7pt trên A3, 5,6pt trên A4 ngang; cột ngày đặt bằng <col> (8,1 mm A3, 5,3 mm A4).
      Khổ A4 ngang ở đây; tờ A3 ngang dùng .paper-a3-land.paper-flow.
      Xưởng Minh Sơn
      Khu công nghiệp Nga Sơn

      BẢNG CHẤM CÔNG THÁNG 10/2026

      31 ngày, 4 chủ nhật · 5 người · in lúc 16:20 ngày 23/10/2026

      Tạm tính
      STTMã thẻHọ tênHạng mục1T52T63T74CN5T26T37T48T59T610T711CN12T213T314T415T516T617T718CN19T220T321T422T523T624T725CN26T227T328T429T530T631T7TổngĐơn giáThành tiềnThực lĩnh
      1CN-014Nguyễn Văn HùngChính thứcGiờ làm88,5488887,5P488Chưa chấm848888820445.0009.180.0009.640.000 đ
      Tăng ca220,526,560.000390.000
      Suất ăn111325.00075.000
      2CN-022Trần Thị LanKhoánGiờ làm4886,5884888Chưa chấm8485K888148,541.0006.088.5006.120.500 đ
      Tăng ca0,50,5160.00060.000
      Suất ăn1125.00025.000
      3CN-031Lê Quang MinhChính thứcGiờ làm8848888848,5788848888819645.0008.820.0008.915.000 đ
      Tăng ca11260.000120.000
      Suất ăn025.0000
      4CN-040Phạm Văn TàiKhoánGiờ làm8848888848888848888820036.0007.200.0007.402.000 đ
      Tăng ca1,51,51,51,51726.000182.000
      Suất ăn2210.00020.000
      5CN-047Đỗ Thị MaiKhoánGiờ làm884888884888884P80,5K8176,520.0003.530.000Âm 1.250.000 đ
      Tăng ca026.0000
      Suất ăn025.0000
      Sản lượng8508508501.2008508508508501.2008508508501.2008508508501.20085085085016.950——
      Tổng giờ92530.827.500 đ
      Tổng tăng ca16,5

      P nghỉ phép (đậm) · K nghỉ không phép (đậm) · số gạch chân: thiếu giờ · ô gạch dày: chưa chấm · ô gạch thưa: chưa vào làm hoặc đã nghỉ · cột xám: chủ nhật (CN) · chữ nghiêng: tự tính

      Người lập bảng(Ký, ghi rõ họ tên)
      Kế toán(Ký, ghi rõ họ tên)
      Giám đốc(Ký, đóng dấu)

      Trên màn hình: một tờ A4 ngang. Khi in với 40 người, bảng chảy qua nhiều trang, mỗi trang có đầu bảng, không khối nào bị cắt đôi, chữ ký ở cùng trang với các dòng cuối.

      Dấu tạm tính, đã chốt

      Con dấu nói trạng thái của cả một bảng hay một phiếu, trên màn hình và khi in.
      span.stamp chữ hoa, viền đôi cùng màu chữ: -warn (tạm tính), -ok (đã chốt), -bad (đã hủy), không thêm gì là màu chữ thường; -lg to và nghiêng 2 độ cho đầu tờ. Trong .paper và khi in là đen trên trắng.
      Tạm tínhĐã chốtĐã hủyBản nhápTạm tínhĐã chốt

      Chữ trên dấu là trạng thái; màu chỉ giúp nhận ra nhanh. Bảng công tạm tính khi bảng lương tháng chưa chốt.

      Quy ước sắp xếp và trạng thái

      Năm lớp, bốn trạng thái, và chỗ đặt một thành phần mới để không phải sắp xếp lại lần nữa.
      Mỗi phần của gallery là một <section class="g-section"> mang bốn thuộc tính: data-layer (lớp), data-group (nhóm trong lớp), data-area (both, desktop, mobile: màn hình dùng được) và, nếu không phải ổn định, data-status. Thanh bên và mục lục tự dựng từ các thuộc tính đó; node check-gallery.mjs kiểm tra.

      Năm lớp

      Nền tảng
      Luật nhìn và cách chạm: token, biểu tượng, cỡ chạm. Không phải một thành phần.
      Thành phần
      Một phần tử dùng lại được, đứng một mình: nút, ô nhập, bảng, thông báo. Mọi biến thể (kiểu, cỡ, trạng thái) nằm trong thành phần đó, không thành mục riêng.
      Mẫu thiết kế
      Nhiều thành phần ghép lại để giải một bài toán: biểu mẫu nhiều bước, chi tiết bản ghi, bảng thành thẻ, màn hình điện thoại.
      Bố cục trang
      Cả một trang hoặc khung: khung ứng dụng, đăng nhập, giấy in.
      Hướng dẫn
      Quy ước, trạng thái, nhật ký thay đổi (phần này).

      Đặt một thứ mới ở đâu

      1. Là biến thể hoặc một phần của thứ đã có: thêm vào phần đó bằng <h3 class="g-sub g-sub-part" id="g-...">, thêm một dòng trong COMPONENTS.md.
      2. Là một phần tử đứng một mình: phần mới trong lớp components, chọn một nhóm có sẵn (actions, inputs, selection, display, tables, feedback, overlays, navigation).
      3. Là nhiều thành phần giải một việc: lớp patterns. Là cả trang: lớp templates. Là luật nhìn hoặc cách chạm: lớp foundations.
      4. Đặt phần trong trang theo thứ tự lớp rồi nhóm (check-gallery.mjs báo nếu sai), phần cơ bản trước phần chuyên biệt.

      Bốn trạng thái

      Ổn địnhmặc định, không gắn nhãn: đã duyệt, dùng lại được.
      Mớidata-status="new": vừa thêm, dùng được.
      Thử nghiệmdata-status="experimental": đang thử, chưa chốt (kiểu app chưa chạy trên máy thật).
      Không dùng nữadata-status="deprecated" kèm data-replaced-by: đừng dùng cho trang mới.

      Nhật ký thay đổi

      Những gì đổi trong kit từ bản v1.3 gốc, mới nhất ở trên; chi tiết nằm trong git.
      Mỗi dòng nói thứ đã đổi và chỗ xem trong gallery. Thay đổi nào làm đổi cách vẽ của thứ đã có thì ghi rõ.

      Chấm công (nhánh ui-kit-attendance)

      • Bảng: .tbl-sticky-cols (tới sáu cột trái và một cột phải dính, kit.js đo bề rộng), tfoot.tbl-foot-rows (nhiều dòng tổng dính), tbody.tbl-block (khối theo người), tr.grp ở mọi bảng, td[data-range], .tbl-formula, toàn màn hình ([data-fullscreen]).
      • Ô chấm công: .tbl-marks (ô giờ số 2,25rem), -warn, -missing, .cell-auto, góc -start và -bottom, data-save với .cell-pop, cột thứ Bảy, hôm nay, chưa tới, .cell-mark-2l, data-grade và .mark-strip; chú giải cho các trạng thái mới.
      • Mẫu: chấm công theo ngày (.entry-row), bảng công trên điện thoại. Giấy: .paper-flow, A3, ô chấm công in đen trắng, .stamp.

      v1.3 (nhánh tranminhchien-kit-test)

      • Gallery: sắp theo năm lớp (Nền tảng, Thành phần, Mẫu thiết kế, Bố cục trang, Hướng dẫn); Desktop, Mobile và Tất cả ở thanh trên; chip và tag thành một phần riêng; trạng thái từng phần (data-status).
      • Cỡ chạm (g-tap): <html data-tap="all"> cho mục tiêu 44px dưới chuột; nút “Bỏ nháp” của banner nháp vào danh sách cỡ chạm; ô khoảng ngày là một mục tiêu cả khung.
      • Chiều cao thanh trên: --topbar-real-h (61, 65 hoặc 69px) cho thứ dính dưới thanh; .page-side dính thấp hơn 5px so với trước (thay đổi cách vẽ có chủ ý).
      • Kỳ: TvKit.periodNav.set(el, value, { unit }) và data-today trên .period-nav.
      • Nhỏ: .kpi-value đọc --font-figure; .panel-head cỡ h2, h3, h4; thẻ dòng không đổi viền vì lỗi ẩn; ô .stat-strip-item[data-tone]; thẻ chọn gọn rộng tối thiểu 6,5rem (ba cột ở 390px: thay đổi cách vẽ của phần chọn quỹ); --cards-cols cho bảng thành thẻ.