@stachelock/ui 0.1.9 → 0.2.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.
@@ -0,0 +1,325 @@
1
+ # Customizing Stachelock UI Design Tokens
2
+
3
+ Stachelock UI now supports customizing design tokens (colors, spacing, typography, etc.) to match your project's design system while maintaining the `sl-` prefixed utility classes.
4
+
5
+ ## Overview
6
+
7
+ The customization system allows you to:
8
+ - Override default colors, spacing, shadows, and typography
9
+ - Generate custom CSS variables and Tailwind configurations
10
+ - Maintain the `sl-` prefix to avoid conflicts with your existing styles
11
+ - Use both programmatic configuration and build-time generation
12
+
13
+ ## Quick Start
14
+
15
+ ### 1. Create a Custom Tokens File
16
+
17
+ Create a JSON file (e.g., `my-tokens.json`) with your custom design tokens:
18
+
19
+ ```json
20
+ {
21
+ "colors": {
22
+ "primary": {
23
+ "50": "#f0f9ff",
24
+ "100": "#e0f2fe",
25
+ "500": "#0ea5e9",
26
+ "600": "#0284c7",
27
+ "700": "#0369a1"
28
+ },
29
+ "stachelock": {
30
+ "600": "#dc2626",
31
+ "700": "#b91c1c"
32
+ }
33
+ },
34
+ "spacing": {
35
+ "18": "4.5rem",
36
+ "22": "5.5rem"
37
+ }
38
+ }
39
+ ```
40
+
41
+ ### 2. Generate Custom Configuration
42
+
43
+ Use the CLI tool to generate custom CSS variables and Tailwind config:
44
+
45
+ ```bash
46
+ # From your project directory
47
+ npx @stachelock/ui generate-config ./my-tokens.json ./generated/
48
+ ```
49
+
50
+ This will create:
51
+ - `stachelock-ui-variables.css` - CSS custom properties
52
+ - `stachelock-ui-tailwind.config.js` - Tailwind configuration
53
+ - `stachelock-ui-tokens.json` - Merged tokens for reference
54
+
55
+ ### 3. Import and Use
56
+
57
+ In your main CSS file:
58
+ ```css
59
+ @import "./generated/stachelock-ui-variables.css";
60
+ @import "@stachelock/ui/style.css";
61
+ ```
62
+
63
+ In your `tailwind.config.js`:
64
+ ```javascript
65
+ const stachelockConfig = require('./generated/stachelock-ui-tailwind.config.js');
66
+
67
+ module.exports = {
68
+ content: ["./src/**/*.{vue,js,ts,jsx,tsx}"],
69
+ theme: {
70
+ extend: {
71
+ ...stachelockConfig.theme.extend
72
+ }
73
+ }
74
+ };
75
+ ```
76
+
77
+ ## Programmatic Configuration
78
+
79
+ ### Using the Vue Plugin
80
+
81
+ ```typescript
82
+ import { createApp } from 'vue';
83
+ import { StachelockUI } from '@stachelock/ui';
84
+ import App from './App.vue';
85
+
86
+ const app = createApp(App);
87
+
88
+ app.use(StachelockUI, {
89
+ designTokens: {
90
+ colors: {
91
+ primary: {
92
+ 600: '#0ea5e9',
93
+ 700: '#0369a1'
94
+ }
95
+ }
96
+ }
97
+ });
98
+
99
+ app.mount('#app');
100
+ ```
101
+
102
+ ### Advanced Plugin Usage
103
+
104
+ ```typescript
105
+ import { StachelockUI } from '@stachelock/ui';
106
+
107
+ // Configure after installation
108
+ StachelockUI.configure({
109
+ designTokens: {
110
+ colors: {
111
+ brand: {
112
+ 500: '#ff6b6b',
113
+ 600: '#ee5a52'
114
+ }
115
+ }
116
+ },
117
+ prefix: 'sl-', // Keep default prefix
118
+ injectCSS: true // Automatically inject CSS variables
119
+ });
120
+
121
+ // Get current design tokens
122
+ const tokens = StachelockUI.getDesignTokens();
123
+
124
+ // Generate custom CSS
125
+ const css = StachelockUI.generateCSS();
126
+ ```
127
+
128
+ ## Design Token Structure
129
+
130
+ ### Colors
131
+ ```typescript
132
+ colors: {
133
+ primary: {
134
+ 50: '#f0f9ff', // Lightest
135
+ 100: '#e0f2fe',
136
+ 200: '#bae6fd',
137
+ 300: '#7dd3fc',
138
+ 400: '#38bdf8',
139
+ 500: '#0ea5e9', // Base
140
+ 600: '#0284c7',
141
+ 700: '#0369a1',
142
+ 800: '#075985',
143
+ 900: '#0c4a6e' // Darkest
144
+ }
145
+ }
146
+ ```
147
+
148
+ ### Spacing
149
+ ```typescript
150
+ spacing: {
151
+ '0': '0',
152
+ '1': '0.25rem',
153
+ '2': '0.5rem',
154
+ '4': '1rem',
155
+ '8': '2rem',
156
+ '16': '4rem',
157
+ '32': '8rem',
158
+ '64': '16rem'
159
+ }
160
+ ```
161
+
162
+ ### Border Radius
163
+ ```typescript
164
+ borderRadius: {
165
+ 'none': '0',
166
+ 'sm': '0.125rem',
167
+ 'DEFAULT': '0.25rem',
168
+ 'md': '0.375rem',
169
+ 'lg': '0.5rem',
170
+ 'xl': '0.75rem',
171
+ '2xl': '1rem',
172
+ 'full': '9999px'
173
+ }
174
+ ```
175
+
176
+ ### Shadows
177
+ ```typescript
178
+ shadows: {
179
+ 'sm': '0 1px 2px 0 rgb(0 0 0 / 0.05)',
180
+ 'DEFAULT': '0 1px 3px 0 rgb(0 0 0 / 0.1)',
181
+ 'md': '0 4px 6px -1px rgb(0 0 0 / 0.1)',
182
+ 'lg': '0 10px 15px -3px rgb(0 0 0 / 0.1)',
183
+ 'xl': '0 20px 25px -5px rgb(0 0 0 / 0.1)'
184
+ }
185
+ ```
186
+
187
+ ### Typography
188
+ ```typescript
189
+ typography: {
190
+ fontFamily: {
191
+ sans: ['Inter', 'ui-sans-serif', 'system-ui', 'sans-serif'],
192
+ display: ['Poppins', 'ui-sans-serif', 'system-ui', 'sans-serif'],
193
+ mono: ['JetBrains Mono', 'ui-monospace', 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas', 'monospace']
194
+ },
195
+ fontSize: {
196
+ 'xs': '0.75rem',
197
+ 'sm': '0.875rem',
198
+ 'base': '1rem',
199
+ 'lg': '1.125rem',
200
+ 'xl': '1.25rem',
201
+ '2xl': '1.5rem',
202
+ '3xl': '1.875rem',
203
+ '4xl': '2.25rem'
204
+ }
205
+ }
206
+ ```
207
+
208
+ ### Breakpoints
209
+ ```typescript
210
+ breakpoints: {
211
+ 'sm': '640px',
212
+ 'md': '768px',
213
+ 'lg': '1024px',
214
+ 'xl': '1280px',
215
+ '2xl': '1536px',
216
+ '3xl': '1600px'
217
+ }
218
+ ```
219
+
220
+ ## Generated CSS Variables
221
+
222
+ The system generates CSS custom properties that you can use in your own CSS:
223
+
224
+ ```css
225
+ :root {
226
+ --sl-color-primary-50: #f0f9ff;
227
+ --sl-color-primary-100: #e0f2fe;
228
+ --sl-color-primary-500: #0ea5e9;
229
+ --sl-color-primary-600: #0284c7;
230
+ --sl-color-primary-700: #0369a1;
231
+
232
+ --sl-spacing-18: 4.5rem;
233
+ --sl-spacing-22: 5.5rem;
234
+
235
+ --sl-border-radius-2xl: 1rem;
236
+ --sl-border-radius-3xl: 1.5rem;
237
+
238
+ --sl-shadow-glow: 0 0 20px rgba(59, 130, 246, 0.5);
239
+ --sl-shadow-glow-lg: 0 0 40px rgba(59, 130, 246, 0.3);
240
+ }
241
+ ```
242
+
243
+ ## Using Custom Tokens in Components
244
+
245
+ ### In Vue Templates
246
+ ```vue
247
+ <template>
248
+ <div class="sl-bg-primary-500 sl-text-white sl-p-18 sl-rounded-2xl sl-shadow-glow">
249
+ Custom styled content
250
+ </div>
251
+ </template>
252
+ ```
253
+
254
+ ### In CSS
255
+ ```css
256
+ .my-custom-component {
257
+ background-color: var(--sl-color-primary-500);
258
+ padding: var(--sl-spacing-18);
259
+ border-radius: var(--sl-border-radius-2xl);
260
+ box-shadow: var(--sl-shadow-glow);
261
+ }
262
+ ```
263
+
264
+ ## Migration from Previous Versions
265
+
266
+ If you're upgrading from a previous version:
267
+
268
+ 1. **No breaking changes** - existing `sl-` classes continue to work
269
+ 2. **New customization options** - you can now override design tokens
270
+ 3. **Backward compatibility** - all existing functionality is preserved
271
+
272
+ ## Best Practices
273
+
274
+ ### 1. Token Naming
275
+ - Use semantic names (e.g., `primary`, `secondary`, `accent`)
276
+ - Follow the 50-900 scale for colors
277
+ - Use consistent naming across your design system
278
+
279
+ ### 2. Color Selection
280
+ - Ensure sufficient contrast for accessibility
281
+ - Test colors in both light and dark modes
282
+ - Consider using tools like [Coolors](https://coolors.co/) for color generation
283
+
284
+ ### 3. Spacing Scale
285
+ - Use consistent spacing increments
286
+ - Consider your design system's needs
287
+ - Common scales: 4px (0.25rem), 8px (0.5rem), 16px (1rem)
288
+
289
+ ### 4. Typography
290
+ - Choose fonts that complement your brand
291
+ - Ensure good readability across devices
292
+ - Consider loading performance for custom fonts
293
+
294
+ ## Troubleshooting
295
+
296
+ ### Common Issues
297
+
298
+ **CSS variables not loading**
299
+ - Check that the CSS file is imported before other styles
300
+ - Verify the file path is correct
301
+ - Ensure the build process completed successfully
302
+
303
+ **Tailwind classes not working**
304
+ - Verify the Tailwind config is properly merged
305
+ - Check that content paths include your components
306
+ - Ensure the prefix is set to `sl-`
307
+
308
+ **Build errors**
309
+ - Check that all required dependencies are installed
310
+ - Verify the JSON syntax in your tokens file
311
+ - Ensure the output directory is writable
312
+
313
+ ### Getting Help
314
+
315
+ - Check the [main README](../README.md) for general usage
316
+ - Review the [examples](../examples/) directory for sample configurations
317
+ - Open an issue on GitHub for bugs or feature requests
318
+
319
+ ## Examples
320
+
321
+ See the [examples](../examples/) directory for complete working examples:
322
+
323
+ - `custom-tokens.json` - Sample token configuration
324
+ - `vue-app/` - Complete Vue application with custom tokens
325
+ - `react-app/` - Complete React application with custom tokens
@@ -0,0 +1,5 @@
1
+ import { StachelockUIDesignTokens } from './design-tokens';
2
+
3
+ export declare function generateCSSVariables(tokens: StachelockUIDesignTokens): string;
4
+ export declare function generateTailwindConfig(tokens: StachelockUIDesignTokens): string;
5
+ //# sourceMappingURL=css-variables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css-variables.d.ts","sourceRoot":"","sources":["../../../src/config/css-variables.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAEhE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,wBAAwB,GAAG,MAAM,CA6E7E;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,wBAAwB,GAAG,MAAM,CA2E/E"}
@@ -0,0 +1,52 @@
1
+ export interface StachelockUIDesignTokens {
2
+ colors?: {
3
+ primary?: {
4
+ 50?: string;
5
+ 100?: string;
6
+ 200?: string;
7
+ 300?: string;
8
+ 400?: string;
9
+ 500?: string;
10
+ 600?: string;
11
+ 700?: string;
12
+ 800?: string;
13
+ 900?: string;
14
+ };
15
+ stachelock?: {
16
+ 50?: string;
17
+ 100?: string;
18
+ 200?: string;
19
+ 300?: string;
20
+ 400?: string;
21
+ 500?: string;
22
+ 600?: string;
23
+ 700?: string;
24
+ 800?: string;
25
+ 900?: string;
26
+ };
27
+ [key: string]: any;
28
+ };
29
+ spacing?: {
30
+ [key: string]: string;
31
+ };
32
+ borderRadius?: {
33
+ [key: string]: string;
34
+ };
35
+ shadows?: {
36
+ [key: string]: string;
37
+ };
38
+ typography?: {
39
+ fontFamily?: {
40
+ [key: string]: string[];
41
+ };
42
+ fontSize?: {
43
+ [key: string]: string;
44
+ };
45
+ };
46
+ breakpoints?: {
47
+ [key: string]: string;
48
+ };
49
+ }
50
+ export declare const defaultDesignTokens: StachelockUIDesignTokens;
51
+ export declare function mergeDesignTokens(customTokens: StachelockUIDesignTokens, defaults?: StachelockUIDesignTokens): StachelockUIDesignTokens;
52
+ //# sourceMappingURL=design-tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-tokens.d.ts","sourceRoot":"","sources":["../../../src/config/design-tokens.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE;YACR,EAAE,CAAC,EAAE,MAAM,CAAC;YACZ,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;SACd,CAAC;QACF,UAAU,CAAC,EAAE;YACX,EAAE,CAAC,EAAE,MAAM,CAAC;YACZ,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;YACb,GAAG,CAAC,EAAE,MAAM,CAAC;SACd,CAAC;QACF,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,CAAC;IACF,OAAO,CAAC,EAAE;QACR,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;KACvB,CAAC;IACF,YAAY,CAAC,EAAE;QACb,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;KACvB,CAAC;IACF,OAAO,CAAC,EAAE;QACR,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;KACvB,CAAC;IACF,UAAU,CAAC,EAAE;QACX,UAAU,CAAC,EAAE;YACX,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;SACzB,CAAC;QACF,QAAQ,CAAC,EAAE;YACT,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;SACvB,CAAC;KACH,CAAC;IACF,WAAW,CAAC,EAAE;QACZ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;KACvB,CAAC;CACH;AAED,eAAO,MAAM,mBAAmB,EAAE,wBAoGjC,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,wBAAwB,EACtC,QAAQ,GAAE,wBAA8C,GACvD,wBAAwB,CAoB1B"}
@@ -0,0 +1,3 @@
1
+ export * from './css-variables';
2
+ export * from './design-tokens';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/config/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { App } from 'vue';
2
+ import { StachelockUIDesignTokens } from '../config/design-tokens';
3
+
4
+ export interface StachelockUIOptions {
5
+ designTokens?: StachelockUIDesignTokens;
6
+ prefix?: string;
7
+ injectCSS?: boolean;
8
+ }
9
+ export interface StachelockUIPlugin {
10
+ install: (app: App, options?: StachelockUIOptions) => void;
11
+ configure: (options: StachelockUIOptions) => void;
12
+ getDesignTokens: () => StachelockUIDesignTokens;
13
+ generateCSS: () => string;
14
+ }
15
+ declare class StachelockUIPluginImpl implements StachelockUIPlugin {
16
+ private designTokens;
17
+ private prefix;
18
+ private injectCSS;
19
+ constructor();
20
+ install(app: App, options?: StachelockUIOptions): void;
21
+ configure(options: StachelockUIOptions): void;
22
+ getDesignTokens(): StachelockUIDesignTokens;
23
+ generateCSS(): string;
24
+ private injectCSSVariables;
25
+ }
26
+ export declare const StachelockUI: StachelockUIPlugin;
27
+ export { StachelockUIPluginImpl };
28
+ //# sourceMappingURL=configure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../../../src/plugin/configure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC/B,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAIxE,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,wBAAwB,CAAC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC3D,SAAS,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAClD,eAAe,EAAE,MAAM,wBAAwB,CAAC;IAChD,WAAW,EAAE,MAAM,MAAM,CAAC;CAC3B;AAED,cAAM,sBAAuB,YAAW,kBAAkB;IACxD,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,SAAS,CAAiB;;IAQlC,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,GAAE,mBAAwB;IAkBnD,SAAS,CAAC,OAAO,EAAE,mBAAmB;IActC,eAAe,IAAI,wBAAwB;IAI3C,WAAW,IAAI,MAAM;IAIrB,OAAO,CAAC,kBAAkB;CAe3B;AAGD,eAAO,MAAM,YAAY,EAAE,kBAAiD,CAAC;AAG7E,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './configure';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugin/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"}
@@ -0,0 +1,70 @@
1
+ {
2
+ "colors": {
3
+ "primary": {
4
+ "50": "#f0f9ff",
5
+ "100": "#e0f2fe",
6
+ "200": "#bae6fd",
7
+ "300": "#7dd3fc",
8
+ "400": "#38bdf8",
9
+ "500": "#0ea5e9",
10
+ "600": "#0284c7",
11
+ "700": "#0369a1",
12
+ "800": "#075985",
13
+ "900": "#0c4a6e"
14
+ },
15
+ "stachelock": {
16
+ "50": "#fef2f2",
17
+ "100": "#fee2e2",
18
+ "200": "#fecaca",
19
+ "300": "#fca5a5",
20
+ "400": "#f87171",
21
+ "500": "#ef4444",
22
+ "600": "#dc2626",
23
+ "700": "#b91c1c",
24
+ "800": "#991b1b",
25
+ "900": "#7f1d1d"
26
+ },
27
+ "accent": {
28
+ "50": "#fdf4ff",
29
+ "100": "#fae8ff",
30
+ "200": "#f5d0fe",
31
+ "300": "#f0abfc",
32
+ "400": "#e879f9",
33
+ "500": "#d946ef",
34
+ "600": "#c026d3",
35
+ "700": "#a21caf",
36
+ "800": "#86198f",
37
+ "900": "#701a75"
38
+ }
39
+ },
40
+ "spacing": {
41
+ "18": "4.5rem",
42
+ "22": "5.5rem",
43
+ "26": "6.5rem",
44
+ "30": "7.5rem"
45
+ },
46
+ "borderRadius": {
47
+ "2xl": "1rem",
48
+ "3xl": "1.5rem",
49
+ "4xl": "2rem"
50
+ },
51
+ "shadows": {
52
+ "glow": "0 0 20px rgba(59, 130, 246, 0.5)",
53
+ "glow-lg": "0 0 40px rgba(59, 130, 246, 0.3)"
54
+ },
55
+ "typography": {
56
+ "fontFamily": {
57
+ "display": ["Poppins", "ui-sans-serif", "system-ui", "sans-serif"],
58
+ "mono": ["JetBrains Mono", "ui-monospace", "SFMono-Regular", "Menlo", "Monaco", "Consolas", "monospace"]
59
+ },
60
+ "fontSize": {
61
+ "2xs": "0.625rem",
62
+ "3xl": "1.875rem",
63
+ "4xl": "2.25rem"
64
+ }
65
+ },
66
+ "breakpoints": {
67
+ "3xl": "1600px",
68
+ "4xl": "1920px"
69
+ }
70
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stachelock/ui",
3
- "version": "0.1.9",
3
+ "version": "0.2.0",
4
4
  "description": "A comprehensive Vue 3 UI component library built with Tailwind CSS",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -13,6 +13,7 @@
13
13
  },
14
14
  "./style.css": "./dist/style.css",
15
15
  "./tailwind.config.js": "./dist/tailwind.config.js",
16
+ "./generate-config": "./scripts/generate-config.cjs",
16
17
  "./components/*": {
17
18
  "import": "./dist/components/*.js",
18
19
  "types": "./dist/components/*.d.ts"
@@ -37,8 +38,14 @@
37
38
  "files": [
38
39
  "dist",
39
40
  "tailwind.config.js",
40
- "README.md"
41
+ "scripts",
42
+ "examples",
43
+ "README.md",
44
+ "CUSTOMIZATION.md"
41
45
  ],
46
+ "bin": {
47
+ "stachelock-ui": "./scripts/generate-config.cjs"
48
+ },
42
49
  "scripts": {
43
50
  "dev": "vite",
44
51
  "build": "vite build && npm run build:css",
@@ -57,7 +64,8 @@
57
64
  "prepack": "npm run build",
58
65
  "postpack": "rm -rf dist",
59
66
  "generate-indexes": "node scripts/generateRecursiveIndex.cjs",
60
- "prebuild": "npm run generate-indexes"
67
+ "prebuild": "npm run generate-indexes",
68
+ "generate-config": "node scripts/generate-config.cjs"
61
69
  },
62
70
  "keywords": [
63
71
  "vue",
@@ -0,0 +1,29 @@
1
+ const { execSync } = require('child_process');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ console.log('🔨 Building Tailwind CSS with sl- prefix...');
6
+
7
+ try {
8
+ // Run Tailwind CLI to build the CSS
9
+ execSync('npx tailwindcss -i ./src/style.css -o ./dist/style.css --minify', {
10
+ stdio: 'inherit',
11
+ cwd: process.cwd()
12
+ });
13
+
14
+ console.log('✅ Tailwind CSS built successfully!');
15
+
16
+ // Read the generated CSS to verify it contains the sl- prefixed utilities
17
+ const cssContent = fs.readFileSync('./dist/style.css', 'utf8');
18
+
19
+ // Check if it contains some basic sl- prefixed utilities
20
+ if (cssContent.includes('.sl-bg-') || cssContent.includes('.sl-text-') || cssContent.includes('.sl-flex')) {
21
+ console.log('✅ CSS contains sl- prefixed Tailwind utilities');
22
+ } else {
23
+ console.log('⚠️ CSS may not contain expected sl- prefixed utilities');
24
+ }
25
+
26
+ } catch (error) {
27
+ console.error('❌ Error building Tailwind CSS:', error.message);
28
+ process.exit(1);
29
+ }
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ console.log('🎨 Stachelock UI Config Generator');
7
+ console.log('==================================\n');
8
+
9
+ // Get command line arguments
10
+ const args = process.argv.slice(2);
11
+ const configFile = args[0];
12
+ const outputDir = args[1] || './';
13
+
14
+ if (!configFile) {
15
+ console.log('Usage: node generate-config.cjs <config-file> [output-dir]');
16
+ console.log('');
17
+ console.log('Example:');
18
+ console.log(' node generate-config.cjs ./my-tokens.json ./generated/');
19
+ console.log('');
20
+ console.log('The config file should contain design tokens in the StachelockUIDesignTokens format.');
21
+ console.log('See the documentation for the expected format.');
22
+ process.exit(1);
23
+ }
24
+
25
+ if (!fs.existsSync(configFile)) {
26
+ console.error(`❌ Config file not found: ${configFile}`);
27
+ process.exit(1);
28
+ }
29
+
30
+ try {
31
+ // Read and parse the config file
32
+ console.log(`📖 Reading config from: ${configFile}`);
33
+ const configContent = fs.readFileSync(configFile, 'utf8');
34
+ const customTokens = JSON.parse(configContent);
35
+
36
+ // Import the required functions
37
+ const { mergeDesignTokens, defaultDesignTokens } = require('../dist/config/design-tokens.js');
38
+ const { generateCSSVariables, generateTailwindConfig } = require('../dist/config/css-variables.js');
39
+
40
+ // Merge with default tokens
41
+ console.log('🔄 Merging with default design tokens...');
42
+ const mergedTokens = mergeDesignTokens(customTokens, defaultDesignTokens);
43
+
44
+ // Generate CSS variables
45
+ console.log('🎨 Generating CSS variables...');
46
+ const cssVariables = generateCSSVariables(mergedTokens);
47
+
48
+ // Generate Tailwind config
49
+ console.log('⚙️ Generating Tailwind config...');
50
+ const tailwindConfig = generateTailwindConfig(mergedTokens);
51
+
52
+ // Ensure output directory exists
53
+ if (!fs.existsSync(outputDir)) {
54
+ fs.mkdirSync(outputDir, { recursive: true });
55
+ }
56
+
57
+ // Write CSS variables file
58
+ const cssOutputPath = path.join(outputDir, 'stachelock-ui-variables.css');
59
+ fs.writeFileSync(cssOutputPath, cssVariables);
60
+ console.log(`✅ CSS variables written to: ${cssOutputPath}`);
61
+
62
+ // Write Tailwind config file
63
+ const configOutputPath = path.join(outputDir, 'stachelock-ui-tailwind.config.js');
64
+ fs.writeFileSync(configOutputPath, tailwindConfig);
65
+ console.log(`✅ Tailwind config written to: ${configOutputPath}`);
66
+
67
+ // Write merged tokens JSON for reference
68
+ const tokensOutputPath = path.join(outputDir, 'stachelock-ui-tokens.json');
69
+ fs.writeFileSync(tokensOutputPath, JSON.stringify(mergedTokens, null, 2));
70
+ console.log(`✅ Merged tokens written to: ${tokensOutputPath}`);
71
+
72
+ console.log('\n🎉 Configuration generation complete!');
73
+ console.log('\nNext steps:');
74
+ console.log('1. Import the CSS variables in your main CSS file:');
75
+ console.log(` @import "${cssOutputPath}";`);
76
+ console.log('2. Use the Tailwind config in your tailwind.config.js:');
77
+ console.log(` const stachelockConfig = require("${configOutputPath}");`);
78
+ console.log('3. Merge it with your existing config as needed.');
79
+
80
+ } catch (error) {
81
+ console.error('❌ Error generating configuration:', error.message);
82
+ process.exit(1);
83
+ }
@@ -0,0 +1,251 @@
1
+ /* eslint-env node */
2
+ /* eslint-disable no-undef */
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ // Function to convert to camelCase
7
+ function toCamelCase(str) {
8
+ return str.replace(/[_-](.)/g, (_, group1) => group1.toUpperCase());
9
+ }
10
+
11
+ // Function to convert to PascalCase
12
+ function toPascalCase(str) {
13
+ const camelCase = toCamelCase(str);
14
+ return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
15
+ }
16
+
17
+ // Function to generate export name for components
18
+ function generateComponentExportName(fileName, directoryName) {
19
+ const cleanFileName = fileName.replace(/^Ui/, '').replace(/^Sl/, '');
20
+ const pascalFileName = toPascalCase(cleanFileName);
21
+
22
+ // For base components, just return the clean name
23
+ if (directoryName === 'components') {
24
+ return pascalFileName;
25
+ }
26
+
27
+ // For subdirectories, use a more intuitive naming
28
+ if (['layouts', 'inputs', 'calendars', 'forms'].includes(directoryName)) {
29
+ // Remove the 's' from the end and capitalize
30
+ const singularDirName = directoryName.slice(0, -1);
31
+ const pascalDirName = toPascalCase(singularDirName);
32
+
33
+ // Special cases for better naming
34
+ if (singularDirName === 'layout') {
35
+ return pascalFileName; // Just use the component name for layouts
36
+ }
37
+ if (singularDirName === 'input') {
38
+ return pascalFileName; // Just use the component name for inputs
39
+ }
40
+ if (singularDirName === 'calendar') {
41
+ return pascalFileName; // Just use the component name for calendars
42
+ }
43
+ if (singularDirName === 'form') {
44
+ // Handle naming conflicts with types
45
+ if (fileName === 'DynamicFormField') {
46
+ return 'FormField'; // Avoid conflict with type
47
+ }
48
+ return pascalFileName; // Just use the component name for forms
49
+ }
50
+
51
+ return `${pascalDirName}${pascalFileName}`;
52
+ }
53
+
54
+ return pascalFileName;
55
+ }
56
+
57
+ // Function to generate index file for a directory
58
+ function generateIndexFile(directoryPath, baseDirectoryPath = directoryPath) {
59
+ const files = fs.readdirSync(directoryPath, { withFileTypes: true });
60
+ let exportStatements = [];
61
+ let hasExports = false;
62
+
63
+ for (const file of files) {
64
+ if (file.isDirectory()) {
65
+ // Skip certain directories
66
+ if (['tests', 'test', '__tests__', 'node_modules', '.git'].includes(file.name.toLowerCase())) {
67
+ continue;
68
+ }
69
+
70
+ // Recursively handle subdirectories
71
+ const subDirectoryPath = path.join(directoryPath, file.name);
72
+ if (generateIndexFile(subDirectoryPath, baseDirectoryPath)) {
73
+ exportStatements.push(`export * from './${file.name}';`);
74
+ hasExports = true;
75
+ }
76
+ } else if (file.name.endsWith('.vue') && file.name !== 'index.vue') {
77
+ const fileName = file.name.replace('.vue', '');
78
+ const directoryName = path.basename(directoryPath);
79
+ const exportName = generateComponentExportName(fileName, directoryName);
80
+
81
+ exportStatements.push(`export { default as ${exportName} } from './${fileName}.vue';`);
82
+ hasExports = true;
83
+ } else if (file.name.endsWith('.ts') && file.name !== 'index.ts' && !file.name.endsWith('.d.ts')) {
84
+ const fileName = file.name.replace('.ts', '');
85
+ // For types directory, export all type files
86
+ if (path.basename(baseDirectoryPath) === 'types') {
87
+ exportStatements.push(`export * from './${fileName}';`);
88
+ } else {
89
+ exportStatements.push(`export * from './${fileName}';`);
90
+ }
91
+ hasExports = true;
92
+ }
93
+ }
94
+
95
+ if (hasExports) {
96
+ const indexPath = path.join(directoryPath, 'index.ts');
97
+ const content = exportStatements.join('\n') + '\n';
98
+
99
+ // Only write if content is different to avoid unnecessary file changes
100
+ if (!fs.existsSync(indexPath) || fs.readFileSync(indexPath, 'utf8') !== content) {
101
+ fs.writeFileSync(indexPath, content);
102
+ console.log(`✅ Generated index.ts for ${path.relative(baseDirectoryPath, directoryPath)}`);
103
+ }
104
+ return true;
105
+ }
106
+
107
+ return false;
108
+ }
109
+
110
+ // Function to generate main index file
111
+ function generateMainIndexFile(srcDirectoryPath) {
112
+ const mainIndexPath = path.join(srcDirectoryPath, 'index.ts');
113
+
114
+ // Generate new content - clean, non-prefixed exports only
115
+ const exportStatements = [
116
+ "// Types",
117
+ "export * from './types';",
118
+ "",
119
+ "// Utils",
120
+ "export * from './utils/id';",
121
+ "export * from './utils/component-registry';",
122
+ "",
123
+ "// Base UI Components",
124
+ "export * from './components';",
125
+ "",
126
+ "// Layout Components",
127
+ "export * from './components/layouts';",
128
+ "",
129
+ "// Input Components",
130
+ "export * from './components/inputs';",
131
+ "",
132
+ "// Calendar Components",
133
+ "export * from './components/calendars';",
134
+ "",
135
+ "// Form Components",
136
+ "export * from './components/forms';",
137
+ "",
138
+ "// Plugin interface and Vue plugin",
139
+ "export { StachelockUI, type StachelockUIOptions } from './plugin';",
140
+ "export { StachelockUI as default } from './plugin';"
141
+ ];
142
+
143
+ const newContent = exportStatements.join('\n') + '\n';
144
+
145
+ // Always write to ensure consistency
146
+ fs.writeFileSync(mainIndexPath, newContent);
147
+ console.log(`✅ Generated main index.ts`);
148
+ }
149
+
150
+ // Function to generate plugin file
151
+ function generatePluginFile(srcDirectoryPath) {
152
+ const pluginPath = path.join(srcDirectoryPath, 'plugin.ts');
153
+
154
+ const pluginContent = `import type { App } from 'vue';
155
+ import * as components from './components';
156
+ import * as layouts from './components/layouts';
157
+ import * as inputs from './components/inputs';
158
+ import * as calendars from './components/calendars';
159
+ import * as forms from './components/forms';
160
+
161
+ // Combine all components
162
+ const allComponents = {
163
+ ...components,
164
+ ...layouts,
165
+ ...inputs,
166
+ ...calendars,
167
+ ...forms,
168
+ };
169
+
170
+ // Plugin interface
171
+ export interface StachelockUIOptions {
172
+ /**
173
+ * Component name prefix (default: 'Sl')
174
+ */
175
+ prefix?: string;
176
+ /**
177
+ * Array of component names to install globally
178
+ */
179
+ components?: string[];
180
+ /**
181
+ * Whether to install all components globally
182
+ */
183
+ installAll?: boolean;
184
+ }
185
+
186
+ // Vue plugin
187
+ export const StachelockUI = {
188
+ install(app: App, options: StachelockUIOptions = {}) {
189
+ const { prefix = 'Sl', installAll = false, components: selectedComponents } = options;
190
+
191
+ // Register components
192
+ Object.entries(allComponents).forEach(([name, component]) => {
193
+ const shouldInstall = installAll ||
194
+ (selectedComponents && selectedComponents.includes(name)) ||
195
+ (!selectedComponents && !installAll);
196
+
197
+ if (shouldInstall) {
198
+ // Register non-prefixed
199
+ app.component(name, component);
200
+ // Also register prefixed alias for back-compat
201
+ const prefixedName = name.startsWith(prefix) ? name : \`\${prefix}\${name}\`;
202
+ app.component(prefixedName, component);
203
+ }
204
+ });
205
+ }
206
+ };
207
+
208
+ export default StachelockUI;
209
+ `;
210
+
211
+ // Only write if content is different
212
+ if (!fs.existsSync(pluginPath) || fs.readFileSync(pluginPath, 'utf8') !== pluginContent) {
213
+ fs.writeFileSync(pluginPath, pluginContent);
214
+ console.log(`✅ Generated plugin.ts`);
215
+ }
216
+ }
217
+
218
+ // Main execution
219
+ function main() {
220
+ console.log('🚀 Generating recursive index files for stachelock-ui...\n');
221
+
222
+ const srcDirectoryPath = path.join(__dirname, '../src');
223
+
224
+ if (!fs.existsSync(srcDirectoryPath)) {
225
+ console.error('❌ src directory not found!');
226
+ process.exit(1);
227
+ }
228
+
229
+ try {
230
+ // Generate index files for all subdirectories
231
+ generateIndexFile(srcDirectoryPath);
232
+
233
+ // Generate main index file
234
+ generateMainIndexFile(srcDirectoryPath);
235
+
236
+ // Generate plugin file
237
+ generatePluginFile(srcDirectoryPath);
238
+
239
+ console.log('\n✨ All index files generated successfully!');
240
+ } catch (error) {
241
+ console.error('❌ Error generating index files:', error);
242
+ process.exit(1);
243
+ }
244
+ }
245
+
246
+ // Run if called directly
247
+ if (require.main === module) {
248
+ main();
249
+ }
250
+
251
+ module.exports = { generateIndexFile, generateMainIndexFile, generatePluginFile };