AI agent skill
Web Component Design
Master React, Vue, and Svelte component patterns including CSS-in-JS, composition strategies, and reusable component architecture. Use when building UI component libraries, designing component APIs, or implementing frontend design systems.
·
When to use this skill
Use Web Component Design when an AI agent needs a reusable SKILL.md workflow for this job: Master React, Vue, and Svelte component patterns including CSS-in-JS, composition strategies, and reusable component architecture. Use when building UI component libraries, designing component APIs, or implementing frontend design systems.
When not to use it
Skip Web Component Design when the task is outside the coding category, or when a more specific skill in this directory already covers the same workflow with clearer triggers.
How to install
- Personal install: create ~/.claude/skills/web-component-design/SKILL.md (and any bundled scripts) so Claude Code, Claude Desktop, and compatible agents can load it in every project.
- Project install: commit the same folder at .claude/skills/web-component-design/ so teammates get the skill with the repo.
- Restart the agent session after copying files so it re-scans the skills directory, then ask for the task in words that match the skill description.
What this skill does
# Web Component Design
Build reusable, maintainable UI components using modern frameworks with clean composition patterns and styling approaches.
## When to Use This Skill
- Designing reusable component libraries or design systems - Implementing complex component composition patterns - Choosing and applying CSS-in-JS solutions - Building accessible, responsive UI components - Creating consistent component APIs across a codebase - Refactoring legacy components into modern patterns - Implementing compound components or render props
## Core Concepts
### 1. Component Composition Patterns
**Compound Components**: Related components that work together
```tsx // Usage <Select value={value} onChange={setValue}> <Select.Trigger>Choose option</Select.Trigger> <Select.Options> <Select.Option value="a">Option A</Select.Option> <Select.Option value="b">Option B</Select.Option> </Select.Options> </Select> ```
**Render Props**: Delegate rendering to parent
```tsx <DataFetcher url="/api/users"> {({ data, loading, error }) => loading ? <Spinner /> : <UserList users={data} /> } </DataFetcher> ```
**Slots (Vue/Svelte)**: Named content injection points
```vue <template> <Card> <template #header>Title</template> <template #content>Body text</template> <template #footer><Button>Action</Button></template> </Card> </template> ```
### 2. CSS-in-JS Approaches
| Solution | Approach | Best For | | --------------------- | ---------------------- | --------------------------------- | | **Tailwind CSS** | Utility classes | Rapid prototyping, design systems | | **CSS Modules** | Scoped CSS files | Existing CSS, gradual adoption | | **styled-components** | Template literals | React, dynamic styling | | **Emotion** | Object/template styles | Flexible, SSR-friendly | | **Vanilla Extract** | Zero-runtime | Performance-critical apps |
### 3. Component API Design
```tsx interface ButtonProps { variant?: "primary" | "secondary" | "ghost"; size?: "sm" | "md" | "lg"; isLoading?: boolean; isDisabled?: boolean; leftIcon?: React.ReactNode; rightIcon?: React.ReactNode; children: React.ReactNode; onClick?: () => void; } ```
**Principles**:
- Use semantic prop names (`isLoading` vs `loading`) - Provide sensible defaults - Support composition via `children` - Allow style overrides via `className` or `style`
## Quick Start: React Component with Tailwind
```tsx import { forwardRef, type ComponentPropsWithoutRef } from "react"; import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "@/lib/utils";
const buttonVariants = cva( "inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50", { variants: { variant: { primary: "bg-blue-600 text-white hover:bg-blue-700", secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200", ghost: "hover:bg-gray-100 hover:text-gray-900", }, size: { sm: "h-8 px-3 text-sm", md: "h-10 px-4 text-sm", lg: "h-12 px-6 text-base", }, }, defaultVariants: { variant: "primary", size: "md", }, }, );
interface ButtonProps extends ComponentPropsWithoutRef<"button">, VariantProps<typeof buttonVariants> { isLoading?: boolean; }
export const Button = forwardRef<HTMLButtonElement, ButtonProps>( ({ className, variant, size, isLoading, children, ...props }, ref) => ( <button ref={ref} className={cn(buttonVariants({ variant, size }), className)} disabled={isLoading || props.disabled} {...props} > {isLoading && <Spinner className="mr-2 h-4 w-4" />} {children} </button> ), ); Button.displayName = "Button"; ```
## Framework Patterns
### React: Compound Components
```tsx import { createContext, useContext, useState, type ReactNode } from "react";
interface AccordionContextValue { openItems: Set<string>; toggle: (id: string) => void; }
const AccordionContext = createContext<AccordionContextValue | null>(null);
function useAccordion() { const context = useContext(AccordionContext); if (!context) throw new Error("Must be used within Accordion"); return context; }
export function Accordion({ children }: { children: ReactNode }) { const [openItems, setOpenItems] = useState<Set<string>>(new Set());
const toggle = (id: string) => { setOpenItems((prev) => { const next = new Set(prev); next.has(id) ? next.delete(id) : next.add(id); return next; }); };
return ( <AccordionContext.Provider value={{ openItems, toggle }}> <div className="divide-y">{children}</div> </AccordionContext.Provider> ); }
Accordion.Item = function AccordionItem({ id, title, children, }: { id: string; title: string; children: ReactNode; }) { const { openItems, toggle } = useAccordion(); const isOpen = openItems.has(id);
return ( <div> <button onClick={() => toggle(id)} className="w-full text-left py-3"> {title} </button> {isOpen && <div className="pb-3">{children}</div>} </div> ); }; ```
### Vue 3: Composables
```vue <script setup lang="ts"> import { ref, computed, provide, inject, type InjectionKey } from "vue";
interface TabsContext { activeTab: Ref<string>; setActive: (id: string) => void; }
const TabsKey: InjectionKey<TabsContext> = Symbol("tabs");
// Parent component const activeTab = ref("tab-1"); provide(TabsKey, { activeTab, setActive: (id: string) => { activeTab.value = id; }, });
// Child component usage const tabs = inject(TabsKey); const isActive = computed(() => tabs?.activeTab.value === props.id); </script> ```
### Svelte 5: Runes
```svelte <script lang="ts"> interface Props { variant?: 'primary' | 'secondary'; size?: 'sm' | 'md' | 'lg'; onclick?: () => void; children: import('svelte').Snippet; }
let { variant = 'primary', size = 'md', onclick, children }: Props = $props();
const classes = $derived( `btn btn-${variant} btn-${size}` ); </script>
<button class={classes} {onclick}> {@render children()} </button> ```
## Best Practices
1. **Single Responsibility**: Each component does one thing well 2. **Prop Drilling Prevention**: Use context for deeply nested data 3. **Accessible by Default**: Include ARIA attributes, keyboard support 4. **Controlled vs Uncontrolled**: Support both patterns when appropriate 5. **Forward Refs**: Allow parent access to DOM nodes 6. **Memoization**: Use `React.memo`, `useMemo` for expensive renders 7. **Error Boundaries**: Wrap components that may fail
## Common Issues
- **Prop Explosion**: Too many props - consider composition instead - **Style Conflicts**: Use scoped styles or CSS Modules - **Re-render Cascades**: Profile with React DevTools, memo appropriately - **Accessibility Gaps**: Test with screen readers and keyboard navigation - **Bundle Size**: Tree-shake unused component variants
Intended uses
- Designing reusable component libraries or design systems
- Implementing complex component composition patterns
- Choosing and applying CSS-in-JS solutions
- Building accessible, responsive UI components
- Creating consistent component APIs across a codebase
- Refactoring legacy components into modern patterns
- Implementing compound components or render props
Related skills
Related skills in this directory, for comparison before you install another skill.
coding
Act as a Patient, Non-Technical Android Studio Guide
A reusable prompt for asking an AI assistant to work as Act as a Patient, Non-Technical Android Studio Guide.
coding
Add Ave Record
The main workflow for this repo. Adds one new AVE record end to end.
coding
Add Backend
Guide for adding a backend (Rust or Python) to the agent-sec-core security middleware. Use when creating new backends, integrating Rust or Python code into the security middleware, or extending with new backend actions.
coding
Agent Device
Drive iOS and Android devices for the Expensify App - testing, debugging, performance profiling, bug reproduction, and feature verification. Use when the developer needs to interact with the mobile app on a device.