domma-js 0.41.0 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,20 +9,50 @@ modern UI components in one cohesive package.
9
9
 
10
10
  ## Features
11
11
 
12
- - **DOM Manipulation** (`$`) - jQuery-compatible API with 90+ methods
13
- - **Utilities** (`_`) - 120+ Lodash-compatible utility functions with function composition
12
+ - **DOM Manipulation** (`$`) - jQuery-compatible API with 106 methods
13
+ - **Utilities** (`_`) - 202 Lodash-compatible utility functions with function composition
14
14
  - **Dates** (`D`) - Moment.js-style chainable date manipulation
15
- - **Models** (`M`) - Reactive data models with pub/sub events and two-way DOM binding
16
- - **Elements** (`E`) - 28 UI components (modals, tabs, carousels, navbars, slideovers, and more)
15
+ - **Models** (`M`) - Reactive models, dependency tracking, pub/sub events and declarative DOM bindings
16
+ - **Blueprints** (`B`) - One schema definition that drives models, forms and CRUD
17
+ - **Elements** (`E`) - 32 UI components (modals, tabs, carousels, navbars, context menus, slideovers, and more)
18
+ - **Effects** (`Domma.effects`) - 12 scripted animations (scribe, reveal, counter, ripple, strobe, and more)
17
19
  - **Tables** (`T`) - DataTable-like functionality (sort, filter, paginate, export to CSV/JSON)
18
20
  - **Forms** (`F`) - Blueprint-driven form generation with validation, wizards, and CRUD helpers
19
- - **Icons** (`I`) - 200+ SVG icons in 15 categories with auto-scan
21
+ - **Router** (`R`) - Client-side routing with view registration and middleware
22
+ - **Icons** (`I`) - 521 SVG icons in 18 categories with auto-scan
23
+ - **Flags** (`FL`) - Nation flags as inline SVG, in an opt-in bundle
20
24
  - **HTTP Client** (`H`) - Simple async HTTP requests with JSON handling
21
- - **Storage** (`S`) - localStorage wrapper with auto JSON serialization
22
- - **Themes** - 10+ built-in themes with light/dark variants and system detection
23
- - **Config Engine** - Declarative JSON-based behavior configuration with mutable API
25
+ - **Storage** (`S`) - localStorage wrapper with auto JSON serialisation
26
+ - **Auth** (`A`) - Token-based authentication with login, register and profile components
27
+ - **Components** - Define real Custom Elements with reactive state and fine-grained bindings
28
+ - **Themes** - 33 built-in themes across 13 light/dark families plus a six-theme Admin set
29
+ - **Config Engine** - Declarative JSON-based behaviour configuration with a mutable API
24
30
  - **CSS Features** - Utility classes (glow, shadows, elevation, fireworks, grid)
