custom-elements

Implements vanilla TypeScript custom HTML elements for client interactivity on Astro pages.

Updated Sep 6, 2026
One-click install
npx skills add https://github.com/malikkotb/shellpluscore --skill custom-elements-malikkotb
Or copy as Structured Prompt for Agent
Please help me install this Agent Skill.
Skill: custom-elements
Source: https://github.com/malikkotb/shellpluscore/tree/main/.agents/skills/custom-elements
Command: npx skills add https://github.com/malikkotb/shellpluscore --skill custom-elements-malikkotb

SYSTEM DOCUMENTATION & REQUIREMENTS

What problem does it solve? Adding client-side interactivity to Astro pages without pulling in React or another framework runtime requires a disciplined pattern. This Skill encodes the project's vanilla custom-element convention so every interactive component follows the same lifecycle, state, and accessibility rules. ## Core Features & Use Cases - Component pairing pattern: Pairs Component.astro (server-rendered markup with a kebab-case tag and data-* hooks) with ComponentElement.ts (an HTMLElement subclass) for all public-page behavior. - Lifecycle and state conventions: Enforces private #field state initialized in connectedCallback, arrow-function event handlers, listener teardown in disconnectedCallback, and guarded customElements.define registration. - Nested element safety: Provides queryUpgraded/queryAllUpgraded helpers to avoid silent instanceof failures after <ClientRouter /> page swaps. - Use Case: When adding a contact form with fetch submission, honeypot spam protection, and aria-live status messages to a public Astro page, this Skill produces the correct element pair with progressive enhancement so the form works without JavaScript. ## Quick Start Add a custom element for a filterable card list on the blog index page following the custom-elements pattern.

Frequently Asked Questions about custom-elements

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I add client-side interactivity to an Astro page without React?

Create a PascalCase folder pairing Component.astro with ComponentElement.ts. The .astro file renders markup with a kebab-case tag and data-* hooks, and its script only imports the element module; the element extends HTMLElement and wires listeners in connectedCallback.

How do I manage state in a vanilla custom element?

Declare state as private #field class fields initialized in connectedCallback from typed querySelector calls against data-* attributes. Never query the DOM in the constructor, since it runs before the DOM exists.

Why does instanceof fail on nested custom elements after an Astro page swap?

After a ClientRouter swap, upgrades run ancestors first, so the descendant is still a plain HTMLElement when the parent's connectedCallback runs. Use queryUpgraded or queryAllUpgraded from the query-upgraded helper, or look the element up lazily at call time.

Can I use React for interactive components on Astro public pages?

No. Public pages use only vanilla custom elements; React exists solely inside the Sanity Studio. The pattern replaces useState with private #fields, props with data-* attributes, and useEffect cleanup with connectedCallback and disconnectedCallback.

How do I prevent memory leaks in custom elements?

Define event handlers as arrow-function class fields so addEventListener and removeEventListener share one reference, then remove every listener in disconnectedCallback. Also abort in-flight fetches and destroy observers or animations there.

When should a custom element use progressive enhancement?

Always. Server-render the complete DOM and let the element only show, hide, fetch, or sync state, so pages work without JavaScript. Filtering toggles the hidden property on server-rendered nodes rather than rebuilding the list.