This document provides instructions for standardizing ProfPowell web component repositories. Use this guide when refactoring any component to match the standard.
Repositories in this suite:
| Component | npm | GitHub Pages |
|---|---|---|
| code-block | @profpowell/code-block | Demo |
| browser-window | @profpowell/browser-window | Demo |
| terminal-window | @profpowell/terminal-window | Demo |
| browser-console | @profpowell/browser-console | Demo |
| http-component | @profpowell/http-component | Demo |
Use bd (beads) for all work tracking:
bd ready # Find available work
bd show <id> # View issue details
bd update <id> --status in_progress # Claim work
bd close <id> # Complete work
bd sync # Sync with gitBefore starting any refactor work, check for existing issues with bd ready.
repo/
├── src/
│ └── component-name.js # Main source with JSDoc comments
├── dist/
│ └── component-name.js # Built ES module (committed)
├── docs/
│ ├── index.html # Home page → GitHub Pages
│ ├── demos.html # Interactive demos
│ ├── api.html # API documentation
│ └── styles.css # Shared styles
├── test/
│ └── component-name.spec.js # Playwright E2E tests
├── component-name.d.ts # Manual TypeScript definitions
├── custom-elements.json # Generated by CEM analyzer
├── package.json
├── vite.config.js
├── eslint.config.js
├── .prettierrc
├── playwright.config.js
├── README.md
├── LICENSE
├── CHANGELOG.md
└── CLAUDE.md
Exactly 5 devDependencies. Unlikely more and not less.
{
"devDependencies": {
"@custom-elements-manifest/analyzer": "^0.11.0",
"@playwright/test": "^1.57.0",
"eslint": "^9.0.0",
"prettier": "^3.0.0",
"vite": "^6.0.0"
}
}Remove any of these if present:
- Storybook packages (
@storybook/*) - Alternative test runners (vitest, jest, mocha, @web/test-runner, @open-wc/testing) but make sure to port to new test replacement
- Alternative bundlers (esbuild, rollup, webpack, terser)
- TypeScript compiler (typescript, tsc) - before removal prompt user and discuss use
- jsdom, happy-dom
import { defineConfig } from 'vite'
export default defineConfig({
build: {
lib: {
entry: 'src/COMPONENT_NAME.js',
formats: ['es'],
fileName: () => 'COMPONENT_NAME.js'
},
rollupOptions: {
// Externalize runtime deps if any (e.g., highlight.js for code-block)
external: [],
output: {
globals: {}
}
}
},
server: {
open: '/docs/index.html'
}
})Replace COMPONENT_NAME with the actual component name (e.g., code-block, browser-window).
import js from '@eslint/js'
export default [
js.configs.recommended,
{
languageOptions: {
ecmaVersion: 2022,
sourceType: 'module',
globals: {
window: 'readonly',
document: 'readonly',
customElements: 'readonly',
HTMLElement: 'readonly',
CustomEvent: 'readonly',
MutationObserver: 'readonly',
IntersectionObserver: 'readonly',
ResizeObserver: 'readonly',
navigator: 'readonly',
console: 'readonly',
setTimeout: 'readonly',
clearTimeout: 'readonly',
setInterval: 'readonly',
clearInterval: 'readonly',
requestAnimationFrame: 'readonly',
cancelAnimationFrame: 'readonly',
fetch: 'readonly',
URL: 'readonly',
URLSearchParams: 'readonly',
Blob: 'readonly',
File: 'readonly',
FileReader: 'readonly',
FormData: 'readonly',
Headers: 'readonly',
Request: 'readonly',
Response: 'readonly',
Event: 'readonly',
KeyboardEvent: 'readonly',
MouseEvent: 'readonly',
ClipboardEvent: 'readonly',
DOMParser: 'readonly',
Node: 'readonly',
NodeList: 'readonly',
Element: 'readonly',
DocumentFragment: 'readonly',
CSSStyleSheet: 'readonly',
ShadowRoot: 'readonly'
}
},
rules: {
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'no-console': 'off'
}
},
{
ignores: ['dist/', 'node_modules/', 'docs/', 'test/']
}
]{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "none",
"printWidth": 100
}import { defineConfig } from '@playwright/test'
export default defineConfig({
testDir: './test',
fullyParallel: false,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: 1,
reporter: 'html',
use: {
baseURL: 'http://localhost:5174',
trace: 'on-first-retry'
},
webServer: {
command: 'npx vite --port 5174',
url: 'http://localhost:5174/test/test-page.html',
reuseExistingServer: !process.env.CI,
timeout: 30000
}
})export default {
globs: ['src/**/*.js'],
exclude: ['dist', 'node_modules'],
outdir: './',
litelement: true
}{
"name": "@profpowell/COMPONENT_NAME",
"version": "X.X.X",
"description": "DESCRIPTION",
"type": "module",
"main": "dist/COMPONENT_NAME.js",
"module": "dist/COMPONENT_NAME.js",
"types": "COMPONENT_NAME.d.ts",
"exports": {
".": {
"types": "./COMPONENT_NAME.d.ts",
"import": "./dist/COMPONENT_NAME.js",
"default": "./dist/COMPONENT_NAME.js"
}
},
"files": [
"dist",
"COMPONENT_NAME.d.ts",
"custom-elements.json",
"README.md",
"LICENSE"
],
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "playwright test",
"test:ui": "playwright test --ui",
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"format": "prettier --write \"src/**/*.js\"",
"format:check": "prettier --check \"src/**/*.js\"",
"analyze": "cem analyze --litelement",
"prepublishOnly": "npm run build && npm run analyze"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ProfPowell/COMPONENT_NAME.git"
},
"keywords": ["web-components", "custom-elements", "vanilla-js"],
"author": "ProfPowell",
"license": "MIT",
"bugs": {
"url": "https://github.com/ProfPowell/COMPONENT_NAME/issues"
},
"homepage": "https://profpowell.github.io/COMPONENT_NAME/",
"devDependencies": {
"@custom-elements-manifest/analyzer": "^0.11.0",
"@playwright/test": "^1.57.0",
"eslint": "^9.0.0",
"prettier": "^3.0.0",
"vite": "^6.0.0"
}
}All public classes, methods, properties, and events must have JSDoc comments.
/**
* A web component that displays syntax-highlighted code blocks.
*
* @element code-block
* @fires copy - Fired when code is copied to clipboard
* @fires collapse - Fired when block is collapsed/expanded
*
* @csspart container - The main container element
* @csspart code - The code display area
*
* @cssprop [--code-block-bg=#1e1e1e] - Background color
* @cssprop [--code-block-text=#d4d4d4] - Text color
*
* @slot - Default slot for code content
*/
class CodeBlock extends HTMLElement {/**
* The programming language for syntax highlighting.
* @type {string}
* @attr language
*/
get language() {
return this.getAttribute('language') || 'text'
}/**
* Copies the code content to the clipboard.
* @returns {Promise<void>}
* @fires copy
*/
async copyCode() {/**
* @event copy
* @type {CustomEvent}
* @property {string} detail.code - The copied code
*/
this.dispatchEvent(new CustomEvent('copy', {
detail: { code: this.getCode() }
}))Create manual .d.ts files at repo root.
/**
* A web component that displays syntax-highlighted code blocks.
*/
export class CodeBlock extends HTMLElement {
/** The programming language for syntax highlighting */
language: string;
/** Whether to show line numbers */
showLines: boolean;
/** Copy the code to clipboard */
copyCode(): Promise<void>;
/** Get the current code content */
getCode(): string;
/** Set new code content */
setCode(code: string): void;
}
declare global {
interface HTMLElementTagNameMap {
'code-block': CodeBlock;
}
}Create test/COMPONENT_NAME.spec.js and test/test-page.html:
test/test-page.html (loads local source for testing):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>COMPONENT_NAME Test Page</title>
<script type="module" src="../src/COMPONENT_NAME.js"></script>
</head>
<body>
<COMPONENT_NAME>Test content</COMPONENT_NAME>
</body>
</html>test/COMPONENT_NAME.spec.js:
import { test, expect } from '@playwright/test'
test.describe('COMPONENT_NAME', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/test/test-page.html')
})
test('renders correctly', async ({ page }) => {
const component = page.locator('COMPONENT_NAME')
await expect(component).toBeVisible()
})
test('has shadow DOM', async ({ page }) => {
const hasShadow = await page.evaluate(() => {
const el = document.querySelector('COMPONENT_NAME')
return el?.shadowRoot !== null
})
expect(hasShadow).toBe(true)
})
// Add component-specific tests
})CRITICAL: Every component MUST have a full documentation site hosted on GitHub Pages, not just a demo page. Reference: https://profpowell.github.io/browser-window/
docs/
├── index.html # Home page with overview, features, quick start
├── demos.html # Interactive examples and use cases
├── api.html # Full API documentation
└── styles.css # Shared styles
IMPORTANT: The docs/ folder uses CDN links (jsdelivr) for production GitHub Pages. The test/test-page.html file uses local ../src/ paths for testing.
Must include:
- Header navigation: Links to Home, Demos, API, GitHub repo
- Hero section: Component name, tagline, quick action buttons
- Features grid: 4-6 key capabilities with icons/descriptions
- Quick Start: Installation (npm + CDN) and basic usage examples
- Related components nav: Links to sibling components
- Footer: License, credits, links
Must include:
- All component variations and configurations
- Live interactive examples
- Code snippets for each example
- Organized by feature/use case
Must include:
- All attributes with types and defaults
- All methods with signatures and descriptions
- All events with payload details
- All CSS custom properties
- All CSS parts and slots
- Clean, minimal design (similar to browser-window site)
- Dark/light theme support
- Mobile responsive
- Consistent header/footer across all pages
- GitHub-inspired styling
- Fast loading (no heavy frameworks)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>COMPONENT_NAME - Description</title>
<!-- Use CDN for production GitHub Pages -->
<script type="module" src="https://cdn.jsdelivr.net/gh/ProfPowell/COMPONENT_NAME@main/dist/COMPONENT_NAME.js"></script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header class="site-header">
<nav>
<a href="index.html" class="nav-brand"><COMPONENT_NAME></a>
<div class="nav-links">
<a href="index.html" class="active">Home</a>
<a href="demos.html">Demos</a>
<a href="api.html">API</a>
<a href="https://github.com/ProfPowell/COMPONENT_NAME">GitHub</a>
</div>
</nav>
</header>
<main>
<section class="hero">
<h1><COMPONENT_NAME></h1>
<p class="tagline">SHORT_DESCRIPTION</p>
<div class="hero-actions">
<a href="demos.html" class="btn primary">View Demos</a>
<a href="api.html" class="btn secondary">API Docs</a>
</div>
</section>
<section class="features">
<h2>Features</h2>
<div class="feature-grid">
<!-- 4-6 feature cards -->
<div class="feature-card">
<h3>Feature Name</h3>
<p>Feature description</p>
</div>
</div>
</section>
<section class="quick-start">
<h2>Quick Start</h2>
<h3>Installation</h3>
<code-block language="bash">npm install @profpowell/COMPONENT_NAME</code-block>
<p>Or via CDN:</p>
<code-block language="html"><script type="module" src="https://unpkg.com/@profpowell/COMPONENT_NAME"></script></code-block>
<h3>Basic Usage</h3>
<code-block language="html"><COMPONENT_NAME>...</COMPONENT_NAME></code-block>
</section>
<section class="related">
<h2>Related Components</h2>
<div class="related-grid">
<a href="https://profpowell.github.io/code-block/" class="related-card">
<strong>code-block</strong>
<span>Syntax highlighted code blocks</span>
</a>
<a href="https://profpowell.github.io/browser-window/" class="related-card">
<strong>browser-window</strong>
<span>Safari-style browser frames</span>
</a>
<a href="https://profpowell.github.io/terminal-window/" class="related-card">
<strong>terminal-window</strong>
<span>Interactive terminal emulator</span>
</a>
<a href="https://profpowell.github.io/browser-console/" class="related-card">
<strong>browser-console</strong>
<span>Console log display</span>
</a>
<a href="https://profpowell.github.io/http-component/" class="related-card">
<strong>http-component</strong>
<span>HTTP request/response viewer</span>
</a>
</div>
</section>
</main>
<footer class="site-footer">
<p>MIT License | <a href="https://github.com/ProfPowell/COMPONENT_NAME">View on GitHub</a></p>
</footer>
</body>
</html>- Go to repo Settings → Pages
- Source: Deploy from a branch
- Branch: main
- Folder: /docs
- Save
Or via CLI:
gh api repos/ProfPowell/COMPONENT_NAME/pages -X POST --input - <<EOF
{
"source": {
"branch": "main",
"path": "/docs"
}
}
EOFThe site will be available at https://profpowell.github.io/COMPONENT_NAME/
Before publishing:
- Version bumped in package.json
- CHANGELOG.md updated
-
npm run buildsucceeds -
npm run testpasses -
npm run lintpasses -
npm run analyzegenerates custom-elements.json - README.md has accurate documentation
- Cross-links to sibling components are present
Publish:
npm login # If not logged in
npm publish --access public # First time
npm publish # Subsequent timesUse this checklist when standardizing a repository:
- Source in
src/directory - Docs site in
docs/(index.html, demos.html, api.html, styles.css) - Tests in
test/directory (test-page.html + spec files) - Built output in
dist/(ES module only)
- vite.config.js present and correct
- eslint.config.js present
- .prettierrc present
- playwright.config.js present
- custom-elements-manifest.config.mjs present
- name is @profpowell/COMPONENT_NAME
- type is "module"
- main points to dist/
- exports configured correctly
- Only 5 devDependencies
- All standard scripts present
- homepage points to GitHub Pages
- JSDoc comments on all public API
- Manual .d.ts file at repo root
- README.md with examples
- CHANGELOG.md maintained
- Cross-links to sibling components
- docs/index.html - Home page with hero, features, quick start
- docs/demos.html - Interactive examples page
- docs/api.html - Full API documentation
- docs/styles.css - Shared styles
- Uses CDN links for component (not local src)
- Header navigation on all pages
- Footer with license and links
- Dark/light theme support
- Mobile responsive
- Related components section
-
npm run lintpasses -
npm run format:checkpasses -
npm run testpasses -
npm run buildsucceeds
- GitHub Pages enabled
- Published to npm under @profpowell scope
mkdir -p src
git mv component-name.js src/
# Update vite.config.js entry pointmkdir -p docs
git mv demo.html docs/index.html
# Or: git mv demos/index.html docs/index.html
# Update vite.config.js server.open to /docs/index.html
# Update HTML to use CDN links for productionnpm uninstall @storybook/web-components @storybook/addon-essentials \
vitest jsdom @web/test-runner @open-wc/testing chai \
esbuild terser typescriptnpm install -D vite@^6.0.0 eslint@^9.0.0 prettier@^3.0.0 \
@playwright/test@^1.57.0 @custom-elements-manifest/analyzer@^0.11.0
npx playwright install # Install browsersChange formats: ['es', 'umd'] to formats: ['es'] and remove UMD-related config.
Every component should reference siblings in:
- README.md - "Related Components" section
- docs/index.html - "Related Components" section at bottom
- package.json - keywords include "profpowell-web-components"
This promotes the vanilla web component ecosystem and helps users discover related tools.