<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Hawk Ticehurst</title><description>Words about code, design, and other stuff.</description><link>https://hawkticehurst.com/</link><image><link>https://hawkticehurst.com/</link><title>Portait of Hawk.</title><url>https://hawkticehurst.com/imgs/hawk-rss.png</url><height>144</height><width>144</width></image><item><title>Declarative signals</title><link>https://hawkticehurst.com/2024/12/declarative-signals/</link><guid isPermaLink="true">https://hawkticehurst.com/2024/12/declarative-signals/</guid><description>Bringing signals to HTML.</description><pubDate>Mon, 30 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A year ago I published an article that talked about &lt;a href=&quot;https://hawkticehurst.com/2023/11/a-year-working-with-html-web-components/&quot;&gt;my experience using HTML Web Components&lt;/a&gt;, and at the end introduced a tiny framework called &lt;a href=&quot;https://github.com/hawkticehurst/stellar&quot;&gt;Stellar&lt;/a&gt; that makes the job of authoring reactive HTML web components a bit easier.&lt;/p&gt;
&lt;p&gt;Since then, the project has largely sat untouched, acting more as a research exercise or a demonstration of the ideas I had been mulling over in 2023.&lt;/p&gt;
&lt;p&gt;As 2024 comes to a close I&apos;ve been working on an evolution to Stellar (my habit of mulling clearly hasn&apos;t changed). It&apos;s still a work in progress, but I&apos;ve stumbled upon an idea that I think is worth sharing now.&lt;/p&gt;
&lt;p&gt;What would happen if we had a way of defining signals directly in HTML?&lt;/p&gt;
&lt;h2&gt;Signals, in JavaScript&lt;/h2&gt;
&lt;p&gt;I&apos;d wager that most who are reading this are at least a little bit familiar with signals.&lt;/p&gt;
&lt;p&gt;At their most basic, signals are a method of defining and managing a small piece of state that can be used to update a user interface in a declarative/reactive way.&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;info&quot;&amp;gt;For even more information, see Corbin Crutchley&apos;s fantastic post: &amp;lt;a href=&quot;https://playfulprogramming.com/posts/what-are-signals&quot;&amp;gt;What are Signals?&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;Here&apos;s a small example of using signals in vanilla JavaScript.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button&amp;gt;Count is &amp;lt;span id=&quot;count&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;

&amp;lt;script type=&quot;module&quot;&amp;gt;
	// The state function is used to create a signal
	import { state } from &apos;./signal.js&apos;;
	// The effect function is used to define what should happen
	// when the value of a signal changes
	import { effect } from &apos;./effect.js&apos;;

	// Get references to the button and span elements
	const button = document.querySelector(&apos;button&apos;);
	const span = document.querySelector(&apos;#count&apos;);

	// Initialize count signal
	const count = state(0);

	// Update the value of the count signal on each button click
	button.addEventListener(&apos;click&apos;, () =&amp;gt; {
		count.value = count.value + 1;
	});

	// Update &amp;lt;span&amp;gt; with the current value of the count signal
	// This runs anytime the value of the count signal is updated
	effect(() =&amp;gt; {
		span.textContent = `${count.value}`;
	});
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I expect that last bit of &lt;code&gt;effect&lt;/code&gt; code might look a tiny bit weird to folks who are used to using signals in a web framework. In most frameworks the code that is responsible for updating the DOM after a signal has been changed is hidden from (read: automatically generated for) you and I.&lt;/p&gt;
&lt;p&gt;Removing the responsbility for developers to write this kind of DOM updating code is a &lt;em&gt;fantastic&lt;/em&gt; idea and is one of the core selling points of a declarative UI programming model.&lt;/p&gt;
&lt;p&gt;There is, however, a catch that I want to focus on today. Declarative signals, as they exist now, are always packaged in a larger web framework or component model. But what if you are working with a technology stack that doesn&apos;t natively support/provide signals?&lt;/p&gt;
&lt;p&gt;You could adopt another framework into your stack that provides signals, but now you need to operate within the conventions of that framework. Or you could import a standalone signal library and write code similar to the example above, but now you&apos;re back to handling DOM updating logic.&lt;/p&gt;
&lt;p&gt;While completely workable, I find both of these options to be a bit unsatisfying, which brings me to my final question:&lt;/p&gt;
&lt;p&gt;Wouldn&apos;t it be great if there was an HTML element that handled reactive state management &lt;em&gt;and&lt;/em&gt; rendering logic in a small package that could be used across any context and technology? What if we could write code that looked something like this?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button&amp;gt;Count is &amp;lt;signal&amp;gt;0&amp;lt;/signal&amp;gt;&amp;lt;/button&amp;gt;

&amp;lt;script&amp;gt;
	const button = document.querySelector(&apos;button&apos;);
	const count = document.querySelector(&apos;signal&apos;);
	button.addEventListener(&apos;click&apos;, () =&amp;gt; {
		count.state = count.state + 1;
	});
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Signals, as HTML&lt;/h2&gt;
&lt;p&gt;Obviously, there isn&apos;t a &lt;code&gt;&amp;lt;signal&amp;gt;&lt;/code&gt; HTML element available today, but it doesn&apos;t mean we can&apos;t make one ourselves!&lt;/p&gt;
&lt;p&gt;To jump straight in, we&apos;ll be building an &lt;code&gt;&amp;lt;x-signal&amp;gt;&lt;/code&gt; custom element. Below is a very basic starter implementation of this custom element that will slowly be updated to include more features during the rest of this article.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { Signal } from &apos;signal-polyfill&apos;;
import { effect } from &apos;./effect.js&apos;;

class SignalElement extends HTMLElement {
	constructor() {
		super();
		// Get initial value from the elements text content
		// Yes, this is weird -- I&apos;ll explain more in a bit
		const initial = this.textContent;
		// Create a new signal
		this.signal = new Signal.State(initial);
		// Everytime the signal value is updated rerender the element
		this.cleanup = effect(() =&amp;gt; this.render());
	}
	connectedCallback() {
		// Render the element when it is first mounted to the DOM
		this.render();
	}
	disconnectedCallback() {
		// Run the effect cleanup when the element is destroyed
		this.cleanup();
	}
	render() {
		// Render the current value of the signal as the child
		// text content of the custom element
		this.textContent = this.signal.get();
	}
	// A getter method to access the current value of the signal
	get state() {
		return this.signal.get();
	}
	// A setter method to update the value of the signal
	set state(v) {
		this.signal.set(v);
	}
}

customElements.define(&apos;x-signal&apos;, SignalElement);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the purposes of this demo we are using a &lt;a href=&quot;https://github.com/proposal-signals/signal-polyfill/tree/main&quot;&gt;polyfill&lt;/a&gt; of the &lt;a href=&quot;https://github.com/tc39/proposal-signals&quot;&gt;JavaScript Signals proposal&lt;/a&gt;. The imported &lt;code&gt;effect&lt;/code&gt; function is copied directly from the polyfill &lt;a href=&quot;https://github.com/proposal-signals/signal-polyfill/tree/main#creating-a-simple-effect&quot;&gt;README&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The imported &lt;code&gt;Signal&lt;/code&gt; class provides the core reactive primitive needed to make this all work and we are simply providing getter and setter methods named &lt;code&gt;state&lt;/code&gt; to access and update the value of the signal.&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;info&quot;&amp;gt;For a great explainer on getters, setters, and signals see this &amp;lt;a href=&quot;https://www.youtube.com/watch?v=NR8L5m73dtE&quot;&amp;gt;video by Rich Harris&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;Finally, the rest of the custom element handles all of the rendering logic. For now the element can only handle strings, but even this basic implementation is enough to create one of the classic hello worlds of reactive UI programming.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;label for=&quot;input&quot;&amp;gt;Enter name:&amp;lt;/label&amp;gt;
&amp;lt;input id=&quot;input&quot; type=&quot;text&quot; value=&quot;World&quot; /&amp;gt;
&amp;lt;p&amp;gt;Hello &amp;lt;x-signal&amp;gt;World&amp;lt;/x-signal&amp;gt;!&amp;lt;/p&amp;gt;

&amp;lt;script&amp;gt;
	const input = document.querySelector(&apos;input&apos;);
	const name = document.querySelector(&apos;x-signal&apos;);
	input.addEventListener(&apos;input&apos;, () =&amp;gt; {
		name.state = input.value;
	});
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;hello-world&amp;gt;
&amp;lt;label for=&quot;input&quot;&amp;gt;Enter name:&amp;lt;/label&amp;gt;
&amp;lt;input id=&quot;input&quot; type=&quot;text&quot; value=&quot;World&quot; /&amp;gt;
&amp;lt;p&amp;gt;Hello &amp;lt;x-signal&amp;gt;World&amp;lt;/x-signal&amp;gt;!&amp;lt;/p&amp;gt;
&amp;lt;/hello-world&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h2&gt;HTML-based state&lt;/h2&gt;
&lt;p&gt;At this point, some of you may have already started to notice and question: How/where is the value of these signals being initialized? To answer that we need to quickly talk through a key idea from Stellar that I worked on last year.&lt;/p&gt;
&lt;p&gt;Like many other web frameworks, the current version of Stellar ships a reactivity model that lets you declaratively define pieces of stateful UI.&lt;/p&gt;
&lt;p&gt;Unlike other web frameworks, however, state is defined a little different than what you might have experienced when using tools like React, Svelte, or Vue. In most frameworks, state is defined in JavaScript and then passed into component markup using some type of templating syntax. &lt;em&gt;In Stellar, this relationship is inversed.&lt;/em&gt; Reactive state is defined in HTML and then made accessible for manipulation in JavaScript.&lt;/p&gt;
&lt;p&gt;I call it &quot;HTML-based state.&quot;&lt;/p&gt;
&lt;p&gt;In Stellar, this means a handful custom attributes called &quot;directives&quot; can be added to elements within a Stellar component to access the reactivity model.&lt;/p&gt;
&lt;p&gt;A key part of all of this is that state is automatically initialized based on the existing text content, inner HTML, or property value of a stateful element.&lt;/p&gt;
&lt;p&gt;As an example, the following Stellar code...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;p $state=&quot;text&quot;&amp;gt;Hello world!&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...should be interpreted in the following way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The paragraph element is marked as stateful via the &lt;code&gt;$state&lt;/code&gt; directive&lt;/li&gt;
&lt;li&gt;The name of the state is &quot;text&quot; and can be accessed in JavaScript via &lt;code&gt;this.text&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The initial value of the state is the string &quot;Hello world!&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;While the syntax for &lt;code&gt;x-signal&lt;/code&gt; looks a bit different (i.e. there are no directives), HTML-based state is also a key idea of &lt;code&gt;&amp;lt;x-signal&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;p&amp;gt;&amp;lt;x-signal&amp;gt;Hello world&amp;lt;x-signal&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;script&amp;gt;
	const text = document.querySelector(&apos;x-signal&apos;);
	console.log(text.state); // Logs &quot;Hello world&quot;
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You are literally encoding the initial value of your signal into HTML itself.&lt;/p&gt;
&lt;h2&gt;Let&apos;s do some coercion&lt;/h2&gt;
&lt;p&gt;Okay great, we can initialize a signal but we still have that pesky problem of text content being returned as a string in JavaScript.&lt;/p&gt;
&lt;p&gt;In Stellar, all reactive state is coerced to the correct data type when being accessed in JavaScript. For example, if you were trying to define a &lt;code&gt;count&lt;/code&gt; state initialized with the value 0 it will be coerced into type &lt;code&gt;number&lt;/code&gt;. So let&apos;s just do the same for &lt;code&gt;&amp;lt;x-signal&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We&apos;ll also add the ability to set initial state via a &lt;code&gt;state&lt;/code&gt; attribute which will be useful later when working with arrays and objects.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// NEW: Function for coercing a value to it&apos;s correct data type
function coerce(value) {
	if (!value) return;
	if (value === &apos;false&apos; || value === &apos;true&apos;) {
		return value === &apos;true&apos;;
	}
	if (!isNaN(Number(value))) {
		return Number(value);
	}
	try {
		// Switch single quotes to double quotes, otherwise
		// JSON.parse will fail to parse what should be valid JSON
		const correctedValue = value.replace(/&apos;/g, &apos;&quot;&apos;);
		const parsed = JSON.parse(correctedValue);
		if (Array.isArray(parsed)) return parsed;
		if (typeof parsed === &apos;object&apos;) return parsed;
	} catch (e) {
		// Not valid JSON, return the original value
	}
	return value;
}

class SignalElement extends HTMLElement {
	constructor() {
		super();
		// UPDATED: Coerce the text content of the element and
		// check for a state attribute
		const initial =
			coerce(this.getAttribute(&apos;state&apos;)) || coerce(this.textContent);
		this.signal = new Signal.State(initial);
		this.cleanup = effect(() =&amp;gt; this.render());
	}
	render() {
		// UPDATED: Coerce signal to a string before rendering it
		this.textContent = `${this.signal.get()}`;
	}
	// ... other methods ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And with those changes, we now can implement the next classic hello world of reactive UI programming.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button&amp;gt;Count is &amp;lt;x-signal&amp;gt;0&amp;lt;/x-signal&amp;gt;&amp;lt;/button&amp;gt;

&amp;lt;script&amp;gt;
	const button = document.querySelector(&apos;button&apos;);
	const count = document.querySelector(&apos;x-signal&apos;);
	button.addEventListener(&apos;click&apos;, () =&amp;gt; {
		count.state = count.state + 1;
	});
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;counter-button&amp;gt;
&amp;lt;button&amp;gt;Count is &amp;lt;x-signal&amp;gt;0&amp;lt;/x-signal&amp;gt;&amp;lt;/button&amp;gt;
&amp;lt;/counter-button&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h2&gt;Rendering HTML&lt;/h2&gt;
&lt;p&gt;Next on the list of useful features that are commonly needed for reactive UI programming is the ability to reactively update the inner HTML of an element instead of the text content.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class SignalElement extends HTMLElement {
	constructor() {
		super();
		// NEW: A flag for checking if the render attribute is set
		this.isHTML = this.getAttribute(&apos;render&apos;) === &apos;html&apos;;
		// UPDATED: Handle HTML rendering
		const initial = this.isHTML
			? coerce(this.getAttribute(&apos;state&apos;)) || this.innerHTML
			: coerce(this.getAttribute(&apos;state&apos;)) || coerce(this.textContent);
		// ... other code ...
	}
	render() {
		// UPDATED: Handle HTML rendering
		if (this.isHTML) {
			this.innerHTML = this.signal.get();
		} else {
			this.textContent = `${this.signal.get()}`;
		}
	}
	// ... other methods ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now we have the pieces needed to create a basic markdown editor/renderer with surprisingly little code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;markdown-editor&amp;gt;
	&amp;lt;textarea&amp;gt;# Hello markdown!&amp;lt;/textarea&amp;gt;
	&amp;lt;x-signal render=&quot;html&quot;&amp;gt;
		&amp;lt;h1&amp;gt;Hello markdown!&amp;lt;/h1&amp;gt;
	&amp;lt;/x-signal&amp;gt;
&amp;lt;/markdown-editor&amp;gt;

&amp;lt;script&amp;gt;
	import { marked } from &apos;https://unpkg.com/marked@9.0.3/lib/marked.esm.js&apos;;
	const editor = document.querySelector(&apos;textarea&apos;);
	const output = document.querySelector(&apos;x-signal&apos;);
	editor.addEventListener(&apos;input&apos;, () =&amp;gt; {
		output.state = marked(editor.value);
	});
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;p class=&quot;info&quot;&amp;gt;This example includes a second custom element (&amp;lt;code&amp;gt;markdown-editor&amp;lt;/code&amp;gt;), but no JavaScript definition for that element. In this case, we&apos;ve created a &amp;lt;a href=&quot;https://hawkticehurst.com/2024/11/css-web-components-for-marketing-sites/&quot;&amp;gt;CSS Web Component&amp;lt;/a&amp;gt; to wrap the text area and signal for the purpose of adding styles.&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;markdown-editor&amp;gt;
&amp;lt;textarea&amp;gt;# Hello markdown!&amp;lt;/textarea&amp;gt;
&amp;lt;x-signal render=&quot;html&quot;&amp;gt;
&amp;lt;h1&amp;gt;Hello markdown!&amp;lt;/h1&amp;gt;
&amp;lt;/x-signal&amp;gt;
&amp;lt;/markdown-editor&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h2&gt;Rendering state in a custom way&lt;/h2&gt;
&lt;p&gt;So far we&apos;ve seen examples of when the underlying state is perfectly reflected to the DOM. But what about when the underlying data/state is different than how that data/state is rendered?&lt;/p&gt;
&lt;p&gt;We can add the ability to pass in a callback that will mutate how the data contained in a signal is rendered.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class SignalElement extends HTMLElement {
	constructor() {
		super();
		// UPDATED: Initialize mutation property and
		// add a default callback that does nothing
		this.mutation = (state) =&amp;gt; state;
		// ... other code ...
	}
	render() {
		// UPDATED: Mutate state before rendering
		const value = this.mutation(this.signal.get());
		if (this.isHTML) {
			this.innerHTML = value;
		} else {
			this.textContent = `${value}`;
		}
	}
	// NEW: Add a method to set the custom mutation callback
	set customRenderer(callback) {
		this.mutation = callback;
		this.render();
	}
	// ... other methods ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;p class=&quot;info&quot;&amp;gt;I personally don&apos;t like the property name &amp;lt;code&amp;gt;customRenderer&amp;lt;/code&amp;gt;, but I couldn&apos;t think of something better. If you have better ideas please do suggest them!&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;Here&apos;s an example of rendering text content in a custom way based on the values in an array passed to the &lt;code&gt;state&lt;/code&gt; attribute we implemented earlier.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;p&amp;gt;
	&amp;lt;x-signal id=&quot;numbers&quot; state=&quot;[1,2,3,4]&quot;&amp;gt;1 + 2 + 3 + 4&amp;lt;/x-signal&amp;gt; =
	&amp;lt;x-signal id=&quot;sum&quot;&amp;gt;10&amp;lt;/x-signal&amp;gt;
&amp;lt;/p&amp;gt;
&amp;lt;button&amp;gt;Add number&amp;lt;/button&amp;gt;

&amp;lt;script&amp;gt;
	const button = document.querySelector(&apos;button&apos;);
	const numbers = document.querySelector(&apos;#numbers&apos;);
	const sum = document.querySelector(&apos;#sum&apos;);
	numbers.customRenderer = (state) =&amp;gt; {
		return state.join(&apos; + &apos;);
	};
	button.addEventListener(&apos;click&apos;, () =&amp;gt; {
		numbers.state = [...numbers.state, numbers.state.length + 1];
		sum.state = numbers.state.reduce((s, c) =&amp;gt; s + c, 0);
	});
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;add-numbers&amp;gt;
&amp;lt;p&amp;gt;
&amp;lt;x-signal id=&quot;numbers&quot; state=&quot;[1,2,3,4]&quot;&amp;gt;1 + 2 + 3 + 4&amp;lt;/x-signal&amp;gt; =
&amp;lt;x-signal id=&quot;sum&quot;&amp;gt;10&amp;lt;/x-signal&amp;gt;
&amp;lt;/p&amp;gt;
&amp;lt;button&amp;gt;Add number&amp;lt;/button&amp;gt;
&amp;lt;/add-numbers&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;Here&apos;s an example of rendering inner HTML in a custom way to create a todo list.. or at least a partial implementation of one.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;label for=&quot;todo-input&quot;&amp;gt;Add todo:&amp;lt;/label&amp;gt;
&amp;lt;div&amp;gt;
	&amp;lt;input id=&quot;todo-input&quot; type=&quot;text&quot; /&amp;gt;
	&amp;lt;button&amp;gt;Add&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;x-signal state=&quot;[&apos;Get groceries&apos;]&quot; render=&quot;html&quot;&amp;gt;
	&amp;lt;ul&amp;gt;
		&amp;lt;li&amp;gt;Get groceries&amp;lt;/li&amp;gt;
	&amp;lt;/ul&amp;gt;
&amp;lt;/x-signal&amp;gt;

&amp;lt;script&amp;gt;
	const input = document.querySelector(&apos;input&apos;);
	const button = document.querySelector(&apos;button&apos;);
	const todos = document.querySelector(&apos;x-signal&apos;);
	todos.customRenderer = (state) =&amp;gt; {
		return `&amp;lt;ul&amp;gt;
			${state.map((todo) =&amp;gt; `&amp;lt;li&amp;gt;${todo}&amp;lt;/li&amp;gt;`).join(&apos;&apos;)}
		&amp;lt;/ul&amp;gt;`;
	};
	function addTodo() {
		if (input.value.length &amp;gt; 0) {
			todos.state = [...todos.state, input.value];
			input.value = &apos;&apos;;
		}
	}
	input.addEventListener(&apos;keydown&apos;, (event) =&amp;gt; {
		if (event.key === &apos;Enter&apos;) {
			addTodo();
		}
	});
	button.addEventListener(&apos;click&apos;, addTodo);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;todo-list&amp;gt;
&amp;lt;label for=&quot;todo-input&quot;&amp;gt;Add todo:&amp;lt;/label&amp;gt;
&amp;lt;div class=&quot;todo-input-container&quot;&amp;gt;
&amp;lt;input id=&quot;todo-input&quot; type=&quot;text&quot; /&amp;gt;
&amp;lt;button id=&quot;todo-btn&quot;&amp;gt;Add&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;x-signal id=&quot;todos&quot; state=&quot;[&apos;Get groceries&apos;]&quot; render=&quot;html&quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Get groceries&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/x-signal&amp;gt;
&amp;lt;/todo-list&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h2&gt;Derived state&lt;/h2&gt;
&lt;p&gt;So far we&apos;ve only covered a very basic usage of signals, but what about derived/computed state?&lt;/p&gt;
&lt;p&gt;We can use &lt;code&gt;Signal.Computed&lt;/code&gt; provided by the signal polyfill to create a &lt;code&gt;computed&lt;/code&gt; property.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class SignalElement extends HTMLElement {
	// ... other methods ...
	set computed(callback) {
		// Clean up existing signal
		this.cleanup();
		// Reset existing signal with a computed signal
		this.signal = new Signal.Computed(callback);
		// Redefine effect code for this new computed signal
		this.cleanup = effect(() =&amp;gt; this.render());
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now we can pass a callback to that property that will correctly derive new state based on the value of existing state.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button&amp;gt;Count is &amp;lt;x-signal id=&quot;count&quot;&amp;gt;0&amp;lt;/x-signal&amp;gt;&amp;lt;/button&amp;gt;
&amp;lt;p&amp;gt;Doubled: &amp;lt;x-signal id=&quot;doubled&quot;&amp;gt;0&amp;lt;/x-signal&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;script&amp;gt;
	const button = document.querySelector(&apos;button&apos;);
	const count = document.querySelector(&apos;#count&apos;);
	const doubled = document.querySelector(&apos;#doubled&apos;);
	button.addEventListener(&apos;click&apos;, () =&amp;gt; {
		count.state = count.state + 1;
	});
	doubled.computed = () =&amp;gt; count.state * 2;
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;double-count&amp;gt;
&amp;lt;button&amp;gt;Count is &amp;lt;x-signal id=&quot;count&quot;&amp;gt;0&amp;lt;/x-signal&amp;gt;&amp;lt;/button&amp;gt;
&amp;lt;p&amp;gt;Doubled: &amp;lt;x-signal id=&quot;doubled&quot;&amp;gt;0&amp;lt;/x-signal&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/double-count&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;And with that, we&apos;ve completed an implementation of &lt;code&gt;&amp;lt;x-signal&amp;gt;&lt;/code&gt;!&lt;/p&gt;
&lt;h2&gt;What did we miss?&lt;/h2&gt;
&lt;p&gt;There&apos;s certainly a fair few edge cases and questions that were missed in this article, but I&apos;ll cover two of them now.&lt;/p&gt;
&lt;h3&gt;Reactive properties&lt;/h3&gt;
&lt;p&gt;In Stellar and other reactive frameworks you have the ability to define reactive properties. For example, the &lt;code&gt;value&lt;/code&gt; property on a slider/range element. While I haven&apos;t gotten around to implementing it, I believe it should be possible to wrap an HTML element with &lt;code&gt;&amp;lt;x-signal&amp;gt;&lt;/code&gt; and reactively track a property of that element.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;x-signal track=&quot;value&quot;&amp;gt;
	&amp;lt;input type=&quot;range&quot; value=&quot;1&quot; /&amp;gt;
&amp;lt;x-signal&amp;gt;

&amp;lt;script&amp;gt;
	const slider = document.querySelector(&quot;x-signal&quot;);
	console.log(slider.state) // Logs 1
	slider.state = 8;
	console.log(slider.state) // Logs 8
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;State that starts in the server&lt;/h3&gt;
&lt;p&gt;Another idea worth calling out is that while the examples in this article demonstrate handwriting state directly inside HTML elements, the real use case is to declare initial state in your server and pass the state into &lt;code&gt;&amp;lt;x-signal&amp;gt;&lt;/code&gt; using the templating language of your server framework.&lt;/p&gt;
&lt;p&gt;When used in a framework like Astro, initial element state can start in the server, be encoded directly into your HTML, and then be seamlessly hydrated on the client without any layout shift issues or flashes of new content once JavaScript is parsed and executed.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
// Server data
const data = [1, 2, 3, 4];
---

&amp;lt;add-numbers&amp;gt;
	&amp;lt;p&amp;gt;
		&amp;lt;x-signal id=&quot;numbers&quot; state={JSON.stringify(data)}&amp;gt;
			{data.join(&apos; + &apos;)}
		&amp;lt;/x-signal&amp;gt; =
		&amp;lt;x-signal id=&quot;sum&quot;&amp;gt;
			{data.reduce((s, c) =&amp;gt; s + c, 0)}
		&amp;lt;/x-signal&amp;gt;
	&amp;lt;/p&amp;gt;
	&amp;lt;button&amp;gt;Add number&amp;lt;/button&amp;gt;
&amp;lt;/add-numbers&amp;gt;

&amp;lt;script&amp;gt;
	import &apos;./signal-element.js&apos;;
	// Add numbers client code
&amp;lt;/script&amp;gt;

&amp;lt;style&amp;gt;
	/* Styles */
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Islets architecture&lt;/h2&gt;
&lt;p&gt;To close out, I&apos;ll end this article with one more thought.&lt;/p&gt;
&lt;p&gt;As I&apos;ve discussed a few times before, custom elements are the &lt;em&gt;perfect&lt;/em&gt; technology for implementing the &lt;a href=&quot;https://www.patterns.dev/vanilla/islands-architecture/&quot;&gt;islands architecture&lt;/a&gt;. Each custom element becomes it&apos;s own island of interactivity within a larger sea of static HTML.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;&amp;lt;x-signal&amp;gt;&lt;/code&gt; I think we&apos;ve stumbled upon an even lower level to this idea. An island that contains and reactively renders a single unit of data, such as a number, a string, an array, a snippet of HTML, and so on.&lt;/p&gt;
&lt;p&gt;In a sea of static HTML and islands, we&apos;ve discovered a tiny island... an islet.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;If you want to see the full implementation of &lt;code&gt;&amp;lt;x-signal&amp;gt;&lt;/code&gt; or try it for yourself, I&apos;ve uploaded a &lt;a href=&quot;https://github.com/hawkticehurst/stellar/blob/vnext/signal-element.js&quot;&gt;self-contained implementation&lt;/a&gt; to the &lt;code&gt;vnext&lt;/code&gt; branch of the Stellar repo. If you run into any questions or bugs feel free to open an issue or [at] me on &lt;a href=&quot;https://bsky.app/profile/hawkticehurst.com&quot;&gt;Bluesky&lt;/a&gt; or &lt;a href=&quot;https://fosstodon.org/@hawkticehurst&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;script type=&quot;module&quot;&amp;gt;
import &quot;/elements/signal-element.js&quot;;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const todoInput = document.querySelector(&quot;#todo-input&quot;);
const todoBtn = document.querySelector(&quot;#todo-btn&quot;);
const todos = document.querySelector(&quot;#todos&quot;);
function addTodo() {
	if (todoInput.value.length &amp;gt; 0) {
		todos.state = [...todos.state, todoInput.value];
		todoInput.value = &quot;&quot;;
	}
}
todos.customRenderer = (state) =&amp;gt; {
	return `&amp;lt;ul&amp;gt;${state.map(todo =&amp;gt; `&amp;lt;li&amp;gt;${todo}&amp;lt;/li&amp;gt;`).join(&quot;&quot;)}&amp;lt;/ul&amp;gt;`;
};
todoInput.addEventListener(&apos;keydown&apos;, (event) =&amp;gt; {
	if (event.key === &apos;Enter&apos;) {
		addTodo();
	}
});
todoBtn.addEventListener(&quot;click&quot;, addTodo);

const counterBtn = document.querySelector(&apos;counter-button button&apos;);
const count = document.querySelector(&apos;counter-button button x-signal&apos;);
counterBtn.addEventListener(&apos;click&apos;, () =&amp;gt; {
	count.state = count.state + 1;
});

const input = document.querySelector(&apos;hello-world input&apos;);
const name = document.querySelector(&apos;hello-world x-signal&apos;);
input.addEventListener(&apos;input&apos;, () =&amp;gt; {
	name.state = input.value;
});

import { marked } from &apos;https://unpkg.com/marked@9.0.3/lib/marked.esm.js&apos;;
const editor = document.querySelector(&apos;markdown-editor textarea&apos;);
const output = document.querySelector(&apos;markdown-editor x-signal&apos;);
editor.addEventListener(&apos;input&apos;, () =&amp;gt; {
	output.state = marked(editor.value);
});

const button = document.querySelector(&apos;add-numbers button&apos;);
const numbers = document.querySelector(&apos;add-numbers #numbers&apos;);
const sum = document.querySelector(&apos;add-numbers #sum&apos;);
numbers.customRenderer = (value) =&amp;gt; {
	return value.join(&apos; + &apos;);
};
button.addEventListener(&apos;click&apos;, () =&amp;gt; {
	numbers.state = [...numbers.state, numbers.state.length + 1];
	sum.state = numbers.state.reduce((s, c) =&amp;gt; s + c, 0);
});

const doubleBtn = document.querySelector(&quot;double-count button&quot;);
const doubleCount = document.querySelector(&quot;double-count #count&quot;);
const doubled = document.querySelector(&quot;double-count #doubled&quot;);
doubleBtn.addEventListener(&quot;click&quot;, () =&amp;gt; {
	doubleCount.state = doubleCount.state + 1;
});
doubled.computed = () =&amp;gt; doubleCount.state * 2;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;style&amp;gt;
:root {
--border-color: var(--code-block-outline);
--component-background: var(--code-block-background);
--component-background-hover: var(--nav-hover-background);
}
button {
font-family: inherit;
font-size: inherit;
font-weight: inherit;
color: inherit;
background-color: var(--component-background);
border: 2px solid var(--border-color);
border-radius: 8px;
padding: 6px 10px;
touch-action: manipulation;
}
button:hover {
cursor: pointer;
background-color: var(--component-background-hover);
}
input {
font-family: inherit;
font-size: inherit;
font-weight: inherit;
color: inherit;
background-color: var(--component-background);
border: 2px solid var(--border-color);
border-radius: 6px;
padding: 6px 10px;
}
add-numbers {
display: flex;
flex-direction: column;
align-items: center;
}
add-numbers p {
margin-top: 0;
}
hello-world {
display: flex;
flex-direction: column;
}
hello-world &amp;gt; label {
margin-bottom: 0.5rem;
}
hello-world &amp;gt; p {
font-size: 1.4rem;
margin: 1rem 0 0 0;
}
markdown-editor {
display: flex;
flex-direction: row;
align-items: flex-start;
border: 2px solid var(--border-color);
border-radius: 6px;
padding: 0;
width: 100%;
height: 300px;
}
markdown-editor &amp;gt; textarea {
resize: none;
font-family: monospace;
font-size: 1.05rem;
font-weight: inherit;
color: inherit;
background-color: var(--component-background);
border: none;
border-right-style: solid;
border-right-width: 2px;
border-right-color: var(--border-color);
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
padding: 10px;
width: 50%;
height: 100%;
overflow: scroll;
}
markdown-editor &amp;gt; textarea:focus-visible {
outline: none;
}
markdown-editor &amp;gt; x-signal {
font-size: inherit;
font-weight: inherit;
color: inherit;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
padding: 10px 20px;
width: 50%;
height: 100%;
overflow: scroll;
}
@media (max-width: 650px) {
markdown-editor {
flex-direction: column;
height: 400px;
}
markdown-editor &amp;gt; textarea,
markdown-editor &amp;gt; x-signal {
width: 100%;
}
markdown-editor &amp;gt; textarea {
border-right-width: 0;
border-top-right-radius: 6px;
border-bottom-left-radius: 0px;
}
}
stop-watch {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px 10px;
}
stop-watch &amp;gt; x-signal {
font-size: 1.8rem;
grid-column: span 2;
margin: 0;
}
double-count p {
margin-bottom: 0;
text-align: center;
}
todo-list {
display: flex;
flex-direction: column;
gap: 1rem;
}
todo-list .todo-input-container {
display: flex;
align-items: center;
gap: 10px;
}
todo-list ul {
margin: 0px;
padding-inline-start: 15px;
}
slider-math {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
slider-math label {
display: flex;
align-items: center;
gap: 10px;
}
slider-math input[type=&apos;number&apos;] {
font-family: inherit;
font-size: inherit;
font-weight: inherit;
color: inherit;
background-color: var(--component-background);
border: 2px solid var(--border-color);
border-radius: 6px;
padding: 6px 10px;
}
:root {
--slider-track-color: var(--text-secondary);
--slider-track-radius: 4px;
--slider-track-height: 6px;
--slider-thumb-color: var(--text-primary);
--slider-thumb-size: 14px;
--slider-thumb-focus: 0 0 0 8px rgba(128, 128, 128, 0.2);
}
slider-math input[type=&apos;range&apos;] {
-webkit-appearance: none;
appearance: none;
background: transparent;
cursor: pointer;
width: 200px;
}
slider-math input[type=&apos;range&apos;]:focus {
outline: none;
}
slider-math input[type=&apos;range&apos;]::-webkit-slider-runnable-track {
background-color: var(--slider-track-color);
border-radius: var(--slider-track-radius);
height: var(--slider-track-height);
}
slider-math input[type=&apos;range&apos;]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
margin-top: -4px;
background-color: var(--slider-thumb-color);
border-radius: 50%;
height: var(--slider-thumb-size);
width: var(--slider-thumb-size);
}
slider-math input[type=&apos;range&apos;]:focus::-webkit-slider-thumb {
box-shadow: var(--slider-thumb-focus);
}
slider-math input[type=&apos;range&apos;]::-moz-range-track {
background-color: var(--slider-track-color);
border-radius: var(--slider-track-radius);
height: var(--slider-track-height);
}
slider-math input[type=&apos;range&apos;]::-moz-range-thumb {
border: none;
border-radius: 50%;
background-color: var(--slider-thumb-color);
height: var(--slider-thumb-size);
width: var(--slider-thumb-size);
}
slider-math input[type=&apos;range&apos;]:focus::-moz-range-thumb {
box-shadow: var(--slider-thumb-focus);
}
&amp;lt;/style&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>CSS Web Components for marketing sites</title><link>https://hawkticehurst.com/2024/11/css-web-components-for-marketing-sites/</link><guid isPermaLink="true">https://hawkticehurst.com/2024/11/css-web-components-for-marketing-sites/</guid><description>The truly No JavaScript web component.</description><pubDate>Mon, 04 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hot take: I think &quot;regular&quot; web components (the ones with Shadow DOM and friends) are a terrible solution for marketing website design systems.&lt;/p&gt;
&lt;p&gt;It has always left a bad taste in my mouth when I run across a web component for a swimlane, banner, card, and so on. Why? Because these are components that (unless you&apos;re doing something mighty fancy) should never require JavaScript as a dependency.&lt;/p&gt;
&lt;p&gt;But, in the world of web components you are locked into JavaScript from the very start. To even register a web component with the browser you need JavaScript.&lt;/p&gt;
&lt;p&gt;But what if... we didn&apos;t do that?&lt;/p&gt;
&lt;h2&gt;HTML Web Components&lt;/h2&gt;
&lt;p&gt;I&apos;ve spent a good chunk of the last year focused on marketing site design systems at work. A regular topic of discussion is the need to build marketing sites that are accessible to folks with lower powered devices and poor internet connections. How do you achieve that? In short, use less JavaScript and ideally build UI with progressive enhancement in mind.&lt;/p&gt;
&lt;p&gt;There are many ways to achieve these goals, but the method I&apos;ve been focused on is how an &lt;a href=&quot;https://hawkticehurst.com/2023/11/a-year-working-with-html-web-components/&quot;&gt;HTML Web Component&lt;/a&gt; archictecture might be applied to implement a marketing site design system.&lt;/p&gt;
&lt;p&gt;As a quick reminder/intro, HTML Web Components is a method of building web components where you write HTML as you would normally and then wrap the parts you want to be interactive using a custom element.&lt;/p&gt;
&lt;p&gt;For example, if you wanted to create a counter button it would look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;counter-button&amp;gt;
  &amp;lt;button&amp;gt;Count is &amp;lt;span&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
&amp;lt;/counter-button&amp;gt;

&amp;lt;style&amp;gt;
  counter-button button {
    /* Counter button styles */
  }
&amp;lt;/style&amp;gt;

&amp;lt;script&amp;gt;
  class Counter extends HTMLElement {
    // Counter button behavior
  }
  customElements.define(&quot;counter-button&quot;, Counter);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The markup in an HTML web component is parsed, rendered, and styled as normal HTML. That HTML will then be seamlessly hydrated once the JavaScript associated with the custom element tag is executed.&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;info&quot;&amp;gt;
In contrast, the markup of a &quot;regular&quot; web component (that uses Shadow DOM) is dynamically generated at runtime using JavaScript -- kind of like an SPA.
&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;This component architecture is a really strong candidate for a marketing design system (and, as a bonus, avoids some of the big gotchas that come with regular web components).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is a perfect implementation of progressively enhanced UI&lt;/li&gt;
&lt;li&gt;It uses minimal and self-contained JavaScript -- HTML Web Components can be thought of as &lt;a href=&quot;https://jasonformat.com/islands-architecture/&quot;&gt;islands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;You still get the power of custom element APIs to implement stuff like design system component variants&lt;/li&gt;
&lt;li&gt;The component markup is fully SSR-able&lt;/li&gt;
&lt;li&gt;The component markup can be styled like regular HTML&lt;/li&gt;
&lt;li&gt;Common accessibility practices can be applied without issue&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But for all these benefits we&apos;re still left with the original problem. HTML Web Components require JavaScript.&lt;/p&gt;
&lt;h2&gt;CSS Web Components&lt;/h2&gt;
&lt;p&gt;So here&apos;s the question: What would happen if we took the ideas of HTML Web Components and skipped all the JavaScript?&lt;/p&gt;
&lt;p&gt;You get CSS Web Components.&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;info&quot;&amp;gt;Note: I&apos;ve never seen anyone talk about or name this concept before, so I&apos;m using &quot;CSS Web Components&quot; to describe the idea. But please let me know if someone has already written about and named this!&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;How do they work? The exact same as HTML Web Components but you just take advantage of the powers of CSS to implement key functionality.&lt;/p&gt;
&lt;p&gt;As an example let&apos;s implement that swimlane component:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;swim-lane&amp;gt;
  &amp;lt;section&amp;gt;
    &amp;lt;h2&amp;gt;Creativity unleashed&amp;lt;/h2&amp;gt;
    &amp;lt;p&amp;gt;A brand new way of illustrating for the web.&amp;lt;/p&amp;gt;
    &amp;lt;a href=&quot;/product&quot;&amp;gt;Learn more&amp;lt;/a&amp;gt;
  &amp;lt;/section&amp;gt;
  &amp;lt;img src=&quot;product.jpg&quot; alt=&quot;Product image&quot; /&amp;gt;
&amp;lt;/swim-lane&amp;gt;

&amp;lt;style&amp;gt;
  swim-lane {
    display: flex;
    align-items: center;
    gap: 2rem;
    color: white;
    background: black;
    padding: 1rem;
    border-radius: 16px;
  }
  swim-lane h2 {
    /* Swimlane title styles */
  }
  swim-lane p {
    /* Swimlane description styles */
  }
  swim-lane a {
    /* Swimlane link styles */
  }
  @media (max-width: 650px) {
    /* Mobile responsive styles */
  }
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;swim-lane&amp;gt;
&amp;lt;section&amp;gt;
&amp;lt;h2&amp;gt;Creativity unleashed&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;A brand new way of illustrating for the web.&amp;lt;/p&amp;gt;
&amp;lt;a href=&quot;#&quot;&amp;gt;Learn more&amp;lt;/a&amp;gt;
&amp;lt;/section&amp;gt;
&amp;lt;img src=&quot;/imgs/ephemeral-canvas.jpg&quot; alt=&quot;Product image&quot;&amp;gt;
&amp;lt;/swim-lane&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;Okay great, we styled some HTML nested inside a custom element. There&apos;s nothing too novel about that. But what about adding some functionality? Say, a component variant that lets you reverse the layout of the swimlane?&lt;/p&gt;
&lt;p&gt;It&apos;s possible using only CSS! Specifically, CSS attribute selectors.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;swim-lane layout=&quot;reverse&quot;&amp;gt;
  &amp;lt;section&amp;gt;
    &amp;lt;h2&amp;gt;Creativity unleashed&amp;lt;/h2&amp;gt;
    &amp;lt;p&amp;gt;A brand new way of illustrating for the web.&amp;lt;/p&amp;gt;
    &amp;lt;a href=&quot;/product&quot;&amp;gt;Learn more&amp;lt;/a&amp;gt;
  &amp;lt;/section&amp;gt;
  &amp;lt;img src=&quot;product.jpg&quot; alt=&quot;Product image&quot; /&amp;gt;
&amp;lt;/swim-lane&amp;gt;

&amp;lt;style&amp;gt;
  /* Other swimlane styles */
  swim-lane[layout=&quot;reverse&quot;] {
    flex-direction: row-reverse;
  }
  @media (max-width: 650px) {
    swim-lane[layout=&quot;reverse&quot;] {
      flex-direction: column-reverse;
    }
  }
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;swim-lane layout=&quot;reverse&quot;&amp;gt;
&amp;lt;section&amp;gt;
&amp;lt;h2&amp;gt;Creativity unleashed&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;A brand new way of illustrating for the web.&amp;lt;/p&amp;gt;
&amp;lt;a href=&quot;#&quot;&amp;gt;Learn more&amp;lt;/a&amp;gt;
&amp;lt;/section&amp;gt;
&amp;lt;img src=&quot;/imgs/ephemeral-canvas.jpg&quot; alt=&quot;Product image&quot;&amp;gt;
&amp;lt;/swim-lane&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;Another really cool perk of this is that because you&apos;re defining an attribute on a custom element you don&apos;t have to worry about naming collisions with HTML attributes. No need to add &lt;code&gt;data-&lt;/code&gt; to the beginning of attributes like you would/should on normal HTML elements.&lt;/p&gt;
&lt;h2&gt;How far does this go?&lt;/h2&gt;
&lt;p&gt;In theory, I believe this method of building design systems can go quite far. If you think about it, the vast majority of basic components you might need in a marketing design system are just vanilla HTML elements with specific style variations.&lt;/p&gt;
&lt;p&gt;A marketing website button is just an anchor tag wrapped in a &lt;code&gt;&amp;lt;link-button&amp;gt;&lt;/code&gt; custom element and styled using custom attribute selectors.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;link-button&amp;gt;
  &amp;lt;a href=&quot;&quot;&amp;gt;Learn more&amp;lt;/a&amp;gt;
&amp;lt;/link-button&amp;gt;
&amp;lt;link-button variant=&quot;secondary&quot;&amp;gt;
  &amp;lt;a href=&quot;&quot;&amp;gt;Learn more&amp;lt;/a&amp;gt;
&amp;lt;/link-button&amp;gt;
&amp;lt;link-button pill&amp;gt;
  &amp;lt;a href=&quot;&quot;&amp;gt;Learn more&amp;lt;/a&amp;gt;
&amp;lt;/link-button&amp;gt;
&amp;lt;link-button size=&quot;large&quot;&amp;gt;
  &amp;lt;a href=&quot;&quot;&amp;gt;Learn more&amp;lt;/a&amp;gt;
&amp;lt;/link-button&amp;gt;

&amp;lt;style&amp;gt;
  link-button a {
    /* Default link button styles */
  }
  link-button[variant=&quot;secondary&quot;] a {
    background: transparent;
    color: white;
  }
  link-button[pill] a {
    border-radius: 50px;
  }
  link-button[size=&quot;large&quot;] a {
    padding: 10px 20px;
    font-size: 1.25rem;
  }
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;section class=&quot;button-container&quot;&amp;gt;
&amp;lt;link-button&amp;gt;
&amp;lt;a href=&quot;#&quot;&amp;gt;Learn more&amp;lt;/a&amp;gt;
&amp;lt;/link-button&amp;gt;
&amp;lt;link-button variant=&quot;secondary&quot;&amp;gt;
&amp;lt;a href=&quot;#&quot;&amp;gt;Learn more&amp;lt;/a&amp;gt;
&amp;lt;/link-button&amp;gt;
&amp;lt;link-button pill&amp;gt;
&amp;lt;a href=&quot;#&quot;&amp;gt;Learn more&amp;lt;/a&amp;gt;
&amp;lt;/link-button&amp;gt;
&amp;lt;link-button size=&quot;large&quot;&amp;gt;
&amp;lt;a href=&quot;#&quot;&amp;gt;Learn more&amp;lt;/a&amp;gt;
&amp;lt;/link-button&amp;gt;
&amp;lt;/section&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;From here, imagine incorporating all the other powers that CSS (and HTML) bring to the table:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/css-cascade-layers/&quot;&gt;Cascade layers&lt;/a&gt; for better control of how styles get applied&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.joshwcomeau.com/css/container-queries-introduction/&quot;&gt;Container queries&lt;/a&gt; for conditional style variants based on a parent container&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/has-m105&quot;&gt;:has()&lt;/a&gt;, &lt;a href=&quot;https://web.dev/articles/css-is-and-where&quot;&gt;:is(), and :where()&lt;/a&gt; to simplify complex selectors&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties&quot;&gt;CSS variables&lt;/a&gt; for theming&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/blog/at-property-baseline&quot;&gt;@property&lt;/a&gt; rule for even more powerful CSS variables&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/light-dark&quot;&gt;light-dark()&lt;/a&gt; for system aware theming&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/blog/popover-api&quot;&gt;Popover API&lt;/a&gt; for menus, toggletips, and dialogs without JS&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/docs/css-ui/exclusive-accordion&quot;&gt;Exclusive accordions&lt;/a&gt; for FAQ sections&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The possibilities are quite large.&lt;/p&gt;
&lt;p&gt;What do you think?&lt;/p&gt;
&lt;p&gt;&amp;lt;style&amp;gt;
swim-lane {
color: white;
background: black;
padding: 1rem;
border-radius: 16px;
display: flex;
align-items: center;
gap: 2rem;
}
swim-lane section {
padding: 0.25rem;
width: 100%;
}
swim-lane h2 {
margin: 0;
font-size: 2rem;
}
swim-lane p {
margin: 0;
margin-bottom: 1.5rem;
}
swim-lane a {
background: white;
color: black;
font-weight: 500;
border-radius: 50px;
padding: 6px 14px;
text-decoration: none;
}
swim-lane img {
max-width: 50%;
}
swim-lane[layout=&quot;reverse&quot;] {
flex-direction: row-reverse;
}
link-button a {
background: var(--text-intense);
color: var(--site-background);
border: solid 1px var(--text-intense);
font-weight: 500;
border-radius: 6px;
padding: 6px 14px;
text-decoration: none;
}
link-button[variant=&quot;secondary&quot;] a {
background: transparent;
color: var(--text-intense);
}
link-button[size=&quot;large&quot;] a {
padding: 10px 20px;
font-size: 1.25rem;
}
link-button[pill] a {
border-radius: 50px;
}
.button-container {
display: flex;
align-items: center;
gap: 1rem;
}
.render-container {
width: 900px;
transform: translateX(-16%);
margin: 2rem 0;
}
@media (max-width: 950px) {
.render-container {
width: 100%;
transform: translateX(0%);
margin: 1rem 0;
}
swim-lane h2 {
font-size: 1.4rem;
}
.button-container {
flex-direction: column;
gap: 2rem;
}
}
@media (max-width: 650px) {
swim-lane {
flex-direction: column;
align-items: flex-start;
}
swim-lane h2 {
font-size: 1.6rem;
}
swim-lane img {
max-width: 100%;
}
swim-lane[layout=&quot;reverse&quot;] {
flex-direction: column-reverse;
}
.button-container {
flex-direction: column;
gap: 2rem;
}
}
@media (max-width: 450px) {
.render-container {
width: 100%;
transform: translateX(0%);
}
swim-lane h2 {
font-size: 1.25rem;
}
}
&amp;lt;/style&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Bring your own base class</title><link>https://hawkticehurst.com/2024/05/bring-your-own-base-class/</link><guid isPermaLink="true">https://hawkticehurst.com/2024/05/bring-your-own-base-class/</guid><description>Dependency free web components that last a life time does not have to mean a bad developer experience.</description><pubDate>Mon, 20 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last December I &lt;a href=&quot;https://hawkticehurst.com/2023/12/let-it-snow/&quot;&gt;started a series of blog posts&lt;/a&gt; that introduced and explored the world of web components. Holiday obligations followed by several months of wedding planning, a move, and an upcoming grad school application meant the series has had to take a pause, but today we&apos;re finally back with the seventh installment!&lt;/p&gt;
&lt;p&gt;You can thank Chris Coyier for this post. &lt;a href=&quot;https://front-end.social/@chriscoyier/112469573847186257&quot;&gt;A discussion on Mastodon&lt;/a&gt; reminded me of the topic I wanted to talk about and so despite looming deadlines and better judgement I&apos;ve been spurred to put pen to paper.&lt;/p&gt;
&lt;p&gt;The basic idea of Chris&apos;s post is that web components stand out as a frontend technology that may finally break the curse of framework dependent componentry always eventually ending up in the proverbial tech junkyard. Aka, web components have the potential to last a life time. But there&apos;s one major caveat: you must write &lt;em&gt;dependency-free&lt;/em&gt; web components (read: without a framework).&lt;/p&gt;
&lt;p&gt;Many often lament creating dependency-free web components because, for lack of better words, it&apos;s a pain. in. the. butt.&lt;/p&gt;
&lt;p&gt;But this doesn&apos;t have to be the case. Building dependency-free web components that last a life time does not have to mean a bad developer experience.&lt;/p&gt;
&lt;p&gt;Today we&apos;re talking about web component base classes.&lt;/p&gt;
&lt;h2&gt;Web components are built on inheritance&lt;/h2&gt;
&lt;p&gt;Before we jump in we need to do some quick context setting.&lt;/p&gt;
&lt;p&gt;When I say &quot;base class&quot; it&apos;s really just a casual (and slightly wrong) way of saying &lt;a href=&quot;https://javascript.info/class-inheritance&quot;&gt;class inheritance&lt;/a&gt; and is used &lt;em&gt;everywhere&lt;/em&gt; in the world of web components. Class inheritance is used to create layers of abstraction where new functionality can be built on top of existing functionality.&lt;/p&gt;
&lt;p&gt;Every single web component framework today uses a base class to expose it&apos;s behavior. In fact, even vanilla web components use class inheritance.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyComponent extends HTMLElement {}
class MyComponent extends LitElement {}
class MyComponent extends FASTElement {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above examples &lt;code&gt;HTMLElement&lt;/code&gt;, &lt;code&gt;LitElement&lt;/code&gt;, and &lt;code&gt;FASTElement&lt;/code&gt; could all be casually referred to as web component base classes.&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;info&quot;&amp;gt;Note: If we&apos;re being precise, a base class is actually defined as a class that doesn&apos;t extend anything else. So in the above example &amp;lt;code&amp;gt;HTMLElement&amp;lt;/code&amp;gt; itself is the only true &quot;base class.&quot; Under the hood classes like &amp;lt;code&amp;gt;LitElement&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;FASTElement&amp;lt;/code&amp;gt; all eventually extend &amp;lt;code&amp;gt;HTMLElement&amp;lt;/code&amp;gt;.&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;It&apos;s common to see more mature web component frameworks using a few layers of class inheritance and a few thousand lines of code to implement their functionality. For example, Lit&apos;s inheritance chain currently looks like this: &lt;code&gt;LitElement&lt;/code&gt; extends &lt;code&gt;ReactiveElement&lt;/code&gt; extends &lt;code&gt;HTMLElement&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;While it&apos;s certainly daunting to look at a wall of code like that and think you have no choice but to use a web component framework to get a good component authoring experience I&apos;m hear to say that is not at all the case.&lt;/p&gt;
&lt;h2&gt;Creating good dependency-free web component DX&lt;/h2&gt;
&lt;p&gt;Creating your own productive web component base class is actually pretty easy. Below are a few examples of how very little code can enable a desirable web component authoring experience.&lt;/p&gt;
&lt;h3&gt;Clean up the boilerplate!&lt;/h3&gt;
&lt;p&gt;The most basic example of the usefulness of a base class is to clean up all that boilerplate we try to avoid by using frameworks.&lt;/p&gt;
&lt;p&gt;In under 20 lines of code we can abstract away some of the most annoying details of building web components –– namely configuring Shadow DOM and simplifying markup creation by enforcing the use a &lt;code&gt;render&lt;/code&gt; method, similar to Lit.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class BaseElement extends HTMLElement {
	constructor() {
		super();
		this.shadow = this.attachShadow({ mode: &apos;open&apos; });
	}
	connectedCallback() {
		this._render();
	}
	_render() {
		if (!this.render) {
			throw new Error(
				&apos;Web components extending BaseElement must implement a `render` method.&apos;
			);
		}
		const markup = this.render();
		const template = document.createElement(&apos;template&apos;);
		template.innerHTML = `${markup}`;
		this.shadow.appendChild(template.content.cloneNode(true));
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here it is in action. Pretty nice right?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyComponent extends BaseElement {
	render() {
		return `&amp;lt;h1&amp;gt;Hello! I&apos;m in the Shadow DOM!&amp;lt;/h1&amp;gt;`;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;my-text&amp;gt;&amp;lt;/my-text&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h3&gt;Make it pop&lt;/h3&gt;
&lt;p&gt;How about convenient styling? Several more lines of code adds the ability to use the &lt;a href=&quot;https://web.dev/articles/constructable-stylesheets&quot;&gt;Constructable Stylesheets&lt;/a&gt; Web API with some nice fallback functionality for browsers that don&apos;t support this API.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class BaseElement extends HTMLElement {
	constructor() {
		super();
		this.shadow = this.attachShadow({ mode: &apos;open&apos; });
	}
	connectedCallback() {
		this._render();
	}
	_render() {
		if (!this.render) {
			throw new Error(
				&apos;Web components extending BaseElement must implement a `render` method.&apos;
			);
		}
		const markup = this.render();
		const styles = this.constructor.styles || &apos;&apos;;
		const template = document.createElement(&apos;template&apos;);
		const useConstructableStyleSheets =
			this.shadow.adoptedStyleSheets !== undefined;
		if (styles.length &amp;gt; 0 &amp;amp;&amp;amp; useConstructableStyleSheets) {
			const sheet = new CSSStyleSheet();
			sheet.replaceSync(styles);
			this.shadow.adoptedStyleSheets = [sheet];
		}
		template.innerHTML = `
      ${
				styles.length &amp;gt; 0 &amp;amp;&amp;amp; !useConstructableStyleSheets
					? `&amp;lt;style&amp;gt;${styles}&amp;lt;/style&amp;gt;`
					: &apos;&apos;
			}
      ${markup}
    `;
		this.shadow.appendChild(template.content.cloneNode(true));
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now we have some easy to author component styles!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyComponent extends BaseElement {
	static styles = `
		h1 { 
			color: teal;
			font-family: cursive;
			font-size: 1.4rem;
		}
	`;
	render() {
		return `&amp;lt;h1&amp;gt;Hello! I&apos;m in the Shadow DOM!&amp;lt;/h1&amp;gt;`;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;my-styled-text&amp;gt;&amp;lt;/my-styled-text&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h3&gt;Declarative interactivity&lt;/h3&gt;
&lt;p&gt;As a final example, we can use the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/createNodeIterator&quot;&gt;NodeIterator&lt;/a&gt; Web API to add custom declarative event binding syntax (i.e. &lt;code&gt;@click&lt;/code&gt;). It can handle native browser events and custom events.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class BaseElement extends HTMLElement {
	constructor() {
		super();
		this.shadow = this.attachShadow({ mode: &apos;open&apos; });
		this._listeners = [];
	}
	connectedCallback() {
		this._render();
	}
	disonnectedCallback() {
		for (const { elem, event, callback } of this._listeners) {
			elem?.removeEventListener(event, callback);
		}
	}
	_render() {
		if (!this.render) {
			throw new Error(
				&apos;Web components extending BaseElement must implement a `render` method.&apos;
			);
		}
		const markup = this.render();
		const styles = this.constructor.styles || &apos;&apos;;
		const template = document.createElement(&apos;template&apos;);
		const useConstructableStyleSheets =
			this.shadow.adoptedStyleSheets !== undefined;
		if (styles.length &amp;gt; 0 &amp;amp;&amp;amp; useConstructableStyleSheets) {
			const sheet = new CSSStyleSheet();
			sheet.replaceSync(styles);
			this.shadow.adoptedStyleSheets = [sheet];
		}
		template.innerHTML = `
			${
				styles.length &amp;gt; 0 &amp;amp;&amp;amp; !useConstructableStyleSheets
					? `&amp;lt;style&amp;gt;${styles}&amp;lt;/style&amp;gt;`
					: &apos;&apos;
			}
      ${markup}
    `;
		this.shadow.appendChild(template.content.cloneNode(true));
		this._configureEventListeners(this.shadow);
	}
	_configureEventListeners(rootNode) {
		let node;
		const nestedCustomElements = [];
		// Create a node iterator to iterate through all elements in the shadow root
		const iterator = document.createNodeIterator(
			rootNode,
			NodeFilter.SHOW_ELEMENT,
			{
				// Function for filtering out nodes we can skip
				acceptNode: (node) =&amp;gt; {
					// Reject any node that is not an HTML element
					if (!(node instanceof HTMLElement)) {
						return NodeFilter.FILTER_REJECT;
					}
					// Check if node is a nested custom element
					if (node.tagName.includes(&apos;-&apos;) &amp;amp;&amp;amp; node.tagName !== this.tagName) {
						nestedCustomElements.push(node);
						return NodeFilter.FILTER_REJECT;
					}
					// Check if node is a child of a nested custom element
					for (const nested of nestedCustomElements) {
						if (nested.contains(node)) {
							return NodeFilter.FILTER_REJECT;
						}
					}
					return NodeFilter.FILTER_ACCEPT;
				},
			}
		);
		// Iterate through all nodes in the shadow root
		while ((node = iterator.nextNode())) {
			if (!node) return;
			for (const attr of node.attributes) {
				// Check for custom event listener attributes
				if (attr.name.startsWith(&apos;@&apos;)) {
					this._processEventHandler(attr);
				}
			}
		}
		for (const { elem, event, callback } of this._listeners) {
			elem?.addEventListener(event, callback);
		}
	}
	_processEventHandler(attr) {
		const elem = attr.ownerElement;
		// Extract the name and value of the attribute
		// Example: `@click=&quot;handleClick&quot;` -&amp;gt; `click` event and `handleClick` method
		const { name: event, value: method } = attr;
		this._listeners.push({
			elem: elem,
			event: event.slice(1),
			callback: (e) =&amp;gt; this[method](e),
		});
		// Remove (non-standard) attribute from element
		elem.removeAttributeNode(attr);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And voila, declarative event binding!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyComponent extends BaseElement {
	static styles = `
		button { 
			color: white;
			background-color: rgb(95, 85, 236, 0.5);
			border: 2px solid rgb(95, 85, 236);
			border-radius: 8px;
			padding: 6px 10px;
		}
	`;
	render() {
		return `&amp;lt;button @click=&quot;helloMsg&quot;&amp;gt;Click me!&amp;lt;/button&amp;gt;`;
	}
	helloMsg() {
		alert(&apos;Hello there!&apos;);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;my-button&amp;gt;&amp;lt;/my-button&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h3&gt;So many possibilities&lt;/h3&gt;
&lt;p&gt;In less than 100 lines of code we&apos;ve built a dependency-free web component base class that I&apos;d argue gets us 80% of the way to a &quot;proper&quot; web component framework in terms of functionality and usability.&lt;/p&gt;
&lt;p&gt;Obviously, the notable missing piece from this post is reactivity. It&apos;s not too much harder to implement once you learn some of the basic concepts, but I ran out of time to go over that today. I won&apos;t leave you hanging though, here&apos;s an example of a &lt;a href=&quot;https://github.com/hawkticehurst/stellar/blob/main/index.ts&quot;&gt;reactive HTML web components implementation&lt;/a&gt; in ~250 lines of code that I made last year.&lt;/p&gt;
&lt;p&gt;Beyond that, however, there are so many possibilities for tweaking and adjusting the examples I gave above to suit your personal preferences. Perhaps you don&apos;t like &lt;code&gt;render&lt;/code&gt; as a method name or you want to add more custom attribute syntax that enables new behavior using the NodeIterator API –– the possibilities are basically endless.&lt;/p&gt;
&lt;p&gt;Go forth and build dependency-free components that are fun to write and last a life time.&lt;/p&gt;
&lt;h2&gt;I want a marketplace of base classes&lt;/h2&gt;
&lt;p&gt;To leave with a final thought, I have often dreamed about the idea of a marketplace of web component base classes.&lt;/p&gt;
&lt;p&gt;Imagine an &lt;a href=&quot;https://unjs.io/&quot;&gt;UnJS&lt;/a&gt;-like ecosystem of composable base classes that all have one tightly scoped purpose and can be freely mixed and matched to build up the ideal component authoring experience for each job.&lt;/p&gt;
&lt;p&gt;To keep in the spirit of Chris&apos;s Mastodon post, all base classes would need to be built without any dependencies and would simply be copy-pasted into each project (à la &lt;a href=&quot;https://ui.shadcn.com/&quot;&gt;shadcn/ui&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;If there&apos;s any interest in this idea, I&apos;d love to hear what the community thinks a base class contract/protocol should look like so anyone could hook into and build for this composable ecosystem. I&apos;d like to write my thoughts on this one day (and perhaps even build some of it), but first I&apos;ve got a wedding to get to 😁.&lt;/p&gt;
&lt;p&gt;Until the next one!&lt;/p&gt;
&lt;p&gt;&amp;lt;script type=&quot;module&quot;&amp;gt;
import { BaseElement } from &quot;/elements/base-element.js&quot;;
class MyText extends BaseElement {
render() {
return &lt;code&gt;&amp;lt;h1&amp;gt;Hello! I&apos;m in the Shadow DOM!&amp;lt;/h1&amp;gt;&lt;/code&gt;;
}
}
customElements.define(&apos;my-text&apos;, MyText);
class MyStyledText extends BaseElement {
static styles = &lt;code&gt; 			h1 {  				color: teal; 				font-family: cursive; 				font-size: 1.4rem; 			} 		&lt;/code&gt;;
render() {
return &lt;code&gt;&amp;lt;h1&amp;gt;Hello! I&apos;m in the Shadow DOM!&amp;lt;/h1&amp;gt;&lt;/code&gt;;
}
}
customElements.define(&apos;my-styled-text&apos;, MyStyledText);
class MyButton extends BaseElement {
static styles = &lt;code&gt; 			button {  				font-family: inherit; 				font-size: inherit; 				font-weight: 500; 				color: white; 				background-color: rgb(95, 85, 236, 0.5); 				border: 2px solid rgb(95, 85, 236); 				border-radius: 8px; 				padding: 6px 10px; 				cursor: pointer; 			} 		&lt;/code&gt;;
render() {
return &lt;code&gt;&amp;lt;button @click=&quot;helloMsg&quot;&amp;gt;Click me!&amp;lt;/button&amp;gt;&lt;/code&gt;;
}
helloMsg() {
alert(&apos;Hello there!&apos;);
}
}
customElements.define(&apos;my-button&apos;, MyButton);
&amp;lt;/script&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Web components reading list</title><link>https://hawkticehurst.com/2023/12/web-components-reading-list/</link><guid isPermaLink="true">https://hawkticehurst.com/2023/12/web-components-reading-list/</guid><description>Day six of a series of interactive posts that explores the world of web components.</description><pubDate>Mon, 18 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Welcome back to 7 Days of Web Components! A series of interactive blog posts that introduces and explores the world of web components.&lt;/p&gt;
&lt;p&gt;This is the post for day six, if you missed the previous posts I encourage you to go back to &lt;a href=&quot;https://hawkticehurst.com/2023/12/let-it-snow/&quot;&gt;day one&lt;/a&gt; and start there.&lt;/p&gt;
&lt;p&gt;Today we&apos;re taking a breather to highlight the work and thoughts of others!&lt;/p&gt;
&lt;p&gt;This reading list includes a smattering of some of my all time favorite web component blog posts and resources. It&apos;s organized in no particular order so feel free to skim and pick up one that looks interesting!&lt;/p&gt;
&lt;h2&gt;My web component reading list&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://javascript.info/web-components&quot;&gt;JavaScript.info web component articles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eisenbergeffect.medium.com/2023-state-of-web-components-c8feb21d4f16&quot;&gt;2023 State of Web Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eisenbergeffect.medium.com/debunking-web-component-myths-and-misconceptions-ea9bb13daf61&quot;&gt;Debunking Web Component Myths and Misconceptions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eisenbergeffect.medium.com/about-web-components-7b2a3ed67a78&quot;&gt;About Web Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jakelazaroff.com/words/web-components-will-outlive-your-javascript-framework/&quot;&gt;Web Components Will Outlive Your JavaScript Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jakelazaroff.com/words/web-components-eliminate-javascript-framework-lock-in/&quot;&gt;Web Components Eliminate JavaScript Framework Lock-in&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://html.spec.whatwg.org/dev/custom-elements.html#custom-elements&quot;&gt;HTML Standard (Web Developer Edition): Custom elements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webcomponents.today/&quot;&gt;Web Components bookmarks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://htmlwithsuperpowers.netlify.app/&quot;&gt;HTML with Superpowers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.abeautifulsite.net/posts/a-web-components-primer/&quot;&gt;A Web Components Primer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.jim-nielsen.com/2023/html-web-components/&quot;&gt;HTML Web Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.jim-nielsen.com/2023/web-components-icon-galleries/&quot;&gt;Using Web Components on My Icon Galleries Websites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/articles/ps-on-the-web&quot;&gt;Photoshop&apos;s journey to the web&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.blog/2021-05-04-how-we-use-web-components-at-github/&quot;&gt;How we use Web Components at GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;In the next episode...&lt;/h2&gt;
&lt;p&gt;Thanks for stopping by! I hope you find an enjoyable read and/or something new!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fosstodon.org/@hawkticehurst&quot;&gt;Follow me on Mastodon&lt;/a&gt; for updates and see you in the next one!&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Next post: &lt;a href=&quot;/2024/05/bring-your-own-base-class/&quot;&gt;Bring your own base class&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Web components in VS Code</title><link>https://hawkticehurst.com/2023/12/web-components-in-vs-code/</link><guid isPermaLink="true">https://hawkticehurst.com/2023/12/web-components-in-vs-code/</guid><description>Day five of a series of interactive posts that explores the world of web components.</description><pubDate>Sun, 17 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Welcome back to 7 Days of Web Components! A series of interactive blog posts that introduces and explores the world of web components.&lt;/p&gt;
&lt;p&gt;This is the post for day five, if you missed the previous posts I encourage you to go back to &lt;a href=&quot;https://hawkticehurst.com/2023/12/let-it-snow/&quot;&gt;day one&lt;/a&gt; and start there.&lt;/p&gt;
&lt;p&gt;Today we&apos;re talking about an IRL (in real life) open-source component library built with web components. It also happens to be a component library that I built and maintain as part of my day job at Microsoft –– it&apos;s the &lt;a href=&quot;https://github.com/microsoft/vscode-webview-ui-toolkit&quot;&gt;Webview UI Toolkit for VS Code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this post, we&apos;re going to talk through some specific examples of how this library is built and some other thoughts and musings.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Please note: This blog post includes interactive code examples. If you&apos;re reading this from an RSS feed, I highly recommend you open the &lt;a href=&quot;https://hawkticehurst.com/2023/12/web-components-in-vs-code/&quot;&gt;original post&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;The toolkit&lt;/h2&gt;
&lt;p&gt;The Webview UI Toolkit is an open-source library of web components that implement the VS Code design system. These components are made available to VS Code extension authors to help them build user interfaces for their extensions (specifically &lt;a href=&quot;https://code.visualstudio.com/api/extension-guides/webview&quot;&gt;webview-based extensions&lt;/a&gt;) that have the same look and feel as the rest of the code editor.&lt;/p&gt;
&lt;p&gt;The library has been built and maintained since early 2021 and is now used in extensions like &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=GitHub.copilot&quot;&gt;GitHub Copilot&lt;/a&gt;, &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens&quot;&gt;GitKraken&lt;/a&gt;, &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=redhat.java&quot;&gt;Red Hat Java Language Support&lt;/a&gt;, &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console&quot;&gt;Nx Console&lt;/a&gt;, &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-bicep&quot;&gt;Azure Bicep&lt;/a&gt;, and &lt;a href=&quot;https://github.com/microsoft/vscode-webview-ui-toolkit/network/dependents&quot;&gt;many other projects&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The components are built using a web component framework built by Microsoft called &lt;a href=&quot;https://fast.design&quot;&gt;FAST&lt;/a&gt;. The toolkit specifically takes advantage of &lt;a href=&quot;https://github.com/microsoft/fast/tree/master/packages/web-components/fast-foundation#readme&quot;&gt;FAST Foundation&lt;/a&gt; which is a set of unstyled base components that the FAST team builds and maintains.&lt;/p&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot; style=&quot;gap: 10px;&quot;&amp;gt;
&amp;lt;vscode-button&amp;gt;Click me!&amp;lt;/vscode-button&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h2&gt;Why web components?&lt;/h2&gt;
&lt;p&gt;In VS Code, webview extensions are a powerful way to add custom functionality beyond what the extension API supports. Under the hood, webviews are implemented using an &lt;code&gt;iframe&lt;/code&gt;, which means a webview extension is effectively an isolated webpage inside of VS Code where extension authors can build whatever they want using any technology they desire.&lt;/p&gt;
&lt;p&gt;Historically, this meant that the responsibility of developing UI which aligns with the VS Code design language and follows the &lt;a href=&quot;https://code.visualstudio.com/api/ux-guidelines/overview#webviews&quot;&gt;webview guidelines&lt;/a&gt; fell to extension authors. A big goal of the toolkit was to shift some of this responsibility away from extension developers by providing a set of core components that make it easier to build higher-quality webview UIs in VS Code.&lt;/p&gt;
&lt;p&gt;Additionally, since webviews operate as environments isolated from the usual resources and APIs available to VS Code, it means the &lt;a href=&quot;https://github.com/microsoft/vscode/tree/main/src/vs/base/browser/ui&quot;&gt;native VS Code UI&lt;/a&gt; could not be used.&lt;/p&gt;
&lt;p&gt;So translating that into a set of requirements, we&apos;re looking for a technology that can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Render UI in a standalone composable fashion&lt;/li&gt;
&lt;li&gt;Integrate with any technology stack that an extension author may want to use&lt;/li&gt;
&lt;li&gt;Implement the VS Code design language with a high degree of control and isolation from extension developer code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Web components are a perfect candidate for this job. They are standalone and composable HTML elements that can be used with any* tech stack and when used with shadow DOM offer a very high degree of control and isolation from other code that may exist.&lt;/p&gt;
&lt;p&gt;*&lt;em&gt;React is the notable odd duck out here.. we&apos;ll talk about this later.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Theming&lt;/h2&gt;
&lt;p&gt;One of the most important and interesting early tasks of this component library was integrating with VS Code&apos;s theming system.&lt;/p&gt;
&lt;p&gt;VS Code has a theme service that &lt;a href=&quot;https://code.visualstudio.com/api/references/theme-color&quot;&gt;exposes somewhere around 500+ color tokens&lt;/a&gt; that correspond to different parts of the user interface. Themes are defined as huge JSON files that individually set color values for each token. Below is an example from the default dark theme in VS Code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
	&quot;$schema&quot;: &quot;vscode://schemas/color-theme&quot;,
	&quot;type&quot;: &quot;dark&quot;,
	&quot;colors&quot;: {
		&quot;button.background&quot;: &quot;#0078d4&quot;,
		&quot;button.foreground&quot;: &quot;#ffffff&quot;,
		// ... other tokens ...
	},
	// ... other configurations ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When a person switches to a new theme VS Code will, under the hood, update all the token values with the color values defined in the JSON file associated with the newly selected theme.&lt;/p&gt;
&lt;p&gt;Native VS Code UI will use a system of internal TypeScript objects/variables to access these color tokens, but code inside a webview cannot do this since it is running in an &lt;code&gt;iframe&lt;/code&gt; isolated from the usual APIs and resources available in VS Code.&lt;/p&gt;
&lt;p&gt;Thankfully, VS Code also exposes all of its theme colors as CSS variables attached to the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element of each webview. When there&apos;s a theme change, every CSS variable will be updated with the new color values.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;html lang=&quot;en&quot; style=&quot;--vscode-button-foreground: #ffffff; --vscode-button-background: #0078d4; ... many other tokens ...&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Listening for theme changes&lt;/h3&gt;
&lt;p&gt;While the simplest way to inherit these theme changes would be to directly use the exposed CSS variables in the toolkit components, FAST has &lt;a href=&quot;https://www.fast.design/docs/design-systems/design-tokens/&quot;&gt;first-class support for Design Tokens&lt;/a&gt; which provide a powerful and typesafe abstraction to represent values in a design system (like color, font, spacing, and so on). They also allow for the flexibility to handle some edge cases that show up later when correctly applying VS Code themes.&lt;/p&gt;
&lt;p&gt;Since the toolkit uses these tokens, we need to find another way to listen for theme changes. We can accomplish this a MutationObserver.&lt;/p&gt;
&lt;p&gt;A &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver&quot;&gt;MutationObserver&lt;/a&gt; is a web API that allows you to watch for changes that happen to the DOM. Since we know that a bunch of CSS variables are added to the DOM and updated each time a theme change occurs we can use an observer to watch for that change.&lt;/p&gt;
&lt;p&gt;Pulled directly from the toolkit source code, here&apos;s the function that will initialize a theme change listener for the toolkit components. As added context, this function is called one time when the toolkit is initially loaded onto the page (even more specifically, initialization happens when the first toolkit design token is created).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function initThemeChangeListener(
	tokenMappings: Map&amp;lt;string, CSSDesignToken&amp;lt;T&amp;gt;&amp;gt;
) {
	// Wait until DOM loads to create observer
	window.addEventListener(&apos;load&apos;, () =&amp;gt; {
		// Create new observer that will call the applyCurrentTheme 
		// function when a change occurs
		const observer = new MutationObserver(() =&amp;gt; {
			applyCurrentTheme(tokenMappings);
		});
		// Configure observer to watch for changes on the class 
		// attribute of the &amp;lt;body&amp;gt; element
		observer.observe(document.body, {
			attributes: true,
			attributeFilter: [&apos;class&apos;],
		});
		// Apply themes for the first time since the DOM already exists 
		// and the observer code will not until the next theme change
		applyCurrentTheme(tokenMappings);
	});
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We notably are listening for changes on the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; class attribute instead of the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element because VS Code conveniently sets/resets a class name representing what type of theme has been applied when the theme changes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;body class=&quot;vscode-dark empty&quot; role=&quot;document&quot; data-vscode-theme-kind=&quot;vscode-dark&quot; data-vscode-theme-name=&quot;Dark Modern&quot; data-vscode-theme-id=&quot;Default Dark Modern&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we move onto the all-important &lt;code&gt;applyCurrentTheme&lt;/code&gt; function, this is where all the values of each CSS variable set on the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element are applied to the design tokens created with FAST.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function applyCurrentTheme(tokenMappings: Map&amp;lt;string, CSSDesignToken&amp;lt;T&amp;gt;&amp;gt;) {
	// Get a reference to the body element
	const body = document.querySelector(&apos;body&apos;);
	// Get the computed styles of the body element
	const styles = getComputedStyle(body);

	if (body) {
		// Get the name of the theme type
		const themeKind = body.getAttribute(&apos;data-vscode-theme-kind&apos;);
		// For every VS Code design token...
		for (const [vscodeTokenName, toolkitToken] of tokenMappings) {
			// Get the value of a given VS Code theme CSS variable
			let value = styles.getPropertyValue(vscodeTokenName).toString();

			// Handle a couple of styling edge cases when a high contrast 
			// theme is applied
			if (themeKind === &apos;vscode-high-contrast&apos;) {
				// There are a handful of VS Code theme tokens that have no 
				// value when a high contrast theme is applied.
				//
				// This is an issue because when no value is set the toolkit 
				// tokens will fall back to their default color values (aka 
				// the VS Code dark theme color palette). This results in the 
				// backgrounds of a couple of components having default dark
				// theme colors––thus breaking the high contrast theme.
				//
				// The below code, catches these tokens which have no value 
				// and are also background tokens, then overrides their value 
				// to be transparent.
				if (
					value.length === 0 &amp;amp;&amp;amp;
					toolkitToken.name.includes(&apos;background&apos;)
				) {
					value = &apos;transparent&apos;;
				}
				// Set icon button hover to be transparent in high contrast 
				// themes
				if (toolkitToken.name === &apos;button-icon-hover-background&apos;) {
					value = &apos;transparent&apos;;
				}
			} else if (themeKind === &apos;vscode-high-contrast-light&apos;) {
				if (
					value.length === 0 &amp;amp;&amp;amp;
					toolkitToken.name.includes(&apos;background&apos;)
				) {
					// Set button variant hover backgrounds to correct values 
					// based on VS Code core source
					switch (toolkitToken.name) {
						case &apos;button-primary-hover-background&apos;:
							value = &apos;#0F4A85&apos;;
							break;
						case &apos;button-secondary-hover-background&apos;:
							value = &apos;transparent&apos;;
							break;
						case &apos;button-icon-hover-background&apos;:
							value = &apos;transparent&apos;;
							break;
					}
				}
			} else {
				// Set contrast-active-border token to be transparent in 
				// non-high-contrast themes
				if (toolkitToken.name === &apos;contrast-active-border&apos;) {
					value = &apos;transparent&apos;;
				}
			}
			// Set a given toolkit token to contain the corresponding 
			// CSS value of the new theme
			toolkitToken.setValueFor(body, value);
		}
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Similar to the observer, we focus on the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element because all the CSS variables will be inherited from the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element and, more importantly, a custom data attribute exists that provides the name of the type of theme that is currently applied (i.e. vscode-dark, vscode-light, etc.). This information is used to correct a handful of styling edge cases and issues in different themes.&lt;/p&gt;
&lt;p&gt;Pulling this all together, it means that we can create and use a single set of design tokens that automatically listen for and correctly apply theme changes in VS Code. The below code snippet shows a brief look at FAST&apos;s method of defining styles that is be passed into a component definition (&lt;a href=&quot;https://github.com/microsoft/vscode-webview-ui-toolkit/blob/main/src/button/index.ts#L99&quot;&gt;in a different file&lt;/a&gt;) and automatically applied to the component shadow DOM.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { css } from &apos;@microsoft/fast-element&apos;;
import { buttonPrimaryBackground, buttonPrimaryForeground } from &apos;../design-tokens.js&apos;;

const BaseButtonStyles = css`
	:host {
		color: ${buttonPrimaryForeground};
		background: ${buttonPrimaryBackground};
		/* ... other styles ... */
	}
`;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was an admittedly quick breeze through the topic of handling theming in the Webview UI Toolkit that leaves out a &lt;em&gt;tonnn&lt;/em&gt; of extra context and tiny details, so apologies for any potential confusion. If you&apos;re curious and want to know more, feel free to ask me questions on &lt;a href=&quot;https://fosstodon.org/@hawkticehurst&quot;&gt;Mastodon&lt;/a&gt; and I&apos;d be happy to answer and/or write a follow-up post at a later time.&lt;/p&gt;
&lt;p&gt;I hope this did at least provide a general sense of what it looks like to solve a real-life problem in the context of a web component library.&lt;/p&gt;
&lt;h2&gt;Integrating with web frameworks&lt;/h2&gt;
&lt;p&gt;Another interesting task that I&apos;ve spent a fair bit of time working on is making sure that the toolkit integrates well with web frameworks. It&apos;s very common to see webview extensions built using frontend web frameworks (especially of the single-page app variety) like React, Vue, and Svelte.&lt;/p&gt;
&lt;p&gt;It&apos;s a choice that makes a lot of sense in this context. VS Code extensions are precompiled and downloaded by developers in the same way you might install an iOS or Android application. This means none of the usual web application concerns like initial load time and other network-based metrics apply here. Furthermore, creating multiple webviews to render different pages can be a huge pain, so a single-page app that can render multiple views within a single page is desirable.&lt;/p&gt;
&lt;p&gt;In most web frameworks web components are natively supported and there isn&apos;t a whole lot of work to do except document any special configurations that are needed and provide good &lt;a href=&quot;https://github.com/microsoft/vscode-webview-ui-toolkit-samples/tree/main/frameworks&quot;&gt;sample code&lt;/a&gt; for extension authors to reference.&lt;/p&gt;
&lt;h3&gt;One notable exception&lt;/h3&gt;
&lt;p&gt;React is the only remaining major web framework today that does not (as of writing this) natively support web components. The two big issues are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;React passes all data to custom elements in the form of HTML attributes. For primitive data this is fine, but this breaks down when passing rich data, like objects or arrays.&lt;/li&gt;
&lt;li&gt;Since React implements its own synthetic event system, it cannot listen for DOM events coming from custom elements without a workaround.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thankfully, there is a &lt;a href=&quot;https://github.com/facebook/react/issues/11347&quot;&gt;plan to natively support custom elements in React v19&lt;/a&gt;, but it means the toolkit has to do some extra work to grapple with the current reality.&lt;/p&gt;
&lt;h3&gt;A word of warning&lt;/h3&gt;
&lt;p&gt;When the toolkit was initially released as a public beta nothing was implemented yet to support React developers and we started getting a lot of requests and issue reports asking for guidance and support.&lt;/p&gt;
&lt;p&gt;The usual workaround to use web components in React is to wrap those components in a React component and then manually handle cases when rich data is passed to the components or when a DOM event is dispatched. Tools like FAST provide a &lt;a href=&quot;https://www.npmjs.com/package/@microsoft/fast-react-wrapper&quot;&gt;utility package&lt;/a&gt; for doing this automatically, so the initial recommendation was for developers to use that.&lt;/p&gt;
&lt;p&gt;This proved to be cumbersome and error-prone for a variety of reasons, so there reached a point where the GitHub Next team (the team behind GitHub Copilot and a predominantly React-based team) graciously volunteered to &lt;a href=&quot;https://github.com/microsoft/vscode-webview-ui-toolkit/pull/300&quot;&gt;contribute a pull request&lt;/a&gt; that would wrap and export the toolkit components as React components.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://githubnext.com/projects/react-webview-ui-toolkit/&quot;&gt;effort was successful and well-received&lt;/a&gt;, but I have one huge regret when I look back.&lt;/p&gt;
&lt;p&gt;When this was built I had recently learned about package exports and I thought it would be an elegant and cute solution if developers could install the React components within the same package, but just at a different export:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Regular web component import
import { vsCodeButton } from &apos;@vscode/webview-ui-toolkit&apos;;

// React component import
import { VSCodeButton } from &apos;@vscode/webview-ui-toolkit/react&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is what we did and while not a catastrophic decision it has led to many headaches during the past two years. By combining everything in the same package, it has:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Complicated the codebase in an unnecessary way&lt;/li&gt;
&lt;li&gt;Made updates harder, more confusing, and more error-prone&lt;/li&gt;
&lt;li&gt;Resulted in some &lt;a href=&quot;https://github.com/microsoft/vscode-webview-ui-toolkit/issues/386&quot;&gt;annoying package manager warnings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Even resulted in some community confusion/mild frustration (i.e. &quot;Why is React a peer dependency of a web component library?&quot;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&apos;s been a decision filled with a lot of wonderful (and at times a bit embarrassing) learning lessons and the next version of the toolkit will finally be separating these components into two separate packages.&lt;/p&gt;
&lt;p&gt;But if I had to summarize these lessons in a shorter way, my parting words of wisdom to you are:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Never be cute. Always do the simple and straightforward thing. You&apos;ll thank yourself later.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;In the next episode...&lt;/h2&gt;
&lt;p&gt;Thanks for stopping by! I hope this was an enjoyable (albeit brief) look at a web component library in the wild. Like I said above, if you have any questions feel free to reach out.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fosstodon.org/@hawkticehurst&quot;&gt;Follow me on Mastodon&lt;/a&gt; for updates and see you in the next one!&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Next post: &lt;a href=&quot;/2023/12/web-components-reading-list/&quot;&gt;Web components reading list&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;style&amp;gt;
:root {
--border-width: 1px !important;
--corner-radius: 8px !important;
}
vscode-button {
font-weight: 500;
}
&amp;lt;/style&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;script type=&quot;module&quot;&amp;gt;
import { provideVSCodeDesignSystem, vsCodeButton } from &apos;https://www.unpkg.com/@vscode/webview-ui-toolkit@1.4.0/dist/toolkit.min.js&apos;;
provideVSCodeDesignSystem().register(vsCodeButton());&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const button = document.querySelector(&apos;vscode-button&apos;);
button.addEventListener(&apos;click&apos;, () =&amp;gt; alert(&quot;Hi! I&apos;m a VS Code button.&quot;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Portable HTML web components</title><link>https://hawkticehurst.com/2023/12/portable-html-web-components/</link><guid isPermaLink="true">https://hawkticehurst.com/2023/12/portable-html-web-components/</guid><description>Day four of a series of interactive posts that explores the world of web components.</description><pubDate>Sat, 16 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Welcome back to 7 Days of Web Components! A series of interactive blog posts that introduces and explores the world of web components.&lt;/p&gt;
&lt;p&gt;This is the post for day four, if you missed the previous posts I encourage you to go back to &lt;a href=&quot;https://hawkticehurst.com/2023/12/let-it-snow/&quot;&gt;day one&lt;/a&gt; and start there.&lt;/p&gt;
&lt;p&gt;Today you get a two-for-one post! We start having some fun covering what I think is one of the most compelling benefits of using web components –– it pairs amazingly with markdown.&lt;/p&gt;
&lt;p&gt;We will then identify and discuss yet another web component architecture that sits somewhere in the middle of HTML and JavaScript Web Components. This doesn&apos;t yet have a name, so I&apos;m proposing &quot;Portable HTML Web Components.&quot;&lt;/p&gt;
&lt;p&gt;Tieing this all together we will also look at our first properly complex web component: an &lt;code&gt;&amp;lt;audio-player&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Please note: This blog post includes interactive code examples. If you&apos;re reading this from an RSS feed, I highly recommend you open the &lt;a href=&quot;https://hawkticehurst.com/2023/12/portable-html-web-components/&quot;&gt;original post&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Browser-native MDX&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://mdxjs.com/&quot;&gt;MDX&lt;/a&gt; is a piece of technology that allows you to use interactive components inside your markdown. It is one of my favorite methods for building websites that I&apos;ve come across.&lt;/p&gt;
&lt;p&gt;It combines a seamless way to embed pockets of interactivity into your web pages with all the convenience of authoring content in markdown.&lt;/p&gt;
&lt;p&gt;The one downside is that MDX is (more precisely) a way of using JSX inside of markdown. To be clear, I do not view JSX as the downside (for those who are unaware JSX is a historically polarizing technology due to its origins as a React/Meta project), but rather that it&apos;s a limiting technology to build on top of.&lt;/p&gt;
&lt;p&gt;While you can author components in JSX using most modern frontend frameworks these days there are no guarantees that JSX (or even MDX for that matter) will be a well-supported method of embedding components into your markdown in the future. If and when that day comes I will have to re-author all of my content if I&apos;m tied to MDX.&lt;/p&gt;
&lt;p&gt;But you know what technology does have a very strong guarantee of sticking around long into the future? HTML. And you know what markdown conveniently supports natively? HTML. And (as we&apos;ve established the last few days) do you remember what web components are, definitionally? Ding ding, HTML.&lt;/p&gt;
&lt;p&gt;Said without all the Q&amp;amp;A, markdown and web components can be used as a browser-native MDX.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Some example markdown

Author your content using normal markdown syntax.

But when you need a component...

&amp;lt;my-component&amp;gt;&amp;lt;/my-component&amp;gt;

...you literally just add it.

And remember, don&apos;t forget to add a script tag.

&amp;lt;script type=&quot;module&quot; scr=&quot;/my-element.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voilà! You embedded an interactive component into your markdown. It&apos;s so simple, it&apos;s honestly why I didn&apos;t make this topic a full blog post.&lt;/p&gt;
&lt;p&gt;With that said, I will leave you with a few tips/insights I&apos;ve gathered along the way.&lt;/p&gt;
&lt;h3&gt;Component definitions right inside markdown&lt;/h3&gt;
&lt;p&gt;You can also author the component code right inside your markdown file if you desire.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Markdown content

Blah blah blah...

&amp;lt;my-component&amp;gt;&amp;lt;/my-component&amp;gt;

&amp;lt;script type=&quot;module&quot;&amp;gt;
	class MyComponent extends HTMLElement {
		// ... component behavior ...
	}
	customElements.define(&apos;my-component&apos;, MyComponent);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;HTML web components too!&lt;/h3&gt;
&lt;p&gt;Again, it&apos;s all HTML so markdown doesn&apos;t mind at all.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# More markdown content

Yada yada yada...

&amp;lt;my-component&amp;gt;
	&amp;lt;p&amp;gt;Some markup that will be hydrated.&amp;lt;/p&amp;gt;
	&amp;lt;button&amp;gt;Click me!&amp;lt;/button&amp;gt;
&amp;lt;/my-component&amp;gt;

&amp;lt;script type=&quot;module&quot;&amp;gt;
	class MyComponent extends HTMLElement {
		// ... component behavior ...
	}
	customElements.define(&apos;my-component&apos;, MyComponent);
&amp;lt;/script&amp;gt;

Or you can still link to an external file.

&amp;lt;script type=&quot;module&quot; scr=&quot;/my-element.js&quot;&amp;gt;&amp;lt;/script&amp;gt;

You can even define component styles!

&amp;lt;style&amp;gt;
	my-component p {
		/* ... styles ... */
	}
	my-component button {
		/* ... styles ... */
	}
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Watch out for how markdown parsers handle script tags&lt;/h3&gt;
&lt;p&gt;Depending on the markdown parser you use to generate HTML, be wary of parsers that will escape your JavaScript code. In those cases, just default back to linking to an external JavaScript file as shown above.&lt;/p&gt;
&lt;h3&gt;An example and a segue&lt;/h3&gt;
&lt;p&gt;Markdown and web components are so flexible/enjoyable it&apos;s what I&apos;ve been using to author this entire series! Here&apos;s an example of the actual HTML I&apos;m writing inside my markdown files to render these posts.&lt;/p&gt;
&lt;p&gt;This also introduces our component of the day, the &lt;code&gt;&amp;lt;audio-player&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;audio-player src=&quot;/audio/requiem-in-d-minor.mp3&quot; title=&quot;Requiem in D Minor&quot;&amp;gt;&amp;lt;/audio-player&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;audio-player src=&quot;/audio/requiem-in-d-minor.mp3&quot; title=&quot;Requiem in D Minor&quot;&amp;gt;&amp;lt;/audio-player&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h2&gt;Portable HTML web components&lt;/h2&gt;
&lt;p&gt;At this point, I&apos;ve discussed across multiple days what HTML and JavaScript web components are and how I like to think of them as living on a spectrum rather than as discrete entities. You can mix and match the browser APIs you use to build web components that find a balance between the capabilities you need while not taking on extra development complexity where you don&apos;t need it.&lt;/p&gt;
&lt;p&gt;There&apos;s a really interesting grey zone that exists between these two that I want to talk about today that I&apos;ve come to call &quot;Portable HTML Web Components.&quot;&lt;/p&gt;
&lt;h3&gt;It&apos;s all a bunch of tradeoffs&lt;/h3&gt;
&lt;p&gt;At the two ends of this spectrum live a bunch of tradeoffs. With HTML web components you get web components that are very server-side rendering friendly, allow for flexible styling, are great for progressive enhancement, and avoid the pitfalls of shadow DOM. However, they come at the cost of portability, scoped styling, and template slots.&lt;/p&gt;
&lt;p&gt;To use our &lt;code&gt;&amp;lt;audio-player&amp;gt;&lt;/code&gt; component as an example, the markup when built as an HTML web component will look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;audio-player src=&quot;/audio/requiem-in-d-minor.mp3&quot; title=&quot;Requiem in D Minor&quot;&amp;gt;
	&amp;lt;audio&amp;gt;&amp;lt;/audio&amp;gt;
	&amp;lt;div class=&quot;left-container&quot;&amp;gt;
		&amp;lt;button id=&quot;play-btn&quot; aria-label=&quot;Play song&quot;&amp;gt;
			&amp;lt;svg id=&quot;play-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 20 20&quot; width=&quot;35&quot; height=&quot;35&quot; fill=&quot;currentColor&quot; aria-hidden=&quot;true&quot;&amp;gt;
				&amp;lt;path d=&quot;M6.3 2.841A1.5 1.5 0 004 4.11V15.89a1.5 1.5 0 002.3 1.269l9.344-5.89a1.5 1.5 0 000-2.538L6.3 2.84z&quot;&amp;gt;&amp;lt;/path&amp;gt;
			&amp;lt;/svg&amp;gt;
			&amp;lt;svg id=&quot;pause-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 25 25&quot; width=&quot;30&quot; height=&quot;30&quot; fill=&quot;currentColor&quot; aria-hidden=&quot;true&quot; style=&quot;display: none;&quot;&amp;gt;
				&amp;lt;path d=&quot;M6 19h4V5H6v14zm8-14v14h4V5h-4z&quot;&amp;gt;&amp;lt;/path&amp;gt;
			&amp;lt;/svg&amp;gt;
		&amp;lt;/button&amp;gt;
	&amp;lt;/div&amp;gt;
	&amp;lt;div class=&quot;right-container&quot;&amp;gt;
		&amp;lt;p id=&quot;song-title&quot;&amp;gt;Song title&amp;lt;/p&amp;gt;
		&amp;lt;div class=&quot;audio-bar&quot;&amp;gt;
			&amp;lt;span id=&quot;current-time&quot;&amp;gt;00:00&amp;lt;/span&amp;gt;
			&amp;lt;progress value=&quot;0&quot; max=&quot;1&quot;&amp;gt;&amp;lt;/progress&amp;gt;
			&amp;lt;span id=&quot;total-time&quot;&amp;gt;00:00&amp;lt;/span&amp;gt;
		&amp;lt;/div&amp;gt;
	&amp;lt;/div&amp;gt;
&amp;lt;/audio-player&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The markup of this component lives in the light DOM, so (as I said above) it&apos;s really easy to style and progressively enhance. What I really dislike about HTML web components, however, is that they are not portable at all. To use this &lt;code&gt;&amp;lt;audio-player&amp;gt;&lt;/code&gt; I will need to copy and paste not only the custom element tag but all of the child elements that live inside it.&lt;/p&gt;
&lt;p&gt;On the other end of the spectrum, JavaScript web components allow for fantastic portability, native scoped styling, and template slots, but come at the cost of poor server-side rendering capabilities*, are not easy to progressively enhance, and are a more complex development model to work with since more browser APIs are used.&lt;/p&gt;
&lt;p&gt;*&lt;em&gt;At the time of writing, &lt;a href=&quot;https://developer.chrome.com/docs/css-ui/declarative-shadow-dom&quot;&gt;Declarative shadow DOM&lt;/a&gt; is very close to having cross-browser support, so this reality will likely change soon.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As a JavaScript web component, our &lt;code&gt;&amp;lt;audio-player&amp;gt;&lt;/code&gt; markup will instead look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;audio-player src=&quot;/audio/requiem-in-d-minor.mp3&quot; title=&quot;Requiem in D Minor&quot;&amp;gt;&amp;lt;/audio-player&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Very portable!&lt;/p&gt;
&lt;h2&gt;A hybrid model&lt;/h2&gt;
&lt;p&gt;Browser APIs are flexible building blocks, however. There are no prescribed ways for how you need to use them, so what if we attempted to implement a sort of hybrid between the two models I described above?&lt;/p&gt;
&lt;p&gt;I&apos;m very interested in building an audio player that is focused on portability while still maintaining the simpler and more flexible development model of HTML web components. I want to be able to put as many audio players as I want across a project and just insert a simple script tag that points to a single JavaScript file like you would with a JavaScript web component.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script type=&quot;module&quot; src=&quot;/audio-player.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means I&apos;ll need to move my component markup out of my custom element tag and (in this case) into a JavaScript-defined template.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const template = document.createElement(&apos;template&apos;);
template.innerHTML = /* html */ `
	&amp;lt;audio&amp;gt;&amp;lt;/audio&amp;gt;
	&amp;lt;div class=&quot;left-container&quot;&amp;gt;
		&amp;lt;button id=&quot;play-btn&quot; aria-label=&quot;Play song&quot;&amp;gt;
			&amp;lt;svg id=&quot;play-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 20 20&quot; width=&quot;35&quot; height=&quot;35&quot; fill=&quot;currentColor&quot; aria-hidden=&quot;true&quot;&amp;gt;
				&amp;lt;path d=&quot;M6.3 2.841A1.5 1.5 0 004 4.11V15.89a1.5 1.5 0 002.3 1.269l9.344-5.89a1.5 1.5 0 000-2.538L6.3 2.84z&quot;&amp;gt;&amp;lt;/path&amp;gt;
			&amp;lt;/svg&amp;gt;
			&amp;lt;svg id=&quot;pause-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 25 25&quot; width=&quot;30&quot; height=&quot;30&quot; fill=&quot;currentColor&quot; aria-hidden=&quot;true&quot; style=&quot;display: none;&quot;&amp;gt;
				&amp;lt;path d=&quot;M6 19h4V5H6v14zm8-14v14h4V5h-4z&quot;&amp;gt;&amp;lt;/path&amp;gt;
			&amp;lt;/svg&amp;gt;
		&amp;lt;/button&amp;gt;
	&amp;lt;/div&amp;gt;
	&amp;lt;div class=&quot;right-container&quot;&amp;gt;
		&amp;lt;p id=&quot;song-title&quot;&amp;gt;Song title&amp;lt;/p&amp;gt;
		&amp;lt;div class=&quot;audio-bar&quot;&amp;gt;
			&amp;lt;span id=&quot;current-time&quot;&amp;gt;00:00&amp;lt;/span&amp;gt;
			&amp;lt;progress value=&quot;0&quot; max=&quot;1&quot;&amp;gt;&amp;lt;/progress&amp;gt;
			&amp;lt;span id=&quot;total-time&quot;&amp;gt;00:00&amp;lt;/span&amp;gt;
		&amp;lt;/div&amp;gt;
	&amp;lt;/div&amp;gt;
`;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will then be possible to add a custom element class in the same file that can reference, clone, and append the template as a direct child of the custom element tag.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class AudioPlayer extends HTMLElement {
	constructor() {
		super();
		this.appendChild(template.content.cloneNode(true));
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From there, all the other behavior to bring this audio player to life can be added. I won&apos;t go over it in detail in this post, but you can find the completed code in the collapsible view below.&lt;/p&gt;
&lt;p&gt;One extremely important thing I want to call out, however. In this model, you need to clone the template content &lt;em&gt;before&lt;/em&gt; most other component code! If you don&apos;t, getting references to DOM nodes and setting event listeners will fail.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;Completed audio player code&amp;lt;/summary&amp;gt;
&amp;lt;pre class=&quot;astro-code dark-plus&quot; style=&quot;background-color:var(--code-block-background);color:var(--text-primary); overflow-x: auto;&quot; tabindex=&quot;0&quot;&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; src&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;/audio/requiem-in-d-minor.mp3&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; title&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;Requiem in D Minor&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;script&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; type&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;module&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; template&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;document&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;createElement&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;template&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	template&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;innerHTML&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#6A9955&quot;&amp;gt;/* html */&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; &lt;code&gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;		&amp;amp;lt;audio&amp;amp;gt;&amp;amp;lt;/audio&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;		&amp;amp;lt;div class=&quot;left-container&quot;&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;			&amp;amp;lt;button id=&quot;play-btn&quot; aria-label=&quot;Play song&quot;&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;				&amp;amp;lt;svg id=&quot;play-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 20 20&quot; width=&quot;35&quot; height=&quot;35&quot; fill=&quot;currentColor&quot; aria-hidden=&quot;true&quot;&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;					&amp;amp;lt;path d=&quot;M6.3 2.841A1.5 1.5 0 004 4.11V15.89a1.5 1.5 0 002.3 1.269l9.344-5.89a1.5 1.5 0 000-2.538L6.3 2.84z&quot;&amp;amp;gt;&amp;amp;lt;/path&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;				&amp;amp;lt;/svg&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;				&amp;amp;lt;svg id=&quot;pause-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 25 25&quot; width=&quot;30&quot; height=&quot;30&quot; fill=&quot;currentColor&quot; aria-hidden=&quot;true&quot; style=&quot;display: none;&quot;&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;					&amp;amp;lt;path d=&quot;M6 19h4V5H6v14zm8-14v14h4V5h-4z&quot;&amp;amp;gt;&amp;amp;lt;/path&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;				&amp;amp;lt;/svg&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;			&amp;amp;lt;/button&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;		&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;		&amp;amp;lt;div class=&quot;right-container&quot;&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;			&amp;amp;lt;p id=&quot;song-title&quot;&amp;amp;gt;Song title&amp;amp;lt;/p&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;			&amp;amp;lt;div class=&quot;audio-bar&quot;&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;				&amp;amp;lt;span id=&quot;current-time&quot;&amp;amp;gt;00:00&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;				&amp;amp;lt;progress value=&quot;0&quot; max=&quot;1&quot;&amp;amp;gt;&amp;amp;lt;/progress&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;				&amp;amp;lt;span id=&quot;total-time&quot;&amp;amp;gt;00:00&amp;amp;lt;/span&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;			&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;		&amp;amp;lt;/div&amp;amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;	&lt;/code&gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#6A9955&quot;&amp;gt;	/**&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#6A9955&quot;&amp;gt;	 * Custom element based on &lt;code&gt;audio-tool&lt;/code&gt; by Kevin Logan&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#6A9955&quot;&amp;gt;	 * https://github.com/kevinlogan94/audio-tool/blob/master/src/audio-tool.js&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#6A9955&quot;&amp;gt;	 */&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	class&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4EC9B0&quot;&amp;gt; AudioPlayer&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt; extends&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4EC9B0&quot;&amp;gt; HTMLElement&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;		static&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt; get&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt; observedAttributes&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;() {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;			return&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; [&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;src&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;title&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;preload&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;auto-play&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;];&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;		constructor&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;() {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			super&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;appendChild&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;template&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;content&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;cloneNode&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;true&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;));&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelector&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;audio&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelector&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;#play-icon&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;pauseIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelector&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;#pause-icon&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playPauseButton&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelector&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;#play-btn&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;progressBar&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelector&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;progress&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;currentTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelector&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;#current-time&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;duration&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelector&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;#total-time&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;songTitle&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelector&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;#song-title&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playing&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;false&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		connectedCallback&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;() {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;configureAudio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;checkFileTypeSupport&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setSongTitle&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setPlayPauseListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setProgressListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setTimeListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		attributeChangedCallback&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;oldValue&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;newValue&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;			switch&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;name&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;				case&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; &apos;src&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;					if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;removeEventListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;timeupdate&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;updateTimeHandler&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;removeEventListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;loadedmetadata&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;metadataTimeHandler&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;					}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;					this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;configureAudio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;					this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;checkFileTypeSupport&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;					if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;pauseIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playPauseButton&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;pauseIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;style&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;none&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;style&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;block&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playPauseButton&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;aria-label&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;Play song&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;pause&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playing&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;false&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;dispatchEvent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;new&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt; Event&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;audiopaused&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;));&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;					}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;					if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;progressBar&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;progressBar&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;value&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;					}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;					this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setTimeListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;					break&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;				case&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; &apos;title&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;					this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setSongTitle&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;					break&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;				default&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;					break&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		configureAudio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;() {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;			if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;hasAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;src&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;)) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;src&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;getAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;src&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) || &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;			if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;hasAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;preload&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;)) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; preload&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;getAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;preload&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;				if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;preload&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; === &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; || &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;preload&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; === &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;none&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; || &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;preload&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; === &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;metadata&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; || &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;preload&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; === &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;auto&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;					this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;preload&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;preload&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;				}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;			if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;hasAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;auto-play&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;)) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;				if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;getAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;auto-play&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) === &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;true&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;					this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;autoplay&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;true&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;					this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;load&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;				}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		checkFileTypeSupport&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;() {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;			if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;src&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; songFile&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;src&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; fileType&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;songFile&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;substring&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;songFile&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;lastIndexOf&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;.&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) + &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;1&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#6A9955&quot;&amp;gt;				// https://diveintohtml5.info/everything.html#audio-vorbis&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;				switch&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;fileType&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;					case&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; &apos;mp3&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;						if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (!(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;canPlayType&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;canPlayType&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;audio/mpeg;&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;).&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;replace&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D16969&quot;&amp;gt;/no/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;))) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;							this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setUnsupportedMessage&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;This browser doesn&apos;t support MP3 audio files.&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;						}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;						break&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;					case&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; &apos;m4a&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;						if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (!(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;canPlayType&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;canPlayType&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;audio/mp4; codecs=&quot;mp4a.40.2&quot;&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;).&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;replace&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D16969&quot;&amp;gt;/no/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;))) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;							this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setUnsupportedMessage&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;This browser doesn&apos;t support M4A audio files.&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;						}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;						break&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;					case&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; &apos;aac&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;						if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (!(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;canPlayType&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;canPlayType&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;audio/mp4; codecs=&quot;mp4a.40.2&quot;&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;).&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;replace&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D16969&quot;&amp;gt;/no/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;))) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;							this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setUnsupportedMessage&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;This browser doesn&apos;t support AAC audio files.&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;						}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;						break&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;					case&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; &apos;wav&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;						if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (!(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;canPlayType&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;canPlayType&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;audio/wav; codecs=&quot;1&quot;&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;).&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;replace&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D16969&quot;&amp;gt;/no/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;))) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;							this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setUnsupportedMessage&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;This browser doesn&apos;t support WAV audio files.&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;						}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;						break&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;					case&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; &apos;ogg&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;						if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (!(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;canPlayType&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;canPlayType&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;audio/ogg; codecs=&quot;vorbis&quot;&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;).&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;replace&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D16969&quot;&amp;gt;/no/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;))) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;							this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setUnsupportedMessage&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;This browser doesn&apos;t support Vorbis audio files.&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;						}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;						break&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;					default&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;						if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (!&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;canPlayType&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;audio/&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; + &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;fileType&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;)) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;							this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setUnsupportedMessage&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;This browser doesn&apos;t support this audio file&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;						}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;						break&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;				}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		setSongTitle&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;() {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;			if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;songTitle&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;hasAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;title&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;)) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;songTitle&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;textContent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;getAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;title&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			} &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;else&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt; if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;songTitle&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;hasAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;src&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;)) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;songTitle&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;textContent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;getAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;src&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		setPlayPauseListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;() {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playPauseButton&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;?.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;addEventListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;click&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, () &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;=&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;				if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;pauseIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playPauseButton&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;					if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (!&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playing&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;pauseIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;style&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;block&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;style&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;none&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playPauseButton&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;aria-label&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;Pause song&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;play&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playing&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;true&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;dispatchEvent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;new&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt; Event&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;audioplaying&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;));&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;					} &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;else&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;pauseIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;style&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;none&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;style&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;block&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playPauseButton&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;aria-label&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;Play song&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;pause&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playing&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;false&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;dispatchEvent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;new&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt; Event&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;audiopaused&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;));&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;					}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;				}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			});&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		setProgressListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;() {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;progressBar&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;?.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;addEventListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;click&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;event&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;=&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;				if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;progressBar&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;					let&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; percent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;event&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;offsetX&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; / &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;progressBar&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;offsetWidth&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;					this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;currentTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;percent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; * &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;duration&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;					this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;progressBar&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;value&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;percent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;				}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			});&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		playSong&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;() {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;			if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;pauseIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playPauseButton&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;pauseIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;style&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;block&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;style&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;none&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playPauseButton&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;aria-label&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;Pause song&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;play&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playing&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;true&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;dispatchEvent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;new&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt; Event&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;audioplaying&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;));&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		setTimeListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;() {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;?.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;addEventListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;loadedmetadata&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, () &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;=&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;metadataTimeHandler&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			});&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		metadataTimeHandler&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;() {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;			if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;currentTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;duration&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;currentTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;textContent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&lt;code&gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;${&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;formatTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;					this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;currentTime&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;				)&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&lt;/code&gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;duration&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;textContent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&lt;code&gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;${&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;formatTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;duration&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&lt;/code&gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;addEventListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;timeupdate&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, () &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;=&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;					this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;updateTimeHandler&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;				});&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		updateTimeHandler&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;() {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;			if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;currentTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;duration&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;progressBar&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;pauseIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;amp;&amp;amp; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playPauseButton&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;				if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (!&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;isNaN&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;currentTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; / &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;duration&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;)) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;					this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;progressBar&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;value&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;currentTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; / &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;duration&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;					this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;currentTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;textContent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&lt;code&gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;${&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;formatTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;currentTime&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;					)&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&lt;/code&gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;					this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;duration&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;textContent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&lt;code&gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;${&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;formatTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;duration&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&lt;/code&gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;					if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;currentTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; === &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;duration&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;audio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;currentTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;pauseIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;style&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;none&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playIcon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;style&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;block&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playPauseButton&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;aria-label&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;Play song&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;playing&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;false&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;dispatchEvent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;new&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt; Event&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;audiopaused&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;));&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;						this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;dispatchEvent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;new&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt; Event&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;audiofinished&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;));&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;					}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;				}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		setUnsupportedMessage&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;message&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;			if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;songTitle&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;songTitle&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;textContent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;message&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		formatTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;seconds&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; hours&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;Math&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;floor&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;seconds&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; / &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;3600&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; minutes&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;Math&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;floor&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;((&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;seconds&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; % &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;3600&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) / &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;60&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; remainingSeconds&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;Math&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;floor&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;seconds&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; % &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;60&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			let&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; formattedTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;			if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;hours&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; &amp;gt; &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; formattedHours&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;hours&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;toString&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;().&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;padStart&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;2&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;0&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;				formattedTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&lt;code&gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;${&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;formattedHours&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;${&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;minutes&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;					.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;toString&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;					.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;padStart&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;2&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;0&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;${&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;remainingSeconds&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;toString&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;().&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;padStart&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;2&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;0&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&lt;/code&gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			} &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;else&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;				formattedTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&lt;code&gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;${&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;minutes&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;toString&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;().&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;padStart&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;2&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;0&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;:&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;${&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;remainingSeconds&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;					.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;toString&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;()&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;					.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;padStart&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;2&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;0&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&lt;/code&gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;			return&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; formattedTime&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	customElements&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;define&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;audio-player&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;AudioPlayer&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;script&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;style&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;grid&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		grid-template-columns&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;15%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 85%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		justify-items&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;center&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		align-items&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;center&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		background-color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;95&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;85&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;236&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0.5&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		border&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;2px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; solid&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt; rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;95&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;85&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;236&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		border-radius&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;8px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		padding&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0.5rem&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		width&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;100%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; button&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;white&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		background-color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;122&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 112&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 255&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		border&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;none&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		border-radius&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;50%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		cursor&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;pointer&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		width&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;40px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		height&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;40px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		padding&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		line-height&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;inline-flex&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		align-items&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;center&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		justify-content&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;center&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; button:focus-visible&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		outline&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;solid&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 2px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt; var&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;--focus-outline&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		outline-offset&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;2px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; button&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; svg&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		width&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;20px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		height&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;20px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		margin&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		padding&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; #play-icon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		margin-left&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;2px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; .audio-bar&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;flex&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		align-items&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;center&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		justify-content&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;space-between&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		gap&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;10px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; progress&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		grid-column&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;3&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; / span &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;10&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		cursor&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;pointer&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;block&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		width&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;100%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		height&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;6px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		border&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;none&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		border-radius&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;100px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		background-color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;190&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;190&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;190&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; progress::-webkit-progress-bar&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		background-color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;190&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;190&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;190&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		border-radius&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;100px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		height&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;6px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; progress::-webkit-progress-value&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		background-color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;122&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 112&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 255&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		border-radius&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;100px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		height&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;6px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; progress::-moz-progress-bar&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		background-color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;122&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 112&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 255&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		border-radius&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;100px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		height&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;6px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; label&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;block&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		font-size&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;1rem&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; #song-title&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		font-size&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;1rem&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		font-weight&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;600&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		margin&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; .left-container&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		order&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;1&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		height&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;auto&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		width&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;auto&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; .right-container&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		order&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;2&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		height&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;auto&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		width&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;95%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;	@media&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; screen&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; and (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;min-width&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;48em&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;		audio-player&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; #song-title&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;			font-size&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;1rem&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;style&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;h3&gt;There are no silver bullets&lt;/h3&gt;
&lt;p&gt;As a reminder, grey zones do not entail an absence of trade-offs so just like the other models, portable HTML web components come with their own tradeoffs. What I&apos;ve gained in newfound portability, I&apos;ve lost in the form of good SSR-ability and easy progressive enhancement.&lt;/p&gt;
&lt;p&gt;But here&apos;s the thing, I think that&apos;s okay! There are no silver bullets in technology (or life for that matter). It&apos;s up to you to pick the correct tool for the task at hand. Portable HTML web components are now just another tool you can add to your toolbox to be used if/when you need it.&lt;/p&gt;
&lt;p&gt;As a final thought, I leave you with this question: What other possible combinations of browser APIs and hybrid web component models live in this grey zone?&lt;/p&gt;
&lt;h2&gt;In the next episode...&lt;/h2&gt;
&lt;p&gt;Thanks for stopping by! In the next post, we&apos;re pivoting from theory and examples to look at a library of web components in production in the real world.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fosstodon.org/@hawkticehurst&quot;&gt;Follow me on Mastodon&lt;/a&gt; for updates and see you in the next one!&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Next post: &lt;a href=&quot;/2023/12/web-components-in-vs-code/&quot;&gt;Web components in VS Code&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;style&amp;gt;
audio-player {
display: grid;
grid-template-columns: 15% 85%;
justify-items: center;
align-items: center;
background-color: rgb(95, 85, 236, 0.5);
border: 2px solid rgb(95, 85, 236);
border-radius: 8px;
padding: 0.5rem;
width: 100%;
}
audio-player button {
color: white;
background-color: rgb(122 112 255);
border: none;
border-radius: 50%;
cursor: pointer;
width: 40px;
height: 40px;
padding: 0;
line-height: 0;
display: inline-flex;
align-items: center;
justify-content: center;
}
audio-player button:focus-visible {
outline: solid 2px var(--focus-outline);
outline-offset: 2px;
}
audio-player button svg {
width: 20px;
height: 20px;
margin: 0;
padding: 0;
}
audio-player #play-icon {
margin-left: 2px;
}
audio-player .audio-bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
audio-player progress {
grid-column: 3 / span 10;
cursor: pointer;
display: block;
width: 100%;
height: 6px;
border: none;
border-radius: 100px;
background-color: rgb(190, 190, 190);
}
audio-player progress::-webkit-progress-bar {
background-color: rgb(190, 190, 190);
border-radius: 100px;
height: 6px;
}
audio-player progress::-webkit-progress-value {
background-color: rgb(122 112 255);
border-radius: 100px;
height: 6px;
}
audio-player progress::-moz-progress-bar {
background-color: rgb(122 112 255);
border-radius: 100px;
height: 6px;
}
audio-player label {
display: block;
font-size: 1rem;
}
audio-player #song-title {
font-size: 1rem;
font-weight: 700;
margin: 0;
}
audio-player .left-container {
order: 1;
height: auto;
width: auto;
}
audio-player .right-container {
order: 2;
height: auto;
width: 95%;
}
@media screen and (min-width: 48em) {
audio-player #song-title {
font-size: 1rem;
}
}
&amp;lt;/style&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;script type=&quot;module&quot; src=&quot;/elements/audio-player.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Getting shady</title><link>https://hawkticehurst.com/2023/12/getting-shady/</link><guid isPermaLink="true">https://hawkticehurst.com/2023/12/getting-shady/</guid><description>Day three of a series of interactive posts that explores the world of web components.</description><pubDate>Fri, 15 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Welcome back to 7 Days of Web Components! A series of interactive blog posts that introduces and explores the world of web components.&lt;/p&gt;
&lt;p&gt;This is the post for day three, if you missed the previous posts I encourage you to go back to &lt;a href=&quot;https://hawkticehurst.com/2023/12/let-it-snow/&quot;&gt;day one&lt;/a&gt; and start there.&lt;/p&gt;
&lt;p&gt;Today will be our final post covering the basics of web components. It&apos;s time to talk about (the often dreaded and mischaracterized) Shadow DOM.&lt;/p&gt;
&lt;p&gt;My goal with this post is to show that Shadow DOM is not nearly as scary or complex as web development discourse might have you think. It &lt;em&gt;does&lt;/em&gt; indeed come with some new rules and conventions that are different from how you might usually think about web development, but I hope this post will help clearly articulate those rules and maybe even have you appreciating what shadow DOM brings to the table by the end.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Please note: This blog post is filled with interactive code examples. If you&apos;re reading this from an RSS feed, I highly recommend you open the &lt;a href=&quot;https://hawkticehurst.com/2023/12/getting-shady/&quot;&gt;original post&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;A sneak peek&lt;/h2&gt;
&lt;p&gt;Here&apos;s what we&apos;re building today... a simple name tag. It&apos;ll also be our first JavaScript Web Component!&lt;/p&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;name-tag&amp;gt;Poppy&amp;lt;/name-tag&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;P.S. for the curious, this is &lt;a href=&quot;https://www.instagram.com/poppythelilpuppy/&quot;&gt;Poppy&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Shadow DOM is just another tool&lt;/h2&gt;
&lt;p&gt;Shadow DOM is usually described in precise (but slightly dense) technical terms as a &quot;hidden&quot; DOM tree that you can create and attach to HTML elements.&lt;/p&gt;
&lt;p&gt;I, however, like to think about it as having the ability to add tiny little isolated web pages inside your document –– almost like a micro &lt;code&gt;iframe&lt;/code&gt; that can hold fragments of HTML versus entire web pages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Extreme isolation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One of the key traits of shadow DOM is that it is &lt;em&gt;extremely&lt;/em&gt; isolated from the Light DOM (read: the rest of the page). Isolation in this case means it is hard or impossible to access/change the contents inside a shadow DOM such as styles, DOM and text nodes, and so on. It also means that, by default, things like styles in the shadow DOM do not affect the light DOM and vice versa.&lt;/p&gt;
&lt;p&gt;This can often be a very frustrating experience for those just learning about Shadow DOM, but I want to point out that this isolation is an intentional feature! Shadow DOM is a standard way for developers to create highly isolated DOM trees that are modeled after the way browser vendors build native built-in HTML elements like &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;. Building native HTML elements requires extremely strong isolation –– you don&apos;t want developers to ever accidentally (or intentionally) start messing around with the internals of the web&apos;s building blocks.&lt;/p&gt;
&lt;p&gt;There are thankfully ways to loosen and break through this isolation that we&apos;ll discuss below, but I hope this is starting to give a sense of the motivations and scenarios shadow DOM is built upon. Fundamentally, it is just another tool in the web development toolbelt, it&apos;s good for some things and not so good for others.&lt;/p&gt;
&lt;h2&gt;Shadow DOM modes&lt;/h2&gt;
&lt;p&gt;Shadow DOM exposes two &quot;modes&quot; –– open and closed.&lt;/p&gt;
&lt;p&gt;A closed shadow DOM is totally, completely, and entirely locked down. This means there is &lt;em&gt;no&lt;/em&gt; way to programmatically access or change the internals of these elements after the shadow DOM has been created.&lt;/p&gt;
&lt;p&gt;I believe every built-in HTML element uses a closed shadow DOM (someone please fact-check me on this) and acts as a good example of what a closed shadow DOM might be used for.&lt;/p&gt;
&lt;p&gt;As a fun aside, there&apos;s a way to see the internals of some built-in HTML elements via Chrome Dev Tools. Go to Settings &amp;gt; Preferences &amp;gt; Elements (section) and turn on the &quot;Show user agent shadow DOM&quot; setting. If you start poking around you should find some elements with user agent shadow roots. For example, here are the internals of an &lt;code&gt;&amp;lt;input type=&quot;text&quot;&amp;gt;&lt;/code&gt; pulled from yesterday&apos;s todo list component!&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;An open shadow DOM, on the other hand, does allow for programmatic access and manipulation after it has been created via an &lt;code&gt;element.shadowRoot&lt;/code&gt; property. Open shadow DOMs are usually what you want to use when developing web components.&lt;/p&gt;
&lt;h2&gt;Creating shadow DOM&lt;/h2&gt;
&lt;p&gt;As I mentioned yesterday, &quot;web component&quot; APIs are browser APIs first and foremost so, just like templates, we can use shadow DOM with more than just web components.&lt;/p&gt;
&lt;p&gt;In this case, the specific set of HTML elements we can use include &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;blockquote&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h4&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h5&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h6&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;. Other elements (such as a &lt;code&gt;button&lt;/code&gt;) cannot host a shadow DOM.&lt;/p&gt;
&lt;p&gt;As an easy starter example, we&apos;ll create an open shadow DOM and attach it to a &lt;code&gt;div&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&quot;example&quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;script type=&quot;module&quot;&amp;gt;
	const div = document.querySelector(&apos;#example&apos;);
	div.attachShadow({ mode: &apos;open&apos; });
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From there you can use the &lt;code&gt;shadowRoot&lt;/code&gt; property and regular DOM manipulation APIs to change and update the shadow DOM however you please. In this case, let&apos;s add some pink text to the shadow DOM.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;div.shadowRoot.innerHTML = `
	&amp;lt;style&amp;gt;
		p {
			color: teal;
		}
	&amp;lt;/style&amp;gt;
	&amp;lt;p&amp;gt;I&apos;m teal text inside the shadow DOM!&amp;lt;/p&amp;gt;
`;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;div id=&quot;example&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;Just like the image from earlier, if you inspect this element you&apos;ll find that it has a shadow root with a style and paragraph tag. These elements will be fully encapsulated within this shadow DOM –– the styles will not affect elements in the light DOM and vice versa.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&quot;example&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;I&apos;m a paragraph in the light DOM! I will not be pink.&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;div id=&quot;example2&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;I&apos;m a paragraph in the light DOM! I will not be pink.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;Said a different way, shadow DOM is a browser-native way of creating UI with scoped styles!&lt;/p&gt;
&lt;h2&gt;Adding styles to shadow DOM programmatically&lt;/h2&gt;
&lt;p&gt;Thus far, we&apos;ve demonstrated creating styles declaratively using a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag, but it is also possible to create styles programmatically using something called &quot;Constructable Stylesheets.&quot;&lt;/p&gt;
&lt;p&gt;We actually won&apos;t have time to cover constructable stylesheets in this series, but you can read this great &lt;a href=&quot;https://web.dev/articles/constructable-stylesheets&quot;&gt;intro to constructable stylesheets&lt;/a&gt; on Google&apos;s web developer blog to learn more about them.&lt;/p&gt;
&lt;h2&gt;Overriding shadow DOM styles&lt;/h2&gt;
&lt;p&gt;When building a web component with shadow DOM, there are three primary ways that styles within the shadow DOM can be overridden. While this can feel limiting, it offers an interesting benefit –– you as a component developer can choose (with different levels of granularity) what styles can be overridden by those who may use your component.&lt;/p&gt;
&lt;h3&gt;1. &lt;code&gt;:host&lt;/code&gt; styles&lt;/h3&gt;
&lt;p&gt;When defining your component styles you can use the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/:host&quot;&gt;&lt;code&gt;:host&lt;/code&gt; psuedo-class&lt;/a&gt; to select the shadow DOM host. In the case of a custom element, the host will refer to the custom element tag you&apos;ve defined (i.e. &lt;code&gt;my-component&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;my-component&amp;gt;&amp;lt;/my-component&amp;gt;

&amp;lt;script type=&quot;module&quot;&amp;gt;
	class MyComponent extends HTMLElement {
		constructor() {
			super();
			this.attachShadow({ mode: &apos;open&apos; });
		}
		connectedCallback() {
			this.shadowRoot.innerHTML = `
				&amp;lt;style&amp;gt;
					:host {
						background: tan; 
						padding: 1rem;
					}
					p {
						color: white; 
						background: teal;
						padding: 1rem;
					}
				&amp;lt;/style&amp;gt;
				&amp;lt;p&amp;gt;A paragraph inside my-component.&amp;lt;/p&amp;gt;
			`;
		}
	}
	customElements.define(&apos;my-component&apos;, MyComponent);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;my-component&amp;gt;&amp;lt;/my-component&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;If you were to open dev tools and inspect &lt;code&gt;&amp;lt;my-component&amp;gt;&lt;/code&gt; you&apos;d see a style declaration that looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:host {
	background: tan;
	padding: 1rem;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&apos;s possible to override these styles in the light DOM by creating a ruleset that selects &lt;code&gt;my-component&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;my-component {
	background: goldenrod;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;my-component style=&quot;background: goldenrod;&quot;&amp;gt;&amp;lt;/my-component&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;An important note is that styles applied to &lt;code&gt;my-component&lt;/code&gt; will not cascade into any elements within the shadow DOM. For example, changing the color will have no effect on the paragraph tag.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;my-component {
	color: yellow; /* &amp;lt;-- This will have no affect on the shadow DOM */
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;my-component style=&quot;color: yellow;&quot;&amp;gt;&amp;lt;/my-component&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h3&gt;2. &lt;code&gt;::part()&lt;/code&gt; psuedo-element&lt;/h3&gt;
&lt;p&gt;But what if we did want to allow for changes on elements within the shadow DOM? Well for that you can look to the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/::part&quot;&gt;&lt;code&gt;::part()&lt;/code&gt; psuedo-element&lt;/a&gt;. With this, we can explicitly expose a part (get it?) of our shadow tree for developers to style.&lt;/p&gt;
&lt;p&gt;It starts by adding a &lt;code&gt;part&lt;/code&gt; attribute with some name value to the element that you want to be exposed for styling. As an example, we can update the paragraph in the shadow DOM from &lt;code&gt;my-component&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;this.shadowRoot.innerHTML = `
	&amp;lt;style&amp;gt;
		/* ... styles ... */
	&amp;lt;/style&amp;gt;
	&amp;lt;p part=&quot;text&quot;&amp;gt;A paragraph inside my-component.&amp;lt;/p&amp;gt;
`;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Back in the light DOM we can now use the &lt;code&gt;::part()&lt;/code&gt; psuedo-element along with the name we gave to the paragraph.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;my-component::part(text) {
	color: yellow; /* &amp;lt;-- This WILL have an affect on the shadow DOM */
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;style&amp;gt;
my-component-with-part::part(text) {
color: yellow;
}
&amp;lt;/style&amp;gt;
&amp;lt;my-component-with-part&amp;gt;&amp;lt;/my-component-with-part&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h3&gt;3. CSS variables&lt;/h3&gt;
&lt;p&gt;At the most granular level of control, we can allow individual CSS properties to be overridden using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties&quot;&gt;CSS variables&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One of the rules of shadow DOM is that CSS variables can &quot;pierce through&quot; the shadow boundary. Practically speaking this means if you define component styles in shadow DOM using a CSS variable, that variable can be changed in the light DOM and it will be reflected in your component.&lt;/p&gt;
&lt;p&gt;Going back to our &lt;code&gt;my-component&lt;/code&gt; example, if we remove the &lt;code&gt;part&lt;/code&gt; so the paragraph tag can no longer be restyled and now add a &lt;code&gt;border-radius&lt;/code&gt; property with a CSS variable as a value...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;this.shadowRoot.innerHTML = `
	&amp;lt;style&amp;gt;
		/* ... other styles ... */
		p {
			color: white; 
			background: teal;
			padding: 1rem;
			border-radius: var(--rounded-corner);
		}
	&amp;lt;/style&amp;gt;
	&amp;lt;p&amp;gt;A paragraph inside my-component.&amp;lt;/p&amp;gt;
`;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...we can define a CSS variable in the light DOM that changes only that property and nothing else in the shadow DOM.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:root {
	--rounded-corner: 10px;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;my-component style=&quot;--rounded-corner: 10px;&quot;&amp;gt;&amp;lt;/my-component&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h3&gt;An API for your styles&lt;/h3&gt;
&lt;p&gt;One of the really interesting ways to think about this is that shadow DOM allows you to create an API for your styles.&lt;/p&gt;
&lt;p&gt;With shadow DOM, you have fine-grained control over how your UI can be changed and adjusted. While this comes at the cost of flexibility, for some use cases like building a design system/component library, this level of control can be extremely desirable.&lt;/p&gt;
&lt;p&gt;As I said earlier, shadow DOM is just another tool that is good for some things and not for others –– if fine-grained control of UI styles is something you&apos;re looking for, shadow DOM is an &lt;em&gt;excellent&lt;/em&gt; tool for that job.&lt;/p&gt;
&lt;h2&gt;Templates and shadow DOM&lt;/h2&gt;
&lt;p&gt;It&apos;s time to start putting the pieces together to build our first JavaScript web component!&lt;/p&gt;
&lt;p&gt;While I have thus far demonstrated using &lt;code&gt;innerHTML&lt;/code&gt; to set the contents of shadow DOM, it is far more desirable to use templates instead. There are three ways that a template can be added to the shadow DOM.&lt;/p&gt;
&lt;h3&gt;1. Statically created templates&lt;/h3&gt;
&lt;p&gt;The first option is to statically define your template directly in HTML and get a reference to it in your custom element class. While this works perfectly well, it is often the least used method of creating a JavaScript web component because your component markup is now separated in a different place from your component behavior.&lt;/p&gt;
&lt;p&gt;This example might look trivial, but in most real-world scenarios it&apos;s common to separate web component behavior into a separate JavaScript file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;name-tag&amp;gt;&amp;lt;/name-tag&amp;gt;

&amp;lt;template id=&quot;name-tag-template&quot;&amp;gt;
	&amp;lt;style&amp;gt;
		section {
			border: 2px solid rgb(95, 85, 236);
			border-bottom: solid 12px rgb(95, 85, 236);
			border-radius: 6px;
			padding: 0;
			margin: 1rem 0 0 0;
		}
		p {
			color: white;
			background: rgb(95, 85, 236, 0.5);
			padding: 10px 40px;
			margin: 0;
			font-weight: 500;
			text-align: center;
		}
		#greeting {
			color: white;
			background: rgb(95, 85, 236);
			border-radius: 3px 3px 0 0;
			font-size: 1.1rem;
		}
		#name {
			font-weight: 700;
			font-size: 1.4rem;
			padding: 20px;
		}
	&amp;lt;/style&amp;gt;
	&amp;lt;section&amp;gt;
		&amp;lt;p id=&quot;greeting&quot;&amp;gt;Hello, my name is:&amp;lt;/p&amp;gt;
		&amp;lt;p id=&quot;name&quot;&amp;gt;Poppy&amp;lt;/p&amp;gt;
	&amp;lt;/section&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script type=&quot;module&quot;&amp;gt;
	class NameTag extends HTMLElement {
		constructor() {
			super();
			this.attachShadow({mode: &apos;open&apos;});
			this.tmpl = document.querySelector(&apos;#name-tag-template&apos;);
		}
		connectedCallback() {
			this.shadowRoot.appendChild(this.tmpl.content.cloneNode(true));
		}
	}
	customElements.define(&apos;name-tag&apos;, NameTag);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. Dynamically created templates&lt;/h3&gt;
&lt;p&gt;Dynamically creating your template at runtime is by far the most common way to see JavaScript web components built. With this method, your component markup, styles, and behavior are all co-located in the same place.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;name-tag&amp;gt;&amp;lt;/name-tag&amp;gt;

&amp;lt;script type=&quot;module&quot;&amp;gt;
	const template = document.createElement(&apos;template&apos;);
	template.innerHTML = `
		&amp;lt;style&amp;gt;
			/* ... styles ... */
		&amp;lt;/style&amp;gt;
		&amp;lt;section&amp;gt;
			&amp;lt;p id=&quot;greeting&quot;&amp;gt;Hello, my name is:&amp;lt;/p&amp;gt;
			&amp;lt;p id=&quot;name&quot;&amp;gt;Poppy&amp;lt;/p&amp;gt;
		&amp;lt;/section&amp;gt;
	`;

	class NameTag extends HTMLElement {
		constructor() {
			super();
			this.attachShadow({mode: &apos;open&apos;});
			this.tmpl = document.querySelector(&apos;#name-tag-template&apos;);
		}
		connectedCallback() {
			this.shadowRoot.appendChild(this.tmpl.content.cloneNode(true));
		}
	}
	customElements.define(&apos;name-tag&apos;, NameTag);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Quick development tip&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When defining markup/styles in JavaScript you typically will do so using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals&quot;&gt;template literals&lt;/a&gt; (as I&apos;ve shown above). This unfortunately means you lose out on code editor features like syntax highlighting and autocomplete. But this can be fixed!&lt;/p&gt;
&lt;p&gt;In editors like VS Code, there are extensions such as &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=Tobermory.es6-string-html&quot;&gt;es6-string-html&lt;/a&gt; that can provide syntax highlighting support for template literals. Once installed you just need to mark the strings you want to be syntax highlighted with a comment saying &quot;html&quot; or &quot;css&quot;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const template = /* html */ `
	&amp;lt;p&amp;gt;I will be syntax highlighted!&amp;lt;/p&amp;gt;
`;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, I can&apos;t show you here in this blog post because the markdown rendered I use for my website doesn&apos;t support this functionality, but trust me it works 😉.&lt;/p&gt;
&lt;p&gt;Another helpful setting to change in VS Code is the &quot;Emmet: Include Languages&quot; setting. It will enable Emmet abbreviations in languages that are not supported by default. For the sake of this discussion adding a mapping of &lt;code&gt;{&quot;javascript&quot;: &quot;html&quot;}&lt;/code&gt; will enable Emmet in JavaScript files!&lt;/p&gt;
&lt;h3&gt;3. Declarative shadow DOM&lt;/h3&gt;
&lt;p&gt;The final method of using templates and shadow DOM together is declarative shadow DOM and like the name suggests this is a fully declarative way to create a shadow DOM and attach it to a custom element –– aka no JavaScript required!&lt;/p&gt;
&lt;p&gt;With that said declarative shadow DOM is only supported in Chrome and Safari at this moment. But fear not! Cross-browser support is genuinely and truly &lt;a href=&quot;https://github.com/mozilla/standards-positions/issues/335#issuecomment-1714182997&quot;&gt;right around the corner&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;name-tag&amp;gt;
	&amp;lt;template shadowrootmode=&quot;open&quot;&amp;gt;
		&amp;lt;style&amp;gt;
			/* ... styles ... */
		&amp;lt;/style&amp;gt;
		&amp;lt;section&amp;gt;
			&amp;lt;p id=&quot;greeting&quot;&amp;gt;Hello, my name is:&amp;lt;/p&amp;gt;
			&amp;lt;p id=&quot;name&quot;&amp;gt;Poppy&amp;lt;/p&amp;gt;
		&amp;lt;/section&amp;gt;
	&amp;lt;/template&amp;gt;
&amp;lt;/name-tag&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Slots&lt;/h2&gt;
&lt;p&gt;Those with a keen eye may have noticed that the name tags in the above examples are hardcoded with the name &quot;Poppy,&quot; but it would be great if we could pass in any name. We can with the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot&quot;&gt;&lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; element&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When used in tandem with templates and shadow DOM, slots act as a way to render children within a component template.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;template&amp;gt;
	&amp;lt;style&amp;gt;
		/* ... styles ... */
	&amp;lt;/style&amp;gt;
	&amp;lt;section&amp;gt;
		&amp;lt;p id=&quot;greeting&quot;&amp;gt;Hello, my name is:&amp;lt;/p&amp;gt;
		&amp;lt;p id=&quot;name&quot;&amp;gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;lt;/p&amp;gt;
	&amp;lt;/section&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now pass in any name we want to our &lt;code&gt;name-tag&lt;/code&gt; component and it will be rendered within the &lt;code&gt;&amp;lt;p id=&quot;name&quot;&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;name-tag&amp;gt;Poppy&amp;lt;/name-tag&amp;gt;
&amp;lt;name-tag&amp;gt;Hawk&amp;lt;/name-tag&amp;gt;
&amp;lt;name-tag&amp;gt;Arthi&amp;lt;/name-tag&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;name-tag&amp;gt;Poppy&amp;lt;/name-tag&amp;gt;
&amp;lt;name-tag&amp;gt;Hawk&amp;lt;/name-tag&amp;gt;
&amp;lt;name-tag&amp;gt;Arthi&amp;lt;/name-tag&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h3&gt;Named slots&lt;/h3&gt;
&lt;p&gt;It is also possible to have more than one slot in your template using named slots. You can do this by adding a &lt;code&gt;name&lt;/code&gt; attribute to slot elements and a &lt;code&gt;slot&lt;/code&gt; attribute to the content you pass into your web component.&lt;/p&gt;
&lt;p&gt;Note: There can only be one slot without a &lt;code&gt;name&lt;/code&gt; attribute in your template. This is called the default slot.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;template&amp;gt;
	&amp;lt;style&amp;gt;
		/* ... styles ... */
	&amp;lt;/style&amp;gt;
	&amp;lt;section&amp;gt;
		&amp;lt;p id=&quot;greeting&quot;&amp;gt;Hello, my name is:&amp;lt;/p&amp;gt;
		&amp;lt;p id=&quot;name&quot;&amp;gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;lt;/p&amp;gt;
	&amp;lt;/section&amp;gt;
	&amp;lt;slot name=&quot;message&quot;&amp;gt;&amp;lt;/slot&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;name-tag&amp;gt;
	Poppy
	&amp;lt;p slot=&quot;message&quot;&amp;gt;This is a message!&amp;lt;/p&amp;gt;
&amp;lt;/name-tag&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;name-tag&amp;gt;
Poppy
&amp;lt;p slot=&quot;message&quot; style=&quot;color: white;&quot;&amp;gt;This is a message!&amp;lt;/p&amp;gt;
&amp;lt;/name-tag&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h3&gt;Styling slotted content&lt;/h3&gt;
&lt;p&gt;To wrap things up, I should also mention that you can style content that is passed into slots with the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/::slotted&quot;&gt;&lt;code&gt;::slotted()&lt;/code&gt; psuedo-element&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;template&amp;gt;
	&amp;lt;style&amp;gt;
		/* ... other styles ... */
		::slotted(p) {
			color: red;
		}
	&amp;lt;/style&amp;gt;
	&amp;lt;!-- ... template markup ... --&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;name-tag&amp;gt;
Poppy
&amp;lt;p slot=&quot;message&quot;&amp;gt;This is a message!&amp;lt;/p&amp;gt;
&amp;lt;/name-tag&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h2&gt;In the next episode...&lt;/h2&gt;
&lt;p&gt;Hopefully, I accomplished my mission and you (dear reader) have an increased clarity and appreciation for what shadow DOM is, how it works, and why it&apos;s special. If not, &lt;a href=&quot;https://fosstodon.org/@hawkticehurst&quot;&gt;let me know&lt;/a&gt; what questions you still have and I&apos;d love to try and answer them!&lt;/p&gt;
&lt;p&gt;We&apos;ve also officially concluded our intro to web components and now we get to have some more fun. In the next post, we&apos;re talking about what I think is one of the most compelling (and not usually talked about) use cases for web components –– markdown.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fosstodon.org/@hawkticehurst&quot;&gt;Follow me on Mastodon&lt;/a&gt; for updates and see you in the next one!&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Next post: &lt;a href=&quot;/2023/12/portable-html-web-components/&quot;&gt;Portable HTML web components&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;template id=&quot;name-tag-template&quot;&amp;gt;
&amp;lt;style&amp;gt;
section {
border: 2px solid rgb(95, 85, 236);
border-bottom: solid 12px rgb(95, 85, 236);
border-radius: 6px;
padding: 0;
margin: 1rem 0 0 0;
}
p {
color: white;
background: rgb(95, 85, 236, 0.5);
padding: 10px 40px;
margin: 0;
font-weight: 500;
text-align: center;
}
#greeting {
color: white;
background: rgb(95, 85, 236);
border-radius: 3px 3px 0 0;
font-size: 1.1rem;
}
#name {
font-weight: 700;
font-size: 1.4rem;
padding: 20px;
}
::slotted(p) {
color: red;
}
&amp;lt;/style&amp;gt;
&amp;lt;section&amp;gt;
&amp;lt;p id=&quot;greeting&quot;&amp;gt;Hello, my name is:&amp;lt;/p&amp;gt;
&amp;lt;p id=&quot;name&quot;&amp;gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/section&amp;gt;
&amp;lt;slot name=&quot;message&quot;&amp;gt;&amp;lt;/slot&amp;gt;
&amp;lt;/template&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;template id=&quot;pink-template&quot;&amp;gt;
&amp;lt;style&amp;gt;
p {
color: teal;
}
&amp;lt;/style&amp;gt;
&amp;lt;p&amp;gt;I&apos;m teal text inside the shadow DOM!&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;template id=&quot;my-component-template&quot;&amp;gt;
&amp;lt;style&amp;gt;
:host {
background: tan;
padding: 1rem;
}
p {
color: white;
background: teal;
padding: 1rem;
border-radius: var(--rounded-corner);
}
&amp;lt;/style&amp;gt;
&amp;lt;p&amp;gt;A paragraph inside my-component.&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;template id=&quot;my-component-with-part-template&quot;&amp;gt;
&amp;lt;style&amp;gt;
:host {
background: tan;
padding: 1rem;
}
p {
color: white;
background: teal;
padding: 1rem;
}
&amp;lt;/style&amp;gt;
&amp;lt;p part=&quot;text&quot;&amp;gt;A paragraph inside my-component.&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;script type=&quot;module&quot;&amp;gt;
window.addEventListener(&apos;load&apos;, () =&amp;gt; {
const tmpl = document.querySelector(&apos;#pink-template&apos;);
const div = document.querySelector(&apos;#example&apos;);
const div2 = document.querySelector(&apos;#example2&apos;);
div.attachShadow({ mode: &quot;open&quot; });
div2.attachShadow({ mode: &quot;open&quot; });
console.log(tmpl.content);
div.shadowRoot.appendChild(tmpl.content.cloneNode(true));
div2.shadowRoot.appendChild(tmpl.content.cloneNode(true));
});
class NameTag extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: &apos;open&apos;});
this.tmpl = document.querySelector(&apos;#name-tag-template&apos;);
}
connectedCallback() {
this.shadowRoot.appendChild(this.tmpl.content.cloneNode(true));
}
}
customElements.define(&apos;name-tag&apos;, NameTag);
class MyComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: &apos;open&apos; });
this.tmpl = document.querySelector(&apos;#my-component-template&apos;);
}
connectedCallback() {
this.shadowRoot.appendChild(this.tmpl.content.cloneNode(true));
}
}
customElements.define(&apos;my-component&apos;, MyComponent);
class MyComponentWithPart extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: &apos;open&apos; });
this.tmpl = document.querySelector(&apos;#my-component-with-part-template&apos;);
}
connectedCallback() {
this.shadowRoot.appendChild(this.tmpl.content.cloneNode(true));
}
}
customElements.define(&apos;my-component-with-part&apos;, MyComponentWithPart);
&amp;lt;/script&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Templates and todo lists</title><link>https://hawkticehurst.com/2023/12/templates-and-todo-lists/</link><guid isPermaLink="true">https://hawkticehurst.com/2023/12/templates-and-todo-lists/</guid><description>Day two of a series of interactive posts that explores the world of web components.</description><pubDate>Thu, 14 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Welcome back to 7 Days of Web Components! A series of interactive blog posts that introduces and explores the world of web components.&lt;/p&gt;
&lt;p&gt;This is the post for day two, if you missed &lt;a href=&quot;https://hawkticehurst.com/2023/12/let-it-snow/&quot;&gt;day one&lt;/a&gt; I encourage you to go back and start there.&lt;/p&gt;
&lt;p&gt;Today we&apos;re diving a bit deeper into the basics of web components and will be covering the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; HTML element and its associated JavaScript API.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Please note: This blog post is filled with interactive code examples. If you&apos;re reading this from an RSS feed, I highly recommend you open the &lt;a href=&quot;https://hawkticehurst.com/2023/12/templates-and-todo-lists/&quot;&gt;original post&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;A sneak peek&lt;/h2&gt;
&lt;p&gt;Here&apos;s what we&apos;re building today. It&apos;s a tried and true classic –– the todo list.&lt;/p&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;todo-list&amp;gt;
&amp;lt;input type=&quot;text&quot; placeholder=&quot;Add todo item&quot;&amp;gt;
&amp;lt;section class=&quot;todos&quot;&amp;gt;
&amp;lt;div class=&quot;todo&quot;&amp;gt;
&amp;lt;input id=&quot;pick-up-groceries&quot; type=&quot;checkbox&quot;&amp;gt;
&amp;lt;label for=&quot;pick-up-groceries&quot;&amp;gt;Pick up groceries&amp;lt;/label&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;todo&quot;&amp;gt;
&amp;lt;input id=&quot;go-on-a-walk&quot; type=&quot;checkbox&quot;&amp;gt;
&amp;lt;label for=&quot;go-on-a-walk&quot;&amp;gt;Go on a walk&amp;lt;/label&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;span id=&quot;remaining&quot;&amp;gt;2&amp;lt;/span&amp;gt; remaining&amp;lt;/p&amp;gt;
&amp;lt;button&amp;gt;Clear completed&amp;lt;/button&amp;gt;
&amp;lt;template&amp;gt;
&amp;lt;div class=&quot;todo&quot;&amp;gt;
&amp;lt;input type=&quot;checkbox&quot;&amp;gt;
&amp;lt;label&amp;gt;&amp;lt;/label&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;
&amp;lt;/todo-list&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h2&gt;Templates&lt;/h2&gt;
&lt;p&gt;You can think of &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; as a storage container for your markup. Any valid HTML fragments that you put inside of this container will not be rendered on the page until you explicitly attach them to the DOM (or Shadow DOM as we&apos;ll discuss tomorrow).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;p&amp;gt;I&apos;m a regular paragraph. You can see me.&amp;lt;/p&amp;gt;
&amp;lt;template&amp;gt;
	&amp;lt;p&amp;gt;I&apos;m a paragraph in a template. You can&apos;t see me.&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can use browser dev tools to inspect the below example. You&apos;ll find a &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; element right after the paragraph containing the HTML fragment shown above.&lt;/p&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;p&amp;gt;I&apos;m a regular paragraph. You can see me.&amp;lt;/p&amp;gt;
&amp;lt;template&amp;gt;
&amp;lt;p&amp;gt;I&apos;m a paragraph in a template. You can&apos;t see me.&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;Templates provide an easy HTML-based way of storing structured pieces of UI that need to be repeatedly used in a component or page.&lt;/p&gt;
&lt;p&gt;These pieces of UI can be accessed via a read-only JavaScript property included on the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLTemplateElement&quot;&gt;&lt;code&gt;HTMLTemplateElement&lt;/code&gt; interface&lt;/a&gt; called &lt;code&gt;content&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const tmpl = document.querySelector(&apos;template&apos;);
const clone = tmpl.content.cloneNode(true);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s two important things to call out here:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Use of &lt;code&gt;cloneNode&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When copying the content of a template it&apos;s extremely common to use the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode&quot;&gt;&lt;code&gt;cloneNode&lt;/code&gt;&lt;/a&gt; method.&lt;/p&gt;
&lt;p&gt;This is a method of the built-in &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Node&quot;&gt;&lt;code&gt;Node&lt;/code&gt; interface&lt;/a&gt; that, as the name suggests, will return a duplicate of the node on which the method was called. Using &lt;code&gt;cloneNode&lt;/code&gt; with a template ensures that you can change and manipulate each copy of a template without affecting others.&lt;/p&gt;
&lt;p&gt;The passed-in boolean parameter also controls if any subtrees in the template content should be cloned (i.e. a deep clone). &lt;em&gt;(The answer is usually yes.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;warning&quot;&amp;gt;Be aware: Cloning a node copies all of its attributes and their values, including inline listeners. This is especially important for attributes like &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; which should not be duplicated in a document. If using an &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; you&apos;ll need to modify each template you clone to be unique.&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;tmpl.content&lt;/code&gt; returns an &lt;code&gt;HTMLFragment&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It&apos;s important to remember that &lt;code&gt;tmpl.content&lt;/code&gt; (and &lt;code&gt;tmpl.content.cloneNode&lt;/code&gt;) will return an &lt;code&gt;HTMLFragment&lt;/code&gt;. This means if you ever want to access and/or manipulate the contents of a template before adding it to the DOM you&apos;ll need to do a bit of extra work.&lt;/p&gt;
&lt;p&gt;The easiest thing you can do is use &lt;code&gt;querySelector&lt;/code&gt; to find the specific element(s) you want and then directly manipulate them with JavaScript.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const paragraph = clone.querySelector(&apos;p&apos;);
paragraph.textContent = &apos;This paragraph has been changed!&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;A quick example&lt;/h2&gt;
&lt;p&gt;To pull this all together, let&apos;s scaffold a tiny vanilla example: A button that, when clicked, will copy the contents of a template and add it to the DOM.&lt;/p&gt;
&lt;p&gt;Also, notice that we haven&apos;t mentioned or used web components at all yet. This is one of the cool features of &quot;web component&quot; APIs –– they are browser APIs first and foremost, you are not restricted in how or where they can be used.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button id=&quot;copy&quot;&amp;gt;Clone template content&amp;lt;/button&amp;gt;
&amp;lt;section id=&quot;container&quot;&amp;gt;&amp;lt;/section&amp;gt;
&amp;lt;template id=&quot;tmpl&quot;&amp;gt;
	&amp;lt;p&amp;gt;I&apos;m a piece of UI!&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script type=&quot;module&quot;&amp;gt;
	const button = document.querySelector(&apos;#copy&apos;);
	const container = document.querySelector(&apos;#container&apos;);
	const tmpl = document.querySelector(&apos;#tmpl&apos;);

	button.addEventListener(&apos;click&apos;, () =&amp;gt; {
		const clone = tmpl.content.cloneNode(true);
		container.appendChild(clone);
	});
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;section class=&quot;demo&quot;&amp;gt;
&amp;lt;button id=&quot;copy&quot;&amp;gt;Clone template content&amp;lt;/button&amp;gt;
&amp;lt;section id=&quot;container&quot;&amp;gt;&amp;lt;/section&amp;gt;
&amp;lt;template id=&quot;tmpl&quot;&amp;gt;
&amp;lt;p&amp;gt;I&apos;m a piece of UI!&amp;lt;/p&amp;gt;
&amp;lt;/template&amp;gt;
&amp;lt;/section&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h2&gt;Using templates in web components&lt;/h2&gt;
&lt;p&gt;In a web component context, templates are almost always demonstrated and used as a tool for creating the underlying markup of a JavaScript Web Component (&lt;em&gt;note: if you don&apos;t know what a JavaScript Web Component is see the post from &lt;a href=&quot;https://hawkticehurst.com/2023/12/let-it-snow/&quot;&gt;day one&lt;/a&gt;&lt;/em&gt;). This, however, requires Shadow DOM so we&apos;ll leave that conversation for tomorrow.&lt;/p&gt;
&lt;p&gt;Instead, we&apos;re going to talk about how templates can be used along with HTML web components to help you avoid manually creating and configuring DOM nodes.&lt;/p&gt;
&lt;p&gt;And yes.. for those paying attention, this &lt;em&gt;does&lt;/em&gt; conflict with what I said yesterday about HTML web components &quot;only using the custom elements API and [nothing else from] the &apos;web components&apos; bucket, such as shadow DOM or templates.&quot;&lt;/p&gt;
&lt;p&gt;As I said above, web component APIs can be mixed and matched as needed. The difference between HTML and JavaScript Web Components exists not so much as two discrete entities but rather as living along a spectrum.&lt;/p&gt;
&lt;h2&gt;A todo list (the hard way)&lt;/h2&gt;
&lt;p&gt;So let&apos;s build our todo list! To demonstrate the power of templates, I&apos;ll first show you what it looks like to do this without the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; element. Each todo item will be manually created and configured using regular JavaScript DOM manipulation APIs.&lt;/p&gt;
&lt;p&gt;Given the following starter HTML...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;todo-list&amp;gt;
	&amp;lt;input type=&quot;text&quot; placeholder=&quot;Add todo item&quot; /&amp;gt;
	&amp;lt;section class=&quot;todos&quot;&amp;gt;&amp;lt;/section&amp;gt;
&amp;lt;/todo-list&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...we&apos;ll add a todo item anytime the &lt;code&gt;Enter&lt;/code&gt; key is pressed in the text field.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class TodoList extends HTMLElement {
	constructor() {
		super();
		this.input = this.querySelector(&apos;input&apos;);
		this.todos = this.querySelector(&apos;.todos&apos;);
		this.input.addEventListener(&apos;keydown&apos;, this.addTodo);
	}
	addTodo = (event) =&amp;gt; {
		if (event.key !== &apos;Enter&apos;) return;
		// Create elements required to make a todo item
		const todo = document.createElement(&apos;div&apos;);
		const label = document.createElement(&apos;label&apos;);
		const checkbox = document.createElement(&apos;input&apos;);
		// Configure elements
		const todoText = event.target.value;
		const uid = todoText.toLowerCase().replace(/\s+/g, &apos;-&apos;);
		todo.classList.add(&apos;todo&apos;);
		checkbox.type = &apos;checkbox&apos;;
		checkbox.setAttribute(&apos;id&apos;, uid);
		label.setAttribute(&apos;for&apos;, uid);
		label.textContent = todoText;
		// Append checkbox and label inside todo item
		todo.appendChild(checkbox);
		todo.appendChild(label);
		// Add todo item to the todos container
		this.todos.appendChild(todo);
		// Clear text field
		event.target.value = &apos;&apos;;
	};
}
customElements.define(&apos;todo-list&apos;, TodoList);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;vanilla-todo-list&amp;gt;
&amp;lt;input type=&quot;text&quot; placeholder=&quot;Add todo item&quot; /&amp;gt;
&amp;lt;section class=&quot;todos&quot;&amp;gt;&amp;lt;/section&amp;gt;
&amp;lt;/vanilla-todo-list&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;It&apos;s just come Classic Vanilla DOM Manipulation™. The only potentially fancy thing to note is that we create a unique identifier (uid) using the value we get from the text field.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const uid = todoText.toLowerCase().replace(/\s+/g, &apos;-&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will turn any space-separated string into a lowercase string separated by dashes. We need this &lt;code&gt;uid&lt;/code&gt; to implement proper accessibility semantics by creating a unique association between the todo item checkbox and label.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;checkbox.setAttribute(&apos;id&apos;, uid);
label.setAttribute(&apos;for&apos;, uid);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&apos;m sure there&apos;s an even smarter/better way to create this association dynamically, but that&apos;s what I came up with for today.&lt;/p&gt;
&lt;p&gt;Unique identifiers aside, I hope you get the big idea. Creating UI using vanilla DOM manipulation is fairly straightforward but not at all fun to write.&lt;/p&gt;
&lt;h2&gt;A todo list (the template way)&lt;/h2&gt;
&lt;p&gt;Let&apos;s create a new todo list that instead includes a &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; tag as a child of our custom element.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;todo-list&amp;gt;
	&amp;lt;input type=&quot;text&quot; placeholder=&quot;Add todo item&quot; /&amp;gt;
	&amp;lt;section class=&quot;todos&quot;&amp;gt;&amp;lt;/section&amp;gt;
	&amp;lt;template&amp;gt;
		&amp;lt;div class=&quot;todo&quot;&amp;gt;
			&amp;lt;input type=&quot;checkbox&quot; /&amp;gt;
			&amp;lt;label&amp;gt;&amp;lt;/label&amp;gt;
		&amp;lt;/div&amp;gt;
	&amp;lt;/template&amp;gt;
&amp;lt;/todo-list&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This template can then be referenced and cloned within our component class.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class TodoList extends HTMLElement {
	constructor() {
		super();
		this.input = this.querySelector(&apos;input&apos;);
		this.todos = this.querySelector(&apos;.todos&apos;);
		// New! Get reference to template
		this.template = this.querySelector(&apos;template&apos;);
		this.input.addEventListener(&apos;keydown&apos;, this.addTodo);
	}
	addTodo = (event) =&amp;gt; {
		if (event.key !== &apos;Enter&apos;) return;
		// Clone template
		const tmpl = this.template.content.cloneNode(true);
		// Get references to checkbox and label inside template
		const checkbox = tmpl.querySelector(&apos;input&apos;);
		const label = tmpl.querySelector(&apos;label&apos;);
		// Insert data/content into template
		const todoText = event.target.value;
		const uid = todoText.toLowerCase().replace(/\s+/g, &apos;-&apos;);
		checkbox.setAttribute(&apos;id&apos;, uid);
		label.setAttribute(&apos;for&apos;, uid);
		label.textContent = todoText;
		// Add todo to the todos container
		this.todos.appendChild(tmpl);
		// Clear text field
		event.target.value = &apos;&apos;;
	};
}
customElements.define(&apos;todo-list&apos;, TodoList);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;slim-todo-list&amp;gt;
&amp;lt;input type=&quot;text&quot; placeholder=&quot;Add todo item&quot; /&amp;gt;
&amp;lt;section class=&quot;todos&quot;&amp;gt;&amp;lt;/section&amp;gt;
&amp;lt;template&amp;gt;
&amp;lt;div class=&quot;todo&quot;&amp;gt;
&amp;lt;input type=&quot;checkbox&quot; /&amp;gt;
&amp;lt;label&amp;gt;&amp;lt;/label&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;
&amp;lt;/slim-todo-list&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;A solid improvement! With templates, we can avoid all of the manual DOM creation and configuration from earlier.&lt;/p&gt;
&lt;p&gt;Admittedly, the above template code still isn&apos;t the most fun to write or look at though. It mainly comes down to the fact that there&apos;s some content (i.e. the todo item text and &lt;code&gt;for&lt;/code&gt;/&lt;code&gt;id&lt;/code&gt; attributes) that needs to be dynamically added when a todo item is created.&lt;/p&gt;
&lt;p&gt;Browser-native templating syntax is what would complete this picture, but that doesn&apos;t exist... yet. A new browser API called &lt;a href=&quot;https://github.com/WICG/webcomponents/blob/gh-pages/proposals/DOM-Parts.md&quot;&gt;DOM Parts&lt;/a&gt; is in the works that aims to provide a better mechanism for updating templates with dynamic content.&lt;/p&gt;
&lt;p&gt;This might draw a common response I see to web components, however –– they&apos;re incomplete and browser support takes too long. Ugh.&lt;/p&gt;
&lt;p&gt;So to close out I want to talk about how I&apos;ve come to think about new web component features.&lt;/p&gt;
&lt;p&gt;That also wraps up all the important parts of our todo list I wanted to talk about today. Below (in the collapsible view) you can find the code for a completed &lt;code&gt;&amp;lt;todo-list&amp;gt;&lt;/code&gt; component.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;Completed todo list code&amp;lt;/summary&amp;gt;
&amp;lt;pre class=&quot;astro-code dark-plus&quot; style=&quot;background-color:var(--code-block-background);color:var(--text-primary); overflow-x: auto;&quot; tabindex=&quot;0&quot;&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;	&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;input&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; type&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;text&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; placeholder&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;Add todo item&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;	&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;section&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; class&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;todos&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;		&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;div&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; class&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;todo&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;			&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;input&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; id&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;pick-up-groceries&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; type&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;checkbox&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;			&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;label&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; for&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;pick-up-groceries&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;Pick up groceries&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;label&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;		&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;div&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;		&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;div&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; class&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;todo&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;			&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;input&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; id&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;go-on-a-walk&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; type&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;checkbox&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;			&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;label&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; for&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;go-on-a-walk&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;Go on a walk&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;label&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;		&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;div&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;	&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;section&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;	&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;p&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;span&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; id&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;remaining&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;2&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;span&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; remaining&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;p&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;	&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;button&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;Clear completed&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;button&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;	&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;template&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;		&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;div&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; class&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;todo&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;			&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;input&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; type&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;checkbox&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;			&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;label&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;label&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;		&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;div&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;	&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;template&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;script&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; type&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;module&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	class&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4EC9B0&quot;&amp;gt; TodoList&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt; extends&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4EC9B0&quot;&amp;gt; HTMLElement&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;		constructor&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;() {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			super&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;input&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelector&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;input&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;todos&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelector&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;.todos&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;button&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelector&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;button&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;template&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelector&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;template&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;remaining&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelector&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;#remaining&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;input&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;addEventListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;keydown&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;addTodo&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;todos&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;addEventListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;change&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;updateRemainder&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;button&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;addEventListener&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;click&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;clearTodos&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		addTodo&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;event&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;=&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;			if&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;event&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;key&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; !== &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;Enter&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;return&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; todoText&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;event&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;target&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;value&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; tmpl&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;template&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;content&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;cloneNode&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;true&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; checkbox&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;tmpl&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelector&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;input&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; label&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;tmpl&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelector&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;label&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; uid&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;todoText&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;toLowerCase&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;().&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;replace&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D16969&quot;&amp;gt;/\s&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt;+&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D16969&quot;&amp;gt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;g&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;-&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;			checkbox&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;id&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;uid&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;			label&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;setAttribute&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;for&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;uid&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;			label&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;textContent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;todoText&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;todos&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;appendChild&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;tmpl&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;			event&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;target&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;value&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;updateRemainder&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		clearTodos&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = () &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;=&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; completed&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;todos&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelectorAll&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;input[type=&quot;checkbox&quot;]:checked&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#C586C0&quot;&amp;gt;			for&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; (&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; todo&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt; of&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt; completed&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;				this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;todos&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;removeChild&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;todo&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;parentNode&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;			}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;updateRemainder&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;();&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;		updateRemainder&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = () &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;=&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			const&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#4FC1FF&quot;&amp;gt; unchecked&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;todos&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;querySelectorAll&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;input[type=&quot;checkbox&quot;]:not(:checked)&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;			this&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;remaining&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;textContent&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; = &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;unchecked&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;length&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	customElements&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;.&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;define&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;todo-list&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;TodoList&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;script&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;style&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; button&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;,&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; input&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		font-family&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;inherit&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		font-size&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;inherit&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		font-weight&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;500&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;white&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		background-color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;95&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;85&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;236&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0.5&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		border&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;2px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; solid&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt; rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;95&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;85&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;236&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		border-radius&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;8px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		padding&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;6px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 10px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; button&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		font-size&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0.9rem&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		padding&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;4px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 10px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; button:hover&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;,&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; button:active&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		cursor&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;pointer&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		background-color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;95&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;85&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;236&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0.7&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; button:focus-visible&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;,&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; input:focus-visible&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		outline&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;solid&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 2px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; white&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		outline-offset&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;2px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; input::placeholder&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;#999&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; .todos&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		margin&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;20px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 0&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;flex&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		flex-direction&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;column&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		gap&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;10px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		margin-bottom&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; .todo&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;flex&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		align-items&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;center&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		gap&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;6px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; label&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		user-select&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;none&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		cursor&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;pointer&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; input&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;[&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;type&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;checkbox&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;] {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		--checkbox-size&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;24px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		-webkit-appearance&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;none&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		appearance&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;none&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		margin&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		width&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;var&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;--checkbox-size&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		height&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;var&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;--checkbox-size&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		border&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;2px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; solid&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt; rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;95&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;85&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;236&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		border-radius&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;6px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;grid&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		place-content&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;center&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		cursor&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;pointer&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; input&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;[&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;type&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;checkbox&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;]&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt;::before&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		--checkmark-size&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;12px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		content&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		box-shadow&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;inset&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt; var&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;--checkmark-size&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;var&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;--checkmark-size&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;)&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;			white&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		background-color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: CanvasText;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		width&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;var&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;--checkmark-size&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		height&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;var&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;--checkmark-size&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		clip-path&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;polygon&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;			12.08%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 50.81%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;,&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;			0.24%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 67.73%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;,&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;			42.23%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 97.13%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;,&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;			99.25%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 15.68%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;,&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;			81.01%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 2.91%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;,&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;			35.84%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 67.44%&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;		);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		transform&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;scale&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		transition&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: transform &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;100ms&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; ease-in-out&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; input&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;[&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;type&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;checkbox&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;]&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt;:checked::before&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		transform&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;scale&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;1&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; input&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;[&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;type&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&apos;checkbox&apos;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;]&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt;:focus-visible&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		outline&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;none&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		border-color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;white&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;	todo-list&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; .todo:has&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt;input&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;[&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;type&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;=&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;&quot;checkbox&quot;&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;]&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt;:checked&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;) &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt;label&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;		text-decoration&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;line-through&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;	}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;lt;/&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;style&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#808080&quot;&amp;gt;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;h2&gt;How to think about new web component features&lt;/h2&gt;
&lt;p&gt;Just like any other browser API, new web component APIs (read: features) must go through their own standards process.&lt;/p&gt;
&lt;p&gt;A combination of various &lt;a href=&quot;https://github.com/WICG/webcomponents&quot;&gt;community&lt;/a&gt; &lt;a href=&quot;https://github.com/w3c/webcomponents-cg/&quot;&gt;groups&lt;/a&gt; and browser vendor teams are continuously working together to propose new APIs, specify and standardize how they should work, and then get them implemented across browsers.&lt;/p&gt;
&lt;p&gt;This. process. takes. time.&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;info&quot;&amp;gt;Historical context: This is, in large part, the reason that web components, despite being introduced back in the early 2010s, have taken so long to increase in adoption. It took almost a decade to get the first set of web component APIs standardized and implemented across all major browsers.&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;Lucky for us, this process is starting to move quicker nowadays. Efforts like &lt;a href=&quot;https://web.dev/blog/interop-2022&quot;&gt;Interop 2022&lt;/a&gt; and &lt;a href=&quot;https://web.dev/blog/interop-2023&quot;&gt;Interop 2023&lt;/a&gt; have seen all major browser vendors working together to solve browser compatibility issues and are to a larger extent hinting at a new state of the web where browser vendors are committing to be better about implementing new and existing APIs in a timely and consistent way.&lt;/p&gt;
&lt;p&gt;Even with a quicker feedback loop, new web component features will (for all intents and purposes) almost always take longer to ship than new web framework features. This is a reality that is often frustrating to some people, but there&apos;s a really important tradeoff being made. The upfront cost of standards work means that any API added to browsers is committing to a contract of backward compatibility defined by the web –– that is to say, it’s here to stay.&lt;/p&gt;
&lt;p&gt;As others have said before me, the idea is that &lt;a href=&quot;https://jakelazaroff.com/words/web-components-will-outlive-your-javascript-framework/&quot;&gt;web components APIs should outlast entire web frameworks&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;In the next episode...&lt;/h2&gt;
&lt;p&gt;Thanks for stopping by! In the next post, we continue deeper into the basics of web components with Shadow DOM.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fosstodon.org/@hawkticehurst&quot;&gt;Follow me on Mastodon&lt;/a&gt; for updates and see you in the next one!&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Next post: &lt;a href=&quot;/2023/12/getting-shady/&quot;&gt;Getting shady&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;style&amp;gt;
.demo {
display: grid;
place-content: center;
text-align: center;
}
.demo p:last-child {
margin-bottom: 0;
}
button,
input {
font-family: inherit;
font-size: inherit;
font-weight: 500;
color: white;
background-color: rgb(95, 85, 236, 0.5);
border: 2px solid rgb(95, 85, 236);
border-radius: 8px;
padding: 6px 10px;
}
button {
font-size: 0.9rem;
padding: 4px 10px;
}
button:hover,
button:active {
cursor: pointer;
background-color: rgb(95, 85, 236, 0.7);
}
button:focus-visible,
input:focus-visible {
outline: solid 2px white;
outline-offset: 2px;
}
input::placeholder {
color: #fff;
}
.todos {
margin: 20px 0;
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 0;
}
.todo {
display: flex;
align-items: center;
gap: 6px;
}
label {
user-select: none;
cursor: pointer;
}
input[type=&apos;checkbox&apos;] {
--checkbox-size: 24px;
-webkit-appearance: none;
appearance: none;
margin: 0;
width: var(--checkbox-size);
height: var(--checkbox-size);
border: 2px solid rgb(95, 85, 236);
border-radius: 6px;
display: grid;
place-content: center;
cursor: pointer;
}
input[type=&apos;checkbox&apos;]::before {
--checkmark-size: 12px;
content: &apos;&apos;;
box-shadow: inset var(--checkmark-size) var(--checkmark-size)
white;
background-color: CanvasText;
width: var(--checkmark-size);
height: var(--checkmark-size);
clip-path: polygon(
12.08% 50.81%,
0.24% 67.73%,
42.23% 97.13%,
99.25% 15.68%,
81.01% 2.91%,
35.84% 67.44%
);
transform: scale(0);
transition: transform 100ms ease-in-out;
}
input[type=&apos;checkbox&apos;]:checked::before {
transform: scale(1);
}
input[type=&apos;checkbox&apos;]:focus-visible {
outline: none;
border-color: white;
}
.todo:has(input[type=&quot;checkbox&quot;]:checked) label {
text-decoration: line-through;
}
&amp;lt;/style&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;script type=&quot;module&quot;&amp;gt;
class TodoList extends HTMLElement {
constructor() {
super();
this.input = this.querySelector(&apos;input&apos;);
this.todos = this.querySelector(&apos;.todos&apos;);
this.button = this.querySelector(&apos;button&apos;);
this.template = this.querySelector(&apos;template&apos;);
this.remaining = this.querySelector(&apos;#remaining&apos;);
this.input.addEventListener(&apos;keydown&apos;, this.addTodo);
this.todos.addEventListener(&apos;change&apos;, this.updateRemainder);
this.button.addEventListener(&apos;click&apos;, this.clearTodos);
}
addTodo = (event) =&amp;gt; {
if (event.key !== &quot;Enter&quot;) return;
const todoText = event.target.value;
const tmpl = this.template.content.cloneNode(true);
const checkbox = tmpl.querySelector(&apos;input&apos;);
const label = tmpl.querySelector(&apos;label&apos;);
const uid = todoText.toLowerCase().replace(/\s+/g, &apos;-&apos;);
checkbox.setAttribute(&quot;id&quot;, uid);
label.setAttribute(&quot;for&quot;, uid);
label.textContent = todoText;
this.todos.appendChild(tmpl);
event.target.value = &quot;&quot;;
this.updateRemainder();
}
clearTodos = () =&amp;gt; {
const completed = this.todos.querySelectorAll(&apos;input[type=&quot;checkbox&quot;]:checked&apos;);
for (const todo of completed) {
this.todos.removeChild(todo.parentNode);
}
this.updateRemainder();
}
updateRemainder = () =&amp;gt; {
const unchecked = this.todos.querySelectorAll(&apos;input[type=&quot;checkbox&quot;]:not(:checked)&apos;);
this.remaining.textContent = unchecked.length;
}
}
customElements.define(&apos;todo-list&apos;, TodoList);&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;window.addEventListener(&apos;load&apos;, () =&amp;gt; {
	const button = document.querySelector(&apos;#copy&apos;);
	const container = document.querySelector(&apos;#container&apos;);
	const tmpl = document.querySelector(&apos;#tmpl&apos;);
	button.addEventListener(&apos;click&apos;, () =&amp;gt; {
		const clone = tmpl.content.cloneNode(true);
		container.appendChild(clone);
	});
});

class VanillaTodoList extends HTMLElement {
	constructor() {
		super();
		this.input = this.querySelector(&apos;input&apos;);
		this.todos = this.querySelector(&apos;.todos&apos;);
		this.input.addEventListener(&apos;keydown&apos;, this.addTodo);
	}
	addTodo = (event) =&amp;gt; {
		if (event.key !== &apos;Enter&apos;) return;
		// Create elements required to make todo item
		const todo = document.createElement(&apos;div&apos;);
		const label = document.createElement(&apos;label&apos;);
		const checkbox = document.createElement(&apos;input&apos;);
		// Configure elements
		const todoText = event.target.value;
		const uid = todoText.toLowerCase().replace(/\s+/g, &apos;-&apos;);
		todo.classList.add(&apos;todo&apos;);
		checkbox.type = &apos;checkbox&apos;;
		checkbox.setAttribute(&quot;id&quot;, uid);
		label.setAttribute(&quot;for&quot;, uid);
		label.textContent = todoText;
		// Append checkbox and label inside todo
		todo.appendChild(checkbox);
		todo.appendChild(label);
		// Add todo item to the todos container
		this.todos.appendChild(todo);
		event.target.value = &apos;&apos;;
	};
}
customElements.define(&apos;vanilla-todo-list&apos;, VanillaTodoList);

class SlimTodoList extends HTMLElement {
	constructor() {
		super();
		this.input = this.querySelector(&apos;input&apos;);
		this.todos = this.querySelector(&apos;.todos&apos;);
		this.template = this.querySelector(&apos;template&apos;);
		this.input.addEventListener(&apos;keydown&apos;, this.addTodo);
	}
	addTodo = (event) =&amp;gt; {
		if (event.key !== &apos;Enter&apos;) return;
		const tmpl = this.template.content.cloneNode(true);
		const checkbox = tmpl.querySelector(&apos;input&apos;);
		const label = tmpl.querySelector(&apos;label&apos;);
		const todoText = event.target.value;
		const uid = todoText.toLowerCase().replace(/\s+/g, &apos;-&apos;);
		checkbox.setAttribute(&apos;id&apos;, uid);
		label.setAttribute(&apos;for&apos;, uid);
		label.textContent = todoText;
		this.todos.appendChild(tmpl);
		event.target.value = &apos;&apos;;
	};
}
customElements.define(&apos;slim-todo-list&apos;, SlimTodoList);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>Let it snow</title><link>https://hawkticehurst.com/2023/12/let-it-snow/</link><guid isPermaLink="true">https://hawkticehurst.com/2023/12/let-it-snow/</guid><description>Day one of a series of interactive posts that explores the world of web components.</description><pubDate>Wed, 13 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Welcome to 7 Days of Web Components! A series of interactive blog posts that will introduce and explore the world of web components.&lt;/p&gt;
&lt;p&gt;This series will cover the basics of web components, more complex and real-world examples, and discussions of the wider ecosystem/future of web components. These posts will also be littered with tricks and insights I&apos;ve picked up along the way.&lt;/p&gt;
&lt;p&gt;The result should be a series that will be in no way technically complete or robust, but will hopefully offer some fresh perspectives to both beginners and seasoned builders of web components alike.&lt;/p&gt;
&lt;p&gt;I hope you enjoy!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Please note: This blog post is filled with interactive code examples. If you&apos;re reading this from an RSS feed, I highly encourage you to open the &lt;a href=&quot;https://hawkticehurst.com/2023/12/let-it-snow/&quot;&gt;original post&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;A sneak peek&lt;/h2&gt;
&lt;p&gt;Today we&apos;re going to start what will be a multi-day introduction to web components. This will lay the groundwork for some more fun and exploratory posts later on.&lt;/p&gt;
&lt;p&gt;Here&apos;s what we&apos;re building today. A tiny portal into a winter wonderland. ☃️&lt;/p&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;let-it-snow&amp;gt;
&amp;lt;button&amp;gt;Let it snow&amp;lt;/button&amp;gt;
&amp;lt;div class=&quot;snow&quot; role=&quot;presentation&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/let-it-snow&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;h2&gt;Your first web component&lt;/h2&gt;
&lt;p&gt;To start with the absolute basics, &quot;web components&quot; is a loose term used to reference a set of evolving browser APIs that can be used to create custom HTML elements. You can think of web components as the browser-native way of building reusable user interfaces.&lt;/p&gt;
&lt;p&gt;There are three primary browser APIs that are commonly used together to build web components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Custom Elements:&lt;/strong&gt; A JavaScript API for creating custom HTML elements&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shadow DOM:&lt;/strong&gt; A JavaScript API that is primarily used for creating scoped styles and component slots (i.e. rendering children within a component template)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTML Templates:&lt;/strong&gt; An HTML element (and associated JavaScript API) that typically acts as a way to define the template markup of a web component&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In today&apos;s post, we&apos;ll just be discussing the custom elements API as it is the bare minimum needed to build a web component. Shadow DOM and templates, along with some other web component APIs, will be discussed in the coming days.&lt;/p&gt;
&lt;p&gt;So what does the &lt;em&gt;most&lt;/em&gt; basic web component look like? Like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;let-it-snow&amp;gt;&amp;lt;/let-it-snow&amp;gt;

&amp;lt;script type=&quot;module&quot;&amp;gt;
	class LetItSnow extends HTMLElement {
		constructor() {
			super();
		}
	}
	customElements.define(&apos;let-it-snow&apos;, LetItSnow);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This web component does absolutely nothing. It renders nothing and has no functionality. But it &lt;em&gt;is&lt;/em&gt; a web component (or more precisely custom element). There are a few key ingredients to make note of:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. A JavaScript class that extends the native &lt;code&gt;HTMLElement&lt;/code&gt; interface&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class LetItSnow extends HTMLElement {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the defining trait of web components, they extend the built-in &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement&quot;&gt;&lt;code&gt;HTMLElement&lt;/code&gt; interface&lt;/a&gt; that all other HTML elements are built on top of.&lt;/p&gt;
&lt;p&gt;A key takeaway to internalize is that web components are, definitionally, HTML elements. From the browser&apos;s perspective, &lt;code&gt;&amp;lt;let-it-snow&amp;gt;&lt;/code&gt; is just as much an HTML element as a &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;The implications of this are extremely important.&lt;/em&gt;&lt;/strong&gt; Web components do not follow the same rules, conventions, and mental models of a “component” you might find in React, Svelte, or Vue. Instead, they follow the rules, conventions, and mental models of HTML elements.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. A class &lt;code&gt;constructor&lt;/code&gt; method that immediately calls &lt;code&gt;super()&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;constructor() {
	super();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;According to JavaScript class semantics calling &lt;code&gt;super()&lt;/code&gt; is how a class inherits from its base class –– that is, calling the super method will execute the code contained within the base class (i.e. &lt;code&gt;HTMLElement&lt;/code&gt;). With super your custom element becomes an HTML element, but without it&apos;s just a plain old JavaScript class.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. The &lt;code&gt;customElements.define&lt;/code&gt; API registers the component with the browser&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;customElements.define(&apos;let-it-snow&apos;, LetItSnow);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is how your custom HTML tag name is associated with a custom element class. Without this API any instances of your custom element in an HTML document will have no functionality.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Custom element tags follow a specific naming convention&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To avoid naming collisions with regular HTML elements, all custom elements must follow a few rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Starts with a lowercase ASCII character&lt;/li&gt;
&lt;li&gt;Contains at least one hyphen&lt;/li&gt;
&lt;li&gt;Does not contain uppercase ASCII characters&lt;/li&gt;
&lt;li&gt;Does not contain some special characters as &lt;a href=&quot;https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name&quot;&gt;defined by the spec&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note: Unfortunately, while the spec says that some names like &lt;code&gt;&amp;lt;emotion-😍&amp;gt;&lt;/code&gt; are valid, in practice, &lt;a href=&quot;https://blog.jim-nielsen.com/2023/validity-of-custom-element-tag-names/&quot;&gt;it has been found&lt;/a&gt; that browsers do not support all possible conventions.&lt;/p&gt;
&lt;h2&gt;Web component architecture&lt;/h2&gt;
&lt;p&gt;Before we talk about interactivity there&apos;s one more thing we need to cover: &quot;HTML Web Components&quot; versus &quot;JavaScript Web Components.&quot;&lt;/p&gt;
&lt;p&gt;These names refer to the two major ways of building/architecting web components.&lt;/p&gt;
&lt;p&gt;HTML web components are perhaps the simpler and more flexible way of building web components (and are what we will focus on in today&apos;s post). The core premise of HTML web components is to write plain HTML and then wrap the parts you want to be interactive using a custom element.&lt;/p&gt;
&lt;p&gt;These components only use the custom elements API and do not use any other APIs you might find in the &quot;web components&quot; bucket, such as shadow DOM or templates.&lt;/p&gt;
&lt;p&gt;They look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;counter-button&amp;gt;
	&amp;lt;button&amp;gt;Clicked &amp;lt;span&amp;gt;0&amp;lt;/span&amp;gt; times&amp;lt;/button&amp;gt;
&amp;lt;/counter-button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;JavaScript web components, on the other hand, are historically what you might think of as &quot;regular&quot; web components. They use a wider variety of web component APIs to create components that are similar to single-page application (SPA) components, popularized by tools like React.&lt;/p&gt;
&lt;p&gt;They look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;counter-button&amp;gt;&amp;lt;/counter-button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Said a different way, the HTML web component will hydrate/progressively enhance existing HTML, while the JavaScript web component will dynamically generate UI at runtime (using APIs like template and shadow DOM). There are benefits and drawbacks to both models, so it&apos;s usually a case of picking the right tool (read: architecture) for the right job.&lt;/p&gt;
&lt;p&gt;We&apos;ll cover both architectures a fair bit during this series, but if don&apos;t want to wait here are some great posts that talk more about HTML web components and some of the differences they have with JavaScript web components.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://adactio.com/journal/20618&quot;&gt;HTML web components&lt;/a&gt; by Jeremy Keith&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.jim-nielsen.com/2023/html-web-components/&quot;&gt;HTML Web Components&lt;/a&gt; by Jim Nielsen&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.oddbird.net/2023/11/17/components/&quot;&gt;HTML Web Components are Just JavaScript?&lt;/a&gt; by Miriam Eric Suzanne&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gomakethings.com/html-web-components/&quot;&gt;HTML Web Components&lt;/a&gt; by Chris Ferdinandi&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hawkticehurst.com/2023/11/a-year-working-with-html-web-components/&quot;&gt;A year working with HTML Web Components&lt;/a&gt; by Hawk Ticehurst&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Adding interactivity&lt;/h2&gt;
&lt;p&gt;Okay, now it&apos;s time to add some behavior!&lt;/p&gt;
&lt;p&gt;Our &lt;code&gt;&amp;lt;let-it-snow&amp;gt;&lt;/code&gt; component will include a button that toggles a snow animation, so we need to create a button that responds to a click event. This can be done like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;let-it-snow&amp;gt;
	&amp;lt;button&amp;gt;Let it snow&amp;lt;/button&amp;gt;
&amp;lt;/let-it-snow&amp;gt;

&amp;lt;script type=&quot;module&quot;&amp;gt;
	class LetItSnow extends HTMLElement {
		constructor() {
			super();
			this.button = this.querySelector(&apos;button&apos;);
			this.button.addEventListener(&apos;click&apos;, this.toggleSnow);
		}
		toggleSnow = () =&amp;gt; {
			alert(&apos;No snow yet, but soon!&apos;);
		};
	}
	customElements.define(&apos;let-it-snow&apos;, LetItSnow);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is also the point where we can add some basic component styles (hidden in a collapsible view).&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;Default component styles&amp;lt;/summary&amp;gt;
&amp;lt;pre class=&quot;astro-code dark-plus&quot; style=&quot;background-color: var(--code-block-background); color:var(--text-primary); overflow-x: auto;&quot; tabindex=&quot;0&quot;&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;let-it-snow&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	display&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;flex&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	justify-content&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;center&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	align-items&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;center&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	width&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;100%&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	aspect-ratio&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;2/1&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	border-radius&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;6px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;let-it-snow&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; button&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	font-family&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;inherit&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	font-size&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;inherit&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	font-weight&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;500&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;white&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	background-color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;95&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;85&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;236&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0.5&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	border&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;2px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; solid&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt; rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;95&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;85&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;236&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	border-radius&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;8px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	padding&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;6px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 10px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	z-index&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;1&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;let-it-snow&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; button:hover&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;,&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;let-it-snow&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; button:active&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	cursor&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;pointer&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	background-color&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#DCDCAA&quot;&amp;gt;rgb&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;(&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;95&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;85&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;236&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;, &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;0.7&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;);&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#569CD6&quot;&amp;gt;let-it-snow&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D7BA7D&quot;&amp;gt; button:focus-visible&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt; {&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	outline&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt;solid&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt; 2px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#CE9178&quot;&amp;gt; white&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#9CDCFE&quot;&amp;gt;	outline-offset&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;: &amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#B5CEA8&quot;&amp;gt;2px&amp;lt;/span&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;;&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span class=&quot;line&quot;&amp;gt;&amp;lt;span style=&quot;color:#D4D4D4&quot;&amp;gt;}&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;
&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;p&gt;You can try it out here:&lt;/p&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;let-it-snow-alert&amp;gt;
&amp;lt;button&amp;gt;Let it snow&amp;lt;/button&amp;gt;
&amp;lt;/let-it-snow-alert&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;There are two important things I want to highlight:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Using &lt;code&gt;this.querySelector&lt;/code&gt; to access the children of your web component&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When writing a web component class, &lt;code&gt;this&lt;/code&gt; refers to the root web component node. We can take advantage of &lt;code&gt;this&lt;/code&gt; to easily get a reference to the button inside &lt;code&gt;&amp;lt;let-it-snow&amp;gt;&lt;/code&gt; by calling &lt;code&gt;this.querySelector&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can think of it as doing a search that will be scoped to your web component and its children. This stands in comparison to calling &lt;code&gt;document.querySelector&lt;/code&gt; which will search the entire document. The former is quicker (i.e. a smaller DOM tree to search) and less error-prone. Nice!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Most event listeners should go in the &lt;code&gt;constructor&lt;/code&gt; method&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One thing we haven&apos;t talked about yet is the fact that custom elements have component lifecycle methods similar to other popular component models. You can add custom code to the following callback methods that will be executed at specific times during the component lifecycle.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;connectedCallback&lt;/strong&gt; – Called when a component is added to the DOM.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;disconnectedCallback&lt;/strong&gt; – Called when a component is removed from the DOM.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;attributeChangedCallback&lt;/strong&gt; – Called when a component attribute is changed, added, removed, or replaced.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;adoptedCallback&lt;/strong&gt; – Called when a component is moved from one HTML document to another via the &lt;code&gt;document.adoptedNode&lt;/code&gt; method.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An extremely common pattern is to see event listener code being set up in &lt;code&gt;connectedCallback&lt;/code&gt; and removed in &lt;code&gt;disconnectedCallback&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class LetItSnow extends HTMLElement {
	constructor() {
		super();
		this.button = this.querySelector(&apos;button&apos;);
	}
	connectedCallback() {
		// Event listener is added when component is added the the DOM
		this.button.addEventListener(&apos;click&apos;, this.toggleSnow);
	}
	disconnectedCallback() {
		// Event listener is removed when component is removed the the DOM
		this.button.removeEventListener(&apos;click&apos;, this.toggleSnow);
	}
	toggleSnow = () =&amp;gt; {
		alert(&apos;No snow yet, but soon!&apos;);
	};
}
customElements.define(&apos;let-it-snow&apos;, LetItSnow);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While this works, it&apos;s unnecessary, error-prone (it&apos;s common for people to forget to remove event listeners in &lt;code&gt;disconnectedCallback&lt;/code&gt;), and technically incorrect.&lt;/p&gt;
&lt;p&gt;According to the &lt;a href=&quot;https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-conformance&quot;&gt;HTML Standard&lt;/a&gt; event listener code should be set in the constructor method.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In general, the constructor should be used to set up initial state and default values, and to &lt;strong&gt;set up event listeners&lt;/strong&gt; and possibly a shadow root.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There&apos;s a little bit more to say about this, but I&apos;ve already written about it so I encourage you to read this &lt;a href=&quot;https://hawkticehurst.com/2023/11/you-are-probably-using-connectedcallback-wrong/&quot;&gt;blog post&lt;/a&gt; that goes into more depth on the topic.&lt;/p&gt;
&lt;p&gt;An easy rule of thumb that I use, however, is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code that only needs to run once when the component is created (like configuring event listeners) should go in the &lt;code&gt;constructor&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Code that needs to be run every time the component is added/removed from the DOM (like rendering tasks) should be put in &lt;code&gt;connectedCallback&lt;/code&gt; and &lt;code&gt;disconnectedCallback&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Stateful styling with custom data attributes&lt;/h2&gt;
&lt;p&gt;To wrap everything up, let&apos;s create our winter wonderland. This will be done almost entirely with CSS and take advantage of my favorite way of styling UI.&lt;/p&gt;
&lt;p&gt;Custom data attributes are a method of storing extra information in HTML elements. You create them by prefixing any attribute name you want with &lt;code&gt;data-&lt;/code&gt; and then store a corresponding value as a string –– for example &lt;code&gt;data-gap=&quot;10px&quot;&lt;/code&gt;. When combined with CSS attribute selectors they can be used to create stateful styles in your UI.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you want a short (and more fun) explainer about this topic, I made a &lt;a href=&quot;https://www.tiktok.com/@hawkticehurst/video/7242835677518236970?is_from_webapp=1&amp;amp;sender_device=pc&amp;amp;web_id=7303204280332371498&quot;&gt;TikTok&lt;/a&gt; and &lt;a href=&quot;https://youtube.com/shorts/XWD6vmSFZ8Q?si=tR5T_mIwx0vgF9q3&quot;&gt;YouTube Short&lt;/a&gt; earlier this year that you can watch.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Also, to be extremely clear, custom data attributes are not considered to be a part of the &quot;web component&quot; label. I do, however, believe they are a wonderful pairing with HTML web components, so I&apos;m giving them some attention here. Consider this one of the aforementioned tricks/insights I&apos;ve gained along the way that I&apos;m sharing now.&lt;/p&gt;
&lt;p&gt;To add our winter wonderland, we can create a &lt;code&gt;data-snow&lt;/code&gt; attribute (no value required in this case!) that will act as a boolean attribute that can be added and removed from &lt;code&gt;&amp;lt;let-it-snow&amp;gt;&lt;/code&gt; to toggle the snow animation on and off.&lt;/p&gt;
&lt;p&gt;We can do this by switching out the &lt;code&gt;alert&lt;/code&gt; from earlier with &lt;code&gt;toggleAttribute&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class LetItSnow extends HTMLElement {
	constructor() {
		super();
		this.button = this.querySelector(&apos;button&apos;);
		this.button.addEventListener(&apos;click&apos;, this.toggleSnow);
	}
	toggleSnow = () =&amp;gt; {
		this.toggleAttribute(&apos;data-snow&apos;); // &amp;lt;-- Here!
	};
}
customElements.define(&apos;let-it-snow&apos;, LetItSnow);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From here we can add the panning image animation (&lt;a href=&quot;/imgs/winter-wonderland.png&quot;&gt;image&lt;/a&gt; courtesy of DALL·E):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let-it-snow[data-snow] {
	background-image: url(&apos;/imgs/winter-wonderland.png&apos;);
	background-size: 150%;
	background-repeat: repeat-y;
	animation: pan-y 10s linear infinite alternate;
}
@keyframes pan-y {
	from {
		background-position-y: top;
	}
	to {
		background-position-y: bottom;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&apos;s the result:&lt;/p&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;let-it-snow-pan-only&amp;gt;
&amp;lt;button&amp;gt;Let it snow&amp;lt;/button&amp;gt;
&amp;lt;/let-it-snow-pan-only&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;Finally, the snow! A new DOM node is needed to create the animation, so we&apos;ll add an empty &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; as a child of our web component.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;let-it-snow&amp;gt;
	&amp;lt;button&amp;gt;Let it snow&amp;lt;/button&amp;gt;
	&amp;lt;div class=&quot;snow&quot; role=&quot;presentation&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/let-it-snow&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The snow animation is an adapted version of Jon Kantner&apos;s &lt;a href=&quot;https://codepen.io/jkantner/pen/XoNJKz&quot;&gt;Snow in 262 Bytes of CSS&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let-it-snow {
	/* ... other let-it-snow styles ... */
	position: relative;
}
let-it-snow[data-snow] .snow,
let-it-snow[data-snow] .snow:before,
let-it-snow[data-snow] .snow:after {
	--move-x: 48px;
	--move-y: 144px;
	content: &apos;&apos;;
	position: absolute;
	background: radial-gradient(#fff, #fff0 6%) 0 0 / var(--move-x) var(--move-x);
	width: 100%;
	height: 100%;
	animation: snow 4s linear infinite;
}
let-it-snow[data-snow] .snow:before {
	animation-duration: 3s;
}
let-it-snow[data-snow] .snow:after {
	animation-duration: 2s;
}
@keyframes snow {
	to {
		background-position: var(--move-x) var(--move-y);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And... we&apos;ve made a &lt;code&gt;&amp;lt;let-it-snow&amp;gt;&lt;/code&gt; web component!&lt;/p&gt;
&lt;p&gt;&amp;lt;section class=&quot;render-container&quot;&amp;gt;
&amp;lt;div class=&quot;render-container-border&quot;&amp;gt;
&amp;lt;let-it-snow&amp;gt;
&amp;lt;button&amp;gt;Let it snow&amp;lt;/button&amp;gt;
&amp;lt;div class=&quot;snow&quot; role=&quot;presentation&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/let-it-snow&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;Admittedly, it&apos;s a bit of a weird component, but hopefully, it was a fun and informative start to this series.&lt;/p&gt;
&lt;p&gt;As I said at the beginning, this is day one of a series of posts that will be released every day for the next 12 days. In the next post, we&apos;re diving a bit deeper into the basics of web components with &lt;code&gt;templates&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fosstodon.org/@hawkticehurst&quot;&gt;Follow me on Mastodon&lt;/a&gt; for updates. See you in the next one!&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Next post: &lt;a href=&quot;/2023/12/templates-and-todo-lists/&quot;&gt;Templates and todo lists&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;style&amp;gt;
let-it-snow,
let-it-snow-alert,
let-it-snow-pan-only {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
border-radius: 6px;
position: relative;
aspect-ratio: 2/1;
}
let-it-snow button,
let-it-snow-alert button,
let-it-snow-pan-only button {
font-family: inherit;
font-size: inherit;
font-weight: 500;
color: white;
background-color: rgb(95, 85, 236, 0.5);
border: 2px solid rgb(95, 85, 236);
border-radius: 8px;
padding: 6px 10px;
z-index: 1;
}
let-it-snow button:hover,
let-it-snow button:active,
let-it-snow-alert button:hover,
let-it-snow-alert button:active,
let-it-snow-pan-only button:hover,
let-it-snow-pan-only button:active {
cursor: pointer;
background-color: rgb(95, 85, 236, 0.7);
}
let-it-snow button:focus-visible,
let-it-snow-alert button:focus-visible,
let-it-snow-pan-only button:focus-visible {
outline: solid 2px white;
outline-offset: 2px;
}
let-it-snow[data-snow],
let-it-snow-pan-only[data-snow] {
background-image: url(&quot;/imgs/winter-wonderland.png&quot;);
background-size: 150%;
background-repeat: repeat-y;
animation: pan-y 10s linear infinite alternate;
}
@keyframes pan-y {
from {
background-position-y: top;
}
to {
background-position-y: bottom;
}
}
let-it-snow[data-snow] .snow,
let-it-snow[data-snow] .snow:before,
let-it-snow[data-snow] .snow:after{
--move-x: 48px;
--move-y: 144px;
background: radial-gradient(#fff, #fff0 6%) 0 0/var(--move-x) var(--move-x);
content: &quot;&quot;;
position: absolute;
width: 100%;
height: 100%;
animation: snow 4s linear infinite;
}
let-it-snow[data-snow] .snow:before {
animation-duration: 3s;
}
let-it-snow[data-snow] .snow:after {
animation-duration: 2s;
}
@keyframes snow {
to {
background-position: var(--move-x) var(--move-y);
}
}
&amp;lt;/style&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;script type=&quot;module&quot;&amp;gt;
class LetItSnow extends HTMLElement {
constructor() {
super();
this.button = this.querySelector(&apos;button&apos;);
this.button.addEventListener(&apos;click&apos;, this.toggleSnow);
}
toggleSnow = () =&amp;gt; {
this.toggleAttribute(&apos;data-snow&apos;);
};
}
customElements.define(&apos;let-it-snow&apos;, LetItSnow);&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class LetItSnowAlert extends HTMLElement {
	constructor() {
		super();
		this.button = this.querySelector(&apos;button&apos;);
		this.button.addEventListener(&apos;click&apos;, this.toggleSnow);
	}
	toggleSnow = () =&amp;gt; {
		alert(&apos;No snow yet, but soon!&apos;);
	};
}
customElements.define(&apos;let-it-snow-alert&apos;, LetItSnowAlert);

class LetItSnowPanOnly extends HTMLElement {
	constructor() {
		super();
		this.button = this.querySelector(&apos;button&apos;);
		this.button.addEventListener(&apos;click&apos;, this.toggleSnow);
	}
	toggleSnow = () =&amp;gt; {
		console.log(&quot;hello?&quot;);
		this.toggleAttribute(&apos;data-snow&apos;);
	};
}
customElements.define(&apos;let-it-snow-pan-only&apos;, LetItSnowPanOnly);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>You&apos;re (probably) using connectedCallback wrong</title><link>https://hawkticehurst.com/2023/11/you-are-probably-using-connectedcallback-wrong/</link><guid isPermaLink="true">https://hawkticehurst.com/2023/11/you-are-probably-using-connectedcallback-wrong/</guid><description>I checked the spec and most people are configuring their web components incorrectly.</description><pubDate>Wed, 29 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In what has become my biggest web component related TIL (today I learned) to date, you and I are almost definitely using the &lt;code&gt;connectedCallback&lt;/code&gt; lifecycle method incorrectly.&lt;/p&gt;
&lt;p&gt;Let me explain.&lt;/p&gt;
&lt;p&gt;Prompted by a recent &lt;a href=&quot;https://elk.zone/piaille.fr/@tbroyer/111489213503641892&quot;&gt;discussion with Thomas Broyer&lt;/a&gt;, I was reminded that when setting up a web component you can take advantage of the fact that the &lt;code&gt;constructor&lt;/code&gt; is only run once to do things like configuring event listeners.&lt;/p&gt;
&lt;p&gt;This, however, has always struck me as having a bad code smell because it is extremely common to see event handling code configured in &lt;code&gt;connectedCallback&lt;/code&gt; and correspondingly removed in &lt;code&gt;disconnectedCallback&lt;/code&gt;. If you look through the other responses to that thread and most web component code online, &lt;em&gt;nearly everyone&lt;/em&gt; will configure their components in the following way:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyComponent extends HTMLElement {
	constructor() {
		super();
	}
	connectedCallback() {
		// Add event listeners when component is added to the DOM
		this.addEventListener(&apos;click&apos;, this.clickHandler);
		this.addEventListener(&apos;mouseover&apos;, this.mouseoverHandler);
	}
	disconnectedCallback() {
		// Remove event listeners when component is removed from the DOM
		this.removeEventListener(&apos;click&apos;, this.clickHandler);
		this.removeEventListener(&apos;mouseover&apos;, this.mouseoverHandler);
	}
	clickHandler = () =&amp;gt; {
		// Handles click event
	};
	mouseoverHandler = () =&amp;gt; {
		// Handles mouseover event
	};
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But this, as I&apos;ve discovered, is incorrect.&lt;/p&gt;
&lt;h2&gt;MDN got it wrong... kind of&lt;/h2&gt;
&lt;p&gt;I vividly remember learning, early on, that most component setup should be offloaded to the &lt;code&gt;connectedCallback&lt;/code&gt; lifecycle method. I found the original MDN article on custom elements (notably one of the very first I ever read about web components) where this information comes from. In the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks&quot;&gt;section on lifecycle methods&lt;/a&gt;, they say:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;connectedCallback(): called each time the element is added to the document. The specification recommends that, as far as possible, developers should implement custom element setup in this callback rather than the constructor.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While it&apos;s easy to casually assume what &quot;custom element setup&quot; entails, it&apos;s not actually clear. And with Thomas&apos;s &lt;a href=&quot;https://elk.zone/fosstodon.org/@tbroyer@piaille.fr/111489648841902820&quot;&gt;suggestion that &quot;most people do too much in connectedCallback,&quot;&lt;/a&gt; I decided to go look at the spec.&lt;/p&gt;
&lt;p&gt;In &lt;a href=&quot;https://html.spec.whatwg.org/multipage/custom-elements.html#custom-element-conformance&quot;&gt;section 4.13.2&lt;/a&gt; of the HTML Standard, I found my answer.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In general, work should be deferred to connectedCallback as much as possible—especially work involving fetching resources or rendering. However, note that connectedCallback can be called more than once, so any initialization work that is truly one-time will need a guard to prevent it from running twice.&lt;/p&gt;
&lt;p&gt;In general, the constructor should be used to set up initial state and default values, and to set up event listeners and possibly a shadow root.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There it is... &lt;strong&gt;&quot;event listeners [should be set up in the constructor].&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;So while MDN was correct in saying that the spec recommends deferring work to &lt;code&gt;connectedCallback&lt;/code&gt;, it fails to clarify that this &quot;work&quot; does not include configuring event listeners –– something I had always assumed should be included.&lt;/p&gt;
&lt;p&gt;According to the spec, we should be writing our web components like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyComponent extends HTMLElement {
	constructor() {
		super();
		// Add event listeners when component is created
		this.addEventListener(&apos;click&apos;, this.clickHandler);
		this.addEventListener(&apos;mouseover&apos;, this.mouseoverHandler);
	}
	clickHandler = () =&amp;gt; {
		// Handles click event
	};
	mouseoverHandler = () =&amp;gt; {
		// Handles mouseover event
	};
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have to admit, this actually makes sense! Event listener code &lt;em&gt;is&lt;/em&gt; ostensibly something that should only be configured once when the component is first created. There&apos;s really no need to reconfigure this anytime the component is removed and re-added to the DOM (such as when persisting a web component across a page transition using the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API&quot;&gt;View Transitions API&lt;/a&gt;) –– it&apos;s unnecessary work.&lt;/p&gt;
&lt;p&gt;Also for those worried about potential bloated memory issues, you probably* don&apos;t need to be. Since this is configured in the &lt;code&gt;constructor&lt;/code&gt; method it follows regular JavaScript class semantics –– any state, default values, event listeners, shadow dom, etc. will be cleaned up when the class/component is no longer used.&lt;/p&gt;
&lt;p&gt;*&lt;em&gt;&lt;strong&gt;One big exception:&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you&apos;ve configured any listeners (or any state for that matter) that live &lt;strong&gt;outside&lt;/strong&gt; of your component (such as on the global &lt;code&gt;window&lt;/code&gt; object) you &lt;strong&gt;will&lt;/strong&gt; need to manually add and remove references to those listeners, states, etc. when the component is added/removed from the DOM. These will not be automatically cleaned up when the component is no longer used.&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyComponent extends HTMLElement {
	constructor() {
		super();
	}
	connectedCallback() {
		// Add event listener when component is added to the DOM
		window.addEventListener(&apos;resize&apos;, this.handler);
	}
	disconnectedCallback() {
		// Remove event listener when component is removed from the DOM
		window.removeEventListener(&apos;resize&apos;, this.handler);
	}
	handler = () =&amp;gt; {
		// Handles window resize event
	};
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;My new rule of thumb&lt;/h2&gt;
&lt;p&gt;This brings me to my new rule of thumb when building web components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code that only needs to run once should be put in the &lt;code&gt;constructor&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Code that needs to be run every time the component is added/removed from the DOM should be put in &lt;code&gt;connectedCallback&lt;/code&gt; and &lt;code&gt;disconnectedCallback&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&apos;ve got a lot of web component code to go back and update... 😅&lt;/p&gt;
&lt;p&gt;How about you?&lt;/p&gt;
</content:encoded></item><item><title>A year working with HTML Web Components</title><link>https://hawkticehurst.com/2023/11/a-year-working-with-html-web-components/</link><guid isPermaLink="true">https://hawkticehurst.com/2023/11/a-year-working-with-html-web-components/</guid><description>A deeply refreshing and encouraging way of building web applications.</description><pubDate>Mon, 27 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;While the term &quot;HTML Web Components&quot; &lt;a href=&quot;https://hawkticehurst.com/2023/11/html-web-components-is-the-wrong-name&quot;&gt;is fresh out of the oven&lt;/a&gt;, the architecture it refers to has been sitting on the counter at room temperature for a while now. I’ve been experimenting with this way of building web components for almost a year, and in this post, I want to talk about some of the big takeaways I&apos;ve come to thus far.&lt;/p&gt;
&lt;h2&gt;“Regular” web components&lt;/h2&gt;
&lt;p&gt;As an increasing number of people already know, “web components” is a loose term that refers to a set of &lt;a href=&quot;https://eisenbergeffect.medium.com/2023-state-of-web-components-c8feb21d4f16&quot;&gt;evolving browser APIs&lt;/a&gt; that allow you to create custom HTML elements. You can think of web components as a browser-native way of building reusable UI.&lt;/p&gt;
&lt;p&gt;There are three primary APIs that are commonly used together to build “web components”:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Custom Elements:&lt;/strong&gt; A JavaScript API for creating custom HTML elements&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shadow DOM:&lt;/strong&gt; A JavaScript API that is primarily used for creating scoped styles and component slots (i.e. rendering children within a component template)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTML Templates:&lt;/strong&gt; An HTML element (and associated JavaScript API) that typically acts as a way to define the template markup of a web component&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the first decade-ish of their inception and usage, web components have been predominantly marketed, documented, and used in a way that mirrors single-page application (SPA) component models, popularized by frameworks like React, Svelte, and Vue.&lt;/p&gt;
&lt;p&gt;Web component APIs, just like SPA component APIs, use JavaScript to encapsulate bits of markup, style, and behavior to represent and dynamically render distinct pieces of UI at runtime. In the same way you can build a React component and access it via a custom JSX tag (i.e. &lt;code&gt;&amp;lt;CounterButton /&amp;gt;&lt;/code&gt;) you can do the same with web component APIs and access it via a custom HTML tag (i.e. &lt;code&gt;&amp;lt;counter-button&amp;gt;&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;This model has worked quite well, but it is not without its paper cuts. You might have heard about a lot of them at this point, but to call out a few big ones, “regular” web components…&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Are unable to be server-side rendered (SSR)*&lt;/li&gt;
&lt;li&gt;Have inflexible styling rules when using Shadow DOM&lt;/li&gt;
&lt;li&gt;Currently have incomplete support for full accessibility and ARIA semantics when using Shadow DOM&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;*&lt;em&gt;The &lt;a href=&quot;https://developer.chrome.com/articles/declarative-shadow-dom/&quot;&gt;Declarative Shadow DOM API&lt;/a&gt; will change this but it does not yet have full browser support at the time of writing.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For me (and others), these paper cuts added up. During the last year, there has been an increasing level of experimentation with a different method of building web components.&lt;/p&gt;
&lt;h2&gt;HTML web components&lt;/h2&gt;
&lt;p&gt;In 2022, projects like Eleventy’s &lt;a href=&quot;https://github.com/11ty/webc&quot;&gt;WebC&lt;/a&gt; and &lt;a href=&quot;https://enhance.dev/&quot;&gt;Enhance&lt;/a&gt; started pioneering a web component architecture that focused exclusively on using the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements&quot;&gt;custom elements API&lt;/a&gt;, and nothing else from the &quot;web component&quot; bucket of APIs. Fast-forward a year, this method of building web components has been pushed forward and finally has a name.&lt;/p&gt;
&lt;p&gt;The core premise of HTML web components is to write HTML as you would normally and then “enhance” it by wrapping the parts you want to be interactive using a custom element tag.&lt;/p&gt;
&lt;p&gt;For example, if you wanted to define the markup for a counter button it would look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- HTML web component --&amp;gt;
&amp;lt;counter-button&amp;gt;
	&amp;lt;button&amp;gt;Count is &amp;lt;span&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
&amp;lt;/counter-button&amp;gt;

&amp;lt;!-- Regular web component (for comparison) --&amp;gt;
&amp;lt;counter-button&amp;gt;&amp;lt;/counter-button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rather than dynamically generating the component UI at runtime using JavaScript like you would with regular web components, the HTML web component is parsed, rendered, and styled as normal HTML. That HTML will then be seamlessly hydrated once the JavaScript associated with the custom element tag is executed.&lt;/p&gt;
&lt;p&gt;It also addresses all the earlier mentioned paper cuts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Component markup is fully SSR-able&lt;/li&gt;
&lt;li&gt;Component markup can now be styled like regular HTML&lt;/li&gt;
&lt;li&gt;Common accessibility practices can be applied without issue&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With that light context setting complete, below are some insights, takeaways, and general musings from a year of working with this architecture.&lt;/p&gt;
&lt;h2&gt;HTML web components are islands&lt;/h2&gt;
&lt;p&gt;I often like to casually describe this way of building web components as “wrapping HTML with custom elements to &lt;em&gt;declaratively&lt;/em&gt; define interactive UI.” The custom element tag acts as a sort of box of interactivity –– any HTML you put inside of that box can be made interactive using the powerful lifecycle methods and functionality that the custom element API provides.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This description/mental model might sound familiar to some — it certainly was for me — these are islands!&lt;/p&gt;
&lt;p&gt;Realizing that HTML web components (and custom elements at large) can be thought of as &quot;islands&quot; as described in the &lt;a href=&quot;https://jasonformat.com/islands-architecture/&quot;&gt;islands architecture&lt;/a&gt;, really made things click for me early on. As the analogy goes, in a sea of static HTML, a custom element/HTML web component denotes an island of interactivity.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Fun fact: Custom elements are so good at being islands it’s &lt;a href=&quot;https://github.com/withastro/astro/blob/main/packages/astro/src/runtime/server/astro-island.ts&quot;&gt;how Astro implemented their islands architecture&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Progressive enhancement is a piece of cake&lt;/h2&gt;
&lt;p&gt;HTML web components are one of the best tools I’ve come across for adhering to the design philosophy of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement&quot;&gt;progressive enhancement&lt;/a&gt;. My favorite feature of custom elements, in particular, enables a method of styling that wonderfully supports the goal of adhering to progressive enhancement: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/:defined&quot;&gt;the &lt;code&gt;:defined&lt;/code&gt; pseudo-class&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With this pseudo-class, you can declare styles for when a custom element has not yet been hydrated. You&apos;ll commonly see this demonstrated by simply hiding a custom element until it has been defined using the &lt;code&gt;customElements.define()&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;counter-button:not(:defined) {
	display: none;
}
counter-button:defined {
	display: block;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But this can lead to layout shift issues if not carefully accounted for. Plus on top of that.. this is CSS! You can push it &lt;em&gt;so&lt;/em&gt; much further. Perhaps, we could add disabled styling to the button when it hasn&apos;t been hydrated (note: you should also probably add the &lt;code&gt;disabled&lt;/code&gt; attribute by default too and remove it once JS executes).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;counter-button:not(:defined) button {
	cursor: not-allowed;
	background: grey;
}
counter-button:defined button {
	cursor: pointer;
	background: white;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maybe you like fancy component skeletons with animated shimmers.. easy.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Some default button styles */
counter-button button {
	background-color: #dfdfdf;
	border: none;
	border-radius: 6px;
	padding: 8px 20px;
}
/* Skeleton/shimmer styles applied before JavaScript has loaded */
counter-button:not(:defined) button {
	color: rgba(0, 0, 0, 0); /* Hide button text */
	display: inline-block;
	-webkit-mask: linear-gradient(
			-70deg,
			rgba(0, 0, 0) 30%,
			rgba(0, 0, 0, 0.3),
			rgba(0, 0, 0) 70%
		) right/300% 100%;
	background-repeat: no-repeat;
	animation: shimmer 3s infinite;
}
@keyframes shimmer {
	100% {
		-webkit-mask-position: left;
	}
}
/* Button styles applied after JavaScript has loaded */
counter-button:defined button {
	color: #000000;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The options are only limited by what you can come up with using CSS. The point is that with &lt;code&gt;:defined&lt;/code&gt; you have a trivial browser-native way of declaring pre-JavaScript and post-JavaScript styles on your components. It’s an amazing tool for the progressive-enhancement-conscious developer.&lt;/p&gt;
&lt;h2&gt;HTML web components are not a silver bullet&lt;/h2&gt;
&lt;p&gt;Like all things, there are some notable trade-offs when using HTML web components compared to regular web components.&lt;/p&gt;
&lt;p&gt;For one, component markup must be copied/repeated anywhere you want to use the component. That is to say, you lose out on the reusable “component-ness” of a regular web component. This is not ergonomic, especially with component templates that have more complex markup.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- HTML web component markup... --&amp;gt;
&amp;lt;counter-button&amp;gt;
	&amp;lt;button&amp;gt;Count is &amp;lt;span&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
&amp;lt;/counter-button&amp;gt;

&amp;lt;!-- ...must be repeated... --&amp;gt;
&amp;lt;counter-button&amp;gt;
	&amp;lt;button&amp;gt;Count is &amp;lt;span&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
&amp;lt;/counter-button&amp;gt;

&amp;lt;!-- ...every time they are used... --&amp;gt;
&amp;lt;counter-button&amp;gt;
	&amp;lt;button&amp;gt;Count is &amp;lt;span&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
&amp;lt;/counter-button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Additionally, by forgoing shadow DOM, you lose out on native-scoped styling. You can still achieve a sort of pseudo-component-scoped styling by prepending the name of your custom element to all CSS styles, but this can get a bit cumbersome to repeat in my experience.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* This is kind of like scoped styling.. right? */
counter-button button {
	font: inherit;
	border: none;
	border-radius: 6px;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without shadow DOM you also have no ability to natively add component slots (i.e. render children within a component template).&lt;/p&gt;
&lt;p&gt;The good news is that if you’re willing to look outside of native browser APIs and to the land of web frameworks (which I’d wager most people are) these are solved problems!&lt;/p&gt;
&lt;p&gt;Projects like WebC and Enhance implemented their own version of component templating, scoped styling, and component slots. In my case, however, I&apos;ve landed on Astro as my preferred pairing with HTML web components.&lt;/p&gt;
&lt;h2&gt;Astro and HTML web components&lt;/h2&gt;
&lt;p&gt;I could spend an entire post gushing about Astro, but to give a brief intro for those less familiar: Astro is a web framework that started as a static site generator and has expanded to support the whole gamut of web architectures and use cases. In 2023, they have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Static, server, and hybrid rendering modes&lt;/li&gt;
&lt;li&gt;A simple templating syntax and component model (we’ll come back to this later)&lt;/li&gt;
&lt;li&gt;Scoped component styles&lt;/li&gt;
&lt;li&gt;An extensive plug-in ecosystem&lt;/li&gt;
&lt;li&gt;Built-in markdown and MDX support&lt;/li&gt;
&lt;li&gt;File system-based routing&lt;/li&gt;
&lt;li&gt;A built-in &lt;code&gt;&amp;lt;Image /&amp;gt;&lt;/code&gt; component&lt;/li&gt;
&lt;li&gt;Built-in internationalization features&lt;/li&gt;
&lt;li&gt;Custom API endpoint support&lt;/li&gt;
&lt;li&gt;Custom middleware support&lt;/li&gt;
&lt;li&gt;A built-in, lightweight, type-safe content management system (CMS)&lt;/li&gt;
&lt;li&gt;And on.. and on..&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On top of that, they also have, without a doubt, the best &lt;a href=&quot;https://docs.astro.build/en/getting-started/&quot;&gt;documentation&lt;/a&gt; on earth right now. &lt;em&gt;If you haven’t given Astro a try yet I can’t recommend it enough.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So where do HTML web components fit into this picture? They pair perfectly with Astro’s component model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Astro component model&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Astro components “are HTML-only templating components with no client-side runtime.” All they can do is send static HTML, CSS, and JavaScript (that will be executed at a global page level) to the browser.&lt;/p&gt;
&lt;p&gt;The anatomy of an Astro component is delightfully simple:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
// Component script (JavaScript)
// This will be executed at build time or on-demand (i.e. on the server)
const greeting = &apos;world&apos;;
---

&amp;lt;!-- Component template (HTML) --&amp;gt;
&amp;lt;p&amp;gt;Hello {greeting}&amp;lt;/p&amp;gt;

&amp;lt;style&amp;gt;
	/* Component styles (CSS) -- scoped by default */
	p {
		color: blue;
	}
&amp;lt;/style&amp;gt;

&amp;lt;script&amp;gt;
	// Client script (JavaScript)
	// This will be sent to and executed on the browser
	console.log(&apos;Hello from the browser&apos;);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With Astro components, we have all the makings to solve the issues with HTML web components.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A component model and templating syntax&lt;/li&gt;
&lt;li&gt;Scoped styling&lt;/li&gt;
&lt;li&gt;Even a &lt;a href=&quot;https://docs.astro.build/en/core-concepts/astro-components/#slots&quot;&gt;&lt;code&gt;&amp;lt;slot /&amp;gt;&lt;/code&gt; element&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Putting it all together, a simple hello world component that will render a greeting based on the value of a text field might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
// Components can start with an initial server rendered value
const initialName = &apos;world&apos;;
---

&amp;lt;!-- 
Astro template syntax will compile this into static HTML 
before sending it to the client
--&amp;gt;
&amp;lt;hello-world&amp;gt;
	&amp;lt;input type=&quot;text&quot; value={initialName} /&amp;gt;
	&amp;lt;p&amp;gt;Hello &amp;lt;span&amp;gt;{initialName}&amp;lt;/span&amp;gt;!&amp;lt;/p&amp;gt;
&amp;lt;/hello-world&amp;gt;

&amp;lt;script&amp;gt;
	// HTML web component code that will execute in the browser
	class HelloWorld extends HTMLElement {
		constructor() {
			super();
		}
		// Lifecycle method that runs when the custom element
		// is connected to the DOM
		connectedCallback() {
			this.input = this.querySelector(&apos;input&apos;);
			this.span = this.querySelector(&apos;span&apos;);
			this.input.addEventListener(&apos;input&apos;, this.updateName);
		}
		updateName = (e) =&amp;gt; {
			const val = e.target.value;
			this.span.textContent = val;
		};
	}
	customElements.define(&apos;hello-world&apos;, HelloWorld);
&amp;lt;/script&amp;gt;

&amp;lt;style&amp;gt;
	/* Styles will be automatically scoped to the component! */
	input {
		font: inherit;
		border: none;
		border-radius: 4px;
	}
	span {
		font-style: italic;
	}
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;HTML web components make Astro better too&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On the flip side, HTML web components solve my biggest issue with Astro components — the fact they don’t have a client-side runtime!&lt;/p&gt;
&lt;p&gt;Typically if you need interactive UI in Astro you either need to write vanilla JavaScript (fine for small scripts but can get unwieldy fast) or you can take advantage of the fact that Astro &lt;a href=&quot;https://docs.astro.build/en/core-concepts/framework-components/&quot;&gt;can render other UI framework components&lt;/a&gt; like React, Svelte, Vue, etc.&lt;/p&gt;
&lt;p&gt;Rendering other component models is a really cool feature but I don’t like that it requires that I need to juggle multiple different syntaxes, framework conventions, and so on while developing. I’d much prefer a model that lets me stay within the boundaries of Astro’s syntax and conventions. HTML web components enable this reality.&lt;/p&gt;
&lt;p&gt;To give credit where it’s due, Astro has actually (for a very long time) &lt;a href=&quot;https://docs.astro.build/en/guides/client-side-scripts/#web-components-with-custom-elements&quot;&gt;documented this method of combining Astro components with custom elements&lt;/a&gt;. These docs are another place where I initially found inspiration to try out the concept of HTML web components.&lt;/p&gt;
&lt;p&gt;Finally, a really exciting premise I’ll leave you with is that when all of this is further combined with the new &lt;a href=&quot;https://docs.astro.build/en/guides/view-transitions/&quot;&gt;View Transitions API&lt;/a&gt; you have the makings for Astro to become a compelling SPA framework as well. There’s so much more to talk about here, but that will need to be left for a future post/video.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A “proper” example&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One of my first “proper” HTML web components used Astro to build an audio player to build &lt;a href=&quot;https://timticehurst.com/&quot;&gt;a website to host my late father’s music&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Like the examples above, this &lt;a href=&quot;https://github.com/hawkticehurst/timticehurst/blob/main/src/components/AudioPlayer.astro&quot;&gt;AudioPlayer component&lt;/a&gt; (along with a supporting &lt;a href=&quot;https://github.com/hawkticehurst/timticehurst/blob/main/src/components/SongList.astro&quot;&gt;SongList component&lt;/a&gt;) is a custom element that wraps around an &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; element and some other bits of HTML to build out the rest of the UI. This is then enhanced by a fair bit of JavaScript and CSS.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;And here’s the thing, without getting into the details further, &lt;em&gt;it just worked&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Of course, there were the usual development hiccups (like how the heck does the HTML audio element API work?!), but as a method of building reusable interactive UI, Astro and HTML web components have come to be my favorite during the last year. In my experience, it’s effectively no different than any of the other major component models.&lt;/p&gt;
&lt;p&gt;With one big exception…&lt;/p&gt;
&lt;h2&gt;The ergonomics could still be improved&lt;/h2&gt;
&lt;p&gt;After a handful of months building with Astro and HTML web components, I continued to discover more paper cuts, specifically regarding the verbosity of the custom elements API.&lt;/p&gt;
&lt;p&gt;To be clear I don’t fault the custom elements API for being low-level and verbose, it needs to be. But as a day-to-day API for developing UI, it’s not my favorite. I went looking for a framework that could provide the terseness and functionality of something like &lt;a href=&quot;https://lit.dev/&quot;&gt;Lit&lt;/a&gt;, but that catered to an HTML web components architecture.&lt;/p&gt;
&lt;p&gt;Unfortunately, WebC and Enhance don’t fit the bill because they don’t integrate well/at all with Astro components and I never found anything else that checked the specific boxes I was looking for, so a few months ago I decided to build my own.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Say hello to Stellar&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/hawkticehurst/stellar&quot;&gt;Stellar&lt;/a&gt; is a tiny framework that improves the ergonomics of building HTML web components.&lt;/p&gt;
&lt;p&gt;This is achieved by providing a handful of custom attributes called “directives” that implement event handling and a reactivity model. With Stellar, building a working counter button looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;counter-button&amp;gt;
	&amp;lt;button @click=&quot;increment&quot;&amp;gt;Count is &amp;lt;span $state=&quot;count&quot;&amp;gt;0&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
&amp;lt;/counter-button&amp;gt;

&amp;lt;script type=&quot;module&quot;&amp;gt;
	import { Stellar } from &apos;stellar-element&apos;;
	class CounterButton extends Stellar {
		increment = () =&amp;gt; this.count++;
	}
	customElements.define(&apos;counter-button&apos;, CounterButton);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There’s much more to &lt;a href=&quot;https://github.com/hawkticehurst/stellar/blob/main/README.md#docs&quot;&gt;read in the docs&lt;/a&gt;, but I do want to quickly address a question (and associated cool feature of Stellar) that some may have after a close look at the above syntax: how/where is &lt;code&gt;count&lt;/code&gt; initialized?&lt;/p&gt;
&lt;p&gt;To answer that I have to introduce a concept I’ve come to call “HTML-based state.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HTML-based state&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Stellar, like many other web frameworks, ships a reactivity model that lets you declaratively define pieces of stateful UI.&lt;/p&gt;
&lt;p&gt;Unlike other web frameworks, however, state is defined a little differently than what you might have experienced when using tools like React, Svelte, or Vue. In most frameworks, state is defined in JavaScript and then passed into component markup using some type of templating syntax. &lt;em&gt;In Stellar, this relationship is inversed.&lt;/em&gt; Reactive state is defined in HTML and then made accessible for manipulation in JavaScript.&lt;/p&gt;
&lt;p&gt;In practice, this means a handful of custom attributes called &quot;directives&quot; that begin with a &lt;code&gt;$&lt;/code&gt; can be declaratively added to elements within a Stellar component to access the reactivity model.&lt;/p&gt;
&lt;p&gt;For example, the &lt;code&gt;$state&lt;/code&gt; directive allows you to define a piece of reactive state based on the existing text content of a stateful element.&lt;/p&gt;
&lt;p&gt;When trying to understand a reactive state declaration, the following code...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;p $state=&quot;text&quot;&amp;gt;Hello world!&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;...should be interpreted in the following way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The paragraph element is marked as stateful via the &lt;code&gt;$state&lt;/code&gt; directive&lt;/li&gt;
&lt;li&gt;The name of the state is &lt;code&gt;text&lt;/code&gt; and can be accessed in JavaScript by calling &lt;code&gt;this.text&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The initial value of the state is the string &quot;Hello world!&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: As a convenience, all reactive state is coerced to the correct data type when being accessed in JavaScript. For example, when accessing the &lt;code&gt;count&lt;/code&gt; state from above it will be of type &lt;code&gt;number&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;State should start on the server&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;An intentional goal of this model is to be extremely SSR-friendly and align (mostly) well with &lt;a href=&quot;https://hypermedia.systems/&quot;&gt;hypermedia-driven principles&lt;/a&gt;. While the above examples demonstrate handwriting state directly inside HTML elements, the real use case and goal is to declare initial state in your server and pass the state into Stellar components using the templating language of your server framework.&lt;/p&gt;
&lt;p&gt;When used in a framework like Astro, initial component state can start in the server, be encoded directly into your HTML, and then be seamlessly hydrated on the client without any layout shift issues or flashes of new content once JavaScript is parsed and executed.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
// State starts in the server!
const initial = &apos;Hello world!&apos;;
const count = 0;
---

&amp;lt;hydrate-server-state&amp;gt;
	&amp;lt;p $state=&quot;text&quot;&amp;gt;{initial}&amp;lt;/p&amp;gt;
	&amp;lt;button @click=&quot;increment&quot;&amp;gt;
		Count is: &amp;lt;span $state=&quot;count&quot;&amp;gt;{count}&amp;lt;/span&amp;gt;
	&amp;lt;/button&amp;gt;
&amp;lt;/hydrate-server-state&amp;gt;

&amp;lt;script&amp;gt;
	import { Stellar } from &apos;stellar-element&apos;;
	class HydrateServerState extends Stellar {
		constructor() {
			super();
			console.log(this.text); // Logs &quot;Hello world!&quot;
			this.text = &apos;Hey there!&apos;; // Update paragraph element with text &quot;Hey there!&quot;
			console.log(this.text); // Logs &quot;Hey there!&quot;
		}
		// Increment count state and update span element with new count
		increment = () =&amp;gt; this.count++;
	}
	customElements.define(&apos;hydrate-server-state&apos;, HydrateServerState);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;The usual disclaimers&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Stellar is still under active development and has mainly been a project built for me and my preferences, so all the usual disclaimers apply: Something something incomplete. Something something here be dragons.&lt;/p&gt;
&lt;p&gt;If you’re still interested in giving it a try, however, please let me know how it goes! You can file an &lt;a href=&quot;https://github.com/hawkticehurst/stellar/issues&quot;&gt;issue&lt;/a&gt; or &lt;a href=&quot;https://elk.zone/fosstodon.org/@hawkticehurst&quot;&gt;send me a toot&lt;/a&gt; with your feedback.&lt;/p&gt;
&lt;h2&gt;The tip of the iceberg&lt;/h2&gt;
&lt;p&gt;Using HTML web components has been a deeply refreshing and encouraging method of building interactive UI that I strongly believe has the potential to be a serious trend in how we build the web going forward.&lt;/p&gt;
&lt;p&gt;While this post has been a collection of my thoughts and takeaways loosely tied together, I’ve been working on an even bigger video (I was a filmmaker for several years before I found tech) about the topic where I dive further into using Astro, HTML web components, and HTMX to build delightfully simple, yet surprisingly robust, web applications.&lt;/p&gt;
&lt;p&gt;If that sounds exciting and/or interesting, subscribe to my &lt;a href=&quot;https://www.youtube.com/@hawkticehurst&quot;&gt;YouTube&lt;/a&gt; and &lt;a href=&quot;https://www.tiktok.com/@hawkticehurst&quot;&gt;TikTok&lt;/a&gt; to be the first to see it when it’s released.&lt;/p&gt;
&lt;p&gt;Until next time 👋🏼&lt;/p&gt;
</content:encoded></item><item><title>HTML Web Components is the wrong name</title><link>https://hawkticehurst.com/2023/11/html-web-components-is-the-wrong-name/</link><guid isPermaLink="true">https://hawkticehurst.com/2023/11/html-web-components-is-the-wrong-name/</guid><description>My thoughts on how we should name the concept that has been recently described as HTML Web Components.</description><pubDate>Tue, 21 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’m psyched. My favorite way of building web components is starting to come into vogue and with all the &lt;a href=&quot;https://adactio.com/journal/20618&quot;&gt;recent&lt;/a&gt; &lt;a href=&quot;https://blog.jim-nielsen.com/2023/html-web-components/&quot;&gt;discussion&lt;/a&gt; &lt;a href=&quot;https://blog.jim-nielsen.com/2023/html-web-components-an-example/&quot;&gt;about&lt;/a&gt; &lt;a href=&quot;https://gomakethings.com/html-web-components/&quot;&gt;this&lt;/a&gt; &lt;a href=&quot;https://www.oddbird.net/2023/11/17/components/&quot;&gt;concept&lt;/a&gt;, I can’t help but toss my hat and thoughts into the ring.&lt;/p&gt;
&lt;p&gt;I&apos;m currently writing a much longer post about my time experimenting with, building, and using these components during the last year (&lt;em&gt;plus I may or may not be soft-launching a tiny web framework that I may or may not have been working on for several months specifically focused on building this type of web component... who knows&lt;/em&gt;), but with things moving fast I wanted to at least chime in with my thoughts about the recently proposed and initially adopted name “HTML Web Components.”&lt;/p&gt;
&lt;p&gt;This is an extremely new term to describe this concept –– about a week old as of writing these words. So while the dust hasn’t fully settled I want to add my two cents and implore those building and writing about this that we consider finding a different name.&lt;/p&gt;
&lt;p&gt;My big issue can be boiled down to the idea that &quot;web components&quot; is an extremely overloaded term that I would argue has a principle definition that can roughly be described as &quot;a set of &lt;a href=&quot;https://eisenbergeffect.medium.com/2023-state-of-web-components-c8feb21d4f16&quot;&gt;evolving browser APIs&lt;/a&gt; that allow you to create custom HTML elements.&quot; That is to say, &quot;web components&quot; are themselves &lt;em&gt;literally&lt;/em&gt; HTML elements, so when I see the term “HTML Web Components” I can’t help but read it as “HTML HTML.”&lt;/p&gt;
&lt;p&gt;For me, this name is confusing and does not do a good enough job of properly or intuitively distinguishing how this model is different from “regular” web components.&lt;/p&gt;
&lt;p&gt;I should also acknowledge and clearly state my bias when thinking about this name. I&apos;ve spent years teaching students and coworkers about various programming concepts (especially web components as of the last couple of years), so I often lean towards the perspective of a student or new learner.&lt;/p&gt;
&lt;p&gt;In some ways, you might consider this post an argument in support of choosing a name that will help new learners ease into the world of web components –– which is already filled with so many potential potholes and points of confusion (no shade, but looking at you Shadow DOM).&lt;/p&gt;
&lt;p&gt;I started &lt;a href=&quot;https://elk.zone/fosstodon.org/@hawkticehurst/111444143563712869&quot;&gt;a thread&lt;/a&gt; that already has some discussion on the topic with some very valid counterpoints raised, but I still think this name is worth reviewing. Below are a few of those counterpoints and my thoughts on them.&lt;/p&gt;
&lt;h2&gt;It would be great to re-use the &quot;Web Component&quot; branding&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://elk.zone/fosstodon.org/@zachleat@zachleat.com/111444254985755015&quot;&gt;Reference&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I actually agree and think this is one of the more compelling reasons to stick with &quot;HTML Web Components!&quot; Brand recognition is powerful and well worth considering.&lt;/p&gt;
&lt;p&gt;With that said, &quot;web components,&quot; I&apos;d argue, is one of the most fraught and convoluted web development brands and concepts of the last decade. I worry that explicitly rolling up to this branding will just add further confusion –– especially for those starting to learn about web components.&lt;/p&gt;
&lt;p&gt;A different name that does not include the words &quot;web component&quot; could still very easily fall under the &quot;web component&quot; banner/branding, but it will not contribute to an overloaded phrase that already means a lot of different things to a lot of people.&lt;/p&gt;
&lt;p&gt;In the best case, this is an opportunity to start fresh, so to speak. I could see an alternative name (if done well) pulling the interest/curiosity of those who have previously held strong negative opinions about &quot;web components&quot; by simply avoiding those words up front and center. Maybe?? 🤷🏻‍♂️&lt;/p&gt;
&lt;h2&gt;&quot;HTML Web Components&quot; focuses on the &quot;HTML&quot; part of web components&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://elk.zone/fosstodon.org/@jimniels@mastodon.social/111444459375161214&quot;&gt;Reference&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Jim Nielsen has written a few posts at this point where he differentiates between the two types of web components by calling them &quot;HTML Web Components&quot; and &quot;JavaScript Web Components.&quot; In his words:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I like the term “HTML web components” because it makes me think of the HTML-centered part of [the web component] “suite of technologies”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Like the branding counterpoint, I also really like this idea –– it makes &lt;em&gt;a lot&lt;/em&gt; of sense to me to roughly categorize &quot;web components&quot; into an HTML-focused bucket and a JS-focused bucket.&lt;/p&gt;
&lt;p&gt;With that said, what does &quot;HTML-centered&quot; mean? Especially for someone who might know less about web components? When you start getting into the weeds of web components you&apos;ll find that &quot;HTML Web Components&quot; and &quot;JavaScript Web Components&quot; require almost nearly the same amount of JavaScript and HTML –– i.e. the Custom Elements API accounts for the vast majority of weight in a web component.&lt;/p&gt;
&lt;p&gt;Of course, as one learns more they will eventually grasp the idea that this naming convention reflects/indicates a web component &lt;em&gt;architecture&lt;/em&gt; versus a web component &lt;em&gt;implementation strategy&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;And I suppose there lies a key question that needs answering: Is it more desirable to have the name indicate an architectural differentiation or have the name better reflect a sort of blueprint of how this type of component is implemented?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;*Editor&apos;s note: As I&apos;m doing a final pass I&apos;m starting to wonder if this is a false dichotomy because an architecture can/does in some way indicate an implementation strategy...&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Hopefully, the point I&apos;m trying to make is coming across, but if not the core idea is that I worry people will not initially understand what &quot;HTML&quot; is supposed to mean in &quot;HTML Web Component&quot; and get wires crossed especially when considering that a non-zero group of people defines &quot;web components&quot; as a way of creating custom HTML elements.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Said a different way, what does an &quot;HTML custom HTML element&quot; or a &quot;JavaScript custom HTML element&quot; entail? I&apos;d argue the naming does not make this clear and I really wish it did a better job of that.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;This simply might not matter in the scheme of things&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&quot;https://elk.zone/fosstodon.org/@cferdinandi@mastodon.social/111444410348836586&quot;&gt;Reference&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Chris Ferdinandi pointed out that this all might not matter in the big picture, noting that some of the alternative names that have been suggested might stray a bit too far into buzzwordy territory. I think he correctly points out that some of these names could &quot;[turn] into a &apos;yesteryear buzzword&apos; the way people sometimes treat progressive enhancement today.&quot;&lt;/p&gt;
&lt;p&gt;I&apos;ve not named nearly enough things to have a strong opinion or experience on this one. I could see it going both ways, but I will admit that &quot;HTML&quot; and &quot;Web Components&quot; lean pretty heavily toward the &quot;timeless&quot; (versus &quot;buzzwordy&quot;) end of the scale. So barring an even better alternative name, this is likely a +1 towards &quot;HTML Web Components.&quot;&lt;/p&gt;
&lt;h2&gt;So.. what &lt;em&gt;should&lt;/em&gt; we name this?&lt;/h2&gt;
&lt;p&gt;Personally, my favorite suggestion has been &lt;a href=&quot;https://elk.zone/fosstodon.org/@McNeely@indieweb.social/111372617963939777&quot;&gt;Ryan McNeely’s “enhanced components” or “progressive tags”&lt;/a&gt; but even those don’t feel &lt;em&gt;quite&lt;/em&gt; right to me. To build on McNeely, I’d perhaps suggest “progressive web components” or even better “progressive custom elements.” They both indicate that progressive enhancement is a core principle of working with this model and the latter name makes clear this is a method of building interactive UI using only custom elements and nothing else from the &quot;web component&quot; bucket of APIs.&lt;/p&gt;
&lt;p&gt;Zach Leatherman also (perhaps unintentionally) &lt;a href=&quot;https://elk.zone/fosstodon.org/@zachleat@zachleat.com/111444189179163568&quot;&gt;suggested &quot;HTML Custom Elements&quot;&lt;/a&gt;, which I like as well. It still kind of reads as &quot;HTML HTML&quot; to me, but at least it avoids using &quot;web component.&quot;&lt;/p&gt;
&lt;p&gt;I’m not fully sold on any of these names to be clear. I think they do a better job of correctly conveying what we’re talking about, but I question if they are any more intuitive than &quot;HTML Web Components&quot; for someone who&apos;s learning about web components for the first time.&lt;/p&gt;
&lt;p&gt;The big call to action is one final round of name suggestions that keep in mind the historical baggage of the term &quot;web components&quot; and are considerate of the new learners that will enter the web component world in the years to come.&lt;/p&gt;
&lt;h2&gt;The train might have left the station already&lt;/h2&gt;
&lt;p&gt;Admittedly the train is already mostly out of the station with the fantastic posts I&apos;ve linked to all using “HTML Web Components” front and center, but if there’s any willingness to reconsider I’d love to discuss and see if this naming can be a more accurate (and ideally intuitive) reflection of the concept.&lt;/p&gt;
&lt;p&gt;With all that said, this is a strong opinion loosely held on my part, and without any great alternative name(s) I do think &quot;HTML Web Components&quot; checks enough boxes as is. So if the consensus and desire (from those who have already written about this and the wider community) are to move forward with &quot;HTML Web Components&quot; I will happily (for the most part) roll with it.&lt;/p&gt;
&lt;p&gt;I at least wanted to convey these thoughts before it was too late. I invite you to add to the discussion in the form of response blog posts or adding to &lt;a href=&quot;https://elk.zone/fosstodon.org/@hawkticehurst/111444143563712869&quot;&gt;this thread&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As always, naming things is the hardest part of programming…&lt;/p&gt;
</content:encoded></item><item><title>Let me dictate my own realm</title><link>https://hawkticehurst.com/2023/05/let-me-dictate-my-own-realm/</link><guid isPermaLink="true">https://hawkticehurst.com/2023/05/let-me-dictate-my-own-realm/</guid><description>A hope for the next era of social media.</description><pubDate>Tue, 02 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;At the end of last year, Robin Sloan published &lt;a href=&quot;https://www.robinsloan.com/lab/new-avenues/&quot;&gt;a newsletter&lt;/a&gt; declaring &quot;the platforms of the last decade are done&quot; and made a call for new thinking, projects, hopes, and dreams about what the next era of platforms and social media may look like.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Below is one of my hopes.&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I&apos;m old enough to just barely remember a time before the internet became a horizon-swallowing monolith.&lt;/p&gt;
&lt;p&gt;By the time I was ready to surf the web, Facebook poke wars, FarmVille, and Truth Is posts were my generation&apos;s starter pack for communicating on the web. It means, by and large, I have never known an internet that isn&apos;t trying to extract every bit of attention and value it can from me.&lt;/p&gt;
&lt;p&gt;It also means I missed many of the early days of internet culture and communication. So, after leaving Twitter last fall, I gave RSS feeds a try for the first time in my life.&lt;/p&gt;
&lt;p&gt;It has been a striking experience.&lt;/p&gt;
&lt;p&gt;During the last decade, I have been conditioned to constantly think about &quot;curating&quot; my algorithm across various platforms. What I like, dislike, comment on, share, save, watch (and for how long) are, at this point, well-worn tools in my algorithmic toolbelt for crafting and steering the content that gets served on each of my feeds.&lt;/p&gt;
&lt;p&gt;With RSS, however, there is none of this –– just a dead simple subscribed or not subscribed. What I read (or don&apos;t read) has no consequence on seeing future content from an author, and I will never be served content from a feed I am not subscribed to.&lt;/p&gt;
&lt;p&gt;Curating an algorithm can produce delightful results when done right, but it can also be exhausting to engage in –– especially when trying to avoid the death spirals of doom scrolling, FOMO/isolation, division, and on and on... I know this. You know this. We all know this.&lt;/p&gt;
&lt;p&gt;What has been shocking to me, however, is how insidious this habit/proclivity towards curation can be.&lt;/p&gt;
&lt;p&gt;While using RSS, I have found myself regularly getting anxious that if I don&apos;t finish reading a post in one sitting it will disappear into the digital abyss, or if I skip out on a post that doesn&apos;t particularly interest me I&apos;ll start seeing less from that author. I have to regularly remind myself that isn&apos;t how RSS works.&lt;/p&gt;
&lt;p&gt;I&apos;ve always known on some level that content algorithms can play a role in how I act and feel, but it has been striking to see, with such clarity, &lt;em&gt;how much&lt;/em&gt; of my thoughts and behavior have conformed to how algorithms of the last decade work.&lt;/p&gt;
&lt;p&gt;On the topic of algorithms, Robin Sloan says in his newsletter:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I suspect the best answers are grounded in good old-fashioned human recommendations, but who knows? Maybe TikTok has it right; maybe everybody deserves one (1) audition with the capricious god-algorithm of the realm. Maybe there’s something to be isolated and improved there.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think I land somewhere in the middle.&lt;/p&gt;
&lt;p&gt;My hope is a future internet where at any time and for any reason I can let an AI delight me with something new or take a breather and tune into the simplicity/reliability of an RSS-like algorithm.&lt;/p&gt;
&lt;p&gt;My hope is that the platforms of the next era will let me choose the algorithms that dictate my realm.&lt;/p&gt;
</content:encoded></item><item><title>An SSR framework in 37 lines of code</title><link>https://hawkticehurst.com/2022/07/an-ssr-framework-in-37-lines-of-code/</link><guid isPermaLink="true">https://hawkticehurst.com/2022/07/an-ssr-framework-in-37-lines-of-code/</guid><description>By building on top of modern web standards and conventions one can achieve a shockingly rich SSR framework with very little code.</description><pubDate>Tue, 12 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;During the last year, I&apos;ve become completely enthralled by the worlds of both frontend web framework development and modern web standards/conventions (such as web components, unbundled development, and so on).&lt;/p&gt;
&lt;p&gt;With a goal of trying to combine these two worlds I&apos;ve been capturing my research, experimentation, and takeaways in the form of &lt;a href=&quot;https://delgada.pages.dev/&quot;&gt;Delgada&lt;/a&gt; –– a web framework for building slim multi-page websites.&lt;/p&gt;
&lt;p&gt;While Delgada is still under very active development, I recently took a step back to synthesize my learning, which resulted in a surprisingly feature-rich server-side rendering (SSR) framework, &lt;em&gt;implemented in just 37 lines of code&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Features of this SSR framework include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ship zero JavaScript by default&lt;/li&gt;
&lt;li&gt;Expressive markup and styling using tagged templates&lt;/li&gt;
&lt;li&gt;Island-based architecture via web components&lt;/li&gt;
&lt;li&gt;No build step&lt;/li&gt;
&lt;li&gt;Zero configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The point of this is not to provide a feature-complete implementation of SSR by 2022 standards, but to show that one can get shockingly far with very little code by building on top of the web standards and conventions available today. Think of it as a celebration of the modern web and what it enables.&lt;/p&gt;
&lt;p&gt;In the rest of this post, I&apos;ll talk about the features, conventions, and syntax of this framework and discuss how it&apos;s enabled.&lt;/p&gt;
&lt;p&gt;Finally, the source code (along with a demo) for the SSR implementation can be found in this &lt;a href=&quot;https://github.com/hawkticehurst/slim-ssr&quot;&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;House-keeping complete. Let&apos;s dive in!&lt;/p&gt;
&lt;h2&gt;Project structure&lt;/h2&gt;
&lt;p&gt;We&apos;ll start with a birds-eye-view of a basic demo project structure to get oriented with the conventions of using this framework.&lt;/p&gt;
&lt;p&gt;The goal of this structure is to emulate modern web conventions and should hopefully feel straightforward and familiar to most reading this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;my-website/
├── public/
│   ├── favicon.png
│   └── global.css
├── src/
│   ├── components/
│   │   ├── SomeComponent.js
│   │   └── AnotherComponent.js
│   └── pages/
│       ├── About.js
│       └── Index.js
├── package.json
└── server.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the root of the project are the server and package.json files. A &lt;code&gt;public&lt;/code&gt; directory contains all the static assets and a &lt;code&gt;src&lt;/code&gt; directory contains the components that will be rendered server-side.&lt;/p&gt;
&lt;h2&gt;The server&lt;/h2&gt;
&lt;p&gt;Below is an example &lt;code&gt;server.js&lt;/code&gt; file. It contains, amongst other things, the primary API of the SSR implementation (which I&apos;ll just call &lt;code&gt;slim-ssr&lt;/code&gt; going forward).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { register } from &apos;slim-ssr&apos;;
import { Index } from &apos;./src/pages/Index.js&apos;;
import { About } from &apos;./src/pages/About.js&apos;;
import express from &apos;express&apos;;

const routes = [
	{ path: &apos;/&apos;, component: Index },
	{ path: &apos;/about&apos;, component: About },
];

const islands = [&apos;WebComponent.js&apos;];

const app = express();
register(app, routes, { islands });

const port = 3000;
app.listen(port, () =&amp;gt; {
	console.log(`Listening on http://localhost:${port}`);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A function called &lt;code&gt;register&lt;/code&gt; is the first introduction to &lt;code&gt;slim-ssr&lt;/code&gt;. The register function is responsible for setting up and handling the routing/file serving of a &lt;code&gt;slim-ssr&lt;/code&gt; website.&lt;/p&gt;
&lt;p&gt;Another thing you might notice is that &lt;a href=&quot;https://expressjs.com/&quot;&gt;Express&lt;/a&gt; is being used as the underlying server. For &lt;code&gt;slim-ssr&lt;/code&gt;, this keeps things simple and gives a solid groundwork to build on top of, but it could be easily switched out for another server or server framework.&lt;/p&gt;
&lt;h2&gt;Routing&lt;/h2&gt;
&lt;p&gt;Routes are defined as a simple array of objects with a &lt;code&gt;path&lt;/code&gt; and &lt;code&gt;component&lt;/code&gt; property.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const routes = [
	{ path: &apos;/&apos;, component: Index },
	{ path: &apos;/about&apos;, component: About },
];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside &lt;code&gt;slim-ssr&lt;/code&gt;, routing is handled by these 6 lines of code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for (const route of routes) {
	app.get(route.path, (req, res) =&amp;gt; {
		res.set(&apos;Content-Type&apos;, &apos;text/html&apos;);
		res.send(Buffer.from(route.component(req)));
	});
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It takes the &lt;code&gt;routes&lt;/code&gt; array discussed above, iterates over every route object, and serves the HTML returned by the &lt;code&gt;component&lt;/code&gt; function at the endpoint defined in &lt;code&gt;path&lt;/code&gt;. Also notice that the component function is passed the client request (i.e. &lt;code&gt;component(req)&lt;/code&gt;) –– we&apos;ll come back to this later.&lt;/p&gt;
&lt;h2&gt;Serving web components/islands&lt;/h2&gt;
&lt;p&gt;Web components/islands (which will also be discussed later in more depth) are registered as an array of strings, where each string is the name of a web component file in the &lt;code&gt;src/components/&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;Each file will be served to the client at the root URL (&lt;code&gt;/&lt;/code&gt;) of a &lt;code&gt;slim-ssr&lt;/code&gt; website. So if there&apos;s a web component called &lt;code&gt;WebComponent.js&lt;/code&gt;, it will be served at &lt;code&gt;/WebComponent.js&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const islands = [&apos;WebComponent.js&apos;, &apos;AnotherWebComponent.js&apos;];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In &lt;code&gt;slim-ssr&lt;/code&gt;, the following code enables this behavior.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (options.islands) {
	for (const island of options.islands) {
		app.get(`/${island}`, (_, res) =&amp;gt; {
			res.set(&apos;Content-Type&apos;, &apos;application/javascript&apos;);
			res.sendFile(island, {
				root: path.join(process.cwd(), &apos;src&apos;, &apos;components&apos;),
			});
		});
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It first checks that an &lt;code&gt;islands&lt;/code&gt; array has been provided (since it&apos;s an optional config). For every file name provided, an absolute path to each web component file is constructed (i.e. the current working directory + &lt;code&gt;/src/components/WebComponent.js&lt;/code&gt;) and then served at the root URL.&lt;/p&gt;
&lt;h2&gt;Static file serving&lt;/h2&gt;
&lt;p&gt;Similar to Next.js, (and just like the web component file serving above) all files in the &lt;code&gt;public&lt;/code&gt; directory are also served to the client at the root URL via the code below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app.use(express.static(`${process.cwd()}/public`));
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Template syntax&lt;/h2&gt;
&lt;p&gt;Before discussing components, we need to cover the template syntax of this framework, which will be used to define component markup and styles.&lt;/p&gt;
&lt;p&gt;JavaScript has a powerful built-in templating language called &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals&quot;&gt;template literals (or template strings)&lt;/a&gt;. A more advanced form of template literals (and what &lt;code&gt;slim-ssr&lt;/code&gt; uses) are something called &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates&quot;&gt;tagged templates&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;slim-ssr&lt;/code&gt;, an &lt;code&gt;html&lt;/code&gt; and &lt;code&gt;css&lt;/code&gt; tag are defined/exported and can be used to write expressive markup and styling like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Basic markup and styles
html`&amp;lt;h1&amp;gt;Hello world!&amp;lt;/h1&amp;gt;`;
css`
	h1 {
		color: red;
	}
`;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// Use JavaScript expressions directly in markup/styles
const name = &apos;Universe&apos;;
const color = &apos;red&apos;;
html`&amp;lt;h1&amp;gt;Hello ${name}!&amp;lt;/h1&amp;gt;`;
css`
	h1 {
		color: ${color};
	}
`;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// Conditional rendering/styles
const age = 17;
let darkMode = true;
html`&amp;lt;p&amp;gt;You ${age &amp;gt;= 16 ? &apos;can&apos; : &apos;cannot&apos;} drive.&amp;lt;/p&amp;gt;`;
css`
	body {
		background: ${darkMode ? &apos;black&apos; : &apos;white&apos;};
	}
`;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;// Mapping over data to generate markup/styles
const fruits = [&apos;apple&apos;, &apos;banana&apos;, &apos;orange&apos;];
const tokens = [
	{ name: &apos;primary-color&apos;, value: &apos;rgb(210, 210, 210)&apos; },
	{ name: &apos;secondary-color&apos;, value: &apos;rgb(180, 180, 180)&apos; },
];
html`
	&amp;lt;ul&amp;gt;
		${fruits.map((fruit) =&amp;gt; html`&amp;lt;li&amp;gt;${fruit}&amp;lt;/li&amp;gt;`)}
	&amp;lt;/ul&amp;gt;
`;
css`
	:root {
		${tokens.map((token) =&amp;gt; css`--${token.name}: ${token.value};`)}
	}
`;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All the above is enabled by just 15 lines of code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function html(strings, ...values) {
	const parts = [strings[0]];
	for (let i = 0; i &amp;lt; values.length; i++) {
		if (Array.isArray(values[i])) {
			for (const value of values[i]) {
				parts.push(String(value));
			}
		} else {
			parts.push(String(values[i]));
		}
		parts.push(strings[i + 1]);
	}
	return parts.join(&apos;&apos;);
}

export const css = html;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;html&lt;/code&gt; function accepts an array of strings and an arbitrary set of value arguments (which represent JavaScript expressions that may exist in a template). It builds up these different parts into an array of strings that are then joined and returned as the final rendered HTML.&lt;/p&gt;
&lt;p&gt;It also notably has some special logic for handling expressions that map over arrays of data to generate markup/styles –– something that is not cleanly handled in regular template literals.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;css&lt;/code&gt; function is simply just the &lt;code&gt;html&lt;/code&gt; function re-exported with a different name.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A quick note on developer experience&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;By default, tagged templates will be rendered/treated as strings in code editors which results in a less than ideal developer experience when writing component markup/styles. This, however, can be changed with extensions/tooling.&lt;/p&gt;
&lt;p&gt;In the case of VS Code, installing the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=bierner.lit-html&quot;&gt;lit-html&lt;/a&gt; and &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=Tobermory.es6-string-html&quot;&gt;es6-string-html&lt;/a&gt; extensions make a world of difference while writing HTML/CSS in tagged templates. They can be used to add a ton of helpful features like syntax highlighting, IntelliSense, quick hover info, HTML tag folding, and so on.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://emmet.io/&quot;&gt;Emmet&lt;/a&gt; support inside tagged templates can also be enabled in VS Code by changing the &quot;Emmet: Include Languages&quot; setting and adding mappings for &lt;code&gt;&quot;javascript&quot;: &quot;html&quot;&lt;/code&gt; and &lt;code&gt;&quot;typescript&quot;: &quot;html&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Component model&lt;/h2&gt;
&lt;p&gt;In 2022, web components are living in a bit of a weird teething phase when it comes to SSR. The &lt;a href=&quot;https://web.dev/declarative-shadow-dom/&quot;&gt;Declarative Shadow DOM&lt;/a&gt; –– which is the API that will enable web components to be server-side rendered –– is &lt;a href=&quot;https://caniuse.com/declarative-shadow-dom&quot;&gt;only supported in Chromium-based browsers&lt;/a&gt; at this time.&lt;/p&gt;
&lt;p&gt;This means if web components are adopted as the sole component model of &lt;code&gt;slim-ssr&lt;/code&gt;, it would fail to reach its stated goal of shipping zero JavaScript by default. That is to say, in any non-Chromium-based browser, client-side JavaScript would be required to render UI that only needs HTML and CSS.&lt;/p&gt;
&lt;p&gt;In the future, it should be possible to use web components for rendering static and dynamic UI server-side, but for now, we have to look elsewhere for defining static content. Lucky for us, it&apos;s possible to achieve an expressive component model that can render static content server-side using functions and the tagged templates discussed above!&lt;/p&gt;
&lt;h2&gt;Static components&lt;/h2&gt;
&lt;p&gt;An idea I&apos;ve been playing with during the last few months while creating Delgada is to have a distinct separation between components that are static (i.e. send HTML/CSS to the client) and components that are dynamic (i.e. send HTML/CSS/JavaScript to the client).&lt;/p&gt;
&lt;p&gt;It&apos;s a design decision that I have come to really enjoy and so I&apos;m using it here.&lt;/p&gt;
&lt;p&gt;To quickly break it down:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Static components are functions that return a string of HTML&lt;/li&gt;
&lt;li&gt;Static component props are function arguments&lt;/li&gt;
&lt;li&gt;Static component styles are variables that contain a string of CSS&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;import { html, css } from &apos;slim-ssr&apos;;

export function Greeting({ name }) {
	return html`&amp;lt;h1&amp;gt;Hello ${name}!&amp;lt;/h1&amp;gt;`;
}

export const styles = css`
	h1 {
		color: red;
	}
`;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To use a static component simply import and add the component function within the markup of another static component.&lt;/p&gt;
&lt;p&gt;To correctly pick up the styles of a component, they also must be imported and added to the styles of the target component as shown in the below code snippet.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { html, css } from &apos;slim-ssr&apos;;
import { Greeting, styles as GreetingStyles } from &apos;Greeting.js&apos;;

export function Index() {
	return html`
		&amp;lt;!DOCTYPE html&amp;gt;
		&amp;lt;html lang=&quot;en&quot;&amp;gt;
			&amp;lt;head&amp;gt;
				&amp;lt;title&amp;gt;Home&amp;lt;/title&amp;gt;
				&amp;lt;style&amp;gt;
					${styles}
				&amp;lt;/style&amp;gt;
			&amp;lt;/head&amp;gt;
			&amp;lt;body&amp;gt;
				${Greeting({ name: &apos;Reader&apos; })}
				&amp;lt;p&amp;gt;This is the home page.&amp;lt;/p&amp;gt;
			&amp;lt;/body&amp;gt;
		&amp;lt;/html&amp;gt;
	`;
}

export const styles = css`
	p {
		color: blue;
	}

	${GreetingStyles}
`;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Using the client request object&lt;/h2&gt;
&lt;p&gt;As briefly mentioned earlier, components that are defined in the &lt;code&gt;routes&lt;/code&gt; object in &lt;code&gt;server.js&lt;/code&gt; will be passed a client request object that can be optionally used.&lt;/p&gt;
&lt;p&gt;This request object can be used to enable features such as conditional rendering based on request parameters. For example, the component below uses a URL parameter to render a greeting.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { html, css } from &apos;slim-ssr&apos;;

export function Hello(req) {
	const name = req.params.name;
	return html`&amp;lt;h1&amp;gt;Hello ${name ?? &apos;Person&apos;}&amp;lt;/h1&amp;gt;`;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A name can be added to the end of the page URL in the form &lt;code&gt;/hello/{name}&lt;/code&gt;. If no name is provided the greeting is conditionally rendered to return &quot;Hello Person&quot; as the default.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;server.js&lt;/code&gt; a new route is added that uses Express&apos;s parameter syntax.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const routes = [{ path: &apos;/hello/:name?&apos;, component: Hello }];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, since the request object is only passed to the components directly contained in &lt;code&gt;routes&lt;/code&gt; if a child component needs access to the request object it will need to be passed down as a prop.&lt;/p&gt;
&lt;h2&gt;Dynamic components / islands&lt;/h2&gt;
&lt;p&gt;Islands architecture (or &quot;component islands&quot;) is a method of building websites that has really come in vogue during the last year. As Jason Miller describes in &lt;a href=&quot;https://jasonformat.com/islands-architecture/&quot;&gt;his 2020 article&lt;/a&gt; introducing the concept:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The general idea of an “Islands” architecture is deceptively simple: render HTML pages on the server, and inject placeholders or slots around highly dynamic regions. These placeholders/slots contain the server-rendered HTML output from their corresponding widget. They denote regions that can then be &quot;hydrated&quot; on the client into small self-contained widgets, reusing their server-rendered initial HTML.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&apos;s an architecture that is great at isolating JavaScript to only the parts of your website that need it. In the case of &lt;code&gt;slim-ssr&lt;/code&gt; websites, we&apos;ll be accomplishing this architecture via web components.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Basic usage&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Given a &lt;code&gt;&amp;lt;counter-button&amp;gt;&lt;/code&gt; web component (that increments a count on every button click), it can be added to a web page by using the counter button in a static component and then manually linking to the web component file (i.e. &lt;code&gt;/CounterButton.js&lt;/code&gt;) in a script tag. Nothing crazy at this point.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { html } from &apos;slim-ssr&apos;;

export function Index() {
	return html`
		&amp;lt;!DOCTYPE html&amp;gt;
		&amp;lt;html lang=&quot;en&quot;&amp;gt;
			&amp;lt;head&amp;gt;
				&amp;lt;title&amp;gt;Home&amp;lt;/title&amp;gt;
				&amp;lt;script type=&quot;module&quot; src=&quot;/CounterButton.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
			&amp;lt;/head&amp;gt;
			&amp;lt;body&amp;gt;
				&amp;lt;counter-button&amp;gt;&amp;lt;/counter-button&amp;gt;
			&amp;lt;/body&amp;gt;
		&amp;lt;/html&amp;gt;
	`;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some will have noticed, however, that this doesn&apos;t actually meet the definition of islands architecture.&lt;/p&gt;
&lt;p&gt;We&apos;ve created a placeholder that will be hydrated on the client into a small self-contained widget, but there is no server-rendered HTML at this point (since we&apos;re not using the Declarative Shadow DOM API).&lt;/p&gt;
&lt;p&gt;Enter: &lt;a href=&quot;https://dev.to/thepassle/server-side-rendering-vanilla-custom-elements-in-astro-5hgg&quot;&gt;Pascal Schilp&apos;s writing on SSR and custom elements&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the article, Pascal points out that any markup nested inside a web component can be conditionally styled during the time it takes for the web component JavaScript to be executed with the following CSS selector.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;web-component:not(:defined) button {
	/* Apply arbitrary styles to a button nested 
  inside &amp;lt;web-component&amp;gt; while it&apos;s not defined. */
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can take this fact and restructure the counter button so that a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; is accepted as a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots&quot;&gt;slotted element&lt;/a&gt; to achieve the server-rendered HTML aspect of islands architecture.&lt;/p&gt;
&lt;p&gt;By simply copying and pasting the initial state of the &lt;code&gt;&amp;lt;counter-button&amp;gt;&lt;/code&gt; and its associated styles into the static component, website visitors will see a button that looks like the final hydrated button before its JavaScript has been run.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A nice bonus: This will also address the issue of &lt;a href=&quot;https://www.abeautifulsite.net/posts/flash-of-undefined-custom-elements/&quot;&gt;flash of undefined custom elements (FOUCE)&lt;/a&gt; that web components often fall prey to.&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;counter-button&amp;gt;
	&amp;lt;button&amp;gt;Clicked &amp;lt;span id=&quot;count&quot;&amp;gt;0&amp;lt;/span&amp;gt; times&amp;lt;/button&amp;gt;
&amp;lt;/counter-button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;counter-button:not(:defined) button {
	background-color: #efefef;
	color: black;
	border: 2px solid #000;
	border-radius: 8px;
	padding: 6px 10px;
}

counter-button:not(:defined) button:hover {
	cursor: pointer;
	background-color: #e6e6e6;
}

/* ... other static component styles ... */ ;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One more scenario worth mentioning (that is also discussed in Pascal&apos;s article), is that we can take advantage of the fact that arbitrary styles can be applied to the button to better represent its current state.&lt;/p&gt;
&lt;p&gt;In this case, when the component is not hydrated it will not be interactive. So instead of styling the button normally, it could instead be styled to imply it&apos;s in a disabled state.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;counter-button:not(:defined) button {
	background-color: lightgrey;
	color: darkgrey;
	border: 2px solid #000;
	border-radius: 8px;
	padding: 6px 10px;
}

counter-button:not(:defined) button:hover {
	cursor: not-allowed;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the component is hydrated the normal button styles defined inside the web component will kick in and override the disabled styles.&lt;/p&gt;
&lt;p&gt;Clearly, exposing the internals of every web component as slotted children is not the most ideal solution, but it does at least meet the stated goals of &lt;code&gt;slim-ssr&lt;/code&gt; and starts to demonstrate what a world with full Declarative Shadow DOM support will look like –– which I think is pretty exciting.&lt;/p&gt;
&lt;p&gt;While this conclusion may be discouraging to some, I think &lt;a href=&quot;https://twitter.com/dannymoerkerke/status/1545763972913287172&quot;&gt;a recent tweet&lt;/a&gt; by Danny Moerkerke is a great reminder of how to think about web components:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Web Components are a standard so features take longer to be added, contrary to frameworks that can basically add any feature they like.&lt;/p&gt;
&lt;p&gt;It doesn’t mean that Web Components are bad or will never be ready.&lt;/p&gt;
&lt;p&gt;Be patient, standards take time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So yes, while it&apos;s unfortunate that the SSR story of web components is still in a teething phase, I hope the ideas above act as a catalyst of excitement for what can still be accomplished today and the fact that there&apos;s a lot of work being done to improve this story in the future.&lt;/p&gt;
&lt;h2&gt;Taking these ideas further&lt;/h2&gt;
&lt;p&gt;At only 37 lines of code, there&apos;s a lot of headroom to play with and ways to push the ideas laid out above even further. Some ideas that I&apos;ve already implemented in Delgada or I&apos;m actively exploring are:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;File-system based routing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For not too much code, file-system based routing can be achieved. &lt;a href=&quot;https://github.com/delgada-ui/delgada/blob/main/src/cli/build.ts#L28-L66&quot;&gt;Delgada already does this to statically generate websites&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It&apos;s a fairly straightforward case of recursively iterating through all the static component files in the &lt;code&gt;src/pages/&lt;/code&gt; directory, executing the component code to render final HTML output, and then writing those outputs to files in a &lt;code&gt;build&lt;/code&gt; directory –– making sure to mirror the directory structure inside &lt;code&gt;src/pages/&lt;/code&gt; in &lt;code&gt;build&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Automatically add script tags for islands&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A minor quality of life improvement that requires very little code is automatically inserting a script tag into every page that uses web components. Here&apos;s an &lt;a href=&quot;https://github.com/delgada-ui/delgada/blob/main/src/core/webComponents.ts#L4-L19&quot;&gt;implementation of this concept&lt;/a&gt; in Delgada.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Optional inline styles&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Some may have noticed that all the page styles in the code snippets above were eventually inlined.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;head&amp;gt;
	&amp;lt;style&amp;gt;
		${styles}
	&amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While this is great for improving first-time page loads, it&apos;s not so great for web pages that have a lot of recurring visitors who would benefit from an external CSS file that can be cached by the browser.&lt;/p&gt;
&lt;p&gt;For about &lt;a href=&quot;https://github.com/delgada-ui/delgada/blob/main/src/core/styles.ts#L3-L24&quot;&gt;20 lines of code&lt;/a&gt;, the option to define styles as inline or as an external file is possible.&lt;/p&gt;
&lt;p&gt;In Delgada, this manifests as the ability to define a &lt;code&gt;metadata&lt;/code&gt; object for each page with various configuration options. One of them is the ability to change whether the styles of a given page should be inlined or not.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export const metadata = {
	// Will generate a separate CSS file for the given page
	inlineCSS: false,
};

// ... other static component code ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Page templates&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Another feature that is basically free because static components are just functions is the ability to define page templates/layouts.&lt;/p&gt;
&lt;p&gt;Template components can be defined using the same syntax as a static component and accepts a &lt;code&gt;slot&lt;/code&gt; prop. In the example below, a template can be used to reduce the boilerplate of web pages.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { html } from &apos;slim-ssr&apos;;

export function Template(slot) {
	return html`
		&amp;lt;!DOCTYPE html&amp;gt;
		&amp;lt;html lang=&quot;en&quot;&amp;gt;
			&amp;lt;head&amp;gt;
				&amp;lt;title&amp;gt;My Website&amp;lt;/title&amp;gt;
			&amp;lt;/head&amp;gt;
			&amp;lt;body&amp;gt;
				${slot}
			&amp;lt;/body&amp;gt;
		&amp;lt;/html&amp;gt;
	`;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import { html } from &apos;slim-ssr&apos;;

export function Index() {
	return html`&amp;lt;h1&amp;gt;Hello World!&amp;lt;/h1&amp;gt;`;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To use the template, the &lt;code&gt;routes&lt;/code&gt; array in &lt;code&gt;server.js&lt;/code&gt; simply needs to be updated so that page components are wrapped by the template component.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { Index } from &apos;./src/pages/Index.js&apos;;
import { Template } from &apos;./src/templates/Template.js&apos;;

const routes = [
	{
		path: &apos;/&apos;,
		component: () =&amp;gt; {
			Template(Index);
		},
	},
];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Delgada takes this one step further by also automatically &lt;a href=&quot;https://github.com/delgada-ui/delgada/blob/main/src/core/buildPage.ts#L48&quot;&gt;passing the &lt;code&gt;metadata&lt;/code&gt; object to all templates&lt;/a&gt; so that it can be used to pass arbitrary data from a page into a template.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export const metadata = {
	title: &apos;My Website&apos;,
	inlineCSS: false,
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import { html } from &apos;slim-ssr&apos;;

export function Template(slot, metadata) {
	return html`
		&amp;lt;!DOCTYPE html&amp;gt;
		&amp;lt;html lang=&quot;en&quot;&amp;gt;
			&amp;lt;head&amp;gt;
				&amp;lt;title&amp;gt;${metadata.title}&amp;lt;/title&amp;gt;
			&amp;lt;/head&amp;gt;
			&amp;lt;body&amp;gt;
				${slot}
			&amp;lt;/body&amp;gt;
		&amp;lt;/html&amp;gt;
	`;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Use a modern runtime like Deno or Bun&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Adding TypeScript support to a Node-based web framework can be kind of tricky.&lt;/p&gt;
&lt;p&gt;An alternative I&apos;ve been exploring is to build a web framework on top of a modern runtime like Deno or Bun that supports TypeScript execution out of the box.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Component scoped CSS in static components&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I&apos;m also looking into adding scoped CSS support in static components since all the styles currently live in the global scope of any given page.&lt;/p&gt;
&lt;p&gt;It&apos;s a topic I haven&apos;t put too much research into yet, so if anyone reading this has any resources or tips please do send a &lt;a href=&quot;https://fosstodon.org/@hawkticehurst&quot;&gt;post or DM&lt;/a&gt; my way!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Template directives&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pulling inspiration from &lt;a href=&quot;https://lit.dev/&quot;&gt;Lit&lt;/a&gt; (a framework for building web components), the templating of &lt;code&gt;slim-ssr&lt;/code&gt; could be vastly improved via &quot;directives.&quot;&lt;/p&gt;
&lt;p&gt;Directives are functions that customize the way a template expression gets rendered and can either simplify the creation of markup/styles or add extra functionality that doesn&apos;t exist currently.&lt;/p&gt;
&lt;p&gt;Lit&apos;s &lt;a href=&quot;https://lit.dev/docs/templates/directives/&quot;&gt;list of built-in directives&lt;/a&gt; offers some great inspiration for what&apos;s possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Incremental build-time rendering&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Another cool optimization that could be added is what Thomas Allmer &lt;a href=&quot;https://twitter.com/daKmoR/status/1514183374121934850&quot;&gt;refers to as &quot;On-Demand to Build-Time Cache SSR&quot; or &quot;Incremental Build-Time Rendering&quot;&lt;/a&gt;. Others might also know this concept from Next.js as &quot;Incremental Static Regeneration.&quot;&lt;/p&gt;
&lt;p&gt;The basic idea is to render and send a page request as normal, but also write the rendered HTML to a file that is saved in a cache. If there&apos;s a subsequent request made for the same page the cached file will be sent instantly instead of re-rendering everything.&lt;/p&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;In a time when everyone seems to be talking about bloated website bundles and inattentive usage of NPM packages, I&apos;ve discovered a breath of fresh air and a delightful simplicity in what the modern web can enable in 2022.&lt;/p&gt;
&lt;p&gt;It, of course, still has its discomforts and growing pains but it has me really excited for what the future holds. I hope after reading this you might be feeling some of that excitement too.&lt;/p&gt;
&lt;p&gt;~~&lt;/p&gt;
&lt;p&gt;Liked what you read? Or maybe not? Have a question? Let me know on &lt;a href=&quot;https://fosstodon.org/@hawkticehurst&quot;&gt;Mastodon&lt;/a&gt;!&lt;/p&gt;
</content:encoded></item><item><title>The mega full-stack resource guide</title><link>https://hawkticehurst.com/2020/09/mega-full-stack-resource-guide/</link><guid isPermaLink="true">https://hawkticehurst.com/2020/09/mega-full-stack-resource-guide/</guid><description>An educational resource guide covering the major topics of full-stack web development.</description><pubDate>Thu, 10 Sep 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Table of Contents&lt;/h2&gt;
&lt;p&gt;The guide is split into five main categories.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;#prelude&quot;&gt;Prelude&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#why-is-this-guide-a-thing&quot;&gt;Why Is This Guide A Thing?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#about-this-guide&quot;&gt;About This (Long) Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;a href=&quot;#dev-resources&quot;&gt;Development Resources&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#text-editor-resources&quot;&gt;Text Editors/IDEs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#terminal-shell-resources&quot;&gt;Terminals/Shells&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#browser-resources&quot;&gt;Browsers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;a href=&quot;#client-side-resources&quot;&gt;Client-Side / Frontend Resources&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#html-resources&quot;&gt;HTML&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#css-resources&quot;&gt;CSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#javascript-resources&quot;&gt;JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#typescript-resources&quot;&gt;TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#javascript-framework-resources&quot;&gt;Frontend JavaScript Frameworks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#client-side-api-resources&quot;&gt;Client-Side APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#webassembly-resources&quot;&gt;WebAssembly&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;a href=&quot;#server-side-resources&quot;&gt;Server-Side / Backend Resources&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#javascript-runtime-resources&quot;&gt;JavaScript Runtimes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#python-resources&quot;&gt;Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#go-resources&quot;&gt;Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#rust-resources&quot;&gt;Rust&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#database-resources&quot;&gt;Databases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#cloud-resources&quot;&gt;The Cloud&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;a href=&quot;#miscellaneous-resources&quot;&gt;Miscellaneous Resources&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#inspiration-resources&quot;&gt;Sources For Inspiration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#general-education-resources&quot;&gt;General Education Resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#reading-resources&quot;&gt;Great Blogs/Articles/Books&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#stock-image-resources&quot;&gt;Stock Image Resources&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;section&amp;gt;
&amp;lt;h2 id=&quot;prelude&quot;&amp;gt;Prelude&amp;lt;/h2&amp;gt;
&amp;lt;h3 id=&quot;why-is-this-guide-a-thing&quot;&amp;gt;Why Is This Guide A Thing?&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;I have a meticulously organized digital life.&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
My computer&apos;s file system allows me to track down any photo, video, or
document I&apos;ve created during any month over the past 13 years.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
My Google Drive is similarly organized to keep track of documents such
as the high school resume I used to get my first job or the documents I
made for every single class during my 5 years of college.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
And, importantly for this post, I have a Chrome bookmark system that
allowed me to stay organized and learn computer science/software
engineering after a sudden change of direction in my last 2 years of
college.
&amp;lt;/p&amp;gt;
&amp;lt;img src=&quot;/imgs/bookmarks.gif&quot; alt=&quot;A gif of my Chrome bookmarks.&quot; /&amp;gt;
&amp;lt;p&amp;gt;
Contained in these bookmark folders are links to hundreds of resources
ranging from artificial intelligence to development tooling. This
catalog of resources allowed me to organize my thinking as I learned new
topics and to ultimately collect some incredible content that was
instrumental to my learning during college.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Now, as a recent graduate, I want to pass on some of those resources
that were so helpful to me. My hope is that those in their own process
of learning might find something useful in all of this just as I did.
&amp;lt;/p&amp;gt;
&amp;lt;h3 id=&quot;about-this-guide&quot;&amp;gt;About This (Long) Guide&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
As hinted via the title above, this guide is focused on full-stack web
development resources.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
This is due in part to an effort to limit the scope of this guide, but
also because I&apos;m really passionate about the internet and what it has to
offer –– it&apos;s quite a festive place.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
In addition to simply providing an organized list of links, I&apos;ve
included high-level explanations of each topic and how it fits into the
world of full-stack web dev. I&apos;ve also included some of my own thoughts
and personal recommendations (denoted by 🔥) for some of the resources.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
It&apos;s my way of highlighting those resources which have been invaluable
to me and an opportunity to give some plain-English insights/information
that might be helpful to those learning about these topics.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;I hope you enjoy it!&amp;lt;/p&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Development Resources --&amp;gt;
&amp;lt;section&amp;gt;
&amp;lt;h2 id=&quot;dev-resources&quot;&amp;gt;Development Resources&amp;lt;/h2&amp;gt;
&amp;lt;img
src=&quot;/imgs/dev-resources.png&quot;
alt=&quot;An illustration of a terminal icon.&quot;
/&amp;gt;
&amp;lt;p&amp;gt;
I start this guide with development tools because even with all the
knowledge of full-stack development in the world you need tooling to put
it to action.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
For those new to the world of software tooling, it encompasses a huge
variety of subtopics. Because of this, you can get lost down some pretty
deep and overwhelming rabbit holes, but at it&apos;s most basic it usually
means
&amp;lt;span class=&quot;italic&quot;
&amp;gt;choosing a good text editor and terminal that is suited to the work
you&apos;ll be doing.&amp;lt;/span
&amp;gt;
&amp;lt;/p&amp;gt;
&amp;lt;h3 id=&quot;text-editor-resources&quot;&amp;gt;Text Editors / IDEs&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Text editors and IDEs (Integrated Development Environments) are desktop
applications, command-line applications, or web applications where you
write code –– basically think Microsoft Word but specialized for writing
code.
&amp;lt;/p&amp;gt;
&amp;lt;img src=&quot;/imgs/vs-code.png&quot; alt=&quot;An illustration of the VS Code logo.&quot; /&amp;gt;
&amp;lt;p&amp;gt;
There is an almost endless number of text editors and IDEs available
that offer various benefits and drawbacks, so don&apos;t be afraid to try out
different options until you find something that feels comfortable.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
My current favorite is the very popular Visual Studio Code made by
Microsoft (which is not to be confused with Microsoft&apos;s IDE––Visual
Studio). VS Code is a flexible text editor with a huge number of
settings and third-party extensions that can customize the editor&apos;s look
and functionality to fit and improve your workflow.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
In addition to that, VS Code comes with some incredible built-in
extensions for JavaScript and TypeScript that make it a very useful
editor for web development.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://discuss.atom.io/t/what-is-the-difference-between-an-ide-and-an-editor/32629&quot;
&amp;gt;What Is The Difference Between An IDE And An Editor?&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://code.visualstudio.com/&quot;&amp;gt;VS Code Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://code.visualstudio.com/docs&quot;&amp;gt;VS Code Documentation&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://welearncode.com/vscode-setup/&quot;&amp;gt;VS Code Setup&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://www.youtube.com/watch?v=u21W_tfPVrY&amp;amp;ab_channel=Fireship&quot;
&amp;gt;VS Code Top Ten Pro Tips&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.openvim.com/&quot;&amp;gt;Interactive Vim Tutorial&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Vim is a very powerful command-line text editor.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://missing.csail.mit.edu/2020/editors/&quot;&amp;gt;Vim Tutorial&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://atom.io/&quot;&amp;gt;Atom Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Atom is another great desktop text editor made by Github.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://atom.io/docs&quot;&amp;gt;Atom Documentation&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://codesandbox.io/&quot;&amp;gt;Code Sandbox IDE&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
Code Sandbox is an online editor that is great for prototyping
JavaScript-based web apps.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h4&amp;gt;Text Editor Extension Resources&amp;lt;/h4&amp;gt;
&amp;lt;p&amp;gt;
Below are some text editor extension resources that are worth a peek.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Emmet makes the job of writing HTML a breeze by defining a succinct
syntax that can be expanded into full HTML. A cheat sheet of the syntax
can be found below.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Kite and TabNine are AI powered coding assistants that provide some very
impressive code autocompletion features.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://docs.emmet.io/cheat-sheet/&quot;&amp;gt;Emmet Cheat Sheet&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://www.kite.com/&quot;&amp;gt;Kite&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://www.tabnine.com/&quot;&amp;gt;TabNine&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3 id=&quot;terminal-shell-resources&quot;&amp;gt;Terminals / Shells&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Terminals, sometimes casually referred to as &quot;command lines&quot; or
&quot;consoles&quot;, are desktop applications that allow you to interact with a
shell.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
A shell is a
&amp;lt;span class=&quot;italic&quot;&amp;gt;text-based user interface&amp;lt;/span&amp;gt; that allows you to
control your computer. Instead of using a mouse to click UI elements and
icons, you type commands into the shell that allow you to do things like
open desktop applications, run code, navigate your computer&apos;s file
system, automate tedious or repetitive tasks, and so much more.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Becoming proficient with a terminal/shell combo is a massively valuable
skill in the world of not only full-stack development but of software
engineering at large.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Finally, just like text editors and IDEs, there are many terminals and
shells with benefits and drawbacks. So once again have fun and try
different options until you find something that works for you.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
My current terminal/shell combination is iTerm2 and zsh. iTerm2 is a
great terminal with a rich set of features and customization options,
but is unfortunately MacOS only. Zsh is the default shell for MacOS.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://www.iterm2.com/&quot;&amp;gt;iTerm2&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://hyper.is/&quot;&amp;gt;Hyper&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
Hyper is another really clean cross-platform terminal created using
web technologies.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://github.com/rothgar/mastering-zsh&quot;&amp;gt;Mastering Zsh&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://ohmyz.sh/&quot;&amp;gt;Ohmyzsh Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
Ohmyzsh is a fantastic framework for generally improving the
experience of using zsh.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://dev.to/aspittel/my-terminal-setup-iterm2--zsh--30lm&quot;
&amp;gt;Terminal Setup: iTerm2 + Zsh&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://scriptingosx.com/2019/06/moving-to-zsh/&quot;
&amp;gt;Moving to Zsh&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;http://zpalexander.com/switching-to-zsh/&quot;
&amp;gt;Switching to Zsh&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/ohmyzsh/ohmyzsh&quot;&amp;gt;Ohmyzsh GitHub&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2 id=&quot;browser-resources&quot;&amp;gt;Browser Resources&amp;lt;/h2&amp;gt;
&amp;lt;img
src=&quot;/imgs/browsers.png&quot;
alt=&quot;An illustration of various major browser logos.&quot;
/&amp;gt;
&amp;lt;p&amp;gt;
In the world of full-stack web development, the next thing to consider
is browsers. These are the applications that allow you to access the
internet and execute/render the frontend code (i.e. websites) you will
create.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
These browsers also offer tools for debugging and analyzing your
website&apos;s code. Learning how to work with these tools early on (and
especially once you start learning JavaScript) is something I can&apos;t
recommend enough.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Finally, it is very important to know that the frontend code you write
will not always work or look the same across different browsers (the
reason for this can be better understood in the &apos;How Browsers Work&apos; and
&apos;Browser Engines&apos; sections below). As a result, it&apos;s highly recommended
to have many of the
&amp;lt;a href=&quot;https://www.w3counter.com/globalstats.php&quot;&amp;gt;popular browsers&amp;lt;/a&amp;gt;
downloaded so you can test your website on each browser.
&amp;lt;/p&amp;gt;
&amp;lt;p class=&quot;italic&quot;&amp;gt;
Pro Tip: An amazing tool for determining whether or not your frontend
code will work on a particular browser is
&amp;lt;a href=&quot;https://caniuse.com/&quot;&amp;gt;Can I Use?&amp;lt;/a&amp;gt;.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;🔥 Browser Development Tools&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Understanding how to debug your code is one of the most important
aspects of learning how to develop for a platform like the web. Browsers
like Chrome and Firefox offer some absolutely incredible debugging and
development tools.
&amp;lt;span class=&quot;bold&quot;&amp;gt;LEARN HOW TO USE THESE TOOLS.&amp;lt;/span&amp;gt;
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://developers.google.com/web/tools/chrome-devtools/&quot;
&amp;gt;Chrome DevTools Guide&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/&quot;
&amp;gt;Google LightHouse Guide&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
Google Lighthouse is a great tool that can automatically audit the
performance, accessibility, SEO, and more of your website.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools&quot;
&amp;gt;Firefox Developer Tools Guide&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developer.apple.com/safari/tools/&quot;
&amp;gt;Safari Web Development Tools&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://docs.microsoft.com/en-us/microsoft-edge/devtools-guide-chromium&quot;
&amp;gt;Microsoft Edge Developer Tools&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;How Browsers Work&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Understanding how browsers work under the hood will give context to the
code you write and fundamentally change how you think about full-stack
web development… or at least it did for me 😁.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
This is an admittedly advanced topic since there is a very different
skillset at play when creating a web browser versus making a website,
but I still think it&apos;s worth reviewing at some point during your
full-stack education and then continuing to revisit the topic as you
learn more.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://alistapart.com/article/from-url-to-interactive/&quot;
&amp;gt;How Browsers Work Under The Hood –– Aaron Gustofson Series&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Browser Engine Resources&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Another really cool topic that is an even more focused look into how
browsers work is learning about browser engines. This topic is
&amp;lt;span class=&quot;bold&quot;&amp;gt;not necessary to learn web development&amp;lt;/span&amp;gt;, but
can once again be a nice way to gain a deeper insight into what is
happening inside your browser.
&amp;lt;span class=&quot;bold&quot;&amp;gt;Also, be warned, this an advanced topic.&amp;lt;/span&amp;gt;
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Browser engines, in short, are the core pieces of software built into
web browsers like Chrome and Firefox that do the work of executing and
rendering the website code that you write. Typically, browser engines
are comprised of two primary types of engines:
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
Rendering Engines –– Responsible for rendering the pixel-by-pixel
layout of a website
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
JavaScript Engines –– Responsible for interpreting/compiling and
executing JS code (as well as WebAssembly code in some cases)
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;p&amp;gt;
These engines are the primary reason that the frontend code you write
may not work or look the same across browsers. Browsers will often use
different engines that have unique implementation details which can
cause differences in how a website is laid out or interpreted.
&amp;lt;/p&amp;gt;
&amp;lt;p class=&quot;italic&quot;&amp;gt;
A Quick Note: It is also common for the term &quot;browser engine&quot; to only
refer to the part of a browser that is in charge of rendering a webpage.
This is because it&apos;s very common for JavaScript engines to be built in a
way that they can function independently from a browser, whereas the
rendering engine is a fundamental and baked in part of browsers. So keep
that all in mind as you learn more about these engines in the wild.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://www.lambdatest.com/blog/browser-engines-the-crux-of-cross-browser-compatibility/&quot;
&amp;gt;Browser Engines: The Crux Of Cross Browser Compatibility&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2017/05/quantum-up-close-what-is-a-browser-engine/&quot;
&amp;gt;Quantum Up Close: What Is A Browser Engine?&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://gizmodo.com/which-browser-engine-powers-your-web-browsing-and-why-d-1833935288&quot;
&amp;gt;Which Browser Engines Powers Your Web Browsing––And Why Does It
Matter?&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.browserstack.com/guide/browser-rendering-engine&quot;
&amp;gt;Understanding The Role Of Rendering Engine In Browsers&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://securitygladiators.com/web-browsers-engines/&quot;
&amp;gt;Web Browsers And Their Engines: Know Everything&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.wikiwand.com/en/Browser_engine&quot;
&amp;gt;Wikipedia: Browser Engines&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h4&amp;gt;Chrome Engines&amp;lt;/h4&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://v8.dev/&quot;&amp;gt;V8 Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.chromium.org/blink&quot;&amp;gt;Blink Website&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h4&amp;gt;Firefox Engines&amp;lt;/h4&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://mozilla-spidermonkey.github.io/&quot;
&amp;gt;SpiderMonkey Website
&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey&quot;
&amp;gt;MDN SpiderMonkey: The Mozilla JavaScript Runtime&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Gecko&quot;
&amp;gt;MDN Gecko Rendering Engine&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://servo.org/&quot;
&amp;gt;Servo––Parallel Browser Engine Project&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/servo/servo/wiki/Design&quot;&amp;gt;Servo Design&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://blog.servo.org/&quot;&amp;gt;Servo Blog&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.youtube.com/watch?v=UGl9VVIOo3E&amp;amp;feature=youtu.be&amp;amp;list=PL4sEzdAGvRgCYXot-o5cVKOobIXZI5iLF&amp;amp;ab_channel=WebEnginesHackfest&quot;
&amp;gt;Servo: Today &amp;amp; Tomorrow&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h4&amp;gt;Safari Engines&amp;lt;/h4&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://webkit.org/&quot;&amp;gt;WebKit Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h4&amp;gt;Miscellaneous Resources&amp;lt;/h4&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://limpet.net/mbrubeck/2014/08/08/toy-layout-engine-1.html&quot;
&amp;gt;Let&apos;s Build A Browser Engine!&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Client-Side Programming --&amp;gt;
&amp;lt;section&amp;gt;
&amp;lt;h2 id=&quot;client-side-resources&quot;&amp;gt;Client-Side / Frontend Programming&amp;lt;/h2&amp;gt;
&amp;lt;img
src=&quot;/imgs/client-side-resources.png&quot;
alt=&quot;An illustration of desktop screen with a web browser open.&quot;
/&amp;gt;
&amp;lt;p&amp;gt;
Client-side or frontend programming is the area of web development
dedicated to creating everything a user of a website will see (i.e. the
UI).
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
In its simplest form, all you need to make a website is HTML.
Unfortunately, an HTML-only website will look and feel quite bland which
is why CSS is also used to change the style of a website, and JavaScript
is used to add more complex behavior to a website.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
From that point other languages, libraries, and tools like TypeScript or
React are additives that make the job of creating and maintaining
websites easier.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
There are many comprehensive web development guides available out there
today but I&apos;ll take a moment to call out the
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/&quot;
&amp;gt;Mozilla Developer Network&amp;lt;/a
&amp;gt;.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Very few resources match the quality and robustness of the Mozilla
Developer Network (MDN). It is a resource that is linked to many times
in this guide and is probably one of the single most helpful resources I
used to grapple with many of the web development topics I&apos;ve learned in
the last two years.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
It&apos;s a website that I highly recommend you take the time to peruse and
see what kind of content is available.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn&quot;
&amp;gt;MDN Learn Web Development&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;MDN&apos;s structured full-stack web development course.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/&quot;
&amp;gt;Dave Stearns Web Development Tutorials&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
These amazing tutorials were used for a server-side programming
course I took in college, but it also includes some incredible
client-side tutorials that are worth reading as well.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://info340.github.io/&quot;
&amp;gt;INFO 340 Client-Side Web Development Book&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
This is a fantastic client-side development book for a class I took
in college. It&apos;s written in a very human readable way.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://frontendmasters.com/books/front-end-handbook/2019/&quot;
&amp;gt;Frontend Developer Handbook&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://blog.codeanalogies.com/2018/05/09/the-relationship-between-html-css-and-javascript-explained/&quot;
&amp;gt;The Relationship Between HTML, CSS, and JavaScript Explained&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;!-- HTML Resources --&amp;gt;
&amp;lt;h2 id=&quot;html-resources&quot;&amp;gt;HTML Resources&amp;lt;/h2&amp;gt;
&amp;lt;img src=&quot;/imgs/html.png&quot; alt=&quot;An illustration of the HTML5 logo.&quot; /&amp;gt;
&amp;lt;p&amp;gt;
As I stated above, HTML (Hyper-Text Markup Language) is the absolute
baseline for what you need to make a website. It is the place where you
include the content of your website and
&amp;lt;span class=&quot;italic&quot;&amp;gt;define the structure&amp;lt;/span&amp;gt; of your webpages.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
An analogy I&apos;m fond of is, if the content (i.e. text, images, etc.) of
your website is a pile of bones then HTML is what you use to structure
those bones into a skeleton (i.e. a webpage).
&amp;lt;/p&amp;gt;
&amp;lt;img
src=&quot;/imgs/content-to-html.png&quot;
alt=&quot;An illustration of a pile of bones with an arrow pointing to a skeleton.&quot;
/&amp;gt;
&amp;lt;p&amp;gt;
You can use a thing called HTML tags to, for example, define a piece of
text as being a paragraph or a title. Then by ordering these tags one
after another you can define how all the pieces fit together.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Getting Started&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
These are some more comprehensive tutorials that cover all the major
aspects of HTML. The HTML Dog resource is one that I&apos;ve found to be
particularly helpful with its simple intuitive explanations.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://htmldog.com/guides/html/&quot;
&amp;gt;HTML Dog –– HTML Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/HTML_basics&quot;
&amp;gt;MDN –– HTML Basics&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML&quot;
&amp;gt;MDN –– HTML Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;HTML Tags References&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Tag Reference resources can be thought of as a sort of HTML tag
dictionary. It&apos;s a place where you can browse all the different tags
that are available to use inside an HTML file with included explanations
and examples of how to use them. Here are a few of my favorites.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://htmlreference.io/&quot;&amp;gt;HTML Reference IO&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://htmldog.com/references/html/tags/&quot;
&amp;gt;HTML Dog –– Tag Reference&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element&quot;
&amp;gt;MDN –– Tag Reference&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;HTML Attribute References&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
HTML attributes are special words that you can use inside an HTML tag to
modify or define behaviors in those tags. For example, by using the
&quot;class&quot; attribute in a tag you can associate some CSS styles with that
tag.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
These references below are once again a sort of dictionary with
definitions of all the attributes you can use in an HTML file.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://htmldog.com/references/html/globalattributes/&quot;
&amp;gt;HTML Dog –– Global Attributes Reference&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes&quot;
&amp;gt;MDN –– Attributes Reference&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes&quot;
&amp;gt;MDN –– Global Attributes Reference&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;HTML Character Codes References&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Character code references are another type of dictionary that defines
all the possible characters you can use in an HTML document and their
associated code that you type.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Say, for example, you wanted to include the division symbol on a webpage
you can type &quot;&amp;amp;divide;&quot; and it will be converted into ÷.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.toptal.com/designers/htmlarrows/arrows/&quot;
&amp;gt;HTML Character Code Reference&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://htmldog.com/references/html/characters/&quot;
&amp;gt;HTML Dog –– Character Code Reference&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.freeformatter.com/html-escape.html&quot;
&amp;gt;HTML Character Escape Tool&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;HTML Meta Tags&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Meta tags are HTML tags that contain information about a particular
webpage. These tags can define things such as the title or description
of a webpage.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Notably, these meta tags can also define the information that is crucial
for website SEO (search engine optimization). Below is a favorite
resource of mine for automatically generating SEO related meta tags and
also provides some great educational material if you scroll down below
the tool.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥🔥🔥 &amp;lt;a href=&quot;https://metatags.io/&quot;&amp;gt;Meta Tag Generator&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Accessibility&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Website accessibility is an incredibly important topic when making
websites that can cater to all users. Accessibility can take many
forms –– everything from blindness to having a broken arm to even having
a slow internet connection. They all affect a user&apos;s ability to access a
website and its content.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Being able to design websites that accommodate the gamut of
accessibility issues will not only lead to a better experience for those
with disabilities but very often leads to great experiences for those
without disabilities too.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.w3.org/WAI/fundamentals/accessibility-intro/&quot;
&amp;gt;Introduction To Web Accessibility&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Accessibility&quot;
&amp;gt;MDN –– Accessibility&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.levelaccess.com/how-browsers-interact-with-screen-readers-and-where-aria-fits-in-the-mix/&quot;
&amp;gt;How Browsers Interact with Screen Readers &amp;amp; Where ARIA Fits In&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developers.google.com/web/fundamentals/accessibility/semantics-builtin/the-accessibility-tree&quot;
&amp;gt;The Accessibility Tree&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.smashingmagazine.com/2018/12/voiceover-screen-reader-web-apps/&quot;
&amp;gt;I Used The Web For A Day Using A Screen Reader&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://bighack.org/5-most-annoying-website-features-i-face-as-a-blind-screen-reader-user-accessibility/&quot;
&amp;gt;5 Most Annoying Website Features I Face As A Blind Person Every
Single Day&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Miscellaneous Resources&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://svgontheweb.com/&quot;
&amp;gt;A Practical Guide To SVGs On The Web&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://dev.to/kenbellows/stop-using-so-many-divs-an-intro-to-semantic-html-3i9i&quot;
&amp;gt;Intro To Semantic HTML&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding&quot;
&amp;gt;MDN –– Multimedia &amp;amp; Embedding Guides&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/HTML/Tables&quot;
&amp;gt;MDN –– HTML Tables Guides&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Forms&quot;
&amp;gt;MDN –– HTML Forms Guides&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://imagekit.io/blog/lazy-loading-images-complete-guide/&quot;
&amp;gt;A Complete Guide To Lazy Loading Images&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;!-- CSS Resources --&amp;gt;
&amp;lt;h2 id=&quot;css-resources&quot;&amp;gt;CSS Resources&amp;lt;/h2&amp;gt;
&amp;lt;img src=&quot;/imgs/css.png&quot; alt=&quot;An illustration of the CSS3 logo.&quot; /&amp;gt;
&amp;lt;p&amp;gt;
After HTML, the next thing you would typically use is CSS (Cascading
Style Sheets). CSS gives you the ability to add style to an HTML file
(read: make your website look festive and fancy).
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Following the same skeleton analogy from above, CSS would be like adding
clothes to your HTML skeleton.
&amp;lt;/p&amp;gt;
&amp;lt;img
src=&quot;/imgs/html-to-css.png&quot;
alt=&quot;An illustration of a skeleton with an arrow pointing to a skeleton with clothes on.&quot;
/&amp;gt;
&amp;lt;p&amp;gt;
CSS is incredibly powerful and lets you define everything from the color
of text to complex animations.
&amp;lt;a href=&quot;http://www.csszengarden.com/&quot;&amp;gt;CSS Zen Garden&amp;lt;/a&amp;gt; is a fantastic
introductory website that shows off this power by using different CSS
styles on the same HTML to create totally unique looking websites.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Getting Started&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Once again, here are some more comprehensive tutorials for those looking
for a structured walkthrough of CSS.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://htmldog.com/guides/css/&quot;&amp;gt;HTML Dog –– CSS Tutorial&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/CSS_basics&quot;
&amp;gt;MDN –– CSS Basics&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/CSS/First_steps/How_CSS_works&quot;
&amp;gt;How CSS Works&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://learn.shayhowe.com/html-css/getting-to-know-css/&quot;
&amp;gt;Getting To Know CSS&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://speckyboy.com/good-bad-css-practices/&quot;
&amp;gt;Good And Bad CSS Practices For Beginners&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;CSS Syntax &amp;amp; Terms&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
A part of learning CSS includes learning some special vocabulary for the
syntax you&apos;re looking at. Knowing what a &quot;property&quot; is, or what a
&quot;selector&quot; is, or what the difference between a &quot;rule set&quot; and
&quot;declaration block&quot; is will make your process of learning CSS much
easier.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.impressivewebs.com/css-terms-definitions/&quot;
&amp;gt;CSS Terms &amp;amp; Definitions&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;http://apps.workflower.fi/vocabs/css/en&quot;&amp;gt;CSS Vocab&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Syntax&quot;
&amp;gt;CSS Syntax&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://flukeout.github.io/&quot;&amp;gt;CSS Diner&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;CSS References&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Similar to HTML, these resources are effectively dictionaries for CSS
properties.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://cssreference.io/&quot;&amp;gt;CSS Reference IO&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Reference?redirectlocale=en-US&amp;amp;redirectslug=CSS%2FCSS_Reference&quot;
&amp;gt;MDN –– CSS Reference&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;CSS Layout / Box Model&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
One of the most powerful aspects of CSS is the fact that it lets you
define the layout of your website and the spacing between elements.
These are a few resources that cover this topic.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Introduction&quot;
&amp;gt;MDN –– Intro To CSS Layout&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://courses.cs.washington.edu/courses/cse154/19au/resources/assets/layout/css-layout.pdf&quot;
&amp;gt;CSS Layout Slidedeck&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model&quot;
&amp;gt;MDN –– Box Model&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://learn.shayhowe.com/html-css/opening-the-box-model/&quot;
&amp;gt;Opening The Box Model&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;CSS Flexbox&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Flexbox is, from my experience, an absolute-must-learn topic when
picking up CSS. It is a much easier and more powerful way of defining
the layout of elements on a webpage.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=K74l26pE4YA&quot;
&amp;gt;CSS Flexbox In 100 Seconds&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://css-tricks.com/snippets/css/a-guide-to-flexbox/&quot;
&amp;gt;A Complete Guide to CSS Flexbox&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
Make sure to read &quot;Background&quot; and &quot;Terminology&quot; sections of this
guide.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://flexboxfroggy.com/&quot;&amp;gt;Flexbox Froggy&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.internetingishard.com/html-and-css/flexbox/&quot;
&amp;gt;Internetting Is Hard –– Flexbox Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://bennettfeely.com/flexplorer/&quot;&amp;gt;Flexplorer&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;CSS Grid&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Grid is another incredibly powerful way to layout elements on a webpage.
As the name suggests it allows you to orient elements on a webpage in a
grid.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=uuOXPWCh-6o&quot;
&amp;gt;CSS Grid In 100 Seconds&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://css-tricks.com/snippets/css/complete-guide-grid/&quot;
&amp;gt;A Complete Guide to CSS Grid&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
Make sure to read all the collapsed sections at the beginning such
as the &quot;Introduction&quot;.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://codepip.com/games/grid-garden/&quot;&amp;gt;Grid Garden&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=705XCEruZFs&quot;
&amp;gt;The Joy Of CSS Grid –– Build 3 Beautifully Simple Responsive
Layouts&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://learncssgrid.com/&quot;&amp;gt;Learn CSS Grid&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;CSS Responsive Design&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
A massive concern when creating websites is the fact that it can be
viewed on everything from a massive desktop screen to a tiny mobile
phone.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Responsive design is the area of CSS and website design that is
concerned with this issue. By using CSS Media Queries you can define how
your website should look on those devices and change the styles of each
element so that they look good on every screen size.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://www.internetingishard.com/html-and-css/responsive-design/&quot;
&amp;gt;Internetting Is Hard –– Responsive Design Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/responsive/&quot;
&amp;gt;Responsive CSS&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Guide/Mobile&quot;
&amp;gt;MDN –– Mobile Web Development&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.internetingishard.com/html-and-css/responsive-images/&quot;
&amp;gt;Internetting Is Hard –– Responsive Images&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;CSS Animations&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
CSS defines some incredibly powerful (yet simple) syntax for creating
animations. With this syntax you can create everything from simple
transitions to complex multistep animations.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Below are a couple of learning resources, as well as some tools and
libraries that make the job of creating complex CSS animations a lot
easier.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=HZHHBwzmJLk&quot;
&amp;gt;CSS Animations In 100 Seconds&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://thoughtbot.com/blog/css-animation-for-beginners&quot;
&amp;gt;CSS Animations For Beginners&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/animations/&quot;
&amp;gt;CSS Animations&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://nelsonmichael.dev/introduction-to-css-transitions-ckc396nae003u5es1097h5rr1&quot;
&amp;gt;Introduction to CSS Transitions&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://animista.net/&quot;&amp;gt;Animista&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;A tool for generating CSS animations.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://keyframes.app/&quot;&amp;gt;Keyframes App&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;A tool for prototyping CSS animations.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://animate.style/&quot;&amp;gt;Animate.css&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;A general purpose CSS animation library.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://michalsnik.github.io/aos/&quot;
&amp;gt;Animate On Scroll (AOS)&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;A CSS animation scroll library.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://easings.net/en&quot;&amp;gt;Easing Functions Cheatsheet&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Color &amp;amp; Gradients Resources&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
As I hinted in the CSS introduction paragraphs above, the ability to
define colors and also gradients is a power granted by CSS. Below are
some of my favorite tools I&apos;ve come across for helping pick color codes,
color palettes, accessible colors, and gradients when creating a
website.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://htmlcolorcodes.com/&quot;&amp;gt;HTML Color Codes&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;http://khroma.co/&quot;&amp;gt;Khroma&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;An AI powered color generator.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://accessible-colors.com/&quot;&amp;gt;Accessible Colors&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://coolors.co/&quot;&amp;gt;Coolors Color Generator&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://webgradients.com/&quot;&amp;gt;Web Gradients&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://cssgradient.io/&quot;&amp;gt;CSS Gradients&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Typography &amp;amp; Font Resources&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;Defining a website font is also a power granted by CSS.&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
I&apos;ll also take this opportunity to sing my praises to Google Fonts as a
resource that you should absolutely try out. They have a decently sized
catalog of high-quality free to use fonts that can drastically improve
the look of your website.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥🔥🔥 &amp;lt;a href=&quot;https://fonts.google.com/&quot;&amp;gt;Google Fonts&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://www.internetingishard.com/html-and-css/web-typography/&quot;
&amp;gt;Internetting Is Hard –– Web Typography&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://jmattthew.github.io/better-font-finder/better-font-finder.html&quot;
&amp;gt;Google Font Finder&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://getsnapfont.com/&quot;&amp;gt;Snapfont&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://www.dafont.com/&quot;&amp;gt;Dafont&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://www.fontsquirrel.com/&quot;&amp;gt;Font Squirrel&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://www.fontspring.com/&quot;&amp;gt;Font Spring&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Icon &amp;amp; Graphic Resources&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
The title says it all. Below are some great resources for finding
website icons and graphics. Each one is a little different in what it
offers so check them all out and see which you prefer.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://orioniconlibrary.com/&quot;&amp;gt;Orion Icon Library&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://css.gg/&quot;&amp;gt;700+ CSS Icons&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://heroicons.com/&quot;&amp;gt;Heroicons&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://fontawesome.com/&quot;&amp;gt;Font Awesome Icons&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://primer.style/octicons/&quot;&amp;gt;Octicons&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://ionicons.com/&quot;&amp;gt;Ionicons&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://worldvectorlogo.com/&quot;&amp;gt;World Vector Logo&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.awwwards.com/martamoskwa/collections/free-icons-sets/&quot;
&amp;gt;Awwwards Icon Sets Collection&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://9elements.github.io/fancy-border-radius/&quot;
&amp;gt;Fancy Border Radius Generator&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://blobs.app/&quot;&amp;gt;CSS Blobs&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://www.humaaans.com/&quot;&amp;gt;Humaaans&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://undraw.co/&quot;&amp;gt;unDraw&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;CSS Preprocessors&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Preprocessors simplify the process of writing complex CSS by defining a
simpler/more powerful CSS syntax you can use and then converts that to
regular CSS which can be run by a browser.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.freecodecamp.org/news/css-preprocessors/&quot;
&amp;gt;CSS Preprocessors Explained&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://raygun.com/blog/css-preprocessors-examples/&quot;
&amp;gt;Popular CSS Preprocessors With Examples&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://sass-lang.com/&quot;&amp;gt;Sass&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;http://lesscss.org/&quot;&amp;gt;Less&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;CSS Frameworks&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
CSS Frameworks are another way to make the job of writing CSS easier.
They offer predefined styles that you can use on your HTML elements and
make the job of creating a website with a cohesive design a breeze.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://blog.zipboard.co/a-beginners-guide-to-css-front-end-frameworks-8045a499456b&quot;
&amp;gt;A Beginner&apos;s Guide To CSS Frameworks&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/cssframeworks/&quot;
&amp;gt;CSS Frameworks&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Miscellaneous Resources&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=NtRmIp4eMjs&quot;
&amp;gt;CSS Variables In 100 Seconds&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=kpXKwDGtjGE&quot;
&amp;gt;CSS Psuedo-Classes In 100 Seconds&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=e1KpKBHJOrA&quot;
&amp;gt;CSS Psuedo-Elements In 100 Seconds&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://medium.com/@devdevcharlie/things-nobody-ever-taught-me-about-css-5d16be8d5d0e&quot;
&amp;gt;Things Nobody Ever Taught Me About CSS&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://ishadeed.com/article/the-just-in-case-mindset-css/&quot;
&amp;gt;The Just In Case Mindset In CSS&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;!-- JavaScript Resources --&amp;gt;
&amp;lt;h2 id=&quot;javascript-resources&quot;&amp;gt;JavaScript Resources&amp;lt;/h2&amp;gt;
&amp;lt;img
src=&quot;/imgs/javascript.png&quot;
alt=&quot;An illustration of the JavaScript logo.&quot;
/&amp;gt;
&amp;lt;p&amp;gt;
With JavaScript comes the power to start creating truly complex and
robust websites.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
In the skeleton analogy, JavaScript is what brings the skeleton to life
(read: gives the webpage interactive behavior).
&amp;lt;/p&amp;gt;
&amp;lt;img
src=&quot;/imgs/css-to-js.png&quot;
alt=&quot;An illustration of a clothed skeleton with an arrow pointing to a dancing clothed skeleton.&quot;
/&amp;gt;
&amp;lt;p&amp;gt;
You can define behaviors like what happens when you click a button. You
can dynamically create, manipulate, or delete elements on a webpage
based on user interaction. You can make HTTP requests to get data from a
server and so much more.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Getting Started&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Nowadays there seems to be an almost endless number of JavaScript
tutorials out there. Below are my top picks.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
I&apos;ll also take a moment to say that The Modern JavaScript Tutorial has
consistently stood out as an absolutely incredible resource for gaining
a comprehensive understanding of JavaScript.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://javascript.info/&quot;&amp;gt;The Modern JavaScript Tutorial&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥
&amp;lt;a
href=&quot;https://www.youtube.com/watch?v=Sh6lK57Cuk4&amp;amp;list=PL0vfts4VzfNixzfaQWwDUg3W5TRbE7CyI&quot;
&amp;gt;That Weird JavaScript Course –– Fireship.io&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide&quot;
&amp;gt;MDN –– JavaScript Guide&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics&quot;
&amp;gt;MDN –– JavaScript Basics&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript&quot;
&amp;gt;MDN –– A Re-Introduction to JavaScript&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;JavaScript Basics&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;Below are some nice resources that cover various JavaScript basics.&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=Mus_vwhTCq0&quot;
&amp;gt;JavaScript Pro Tips&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=x7Xzvm0iLCI&quot;
&amp;gt;JavaScript Loops&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=YOlr79NaAtQ&quot;
&amp;gt;What Is &apos;this&apos; In JavaScript? In 100 Seconds&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=L8CDt1J3DAw&quot;
&amp;gt;Beyond Console Log In 100 Seconds&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://info340.github.io/javascript.html&quot;
&amp;gt;JavaScript Fundamentals&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://dmitripavlutin.com/gentle-explanation-of-this-in-javascript/&quot;
&amp;gt;A Gentle Explanation Of &quot;this&quot; In JavaScript&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;The DOM&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Understanding the DOM (Document Object Model) is one of the most
important parts of learning client-side JavaScript.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
At a high-level, the DOM is a representation (specifically a tree data
structure) of every element on a webpage that browsers use to keep track
of page changes and rendering. The reason we care about this is because
browsers expose the ability to read and manipulate the DOM using
JavaScript.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
This is how JavaScript has the power to dynamically create, manipulate,
or delete elements on a webpage. The DOM is also the reason that single
page applications or UI libraries like React are possible.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents&quot;
&amp;gt;MDN –– Intro To The DOM&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.digitalocean.com/community/tutorials/how-to-make-changes-to-the-dom&quot;
&amp;gt;How To Make Changes To The DOM&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.digitalocean.com/community/tutorials/how-to-traverse-the-dom&quot;
&amp;gt;How To Traverse The DOM&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.loom.com/share/1fb07561c8984a4f8cbf37d77f969088&quot;
&amp;gt;DOM Tree Video Explanation&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;JavaScript Events&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Events are another very important topic to understand when learning
about client-side JavaScript.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
They enable your website to have dynamic interactivity with a user.
There are many events that you can set up your JavaScript to &quot;listen&quot;
for such as a mouse click, keyboard input, or even if the user has
access to the internet. When one of those events occurs you can define a
piece of code that will be executed as a result.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
The combination of listening for events and then executing code as a
result enables some truly complex behavior in your websites.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events&quot;
&amp;gt;MDN –– Intro To Events&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/eventarch/&quot;
&amp;gt;Event-Driven Application Architecture&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Events&quot;
&amp;gt;DOM Events Reference&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;JSON&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
JSON (JavaScript Object Notation) maintains a special importance in the
world of web development because a majority of the data sent across the
internet is formatted as JSON.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Most modern programming languages have methods for writing/reading
(often referred to as &quot;serializing/deserializing&quot;) JSON which means that
you can have a frontend website written in JavaScript sending data and
communicating with backend server written in something like Go without
any issues.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Below you will find some links for learning more about JSON and its
syntax.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.digitalocean.com/community/tutorials/an-introduction-to-json&quot;
&amp;gt;An Introduction To JSON&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.impressivewebs.com/what-is-json-introduction-guide-for-beginners/&quot;
&amp;gt;What Is JSON? An Introduction and Guide For Beginners&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;AJAX &amp;amp; Fetch&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
On the note of sending data and communicating with servers, AJAX
(Asynchronous JavaScipt and XML) and Fetch are the way that JavaScript
is able to communicate with servers by sending HTTP requests.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
In particular, Fetch offers a very clean way of requesting and receiving
data from a server using promises.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch&quot;
&amp;gt;MDN –– Using Fetch&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX&quot;
&amp;gt;MDN –– AJAX Guide&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Promises &amp;amp; Async/Await&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
In short, promises and async/await allow JavaScript developers to do
asynchronous programming. I&apos;ll let the resources below explain the
benefit of asynchronous programming in more detail, but it&apos;s basically a
way of achieving faster programs using concurrency.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
As an example, a regular synchronous HTTP request will &quot;block&quot; your
JavaScript from running other code until the results of that request are
returned. However, by using promises/async/await your JavaScript can
execute other code while it waits on the HTTP request to be returned.
The ultimate result is that more code is run in the same amount of time
when using promises and async/await.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Additionally, to be even more clear, promises are JavaScript&apos;s way of
handling asynchronous values, while async/await is a cleaner syntax for
working with promises.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=RvYYCGs45L4&quot;
&amp;gt;JavaScript Promise In 100 Seconds&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=vn3tm0quoqE&quot;
&amp;gt;The Async Await Episode I Promised&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise&quot;
&amp;gt;MDN –– Promises&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://codeburst.io/javascript-es-2017-learn-async-await-by-example-48acc58bad65&quot;
&amp;gt;Learn Async/Await By Example&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://codewithhugo.com/async-js/&quot;
&amp;gt;Async JavaScript: Histories, Patterns, and Gotchas&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Introducing&quot;
&amp;gt;MDN –– Intro To Asynchronous JavaScript&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises&quot;
&amp;gt;MDN –– Using Promises&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop&quot;
&amp;gt;MDN –– Concurrency Model And The Event Loop&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Testing &amp;amp; Debugging Resources&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Testing your code is a ridiculously important part of maintaining
healthy codebases and projects. It forces you to think critically about
the edge cases of your programs and also ensures that new
features/updates don&apos;t break something you&apos;ve previously implemented.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Testing frameworks make the job of testing your code much easier and
streamlined and are well worth checking out.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Below are a few resources for diving deeper into the world of JavaScript
testing frameworks, as well as some debugging resources.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.lambdatest.com/blog/top-javascript-automation-testing-framework/&quot;
&amp;gt;Best 8 JavaScript Testing Framework In 2020&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.testcraft.io/front-end-testing/&quot;
&amp;gt;A Comprehensive Guide To Front End Testing&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://info340.github.io/jest.html&quot;&amp;gt;Testing With Jest&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developers.google.com/web/tools/chrome-devtools/javascript&quot;
&amp;gt;Debugging JavaScript In Chrome DevTools&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;JavaScript Modules&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Modules are simply JavaScript files/functions that can be imported into
another JavaScript file/project.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=qgRUr-YUk1Q&quot;
&amp;gt;JavaScript Modules In 100 Seconds&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/&quot;
&amp;gt;ES Modules: A Cartoon Deep Dive&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules&quot;
&amp;gt;MDN –– JavaScript Modules&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=WGlT921ixx4&quot;
&amp;gt;Unbundling The JavaScript Module Bundler&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;JavaScript ES6&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Fun fact, &quot;ECMAScript&quot; is actually the official name of JavaScript. This
is because the committee that defines the language specification of
JavaScript is part of the European Computer Manufacturers Association
(ECMA).
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
In 2015, the ECMA released a new specification for JavaScript that
brought a ton of new features and improvements to the language. The name
of this release was &quot;ECMAScript 2015,&quot; but since it is considered the
sixth version of the language to be released it is commonly referred to
as simply &quot;ES6.&quot;
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Below are a number of resources that cover those changes and
improvements.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://flaviocopes.com/es6/&quot;&amp;gt;The ES6 Guide&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;http://stack.formidable.com/es6-interactive-guide/#/&quot;
&amp;gt;Interactive ES6 Guide&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥 &amp;lt;a href=&quot;https://info340.github.io/es6.html&quot;&amp;gt;ES6 Features&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/es6/&quot;
&amp;gt;ECMAScript6 Features&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://flaviocopes.com/javascript-arrow-functions/&quot;
&amp;gt;Intro To JavaScript Arrow Functions&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Regular Expressions&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Regular expressions (regex for short) are a way of performing complex
pattern matching and are incredibly helpful for validating user input.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
As an example you can use regex to ensure that a user has written a
valid email address in a sign up form.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=sXQxhojSdZM&quot;
&amp;gt;Regular Expressions (Regex) In 100 Seconds&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://regexone.com/&quot;
&amp;gt;RegexOne –– Interactive Regex Guide&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://rubular.com/&quot;
&amp;gt;Rubular –– Regular Expression Tester&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions&quot;
&amp;gt;MDN –– Regular Expressions&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://regexcrossword.com/&quot;&amp;gt;Regex Crossword&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;🔥 JavaScript Libraries&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
JavaScript has become quite famous at this point for its massive
ecosystem of third-party packages/libraries.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Below you will find some of my favorite libraries that I&apos;ve come across.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://info340.github.io/javascript-libraries.html&quot;
&amp;gt;JavaScript Libraries&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
This reading gives a nice introduction to libraries and how to use
them.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://lodash.com/&quot;&amp;gt;Lodash&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
Lodash is an amazing utility library that makes it easier to work
with arrays, numbers, objects, strings, and so on.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://immutable-js.github.io/immutable-js/&quot;
&amp;gt;Immutable.js&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
Immutable is another utility library for immutable data structures.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://blotter.js.org/&quot;&amp;gt;Blotter.js&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
Blotter is a library that provides some super cool and unconvential
text effects.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://animejs.com/&quot;&amp;gt;Anime.js&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
Anime is an &amp;lt;span class=&quot;bold&quot;&amp;gt;incredible&amp;lt;/span&amp;gt; JavaScript
animation library.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://brm.io/matter-js/&quot;&amp;gt;Matter.js&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Matter is a 2D physics engine for the web.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://ptsjs.org/&quot;&amp;gt;Pts.js&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Pts is a really cool visualization library.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://dixonandmoe.com/rellax/&quot;&amp;gt;Rellax&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Rellax is a lightweight parallax library.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://riccardoscalco.it/textures/&quot;&amp;gt;Textures.js&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Textures provides SVG patterns for data visualizations.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;JavaScript 3D Graphics&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
That&apos;s right! You can create 3D graphics using JavaScript. It&apos;s pretty
neat.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Getting_started_with_WebGL&quot;
&amp;gt;MDN –– Getting Started With WebGL&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.awwwards.com/awwwards/collections/webgl/&quot;
&amp;gt;Awwwards WebGL Collection&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://threejs.org/&quot;&amp;gt;Three.js&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.awwwards.com/awwwards/collections/three-js/&quot;
&amp;gt;Awwwards Three.js Collection&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Miscellaneous Resources&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://whatthefuck.is/&quot;&amp;gt;Whatthefuck.is&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
A series of articles written by the creator of Redux (Dan Abramov)
explaining various JavaScript topics/concepts.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://stateofjs.com/&quot;&amp;gt;State of JavaScript Report&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://overapi.com/javascript&quot;&amp;gt;JavaScript Cheat Sheet&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://info340.github.io/functional-programming.html&quot;
&amp;gt;Functional Programming In JS&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://flaviocopes.com/javascript-timers/&quot;
&amp;gt;Discover JavaScript Timers&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/mvc/&quot;
&amp;gt;The Model-View-Controller (MVC) Architecture&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode&quot;
&amp;gt;MDN –– Strict Mode&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation&quot;
&amp;gt;MDN –– Client-Side Form Validation&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain&quot;
&amp;gt;MDN –– Inheritance And The Prototype Chain&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2017/06/a-crash-course-in-memory-management/&quot;
&amp;gt;A Crash Course In Memory Management&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management&quot;
&amp;gt;MDN –– Memory Management&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2 id=&quot;typescript-resources&quot;&amp;gt;TypeScript Resources&amp;lt;/h2&amp;gt;
&amp;lt;img
src=&quot;/imgs/typescript.png&quot;
alt=&quot;An illustration of the TypeScript logo.&quot;
/&amp;gt;
&amp;lt;p&amp;gt;
Moving on from JavaScript we start to enter the territory of languages,
libraries, and tools that are not strictly necessary for creating
frontend websites but sure do make the job of creating them easier.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
One such language is TypeScript. Created by Microsoft, TypeScript is a
&quot;superset of JavaScript&quot; meaning it&apos;s JavaScript with extra features.
Those extra features are namely a type system.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Instead of letting JavaScript determine what types of data (i.e.
numbers, strings, etc.) your variables contain or your functions return,
you explicitly define the data types yourself like you would in a
language such as Java or C.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
The benefit of doing this is that you and larger teams can catch a ton
of small mistakes that could lead to runtime errors. Accidentally passed
a number into a function that should accept a string? No problem,
TypeScript has got your back and will let you know.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Getting Started&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/&quot;
&amp;gt;TypeScript Handbook&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=eCZhz0JCVx0&quot;
&amp;gt;All You Need To Know About TypeScript&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.typescriptlang.org/&quot;&amp;gt;TypeScript Website&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=ahCwqrYpIuM&quot;
&amp;gt;TypeScript –– The Basics&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=O6A-u_FoEX8&quot;
&amp;gt;The Magic Of TypeScript Decorators&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://basarat.gitbook.io/typescript/&quot;
&amp;gt;TypeScript Deep Dive&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Miscellaneous Resources&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.typescriptlang.org/play&quot;
&amp;gt;TypeScript Playground&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.typescriptlang.org/tsconfig&quot;
&amp;gt;TypeScript TSConfig Reference&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.typescriptlang.org/tools&quot;&amp;gt;TypeScript Tools&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://2ality.com/2018/04/type-notation-typescript.html&quot;
&amp;gt;Understanding TypeScripts Type Notations&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/formium/typescript&quot;
&amp;gt;Helpful TypeScript Coding Guidelines&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/&quot;
&amp;gt;Announcing TypeScript 4.0&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2 id=&quot;javascript-framework-resources&quot;&amp;gt;
Frontend JavaScript Frameworks
&amp;lt;/h2&amp;gt;
&amp;lt;img src=&quot;/imgs/react.png&quot; alt=&quot;An illustration of the React logo.&quot; /&amp;gt;
&amp;lt;p&amp;gt;
Another massive topic of frontend web development that is not strictly
needed but is very often used these days is frontend JavaScript
libraries and frameworks.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
These libraries and frameworks make the job of creating and maintaining
complex user interfaces/websites much easier.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Below are some resources focused on general frontend framework
information, as well as the most popular front-end framework in
2020 –– React.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Getting Started&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://blog.logrocket.com/history-of-frontend-frameworks/&quot;
&amp;gt;History of Front-End Frameworks&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://jsreport.io/the-ultimate-guide-to-javascript-frameworks/&quot;
&amp;gt;The Ultimate Guide to JavaScript Frameworks&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks&quot;
&amp;gt;Understanding Client-Side JavaScript Frameworks&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Getting Started With React&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=Tn6-PIqc4UM&quot;
&amp;gt;React In 100 Seconds&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://reactjs.org/tutorial/tutorial.html&quot;
&amp;gt;Official React Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://info340.github.io/react.html&quot;
&amp;gt;Introduction To React&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://github.com/petehunt/react-howto&quot;&amp;gt;React How To&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://ui.dev/react-aha-moments/&quot;&amp;gt;React &apos;Aha&apos; Moments&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://reactjs.org/&quot;&amp;gt;React Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://camjackson.net/post/9-things-every-reactjs-beginner-should-know&quot;
&amp;gt;9 Things Every React Beginner Should Know&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.taniarascia.com/getting-started-with-react/&quot;
&amp;gt;React Tutorial: An Overview and Walkthrough&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://hackernoon.com/simple-react-development-in-2017-113bd563691f&quot;
&amp;gt;Simple React Development In 2019&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;React State Management&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://wattenberger.com/blog/react-hooks&quot;
&amp;gt;Thinking In React Hooks&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://daveceddia.com/visual-guide-to-state-in-react/&quot;
&amp;gt;A Visual Guide To State In React&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://code-cartoons.com/a-cartoon-guide-to-flux-6157355ab207&quot;
&amp;gt;A Cartoon Guide To Flux&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://code-cartoons.com/a-cartoon-intro-to-redux-3afb775501a6&quot;
&amp;gt;A Cartoon Intro To Redux&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;React + TypeScript&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;Why not combine two great technologies? Well you can!&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://fettblog.eu/typescript-react/getting-started/&quot;
&amp;gt;TypeScript and React Guide&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://react-typescript-cheatsheet.netlify.app/&quot;
&amp;gt;React TypeScript Cheatsheets&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://dev.to/busypeoples/notes-on-typescript-pick-exclude-and-higher-order-components-40cp&quot;
&amp;gt;React &amp;amp; TypeScript Series&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://egghead.io/courses/use-typescript-to-develop-react-applications&quot;
&amp;gt;Use TypeScript To Develop React Applications&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Miscellaneous Resources&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://reactrouter.com/&quot;&amp;gt;React Router&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
The go to library for implementing client-side routing in React.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://create-react-app.dev/&quot;&amp;gt;Create React App&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;The go to tool for creating new React applications.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://www.framer.com/motion/&quot;&amp;gt;Framer Motion&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;A library for implementing super clean UI animations.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://nextjs.org/&quot;&amp;gt;Next.js&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;A great library for server-side rendering using React.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://react-dnd.github.io/react-dnd/about&quot;
&amp;gt;React Drag and Drop&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
A library for implementing drag and drop functionality in your React
app.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://formik.org/&quot;&amp;gt;Formik&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;A library for creating and using forms in React.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://medium.com/life-at-paperless/how-to-use-the-react-profiler-component-to-measure-performance-improvements-from-hooks-d43b7092d7a8&quot;
&amp;gt;How To Use React Profiler Component To Measure Render
Performance&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://jasonformat.com/wtf-is-jsx/&quot;&amp;gt;WTF Is JSX?&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://pomb.us/build-your-own-react/&quot;
&amp;gt;Build Your Own React&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://mfrachet.github.io/create-frontend-framework/&quot;
&amp;gt;Build A Frontend Framework From Scratch To Understand How It
Works&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2 id=&quot;client-side-api-resources&quot;&amp;gt;Client-Side APIs&amp;lt;/h2&amp;gt;
&amp;lt;img
src=&quot;/imgs/client-side-apis.png&quot;
alt=&quot;An illustration of a desktop computer connecting with the internet.&quot;
/&amp;gt;
&amp;lt;p&amp;gt;
Application Programming Interfaces (APIs) are the way that different
pieces of software are able to interact with each other.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
As the &quot;What Is An API?&quot; article below describes, an API is like a menu
at a restaurant. When thinking of APIs from a client-side perspective
you are the customer who is ordering food, while comparatively, when
thinking of APIs from a server-side perspective you are the restaurant
making/delivering the food.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
As a client/customer, you don&apos;t have to worry about how the food is
sourced or made. You simply just have to know the phone number of the
restaurant, order a dish that&apos;s on the menu, and it will be delivered to
you.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Translated to the world of programming, it means you can request (read:
order) data or resources such as
&amp;lt;a href=&quot;https://dog.ceo/dog-api/&quot;&amp;gt;pictures of dogs&amp;lt;/a&amp;gt; or
&amp;lt;a href=&quot;https://icanhazdadjoke.com/api&quot;&amp;gt;horrible dad jokes&amp;lt;/a&amp;gt;
from an API (read: menu) that lives on a server (read: restaurant)
somewhere on the internet –– it could even be your own server. When that
data is delivered to your website you can then use it however you
please.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
One final note is that client-side APIs can take three primary forms in
web development: 1) Your Own API, 2) Third-Party APIs, and 3) Browser
APIs.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Getting Started&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://www.leverege.com/blogpost/what-is-an-api&quot;
&amp;gt;What Is An API? –– A Simple, Non-Technical Explanation&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs&quot;
&amp;gt;MDN –– Client-Side Web APIs&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Your Own API&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Creating your own APIs is essentially what server-side development
is/can be boiled down to. Thus resources and discussion on the topic can
be found in the sections below.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Third-Party API Resources&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Third-Party APIs are APIs created by someone else (usually a company).
The relationship to those APIs is exactly as described above where you
are the customer ordering data or resources that will be delivered to
your website.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs&quot;
&amp;gt;MDN –– Third-Party APIs&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://apilist.fun/&quot;&amp;gt;API List&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;A public list of free third-party APIs&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://rapidapi.com/&quot;&amp;gt;API Marketplace&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Another public list of free third-party APIs&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Browser API Resources&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Browser APIs are a little bit different (and a little bit cooler in my
opinion). The same customer-restaurant relationship can be used, but the
restaurant location has changed. Rather than being located at some
far-away server, the restaurant is the web browser itself (i.e. Chrome
or Firefox).
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
This new type of restaurant also serves slightly different and slightly
cooler menu items. There are browser APIs for using
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API&quot;
&amp;gt;Bluetooth&amp;lt;/a
&amp;gt;, checking
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Ambient_Light_Events&quot;
&amp;gt;ambient light levels&amp;lt;/a
&amp;gt;, connecting
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API&quot;
&amp;gt;game controllers&amp;lt;/a
&amp;gt;,
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/MediaStream_Image_Capture_API&quot;
&amp;gt;capturing photos or videos&amp;lt;/a
&amp;gt;, and many more options.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://blog.greenroots.info/10-lesser-known-web-apis-you-may-want-to-use-ckejv75cr012y70s158n85yhn&quot;
&amp;gt;10 Lesser-Known Web APIs You May Want To Use&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction&quot;
&amp;gt;MDN –– Introduction To Web APIs&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Guide/API&quot;
&amp;gt;MDN –– Guide To Web APIs&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Reference/API&quot;
&amp;gt;MDN –– Web API Reference&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developer.chrome.com/apps/api_index&quot;
&amp;gt;Chrome APIs List&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developer.chrome.com/apps/api_other&quot;
&amp;gt;Chrome Web APIs&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2 id=&quot;webassembly-resources&quot;&amp;gt;WebAssembly Resources&amp;lt;/h2&amp;gt;
&amp;lt;img
src=&quot;/imgs/webassembly.png&quot;
alt=&quot;An illustration of the WebAssembly logo.&quot;
/&amp;gt;
&amp;lt;p&amp;gt;
At the absolute cutting edge of frontend web technologies is one of my
favorite topics to get lost in during the last year or
so –– WebAssembly.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
WebAssembly is a low-level binary format/assembly-like language that
runs in the browser –– in fact as of December 2019 it is
&amp;lt;a href=&quot;https://www.w3.org/2019/12/pressrelease-wasm-rec.html.en&quot;
&amp;gt;the 4th official language of the web&amp;lt;/a
&amp;gt;.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
By writing programs in languages like C/C++, Rust, Go, and
&amp;lt;a href=&quot;https://webassembly.org/getting-started/developers-guide/&quot;
&amp;gt;others&amp;lt;/a
&amp;gt;
that can compile to WebAssembly the internet could eventually be capable
of running photo/video editing software, AAA video games, and
&amp;lt;a href=&quot;https://webassembly.org/docs/use-cases/&quot;&amp;gt;much more&amp;lt;/a&amp;gt;
right inside a browser.
&amp;lt;/p&amp;gt;
&amp;lt;p class=&quot;italic&quot;&amp;gt;
Note: Even though WebAssembly is the 4th official language of the web it
is not supposed to be a language that will replace JavaScript but will
rather work alongside it.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Getting Started&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;Read a Cartoon Intro To WebAssembly. Just read it.&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2017/02/a-cartoon-intro-to-webassembly/&quot;
&amp;gt;A Cartoon Intro To WebAssembly [Article Series]&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=HktWin_LPf4&quot;
&amp;gt;A Cartoon Intro To WebAssembly [YouTube]&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/WebAssembly&quot;
&amp;gt;MDN Guide To WebAssembly&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://webassembly.org/&quot;&amp;gt;WebAssembly Website&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/WebAssembly&quot;&amp;gt;WebAssembly GitHub&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://flaviocopes.com/webassembly/&quot;
&amp;gt;Flavio Copes: An Introduction To WebAssembly&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://rsms.me/wasm-intro&quot;&amp;gt;Introduction To WebAssembly&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
This introduction to WebAssembly is more technical than the others.
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://codelabs.developers.google.com/codelabs/web-assembly-intro/index.html&quot;
&amp;gt;Google Codelabs: An Introduction To WebAssembly&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;How WebAssembly Is Being Used&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Since WebAssembly is still in its infancy, the use cases for when to use
it are actively being explored. Watching and reading about what other
companies are doing with WebAssembly can be a great way to start
envisioning what&apos;s possible.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=BnYq7JapeDA&quot;
&amp;gt;How AutoCAD Used WebAssembly&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://www.figma.com/blog/webassembly-cut-figmas-load-time-by-3x/&quot;
&amp;gt;WebAssembly Cut Figma&apos;s Load Time By 3X&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Diving Deeper Into WebAssembly&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
These are all fantastic articles and videos that explore the various
facets and histories of WebAssembly.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2018/10/webassemblys-post-mvp-future/&quot;
&amp;gt;WebAssembly&apos;s Post-MVP Future: A Cartoon Skill Tree&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=MaJCfdmr9Wg&quot;
&amp;gt;WebAssembly: A Game Changer For The Web&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=6r0NKEQqkz0&quot;
&amp;gt;The History Of WebAssembly&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=7mBf3Gig9io&quot;
&amp;gt;WebAssembly: Disrupting JavaScript&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2018/06/babys-first-rustwebassembly-module-say-hi-to-jsconf-eu/&quot;
&amp;gt;Baby&apos;s First WebAssembly Module&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2017/07/creating-a-webassembly-module-instance-with-javascript/&quot;
&amp;gt;Creating A WebAssmebly Module Instance In JavaScript&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2017/07/memory-in-webassembly-and-why-its-safer-than-you-think/&quot;
&amp;gt;Memory In WebAssembly (And Why It&apos;s Safer Than You Think)&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2018/10/calls-between-javascript-and-webassembly-are-finally-fast-%F0%9F%8E%89/&quot;
&amp;gt;Calls Between JS and WASM Are Finally Fast&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2018/01/making-webassembly-even-faster-firefoxs-new-streaming-and-tiering-compiler/&quot;
&amp;gt;Making WebAssembly Even Faster&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;WebAssembly + Rust&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
During the last few years, Rust has positioned itself to be one of the
primary languages to use for developing WebAssembly modules.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Some incredible tools and libraries have been created to support the
Rust/WebAssembly ecosystem. There is even a WebAssembly working group
that is actively working to make Rust&apos;s support of WebAssembly even
better.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
As a final note, a great place to start is the Rust and WebAssembly book
that covers all the major aspects of Rust/WebAssembly development.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://rustwasm.github.io/docs/book/&quot;
&amp;gt;Rust and WebAssembly Book&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://rustwasm.github.io/&quot;&amp;gt;Rust and WebAssembly Website&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2018/03/making-webassembly-better-for-rust-for-all-languages/&quot;
&amp;gt;Making WebAssembly Better For Rust &amp;amp; For All Languages&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2018/04/javascript-to-rust-and-back-again-a-wasm-bindgen-tale/&quot;
&amp;gt;JavaScript to Rust and Back Again: A wasm-bindgen Tale&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://rustwasm.github.io/docs/wasm-bindgen/&quot;
&amp;gt;The wasm-bindgen Guide&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://rustwasm.github.io/docs/wasm-pack/&quot;
&amp;gt;The wasm-pack Book&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://rustwasm.github.io/wasm-pack/&quot;&amp;gt;wasm-pack Website&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/rustwasm/wasm-pack&quot;&amp;gt;wasm-pack GitHub&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://rustwasm.github.io/2018/09/26/announcing-web-sys.html&quot;
&amp;gt;Web-sys Crate Announcement&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;WebAssembly + TypeScript&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
For those who don&apos;t want to go off and learn Rust you can still take
advantage of WebAssembly by using TypeScript! Kind of.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
AssemblyScript is a strict variant of TypeScript that can be used to
write WebAssembly modules. It means you can use your knowledge of
JavaScript to gain some of the powers of WebAssembly.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
And just like Rust, there is a great guide/documentation that covers how
to get up and running.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.assemblyscript.org/introduction.html&quot;
&amp;gt;AssemblyScript Documentation&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.assemblyscript.org/&quot;&amp;gt;AssemblyScript Website&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/AssemblyScript/assemblyscript&quot;
&amp;gt;AssemblyScript GitHub&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;WebAssembly Runtimes&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Even though WebAssembly is predominantly discussed in the context of the
web it does not mean it is restricted to it. In the same way that
JavaScript was able to move off the web with JavaScript runtimes like
Node.js and Deno, so to can WebAssembly.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Currently, there is active and evolving development on two primary
WebAssembly runtimes called Wasmtime and Wasmer.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://wasmtime.dev/&quot;&amp;gt;Wasmtime Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://bytecodealliance.github.io/wasmtime/introduction.html&quot;
&amp;gt;Wasmtime Guide&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/bytecodealliance/wasmtime&quot;
&amp;gt;Wasmtime GitHub&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/bytecodealliance/wasmtime-demos&quot;
&amp;gt;Wasmtime Demos&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://wasmer.io/&quot;&amp;gt;Wasmer Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://webassembly.sh/&quot;&amp;gt;WebAssembly.sh&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2019/09/debugging-webassembly-outside-of-the-browser/&quot;
&amp;gt;Debugging WebAssembly Outside The Browser&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;WebAssembly System Interface (WASI)&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;Taken from WASI&apos;s introductory document, it is:&amp;lt;/p&amp;gt;
&amp;lt;p class=&quot;italic&quot;&amp;gt;
&quot;An API designed by the Wasmtime project that provides access to several
operating-system-like features, including files and filesystems,
Berkeley sockets, clocks, and random numbers.&quot;
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
The goal is for this API to be standardized and designed to work
independently of browsers and JavaScript.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-intro.md&quot;
&amp;gt;WASI Intro Document&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://wasi.dev/&quot;&amp;gt;WASI Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2019/08/webassembly-interface-types/&quot;
&amp;gt;WebAssembly Interface Types&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/&quot;
&amp;gt;Standardizing WASI&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-background.md&quot;
&amp;gt;WASI Background&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-documents.md&quot;
&amp;gt;WASI Document Guide&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Miscellaneous Resources&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://github.com/mbasso/awesome-wasm&quot;
&amp;gt;Awesome WASM GitHub&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://webassembly.studio/&quot;&amp;gt;WebAssembly Studio&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://blog.scottlogic.com/2019/05/17/webassembly-compiler.html&quot;
&amp;gt;Build Your Own WebAssembly Compiler In TypeScript&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://hacks.mozilla.org/2019/11/announcing-the-bytecode-alliance/&quot;
&amp;gt;Announcing The Bytecode Alliance&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://bytecodealliance.org/&quot;&amp;gt;Bytecode Alliance Website&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/bytecodealliance&quot;
&amp;gt;Bytecode Alliance GitHub&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developers.google.com/web/updates/2019/12/webassembly&quot;
&amp;gt;WebAssembly Debugging In Chrome DevTools&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://webassembly.github.io/spec/core/index.html&quot;
&amp;gt;WebAssembly Specification&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.w3.org/community/webassembly/&quot;
&amp;gt;W3C WebAssembly Community&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.w3.org/wasm/&quot;&amp;gt;W3C WebAssembly Working Group&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.w3.org/TR/wasm-core-1/&quot;
&amp;gt;W3C WebAssembly Core Spec&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://webassembly.github.io/spec/js-api/index.html&quot;
&amp;gt;W3C WebAssembly JavaScript Interface&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- Server-Side Programming --&amp;gt;
&amp;lt;section&amp;gt;
&amp;lt;h2 id=&quot;server-side-resources&quot;&amp;gt;Server-Side / Backend Programming&amp;lt;/h2&amp;gt;
&amp;lt;img
src=&quot;/imgs/server-side-resources.png&quot;
alt=&quot;An illustration of a server icon.&quot;
/&amp;gt;
&amp;lt;p class=&quot;italic&quot;&amp;gt;
In a sense, server-side programming could be described as the job of
designing and implementing APIs.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
As stated earlier, Application Programming Interfaces (&amp;lt;a
href=&quot;https://www.youtube.com/watch?v=JjXBrJfp5TE&quot;
&amp;gt;APIs&amp;lt;/a
&amp;gt;) are the way that different pieces of software are able to interact
with each other.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Continuing the earlier customer-restaurant analogy, as a restaurant you
are exclusively preoccupied with taking customer orders, sourcing and
making food, and delivering it back to the customer. Another really
important component of this job is designing a menu (read: API) that is
clear and best serves the customer.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Translated to the world of programming, it means at its core server-side
programming is comprised of:
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Accepting HTTP requests (read: accept orders)&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
Processing the requests and finding/creating the data or resources
that were requested (read: make the food)
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
Sending the data/resources back to the client (read: deliver the
order)
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;p&amp;gt;
With all this being said, this analogy breaks down in a couple of ways
because for starters a client does not always ask for data/resources to
be returned to them. Rather, they might also make a request to add,
update, or delete data stored on the server-side (such as adding a
profile picture, updating a credit card number, or deleting a user
account).
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
In addition to that, the model I&apos;ve described above (that of getting,
adding, updating, and deleting data/resources) is based on a form of
internet communication called HTTP (more details below). HTTP is the
predominant method of communication on the internet but it does not mean
it&apos;s the only one.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Getting Started&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
These are some great resources to get a better understanding of what
server-side programming is and what it entails.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/&quot;
&amp;gt;Dave Stearns Server-Side Web Development Tutorials&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_web_server&quot;
&amp;gt;MDN –– What Is A Web Server&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Server-side/First_steps/Introduction&quot;
&amp;gt;MDN –– Introduction To The Serverside&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Server-side/First_steps/Client-Server_overview&quot;
&amp;gt;MDN –– Client-Server Overview&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;How To Communicate On The Internet&amp;lt;/h3&amp;gt;
&amp;lt;img
src=&quot;/imgs/server-side-apis.png&quot;
alt=&quot;An illustration of a server communicating with the internet.&quot;
/&amp;gt;
&amp;lt;p&amp;gt;
Thus far when describing how APIs work we&apos;ve focused on how the API is a
menu that one piece of software can use to determine what another piece
of software offers/provides.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
What we haven&apos;t focused on is how these two pieces of software
communicate with each other. To reference the analogy from client-side
APIs, I said the frontend would use a phone number to call/message the
restaurant to tell them its order. But this does not mean that
calling/messaging is the only way to communicate with the
restaurant –– the same holds true for the internet.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
The defacto way of communicating on the internet is HTTP/HTTPS
(Hyper-Text Transfer Protocol [Secure]). Understanding this
communication protocol and how to work with it is crucial for
server-side development. In particular, learning about the meaning of
HTTP functions like GET, POST, PUT, and DELETE will be very helpful as
you dive into learning and using server-side languages/frameworks.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
For the majority of web development use cases, you only need to worry
about HTTP, but as you continue your backend journey you&apos;ll discover
other communication protocols like WebSockets, MQTT, SMTP, XMPP, and
more.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/http/&quot;
&amp;gt;The Hyper-Text Transfer Protocol&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP&quot;
&amp;gt;MDN –– HTTP&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/https/&quot;
&amp;gt;Communicating Securely With HTTPS&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.ably.io/concepts&quot;
&amp;gt;Real-Time Messaging Concepts/Protocols&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API&quot;
&amp;gt;MDN –– WebSockets APIs&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Testing / Debugging Your APIs&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Testing and debugging your APIs is a critical part of the server-side
development process and lucky for you (and I) there are some great tools
created for doing just that.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://hoppscotch.io/&quot;&amp;gt;Hoppscotch&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://www.postman.com/&quot;&amp;gt;Postman&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://insomnia.rest/&quot;&amp;gt;Insomnia&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;API Design&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
As I stated earlier the design of an API (read: menu) is also a
critically important job as it can help the client (read: customer) be
confident that they are requesting the correct data/resources/actions.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
The predominant way of designing APIs in 2020 is to follow the REST
pattern and is well worth learning.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/rest/&quot;
&amp;gt;Designing APIs According To A RESTful Pattern&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://bocoup.com/blog/documenting-your-api&quot;
&amp;gt;REST API Documentation Best Practices&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.restapitutorial.com/&quot;&amp;gt;REST API Tutorial&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/RestCheatSheet/api-cheat-sheet&quot;
&amp;gt;API Design Cheat Sheet&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm&quot;
&amp;gt;The Original REST Paper&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Server-Side Architectures&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
As the size and complexity of your server-side applications grow (as
well as how many users your application has), how you organize or
architect the various components will become a greater and greater
concern.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
In 2020, a hugely popular architecture is that of microservices. The
idea of microservices is to break up your server-side application into
small connected logical units using technologies like Docker (resources
can be found in the Cloud section) so that your application is easier to
maintain and can scale to many more users.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥
&amp;lt;a href=&quot;https://www.nginx.com/blog/introduction-to-microservices/&quot;
&amp;gt;Introduction to Microservices Series&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://martinfowler.com/articles/microservices.html&quot;
&amp;gt;Another Microservices Series&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/RestCheatSheet/microservices-cheat-sheet&quot;
&amp;gt;Microservice Cheat Sheet&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/RestCheatSheet/platform-cheat-sheet&quot;
&amp;gt;Platform-Building Cheat Sheet&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://engineering.videoblocks.com/web-architecture-101-a3224e126947&quot;
&amp;gt;Web Architecture 101&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Miscellaneous Resources&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Server-side&quot;
&amp;gt;MDN––Server-side Website Programming&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Server-side/First_steps/Web_frameworks&quot;
&amp;gt;MDN –– Guide To Server-Side Frameworks&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/cors/&quot;
&amp;gt;Cross-Origin Resource Sharing Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.codecademy.com/articles/what-is-cors&quot;
&amp;gt;What Is CORS?&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/env/&quot;
&amp;gt;Environment Variables&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/sessions/&quot;
&amp;gt;Tracking Sessions&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/authentication/&quot;
&amp;gt;Authenticating Users&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2&amp;gt;Server-Side Programming Languages&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;
The next couple of sections will be dedicated to a handful of
programming languages you can use on the server-side. Over time you will
discover the same high-level ideas, goals, and strategies of server-side
programming are generally used no matter what language and server-side
framework you choose.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
As a result, picking a server-side language just comes down to a matter
of the language/framework itself and what kinds of benefits and
drawbacks they offer.
&amp;lt;/p&amp;gt;
&amp;lt;h2 id=&quot;javascript-runtime-resources&quot;&amp;gt;JavaScript Runtimes&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;
Starting off with what I think is a natural transition from frontend to
backend programming is using JavaScript on the server!
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
From its origins as a scripting language for Netscape Navigator (a very
old web browser), JavaScript could only be interpreted and run in a
browser.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
However, when creating server-side programs you need code that can run
on a computer (a server is essentially just a specialized computer for
handling network requests). Lucky for you, there are pieces of software
called JavaScript runtimes to do just that.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
These runtimes allow you to run JavaScript on a computer like a regular
Java or Python program, while also giving extra functionality such as
accessing a computer&apos;s file system and responding to network
requests––creating the foundation for writing JavaScript that runs on a
server.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
The most famous and widely used JavaScript runtime is Node.js. However,
a new runtime called Deno (created by the same person who invented
Node.js) is also starting to pick up steam and capture attention.
&amp;lt;/p&amp;gt;
&amp;lt;h2&amp;gt;Node.js Resources&amp;lt;/h2&amp;gt;
&amp;lt;img src=&quot;/imgs/node-js.png&quot; alt=&quot;An illustration of the Node.js logo.&quot; /&amp;gt;
&amp;lt;p&amp;gt;
As stated above, Node.js (also sometimes referred to as &quot;Node&quot;) is
easily the most famous and widely used JavaScript runtime for executing
JS code outside a browser.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Getting Started&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
These are some great resources to get oriented with Node.js and dive
deeper into its core topics. The Introduction to Node.js tutorials are
one of the primary sources I used to learn Node and are well worth a
read.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://nodejs.dev/learn&quot;&amp;gt;Introduction to Node.js&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=ENrzD9HAZK4&quot;
&amp;gt;Node.js Ultimate Beginner&apos;s Guide In 7 Easy Steps&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://nodejs.dev/&quot;&amp;gt;Nodejs.dev Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://nodejs.org/en/&quot;&amp;gt;Nodejs.org Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://nodejs.org/dist/latest-v14.x/docs/api/&quot;
&amp;gt;Node Documentation&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.freecodecamp.org/news/the-definitive-node-js-handbook-6912378afc6e/?source=linkShare-53f0cd20ca9c-1537896927&quot;
&amp;gt;The Definitive Node.js Handbook&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.tutorialspoint.com/nodejs/index.htm&quot;
&amp;gt;Node.js Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.toptal.com/nodejs/why-the-hell-would-i-use-node-js&quot;
&amp;gt;Why The Hell Would I Use Node.js? A Case By Case Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Node.js Development Environment&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Setting up a Node.js development environment is actually fairly
straightforward since all the primary tools you need (i.e. the Node.js
runtime and the Node Package Manager) are both downloaded when you
install Node.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
The only other useful piece of a Node developer environment would be to
use an IDE or a text editor extension for working with Node. My
recommendation is to use VS Code as it already comes with an incredible
JavaScript/TypeScript/Node extension built-in.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.tutorialsteacher.com/nodejs/setup-nodejs-development-environment&quot;
&amp;gt;Setup Node.js Development Environment&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment&quot;
&amp;gt;Setting Up A Node.js Development Environment&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://cloud.google.com/nodejs/docs/setup&quot;
&amp;gt;Node.js Development Environment Setup&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Node.js Package Management&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
A huge benefit to using Node.js is its
&amp;lt;a
href=&quot;https://snyk.io/blog/npm-passes-the-1-millionth-package-milestone-what-can-we-learn/&quot;
&amp;gt;insanely large&amp;lt;/a
&amp;gt;
ecosystem of third-party packages you can use (i.e. pieces of Node
software written by someone else that you can use in your own Node
projects).
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
A piece of software called the Node Package Manager (NPM) is installed
when you install the Node.js runtime and is the mechanism for including
and tracking the packages your Node projects use.
&amp;lt;/p&amp;gt;
&amp;lt;p class=&quot;italic&quot;&amp;gt;
Note: Another popular Node.js package management tool that you&apos;ll often
see is called Yarn.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.sitepoint.com/npm-node-package-manager/&quot;
&amp;gt;Beginner&apos;s Guide To NPM&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://www.npmjs.com/&quot;&amp;gt;NPM Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://www.npmtrends.com/&quot;&amp;gt;NPM Trends&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/g-plane/tiny-package-manager&quot;
&amp;gt;Tiny-Package-Manager: Learn How NPM Or Yarn Works&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Node.js Web Frameworks&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Just as you can use frameworks in client-side development to make the
job of creating websites easier, so to can you use server-side
frameworks to make the job of creating server-side applications easier.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
There are many Node.js frameworks to choose from, but easily one of the
most well known and well-used frameworks is Express.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://flaviocopes.com/express/&quot;&amp;gt;Introduction To Express&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://expressjs.com/&quot;&amp;gt;Express Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs&quot;
&amp;gt;MDN –– Express / Node Guide&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;http://expressjs.com/en/guide/using-middleware.html&quot;
&amp;gt;Using Express Middleware&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Miscellaneous Resources&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/nodeweb/&quot;
&amp;gt;Node.js Microservices&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://nodejs.org/en/docs/guides/debugging-getting-started/&quot;
&amp;gt;Debugging Node.js Programs&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.codementor.io/@ziad-saab/let-s-code-a-web-server-from-scratch-with-nodejs-streams-h4uc9utji&quot;
&amp;gt;Let&apos;s Code A Web Server From Scratch With Node.js Streams&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://codeburst.io/building-a-simple-single-sign-on-sso-server-and-solution-from-scratch-in-node-js-ea6ee5fdf340&quot;
&amp;gt;Building A Simple Single Sign On (SSO) Server From Scratch&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/antsmartian/lets-build-express&quot;
&amp;gt;Let&apos;s Build Express&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2&amp;gt;Deno Resources&amp;lt;/h2&amp;gt;
&amp;lt;img src=&quot;/imgs/deno.png&quot; alt=&quot;An illustration of the Deno logo.&quot; /&amp;gt;
&amp;lt;p&amp;gt;
Deno is a new JavaScript runtime, created by the same person who made
Node (Ryan Dahl). The runtime builds on the learning/takeaways Ryan had
from building Node and as a result, includes some pretty great features
when compared to Node.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Some of these features include:&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Deno has much better security built into it&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;It supports both JavaScript and TypeScript&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
It has a pretty incredible set of built-in tools inspired by the Go
language
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
It has a high-quality audited standard library also inspired by the Go
language
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;p&amp;gt;
As a final note, don&apos;t worry about needing to learn Deno right now in
2020. Node is not going anywhere and will likely remain the dominant
JavaScript runtime for many years to come. I&apos;d rather suggest casual
exploration when approaching Deno –– it has some seriously cool features
that can be fun to play around with. Plus, if/when it reaches industry
dominance you&apos;ll already be slightly ahead of everyone else 😉.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Getting Started&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
When diving into Deno I highly recommend watching Ryan Dahl&apos;s talk on
the 10 things he regrets about Node.js. It gives some great insight and
context for the goals and design choices of Deno.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Beyond that, the Deno Manual is a wonderfully written resource for
getting started with Deno.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥🔥🔥 &amp;lt;a href=&quot;https://deno.land/manual&quot;&amp;gt;The Deno Manual&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=M3BM9TB-8yA&quot;
&amp;gt;10 Things I Regret About Node.js - Ryan Dahl Talk&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=F0G9lZ7gecE&quot;
&amp;gt;Deno In 100 Seconds&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🔥🔥 &amp;lt;a href=&quot;https://deno.land/&quot;&amp;gt;Deno Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://blog.logrocket.com/deno-1-0-what-you-need-to-know/&quot;
&amp;gt;Deno 1.0: What You Need To Know&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://dev.to/kryz/write-a-small-api-using-deno-1cl0&quot;
&amp;gt;Write A Small API Using Deno&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Deno Development Environment&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
This is the official setup guide found in the Deno Manual. It covers all
the bases in a well written and clear way. It&apos;s all you should need.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://deno.land/manual/getting_started/setup_your_environment&quot;
&amp;gt;Set Up Your Deno Environment&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Deno Package Management&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Deno takes a very different approach to package management compared to
Node. Rather than using NPM to install and track dependencies, you
import URLs pointing to the third-party modules you want to use right
within any given JavaScript/TypeScript file you&apos;re creating. When your
code is executed the modules located at the given URLs will be
automatically fetched and installed.
&amp;lt;/p&amp;gt;
&amp;lt;p class=&quot;italic&quot;&amp;gt;
Note: These modules are only downloaded and installed the first time
your code is run. After that, the modules are cached (read: saved
locally on your computer) so that the code can be re-executed many times
without the worry of having to fetch and install the modules every
single time––which could take forever on slow networks.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Modules can be hosted anywhere on the web such as GitHub, a personal web
server, or a CDN (content distribution network). Below are some links to
Deno&apos;s own third-party module hosting service and other popular CDNs
where Deno modules can be found.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
As an additional aside, this way of approaching package management is
once again a design decision inspired by the Go language and is, in my
opinion, a very welcome and super clean solution.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://deno.land/x&quot;&amp;gt;Deno.land/x&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://www.skypack.dev/&quot;&amp;gt;Skypack&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://jspm.org/&quot;&amp;gt;JSPM&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Deno Web Frameworks&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Since Deno is such a new runtime the landscape of popular frameworks and
libraries is still evolving. Oak seems to be gathering some popularity
since it takes a lot of design inspiration from Express and could be
worth checking out.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
That being said, I also left a link to the &quot;Modules&quot; section of the
Awesome Deno GitHub where you can find links to other web frameworks as
well.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://oakserver.github.io/oak/&quot;&amp;gt;Oak Website&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://github.com/oakserver/oak&quot;&amp;gt;Oak GitHub&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/denolib/awesome-deno#modules&quot;
&amp;gt;Other Web Frameworks&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Miscellaneous Resources&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/denolib/awesome-deno&quot;&amp;gt;Awesome Deno&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/denoland/deno&quot;&amp;gt;Deno GitHub&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://deno.land/posts&quot;&amp;gt;Deno News&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://deno.land/std@0.66.0&quot;&amp;gt;Deno Standard Library&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://doc.deno.land/builtin/stable&quot;&amp;gt;Deno Runtime API&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://doc.deno.land/&quot;&amp;gt;Deno Doc&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://github.com/denoland/deno/blob/master/docs/contributing/style_guide.md&quot;
&amp;gt;Deno Style Guide&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/denosaurs/denon&quot;&amp;gt;Denon GitHub&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;This is analogous to Node&apos;s nodemon package.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2 id=&quot;python-resources&quot;&amp;gt;Python Resources&amp;lt;/h2&amp;gt;
&amp;lt;img src=&quot;/imgs/python.png&quot; alt=&quot;An illustration of the Python logo.&quot; /&amp;gt;
&amp;lt;p&amp;gt;
For those who are not as familiar with Python, it is a very high-level
general-purpose programming language.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
A huge goal of Python is readability and results in a language that can
accomplish a lot with very little code.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Like Node, Python also has a massive ecosystem of third-party modules.
When combined with the fact that Python is a general-purpose language it
means that you can write high-quality code for many areas of computer
science such as data science, machine learning, scientific computing,
IoT, and (importantly for this guide) web development.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Getting Started&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
The official Python tutorial was my primary method of learning the
syntax and ideas behind Python and is worth reading through (even if
casually).
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Beyond that, I&apos;ve included some other great resources I&apos;ve used to pick
up bits and pieces of Python info.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://docs.python.org/3/tutorial/index.html&quot;
&amp;gt;The Official Python Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://wiki.python.org/moin/BeginnersGuide/Programmers&quot;
&amp;gt;Python Wiki Beginner&apos;s Guide&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://medium.com/quick-code/advanced-python-made-easy-eece317334fa&quot;
&amp;gt;Advanced Python Made Easy&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://docs.python-guide.org/&quot;
&amp;gt;The Hitchhiker&apos;s Guide To Python&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.fullstackpython.com/flask.html&quot;
&amp;gt;Full Stack Python&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Python Development Environment&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
One of the downsides of Python, in my opinion, is that setting up a
development environment is not as trivial as one might hope. There are a
lot of ways that a development environment can be set up and truth be
told I&apos;m not even completely confident in the one I have––it&apos;s still a
work in progress.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
That being said the resources below are some of the most helpful ones
I&apos;ve come across for learning about the various ways that one can
approach a Python development environment for web development.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://jacobian.org/2019/nov/11/python-environment-2020/#atom-entries&quot;
&amp;gt;Python Development Environment, 2020 Edition&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://medium.com/@briantorresgil/definitive-guide-to-python-on-mac-osx-65acd8d969d0&quot;
&amp;gt;Definitive Guide To Python On MacOS&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://code.visualstudio.com/docs/python/python-tutorial&quot;
&amp;gt;Getting Started With Python In VS Code&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.digitalocean.com/community/tutorials/how-to-install-python-3-and-set-up-a-local-programming-environment-on-macos&quot;
&amp;gt;How To Install Python 3 and Set Up a Local Programming Environment
On MacOS&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://realpython.com/python-virtual-environments-a-primer/&quot;
&amp;gt;Python Virtual Environments: A Primer&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://towardsdatascience.com/comparing-python-virtual-environment-tools-9a6543643a44&quot;
&amp;gt;Comparing Python Virtual Environment Tools&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://medium.com/@grassfedcode/goodbye-virtual-environments-b9f8115bc2b6&quot;
&amp;gt;Goodbye Virtual Environments?&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.kevinpeters.net/auto-formatters-for-python&quot;
&amp;gt;Auto Formatters For Python&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/psf/black&quot;&amp;gt;Black Formatter&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Python Package Management&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Another big downside of Python is its disjointed package management
ecosystem. There are a lot of tools trying to solve the same problem
with no community consensus on what is best.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
The result, at least for me, has been a lot of reading, testing, and
general uncertainty about what fits my workflow the best. My current
favorite is called Poetry and is very similar to NPM.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥🔥 &amp;lt;a href=&quot;https://python-poetry.org/&quot;&amp;gt;Poetry Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥
&amp;lt;a
href=&quot;https://www.pythoncheatsheet.org/blog/python-projects-with-poetry-and-vscode-part-1/&quot;
&amp;gt;Python Projects With Poetry Series&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://johnfraney.ca/posts/2019/05/28/create-publish-python-package-poetry/&quot;
&amp;gt;Create and Publish a Python Package With Poetry&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://codingdose.info/2019/06/16/develop-and-publish-with-poetry/&quot;
&amp;gt;Develop and Publish Your Python Package With Poetry&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;http://andrewsforge.com/article/python-new-package-landscape/&quot;
&amp;gt;Python&apos;s New Package Landscape&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://hynek.me/articles/python-app-deps-2018/&quot;
&amp;gt;Python Application Dependency Management in 2018&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://johnfraney.ca/posts/2019/03/06/pipenv-poetry-benchmarks-ergonomics/&quot;
&amp;gt;Pipenv and Poetry: Benchmarks and Ergonomics&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://johnfraney.ca/posts/2019/11/19/pipenv-poetry-benchmarks-ergonomics-2/&quot;
&amp;gt;Pipenv and Poetry: Benchmarks and Ergonomics II&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://blog.ionelmc.ro/2014/05/25/python-packaging/&quot;
&amp;gt;Packaging a Python Library&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://medium.com/hackernoon/reaching-python-development-nirvana-bb5692adf30c&quot;
&amp;gt;Why You Should Use pyenv + Pipenv For Your Python Projects&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://pythonspeed.com/articles/pipenv-docker/&quot;
&amp;gt;Faster Docker Builds With Pipenv, Poetry, or pip-tools&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://gist.github.com/pandafulmanda/730a9355e088a9970b18275cb9eadef3&quot;
&amp;gt;Python3 Virtualenv Setup&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://virtualenv.pypa.io/en/stable/&quot;
&amp;gt;Virtualenv Documentation&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Python Web Frameworks&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
From my knowledge and experience, the two big web frameworks for Python
are Flask and Django. Flask is a more lightweight framework that gives
you the flexibility to implement whatever you might need but at the cost
of writing more code.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Comparatively, Django is a heavier framework that gives you a lot of
prebuilt software for things such as user authentication. This means you
can write less code but at the cost of less flexibility if you need to
create something non-traditional or more customized to your needs.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
I&apos;ve only worked with Flask at this point, so those are the resources
that will be linked below.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://flask.palletsprojects.com/en/1.1.x/&quot;
&amp;gt;Flask Website&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://codeburst.io/flask-for-dummies-a-beginners-guide-to-flask-part-uno-53aec6afc5b1&quot;
&amp;gt;Flask For Dummies&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world&quot;
&amp;gt;The Flask Mega Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://dev.to/dbanty/flask-rest-api-introduction-la4&quot;
&amp;gt;Flask REST API Series&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://pythonhow.com/how-a-flask-app-works/&quot;
&amp;gt;How A Flask App Works&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Miscellaneous Resources&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://realpython.com/&quot;&amp;gt;Real Python Tutorials&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://www.pythoncheatsheet.org/&quot;&amp;gt;Python Cheat Sheet&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://mathtocode.com/&quot;&amp;gt;Math To Code&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;An interactive NumPy tutorial&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.learnpython.org/&quot;&amp;gt;LearnPython.org&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://towardsdatascience.com/whats-init-for-me-d70a312da583&quot;
&amp;gt;What&apos;s &lt;strong&gt;init&lt;/strong&gt; For Me?&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://medium.com/free-code-camp/whats-in-a-python-s-name-506262fe61e8&quot;
&amp;gt;What&apos;s In Python&apos;s &lt;strong&gt;name&lt;/strong&gt;?&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://medium.com/@m0etaz/python-beyond-the-basics-ii-16964d70c4da&quot;
&amp;gt;Python: Beyond The Basics II&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://medium.com/hackernoon/python-tricks-101-2836251922e0&quot;
&amp;gt;Python Tricks 101&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://medium.com/hultner/try-out-walrus-operator-in-python-3-8-d030ce0ce601&quot;
&amp;gt;Try Out The Walrus Operator In Python 3.8&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://python.swaroopch.com/&quot;&amp;gt;A Byte Of Python eBook&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://medium.com/octopus-wealth/python-scripts-26e3d0bd5277&quot;
&amp;gt;Python Scripts&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.jeffknupp.com/blog/2013/08/16/open-sourcing-a-python-project-the-right-way/&quot;
&amp;gt;Open Sourcing a Python Project The Right Way&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://medium.com/georgian-impact-blog/python-tooling-makes-a-project-tick-181d567eea44&quot;
&amp;gt;Python Tooling Makes a Project Tick&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://isaak.dev/2020/08/python-libraries-to-make-your-code-readable-and-maintainable&quot;
&amp;gt;Python Libraries To Make Your Code Readable, Reliable, And
Maintainable&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://medium.com/testcult/intro-to-test-framework-pytest-5b1ce4d011ae&quot;
&amp;gt;Intro To Test Framework Pytest&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://pytest.org/en/latest/&quot;&amp;gt;Official Pytest Website&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://medium.com/@yeraydiazdiaz/what-the-mock-cheatsheet-mocking-in-python-6a71db997832&quot;
&amp;gt;What The Mock? A Cheatsheet for Mocking in Python&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2 id=&quot;go-resources&quot;&amp;gt;Go Resources&amp;lt;/h2&amp;gt;
&amp;lt;img src=&quot;/imgs/go.png&quot; alt=&quot;An illustration of the Go logo.&quot; /&amp;gt;
&amp;lt;p&amp;gt;
Go is another general-purpose programming language that can be casually
put in the same category as C/C++ and Rust.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
It does not run quite as fast as
&amp;lt;a
href=&quot;https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/go-gpp.html&quot;
&amp;gt;C++&amp;lt;/a
&amp;gt;
or
&amp;lt;a
href=&quot;https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/go-rust.html&quot;
&amp;gt;Rust&amp;lt;/a
&amp;gt;
(although it is still much faster than
&amp;lt;a
href=&quot;https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/go-node.html&quot;
&amp;gt;Node&amp;lt;/a
&amp;gt;
or
&amp;lt;a
href=&quot;https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/go-python3.html&quot;
&amp;gt;Python&amp;lt;/a
&amp;gt;). However, what it loses (slightly) in speed it makes up for in
convenience, readability, and all the comforts of a modern language. For
example, Go&apos;s
&amp;lt;a
href=&quot;https://www.reddit.com/r/golang/comments/5hqvc1/what_are_the_built_in_go_tools_what_are_they_used/&quot;
&amp;gt;built-in tooling&amp;lt;/a
&amp;gt;
is easily the best I&apos;ve ever seen in a language and has led to an
incredibly enjoyable developer experience anytime I&apos;ve had to use it.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Along with some of its other
&amp;lt;a href=&quot;https://go.dev/solutions/webdev/&quot;&amp;gt;features&amp;lt;/a&amp;gt;, it makes Go an
amazing language to work with for server-side development.
&amp;lt;/p&amp;gt;
&amp;lt;p class=&quot;italic&quot;&amp;gt;
Pro-Tip: If you&apos;re just starting out with Go always type &quot;Golang&quot;
instead of &quot;Go&quot; into Google searches––the results will be far more
relevant.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Getting Started&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
These are a few great resources for getting started with Go, however,
the server-side tutorials by Dave Stearns discussed below were my
primary method for learning Go.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://go.dev/&quot;&amp;gt;Go.dev Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://tour.golang.org/welcome/1&quot;&amp;gt;A Tour Of Go&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://golang.org/&quot;&amp;gt;Go Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://golang.org/doc/code.html&quot;&amp;gt;How To Write Go Code&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://golang.org/doc/&quot;&amp;gt;Go Documentation&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.golang-book.com/books/intro&quot;
&amp;gt;An Introduction to Go Book&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://gowebexamples.com/&quot;
&amp;gt;Learn Web Programming In Go By Example&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://gobyexample.com/&quot;&amp;gt;Go By Example&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;🔥🔥🔥 Dave Stearns Server-Side Tutorials&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
During college, I took a class on server-side development that used Go
as the primary language. The creator of the course (Dave Stearns) wrote
a series of tutorials to parallel the curriculum.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
The tutorials on Go are some of the most clearly written and
informational readings I&apos;ve ever come across and were the primary
mechanism that I used to learn the language. They are all worth reading.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/gointro/&quot;
&amp;gt;Introduction To Go&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/golang/&quot;
&amp;gt;The Go Language&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/goslicemap/&quot;
&amp;gt;Go Slices And Maps&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/gojson/&quot;
&amp;gt;Go Structs And JSON&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/goweb/&quot;
&amp;gt;Go Web Servers&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/tokenizing/&quot;
&amp;gt;Tokenizing HTML Streams In Go&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/testing/&quot;
&amp;gt;Automated Testing In Go&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/godb/&quot;
&amp;gt;Talking To Databases From Go&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/gohandlerctx/&quot;
&amp;gt;Sharing Values With Go Handlers&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/gomiddleware/&quot;
&amp;gt;Middleware Patterns In Go&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/mutexes/&quot;
&amp;gt;Protecting Data Structures With Mutexes&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Go Development Environment&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Setting up a development environment for Go is pretty straightforward.
All you need is to download the language (and thus the command line
tools for working with Go) and a Go language extension for your text
editor of choice.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://golang.org/doc/editors.html&quot;
&amp;gt;Editor Plugins And IDEs&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/golang/go/wiki/IDEsAndTextEditorPlugins&quot;
&amp;gt;IDEs And Plugins For Go&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.golang-book.com/guides/machine_setup&quot;
&amp;gt;Go Machine Setup (Windows/MacOS)&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Go Package Management&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
As I hinted at in the Deno section above, Go has a pretty unique (and
cool) way of approaching package management when compared to other
languages like Node or Python.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
As part of Go&apos;s command line tool that is installed with the language
there is a command called &amp;lt;code&amp;gt;go get&amp;lt;/code&amp;gt; that can be used to
download a package at a given URL. The package is installed locally to
your computer and from there you can import it into any Go file you
might be writing.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://golang.org/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them&quot;
&amp;gt;Go Get Documentation&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://pkg.go.dev/&quot;&amp;gt;Search For Go Packages&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Go Web Frameworks&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
There are some web frameworks out there for Go, but in most cases, the
built-in packages mean you don&apos;t need any third-party libraries for
server-side development!
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Miscellaneous Resources&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://awesome-go.com/&quot;&amp;gt;Awesome Go&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://golang.org/doc/cmd&quot;&amp;gt;Go Command Documentation&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://golang.org/doc/effective_go.html&quot;&amp;gt;Effective Go&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://golang.org/doc/articles/wiki/&quot;
&amp;gt;Writing Go Web Applications Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://kasvith.me/posts/lets-create-a-simple-lb-go/&quot;
&amp;gt;Create A Simple Load Balancer With Go&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://gophercises.com/&quot;&amp;gt;Gophercises&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/golang/go/wiki&quot;&amp;gt;The Go Wiki&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://blog.golang.org/&quot;&amp;gt;Go Blog&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://play.golang.org/&quot;&amp;gt;Go Playground&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/tmrts/go-patterns&quot;
&amp;gt;A Curated List Of Go Patterns&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://golang.org/pkg/&quot;&amp;gt;Go Standard Packages&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://blog.golang.org/cover&quot;&amp;gt;Go Test Coverage&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.reddit.com/r/golang/comments/ii64br/howwhy_is_concurrent_code_faster_than_sequential/?utm_source=share&amp;amp;utm_medium=ios_app&amp;amp;utm_name=iossmf&quot;
&amp;gt;How/Why Is Concurrent Code Faster Than Sequential Code?&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;http://www.doxsey.net/blog/go-concurrency-from-the-ground-up&quot;
&amp;gt;Go Concurrency From The Ground Up&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=cN_DpYBzKso&quot;
&amp;gt;Concurrency Is Not Parallelism Talk&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=f6kdp27TYZs&quot;
&amp;gt;Go Concurrency Patterns Talk&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://golang.org/ref/mem&quot;&amp;gt;The Go Memory Model&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2 id=&quot;rust-resources&quot;&amp;gt;Rust Resources&amp;lt;/h2&amp;gt;
&amp;lt;img src=&quot;/imgs/rust.png&quot; alt=&quot;An illustration of the Rust logo.&quot; /&amp;gt;
&amp;lt;p&amp;gt;
Rust is a language I&apos;m only starting to get familiar with but one that
I&apos;m pretty excited about. It is a low-level systems language that can be
thought of as a C/C++ competitor with some very nice benefits and
features.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Notably, it can run really fast (as fast as
&amp;lt;a
href=&quot;https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/rust.html&quot;
&amp;gt;C&amp;lt;/a
&amp;gt;/&amp;lt;a
href=&quot;https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/rust-gpp.html&quot;
&amp;gt;C++&amp;lt;/a
&amp;gt;) while at the same time providing memory safety and an incredible(-y
strict) compiler/type system that together can catch a ton of bugs and
output very helpful/descriptive errors messages and hints.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
On the downside, Rust has some fairly complicated syntax and idioms
compared to other languages, so it comes with a pretty steep learning
curve. It&apos;s definitely not a server-side language you should start out
with.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
With all that being said, Rust&apos;s position sat squarely in the world of
&amp;lt;a href=&quot;https://www.rust-lang.org/what/wasm&quot;&amp;gt;WebAssembly&amp;lt;/a&amp;gt; and
&amp;lt;a href=&quot;https://www.rust-lang.org/what/networking&quot;&amp;gt;server-side&amp;lt;/a&amp;gt;
use cases has caught my attention in a big way.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Getting Started&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
When it comes to learning Rust you will find that the primary method of
conferring information is through books. In fact, the official method
for learning Rust is often called &quot;The Book&quot; in the Rust community and
it&apos;s well worth reading.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
There is also a counter-part to the book that teaches Rust using a more
hands-on example-based approach which I&apos;ve personally found very
helpful.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥🔥 &amp;lt;a href=&quot;https://doc.rust-lang.org/book/&quot;&amp;gt;The Book&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥
&amp;lt;a href=&quot;https://doc.rust-lang.org/stable/rust-by-example/&quot;
&amp;gt;Rust By Example&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🔥🔥 &amp;lt;a href=&quot;http://www.arewewebyet.org/&quot;&amp;gt;Are We Web Yet? &amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://brson.github.io/fireflowers/&quot;&amp;gt;Fireflowers&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://www.rust-lang.org/&quot;&amp;gt;Rust Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Rust Development Environment&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
This is the official getting started page found on the Rust website. It
covers installing the language and provides links to developer tools for
popular text editors and IDEs. It&apos;s all you should need to get up and
running.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.rust-lang.org/learn/get-started&quot;
&amp;gt;Getting Started With Rust&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Rust Package Management&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Rust has a fantastic package management ecosystem that is very similar
to NPM. Rust provides a command-line tool called Cargo that operates
very similar to NPMs command-line tool. Also instead of calling
third-party code &quot;packages&quot;, the term &quot;crates&quot; is used.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://crates.io/&quot;&amp;gt;Crates.io&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://doc.rust-lang.org/cargo/index.html&quot;
&amp;gt;The Cargo Book&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Rust Web Frameworks&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Similar to Deno, the ecosystem of Rust web frameworks seems to be
evolving still. However, it is much further along than Deno with Rust
being nearly 5 years older.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
The first link below does a great job analyzing some of the popular
options in the ecosystem and providing recommendations.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://www.lpalmieri.com/posts/2020-07-04-choosing-a-rust-web-framework-2020-edition/&quot;
&amp;gt;Choosing A Rust Web Framework, 2020 Edition&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://actix.rs/&quot;&amp;gt;Actix Framework&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Similar to Flask or Express&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://rocket.rs/&quot;&amp;gt;Rocket Framework&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Similar to Django&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Miscellaneous Resources&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.figma.com/blog/rust-in-production-at-figma/&quot;
&amp;gt;Rust In Production At Figma&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://hacks.mozilla.org/2018/12/rust-2018-is-here/&quot;
&amp;gt;Rust 2018 Is Here… But What Is It?&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://polyfloyd.net/post/how-rust-helps-you-prevent-bugs/&quot;
&amp;gt;How Rust Helps You Prevent Bugs&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://github.com/rust-unofficial/awesome-rust/blob/master/README.md#audio&quot;
&amp;gt;Awesome Rust&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;http://nbaksalyar.github.io/2015/07/10/writing-chat-in-rust.html&quot;
&amp;gt;Rust In Detail: Writing A Scalable Chat Service From Scratch&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://doc.rust-lang.org/nightly/edition-guide/introduction.html&quot;
&amp;gt;Rust Nightly Edition Book&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://cfsamson.github.io/books-futures-explained/introduction.html&quot;
&amp;gt;Futures Explained In 200 Lines of Rust&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2 id=&quot;database-resources&quot;&amp;gt;Database Resources&amp;lt;/h2&amp;gt;
&amp;lt;img src=&quot;/imgs/database.png&quot; alt=&quot;An illustration of a database icon.&quot; /&amp;gt;
&amp;lt;p&amp;gt;
Continuing the customer-restaurant analogy, databases can be thought of
as the fridge or cold room in a restaurant. It&apos;s the place where food
and ingredients (read: your application data) can be stored before
they&apos;re ready to be cooked and delivered to a customer.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Just as fridges come in many different shapes and sizes, there are a
variety of database types that solve different problems. The link below
is an amazing video that discusses the various database types you might
use.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
For the purposes of this guide (and my own limited knowledge of the
entire database ecosystem), we will mainly focus on the most popular
type of database––&amp;lt;span class=&quot;italic&quot;&amp;gt;relational databases&amp;lt;/span&amp;gt;. The
most simple analogy I can think of is that relational databases are like
spreadsheets that can be connected together. They use SQL (Structured
Query Language) to create and maintain these spreadsheets. Below you
will find resources for a popular dialect of SQL called MySQL.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Additionally, relational databases and SQL are so dominant that you will
sometimes see non-relational database options casually referred to as
&quot;NoSQL databases.&quot; That being said, I would once again highly recommend
watching the video linked below to get a better understanding of what
else is out there.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=W2Z7fbCLSTw&quot;
&amp;gt;7 Database Paradigms&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2&amp;gt;MySQL Resources&amp;lt;/h2&amp;gt;
&amp;lt;img src=&quot;/imgs/mysql.png&quot; alt=&quot;An illustration of the MySQL logo.&quot; /&amp;gt;
&amp;lt;p&amp;gt;
MySQL is one of many flavors of SQL and just happens to be the version
of SQL I learned. It can do all the core operations of any other SQL
language but with minor syntax and feature differences.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Getting Started&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.mysqltutorial.org/basic-mysql-tutorial.aspx&quot;
&amp;gt;Basic MySQL Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.elated.com/mysql-for-absolute-beginners/&quot;
&amp;gt;MySQL For Absolute Beginners&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Advanced SQL Concepts&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.mysqltutorial.org/mysql-stored-procedure-tutorial.aspx&quot;
&amp;gt;MySQL Stored Procedures&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.mysqltutorial.org/mysql-triggers.aspx&quot;
&amp;gt;MySQL Triggers&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.mysqltutorial.org/mysql-views-tutorial.aspx&quot;
&amp;gt;MySQL Views&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.mysqltutorial.org/mysql-functions.aspx&quot;
&amp;gt;MySQL Functions&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.reddit.com/r/webdev/comments/b46iyd/sql_joins/&quot;
&amp;gt;SQL Joins Infographic&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Using MySQL With Backend Languages&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.mysqltutorial.org/mysql-nodejs/&quot;
&amp;gt;MySQL Node.js Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.mysqltutorial.org/php-mysql/&quot;
&amp;gt;MySQL PHP Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.mysqltutorial.org/python-mysql/&quot;
&amp;gt;MySQL Python Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2&amp;gt;NoSQL Resources&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;
A big name in the NoSQL space that often comes up is MongoDB which uses
JSON as it&apos;s storage medium (compared to the spreadsheet-like format of
SQL databases).
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://flaviocopes.com/mongodb-vs-sql/&quot;
&amp;gt;How MongoDB Is Different From A SQL Database&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://flaviocopes.com/mongodb/&quot;&amp;gt;MongoDB Basics Tutorial&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://flaviocopes.com/node-mongodb/&quot;
&amp;gt;How To Use MongoDB With Node.js&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2 id=&quot;cloud-resources&quot;&amp;gt;Cloud Resources&amp;lt;/h2&amp;gt;
&amp;lt;img
src=&quot;/imgs/cloud.png&quot;
alt=&quot;An illustration of various Cloud company logos.&quot;
/&amp;gt;
&amp;lt;p&amp;gt;Finally, we get to the cloud.&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Physically speaking, the cloud is just a network of servers spread
across the entire world that is owned and operated by a variety of
companies (notably Amazon, Microsoft, and Google).
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
For the purposes of full-stack software engineering, the benefit of the
cloud is that you can rent the computing power and storage capacity of
this network to run your web applications at any scale––whether your app
has tens of users or billions of users.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
One of the big tasks of using the cloud is determining what services and
technologies to use.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
There are a mind-boggling number of these services available to use
(Amazon&apos;s AWS has over 150+ options alone). Notably for web development,
there are tons of options for web application deployment and options for
database deployment. Some of these options lean towards automating the
process of deployment while others are more flexible and let you use
technologies like Docker and Kubernetes to set up your own custom
environment.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Take your time to research these various services along with there
drawbacks and benefits and you&apos;ll be golden. Additionally, the first
link below offers a great introduction to some of these services and
what&apos;s possible in the cloud.
&amp;lt;/p&amp;gt;
&amp;lt;h3&amp;gt;Getting Started&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=1pBuwKwaHp0&quot;
&amp;gt;Cloud Computing In The Year 2020&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://mashable.com/2013/08/26/what-is-the-cloud/&quot;
&amp;gt;What Is The Cloud?&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://linchpinseo.com/guide-to-the-cloud/&quot;
&amp;gt;Beginner&apos;s Guide To Understanding The Cloud&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Docker / Kubernetes (K8s) Resources&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
At a high-level (that leaves out a lot of details), Docker is a way of
packaging your web applications into what are called &quot;containers&quot; so
that they can be deployed basically anywhere. This is very powerful
because it means you as a developer don&apos;t have to worry about getting
your application to run on different servers with different operating
systems.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Kubernetes (sometimes called K8s for short) is essentially a piece of
software for organizing and managing Docker containers in the Cloud.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=Gjnup-PuquQ&quot;
&amp;gt;Docker In 100 Seconds&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=gAkwW2tuIqE&quot;
&amp;gt;Learn Docker In 7 Easy Steps - Full Beginner&apos;s Tutorial&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/docker/&quot;
&amp;gt;Containerizing With Docker&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://www.freecodecamp.org/news/comprehensive-introductory-guide-to-docker-vms-and-containers-4e42a13ee103/&quot;
&amp;gt;A Comprehensive Intro To Docker, Virtual Machines, and
Containers&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://www.docker.com/&quot;&amp;gt;Docker Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.docker.com/get-started&quot;
&amp;gt;Docker Getting Started&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://docs.docker.com/get-started/overview/&quot;
&amp;gt;Docker Overview&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://docs.docker.com/develop/develop-images/dockerfile_best-practices/&quot;
&amp;gt;Best Practices For Writing Dockerfiles
&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://kubernetes.io/&quot;&amp;gt;Kubernetes Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://kubernetes.io/docs/tutorials/kubernetes-basics/&quot;
&amp;gt;Kubernetes Basics&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://kubernetes.io/docs/home/&quot;
&amp;gt;Kubernetes Documentation&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Heroku Resources&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;
Heroku is another Cloud service (just like Amazon AWS or Microsoft
Azure), but notably it is my go to service for deploying smaller
applications and websites.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
Plus when combined with the tips from the third link you can effectively
get 24/7 uptime for free.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://railsautoscale.com/heroku-free-dynos/&quot;
&amp;gt;Tips For Running Free Dynos On Heroku In 2020&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://trifinlabs.com/what-is-heroku/&quot;&amp;gt;What Is Heroku?&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://devcenter.heroku.com/start&quot;
&amp;gt;Heroku Getting Started&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://devcenter.heroku.com/categories/reference&quot;
&amp;gt;Heroku Documentation&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3&amp;gt;Miscellaneous Resources&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://aws.amazon.com/&quot;&amp;gt;AWS Website&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/deploy2aws/&quot;
&amp;gt;Deploying to AWS&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://aws.amazon.com/education/awseducate/&quot;&amp;gt;AWS Educate&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://azure.microsoft.com/en-us/&quot;
&amp;gt;Microsoft Azure Website&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://azure.microsoft.com/en-us/developer/students/&quot;
&amp;gt;Azure Student Developer Resources&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://cloud.google.com/&quot;&amp;gt;Google Cloud Website&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://cloud.google.com/gcp/getting-started&quot;
&amp;gt;Google Cloud Getting Started&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.digitalocean.com/&quot;&amp;gt;DigitalOcean Website&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://drstearns.github.io/tutorials/deploy2do/&quot;
&amp;gt;Deploying to Digital Ocean&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://workers.cloudflare.com/&quot;&amp;gt;Cloudflare Workers&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.youtube.com/watch?v=3OP-q55hOUI&quot;
&amp;gt;Cloud Run Quick Start––Docker To Serverless&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2 id=&quot;miscellaneous-resources&quot;&amp;gt;Miscellaneous Resources&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;
Finally, here are all the resources that didn&apos;t neatly fit into the
other categories but are still well worth a look 👀.
&amp;lt;/p&amp;gt;
&amp;lt;h3 id=&quot;inspiration-resources&quot;&amp;gt;Sources For Inspiration&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.awwwards.com/collections/&quot;
&amp;gt;Awwwards Collections&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://dribbble.com/&quot;&amp;gt;Dribbble&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://www.awwwards.com/&quot;&amp;gt;Awwwards&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://lookup.design/&quot;&amp;gt;Lookup.design&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.cssdesignawards.com/&quot;&amp;gt;CSS Design Awards&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://bestwebsite.gallery/&quot;&amp;gt;Best Website Gallery&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://codepen.io/&quot;&amp;gt;Codepen&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3 id=&quot;general-education-resources&quot;&amp;gt;General Education Resources&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥🔥🔥 &amp;lt;a href=&quot;https://roadmap.sh/&quot;&amp;gt;Developer Roadmaps&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://sidewaysdictionary.com&quot;&amp;gt;Sideways Dictionary&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://choosealicense.com/&quot;
&amp;gt;Choose An Open Source License&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.freecodecamp.org/learn/&quot;&amp;gt;FreeCodeCamp&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a
href=&quot;https://www.youtube.com/watch?v=tpIctyqH29Q&amp;amp;list=PL8dPuuaLjXtNlUrzyH5r6jN9ulIgZBpdo&amp;amp;index=1&quot;
&amp;gt;Crash Course Computer Science Series&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://missing.csail.mit.edu/&quot;
&amp;gt;The Missing Semester of Your CS Education&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://research.hackerrank.com/&quot;
&amp;gt;HackerRank Developer Skills Report&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://web.dev/&quot;&amp;gt;Web.dev&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://developers.google.com/web&quot;
&amp;gt;Google Developers - Web&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.tutorialspoint.com/index.htm&quot;&amp;gt;TutorialsPoint&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3 id=&quot;reading-resources&quot;&amp;gt;Great Blogs / Books / Articles&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;
🔥🔥🔥
&amp;lt;a href=&quot;https://code-cartoons.com/@linclark&quot;&amp;gt;Anything&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;https://hacks.mozilla.org/author/lclarkmozilla-com/&quot;
&amp;gt;Written&amp;lt;/a
&amp;gt;
By &amp;lt;a href=&quot;https://twitter.com/codecartoons&quot;&amp;gt;Lin Clark&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://www.taniarascia.com/&quot;&amp;gt;Tania Rascia Blog&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://flaviocopes.com/&quot;&amp;gt;Flavio Copes Blog&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://www.zainrizvi.io/blog/the-impostors-advantage/&quot;
&amp;gt;The Impostor&apos;s Advantage&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://mtlynch.io/human-code-reviews-1/&quot;
&amp;gt;How To Do Code Reviews Like A Human (Part 1)&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
🔥
&amp;lt;a href=&quot;https://mtlynch.io/human-code-reviews-2/&quot;
&amp;gt;How To Do Code Reviews Like A Human (Part 2)&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.freecodecamp.org/news/&quot;&amp;gt;FreeCodeCamp News&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://www.uxpin.com/studio/ebooks/&quot;&amp;gt;Free UX Ebooks&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://code.tutsplus.com/tutorials/mvc-for-noobs--net-10488&quot;
&amp;gt;MVC For Noobs&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://github.com/kamranahmedse/design-patterns-for-humans&quot;
&amp;gt;Design Patterns For Humans&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://12factor.net/&quot;&amp;gt;The 12 Factor App&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a
href=&quot;https://faculty.washington.edu/ajko/books/cooperative-software-development/#/&quot;
&amp;gt;Cooperative Software Development Book&amp;lt;/a
&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h3 id=&quot;stock-image-resources&quot;&amp;gt;Stock Image Resources&amp;lt;/h3&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;🔥 &amp;lt;a href=&quot;https://www.pexels.com/&quot;&amp;gt;Pexels&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Free/copyright free stock images.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&quot;https://unsplash.com/&quot;&amp;gt;Unsplash&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;Free/copyright free stock images.&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;li&amp;gt;
&amp;lt;a href=&quot;https://generated.photos/&quot;&amp;gt;AI-Generated Human Photos&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;h2&amp;gt;Wrapping Up&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;
Congratulations! You&apos;ve officially made it through the mega full-stack
web development resource guide. It was definitely not a short read 😅.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
I hope you picked up some new knowledge along the way. Learning the many
domains of full-stack web development can be a lot to grapple with at
times but if you take your time and pace it out the results are more
than worth the effort.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;
If you would like more useful resources, thoughts, and information on
web development topics, you can
&amp;lt;a class=&quot;bold&quot; href=&quot;https://fosstodon.org/@hawkticehurst&quot;&amp;gt;follow me&amp;lt;/a&amp;gt;
on Mastodon.
&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;Best of luck on your own web development journeys! You got this. 💫&amp;lt;/p&amp;gt;
&amp;lt;/section&amp;gt;&lt;/p&gt;
</content:encoded></item></channel></rss>