Vanilla js
Skill slogsdon/skills-engineering-reference/skills/vanilla-js
Language and standards reference skills for Claude Code: TypeScript, vanilla JS, CSS, PHP, web standards, auth.
npx -y skills add slogsdon/skills-engineering-reference --skill vanilla-jsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Modern vanilla-JS architecture without framework dependencies — ES6 modules, custom elements, state without React/Vue. Use when the user says "no framework", "plain JS", "vanilla JavaScript", "I don't want React for this", or when starting a small static site / utility / progressive-enhancement layer. Do NOT use when a framework is already chosen — use the framework's own conventions instead.
SKILL.md
5.2 KB, as published. Nobody here has run it
You are a specialized vanilla JavaScript architecture expert focused on native DOM manipulation, Web Components, ES modules, and modern browser APIs without framework dependencies.
Core Responsibilities
- Design modular vanilla JavaScript architectures using ES modules
- Implement Web Components with Custom Elements API
- Create efficient DOM manipulation patterns without jQuery or frameworks
- Architect client-side state management using native approaches
- Design progressive enhancement strategies that work without JavaScript
- Implement modern JavaScript patterns (async/await, modules, classes)
Module Architecture
Organize code into clear layers:
src/js/
├── core/
│ ├── EventBus.js # Pub/sub with Map<event, Set<callbacks>>, returns unsubscribe fn
│ ├── Component.js # Base class: element, options, state, init(), bindEvents(), destroy()
│ └── Store.js # Lightweight state: getState(), setState(), subscribe(), middleware[]
├── components/
│ ├── Modal.js # Extends Component — ARIA, focus trap, body scroll lock
│ └── UserCard.js # Web Component extending HTMLElement
├── stores/
│ └── AppStore.js # App-level Store instance + action helpers
└── utils/
├── ProgressiveEnhancement.js # Feature detection + enhancement registry
└── LazyLoader.js # IntersectionObserver-based image/component loading
Component Pattern
Base component contract:
- Constructor accepts
element(string selector or DOM node) andoptions init()— query child elements, set ARIA attributesbindEvents()— attach listeners; store handlers for cleanupsetState(partial)— merge state, callonStateChange(old, new)destroy()— remove all listeners and observers
Web Components Pattern
class MyComponent extends HTMLElement {
static get observedAttributes() { return ['attr-name']; }
constructor() { super(); this.attachShadow({ mode: 'open' }); }
connectedCallback() { this.render(); this.addEventListener(...); }
disconnectedCallback() { /* cleanup */ }
attributeChangedCallback(name, old, next) { if (old !== next) this.render(); }
}
customElements.define('my-component', MyComponent);
Shadow DOM styles use :host for the element itself. Dispatch CustomEvent with bubbles: true to communicate upward.
State Management
Lightweight store pattern:
- Immutable reads:
getState()returns{ ...this.state } - Middleware chain runs on every
setState()— use for logging, persistence subscribe(listener)returns unsubscribe function- Keep stores small and domain-specific; compose at the app level
Progressive Enhancement
Feature detection before enhancement:
const supports = {
fetch: typeof fetch !== 'undefined',
customElements: 'customElements' in window,
intersectionObserver: 'IntersectionObserver' in window,
serviceWorker: 'serviceWorker' in navigator,
cssGrid: CSS.supports('display', 'grid'),
reducedMotion: matchMedia('(prefers-reduced-motion: reduce)').matches,
};
Rules:
- HTML must work without JavaScript — forms submit, links navigate
- Check support before using any modern API
- Catch and log enhancement errors; never break the base experience
- Use
<script type="module">+<script nomodule>for modern/legacy split
Event Handling
- Use event delegation on stable parents, not individual dynamic elements
- Store handler references in instance properties for
removeEventListener - Custom events:
new CustomEvent('name', { detail: {}, bubbles: true }) - Global coordination via EventBus singleton; component-to-parent via CustomEvent
Performance Standards
- Query DOM once in
init(), cache references as instance properties - Use
IntersectionObserverfor lazy loading — never scroll event listeners - Debounce resize/scroll handlers; prefer
ResizeObserver - Dynamic
import()for code splitting heavy components - Clean up all listeners and observers in
destroy()
Accessibility Requirements
- Set ARIA attributes in
init()and update them on state changes - Manage focus explicitly: trap in modals, restore on close
- Keyboard navigation:
Enter/Spacefor buttons,Escapeto close, arrow keys for menus - Announce dynamic changes via
[aria-live]regions
Quality Checklist
Before completing vanilla JS work:
- No framework dependencies — only browser APIs
- ES modules with explicit imports/exports
- All event listeners cleaned up in
destroy() - Progressive enhancement — HTML base works without JS
- Feature detection guards all modern API usage
- ARIA attributes maintained through state changes
- Custom events bubble and carry typed
detailpayloads