원클릭으로
add-component
Create a reusable UI component with factory pattern, theme compatibility, and proper cleanup
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Create a reusable UI component with factory pattern, theme compatibility, and proper cleanup
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Create a new card part within a section with factory/class pattern, Card wrapper, and proper cleanup
Add a new Jotai atom to the state system with type definitions, registry, and Store API access
Scaffold a new toggleable feature with full structure, storage, API exposure, and bootstrap registration
Create a reactive global variable that derives from atoms with subscription support
Create a game UI injection that modifies existing game elements with proper cleanup
Create a new core infrastructure module with standard API, lazy init, and proper structure
| name | add-component |
| description | Create a reusable UI component with factory pattern, theme compatibility, and proper cleanup |
| aliases | ["new-component","create-component"] |
/add-component <ComponentName>
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
Brief description (1 sentence):
What options does it need?
- Required: ___________
- Optional with defaults: ___________
- Event handlers: ___________
Does it display game sprites? → MGSprite.toCanvas()
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()!
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
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/*/
export interface <ComponentName>Options {
// Required (no default)
label: string;
// Optional (have defaults)
variant?: 'primary' | 'secondary';
disabled?: boolean;
// Event handlers
onClick?: () => void;
}
export interface <ComponentName>Handle {
root: HTMLElement; // REQUIRED
set<Property>(value): void; // Public setters (minimal)
destroy(): void; // REQUIRED - cleanup
}
export function create<ComponentName>(options: <ComponentName>Options): <ComponentName>Handle
/* NO hardcoded colors */
background: var(--color-bg);
color: var(--color-text);
border: 1px solid var(--color-border);
min-height: 44px; /* Touch-friendly */
width: 100%; /* Flexible, not fixed */
max-width: 300px; /* Constraint if needed */
/* Prefix all classes with component name */
.component-name { }
.component-name__label { }
.component-name--variant { }
src/ui/components/index.tsexport { create<ComponentName> } from './<ComponentName>/<ComponentName>';
export type { <ComponentName>Options, <ComponentName>Handle } from './<ComponentName>/<ComponentName>';
root: HTMLElement in Handledestroy() removes ALL listeners/observers/intervals:focus-visible)root, not document.headdestroy() called in parent's destroy()MGSprite.toCanvas() (never hardcoded paths)destroy() (memory leak otherwise!)import { createProgressBar, createArcadeButton } from '../index';
// In factory:
const progressBar = createProgressBar({ ... });
root.appendChild(progressBar.root);
// In destroy:
destroy() {
progressBar.destroy();
root.remove();
}
import { getMyInventory } from '../../../globals/variables/myInventory';
// In factory:
const unsub = getMyInventory().subscribe((inventory) => {
// Update UI when inventory changes
updateItemCount(inventory.items.length);
});
// In destroy - CRITICAL!
destroy() {
unsub(); // Unsubscribe from Global
root.remove();
}
.claude/rules/ui/components.mdsrc/ui/components/*/src/ui/theme/.claude/workflows/ui/component/reuse-existing-component.md