Component
Stable Data Display

Table

Data tables display structured information in rows and columns. Syhrezz tables are always wrapped in a card container with a toolbar, support row hover, selection, sorting, and pagination.

Anatomy

Every table follows a fixed structure: table-wrap (card container) → toolbar (title + actions) → table elementpagination. This structure is non-negotiable.

LayerClassContents
Container.table-wrapbg:#fff, radius:14px, border, shadow-xs, overflow:hidden
Toolbar.table-toolbarTitle (14px fw-700) + action buttons. Border-bottom.
Head rowthead trbg:#F8FAFC, 10px UPPERCASE fw-700 #64748B headers
Body rowstbody trBorder-bottom:#F1F5F9. Last row: no border. Hover: bg:#FAFBFF
User cell.user-cell30px avatar + stacked name/subtext
Mono values.mono-valJetBrains Mono 11px for dates, IDs, numbers
Actions colAlways last column. Ghost buttons for row actions.
Pagination.table-paginationCount text left, page buttons right. Border-top.

Full Example

Table with toolbar, selection, badges, and pagination
User Directory 124 users
User Role Region Status Last Login Actions
BS
Budi Santoso
budi@Syhrezz.id
Supervisor Jakarta Active 2026-07-24 09:14
AW
Ani Wijaya
ani@Syhrezz.id
Director Surabaya Active 2026-07-24 07:32
RP
Rina Putri
rina@Syhrezz.id
Sales Bandung Pending
Showing 1–3 of 124 users

AI Implementation Notes

For AI coding assistants: Tables are ALWAYS inside a .table-wrap container — never a bare <table>. User cells combine a 30px circle avatar + stacked name/email. Dates and IDs always use .mono-val (JetBrains Mono). Action buttons in the last column use .btn-ghost-sm — never full-sized buttons. Selected rows add .selected class (bg:#EEF2FF). Row hover is CSS-only via tbody tr:hover.

When to Use

Use Table when
  • Displaying structured data with multiple attributes per row
  • Users need to scan, sort, filter, or select records
  • CRUD operations on a collection of items
  • Comparing values across multiple items
Don't use Table when
  • Displaying 1-2 attributes — use a list instead
  • Non-tabular data like settings or profiles
  • Only 1-3 items — use cards instead

Properties

Class/ElementTypeRequiredDescription
.table-wrapcontainerrequiredOuter wrapper — bg:#fff, radius:14px, border, shadow-xs, overflow:hidden. NEVER use bare table.
.table-toolbarheaderrequiredTitle + actions row. border-bottom. flex space-between.
.user-cellcell patternoptional30px avatar + stacked name (fw-600) + sub-text (10px muted). Use for entity columns.
.table-paginationfooterrequiredCount left + page buttons right. border-top.
.mono-valcell styleoptionalJetBrains Mono 11px for dates, IDs, amounts. Never use for names or descriptions.

Behavior

TriggerBehavior
Row hoverbackground:#FAFBFF. Transition:0.15s. Cursor:pointer if row is clickable.
Checkbox selectRow gets background:#EEF2FF (.selected class). Header checkbox shows indeterminate when partial rows selected.
Sort click (th)Toggle .sort-asc / .sort-desc class on th. ::after adds arrow character. Re-fetch or re-sort data.
Empty stateWhen data.length===0, replace tbody with empty state div. Different messages for "no data" vs "no filter results".

Best Practices

Do
Always wrap in .table-wrap with toolbar + pagination
Provides consistent chrome, sort, filter, and action affordances.
Don't
Full-size .btn buttons in table action columns
Use .btn-ghost-sm instead — full buttons clutter rows and break density.

Design Decisions

Why border-radius:14px on .table-wrap instead of matching the card (20px)?
Tables are data-dense and rectangular in nature. A slightly smaller radius (14px) reduces the visual softness, giving tables a more structured, data-focused feel compared to content cards (20px). It signals "this is a structured data view" not "this is a content block."

Changelog

v1.0.0July 2026Added
  • Initial release — toolbar, sortable headers, user cell pattern, pagination
  • Row selection with checkbox + .selected state
  • Mono-val pattern for dates, IDs, amounts
  • Empty state integration

States

Default
White rows, subtle dividers
Row Hover
bg:#FAFBFF, cursor:pointer
Row Selected
bg:#EEF2FF, .selected class

Accessibility

RequirementImplementation
scope="col"Add to all <th> elements for column headers.
role="grid"Add to <table> for interactive tables with selection.
aria-sortAdd to sorted th: aria-sort="ascending" or "descending".
Checkbox labelSelect-all and row checkboxes need aria-label="Select all" / "Select row".

Composition

Allowed
Inside .table-wrap with toolbar
Filter bar above table
Badges in status columns
User-cell pattern for entity columns
Forbidden
Full-size buttons in action columns (use ghost-sm)
Bare table without .table-wrap
More than 8 columns without horizontal scroll

Best Practices

Do
Keep action columns minimal — 2 ghost-sm buttons max per row.
Dense action columns overwhelm the data.
Don't
Full-size .btn buttons in every table row cell.
Buttons overpower the data and waste row height.

Content Rules

Last Login
Noun phrase. Concise. 10px UPPERCASE.
The Date When The User Last Logged In
Column headers must be short — max 2-3 words.
No users found · Try adjusting your filters.
Specific entity + actionable guidance.
No data
Vague, provides no path forward.