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 element → pagination. This structure is non-negotiable.
| Layer | Class | Contents |
|---|---|---|
| Container | .table-wrap | bg:#fff, radius:14px, border, shadow-xs, overflow:hidden |
| Toolbar | .table-toolbar | Title (14px fw-700) + action buttons. Border-bottom. |
| Head row | thead tr | bg:#F8FAFC, 10px UPPERCASE fw-700 #64748B headers |
| Body rows | tbody tr | Border-bottom:#F1F5F9. Last row: no border. Hover: bg:#FAFBFF |
| User cell | .user-cell | 30px avatar + stacked name/subtext |
| Mono values | .mono-val | JetBrains Mono 11px for dates, IDs, numbers |
| Actions col | — | Always last column. Ghost buttons for row actions. |
| Pagination | .table-pagination | Count text left, page buttons right. Border-top. |
Full Example
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/Element | Type | Required | Description |
|---|---|---|---|
| .table-wrap | container | required | Outer wrapper — bg:#fff, radius:14px, border, shadow-xs, overflow:hidden. NEVER use bare table. |
| .table-toolbar | header | required | Title + actions row. border-bottom. flex space-between. |
| .user-cell | cell pattern | optional | 30px avatar + stacked name (fw-600) + sub-text (10px muted). Use for entity columns. |
| .table-pagination | footer | required | Count left + page buttons right. border-top. |
| .mono-val | cell style | optional | JetBrains Mono 11px for dates, IDs, amounts. Never use for names or descriptions. |
Behavior
| Trigger | Behavior |
|---|---|
| Row hover | background:#FAFBFF. Transition:0.15s. Cursor:pointer if row is clickable. |
| Checkbox select | Row 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 state | When 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
| Requirement | Implementation |
|---|---|
| scope="col" | Add to all <th> elements for column headers. |
| role="grid" | Add to <table> for interactive tables with selection. |
| aria-sort | Add to sorted th: aria-sort="ascending" or "descending". |
| Checkbox label | Select-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.
Related Components
Content Rules
Good column header
Last Login
Noun phrase. Concise. 10px UPPERCASE.
Bad column header
The Date When The User Last Logged In
Column headers must be short — max 2-3 words.
Empty state message
No users found · Try adjusting your filters.
Specific entity + actionable guidance.
Bad empty state
No data
Vague, provides no path forward.