Standard Table
The default table layout with striped rows, hover highlighting, and rounded borders
| Name | Role | Status | |
|---|---|---|---|
| Ahmed Al-Rashidi | ahmed.rashidi@gov.sa | Senior Developer | Active |
| Fatima Al-Harbi | fatima.harbi@gov.sa | UX Designer | Active |
| Sara Al-Dosari | sara.dosari@gov.sa | Marketing Lead | Active |
| Layla Al-Qahtani | layla.qahtani@gov.sa | HR Specialist | Active |
Sortable Table
Click any column header to cycle through ascending, descending, and original order
|
Department
|
Employees
|
Budget
|
Status |
|---|---|---|---|
| Information Technology | 25 | 5,625,000 SAR | Active |
| Human Resources | 12 | 3,000,000 SAR | Active |
| Sales | 18 | 4,500,000 SAR | Under Review |
| Marketing | 8 | 2,250,000 SAR | Planning |
Table with Feedback Icons
Status columns using feedback icons for quick visual scanning of row states
| Ticket ID | Customer | Issue Type | Priority | Status |
|---|---|---|---|---|
|
#TK-001
|
Omar Al-Ahmad | Login Issue | Low | |
|
#TK-002
|
Layla Al-Mansouri | Performance | Medium | |
|
#TK-003
|
Yusuf Al-Kindi | Data Loss | High | |
|
#TK-004
|
Aisha Al-Farisi | Feature Request | Low |
Table with Selection
Row checkboxes with a select-all header for bulk operations. The header checkbox shows an indeterminate state when some rows are selected.
|
|
Name | Department | Status | |
|---|---|---|---|---|
|
|
Hassan Al-Mukhtar | hassan.almukhtar@moi.gov.sa | Engineering | Active |
|
|
Nadia Al-Khatib | nadia.alkhatib@moi.gov.sa | Design | Active |
|
|
Tariq Al-Sudairi | tariq.alsudairi@moi.gov.sa | Marketing | Pending |
|
|
Zara Al-Habib | zara.alhabib@moi.gov.sa | Sales | On Leave |
Center Aligned Table
Center-align all cell content when the data benefits from symmetrical presentation
| Quarter | Revenue | Growth | Status |
|---|---|---|---|
| Q1 2024 | 2,450,000 SAR | +12% | On Track |
| Q2 2024 | 2,780,000 SAR | +13.5% | On Track |
| Q3 2024 | 2,610,000 SAR | -6.1% | At Risk |
| Q4 2024 | 3,100,000 SAR | +18.8% | On Track |
Loading State
A shimmer animation on table cells indicates data is being fetched
| Name | Department | Status | |
|---|---|---|---|
| Loading... | Loading... | Loading... | Loading... |
| Loading... | Loading... | Loading... | Loading... |
| Loading... | Loading... | Loading... | Loading... |
<table class="nds-table" data-state="loading">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Department</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>Loading...</td>
<td>Loading...</td>
<td>Loading...</td>
<td>Loading...</td>
</tr>
<tr>
<td>Loading...</td>
<td>Loading...</td>
<td>Loading...</td>
<td>Loading...</td>
</tr>
<tr>
<td>Loading...</td>
<td>Loading...</td>
<td>Loading...</td>
<td>Loading...</td>
</tr>
</tbody>
</table>
Responsive Table
All tables are responsive by default. JS auto-wraps every nds-table in an nds-table-wrapper with horizontal scroll on overflow. Add nds-mask to opt into gradient fade masks on the overflow edges. Use --max-width to constrain wrapper width and --min-width to lock the table's minimum width. If --min-width is not set, the JS auto-calculates it from the table's natural content width so cells never shrink.
| Employee ID | Full Name | Email Address | Department | Position | Start Date | Status |
|---|---|---|---|---|---|---|
| EMP-001 | Ahmed Al-Rashidi | ahmed.rashidi@gov.sa | Engineering | Senior Developer | 2023-01-15 | Active |
| EMP-002 | Fatima Al-Harbi | fatima.harbi@gov.sa | Design | UX Designer | 2023-02-15 | Active |
| EMP-003 | Sara Al-Dosari | sara.dosari@gov.sa | Marketing | Marketing Lead | 2023-03-15 | Active |
| EMP-004 | Layla Al-Qahtani | layla.qahtani@gov.sa | Human Resources | HR Specialist | 2023-04-15 | Active |
Table with Pagination
Large datasets can be paginated using the data-auto-pagination attribute. Add nds-page-item class to each <tr> in <tbody> and set --per-page on the content wrapper.
|
#
|
Service
|
System
|
Popularity |
|---|---|---|---|
| 1 | Identity Verification | Identity & Records | Most Used |
| 2 | Passport Renewal | Identity & Records | Most Used |
| 3 | Birth Certificate Request | Identity & Records | Standard |
| 4 | Marriage Contract Registration | Identity & Records | Standard |
| 5 | Driver's License Services | Transport & Vehicles | Most Used |
| 6 | Vehicle Registration | Transport & Vehicles | Most Used |
| 7 | Visa Application | Transport & Vehicles | Standard |
| 8 | Health Insurance Enrollment | Healthcare & Social | Most Used |
| 9 | Medical Appointment Booking | Healthcare & Social | Most Used |
| 10 | Employment Certificate | Healthcare & Social | Most Used |
| 11 | Work Permit Processing | Healthcare & Social | Standard |
| 12 | Retirement Benefits Application | Healthcare & Social | Standard |
| 13 | Tax Declaration Filing | Business & Finance | Most Used |
| 14 | VAT Registration | Business & Finance | Standard |
| 15 | Business License Application | Business & Finance | Standard |
| 16 | Property Registration | Business & Finance | Standard |
| 17 | Building Permit Request | Business & Finance | Standard |
| 18 | School Enrollment | Education & Justice | Most Used |
| 19 | Certificate Authentication | Education & Justice | Standard |
| 20 | Court Case Filing | Education & Justice | Standard |
Built-in Features
Every .nds-table on the page is automatically wrapped in a responsive scroll container. Opt into gradient fade masks with nds-mask.
Columns cycle through ascending, descending, and original order. Numbers, dates, and text are detected and sorted appropriately.
Header checkbox toggles all rows with indeterminate state support. Selected rows receive a distinct background highlight that persists across striped rows.
Add nds-mask to fade the overflow edges with a gradient that updates as the user scrolls to indicate more content in either direction. The mask clips descendants to the wrapper, so avoid it on tables with dropmenus, tooltips, or other overflowing popovers.
Sort headers are focusable buttons that respond to Enter and Space. Interactive elements within cells receive visible focus rings.
Alternating row backgrounds and hover highlighting are applied automatically for easier scanning of large datasets.
Add data-state="loading" to show a shimmer animation across all cells while data is being fetched.
Access sort state, reset sorting, and reinitialize tables after dynamic content changes through the NDS.Tables namespace.
Usage Guidelines
Best Practices
- Use tables for structured, comparable data where users need to scan across rows and columns. For simple key-value pairs, use a Definition List instead
- Do not use tables for page layout or displaying Cards in a grid. Use the Grid layout for that
- Choose compact tables for dense administrative data (logs, inventories, audit trails) and standard tables when rows contain rich content like tags, avatars, or action buttons
- Enable sorting only on columns with meaningful sort order. Status columns with tags are poor candidates for sorting
- Add row selection when the interface supports bulk operations (delete, export, assign). Pair the table with an action bar that appears when rows are selected
- Set
--max-widthwhen placing a table in a narrow container or side panel to trigger the responsive scroll wrapper early - Use pagination for datasets over 15-20 rows. Showing too many rows slows rendering and makes scanning harder
- Keep header labels short and descriptive. Avoid abbreviations that require explanation
- Place the most important identifier column (name, ID, title) first. Put action buttons or status indicators in the last column
- Add a
<caption>element for screen readers when the table's purpose is not clear from surrounding headings
Modifier Classes
| Class | Description |
|---|---|
nds-compact | Reduces row height to 48px. Override with --table-row-height for custom values |
nds-mask | Applies gradient fade masks on the overflow edges when the table scrolls horizontally. Off by default. Note: mask clips descendants to the wrapper and breaks overflowing UI like dropmenus, tooltips, and popovers that escape table bounds |
nds-sortable | Enables column sorting. Use nds-col-header with nds-sort-btn nds-icon-only inside sortable <th> elements |
nds-center | Center-aligns all cell content across the table |
nds-col-header | Flex container inside <th> that holds the label and actions side by side |
nds-col-actions | Container for action buttons (sort, filter, etc.) inside a column header |
nds-sort-btn nds-icon-only | Sort button class inside column headers that triggers column sorting |
nds-page-item | Applied to <tr> elements for client-side pagination (used with nds-paged-content) |
table-actions | Flex container for grouping action buttons within a cell |
actions-column | Shrinks column to fit content width, preventing unnecessary whitespace |
checkbox-column | Fallback for browsers without :has() support. Apply to <th> and <td> containing checkboxes to fix column width |
Data Attributes
| Attribute | Description |
|---|---|
data-state="sorted-asc" | Set on <th> to mark the initial sort column as ascending |
data-state="sorted-desc" | Set on <th> to mark the initial sort column as descending |
data-state="selected" | Set on <tr> to visually highlight a selected row. JS toggles this automatically when checkboxes change |
data-state="loading" | Set on <table> to show the loading shimmer animation |
data-auto-pagination | Set on <nav class="nds-pagination"> to enable auto-pagination for the preceding nds-paged-content wrapper |
CSS Custom Properties
| Property | Default | Description |
|---|---|---|
--table-row-height | 64px (48px in compact) | Row height for data cells. Set on the table or use nds-compact for the 48px preset |
--max-width | 100% | Maximum width of the responsive scroll wrapper |
--min-width | auto-calculated | Minimum width of the table inside the wrapper. Prevents cells from shrinking below content width |
--mask-fade-distance | 48px | Width of the gradient fade mask on scroll edges |
--per-page | 10 | Number of rows shown per page when using pagination (set on nds-paged-content) |
JavaScript API
The NDS.Tables API provides methods to initialize, sort, and manage table instances. All tables auto-initialize on page load. Call NDS.Tables.reinit() after dynamically adding new tables to the DOM.