Overview

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).

Enable dev warnings with window.DD_DEV = true before the script tag. Warnings appear in the browser console for missing elements or invalid attributes.
Overview

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>
Foundation

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

TokenLight defaultUsage
--dd-bg#e4e4e4Page background
--dd-surface#e4e4e4Panel / card background (same as bg on neu themes)
--dd-surface-soft#e0e0e0Slightly different surface
--dd-surface-muted#d8d8d8Table headers, muted areas

Text tokens

TokenLight defaultUsage
--dd-text#1a1a1aPrimary text
--dd-text-soft#2a2a2aSlightly dimmed
--dd-text-muted#555Secondary, captions
--dd-text-faint#888Placeholders, metadata

Brand tokens

TokenUsage
--dd-brandButtons, links, active states, focus rings
--dd-borderVisible borders
--dd-border-softSubtle borders, dividers
--dd-brand-softHover highlights, soft badge backgrounds

Semantic colour tokens

TokenValueUsage
--dd-success#1c7c34Success states
--dd-danger#a51d2dError / destructive
--dd-warning#a66a00Warning states
--dd-info#106f80Informational

Radius tokens

TokenValue
--dd-radius-xs6px
--dd-radius-sm8px
--dd-radius-md12px
--dd-radius-lg20px
--dd-radius-xl28px
--dd-radius-full999px

Header & nav tokens

TokenUsage
--dd-header-bgHeader background (default #000 — always black)
--dd-header-colorHeader text / link colour
--dd-header-heightHeader height (72px)
--dd-nav-bgNav pill / sidebar background
--dd-nav-colorNav pill text
--dd-logo-heightLogo 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 */
Foundation

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
Neumorphic themes: 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">
The <style> block must come AFTER the CDN <link> to win the cascade.
Foundation

Typography CSS

ElementSizeWeightTracking
h1clamp(2.8rem, 6vw, 5rem)800-0.045em
h2clamp(2rem, 4.5vw, 3.25rem)800-0.035em
h3clamp(1.4rem, 3vw, 1.75rem)800-0.025em
h41.2rem800-0.018em
p1rem400normal / line-height 1.8
h60.72rem900+0.12em uppercase label
Layout

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>
Layout

Grid System CSS

ClassBehaviour
dd-gridGrid container, gap 1rem
dd-grid-22 equal columns → 1 col at 480px
dd-grid-33 cols → 2 at 768px → 1 at 480px
dd-grid-44 cols → 2 at 768px → 1 at 480px
dd-grid-autoAuto-fit, min 240px per column
dd-splitTwo unequal columns → 1 at 768px
<div class="dd-grid dd-grid-3">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
</div>
Layout

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 -->
Navigation

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>
Components

Buttons CSS JS

Variants

ClassStyle
dd-buttonDefault — brand bg, page-bg text, raised on light/dark neu
dd-button-successGreen
dd-button-dangerRed
dd-button-warningAmber
dd-button-infoTeal
dd-button-outlineTransparent + brand border
dd-button-softBrand-soft background
dd-button-ghostFully transparent
dd-button-lightSurface background

Sizes & shapes

ClassEffect
dd-button-xs / sm / lg / xlExtra small → Extra large
dd-button-blockFull width
dd-button-iconSquare icon-only button

Processing state JS

<button class="dd-button"
  data-dd-processing="Saving…"
  data-dd-duration="2000">Save</button>
Components

Panels CSS

ClassEffect
dd-panelBase card — raised on neu themes, hover lift
dd-panel-staticNo hover lift
dd-panel-darkAlways-dark panel. On light neu: dark embedded widget with glow
dd-panel-featureBrand border accent
dd-panel-sideHorizontal — image left, content right
dd-panel-headPanel header row
dd-panel-bodyPanel body padding
dd-panel-footPanel footer row
dd-panel-titleBold heading inside panel body
dd-panel-textMuted paragraph inside panel body
dd-panel-mediaImage / 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>
Components

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>
Components

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>
Components

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>
Components

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>
Components

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>
Components

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>
Components

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 -->
Overlays

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>
Overlays

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 });
Interactive

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>
Interactive

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>
Sliders

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
});
Sliders

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 -->
Forms — Text

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>
Forms — Text

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>
Forms — Text

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>
Forms — Text

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 -->
Forms — Controls

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>
Forms — Controls

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>
Forms — Controls

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>
On dark neu theme the checked state uses cyan (--dd-accent-cyan) automatically.
Forms — Controls

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 -->
Forms — Special

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'
});
Forms — Special

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 -->
Forms — Special

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>
Forms — Special

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));
Forms — Special

Date & Time New CSS

<input class="dd-date"     type="date">
<input class="dd-time"     type="time">
<input class="dd-datetime" type="datetime-local">
Forms — Special

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);
Forms — Special

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 -->
Forms — Layout

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>
Forms — Layout

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

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>
JavaScript

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
JavaScript

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 });
JavaScript

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>
JavaScript

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' }]);
JavaScript

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));
JavaScript

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">
JavaScript

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);
JavaScript

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));
JavaScript

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
});
JavaScript

All data-dd-* Attributes HTML

AttributeEffect
data-dd-themeOn <html> — sets active theme
data-dd-set-theme="name"Set theme on click
data-dd-theme-toggleCycle 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-closeClose nearest modal
data-dd-drawer-open="#id"Open drawer
data-dd-drawer-closeClose nearest drawer
data-dd-dropdown-toggleToggle dropdown
data-dd-accordion-singleSingle-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-validateBuilt-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-stickySticky on scroll
data-dd-lazyLazy-load image (use data-src)
data-dd-nav-toggleMobile nav hamburger
data-dd-bell-count="5"Notification bell count
JavaScript

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 &nbsp;·&nbsp; Item two &nbsp;·&nbsp; Item three
</dd-marquee>

<dd-marquee direction="up" speed="30" style="height:120px">
  <div>Line one</div>
  <div>Line two</div>
</dd-marquee>
Utilities

Display & Flex CSS

ClassProperty
dd-hiddendisplay:none
dd-blockdisplay:block
dd-flexdisplay:flex
dd-inline-flexdisplay:inline-flex
dd-items-centeralign-items:center
dd-items-startalign-items:flex-start
dd-justify-centerjustify-content:center
dd-justify-betweenjustify-content:space-between
dd-flex-colflex-direction:column
dd-flex-1flex:1
dd-flex-wrapflex-wrap:wrap
Utilities

Spacing CSS

Scale: 0=0 · 1=.25rem · 2=.5rem · 3=1rem · 4=1.5rem · 5=3rem

PrefixProperty
dd-mt-{0–5}margin-top
dd-mb-{0–5}margin-bottom
dd-mx-automargin 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
Utilities

Typography Utilities CSS

ClassEffect
dd-text-centertext-align:center
dd-text-righttext-align:right
dd-truncateEllipsis overflow
dd-uppercasetext-transform:uppercase
dd-mutedcolor:var(--dd-text-muted)
dd-brand-textcolor:var(--dd-brand)
dd-fw-{300–900}font-weight (300,400,500,600,700,800,900)
dd-fs-xsdd-fs-2xlfont-size from type scale
Utilities

Responsive Utilities CSS

ClassHides at
dd-hide-sm≤ 576px
dd-hide-md≤ 768px
dd-hide-lg≤ 1024px
dd-hide-xl≤ 1280px

Grid breakpoints

BreakpointEffect
768pxdd-grid-3 and dd-grid-4 → 2 columns. dd-split → 1 column.
480pxAll grids → 1 column. Touch targets → 44px minimum.