{"id":17956330,"url":"https://github.com/o0101/good.html","last_synced_at":"2025-08-24T14:22:59.865Z","repository":{"id":71110053,"uuid":"404716300","full_name":"o0101/good.html","owner":"o0101","description":"💎 Good.HTML. A nice framework without the bad stuff. Lots of custom elements, and nice templates. Good. HTML","archived":false,"fork":false,"pushed_at":"2024-07-29T15:05:05.000Z","size":2098,"stargazers_count":22,"open_issues_count":14,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-15T08:05:48.743Z","etag":null,"topics":["async","custom-elements","custom-elements-v1","html","html-css-javascript","javascript","reactive","self-closing","shadow-dom","template-engine","templates","vanilla-js","web-components","web-components-library","web-framework","webcomponents"],"latest_commit_sha":null,"homepage":"https://o0101.github.io/good.html/","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/o0101.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}},"created_at":"2021-09-09T12:38:25.000Z","updated_at":"2025-01-01T13:46:59.000Z","dependencies_parsed_at":"2023-03-11T09:54:53.813Z","dependency_job_id":"b4c67689-32eb-4d16-8425-eeb6cbf7d075","html_url":"https://github.com/o0101/good.html","commit_stats":{"total_commits":612,"total_committers":3,"mean_commits":204.0,"dds":0.1568627450980392,"last_synced_commit":"bba95fe9ee116a7f604ab30622c148f5fe35bf49"},"previous_names":["i5ik/bang","o0101/good.html"],"tags_count":124,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/o0101%2Fgood.html","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/o0101%2Fgood.html/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/o0101%2Fgood.html/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/o0101%2Fgood.html/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/o0101","download_url":"https://codeload.github.com/o0101/good.html/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245066675,"owners_count":20555427,"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":["async","custom-elements","custom-elements-v1","html","html-css-javascript","javascript","reactive","self-closing","shadow-dom","template-engine","templates","vanilla-js","web-components","web-components-library","web-framework","webcomponents"],"created_at":"2024-10-29T10:37:21.465Z","updated_at":"2025-03-25T02:31:17.903Z","avatar_url":"https://github.com/o0101.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=center\u003e\n  \u003cimg width=80% \n    src=https://github.com/crisdosyago/good.html/raw/main/.github/BANG!%20logo%20mediumseagreen-mincream.png\n  \u003e\n  \u003cimg width=80% \n    src=https://user-images.githubusercontent.com/22254235/135863650-a0a44bbd-414e-4606-aaf4-64e43f5abcc9.PNG\n  \u003e\n\u003c/p\u003e\n\n# ***[:gem: BANG!](https://github.com/crisdosyago/dar.Knall.Gerust/)*** ![npm](https://img.shields.io/npm/v/bang.html?color=turquoise) ![npm](https://img.shields.io/npm/dt/bang.html) [![visitors+++](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fcrisdosyago%2Fgood.html\u0026count_bg=%2379C83D\u0026title_bg=%23555555\u0026icon=\u0026icon_color=%23E7E7E7\u0026title=%28today%2Ftotal%29%20visitors%2B%2B%2B%20since%20Oct%208%202021\u0026edge_flat=false)](https://hits.seeyoufarm.com)\n\n# News\n\nBang is now know as **GOOD**\n\nHolen Sie es hier, [or on NPM @bang.html](https://npmjs.com/package/bang.html)\n\n-----------------------------\n## Table of Contents\n\n  * [Demos](#demos)\n  * [Introducing: self-closing tags for Web Components](#introducing-self-closing-tags-for-web-components)\n  * [Regular tags](#regular-tags)\n  * [Templates, and with async values](#templates-and-with-async-values)\n  * [Minimal DOM diffing with minimal granular updates](#minimal-dom-diffing-with-minimal-granular-updates)\n  * [Lazy loading](#lazy-loading)\n  * [More Goodies](#more-goodies)\n  * [More info on templating ](#more-info-on-templating)\n    + [A note on scope](#a-note-on-scope)\n    + [The `F` tag function](#the-f-tag-function)\n  * [The big picture: defining custom element bang components: markup, script and style](#the-big-picture-defining-custom-element-bang-components-markup-script-and-style)\n  * [Get started in 5 simple steps](#get-started-in-5-simple-steps)\n    + [Step 1: Make your directory structure:](#step-1-make-your-directory-structure)\n    + [Step 2: Use a custom element.](#step-2-use-a-custom-element)\n    + [Step 3: Add markup](#step-3-add-markup)\n    + [Step 4: Make some variable and state](#step-4-make-some-variable-and-state)\n    + [Step 5: Make it interactive](#step-5-make-it-interactive)\n  * [Slots and variables](#slots-and-variables)\n    + [1. Save the state to the store](#1-save-the-state-to-the-store)\n    + [2. Pass state to components](#2-pass-state-to-components)\n    + [3. Template the properties](#3-template-the-properties)\n  * [The `state=` attribute, nested objects and template replacement slots](#the-state-attribute-nested-objects-and-template-replacement-slots)\n  * [Async templating](#async-templating)\n  * [More information](#more-information)\n- [Q\u0026A ](#qa)\n    + [Why use *BANG!* and not just a `\u003ccustom-self-closing-tag /\u003e` or a single `\u003ccustom-tag\u003e`? ](#why-use-bang-and-not-just-a-custom-self-closing-tag-or-a-single-custom-tag)\n    + [How do I add event handlers or event listeners to elements?](#how-do-i-add-event-handlers-or-event-listeners-to-elements)\n      - [A second slightly different syntax to add event handlers not currently supported](#a-second-slightly-different-syntax-to-add-event-handlers-not-currently-supported)\n    + [What are some gotchas or syntax I need to beware of?](#what-are-some-gotchas-or-syntax-i-need-to-beware-of)\n      - [`use(\u003cname: string\u003e)`](#usename-string)\n      - [Self-closing syntax](#self-closing-syntax)\n      - [Component classes](#component-classes)\n      - [Top-level element](#top-level-element)\n      - [Config](#config)\n      - [Programmatic state-keys](#programmatic-state-keys)\n  * [Contributions](#contributions)\n  * [Roadmap](#roadmap)\n- [HTML ***but with a BANG!***](#html-but-with-a-bang)\n\n\n## Demos\n\n- [7GUIs](https://crisdosyago.github.io/good.html/7guis/)\n- [Spreadsheet Component (*a work in progress*)](https://crisdosyago.github.io/good.html/cellophane/)\n- [Simple Counter Demo](https://crisdosyago.github.io/good.html/ctr/)\n\nThe page in the Lighthouse shot above has over 10,000 DOM nodes, yet Lighthouse manages to love it, anyway. How is this possible? Mostly through the use of the `lazy` and `super lazy` attributes, particularly on the large 'Cells' spreadsheet component.\n\n***EXPLORE BANG! FOR KNOWLEDGE***\n\nThis is ***BANG!***, making a component:\n\n[`components/sg-counter/markup.html`](https://github.com/crisdosyago/good.html/blob/main/docs/7guis/components/sg-counter/markup.html)\n```jsx\n\u003csg-frame state=${_self}\u003e \n  \u003cbutton id=counter onclick=Increment\u003eCount\u003c/button\u003e\n  \u003clabel for=counter\u003e${count}\u003c/label\u003e\n\u003c/sg-frame\u003e\n```\n\n[`components/sg-counter/script.js`](https://github.com/crisdosyago/good.html/blob/main/docs/7guis/components/sg-counter/script.js):\n```jsx\nclass Counter extends Base {\n  Increment() {\n    const {state} = this;\n    state.count++;\n    this.state = state;\n  }\n}\n```\n\n[`components/sg-counter/style.css`](https://github.com/crisdosyago/good.html/blob/main/docs/7guis/components/sg-counter/style.css):\n```css\nlabel {\n  min-width: 4.5ch;\n  display: inline-block;\n  text-align: right;\n}\n```\n\nThis is ***BANG!***, using a component:\n\n`index.html`:\n```html\n\u003clink rel=stylesheet href=components/style.css\u003e\n\u003cscript src=//unpkg.com/bang.html\u003e\u003c/script\u003e\n\u003cscript\u003e\n  use('sg-frame');\n  use('sg-counter');\n\u003c/script\u003e\n\u003c!sg-counter lazy state=ctr /\u003e\n```\n\nYou need to fill out the `sg-frame` component. But that is all.\n\n\nThis is ***BANG!***\n\nFor more, the best way is just read the code for the 7GUIs implementation. But you could also  [read the intro](#intro) below. And finally here's a quick overview of features:\n\n- Minimal DOM diffs (lists, keyed components, attribute names, attribute values, text values) with no absolutely VDOM overhead, instead we use granular DOM-based updator functions.\n- JavaScript templating syntax. Forget a DSL, or \"pseudo-code\" with its own quirks and weird edge-cases, just use JavaScript to `${template}` in all the ``${values.map(v =\u003e F`\u003c!my-item state=${v}\u003e`)}`` you need.\n- Custom Elements. Simply put `use('my-item');` in your JavaScript, and fill out any of the `markup.html`, `style.css`, and `script.js` files in the `./components/my-item/` directory, that your `my-item` component needs.\n- Keyed components, with optional keys. Just supply a key to create an instance component that can have many different instantiations in the DOM, or no key to get a singleton component. Both of these are pinned to their DOM location and will automatically be updated when you update relevant state.\n- Re-rendering only the changes when you `setState`. You can also use the `this.state = ` convenience setter within a component that uses string key as the value of its `state=` attribute.\n- Simple, low learning curve. Uses the HTML, JavaScript and CSS you already know, just provides some enhancements. No DSLs or transpilers required.\n- `lazy` and `super lazy` loading options, initiated by those attributes on your component. Lazy components do not delay the load of any ancestor components, and super lazy components only begin loading after the rest of the page has finished loading first.\n- Async value resolution in templates. You can plop async values right into your templates, even lists of async values, arbitrarily nested, and ***BANG!*** will resolve them all.\n- Lighthouse and performance friendly: ![lighthouse is happy with BANG and 7GUIs](https://user-images.githubusercontent.com/22254235/135863650-a0a44bbd-414e-4606-aaf4-64e43f5abcc9.PNG)\n\n\n------------------------------------------------------------------\n\n\u003cp id=intro align=center\u003e\n  \u003cimg width=80% \n    src=https://github.com/crisdosyago/bang/raw/main/.github/BANG!%20logo%20mediumseagreen-mincream.png\n  \u003e\n\u003c/p\u003e\n\n# 🌱 *BANG!* *A zero-dependency, no build-step, no transpilation, JSX-free, good old fashioned HTML and JavaScript Custom Element library for the new age.* ![npm](https://img.shields.io/npm/v/bang.html?color=turquoise) ![npm](https://img.shields.io/npm/dt/bang.html) [![visitors+++](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fcrisdosyago%2Fgood.html\u0026count_bg=%2379C83D\u0026title_bg=%23555555\u0026icon=\u0026icon_color=%23E7E7E7\u0026title=%28today%2Ftotal%29%20visitors%2B%2B%2B%20since%20Oct%2027%202020\u0026edge_flat=false)](https://hits.seeyoufarm.com)\n\n***BANG!*** makes your UI work easy, and your syntax beautiful, by pairing **Web Components** with smooth template syntax, minimal DOM updates (*and without VDOM*), lazy loading, async values and **[empty elements](https://developer.mozilla.org/en-US/docs/Glossary/Empty_element)** (*a.k.a void elements / self-closing tags*).\n\n## Introducing: self-closing tags for Web Components\n\n**Void tags** have *finally* come to custom-elements\u0026mdash;*with a **BANG!***\n\n***BANG!*** is your library of choice for self-closing tags with Web Components:\n\n```js\n\u003c!app-header /\u003e\n\u003cdiv\u003e\n  \u003c!app-content /\u003e\n\u003c/div\u003e\n```\nThese self-closing tags are known as **bang-tags** (*web components with a **bang!***)\n\nThey're actually just ***valid*** HTML comments that ***BANG!*** converts into valid [Web Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components).\n \nLike HTML [void tags](https://developer.mozilla.org/en-US/docs/Glossary/Empty_element), when you use **bang-tags** you can omit the self-closing slash `/\u003e`. So, `\u003c!custom-el2\u003e` is also a valid void self-closing **bang-tag**. \n\n## Regular tags\n\n***BANG!*** also makes it easy to define and use regular custom elements.\n\n## Templates, and with async values\n\nNormally using web components you need to use the `\u003cslot\u003e\u003c/slot\u003e` tag to pull in values. In ***BANG!*** you can still use slots (they work just fine), but for simple values this can be too much HTML, and excessive typing when all you really want to say is, `${myValue}`. Now you can:\n\n```html\n\u003ch1\u003e${firstName} ${lastName}\u003c/h1\u003e\n```\n\n## Minimal DOM diffing with minimal granular updates\n\nMinimal diffing is all the rage these days. It ensures that you don't do more work than you need to do to reflect the changes in your state into the DOM. Other frameworks use concepts like VDOM, which has a large overhead, as well as a large amount of code. ***BANG!*** uses the high-tech granular, linear-time updating technology from [vanillaview](https://github.com/crisdosyago/vanillaview), but builds on it and adds improvements to make the updates even more minimal in the case of lists (only items inserted or delete are the ones that change). In all other cases, only the text, attribute names or values, that contain templated state values that have changed, are the things that change. \n\nIt really is optimally minimal. \n\n## Lazy loading\n\nJust add a `lazy` attribute to your component to make its parent components not depend on it being loaded, in order for them to load. The component will load when its ready, a `lazy` component will not hold up it's ancestor components.\n\n```js\n\u003c!hot-button lazy state=buy onclick=Purchase /\u003e\n```\n\n## More Goodies\n\nApart from self-closing tags, minimal granular updates  ***BANG!*** has numerous other special features that make it super productive for building interfaces. Read on to discover how ***BANG!*** makes UI work more productive. \n\nYou can jump in right away and get it on the npm: \n[npmjs.com/@bang.html](https://www.npmjs.com/package/bang.html)\n\n------\n\n***The problem of custom void tags in HTML has been solved. Hoo-ray!***\n\n\u003cp align=center\u003e\n  \u003cimg width=80% \n       src=https://github.com/crisdosyago/bang/raw/main/.github/BANG!%20logo%20tomato-whitesmoke%20(1).png\n  \u003e\n\u003c/p\u003e\n\n------\n\n## More info on templating \n\nIn fact, any JavaScript expression works fine, even async/await expressions and Promises:\n\n`/components/item-list/markup.html`:\n```html\n\u003cul\u003e${\n  items.map(async item =\u003e F`\n  \u003cli\u003e\n    \u003cspan class=name\u003e${item.name}\u003c/span\u003e\n    \u003cimg src=${img.imgSrc}\u003c/span\u003e\n    \u003cspan class=price\u003e${await item.getLatestPrice()}\u003c/span\u003e\n    \u003c!item-details state=${item.details} /\u003e\n  \u003c/li\u003e\n `)}\u003c/ul\u003e\n```\n\n***BANG!*** automatically handles and awaits Promises to resolve, so the `await item.getLatestPrice()`, will fetch the most up to date price from the server and print that value into the template, with ***BANG!*** handling all the details. You can even leave out the `await` keyword, because ***BANG!*** will know it's value a Promise and treat it the same way. \n\n### A note on scope\n\nVariables (like `items`, and `firstName` and `lastName`) in the above examples are *in scope* for ***BANG!*** when they are properties of the state object you pass to the component the markup appears in. \n\nSo, in the example above, you'd be able to access the `items` variable if you saved state for your `item-list` component, like so:\n\n```js\n  setState('Items', {\n    items: myItemList\n  });\n```\n\nThen passed it to `item-list` components using `state=`:\n\n```js\n\u003citem-list state=Items\u003e\u003c/item-list\u003e\n```\n\n*Also note:* that in the example above the `item-details` component is passed state as an object to its `state=` attribute. This is fine too, but the object passing syntax is only available inside a component's `markup.html` file.\n\n### The `F` tag function\n\nInside a template slot if you want to include markup you need to wrap it in a `F` template tag function, like: ``` ${F`\u003cmy-markup\u003e\u003c/my-markup\u003e`} ``` \n\nYou'll notice in the above `\u003citem-list\u003e` example, we printed `\u003cli\u003e` elements within a `.map` iterator function using a special template tag function, `F`. This function is necessary if you want your markup to get all the benefits of ***BANG!***. In fact, all markup in your `markup.html` files is *implicitly* wrapped in `F` by ***BANG!***, so if you create new markup inside \\'template backticks\\`, you need to wrap that template string in an `F` tag. `F` is just a standard template tag function that is always in scope for your `markup.html` files. \n\n## The big picture: defining custom element bang components: markup, script and style\n\n```jsx\n// somewhere deep in your markup:\n\n\u003cmy-el state=myState\u003e\n  \u003cmarquee\u003eMy Humps\u003c/marquee\u003e\n\u003c/my-el\u003e\n\n// elsewhere, in /my-el/markup.html\n// you define the Shadow DOM content:\n\n\u003cdialog class=modal ${isOpen ? 'open' : ''}\u003e\n  \u003cnav class=titlebar\u003e${message.title}\u003c/nav\u003e\n  \u003csection class=sparkles\u003e\n    \u003cdetails\u003e\n      \u003csummary\u003e\n        ${message.summary}\n        \u003cspan class=button\u003eMore\u003c/span\u003e\n      \u003c/summary\u003e    \n      \u003cslot\u003e\u003c/slot\u003e\n    \u003c/details\u003e\n    \u003cbutton class=close onclick=close\u003eOK\u003c/button\u003e\n  \u003c/section\u003e\n\u003c/aside\u003e\n\n// eslewhere still, you seek out control with scripting\n// in /my-el/script.js\n\nclass MyModal extends Base {\n  open(message) {\n    const state = getState('modalState');\n    state.open = true;\n    state.message = message;\n    setState('modalState', state);\n  }\n  \n  close(clickEvent) {\n    const state = getState('modalState');\n    state.open = false;\n    setState('modalState', state);\n  }\n}\n\n// meanwhile, across town in your /my-el/style.css you set the tone\n\ndialog.modal {\n  border: medium dashed var(--sparkle);\n}\n\nspan.button {\n  appearance: button;\n  /* ... flesh it out ... */\n}\n\n```\n\nAll these files are optional. You don't need to provide a `markup.html`, `script.js` or `style.css` file in your component directory, but if you do, they will be utilized.\n\nAlso if you define a top-level `style.css` file in your `/components` base directory, it will be automatically imported into all component's style files.\n\n## Get started in 5 simple steps\n\nFor this mini-tutorial we'll be building a simple greeter component:\n\n```js\n\u003c!warm-greeter /\u003e\n```\n\nFollow along with the below steps to learn how to create your very own greeter component. Or just [jump straight to a work demo](https://crisdosyago.github.io/BANG/) if you prefer to have something to play with.\n\nFirst, to get you setup for the tutorial, install the **BANG!** package from NPM: \n\n```sh\n$ npm i --save bang.html\n```\n\nAnd since we'll also be using [serve](https://npmjs.com/package/serve) to run a static development server, install that, too, using:\n\n```sh\n$ npm i --save-dev serve\n```\n\nNow, onto the tutorial!\n\n### Step 1: Make your directory structure:\n\n```\nmy-project/\n├── README.md\n├── package.json   \n└── public/\n    ├── index.html\n    └── components/\n        ├── config.js (optional)\n        ├── warm-greeter/\n        │   ├── markup.html (optional)\n        │   ├── style.css (optional)\n        │   └── script.js (optional)\n        └── greet-count/\n            ├── markup.html (optional)\n            ├── style.css (optional)\n            └── script.js (optional)\n...\n```\n\nNote that each component lives in sub-folder under the components directory. Serve the components directory from your site root at `/components`. \n\nEach component is defined by 3 files, all optional:\n\n- markup.html: the shadow DOM content\n- style.css: the scoped styles applied. Note that [standard Web Components CSS pseudo-classes](https://developer.mozilla.org/en-US/docs/Web/Web_Components#:~:text=built-in%20element.-,CSS%20pseudo-classes,-Pseudo-classes%20relating) work here. \n- script.js: the class extension extending the default base class. You'll learn about the default base class of the component in a second.\n\n### Step 2: Use a custom element.\n\nCopy the below into your `public/index.html` file:\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003cscript type=application/javascript src=https://unpkg.com/bang.html\u003e\u003c/script\u003e\n\u003clink rel=stylesheet href=https://unpkg.com/bang.html/src/style.css\u003e\n\u003cscript\u003e\n  use('warm-greeter');\n\u003c/script\u003e\n\u003c!warm-greeter\u003e\n```\n\nStart your development server:\n\n```sh\n$ npx serve -p 8080 public/\n```\n\nNow visit your server in a web browser. You should just see a blank page. Open DevTools and see that:\n\n```js\n\u003c!warm-greeter /\u003e\n```\n\nhas become:\n\n```js\n\u003cwarm-greeter\u003e\u003c/warm-greeter\u003e\n```\n\nA valid Web Component, that you defined using a void self-closing tag. \n\nNow let's flesh out your component and show some markup and styles by adding some content to the component directory.\n\n### Step 3: Add markup\n\nFirst we're going to add some **templated markup** to the `markup.html` files of the two components we'll use.\n\nGo ahead and paste the following HTML into `public/components/warm-greeter/markup.html`:\n\n```html\n\u003ch1\u003eHello ${name}\u003c/h1\u003e\n\u003cp\u003eWe are very pleased to meet you \u003cgreet-count state=${greetCounts}\u003ehappy\u003c/greet-count\u003e times\u003c/p\u003e\n\u003cbutton onclick=Regreet\u003eRegreet!\u003c/button\u003e\n```\n\nAnd also add the following markup to `public/components/greet-count/markup.html`:\n\n```html\n\u003cspan class=count\u003e${value}\u003c/span\u003e\n\u003cslot\u003e\u003c/slot\u003e\n```\n\nYou'll notice that the markup contains the sequences `${...}` above. These sequences are **template replacement slots** which are how you display your  components variables and state. \n\nThese are different to [Web Components slot elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot) which you can also see we use. The differences are that **web component slot elements** pull in content you put between the start and end tags of your component, but **template replacement slots** pull in variables and state from the component itself. \n\nYou'll learn more about component variables and state in the next step of this tutorial.\n\n### Step 4: Make some variable and state\n\nGo back to your `public/index.html` file and change the code in the `\u003cscript\u003e` tag and the greeter as follows.\n\nUpdate the `\u003cscript\u003e` tag content to this:\n\n```js\n  use('warm-greeter');\n  use('greet-count');\n  setState('MyState', {\n    name: 'Uncle Bob',\n    greetCounts: {\n      value: 1\n    }\n  });\n```\n\nAnd change this:\n\n```js\n\u003c!warm-greeter /\u003e\n```\n\nTo this:\n\n```js\n\u003c!warm-greeter state=MyState /\u003e\n```\n\nNow reload the development page in your browser and you should be able to see your greeter taking shape, display a greeting to Uncle Bob, a count and a button. \n\nIf you open up the DevTools Elements tag and inspect the warm-greeter tag you probably noticed that it has a Shadow Root that is now hosting some content.\n\n### Step 5: Make it interactive\n\nIn this step, you'll add an event handler to the **warm-greeter** component so it will do something when the button is clicked.\n\nTo do that, you'll be extending the default base class for the **warm-greeter** component, by adding some methods to its script file. \n\nSo, go ahead, and open up 'public/components/warm-greeter/script.js' and add the following content:\n\n```js\nclass Component extends Base {\n  Regreet(clickEvent) {\n    const newState = cloneState('MyState');\n    newState.greetCounts.value += 1;\n    setState('MyState', newState);\n  }\n}\n```\n\nMake sure you save that file, then reload the development page in your web browser. \n\nThis time, when you click the button, you'll see something happen. \n\nA new message will appear, telling you that: \"We are very pleased to meet you 2 happy times.\"\n\nThat's the end of this mini-tutorial, so, ***Congratulations!*** You've done really well, and you're ready to start writing components on your own and learning more. \n\nIf you read on you'll discover more about some details you saw in the tutorial. \n\n-----------------\n\n## Slots and variables\n\nYou'll notice in the examples above that we used both `\u003cslot\u003e\u003c/slot\u003e` elements and variables. In BANG! **slot** elements function just like they do in regular Web Components, so if you know how to use them there, you know how to use them in BANG. If you don't, you can read up on [information about slot elements here](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot).\n\nYou also saw that we used a *new syntax* for templating called **template replacement slots**. This is not part of web standard, and is in fact a convenient syntax for display variables and state in your components. It's quite different to `\u003cslot\u003e\u003c/slot\u003e` elements, so read on to find out more.\n\nIn order to have your component display variables form an object, you need to do 3 things:\n\n1. Assign that state object a string key, it's **state key**, and save it in the object store using `setState(\u003ckey: string\u003e, \u003cvalue: object\u003e)`;\n2. Pass that **state key** to the component by setting its `state` attribute; and\n3. Reference **properties** from that state object using **template replacement slot** syntax\n\nLet's run through an example to tease out the details of these 3 steps:\n\n### 1. Save the state to the store\n\nIn a script you would write: \n\n```js\nsetState('MyState', {\n  deviceFormat: 'mobile',\n  screen: {\n    width: 420\n  }\n});\n```\n\nYou can now access the state object using the key `MyStae`.\n\n### 2. Pass state to components\n\nIn a markup file for a component (or in the top-level HTML file for your app you would write):\n\n```js\n\u003c!test-el state=MyState /\u003e\n```\n\n### 3. Template the properties\n\nIn a markup file for your component, you could then write:\n\n```html\n\u003cdiv class=\"big-banner ${deviceFormat}\"\u003e\n  \u003cimg src=wide-cat.png width=${screen.width*0.75}px\n\u003c/div\u003e\n```\n\n-----------------------\n\n## The `state=` attribute, nested objects and template replacement slots\n\nYou probably noticed above that you didn't need to refer to the parent object when using the **template replacement slot** syntax. This is because you can simply use the property name inside the markup of the component you pass the state to. If you need to access the outer state object, you can do that via the special `_self` property. \n\nAlso what if you want pass **nested objects** in your state object to be the `state=` properties of a sub-component? \n\nFor example, in the tutorial we created a **warm-greeter** component that incorporated a sub-component, **greet-count** like so:\n\n```html\n\u003ch1\u003eHello ${name}\u003c/h1\u003e\n\u003cp\u003e\n  We are very pleased to meet you \n  \u003cgreet-count state=${greetCounts}\u003ehappy\u003c/greet-count\u003e times\n\u003c/p\u003e\n\u003cbutton onclick=Regreet\u003eRegreet!\u003c/button\u003e\n```\n\nYou might have noticed that the `state=` property of the **greet-count** component is not passed by string key, but instead passed a *nested object* using our standard **template replacement slot** syntax. Despite this, **greet-count** behaves as if it had been passed a **state-key**.\n\nSo what's going on?\n\nThis is the expected behavior. You *can pass state directly to your sub-components using **template replacement slots*** in any component markup file but not in a top-level HTML file (because **template replacement slots** are not processed there, only in component markup). \n\nThis means that in the above example, **greet-count** behaves the same as if you explicitly saved that nested object to the state store using a string **state-key** then passed that key to your component using the `state=` property. \n\nInstead of having to write that extra step, ***BANG!*** detects the nested object and saves it to the store for you, and passes to new components as they are created to load the state, without you having to worry about the details.\n\n----------------------\n\n## Async templating\n\n***BANG!*** can also accept state properties that are functions, async functions and Promises. In these cases, here's what happens:\n\n- Promise: ***BANG!*** awaits the Promise to resolve, then templates in the value returned by the resolved Promise.\n- Async Function: ***BANG!*** executes the async function and awaits the result, then templates in the value.\n- Function: ***BANG!*** executes the function and templates in the result.\n\n---------------\n\n## More information\n\n***BANG!*** is new, and it might take you some time to learn.\n\nThese documents, and ***BANG!*** itself are a work in progress.\n\nPlans may change, but right now, some aims for the future are:\n\n- re-render function in the base class\n- improve documentation\n- add minimal DOM diffing using [vanillaview](https://github.com/crisdosyago/vanillaview) granular DOM updator function technology\n- add **state-queries** with automatic data binding, to fully decouple state objects from components, and decouple components from each other, and enables dependent components to be automatically re-rendered when data they use in the store changes.\n\n-----------------\n\n# Q\u0026A \n\n### Why use *BANG!* and not just a `\u003ccustom-self-closing-tag /\u003e` or a single `\u003ccustom-tag\u003e`? \n\nWhen the HTML parser [encounters a self-closing slash in a non-void element, it acts as if the slash isn't there](https://html.spec.whatwg.org/multipage/parsing.html#parse-error-non-void-html-element-start-tag-with-trailing-solidus), in effect opening the tag, and wrapping any subsequent content up to the next valid closing tag for that element, inside that open tag. This is not what you intend when you try to use a self-closing tag.\n\nSimilarly, when the HTML parser encounters a single `\u003ccustom-tag\u003e` it opens it, and so subsequent tags will be placed inside that open tag.\n\n### How do I add event handlers or event listeners to elements?\n\nEasy! Just implement a handler in your component class (the class that extends from `Base` and is located in component's `script.js` file), and use that handlers method name as the value of an `onevent=` attribute. For example:\n\n`/components/cta-button/script.js`:\n```js\nclass Component extends Base {\n  BuyNow(clickEvent) {\n    this.paymentService.createCharge(this.from.getCardDetails()).then(status =\u003e this.Report(status));\n  }\n}\n```\n\n`/components/cta-button/markup.html`:\n```html\n\u003cbutton onclick=BuyNow\u003eBuy. Like RIGHT NOW\u003c/button\u003e\n```\n\nNotice how we pass the string `BuyNow`, which is the name of our event handler function `BuyNow()` in our component class, to the `\u003cbutton\u003e` element using the `onclick=` event handler? This syntax ensures the `BuyNow()` function binds to that button, and listens for its `click` event. \n\n#### A second slightly different syntax to add event handlers not currently supported\n\nRight now, you can't do this:\n```html\n\u003cbutton click=${myHandlerFunc}\u003eDo Stuff\u003c/button\u003e\n```\n\nThis old, vanillaview style syntax, by passing in a function, to a an attribute is not currently supported. This is because, right now, ***BANG!*** interprets any function value given it as a function to execute, not as an event handler to bind to an element. So if you pass it a function, it will execute that function and pass in the state object in scope in that component, `func(state)` and print the value returned by that call into the slot where the function was, in the markup.\n\n### What are some gotchas or syntax I need to beware of?\n\n#### `use(\u003cname: string\u003e)`\n\nIf you don't call `use` with the name of the component (*the name of its directory*) then your component will not be a custom element, and will just be a regular HTML tag. \n\nDon't forget to always `use` all components, even nested components in your script.\n\n#### Self-closing syntax\n\n***BANG!*** is design to be pretty intuitive and smooth with the syntax, so most things work as you expect. But there are still some things that may catch you out if you forget. \n\nSo:\n\n- don't omit the bang (**!**) because that's how we signal it's not a normal tag; and\n- don't start any comment with a double-barrelled word, because that's how we signal it's a self-closing tag, not a comment. \n\n#### Component classes\n\nAlso, regarding extending classes it doesn't matter what you call your component class (you can call it `Component` or anything you like) but you do need to extend it from the `Base` class and use the exact name `Base` like so:\n\n```js\nclass MyComponent extends Base {\n /* ... */\n}\n```\n\n#### Top-level element\n\nYou need to include a `\u003cbody\u003e` tag in your top-level HTML document, or another displayable tag, and put your self-closing tags *after* that displayable tag, otherwise they will end up `\u003chead\u003e` tag of the document, and not be displayed.\n\n#### Config\n\nYou can configure the following properties:\n\n```js\n{\n  htmlFile: 'markup.html',\n  scriptFile: 'script.js',\n  styleFile: 'style.css',\n  bangKey: '_bang_key',\n  componentsPath: './components',\n  allowUnset: false,\n  unsetPlaceholder: '',\n  EVENTS: `error load click pointerdown pointerup pointermove mousedown mouseup \n    mousemove touchstart touchend touchmove touchcancel dblclick dragstart dragend \n    dragmove drag mouseover mouseout focus blur focusin focusout scroll\n  `.split(/\\s+/g).filter(s =\u003e s.length)\n}\n```\n\nTo override the above defaults, pass in a new config object, like so:\n\n```js\nbangFig({\n  markupFile: 'html.html`,\n  componentsPath: `coco`\n});\n```\n\n#### Programmatic state-keys\n\nYou can set a specific state key for a nested object to override the autogenerated key by setting the CONFIG.bangKey property name (by default `_bang_key`) on the nested object. For example:\n\n```js\nsetState(`S1`, {\n  nester: {\n    _bang_key: `happy1`,\n    prop1: 'okay ;p ;) xx`\n  }\n});\n```\n\nThen later:\n\n```js\nconst nestedState = cloneState(`happy1`);\n```\n\nOr\n\n```js\n\u003csome-guy state=happy1\u003e\u003c/some-guy\u003e\n```\n\n--------\n\n## Contributions\n\nContributions are very welcome. No CLA needed. No license restrictions. Just get-in, muck-in and get involved! :P ;) xx \n\nIf you want to, of course. :P ;) xx\n\n## Roadmap\n\nThese are just ideas, and I might not do them. :)\n\n- Convenience getters for named state (in other words, you can call `this.state.modalState` rather than writing `const state = getState('modalState')`\n- Passing instance property arguments or state scope through the custom element tag. Need to consider the syntax for this whether the values go to the constructor, or to the state or both. \n\n------------\n\n# HTML ***aber mit einem BANG!***\n\n\u003cp align=center\u003e\n  \u003cimg width=80% \n    src=https://github.com/crisdosyago/good.html/raw/main/.github/BANG!%20logo%20mediumseagreen-mincream.png\n  \u003e\n\u003c/p\u003e\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fo0101%2Fgood.html","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fo0101%2Fgood.html","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fo0101%2Fgood.html/lists"}