{"id":28883008,"url":"https://github.com/declarative-dom/ddom","last_synced_at":"2026-01-16T11:53:31.110Z","repository":{"id":298499001,"uuid":"990845072","full_name":"declarative-dom/ddom","owner":"declarative-dom","description":"DOM-aligned syntax for Web Components and Apps ","archived":false,"fork":false,"pushed_at":"2026-01-14T14:36:37.000Z","size":1831,"stargazers_count":3,"open_issues_count":8,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-14T18:11:42.739Z","etag":null,"topics":["dom","javascript","schema","types","web","web-components"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/declarative-dom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-05-26T18:18:12.000Z","updated_at":"2026-01-14T14:36:36.000Z","dependencies_parsed_at":"2025-07-21T18:27:34.885Z","dependency_job_id":"9c3c205d-c540-4475-8db2-0337dbeab214","html_url":"https://github.com/declarative-dom/ddom","commit_stats":null,"previous_names":["declarative-dom/ddom"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/declarative-dom/ddom","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/declarative-dom%2Fddom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/declarative-dom%2Fddom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/declarative-dom%2Fddom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/declarative-dom%2Fddom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/declarative-dom","download_url":"https://codeload.github.com/declarative-dom/ddom/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/declarative-dom%2Fddom/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28478391,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T06:30:42.265Z","status":"ssl_error","status_checked_at":"2026-01-16T06:30:16.248Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dom","javascript","schema","types","web","web-components"],"created_at":"2025-06-20T21:02:16.585Z","updated_at":"2026-01-16T11:53:31.100Z","avatar_url":"https://github.com/declarative-dom.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!-- Logo --\u003e\n\n\u003cp align=\"left\"\u003e\n  \u003cimg src=\"logo/ddom.min.svg\" alt=\"Declarative DOM Logo\" width=\"100\" height=\"100\" /\u003e\n\u003c/p\u003e\n\n# Declarative DOM\n\n## ⚠️ This is a preview of an in-progress schema definition and could change at any time. Do not use this in production. ⚠️\n\n**The Declarative Document Object Model** *(or DDOM)* is a schema and runtime for building and deploying web applications  (documents, variables, functions, and elements) via [JavaScript objects](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Scripting/Object_basics) which closely mirror the [Document Object Model](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model).\n\nJust as JSON provides a syntax and grammar for defining arbitrary data, DDOM defines a type-enforced object structure for exposing [Open Web Platform](https://www.w3.org/wiki/Open_Web_Platform) functionality within an object syntax inspired by the [official DOM API](https://dom.spec.whatwg.org/), the [CSSOM](https://www.w3.org/TR/cssom-1/), [ECMAScript static methods](https://tc39.es/), and related web standards.\n\nDDOM is developed as a [specification](spec/spec.md), a [collection of types](lib/src/types/), and a [reference runtime library](lib/) for deploying reactive web applications using the DDOM syntax. The DDOM runtime library is developed in Typescript and integrates the [TC39 JavaScript Signals proposal](https://github.com/tc39/proposal-signals) to provide a standardized signal-based reactivity model.\n\n## Quick Example\n\nCreate a reactive application web component with a simple JavaScript object:\n\n```JavaScript\nimport DDOM from '@declarative-dom/lib';\n\nDDOM.customElements.define({\n  tagName: 'my-app',\n  $count: 0,        // ← Dollar-prefixed properties become reactive signals\n  $name: 'World',   // ← Accessed via .get() and .set() methods\n  \n  children: [{\n    tagName: 'h1',\n    textContent: 'Hello ${this.$name}!' // ← Signals available in template strings\n  }, {\n    tagName: 'p', \n    textContent: 'Count: ${this.$count}' // ← Clean syntax without .get()\n  }, {\n    tagName: 'button',\n    textContent: 'Increment',\n    onclick: function() { \n      this.$count.set(this.$count.get() + 1); // ← .get()/.set() only needed in JavaScript functions\n    }\n  }]\n});\n```\n\nThat's it for a simple but powerful custom [Web Component](https://developer.mozilla.org/en-US/docs/Web/API/Web_components)! The syntax follows the DOM, \\$-prefixed properties automatically become reactive signals that are shared across the component scope. Template literals update the DOM reactively.\n\n## Key Features\n\n### 🏗️ DOM Conformance\n\nDDOM aligns closely with the native DOM API: object properties mirror standard DOM element properties. This makes for an efficient, comprehensive, standards-driven syntax.\n\n```JavaScript\n/**\n * DOM/DDOM Equivalent to:\n * \u003cdiv id=\"my-element\" class=\"container\" hidden=\"false\" tabindex=\"0\"\u003e\n *   Hello World\n * \u003c/div\u003e\n */\n{\n  tagName: 'div',\n  id: 'my-element',\n  className: 'container',\n  textContent: 'Hello World',\n  hidden: false,\n  tabIndex: 0\n}\n```\n\n### 🌳 Child Arrays\n\nDDOM supports declarative child elements using arrays, enabling nested structures that mirror the DOM tree:\n\n```JavaScript\n/**\n * DDOM equivalent to:\n * \u003cdiv\u003e\n *   \u003ch1\u003eTitle\u003c/h1\u003e\n *   \u003cp\u003ePargraph content \u003cstrong\u003eBold text\u003c/strong\u003e\u003c/p\u003e\n * \u003c/div\u003e\n */\n{\n  tagName: 'div',\n  children: [\n    {\n      tagName: 'h1',\n      textContent: 'Title'\n    },\n    {\n      tagName: 'p',\n      textContent: 'Paragraph content ',\n      children: [\n        {\n          tagName: 'strong',\n          textContent: 'Bold text'\n        }\n      ]\n    }\n  ]\n}\n```\n\n### 📦 Slot Support for Custom Elements\n\nCustom elements support the standard `\u003cslot\u003e` element for content composition, enabling reusable components with customizable content areas:\n\n```JavaScript\n// Define a custom element with slots\nDDOM({\n  customElements: [{\n    tagName: 'card-component',\n    children: [\n      {\n        tagName: 'header',\n        children: [\n          { tagName: 'slot', attributes: { name: 'header' } } // Named slot\n        ]\n      },\n      {\n        tagName: 'main',\n        children: [\n          { tagName: 'slot' } // Default slot\n        ]\n      }\n    ]\n  }]\n});\n\n// Use the component with slotted content\nDDOM({\n  document: {\n    body: {\n      children: [{\n        tagName: 'card-component',\n        children: [\n          {\n            tagName: 'h2',\n            attributes: { slot: 'header' }, // Goes to header slot\n            textContent: 'Card Title'\n          },\n          {\n            tagName: 'p',\n            textContent: 'Card content' // Goes to default slot\n          }\n        ]\n      }]\n    }\n  }\n});\n```\n\n**Slot Features:**\n* **Default slots** - Accept content without slot attribute\n* **Named slots** - Target specific insertion points with `slot=\"name\"`\n* **Fallback content** - Display default content when no slotted content provided\n* **Standard compliance** - Follows the HTML slot specification\n\nSee the [Slot Documentation](docs/slots.md) for more examples and patterns.\n\n### 🎨 Nested CSS\n\nStyles are represented as objects with CSSOM camelCase property names and support full CSS nesting syntax:\n\n```JavaScript\n/**\n * DDOM Equivalent to:\n * div {\n *   background-color: blue;\n *   margin-top: 10px;\n *   font-size: 16px;\n *   display: flex;\n * }\n *\n * div:hover {\n *   background-color: red;\n *   cursor: pointer;\n * }\n *\n * div .child {\n *   color: white;\n *   font-weight: bold;\n * }\n */\n{\n  tagName: 'div',\n  style: {\n    backgroundColor: 'blue',\n    marginTop: '10px',\n    fontSize: '16px',\n    display: 'flex',\n    ':hover': {\n      backgroundColor: 'red',\n      cursor: 'pointer'\n    },\n    '.child': {\n      color: 'white',\n      fontWeight: 'bold'\n    }\n  }\n}\n```\n\n### 🌐 Property Accessor Resolution\n\nStrings beginning with `document.`, `this.` or `window.` are provisioned as property accessors. Reference data from anywhere in your application using standard JavaScript dot notation:\n\n```JavaScript\n{\n  // Reference global data\n  userData: 'window.currentUser',\n  settings: 'document.appConfig',\n  \n  // Reference parent element data  \n  parentData: 'this.parentNode.sharedState',\n  \n  // Use in any property\n  items: 'window.$todoList',\n  signal: 'this.$count'\n}\n```\n\n### ⚡ Template Literal Reactivity\n\nStrings with `${...}` are automatically converted to reactive template expressions, allowing dynamic content updates. **Signals are automatically unwrapped** - no need for explicit `.get()` calls in templates:\n\n```JavaScript\n{\n  tagName: 'div',\n  $count: 42,\n  $status: 'active',\n  \n  // Signals auto-unwrapped in templates - clean syntax!\n  textContent: 'Count is ${this.$count}',                    // ← No .get() needed!\n  className: 'status ${this.$status} ${this.$count \u003e 10 ? \"high\" : \"low\"}',\n  title: 'Current value: ${this.$count} (${this.$status})', // ← Multiple signals\n  \n  // Mix signals with regular JavaScript expressions\n  'data-info': 'User ${this.$count} of ${Math.max(100, this.$count)}',\n  \n  attributes: {\n    'aria-label': 'Counter showing ${this.$count} items'\n  }\n}\n```\n\n**Template Benefits:**\n\n* **Automatic unwrapping** - Signals work like regular variables\n* **Reactive updates** - DOM updates when any referenced signal changes\n* **Clean syntax** - No boilerplate `.get()` calls required\n* **Multiple signals** - Use any number of signals in one template\n* **JavaScript expressions** - Full expression support with auto-unwrapping\n\n### 🎯 Scoped \u0026 Reactive Properties\n\n`$`-prefixed properties are mapped across the current scope, providing direct addressing throughout the object.\n\nNon-function properties automatically become Signals to facilitate reactivity. The type of signal depends on the value:\n\n```JavaScript\nconst app = DDOM({\n  // Data values become State signals\n  $count: 0,\n  $message: 'Hello',\n  $isVisible: true,\n  \n  // Template literals become Computed signals\n  $displayText: 'Count is ${this.$count}',\n  $status: '${this.$isVisible ? \"Visible\" : \"Hidden\"}',\n  \n  // Functions are shared in scope (as functions, not signals)\n  $increment: function() {\n    this.$count.set(this.$count.get() + 1);\n  },\n  \n  children: [{\n    tagName: 'p',\n    textContent: '${this.$displayText}' // ← Signals auto-unwrapped in templates!\n  }, {\n    tagName: 'button',\n    textContent: 'Click me',\n    onclick: function() {\n      this.$increment(); // ← Shared function available here\n    }\n  }]\n});\n\n// Signal access uses explicit .get() and .set() methods\napp.$count.set(42);                    // Sets the signal value\nconsole.log(app.$count.get());         // Gets the signal value (42)\nconsole.log(app.$displayText.get());     // Gets computed value: \"Count is 42\"\n\n// Direct property access returns the signal object\nconsole.log(app.$counter);               // Signal.State { ... }\nconsole.log(app.$displayText);           // Signal.Computed { ... }\n```\n\nNOTE: Scopes are partitioned at the window, document, and custom element levels. Each custom element creates a new scope boundary, enforcing component-level isolation.\n\n### 🔄 Fine-grained Reactivity\n\nEach property manages its own reactivity - no component-level re-rendering:\n\n```JavaScript\nconst app = DDOM({\n  // Only dollar-prefixed properties become signals\n  $name: 'John',\n  $age: 30,\n  \n  // Regular properties are static\n  id: 'user-profile',\n  className: 'container',\n  \n  // But can be signal-driven with templates or getters\n  textContent: 'Name: ${this.$name}',\n  title: function () {\n    return `User: ${this.$name.get()} (${this.$age.get()})`;\n  }\n});\n\n// Only signal updates trigger reactivity\napp.$name.set('Jane');  // ← Updates textContent and title\napp.$age.set(31);       // ← Updates title\n\n// Regular property changes don't trigger reactivity\napp.className = 'updated'; // ← Just sets the property directly\n```\n\n### 🌐 Dynamic Mapped Arrays\n\nCreate dynamic lists that automatically update when data changes using the prototype-based namespace syntax:\n\n```JavaScript\n{ // Object for defining an entire window\n    $todoList: [\n      { id: 1, text: 'Learn DDOM basics', completed: false },\n      { id: 2, text: 'Build a todo app', completed: false },\n      { id: 3, text: 'Deploy to production', completed: true }\n    ],\n\n    // Define the todo-item custom element\n    customElements: [{\n        tagName: 'todo-item',\n        $todoItem: {},\n        $todoIndex: 0,\n\n        textContent: '${this.$todoItem.text}' // ← Explicit signal access\n    }],\n    \n    // Document body structure\n    document: {\n        body: {\n            children: { // ← Array namespace with prototype-based configuration\n                prototype: 'Array',\n                items: 'window.$todoList', // ← Reference data from anywhere\n                // items: [{ id: 1, text: 'Task 1' }, { id: 2, text: 'Task 2' }], // ← Or a static array\n                map: {\n                    tagName: 'todo-item', // ← element tag for each item\n                    $todoItem: 'item', // ← Declarative accessor for each array item\n                    $todoIndex: 'index', // ← Declarative accessor for item index\n                }\n            }\n        }\n    }\n}\n```\n\n### 🌐 Web API Namespaces\n\nDDOM provides declarative access to Web APIs through prototype-based namespace configuration, enabling reactive integration with browser functionality:\n\n```JavaScript\n{\n  // Reactive HTTP requests\n  $userData: {\n    prototype: 'Request',\n    url: '/api/users/${this.$userId}',  // ← Signals auto-unwrapped in templates!\n    method: 'GET',\n    debounce: 300 // Debounce requests\n  },\n  \n  // Reactive form data\n  $uploadForm: {\n    prototype: 'FormData',\n    file: '${this.$selectedFile}',     // ← Clean syntax without .get()\n    description: '${this.$description}'\n  },\n  \n  // Reactive URL parameters\n  $searchParams: {\n    prototype: 'URLSearchParams',\n    q: '${this.$query}',               // ← Auto-unwrapped signals\n    page: '${this.$page}'\n  },\n  \n  // Use in API calls\n  $searchResults: {\n    prototype: 'Request',\n    url: '/api/search?${this.$searchParams}'\n  }\n}\n```\n\n**Supported Prototypes:**\n\n* **Request** - Declarative fetch API integration\n* **FormData** - Reactive form data construction\n* **URLSearchParams** - Reactive URL parameter handling\n* **Array, Set, Map** - Reactive collections with filtering, mapping, and sorting\n* **TypedArrays** - Reactive binary data arrays\n* **Blob** - Reactive binary data creation\n* **ArrayBuffer** - Reactive buffer management\n* **ReadableStream** - Reactive stream creation\n* **LocalStorage, SessionStorage, Cookie** - Reactive storage APIs\n* **IndexedDB** - Full reactive database support with CRUD operations\n\n## Getting Started\n\n### Installation\n\n```Shell\n# Clone the repository\ngit clone https://github.com/declarative-dom/ddom.git\ncd ddom\n\n# Install dependencies\ncd lib \u0026\u0026 npm install\n\n# Build the library\nnpm run build\n\n# Run tests\nnpm test\n\n# Lint code\nnpm run lint\n\n# Lint and auto-fix issues\nnpm run lint:fix\n```\n\n### Basic Usage\n\n```HTML\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n    \u003ctitle\u003eDDOM App\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n    \u003cscript type=\"module\"\u003e\n      import DDOM from '@declarative-dom/lib';\n      \n      // Create a reactive app\n      const app = DDOM({\n          $count: 0,\n          $name: 'World',\n          \n          // Define the DOM structure\n          document: {\n              body: {\n                  children: [\n                      {\n                          tagName: 'h1',\n                          textContent: 'Hello ${this.$name}!'\n                      },\n                      {\n                          tagName: 'p',\n                          textContent: 'Count: ${this.$count}'\n                      },\n                      {\n                          tagName: 'button',\n                          textContent: 'Increment',\n                          onclick: function() { \n                              this.$count.set(this.$count.get() + 1);\n                          }\n                      }\n                  ]\n              }\n          }\n        });\n        \n        console.log('App created:', app);\n    \u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n## Testing\n\nDDOM includes a comprehensive testing framework using Vitest:\n\n```Shell\n# Run all tests\nnpm test\n\n# Run tests in watch mode  \nnpm run test:watch\n\n# Run tests with coverage\nnpm run test:coverage\n```\n\n## Core Concepts\n\n### Dollar-Prefixed Signal Properties\n\nDDOM automatically wraps dollar-prefixed properties in reactive signals based on their value type:\n\n* **Data values** (strings, numbers, booleans, objects) become **State signals**\n* **Template literals** (strings with `${...}`) become **Computed signals**\n* **Functions** are shared in scope but are not wrapped in signals\n\nThese signals provide explicit `.get()` and `.set()` methods for predictable access and maintain fine-grained reactivity for property-level updates.\n\n### Component-Level Scoping\n\nDollar-prefixed properties are automatically shared across the current component scope:\n\n* **Component definition** creates a new scope boundary\n* **All dollar properties** (signals and functions) are available as `this.$property`\n* **Child elements** inherit parent scope properties automatically\n* **Local properties** always take precedence over inherited ones\n\n### Template Literal Processing\n\nDDOM converts strings containing `${...}` patterns to reactive expressions:\n\n* **Dollar-prefixed properties** with templates become computed signals\n* **Regular properties** with templates get reactive DOM bindings\n* **Template expressions** support **automatic signal unwrapping** - no `.get()` needed!\n* **Automatic updates** when referenced signals change\n\n### Non-Reactive Property Handling\n\nProperties without the `$` prefix remain static but can still be signal-driven:\n\n* **Static values** are set once and don't create signals\n* **Getters/setters** can reference signals for reactive behavior\n* **Template literals** on regular properties create reactive DOM bindings\n* **Property accessors** (`this.$signal`, `window.data`) are resolved once\n\n### Protected Properties\n\nDOM immutable properties `id` and `tagName` are automatically protected from reactivity to maintain element identity and prevent conflicts with the browser's internal handling.\n\n## Philosophy\n\n### DOM-First Design\n\nDDOM maintains strict alignment with DOM APIs and web standards. In general, DDOM aims to mirror and support valid DOM properties, keys, and value types as closely as possible.\n\n### The Rule of Least Power\n\nDDOM follows [Tim Berners-Lee's Rule of Least Power](https://www.w3.org/DesignIssues/Principles.html#PLP): *\"Given a choice of solutions, pick the least powerful solution capable of solving the problem.\"* This principle guides every design decision:\n\n* **Declarative over imperative** - Use data structures instead of functions where possible\n* **Templates over functions** - Template literals `'${item.name}'` instead of `(item) =\u003e item.name`\n* **Configuration over code** - Describe what you want, not how to build it\n* **Web standards over custom APIs** - Leverage existing browser capabilities\n\nThis approach creates more maintainable, serializable, and understandable code while preventing over-engineering.\n\n### Developer Experience\n\nDDOM prioritizes:\n\n* **Familiar syntax** - Works like normal JavaScript\n* **Minimal novelty** - Standards-alignment over DSLs\n* **Least power** - Simplest solution that solves the problem\n* **Predictable behavior** - Standard property access patterns\n* **Debugging friendly** - Properties work in dev tools\n\n### Standards Alignment\n\n* Uses standard JavaScript object patterns\n* Template literals follow ES6 specifications\n* DOM property names and behavior match web standards\n* Custom elements align with Web Components specs\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Artificial Intelligence Notice\n\nThe contents of this repository have been developed with support from one or more generative artificial intelligence solutions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeclarative-dom%2Fddom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeclarative-dom%2Fddom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeclarative-dom%2Fddom/lists"}