{"id":20095207,"url":"https://github.com/bcosca/undertone","last_synced_at":"2025-05-06T05:31:10.020Z","repository":{"id":57385938,"uuid":"213585220","full_name":"bcosca/undertone","owner":"bcosca","description":"A Custom Elements (v1-spec) code organizer/compiler that tones down Javascript by emphasizing the structure and beauty of HTML","archived":false,"fork":false,"pushed_at":"2019-10-16T15:43:41.000Z","size":46,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-22T18:54:25.574Z","etag":null,"topics":["custom-elements-v1","declarative","es6-javascript","frontend","html5","javascript","undertone","web-components"],"latest_commit_sha":null,"homepage":null,"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/bcosca.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}},"created_at":"2019-10-08T08:12:14.000Z","updated_at":"2025-02-05T21:04:33.000Z","dependencies_parsed_at":"2022-09-14T20:52:30.890Z","dependency_job_id":null,"html_url":"https://github.com/bcosca/undertone","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bcosca%2Fundertone","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bcosca%2Fundertone/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bcosca%2Fundertone/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bcosca%2Fundertone/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bcosca","download_url":"https://codeload.github.com/bcosca/undertone/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252628820,"owners_count":21779088,"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","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":["custom-elements-v1","declarative","es6-javascript","frontend","html5","javascript","undertone","web-components"],"created_at":"2024-11-13T16:54:25.694Z","updated_at":"2025-05-06T05:31:09.676Z","avatar_url":"https://github.com/bcosca.png","language":"JavaScript","funding_links":["https://www.patreon.com/bePatron?u=20757210"],"categories":[],"sub_categories":[],"readme":"![Logo](https://github.com/bcosca/undertone/raw/master/logo.png)\n\nA [Custom Elements (v1-spec)](https://html.spec.whatwg.org/multipage/custom-elements.html) code organizer/compiler that tones down Javascript by emphasizing the structure and beauty of HTML.\n\n![GitHub package.json version](https://img.shields.io/github/package-json/v/bcosca/undertone) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](//standardjs.com) ![GitHub](https://img.shields.io/github/license/bcosca/undertone?color=blue)\n\n## Installation\n\n```shell\nnpm install --save-dev undertone\n```\n\n## How it works\n\nA custom element definition is nothing more than a self-contained HTML fragment with a custom element tag name that wraps its content, appearance and behavior. Definitions may contain any mix of text, HTML tags, including **`\u003cscript\u003e`**, **`\u003cstyle\u003e`**, **`\u003clink rel=\"stylesheet\"\u003e`** and **`\u003cslot\u003e`** tags.\n\n```\n\u003cplanet-mars\u003e\n  \u003cdiv\u003e${message}\u003c/div\u003e\n  \u003cscript\u003e\n    const message = 'Hello, world'\n  \u003c/script\u003e\n\u003c/planet-mars\u003e\n```\n\nThe compiler rebuilds the above structure representing the source code into a reusable Web component that works on any browser aligned with the ES6+ Custom Elements v1 language specs. The following generated code is an approximate translation:\n\n```\ncustomElements.define(\n  'planet-mars',\n  class extends HTMLElement {\n    constructor () {\n      super()\n      this.attachShadow({ mode: 'open' })\n    }\n    connectedCallback () {\n      const message = 'Hello, world'\n      let template = document.createElement('template')\n      template.innerHTML = `\u003cdiv\u003e${message}\u003c/div\u003e`\n      this.shadowRoot.appendChild(template.content)\n    }\n  }\n)\n```\n\nModern and fairly recent mobile and desktop browsers already have native Javascript ES6+ with Custom Elements capability, so polyfills to augment browser capabilities are unnecessary.\n\nSee which browsers support these features here:\n\n* [Javascript ES6+](https://caniuse.com/#search=ecmascript%202015)\n* [Custom Elements v1 language spec](https://caniuse.com/#feat=custom-elementsv1)\n\n## Features\n\n* Clear separation of form and function (content and behavior)\n* Simple format, declarative style: regular HTML5 sprinkled with CSS and Javascript\n* Easy to read: IDEs/editors already provide syntax highlighting for HTML\n* Generation of source maps to help with debugging\n\n## Putting it all together\n\nBasic document layout\n\n```\n./index.html\n\n\u003c!doctype html\u003e\n\u003chtml lang=\"en\"\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"utf-8\"\u003e\n    \u003cmeta http-equiv=\"x-ua-compatible\" content=\"ie=edge\"\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\"\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cx-foo\u003e\u003c/x-foo\u003e\n    \u003cscript src=\"js/app.js\"\u003e\u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nExample of a component\n\n```\n./html/x-foo.html\n\n\u003cx-foo\u003e\n  \u003cscript\u003e\n    console.log('Hello, world')\n  \u003c/script\u003e\n\u003c/x-foo\u003e\n```\n\nUse the command line tool to compile the component's source code to Javascript and save it to a file.\n\n```\nundertone ./html/x-foo.html -m inline -o ./js/app.js\n```\n\n\n## CLI\n\n```plaintext\nUSAGE: undertone [options] input-file ...\n\nBasic options:\n  -o, --output output-file  Target file (default: stdout)\n  -m, --map sourcemap-file  Generate source map (default: none)\n  -h, --help                Usage information\n\nAdvanced options (see documentation):\n  -c, --class class-file    DOM interface (default: undertone.js)\n  -g, --global              Enable global styles (default: scoped styles)\n  --no-preamble             Exclude preamble from output\n```\n\n### Options\n\n**`-o, --output output-file`**\n\nThe name of the generated Javascript file. If omitted, output is sent to **`stdout`**.\n\n**`-m, --map sourcemap-file`**\n\nThe name of the source map linked to the output file.\n\n* If the special **`inline`** keyword is supplied as an argument, the source map is generated along with the output.\n\n* Any other value supplied as argument to **`--map`** is treated as a file name.\n\n**`-c, --class class-file`**\n\nThe name of the base class that the compiled components will inherit from. By default, **`undertone.js`** generates custom elements that extend the `Undertone` class (contained in `src/undertone.js`). This DOM interface is responsible for rewiring the v1-spec to the [custom element definitions](#how-it-works). Review the source to gain more info before attempting to override it.\n\n**`-g, --global`**\n\nCustom elements do not normally inherit from the document's global stylesheets and style tags by default. Enable this flag to override this behavior.\n\n**`--no-preamble`**\n\nDepending on your build process, you may choose to defer or not include the `Undertone` class in the generated output. Enable this flag to inform the compiler that `src/undertone.js` should not be prepended to the output.\n\n\n## Component structure and definition\n\nA custom element definition must have a [valid](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name) tag name and it must be declared as a top level HTML element in the source file.\n\n#### Example\n\n```\n\u003cmy-widget\u003e\n  \u003c!-- this is a valid custom element definition --\u003e\n\u003c/my-widget\u003e\n```\n\n\u003e You may have several top level custom elements in one file, although one custom element definition per file is recommended.\n\n#### Anatomy of a component\n\nA custom element definition may contain a mix of HTML and secondary level **`\u003cstyle\u003e`**, **`\u003clink rel=\"stylesheet\"\u003e`** and **`\u003cscript\u003e`** tags that work in harmony to define its overall appearance and behavior. Everything inside a custom element definition is optional.\n\n```\n\u003cmy-widget\u003e\n  \u003clink rel=\"stylesheet\" href=\"stylesheet.css\"\u003e\n  \u003cstyle light-dom\u003e\n    /* Light DOM styles */\n  \u003c/style\u003e\n  \u003cstyle\u003e\n    /* Shadow root styles */\n  \u003c/style\u003e\n  \u003c!--\n    HTML tags, text and ES6+ literals\n  --\u003e\n  \u003cscript src=\"script.js\"\u003e\u003c/script\u003e\n  \u003cscript\u003e\n    /* Component-specific Javascript code */\n  \u003c/script\u003e\n\u003c/my-widget\u003e\n```\n\nCustom elements may employ an external stylesheet by simply inserting a **`\u003clink\u003e`** tag in the element definition. Styles declared in external stylesheets affect the light DOM.\n\nStyles defined within **`\u003cstyle\u003e`** tags affect the corresponding CSS properties of either light DOM or shadow root elements, depending on whether the **`light-dom`** attribute is present or not.\n\n**`\u003cslot\u003e`** tags that serve as placeholders for external content may be employed anywhere within the custom element's markup. More information about **`\u003cslot\u003e`** elements is available [here](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Slot).\n\nThe presence of an **`observe`** attribute in a custom element declaration tag (e.g. **`\u003cmy-widget observe=\"size\"\u003e`**) provides a hint to the compiler that the attribute specified should be observed throughout the life span of the element and any change to its value triggers a corresponding component update.\n\nMultiple attributes can be observed by specifying a comma- or space-delimited string representing the attributes to be observed.\n\n\n## Undertone class\n\nCode generated by the compiler automatically inherits from the [Undertone class](src/undertone.js) by default. This Javascript class serves as a thin wrapper around the standard **`HTMLElement`** that all custom elements derive from. At compilation time, this class is inserted into the aggregated output as a preamble to satisfy code dependencies.\n\nUnder the hood, this little class provides the following services:\n\n* A component caching mechanism to optimize rendering performance\n* Automatic synchronization of element attributes and properties\n* Redirection of basic v1-spec lifecycle callbacks to synthetic events that can be handled by code inside the component's **`\u003cscript\u003e`** tags\n* A **`refs`** object for obtaining references to shadow DOM elements\n* A compile-time option for scoped or global styles\n\n\u003e The Undertone class is a minimal implementation of a v1-spec DOM interface that provides these services. Being neither a platform nor a framework, it does not enforce any rigid methodology. The philosophy behind Undertone: No overengineered and no opinionated stuff.\n\n\n### Events\n\nUndertone events are directly mapped to basic [v1-spec](https://html.spec.whatwg.org/multipage/custom-elements.html) lifecycle callbacks of custom elements. These synthetic events are prefixed with a colon `(:)` to make them easily identifiable.\n\n#### :connect\n\n```\nthis.addEventListener(':connect', () =\u003e {\n})\n```\n\nThe **`:connect`** event handler is called each time the custom element is inserted into the DOM. Its primary job is to prepare the element for subsequent display. When its task is done, the Undertone class assembles the markup inside the component definition and renders the content in the [shadow DOM](https://developers.google.com/web/fundamentals/web-components/shadowdom).\n\nThis event fires when the browser's native **`connectedCallBack`** of a custom element is triggered.\n\n#### :ready\n\n```\nthis.addEventListener(':ready', () =\u003e {\n})\n```\n\nThis event occurs when the Web browser is done rendering the component's HTML content. The event handler is responsible for visual tweaks made by changing element properties that are not available before rendering.\n\n#### :update\n\n```\nthis.addEventListener(':update', () =\u003e {\n})\n```\n\nThe **`:update`** event is wired to the native **`attributeChangedCallBack`** method. It fires when the custom element's observed attributes change during its lifecycle.\n\n#### :disconnect\n\n```\nthis.addEventListener(':disconnect', () =\u003e {\n})\n```\n\nThe **`:disconnect`** event maps to the custom element's native **`disconnectedCallBack`** method.\n\n\n### Compiler directives\n\n#### %if\n\nThe **`%if`** directive renders a block if the specified condition is true.\n\n```\n\u003cmy-bar\u003e\n  %if (age \u003e= 18):\n  \u003cspan class=\"green\"\u003eHave a drink\u003c/span\u003e\n  %end\n  \u003cscript\u003e\n    const age = 18\n  \u003c/script\u003e\n\u003c/my-bar\u003e\n```\n\nAn optional **`%else`** directive may be inserted to provide a fallback mechanism if needed.\n\n```\n\u003cmy-bar\u003e\n  %if (age \u003e 18):\n  \u003cspan class=\"green\"\u003eHave a drink\u003c/span\u003e\n  %else if (age === 18):\n  \u003cspan class=\"orange\"\u003eID please\u003c/span\u003e\n  %else:\n  \u003cspan class=\"red\"\u003eGo away!\u003c/span\u003e\n  %end\n  \u003cscript\u003e\n    const age = 18\n  \u003c/script\u003e\n\u003c/my-bar\u003e\n```\n\n#### %each\n\nUse the **`each`** statement to iterate over a list.\n\n```\n\u003cmy-list\u003e\n  \u003cul\u003e\n    %each (item in items):\n    \u003cli\u003e${item}\u003c/li\u003e\n    %end\n  \u003c/ul\u003e\n  \u003cscript\u003e\n    const items = [ 'Budweiser', 'Heineken', 'Skol' ]\n  \u003c/script\u003e\n\u003c/my-list\u003e\n```\n\n\n## Contributing\n\nBefore you submit a pull request, please check the code base and past issues to avoid duplication.\n\nIn addition to improving the project, refactoring code, and implementing features, this project welcomes the following types of contributions:\n\n* Ideas: Participate in an issue thread or start your own to have your voice heard\n* Writing: Contribute your expertise in an area by helping expand the documentation\n* Copy editing: Fix typos, clarify language, and generally improve the quality of the content\n* Formatting: Help keep content easy to read with consistent formatting\n\n#### Testing your code\n\nPlease run **`npm test`** to ensure all tests are passing before submitting a pull request - unless you're creating a failing test to increase test coverage or show a problem.\n\n\n## Support\n\nHelp support further development and maintenance of the **undertone.js** project.\n\n[![Become a Patron!](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/bePatron?u=20757210)\n\n\n## License\n\n**undertone.js** is released under the [MIT License](./LICENSE.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbcosca%2Fundertone","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbcosca%2Fundertone","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbcosca%2Fundertone/lists"}