Skip to content

Instantly share code, notes, and snippets.

@cmdr2
Created July 29, 2025 10:11
Show Gist options
  • Select an option

  • Save cmdr2/2506b1d9ff67f51ac6911741c2e21d45 to your computer and use it in GitHub Desktop.

Select an option

Save cmdr2/2506b1d9ff67f51ac6911741c2e21d45 to your computer and use it in GitHub Desktop.
Demo of foundational tags like img, b, button implemented using Web Components
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Custom Elements Demo</title>
</head>
<body>
<custom-b>Hello, bold world!</custom-b>
<br><br>
<custom-img src="https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/PNG_transparency_demonstration_1.png/320px-PNG_transparency_demonstration_1.png"></custom-img>
<br><br>
<custom-button id="mybtn">Click me</custom-button>
<script>
// <custom-b>
customElements.define('custom-b', class extends HTMLElement {
connectedCallback() {
this.style.fontWeight = 'bold';
}
});
// <custom-img>
customElements.define('custom-img', class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.canvas = document.createElement('canvas');
this.ctx = this.canvas.getContext('2d');
this.shadowRoot.appendChild(this.canvas);
}
static get observedAttributes() {
return ['src'];
}
attributeChangedCallback(name, _, value) {
if (name === 'src') this.loadImage(value);
}
async loadImage(src) {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
this.canvas.width = img.naturalWidth;
this.canvas.height = img.naturalHeight;
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.drawImage(img, 0, 0);
};
img.onerror = () => {
this.canvas.width = this.canvas.height = 0;
};
img.src = src;
}
connectedCallback() {
const style = document.createElement('style');
style.textContent = `
canvas {
display: block;
max-width: 100%;
height: auto;
}
`;
this.shadowRoot.prepend(style);
if (this.hasAttribute('src')) {
this.loadImage(this.getAttribute('src'));
}
}
});
// <custom-button>
customElements.define('custom-button', class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
const div = document.createElement('div');
div.setAttribute('tabindex', '0');
div.setAttribute('role', 'button');
div.innerHTML = `<slot></slot>`;
const style = document.createElement('style');
style.textContent = `
div {
all: unset;
display: inline-block;
padding: 0.4em 1em;
border: 1px solid #ccc;
border-radius: 4px;
background: #eee;
cursor: pointer;
font: inherit;
}
div:focus {
outline: 2px solid blue;
}
div:active {
background: #ddd;
}
`;
this.shadowRoot.append(style, div);
this.btn = div;
}
connectedCallback() {
this.btn.addEventListener('keydown', e => {
if (e.key === 'Enter' || e.key === ' ') {
this.btn.click();
e.preventDefault();
}
});
this.btn.addEventListener('click', e => {
this.dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }));
});
}
});
// Example click listener
document.getElementById('mybtn').addEventListener('click', () => {
alert('Button clicked!');
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment