| name | create-component |
| description | Create new Preact components following project conventions with proper file structure, TypeScript types, and Tailwind styling. Use when adding UI components, sections, or interactive elements to the site. |
Create Component
Create Preact components for agentconfig.org following project conventions.
File Structure
Every component lives in its own folder under site/src/components/:
site/src/components/
└── {ComponentName}/
├── index.ts # Re-exports component and types
└── {ComponentName}.tsx # Main implementation
Step-by-Step Process
- Create the component folder in
site/src/components/{ComponentName}/
- Copy the template from
assets/component-template.tsx
- Rename and customize the component
- Create index.ts with exports
- Import in App.tsx or parent component
Template Files
index.ts
export { ComponentName } from './ComponentName'
export type { ComponentNameProps } from './ComponentName'
ComponentName.tsx
See assets/component-template.tsx for the full template.
TypeScript Rules
- No semicolons - Omit semicolons at end of statements
- Explicit return types - Always specify
: ReactNode
- Interface over type - Use
interface for props
- JSDoc comments - Document props with
/** */
- No
any - Use proper types or unknown
- Readonly arrays - Use
readonly for array props
interface ListProps {
readonly items: readonly string[]
}
type ListProps = {
items: string[];
}
Styling Rules
- Tailwind utilities - Use Tailwind classes for all styling
- cn() helper - Use
cn() from @/lib/utils for conditional classes
- Mobile-first - Start with mobile, add
md: and lg: for larger screens
- Theme-aware - Use CSS variables for theme colors
<div className="p-4 md:p-6 lg:p-8 bg-background text-foreground">
<div className="p-8 sm:p-4">
Component Guidelines
Keep Components Focused
- One component = one responsibility
- Under 150 lines (split if longer)
- Extract complex logic into custom hooks in
site/src/hooks/
Props Design
- Always include
className prop for style overrides
- Use
children for flexible content
- Use discriminated unions for variants
interface ButtonProps {
variant: 'primary' | 'secondary' | 'ghost'
className?: string
children?: ReactNode
}
Event Handlers
- Prefix with
on: onClick, onToggle, onSelect
- Use specific types, not generic
Function
interface Props {
onSelect: (id: string) => void
onSelect: Function
}
Accessibility
- Semantic HTML - Use
button for buttons, nav for navigation
- ARIA attributes - Add
aria-label, aria-expanded for interactive elements
- Keyboard navigation - Ensure focusable and keyboard-usable
- Focus visible - Use
focus-visible: for focus rings
<button
aria-expanded={isOpen}
aria-controls="menu-content"
className="focus-visible:ring-2 focus-visible:ring-primary"
>
Checklist
Before considering the component complete: