Standard
Six variants across all interaction states
<button class="nds-btn nds-primary">
<span class="nds-label">Button</span>
</button>
Destructive
Red-toned variants for delete, remove, and irreversible actions
<button class="nds-btn nds-primary nds-destructive">
<span class="nds-label">Delete</span>
</button>
Layout
Content visibility, icon position, shape, and state modifiers
<button class="nds-btn nds-primary">
<i class="nds-icon nds-hgi-plus-sign" aria-hidden="true"></i>
<span class="nds-label">Add Item</span>
</button>
Menu Button
Dropdown trigger with auto-rotating indicator
Directional Buttons
Pseudo-element icon buttons for navigation: next, previous, up, down, and ellipsis
<!-- Next: arrow points forward (RTL-aware) -->
<button class="nds-btn nds-secondary-outline nds-next">
<span class="nds-label">Next</span>
</button>
<!-- Prev: arrow points backward (RTL-aware), icon placed after label -->
<button class="nds-btn nds-secondary-outline nds-prev">
<span class="nds-label">Prev</span>
</button>
<!-- Up -->
<button class="nds-btn nds-secondary-outline nds-up">
<span class="nds-label">Up</span>
</button>
<!-- Down -->
<button class="nds-btn nds-secondary-outline nds-down">
<span class="nds-label">Down</span>
</button>
<!-- Ellipsis (icon-only, no label needed) -->
<button class="nds-btn nds-secondary-outline nds-ellipsis nds-icon-only" aria-label="More"></button>
Button Group
Grouped buttons with merged borders and shared border radius
<div class="nds-btn-group">
<button class="nds-btn nds-secondary-outline nds-lg"><span class="nds-label">Left</span></button>
<button class="nds-btn nds-secondary-outline nds-lg"><span class="nds-label">Center</span></button>
<button class="nds-btn nds-secondary-outline nds-lg"><span class="nds-label">Right</span></button>
</div>
Animated Progress
Circular countdown timer controlled by --progress-duration
Static Progress
Fixed percentage indicator controlled by --progress-value
Status Feedback
Contextual color and icon change via data-status attribute
<button class="nds-btn nds-secondary">
<i class="nds-icon nds-hgi-copy-01"></i>
<span class="nds-label">Copy Link</span>
</button>
// Set status with auto-reset after 2 seconds
function copyAction(button) {
button.dataset.status = 'success';
setTimeout(() => delete button.dataset.status, 2000);
}
const btn = document.querySelector('.nds-btn');
btn.addEventListener('click', () => copyAction(btn));
Badge
Notification count overlay on icon buttons
<button class="nds-btn nds-icon-only nds-subtle">
<i class="hgi hgi-stroke hgi-notification-03">
<span class="nds-badge">3</span>
</i>
</button>
Cooldown Button
Click triggers a loading phase then a live countdown before the button is re-enabled
<button class="nds-btn nds-subtle nds-cooldown"
data-cooldown="10"
data-cooldown-loading="2"
data-cooldown-label="Resend in {s}s"
data-resend-label="Resend code"
data-sent-title="Code sent"
data-sent-message="Check your inbox for the verification code.">
<i class="hgi hgi-stroke hgi-mail-send-01" aria-hidden="true"></i>
<span class="nds-label">Send code</span>
</button>
// Trigger programmatically
const btn = document.querySelector('.nds-cooldown');
NDS.CooldownButton.start(btn);
// Reset mid-countdown (restores label, re-enables button)
NDS.CooldownButton.reset(btn);
// Listen for lifecycle events
btn.addEventListener('nds:cooldown:triggered', () => {
console.log('Cooldown started');
});
btn.addEventListener('nds:cooldown:tick', (e) => {
console.log('Remaining:', e.detail.remaining);
});
btn.addEventListener('nds:cooldown:end', () => {
console.log('Button restored');
});
Built-in Features
No JavaScript required. All variants, sizes, and states render from HTML markup with design tokens.
Primary, neutral, secondary, secondary-outline, subtle, and transparent with full state coverage.
24px to 40px. Font, icon, and indicator scale proportionally.
Lead icon, trail icon, icon-only, label-only, circular, full-width, and loading state.
High contrast borders, reduced motion fallback, print styles, and focus-visible ring.
Combine with destructive, on-color, loading, progress, status, badge, indicator, and groups.
Usage Guidelines
When to Use
- Use primary for the main action on the page. Limit to one per view
- Use neutral for strong secondary actions that need visual weight
- Use secondary/outline for supporting actions alongside a primary button
- Use subtle/transparent for low-emphasis actions like cancel, dismiss, or tertiary options
- Use destructive for delete, remove, or irreversible actions. Pair with a confirmation dialog
- Use on-color variants when placing buttons on colored or dark backgrounds
- Use icon-only for toolbar actions where space is limited. Always include aria-label
- Use loading state after a click to indicate processing. Disable the button to prevent double submissions
- Use cooldown for rate-limited actions like resend OTP, where the button must be locked for a set period after each click
Modifier Classes
| Class | Description |
|---|---|
nds-primary | Primary variant, green background |
nds-neutral | Neutral variant, dark background |
nds-secondary | Secondary solid variant, light gray background |
nds-secondary-outline | Secondary outlined variant, transparent background with border |
nds-subtle | No background until hover |
nds-transparent | No background at any state; text color shifts on hover |
nds-transparent nds-color | Transparent variant where default text uses the primary brand color and reverts to default on hover |
nds-destructive | Red-tone modifier, combine with any variant |
nds-oncolor | Light-on-dark modifier for colored/dark backgrounds |
nds-lg | 40px height (default, no class required) |
nds-md | 32px height |
nds-sm | 24px height |
nds-icon-only | Hides label text (screen-reader accessible via clip) |
nds-label-only | Hides icon elements |
nds-lead-icon | Forces icon before label (row direction) |
nds-trail-icon | Forces icon after label (row-reverse) |
nds-full | Full-width, centered label |
nds-circle | 999px border-radius for circular icon buttons |
nds-indicator | Adds a bottom-edge active indicator bar |
nds-menu-btn | Dropdown trigger with rotating chevron indicator |
nds-next | Directional button, arrow points forward (RTL-aware) |
nds-prev | Directional button, arrow points backward with reversed flex order (RTL-aware) |
nds-up | Directional button, arrow points up |
nds-down | Directional button, arrow points down |
nds-ellipsis | Fixed-width icon-only button with a horizontal more icon |
nds-progress | Shows the .nds-progress-circle overlay |
nds-progress-static | Disables the countdown animation; progress driven by --progress-value |
nds-cooldown | Marks the button for cooldown behavior. Requires data-cooldown |
nds-vertical | Set on a .nds-btn-group to stack it down instead of across. The rounded corners move to the top and bottom of the column and the seam runs along the block axis |
Data Attributes
| Attribute | Element | Description |
|---|---|---|
data-state | .nds-btn | Simulates interaction states: default, hover, pressed, selected, focused, loading, cooldown |
data-status | .nds-btn | Applies a contextual color and swaps the icon. Values: success, error, info, warning |
data-cooldown | .nds-cooldown | Cooldown duration in seconds (required). Read once at wire time and frozen. |
data-cooldown-loading | .nds-cooldown | Seconds to hold the loading state before the countdown begins (default: 0) |
data-cooldown-label | .nds-cooldown | Label template during countdown. Use {s} for remaining seconds (default: {s}) |
data-resend-label | .nds-cooldown | Label to restore after the first completed cycle (e.g., initial "Send" becomes "Resend"). Omit to keep the original label. |
data-sent-title | .nds-cooldown | Optional toast title shown when the cooldown begins (requires NDS.Alert to be loaded) |
data-sent-message | .nds-cooldown | Optional toast description shown when the cooldown begins (requires NDS.Alert to be loaded) |
CSS Custom Properties
| Property | Default | Description |
|---|---|---|
--btn-size | 40px | Controls button height and the square dimension for icon-only buttons. Set by size modifier classes. |
--btn-group-radius | var(--radius-md) | Border-radius applied to the .nds-btn-group ends. Automatically reduces to --radius-sm when the group contains nds-md or nds-sm buttons. |
--progress-duration | 4000ms | Duration of the animated progress countdown. Set inline on the button: style="--progress-duration: 4000ms;" |
--progress-value | 0 | Static progress percentage (0-100) for nds-progress-static buttons. Set inline: style="--progress-value: 25;" |
--progress-circumference | 62.83 | SVG circle circumference in px. Matches the stroke-dasharray value on the SVG circle. Override only if the SVG radius is changed. |
JavaScript API: NDS.CooldownButton
| Method | Parameters | Description |
|---|---|---|
NDS.CooldownButton.start(btn) | btn: HTMLElement | Programmatically triggers the cooldown cycle on the given button. No-op if the button is already in a cycle or has no data-cooldown attribute. |
NDS.CooldownButton.reset(btn) | btn: HTMLElement | Cancels an active cooldown or loading phase and restores the button immediately. Fires nds:cooldown:end. |
Custom Events
All events bubble and are dispatched on the .nds-cooldown button element.
| Event | Detail | When fired |
|---|---|---|
nds:cooldown:loading | The loading phase begins (only when data-cooldown-loading is greater than 0) | |
nds:cooldown:triggered | The loading phase ends and the countdown starts. Fire toast notifications here for custom variants. | |
nds:cooldown:tick | { remaining: number } | Fires every second during the countdown. detail.remaining is the seconds left. |
nds:cooldown:end | The cooldown finished naturally or reset() was called. The button is restored and re-enabled. |