Skip to content

Instantly share code, notes, and snippets.

View knowler's full-sized avatar

Nathan Knowler knowler

View GitHub Profile
@knowler
knowler / restart-animations-element.js
Created November 30, 2023 23:49
A web component for restarting all the animations on the page.
export class RestartAnimationsElement extends HTMLElement {
#controller;
get #buttonElement() { return this.shadowRoot.querySelector(':host > button'); }
connectedCallback() {
if (!this.shadowRoot) {
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `<button type="button" part="button"><slot>Restart Animations</slot></button>`;
this.#controller = new AbortController();
/*
* Modern CSS crash course:
*
* - Leverage flexibility for everything. The web is magic paper that adapts to whatever viewport it’s in.
* - This means using `display` values like `flex` and `grid`.
* - Avoid using `@media` for setting sizes: use fluid type and spacing (i.e. `clamp()`, `min()`, `max()` math functions).
* - Use logical properties for sizing and spacing. This helps with internationalization.
* - Think in axes: block (vertical: top is start, bottom is end), inline (horizontal: left is start, right is end).
* - Those are the left-to-right/top-to-bottom equivalances, but for other writing modes/directions you’ll be setting the correct direction (e.g. in RTL, inline-start is right and inline-end is left).
* - For flexbox and grid: “align” properties apply to the block axis and “justify” properties apply to the inline axis.
@knowler
knowler / shadow.pug
Last active January 12, 2024 20:06
Pug mixin for an open Declarative Shadow DOM that polyfills itself.
mixin shadow
template(shadowrootmode="open")
block
script.
{
const element = document.currentScript.parentElement;
if (!element.shadowRoot) {
element.attachShadow({ mode: "open"})
const template = element.querySelector(":scope > template[shadowrootmode=open]");
if (template) element.shadowRoot.appendChild(template.content.cloneNode(true));
@knowler
knowler / connected-event.js
Last active January 13, 2025 21:29
Demonstration of how to patch the custom element registry to add a `connected` event.
const originalDefine = CustomElementRegistry.prototype.define;
CustomElementRegistry.prototype.define = function(_name, constructor) {
const originalConnectedCallback = constructor.prototype.connectedCallback;
constructor.prototype.connectedCallback = function() {
// Maybe use a prefixed event name?
// Maybe configure some options (i.e. whether it bubbles)
this.dispatchEvent(new CustomEvent("connected"));
originalConnectedCallback?.call(this, ...arguments);
}
originalDefine.call(this, ...arguments);
@knowler
knowler / unconstructable-uncallable-constructors.js
Last active January 17, 2025 08:14
These can only be subclassed, not constructed on their own or called as a function.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new.target
class Special {
constructor() {
if (new.target === Special)
throw new TypeError("Failed to construct 'Special': Illegal constructor");
}
}
function AlsoSpecial() {
@knowler
knowler / object-cache-for-deno-kv.js
Last active February 4, 2025 16:14
Demo using the Web Cache API as an object cache for Deno Deploy KV.
import { Hono } from "hono";
const kv = await Deno.openKv();
// You could add a version to this if you wanted to bust it (e.g. if a key needed to be deleted).
const kvCache = await caches.open("kv-cache");
// Helper for constructing the URL.
function kvCacheURL(slug) {
return new URL(slug, "http://kv.invalid/pages/");