DivDrop Documentation v1.0.0
DivDrop is a component-first CSS framework with a reactive JavaScript layer. All CSS classes use the dd- prefix. All CSS custom properties use --dd-. The JS global is window.DD (also window.DivDrop).
window.DD_DEV = true before the script tag. Warnings appear in the browser console for missing elements or invalid attributes.Installation
No npm, no webpack, no config. Add CSS in <head>, JS before </body>.
<!-- Flash prevention — in <head> BEFORE the CSS -->
<script>
(function(){
var t = localStorage.getItem('dd-theme');
if (t) document.documentElement.setAttribute('data-dd-theme', t);
})();
</script>
<!-- CSS -->
<link rel="stylesheet" href="https://divdrop.io/CDN/CSS/Def/divdrop.css">
<!-- JS -->
<script src="https://divdrop.io/CDN/JS/divdrop.js"></script>Design Tokens CSS
Override any token in a <style> tag after the CDN link. The inline style wins because it loads later in the cascade.
/* After the CDN link */
:root {
--dd-brand: #5046e4;
--dd-radius-lg: 8px;
--dd-bg: #f8f8f8;
}Background tokens
| Token | Light default | Usage |
|---|---|---|
--dd-bg | #e4e4e4 | Page background |
--dd-surface | #e4e4e4 | Panel / card background (same as bg on neu themes) |
--dd-surface-soft | #e0e0e0 | Slightly different surface |
--dd-surface-muted | #d8d8d8 | Table headers, muted areas |
Text tokens
| Token | Light default | Usage |
|---|---|---|
--dd-text | #1a1a1a | Primary text |
--dd-text-soft | #2a2a2a | Slightly dimmed |
--dd-text-muted | #555 | Secondary, captions |
--dd-text-faint | #888 | Placeholders, metadata |
Brand tokens
| Token | Usage |
|---|---|
--dd-brand | Buttons, links, active states, focus rings |
--dd-border | Visible borders |
--dd-border-soft | Subtle borders, dividers |
--dd-brand-soft | Hover highlights, soft badge backgrounds |
Semantic colour tokens
| Token | Value | Usage |
|---|---|---|
--dd-success | #1c7c34 | Success states |
--dd-danger | #a51d2d | Error / destructive |
--dd-warning | #a66a00 | Warning states |
--dd-info | #106f80 | Informational |
Radius tokens
| Token | Value |
|---|---|
--dd-radius-xs | 6px |
--dd-radius-sm | 8px |
--dd-radius-md | 12px |
--dd-radius-lg | 20px |
--dd-radius-xl | 28px |
--dd-radius-full | 999px |
Header & nav tokens
| Token | Usage |
|---|---|
--dd-header-bg | Header background (default #000 — always black) |
--dd-header-color | Header text / link colour |
--dd-header-height | Header height (72px) |
--dd-nav-bg | Nav pill / sidebar background |
--dd-nav-color | Nav pill text |
--dd-logo-height | Logo image height (36px) |
Type scale tokens
--dd-text-xs: .72rem /* labels, badges */ --dd-text-sm: .85rem /* secondary UI */ --dd-text-base: 1rem /* body */ --dd-text-lg: 1.15rem /* lead paragraphs */ --dd-text-xl: 1.35rem --dd-text-2xl: 1.75rem --dd-text-3xl: 2.25rem --dd-text-4xl: 3.25rem --dd-text-5xl: 4.75rem /* display / hero */
Themes CSS
Set data-dd-theme on your <html> tag. Every component re-renders instantly — no JavaScript needed.
<html data-dd-theme="midnight"> <!-- 9 built-in themes --> light — Neumorphic #e4e4e4 (default) dark — Neumorphic #2d2d2d charcoal + cyan accents midnight — Deep navy + blue forest — Dark moss green sand — Desert sunset rose — Soft blush slate — Cool blue-grey carbon — Black + red paper — Clean flat white
light uses #e4e4e4 with raised/inset shadows. dark uses #2d2d2d charcoal with cyan (#00d4ff) + purple (#8b5cf6) accents. Both have full component coverage. Other themes are flat.Custom theme
<link rel="stylesheet" href="https://divdrop.io/CDN/CSS/Def/divdrop.css">
<style>
[data-dd-theme="mybrand"] {
--dd-bg: #1a1a2e;
--dd-surface: #16213e;
--dd-text: #e8e8f4;
--dd-brand: #7c3aed;
--dd-border: rgba(124,58,237,.25);
--dd-border-soft: rgba(124,58,237,.12);
--dd-brand-soft: rgba(124,58,237,.1);
--dd-header-bg: #0d0d1a;
--dd-header-color:#e8e8f4;
--dd-nav-bg: #7c3aed;
--dd-nav-color: #fff;
}
</style>
<html data-dd-theme="mybrand"><style> block must come AFTER the CDN <link> to win the cascade.Typography CSS
| Element | Size | Weight | Tracking |
|---|---|---|---|
h1 | clamp(2.8rem, 6vw, 5rem) | 800 | -0.045em |
h2 | clamp(2rem, 4.5vw, 3.25rem) | 800 | -0.035em |
h3 | clamp(1.4rem, 3vw, 1.75rem) | 800 | -0.025em |
h4 | 1.2rem | 800 | -0.018em |
p | 1rem | 400 | normal / line-height 1.8 |
h6 | 0.72rem | 900 | +0.12em uppercase label |
Containers CSS
<div class="dd-container">…</div> <!-- 1240px --> <div class="dd-container-sm">…</div> <!-- 860px --> <div class="dd-container-lg">…</div> <!-- 1440px --> <div class="dd-shell">…</div> <!-- alias for dd-container --> <!-- Add dd-page to offset for sticky header: --> <main class="dd-page">…</main>
Grid System CSS
| Class | Behaviour |
|---|---|
dd-grid | Grid container, gap 1rem |
dd-grid-2 | 2 equal columns → 1 col at 480px |
dd-grid-3 | 3 cols → 2 at 768px → 1 at 480px |
dd-grid-4 | 4 cols → 2 at 768px → 1 at 480px |
dd-grid-auto | Auto-fit, min 240px per column |
dd-split | Two unequal columns → 1 at 768px |
<div class="dd-grid dd-grid-3"> <div>One</div> <div>Two</div> <div>Three</div> </div>
Stack & Cluster CSS
<!-- Stack: vertical spacing --> <div class="dd-stack">…</div> <!-- gap 1rem --> <div class="dd-stack dd-stack-sm">…</div> <!-- gap .5rem --> <div class="dd-stack dd-stack-lg">…</div> <!-- gap 1.5rem --> <div class="dd-stack dd-stack-xl">…</div> <!-- gap 2.5rem --> <!-- Cluster: horizontal wrapping --> <div class="dd-cluster">…</div> <!-- wrap, gap .75rem, center --> <div class="dd-cluster-between">…</div> <!-- space-between -->
Header CSS
dd-header is sticky and black by default. No class needed. Add dd-header-static to opt out of sticky.
| Class | Effect |
|---|---|
dd-header | Sticky black header, 72px, z-index 400 |
dd-header-static | Non-sticky override |
dd-header-light | Force themed/light header |
dd-header-inner | Inner flex row, max-width 1280px |
dd-logo | Logo link — auto-inverts image to white |
dd-logo-no-invert | Opt out of auto white inversion |
dd-nav-link | Nav link — 1rem/700, white on black |
dd-nav-link.is-active | Active state |
<header class="dd-header">
<div class="dd-header-inner">
<a class="dd-logo" href="/">
<img src="logo.png" alt="Brand">
</a>
<nav>
<a class="dd-nav-link is-active" href="/">Home</a>
<a class="dd-nav-link" href="/about">About</a>
</nav>
</div>
</header>Breadcrumb CSS
<ol class="dd-breadcrumb"> <li><a href="/">Home</a></li> <li><a href="/docs">Docs</a></li> <li>Current page</li> </ol> <!-- Variants: dd-breadcrumb-arrow / dd-breadcrumb-dot -->
Pagination CSS
<div class="dd-pagination"> <span class="dd-page-item is-disabled">«</span> <span class="dd-page-item is-active">1</span> <span class="dd-page-item">2</span> <span class="dd-page-item">3</span> <span class="dd-page-item">»</span> </div>
Buttons CSS JS
Variants
| Class | Style |
|---|---|
dd-button | Default — brand bg, page-bg text, raised on light/dark neu |
dd-button-success | Green |
dd-button-danger | Red |
dd-button-warning | Amber |
dd-button-info | Teal |
dd-button-outline | Transparent + brand border |
dd-button-soft | Brand-soft background |
dd-button-ghost | Fully transparent |
dd-button-light | Surface background |
Sizes & shapes
| Class | Effect |
|---|---|
dd-button-xs / sm / lg / xl | Extra small → Extra large |
dd-button-block | Full width |
dd-button-icon | Square icon-only button |
Processing state JS
<button class="dd-button" data-dd-processing="Saving…" data-dd-duration="2000">Save</button>
Panels CSS
| Class | Effect |
|---|---|
dd-panel | Base card — raised on neu themes, hover lift |
dd-panel-static | No hover lift |
dd-panel-dark | Always-dark panel. On light neu: dark embedded widget with glow |
dd-panel-feature | Brand border accent |
dd-panel-side | Horizontal — image left, content right |
dd-panel-head | Panel header row |
dd-panel-body | Panel body padding |
dd-panel-foot | Panel footer row |
dd-panel-title | Bold heading inside panel body |
dd-panel-text | Muted paragraph inside panel body |
dd-panel-media | Image / media area |
<div class="dd-panel">
<div class="dd-panel-head">Header</div>
<div class="dd-panel-body">
<p class="dd-panel-title">Card title</p>
<p class="dd-panel-text">Body copy.</p>
</div>
<div class="dd-panel-foot">
<button class="dd-button dd-button-sm">Action</button>
</div>
</div>Badges CSS
<span class="dd-badge">Default</span> <span class="dd-badge dd-badge-soft">Soft</span> <span class="dd-badge dd-badge-success">Success</span> <span class="dd-badge dd-badge-danger">Danger</span> <span class="dd-badge dd-badge-warning">Warning</span> <span class="dd-badge dd-badge-info">Info</span> <span class="dd-badge dd-badge-light">Light</span> <!-- Dot: --> <span class="dd-dot dd-dot-success"></span>
Alerts CSS
<div class="dd-alert">Default</div> <div class="dd-alert dd-alert-success">Success</div> <div class="dd-alert dd-alert-danger">Danger</div> <div class="dd-alert dd-alert-warning">Warning</div> <div class="dd-alert dd-alert-info">Info</div>
Avatar CSS
<div class="dd-avatar dd-avatar-sm">AB</div> <div class="dd-avatar">JD</div> <div class="dd-avatar dd-avatar-lg">EF</div> <div class="dd-avatar dd-avatar-xl"><img src="photo.jpg" alt=""></div> <!-- Group (overlapping) --> <div class="dd-avatar-group"> <div class="dd-avatar">A</div> <div class="dd-avatar">B</div> <div class="dd-avatar dd-avatar-sm">+4</div> </div>
Progress CSS
On dark neu theme the fill uses the cyan→purple gradient automatically.
<div class="dd-progress"> <div class="dd-progress-bar" style="--dd-progress:65%"></div> </div> <!-- Striped --> <div class="dd-progress"> <div class="dd-progress-bar dd-progress-striped" style="--dd-progress:40%"></div> </div>
Skeleton Loaders CSS
<div class="dd-skeleton dd-skeleton-text"></div> <div class="dd-skeleton dd-skeleton-title"></div> <div class="dd-skeleton dd-skeleton-circle"></div> <div class="dd-skeleton dd-skeleton-img"></div> <div class="dd-skeleton dd-skeleton-btn"></div> <div class="dd-skeleton dd-skeleton-avatar"></div> <div class="dd-skeleton dd-skeleton-card"></div>
Tooltip CSS-only
<button data-dd-tooltip="Default (top)">Hover</button> <button data-dd-tooltip="Bottom" data-dd-tooltip-pos="bottom">Hover</button> <button data-dd-tooltip="Left" data-dd-tooltip-pos="left">Hover</button> <button data-dd-tooltip="Right" data-dd-tooltip-pos="right">Hover</button>
Divider CSS
<hr class="dd-divider"> <div class="dd-divider">or</div> <!-- text in centre --> <hr class="dd-divider dd-divider-line"> <!-- lighter --> <div class="dd-divider-v"></div> <!-- vertical -->
Modal JS
Focus trap included (WCAG 2.1). Escape closes. Backdrop click closes. Focus restored to trigger on close. aria-modal and role="dialog" set automatically.
<button data-dd-modal-open="#myModal">Open</button>
<div id="myModal" class="dd-modal">
<div class="dd-modal-box"> <!-- dd-modal-sm / dd-modal-lg -->
<div class="dd-stack">
<h3>Title</h3>
<p>Content.</p>
<div class="dd-cluster">
<button class="dd-button" data-dd-modal-close>Confirm</button>
<button class="dd-button dd-button-soft" data-dd-modal-close>Cancel</button>
</div>
</div>
</div>
</div>Drawer JS
<button data-dd-drawer-open="#myDrawer">Open</button> <aside id="myDrawer" class="dd-drawer"> <!-- default: left --> <button data-dd-drawer-close class="dd-button dd-button-sm">Close</button> </aside> <aside id="myDrawer" class="dd-drawer dd-drawer-right">…</aside>
Dropdown JS
<div class="dd-dropdown">
<button class="dd-button" data-dd-dropdown-toggle>Options ▾</button>
<div class="dd-dropdown-menu"> <!-- dd-dropdown-right to right-align -->
<div class="dd-dropdown-header">Section</div>
<a class="dd-dropdown-item" href="#">Item one</a>
<a class="dd-dropdown-item" href="#">Item two</a>
<div class="dd-dropdown-divider"></div>
<a class="dd-dropdown-item dd-item-danger" href="#">Delete</a>
</div>
</div>Toast JS
<!-- HTML attribute -->
<button data-dd-toast="Message">Default</button>
<button data-dd-toast="Saved!" data-dd-toast-type="success">Success</button>
<button data-dd-toast="Error" data-dd-toast-type="danger">Error</button>
<button data-dd-toast="Note" data-dd-toast-type="warning">Warning</button>
<button data-dd-toast="Tip" data-dd-toast-type="info">Info</button>
<!-- Programmatic -->
DD.toast({ title:'Done', message:'Saved.', type:'success', duration:4000 });Tabs JS
<dd-tabs>
<div class="dd-tabs-list">
<button class="dd-tab-trigger" data-dd-tab="one">Tab one</button>
<button class="dd-tab-trigger" data-dd-tab="two">Tab two</button>
</div>
<div class="dd-tab-panel" data-dd-tab-panel="one">Content one</div>
<div class="dd-tab-panel" data-dd-tab-panel="two">Content two</div>
</dd-tabs>Accordion JS
<!-- Single-open mode -->
<div class="dd-stack dd-stack-sm" data-dd-accordion-single>
<div class="dd-accordion-item is-open">
<button class="dd-accordion-trigger">Open by default</button>
<div class="dd-accordion-panel">Content.</div>
</div>
<div class="dd-accordion-item">
<button class="dd-accordion-trigger">Closed</button>
<div class="dd-accordion-panel">Content.</div>
</div>
</div>Slim Slider — dd-slider-wrap New JS
10px engraved track, 22px circle thumb. JS auto-sets --dd-slider-pct as you drag. Fires dd:slider-change event.
<!-- Basic -->
<div class="dd-slider-wrap">
<input class="dd-slider" type="range" min="0" max="100" value="65">
</div>
<!-- Colour variants -->
<div class="dd-slider-wrap dd-slider-success">…</div>
<div class="dd-slider-wrap dd-slider-danger">…</div>
<div class="dd-slider-wrap dd-slider-warning">…</div>
<div class="dd-slider-wrap dd-slider-info">…</div>
<div class="dd-slider-wrap dd-slider-cyan">…</div>
<div class="dd-slider-wrap dd-slider-purple">…</div>
<div class="dd-slider-wrap dd-slider-gradient">…</div>
<!-- Custom fill colour -->
<div class="dd-slider-wrap" style="--dd-slider-color:#e91e63">…</div>
<!-- Custom thumb colour -->
<div class="dd-slider-wrap dd-slider-cyan" style="--dd-slider-thumb-bg:#fff">…</div>
<!-- Listen to value changes -->
wrap.addEventListener('dd:slider-change', function(e) {
console.log(e.detail.value); // 0–100
console.log(e.detail.pct); // percentage
});Capsule Slider — dd-slider-capsule New JS
Thick engraved pill. Oval thumb lives inside the capsule. Default: 36px height, purple fill. Same JS event as slim slider.
<!-- Basic (36px, purple fill) -->
<div class="dd-slider-capsule">
<input class="dd-slider" type="range" min="0" max="100" value="65">
</div>
<!-- Same colour variants as slim slider -->
<div class="dd-slider-capsule dd-slider-gradient">…</div>
<!-- Custom fill + thumb colour -->
<div class="dd-slider-capsule"
style="--dd-slider-color:#e91e63; --dd-slider-thumb-bg:#fff">…</div>
<!-- Custom height (configurable) -->
<div class="dd-slider-capsule" style="--dd-slider-capsule-height:28px">…</div>
<div class="dd-slider-capsule" style="--dd-slider-capsule-height:52px">…</div>
<!-- Tokens -->
<!-- --dd-slider-capsule-height: 36px default height -->
<!-- --dd-slider-capsule-pad: 5px thumb clearance -->
<!-- --dd-slider-oval-w: 26px oval thumb width -->
<!-- --dd-slider-color: #8b5cf6 fill colour -->
<!-- --dd-slider-thumb-bg: #888 thumb colour -->Underline Inputs CSS
Minimal underline-only style. Use inside dd-form and dd-field wrappers.
<div class="dd-form">
<div class="dd-field-row">
<div class="dd-field">
<label class="dd-label">First name</label>
<input class="dd-input" type="text" placeholder="Jane">
</div>
<div class="dd-field">
<label class="dd-label">Last name</label>
<input class="dd-input" type="text" placeholder="Smith">
</div>
</div>
<div class="dd-field">
<label class="dd-label">Email</label>
<input class="dd-input" type="email">
<span class="dd-help">We'll never share this.</span>
</div>
<div class="dd-field">
<label class="dd-label">Role</label>
<select class="dd-select">
<option>Designer</option>
</select>
</div>
<div class="dd-field">
<label class="dd-label">Message</label>
<textarea class="dd-textarea" placeholder="…"></textarea>
</div>
<button class="dd-button">Submit</button>
</div>Box Inputs CSS
<input class="dd-input-box" type="text"> <select class="dd-select-box">…</select> <textarea class="dd-textarea-box"></textarea> <!-- Validation states --> <input class="dd-input-box is-valid"> <input class="dd-input-box is-invalid"> <span class="dd-field-error" role="alert">Error message</span>
Search Input New CSS
<div class="dd-input-search"> <input type="search" placeholder="Search…"> </div>
Input Group New CSS
<!-- Prefix --> <div class="dd-input-group"> <span class="dd-input-prefix">£</span> <input class="dd-input-box" type="number" placeholder="0.00"> </div> <!-- Suffix --> <div class="dd-input-group"> <input class="dd-input-box" type="text" placeholder="username"> <span class="dd-input-suffix">@divdrop.io</span> </div> <!-- Both --> <div class="dd-input-group"> <span class="dd-input-prefix">https://</span> <input class="dd-input-box" type="text" placeholder="domain.com"> <span class="dd-input-suffix">/path</span> </div>
Floating Label New CSS
<div class="dd-float-field"> <input class="dd-float-input" type="text" placeholder=" " id="fname"> <label class="dd-float-label" for="fname">Full name</label> </div> <!-- Note: placeholder=" " (single space) is required for the CSS :not(:placeholder-shown) trick -->
Checkbox New CSS
<label class="dd-checkbox"> <input type="checkbox"> <span class="dd-checkbox-mark"></span> I agree to the terms </label> <!-- Checked by default --> <label class="dd-checkbox"> <input type="checkbox" checked> <span class="dd-checkbox-mark"></span> Already checked </label> <!-- Colour variants --> <label class="dd-checkbox dd-checkbox-success">…</label> <label class="dd-checkbox dd-checkbox-danger">…</label> <label class="dd-checkbox dd-checkbox-warning">…</label> <label class="dd-checkbox dd-checkbox-info">…</label> <!-- Disabled --> <label class="dd-checkbox"> <input type="checkbox" disabled> <span class="dd-checkbox-mark"></span> Disabled </label>
Radio New CSS
<!-- Radio group (vertical) -->
<div class="dd-radio-group">
<label class="dd-radio">
<input type="radio" name="plan" value="free" checked>
<span class="dd-radio-mark"></span>
Free plan
</label>
<label class="dd-radio">
<input type="radio" name="plan" value="pro">
<span class="dd-radio-mark"></span>
Pro plan
</label>
</div>
<!-- Inline group -->
<div class="dd-radio-group-inline">
<label class="dd-radio">…</label>
<label class="dd-radio">…</label>
</div>
<!-- Colour variants -->
<label class="dd-radio dd-radio-success">…</label>
<label class="dd-radio dd-radio-danger">…</label>Toggle Switch New CSS
<label class="dd-toggle">
<input type="checkbox">
<span class="dd-toggle-track">
<span class="dd-toggle-thumb"></span>
</span>
Enable notifications
</label>
<!-- Sizes -->
<label class="dd-toggle dd-toggle-sm">…</label>
<label class="dd-toggle">…</label> <!-- default -->
<label class="dd-toggle dd-toggle-lg">…</label>
<!-- Colour variants -->
<label class="dd-toggle dd-toggle-success">…</label>
<label class="dd-toggle dd-toggle-danger">…</label>
<label class="dd-toggle dd-toggle-warning">…</label>
<label class="dd-toggle dd-toggle-info">…</label>--dd-accent-cyan) automatically.Star Rating New CSS-only
<!-- Note: inputs must be in reverse order (5 to 1) for the CSS sibling trick --> <div class="dd-rating" role="group" aria-label="Rating"> <input type="radio" name="rate" id="r5" value="5"><label for="r5">★</label> <input type="radio" name="rate" id="r4" value="4"><label for="r4">★</label> <input type="radio" name="rate" id="r3" value="3"><label for="r3">★</label> <input type="radio" name="rate" id="r2" value="2"><label for="r2">★</label> <input type="radio" name="rate" id="r1" value="1"><label for="r1">★</label> </div> <!-- Sizes: dd-rating-sm / dd-rating-lg -->
PIN / OTP Input New JS
Auto-advances focus on input. Backspace goes back. Paste fills all boxes. Fires dd:pin-change event with the full value.
<div class="dd-pin">
<input class="dd-pin-box" type="text" maxlength="1" inputmode="numeric">
<input class="dd-pin-box" type="text" maxlength="1" inputmode="numeric">
<input class="dd-pin-box" type="text" maxlength="1" inputmode="numeric">
<span class="dd-pin-sep">—</span>
<input class="dd-pin-box" type="text" maxlength="1" inputmode="numeric">
<input class="dd-pin-box" type="text" maxlength="1" inputmode="numeric">
<input class="dd-pin-box" type="text" maxlength="1" inputmode="numeric">
</div>
<!-- Sizes: dd-pin-sm / dd-pin-lg -->
<!-- Listen -->
document.querySelector('.dd-pin').addEventListener('dd:pin-change', function(e) {
console.log(e.detail.value); // '123456'
});Stepper New JS
<div class="dd-stepper"> <button class="dd-stepper-btn" data-dd-step="-1">−</button> <input class="dd-stepper-input" type="number" value="1" min="0" max="99"> <button class="dd-stepper-btn" data-dd-step="1">+</button> </div> <!-- Listen to changes on the input via standard 'input' / 'change' events -->
File Upload New CSS
<label class="dd-file">
<input type="file" accept="image/*">
<span class="dd-file-label">
Drop files here or click to browse
</span>
</label>
<!-- Small -->
<label class="dd-file dd-file-sm">…</label>Tag Input New JS
Press Enter or comma to add a tag. Backspace removes the last tag. Fires dd:tag-add and dd:tag-remove.
<div class="dd-select-tags">
<div class="dd-tag-pill">Design <button class="dd-tag-remove">×</button></div>
<div class="dd-tag-pill">CSS <button class="dd-tag-remove">×</button></div>
<input class="dd-tag-input" placeholder="Add tag…">
</div>
<!-- Listen -->
wrap.addEventListener('dd:tag-add', e => console.log('Added:', e.detail.tag));
wrap.addEventListener('dd:tag-remove', e => console.log('Removed:', e.detail.tag));Date & Time New CSS
<input class="dd-date" type="date"> <input class="dd-time" type="time"> <input class="dd-datetime" type="datetime-local">
Colour Picker New CSS
<div class="dd-color-field">
<input class="dd-color-input" type="color" value="#4f8ef7" id="colPick">
<label class="dd-color-label" for="colPick">#4f8ef7</label>
</div>
<!-- Update label via JS -->
colPick.addEventListener('input', () => label.textContent = colPick.value);Character Counter New JS
Place dd-char-count immediately after the input/textarea. JS attaches automatically.
<div class="dd-field"> <label class="dd-label">Bio</label> <textarea class="dd-textarea-box" maxlength="280"></textarea> <div class="dd-char-count">0/280</div> </div> <!-- States: is-near (≥80%) turns warning, is-limit (100%) turns danger -->
Form Layouts CSS
<!-- Two column --> <div class="dd-form-2col"> <div class="dd-field">…</div> <div class="dd-field">…</div> </div> <!-- Three column --> <div class="dd-form-3col">…</div> <!-- dd-field-row: side by side in a single field --> <div class="dd-field-row"> <div class="dd-field">…</div> <div class="dd-field">…</div> </div> <!-- Section heading --> <div class="dd-form-section"> <h4 class="dd-form-section-title">Personal info</h4> <p class="dd-form-section-sub">Tell us about yourself.</p> </div>
Validation JS
Add data-dd-validate to a <form>. Validates on submit and on blur. Errors have role="alert" (WCAG). Fields linked to errors via aria-describedby.
<form class="dd-form" data-dd-validate>
<div class="dd-field">
<label class="dd-label">Email</label>
<input class="dd-input-box" type="email"
required
data-dd-message="Please enter a valid email.">
</div>
<div class="dd-field">
<label class="dd-label">Password</label>
<input class="dd-input-box" type="password"
required minlength="8">
</div>
<button type="submit" class="dd-button">Submit</button>
</form>
<!-- Manual validation states -->
<input class="dd-input-box is-valid">
<input class="dd-input-box is-invalid">
<span class="dd-field-error" role="alert">Error message shown here</span>Tables CSS
Always wrap in dd-table-wrap for responsive horizontal scroll.
<div class="dd-table-wrap">
<table class="dd-table dd-table-striped dd-table-hover">
<thead>
<tr><th>Name</th><th>Status</th><th>Plan</th></tr>
</thead>
<tbody>
<tr>
<td>Jane Smith</td>
<td><span class="dd-badge dd-badge-success">Active</span></td>
<td><span class="dd-badge dd-badge-info">Pro</span></td>
</tr>
</tbody>
</table>
</div>
<!-- Modifiers -->
<table class="dd-table"> <!-- base -->
<table class="dd-table dd-table-striped">
<table class="dd-table dd-table-hover">
<table class="dd-table dd-table-striped dd-table-hover">Reactive data table JS
<div class="dd-table-wrap">
<table class="dd-table dd-table-hover">
<thead><tr><th>Name</th><th>Role</th></tr></thead>
<tbody data-dd-repeat="users">
<template>
<tr>
<td data-dd-key="name" style="font-weight:600"></td>
<td data-dd-key="role"></td>
</tr>
</template>
</tbody>
</table>
</div>
<script>
const store = DD.globalStore({
users: [
{ name: 'Jane', role: 'Designer' },
{ name: 'Alex', role: 'Developer' },
]
});
// Add a row — DOM updates automatically
store.update('users', rows => [...rows, { name: 'Sam', role: 'Manager' }]);
</script>Overview & API Reference JS
Auto-initialises on DOMContentLoaded. Enable warnings: window.DD_DEV = true before the script.
DD.version // '1.0.0' DD.themes // ['light','dark','midnight','forest','sand','rose','slate','carbon','paper'] DD.setTheme(name) // switch theme + save to localStorage DD.store(initial) // create reactive store DD.binder(store) // create DOM binder → .mount() DD.globalStore(i) // store + auto-mounts binder DD.action(name, fn) // register named action DD.dispatch(name, d)// fire action DD.on(event, fn) // event bus subscribe DD.off(event, fn) // unsubscribe DD.emit(event, data)// emit event DD.router(routes) // hash router DD.navigate(path) // programmatic navigation DD.toast(opts) // show toast DD.setBellCount(n) // update nav bell badge
Reactive Store JS
const store = DD.store({ count: 0, name: '' });
store.get('count') // → 0
store.set('count', 5)
store.update('count', n => n + 1)
store.patch({ count: 3, name: 'DD' })
store.subscribe('count', (val, prev) => {})
store.watch(['count', 'name'], (key, val) => {})
store.derived('doubled', () => store.get('count') * 2)
store.snapshot() // plain object copy
store.reset() // back to initial
// For single-page apps — store + auto-binder
const store = DD.globalStore({ count: 0 });Data Binding HTML attrs
<!-- Two-way --> <input data-dd-bind="userName" type="text"> <input data-dd-bind="isDark" type="checkbox"> <!-- Output --> <span data-dd-output="userName"></span> <img data-dd-output-attr="src:avatarUrl"> <!-- Conditional --> <div data-dd-if="isLoggedIn">Welcome!</div> <div data-dd-if="role==admin">Admin panel</div> <div data-dd-if="role!=admin">Regular user</div> <div data-dd-show="isVisible">…</div> <div data-dd-class="isDark:dark-mode">…</div>
List Rendering HTML attr
<ul data-dd-repeat="items">
<template>
<li>
<span data-dd-key="name"></span>
<a data-dd-key-attr="href:url">Link</a>
</li>
</template>
</ul>
const store = DD.globalStore({ items: [{ name: 'One', url: '/one' }] });
// Update → DOM re-renders automatically
store.update('items', list => [...list, { name: 'Two', url: '/two' }]);Hash Router JS
Route changes automatically close any open modals and drawers.
DD.router({
'/': () => store.set('page', 'home'),
'/about': () => store.set('page', 'about'),
'/users/:id': ({ id }) => store.set('userId', id),
'*': () => store.set('page', '404'),
});
DD.navigate('/about');
<a data-dd-route="/about">About</a>
DD.on('dd:route', ({ path, params }) => console.log(path));Actions JS
DD.action('user.save', (data, store) => {
store.set('saved', true);
console.log('Saving:', data);
});
DD.dispatch('user.save', { id: 42 });
<!-- HTML triggers -->
<button data-dd-on-click="user.save" data-dd-payload='{"id":42}'>Save</button>
<input data-dd-on-change="search.run">
<input data-dd-on-input="filter.update">
<form data-dd-on-submit="form.submit">Event Bus JS
// Built-in events
DD.on('dd:theme-change', ({ theme }) => {});
DD.on('dd:route', ({ path, params }) => {});
DD.on('dd:modal-open', e => {});
DD.on('dd:modal-close', e => {});
DD.on('dd:slider-change',({ value, pct }) => {}); // fired on wrap element
DD.on('dd:pin-change', ({ value }) => {}); // fired on dd-pin element
DD.on('dd:tag-add', ({ tag }) => {});
DD.on('dd:tag-remove', ({ tag }) => {});
// Custom events
DD.on('app:ready', data => {});
DD.emit('app:ready', { user: 'Jane' });
// Unsubscribe
const handler = data => {};
DD.on('app:event', handler);
DD.off('app:event', handler);Theme API JS
DD.setTheme('forest'); // validates + sets + saves to localStorage
DD.themes; // ['light','dark','midnight',…]
<button data-dd-set-theme="midnight">Go Midnight</button>
<button data-dd-theme-toggle>Toggle dark</button>
DD.on('dd:theme-change', ({ theme }) => console.log(theme));Toast API JS
DD.toast('Simple message');
DD.toast({
title: 'Success',
message: 'File saved.',
type: 'success', // success | danger | warning | info
duration: 4000, // ms, default 3000
});All data-dd-* Attributes HTML
| Attribute | Effect |
|---|---|
data-dd-theme | On <html> — sets active theme |
data-dd-set-theme="name" | Set theme on click |
data-dd-theme-toggle | Cycle light ↔ dark on click |
data-dd-bind="key" | Two-way input ↔ store |
data-dd-output="key" | Store → textContent |
data-dd-output-attr="attr:key" | Store → attribute |
data-dd-if="key" | Show/hide on truthy (supports == !=) |
data-dd-show="key" | Toggle via .is-visible |
data-dd-class="key:cls" | Toggle class on truthy |
data-dd-repeat="key" | List rendering from store array |
data-dd-key="prop" | In template — bind textContent |
data-dd-key-attr="attr:prop" | In template — bind attribute |
data-dd-on-click="action" | Fire action on click |
data-dd-on-change="action" | Fire action on change |
data-dd-on-input="action" | Fire action on input |
data-dd-on-submit="action" | Fire action on form submit |
data-dd-payload='{}' | JSON payload sent with action |
data-dd-route="/path" | Navigate hash route on click |
data-dd-modal-open="#id" | Open modal |
data-dd-modal-close | Close nearest modal |
data-dd-drawer-open="#id" | Open drawer |
data-dd-drawer-close | Close nearest drawer |
data-dd-dropdown-toggle | Toggle dropdown |
data-dd-accordion-single | Single-open accordion mode |
data-dd-toast="msg" | Show toast on click |
data-dd-toast-type="success" | Toast type |
data-dd-processing="Label" | Button loading state |
data-dd-duration="2000" | Processing duration ms |
data-dd-scroll-to="#id" | Smooth scroll |
data-dd-clipboard="text" | Copy to clipboard |
data-dd-confirm="Sure?" | Confirm dialog |
data-dd-confirm-type="danger" | Confirm dialog type |
data-dd-validate | Built-in form validation |
data-dd-message="text" | Custom validation error |
data-dd-tooltip="text" | CSS tooltip |
data-dd-tooltip-pos="bottom" | Tooltip position |
data-dd-step="+1" | Stepper button increment |
data-dd-sticky | Sticky on scroll |
data-dd-lazy | Lazy-load image (use data-src) |
data-dd-nav-toggle | Mobile nav hamburger |
data-dd-bell-count="5" | Notification bell count |
Custom HTML Elements JS
dd-typewriter
<!-- Looping multi-phrase --> <dd-typewriter speed="150" delete-speed="65" pause="2400" phrases='["Build fast.","Zero deps.","Ship today."]'> </dd-typewriter> <!-- Once mode — AI style, no loop, cursor blinks at end --> <dd-typewriter once="true" speed="42"> Your sentence types out once. </dd-typewriter> <!-- Attributes --> <!-- speed ms per character (default 150 loop / 42 once) --> <!-- delete-speed ms per deletion (default speed × 0.45) --> <!-- pause ms after each phrase (default 2400) --> <!-- once="true" single-pass AI mode --> <!-- caret="false" hide blinking cursor -->
dd-counter
<dd-counter to="4200" suffix="+" duration="1400">0</dd-counter> <dd-counter to="99" suffix="%">0</dd-counter> <dd-counter to="14.5" prefix="$" decimals="1">0</dd-counter> <!-- Triggers on scroll into view -->
dd-reveal
<dd-reveal effect="fade">…</dd-reveal> <dd-reveal effect="fly-left">…</dd-reveal> <dd-reveal effect="fly-right">…</dd-reveal> <dd-reveal effect="zoom">…</dd-reveal> <dd-reveal effect="blur">…</dd-reveal> <!-- Stagger --> <dd-reveal effect="fade" style="--dd-reveal-delay:0ms">…</dd-reveal> <dd-reveal effect="fade" style="--dd-reveal-delay:120ms">…</dd-reveal>
dd-marquee
<dd-marquee speed="40"> Item one · Item two · Item three </dd-marquee> <dd-marquee direction="up" speed="30" style="height:120px"> <div>Line one</div> <div>Line two</div> </dd-marquee>
Display & Flex CSS
| Class | Property |
|---|---|
dd-hidden | display:none |
dd-block | display:block |
dd-flex | display:flex |
dd-inline-flex | display:inline-flex |
dd-items-center | align-items:center |
dd-items-start | align-items:flex-start |
dd-justify-center | justify-content:center |
dd-justify-between | justify-content:space-between |
dd-flex-col | flex-direction:column |
dd-flex-1 | flex:1 |
dd-flex-wrap | flex-wrap:wrap |
Spacing CSS
Scale: 0=0 · 1=.25rem · 2=.5rem · 3=1rem · 4=1.5rem · 5=3rem
| Prefix | Property |
|---|---|
dd-mt-{0–5} | margin-top |
dd-mb-{0–5} | margin-bottom |
dd-mx-auto | margin inline auto |
dd-p-{0–5} | padding all |
dd-px-{2–4} | padding inline |
dd-py-{2–4} | padding block |
dd-gap-{0–5} | gap |
Typography Utilities CSS
| Class | Effect |
|---|---|
dd-text-center | text-align:center |
dd-text-right | text-align:right |
dd-truncate | Ellipsis overflow |
dd-uppercase | text-transform:uppercase |
dd-muted | color:var(--dd-text-muted) |
dd-brand-text | color:var(--dd-brand) |
dd-fw-{300–900} | font-weight (300,400,500,600,700,800,900) |
dd-fs-xs … dd-fs-2xl | font-size from type scale |
Responsive Utilities CSS
| Class | Hides at |
|---|---|
dd-hide-sm | ≤ 576px |
dd-hide-md | ≤ 768px |
dd-hide-lg | ≤ 1024px |
dd-hide-xl | ≤ 1280px |
Grid breakpoints
| Breakpoint | Effect |
|---|---|
| 768px | dd-grid-3 and dd-grid-4 → 2 columns. dd-split → 1 column. |
| 480px | All grids → 1 column. Touch targets → 44px minimum. |