Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/devpunks/snuggsi
snuggsi ツ - Easy Custom Elements in ~1kB
https://github.com/devpunks/snuggsi
custom-elements dom ecmascript es6 frontend html javascript template-literals webcomponents
Last synced: 30 days ago
JSON representation
snuggsi ツ - Easy Custom Elements in ~1kB
- Host: GitHub
- URL: https://github.com/devpunks/snuggsi
- Owner: devpunks
- License: mit
- Created: 2010-04-07T04:30:03.000Z (over 14 years ago)
- Default Branch: main
- Last Pushed: 2024-10-11T08:03:52.000Z (about 1 month ago)
- Last Synced: 2024-10-12T05:07:01.243Z (about 1 month ago)
- Topics: custom-elements, dom, ecmascript, es6, frontend, html, javascript, template-literals, webcomponents
- Language: JavaScript
- Homepage: https://snuggsi.com
- Size: 35.1 MB
- Stars: 395
- Watchers: 21
- Forks: 17
- Open Issues: 37
-
Metadata Files:
- Readme: README.md
- Contributing: .github/CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE.txt
- Codeowners: CODEOWNERS
Awesome Lists containing this project
README
snuggsi ツ - Easy Custom Elements in ~1kiloByte
All you need is a browser and basic understanding of HTML, CSS, & JavaScript classes to be productive!
Performance is the art of avoiding work
- #FreeJewelry 💍 💎# Navigation
- [Why ?](#why-)
- [Easy Installation](#easy-installation)
- [Browser Support](#browser-support)
- [Quick Tour](#quick-tour)
- [<`custom-elements`>](#custom-elements)
- [`HTML` Declaration](#html-declaration)
- [`Element` Definition](#element-definition)
- [`class` Description](#class-description)
- [`Template`](#template)# [Why ?](https://github.com/devpunks/snuggsi/wiki/Why%3F)
1. You prefer to be [D.R.Y.](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) and build reusable web components on a gradual learning curve.
2. Because [You _(probably)_ don't need a JavaScript Framework](https://dev.to/steelvoltage/you-probably-don-t-need-a-front-end-framework-26o6).
3. You prefer [convention over configuration](https://en.wikipedia.org/wiki/Convention_over_configuration).4. [Web Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components)
are [ready](https://twitter.com/domenic/status/904114041752358913)
for [production](https://twitter.com/WebReflection/status/761316429559922688)
& [Custom Elements v1](https://www.w3.org/TR/custom-elements) has
[support for every modern 🍃 greenfield browser](#browser-support).# Easy Installation
Made with [💖 Vanilla JS™](http://vanilla-js.com) No need to learn Node.js, React, Webpack, Babel, or Gulp.
_(You can if ya **want** to use **snuggsiツ** with those tools. But you don't **need** to!)___*#UseThePlatform*__
**snuggsiツ** works in a plain 'ol HTML file! Simply place the following **<script>** within your webpage:
```html
```
Et Voila _(that's it!)_ ツ
# Browser Support
_**snuggsiツ** provides a [prolyfill](https://github.com/devpunks/snuggsi/wiki/What-is-a-ProlyFill) for the following native web platform features:_
| Support | Edge* | Chrome* | Firefox* | Safari 9+ | Android* | iOS Safari* |
|:----------:|:-----:|:-------:|:--------:|:---------:|:--------:|:--------------:|
| [Templates](#templates) |✅ |✅ |✅ |✅ |✅ |✅ |
| [Custom Elements](#custom-elements) |✅ |✅ |✅ |✅ |✅ |✅ |
| Slot Replacement |✅ |✅ |✅ |✅ |✅ |✅ |_\*Indicates the current version of the browser_
# Quick Tour
**snuggsiツ** encourages [convention over configuration](https://en.wikipedia.org/wiki/Convention_over_configuration) using familiar techniques that are native to all browsers.
Gone are the sleepless nights where your code [suffers from `
`itus](https://css-tricks.com/css-beginner-mistakes-1/), or need to install packages on a terminal just to write `HTML`. **People who are more comfortable with `HTML` should be able to start marking up their ideas immediately!** You shouldn't have to know CSS or JavaScript! _(But it definitely helps if you need styling and functionality)_.**snuggsiツ** believes in using [Progressive Enhancement](https://en.wikipedia.org/wiki/Progressive_enhancement). Not just with your code, but also with your Developer eXperience _(DX)_. We will start from the beginning with a simple Custom Element and gradually enhance functionality step-by-step.
## [<`custom-elements`>](/element#readme)
When picking a name for your custom element [there are a few naming conventions](https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name) you must be aware of. We can simply use `hello-world` for now.
### `HTML` Declaration
`HTML` has always been a declarative language. Why not just use it to declare Custom Elements?
If you know [how to write `HTML`](https://developer.mozilla.org/en-US/docs/Web/HTML) you can start using **snuggsiツ**. Sometimes you need to sandbox a section of your page for styling. Other times you need a custom container of complex functionality. Either way you usually start with a plain ole' `HTML` element declaration:#### A Brief History Lesson
_Not all HTML tags are created equal!_
A _"valid `HTML` Element"_ has always _allowed_ non-standard tag names _(as long as you remember to provide a closing tag)_. In the bad old days of the web, [`HTML5` elements were once _"non-standard"_ to `HTML 4.0`](https://johnresig.com/blog/html5-shiv):
```html
…
…
…
……
…
```
👍 Rule of thumb: _Close all **non-standard** `HTML` Element tags!_
As you learned earlier there are a few conventions to adhere to be considered a _"valid **Custom** Element"_ you will need an alpha-numeric character followed by a hyphen in the tag name _(at minimum)_:
```html
```👍 Rule of thumb: _Use [kebab case (with hyphens)](https://en.wiktionary.org/wiki/kebab_case) for tag names._
We now know enough to be dangerous and make your own Custom Element tag:
```html
```
Et Voila ツ _(No really … That's it!)_
At this point your **custom** element can be styled using CSS just like any other element.
```html
hello-world {
background: #bada55
}Hello
```
See [A JavaScript-free custom element implementation](https://www.stefanjudis.com/notes/a-javascript-free-custom-element-implementation/)
And [Building a `` component](https://web.dev/building-a-tooltip-component/) for more _(sans JavaScript)_ custom elements CSS fun!
#### Live `{token}` Declarations
The `{token}` is simply a [well named dynamic variable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Variables) you will **Describe** later. `{token}`s are placeholders which watch for changes to your custom element's `class` property of the same name. Since they are _"placeholders"_ and not live code, Front-end designers are no longer blocked by needing to install a JavaScript framework just to write `CSS`!
```html
This is a token 👉 {baz} and {bat} is another!
```
👍 Rule of thumb: _If the `{token}` name is [not in a thesaurus](https://en.wikipedia.org/wiki/Metasyntactic_variable) then I probably shouldn't use it._
A _"live token"_ is a declarative way to bind data values to your Custom Element. A nice convention to a real historical P.I.T.A. of keeping values updated. Live `{token}`s are also _"✨ automagically"_ updated each time the element re-renders.
Let's add a `{token}` to ``:
```html
Hello {planet}
```
👍 Rule of thumb: _A `{token}` is not _"live"_ until there is a `class` description for its functionality._
Lastly, we can visually enhance your `` Custom Element by making it [_"block level"_](https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements) with CSS `display: block`. This way your element plays nicely in your layout:
```html
Hello {planet}
hello-world { display: block }
```
We have finished your Custom Element **Declaration** using `HTML`, & `CSS`!🌟 Now on to your **Definition**.
### `Element` Definition
Every Custom `Element` **MUST** be [_Defined_ within the `CustomElementsRegistry`](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry). This is simple with **snuggsiツ**
Let's `define` your element using the `Element` interface:
```javascript
// …Element `hello-world`
```
👍 Rule of thumb: _Use backticks around tag names (``)._This syntax is not JSX. It's actually called [tagged template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates) and is native to the platform.
Custom elements use the native `Element` interface definition strategy for two reasons:1. To prevent you from worrying about browser inconsistencies as the technology matures.
2. Prevent global namespace pollution. _([`Element` has been native to the web platform for decades!](https://developer.mozilla.org/en-US/docs/Web/API/Element))_Classic JavaScript syntax may also be used. However [this should be the job of a transpiler not the developer](https://en.wikipedia.org/wiki/Source-to-source_compiler).
Transpilers take care of [normalizing Modern JavaScript to a specific retrograde](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/JavaScript).```javascript
Element ('hello-world') // classic javascript definition
```
### `class` Description
Great so far!🎉 Although your Element behaves like any other `HTMLElement`, we should add some functionality custom to your needs.
Next, we need to pass a `class` description to the function returned by your `Element` definition.
```javascript
// …Element `hello-world`
( class HelloWorld extends HTMLElement { … } )
```👍 Rule of thumb: _**MUST** define [a `class` which `extends HTMLElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement)_
Let's shorten your description up a bit by using an [anonymous class expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/class) to describe the `class`. This convention is preferred as using an [explicit `class` declaration](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class) name can potentially pollute the global namespace:
```javascript
// …Element `hello-world`
( class extends HTMLElement { … } )
```👍 Rule of thumb: _Use enclosing parenthesis around `(class …)` definitions._
#### Live `{token}` Definitions
Since we [previously declared a `{planet}` token](#live-tokens) within your `` element we need to also define a `class property` **of the same name** to replace the `{planet}` token with a value.
Class properties may look like typical JavaScript Functions. However, they are treated as properties. _(called without parenthesis)_. `class` properties are described by using the [`get`ter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) and [`set`ter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set) annotations before the name.
Let's add a property to your `class` definition and give `{planet}` some life:
```javascript
// … {planet} …Element `hello-world`
(class extends HTMLElement {
get planet () // used for {planet} token
// "✨ automagic" token binding
{ return 'world 🌎' }
})
```👍 Rule of thumb: _`class` properties are functions begining with the keywords `get` & `set`._
👍 Rule of thumb: _`{tokens}` will use the `class` property value of the same name by default._
⚠️ The Live `{token}` value is updated after each re-render but it beyond the scope of this simple example.
Since your `hello-world` Custom Element is an `HTMLElement` at its core, we can access your property directly from the DOM!
```javascript
// …document.querySelector
('hello-world').planet // world 🌎
```
👍 Rule of thumb: _Do not use parenthesis `()` when calling `get`ters & `set`ters._
#### Global `event` Listeners
`event` handlers can be any method function which can be placed on any child elements and also onto the custom element itself _(i.e.`onclick=eatBacon`)_. However, you will not have to explicitly set the handler in HTML when you follow native naming conventions. This is the magic behind **snuggsiツ** Global `event` Listeners. They register themselves onto the custom element and _"listen"_ for you! As a convenience, your new custom element uses [Event Delegation](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_delegation) to capture all its children's [event bubbles of the same name](https://javascript.info/bubbling-and-capturing).
```javascript
//
// `onclick` is "automagically" attached
//
// ACHOO!
//Element `hello-world`
(class extends HTMLElement {
// native event handler names
// are "✨automagically" attached to `hello-world`
onclick (event) {// prevent event from bubbling
// Custom Element will re-render
// after event unless prevented
event.preventDefault ()event.target // element which triggered event
this // is ALWAYS bound to the Custom Element container 🎉
}onsneeze (event) {
/* must be declared in HTML
*/
}
})
```👍 Rule of thumb: _Use native `GlobalEventHandlers`_ names if you don't want to explicitly set listeners in HTML.
_See full list of [Global Event Handlers on MDN](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers)_
Lastly, all `event` handlers _(and global `event` listeners)_ are passed a native [`event` object](https://developer.mozilla.org/en-US/docs/Web/API/Event).
_P.S._ **YES** the event handler will _auto`bind`_ `this` to the **current** custom element instance! :tada:
### Hello World! _(simple)_
[Play `` Demo](http://jsfiddle.net/vw4u6ycx)
_…or just copy & 🍝pasta into a new HTML file and have at it!_
```html
Hello {planet}
Greet
// 👇 Definition -------------------------------
Element `hello-world`
// 👇 Description ------------------------------
(class extends HTMLElement {
get planet () // property token
// "✨ automagic" token binding
{ return 'world 🌎' }onclick (e) { // event handler
// "✨ automagic" event registration
alert(`You clicked on ${e.target.tagName}`)
}
speak(e) { // bound event handler
e.preventDefault()
alert('One small step...')
}
})```
### Hello Kitty! _(advanced)_
[Play `` Demo](https://jsfiddle.net/yLdatmvz)
_…or just copy & 🍝pasta into a new HTML file and have at it!_
```html
{greeting}
Hello new kitty!
hello-kitty *
{ margin: 1em; text-align: center }
Element `hello-kitty`
(class extends HTMLElement {
// CONSTRUCTOR ---------------------------------------
initialize ()
// see `meow` event handler
{ this.url = 'https://placekitten.com/400/400?image=' }// PROPERTIES ----------------------------------------
set icon // on element
// default to html attribute
( value = this.getAttribute `icon` )
// set html attribute to new value
{ this.setAttribute (`icon`, value) }get icon () // from element
{ return this.getAttribute `icon` }get greeting () // "✨ automagic" token binding
{ return `<hello-kitty> Carousel ${ this.icon }` }// METHODS -------------------------------------------
random () {
return Math.round
( Math.random `` * 16 )
}// EVENT HANDLERS ------------------------------------
onclick (e) {
// "✨ automagic" global event handler registration
alert (`You clicked on ${e.target.tagName} ${ this.icon }`)
}pet ()
{ alert `Puuuuuurrrrrrrrrrrr!!!` }meow (e) { // custom handler
e.preventDefault ``this.querySelector `img`
.setAttribute (`src`, this.url + this.random () )// element will "✨ automagically" re-render !!!
}
})```
## [`Template`](/html-template-element#readme)
The `` is used to define custom element content for use within multiple elements.
Useful when we need to:
1. Separate a custom element definition into a [Web Component](https://developer.mozilla.org/en-US/docs/Web/Web_Components)
2. Bind a context to the template using An `Array` or POJO _(Plain Ol' JavaScript `Object`)_
3. Append rendered template to the document:
- If `context` is an object `bind` a single ``
- If `context` is a collection _(i.e. an `Array`)_ `bind` a sequential `` [`DocumentFragment`](https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment) per item### `` With `Object` Context
```html
{nickname}
const
template = Template `developer`
, context = { nickname: 'That Beast' }template.bind (context)
```
### Rendered Result
```htmlThat Beast
```### `` With `Array` of `Object`s Context
Each `` will be mapped over each
context item within the array. When the array items
are objects each property will map to a respective
`{token}` of the same name._**Note:** The `#` symbol is used to retrieve the collection's current index of iteration._
```html
- Hello {name}! Your index # is {#}
// when context is a collection
const
template = Template `item`
, context = [ {name: 'DevPunk'}, {name: 'Snuggsi'} ]// internal template render for each item in context
template.bind (context)```
### Rendered Result
```html
- Hello DevPunk! Your number index # is 0
- Hello Snuggsi! Your number index # is 1
```### `` With Scalar `Array` Context
Each `` will be mapped over each
context item within the array. When the array items
are scalar _(i.e. strings, numbers, booleans)_
each item will map to a `{self}` helper token._**Note:** The `#` symbol is used to retrieve the collection's current index of iteration._
```html
- Step {#}.
- {self}.
// when context is a collection of scalar variables (i.e. Strings)
const
template = Template `recipe`
, context = [
"Preheat oven"
, "Place pre-made cake in oven"
, "Don't burn the cake"
, "Nom Nom"
]// internal template render for each item in context
template.bind (context)```
### Rendered Result
```html
…
- Step 1.
- Preheat oven.
- Step 2.
- Place pre-made cake in oven.
- Step 3.
- Don't burn the cake!
- Step 4.
- Nom Nom!
```## Build Process
**snuggsiツ** is able to use modern compression algorithms to create
bundles as small as *~1500 OCTETS* _(or one 1500byte Ethernet packet frame)_[Read More](https://github.com/devpunks/snuggsi/tree/master/dist#readme)
### CalVer _(Calendar Versioning)_
A chronological [CalVer](https://calver.org) strategy is used instead of [SemVer](https://semver.org).
[Read More](bin#distribute)## Contributors
Contributing while using [Visual Studio Code](https://code.visualstudio.com/) is simple! _[Read More](./.vscode#readme)_