@reinvented/design 1.0.0 → 1.1.0
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/README.md +1 -1
- package/SKILL.md +214 -0
- package/package.json +4 -3
- package/skills/apps/analytics.md +103 -0
- package/skills/apps/booking-scheduling.md +97 -0
- package/skills/apps/content-management.md +52 -0
- package/skills/apps/crm.md +80 -0
- package/skills/apps/e-commerce.md +109 -0
- package/skills/apps/education.md +79 -0
- package/skills/apps/finance.md +68 -0
- package/skills/apps/health-fitness.md +72 -0
- package/skills/apps/marketplace.md +99 -0
- package/skills/apps/messaging.md +84 -0
- package/skills/apps/portfolio-personal.md +90 -0
- package/skills/apps/project-management.md +95 -0
- package/skills/apps/saas-dashboard.md +104 -0
- package/skills/apps/social-platform.md +50 -0
- package/skills/blocks/auth.md +106 -0
- package/skills/blocks/communication.md +98 -0
- package/skills/blocks/content.md +107 -0
- package/skills/blocks/data-management.md +109 -0
- package/skills/blocks/data-viz.md +92 -0
- package/skills/blocks/ecommerce.md +126 -0
- package/skills/blocks/feedback.md +97 -0
- package/skills/blocks/filtering.md +127 -0
- package/skills/blocks/marketing.md +136 -0
- package/skills/blocks/media.md +102 -0
- package/skills/blocks/navigation.md +136 -0
- package/skills/blocks/onboarding.md +75 -0
- package/skills/blocks/profiles.md +131 -0
- package/skills/blocks/scheduling.md +117 -0
- package/skills/blocks/settings.md +102 -0
- package/skills/components/advanced-components.md +142 -0
- package/skills/components/avatar.md +92 -0
- package/skills/components/badge.md +105 -0
- package/skills/components/button.md +87 -0
- package/skills/components/card.md +144 -0
- package/skills/components/chart.md +88 -0
- package/skills/components/dialog.md +109 -0
- package/skills/components/dropdown-menu.md +117 -0
- package/skills/components/extended-components.md +187 -0
- package/skills/components/feedback.md +165 -0
- package/skills/components/form.md +112 -0
- package/skills/components/input.md +107 -0
- package/skills/components/map.md +53 -0
- package/skills/components/navigation.md +73 -0
- package/skills/components/overlay.md +77 -0
- package/skills/components/page-header.md +51 -0
- package/skills/components/select.md +175 -0
- package/skills/components/table.md +102 -0
- package/skills/components/tabs.md +105 -0
- package/skills/components/utilities.md +138 -0
- package/skills/devices/desktop.md +43 -0
- package/skills/devices/mobile.md +77 -0
- package/skills/foundation/design-principles.md +77 -0
- package/skills/foundation/design-tokens.md +121 -0
- package/skills/foundation/mockup-generation.md +118 -0
- package/skills/foundation/rules.md +54 -0
- package/skills/foundation/tailwind-usage.md +204 -0
- package/skills/layouts/dashboard.md +71 -0
- package/skills/layouts/full-page-form.md +75 -0
- package/skills/layouts/list-detail.md +70 -0
- package/skills/layouts/marketing.md +70 -0
- package/skills/layouts/responsive.md +67 -0
- package/skills/layouts/settings-page.md +106 -0
- package/skills/layouts/sidebar.md +73 -0
- package/skills/layouts/topbar.md +68 -0
- package/skills/patterns/auth.md +131 -0
- package/skills/patterns/content-display.md +164 -0
- package/skills/patterns/dashboards.md +104 -0
- package/skills/patterns/data-tables.md +113 -0
- package/skills/patterns/empty-states.md +71 -0
- package/skills/patterns/error-states.md +73 -0
- package/skills/patterns/forms.md +136 -0
- package/skills/patterns/loading-states.md +92 -0
- package/skills/patterns/navigation.md +113 -0
- package/skills/patterns/notifications.md +91 -0
- package/skills/patterns/onboarding.md +42 -0
- package/skills/patterns/search.md +55 -0
- package/skills/patterns/settings.md +132 -0
- package/skills/patterns/user-profiles.md +67 -0
- package/skills/personas/business-operator.md +114 -0
- package/skills/personas/consumer-casual.md +60 -0
- package/skills/personas/consumer-power-user.md +109 -0
- package/skills/personas/creative-professional.md +109 -0
- package/skills/personas/enterprise-admin.md +134 -0
- package/skills/visual/color-usage.md +62 -0
- package/skills/visual/dark-mode.md +50 -0
- package/skills/visual/polish-techniques.md +101 -0
- package/skills/visual/spacing-composition.md +69 -0
- package/skills/visual/transitions-animations.md +66 -0
- package/skills/visual/typography-hierarchy.md +66 -0
- package/DESIGN_GUIDE.md +0 -148
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Input, Textarea, Label
|
|
2
|
+
|
|
3
|
+
Text entry components. Never use raw `<input>` or `<textarea>`.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { Input, Textarea, Label } from '@reinvented/design'
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Input
|
|
10
|
+
|
|
11
|
+
Standard single-line text field with built-in styling for focus, disabled, and placeholder states.
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<div className="space-y-2">
|
|
15
|
+
<Label for="email">Email</Label>
|
|
16
|
+
<Input id="email" type="email" placeholder="sarah@example.com" defaultValue="email" />
|
|
17
|
+
</div>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Input with Icon
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
<div className="relative">
|
|
24
|
+
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
25
|
+
<Input className="pl-9" placeholder="Search..." defaultValue="query" />
|
|
26
|
+
</div>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Key: Use `pl-9` (36px) to make room for the icon. Icon is positioned with `absolute left-3 top-1/2 -translate-y-1/2`.
|
|
30
|
+
|
|
31
|
+
### Input Sizes
|
|
32
|
+
|
|
33
|
+
The default Input height matches `h-9` (36px). For different contexts:
|
|
34
|
+
- Standard: default (no size prop needed)
|
|
35
|
+
- Custom height: apply via class `className="h-8"` for compact, `className="h-10"` for larger
|
|
36
|
+
|
|
37
|
+
### Disabled Input
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
<Input disabled :value="user.email" />
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Form Field Pattern (Label + Input + Description + Error)
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<div className="space-y-2">
|
|
47
|
+
<Label for="name">Display Name</Label>
|
|
48
|
+
<Input id="name" defaultValue="name" placeholder="Your display name" />
|
|
49
|
+
<p className="text-xs text-muted-foreground">This is how others will see you.</p>
|
|
50
|
+
</div>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
For error state:
|
|
54
|
+
```tsx
|
|
55
|
+
<div className="space-y-2">
|
|
56
|
+
<Label for="name">Display Name</Label>
|
|
57
|
+
<Input id="name" defaultValue="name" className="border-destructive" />
|
|
58
|
+
<p className="text-xs text-destructive">Name is required.</p>
|
|
59
|
+
</div>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Textarea
|
|
63
|
+
|
|
64
|
+
Multi-line text input.
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
<div className="space-y-2">
|
|
68
|
+
<Label for="bio">Bio</Label>
|
|
69
|
+
<Textarea id="bio" defaultValue="bio" placeholder="Tell us about yourself..." rows="4" />
|
|
70
|
+
</div>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Use `rows` to control initial height. Default is 3 rows.
|
|
74
|
+
|
|
75
|
+
## Label
|
|
76
|
+
|
|
77
|
+
Always pair with an input using matching `for`/`id` attributes.
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
<Label for="username" className="text-sm font-medium">Username</Label>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Label is `text-sm font-medium` by default. Never use `text-base` or larger for form labels.
|
|
84
|
+
|
|
85
|
+
## Anti-Patterns
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
<!-- ❌ No label -->
|
|
89
|
+
<Input placeholder="Enter name" />
|
|
90
|
+
|
|
91
|
+
<!-- ✅ Always pair with Label -->
|
|
92
|
+
<div className="space-y-2">
|
|
93
|
+
<Label for="name">Name</Label>
|
|
94
|
+
<Input id="name" placeholder="Enter name" />
|
|
95
|
+
</div>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
<!-- ❌ Using icon inside Input via slot (doesn't exist) -->
|
|
100
|
+
<Input><SearchIcon /></Input>
|
|
101
|
+
|
|
102
|
+
<!-- ✅ Use the absolute-positioned icon pattern -->
|
|
103
|
+
<div className="relative">
|
|
104
|
+
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
105
|
+
<Input className="pl-9" placeholder="Search..." />
|
|
106
|
+
</div>
|
|
107
|
+
```
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Interactive Map
|
|
2
|
+
|
|
3
|
+
Data-focused map visualization wrapper around Google Maps.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { InteractiveMap } from '@reinvented/design'
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Basic Structure
|
|
10
|
+
|
|
11
|
+
The `InteractiveMap` component renders a fluid, styled Google Map meant for spatial data visualization like finding locations, tracking fleets, or plotting regional data.
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<InteractiveMap
|
|
15
|
+
:markers="[
|
|
16
|
+
{ id: 1, lat: 40.7128, lng: -74.0060, label: 'NYC Hub' },
|
|
17
|
+
{ id: 2, lat: 34.0522, lng: -118.2437, label: 'LA Port' }
|
|
18
|
+
]"
|
|
19
|
+
className="h-[400px]"
|
|
20
|
+
/>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
When `markers` are provided, the map will **automatically fit its bounds** to ensure all markers are visible. You do not need to provide a `center` or `zoom` when providing `markers`.
|
|
24
|
+
|
|
25
|
+
## Usage Without Markers
|
|
26
|
+
|
|
27
|
+
If you want to render a map without markers, explicitly provide `center` and `zoom`:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
<InteractiveMap
|
|
31
|
+
:center="{ lat: 51.5072, lng: -0.1276 }"
|
|
32
|
+
:zoom="12"
|
|
33
|
+
className="h-[400px]"
|
|
34
|
+
/>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Mock Mode (Mockups Only)
|
|
38
|
+
|
|
39
|
+
When generating pure mockups without real data or needing a strict wireframe, use the `mock` prop to bypass the heavyweight map engine and display a beautiful placeholder.
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
<InteractiveMap
|
|
43
|
+
mock
|
|
44
|
+
:markers="mockLocations"
|
|
45
|
+
className="h-[400px]"
|
|
46
|
+
/>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Component Rules
|
|
50
|
+
|
|
51
|
+
- **Always set height**: The wrapper `<div className="relative w-full h-full">` inherits dimensions. Give the component an explicit absolute height (e.g., `className="h-96"`, `className="h-[400px]"`) or place it in a container that has explicit height.
|
|
52
|
+
- **Data formats**: `lat` and `lng` must be **numbers**, not strings.
|
|
53
|
+
- **Do not use `ComponentPlaceholder` for Maps**: Now that the design system formally provides `<InteractiveMap>`, never use a `ComponentPlaceholder` for maps in your layouts. Use this native component.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Navigation Components
|
|
2
|
+
|
|
3
|
+
Breadcrumbs, pagination, and navigation menus.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from '@reinvented/design'
|
|
7
|
+
import { Pagination, PaginationContent, PaginationEllipsis, PaginationFirst, PaginationLast, PaginationNext, PaginationPrev } from '@reinvented/design'
|
|
8
|
+
import { NavigationMenu, NavigationMenuContent, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger } from '@reinvented/design'
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Breadcrumb
|
|
12
|
+
|
|
13
|
+
Path navigation for hierarchies of 3+ levels. Shows the user where they are.
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<Breadcrumb>
|
|
17
|
+
<BreadcrumbList>
|
|
18
|
+
<BreadcrumbItem>
|
|
19
|
+
<BreadcrumbLink href="/">Home</BreadcrumbLink>
|
|
20
|
+
</BreadcrumbItem>
|
|
21
|
+
<BreadcrumbSeparator />
|
|
22
|
+
<BreadcrumbItem>
|
|
23
|
+
<BreadcrumbLink href="/projects">Projects</BreadcrumbLink>
|
|
24
|
+
</BreadcrumbItem>
|
|
25
|
+
<BreadcrumbSeparator />
|
|
26
|
+
<BreadcrumbItem>
|
|
27
|
+
<BreadcrumbPage>Project Alpha</BreadcrumbPage>
|
|
28
|
+
</BreadcrumbItem>
|
|
29
|
+
</BreadcrumbList>
|
|
30
|
+
</Breadcrumb>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- The last item uses `BreadcrumbPage` (non-clickable, current location)
|
|
34
|
+
- All intermediate items use `BreadcrumbLink` (clickable)
|
|
35
|
+
- Only use breadcrumbs for 3+ level hierarchies. For simpler navigation, use a Back button.
|
|
36
|
+
|
|
37
|
+
## Pagination
|
|
38
|
+
|
|
39
|
+
Page-based navigation for large datasets.
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
<div className="flex items-center justify-between">
|
|
43
|
+
<p className="text-sm text-muted-foreground">Showing 1–10 of 97 results</p>
|
|
44
|
+
<Pagination :total="97" :sibling-count="1" show-edges :default-page="1">
|
|
45
|
+
<PaginationContent>
|
|
46
|
+
<PaginationFirst />
|
|
47
|
+
<PaginationPrev />
|
|
48
|
+
<!-- Page buttons are auto-generated -->
|
|
49
|
+
<PaginationNext />
|
|
50
|
+
<PaginationLast />
|
|
51
|
+
</PaginationContent>
|
|
52
|
+
</Pagination>
|
|
53
|
+
</div>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Place pagination at the bottom of data tables or list views, with a results count on the left.
|
|
57
|
+
|
|
58
|
+
## Back Button
|
|
59
|
+
|
|
60
|
+
For detail views, use a ghost button instead of breadcrumbs:
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
<Button variant="ghost" size="sm" className="gap-1">
|
|
64
|
+
<ArrowLeft className="w-4 h-4" />
|
|
65
|
+
Back
|
|
66
|
+
</Button>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Rules
|
|
70
|
+
- **Breadcrumbs**: Only for 3+ level hierarchies
|
|
71
|
+
- **Back button**: Always present on detail/drill-down views
|
|
72
|
+
- **Tabs**: For related content within a single view (not for navigation between views)
|
|
73
|
+
- **Pagination**: Always show results count alongside page controls
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Sheet, ScrollArea
|
|
2
|
+
|
|
3
|
+
Overlay and scroll components.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetFooter, SheetTrigger, SheetClose } from '@reinvented/design'
|
|
7
|
+
import { ScrollArea } from '@reinvented/design'
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Sheet
|
|
11
|
+
|
|
12
|
+
Slide-out panel from the edge of the screen. Use for secondary forms, filters, or detail previews that don't warrant a full page.
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
<Sheet>
|
|
16
|
+
<SheetTrigger as-child>
|
|
17
|
+
<Button variant="outline">View Details</Button>
|
|
18
|
+
</SheetTrigger>
|
|
19
|
+
<SheetContent side="right" className="w-full max-w-md sm:max-w-lg">
|
|
20
|
+
<SheetHeader>
|
|
21
|
+
<SheetTitle>Task Details</SheetTitle>
|
|
22
|
+
<SheetDescription>View and edit task information.</SheetDescription>
|
|
23
|
+
</SheetHeader>
|
|
24
|
+
<div className="space-y-6 py-6">
|
|
25
|
+
<!-- Content -->
|
|
26
|
+
</div>
|
|
27
|
+
<SheetFooter>
|
|
28
|
+
<SheetClose as-child>
|
|
29
|
+
<Button variant="outline">Cancel</Button>
|
|
30
|
+
</SheetClose>
|
|
31
|
+
<Button>Save Changes</Button>
|
|
32
|
+
</SheetFooter>
|
|
33
|
+
</SheetContent>
|
|
34
|
+
</Sheet>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Sheet Sides
|
|
38
|
+
| Side | When to Use |
|
|
39
|
+
|------|-------------|
|
|
40
|
+
| `right` | Default. Detail panels, edit forms, filters. |
|
|
41
|
+
| `left` | Mobile navigation drawer. |
|
|
42
|
+
| `bottom` | Mobile action sheets, bottom drawers. |
|
|
43
|
+
| `top` | Rarely used. Notification panels. |
|
|
44
|
+
|
|
45
|
+
### Sheet vs Dialog
|
|
46
|
+
| Use Case | Component |
|
|
47
|
+
|----------|-----------|
|
|
48
|
+
| Quick form (≤5 fields) | Dialog |
|
|
49
|
+
| Detail panel / preview | Sheet (right) |
|
|
50
|
+
| Complex edit form | Sheet (right, wider) |
|
|
51
|
+
| Mobile navigation | Sheet (left) |
|
|
52
|
+
| Destructive confirmation | AlertDialog |
|
|
53
|
+
|
|
54
|
+
## ScrollArea
|
|
55
|
+
|
|
56
|
+
Styled scrollbar container. Use when content might overflow.
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
<ScrollArea className="h-72">
|
|
60
|
+
<div className="space-y-4 pr-4">
|
|
61
|
+
<!-- Scrollable content -->
|
|
62
|
+
</div>
|
|
63
|
+
</ScrollArea>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Common Uses
|
|
67
|
+
- Long lists inside fixed-height containers
|
|
68
|
+
- Sidebar navigation that exceeds viewport
|
|
69
|
+
- Modal content that might overflow
|
|
70
|
+
- Code preview areas
|
|
71
|
+
|
|
72
|
+
### Rules
|
|
73
|
+
- Add `pr-4` to content inside ScrollArea to prevent scrollbar overlapping text
|
|
74
|
+
- **In Normal Containers**: Set an explicit height (`h-72`, `max-h-96`, or use a named size).
|
|
75
|
+
- **In Flex/Grid Containers**: If placed inside a flex column or grid cell that stretches, DO NOT use a fixed height. Instead, ensure the parent has `flex-1 min-h-0` (or `min-w-0`) and make the `ScrollArea` `h-full` to fill the available space dynamically.
|
|
76
|
+
- **Avoid Nested Scrolling**: Never put a vertical `<ScrollArea>` inside a page that also scrolls vertically ("scrolling within scrolling"). If you require an inner scrollable area (like a kanban column), ensure the root wrapper of the view is bounded and cannot scroll (e.g., `h-[100dvh] overflow-hidden`).
|
|
77
|
+
- Use `<ScrollArea>` instead of `overflow-y-auto` for consistent scrollbar styling
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Component: PageHeader
|
|
2
|
+
|
|
3
|
+
A structured set of components for building consistent, responsive page headers across the application. These prevent common layout errors when switching from desktop (horizontal) to mobile (vertical).
|
|
4
|
+
|
|
5
|
+
## Components
|
|
6
|
+
|
|
7
|
+
- `PageHeader`: The outer container. Handles responsive flex flow.
|
|
8
|
+
- `PageHeaderHeading`: Groups the title and description vertically.
|
|
9
|
+
- `PageHeaderTitle`: The main `<h1>`. Handles font scaling from mobile (`text-xl`) to desktop (`text-2xl`).
|
|
10
|
+
- `PageHeaderDescription`: Secondary text below the title.
|
|
11
|
+
- `PageHeaderActions`: The container for buttons. On mobile, elements stack to full width.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
// React Component
|
|
17
|
+
import {
|
|
18
|
+
PageHeader,
|
|
19
|
+
PageHeaderHeading,
|
|
20
|
+
PageHeaderTitle,
|
|
21
|
+
PageHeaderDescription,
|
|
22
|
+
PageHeaderActions,
|
|
23
|
+
Button
|
|
24
|
+
} from '@reinvented/design'
|
|
25
|
+
// ...
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
export default function Component() {
|
|
29
|
+
return (
|
|
30
|
+
<>
|
|
31
|
+
<PageHeader>
|
|
32
|
+
<PageHeaderHeading>
|
|
33
|
+
<PageHeaderTitle>Settings</PageHeaderTitle>
|
|
34
|
+
<PageHeaderDescription>Manage your account preferences and defaults.</PageHeaderDescription>
|
|
35
|
+
</PageHeaderHeading>
|
|
36
|
+
|
|
37
|
+
<PageHeaderActions>
|
|
38
|
+
<Button variant="outline">Cancel</Button>
|
|
39
|
+
<Button>Save Changes</Button>
|
|
40
|
+
</PageHeaderActions>
|
|
41
|
+
</PageHeader>
|
|
42
|
+
</>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Rules
|
|
48
|
+
|
|
49
|
+
- **Always use these components** instead of raw `<h1>` or `<div>` trees for primary page headers.
|
|
50
|
+
- **Do not manually add sizing classes** to `<PageHeaderTitle>`: it inherently handles the `text-xl sm:text-2xl` scaling.
|
|
51
|
+
- **Do not manually add width classes** to buttons inside `<PageHeaderActions>`: the container handles stretching them on mobile (`w-full`) and sizing them to content on desktop (`sm:w-auto`).
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Select, Checkbox, Switch, RadioGroup, Slider
|
|
2
|
+
|
|
3
|
+
Form controls for choosing values.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from '@reinvented/design'
|
|
7
|
+
import { Checkbox } from '@reinvented/design'
|
|
8
|
+
import { Switch } from '@reinvented/design'
|
|
9
|
+
import { RadioGroup, RadioGroupItem } from '@reinvented/design'
|
|
10
|
+
import { Slider } from '@reinvented/design'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Select
|
|
14
|
+
|
|
15
|
+
Dropdown for choosing one value from a list. Use when there are 4+ options.
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
<div className="space-y-2">
|
|
19
|
+
<Label>Status</Label>
|
|
20
|
+
<Select defaultValue="status">
|
|
21
|
+
<SelectTrigger className="w-48">
|
|
22
|
+
<SelectValue placeholder="Select status" />
|
|
23
|
+
</SelectTrigger>
|
|
24
|
+
<SelectContent>
|
|
25
|
+
<SelectGroup>
|
|
26
|
+
<SelectLabel>Status</SelectLabel>
|
|
27
|
+
<SelectItem value="active">Active</SelectItem>
|
|
28
|
+
<SelectItem value="inactive">Inactive</SelectItem>
|
|
29
|
+
<SelectItem value="pending">Pending</SelectItem>
|
|
30
|
+
</SelectGroup>
|
|
31
|
+
</SelectContent>
|
|
32
|
+
</Select>
|
|
33
|
+
</div>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Select Width
|
|
37
|
+
- **Filter select** (in toolbar): `w-36` or `w-48`
|
|
38
|
+
- **Form select** (in form): `w-full` (takes full width of form column)
|
|
39
|
+
|
|
40
|
+
### Grouped Select
|
|
41
|
+
```tsx
|
|
42
|
+
<SelectContent>
|
|
43
|
+
<SelectGroup>
|
|
44
|
+
<SelectLabel>Roles</SelectLabel>
|
|
45
|
+
<SelectItem value="admin">Admin</SelectItem>
|
|
46
|
+
<SelectItem value="editor">Editor</SelectItem>
|
|
47
|
+
</SelectGroup>
|
|
48
|
+
<SelectGroup>
|
|
49
|
+
<SelectLabel>Access</SelectLabel>
|
|
50
|
+
<SelectItem value="viewer">Viewer</SelectItem>
|
|
51
|
+
<SelectItem value="none">No Access</SelectItem>
|
|
52
|
+
</SelectGroup>
|
|
53
|
+
</SelectContent>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Checkbox
|
|
57
|
+
|
|
58
|
+
Boolean toggle with checkmark. Use for multi-select from a set of options.
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
<div className="flex items-start gap-3">
|
|
62
|
+
<Checkbox id="terms" v-model:checked="agreed" className="mt-0.5" />
|
|
63
|
+
<div>
|
|
64
|
+
<Label for="terms" className="text-sm font-medium">Accept terms</Label>
|
|
65
|
+
<p className="text-xs text-muted-foreground">You agree to our Terms of Service and Privacy Policy.</p>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Checkbox Group
|
|
71
|
+
```tsx
|
|
72
|
+
<div className="space-y-3">
|
|
73
|
+
<Label className="text-sm font-medium">Notifications</Label>
|
|
74
|
+
<div v-for="opt in options" :key="opt.id" className="flex items-center gap-3">
|
|
75
|
+
<Checkbox :id="opt.id" v-model:checked="opt.enabled" />
|
|
76
|
+
<Label :for="opt.id" className="text-sm">{{ opt.label }}</Label>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Switch
|
|
82
|
+
|
|
83
|
+
On/off toggle. Use for boolean settings where the effect is immediate.
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
<div className="flex items-center justify-between">
|
|
87
|
+
<div>
|
|
88
|
+
<p className="text-sm font-medium">Email Notifications</p>
|
|
89
|
+
<p className="text-xs text-muted-foreground">Receive emails about account activity.</p>
|
|
90
|
+
</div>
|
|
91
|
+
<Switch v-model:checked="emailEnabled" />
|
|
92
|
+
</div>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The standard pattern: label/description on the left, Switch on the right, separated by `justify-between`.
|
|
96
|
+
|
|
97
|
+
## RadioGroup
|
|
98
|
+
|
|
99
|
+
Single choice from a small set (2–5 options). Use when all options should be visible.
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
<div className="space-y-3">
|
|
103
|
+
<Label className="text-sm font-medium">Plan</Label>
|
|
104
|
+
<RadioGroup defaultValue="plan" className="space-y-2">
|
|
105
|
+
<div className="flex items-center gap-3">
|
|
106
|
+
<RadioGroupItem value="free" id="free" />
|
|
107
|
+
<Label for="free" className="text-sm">Free — $0/month</Label>
|
|
108
|
+
</div>
|
|
109
|
+
<div className="flex items-center gap-3">
|
|
110
|
+
<RadioGroupItem value="pro" id="pro" />
|
|
111
|
+
<Label for="pro" className="text-sm">Pro — $19/month</Label>
|
|
112
|
+
</div>
|
|
113
|
+
<div className="flex items-center gap-3">
|
|
114
|
+
<RadioGroupItem value="team" id="team" />
|
|
115
|
+
<Label for="team" className="text-sm">Team — $49/month</Label>
|
|
116
|
+
</div>
|
|
117
|
+
</RadioGroup>
|
|
118
|
+
</div>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Color Swatch Selector (RadioGroup Variant)
|
|
122
|
+
|
|
123
|
+
For e-commerce or customizable settings, use `RadioGroup` and style the items as color swatches to preserve visual aesthetics and keyboard accessibility.
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
<div className="space-y-3">
|
|
127
|
+
<Label className="text-xs font-bold text-muted-foreground uppercase tracking-widest">Theme Color</Label>
|
|
128
|
+
<RadioGroup defaultValue="activeColor" className="flex items-center gap-3">
|
|
129
|
+
<RadioGroupItem
|
|
130
|
+
v-for="color in colors"
|
|
131
|
+
:key="color.id"
|
|
132
|
+
:value="color.id"
|
|
133
|
+
:id="color.id"
|
|
134
|
+
className="w-8 h-8 rounded-full border border-black/10 dark:border-white/10 shadow-sm transition-all duration-200"
|
|
135
|
+
className={[
|
|
136
|
+
color.bgClass, // e.g., 'bg-amber-400'
|
|
137
|
+
activeColor === color.id ? 'ring-2 ring-offset-2 ring-foreground scale-110 border-transparent' : 'opacity-90 hover:opacity-100 hover:scale-105'
|
|
138
|
+
]}
|
|
139
|
+
/>
|
|
140
|
+
</RadioGroup>
|
|
141
|
+
</div>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
> **Warning:** When applying `ring` and `scale` transforms to active elements within collapsible panels, verify the parent container has enough bottom margin/padding (e.g. `pb-2`). Otherwise, strict `overflow-hidden` transitions will clip the focus ring.
|
|
145
|
+
|
|
146
|
+
## Slider
|
|
147
|
+
|
|
148
|
+
Numeric range input.
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
<div className="space-y-3">
|
|
152
|
+
<div className="flex items-center justify-between">
|
|
153
|
+
<Label className="text-sm font-medium">Volume</Label>
|
|
154
|
+
<span className="text-sm text-muted-foreground">{{ volume }}%</span>
|
|
155
|
+
</div>
|
|
156
|
+
<Slider defaultValue="volume" :max="100" :step="1" className="w-full" />
|
|
157
|
+
</div>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Decision Tree
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
How many options?
|
|
164
|
+
├─ 2 (on/off, yes/no)
|
|
165
|
+
│ ├─ Setting that takes effect immediately? → Switch
|
|
166
|
+
│ └─ Checkbox in a form submission? → Checkbox
|
|
167
|
+
├─ 2–5 (all visible at once)
|
|
168
|
+
│ └─ → RadioGroup
|
|
169
|
+
├─ 4+ (too many for radio buttons)
|
|
170
|
+
│ └─ → Select dropdown
|
|
171
|
+
├─ Multi-select from options
|
|
172
|
+
│ └─ → Checkbox group
|
|
173
|
+
├─ Numeric range
|
|
174
|
+
│ └─ → Slider
|
|
175
|
+
```
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Table
|
|
2
|
+
|
|
3
|
+
Data tables for structured, tabular content. Never use raw `<table>`.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow } from '@reinvented/design'
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Basic Table
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
<Table>
|
|
13
|
+
<TableHeader>
|
|
14
|
+
<TableRow>
|
|
15
|
+
<TableHead>Name</TableHead>
|
|
16
|
+
<TableHead>Email</TableHead>
|
|
17
|
+
<TableHead>Role</TableHead>
|
|
18
|
+
<TableHead>Status</TableHead>
|
|
19
|
+
<TableHead className="text-right">Amount</TableHead>
|
|
20
|
+
<TableHead className="w-10"></TableHead>
|
|
21
|
+
</TableRow>
|
|
22
|
+
</TableHeader>
|
|
23
|
+
<TableBody>
|
|
24
|
+
<TableRow v-for="user in users" :key="user.id">
|
|
25
|
+
<TableCell>
|
|
26
|
+
<div className="flex items-center gap-3">
|
|
27
|
+
<div className="w-8 h-8 rounded-full shrink-0 flex items-center justify-center text-xs font-medium bg-muted text-muted-foreground">
|
|
28
|
+
{{ user.name.split(' ').map(n => n[0]).join('') }}
|
|
29
|
+
</div>
|
|
30
|
+
<div className="min-w-0">
|
|
31
|
+
<p className="text-sm font-medium truncate">{{ user.name }}</p>
|
|
32
|
+
<p className="text-xs text-muted-foreground truncate">{{ user.email }}</p>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</TableCell>
|
|
36
|
+
<TableCell className="text-muted-foreground">{{ user.email }}</TableCell>
|
|
37
|
+
<TableCell>{{ user.role }}</TableCell>
|
|
38
|
+
<TableCell>
|
|
39
|
+
<Badge :variant="user.status === 'active' ? 'default' : 'secondary'">{{ user.status }}</Badge>
|
|
40
|
+
</TableCell>
|
|
41
|
+
<TableCell className="text-right font-medium">${{ user.amount.toLocaleString() }}</TableCell>
|
|
42
|
+
<TableCell>
|
|
43
|
+
<DropdownMenu>
|
|
44
|
+
<DropdownMenuTrigger as-child>
|
|
45
|
+
<Button variant="ghost" size="icon" className="h-8 w-8">
|
|
46
|
+
<MoreHorizontal className="w-4 h-4" />
|
|
47
|
+
</Button>
|
|
48
|
+
</DropdownMenuTrigger>
|
|
49
|
+
<DropdownMenuContent align="end">
|
|
50
|
+
<DropdownMenuItem><Pencil className="w-4 h-4" /> Edit</DropdownMenuItem>
|
|
51
|
+
<DropdownMenuSeparator />
|
|
52
|
+
<DropdownMenuItem className="text-destructive"><Trash2 className="w-4 h-4" /> Delete</DropdownMenuItem>
|
|
53
|
+
</DropdownMenuContent>
|
|
54
|
+
</DropdownMenu>
|
|
55
|
+
</TableCell>
|
|
56
|
+
</TableRow>
|
|
57
|
+
</TableBody>
|
|
58
|
+
</Table>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Column Formatting Rules
|
|
62
|
+
|
|
63
|
+
| Content Type | Alignment | Class |
|
|
64
|
+
|-------------|-----------|-------|
|
|
65
|
+
| Text (names, descriptions) | Left (default) | — |
|
|
66
|
+
| Numbers, currency, amounts | Right | `className="text-right"` |
|
|
67
|
+
| Status badges | Left | — |
|
|
68
|
+
| Dates | Left | `className="text-muted-foreground"` |
|
|
69
|
+
| Actions (menu) | Right | `className="w-10"` on the head |
|
|
70
|
+
| Primary identifier (name) | Left | `className="font-medium"` |
|
|
71
|
+
|
|
72
|
+
## Empty Table
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
<TableBody>
|
|
76
|
+
<TableRow v-if="items.length === 0">
|
|
77
|
+
<TableCell :colspan="columns.length" className="h-32 text-center text-muted-foreground">
|
|
78
|
+
No results found.
|
|
79
|
+
</TableCell>
|
|
80
|
+
</TableRow>
|
|
81
|
+
</TableBody>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Table with Selection
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
<TableHead className="w-10">
|
|
88
|
+
<Checkbox :checked="allSelected" @update:checked="toggleAll" />
|
|
89
|
+
</TableHead>
|
|
90
|
+
<!-- ... -->
|
|
91
|
+
<TableCell>
|
|
92
|
+
<Checkbox :checked="selectedIds.includes(user.id)" @update:checked="toggleUser(user.id)" />
|
|
93
|
+
</TableCell>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Rules
|
|
97
|
+
- **Primary identifier column**: Use `font-medium` on the main entity name
|
|
98
|
+
- **Right-align numbers**: All currency, amounts, counts use `text-right`
|
|
99
|
+
- **Actions column**: Always last. Use `w-10` on `TableHead` and a three-dot `DropdownMenu`
|
|
100
|
+
- **Row count**: Show 5–8 rows for realistic density. Not 2, not 20.
|
|
101
|
+
- **Wrap in Card**: Data tables should typically be inside a `Card` for containment. When wrapping in a Card, note that the `CardHeader` has 24px horizontal padding, but `Table` cells default to 16px. To visually align the table headers and values with the card title, always add `className="pl-6"` to the first `TableHead` and `TableCell`, and `className="pr-6"` to the last `TableHead` and `TableCell`.
|
|
102
|
+
- **Always include a `TableHeader`**: Even if columns seem obvious.
|