v1.6.x-dev NDS IQ v6

Selection - National Design System

A selected-items counter for tables, card grids, and any checkbox list, feeding "5 selected of 48" record widgets that swap in automatically while a selection is active

Counting Selections

Point any element at a selectable list with data-selection-target="id" and its [data-selection-count] slots stay live. Anything with a checked input.nds-check inside counts: no wiring, no table required

Checkbox cards, no JS owner
0 selected of 3 services
<div class="nds-toolbar"> <div class="nds-bar-row"> <div class="nds-bar-start"> <span class="nds-bar-text" data-selection-target="selection_cards_demo"> <b data-selection-count>0</b> selected of <b data-selection-total>3</b> services </span> </div> </div> </div> <div id="selection_cards_demo" class="nds-grid" style="--max-col: 3; --mid-col: 2; --min-col: 1;"> <label class="nds-card nds-stroke"> <div class="nds-card-checkbox"> <div class="nds-form-container nds-check-container"> <div class="nds-form-control"> <input type="checkbox" class="nds-check" aria-label="Select Passport Renewal"> </div> </div> </div> <div class="nds-card-content"> <div class="nds-card-text"> <span class="nds-card-title">Passport Renewal</span> <p class="nds-card-description">General Directorate of Passports</p> </div> </div> </label> <label class="nds-card nds-stroke"> <div class="nds-card-checkbox"> <div class="nds-form-container nds-check-container"> <div class="nds-form-control"> <input type="checkbox" class="nds-check" aria-label="Select Driving Licence"> </div> </div> </div> <div class="nds-card-content"> <div class="nds-card-text"> <span class="nds-card-title">Driving Licence</span> <p class="nds-card-description">General Department of Traffic</p> </div> </div> </label> <label class="nds-card nds-stroke"> <div class="nds-card-checkbox"> <div class="nds-form-container nds-check-container"> <div class="nds-form-control"> <input type="checkbox" class="nds-check" aria-label="Select Commercial Registration"> </div> </div> </div> <div class="nds-card-content"> <div class="nds-card-text"> <span class="nds-card-title">Commercial Registration</span> <p class="nds-card-description">Ministry of Commerce</p> </div> </div> </label> </div>

Swapping with the Records Counter

The counter's home in real layouts is the Toolbar above a table. Give one wrapper both targets and two views: the Pagination records line shows while browsing, and the selection line takes over when rows are selected, including via the table's select-all. Ship nds-selection-view with hidden so first paint is correct

Select rows or use select-all to swap the line
Showing 14 of 6 requests
Reference Service
REQ-1001 Passport Renewal
REQ-1002 Driving Licence
REQ-1003 Commercial Registration
REQ-1004 Building Permit
REQ-1005 Health Card
REQ-1006 Work Visa
<!-- One wrapper, both targets, two views — in the toolbar's info slot. Prerender the initial numbers. --> <div class="nds-toolbar"> <div class="nds-bar-row"> <div class="nds-bar-start"> <span class="nds-bar-text" data-paged-target="selection_table_demo" data-selection-target="selection_table_demo"> <span class="nds-records-view">Showing <b data-paged-from>1</b>–<b data-paged-to>4</b> of <b data-paged-count>6</b> requests</span> <span class="nds-selection-view" hidden><b data-selection-count>0</b> selected of <b data-paged-count>6</b> requests</span> </span> </div> </div> </div> <table class="nds-table"> <thead> <tr> <th> <div class="nds-form-container nds-check-container"> <div class="nds-form-control"> <input type="checkbox" class="nds-check" aria-label="Select all requests"> </div> </div> </th> <th>Reference</th> <th>Service</th> </tr> </thead> <tbody id="selection_table_demo" class="nds-paged-content" style="--per-page: 4;"> <tr class="nds-page-item"> <td> <div class="nds-form-container nds-check-container"> <div class="nds-form-control"> <input type="checkbox" class="nds-check" aria-label="Select REQ-1001"> </div> </div> </td> <td>REQ-1001</td> <td>Passport Renewal</td> </tr> <tr class="nds-page-item"> <td> <div class="nds-form-container nds-check-container"> <div class="nds-form-control"> <input type="checkbox" class="nds-check" aria-label="Select REQ-1002"> </div> </div> </td> <td>REQ-1002</td> <td>Driving Licence</td> </tr> <tr class="nds-page-item"> <td> <div class="nds-form-container nds-check-container"> <div class="nds-form-control"> <input type="checkbox" class="nds-check" aria-label="Select REQ-1003"> </div> </div> </td> <td>REQ-1003</td> <td>Commercial Registration</td> </tr> <tr class="nds-page-item"> <td> <div class="nds-form-container nds-check-container"> <div class="nds-form-control"> <input type="checkbox" class="nds-check" aria-label="Select REQ-1004"> </div> </div> </td> <td>REQ-1004</td> <td>Building Permit</td> </tr> <tr class="nds-page-item"> <td> <div class="nds-form-container nds-check-container"> <div class="nds-form-control"> <input type="checkbox" class="nds-check" aria-label="Select REQ-1005"> </div> </div> </td> <td>REQ-1005</td> <td>Health Card</td> </tr> <tr class="nds-page-item"> <td> <div class="nds-form-container nds-check-container"> <div class="nds-form-control"> <input type="checkbox" class="nds-check" aria-label="Select REQ-1006"> </div> </div> </td> <td>REQ-1006</td> <td>Work Visa</td> </tr> </tbody> </table> <nav class="nds-pagination" data-auto-pagination="selection_table_demo" aria-label="Pagination"></nav>

