czero 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # CZero
2
+
3
+ A lightweight, design-token-driven React component library.
4
+
5
+ ## Features
6
+
7
+ - 🎨 **Design Token Driven** - Single config file controls entire look & feel
8
+ - âš¡ **No Runtime Dependencies** - Precompiled CSS, no Tailwind required
9
+ - 🌙 **Dark Mode Ready** - Add `.dark` class and all components adapt
10
+ - ♿ **Accessible** - Built with accessibility in mind
11
+
12
+ ## Quick Start
13
+
14
+ ### 1. Install
15
+
16
+ ```bash
17
+ npm install czero
18
+ ```
19
+
20
+ ### 2. Create Theme Config
21
+
22
+ Create `czero.config.js` in your project root:
23
+
24
+ ```js
25
+ // czero.config.js
26
+ export default {
27
+ color: {
28
+ primary: { light: "222 47% 45%", dark: "210 80% 65%" },
29
+ primaryFg: { light: "0 0% 100%", dark: "220 40% 3%" },
30
+ // Override any token...
31
+ },
32
+ radius: {
33
+ md: "0.5rem",
34
+ lg: "0.75rem",
35
+ },
36
+ typography: {
37
+ fontFamily: "Inter, system-ui, sans-serif",
38
+ },
39
+ };
40
+ ```
41
+
42
+ ### 3. Generate CSS
43
+
44
+ ```bash
45
+ npx czero build
46
+ ```
47
+
48
+ This creates `czero.css` with your custom tokens + component styles.
49
+
50
+ Options:
51
+ - `--config <path>` - Config file path (default: `czero.config.js`)
52
+ - `--output <path>` - Output CSS path (default: `czero.css`)
53
+
54
+ ### 4. Import & Use
55
+
56
+ ```jsx
57
+ // Import generated CSS
58
+ import "./czero.css";
59
+
60
+ // Import components
61
+ import { Button, Input, Card, Badge } from "czero/react";
62
+
63
+ function App() {
64
+ return (
65
+ <Card>
66
+ <Card.Header>
67
+ <Card.Title>Hello CZero</Card.Title>
68
+ </Card.Header>
69
+ <Card.Body>
70
+ <Button variant="primary">Click me</Button>
71
+ </Card.Body>
72
+ </Card>
73
+ );
74
+ }
75
+ ```
76
+
77
+ ## Components
78
+
79
+ ### Button
80
+
81
+ ```jsx
82
+ <Button variant="primary">Primary</Button>
83
+ <Button variant="secondary">Secondary</Button>
84
+ <Button variant="outline">Outline</Button>
85
+ <Button variant="ghost">Ghost</Button>
86
+ <Button variant="danger">Danger</Button>
87
+
88
+ <Button size="sm">Small</Button>
89
+ <Button size="md">Medium</Button>
90
+ <Button size="lg">Large</Button>
91
+
92
+ <Button loading>Loading...</Button>
93
+ <Button disabled>Disabled</Button>
94
+ ```
95
+
96
+ ### Input
97
+
98
+ ```jsx
99
+ <Input placeholder="Enter text..." />
100
+ <Input label="Email" type="email" />
101
+ <Input label="Password" error="Required" />
102
+ <Input size="sm" />
103
+ <Input size="lg" />
104
+ ```
105
+
106
+ ### Card
107
+
108
+ ```jsx
109
+ <Card>
110
+ <Card.Header>
111
+ <Card.Title>Title</Card.Title>
112
+ <Card.Description>Description text</Card.Description>
113
+ </Card.Header>
114
+ <Card.Body>Content goes here</Card.Body>
115
+ <Card.Footer>
116
+ <Button variant="ghost">Cancel</Button>
117
+ <Button>Save</Button>
118
+ </Card.Footer>
119
+ </Card>
120
+ ```
121
+
122
+ ### Badge
123
+
124
+ ```jsx
125
+ <Badge>Default</Badge>
126
+ <Badge variant="primary">Primary</Badge>
127
+ <Badge variant="success">Success</Badge>
128
+ <Badge variant="danger">Danger</Badge>
129
+ <Badge variant="warning">Warning</Badge>
130
+ <Badge variant="outline">Outline</Badge>
131
+ ```
132
+
133
+ ## Dark Mode
134
+
135
+ Add the `.dark` class to your root element:
136
+
137
+ ```html
138
+ <html class="dark">
139
+ ...
140
+ </html>
141
+ ```
142
+
143
+ All components automatically adapt to the dark color scheme.
144
+
145
+ ## Theming
146
+
147
+ All design tokens use the `--cz-*` prefix. Override any token in your CSS:
148
+
149
+ ```css
150
+ :root {
151
+ --cz-color-primary: 280 70% 50%; /* Purple */
152
+ --cz-radius-md: 1rem; /* More rounded */
153
+ }
154
+ ```
155
+
156
+ ## Available Tokens
157
+
158
+ | Category | Tokens |
159
+ |----------|--------|
160
+ | Colors | `bg`, `fg`, `primary`, `secondary`, `muted`, `danger`, `success`, `warning`, `border`, `ring` |
161
+ | Radius | `none`, `sm`, `md`, `lg`, `xl`, `full` |
162
+ | Spacing | `xs`, `sm`, `md`, `lg`, `xl`, `2xl` |
163
+ | Typography | `fontFamily`, `size-*`, `weight-*`, `lineHeight-*` |
164
+ | Shadow | `none`, `sm`, `md`, `lg` |
165
+ | Transition | `fast`, `normal`, `slow` |
166
+
167
+ ## License
168
+
169
+ MIT
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env node
2
+
3
+ // cli/index.ts
4
+ import * as fs from "fs";
5
+ import * as path from "path";
6
+ import { fileURLToPath } from "url";
7
+ var __filename = fileURLToPath(import.meta.url);
8
+ var __dirname = path.dirname(__filename);
9
+ var defaultTheme = {
10
+ color: {
11
+ bg: { light: "0 0% 100%", dark: "220 40% 3%" },
12
+ fg: { light: "220 15% 10%", dark: "210 40% 96%" },
13
+ primary: { light: "222 47% 45%", dark: "210 80% 65%" },
14
+ primaryFg: { light: "0 0% 100%", dark: "220 40% 3%" },
15
+ secondary: { light: "220 10% 95%", dark: "220 8% 25%" },
16
+ secondaryFg: { light: "220 15% 10%", dark: "210 40% 96%" },
17
+ muted: { light: "220 10% 95%", dark: "220 8% 20%" },
18
+ mutedFg: { light: "220 10% 40%", dark: "220 10% 60%" },
19
+ danger: { light: "0 70% 55%", dark: "0 80% 65%" },
20
+ dangerFg: { light: "0 0% 100%", dark: "0 0% 100%" },
21
+ success: { light: "142 70% 45%", dark: "142 70% 55%" },
22
+ successFg: { light: "0 0% 100%", dark: "0 0% 100%" },
23
+ warning: { light: "38 92% 50%", dark: "38 92% 60%" },
24
+ warningFg: { light: "0 0% 100%", dark: "0 0% 0%" },
25
+ border: { light: "220 13% 90%", dark: "220 10% 20%" },
26
+ ring: { light: "222 47% 45%", dark: "210 80% 65%" }
27
+ },
28
+ radius: {
29
+ none: "0",
30
+ sm: "0.25rem",
31
+ md: "0.5rem",
32
+ lg: "0.75rem",
33
+ xl: "1rem",
34
+ full: "9999px"
35
+ },
36
+ shadow: {
37
+ none: "none",
38
+ sm: "0 1px 2px rgb(0 0 0 / 0.05)",
39
+ md: "0 2px 4px rgb(0 0 0 / 0.08)",
40
+ lg: "0 4px 12px rgb(0 0 0 / 0.12)"
41
+ },
42
+ spacing: {
43
+ xs: "0.25rem",
44
+ sm: "0.5rem",
45
+ md: "0.75rem",
46
+ lg: "1rem",
47
+ xl: "1.5rem",
48
+ "2xl": "2rem"
49
+ },
50
+ typography: {
51
+ fontFamily: "Inter, system-ui, -apple-system, sans-serif",
52
+ size: { xs: "0.75rem", sm: "0.875rem", md: "1rem", lg: "1.125rem", xl: "1.25rem" },
53
+ weight: { normal: "400", medium: "500", semibold: "600", bold: "700" },
54
+ lineHeight: { tight: "1.25", normal: "1.5", relaxed: "1.75" }
55
+ },
56
+ transition: { fast: "150ms ease", normal: "200ms ease", slow: "300ms ease" }
57
+ };
58
+ function deepMerge(target, source) {
59
+ const result = { ...target };
60
+ for (const key of Object.keys(source)) {
61
+ if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
62
+ result[key] = deepMerge(target[key] || {}, source[key]);
63
+ } else {
64
+ result[key] = source[key];
65
+ }
66
+ }
67
+ return result;
68
+ }
69
+ function generateTokensCSS(theme) {
70
+ const lightVars = [];
71
+ const darkVars = [];
72
+ for (const [name, values] of Object.entries(theme.color)) {
73
+ if (typeof values === "object" && "light" in values) {
74
+ lightVars.push(` --cz-color-${name}: ${values.light};`);
75
+ darkVars.push(` --cz-color-${name}: ${values.dark};`);
76
+ }
77
+ }
78
+ for (const [name, value] of Object.entries(theme.radius)) {
79
+ lightVars.push(` --cz-radius-${name}: ${value};`);
80
+ }
81
+ for (const [name, value] of Object.entries(theme.shadow)) {
82
+ lightVars.push(` --cz-shadow-${name}: ${value};`);
83
+ }
84
+ for (const [name, value] of Object.entries(theme.spacing)) {
85
+ lightVars.push(` --cz-spacing-${name}: ${value};`);
86
+ }
87
+ lightVars.push(` --cz-font-fontFamily: ${theme.typography.fontFamily};`);
88
+ for (const [name, value] of Object.entries(theme.typography.size)) {
89
+ lightVars.push(` --cz-font-size-${name}: ${value};`);
90
+ }
91
+ for (const [name, value] of Object.entries(theme.typography.weight)) {
92
+ lightVars.push(` --cz-font-weight-${name}: ${value};`);
93
+ }
94
+ for (const [name, value] of Object.entries(theme.typography.lineHeight)) {
95
+ lightVars.push(` --cz-font-lineHeight-${name}: ${value};`);
96
+ }
97
+ for (const [name, value] of Object.entries(theme.transition)) {
98
+ lightVars.push(` --cz-transition-${name}: ${value};`);
99
+ }
100
+ return `/**
101
+ * CZero Design Tokens
102
+ * Generated from czero.config.js
103
+ */
104
+
105
+ :root {
106
+ ${lightVars.join("\n")}
107
+ }
108
+
109
+ .dark {
110
+ ${darkVars.join("\n")}
111
+ }
112
+ `;
113
+ }
114
+ function getComponentsCSS() {
115
+ const possiblePaths = [
116
+ path.join(__dirname, "components.css"),
117
+ // dist/cli/../components.css = dist/components.css
118
+ path.join(__dirname, "../components.css"),
119
+ // dist/components.css
120
+ path.join(__dirname, "../dist/components.css"),
121
+ // from root
122
+ path.join(__dirname, "../../dist/components.css"),
123
+ // from cli folder
124
+ path.join(__dirname, "../src/core/styles/components.css"),
125
+ // dev mode
126
+ path.join(__dirname, "../../src/core/styles/components.css")
127
+ // dev mode from cli
128
+ ];
129
+ for (const p of possiblePaths) {
130
+ if (fs.existsSync(p)) {
131
+ return fs.readFileSync(p, "utf-8");
132
+ }
133
+ }
134
+ console.error("Error: Could not find components.css");
135
+ console.error("Searched paths:", possiblePaths);
136
+ process.exit(1);
137
+ }
138
+ function getResetCSS() {
139
+ const possiblePaths = [
140
+ path.join(__dirname, "reset.css"),
141
+ path.join(__dirname, "../reset.css"),
142
+ path.join(__dirname, "../dist/reset.css"),
143
+ path.join(__dirname, "../../dist/reset.css"),
144
+ path.join(__dirname, "../src/core/styles/reset.css"),
145
+ path.join(__dirname, "../../src/core/styles/reset.css")
146
+ ];
147
+ for (const p of possiblePaths) {
148
+ if (fs.existsSync(p)) {
149
+ return fs.readFileSync(p, "utf-8");
150
+ }
151
+ }
152
+ return `*, *::before, *::after { box-sizing: border-box; }
153
+ * { margin: 0; }
154
+ body { line-height: 1.5; -webkit-font-smoothing: antialiased; }
155
+ button, input, textarea, select { font: inherit; }
156
+ `;
157
+ }
158
+ async function loadUserConfig(configPath) {
159
+ const absolutePath = path.resolve(process.cwd(), configPath);
160
+ if (!fs.existsSync(absolutePath)) {
161
+ console.log(`No config found at ${configPath}, using defaults...`);
162
+ return {};
163
+ }
164
+ try {
165
+ const config = await import(`file://${absolutePath}`);
166
+ return config.default || config.theme || config;
167
+ } catch (error) {
168
+ console.error(`Error loading config: ${error}`);
169
+ return {};
170
+ }
171
+ }
172
+ async function main() {
173
+ const args = process.argv.slice(2);
174
+ let configPath = "czero.config.js";
175
+ let outputPath = "czero.css";
176
+ for (let i = 0; i < args.length; i++) {
177
+ if (args[i] === "--config" && args[i + 1]) {
178
+ configPath = args[i + 1];
179
+ i++;
180
+ } else if (args[i] === "--output" && args[i + 1]) {
181
+ outputPath = args[i + 1];
182
+ i++;
183
+ } else if (args[i] === "build") {
184
+ } else if (args[i] === "--help" || args[i] === "-h") {
185
+ console.log(`
186
+ CZero CLI - Generate CSS from your theme configuration
187
+
188
+ Usage:
189
+ npx czero build [options]
190
+
191
+ Options:
192
+ --config <path> Path to config file (default: czero.config.js)
193
+ --output <path> Output CSS file path (default: czero.css)
194
+ --help, -h Show this help message
195
+
196
+ Example:
197
+ npx czero build --config my-theme.js --output src/styles/czero.css
198
+ `);
199
+ process.exit(0);
200
+ }
201
+ }
202
+ console.log("\u{1F3A8} CZero CSS Generator");
203
+ console.log(` Config: ${configPath}`);
204
+ console.log(` Output: ${outputPath}`);
205
+ console.log("");
206
+ const userConfig = await loadUserConfig(configPath);
207
+ const theme = deepMerge(defaultTheme, userConfig);
208
+ const resetCSS = getResetCSS();
209
+ const tokensCSS = generateTokensCSS(theme);
210
+ const componentsCSS = getComponentsCSS();
211
+ const finalCSS = `${resetCSS}
212
+ ${tokensCSS}
213
+ ${componentsCSS}`;
214
+ fs.writeFileSync(outputPath, finalCSS);
215
+ console.log(`\u2705 Generated ${outputPath}`);
216
+ console.log(` ${finalCSS.split("\n").length} lines`);
217
+ }
218
+ main().catch(console.error);