@maxsteinwender/sort-ui 1.1.0 → 1.1.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/README.md +4 -0
- package/guidelines/Guidelines.md +294 -0
- package/guidelines/components.md +406 -0
- package/guidelines/icon-discovery.md +247 -0
- package/guidelines/setup.md +343 -0
- package/guidelines/styles.md +356 -0
- package/guidelines/tokens.md +354 -0
- package/package.json +3 -2
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
# SortUI Design System — Setup Instructions
|
|
2
|
+
|
|
3
|
+
Complete setup guide for integrating `@maxsteinwender/sort-ui` into your project.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Package Manager
|
|
8
|
+
|
|
9
|
+
**Use npm.** This package is authored and published using npm. Examples in these guidelines always show `npm install`. Do not suggest `pnpm add` or `yarn add`.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Package Installation
|
|
14
|
+
|
|
15
|
+
**CRITICAL**: You MUST explicitly install every package listed below as a direct dependency, exactly as written. Transitive availability does NOT count as installed.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @maxsteinwender/sort-ui
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Add to your `package.json`:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@maxsteinwender/sort-ui": "^1.0.8"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Peer dependencies (required):**
|
|
32
|
+
- `react` >= 19.0.0
|
|
33
|
+
- `react-dom` >= 19.0.0
|
|
34
|
+
|
|
35
|
+
If using form components (React Hook Form wrappers), install:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install react-hook-form
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## CSS Import
|
|
44
|
+
|
|
45
|
+
Import the bundled CSS **once** at your app entry point (e.g., root layout, `_app.tsx`, or `main.tsx`):
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import "@maxsteinwender/sort-ui/styles.css";
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This loads:
|
|
52
|
+
- Design tokens (CSS custom properties)
|
|
53
|
+
- Remix Icons (font icons, bundled)
|
|
54
|
+
- Component styles
|
|
55
|
+
- Redaction font (bundled)
|
|
56
|
+
- Geist Sans + Geist Mono (bundled)
|
|
57
|
+
|
|
58
|
+
**IMPORTANT:** Only import this CSS file once. Do not import it in multiple files.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Theme Configuration — All 4 Themes
|
|
63
|
+
|
|
64
|
+
Apply ONE of these classes to the root `<html>` (or `<body>`) element. The SortUI Design System ships **four** themes and all four must render correctly for any generated UI.
|
|
65
|
+
|
|
66
|
+
| Theme Class | Color Scheme | Mode |
|
|
67
|
+
|-------------|--------------|------|
|
|
68
|
+
| `light` | zinc/blue | light (default) |
|
|
69
|
+
| `dark` | zinc/blue | dark |
|
|
70
|
+
| `theme-2` | neutral/orange | light |
|
|
71
|
+
| `theme-2-dark` | neutral/orange | dark |
|
|
72
|
+
|
|
73
|
+
**Example — light (default):**
|
|
74
|
+
```tsx
|
|
75
|
+
// app/layout.tsx
|
|
76
|
+
import "@maxsteinwender/sort-ui/styles.css";
|
|
77
|
+
|
|
78
|
+
export default function RootLayout({ children }) {
|
|
79
|
+
return (
|
|
80
|
+
<html lang="en" className="light">
|
|
81
|
+
<body>{children}</body>
|
|
82
|
+
</html>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Example — dark:**
|
|
88
|
+
```tsx
|
|
89
|
+
<html lang="en" className="dark">
|
|
90
|
+
<body>{children}</body>
|
|
91
|
+
</html>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Example — theme-2 (neutral/orange, light):**
|
|
95
|
+
```tsx
|
|
96
|
+
<html lang="en" className="theme-2">
|
|
97
|
+
<body>{children}</body>
|
|
98
|
+
</html>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Example — theme-2-dark (neutral/orange, dark):**
|
|
102
|
+
```tsx
|
|
103
|
+
<html lang="en" className="theme-2-dark">
|
|
104
|
+
<body>{children}</body>
|
|
105
|
+
</html>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Dynamic theme switching:**
|
|
109
|
+
```tsx
|
|
110
|
+
"use client";
|
|
111
|
+
|
|
112
|
+
import { useEffect, useState } from "react";
|
|
113
|
+
|
|
114
|
+
type Theme = "light" | "dark" | "theme-2" | "theme-2-dark";
|
|
115
|
+
|
|
116
|
+
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|
117
|
+
const [theme, setTheme] = useState<Theme>("light");
|
|
118
|
+
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
document.documentElement.className = theme;
|
|
121
|
+
}, [theme]);
|
|
122
|
+
|
|
123
|
+
return <>{children}</>;
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Theme variants swap CSS variables automatically — never hard-code theme-specific class logic in components.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Tailwind CSS v3 Setup
|
|
132
|
+
|
|
133
|
+
**IMPORTANT:** SortUI targets **Tailwind CSS v3** (`^3.4.1`). It is **NOT** Tailwind v4. Do not use the v4 `@theme` directive or v4-only APIs. The package ships a **Tailwind v3 preset** that extends Tailwind with SUI design tokens.
|
|
134
|
+
|
|
135
|
+
### 1. Install Tailwind CSS v3
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npm install -D tailwindcss@^3.4.1 postcss autoprefixer
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 2. Configure Tailwind
|
|
142
|
+
|
|
143
|
+
Import the SortUI preset in your `tailwind.config.ts` (or `tailwind.config.js`):
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
// tailwind.config.ts
|
|
147
|
+
import type { Config } from "tailwindcss";
|
|
148
|
+
import sortUiPreset from "@maxsteinwender/sort-ui/tailwind-preset";
|
|
149
|
+
|
|
150
|
+
const config: Config = {
|
|
151
|
+
presets: [sortUiPreset],
|
|
152
|
+
content: [
|
|
153
|
+
"./src/app/**/*.{ts,tsx,js,jsx}",
|
|
154
|
+
"./src/components/**/*.{ts,tsx,js,jsx}",
|
|
155
|
+
],
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export default config;
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**JavaScript version:**
|
|
162
|
+
```js
|
|
163
|
+
// tailwind.config.js
|
|
164
|
+
const sortUiPreset = require("@maxsteinwender/sort-ui/tailwind-preset");
|
|
165
|
+
|
|
166
|
+
module.exports = {
|
|
167
|
+
presets: [sortUiPreset],
|
|
168
|
+
content: [
|
|
169
|
+
"./src/app/**/*.{ts,tsx,js,jsx}",
|
|
170
|
+
"./src/components/**/*.{ts,tsx,js,jsx}",
|
|
171
|
+
],
|
|
172
|
+
};
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### 3. Add Tailwind v3 directives
|
|
176
|
+
|
|
177
|
+
Create or update your global CSS file (e.g., `globals.css`):
|
|
178
|
+
|
|
179
|
+
```css
|
|
180
|
+
@tailwind base;
|
|
181
|
+
@tailwind components;
|
|
182
|
+
@tailwind utilities;
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**IMPORTANT:** Import `@maxsteinwender/sort-ui/styles.css` BEFORE your global CSS file:
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
// app/layout.tsx
|
|
189
|
+
import "@maxsteinwender/sort-ui/styles.css";
|
|
190
|
+
import "./globals.css";
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Tailwind Utility Classes (correct naming)
|
|
196
|
+
|
|
197
|
+
The SortUI preset exposes utility classes that follow the `sui-` prefix rules in `Guidelines.md`:
|
|
198
|
+
|
|
199
|
+
| Token Category | Prefix? | Tailwind Classes | Example |
|
|
200
|
+
|----------------|---------|------------------|---------|
|
|
201
|
+
| Background | `sui-` | `bg-sui-bg-default`, `bg-sui-bg-subtle` | `<div className="bg-sui-bg-default" />` |
|
|
202
|
+
| Text | `sui-` | `text-sui-text-default`, `text-sui-text-subtle` | `<p className="text-sui-text-default" />` |
|
|
203
|
+
| Border color | `sui-` | `border-sui-border-default` | `<div className="border border-sui-border-default" />` |
|
|
204
|
+
| Icon color | `sui-` | `text-sui-icon-default`, `text-sui-icon-destructive` | `<i className="ri-user-line text-sui-icon-subtle" />` |
|
|
205
|
+
| Spacing | `sui-` | `p-sui-24`, `gap-sui-16`, `m-sui-8`, `space-y-sui-16` | `<div className="p-sui-24 gap-sui-16" />` |
|
|
206
|
+
| Radius | **no prefix** | `rounded-md`, `rounded-lg`, `rounded-full`, `rounded-card-md` | `<div className="rounded-lg" />` |
|
|
207
|
+
| Shadow | **no prefix** | `shadow-default`, `shadow-card`, `shadow-focus`, `shadow-modal-md` | `<div className="shadow-card" />` |
|
|
208
|
+
| Border width | **no prefix** | `border`, `border-md`, `border-lg` | `<div className="border-md" />` |
|
|
209
|
+
|
|
210
|
+
❌ `shadow-sui-default`, `shadow-sui-card`, `shadow-sm`, `shadow-md`, `shadow-lg` — **these do not exist.**
|
|
211
|
+
❌ `rounded-sui-md`, `rounded-sui-lg`, `rounded-sui-full` — **these do not exist.**
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Toast Provider Setup
|
|
216
|
+
|
|
217
|
+
If using the Toast component (powered by Sonner), add the `<Toaster />` component to your root layout:
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
// app/layout.tsx
|
|
221
|
+
import { Toaster } from "@maxsteinwender/sort-ui";
|
|
222
|
+
import "@maxsteinwender/sort-ui/styles.css";
|
|
223
|
+
|
|
224
|
+
export default function RootLayout({ children }) {
|
|
225
|
+
return (
|
|
226
|
+
<html lang="en" className="light">
|
|
227
|
+
<body>
|
|
228
|
+
{children}
|
|
229
|
+
<Toaster />
|
|
230
|
+
</body>
|
|
231
|
+
</html>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Usage:**
|
|
237
|
+
```tsx
|
|
238
|
+
import { toast } from "sonner";
|
|
239
|
+
|
|
240
|
+
toast.success("Operation successful");
|
|
241
|
+
toast.error("Something went wrong");
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Tooltip Provider Setup
|
|
247
|
+
|
|
248
|
+
If using Tooltip components, wrap your app with `<TooltipProvider>`:
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
import { TooltipProvider } from "@maxsteinwender/sort-ui";
|
|
252
|
+
|
|
253
|
+
export default function RootLayout({ children }) {
|
|
254
|
+
return (
|
|
255
|
+
<html lang="en" className="light">
|
|
256
|
+
<body>
|
|
257
|
+
<TooltipProvider>{children}</TooltipProvider>
|
|
258
|
+
</body>
|
|
259
|
+
</html>
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Form Integration
|
|
267
|
+
|
|
268
|
+
For form components, install `react-hook-form` and import from the `/form` entry point:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
npm install react-hook-form
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
import { useForm } from "react-hook-form";
|
|
276
|
+
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@maxsteinwender/sort-ui/form";
|
|
277
|
+
import { InputField, Button } from "@maxsteinwender/sort-ui";
|
|
278
|
+
|
|
279
|
+
const form = useForm({ defaultValues: { email: "" } });
|
|
280
|
+
|
|
281
|
+
<Form {...form}>
|
|
282
|
+
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-sui-16">
|
|
283
|
+
<FormField
|
|
284
|
+
control={form.control}
|
|
285
|
+
name="email"
|
|
286
|
+
render={({ field }) => (
|
|
287
|
+
<FormItem>
|
|
288
|
+
<FormLabel>Email</FormLabel>
|
|
289
|
+
<FormControl>
|
|
290
|
+
<InputField {...field} placeholder="you@example.com" />
|
|
291
|
+
</FormControl>
|
|
292
|
+
<FormMessage />
|
|
293
|
+
</FormItem>
|
|
294
|
+
)}
|
|
295
|
+
/>
|
|
296
|
+
<Button type="submit">Submit</Button>
|
|
297
|
+
</form>
|
|
298
|
+
</Form>
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Icon Usage
|
|
304
|
+
|
|
305
|
+
Icons are bundled with the design system (Remix Icons). No additional installation required.
|
|
306
|
+
|
|
307
|
+
```tsx
|
|
308
|
+
<i className="ri-user-line" />
|
|
309
|
+
<i className="ri-settings-fill" />
|
|
310
|
+
|
|
311
|
+
<Button>
|
|
312
|
+
<i className="ri-search-line" />
|
|
313
|
+
Search
|
|
314
|
+
</Button>
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**DO NOT install `lucide-react`, `heroicons`, `react-icons`, or any other icon package.**
|
|
318
|
+
|
|
319
|
+
See `icon-discovery.md` for the icon verification workflow.
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## TypeScript Support
|
|
324
|
+
|
|
325
|
+
The package ships TypeScript definitions; no additional setup required.
|
|
326
|
+
|
|
327
|
+
```tsx
|
|
328
|
+
import type { ButtonProps, InputFieldProps } from "@maxsteinwender/sort-ui";
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## Setup Checklist
|
|
334
|
+
|
|
335
|
+
- [ ] Installed `@maxsteinwender/sort-ui` via **npm** (not pnpm/yarn)
|
|
336
|
+
- [ ] Imported `@maxsteinwender/sort-ui/styles.css` once at app entry
|
|
337
|
+
- [ ] Set one of the 4 theme classes on `<html>`: `light`, `dark`, `theme-2`, or `theme-2-dark`
|
|
338
|
+
- [ ] Configured **Tailwind v3** with SortUI preset (not v4)
|
|
339
|
+
- [ ] Added `@tailwind base; @tailwind components; @tailwind utilities;` to global CSS
|
|
340
|
+
- [ ] Added `<Toaster />` to root layout (if using toasts)
|
|
341
|
+
- [ ] Wrapped app with `<TooltipProvider>` (if using tooltips)
|
|
342
|
+
- [ ] Installed `react-hook-form` (if using form components)
|
|
343
|
+
- [ ] Did NOT install `lucide-react` or any additional icon package
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# Style System Guidelines
|
|
2
|
+
|
|
3
|
+
Spacing, layout, responsive patterns, and CSS methodology for SortUI.
|
|
4
|
+
|
|
5
|
+
## CSS Methodology
|
|
6
|
+
|
|
7
|
+
SortUI uses **Tailwind CSS v3** (`^3.4.1`). It is **NOT** Tailwind v4. The design system provides:
|
|
8
|
+
|
|
9
|
+
1. **Design tokens** (CSS custom properties)
|
|
10
|
+
2. **Tailwind v3 preset** (extends Tailwind with SUI tokens)
|
|
11
|
+
3. **Component classes** (component-specific styles)
|
|
12
|
+
|
|
13
|
+
**Never emit Tailwind v4 syntax** (`@theme` directive, v4-only APIs). Always author styles for Tailwind v3.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Spacing Scale (WITH `sui-` prefix)
|
|
18
|
+
|
|
19
|
+
SortUI uses a numeric spacing scale based on pixels. Use with `gap-`, `p-`, `m-`, `space-x-`, `space-y-`, etc.
|
|
20
|
+
|
|
21
|
+
| Class | Size | Common Usage |
|
|
22
|
+
|-------|------|--------------|
|
|
23
|
+
| `gap-sui-none` | 0px | No spacing |
|
|
24
|
+
| `gap-sui-1` | 1px | Hairline |
|
|
25
|
+
| `gap-sui-2` | 2px | Minimal |
|
|
26
|
+
| `gap-sui-4` | 4px | Extra small |
|
|
27
|
+
| `gap-sui-6` | 6px | Small− |
|
|
28
|
+
| `gap-sui-8` | 8px | Small |
|
|
29
|
+
| `gap-sui-12` | 12px | Medium small |
|
|
30
|
+
| `gap-sui-16` | 16px | Medium — form field gap |
|
|
31
|
+
| `gap-sui-20` | 20px | Medium+ |
|
|
32
|
+
| `gap-sui-24` | 24px | Large — card padding |
|
|
33
|
+
| `gap-sui-32` | 32px | Section gap |
|
|
34
|
+
| `gap-sui-40` | 40px | XXL |
|
|
35
|
+
| `gap-sui-48` | 48px | XXXL |
|
|
36
|
+
| `gap-sui-64` | 64px | Large sections |
|
|
37
|
+
| `gap-sui-80` | 80px | Hero |
|
|
38
|
+
| `gap-sui-96` – `gap-sui-384` | 96–384px | Page sections, hero spacing |
|
|
39
|
+
|
|
40
|
+
**Usage:**
|
|
41
|
+
```tsx
|
|
42
|
+
<div className="p-sui-24" /> {/* padding: 24px */}
|
|
43
|
+
<div className="m-sui-16" /> {/* margin: 16px */}
|
|
44
|
+
<div className="gap-sui-12" /> {/* gap: 12px */}
|
|
45
|
+
<div className="space-x-sui-8" /> {/* horizontal spacing */}
|
|
46
|
+
<div className="space-y-sui-16" /> {/* vertical spacing */}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Common Spacing Patterns
|
|
52
|
+
|
|
53
|
+
### Component Spacing
|
|
54
|
+
|
|
55
|
+
| Pattern | Class |
|
|
56
|
+
|---------|-------|
|
|
57
|
+
| Input field padding | `px-sui-12 py-sui-10` |
|
|
58
|
+
| Button padding (md) | `px-sui-12 py-sui-8` |
|
|
59
|
+
| Button padding (lg) | `px-sui-16 py-sui-12` |
|
|
60
|
+
| Card padding | `p-sui-24` |
|
|
61
|
+
| Form field gap | `gap-sui-16` |
|
|
62
|
+
| Section gap | `gap-sui-32` or `gap-sui-48` |
|
|
63
|
+
| Page margins | `mx-sui-24` or `mx-sui-32` |
|
|
64
|
+
|
|
65
|
+
### Layout Spacing
|
|
66
|
+
|
|
67
|
+
| Context | Class | Rationale |
|
|
68
|
+
|---------|-------|-----------|
|
|
69
|
+
| Between form fields | `gap-sui-16` | Comfortable reading distance |
|
|
70
|
+
| Between form sections | `gap-sui-32` | Clear visual grouping |
|
|
71
|
+
| Card internal padding | `p-sui-24` | Balanced white space |
|
|
72
|
+
| Modal/dialog padding | `p-sui-24` or `p-sui-32` | |
|
|
73
|
+
| Navigation items | `gap-sui-8` or `gap-sui-12` | |
|
|
74
|
+
|
|
75
|
+
**Example form spacing:**
|
|
76
|
+
```tsx
|
|
77
|
+
<form className="flex flex-col gap-sui-16">
|
|
78
|
+
<InputField label="Name" />
|
|
79
|
+
<InputField label="Email" />
|
|
80
|
+
<div className="mt-sui-16">
|
|
81
|
+
<h3 className="text-sui-text-default">Additional Info</h3>
|
|
82
|
+
</div>
|
|
83
|
+
<TextAreaField label="Bio" />
|
|
84
|
+
<Button className="mt-sui-24">Submit</Button>
|
|
85
|
+
</form>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Layout Primitives
|
|
91
|
+
|
|
92
|
+
SortUI does not provide explicit layout components (Stack, Inline, Box). Use **Tailwind Flexbox and Grid utilities** for layout.
|
|
93
|
+
|
|
94
|
+
### Flexbox Patterns
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
{/* Vertical stack */}
|
|
98
|
+
<div className="flex flex-col gap-sui-16">…</div>
|
|
99
|
+
|
|
100
|
+
{/* Horizontal inline */}
|
|
101
|
+
<div className="flex flex-row gap-sui-8 items-center">…</div>
|
|
102
|
+
|
|
103
|
+
{/* Space between (navbar) */}
|
|
104
|
+
<div className="flex items-center justify-between">…</div>
|
|
105
|
+
|
|
106
|
+
{/* Centered content */}
|
|
107
|
+
<div className="flex items-center justify-center min-h-screen">…</div>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Grid Patterns
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
{/* Responsive cards grid */}
|
|
114
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-sui-24">
|
|
115
|
+
<Card>Item 1</Card>
|
|
116
|
+
<Card>Item 2</Card>
|
|
117
|
+
<Card>Item 3</Card>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
{/* Form grid */}
|
|
121
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-sui-16">
|
|
122
|
+
<InputField label="First Name" />
|
|
123
|
+
<InputField label="Last Name" />
|
|
124
|
+
<InputField label="Email" className="md:col-span-2" />
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
{/* Dashboard grid */}
|
|
128
|
+
<div className="grid grid-cols-12 gap-sui-24">
|
|
129
|
+
<div className="col-span-12 lg:col-span-8">Main</div>
|
|
130
|
+
<div className="col-span-12 lg:col-span-4">Sidebar</div>
|
|
131
|
+
</div>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Responsive Patterns
|
|
137
|
+
|
|
138
|
+
### Breakpoints (Tailwind v3)
|
|
139
|
+
|
|
140
|
+
SortUI uses the **default Tailwind v3 breakpoints**:
|
|
141
|
+
|
|
142
|
+
| Breakpoint | Min Width | Usage |
|
|
143
|
+
|------------|-----------|-------|
|
|
144
|
+
| `sm` | 640px | Small tablets |
|
|
145
|
+
| `md` | 768px | Tablets |
|
|
146
|
+
| `lg` | 1024px | Laptops |
|
|
147
|
+
| `xl` | 1280px | Desktops |
|
|
148
|
+
| `2xl` | 1536px | Large desktops |
|
|
149
|
+
|
|
150
|
+
**Mobile-first approach:**
|
|
151
|
+
```tsx
|
|
152
|
+
<div className="p-sui-16 md:p-sui-24 lg:p-sui-32">Content</div>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Responsive Spacing
|
|
156
|
+
|
|
157
|
+
| Pattern | Mobile | Tablet | Desktop |
|
|
158
|
+
|---------|--------|--------|---------|
|
|
159
|
+
| Page container padding | `px-sui-16` | `md:px-sui-24` | `lg:px-sui-32` |
|
|
160
|
+
| Section gap | `gap-sui-32` | `md:gap-sui-48` | `lg:gap-sui-64` |
|
|
161
|
+
| Grid columns | 1 | 2 | 3–4 |
|
|
162
|
+
|
|
163
|
+
```tsx
|
|
164
|
+
<div className="px-sui-16 md:px-sui-24 lg:px-sui-32 py-sui-32 md:py-sui-48 lg:py-sui-64">
|
|
165
|
+
<div className="max-w-7xl mx-auto">
|
|
166
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-sui-24">
|
|
167
|
+
{/* Cards */}
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Radius & Shadow in Layouts (NO `sui-` prefix)
|
|
176
|
+
|
|
177
|
+
Always use the correct class names (see `tokens.md` for the full list):
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
{/* ✅ Card with radius + shadow */}
|
|
181
|
+
<div className="bg-sui-bg-card border border-sui-border-default rounded-lg shadow-card p-sui-24">
|
|
182
|
+
Content
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
{/* ✅ Pill */}
|
|
186
|
+
<div className="rounded-full bg-sui-badge-blue px-sui-12 py-sui-4">Label</div>
|
|
187
|
+
|
|
188
|
+
{/* ❌ Wrong */}
|
|
189
|
+
<div className="rounded-sui-lg shadow-sui-card" />
|
|
190
|
+
<div className="rounded-lg shadow-md" />
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Styling Approach
|
|
196
|
+
|
|
197
|
+
**Use Tailwind utilities FIRST:**
|
|
198
|
+
```tsx
|
|
199
|
+
<div className="flex items-center gap-sui-12 p-sui-16 bg-sui-bg-default border border-sui-border-default rounded-lg">
|
|
200
|
+
Content
|
|
201
|
+
</div>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Use CSS custom properties for dynamic values:**
|
|
205
|
+
```tsx
|
|
206
|
+
<div style={{
|
|
207
|
+
color: "var(--text-subtle)",
|
|
208
|
+
padding: "var(--spacing-16)",
|
|
209
|
+
}}>
|
|
210
|
+
Content
|
|
211
|
+
</div>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Override component styles via `className`:**
|
|
215
|
+
```tsx
|
|
216
|
+
<Button className="w-full md:w-auto">
|
|
217
|
+
Full width on mobile, auto on desktop
|
|
218
|
+
</Button>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Custom CSS (when needed, token-based only)
|
|
222
|
+
|
|
223
|
+
```css
|
|
224
|
+
.custom-card {
|
|
225
|
+
background: var(--bg-card);
|
|
226
|
+
border: 1px solid var(--border-default);
|
|
227
|
+
border-radius: var(--radius-md);
|
|
228
|
+
padding: var(--spacing-24);
|
|
229
|
+
box-shadow: var(--shadow-card);
|
|
230
|
+
}
|
|
231
|
+
.custom-card:hover {
|
|
232
|
+
box-shadow: var(--shadow-modal-sm);
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Component Reuse Rule (CRITICAL)
|
|
239
|
+
|
|
240
|
+
**Reuse existing SortUI components** instead of inlining custom markup. This keeps styling consistent across themes.
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
// ✅ DO — reuse components
|
|
244
|
+
<Badge color="green" shape="pill">Active</Badge>
|
|
245
|
+
<Avatar src="/u.jpg" fallback="MS" size="md" />
|
|
246
|
+
<ProgressBar value={75} label labelText="Upload" />
|
|
247
|
+
<Button variant="default" size="md">Save</Button>
|
|
248
|
+
|
|
249
|
+
// ❌ DON'T — inline markup
|
|
250
|
+
<span className="inline-flex bg-green-100 text-green-800 px-sui-8 py-sui-4 rounded-full text-xs">Active</span>
|
|
251
|
+
<img src="/u.jpg" className="w-10 h-10 rounded-full" />
|
|
252
|
+
<div className="h-2 bg-sui-bg-muted rounded-full overflow-hidden">
|
|
253
|
+
<div style={{ width: "75%" }} className="h-full bg-sui-state-primary" />
|
|
254
|
+
</div>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## DO / DON'T
|
|
260
|
+
|
|
261
|
+
**DO:**
|
|
262
|
+
```tsx
|
|
263
|
+
<div className="gap-sui-16 p-sui-24" />
|
|
264
|
+
<div className="bg-sui-bg-default text-sui-text-default" />
|
|
265
|
+
<div className="p-sui-16 md:p-sui-24 lg:p-sui-32" />
|
|
266
|
+
<div className="flex flex-col gap-sui-16" />
|
|
267
|
+
<div className="rounded-md shadow-card" />
|
|
268
|
+
<Button className="w-full md:w-auto">Submit</Button>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
**DON'T:**
|
|
272
|
+
```tsx
|
|
273
|
+
<div className="gap-[16px] p-[24px]" /> {/* ❌ arbitrary */}
|
|
274
|
+
<div className="bg-[#ffffff] text-[#111115]" /> {/* ❌ hex */}
|
|
275
|
+
<div className="lg:p-sui-32 md:p-sui-24 p-sui-16" /> {/* ❌ desktop-first */}
|
|
276
|
+
<Button style={{ padding: "20px" }}>Submit</Button> {/* ❌ inline style */}
|
|
277
|
+
<div className="rounded-sui-md shadow-sui-default" /> {/* ❌ wrong prefix */}
|
|
278
|
+
<div className="rounded-md shadow-md shadow-lg" /> {/* ❌ generic shadow */}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Layout Examples
|
|
284
|
+
|
|
285
|
+
### Dashboard Layout
|
|
286
|
+
```tsx
|
|
287
|
+
<div className="min-h-screen bg-sui-bg-subtle">
|
|
288
|
+
<header className="bg-sui-bg-default border-b border-sui-border-default px-sui-24 py-sui-16">
|
|
289
|
+
<div className="max-w-7xl mx-auto flex items-center justify-between">
|
|
290
|
+
<h1 className="text-sui-text-default font-semibold">Dashboard</h1>
|
|
291
|
+
<nav className="flex gap-sui-12">
|
|
292
|
+
<Button variant="ghost">Settings</Button>
|
|
293
|
+
</nav>
|
|
294
|
+
</div>
|
|
295
|
+
</header>
|
|
296
|
+
|
|
297
|
+
<main className="max-w-7xl mx-auto px-sui-24 py-sui-32">
|
|
298
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-sui-24">
|
|
299
|
+
<Card>…</Card>
|
|
300
|
+
<Card>…</Card>
|
|
301
|
+
<Card>…</Card>
|
|
302
|
+
</div>
|
|
303
|
+
</main>
|
|
304
|
+
</div>
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Form Layout
|
|
308
|
+
```tsx
|
|
309
|
+
<div className="max-w-md mx-auto p-sui-24">
|
|
310
|
+
<form className="flex flex-col gap-sui-16">
|
|
311
|
+
<h2 className="text-2xl font-semibold text-sui-text-default mb-sui-8">Sign Up</h2>
|
|
312
|
+
<InputField label="Name" required />
|
|
313
|
+
<InputField label="Email" type="email" required />
|
|
314
|
+
<InputField label="Password" type="password" required />
|
|
315
|
+
<CheckboxWithText label="I agree to the terms" />
|
|
316
|
+
<Button type="submit" className="mt-sui-16">Create Account</Button>
|
|
317
|
+
</form>
|
|
318
|
+
</div>
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Article Layout
|
|
322
|
+
```tsx
|
|
323
|
+
<div className="max-w-4xl mx-auto px-sui-24 py-sui-64">
|
|
324
|
+
<article className="space-y-sui-32">
|
|
325
|
+
<header className="space-y-sui-16">
|
|
326
|
+
<h1 className="text-4xl font-bold text-sui-text-default">Article Title</h1>
|
|
327
|
+
<p className="text-sui-text-muted">Published on Jan 1, 2026</p>
|
|
328
|
+
</header>
|
|
329
|
+
<div className="prose">
|
|
330
|
+
<p className="text-sui-text-default">Article content…</p>
|
|
331
|
+
</div>
|
|
332
|
+
<footer className="border-t border-sui-border-default pt-sui-24">
|
|
333
|
+
<div className="flex items-center gap-sui-12">
|
|
334
|
+
<Avatar src="/author.jpg" fallback="JD" size="md" />
|
|
335
|
+
<div>
|
|
336
|
+
<p className="font-semibold text-sui-text-default">John Doe</p>
|
|
337
|
+
<p className="text-sm text-sui-text-subtle">Author</p>
|
|
338
|
+
</div>
|
|
339
|
+
</div>
|
|
340
|
+
</footer>
|
|
341
|
+
</article>
|
|
342
|
+
</div>
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Summary
|
|
348
|
+
|
|
349
|
+
- **Tailwind v3**, mobile-first, semantic tokens
|
|
350
|
+
- **Spacing with `sui-` prefix**: `gap-sui-16`, `p-sui-24`, `m-sui-8`
|
|
351
|
+
- **Radius & Shadow WITHOUT `sui-` prefix**: `rounded-md`, `shadow-card`
|
|
352
|
+
- **Use Flexbox and Grid utilities** for layout
|
|
353
|
+
- **Reuse SortUI components** (Badge, Avatar, Button, ProgressBar, etc.) instead of inlining markup
|
|
354
|
+
- **Responsive breakpoints**: `sm`, `md`, `lg`, `xl`, `2xl`
|
|
355
|
+
- **Semantic color tokens** over hard-coded hex/rgb
|
|
356
|
+
- **Common spacing**: 16px between form fields, 24px card padding, 32px section gaps
|