25
- - **Developer Tools** - Theme Roller, Page Roller, Editor, Print-to-PDF
31
+ - **Developer Tools** - Theme Roller, Page Roller, Schema Builder, Editor, Print-to-PDF
32
+
33
+ Everything above ships in one bundle with no runtime dependencies. Nothing uses `eval` or the `Function`
34
+ constructor, so Domma runs under a `script-src 'self'` Content Security Policy.
35
+
36
+ ## Ecosystem
37
+
38
+ Two pieces of Domma are now published as their own packages, so they can be used **without** Domma. This
39
+ repository is simply one consumer of each.
40
+
41
+ | Package | What it is | Where it lives |
42
+ |---------|-----------|----------------|
43
+ | [`domma-reactive`](https://www.npmjs.com/package/domma-reactive) | Dependency-tracked reactivity and fine-grained DOM bindings - the engine beneath `M.observable`, `M.computed` and `M.applyBindings` | Bundled into `domma.min.js`; [repo](https://github.com/pinpointzero73/domma-reactive) |
44
+ | `domma-celebrate` | Eight seasonal celebration themes and their canvas engine, code-split so each theme loads only when it is in season | Served from `dist/celebrate/` on dommajs.org |
45
+
46
+ **`domma-reactive` has moved out.** The reactive core was extracted into a standalone MIT-licensed package
47
+ and is now consumed from the npm registry rather than lived in this repository. Nothing changes for Domma
48
+ users: `M.observable()`, `M.computed()`, `M.effect()` and `M.applyBindings()` behave exactly as before, and
49
+ the package is bundled into `domma.min.js` by rollup. What has changed is that the reactivity layer is now
50
+ usable on its own - about 20 KB gzipped, no dependencies, no build step - in projects that want bindings
51
+ without the rest of the framework.
52
+
53
+ ```bash
54
+ npm install domma-reactive # reactivity and bindings, standalone
55
+ ```
26
56
 
27
57
  ## Installation
28
58
 
@@ -33,26 +63,42 @@ Get a complete project structure with development server in seconds:
33
63
  ```bash
34
64
  npm init -y
35
65
  npm install domma-js
36
- npx domma init # Creates project structure (prompts to start server)
66
+ npx domma-js init # Creates project structure (prompts to start server)
37
67
  ```
38
68
 
39
- **Or manually start the server:**
69
+ `init` scaffolds a **Single Page Application** by default. Pass `--mpa` for a traditional multi-page site,
70
+ or `--quick` to skip the prompts.
71
+
40
72
  ```bash
41
- npm start # Starts on port 3000
42
- npm run serve # Same as npm start
43
- npm run serve:8080 # Custom port
73
+ npx domma-js init # Interactive - choose SPA or MPA (SPA is the default)
74
+ npx domma-js init --spa # Explicit SPA
75
+ npx domma-js init --mpa # Multi-Page Application
76
+ npx domma-js init --quick # Accept every default
44
77
  ```
45
78
 
46
79
  This creates:
47
80
 
48
- - ✅ Complete project structure with 5 sample pages
81
+ - ✅ Complete project structure with sample pages or views
49
82
  - ✅ JSON configuration (`domma.config.json`)
50
83
  - ✅ Navbar and footer (configured via JSON)
51
- - ✅ Theme system with 16+ themes
84
+ - ✅ Theme system with all 33 themes
52
85
  - ✅ npm scripts (`start`, `serve`) in package.json
53
86
  - ✅ Development server with live reload
54
87
  - ✅ All features ready to use
55
88
 
89
+ ### Growing a Project
90
+
91
+ The CLI scaffolds new pages and views for you, wiring up paths and routes:
92
+
93
+ ```bash
94
+ npx domma-js add page admin # MPA: creates admin/
95
+ npx domma-js add page pages/dashboard # MPA: one level deep
96
+ npx domma-js add view settings # SPA: creates the view and its route
97
+ npx domma-js setup-ai # Add AI assistance files to an existing project
98
+ ```
99
+
100
+ Page paths are resolved automatically from folder depth, so a page two levels down still finds its assets.
101
+
56
102
  ### Development Server
57
103
 
58
104
  Start the built-in development server with live reload:
@@ -66,8 +112,8 @@ npm run serve:3096 # Explicit port 3096
66
112
 
67
113
  **Using CLI directly:**
68
114
  ```bash
69
- npx domma serve # Start server (auto-detects MPA/SPA)
70
- npx domma serve --port 3096 # Custom port
115
+ npx domma-js serve # Start server (auto-detects MPA/SPA)
116
+ npx domma-js serve --port 8080 # Custom port
71
117
  ```
72
118
 
73
119
  The server auto-detects your project type (MPA/SPA) and serves from the correct directory with live reload enabled.
@@ -76,7 +122,7 @@ The server auto-detects your project type (MPA/SPA) and serves from the correct
76
122
  ```bash
77
123
  # 1. Create project
78
124
  npm install domma-js
79
- npx domma init
125
+ npx domma-js init
80
126
 
81
127
  # 2. Start developing (auto-starts server or run manually)
82
128
  npm start # Opens http://localhost:3096
@@ -152,16 +198,16 @@ For quick prototyping without npm:
152
198
  <script src="https://cdn.jsdelivr.net/npm/domma-js@latest/public/dist/domma.min.js"></script>
153
199
  ```
154
200
 
155
- > **Tip:** Replace `@latest` with a specific version (e.g., `@0.9.18`) for production to ensure stability.
201
+ > **Tip:** Replace `@latest` with a specific version (e.g., `@0.43.0`) for production to ensure stability.
156
202
 
157
203
  ## Live Demo
158
204
 
159
- 🚀 **[Try Domma Online](https://dommajs.org/)** - Interactive showcase with 40+ examples
205
+ 🚀 **[Try Domma Online](https://dommajs.org/)** - Interactive showcase with 87 example pages
160
206
 
161
207
  Explore all features:
162
208
  - **[Showcase](https://dommajs.org/showcase/)** - Component examples and tutorials
163
209
  - **[MiniApps](https://dommajs.org/miniapps/)** - Real-world applications
164
- - **[Download](https://dommajs.org/showcase/download/)** - Preset bundles and archives
210
+ - **[Download](https://dommajs.org/download/)** - Preset bundles, archives and the Kickstart Builder
165
211
 
166
212
  ## Short Aliases
167
213
 
@@ -169,7 +215,7 @@ Explore all features:
169
215
  |------------|-------------------|-------|------------------------------------------|
170
216
  | DOM | `Domma()` | `$` | jQuery-style DOM manipulation |
171
217
  | Utils | `Domma.utils` | `_` | Lodash-style utilities |
172
- | Models | `Domma.models` | `M` | Reactive models & pub/sub |
218
+ | Models | `Domma.models` | `M` | Reactive models, bindings & pub/sub |
173
219
  | Blueprints | Blueprint Methods | `B` | Blueprint composition (extend, pick, omit) |
174
220
  | Dates | `Domma.dates` | `D` | Date manipulation |
175
221
  | Storage | `Domma.storage` | `S` | localStorage wrapper |
@@ -179,28 +225,34 @@ Explore all features:
179
225
  | Elements | `Domma.elements` | `E` | UI components |
180
226
  | Icons | `Domma.icons` | `I` | SVG icon system |
181
227
  | Tables | `Domma.tables` | `T` | DataTable functionality |
228
+ | Router | `Domma.router` | `R` | Client-side router |
229
+ | Flags | `Domma.flags` | `FL` | Nation flags (opt-in bundle) |
230
+
231
+ `Domma.effects`, `Domma.theme`, `Domma.sanitize` and `Domma.components` have no single-letter alias.
182
232
 
183
233
  ## Bundle Options
184
234
 
185
- Choose the right bundle for your needs:
235
+ Choose the right bundle for your needs. Gzipped is the size that reaches the browser.
236
+
237
+ | Bundle | Minified | Gzipped | Includes |
238
+ |--------|---------:|--------:|----------|
239
+ | **full** | 628 KB | 148 KB | All modules and components |
240
+ | **essentials** | 518 KB | 120 KB | Core + models, elements, config |
241
+ | **data-focused** | 203 KB | 45 KB | Core + models, tables, http, storage |
242
+ | **minimal** | 175 KB | 37 KB | DOM, utils, dates - zero UI |
243
+ | **no-ui** | 102 KB | 32 KB | Core utilities only (DOM + utils) |
186
244
 
187
- | Bundle | Size | Includes |
188
- |--------|------|----------|
189
- | **full** | 390KB | All modules and components |
190
- | **minimal** | 163KB | DOM, utils, dates - zero UI |
191
- | **essentials** | 328KB | Core + models, elements, config |
192
- | **data-focused** | 190KB | Core + models, tables, http, storage |
193
- | **no-ui** | 41KB | Core utilities only (DOM + utils) |
245
+ Two further bundles load alongside any of the above:
246
+
247
+ | Bundle | Minified | Gzipped | Includes |
248
+ |--------|---------:|--------:|----------|
249
+ | **tools** | 228 KB | 52 KB | Theme Roller, Page Roller, Schema Builder, Editor, Print-to-PDF |
250
+ | **flags** | 20 KB | 6 KB | Nation flags (`FL`) |
194
251
 
195
252
  ```html
196
253
  <!-- Use specific bundles via CDN -->
197
- <!-- Minimal bundle (163KB) -->
198
254
  <script src="https://dommajs.org/dist/domma-minimal.min.js"></script>
199
-
200
- <!-- Essentials bundle (328KB) -->
201
255
  <script src="https://dommajs.org/dist/domma-essentials.min.js"></script>
202
-
203
- <!-- Data-focused bundle (190KB) -->
204
256
  <script src="https://dommajs.org/dist/domma-data-focused.min.js"></script>
205
257
 
206
258
  <!-- Via unpkg -->
@@ -235,11 +287,11 @@ M.on('cart:add', (item) => updateCart(item));
235
287
  M.emit('cart:add', {id: 1, name: 'Widget'});
236
288
 
237
289
  // UI Components
238
- const modal = Domma.elements.modal('#my-modal', {backdrop: true});
290
+ const modal = E.modal('#my-modal', {backdrop: true});
239
291
  modal.open();
240
292
 
241
293
  // Interactive Tables
242
- const table = Domma.tables.create('#users', {
294
+ const table = T.create('#users', {
243
295
  data: usersArray,
244
296
  columns: [
245
297
  {key: 'name', title: 'Name', sortable: true},
@@ -250,6 +302,122 @@ const table = Domma.tables.create('#users', {
250
302
  });
251
303
  ```
252
304
 
305
+ ## Reactivity & Bindings
306
+
307
+ The reactive layer - published standalone as [`domma-reactive`](https://www.npmjs.com/package/domma-reactive)
308
+ and bundled here - lets you write down a relationship once and have Domma maintain it.
309
+
310
+ **Observables and computed values** work out their own dependencies at runtime:
311
+
312
+ ```javascript
313
+ const count = M.observable(0);
314
+ const doubled = M.computed(() => count.value * 2);
315
+
316
+ doubled.get(); // 0
317
+ count.value = 5;
318
+ doubled.get(); // 10
319
+
320
+ count.peek(); // read without subscribing
321
+
322
+ M.effect(() => console.log('count is', count.value)); // re-runs on every write
323
+ ```
324
+
325
+ Writes are batched and flushed on the next microtask, so ten assignments cause one update.
326
+ `M.flush()` forces it synchronously; `M.untracked(fn)` reads without creating a dependency.
327
+
328
+ **Declarative bindings** activate markup that already exists - they never rewrite your page:
329
+
330
+ ```html
331
+ <div id="app">
332
+ <h1 data-bind-text="title">Rendered by the server</h1>
333
+
334
+ <input data-model="query" placeholder="Search…">
335
+ <p data-if="query">Searching for <span data-bind-text="query"></span></p>
336
+
337
+ <ul data-each="rows key=id">
338
+ <li data-bind-text="name">template row</li>
339
+ </ul>
340
+
341
+ <button data-on-click="clear">Clear</button>
342
+ </div>
343
+ ```
344
+
345
+ ```javascript
346
+ const model = M.create({
347
+ title: {type: M.types.string},
348
+ query: {type: M.types.string},
349
+ rows: {type: M.types.array}
350
+ }, {
351
+ title: 'Live',
352
+ query: '',
353
+ rows: [{id: 1, name: 'Ada'}, {id: 2, name: 'Grace'}]
354
+ });
355
+
356
+ M.applyBindings(model, '#app', {
357
+ methods: {
358
+ clear() { model.set('query', ''); }
359
+ }
360
+ });
361
+ ```
362
+
363
+ The binding vocabulary is `data-bind-text`, `data-bind-class`, `data-bind-style`, `data-bind-style-<prop>`,
364
+ `data-bind-<prop>`, `data-model`, `data-on-<event>`, `data-if`, `data-each`, `data-options` and
365
+ `data-focus`. `data-each` reconciles by key, so deleting a row leaves every other row's DOM node, focus and
366
+ scroll position intact. Extend the vocabulary with `M.registerBinding()` and `M.registerHelper()`.
367
+
368
+ Expressions are parsed by hand rather than compiled with the `Function` constructor, so bindings work under
369
+ a `script-src 'self'` Content Security Policy. A binding whose expression will not parse logs one warning
370
+ naming the expression and is skipped; the rest of the page keeps working.
371
+
372
+ See [docs/Reactivity.md](./docs/Reactivity.md) and [docs/Bindings.md](./docs/Bindings.md).
373
+
374
+ ## Components
375
+
376
+ Define a real Custom Element with reactive state, props, computed values and lifecycle hooks. The same
377
+ bindings work inside a component template.
378
+
379
+ ```javascript
380
+ Domma.component('user-card', {
381
+ templateUrl: 'components/user-card/template.html',
382
+
383
+ props: {
384
+ userId: {type: M.types.number, required: true},
385
+ label: {type: M.types.string, default: 'User'}
386
+ },
387
+
388
+ data() {
389
+ return {name: '', email: '', loading: true};
390
+ },
391
+
392
+ computed: {
393
+ initials() {
394
+ return this.data.name.split(' ').map(n => n[0]).join('').toUpperCase();
395
+ }
396
+ },
397
+
398
+ methods: {
399
+ async fetchUser() {
400
+ const user = await H.get(`/api/users/${this.props.userId}`);
401
+ this.set({ name: user.name, email: user.email, loading: false });
402
+ }
403
+ },
404
+
405
+ onMount() { this.fetchUser(); },
406
+ onPropsChanged(name) { if (name === 'userId') this.fetchUser(); },
407
+
408
+ // Scoped styles, injected into the Shadow DOM
409
+ style: `.card { padding: var(--dm-spacing-md, 1rem); border-radius: 8px; }`
410
+ });
411
+ ```
412
+
413
+ ```html
414
+ <user-card user-id="42" label="Account owner"></user-card>
415
+ ```
416
+
417
+ Tag names must contain a hyphen; Domma prefixes `domma-` automatically if yours does not. Lifecycle hooks
418
+ are `onBeforeMount`, `onMount`, `onUpdated`, `onPropsChanged`, `onBeforeUnmount` and `onUnmount`.
419
+ See [docs/Components.md](./docs/Components.md).
420
+
253
421
  ## CSS Features
254
422
 
255
423
  Domma includes comprehensive utility classes for rapid UI development.
@@ -289,12 +457,16 @@ Domma includes comprehensive utility classes for rapid UI development.
289
457
  </div>
290
458
  ```
291
459
 
460
+ Display utilities use the Tailwind spellings (`.block`, `.flex`, `.grid`, `.hidden`). There are no
461
+ Bootstrap-style `.d-*` variants.
462
+
292
463
  ## Themes
293
464
 
294
- 10+ built-in themes with light/dark variants and automatic system detection.
465
+ 33 built-in themes: 13 families with light and dark variants, a bare `core-light`, and a six-theme Admin
466
+ set that pairs two finishes with three accents.
295
467
 
296
468
  ```javascript
297
- // Initialize with theme
469
+ // Initialise with theme
298
470
  Domma.theme.init({ theme: 'ocean-dark' });
299
471
 
300
472
  // Change theme dynamically
@@ -305,58 +477,78 @@ Domma.theme.toggle();
305
477
 
306
478
  // Auto-detect system preference
307
479
  Domma.theme.init({ autoDetect: true });
480
+
481
+ // List everything available
482
+ Domma.theme.listThemes(); // also listBases() and listVariants()
308
483
  ```
309
484
 
310
- **Available Themes:**
311
- - charcoal (light/dark)
312
- - ocean (light/dark)
313
- - forest (light/dark)
314
- - sunset (light/dark)
315
- - lavender (light/dark)
316
- - crimson (light/dark)
317
- - slate (light/dark)
318
- - amber (light/dark)
319
- - teal (light/dark)
320
- - rose (light/dark)
485
+ **Light/dark families:**
486
+ charcoal · ocean · forest · sunset · royal · lemon · silver · mint · dreamy · grayve · unicorn ·
487
+ christmas · wedding
488
+
489
+ **Admin family** (standalone, no light/dark variant):
490
+ `admin-smooth-steel` · `admin-smooth-indigo` · `admin-smooth-teal` ·
491
+ `admin-sharp-steel` · `admin-sharp-indigo` · `admin-sharp-teal`
492
+
493
+ The default is `charcoal-dark`. Pass `{disabled: true}` to `theme.init()` (or `noStyles: true` to
494
+ `$.setup()`) to turn Domma theming off entirely and bring your own stylesheet.
321
495
 
322
496
  ## Developer Tools
323
497
 
324
- Domma includes powerful developer tools in a separate bundle.
498
+ Domma includes developer tools in a separate bundle. Load it **after** the core bundle; each tool is then
499
+ available as an `Domma.elements` factory taking a host selector.
325
500
 
326
501
  ```html
327
- <!-- Load tools bundle via CDN -->
502
+ <script src="https://dommajs.org/dist/domma.min.js"></script>
328
503
  <script src="https://dommajs.org/dist/domma-tools.min.js"></script>
504
+ ```
329
505
 
330
- <!-- Or via unpkg -->
331
- <script src="https://unpkg.com/domma-js@latest/public/dist/domma-tools.min.js"></script>
506
+ **Theme Roller** - visual theme customisation with live preview:
507
+ ```javascript
508
+ const roller = E.themeRoller('#theme-roller-container', {
509
+ baseTheme: 'charcoal-light',
510
+ showPresets: true,
511
+ livePreview: true,
512
+ onChange: (variable, value) => console.log(variable, value)
513
+ });
332
514
  ```
333
515
 
334
- **Theme Roller** - Visual theme customization tool:
516
+ **Page Roller** - drag-and-drop page builder:
335
517
  ```javascript
336
- Domma.themeRoller.open();
518
+ const builder = E.pageRoller('#page-roller-container');
337
519
  ```
338
520
 
339
- **Page Roller** - Drag-and-drop page builder:
521
+ **Schema Builder** - visual Blueprint designer:
340
522
  ```javascript
341
- Domma.pageRoller.init();
523
+ const schema = E.schemaBuilder('#schema-builder-container');
342
524
  ```
343
525
 
344
- **Editor** - Rich content editor with export:
526
+ **Editor** - text, rich-text and code modes with autosave:
345
527
  ```javascript
346
- Domma.editor.create('#editor', {
347
- toolbar: ['bold', 'italic', 'heading', 'list'],
348
- onSave: (content) => console.log(content)
528
+ E.editor('#editor', {
529
+ mode: 'rich', // 'text' | 'rich' | 'code'
530
+ toolbar: [['bold', 'italic'], ['link', 'image']],
531
+ imagePaste: true,
532
+ autosave: true,
533
+ storage: 'my-notes'
349
534
  });
350
535
  ```
351
536
 
352
- **Print-to-PDF** - Export any element to PDF:
537
+ **Print-to-PDF** - export any element:
353
538
  ```javascript
354
- Domma.printToPDF('#content', { filename: 'export.pdf' });
539
+ const printer = E.printToPDF('#content', {
540
+ pageSize: 'A4',
541
+ orientation: 'portrait',
542
+ margins: 'normal'
543
+ });
544
+ printer.print();
355
545
  ```
356
546
 
547
+ The bundle also registers `window.DommaTools` with the same five factories.
548
+
357
549
  ## Config Engine
358
550
 
359
- Define behavior declaratively with JSON configuration.
551
+ Define behaviour declaratively with JSON configuration.
360
552
 
361
553
  **Basic Setup:**
362
554
  ```javascript
@@ -381,6 +573,9 @@ $.setup({
381
573
  });
382
574
  ```
383
575
 
576
+ `$.setup()` also accepts top-level `theme`, `autoDetectTheme`, `persistTheme`, `scanIcons`, `cloak` and
577
+ `noStyles` keys.
578
+
384
579
  **Mutable Configuration API:**
385
580
  ```javascript
386
581
  // Update existing configuration
@@ -390,22 +585,28 @@ $.update('#my-modal', {
390
585
 
391
586
  // Retrieve configuration
392
587
  const config = $.config('#my-modal');
393
- console.log(config);
588
+
589
+ // Reach the live component instance
590
+ const modal = $.getComponent('#my-modal');
591
+ modal.open();
394
592
 
395
593
  // Reset/destroy configuration
396
594
  $.reset('#my-modal'); // Removes component and unbinds events
397
595
  ```
398
596
 
399
- **Supported Components (16 total):**
400
- - card, modal, tabs, accordion, tooltip, carousel, dropdown
401
- - badge, backToTop, buttonGroup, loader, breadcrumbs, navbar
402
- - notification, timer, alarm
597
+ **Recognised component types (29):**
598
+ - Overlays: modal, dropdown, contextMenu, cookieConsent, notification
599
+ - Navigation: tabs, navbar, sidebar, footer, breadcrumbs, backToTop
600
+ - Content: card, accordion, carousel, listGroup, tooltip
601
+ - Indicators: badge, numberBadge, loader, timer, alarm
602
+ - Input: form, buttonGroup, autocomplete, pillbox, signature
603
+ - Effects: breathe, pulse, scribe
403
604
 
404
605
  ## Namespaces
405
606
 
406
607
  ### DOM (`$` or `Domma`)
407
608
 
408
- jQuery-compatible DOM manipulation with 90+ methods.
609
+ jQuery-compatible DOM manipulation with 106 methods.
409
610
 
410
611
  ```javascript
411
612
  // Selecting & traversing
@@ -425,7 +626,7 @@ $('#box').fadeIn(400).slideUp(300);
425
626
 
426
627
  ### Utils (`_` or `Domma.utils`)
427
628
 
428
- 120+ Lodash-compatible utility functions.
629
+ 202 Lodash-compatible utility functions.
429
630
 
430
631
  ```javascript
431
632
  // Arrays
@@ -452,6 +653,9 @@ _.memoize(expensive);
452
653
  _.camelCase('hello world'); // 'helloWorld'
453
654
  _.kebabCase('helloWorld'); // 'hello-world'
454
655
  _.truncate(text, { length: 50 });
656
+
657
+ // Templates (Mustache-style)
658
+ _.render('Hello {{name}}', { name: 'Ada' });
455
659
  ```
456
660
 
457
661
  ### Dates (`D` or `Domma.dates`)
@@ -482,15 +686,41 @@ D('2025-01-01').isBefore('2025-12-31');
482
686
  D().diff(otherDate, 'days');
483
687
  ```
484
688
 
689
+ ### Blueprints (`B`)
690
+
691
+ One schema definition, used everywhere: models, forms and CRUD all read the same blueprint.
692
+
693
+ ```javascript
694
+ const person = {
695
+ name: { type: M.types.string, required: true },
696
+ email: { type: M.types.string, pattern: /@/ },
697
+ age: { type: M.types.number, min: 0, max: 150 }
698
+ };
699
+
700
+ const employee = B.extend(person, {
701
+ department: { type: M.types.string, required: true }
702
+ });
703
+
704
+ const publicView = B.pick(employee, ['name', 'department']);
705
+ const withoutAge = B.omit(employee, ['age']);
706
+
707
+ M.create(employee); // a reactive model
708
+ F.render('#form', employee, {}); // a validated form
709
+ F.crud({ schema: employee, endpoint: '/api/staff' }); // the whole CRUD screen
710
+ ```
711
+
712
+ See [docs/Blueprints.md](./docs/Blueprints.md).
713
+
485
714
  ### Models (`M` or `Domma.models`)
486
715
 
487
- Reactive data models with pub/sub events.
716
+ Reactive data models, dependency tracking, pub/sub and DOM bindings.
488
717
 
489
718
  ```javascript
490
- // Pub/Sub
719
+ // Pub/Sub (subscribe/publish are the long forms of on/emit)
491
720
  M.on('user:login', (data) => console.log(data));
492
721
  M.emit('user:login', { username: 'alice' });
493
722
  M.off('user:login', handler);
723
+ M.once('app:ready', init);
494
724
 
495
725
  // Reactive Models
496
726
  const user = M.create({
@@ -503,25 +733,39 @@ user.set('name', 'Bob');
503
733
  user.onChange((field, newVal, oldVal) => {});
504
734
  user.validate();
505
735
 
506
- // DOM Binding
736
+ // Persistence
737
+ user.save();
738
+ user.load();
739
+
740
+ // Shared stores
741
+ M.store('settings', { theme: 'dark' });
742
+ M.getStore('settings');
743
+
744
+ // DOM Binding - one field, one element
507
745
  M.bind(user, 'name', '#name-input', { twoWay: true });
508
746
  M.bind(user, 'name', '#display', { format: v => `Hello, ${v}!` });
747
+
748
+ // Declarative bindings - whole subtrees
749
+ M.applyBindings(user, '#app');
509
750
  ```
510
751
 
752
+ See [Reactivity & Bindings](#reactivity--bindings) above for observables, computeds and `data-bind-*`.
753
+
511
754
  ### Elements (`E` or `Domma.elements`)
512
755
 
513
- 28 production-ready UI components with JavaScript interactivity.
756
+ 32 production-ready UI components.
514
757
 
515
758
  **Dialogs & Overlays:**
516
759
  - `modal()` - Modal dialogs with backdrop
517
- - `dialog` - Promise-based alert/confirm/prompt
518
- - `toast()` - Toast notifications (static method)
760
+ - `alert()` / `confirm()` / `prompt()` - Promise-based dialogs
761
+ - `toast()` - Toast notifications
519
762
  - `slideover()` - Panel overlays from screen edges
520
763
  - `dropdown()` - Dropdown menus
764
+ - `contextMenu()` - Right-click menus, delegated and nestable
521
765
 
522
766
  **Navigation:**
523
767
  - `tabs()` - Tabbed navigation
524
- - `navbar()` - Responsive navigation bar
768
+ - `navbar()` - Responsive navigation bar with recursive multi-level dropdowns
525
769
  - `sidebar()` - Sidebar with unlimited nesting
526
770
  - `breadcrumbs()` - Navigation breadcrumbs
527
771
  - `backToTop()` - Scroll-to-top button
@@ -529,7 +773,8 @@ M.bind(user, 'name', '#display', { format: v => `Hello, ${v}!` });
529
773
  **Content Display:**
530
774
  - `card()` - Cards with hover and collapsible support
531
775
  - `accordion()` - Expandable/collapsible sections
532
- - `carousel()` - Image/content carousels
776
+ - `carousel()` - Carousels with slide, fade and crossfade transitions
777
+ - `listGroup()` - Selectable lists with keyboard navigation
533
778
  - `hero` - Hero sections (CSS-only)
534
779
  - `footer()` - Footer with 3 layout modes
535
780
 
@@ -537,17 +782,20 @@ M.bind(user, 'name', '#display', { format: v => `Hello, ${v}!` });
537
782
  - `autocomplete()` - Input with suggestions
538
783
  - `pillbox()` - Multi-select tag input
539
784
  - `buttonGroup()` - Radio/checkbox button groups
785
+ - `chooser()` - Card or chip single/multi-select with theme-aware accents
786
+ - `signature()` - Canvas signature pad with undo/redo and PNG/SVG export
540
787
 
541
788
  **Feedback & Indicators:**
542
789
  - `loader()` - Loading indicators (spinner, dots, pulse, bars)
543
- - `badge()` - Badge/notification indicators
790
+ - `badge()` - Badge indicators
791
+ - `numberBadge()` - Notification counters with dot mode and pulse
544
792
  - `tooltip()` - Hover tooltips
545
793
  - `notification()` - Desktop notifications
546
794
  - `cookieConsent()` - Cookie consent banner
547
795
 
548
- **Data & Visualization:**
796
+ **Data & Time:**
549
797
  - `treeView()` - Hierarchical tree display
550
- - `progression()` - Timeline/roadmap component
798
+ - `progression()` - Unified timeline/roadmap component
551
799
  - `timer()` - Countdown timer
552
800
  - `alarm()` - Scheduled alerts
553
801
 
@@ -563,13 +811,21 @@ const tabs = E.tabs('#product-tabs', {
563
811
 
564
812
  // Autocomplete with async data
565
813
  E.autocomplete('#search', {
566
- dataSource: async (query) => {
567
- const res = await fetch(`/api/search?q=${query}`);
568
- return res.json();
569
- },
814
+ dataSource: async (query) => H.get(`/api/search?q=${query}`),
570
815
  onSelect: (item) => console.log(item)
571
816
  });
572
817
 
818
+ // Right-click menu, bound to a container and delegated to its rows
819
+ E.contextMenu('#invoice-table', {
820
+ match: 'tr[data-id]',
821
+ items: (row) => [
822
+ { label: 'Edit', icon: 'edit', action: () => edit(row.dataset.id) },
823
+ { label: 'Delete', icon: 'trash', danger: true },
824
+ { type: 'divider' },
825
+ { label: 'Export', icon: 'download', submenu: exportFormats }
826
+ ]
827
+ });
828
+
573
829
  // Sidebar with nesting
574
830
  E.sidebar('#main-nav', {
575
831
  mobileBreakpoint: 768,
@@ -581,12 +837,60 @@ const confirmed = await E.confirm('Delete this item?');
581
837
  if (confirmed) deleteItem();
582
838
  ```
583
839
 
584
- ### Tables (`Domma.tables`)
840
+ > `timeline()` still works but is deprecated - use `progression({ mode: 'timeline' })`.
841
+
842
+ ### Effects (`Domma.effects`)
843
+
844
+ 12 scripted animations. Every effect honours `prefers-reduced-motion` and returns a control object with
845
+ `pause()`, `resume()`, `stop()`, `restart()` and `destroy()`.
846
+
847
+ | Effect | Does |
848
+ |--------|------|
849
+ | `scribe()` | Types text by character, word or sentence, with undo and looping |
850
+ | `reveal()` | Scroll-triggered entrance animations via IntersectionObserver |
851
+ | `counter()` | Animated number counting with easing and scroll trigger |
852
+ | `scramble()` | Cipher/decode text reveal in four orders |
853
+ | `ripple()` | Material-style click ripple |
854
+ | `shake()` | Attention/error shake |
855
+ | `pulse()` | Pulsing scale |
856
+ | `breathe()` | Sinusoidal floating |
857
+ | `twinkle()` | Sparkle overlay |
858
+ | `tickerTape()` | Canvas ticker-tape parade, continuous or one-shot burst |
859
+ | `butterflies()` | Canvas butterflies that wander, rise and flap |
860
+ | `strobe()` | Canvas strobe lighting with six presets |
861
+
862
+ ```javascript
863
+ Domma.effects.scribe('#headline', {
864
+ mode: 'word', // 'typewriter' | 'word' | 'sentence'
865
+ speed: 50,
866
+ loop: true,
867
+ actions: [
868
+ { render: 'Hello', effect: 'bounce' },
869
+ { wait: '2s' },
870
+ { undoRender: 'all' },
871
+ { render: 'Welcome to Domma', effect: 'fade' }
872
+ ]
873
+ });
874
+
875
+ Domma.effects.counter('#revenue', {
876
+ to: 148320,
877
+ prefix: '£',
878
+ separator: ',',
879
+ trigger: 'scroll' // 'immediate' | 'scroll'
880
+ });
881
+
882
+ const party = Domma.effects.tickerTape(null, { palette: 'theme', burst: true });
883
+ ```
884
+
885
+ Canvas effects accept a container selector, or `null` for a full-page overlay. `strobe()` warns above 5 Hz
886
+ and disables itself under `prefers-reduced-motion`.
887
+
888
+ ### Tables (`T` or `Domma.tables`)
585
889
 
586
890
  DataTable-like functionality.
587
891
 
588
892
  ```javascript
589
- const table = Domma.tables.create('#users', {
893
+ const table = T.create('#users', {
590
894
  data: usersArray,
591
895
  columns: [
592
896
  { key: 'name', title: 'Name', sortable: true },
@@ -620,46 +924,108 @@ table.toJSON();
620
924
 
621
925
  Blueprint-driven form generation with validation, wizards, and CRUD helpers.
622
926
 
927
+ A form is built from a blueprint and some initial data, then rendered where you want it.
928
+
623
929
  ```javascript
624
- // Blueprint-driven form
625
- const form = F.create('#user-form', {
626
- fields: [
627
- { name: 'name', type: 'string', label: 'Full Name', required: true },
628
- { name: 'email', type: 'email', label: 'Email', required: true },
629
- { name: 'age', type: 'number', label: 'Age', min: 18, max: 120 },
630
- { name: 'role', type: 'select', label: 'Role', options: ['Admin', 'User'] }
631
- ],
930
+ const userBlueprint = {
931
+ name: { type: 'string', label: 'Full Name', required: true },
932
+ email: { type: 'email', label: 'Email', required: true },
933
+ age: { type: 'number', label: 'Age', min: 18, max: 120 },
934
+ role: { type: 'select', label: 'Role', options: ['Admin', 'User'] }
935
+ };
936
+
937
+ // Build, then render
938
+ const form = F.create(userBlueprint, initialData, {
632
939
  layout: 'grid', // 'stacked', 'inline', or 'grid'
633
- onSubmit: (data) => console.log(data)
940
+ onSubmit: (data, instance) => console.log(data)
941
+ });
942
+ form.renderTo('#user-form');
943
+
944
+ // Or do both in one call
945
+ F.render('#user-form', userBlueprint, initialData, { layout: 'grid' });
946
+
947
+ // Form in a modal, with save/error handling
948
+ F.modal(userBlueprint, initialData, {
949
+ onSave: async (data) => H.post('/api/users', data),
950
+ onError: (err) => E.toast(err.message, { type: 'danger' })
634
951
  });
635
952
 
636
953
  // Multi-step wizard
637
- const wizard = F.wizard('#registration', {
638
- steps: [
639
- { title: 'Account', fields: [/*...*/] },
640
- { title: 'Profile', fields: [/*...*/] },
641
- { title: 'Confirm', fields: [/*...*/] }
642
- ],
954
+ F.wizard('#registration', {
955
+ schema: {
956
+ steps: [
957
+ { title: 'Account', fields: { email: {...}, password: {...} } },
958
+ { title: 'Profile', fields: { name: {...} } }
959
+ ]
960
+ },
961
+ data: {},
643
962
  onComplete: (data) => saveUser(data)
644
963
  });
645
964
 
646
- // CRUD helper
965
+ // CRUD helper - form, table and API wired together
647
966
  const crud = F.crud({
648
- apiUrl: '/api/users',
649
- fields: blueprint,
650
- table: '#users-table'
967
+ schema: userBlueprint,
968
+ endpoint: '/api/users',
969
+ tableSelector: '#users-table',
970
+ title: 'Manage Users',
971
+ primaryKey: 'id',
972
+ displayField: 'name'
651
973
  });
974
+ await crud.init();
652
975
  ```
653
976
 
654
- ### Icons (`I` or `Domma.icons`)
977
+ ### Router (`R` or `Domma.router`)
655
978
 
656
- 200+ SVG icons in 15 categories with declarative and imperative APIs.
979
+ Hash-based client-side routing for single-page applications - the mode `npx domma-js init` scaffolds by
980
+ default. Views render into one container; navigation links are ordinary `<a href="#/about">` anchors.
657
981
 
658
982
  ```javascript
659
- // Declarative (auto-scan on load)
983
+ R.init({
984
+ container: '#app',
985
+ views: {
986
+ home: '<h1>Home</h1>',
987
+ about: { template: '<h1>About</h1>', onMount: ($el) => {} },
988
+ user: async (params) => `<h1>User ${params.id}</h1>`
989
+ },
990
+ routes: [
991
+ { path: '/', view: 'home', title: 'Home' },
992
+ { path: '/user/:id', view: 'user', title: 'User Profile',
993
+ onEnter: async (params) => loadUser(params.id) }
994
+ ],
995
+ default: '/',
996
+ notFound: '404',
997
+ transitions: { enter: 'fadeIn', leave: 'fadeOut', duration: 200 }
998
+ });
999
+
1000
+ // Register more later
1001
+ R.view('settings', { templateUrl: 'views/settings.html' });
1002
+ R.route({ path: '/settings', view: 'settings' });
1003
+
1004
+ // Middleware - runs before every navigation
1005
+ R.use((to, from, next) => {
1006
+ if (to.meta?.requiresAuth && !A.isAuthenticated()) R.navigate('/login');
1007
+ else next();
1008
+ });
1009
+
1010
+ // Navigate
1011
+ R.navigate('/user/42');
1012
+ R.navigate('/login', { replace: true });
1013
+ R.back();
1014
+ R.forward();
1015
+ R.current(); // the current route object
1016
+ R.destroy();
1017
+ ```
1018
+
1019
+ ### Icons (`I` or `Domma.icons`)
1020
+
1021
+ 521 SVG icons in 18 categories, with declarative and imperative APIs.
1022
+
1023
+ ```html
660
1024
  <button data-icon="check">Save</button>
661
1025
  <span data-icon="user" data-icon-size="24"></span>
1026
+ ```
662
1027
 
1028
+ ```javascript
663
1029
  // Manual scan after DOM updates
664
1030
  I.scan();
665
1031
 
@@ -671,11 +1037,39 @@ $('#icon-container').html(html);
671
1037
  I.inject('#my-icon', 'star');
672
1038
  ```
673
1039
 
674
- **Categories:** actions, arrows, communication, devices, editor, files, hardware, media, navigation, notifications, social, status, toggle, ui, weather
1040
+ **Categories:** ui · navigation · communication · media · files · social · commerce · finance · status ·
1041
+ devices · weather · health · sport · buildings · transport · code · emojis · seasonal
1042
+
1043
+ Icons are single-colour and follow `currentColor`, so they theme for free. `$.setup({ scanIcons: true })`
1044
+ scans on startup.
1045
+
1046
+ ### Flags (`FL` or `Domma.flags`)
1047
+
1048
+ Nation flags as inline SVG, keyed by ISO 3166-1 alpha-2 code. **Opt-in bundle** - flags are multi-colour
1049
+ artwork that cannot follow `currentColor`, so they are not in the core bundle.
1050
+
1051
+ ```html
1052
+ <script src="dist/domma.min.js"></script>
1053
+ <script src="dist/domma-flags.min.js"></script>
1054
+
1055
+ <span data-flag="gb"></span>
1056
+ <span data-flag="fr" data-flag-shape="circle"></span>
1057
+ ```
1058
+
1059
+ ```javascript
1060
+ FL.render('us', { shape: 'rounded', width: 32, border: true });
1061
+ FL.scan(); // activate data-flag attributes
1062
+ FL.list('europe'); // codes in a region
1063
+ FL.search('king'); // fuzzy name search
1064
+ FL.register('xx', { /* ... */ }); // add your own
1065
+ ```
1066
+
1067
+ 56 flags across five regions ship as compact descriptors, expanded to SVG lazily on first render and then
1068
+ memoised. About 20 KB minified, 6 KB gzipped. See [docs/Flags.md](./docs/Flags.md).
675
1069
 
676
1070
  ### HTTP (`H` or `Domma.http`)
677
1071
 
678
- Simple async HTTP client with automatic JSON handling.
1072
+ Simple async HTTP client with automatic JSON handling. `H.get()` resolves to parsed JSON, not a `Response`.
679
1073
 
680
1074
  ```javascript
681
1075
  // GET request
@@ -694,15 +1088,16 @@ await H.delete('/api/users/1');
694
1088
 
695
1089
  ### Storage (`S` or `Domma.storage`)
696
1090
 
697
- localStorage wrapper with automatic JSON serialization.
1091
+ localStorage wrapper with automatic JSON serialisation. Keys are namespaced under a `domma:` prefix, so
1092
+ `S.set('user', …)` does not collide with anything else on the origin.
698
1093
 
699
1094
  ```javascript
700
- // Store data (auto-serializes)
1095
+ // Store data (auto-serialises)
701
1096
  S.set('user', { name: 'Alice', role: 'admin' });
702
1097
  S.set('preferences', { theme: 'dark', lang: 'en' });
703
1098
 
704
1099
  // Retrieve (auto-parses)
705
- const user = S.get('user'); // Returns object
1100
+ const user = S.get('user'); // Returns object
706
1101
  const theme = S.get('theme', 'light'); // With default
707
1102
 
708
1103
  // Other methods
@@ -712,16 +1107,62 @@ S.keys(); // List all Domma keys
712
1107
  S.clear(); // Clear all Domma data
713
1108
  ```
714
1109
 
1110
+ ### Auth (`A` or `Domma.auth`)
1111
+
1112
+ Token-based authentication with ready-made login, register and profile components.
1113
+
1114
+ ```javascript
1115
+ A.init({ apiUrl: '/api', autoCheck: true });
1116
+
1117
+ await A.login({ email, password });
1118
+ await A.register({ name, email, password });
1119
+ A.logout();
1120
+
1121
+ A.isAuthenticated();
1122
+ A.getUser();
1123
+ A.getHeaders(); // Authorization header for your own requests
1124
+
1125
+ // Role helpers
1126
+ A.getRole();
1127
+ A.hasRole('editor');
1128
+ A.hasAnyRole(['admin', 'editor']);
1129
+ A.isAdmin();
1130
+ A.isSubscriber();
1131
+ A.isGuest();
1132
+
1133
+ A.on('login', (user) => R.navigate('/dashboard'));
1134
+ A.on('logout', () => R.navigate('/'));
1135
+ A.on('tokenExpired', () => R.navigate('/login'));
1136
+ ```
1137
+
1138
+ ### Sanitize (`Domma.sanitize`)
1139
+
1140
+ HTML sanitisation used wherever user content reaches `innerHTML`. It delegates to **DOMPurify** when that
1141
+ is present on the page. When it is not, it escapes the HTML entirely and warns - safe, but markup is lost,
1142
+ so include DOMPurify if you need user formatting to survive.
1143
+
1144
+ ```html
1145
+ <script src="https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.min.js"></script>
1146
+ ```
1147
+
1148
+ ```javascript
1149
+ Domma.sanitize.sanitise(userHtml); // 'default' preset
1150
+ Domma.sanitize.sanitise(userHtml, { preset: 'strict' });
1151
+ Domma.sanitize.sanitiseBasic(comment); // only b, i, em, strong, br
1152
+ Domma.sanitize.sanitiseUserContent(post); // strict preset
1153
+ Domma.sanitize.escape(text); // no HTML at all
1154
+ Domma.sanitize.hasDOMPurify(); // is the real thing loaded?
1155
+ ```
1156
+
715
1157
  ## Demo & Showcase
716
1158
 
717
1159
  ```bash
718
- # Run the demo
719
- npm run demo
720
-
721
- # Run the showcase (comprehensive examples)
722
- npm run showcase
1160
+ npm run dev # build, then serve public/ with live reload
723
1161
  ```
724
1162
 
1163
+ Then open `/showcase/`. 87 showcase pages cover every module. Each one is loaded into jsdom by `npm test`,
1164
+ which asserts it renders and logs nothing - so an example that stops working fails the build.
1165
+
725
1166
  ## MiniApps
726
1167
 
727
1168
  Real-world, production-ready applications built with Domma.
@@ -747,13 +1188,20 @@ Powerful document editor with rich text formatting and export capabilities.
747
1188
  - Auto-save with Domma.storage
748
1189
  - **Try it:** [Domma Docs Live](https://dommajs.org/miniapps/docs/)
749
1190
 
1191
+ ### Address Lookup
1192
+
1193
+ UK postcode and address finder.
1194
+
1195
+ - Search by postcode, pick from the matching addresses
1196
+ - Results cached client-side with Domma.storage
1197
+ - **Try it:** [Address Lookup Live](https://dommajs.org/miniapps/address/)
1198
+
750
1199
  ### Nexus
751
1200
 
752
- Project management and collaboration platform.
1201
+ Contact management and collaboration platform.
753
1202
 
754
- - Task tracking and assignment
1203
+ - Contact tracking and organisation
755
1204
  - Team collaboration tools
756
- - Real-time updates
757
1205
  - **Try it:** [Nexus Live](https://dommajs.org/miniapps/nexus/)
758
1206
 
759
1207
  ## Documentation
@@ -768,21 +1216,29 @@ Domma uses distributed CLAUDE.md files for focused, context-specific guidance:
768
1216
  - Showcase-specific guides in `/public/showcase/*/CLAUDE.md`:
769
1217
  - [Showcase Meta Guide](./public/showcase/CLAUDE.md)
770
1218
  - [DOM](./public/showcase/dom/CLAUDE.md), [Utils](./public/showcase/utils/CLAUDE.md), [Dates](./public/showcase/dates/CLAUDE.md), [Models](./public/showcase/models/CLAUDE.md)
771
- - [Tables](./public/showcase/tables/CLAUDE.md), [Elements](./public/showcase/elements/CLAUDE.md), [Config](./public/showcase/config/CLAUDE.md)
772
- - [HTTP](./public/showcase/http/CLAUDE.md), [Storage](./public/showcase/storage/CLAUDE.md), [Developer Tools](./public/showcase/theme-roller/CLAUDE.md)
1219
+ - [Tables](./public/showcase/tables/CLAUDE.md), [Elements](./public/showcase/elements/CLAUDE.md), [Effects](./public/showcase/effects/CLAUDE.md), [Config](./public/showcase/config/CLAUDE.md)
1220
+ - [HTTP](./public/showcase/http/CLAUDE.md), [Storage](./public/showcase/storage/CLAUDE.md), [Router](./public/showcase/router/CLAUDE.md), [Developer Tools](./public/showcase/theme-roller/CLAUDE.md)
773
1221
 
774
1222
  ### For Users
775
1223
 
776
1224
  - [`docs/GettingStarted.md`](./docs/GettingStarted.md) - Quick start guide
777
1225
  - [`docs/API.md`](./docs/API.md) - Complete API reference
778
1226
  - [`docs/DommaDocumentation.md`](./docs/DommaDocumentation.md) - Comprehensive documentation
779
- - [Showcase](./public/showcase/index.html) - 40+ interactive examples
1227
+ - [`docs/Reactivity.md`](./docs/Reactivity.md) - Dependency tracking, computeds, batching
1228
+ - [`docs/Bindings.md`](./docs/Bindings.md) - `M.applyBindings()` and the `data-bind-*` vocabulary
1229
+ - [`docs/Blueprints.md`](./docs/Blueprints.md) - One schema for models, forms and CRUD
1230
+ - [`docs/Components.md`](./docs/Components.md) - Custom Elements with reactive state
1231
+ - [`docs/ContextMenu.md`](./docs/ContextMenu.md) - Container binding, delegation and the nesting cascade
1232
+ - [`docs/Flags.md`](./docs/Flags.md) - The opt-in flags module
1233
+ - [`docs/SchemaBuilder.md`](./docs/SchemaBuilder.md) - Visual Blueprint designer
1234
+ - [Showcase](./public/showcase/index.html) - 87 interactive examples
780
1235
 
781
1236
  ### For Contributors
782
1237
 
783
1238
  - Build system: See `npm run build` and `rollup.config.js`
784
- - Testing: Open `tests/test.html` in browser or run `npm test`
1239
+ - Testing: `npm test` runs the Vitest suite (20 test files, plus the showcase page harness)
785
1240
  - Custom bundles: See [`src/bundles/CLAUDE.md`](./src/bundles/CLAUDE.md)
1241
+ - Release process: [`docs/RELEASING.md`](./docs/RELEASING.md)
786
1242
 
787
1243
  ## Development
788
1244
 
@@ -790,11 +1246,19 @@ Domma uses distributed CLAUDE.md files for focused, context-specific guidance:
790
1246
  # Install dependencies
791
1247
  npm install
792
1248
 
793
- # Build
1249
+ # Build (JS bundles, CSS, archives, kickstart files, miniapps)
794
1250
  npm run build
795
1251
 
1252
+ # Build just the JS or just the CSS
1253
+ npm run build:js
1254
+ npm run build:css
1255
+
796
1256
  # Run tests
797
1257
  npm test
1258
+
1259
+ # Validators (ratchets - each fails only when a file gets worse)
1260
+ npm run validate # classes, theme contrast, conventions
1261
+ npm run validate:showcase # load all 87 showcase pages in jsdom
798
1262
  ```
799
1263
 
800
1264
  **MiniApps:**
@@ -805,21 +1269,10 @@ npm run build:miniapp:garage # Build specific miniapp
805
1269
  NODE_ENV=production npm run build # Production build
806
1270
  ```
807
1271
 
808
- ## Bundle Size
809
-
810
- | Bundle | Size | Description |
811
- |--------|------|-------------|
812
- | **full** | 390KB | Complete framework with all modules |
813
- | **minimal** | 163KB | DOM + Utils + Dates only |
814
- | **essentials** | 328KB | Core + Models + Elements + Config |
815
- | **data-focused** | 190KB | Core + Models + Tables + HTTP + Storage |
816
- | **no-ui** | 41KB | Core utilities without UI components |
817
-
818
- All sizes are minified. Choose the bundle that fits your needs!
819
-
820
1272
  ## Browser Support
821
1273
 
822
- Modern browsers (ES6+)
1274
+ Modern browsers (ES6+). No `eval` and no `Function` constructor, so Domma runs under a
1275
+ `script-src 'self'` Content Security Policy.
823
1276
 
824
1277
  ## License
825
1278