Agent skill
web-components
Web Component patterns for Oh My Brand! frontend interactivity. Custom element lifecycle, attribute observation, event handling, and accessibility. Use when writing view.ts files.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/web-components-wesleysmits-oh-my-brand-wp-fse-4d67115c
Metadata
Additional technical details for this skill
- author
- Wesley Smits
- version
- 1.0.0
SKILL.md
Web Components
Web Component patterns for frontend interactivity in the Oh My Brand! WordPress FSE theme.
When to Use
- Adding frontend interactivity to blocks (carousels, accordions, lightboxes)
- Creating reusable interactive components
- Handling user interactions (clicks, keyboard, touch)
- Managing component state on the frontend
Reference Files
| File | Purpose |
|---|---|
| OmbGalleryCarousel.ts | Full Web Component example (~200 lines) |
| view.ts | Web Component scaffold |
Custom Element Structure
class OmbGalleryCarousel extends HTMLElement {
static observedAttributes = ['visible-images'];
#gallery: HTMLElement | null = null;
#items: NodeListOf<HTMLElement> | null = null;
#currentIndex = 0;
connectedCallback(): void {
this.#readAttributes();
this.#queryElements();
this.#bindEvents();
this.#initialize();
}
disconnectedCallback(): void {
this.#unbindEvents();
}
attributeChangedCallback(
name: string,
oldValue: string | null,
newValue: string | null
): void {
if (oldValue === newValue) return;
// Handle attribute change
}
}
if (!customElements.get('omb-gallery-carousel')) {
customElements.define('omb-gallery-carousel', OmbGalleryCarousel);
}
See OmbGalleryCarousel.ts for the complete implementation.
Lifecycle Methods
connectedCallback
Called when element is added to the DOM:
connectedCallback(): void {
this.#readAttributes(); // 1. Read attributes
this.#queryElements(); // 2. Query child elements
this.#bindEvents(); // 3. Bind events
this.#initialize(); // 4. Initialize state
}
disconnectedCallback
Called when element is removed from the DOM:
disconnectedCallback(): void {
this.#unbindEvents();
if (this.#animationFrame) cancelAnimationFrame(this.#animationFrame);
if (this.#debounceTimer) clearTimeout(this.#debounceTimer);
}
attributeChangedCallback
Called when observed attribute changes:
static observedAttributes = ['visible-images', 'autoplay'];
attributeChangedCallback(
name: string,
oldValue: string | null,
newValue: string | null
): void {
if (oldValue === newValue) return;
switch (name) {
case 'visible-images':
this.#visibleImages = newValue ? parseInt(newValue, 10) : 3;
this.#updateLayout();
break;
case 'autoplay':
newValue !== null ? this.#startAutoplay() : this.#stopAutoplay();
break;
}
}
Attribute Handling
Boolean Attributes
const hasAutoplay = this.hasAttribute('autoplay');
this.setAttribute('loading', ''); // Add
this.removeAttribute('loading'); // Remove
this.toggleAttribute('loading'); // Toggle
Value Attributes
const value = this.getAttribute('visible-images');
const parsed = value ? parseInt(value, 10) : 3;
this.setAttribute('visible-images', '4');
Data Attributes
const config = JSON.parse(this.dataset.config || '{}');
this.dataset.state = 'loading';
Event Handling
Arrow Function Methods
Use arrow functions to preserve this context:
class OmbComponent extends HTMLElement {
#handleClick = (event: MouseEvent): void => {
event.preventDefault();
this.#doSomething();
};
#bindEvents(): void {
this.addEventListener('click', this.#handleClick);
}
#unbindEvents(): void {
this.removeEventListener('click', this.#handleClick);
}
}
Custom Events
this.dispatchEvent(
new CustomEvent('omb-gallery:slide-change', {
bubbles: true,
detail: { index: this.#currentIndex, total: this.#items?.length ?? 0 },
})
);
Event Delegation
#handleContainerClick = (event: MouseEvent): void => {
const target = event.target as HTMLElement;
const item = target.closest('[data-gallery-item]');
if (item) this.#handleItemClick(item as HTMLElement);
};
DOM Queries
Query and Cache Elements
#queryElements(): void {
this.#container = this.querySelector('[data-container]');
this.#items = this.querySelectorAll('[data-item]');
this.#button = this.querySelector('button') as HTMLButtonElement | null;
}
Null Safety
// Guard clause
if (!this.#container) return;
// Optional chaining
this.#button?.click();
// Nullish coalescing
const count = this.#items?.length ?? 0;
Accessibility
Live Regions
#announce(): void {
if (!this.#liveRegion) return;
this.#liveRegion.textContent = `Showing image ${this.#currentIndex + 1} of ${this.#items?.length}`;
}
Keyboard Navigation
#handleKeydown = (event: KeyboardEvent): void => {
switch (event.key) {
case 'ArrowLeft':
case 'ArrowUp':
event.preventDefault();
this.#navigatePrevious();
break;
case 'ArrowRight':
case 'ArrowDown':
event.preventDefault();
this.#navigateNext();
break;
case 'Escape':
event.preventDefault();
this.#close();
break;
}
};
Focus Management
#open(): void {
this.#previousFocus = document.activeElement as HTMLElement;
this.#dialog?.showModal();
this.#dialog?.querySelector<HTMLElement>('button')?.focus();
}
#close(): void {
this.#dialog?.close();
this.#previousFocus?.focus();
}
Reduced Motion
#shouldReduceMotion(): boolean {
return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
}
#scrollToIndex(): void {
const behavior = this.#shouldReduceMotion() ? 'auto' : 'smooth';
this.#gallery?.scrollTo({ left: scrollLeft, behavior });
}
Registration
Guard Against Re-registration
if (!customElements.get('omb-gallery-carousel')) {
customElements.define('omb-gallery-carousel', OmbGalleryCarousel);
}
Naming Convention
| Pattern | Example |
|---|---|
| Tag name | omb-{block-name} |
| Class name | Omb{BlockName} |
Examples:
omb-gallery-carousel→OmbGalleryCarouselomb-faq-accordion→OmbFaqAccordion
Related Skills
- typescript-standards - TypeScript conventions
- html-standards - HTML accessibility
- vitest-testing - Testing Web Components
- native-block-development - Block structure
- block-scaffolds - Web Component template
References
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?