@reinvented/design 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN_GUIDE.md +148 -0
- package/README.md +39 -162
- package/docs/components/alert-dialog.md +32 -0
- package/docs/components/avatar.md +14 -0
- package/docs/components/badge.md +24 -0
- package/docs/components/button.md +69 -0
- package/docs/components/card.md +49 -0
- package/docs/components/dialog.md +46 -0
- package/docs/components/dropdown-menu.md +32 -0
- package/docs/components/index.md +69 -0
- package/docs/components/input.md +34 -0
- package/docs/components/remaining-components.md +253 -0
- package/docs/components/scroll-area.md +17 -0
- package/docs/components/select.md +31 -0
- package/docs/components/separator.md +14 -0
- package/docs/components/sheet.md +32 -0
- package/docs/components/skeleton.md +20 -0
- package/docs/components/table.md +33 -0
- package/docs/components/tabs.md +23 -0
- package/docs/conventions.md +74 -0
- package/docs/layouts/dashboard.md +70 -0
- package/docs/layouts/detail-page.md +83 -0
- package/docs/layouts/index.md +45 -0
- package/docs/layouts/list-page.md +107 -0
- package/docs/layouts/settings-page.md +79 -0
- package/docs/layouts/step-wizard.md +73 -0
- package/docs/patterns/index.md +39 -0
- package/docs/rules.md +43 -0
- package/docs/visual-polish.md +141 -0
- package/package.json +40 -61
- package/src/components/ui/accordion/Accordion.vue +13 -0
- package/src/components/ui/accordion/AccordionContent.vue +20 -0
- package/src/components/ui/accordion/AccordionItem.vue +15 -0
- package/src/components/ui/accordion/AccordionTrigger.vue +25 -0
- package/src/components/ui/accordion/index.ts +4 -0
- package/src/components/ui/alert/Alert.vue +38 -0
- package/src/components/ui/alert/AlertDescription.vue +12 -0
- package/src/components/ui/alert/AlertTitle.vue +12 -0
- package/src/components/ui/alert/index.ts +3 -0
- package/src/components/ui/alert-dialog/AlertDialog.vue +13 -0
- package/src/components/ui/alert-dialog/AlertDialogAction.vue +21 -0
- package/src/components/ui/alert-dialog/AlertDialogCancel.vue +21 -0
- package/src/components/ui/alert-dialog/AlertDialogContent.vue +39 -0
- package/src/components/ui/alert-dialog/AlertDialogDescription.vue +15 -0
- package/src/components/ui/alert-dialog/AlertDialogFooter.vue +12 -0
- package/src/components/ui/alert-dialog/AlertDialogHeader.vue +12 -0
- package/src/components/ui/alert-dialog/AlertDialogTitle.vue +15 -0
- package/src/components/ui/alert-dialog/AlertDialogTrigger.vue +11 -0
- package/src/components/ui/alert-dialog/index.ts +9 -0
- package/src/components/ui/avatar/Avatar.vue +14 -0
- package/src/components/ui/avatar/index.ts +1 -0
- package/src/components/ui/badge/Badge.vue +27 -0
- package/src/components/ui/badge/index.ts +1 -0
- package/src/components/ui/breadcrumb/Breadcrumb.vue +6 -0
- package/src/components/ui/breadcrumb/BreadcrumbEllipsis.vue +12 -0
- package/src/components/ui/breadcrumb/BreadcrumbItem.vue +6 -0
- package/src/components/ui/breadcrumb/BreadcrumbLink.vue +20 -0
- package/src/components/ui/breadcrumb/BreadcrumbList.vue +6 -0
- package/src/components/ui/breadcrumb/BreadcrumbPage.vue +6 -0
- package/src/components/ui/breadcrumb/BreadcrumbSeparator.vue +11 -0
- package/src/components/ui/breadcrumb/index.ts +7 -0
- package/src/components/ui/button/Button.vue +65 -0
- package/src/components/ui/button/index.ts +1 -0
- package/src/components/ui/card/Card.vue +13 -0
- package/src/components/ui/card/CardContent.vue +7 -0
- package/src/components/ui/card/CardDescription.vue +7 -0
- package/src/components/ui/card/CardFooter.vue +7 -0
- package/src/components/ui/card/CardHeader.vue +9 -0
- package/src/components/ui/card/CardTitle.vue +7 -0
- package/src/components/ui/card/index.ts +6 -0
- package/src/components/ui/checkbox/Checkbox.vue +25 -0
- package/src/components/ui/checkbox/index.ts +1 -0
- package/src/components/ui/collapsible/Collapsible.vue +13 -0
- package/src/components/ui/collapsible/index.ts +2 -0
- package/src/components/ui/command/Command.vue +16 -0
- package/src/components/ui/command/CommandEmpty.vue +5 -0
- package/src/components/ui/command/CommandGroup.vue +22 -0
- package/src/components/ui/command/CommandInput.vue +21 -0
- package/src/components/ui/command/CommandItem.vue +22 -0
- package/src/components/ui/command/CommandList.vue +17 -0
- package/src/components/ui/command/CommandSeparator.vue +5 -0
- package/src/components/ui/command/index.ts +7 -0
- package/src/components/ui/context-menu/ContextMenuContent.vue +24 -0
- package/src/components/ui/context-menu/ContextMenuItem.vue +16 -0
- package/src/components/ui/context-menu/ContextMenuLabel.vue +9 -0
- package/src/components/ui/context-menu/ContextMenuSeparator.vue +9 -0
- package/src/components/ui/context-menu/ContextMenuSubContent.vue +14 -0
- package/src/components/ui/context-menu/index.ts +9 -0
- package/src/components/ui/dialog/Dialog.vue +14 -0
- package/src/components/ui/dialog/DialogClose.vue +12 -0
- package/src/components/ui/dialog/DialogContent.vue +48 -0
- package/src/components/ui/dialog/DialogDescription.vue +23 -0
- package/src/components/ui/dialog/DialogFooter.vue +12 -0
- package/src/components/ui/dialog/DialogHeader.vue +12 -0
- package/src/components/ui/dialog/DialogScrollContent.vue +47 -0
- package/src/components/ui/dialog/DialogTitle.vue +23 -0
- package/src/components/ui/dialog/DialogTrigger.vue +12 -0
- package/src/components/ui/dialog/index.ts +9 -0
- package/src/components/ui/dropdown-menu/DropdownMenu.vue +13 -0
- package/src/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue +28 -0
- package/src/components/ui/dropdown-menu/DropdownMenuContent.vue +33 -0
- package/src/components/ui/dropdown-menu/DropdownMenuGroup.vue +11 -0
- package/src/components/ui/dropdown-menu/DropdownMenuItem.vue +27 -0
- package/src/components/ui/dropdown-menu/DropdownMenuLabel.vue +23 -0
- package/src/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue +13 -0
- package/src/components/ui/dropdown-menu/DropdownMenuRadioItem.vue +27 -0
- package/src/components/ui/dropdown-menu/DropdownMenuSeparator.vue +13 -0
- package/src/components/ui/dropdown-menu/DropdownMenuShortcut.vue +12 -0
- package/src/components/ui/dropdown-menu/DropdownMenuSub.vue +13 -0
- package/src/components/ui/dropdown-menu/DropdownMenuSubContent.vue +27 -0
- package/src/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue +23 -0
- package/src/components/ui/dropdown-menu/DropdownMenuTrigger.vue +11 -0
- package/src/components/ui/dropdown-menu/index.ts +14 -0
- package/src/components/ui/form/FormControl.vue +3 -0
- package/src/components/ui/form/FormDescription.vue +6 -0
- package/src/components/ui/form/FormItem.vue +6 -0
- package/src/components/ui/form/FormLabel.vue +10 -0
- package/src/components/ui/form/FormMessage.vue +10 -0
- package/src/components/ui/form/index.ts +9 -0
- package/src/components/ui/hover-card/HoverCard.vue +13 -0
- package/src/components/ui/hover-card/HoverCardContent.vue +26 -0
- package/src/components/ui/hover-card/HoverCardTrigger.vue +11 -0
- package/src/components/ui/hover-card/index.ts +3 -0
- package/src/components/ui/input/Input.vue +23 -0
- package/src/components/ui/input/index.ts +1 -0
- package/src/components/ui/label/Label.vue +18 -0
- package/src/components/ui/label/index.ts +1 -0
- package/src/components/ui/lib/utils.ts +2 -0
- package/src/components/ui/menubar/MenubarContent.vue +15 -0
- package/src/components/ui/menubar/MenubarItem.vue +13 -0
- package/src/components/ui/menubar/MenubarTrigger.vue +13 -0
- package/src/components/ui/menubar/index.ts +5 -0
- package/src/components/ui/navigation-menu/NavigationMenuContent.vue +14 -0
- package/src/components/ui/navigation-menu/NavigationMenuTrigger.vue +15 -0
- package/src/components/ui/navigation-menu/index.ts +4 -0
- package/src/components/ui/pagination/PaginationContent.vue +13 -0
- package/src/components/ui/pagination/PaginationEllipsis.vue +12 -0
- package/src/components/ui/pagination/PaginationNext.vue +14 -0
- package/src/components/ui/pagination/PaginationPrev.vue +14 -0
- package/src/components/ui/pagination/index.ts +6 -0
- package/src/components/ui/popover/Popover.vue +13 -0
- package/src/components/ui/popover/PopoverContent.vue +27 -0
- package/src/components/ui/popover/PopoverTrigger.vue +11 -0
- package/src/components/ui/popover/index.ts +3 -0
- package/src/components/ui/progress/Progress.vue +21 -0
- package/src/components/ui/progress/index.ts +1 -0
- package/src/components/ui/radio-group/RadioGroup.vue +16 -0
- package/src/components/ui/radio-group/RadioGroupItem.vue +24 -0
- package/src/components/ui/radio-group/index.ts +2 -0
- package/src/components/ui/scroll-area/ScrollArea.vue +13 -0
- package/src/components/ui/scroll-area/index.ts +1 -0
- package/src/components/ui/select/Select.vue +13 -0
- package/src/components/ui/select/SelectContent.vue +40 -0
- package/src/components/ui/select/SelectGroup.vue +15 -0
- package/src/components/ui/select/SelectItem.vue +30 -0
- package/src/components/ui/select/SelectLabel.vue +15 -0
- package/src/components/ui/select/SelectSeparator.vue +13 -0
- package/src/components/ui/select/SelectTrigger.vue +23 -0
- package/src/components/ui/select/SelectValue.vue +11 -0
- package/src/components/ui/select/index.ts +8 -0
- package/src/components/ui/separator/Separator.vue +16 -0
- package/src/components/ui/separator/index.ts +1 -0
- package/src/components/ui/sheet/Sheet.vue +13 -0
- package/src/components/ui/sheet/SheetClose.vue +11 -0
- package/src/components/ui/sheet/SheetContent.vue +65 -0
- package/src/components/ui/sheet/SheetDescription.vue +15 -0
- package/src/components/ui/sheet/SheetFooter.vue +12 -0
- package/src/components/ui/sheet/SheetHeader.vue +12 -0
- package/src/components/ui/sheet/SheetTitle.vue +15 -0
- package/src/components/ui/sheet/SheetTrigger.vue +11 -0
- package/src/components/ui/sheet/index.ts +8 -0
- package/src/components/ui/skeleton/Skeleton.vue +9 -0
- package/src/components/ui/skeleton/index.ts +1 -0
- package/src/components/ui/slider/Slider.vue +26 -0
- package/src/components/ui/slider/index.ts +1 -0
- package/src/components/ui/switch/Switch.vue +24 -0
- package/src/components/ui/switch/index.ts +1 -0
- package/src/components/ui/table/Table.vue +13 -0
- package/src/components/ui/table/TableBody.vue +6 -0
- package/src/components/ui/table/TableCaption.vue +6 -0
- package/src/components/ui/table/TableCell.vue +6 -0
- package/src/components/ui/table/TableFooter.vue +6 -0
- package/src/components/ui/table/TableHead.vue +6 -0
- package/src/components/ui/table/TableHeader.vue +6 -0
- package/src/components/ui/table/TableRow.vue +6 -0
- package/src/components/ui/table/index.ts +8 -0
- package/src/components/ui/tabs/Tabs.vue +13 -0
- package/src/components/ui/tabs/TabsContent.vue +21 -0
- package/src/components/ui/tabs/TabsList.vue +21 -0
- package/src/components/ui/tabs/TabsTrigger.vue +21 -0
- package/src/components/ui/tabs/index.ts +4 -0
- package/src/components/ui/textarea/Textarea.vue +29 -0
- package/src/components/ui/textarea/index.ts +1 -0
- package/src/components/ui/toggle/Toggle.vue +40 -0
- package/src/components/ui/toggle/index.ts +1 -0
- package/src/components/ui/toggle-group/ToggleGroup.vue +16 -0
- package/src/components/ui/toggle-group/ToggleGroupItem.vue +21 -0
- package/src/components/ui/toggle-group/index.ts +2 -0
- package/src/components/ui/tooltip/Tooltip.vue +13 -0
- package/src/components/ui/tooltip/TooltipContent.vue +27 -0
- package/src/components/ui/tooltip/TooltipProvider.vue +12 -0
- package/src/components/ui/tooltip/TooltipTrigger.vue +11 -0
- package/src/components/ui/tooltip/index.ts +4 -0
- package/src/env.d.ts +7 -0
- package/src/index.ts +63 -0
- package/src/lib/utils.ts +7 -0
- package/src/patterns/DetailView.vue +46 -0
- package/src/patterns/EmptyState.vue +27 -0
- package/src/patterns/FormView.vue +34 -0
- package/src/patterns/ListView.vue +45 -0
- package/src/styles/index.css +4 -0
- package/src/styles/tokens.css +144 -0
- package/tailwind.config.js +108 -0
- package/tsconfig.json +21 -0
- package/dist/index.css +0 -1890
- package/dist/index.d.ts +0 -406
- package/dist/index.js +0 -1721
- package/dist/index.js.map +0 -1
- package/tailwind.config.ts +0 -174
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Component Index
|
|
2
|
+
|
|
3
|
+
All DS components available from `@reinvented/design`. Each component links to its documentation.
|
|
4
|
+
|
|
5
|
+
## Core Display
|
|
6
|
+
| Component | Doc | Description |
|
|
7
|
+
|-----------|-----|-------------|
|
|
8
|
+
| [Button](button.md) | ✅ | Actions with 7 variants + 6 sizes |
|
|
9
|
+
| [Card](card.md) | ✅ | Content containers with header/footer |
|
|
10
|
+
| [Badge](badge.md) | ✅ | Status labels and tags |
|
|
11
|
+
| [Avatar](avatar.md) | ✅ | User/entity images with fallback |
|
|
12
|
+
| [Separator](separator.md) | ✅ | Visual dividers |
|
|
13
|
+
| [Skeleton](skeleton.md) | ✅ | Loading placeholders |
|
|
14
|
+
| [ScrollArea](scroll-area.md) | ✅ | Custom-scrollbar containers |
|
|
15
|
+
|
|
16
|
+
## Data Input
|
|
17
|
+
| Component | Doc | Description |
|
|
18
|
+
|-----------|-----|-------------|
|
|
19
|
+
| [Input](input.md) | ✅ | Text input fields |
|
|
20
|
+
| [Textarea](remaining-components.md#textarea) | ✅ | Multi-line text |
|
|
21
|
+
| [Select](select.md) | ✅ | Single-value dropdown selection |
|
|
22
|
+
| [Checkbox](remaining-components.md#checkbox) | ✅ | Boolean toggle (checkmark) |
|
|
23
|
+
| [Switch](remaining-components.md#switch) | ✅ | Boolean toggle (slider) |
|
|
24
|
+
| [RadioGroup](remaining-components.md#radiogroup) | ✅ | Single-value from small set |
|
|
25
|
+
| [Slider](remaining-components.md#slider) | ✅ | Numeric range input |
|
|
26
|
+
| [Label](remaining-components.md#label) | ✅ | Form field labels |
|
|
27
|
+
| [Form](remaining-components.md) | ✅ | vee-validate form integration |
|
|
28
|
+
|
|
29
|
+
## Overlays
|
|
30
|
+
| Component | Doc | Description |
|
|
31
|
+
|-----------|-----|-------------|
|
|
32
|
+
| [Dialog](dialog.md) | ✅ | Modal dialogs for forms/actions |
|
|
33
|
+
| [AlertDialog](alert-dialog.md) | ✅ | Confirmation dialogs for destructive actions |
|
|
34
|
+
| [Sheet](sheet.md) | ✅ | Side panels (left/right/top/bottom) |
|
|
35
|
+
| [Popover](remaining-components.md#popover) | ✅ | Floating content panels |
|
|
36
|
+
| [Tooltip](remaining-components.md#tooltip) | ✅ | Hover hints |
|
|
37
|
+
| [HoverCard](remaining-components.md#hovercard) | ✅ | Rich hover previews |
|
|
38
|
+
|
|
39
|
+
## Navigation & Menus
|
|
40
|
+
| Component | Doc | Description |
|
|
41
|
+
|-----------|-----|-------------|
|
|
42
|
+
| [DropdownMenu](dropdown-menu.md) | ✅ | Action menus |
|
|
43
|
+
| [ContextMenu](remaining-components.md) | ✅ | Right-click menus |
|
|
44
|
+
| [Menubar](remaining-components.md) | ✅ | App menu bars |
|
|
45
|
+
| [NavigationMenu](remaining-components.md) | ✅ | Site navigation |
|
|
46
|
+
| [Tabs](tabs.md) | ✅ | Tab navigation |
|
|
47
|
+
| [Breadcrumb](remaining-components.md#breadcrumb) | ✅ | Path navigation |
|
|
48
|
+
| [Pagination](remaining-components.md) | ✅ | Page navigation |
|
|
49
|
+
|
|
50
|
+
## Data Display
|
|
51
|
+
| Component | Doc | Description |
|
|
52
|
+
|-----------|-----|-------------|
|
|
53
|
+
| [Table](table.md) | ✅ | Tabular data |
|
|
54
|
+
| [Accordion](remaining-components.md#accordion) | ✅ | Collapsible sections |
|
|
55
|
+
| [Collapsible](remaining-components.md) | ✅ | Show/hide content |
|
|
56
|
+
| [Progress](remaining-components.md#progress) | ✅ | Progress bars |
|
|
57
|
+
|
|
58
|
+
## Actions
|
|
59
|
+
| Component | Doc | Description |
|
|
60
|
+
|-----------|-----|-------------|
|
|
61
|
+
| [Toggle](remaining-components.md#toggle) | ✅ | State toggle buttons |
|
|
62
|
+
| [ToggleGroup](remaining-components.md) | ✅ | Grouped toggles |
|
|
63
|
+
| [Command](remaining-components.md#command) | ✅ | Command palette / searchable list |
|
|
64
|
+
|
|
65
|
+
## Feedback
|
|
66
|
+
| Component | Doc | Description |
|
|
67
|
+
|-----------|-----|-------------|
|
|
68
|
+
| [Alert](remaining-components.md#alert) | ✅ | Inline notifications |
|
|
69
|
+
| Toast (`vue-sonner`) | — | Ephemeral notifications |
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Input
|
|
2
|
+
|
|
3
|
+
## Skeleton Code
|
|
4
|
+
|
|
5
|
+
```vue
|
|
6
|
+
<script setup>
|
|
7
|
+
import { Input, Label } from '@reinvented/design'
|
|
8
|
+
import { ref } from 'vue'
|
|
9
|
+
const value = ref('')
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<div class="space-y-2">
|
|
14
|
+
<Label for="email">Email</Label>
|
|
15
|
+
<Input id="email" v-model="value" type="email" placeholder="name@example.com" />
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## With Icon
|
|
21
|
+
|
|
22
|
+
```vue
|
|
23
|
+
<template>
|
|
24
|
+
<div class="relative">
|
|
25
|
+
<Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
26
|
+
<Input class="pl-9" placeholder="Search..." />
|
|
27
|
+
</div>
|
|
28
|
+
</template>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Gotchas
|
|
32
|
+
- Always pair with `<Label>` for accessibility
|
|
33
|
+
- Use `placeholder` for format hints, not labels
|
|
34
|
+
- Supports `v-model` for two-way binding
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# Tooltip
|
|
2
|
+
## Skeleton Code
|
|
3
|
+
```vue
|
|
4
|
+
<script setup>
|
|
5
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Button } from '@reinvented/design'
|
|
6
|
+
</script>
|
|
7
|
+
<template>
|
|
8
|
+
<TooltipProvider>
|
|
9
|
+
<Tooltip>
|
|
10
|
+
<TooltipTrigger as-child>
|
|
11
|
+
<Button variant="ghost" size="icon"><Settings class="w-4 h-4" /></Button>
|
|
12
|
+
</TooltipTrigger>
|
|
13
|
+
<TooltipContent><p>Settings</p></TooltipContent>
|
|
14
|
+
</Tooltip>
|
|
15
|
+
</TooltipProvider>
|
|
16
|
+
</template>
|
|
17
|
+
```
|
|
18
|
+
## Gotchas
|
|
19
|
+
- Wrap the app root with `<TooltipProvider>` once
|
|
20
|
+
- Use for icon-only buttons to provide text labels
|
|
21
|
+
- Keep tooltip text to 1-2 words
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# Popover
|
|
26
|
+
## Skeleton Code
|
|
27
|
+
```vue
|
|
28
|
+
<script setup>
|
|
29
|
+
import { Popover, PopoverContent, PopoverTrigger, Button } from '@reinvented/design'
|
|
30
|
+
</script>
|
|
31
|
+
<template>
|
|
32
|
+
<Popover>
|
|
33
|
+
<PopoverTrigger as-child>
|
|
34
|
+
<Button variant="outline">Open</Button>
|
|
35
|
+
</PopoverTrigger>
|
|
36
|
+
<PopoverContent class="w-80">
|
|
37
|
+
<div class="grid gap-4">
|
|
38
|
+
<h4 class="font-medium leading-none">Dimensions</h4>
|
|
39
|
+
<p class="text-sm text-muted-foreground">Set the dimensions for the layer.</p>
|
|
40
|
+
</div>
|
|
41
|
+
</PopoverContent>
|
|
42
|
+
</Popover>
|
|
43
|
+
</template>
|
|
44
|
+
```
|
|
45
|
+
## Gotchas
|
|
46
|
+
- Override width with class on `PopoverContent`
|
|
47
|
+
- Default alignment is `center` — use `align="start"` or `align="end"` as needed
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
# HoverCard
|
|
52
|
+
## Skeleton Code
|
|
53
|
+
```vue
|
|
54
|
+
<script setup>
|
|
55
|
+
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@reinvented/design'
|
|
56
|
+
</script>
|
|
57
|
+
<template>
|
|
58
|
+
<HoverCard>
|
|
59
|
+
<HoverCardTrigger as-child>
|
|
60
|
+
<a href="/user/janakan" class="underline">@janakan</a>
|
|
61
|
+
</HoverCardTrigger>
|
|
62
|
+
<HoverCardContent class="w-80">
|
|
63
|
+
<div class="flex justify-between gap-4">
|
|
64
|
+
<!-- User preview card content -->
|
|
65
|
+
</div>
|
|
66
|
+
</HoverCardContent>
|
|
67
|
+
</HoverCard>
|
|
68
|
+
</template>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
# Alert
|
|
74
|
+
## Variants
|
|
75
|
+
| Variant | Use for |
|
|
76
|
+
|---------|---------|
|
|
77
|
+
| `default` | Informational notices |
|
|
78
|
+
| `destructive` | Errors, critical warnings |
|
|
79
|
+
|
|
80
|
+
## Skeleton Code
|
|
81
|
+
```vue
|
|
82
|
+
<script setup>
|
|
83
|
+
import { Alert, AlertDescription, AlertTitle } from '@reinvented/design'
|
|
84
|
+
import { AlertCircle } from 'lucide-vue-next'
|
|
85
|
+
</script>
|
|
86
|
+
<template>
|
|
87
|
+
<Alert variant="destructive">
|
|
88
|
+
<AlertCircle class="h-4 w-4" />
|
|
89
|
+
<AlertTitle>Error</AlertTitle>
|
|
90
|
+
<AlertDescription>Something went wrong.</AlertDescription>
|
|
91
|
+
</Alert>
|
|
92
|
+
</template>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
# Form Components
|
|
98
|
+
|
|
99
|
+
## Label
|
|
100
|
+
```vue
|
|
101
|
+
<script setup>
|
|
102
|
+
import { Label } from '@reinvented/design'
|
|
103
|
+
</script>
|
|
104
|
+
<template><Label for="email">Email</Label></template>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Textarea
|
|
108
|
+
```vue
|
|
109
|
+
<script setup>
|
|
110
|
+
import { Textarea, Label } from '@reinvented/design'
|
|
111
|
+
</script>
|
|
112
|
+
<template>
|
|
113
|
+
<div class="space-y-2">
|
|
114
|
+
<Label for="bio">Bio</Label>
|
|
115
|
+
<Textarea id="bio" placeholder="Tell us about yourself" />
|
|
116
|
+
</div>
|
|
117
|
+
</template>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Switch
|
|
121
|
+
```vue
|
|
122
|
+
<script setup>
|
|
123
|
+
import { Switch, Label } from '@reinvented/design'
|
|
124
|
+
</script>
|
|
125
|
+
<template>
|
|
126
|
+
<div class="flex items-center gap-2">
|
|
127
|
+
<Switch id="dark-mode" />
|
|
128
|
+
<Label for="dark-mode">Dark mode</Label>
|
|
129
|
+
</div>
|
|
130
|
+
</template>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Checkbox
|
|
134
|
+
```vue
|
|
135
|
+
<script setup>
|
|
136
|
+
import { Checkbox, Label } from '@reinvented/design'
|
|
137
|
+
</script>
|
|
138
|
+
<template>
|
|
139
|
+
<div class="flex items-center gap-2">
|
|
140
|
+
<Checkbox id="terms" />
|
|
141
|
+
<Label for="terms">Accept terms and conditions</Label>
|
|
142
|
+
</div>
|
|
143
|
+
</template>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## RadioGroup
|
|
147
|
+
```vue
|
|
148
|
+
<script setup>
|
|
149
|
+
import { RadioGroup, RadioGroupItem, Label } from '@reinvented/design'
|
|
150
|
+
</script>
|
|
151
|
+
<template>
|
|
152
|
+
<RadioGroup default-value="comfortable">
|
|
153
|
+
<div class="flex items-center gap-2">
|
|
154
|
+
<RadioGroupItem value="default" id="r1" />
|
|
155
|
+
<Label for="r1">Default</Label>
|
|
156
|
+
</div>
|
|
157
|
+
<div class="flex items-center gap-2">
|
|
158
|
+
<RadioGroupItem value="comfortable" id="r2" />
|
|
159
|
+
<Label for="r2">Comfortable</Label>
|
|
160
|
+
</div>
|
|
161
|
+
</RadioGroup>
|
|
162
|
+
</template>
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
# Accordion
|
|
168
|
+
## Skeleton Code
|
|
169
|
+
```vue
|
|
170
|
+
<script setup>
|
|
171
|
+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@reinvented/design'
|
|
172
|
+
</script>
|
|
173
|
+
<template>
|
|
174
|
+
<Accordion type="single" collapsible>
|
|
175
|
+
<AccordionItem value="item-1">
|
|
176
|
+
<AccordionTrigger>Is it accessible?</AccordionTrigger>
|
|
177
|
+
<AccordionContent>Yes. It adheres to the WAI-ARIA design pattern.</AccordionContent>
|
|
178
|
+
</AccordionItem>
|
|
179
|
+
</Accordion>
|
|
180
|
+
</template>
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
# Breadcrumb
|
|
186
|
+
## Skeleton Code
|
|
187
|
+
```vue
|
|
188
|
+
<script setup>
|
|
189
|
+
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from '@reinvented/design'
|
|
190
|
+
</script>
|
|
191
|
+
<template>
|
|
192
|
+
<Breadcrumb>
|
|
193
|
+
<BreadcrumbList>
|
|
194
|
+
<BreadcrumbItem><BreadcrumbLink href="/">Home</BreadcrumbLink></BreadcrumbItem>
|
|
195
|
+
<BreadcrumbSeparator />
|
|
196
|
+
<BreadcrumbItem><BreadcrumbLink href="/items">Items</BreadcrumbLink></BreadcrumbItem>
|
|
197
|
+
<BreadcrumbSeparator />
|
|
198
|
+
<BreadcrumbItem><BreadcrumbPage>Current</BreadcrumbPage></BreadcrumbItem>
|
|
199
|
+
</BreadcrumbList>
|
|
200
|
+
</Breadcrumb>
|
|
201
|
+
</template>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
# Progress
|
|
207
|
+
```vue
|
|
208
|
+
<script setup>
|
|
209
|
+
import { Progress } from '@reinvented/design'
|
|
210
|
+
</script>
|
|
211
|
+
<template><Progress :model-value="60" /></template>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
# Slider
|
|
215
|
+
```vue
|
|
216
|
+
<script setup>
|
|
217
|
+
import { Slider } from '@reinvented/design'
|
|
218
|
+
</script>
|
|
219
|
+
<template><Slider :model-value="[50]" :max="100" :step="1" /></template>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
# Toggle
|
|
223
|
+
```vue
|
|
224
|
+
<script setup>
|
|
225
|
+
import { Toggle } from '@reinvented/design'
|
|
226
|
+
import { Bold } from 'lucide-vue-next'
|
|
227
|
+
</script>
|
|
228
|
+
<template><Toggle variant="outline" aria-label="Toggle bold"><Bold class="h-4 w-4" /></Toggle></template>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
# Command
|
|
232
|
+
## When to Use
|
|
233
|
+
- Command palette (Ctrl+K)
|
|
234
|
+
- Searchable list selection
|
|
235
|
+
|
|
236
|
+
## Skeleton Code
|
|
237
|
+
```vue
|
|
238
|
+
<script setup>
|
|
239
|
+
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@reinvented/design'
|
|
240
|
+
</script>
|
|
241
|
+
<template>
|
|
242
|
+
<Command>
|
|
243
|
+
<CommandInput placeholder="Type to search..." />
|
|
244
|
+
<CommandList>
|
|
245
|
+
<CommandEmpty>No results found.</CommandEmpty>
|
|
246
|
+
<CommandGroup heading="Actions">
|
|
247
|
+
<CommandItem value="create">Create new item</CommandItem>
|
|
248
|
+
<CommandItem value="search">Search items</CommandItem>
|
|
249
|
+
</CommandGroup>
|
|
250
|
+
</CommandList>
|
|
251
|
+
</Command>
|
|
252
|
+
</template>
|
|
253
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# ScrollArea
|
|
2
|
+
## Skeleton Code
|
|
3
|
+
```vue
|
|
4
|
+
<script setup>
|
|
5
|
+
import { ScrollArea } from '@reinvented/design'
|
|
6
|
+
</script>
|
|
7
|
+
<template>
|
|
8
|
+
<ScrollArea class="h-72 w-48 rounded-md border">
|
|
9
|
+
<div class="p-4">
|
|
10
|
+
<!-- Scrollable content -->
|
|
11
|
+
</div>
|
|
12
|
+
</ScrollArea>
|
|
13
|
+
</template>
|
|
14
|
+
```
|
|
15
|
+
## Gotchas
|
|
16
|
+
- Must have explicit height set via class
|
|
17
|
+
- Use instead of native scrollbars per DS rules
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Select
|
|
2
|
+
## When to Use
|
|
3
|
+
- Single-value selection from a list of options
|
|
4
|
+
- When NOT to use: multi-select (use Checkbox group), binary toggle (use Switch)
|
|
5
|
+
|
|
6
|
+
## Skeleton Code
|
|
7
|
+
```vue
|
|
8
|
+
<script setup>
|
|
9
|
+
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from '@reinvented/design'
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<Select>
|
|
14
|
+
<SelectTrigger class="w-[180px]">
|
|
15
|
+
<SelectValue placeholder="Select a fruit" />
|
|
16
|
+
</SelectTrigger>
|
|
17
|
+
<SelectContent>
|
|
18
|
+
<SelectGroup>
|
|
19
|
+
<SelectLabel>Fruits</SelectLabel>
|
|
20
|
+
<SelectItem value="apple">Apple</SelectItem>
|
|
21
|
+
<SelectItem value="banana">Banana</SelectItem>
|
|
22
|
+
<SelectItem value="orange">Orange</SelectItem>
|
|
23
|
+
</SelectGroup>
|
|
24
|
+
</SelectContent>
|
|
25
|
+
</Select>
|
|
26
|
+
</template>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Gotchas
|
|
30
|
+
- Use `placeholder` on `SelectValue` for the empty state text
|
|
31
|
+
- Supports `v-model` via `modelValue` prop on `Select`
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Separator
|
|
2
|
+
## Skeleton Code
|
|
3
|
+
```vue
|
|
4
|
+
<script setup>
|
|
5
|
+
import { Separator } from '@reinvented/design'
|
|
6
|
+
</script>
|
|
7
|
+
<template>
|
|
8
|
+
<Separator /> <!-- Horizontal -->
|
|
9
|
+
<Separator orientation="vertical" /> <!-- Vertical -->
|
|
10
|
+
</template>
|
|
11
|
+
```
|
|
12
|
+
## Gotchas
|
|
13
|
+
- Use between logical sections, not decoratively
|
|
14
|
+
- Vertical separators need a container with `flex` and explicit height
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Sheet
|
|
2
|
+
## When to Use
|
|
3
|
+
- Side panels for details or filters
|
|
4
|
+
- Mobile navigation drawers
|
|
5
|
+
- Quick edit forms that need more space than a dialog
|
|
6
|
+
|
|
7
|
+
## Skeleton Code
|
|
8
|
+
```vue
|
|
9
|
+
<script setup>
|
|
10
|
+
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger, Button } from '@reinvented/design'
|
|
11
|
+
</script>
|
|
12
|
+
<template>
|
|
13
|
+
<Sheet>
|
|
14
|
+
<SheetTrigger as-child>
|
|
15
|
+
<Button variant="outline">Open</Button>
|
|
16
|
+
</SheetTrigger>
|
|
17
|
+
<SheetContent>
|
|
18
|
+
<SheetHeader>
|
|
19
|
+
<SheetTitle>Edit Profile</SheetTitle>
|
|
20
|
+
<SheetDescription>Make changes to your profile here.</SheetDescription>
|
|
21
|
+
</SheetHeader>
|
|
22
|
+
<!-- Content here -->
|
|
23
|
+
</SheetContent>
|
|
24
|
+
</Sheet>
|
|
25
|
+
</template>
|
|
26
|
+
```
|
|
27
|
+
## Side Variants
|
|
28
|
+
Use `side` prop on `SheetContent`: `"top"`, `"bottom"`, `"left"`, `"right"` (default: `"right"`).
|
|
29
|
+
|
|
30
|
+
## Gotchas
|
|
31
|
+
- Default width is `sm:max-w-sm` — override with class if wider content needed
|
|
32
|
+
- Mobile: use `side="bottom"` for bottom sheets
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Skeleton
|
|
2
|
+
## Skeleton Code
|
|
3
|
+
```vue
|
|
4
|
+
<script setup>
|
|
5
|
+
import { Skeleton } from '@reinvented/design'
|
|
6
|
+
</script>
|
|
7
|
+
<template>
|
|
8
|
+
<!-- Match shape of real content -->
|
|
9
|
+
<div class="flex items-center gap-3">
|
|
10
|
+
<Skeleton class="w-10 h-10 rounded-full" />
|
|
11
|
+
<div class="flex-1 space-y-2">
|
|
12
|
+
<Skeleton class="h-4 w-3/4" />
|
|
13
|
+
<Skeleton class="h-3 w-1/2" />
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</template>
|
|
17
|
+
```
|
|
18
|
+
## Gotchas
|
|
19
|
+
- Must match the shape of actual content layout
|
|
20
|
+
- Never show bare spinner — always show skeletons matching the data shape
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Table
|
|
2
|
+
## Sub-components
|
|
3
|
+
`Table`, `TableHeader`, `TableBody`, `TableFooter`, `TableRow`, `TableHead`, `TableCell`, `TableCaption`
|
|
4
|
+
|
|
5
|
+
## Skeleton Code
|
|
6
|
+
```vue
|
|
7
|
+
<script setup>
|
|
8
|
+
import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from '@reinvented/design'
|
|
9
|
+
</script>
|
|
10
|
+
<template>
|
|
11
|
+
<Table>
|
|
12
|
+
<TableCaption>A list of your recent invoices.</TableCaption>
|
|
13
|
+
<TableHeader>
|
|
14
|
+
<TableRow>
|
|
15
|
+
<TableHead>Invoice</TableHead>
|
|
16
|
+
<TableHead>Status</TableHead>
|
|
17
|
+
<TableHead class="text-right">Amount</TableHead>
|
|
18
|
+
</TableRow>
|
|
19
|
+
</TableHeader>
|
|
20
|
+
<TableBody>
|
|
21
|
+
<TableRow v-for="item in items" :key="item.id">
|
|
22
|
+
<TableCell class="font-medium">{{ item.invoice }}</TableCell>
|
|
23
|
+
<TableCell>{{ item.status }}</TableCell>
|
|
24
|
+
<TableCell class="text-right">{{ item.amount }}</TableCell>
|
|
25
|
+
</TableRow>
|
|
26
|
+
</TableBody>
|
|
27
|
+
</Table>
|
|
28
|
+
</template>
|
|
29
|
+
```
|
|
30
|
+
## Gotchas
|
|
31
|
+
- Use `text-right` for numeric columns
|
|
32
|
+
- Use `font-medium` for the primary identifier column
|
|
33
|
+
- For empty state, show a full-width row with empty state message
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Tabs
|
|
2
|
+
## Skeleton Code
|
|
3
|
+
```vue
|
|
4
|
+
<script setup>
|
|
5
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@reinvented/design'
|
|
6
|
+
</script>
|
|
7
|
+
<template>
|
|
8
|
+
<Tabs default-value="overview">
|
|
9
|
+
<TabsList>
|
|
10
|
+
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
11
|
+
<TabsTrigger value="analytics">Analytics</TabsTrigger>
|
|
12
|
+
<TabsTrigger value="settings">Settings</TabsTrigger>
|
|
13
|
+
</TabsList>
|
|
14
|
+
<TabsContent value="overview">Overview content</TabsContent>
|
|
15
|
+
<TabsContent value="analytics">Analytics content</TabsContent>
|
|
16
|
+
<TabsContent value="settings">Settings content</TabsContent>
|
|
17
|
+
</Tabs>
|
|
18
|
+
</template>
|
|
19
|
+
```
|
|
20
|
+
## Gotchas
|
|
21
|
+
- Use `default-value` for uncontrolled, `v-model` for controlled
|
|
22
|
+
- Tab values must be unique strings
|
|
23
|
+
- Keep tab labels short (1-2 words)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# UX Conventions
|
|
2
|
+
|
|
3
|
+
Opinionated design decisions that ensure consistency across all Reinvented apps.
|
|
4
|
+
|
|
5
|
+
## Create/Edit Flows
|
|
6
|
+
|
|
7
|
+
| Scenario | Pattern | Why |
|
|
8
|
+
|----------|---------|-----|
|
|
9
|
+
| Simple form (≤ 5 fields) | Modal dialog | Fast, keeps context |
|
|
10
|
+
| Medium form (6–8 fields) | Modal dialog with sections | Still fits in a modal |
|
|
11
|
+
| Complex form (8+ fields) | Full page | Too much for a modal |
|
|
12
|
+
| Rich content editing (markdown, images) | Full page | Needs space and toolbars |
|
|
13
|
+
| Single-field update (e.g., rename) | Inline editing (click-to-edit) | Minimal friction |
|
|
14
|
+
| Toggle/switch (enable/disable) | Inline toggle | Immediate feedback |
|
|
15
|
+
| Status change | Inline dropdown or button group | Immediate feedback |
|
|
16
|
+
|
|
17
|
+
**Default: modal.** Only escalate to full page when the modal becomes cramped.
|
|
18
|
+
|
|
19
|
+
## Delete/Destructive Actions
|
|
20
|
+
|
|
21
|
+
1. Always use `<AlertDialog>` with explicit confirmation
|
|
22
|
+
2. Confirm button must say what it does: "Delete Task" not "OK"
|
|
23
|
+
3. Cancel button is always the default focus
|
|
24
|
+
4. Consider "undo via toast" for low-risk deletions instead of pre-confirmation
|
|
25
|
+
|
|
26
|
+
## Navigation
|
|
27
|
+
|
|
28
|
+
- **Back button**: Always present on detail views. Uses router history, not hardcoded routes
|
|
29
|
+
- **Breadcrumbs**: Use on 3+ level deep hierarchies
|
|
30
|
+
- **Tabs**: Use for related content within a single view (e.g., "Overview / Activity / Settings")
|
|
31
|
+
- **Bottom navigation**: Mobile only, max 5 items
|
|
32
|
+
|
|
33
|
+
## Information Hierarchy
|
|
34
|
+
|
|
35
|
+
- **Page title**: `text-2xl font-bold` — one per page, describes the primary content
|
|
36
|
+
- **Section headings**: `text-lg font-semibold` — group related content
|
|
37
|
+
- **Cards**: Use to visually separate distinct content blocks
|
|
38
|
+
- **Secondary text**: Always `text-muted-foreground`, never arbitrary gray
|
|
39
|
+
|
|
40
|
+
## Empty States
|
|
41
|
+
|
|
42
|
+
Every empty state must have:
|
|
43
|
+
1. A relevant Lucide icon (`w-12 h-12 text-muted-foreground`)
|
|
44
|
+
2. A title explaining what goes here
|
|
45
|
+
3. A description explaining how to get started
|
|
46
|
+
4. A primary CTA button
|
|
47
|
+
|
|
48
|
+
## Loading States
|
|
49
|
+
|
|
50
|
+
- **Page/section load**: Skeleton shapes matching the real content layout
|
|
51
|
+
- **Button during mutation**: Spinner icon + disabled state
|
|
52
|
+
- **Optimistic updates**: Use for toggles, status changes, likes — anything easily reversible
|
|
53
|
+
- **Never**: bare spinner with no context. Always indicate WHAT is loading
|
|
54
|
+
|
|
55
|
+
## Error States
|
|
56
|
+
|
|
57
|
+
- **Query failures**: `<Alert variant="destructive">` with error message + retry button
|
|
58
|
+
- **Mutation failures**: Toast notification with error message + retry action
|
|
59
|
+
- **Form validation**: Inline field errors below the input, shown on blur or submit
|
|
60
|
+
- **Network offline**: Banner at top of page
|
|
61
|
+
|
|
62
|
+
## Responsive Behavior
|
|
63
|
+
|
|
64
|
+
| Breakpoint | Width | Behavior |
|
|
65
|
+
|------------|-------|----------|
|
|
66
|
+
| Mobile | < 640px | Single column, bottom sheets, stacked cards |
|
|
67
|
+
| Tablet | 640-1024px | Two columns possible, side panels collapse |
|
|
68
|
+
| Desktop | > 1024px | Full layout, side panels open |
|
|
69
|
+
|
|
70
|
+
## Consistency
|
|
71
|
+
|
|
72
|
+
- Same object type → same card component everywhere
|
|
73
|
+
- Same action → same pattern everywhere (e.g., all "delete" flows use AlertDialog)
|
|
74
|
+
- Same icon → same meaning everywhere (e.g., Trash2 always means delete)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Dashboard Layout
|
|
2
|
+
|
|
3
|
+
## When to Use
|
|
4
|
+
- App home/overview pages
|
|
5
|
+
- Stats summaries with key metrics
|
|
6
|
+
- Quick-access to recent items
|
|
7
|
+
|
|
8
|
+
## Skeleton Code
|
|
9
|
+
|
|
10
|
+
```vue
|
|
11
|
+
<script setup>
|
|
12
|
+
import {
|
|
13
|
+
Card, CardContent, CardDescription, CardHeader, CardTitle, Skeleton,
|
|
14
|
+
} from '@reinvented/design'
|
|
15
|
+
import { Activity, Users, CreditCard, DollarSign } from 'lucide-vue-next'
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 space-y-6">
|
|
20
|
+
|
|
21
|
+
<!-- Page Header -->
|
|
22
|
+
<div>
|
|
23
|
+
<h1 class="text-2xl font-bold tracking-tight">Dashboard</h1>
|
|
24
|
+
<p class="text-muted-foreground">Overview of your workspace.</p>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<!-- Stats Grid -->
|
|
28
|
+
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
|
29
|
+
<Card>
|
|
30
|
+
<CardHeader class="flex flex-row items-center justify-between pb-2">
|
|
31
|
+
<CardTitle class="text-sm font-medium">Total Revenue</CardTitle>
|
|
32
|
+
<DollarSign class="h-4 w-4 text-muted-foreground" />
|
|
33
|
+
</CardHeader>
|
|
34
|
+
<CardContent>
|
|
35
|
+
<p class="text-2xl font-bold">$45,231</p>
|
|
36
|
+
<p class="text-xs text-muted-foreground">+20.1% from last month</p>
|
|
37
|
+
</CardContent>
|
|
38
|
+
</Card>
|
|
39
|
+
<!-- Repeat for other stats -->
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<!-- Content Grid -->
|
|
43
|
+
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-7">
|
|
44
|
+
<Card class="col-span-4">
|
|
45
|
+
<CardHeader>
|
|
46
|
+
<CardTitle>Overview</CardTitle>
|
|
47
|
+
</CardHeader>
|
|
48
|
+
<CardContent>
|
|
49
|
+
<!-- Chart or main content -->
|
|
50
|
+
</CardContent>
|
|
51
|
+
</Card>
|
|
52
|
+
<Card class="col-span-3">
|
|
53
|
+
<CardHeader>
|
|
54
|
+
<CardTitle>Recent Activity</CardTitle>
|
|
55
|
+
<CardDescription>Latest actions in your workspace.</CardDescription>
|
|
56
|
+
</CardHeader>
|
|
57
|
+
<CardContent>
|
|
58
|
+
<!-- Activity list -->
|
|
59
|
+
</CardContent>
|
|
60
|
+
</Card>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</template>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Key Patterns
|
|
67
|
+
- **Stats row**: 4-column grid of metric cards
|
|
68
|
+
- **Content grid**: Asymmetric grid (4/7 + 3/7) for main content + sidebar
|
|
69
|
+
- **Card headers**: Icon + title aligned horizontally
|
|
70
|
+
- **Responsive**: Stack to 2-col then 1-col on smaller screens
|