Claude-skill-registry component-hierarchy
Guide component selection in ResRequest Vue projects. Ensures correct usage of ShadCN-Vue vs custom components, proper imports, and design system compliance. Activates when creating or modifying Vue components.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/component-hierarchy" ~/.claude/skills/majiayu000-claude-skill-registry-component-hierarchy && rm -rf "$T"
manifest:
skills/data/component-hierarchy/SKILL.mdsource content
Component Hierarchy
Follow this selection order when choosing components for ResRequest projects.
Selection Priority
1. ShadCN-Vue Components (First Choice)
Located at
@/components/ui/
Always check ShadCN-Vue first. These are the base UI components.
NEVER modify files in
- these are managed by ShadCN.@/components/ui/
2. Custom Components (Second Choice)
Located at
@/components/custom/
Project-specific wrappers and extensions of ShadCN components.
3. Feature Components (Third Choice)
Located at
@/components/[feature]/
Domain-specific components for features like email-threads, comments, bookings.
ShadCN-Vue Component Reference
Buttons
import { Button } from '@/components/ui/button' <Button>Default</Button> <Button variant="destructive">Delete</Button> <Button variant="outline">Cancel</Button> <Button variant="secondary">Secondary</Button> <Button variant="ghost">Ghost</Button> <Button variant="link">Link</Button> <Button size="sm">Small</Button> <Button size="lg">Large</Button> <Button disabled>Disabled</Button>
Form Inputs
import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Textarea } from '@/components/ui/textarea' import { Checkbox } from '@/components/ui/checkbox' import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group' import { Switch } from '@/components/ui/switch' // Basic input <div class="space-y-2"> <Label for="email">Email</Label> <Input id="email" type="email" v-model="email" /> </div>
Select/Dropdown
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' <Select v-model="selected"> <SelectTrigger> <SelectValue placeholder="Select option" /> </SelectTrigger> <SelectContent> <SelectItem value="option1">Option 1</SelectItem> <SelectItem value="option2">Option 2</SelectItem> </SelectContent> </Select>
Cards
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from '@/components/ui/card' <Card> <CardHeader> <CardTitle>Card Title</CardTitle> <CardDescription>Card description</CardDescription> </CardHeader> <CardContent> <p>Card content</p> </CardContent> <CardFooter> <Button>Action</Button> </CardFooter> </Card>
Dialogs/Modals
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog' <Dialog v-model:open="isOpen"> <DialogTrigger as-child> <Button>Open Dialog</Button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Dialog Title</DialogTitle> <DialogDescription>Dialog description</DialogDescription> </DialogHeader> <div>Content here</div> <DialogFooter> <Button variant="outline" @click="isOpen = false">Cancel</Button> <Button @click="save">Save</Button> </DialogFooter> </DialogContent> </Dialog>
Tables
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' <Table> <TableHeader> <TableRow> <TableHead>Name</TableHead> <TableHead>Email</TableHead> <TableHead>Actions</TableHead> </TableRow> </TableHeader> <TableBody> <TableRow v-for="user in users" :key="user.id"> <TableCell>{{ user.name }}</TableCell> <TableCell>{{ user.email }}</TableCell> <TableCell> <Button size="sm" variant="ghost">Edit</Button> </TableCell> </TableRow> </TableBody> </Table>
Badges
import { Badge } from '@/components/ui/badge' <Badge>Default</Badge> <Badge variant="secondary">Secondary</Badge> <Badge variant="destructive">Error</Badge> <Badge variant="outline">Outline</Badge>
Alerts
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' <Alert> <AlertTitle>Heads up!</AlertTitle> <AlertDescription>This is an alert message.</AlertDescription> </Alert> <Alert variant="destructive"> <AlertTitle>Error</AlertTitle> <AlertDescription>Something went wrong.</AlertDescription> </Alert>
Tabs
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' <Tabs default-value="tab1"> <TabsList> <TabsTrigger value="tab1">Tab 1</TabsTrigger> <TabsTrigger value="tab2">Tab 2</TabsTrigger> </TabsList> <TabsContent value="tab1">Content 1</TabsContent> <TabsContent value="tab2">Content 2</TabsContent> </Tabs>
Custom Components
FloatingLabelInput (ALWAYS use for form fields)
import FloatingLabelInput from '@/components/custom/FloatingLabelInput.vue' <FloatingLabelInput v-model="form.name" label="Name" :error="form.errors.name" required /> <FloatingLabelInput v-model="form.email" label="Email" type="email" :error="form.errors.email" />
RRIcon (Custom icons)
import RRIcon from '@/components/custom/icon/RRIcon.vue' <RRIcon name="calendar" /> <RRIcon name="user" class="h-5 w-5" />
Component Rules
1. Never Modify ShadCN Files
@/components/ui/ is READ-ONLY To customise ShadCN components: 1. Create wrapper in @/components/custom/ 2. Import and extend the base component 3. Add custom props/styles in wrapper
2. Always Use FloatingLabelInput for Forms
<!-- WRONG: Raw Input --> <Input v-model="name" /> <!-- CORRECT: FloatingLabelInput --> <FloatingLabelInput v-model="name" label="Name" />
3. Use Tailwind Utilities Only
<!-- WRONG: Custom CSS --> <style scoped> .custom-button { background-color: blue; } </style> <!-- CORRECT: Tailwind classes --> <Button class="bg-blue-500 hover:bg-blue-600"> Custom Button </Button>
4. Check ShadCN Examples
Before creating a custom component, check if ShadCN already provides it:
- View examples at
in the app/page/admin/shadcn-components - Check ShadCN-Vue documentation
5. Component Composition
Compose complex UIs from ShadCN primitives:
<Card> <CardHeader> <div class="flex items-center justify-between"> <CardTitle>{{ title }}</CardTitle> <Badge :variant="statusVariant">{{ status }}</Badge> </div> </CardHeader> <CardContent> <Table> <!-- Table content --> </Table> </CardContent> <CardFooter class="flex justify-end gap-2"> <Button variant="outline">Cancel</Button> <Button>Save</Button> </CardFooter> </Card>
Import Patterns
Correct Imports
// ShadCN components - destructured import import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader } from '@/components/ui/card' // Custom components - default import import FloatingLabelInput from '@/components/custom/FloatingLabelInput.vue' import RRIcon from '@/components/custom/icon/RRIcon.vue' // Feature components - default import import CommentThread from '@/components/comments/CommentThread.vue'
Import Organisation
// 1. Vue core import { ref, computed, watch } from 'vue' // 2. Inertia import { useForm, router, Link } from '@inertiajs/vue3' // 3. ShadCN UI components import { Button } from '@/components/ui/button' import { Card, CardContent } from '@/components/ui/card' // 4. Custom components import FloatingLabelInput from '@/components/custom/FloatingLabelInput.vue' // 5. Composables import { useBookings } from '@/composables/useBookings' // 6. Types import type { Booking, Property } from '@/types'