{"id":30821113,"url":"https://github.com/tram-one/tram-deco","last_synced_at":"2025-09-06T10:15:03.923Z","repository":{"id":220464607,"uuid":"751676601","full_name":"Tram-One/tram-deco","owner":"Tram-One","description":"🏢 Declarative Custom Elements using native Web Component APIs and specs.","archived":false,"fork":false,"pushed_at":"2025-04-13T19:42:04.000Z","size":399,"stargazers_count":12,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-13T20:35:27.552Z","etag":null,"topics":["webcomponents"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/Tram-One.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}},"created_at":"2024-02-02T04:43:32.000Z","updated_at":"2025-04-13T19:42:07.000Z","dependencies_parsed_at":"2024-03-29T23:36:46.898Z","dependency_job_id":"40ee81df-2d0b-4a5e-951d-85aef98d224b","html_url":"https://github.com/Tram-One/tram-deco","commit_stats":null,"previous_names":["jrjurman/nite-lite","tram-one/nite-lite"],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/Tram-One/tram-deco","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tram-One%2Ftram-deco","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tram-One%2Ftram-deco/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tram-One%2Ftram-deco/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tram-One%2Ftram-deco/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Tram-One","download_url":"https://codeload.github.com/Tram-One/tram-deco/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tram-One%2Ftram-deco/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273888684,"owners_count":25185929,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-06T02:00:13.247Z","response_time":2576,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["webcomponents"],"created_at":"2025-09-06T10:15:00.489Z","updated_at":"2025-09-06T10:15:03.901Z","avatar_url":"https://github.com/Tram-One.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tram-Deco\n\n\u003cimg src=\"./logo.png\" width=\"200px\" alt=\"Tram-Deco logo, minimalistic icon that looks like the front of a red trolly car, with a yellow tinted window and three headlights, made of simple geometric shapes\"\u003e\n\n_Declarative Custom Elements using native Web Component APIs and specs._\n\nTram-Deco provides a more elegant interface for building Web Components, that remains as close as possible to the\nexisting browser APIs. Tram-Deco is a experiment to understand what a declarative interface for building Web Components\nmight look like, without the addition of APIs that don't already exist.\n\n\u003c!-- prettier-ignore --\u003e\n\u003cimg src=\"https://img.shields.io/npm/dm/tram-deco.svg\" alt=\"Downloads\"\u003e \u003cimg src=\"https://img.shields.io/npm/v/tram-deco.svg\" alt=\"Version\"\u003e\n\u003ca href=\"https://unpkg.com/tram-deco@6/tram-deco.min.js\"\u003e\u003cimg src=\"https://img.shields.io/badge/gzip-722B-006369.svg?style=flat\" alt=\"Gzipped Size\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/Tram-One/tram-deco/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/npm/l/tram-deco.svg\" alt=\"License\"\u003e\u003c/a\u003e\n\u003ca href=\"https://discord.gg/dpBXAQC\"\u003e\u003cimg src=\"https://img.shields.io/badge/discord-join-5865F2.svg?style=flat\" alt=\"Join Discord\"\u003e\u003c/a\u003e\n\u003ca href=\"https://codepen.io/pen?template=JjzQmaL\"\u003e\u003cimg src=\"https://img.shields.io/badge/codepen-template-DD6369.svg?style=flat\" alt=\"Codepen Template\"\u003e\u003c/a\u003e\n\u003ca href=\"https://chatgpt.com/g/g-wTNI3WZS6-tram-deco\"\u003e\u003cimg src=\"https://img.shields.io/badge/gpt-tram--deco-FFCC49.svg?style=flat\" alt=\"Codepen Template\"\u003e\u003c/a\u003e\n\n## Example\n\n```html\n\u003c!-- include the Tram-Deco library --\u003e\n\u003cscript src=\"https://unpkg.com/tram-deco@6\"\u003e\u003c/script\u003e\n\n\u003c!-- define some web components --\u003e\n\u003ctemplate id=\"definitions\"\u003e\n  \u003c!-- definition for a custom header tag --\u003e\n  \u003cheader-anchor\u003e\n    \u003c!-- declarative shadow dom for the insides --\u003e\n    \u003ctemplate shadowrootmode=\"open\"\u003e\n      \u003c!-- styles, for just this element --\u003e\n      \u003cstyle\u003e\n        a {\n          color: inherit;\n        }\n        ::slotted(*)::before {\n          content: '# ';\n          opacity: 0.3;\n        }\n        ::slotted(*:hover)::before {\n          opacity: 0.7;\n        }\n        a:not(:hover) {\n          text-decoration: none;\n        }\n      \u003c/style\u003e\n\n      \u003c!-- dom, to show on the page --\u003e\n      \u003ca\u003e\u003cslot\u003e\u003c/slot\u003e\u003c/a\u003e\n    \u003c/template\u003e\n\n    \u003c!-- scripts, that let you define lifecycle and custom methods --\u003e\n    \u003cscript td-method=\"constructor\"\u003e\n      new MutationObserver(() =\u003e {\n        this.decorateHeader();\n      }).observe(this, { childList: true });\n    \u003c/script\u003e\n    \u003cscript td-method=\"decorateHeader\"\u003e\n      this.id = this.textContent.trim().toLowerCase().replace(/\\s+/g, '-');\n\n      const link = this.shadowRoot.querySelector('a');\n      link.href = `#${this.id}`;\n    \u003c/script\u003e\n  \u003c/header-anchor\u003e\n\u003c/template\u003e\n\n\u003c!-- process the template to generate new definitions --\u003e\n\u003cscript\u003e\n  TramDeco.processTemplate(definitions);\n\u003c/script\u003e\n\n\u003c!-- use our new element! --\u003e\n\u003cheader-anchor\u003e\n  \u003ch1\u003eIntroduction\u003c/h1\u003e\n\u003c/header-anchor\u003e\nThis is some introductory content\n\u003cheader-anchor\u003e\n  \u003ch2\u003eMore Details\u003c/h2\u003e\n\u003c/header-anchor\u003e\nIf you want to read more, checkout the README.\n```\n\n[Live on Codepen](https://codepen.io/JRJurman/pen/RwXPqEe)\n\n## How to use\n\nThere are two ways to use Tram-Deco in your project - you can either have component definitions in your served HTML\ntemplate (in template tags), or you can export the components as part of a build step to be imported with script tags.\n\n### Template Component Definitions\n\nIf you don't want a build step, or are just building components for a dedicated static page, you can do the following to\nwrite component definitions in your main template:\n\nInclude the Tram-Deco library (you can point to either `tram-deco.js` or `tram-deco.min.js`)\n\n```html\n\u003cscript src=\"https://unpkg.com/tram-deco@6/tram-deco.min.js\"\u003e\u003c/script\u003e\n```\n\nCreate a template tag with your component definitions, and then use Tram-Deco to process that template\n\n```html\n\u003ctemplate id=\"myDefinitions\"\u003e\n  \u003c!-- component definitions --\u003e\n\u003c/template\u003e\n\n\u003cscript\u003e\n  TramDeco.processTemplate(myDefinitions);\n\u003c/script\u003e\n```\n\n### Export JS Definition\n\nIf you want to export your component definition, to be used in other projects, or to organize the components in\ndifferent files, you can do the following:\n\nCreate a component definition file (`.html`) - this can include as many top-level component definitions as you'd like.\n\n```html\n\u003c!-- my-counter.html --\u003e\n\u003cmy-counter\u003e\n  \u003ctemplate shadowrootmode=\"open\"\u003e\n    \u003c!-- ... --\u003e\n  \u003c/template\u003e\n\u003c/my-counter\u003e\n```\n\nRun the following command in the command line, or as part of a build step:\n\n```sh\nnpx tram-deco export-components my-counter.html\n```\n\nThis will create a JS file that can be imported using a standard script tag:\n\n```html\n\u003cscript src=\"./my-counter.js\"\u003e\n```\n\n## API\n\n### JS API\n\n\u003cdl\u003e\n\u003cdt\u003e\u003ccode\u003eTramDeco.processTemplate(templateTag)\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003e\n\nThe `processTemplate` function takes in a single template tag with several component definitions, and builds Web\nComponent definitions for all of them in the global custom elements registry.\n\n\u003c/dd\u003e\n\n\u003cdt\u003e\u003ccode\u003eTramDeco.define(elementDefinition)\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003e\n\nThe `define` function takes in a single element definition and builds a Web Component definition for the global custom\nelements registry. It is the underlying method used in `processTemplate`, and probably does not need to be called in\nisolation.\n\n\u003c/dd\u003e\n\u003c/dl\u003e\n\n### Component API\n\nThese attributes can be used to provide or inherit logic for different life cycle events of your component. They follow\nthe standard API for Web Components.\n\n\u003cdl\u003e\n\u003cdt\u003e\u003ccode\u003etd-extends=\"tag-name\"\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003e\n\nAttribute to be used on the top-level definition tag. The class associated with the `tag-name` will be used as a parent\nclass when building this Web Component definition. That tag must be already registered in the `customElements` registry.\n\n\u003c/dd\u003e\n\u003cdt\u003e\u003ccode\u003etd-property=\"propertyName\"\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003e\n\nAttribute to be used on a `script` tag in your component definition. The assigned property name will be attached to the\nelement as a static property, and can be useful for adding `observedAttributes`, `formAssociated`, `disableInternals`,\nor `disableShadow`. You can also define custom static properties for your element.\n\n\u003c/dd\u003e\n\u003cdt\u003e\u003ccode\u003etd-method=\"methodName\"\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003e\n\nAttribute to be used on a `script` tag in your component definition. The assigned method name will be attached to the\nelement, and can be useful for adding to the `constructor`, or setting other Web Component APIs, such as\n`connectedCallback`, `disconnectedCallback`, `adoptedCallback`, or `attributeChangedCallback`. You can also define\ncustom methods for your element.\n\n\u003e [!tip]\n\u003e\n\u003e If you are using `attributeChangedCallback` you can access the parameters (`name`, `oldValue`, and `newValue`) using\n\u003e the `arguments` keyword. See the example below to see how this works!\n\n\u003c/dd\u003e\n\u003c/dl\u003e\n\n#### Example Using Component API\n\n```html\n\u003cscript src=\"https://unpkg.com/tram-deco@6\"\u003e\u003c/script\u003e\n\n\u003ctemplate id=\"myCounter\"\u003e\n  \u003cmy-counter\u003e\n    \u003c!-- observed attributes, to watch for attribute changes on count --\u003e\n    \u003cscript td-property=\"observedAttributes\"\u003e\n      ['count'];\n    \u003c/script\u003e\n\n    \u003ctemplate shadowrootmode=\"open\" shadowrootdelegatesfocus\u003e\n      \u003cbutton\u003e\u003cslot\u003eCounter\u003c/slot\u003e: \u003cspan\u003e0\u003c/span\u003e\u003c/button\u003e\n    \u003c/template\u003e\n\n    \u003c!-- when we mount this component, add an event listener --\u003e\n    \u003cscript td-method=\"connectedCallback\"\u003e\n      const button = this.shadowRoot.querySelector('button');\n      button.addEventListener('click', (event) =\u003e {\n        const newCount = parseInt(this.getAttribute('count')) + 1;\n        this.setAttribute('count', newCount);\n      });\n    \u003c/script\u003e\n\n    \u003c!-- when the count updates, update the template --\u003e\n    \u003cscript td-method=\"attributeChangedCallback\"\u003e\n      const [name, oldValue, newValue] = arguments;\n      if (name === 'count') {\n        const span = this.shadowRoot.querySelector('span');\n        span.textContent = newValue;\n      }\n    \u003c/script\u003e\n  \u003c/my-counter\u003e\n\u003c/template\u003e\n\n\u003cscript\u003e\n  TramDeco.processTemplate(myCounter);\n\u003c/script\u003e\n\n\u003cmy-counter count=\"0\"\u003eTram-Deco\u003c/my-counter\u003e\n```\n\n[Live on Codepen](https://codepen.io/JRJurman/pen/RwmbMmg)\n\n## Motivation\n\nTram-Deco was written to showcase a potential implementation of Declarative Custom Elements that could be trivially\nadopted by browser implementers. While many alternatives exist, most include new custom APIs, behavior, or syntax that\nwould necessitate discussions, deliberations, and implementation before making progress on the true goal.\n\nTram-Deco strives to be as close to existing APIs as possible, so that the path to browser implementation is as direct\nas possible. While many libraries exist to make Web-Component creation easier and more elegant, this library exclusively\nhighlights how we can leverage existing APIs to get to Declarative Custom Elements.\n\n## contributions / discussions\n\nIf you think this is useful or interesting, I'd love to hear your thoughts! Feel free to\n[reach out to me on mastodon](https://fosstodon.org/@jrjurman), or join the\n[Tram-One discord](https://discord.gg/dpBXAQC).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftram-one%2Ftram-deco","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftram-one%2Ftram-deco","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftram-one%2Ftram-deco/lists"}