| name | add-component |
| description | Create a reusable UI component with factory pattern, theme compatibility, and proper cleanup |
| aliases | ["new-component","create-component"] |
Add Component Skill
Usage
/add-component <ComponentName>
Step 1: Ask Questions
1. Check Existing Components First
Does an existing component cover ~80% of the need?
→ Check src/ui/components/ before creating new
Existing: ArcadeButton, BasePetCard, GeminiIconButton, Modal, ProgressBar, SegmentedControl, SeeMore, SoundPicker, Tab, TeamListItem
2. Component Purpose
Brief description (1 sentence):
3. Configuration Options
What options does it need?
- Required: ___________
- Optional with defaults: ___________
- Event handlers: ___________
4. Sprites
Does it display game sprites? → MGSprite.toCanvas()
5. Dynamic State
Does it need reactive updates?
A) Manual setters only (setLabel, setDisabled, setValue, etc.)
B) Reactive to Globals (subscribe to myInventory, currentTile, weather, etc.)
C) Both
If B/C: Component subscribes to Globals and auto-updates UI
→ Remember to unsubscribe in destroy()!
6. Child Components (IMPORTANT)
Does this component need sub-components?
→ REUSE existing components, never recreate!
Available:
- ArcadeButton, GeminiIconButton → Buttons
- Modal → Dialogs/popups
- ProgressBar → Progress indicators
- SegmentedControl → Tab-like selection
- Tab → Tabs
- SoundPicker → Audio selection
- BasePetCard, TeamListItem → List items
- SeeMore → Expandable content
Example: A "SettingsPanel" component might use:
- SegmentedControl for sections
- Toggle for on/off settings
- ArcadeButton for actions
Step 2: Create Structure
src/ui/components/<ComponentName>/
├── <ComponentName>.ts # Logic + factory function
├── <componentName>.css.ts # Styles (CSS string)
└── index.ts # Re-exports
Read existing components for templates: src/ui/components/*/
Step 3: Required API
Options Interface
export interface <ComponentName>Options {
label: string;
variant?: 'primary' | 'secondary';
disabled?: boolean;
onClick?: () => void;
}
Handle Interface
export interface <ComponentName>Handle {
root: HTMLElement;
set<Property>(value): void;
destroy(): void;
}
Factory Function
export function create<ComponentName>(options: <ComponentName>Options): <ComponentName>Handle
Step 4: Style Rules
Theme Tokens (REQUIRED)
background: var(--color-bg);
color: var(--color-text);
border: 1px solid var(--color-border);
Responsive (REQUIRED)
min-height: 44px;
width: 100%;
max-width: 300px;
Scoped Styles
.component-name { }
.component-name__label { }
.component-name--variant { }
Step 5: Register
Export → src/ui/components/index.ts
export { create<ComponentName> } from './<ComponentName>/<ComponentName>';
export type { <ComponentName>Options, <ComponentName>Handle } from './<ComponentName>/<ComponentName>';
Step 6: Validate
Required
Styling
Composability
If using Sprites
If using Globals (reactive)
If using Child Components
import { createProgressBar, createArcadeButton } from '../index';
const progressBar = createProgressBar({ ... });
root.appendChild(progressBar.root);
destroy() {
progressBar.destroy();
root.remove();
}
If using Globals (reactive UI)
import { getMyInventory } from '../../../globals/variables/myInventory';
const unsub = getMyInventory().subscribe((inventory) => {
updateItemCount(inventory.items.length);
});
destroy() {
unsub();
root.remove();
}
References
- Rules:
.claude/rules/ui/components.md
- Existing components:
src/ui/components/*/
- Theme tokens:
src/ui/theme/
- Reuse workflow:
.claude/workflows/ui/component/reuse-existing-component.md