Built-in Features

Auto-initialization

Activates when a data-selection-target element is on the page and recounts on every checkbox change, including a table's select-all.

Content-Agnostic Counting

Table rows, checkbox cards, and plain checkbox lists all count with the same rule: a checked input.nds-check inside the item. The card demo above has no JS of its own.

View Swap

Wrappers holding nds-records-view and nds-selection-view children switch between them automatically while anything is selected.

Programmatic Control

Recount a list or every widget after dynamic DOM changes through the NDS.Selection API.

Usage Guidelines

Best Practices

  • Use a selection counter wherever a bulk action exists (export, delete, assign): the number tells users what the action will affect before they commit
  • The count is the bulk-action truth: it includes selections on other pages and on rows an active filter currently hides, matching exactly what Export's selected scope exports
  • Place the counter in the Toolbar's info slot above the list, as the table demo shows: that is where users look for record counts
  • Pair with the Pagination records counter through the two-view swap so one line serves both browsing and selecting
  • Ship nds-selection-view with the hidden attribute: nothing is selected at first paint, and the view stays correct before scripts load
  • A single always-visible counter (like the card demo) needs no view classes: just the wrapper and a count slot
  • Style selection-mode chrome (highlighted toolbar, action buttons appearing) off the wrapper's data-state="has-selection" stamp rather than wiring your own listeners
  • Don't build per-page selection counts: if users need "selected on this page", rethink the flow, since bulk actions operate on the full selection
  • Keep the sentence in your markup, in your language: the component only writes numbers, so plurals and Arabic phrasing are entirely yours

Data Attributes

AttributeDescription
data-selection-target="id"Makes the element a selection counter for the list with that id. Its [data-selection-count] slots receive the count (with thousand separators), and it carries data-state="has-selection" while the count is above zero
data-selection-countNumber slot inside the wrapper; any element works (<b> renders medium emphasis inside nds-bar-text)
data-selection-totalSlot for the list's item count, so a standalone "x selected of y" works without Pagination. Paged lists can use data-paged-count instead (the filtered count)
data-state="selected"Alternative selected marker on an item, for content without checkboxes. Tables stamp it on rows automatically

JavaScript API

The counter initializes automatically when a data-selection-target element exists on the page and reacts to checkbox changes on its own. Use the API after DOM changes checkboxes don't announce.

// Wire the document listener (loader does this automatically) NDS.Selection.init(); // Recount every widget — call after adding/removing items or // setting .checked programmatically (no change event fires then) NDS.Selection.reinit(); // Recount one list's widgets by id NDS.Selection.recount('ordersList'); // Detach the listener (SPA teardown) NDS.Selection.destroy();
Last Modified Date: 29/07/2026 - 04:31 PM
Was this page useful?
60% of users said Yes from 2843 Feedbacks