@devalok/shilp-sutra 0.41.0 → 0.42.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.
@@ -0,0 +1,215 @@
1
+ # Table
2
+
3
+ Server-safe semantic wrappers around `<table>`. For static / presentational tables.
4
+
5
+ ```tsx
6
+ import {
7
+ Table,
8
+ TableHeader,
9
+ TableBody,
10
+ TableFooter,
11
+ TableRow,
12
+ TableHead,
13
+ TableCell,
14
+ TableCaption,
15
+ } from '@devalok/shilp-sutra/ui/table'
16
+ ```
17
+
18
+ ## When to use
19
+
20
+ - Static or small data displays where you control every row and cell.
21
+ - Marketing / pricing comparison tables.
22
+ - Documentation tables (API references, prop tables).
23
+ - Server-rendered tables (RSC) — Table and sub-components are server-safe.
24
+ - Need sorting / filtering / pagination / selection / virtualization? Use `<DataTable>` from `@devalok/shilp-sutra/ui/data-table` — out of scope for this guide.
25
+
26
+ ## Compound shape
27
+
28
+ ```
29
+ Table (<table>)
30
+ TableCaption (<caption>) ← optional summary for screen readers
31
+ TableHeader (<thead>)
32
+ TableRow (<tr>)
33
+ TableHead (<th scope="col">)
34
+ TableBody (<tbody>)
35
+ TableRow (<tr>)
36
+ TableCell (<td>)
37
+ TableFooter (<tfoot>)
38
+ TableRow
39
+ TableCell
40
+ ```
41
+
42
+ Each component is a thin semantic wrapper — no props beyond standard HTML attributes plus `className`.
43
+
44
+ ## Examples
45
+
46
+ **Standard:**
47
+ ```tsx
48
+ <Table>
49
+ <TableHeader>
50
+ <TableRow>
51
+ <TableHead>Name</TableHead>
52
+ <TableHead>Status</TableHead>
53
+ <TableHead>Owner</TableHead>
54
+ <TableHead>Updated</TableHead>
55
+ </TableRow>
56
+ </TableHeader>
57
+ <TableBody>
58
+ {projects.map((p) => (
59
+ <TableRow key={p.id}>
60
+ <TableCell>{p.name}</TableCell>
61
+ <TableCell>
62
+ <Badge color={p.status === 'active' ? 'success' : 'neutral'}>
63
+ {p.status}
64
+ </Badge>
65
+ </TableCell>
66
+ <TableCell>
67
+ <Stack direction="horizontal" gap="ds-02" align="center">
68
+ <Avatar size="xs" src={p.owner.avatar} alt={p.owner.name} />
69
+ <Text variant="body-sm">{p.owner.name}</Text>
70
+ </Stack>
71
+ </TableCell>
72
+ <TableCell>
73
+ <Text variant="body-sm" className="text-fg-muted">
74
+ {formatDate(p.updatedAt)}
75
+ </Text>
76
+ </TableCell>
77
+ </TableRow>
78
+ ))}
79
+ </TableBody>
80
+ </Table>
81
+ ```
82
+
83
+ **With caption + footer:**
84
+ ```tsx
85
+ <Table>
86
+ <TableCaption>Q4 revenue by region.</TableCaption>
87
+ <TableHeader>
88
+ <TableRow>
89
+ <TableHead>Region</TableHead>
90
+ <TableHead>Revenue</TableHead>
91
+ </TableRow>
92
+ </TableHeader>
93
+ <TableBody>
94
+ <TableRow><TableCell>NA</TableCell><TableCell>$1.2M</TableCell></TableRow>
95
+ <TableRow><TableCell>EU</TableCell><TableCell>$0.8M</TableCell></TableRow>
96
+ <TableRow><TableCell>APAC</TableCell><TableCell>$0.4M</TableCell></TableRow>
97
+ </TableBody>
98
+ <TableFooter>
99
+ <TableRow>
100
+ <TableCell><Text variant="label-sm">Total</Text></TableCell>
101
+ <TableCell><Text variant="label-sm">$2.4M</Text></TableCell>
102
+ </TableRow>
103
+ </TableFooter>
104
+ </Table>
105
+ ```
106
+
107
+ **Row actions (IconButton in last cell):**
108
+ ```tsx
109
+ <Table>
110
+ <TableHeader>
111
+ <TableRow>
112
+ <TableHead>File</TableHead>
113
+ <TableHead>Size</TableHead>
114
+ <TableHead className="w-12" />
115
+ </TableRow>
116
+ </TableHeader>
117
+ <TableBody>
118
+ {files.map((f) => (
119
+ <TableRow key={f.id}>
120
+ <TableCell>{f.name}</TableCell>
121
+ <TableCell>{formatFileSize(f.size)}</TableCell>
122
+ <TableCell>
123
+ <DropdownMenu>
124
+ <DropdownMenuTrigger asChild>
125
+ <IconButton icon={<Icon icon={IconDots} />} variant="ghost" size="sm" aria-label="Actions" />
126
+ </DropdownMenuTrigger>
127
+ <DropdownMenuContent>
128
+ <DropdownMenuItem onSelect={() => download(f)}>Download</DropdownMenuItem>
129
+ <DropdownMenuSeparator />
130
+ <DropdownMenuItem onSelect={() => remove(f)}>Delete</DropdownMenuItem>
131
+ </DropdownMenuContent>
132
+ </DropdownMenu>
133
+ </TableCell>
134
+ </TableRow>
135
+ ))}
136
+ </TableBody>
137
+ </Table>
138
+ ```
139
+
140
+ **Inside a Card:**
141
+ ```tsx
142
+ <Card>
143
+ <CardHeader>
144
+ <CardTitle>Team members</CardTitle>
145
+ </CardHeader>
146
+ <CardContent>
147
+ <Table>
148
+ <TableHeader>
149
+ <TableRow>
150
+ <TableHead>Member</TableHead>
151
+ <TableHead>Role</TableHead>
152
+ </TableRow>
153
+ </TableHeader>
154
+ <TableBody>
155
+ {members.map((m) => (
156
+ <TableRow key={m.id}>
157
+ <TableCell>{m.name}</TableCell>
158
+ <TableCell><Badge color="neutral">{m.role}</Badge></TableCell>
159
+ </TableRow>
160
+ ))}
161
+ </TableBody>
162
+ </Table>
163
+ </CardContent>
164
+ </Card>
165
+ ```
166
+
167
+ When inside a `<Card>`, the Table's surrounding padding comes from `CardContent`. Don't add extra padding on the Table.
168
+
169
+ **Server-rendered table (RSC):**
170
+ ```tsx
171
+ // app/projects/page.tsx — no 'use client'
172
+ export default async function ProjectsPage() {
173
+ const projects = await db.projects.findMany()
174
+ return (
175
+ <Table>
176
+ <TableHeader>
177
+ <TableRow>
178
+ <TableHead>Name</TableHead>
179
+ <TableHead>Status</TableHead>
180
+ </TableRow>
181
+ </TableHeader>
182
+ <TableBody>
183
+ {projects.map((p) => (
184
+ <TableRow key={p.id}>
185
+ <TableCell>{p.name}</TableCell>
186
+ <TableCell><Badge>{p.status}</Badge></TableCell>
187
+ </TableRow>
188
+ ))}
189
+ </TableBody>
190
+ </Table>
191
+ )
192
+ }
193
+ ```
194
+
195
+ Table + Badge (server-safe via Badge.Group context — verify per use) + Avatar all render in RSC trees. Skip client components.
196
+
197
+ ## Composability
198
+
199
+ - **Server-safe:** Table and its sub-components are pure HTML semantic wrappers. No state, no context. Use in RSC trees without `'use client'`.
200
+ - **TableHead scope:** Headers automatically get `scope="col"` for screen-reader navigation. Don't override it.
201
+ - **Composes with primitives:** Drop `<Badge>`, `<Avatar>`, `<IconButton>`, `<StatusDot>` inside cells. Check each component's server-safety if you need RSC compatibility.
202
+ - **TableCaption:** Renders as HTML `<caption>` — screen readers announce it before content. Use it for any non-trivial table.
203
+
204
+ See `foundations/typography.md` for the body / label variants inside cells, `foundations/surfaces.md` for table-in-card surface guidance.
205
+
206
+ ## Rules
207
+
208
+ - For anything with sorting / filtering / pagination / selection / virtualization, use `<DataTable>` from `/ui/data-table`. Don't rebuild that machinery on bare Table.
209
+ - Always wrap header cells in `<TableHead>` (renders `<th>`). Don't use `<TableCell>` (`<td>`) in headers — breaks screen-reader column scope.
210
+ - Use `<TableCaption>` for any table that's not self-evident. Renders the HTML `<caption>` which screen readers announce.
211
+ - Inside Card, drop the Table directly in `<CardContent>` — don't add wrapper divs that fight the card's padding cascade.
212
+ - For wide tables on mobile, wrap in an `overflow-x-auto` container. Don't try to make a Table responsive via column stacking — switch to a card list on small viewports.
213
+ - Don't style cell text with raw Tailwind palette utilities. Use `text-fg-muted` from `foundations/color.md`.
214
+ - Compose with Badge for status, Avatar for users, IconButton for row actions. Don't invent new primitives per table.
215
+ - Keep TableCell content single-line where possible — multi-line cells make scanning hard. Use `<Stack>` only when each row genuinely needs two lines.
@@ -0,0 +1,162 @@
1
+ # Tabs
2
+
3
+ Switch between sibling views inside a single region. Not for top-level navigation.
4
+
5
+ ```tsx
6
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from '@devalok/shilp-sutra/ui/tabs'
7
+ ```
8
+
9
+ ## When to use
10
+
11
+ - Sub-sections of a single page / panel that share context (Overview / Activity / Settings on a project).
12
+ - Filtered views over the same dataset (All / Mine / Archived).
13
+ - Need URL-driven routes per tab? Wire `value` / `onValueChange` to router state.
14
+ - Top-level app navigation? Use `<Sidebar>` / `<TopBar>`, not Tabs.
15
+ - Multi-step flows? Use `<Stepper>` or a wizard pattern.
16
+
17
+ ## Compound shape
18
+
19
+ ```
20
+ Tabs (root — value, defaultValue, onValueChange)
21
+ TabsList (variant, size, orientation)
22
+ TabsTrigger (value) ← inherits variant/size/orientation from TabsList
23
+ TabsContent (value) ← rendered inline (not portalled)
24
+ ```
25
+
26
+ ## TabsList props
27
+
28
+ | Prop | Type | Notes |
29
+ |---|---|---|
30
+ | `variant` | `'line'\|'contained'` | Default `line`. |
31
+ | `size` | `'sm'\|'md'\|'lg'` | Default `md`. |
32
+ | `orientation` | `'horizontal'\|'vertical'` | Default `horizontal`. Vertical also changes keyboard nav to ArrowUp/Down. |
33
+ | `color` | `'accent'\|'neutral'` | Affects the line-variant active indicator. |
34
+
35
+ ## Variants
36
+
37
+ | Variant | When |
38
+ |---|---|
39
+ | `line` (default) | Underline active indicator. Most common — pairs with section headings. |
40
+ | `contained` | Pill background per active trigger. Use inside cards or compact toolbars. |
41
+
42
+ ## Root state props (Radix passthrough)
43
+
44
+ | Prop | Type | Notes |
45
+ |---|---|---|
46
+ | `value` | `string` | Controlled active tab. |
47
+ | `defaultValue` | `string` | Uncontrolled initial tab. |
48
+ | `onValueChange` | `(value: string) => void` | Fires on tab change. |
49
+
50
+ ## TabsTrigger / TabsContent
51
+
52
+ Both require a `value: string` prop. The values must match between a trigger and its content.
53
+
54
+ `TabsTrigger` reads `variant` / `size` / `orientation` from `TabsList` via context. Override per-trigger if needed, but normally don't.
55
+
56
+ ## Examples
57
+
58
+ **Standard line tabs:**
59
+ ```tsx
60
+ <Tabs defaultValue="overview">
61
+ <TabsList>
62
+ <TabsTrigger value="overview">Overview</TabsTrigger>
63
+ <TabsTrigger value="activity">Activity</TabsTrigger>
64
+ <TabsTrigger value="settings">Settings</TabsTrigger>
65
+ </TabsList>
66
+ <TabsContent value="overview">
67
+ <ProjectOverview />
68
+ </TabsContent>
69
+ <TabsContent value="activity">
70
+ <ActivityFeed />
71
+ </TabsContent>
72
+ <TabsContent value="settings">
73
+ <ProjectSettings />
74
+ </TabsContent>
75
+ </Tabs>
76
+ ```
77
+
78
+ **Contained variant inside a card:**
79
+ ```tsx
80
+ <Card>
81
+ <CardContent>
82
+ <Tabs defaultValue="day">
83
+ <TabsList variant="contained" size="sm">
84
+ <TabsTrigger value="day">Day</TabsTrigger>
85
+ <TabsTrigger value="week">Week</TabsTrigger>
86
+ <TabsTrigger value="month">Month</TabsTrigger>
87
+ </TabsList>
88
+ <TabsContent value="day"><Chart range="day" /></TabsContent>
89
+ <TabsContent value="week"><Chart range="week" /></TabsContent>
90
+ <TabsContent value="month"><Chart range="month" /></TabsContent>
91
+ </Tabs>
92
+ </CardContent>
93
+ </Card>
94
+ ```
95
+
96
+ **Vertical orientation (settings-style):**
97
+ ```tsx
98
+ <Tabs defaultValue="account" orientation="vertical">
99
+ <Stack direction="horizontal" gap="ds-07" align="start">
100
+ <TabsList orientation="vertical">
101
+ <TabsTrigger value="account">Account</TabsTrigger>
102
+ <TabsTrigger value="billing">Billing</TabsTrigger>
103
+ <TabsTrigger value="notifications">Notifications</TabsTrigger>
104
+ </TabsList>
105
+ <div className="flex-1">
106
+ <TabsContent value="account"><AccountForm /></TabsContent>
107
+ <TabsContent value="billing"><BillingForm /></TabsContent>
108
+ <TabsContent value="notifications"><NotificationsForm /></TabsContent>
109
+ </div>
110
+ </Stack>
111
+ </Tabs>
112
+ ```
113
+
114
+ **With icons + badges:**
115
+ ```tsx
116
+ <Tabs defaultValue="inbox">
117
+ <TabsList>
118
+ <TabsTrigger value="inbox">
119
+ <Icon icon={IconInbox} /> Inbox
120
+ <Badge size="xs" color="accent">12</Badge>
121
+ </TabsTrigger>
122
+ <TabsTrigger value="sent">
123
+ <Icon icon={IconSend} /> Sent
124
+ </TabsTrigger>
125
+ </TabsList>
126
+ <TabsContent value="inbox"><InboxList /></TabsContent>
127
+ <TabsContent value="sent"><SentList /></TabsContent>
128
+ </Tabs>
129
+ ```
130
+
131
+ **Router-driven (Next.js App Router):**
132
+ ```tsx
133
+ 'use client'
134
+ const router = useRouter()
135
+ const pathname = usePathname()
136
+ const tab = pathname.split('/').pop() ?? 'overview'
137
+
138
+ <Tabs value={tab} onValueChange={(v) => router.push(`/projects/${id}/${v}`)}>
139
+ <TabsList>
140
+ <TabsTrigger value="overview">Overview</TabsTrigger>
141
+ <TabsTrigger value="activity">Activity</TabsTrigger>
142
+ </TabsList>
143
+ </Tabs>
144
+ ```
145
+
146
+ ## Composability
147
+
148
+ - **Context cascade:** `TabsList` propagates `variant` / `size` / `orientation` to every child `TabsTrigger`. Don't repeat those props on each trigger.
149
+ - **Inline content:** `TabsContent` renders inline (not portalled). Container-scoped queries in tests work.
150
+ - **Keyboard:** Roving tabindex via Radix — ArrowLeft/Right (horizontal) or ArrowUp/Down (vertical). Home / End jump to first / last. Don't re-implement.
151
+
152
+ See `foundations/spacing.md` for the gap between TabsList and TabsContent, `foundations/icons.md` for icon sizing inside triggers.
153
+
154
+ ## Rules
155
+
156
+ - Put `variant` / `size` / `orientation` on `TabsList`, NOT on `Tabs` root or `TabsTrigger`.
157
+ - Every `TabsTrigger` and `TabsContent` needs a `value` — the values must match.
158
+ - For top-level app navigation use Sidebar / TopBar, not Tabs.
159
+ - Don't stack two Tabs inside the same region — pick one. Nested tabs confuse keyboard nav and section structure.
160
+ - For router-bound tabs, keep `value` controlled — don't mix `defaultValue` with router-driven URLs.
161
+ - For 5+ tabs that overflow on mobile, consider a Select dropdown on small viewports or wrap in a horizontally scrollable container.
162
+ - Icons in TabsTrigger don't auto-size via IconProvider — set explicit `<Icon icon={...} size="sm" />` when the trigger feels off.
@@ -0,0 +1,139 @@
1
+ # Text
2
+
3
+ Typography primitive. Use instead of raw `<h1>`–`<h6>`, `<p>`, `<span>` for any visible text.
4
+
5
+ ```tsx
6
+ import { Text } from '@devalok/shilp-sutra/ui/text'
7
+ ```
8
+
9
+ ## When to use
10
+
11
+ - Any visible text: headings, body copy, captions, section labels, inline microcopy.
12
+ - Inline code spans inside body text? Pair with `<Code>`.
13
+ - Polymorphic — `<Text as="span">`, `<Text as="div">`, etc. — to demote semantics while keeping visual weight.
14
+ - Server-safe — renders in RSC trees without `'use client'`.
15
+
16
+ ## Variants
17
+
18
+ | Variant | Default element | Use |
19
+ |---|---|---|
20
+ | `heading-2xl` | `h1` | Page title / hero. |
21
+ | `heading-xl` | `h2` | Section title. |
22
+ | `heading-lg` | `h3` | Sub-section title. |
23
+ | `heading-md` | `h4` | Card title (used by `<CardTitle>`). |
24
+ | `heading-sm` | `h5` | Compact sub-heading. |
25
+ | `heading-xs` | `h6` | Smallest heading. |
26
+ | `body-lg` | `p` | Lead paragraphs. |
27
+ | `body-md` (default) | `p` | Standard body. |
28
+ | `body-sm` | `p` | Dense body — captions inside cards, table cells. |
29
+ | `body-xs` | `p` | Footnotes, fine print. |
30
+ | `label-lg` / `md` / `sm` / `xs` | `span` | UPPERCASE section labels, eyebrows. |
31
+ | `label-plain-lg` / `md` / `sm` / `xs` | `span` | Mixed-case labels (form labels, inline UI text). |
32
+ | `caption` | `span` | Image / chart captions. |
33
+ | `overline` | `span` | UPPERCASE marketing eyebrow. |
34
+ | `code` | `code` | Inline monospace code. |
35
+
36
+ `label-*` and `overline` variants are automatically uppercase. `label-plain-*` keeps mixed case.
37
+
38
+ ## Props
39
+
40
+ | Prop | Type | Notes |
41
+ |---|---|---|
42
+ | `variant` | See variants table | Default `body-md`. |
43
+ | `as` | `ElementType` | Override the auto-selected HTML element. |
44
+ | `className` | `string` | For color / alignment overrides. Use semantic tokens only. |
45
+
46
+ Plus all standard HTML attributes for whatever element it renders.
47
+
48
+ ## Examples
49
+
50
+ **Page heading + body:**
51
+ ```tsx
52
+ <Stack gap="ds-04">
53
+ <Text variant="heading-2xl">Projects</Text>
54
+ <Text variant="body-md" className="text-fg-muted">
55
+ A workspace for everything you ship.
56
+ </Text>
57
+ </Stack>
58
+ ```
59
+
60
+ **Section label + heading pair:**
61
+ ```tsx
62
+ <Stack gap="ds-02">
63
+ <Text variant="label-sm" className="text-fg-muted">REVENUE</Text>
64
+ <Text variant="heading-xl">$2.4M</Text>
65
+ <Text variant="body-sm" className="text-fg-muted">+18% YoY</Text>
66
+ </Stack>
67
+ ```
68
+
69
+ **Visual demotion via `as`:**
70
+ ```tsx
71
+ {/* h2-sized heading rendered as a div — useful when the element already has a heading ancestor */}
72
+ <Card>
73
+ <CardHeader>
74
+ <CardTitle>Activity</CardTitle> {/* h4 */}
75
+ <Text variant="heading-xl" as="div">Weekly summary</Text>
76
+ </CardHeader>
77
+ </Card>
78
+ ```
79
+
80
+ **Inline span inside a paragraph:**
81
+ ```tsx
82
+ <Text>
83
+ Press <Text as="kbd" variant="code">⌘ K</Text> to open the command palette.
84
+ </Text>
85
+ ```
86
+
87
+ **Caption under an image:**
88
+ ```tsx
89
+ <Stack gap="ds-02">
90
+ <img src={chart} alt="" />
91
+ <Text variant="caption" className="text-fg-muted">
92
+ Figure 1. Active users by region.
93
+ </Text>
94
+ </Stack>
95
+ ```
96
+
97
+ **Form field label (label-plain variant):**
98
+ ```tsx
99
+ <Stack gap="ds-02">
100
+ <Text variant="label-plain-sm" as="label" htmlFor="email">Email</Text>
101
+ <Input id="email" type="email" />
102
+ </Stack>
103
+ ```
104
+
105
+ Use `<Label>` for form fields when wired with FormField — `Text` is for non-form labels.
106
+
107
+ **Body with inline code:**
108
+ ```tsx
109
+ <Text>
110
+ Call <Code>onSubmit</Code> with the form values, or pass <Code>asChild</Code> to merge with a child element.
111
+ </Text>
112
+ ```
113
+
114
+ **Truncated single line:**
115
+ ```tsx
116
+ <Text variant="body-sm" className="truncate max-w-[200px]">
117
+ {longUserName}
118
+ </Text>
119
+ ```
120
+
121
+ ## Composability
122
+
123
+ - **Server-safe.** No client hooks. Use freely in RSC trees.
124
+ - **No context cascade** — pure typography primitive. Variants don't propagate through children.
125
+ - **Underpins other components.** `<CardTitle>`, `<Alert>`'s title, `<PageHeader>`, `<EmptyState>`, `<SectionHeader>` all render Text internally with specific variants. Don't wrap another Text inside them.
126
+ - **`as` overrides element only, not variant.** Visual weight stays. Use to demote semantics when you have a heading ancestor.
127
+
128
+ See `foundations/typography.md` for the full type scale, line-height tokens, font stack.
129
+
130
+ ## Rules
131
+
132
+ - Use Text for every visible text element. Don't write raw `<h1>` / `<p>` / `<span>` with manual classes.
133
+ - Pick variant by semantic intent, not visual size. `heading-xl` is an `<h2>` — use it for section headings, not because you want big text in a body paragraph (use `as="div"` for that).
134
+ - Don't wrap a `<Text>` inside `<CardTitle>` / `<Alert>` title — they already render Text internally.
135
+ - `label-*` and `overline` variants are automatically uppercase. Don't add `uppercase` class.
136
+ - For form labels paired with controls, use `<Label htmlFor>` from `/ui/label`. `Text variant="label-plain-*"` is for non-form labels.
137
+ - Color overrides go through semantic tokens (`text-fg-muted`, `text-fg`, `text-fg-subtle`). Never raw Tailwind palette utilities.
138
+ - Don't use Text inside another semantic heading — it produces nested headings that break screen-reader navigation.
139
+ - For inline code, use `<Code>`. `Text variant="code"` works but Code is the dedicated primitive.