@maxsteinwender/sort-ui 1.0.8 → 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.
@@ -1,406 +0,0 @@
1
- # Component Usage Guidelines
2
-
3
- Complete reference for components in `@maxsteinwender/sort-ui`.
4
-
5
- ## Component Reuse (CRITICAL)
6
-
7
- **Always reuse existing SortUI components — never inline custom markup when a component exists.**
8
-
9
- This is the most common failure mode for generated UIs. Pattern:
10
-
11
- - Status pill → `<Badge>` (not `<span className="bg-green-100 px-2 py-1 rounded-full">`)
12
- - Progress indicator → `<ProgressBar>` / `<RadialProgressBar>` (not a custom `<div>`)
13
- - User image → `<Avatar>` (not `<img className="rounded-full">`)
14
- - Clickable action → `<Button>` / `<LinkButton>` / `<ControlButton>` (not `<div onClick>`)
15
- - Form text input → `<InputField>` (not `<input>` with hand-rolled styling)
16
- - Section header + body → `<Card>` with `CardHeader`/`CardContent`/`CardFooter`
17
-
18
- ```tsx
19
- // ✅ DO
20
- <Badge color="green" shape="pill">Active</Badge>
21
- <Avatar src="/u.jpg" fallback="MS" size="md" />
22
- <ProgressBar value={75} label labelText="Upload" />
23
-
24
- // ❌ DON'T
25
- <span className="inline-flex bg-green-100 text-green-800 px-2 py-1 rounded-full text-xs">Active</span>
26
- <img src="/u.jpg" className="w-10 h-10 rounded-full" />
27
- <div className="h-2 bg-sui-bg-muted"><div style={{width:"75%"}} className="h-full bg-sui-state-primary" /></div>
28
- ```
29
-
30
- ---
31
-
32
- ## Import Paths
33
-
34
- ```tsx
35
- // Main components
36
- import { Button, InputField, Avatar, Badge, Card } from "@maxsteinwender/sort-ui";
37
-
38
- // Form components (React Hook Form wrappers) — separate sub-entry
39
- import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@maxsteinwender/sort-ui/form";
40
-
41
- // Toast
42
- import { Toaster } from "@maxsteinwender/sort-ui";
43
- import { toast } from "sonner";
44
- ```
45
-
46
- ---
47
-
48
- ## Buttons & Actions
49
-
50
- | Component | Variants | Key Props | Common Use |
51
- |-----------|----------|-----------|------------|
52
- | **Button** | `default`, `ghost`, `destructive`, `secondary`, `outline`, `ghost-muted` | `variant`, `size`, `shape`, `iconOnly`, `loading`, `asChild` | Primary actions |
53
- | **ButtonGroup** | — | `size`, `aria-label` (required), `orientation` | Toolbar groups |
54
- | **ButtonGroupItem** | — | `label`, `leadIcon`, `tailIcon`, `badge`, `iconOnly` | Items in ButtonGroup |
55
- | **LinkButton** | `default`, `muted`, `informative` | `variant`, `size`, `leadIcon`, `tailIcon` | Styled links |
56
- | **SocialButton** | `secondary`, `brand` | `brand` (required), `shape`, `iconOnly` | Social login |
57
- | **ControlButton** | `default`, `inverted` | `size`, `shape`, `dismissLabel`, `asSpan` | Dismiss / small controls |
58
- | **FilterButton** | — | `selected`, `filterValue`, `onClearFilter` | Filter toggles |
59
-
60
- ```tsx
61
- // ✅ iconOnly requires aria-label
62
- <Button iconOnly aria-label="Settings"><i className="ri-settings-line" /></Button>
63
-
64
- // ✅ loading prop replaces manual spinner
65
- <Button loading>Saving…</Button>
66
-
67
- // ✅ Size + variant
68
- <Button size="lg" variant="destructive">Delete</Button>
69
-
70
- // ✅ ButtonGroup
71
- <ButtonGroup size="md" aria-label="Text formatting">
72
- <ButtonGroupItem label="Bold" leadIcon="ri-bold" />
73
- <ButtonGroupItem label="Italic" leadIcon="ri-italic" />
74
- </ButtonGroup>
75
- ```
76
-
77
- ---
78
-
79
- ## Navigation
80
-
81
- | Component | Variants | Key Props | Common Use |
82
- |-----------|----------|-----------|------------|
83
- | **SortBreadcrumb** | `slash`, `arrow`, `dot` | `items`, `variant` | Page navigation |
84
- | **NavigationMenu** | — | — | Top-level nav with dropdowns |
85
- | **Tabs** | — | `value`, `onValueChange`, `defaultValue` | Content switching |
86
- | **Paginator** | `simple`, `numbered`, `dots` | `currentPage`, `totalPages`, `onPageChange` | Pagination |
87
- | **HorizontalStepper**, **VerticalStepper** | — | `steps`, `currentStep` | Multi-step flows |
88
-
89
- ```tsx
90
- <SortBreadcrumb
91
- variant="slash"
92
- items={[
93
- { label: "Home", href: "/" },
94
- { label: "Projects", href: "/projects" },
95
- { label: "Current Project" },
96
- ]}
97
- />
98
-
99
- <Tabs value={activeTab} onValueChange={setActiveTab}>
100
- <TabsList>
101
- <TabsTrigger value="tab1">Tab 1</TabsTrigger>
102
- </TabsList>
103
- <TabsContent value="tab1">…</TabsContent>
104
- </Tabs>
105
- ```
106
-
107
- ---
108
-
109
- ## Forms & Inputs
110
-
111
- | Component | Variants | Key Props |
112
- |-----------|----------|-----------|
113
- | **InputField** | `default`, `soft`, `shadow` | `label`, `error`, `leadIcon`, `tailIcon`, `prefix`, `suffix`, `leadAddon`, `tailAddon`, `tags`, `leadButton`, `tailButton` |
114
- | **TextAreaField** | `default`, `soft`, `shadow` | `label`, `error`, `rows` |
115
- | **Checkbox** | `default`, `shadow` | `checked`, `onCheckedChange`, `variant` |
116
- | **CheckboxWithText** / **CheckboxCard** | — | `label`, `description`, `checkboxPosition` / `title`, `description`, `layout` |
117
- | **RadioButton** / **WithText** / **Card** | — | `value`, `label`, `description`, `radioPosition`, `layout` |
118
- | **Switch** / **SwitchWithText** | — | `checked`, `onCheckedChange`, `label`, `description` |
119
- | **Select**, **SelectMenu** | — | `value`, `onValueChange`, `searchInput` |
120
- | **DatePicker**, **DateRangePicker** | — | `date`, `onDateChange`, `dateRange`, `onDateRangeChange` |
121
- | **FileUpload** | — | `files`, `onFilesAdded`, `accept`, `maxSize`, `multiple`, `autoPreview` |
122
- | **Slider**, **SliderRange**, **DataRangeSlider** | — | `value`, `onValueChange`, `min`, `max`, `step` |
123
-
124
- ```tsx
125
- // InputField with error
126
- <InputField
127
- label="Email"
128
- required
129
- error={errors.email}
130
- leadIcon={<i className="ri-mail-line" />}
131
- />
132
-
133
- // Addons / prefix / suffix
134
- <InputField label="Website" leadAddon="https://" tailAddon=".com" />
135
- <InputField label="Amount" prefix="$" suffix="USD" />
136
-
137
- // Checkbox patterns
138
- <CheckboxCard
139
- title="Feature A"
140
- description="Description"
141
- selected={selected}
142
- onSelectedChange={setSelected}
143
- checkboxPosition="right"
144
- />
145
- ```
146
-
147
- **FileUpload workflow (parent-owned state):**
148
- ```tsx
149
- const [files, setFiles] = useState<UploadFile[]>([]);
150
-
151
- <FileUpload
152
- files={files}
153
- onFilesAdded={(newFiles) => {
154
- const uploads = newFiles.map(file => ({
155
- id: crypto.randomUUID(),
156
- file,
157
- state: "uploading" as const,
158
- progress: 0,
159
- }));
160
- setFiles([...files, ...uploads]);
161
- }}
162
- onRemove={(id) => setFiles(files.filter(f => f.id !== id))}
163
- accept="image/*"
164
- maxSize={10 * 1024 * 1024}
165
- multiple
166
- autoPreview
167
- />
168
- ```
169
-
170
- ---
171
-
172
- ## Data Display
173
-
174
- | Component | Variants | Key Props |
175
- |-----------|----------|-----------|
176
- | **Avatar** | — | `src`, `fallback`, `size`, `shape`, `ring`, `topBadge`, `bottomBadge`, `color` |
177
- | **AvatarGroup** | — | `items`, `size`, `stacking`, `aria-label` |
178
- | **Badge** | `label`, `icon`, `image`, `dot` | `color`, `size`, `shape`, `variant`, `onClose` |
179
- | **Card** | `default`, `flat` | `variant` |
180
- | **Table** / **GridTable** | — | row/col composition |
181
- | **Skeleton** | — | `className` |
182
- | **EmptyState** | `default`, `icon`, `placeholder-icon` | `title`, `description`, `icon`, `action` |
183
-
184
- ```tsx
185
- // Avatar with badge
186
- <Avatar
187
- src="/user.jpg"
188
- alt="User"
189
- fallback="UN"
190
- size="md"
191
- bottomBadge
192
- bottomBadgeVariant="online"
193
- bottomBadgeLabel="Online"
194
- />
195
-
196
- // Avatar group
197
- <AvatarGroup
198
- items={[
199
- { src: "/a.jpg", alt: "Alice", fallback: "A" },
200
- { fallback: "B" },
201
- ]}
202
- size="md"
203
- aria-label="2 team members"
204
- />
205
-
206
- // Badge variants
207
- <Badge color="green" shape="pill">Active</Badge>
208
- <Badge variant="icon" icon={<i className="ri-star-fill" />}>Featured</Badge>
209
- <Badge color="blue" onClose={() => remove()}>Removable Tag</Badge>
210
-
211
- // Card composition
212
- <Card variant="default">
213
- <CardCover flush aspectRatio="16/9">
214
- <img src="/cover.jpg" alt="Cover" />
215
- </CardCover>
216
- <CardHeader>
217
- <CardTitle>Title</CardTitle>
218
- <CardDescription>Description</CardDescription>
219
- </CardHeader>
220
- <CardContent>{/* Main content */}</CardContent>
221
- <CardFooter>{/* Actions */}</CardFooter>
222
- </Card>
223
- ```
224
-
225
- ---
226
-
227
- ## Feedback
228
-
229
- | Component | Variants | Key Props |
230
- |-----------|----------|-----------|
231
- | **Alert** | `default`, `destructive` | `variant` |
232
- | **Toast** | — | via Sonner |
233
- | **ProgressBar** | `linear`, `dashed` | `value`, `label`, `labelText`, `caption` |
234
- | **RadialProgressBar** | `default`, `success`, `failed` | `value`, `size`, `shape`, `showValue` |
235
- | **InlineTips** | `default`, `error`, `success`, `information`, `warning` | `variant`, `colorStyle`, `title` |
236
-
237
- ```tsx
238
- // Toast
239
- import { toast } from "sonner";
240
- toast.success("Saved");
241
- toast.error("Failed");
242
-
243
- // Progress
244
- <ProgressBar value={75} label labelText="Upload progress" />
245
- <RadialProgressBar value={80} size={120} showValue />
246
- <RadialProgressBar variant="success" value={100} label="Done" shape="half" />
247
- ```
248
-
249
- ---
250
-
251
- ## Charts (Recharts — React 19 compat CRITICAL)
252
-
253
- **Every Recharts series MUST set `isAnimationActive={false}`.** Recharts 2.x animations rely on removed React 19 APIs and render empty without this prop.
254
-
255
- ```tsx
256
- import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@maxsteinwender/sort-ui";
257
- import { LineChart, Line, BarChart, Bar, PieChart, Pie, AreaChart, Area } from "recharts";
258
-
259
- const config: ChartConfig = {
260
- revenue: { label: "Revenue", color: "var(--chart-1)" },
261
- sales: { label: "Sales", color: "var(--chart-2)" },
262
- };
263
-
264
- // ✅ Line chart
265
- <ChartContainer config={config}>
266
- <LineChart data={data}>
267
- <ChartTooltip content={<ChartTooltipContent />} />
268
- <Line dataKey="revenue" stroke="var(--color-revenue)" isAnimationActive={false} />
269
- </LineChart>
270
- </ChartContainer>
271
-
272
- // ✅ Bar chart
273
- <ChartContainer config={config}>
274
- <BarChart data={data}>
275
- <ChartTooltip content={<ChartTooltipContent />} />
276
- <Bar dataKey="sales" fill="var(--color-sales)" isAnimationActive={false} />
277
- </BarChart>
278
- </ChartContainer>
279
-
280
- // ✅ Area + Pie
281
- <Area dataKey="revenue" stroke="var(--color-revenue)" fill="var(--color-revenue)" isAnimationActive={false} />
282
- <Pie data={pieData} dataKey="value" isAnimationActive={false} />
283
-
284
- // ❌ WRONG — animation enabled (default) → empty chart in React 19
285
- <Line dataKey="revenue" stroke="var(--color-revenue)" />
286
- ```
287
-
288
- Chart color tokens: `var(--chart-1)` through `var(--chart-5)`.
289
-
290
- ---
291
-
292
- ## Overlays
293
-
294
- | Component | Type | Key Props |
295
- |-----------|------|-----------|
296
- | **Dialog** | Modal | composition: `DialogTrigger`, `DialogContent`, `DialogHeader`, `DialogTitle`, `DialogDescription`, `DialogFooter`, `DialogClose` |
297
- | **AlertDialog** | Modal | confirmation dialogs |
298
- | **Sheet** | Drawer | side panels |
299
- | **Popover** | Floating | popovers |
300
- | **Tooltip** | Floating | hover tooltips (requires `<TooltipProvider>`) |
301
- | **DropdownMenu** | Floating | `search`, `searchValue`, `onSearchChange` |
302
- | **CommandPalette** | Command | cmdk-powered palette |
303
-
304
- ```tsx
305
- <Dialog>
306
- <DialogTrigger asChild>
307
- <Button>Open</Button>
308
- </DialogTrigger>
309
- <DialogContent>
310
- <DialogHeader>
311
- <DialogTitle>Title</DialogTitle>
312
- <DialogDescription>Description</DialogDescription>
313
- </DialogHeader>
314
- {/* Content */}
315
- <DialogFooter>
316
- <DialogClose asChild><Button variant="ghost">Cancel</Button></DialogClose>
317
- <Button>Confirm</Button>
318
- </DialogFooter>
319
- </DialogContent>
320
- </Dialog>
321
-
322
- <DropdownMenu>
323
- <DropdownMenuTrigger asChild>
324
- <Button>Menu</Button>
325
- </DropdownMenuTrigger>
326
- <DropdownMenuContent search searchPlaceholder="Search…">
327
- <DropdownMenuLabel>Actions</DropdownMenuLabel>
328
- <DropdownMenuItem>Edit</DropdownMenuItem>
329
- <DropdownMenuSeparator />
330
- <DropdownMenuItem variant="destructive">Delete</DropdownMenuItem>
331
- </DropdownMenuContent>
332
- </DropdownMenu>
333
-
334
- <TooltipProvider>
335
- <Tooltip>
336
- <TooltipTrigger asChild>
337
- <Button iconOnly aria-label="Help"><i className="ri-question-line" /></Button>
338
- </TooltipTrigger>
339
- <TooltipContent><p>Help text</p></TooltipContent>
340
- </Tooltip>
341
- </TooltipProvider>
342
- ```
343
-
344
- ---
345
-
346
- ## Form Integration (React Hook Form)
347
-
348
- **IMPORTANT:** Form components use a separate import path.
349
-
350
- ```tsx
351
- import { useForm } from "react-hook-form";
352
- import { Form, FormField, FormItem, FormLabel, FormControl, FormDescription, FormMessage } from "@maxsteinwender/sort-ui/form";
353
- import { InputField, Button } from "@maxsteinwender/sort-ui";
354
-
355
- const form = useForm({ defaultValues: { email: "" } });
356
-
357
- <Form {...form}>
358
- <form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-sui-16">
359
- <FormField
360
- control={form.control}
361
- name="email"
362
- render={({ field }) => (
363
- <FormItem>
364
- <FormLabel>Email</FormLabel>
365
- <FormControl>
366
- <InputField {...field} placeholder="you@example.com" />
367
- </FormControl>
368
- <FormDescription>We'll never share your email.</FormDescription>
369
- <FormMessage />
370
- </FormItem>
371
- )}
372
- />
373
- <Button type="submit">Submit</Button>
374
- </form>
375
- </Form>
376
- ```
377
-
378
- ---
379
-
380
- ## Composite Components
381
-
382
- | Component | Purpose |
383
- |-----------|---------|
384
- | **SignInCard** | Pre-composed sign-in card |
385
- | **ProductCard** | Pre-composed product card |
386
- | **MarkdownEditor** | Lexical-based markdown editor |
387
- | **CodeBlock** | Code display with syntax highlighting |
388
- | **Userbar**, **SidebarMenuItem**, **SidebarCard** | Sidebar layout primitives |
389
-
390
- ---
391
-
392
- ## Icons
393
-
394
- **IMPORTANT:** See `icon-discovery.md` for detailed icon guidelines.
395
-
396
- SortUI uses **Remix Icons** (bundled). Use `<i className="ri-{name}-{variant}" />` syntax.
397
-
398
- ```tsx
399
- <Button><i className="ri-search-line" />Search</Button>
400
- <i className="ri-user-fill" />
401
-
402
- // Icon color via token (no sui- prefix variations — text-sui-icon-* only)
403
- <i className="ri-user-fill text-sui-icon-subtle" />
404
- ```
405
-
406
- **DO NOT** install `lucide-react`, `heroicons`, `react-icons`, or any other icon package.
@@ -1,247 +0,0 @@
1
- # Icon Discovery and Usage Guidelines
2
-
3
- This document explains how to find and use icons in the SortUI Design System.
4
-
5
- ## Icon Package
6
-
7
- **Bundled with:** `@maxsteinwender/sort-ui` (Remix Icons — no separate install)
8
- **Import:** Icons are loaded automatically via the main CSS bundle
9
- **Location in package:** `node_modules/@maxsteinwender/sort-ui/dist/styles.css` (font-icon classes)
10
-
11
- **DO NOT install `lucide-react`, `heroicons`, `react-icons`, or any other icon package.** Remix Icons is the single source.
12
-
13
- ## Naming Convention
14
-
15
- ```
16
- ri-{icon-name}-{variant}
17
- ```
18
-
19
- **Variants:**
20
- - `-line` — outlined/stroke style (most common)
21
- - `-fill` — filled/solid style
22
-
23
- **Examples:**
24
- - `ri-user-line`, `ri-user-fill`
25
- - `ri-settings-line`, `ri-settings-fill`
26
- - `ri-arrow-right-line`, `ri-arrow-right-fill`
27
- - `ri-check-line`, `ri-check-fill`
28
- - `ri-close-line`, `ri-close-fill`
29
-
30
- ## Usage
31
-
32
- Icons are rendered as `<i>` elements with Remix Icon class names:
33
-
34
- ```tsx
35
- <i className="ri-user-line" />
36
- <i className="ri-settings-fill" />
37
- <i className="ri-arrow-right-line" />
38
- ```
39
-
40
- **Color via icon tokens** (note: icon colors use `text-sui-icon-*`):
41
- ```tsx
42
- <i className="ri-user-fill text-sui-icon-default" />
43
- <i className="ri-error-warning-line text-sui-icon-destructive" />
44
- <i className="ri-check-line text-sui-icon-success" />
45
- <i className="ri-information-line text-sui-icon-informative" />
46
- <i className="ri-alarm-warning-line text-sui-icon-warning" />
47
- <i className="ri-user-line text-sui-icon-subtle" />
48
- ```
49
-
50
- **Within SortUI components:**
51
- ```tsx
52
- // Button with lead icon
53
- <Button>
54
- <i className="ri-search-line" />
55
- Search
56
- </Button>
57
-
58
- // Icon-only button — MUST have aria-label
59
- <Button iconOnly aria-label="Settings">
60
- <i className="ri-settings-line" />
61
- </Button>
62
-
63
- // InputField with lead icon
64
- <InputField
65
- leadIcon={<i className="ri-mail-line" />}
66
- placeholder="Email"
67
- />
68
- ```
69
-
70
- ## Sizing
71
-
72
- Most SortUI components handle icon sizing automatically. When direct size control is needed, use Tailwind text-size utilities — do NOT use `ri-xs` / `ri-sm` helpers:
73
-
74
- ```tsx
75
- <i className="ri-user-line text-sm" />
76
- <i className="ri-user-line text-base" />
77
- <i className="ri-user-line text-lg" />
78
- <i className="ri-user-line text-xl" />
79
- <i className="ri-user-line text-2xl" />
80
- ```
81
-
82
- ## How to Find Icons
83
-
84
- **CRITICAL:** Do NOT guess icon names. Icon names must be verified before use.
85
-
86
- ### Method 1: Search the bundled CSS
87
-
88
- The bundled CSS contains every available icon class. Search `node_modules/@maxsteinwender/sort-ui/dist/styles.css` for keywords:
89
-
90
- ```bash
91
- grep -o "\.ri-[a-z0-9-]*user[a-z0-9-]*:before" node_modules/@maxsteinwender/sort-ui/dist/styles.css
92
- grep -o "\.ri-[a-z0-9-]*calendar[a-z0-9-]*:before" node_modules/@maxsteinwender/sort-ui/dist/styles.css
93
- ```
94
-
95
- Example workflow:
96
- 1. Identify keyword (e.g., "calendar")
97
- 2. Search styles.css for `ri-calendar`
98
- 3. Find matching icons: `ri-calendar-line`, `ri-calendar-fill`, `ri-calendar-event-line`, `ri-calendar-2-line`, etc.
99
-
100
- ### Method 2: Official Remix Icon reference
101
-
102
- The design system uses **Remix Icon v4.9.1**. All icons from Remix Icon 4.x are available.
103
-
104
- Reference: https://remixicon.com/
105
-
106
- **Common icon categories:**
107
- - **System:** settings, more, menu, close, check, error, information, question, warning
108
- - **Arrows:** arrow-up, arrow-down, arrow-left, arrow-right, chevron-up, chevron-down
109
- - **Media:** play, pause, stop, volume, image, camera, video
110
- - **User:** user, team, account, contacts, profile
111
- - **Files:** file, folder, attachment, download, upload
112
- - **Communication:** mail, message, chat, notification, phone
113
- - **Design:** pencil, brush, palette, format, layout
114
- - **Business:** briefcase, bank, wallet, price-tag, shopping-cart
115
- - **Maps:** map, pin, location, navigation, compass
116
- - **Device:** phone, tablet, computer, keyboard, mouse
117
- - **Weather:** sun, moon, cloud, rain, snow
118
-
119
- ## Common Icon Patterns
120
-
121
- ### Navigation icons
122
- ```tsx
123
- <i className="ri-arrow-left-line" />
124
- <i className="ri-arrow-right-line" />
125
- <i className="ri-arrow-up-line" />
126
- <i className="ri-arrow-down-line" />
127
- <i className="ri-chevron-left-line" />
128
- <i className="ri-chevron-right-line" />
129
- <i className="ri-menu-line" />
130
- <i className="ri-close-line" />
131
- <i className="ri-more-line" />
132
- ```
133
-
134
- ### Action icons
135
- ```tsx
136
- <i className="ri-add-line" />
137
- <i className="ri-subtract-line" />
138
- <i className="ri-edit-line" />
139
- <i className="ri-delete-bin-line" />
140
- <i className="ri-save-line" />
141
- <i className="ri-download-line" />
142
- <i className="ri-upload-line" />
143
- <i className="ri-share-line" />
144
- <i className="ri-search-line" />
145
- <i className="ri-filter-line" />
146
- ```
147
-
148
- ### Status icons
149
- ```tsx
150
- <i className="ri-check-line text-sui-icon-success" />
151
- <i className="ri-close-circle-line text-sui-icon-destructive" />
152
- <i className="ri-error-warning-line text-sui-icon-warning" />
153
- <i className="ri-information-line text-sui-icon-informative" />
154
- <i className="ri-checkbox-circle-line text-sui-icon-success" />
155
- ```
156
-
157
- ### User / Social icons
158
- ```tsx
159
- <i className="ri-user-line" />
160
- <i className="ri-user-fill" />
161
- <i className="ri-team-line" />
162
- <i className="ri-account-circle-line" />
163
- <i className="ri-heart-line" />
164
- <i className="ri-star-line" />
165
- <i className="ri-thumb-up-line" />
166
- ```
167
-
168
- ### Communication icons
169
- ```tsx
170
- <i className="ri-mail-line" />
171
- <i className="ri-message-line" />
172
- <i className="ri-chat-line" />
173
- <i className="ri-notification-line" />
174
- <i className="ri-phone-line" />
175
- <i className="ri-send-plane-line" />
176
- ```
177
-
178
- ### File / Document icons
179
- ```tsx
180
- <i className="ri-file-line" />
181
- <i className="ri-folder-line" />
182
- <i className="ri-attachment-line" />
183
- <i className="ri-file-text-line" />
184
- <i className="ri-file-pdf-line" />
185
- <i className="ri-image-line" />
186
- ```
187
-
188
- ### Settings / System icons
189
- ```tsx
190
- <i className="ri-settings-line" />
191
- <i className="ri-equalizer-line" />
192
- <i className="ri-tools-line" />
193
- <i className="ri-lock-line" />
194
- <i className="ri-eye-line" />
195
- <i className="ri-eye-off-line" />
196
- ```
197
-
198
- ## DO / DON'T
199
-
200
- **DO:**
201
- ```tsx
202
- <i className="ri-user-line" />
203
- <i className="ri-check-line" /> {/* outlined */}
204
- <i className="ri-check-fill" /> {/* filled */}
205
-
206
- // Icon-only button with aria-label
207
- <Button iconOnly aria-label="Settings">
208
- <i className="ri-settings-line" />
209
- </Button>
210
- ```
211
-
212
- **DON'T:**
213
- ```tsx
214
- // ❌ Guessed icon names
215
- <i className="ri-profile-line" /> {/* doesn't exist — use ri-user-line */}
216
-
217
- // ❌ Installing alternate icon packages
218
- import { User } from "lucide-react";
219
-
220
- // ❌ Icon-only button without aria-label
221
- <Button iconOnly>
222
- <i className="ri-settings-line" />
223
- </Button>
224
- ```
225
-
226
- ## Verifying Icon Existence
227
-
228
- Before using an icon in generated code:
229
-
230
- 1. **Search the bundled CSS** for the icon class
231
- 2. **Check the pattern** matches `ri-{name}-line` or `ri-{name}-fill`
232
- 3. **If uncertain**, pick a similar known icon (e.g., `ri-user-line` instead of guessing `ri-profile-line`)
233
-
234
- ```bash
235
- # Verify ri-calendar-event-line exists
236
- grep "ri-calendar-event-line:before" node_modules/@maxsteinwender/sort-ui/dist/styles.css
237
- # ✓ If output contains "content:" the icon is valid
238
- ```
239
-
240
- ## Reminders
241
-
242
- - **Remix Icons are bundled** — no separate package install
243
- - **Do NOT install `lucide-react`** or other icon packages
244
- - **Always verify icon existence** before use
245
- - **Use `-line` for outlined**, `-fill` for solid
246
- - **Provide `aria-label`** for icon-only buttons
247
- - **Icon color via `text-sui-icon-*` tokens** (never raw hex)