@launch77-shared/plugin-design-system 1.0.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 +244 -0
- package/dist/generator.js +219 -0
- package/package.json +47 -0
- package/plugin.json +10 -0
- package/templates/app/design-system-test/page.tsx +203 -0
- package/templates/src/.gitkeep +0 -0
- package/templates/src/modules/design-system/README.md +318 -0
- package/templates/src/modules/design-system/config/brand.css +23 -0
package/README.md
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Design System Plugin
|
|
2
|
+
|
|
3
|
+
Launch77 plugin for setting up a token-driven design system with Tailwind CSS integration.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎨 **Semantic Design Tokens** - HSL-format CSS custom properties for consistent theming
|
|
8
|
+
- 🌗 **Dark Mode** - Complete dark mode support via `.dark` class
|
|
9
|
+
- ⚡ **Tailwind Integration** - Automatic preset configuration with all theme colors
|
|
10
|
+
- 🎬 **Animations** - 8 built-in keyframe animations
|
|
11
|
+
- 🎯 **Brand Customization** - Simple CSS override system
|
|
12
|
+
- 🔧 **shadcn Compatible** - Follows shadcn/ui token architecture
|
|
13
|
+
- 📄 **Test Page** - Comprehensive visual testing route
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
From your Launch77 app directory:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
launch77 plugin:install design-system
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The plugin will automatically:
|
|
24
|
+
|
|
25
|
+
1. Add `@launch77-shared/lib-design-system` dependency to package.json
|
|
26
|
+
2. Insert CSS token imports into `app/globals.css`
|
|
27
|
+
3. Add Tailwind preset to `tailwind.config.ts`
|
|
28
|
+
4. Copy template files:
|
|
29
|
+
- `/app/design-system-test/page.tsx` - Visual test page
|
|
30
|
+
- `/src/modules/design-system/config/brand.css` - Brand customization
|
|
31
|
+
- `/src/modules/design-system/README.md` - Module documentation
|
|
32
|
+
|
|
33
|
+
## What Gets Modified
|
|
34
|
+
|
|
35
|
+
### `package.json`
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@launch77-shared/lib-design-system": "^0.1.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### `app/globals.css`
|
|
46
|
+
|
|
47
|
+
```css
|
|
48
|
+
/* Launch77 Design System */
|
|
49
|
+
@import '@launch77-shared/lib-design-system/tokens.css';
|
|
50
|
+
@import '../src/modules/design-system/config/brand.css';
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### `tailwind.config.ts`
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
const config: Config = {
|
|
57
|
+
presets: [require('@launch77-shared/lib-design-system')],
|
|
58
|
+
// ... rest of config
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
After installation:
|
|
65
|
+
|
|
66
|
+
1. **Test the design system**
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm run dev
|
|
70
|
+
# Visit http://localhost:3000/design-system-test
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
2. **Customize your brand colors**
|
|
74
|
+
|
|
75
|
+
Edit `src/modules/design-system/config/brand.css`:
|
|
76
|
+
|
|
77
|
+
```css
|
|
78
|
+
:root {
|
|
79
|
+
/* Your brand's primary color */
|
|
80
|
+
--color-primary: 158 64% 52%;
|
|
81
|
+
--color-primary-foreground: 0 0% 100%;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.dark {
|
|
85
|
+
/* Dark mode overrides */
|
|
86
|
+
--color-primary: 158 64% 52%;
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
3. **Start building with semantic tokens**
|
|
91
|
+
```tsx
|
|
92
|
+
<button className="bg-primary text-primary-foreground hover:bg-primary/90 rounded-lg px-4 py-2">Primary Action</button>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Available Tokens
|
|
96
|
+
|
|
97
|
+
The design system provides semantic color tokens:
|
|
98
|
+
|
|
99
|
+
- `background` / `foreground` - Main app background and text
|
|
100
|
+
- `primary` / `primary-foreground` - Brand color
|
|
101
|
+
- `secondary` / `secondary-foreground` - Secondary actions
|
|
102
|
+
- `accent` / `accent-foreground` - Accent/highlight color
|
|
103
|
+
- `muted` / `muted-foreground` - Subtle backgrounds
|
|
104
|
+
- `destructive` / `destructive-foreground` - Errors/warnings
|
|
105
|
+
- `card` / `card-foreground` - Card backgrounds
|
|
106
|
+
- `border` - Border color
|
|
107
|
+
- `input` - Input border color
|
|
108
|
+
- `ring` - Focus ring color
|
|
109
|
+
|
|
110
|
+
Use with Tailwind utilities:
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
<div className="bg-card text-card-foreground border border-border rounded-lg p-6">
|
|
114
|
+
<p className="text-muted-foreground">Subtle text</p>
|
|
115
|
+
</div>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Alpha Channel Support
|
|
119
|
+
|
|
120
|
+
All colors support Tailwind's alpha channel syntax:
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
<div className="bg-primary/10">10% opacity</div>
|
|
124
|
+
<div className="bg-primary/20">20% opacity</div>
|
|
125
|
+
<button className="bg-primary/90 hover:bg-primary">Hover effect</button>
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Dark Mode
|
|
129
|
+
|
|
130
|
+
Toggle dark mode by adding/removing `.dark` class on `<html>`:
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
'use client'
|
|
134
|
+
|
|
135
|
+
export function DarkModeToggle() {
|
|
136
|
+
const toggleDark = () => {
|
|
137
|
+
document.documentElement.classList.toggle('dark')
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return <button onClick={toggleDark}>Toggle Dark Mode</button>
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Animations
|
|
145
|
+
|
|
146
|
+
Built-in animations available as Tailwind utilities:
|
|
147
|
+
|
|
148
|
+
- `animate-fade-in` / `animate-fade-out`
|
|
149
|
+
- `animate-slide-in-from-top` / `-bottom` / `-left` / `-right`
|
|
150
|
+
- `animate-accordion-down` / `animate-accordion-up`
|
|
151
|
+
|
|
152
|
+
All animations respect `prefers-reduced-motion` preferences.
|
|
153
|
+
|
|
154
|
+
## shadcn/ui Integration
|
|
155
|
+
|
|
156
|
+
This design system follows shadcn/ui's token architecture. To add shadcn components:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
npx shadcn@latest init
|
|
160
|
+
npx shadcn@latest add button
|
|
161
|
+
npx shadcn@latest add card
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Components will automatically use your theme tokens.
|
|
165
|
+
|
|
166
|
+
## Documentation
|
|
167
|
+
|
|
168
|
+
For detailed documentation, see:
|
|
169
|
+
|
|
170
|
+
- Module README: `src/modules/design-system/README.md` (installed with plugin)
|
|
171
|
+
- Library README: `@launch77-shared/lib-design-system` package
|
|
172
|
+
|
|
173
|
+
## Development
|
|
174
|
+
|
|
175
|
+
### Building the Plugin
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
npm run build
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Compiles TypeScript to `dist/generator.js`.
|
|
182
|
+
|
|
183
|
+
### Testing Changes
|
|
184
|
+
|
|
185
|
+
1. Build the plugin: `npm run build`
|
|
186
|
+
2. Install in a test app: `launch77 plugin:install design-system`
|
|
187
|
+
3. Verify all features work
|
|
188
|
+
|
|
189
|
+
### Type Checking
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
npm run typecheck
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Architecture
|
|
196
|
+
|
|
197
|
+
This plugin uses the `StandardGenerator` pattern:
|
|
198
|
+
|
|
199
|
+
1. **updateDependencies()** - Reads `plugin.json`, merges into app's `package.json`
|
|
200
|
+
2. **installDependencies()** - Runs `npm install` at workspace root
|
|
201
|
+
3. **copyTemplates()** - Copies `templates/` to app
|
|
202
|
+
4. **injectCode()** - Surgical code edits (CSS imports, Tailwind preset)
|
|
203
|
+
|
|
204
|
+
All operations are idempotent - safe to run multiple times.
|
|
205
|
+
|
|
206
|
+
## Template Files
|
|
207
|
+
|
|
208
|
+
The `templates/` directory contains:
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
templates/
|
|
212
|
+
├── app/
|
|
213
|
+
│ └── design-system-test/
|
|
214
|
+
│ └── page.tsx # Comprehensive test page
|
|
215
|
+
└── src/
|
|
216
|
+
└── modules/
|
|
217
|
+
└── design-system/
|
|
218
|
+
├── config/
|
|
219
|
+
│ └── brand.css # Brand customization
|
|
220
|
+
└── README.md # Module documentation
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Troubleshooting
|
|
224
|
+
|
|
225
|
+
### Colors Not Applying
|
|
226
|
+
|
|
227
|
+
1. Verify CSS imports in `app/globals.css`
|
|
228
|
+
2. Check Tailwind preset in `tailwind.config.ts`
|
|
229
|
+
3. Restart dev server
|
|
230
|
+
|
|
231
|
+
### Dark Mode Not Working
|
|
232
|
+
|
|
233
|
+
1. Ensure `.dark` class is on `<html>` element (not `<body>`)
|
|
234
|
+
2. Check browser DevTools to verify class is applied
|
|
235
|
+
|
|
236
|
+
### Build Errors
|
|
237
|
+
|
|
238
|
+
1. Run `npm install` at workspace root
|
|
239
|
+
2. Verify `@launch77-shared/lib-design-system` in package.json
|
|
240
|
+
3. Restart dev server
|
|
241
|
+
|
|
242
|
+
## License
|
|
243
|
+
|
|
244
|
+
UNLICENSED
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/generator.ts
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { StandardGenerator } from "@launch77/plugin-runtime";
|
|
7
|
+
|
|
8
|
+
// src/utils/config-modifier.ts
|
|
9
|
+
import fs from "fs/promises";
|
|
10
|
+
async function addTailwindPreset(filePath, presetRequire) {
|
|
11
|
+
try {
|
|
12
|
+
const fileExists = await fs.access(filePath).then(() => true).catch(() => false);
|
|
13
|
+
if (!fileExists) {
|
|
14
|
+
return {
|
|
15
|
+
success: false,
|
|
16
|
+
error: `File not found: ${filePath}`
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
20
|
+
if (content.includes(presetRequire)) {
|
|
21
|
+
return {
|
|
22
|
+
success: true,
|
|
23
|
+
alreadyExists: true
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const lines = content.split("\n");
|
|
27
|
+
let configStartIndex = -1;
|
|
28
|
+
let presetsIndex = -1;
|
|
29
|
+
let indentation = " ";
|
|
30
|
+
for (let i = 0; i < lines.length; i++) {
|
|
31
|
+
const line = lines[i];
|
|
32
|
+
if (line.includes("satisfies Config") || line.includes(": Config =")) {
|
|
33
|
+
configStartIndex = i;
|
|
34
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
35
|
+
const propLine = lines[j];
|
|
36
|
+
const match = propLine.match(/^(\s+)\w+:/);
|
|
37
|
+
if (match) {
|
|
38
|
+
indentation = match[1];
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (line.includes("presets:")) {
|
|
44
|
+
presetsIndex = i;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (configStartIndex === -1) {
|
|
48
|
+
return {
|
|
49
|
+
success: false,
|
|
50
|
+
error: "Could not find Tailwind config object"
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
let newContent;
|
|
54
|
+
if (presetsIndex !== -1) {
|
|
55
|
+
const presetsLine = lines[presetsIndex];
|
|
56
|
+
if (presetsLine.includes("[]")) {
|
|
57
|
+
lines[presetsIndex] = presetsLine.replace("[]", `[${presetRequire}]`);
|
|
58
|
+
} else {
|
|
59
|
+
const openBracketIndex = presetsLine.indexOf("[");
|
|
60
|
+
if (openBracketIndex !== -1) {
|
|
61
|
+
lines[presetsIndex] = presetsLine.slice(0, openBracketIndex + 1) + presetRequire + ", " + presetsLine.slice(openBracketIndex + 1);
|
|
62
|
+
} else {
|
|
63
|
+
return {
|
|
64
|
+
success: false,
|
|
65
|
+
error: "Could not parse existing presets array"
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
newContent = lines.join("\n");
|
|
70
|
+
} else {
|
|
71
|
+
let insertIndex = configStartIndex + 1;
|
|
72
|
+
if (lines[configStartIndex].includes("{")) {
|
|
73
|
+
} else {
|
|
74
|
+
while (insertIndex < lines.length && !lines[insertIndex].includes("{")) {
|
|
75
|
+
insertIndex++;
|
|
76
|
+
}
|
|
77
|
+
insertIndex++;
|
|
78
|
+
}
|
|
79
|
+
const presetsLine = `${indentation}presets: [${presetRequire}],`;
|
|
80
|
+
lines.splice(insertIndex, 0, presetsLine);
|
|
81
|
+
newContent = lines.join("\n");
|
|
82
|
+
}
|
|
83
|
+
await fs.writeFile(filePath, newContent, "utf-8");
|
|
84
|
+
return {
|
|
85
|
+
success: true
|
|
86
|
+
};
|
|
87
|
+
} catch (error) {
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
error: error instanceof Error ? error.message : String(error)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/utils/css-modifier.ts
|
|
96
|
+
import fs2 from "fs/promises";
|
|
97
|
+
async function insertCssImports(filePath, imports) {
|
|
98
|
+
try {
|
|
99
|
+
const fileExists = await fs2.access(filePath).then(() => true).catch(() => false);
|
|
100
|
+
if (!fileExists) {
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
error: `File not found: ${filePath}`
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const content = await fs2.readFile(filePath, "utf-8");
|
|
107
|
+
const allImportsExist = imports.every((importLine) => content.includes(importLine));
|
|
108
|
+
if (allImportsExist) {
|
|
109
|
+
return {
|
|
110
|
+
success: true,
|
|
111
|
+
alreadyExists: true
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const lines = content.split("\n");
|
|
115
|
+
let lastTailwindIndex = -1;
|
|
116
|
+
for (let i = 0; i < lines.length; i++) {
|
|
117
|
+
if (lines[i].trim().startsWith("@tailwind")) {
|
|
118
|
+
lastTailwindIndex = i;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (lastTailwindIndex === -1) {
|
|
122
|
+
return {
|
|
123
|
+
success: false,
|
|
124
|
+
error: "Could not find @tailwind directives"
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const importsToAdd = imports.filter((importLine) => !content.includes(importLine));
|
|
128
|
+
if (importsToAdd.length === 0) {
|
|
129
|
+
return {
|
|
130
|
+
success: true,
|
|
131
|
+
alreadyExists: true
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const before = lines.slice(0, lastTailwindIndex + 1);
|
|
135
|
+
const after = lines.slice(lastTailwindIndex + 1);
|
|
136
|
+
const newContent = [...before, "", "/* Launch77 Design System */", ...importsToAdd, ...after].join("\n");
|
|
137
|
+
await fs2.writeFile(filePath, newContent, "utf-8");
|
|
138
|
+
return {
|
|
139
|
+
success: true
|
|
140
|
+
};
|
|
141
|
+
} catch (error) {
|
|
142
|
+
return {
|
|
143
|
+
success: false,
|
|
144
|
+
error: error instanceof Error ? error.message : String(error)
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/generator.ts
|
|
150
|
+
var DesignSystemGenerator = class extends StandardGenerator {
|
|
151
|
+
constructor(context) {
|
|
152
|
+
super(context);
|
|
153
|
+
}
|
|
154
|
+
async injectCode() {
|
|
155
|
+
console.log(chalk.cyan("\u{1F527} Configuring design system...\n"));
|
|
156
|
+
await this.updateGlobalsCss();
|
|
157
|
+
await this.updateTailwindConfig();
|
|
158
|
+
}
|
|
159
|
+
async updateGlobalsCss() {
|
|
160
|
+
const globalsCssPath = path.join(this.context.appPath, "app/globals.css");
|
|
161
|
+
const result = await insertCssImports(globalsCssPath, ["@import '@launch77-shared/lib-design-system/tokens.css';", "@import '../src/modules/design-system/config/brand.css';"]);
|
|
162
|
+
if (result.success) {
|
|
163
|
+
if (result.alreadyExists) {
|
|
164
|
+
console.log(chalk.gray(" \u2713 CSS imports already exist in app/globals.css"));
|
|
165
|
+
} else {
|
|
166
|
+
console.log(chalk.green(" \u2713 Added CSS imports to app/globals.css"));
|
|
167
|
+
}
|
|
168
|
+
} else {
|
|
169
|
+
console.log(chalk.yellow(` \u26A0\uFE0F Could not auto-configure app/globals.css: ${result.error}`));
|
|
170
|
+
console.log(chalk.gray(" You will need to add imports manually"));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async updateTailwindConfig() {
|
|
174
|
+
const tailwindConfigPath = path.join(this.context.appPath, "tailwind.config.ts");
|
|
175
|
+
const result = await addTailwindPreset(tailwindConfigPath, "require('@launch77-shared/lib-design-system')");
|
|
176
|
+
if (result.success) {
|
|
177
|
+
if (result.alreadyExists) {
|
|
178
|
+
console.log(chalk.gray(" \u2713 Design system preset already exists in tailwind.config.ts"));
|
|
179
|
+
} else {
|
|
180
|
+
console.log(chalk.green(" \u2713 Added design system preset to tailwind.config.ts"));
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
console.log(chalk.yellow(` \u26A0\uFE0F Could not auto-configure tailwind.config.ts: ${result.error}`));
|
|
184
|
+
console.log(chalk.gray(" You will need to add preset manually"));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
showNextSteps() {
|
|
188
|
+
console.log(chalk.bold.green("\n\u2705 Plugin installed successfully!\n"));
|
|
189
|
+
console.log(chalk.bold("Next Steps:\n"));
|
|
190
|
+
console.log("1. Test your design system:");
|
|
191
|
+
console.log(chalk.cyan(" Visit http://localhost:3000/design-system-test\n"));
|
|
192
|
+
console.log("2. Customize your brand:");
|
|
193
|
+
console.log(chalk.cyan(" Edit src/modules/design-system/config/brand.css\n"));
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
async function main() {
|
|
197
|
+
const args = process.argv.slice(2);
|
|
198
|
+
const appPath = args.find((arg) => arg.startsWith("--appPath="))?.split("=")[1];
|
|
199
|
+
const appName = args.find((arg) => arg.startsWith("--appName="))?.split("=")[1];
|
|
200
|
+
const workspaceName = args.find((arg) => arg.startsWith("--workspaceName="))?.split("=")[1];
|
|
201
|
+
const pluginPath = args.find((arg) => arg.startsWith("--pluginPath="))?.split("=")[1];
|
|
202
|
+
if (!appPath || !appName || !workspaceName || !pluginPath) {
|
|
203
|
+
console.error(chalk.red("Error: Missing required arguments"));
|
|
204
|
+
console.error("Usage: generate --appPath=<path> --appName=<name> --workspaceName=<workspace> --pluginPath=<path>");
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
const generator = new DesignSystemGenerator({ appPath, appName, workspaceName, pluginPath });
|
|
208
|
+
await generator.run();
|
|
209
|
+
}
|
|
210
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
211
|
+
main().catch((error) => {
|
|
212
|
+
console.error(chalk.red("\n\u274C Error during design system setup:"));
|
|
213
|
+
console.error(error);
|
|
214
|
+
process.exit(1);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
export {
|
|
218
|
+
DesignSystemGenerator
|
|
219
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@launch77-shared/plugin-design-system",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Launch77 design system plugin - Setup design tokens and Tailwind preset for elegant web applications",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/generator.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"generate": "./dist/generator.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist/",
|
|
13
|
+
"templates/",
|
|
14
|
+
"plugin.json"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup",
|
|
18
|
+
"dev": "tsup --watch",
|
|
19
|
+
"typecheck": "tsc --noEmit",
|
|
20
|
+
"lint": "eslint src/**/*.ts",
|
|
21
|
+
"release:connect": "launch77-release-connect",
|
|
22
|
+
"release:verify": "launch77-release-verify"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@launch77-shared/lib-design-system": "^0.1.0",
|
|
26
|
+
"@launch77/plugin-runtime": "^0.2.0",
|
|
27
|
+
"chalk": "^5.3.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^20.10.0",
|
|
31
|
+
"tsup": "^8.0.0",
|
|
32
|
+
"typescript": "^5.3.0"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"launch77": {
|
|
38
|
+
"installedPlugins": {
|
|
39
|
+
"release": {
|
|
40
|
+
"package": "release",
|
|
41
|
+
"version": "1.0.0",
|
|
42
|
+
"installedAt": "2026-01-22T23:35:14.175Z",
|
|
43
|
+
"source": "local"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
package/plugin.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "design-system",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Launch77 design system - Token-driven theming foundation with Tailwind preset",
|
|
5
|
+
"targets": ["app", "library"],
|
|
6
|
+
"pluginDependencies": {},
|
|
7
|
+
"libraryDependencies": {
|
|
8
|
+
"@launch77-shared/lib-design-system": "^0.1.0"
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
|
|
5
|
+
export default function DesignSystemTest() {
|
|
6
|
+
const [isDark, setIsDark] = useState(false)
|
|
7
|
+
|
|
8
|
+
const toggleDark = () => {
|
|
9
|
+
if (isDark) {
|
|
10
|
+
document.documentElement.classList.remove('dark')
|
|
11
|
+
} else {
|
|
12
|
+
document.documentElement.classList.add('dark')
|
|
13
|
+
}
|
|
14
|
+
setIsDark(!isDark)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div className="min-h-screen bg-background text-foreground p-8">
|
|
19
|
+
{/* Header */}
|
|
20
|
+
<header className="max-w-6xl mx-auto mb-12">
|
|
21
|
+
<div className="flex items-center justify-between mb-6">
|
|
22
|
+
<h1 className="text-4xl font-bold">Design System Test</h1>
|
|
23
|
+
<button onClick={toggleDark} className="px-4 py-2 rounded-lg bg-secondary text-secondary-foreground hover:bg-secondary/80 transition-colors">
|
|
24
|
+
{isDark ? '☀️ Light Mode' : '🌙 Dark Mode'}
|
|
25
|
+
</button>
|
|
26
|
+
</div>
|
|
27
|
+
<p className="text-muted-foreground">Testing all features of @launch77-shared/lib-design-system</p>
|
|
28
|
+
</header>
|
|
29
|
+
|
|
30
|
+
<div className="max-w-6xl mx-auto space-y-12">
|
|
31
|
+
{/* Color Tokens */}
|
|
32
|
+
<section>
|
|
33
|
+
<h2 className="text-2xl font-bold mb-6">Semantic Color Tokens</h2>
|
|
34
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
35
|
+
<ColorCard name="Background" className="bg-background text-foreground border border-border" />
|
|
36
|
+
<ColorCard name="Primary" className="bg-primary text-primary-foreground" />
|
|
37
|
+
<ColorCard name="Secondary" className="bg-secondary text-secondary-foreground" />
|
|
38
|
+
<ColorCard name="Accent" className="bg-accent text-accent-foreground" />
|
|
39
|
+
<ColorCard name="Muted" className="bg-muted text-muted-foreground" />
|
|
40
|
+
<ColorCard name="Destructive" className="bg-destructive text-destructive-foreground" />
|
|
41
|
+
<ColorCard name="Card" className="bg-card text-card-foreground border border-border" />
|
|
42
|
+
</div>
|
|
43
|
+
</section>
|
|
44
|
+
|
|
45
|
+
{/* Alpha Channel Support */}
|
|
46
|
+
<section>
|
|
47
|
+
<h2 className="text-2xl font-bold mb-6">Alpha Channel Support</h2>
|
|
48
|
+
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
|
|
49
|
+
<div className="bg-primary/10 text-primary p-4 rounded-lg text-center">10%</div>
|
|
50
|
+
<div className="bg-primary/20 text-primary p-4 rounded-lg text-center">20%</div>
|
|
51
|
+
<div className="bg-primary/40 text-primary p-4 rounded-lg text-center">40%</div>
|
|
52
|
+
<div className="bg-primary/60 text-primary p-4 rounded-lg text-center">60%</div>
|
|
53
|
+
<div className="bg-primary/80 text-primary p-4 rounded-lg text-center">80%</div>
|
|
54
|
+
</div>
|
|
55
|
+
</section>
|
|
56
|
+
|
|
57
|
+
{/* Buttons */}
|
|
58
|
+
<section>
|
|
59
|
+
<h2 className="text-2xl font-bold mb-6">Button Variants</h2>
|
|
60
|
+
<div className="flex flex-wrap gap-4">
|
|
61
|
+
<button className="px-6 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors">Primary</button>
|
|
62
|
+
<button className="px-6 py-2 rounded-lg bg-secondary text-secondary-foreground hover:bg-secondary/80 transition-colors">Secondary</button>
|
|
63
|
+
<button className="px-6 py-2 rounded-lg bg-destructive text-destructive-foreground hover:bg-destructive/90 transition-colors">Destructive</button>
|
|
64
|
+
<button className="px-6 py-2 rounded-lg border border-input bg-background hover:bg-accent transition-colors">Outline</button>
|
|
65
|
+
<button className="px-6 py-2 hover:bg-accent hover:text-accent-foreground transition-colors rounded-lg">Ghost</button>
|
|
66
|
+
</div>
|
|
67
|
+
</section>
|
|
68
|
+
|
|
69
|
+
{/* Border Radius */}
|
|
70
|
+
<section>
|
|
71
|
+
<h2 className="text-2xl font-bold mb-6">Border Radius Scale</h2>
|
|
72
|
+
<div className="flex flex-wrap gap-4">
|
|
73
|
+
<div className="bg-primary text-primary-foreground p-6 rounded-sm">rounded-sm</div>
|
|
74
|
+
<div className="bg-primary text-primary-foreground p-6 rounded-md">rounded-md</div>
|
|
75
|
+
<div className="bg-primary text-primary-foreground p-6 rounded-lg">rounded-lg</div>
|
|
76
|
+
</div>
|
|
77
|
+
</section>
|
|
78
|
+
|
|
79
|
+
{/* Animations */}
|
|
80
|
+
<section>
|
|
81
|
+
<h2 className="text-2xl font-bold mb-6">Animations</h2>
|
|
82
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
83
|
+
<AnimationDemo name="Fade In" className="animate-fade-in" />
|
|
84
|
+
<AnimationDemo name="Slide from Top" className="animate-slide-in-from-top" />
|
|
85
|
+
<AnimationDemo name="Slide from Bottom" className="animate-slide-in-from-bottom" />
|
|
86
|
+
<AnimationDemo name="Slide from Left" className="animate-slide-in-from-left" />
|
|
87
|
+
<AnimationDemo name="Slide from Right" className="animate-slide-in-from-right" />
|
|
88
|
+
</div>
|
|
89
|
+
</section>
|
|
90
|
+
|
|
91
|
+
{/* Form Elements */}
|
|
92
|
+
<section>
|
|
93
|
+
<h2 className="text-2xl font-bold mb-6">Form Elements</h2>
|
|
94
|
+
<div className="space-y-4 max-w-md">
|
|
95
|
+
<div>
|
|
96
|
+
<label className="block text-sm font-medium mb-2">Input Field</label>
|
|
97
|
+
<input type="text" placeholder="Enter text..." className="w-full px-3 py-2 rounded-md border border-input bg-background ring-offset-background focus-visible:ring-2 focus-visible:ring-ring" />
|
|
98
|
+
</div>
|
|
99
|
+
<div>
|
|
100
|
+
<label className="block text-sm font-medium mb-2">Textarea</label>
|
|
101
|
+
<textarea placeholder="Enter multiline text..." rows={4} className="w-full px-3 py-2 rounded-md border border-input bg-background ring-offset-background focus-visible:ring-2 focus-visible:ring-ring" />
|
|
102
|
+
</div>
|
|
103
|
+
<div>
|
|
104
|
+
<label className="block text-sm font-medium mb-2">Select</label>
|
|
105
|
+
<select className="w-full px-3 py-2 rounded-md border border-input bg-background ring-offset-background focus-visible:ring-2 focus-visible:ring-ring">
|
|
106
|
+
<option>Option 1</option>
|
|
107
|
+
<option>Option 2</option>
|
|
108
|
+
<option>Option 3</option>
|
|
109
|
+
</select>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</section>
|
|
113
|
+
|
|
114
|
+
{/* Cards */}
|
|
115
|
+
<section>
|
|
116
|
+
<h2 className="text-2xl font-bold mb-6">Cards</h2>
|
|
117
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
118
|
+
<div className="bg-card text-card-foreground p-6 rounded-lg border border-border">
|
|
119
|
+
<h3 className="text-lg font-semibold mb-2">Card Title</h3>
|
|
120
|
+
<p className="text-muted-foreground">This is a card component using semantic tokens.</p>
|
|
121
|
+
</div>
|
|
122
|
+
<div className="bg-card text-card-foreground p-6 rounded-lg border border-border">
|
|
123
|
+
<h3 className="text-lg font-semibold mb-2">Another Card</h3>
|
|
124
|
+
<p className="text-muted-foreground">Cards adapt to dark mode automatically.</p>
|
|
125
|
+
</div>
|
|
126
|
+
<div className="bg-card text-card-foreground p-6 rounded-lg border border-border">
|
|
127
|
+
<h3 className="text-lg font-semibold mb-2">Third Card</h3>
|
|
128
|
+
<p className="text-muted-foreground">All using the same semantic tokens.</p>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</section>
|
|
132
|
+
|
|
133
|
+
{/* Typography */}
|
|
134
|
+
<section>
|
|
135
|
+
<h2 className="text-2xl font-bold mb-6">Typography</h2>
|
|
136
|
+
<div className="space-y-4">
|
|
137
|
+
<h1 className="text-4xl font-bold">Heading 1</h1>
|
|
138
|
+
<h2 className="text-3xl font-bold">Heading 2</h2>
|
|
139
|
+
<h3 className="text-2xl font-bold">Heading 3</h3>
|
|
140
|
+
<p className="text-base">This is regular body text using the foreground color.</p>
|
|
141
|
+
<p className="text-muted-foreground">This is muted text for secondary information.</p>
|
|
142
|
+
<p className="text-sm text-muted-foreground">Small text for captions and helper text.</p>
|
|
143
|
+
</div>
|
|
144
|
+
</section>
|
|
145
|
+
|
|
146
|
+
{/* Accessibility Features */}
|
|
147
|
+
<section>
|
|
148
|
+
<h2 className="text-2xl font-bold mb-6">Accessibility Features</h2>
|
|
149
|
+
<div className="space-y-4">
|
|
150
|
+
<div className="bg-muted p-4 rounded-lg">
|
|
151
|
+
<h3 className="font-semibold mb-2">✅ Focus Rings</h3>
|
|
152
|
+
<p className="text-sm text-muted-foreground mb-2">Tab through these buttons to see accessible focus rings:</p>
|
|
153
|
+
<div className="flex gap-2">
|
|
154
|
+
<button className="px-4 py-2 bg-primary text-primary-foreground rounded-lg">Button 1</button>
|
|
155
|
+
<button className="px-4 py-2 bg-secondary text-secondary-foreground rounded-lg">Button 2</button>
|
|
156
|
+
<button className="px-4 py-2 bg-accent text-accent-foreground rounded-lg">Button 3</button>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<div className="bg-muted p-4 rounded-lg">
|
|
161
|
+
<h3 className="font-semibold mb-2">✅ Minimum Tap Targets</h3>
|
|
162
|
+
<p className="text-sm text-muted-foreground">All interactive elements have minimum 44x44px tap targets (WCAG AAA).</p>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<div className="bg-muted p-4 rounded-lg">
|
|
166
|
+
<h3 className="font-semibold mb-2">✅ Reduced Motion Support</h3>
|
|
167
|
+
<p className="text-sm text-muted-foreground">Animations respect prefers-reduced-motion preferences.</p>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<div className="bg-muted p-4 rounded-lg">
|
|
171
|
+
<h3 className="font-semibold mb-2">✅ Smooth Scrolling</h3>
|
|
172
|
+
<p className="text-sm text-muted-foreground">Smooth scrolling enabled (respects reduced motion).</p>
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
</section>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function ColorCard({ name, className }: { name: string; className: string }) {
|
|
182
|
+
return (
|
|
183
|
+
<div className={`p-6 rounded-lg ${className}`}>
|
|
184
|
+
<div className="font-semibold">{name}</div>
|
|
185
|
+
<div className="text-sm opacity-75 mt-1">Semantic token</div>
|
|
186
|
+
</div>
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function AnimationDemo({ name, className }: { name: string; className: string }) {
|
|
191
|
+
const [key, setKey] = useState(0)
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
<div className="bg-muted p-4 rounded-lg">
|
|
195
|
+
<div key={key} className={`bg-primary text-primary-foreground p-4 rounded-md mb-2 ${className}`}>
|
|
196
|
+
{name}
|
|
197
|
+
</div>
|
|
198
|
+
<button onClick={() => setKey((k) => k + 1)} className="text-sm px-3 py-1 bg-secondary text-secondary-foreground rounded hover:bg-secondary/80 transition-colors">
|
|
199
|
+
Replay
|
|
200
|
+
</button>
|
|
201
|
+
</div>
|
|
202
|
+
)
|
|
203
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
# Launch77 Design System
|
|
2
|
+
|
|
3
|
+
Token-driven design system providing a consistent theming foundation for your application.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎨 **Semantic Design Tokens** - HSL-format CSS custom properties
|
|
8
|
+
- 🌗 **Dark Mode** - Complete dark mode support via `.dark` class
|
|
9
|
+
- ⚡ **Tailwind Integration** - Preset with all theme colors and utilities
|
|
10
|
+
- ♿ **Accessibility** - Focus rings, reduced motion, minimum tap targets
|
|
11
|
+
- 🎬 **Animations** - 8 built-in keyframe animations
|
|
12
|
+
- 🎯 **Brand Customization** - Simple CSS override system
|
|
13
|
+
- 🔧 **shadcn Compatible** - Follows shadcn/ui token architecture
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
The plugin has already configured your app with:
|
|
18
|
+
|
|
19
|
+
1. ✅ Tailwind preset in `tailwind.config.ts`
|
|
20
|
+
2. ✅ Token imports in `app/globals.css`
|
|
21
|
+
3. ✅ Brand customization file at `src/modules/design-system/config/brand.css`
|
|
22
|
+
|
|
23
|
+
You're ready to start using the design system immediately!
|
|
24
|
+
|
|
25
|
+
## Testing
|
|
26
|
+
|
|
27
|
+
Visit the test page to see all design tokens in action:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm run dev
|
|
31
|
+
# Navigate to http://localhost:3000/design-system-test
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The test page demonstrates:
|
|
35
|
+
|
|
36
|
+
- All semantic color tokens
|
|
37
|
+
- Alpha channel support
|
|
38
|
+
- Button variants
|
|
39
|
+
- Form elements
|
|
40
|
+
- Cards
|
|
41
|
+
- Typography
|
|
42
|
+
- Animations
|
|
43
|
+
- Accessibility features
|
|
44
|
+
- Dark mode toggle
|
|
45
|
+
|
|
46
|
+
## Customizing Your Brand
|
|
47
|
+
|
|
48
|
+
Edit `src/modules/design-system/config/brand.css` to override colors:
|
|
49
|
+
|
|
50
|
+
```css
|
|
51
|
+
:root {
|
|
52
|
+
/* Your brand's primary color */
|
|
53
|
+
--color-primary: 158 64% 52%;
|
|
54
|
+
--color-primary-foreground: 0 0% 100%;
|
|
55
|
+
|
|
56
|
+
/* Your brand's accent color */
|
|
57
|
+
--color-accent: 156 72% 72%;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.dark {
|
|
61
|
+
/* Dark mode overrides */
|
|
62
|
+
--color-primary: 158 64% 52%;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### HSL Color Format
|
|
67
|
+
|
|
68
|
+
Colors use HSL format: `Hue Saturation% Lightness%`
|
|
69
|
+
|
|
70
|
+
- **Hue**: 0-360 (color wheel position)
|
|
71
|
+
- **Saturation**: 0-100% (color intensity)
|
|
72
|
+
- **Lightness**: 0-100% (brightness)
|
|
73
|
+
|
|
74
|
+
Benefits:
|
|
75
|
+
|
|
76
|
+
- Alpha channel support: `bg-primary/20` for 20% opacity
|
|
77
|
+
- Easy to adjust (change lightness for variations)
|
|
78
|
+
- Compatible with color pickers
|
|
79
|
+
|
|
80
|
+
### Finding HSL Values
|
|
81
|
+
|
|
82
|
+
Use any color picker tool:
|
|
83
|
+
|
|
84
|
+
- https://hslpicker.com
|
|
85
|
+
- Browser DevTools color picker
|
|
86
|
+
- Design tools (Figma, Sketch, etc.)
|
|
87
|
+
|
|
88
|
+
## Available Tokens
|
|
89
|
+
|
|
90
|
+
### Color Tokens
|
|
91
|
+
|
|
92
|
+
```css
|
|
93
|
+
--color-background /* Main app background */
|
|
94
|
+
--color-foreground /* Main text color */
|
|
95
|
+
|
|
96
|
+
--color-primary /* Brand color */
|
|
97
|
+
--color-primary-foreground
|
|
98
|
+
|
|
99
|
+
--color-secondary /* Secondary actions */
|
|
100
|
+
--color-secondary-foreground
|
|
101
|
+
|
|
102
|
+
--color-accent /* Accent/highlight color */
|
|
103
|
+
--color-accent-foreground
|
|
104
|
+
|
|
105
|
+
--color-muted /* Subtle backgrounds */
|
|
106
|
+
--color-muted-foreground
|
|
107
|
+
|
|
108
|
+
--color-destructive /* Errors/destructive actions */
|
|
109
|
+
--color-destructive-foreground
|
|
110
|
+
|
|
111
|
+
--color-card /* Card backgrounds */
|
|
112
|
+
--color-card-foreground
|
|
113
|
+
|
|
114
|
+
--color-border /* Border color */
|
|
115
|
+
--color-input /* Input border color */
|
|
116
|
+
--color-ring /* Focus ring color */
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Border Radius
|
|
120
|
+
|
|
121
|
+
```css
|
|
122
|
+
--radius: 1rem /* Base radius (16px) */;
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Tailwind utilities:
|
|
126
|
+
|
|
127
|
+
- `rounded-lg` → `var(--radius)`
|
|
128
|
+
- `rounded-md` → `calc(var(--radius) - 2px)`
|
|
129
|
+
- `rounded-sm` → `calc(var(--radius) - 4px)`
|
|
130
|
+
|
|
131
|
+
## Usage Examples
|
|
132
|
+
|
|
133
|
+
### Buttons
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
<button className="bg-primary text-primary-foreground hover:bg-primary/90 rounded-lg px-4 py-2">
|
|
137
|
+
Primary Action
|
|
138
|
+
</button>
|
|
139
|
+
|
|
140
|
+
<button className="bg-secondary text-secondary-foreground hover:bg-secondary/80 rounded-lg px-4 py-2">
|
|
141
|
+
Secondary Action
|
|
142
|
+
</button>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Cards
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
<div className="bg-card text-card-foreground border border-border rounded-lg p-6">
|
|
149
|
+
<h2 className="text-xl font-bold mb-2">Card Title</h2>
|
|
150
|
+
<p className="text-muted-foreground">Card description text</p>
|
|
151
|
+
</div>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Form Inputs
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
<input type="text" className="w-full px-3 py-2 rounded-md border border-input bg-background focus-visible:ring-2 focus-visible:ring-ring" placeholder="Enter text..." />
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Alpha Channel
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
{/* Light backgrounds */}
|
|
164
|
+
<div className="bg-primary/10 text-primary">10% opacity</div>
|
|
165
|
+
<div className="bg-primary/20 text-primary">20% opacity</div>
|
|
166
|
+
|
|
167
|
+
{/* Hover effects */}
|
|
168
|
+
<button className="bg-primary/90 hover:bg-primary">
|
|
169
|
+
Hover me
|
|
170
|
+
</button>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Dark Mode
|
|
174
|
+
|
|
175
|
+
Toggle dark mode by adding/removing `.dark` class on `<html>`:
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
'use client'
|
|
179
|
+
|
|
180
|
+
export function DarkModeToggle() {
|
|
181
|
+
const toggleDark = () => {
|
|
182
|
+
document.documentElement.classList.toggle('dark')
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return <button onClick={toggleDark}>Toggle Dark Mode</button>
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Persisting User Preference
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
// Save to localStorage
|
|
193
|
+
const toggleDark = () => {
|
|
194
|
+
const isDark = document.documentElement.classList.toggle('dark')
|
|
195
|
+
localStorage.setItem('theme', isDark ? 'dark' : 'light')
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Restore on mount
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
const theme = localStorage.getItem('theme')
|
|
201
|
+
if (theme === 'dark') {
|
|
202
|
+
document.documentElement.classList.add('dark')
|
|
203
|
+
}
|
|
204
|
+
}, [])
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Animations
|
|
208
|
+
|
|
209
|
+
Use built-in animation utilities:
|
|
210
|
+
|
|
211
|
+
```tsx
|
|
212
|
+
{/* Fade animations */}
|
|
213
|
+
<div className="animate-fade-in">Fades in</div>
|
|
214
|
+
<div className="animate-fade-out">Fades out</div>
|
|
215
|
+
|
|
216
|
+
{/* Slide animations */}
|
|
217
|
+
<div className="animate-slide-in-from-top">Slides from top</div>
|
|
218
|
+
<div className="animate-slide-in-from-bottom">Slides from bottom</div>
|
|
219
|
+
<div className="animate-slide-in-from-left">Slides from left</div>
|
|
220
|
+
<div className="animate-slide-in-from-right">Slides from right</div>
|
|
221
|
+
|
|
222
|
+
{/* Accordion animations (for Radix UI) */}
|
|
223
|
+
<div className="animate-accordion-down">Expands</div>
|
|
224
|
+
<div className="animate-accordion-up">Collapses</div>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Animations automatically respect `prefers-reduced-motion` preferences.
|
|
228
|
+
|
|
229
|
+
## shadcn/ui Integration
|
|
230
|
+
|
|
231
|
+
This design system follows shadcn/ui's token architecture. To add shadcn components:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
npx shadcn@latest init
|
|
235
|
+
npx shadcn@latest add button
|
|
236
|
+
npx shadcn@latest add card
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Components will automatically use your theme tokens!
|
|
240
|
+
|
|
241
|
+
## Troubleshooting
|
|
242
|
+
|
|
243
|
+
### Colors Not Applying
|
|
244
|
+
|
|
245
|
+
**Issue:** Semantic utilities like `bg-primary` don't work.
|
|
246
|
+
|
|
247
|
+
**Solution:**
|
|
248
|
+
|
|
249
|
+
1. Verify tokens are imported in `app/globals.css`
|
|
250
|
+
2. Check Tailwind preset is in `tailwind.config.ts`
|
|
251
|
+
3. Restart dev server after changes
|
|
252
|
+
|
|
253
|
+
### Dark Mode Not Working
|
|
254
|
+
|
|
255
|
+
**Issue:** Colors don't change in dark mode.
|
|
256
|
+
|
|
257
|
+
**Solution:**
|
|
258
|
+
|
|
259
|
+
1. Ensure `.dark` class is on `<html>` element (not `<body>`)
|
|
260
|
+
2. Check browser DevTools to verify class is applied
|
|
261
|
+
3. Verify dark mode tokens exist in `tokens.css`
|
|
262
|
+
|
|
263
|
+
### Brand Colors Not Showing
|
|
264
|
+
|
|
265
|
+
**Issue:** Custom colors in `brand.css` aren't visible.
|
|
266
|
+
|
|
267
|
+
**Solution:**
|
|
268
|
+
|
|
269
|
+
1. Check import order in `app/globals.css`:
|
|
270
|
+
- Tokens must be imported first
|
|
271
|
+
- Brand CSS must come after
|
|
272
|
+
2. Verify HSL format: `158 64% 52%` (no `hsl()` wrapper)
|
|
273
|
+
3. Clear browser cache and rebuild
|
|
274
|
+
|
|
275
|
+
### Build Errors
|
|
276
|
+
|
|
277
|
+
**Issue:** Build fails with module not found.
|
|
278
|
+
|
|
279
|
+
**Solution:**
|
|
280
|
+
|
|
281
|
+
1. Run `npm install` at workspace root
|
|
282
|
+
2. Verify `@launch77-shared/lib-design-system` in package.json
|
|
283
|
+
3. Restart dev server
|
|
284
|
+
|
|
285
|
+
## Best Practices
|
|
286
|
+
|
|
287
|
+
### Do's ✅
|
|
288
|
+
|
|
289
|
+
- Use semantic utilities (`bg-primary`, `text-foreground`)
|
|
290
|
+
- Use alpha channel for transparency (`bg-primary/20`)
|
|
291
|
+
- Keep components neutral (no hardcoded colors)
|
|
292
|
+
- Override tokens in `brand.css` only
|
|
293
|
+
|
|
294
|
+
### Don'ts ❌
|
|
295
|
+
|
|
296
|
+
- Don't hardcode colors in components
|
|
297
|
+
- Don't use `!important` for overrides
|
|
298
|
+
- Don't modify node_modules
|
|
299
|
+
- Don't skip the design system tokens
|
|
300
|
+
|
|
301
|
+
## Next Steps
|
|
302
|
+
|
|
303
|
+
1. **Customize Your Brand** - Edit `brand.css` with your colors
|
|
304
|
+
2. **Test Thoroughly** - Visit `/design-system-test` and test all features
|
|
305
|
+
3. **Build Components** - Create reusable components with semantic tokens
|
|
306
|
+
4. **Add shadcn Components** - Integrate shadcn/ui for pre-built components
|
|
307
|
+
5. **Set Up Dark Mode** - Implement dark mode toggle with persistence
|
|
308
|
+
|
|
309
|
+
## Resources
|
|
310
|
+
|
|
311
|
+
- **Library Documentation**: Check node_modules/@launch77-shared/lib-design-system
|
|
312
|
+
- **shadcn/ui**: https://ui.shadcn.com
|
|
313
|
+
- **HSL Color Picker**: https://hslpicker.com
|
|
314
|
+
- **Tailwind CSS**: https://tailwindcss.com
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
Need help? Check the design system library README or ask in the Launch77 community.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* Brand Colors */
|
|
2
|
+
/* Customize your theme by overriding CSS variables */
|
|
3
|
+
|
|
4
|
+
:root {
|
|
5
|
+
/* Example: Customize primary color */
|
|
6
|
+
/* --color-primary: 158 64% 52%; */
|
|
7
|
+
/* --color-primary-foreground: 0 0% 100%; */
|
|
8
|
+
|
|
9
|
+
/* Example: Customize accent color */
|
|
10
|
+
/* --color-accent: 25 95% 53%; */
|
|
11
|
+
/* --color-accent-foreground: 0 0% 100%; */
|
|
12
|
+
|
|
13
|
+
/* Example: Customize secondary color */
|
|
14
|
+
/* --color-secondary: 270 50% 40%; */
|
|
15
|
+
/* --color-secondary-foreground: 0 0% 100%; */
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* Dark mode overrides */
|
|
19
|
+
.dark {
|
|
20
|
+
/* Example: Customize colors for dark mode */
|
|
21
|
+
/* --color-primary: 158 64% 52%; */
|
|
22
|
+
/* --color-primary-foreground: 240 5.9% 10%; */
|
|
23
|
+
}
|