IncludeIf: A digital space written in pencil
HTMLElementSugar provides declarative attribute definitions, template
handling, component aliases, and configuration inheritance — all while staying
close to vanilla web components. In this blog I’ll explain the why, the how,
and the because. You can find it on
Webcomponents solve a really fun problem, they only introduce a new one: BOILERPLATE much?
For observedAttributes you need a list of attributes. For attributeMap, you
need to define optional parsers, and for defaultConfig, you need to define if
you need default values and what they are. This can probably be done with a
good snippet, but it needs too much coding on the wrong side of my editor.
So I decided to tackle the problem on the javascript side. I started writing
HTMLElementSugar. It had a different name when I started, but it is now
called HTMLElementSugar. It’s important to note that all my web components
operate at the light DOM. I don’t operate in the shadow DOM, as I don’t have a
compelling reason to do so at this moment. Besides this reason, it is also very
SEO and crawler-friendly. Well… in theory at least, search engines and SEO
tools have a lot of issues with pages that are written with webcomponents.
So what does HTMLElementSugar do? Well, a lot. It removes the “need” to write
observedAttributes, attributeMap, and defaultConfig. You only need to
declare attribute definitions: attributeDefs. This is essentially a mapping
that transforms the data into observedAttributes, attributeMap, and
defaultConfig. It is a small hash/map/dictionary/associative array — every
language I know has a different term for what I, a perl dev, name a hash, so
pick one you like ;)) — where you define the name and a specification. By
default, attributes are observed, unless you add observed in a falsey manner.
You can define a default and a parser, all are optional:
Compare that to this:
We simplified everything by declaring it once, and we then populated
observedAttributes, attributeMap, and defaultConfig. Let me show you by
using our test file:
Simple, effective, and no repetition.
Pre-v0.1.0 we allowed authors to write their own
observedAttributes,attributeMap, anddefaultConfigand opt out of usingattributeDefs. This is no longer the case due to inheritance problems.
So now our mixtape component will look like this:
Now you can register the component:
This will run some sanity checks, e.g., does your tag exist, and transforms the
data. It will also fail fast once an error is detected. And to make your
development experience nicer, you don’t need to register() your component in
a test suite, you can call init() and all the checks are done for you.
But this is not all…
Web components also use templates. If you have written web components before,
you know that you can write them in HTML directly, or you can include them in
your web component. Because HTMLElementSugar operates in the light DOM its
template handling is flexible. HTMLElementSugar supports four, or five, use
cases, depending on how you look at them. You can use standard HTML template
references, javascript template references, a regular JS function or via a
tuple. Or you write none. The tuple is the one I use most often as it allows
overrides that one may want to have in a project. It allows you to add some
hooks for CSS targetting.
HTMLElementSugarBesides the sugar for attribute definitions and templates, several other features are available: aliases and configuration.
The beautiful thing about web components (I’m being sarcastic here) is that
they require you to define them as foo-bar and you cannot have foobar; it
needs a hyphen. This ensures you’ll never clash with newly introduced or future
HTML elements. Understandable, but it limits some expressive ways to write your
web components. So… this is actually one of the reasons I added alias
support. I wanted proper web components — spec-compliant, hyphenated,
well-behaved — but I also wanted the freedom to write more semantic,
human-friendly HTML.
Under the hood, everything stays compliant. We register a canonical element
like track-item. But at authoring time, we can allow expressive tags like
<song> or <book>. A small rewrite layer transforms those into their
hyphenated counterparts before the browser even complains.
You get the discipline of the spec. But you also get to write HTML that reads like English.
jsdom
jsdom breaks when you register a web
component without hyphens).To see this in action:
This adds the alias and sets some attributes on the track-item, although I
realize that while writing, type doesn’t exist in the track-item example. But
you can see where I’m going with this. Instead of your-book, you could use
book.
So this allows you to write the following HTML:
Aliases enable you to be expressive while remaining spec-compliant. And allows
you to pass in often-used attributes or bend the spec when you absolutely want
to, because you feel the semantics vibe better with mixtape instead of
some-mixtape.
You do need to add one small utility helper in your HTML:
Otherwise it won’t work.
In my mixtape project, I found that I needed configuration from a parent
component. So I decided to create a way to be able to select configuration
details from a parent, but it expanded into something more. You can actually
target specific elements. We do this with getNearestConfig().
You can define how configuration is found: via a CSS selector or a more complex
XPath expression. It uses the first node that is found and checks if the
element supports a getConfig() call. It will then return that configuration.
With a CSS pattern, it looks like this:
Which you can optionally override in HTML:
This means your components don’t need to duplicate configuration. You only need to define the configuration once, thus making it leaner.
You could also opt for a more fine-grained search by using xpath:
I was writing a mixtape. And once I got tired of having to type a lot of the
same boilerplate, I wrote HTMLElementSugar, and it wasn’t trying to solve a
complex problem. It tried to reduce noise and repetition. If you also want to
avoid repetition and focus on writing just enough code to make it happen in a
meaningful way: Sample it. Use it. Remix it.
You can install it using:
Or check it out on npm:
When I wrote this blogpost, sugar was alone. Since then it has grown,
significantly. It now works under Livewire without having to use wire:ignore,
it is resistant against morphing. And is now part of something bigger I call
Sugar, via skirbi, powers this blog via a hugo theme called