piloti 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 +207 -0
- package/bin/piloti.js +264 -0
- package/config/config.json +303 -0
- package/package.json +43 -0
- package/src/0-gen-settings/index.css +4 -0
- package/src/0-gen-settings/responsive.css +501 -0
- package/src/0-gen-settings/utilities.css +218 -0
- package/src/0-gen-settings/variables.css +72 -0
- package/src/1-reset/reset.css +48 -0
- package/src/2-base/global.css +25 -0
- package/src/3-compositions/cluster.css +15 -0
- package/src/3-compositions/flow.css +22 -0
- package/src/3-compositions/stack.css +14 -0
- package/src/4-utilities/custom.css +4 -0
- package/src/5-blocks/nav.css +0 -0
- package/src/5-blocks/pricing-card.css +0 -0
- package/src/main.css +12 -0
package/README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Piloti
|
|
2
|
+
|
|
3
|
+
A config-driven CSS framework following **CUBE CSS methodology**.
|
|
4
|
+
|
|
5
|
+
> *Piloti* (pilotis) ā architectural columns that lift a building, providing the foundation. Just like pilotis support architecture, Piloti supports your design system.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- šØ **Token-based** ā Colors, spacing, typography from config
|
|
10
|
+
- ā” **Auto-generated utilities** ā Build from your tokens
|
|
11
|
+
- š± **Responsive** ā Breakpoint-prefixed classes (sm:, md:, lg:, xl:)
|
|
12
|
+
- š§© **CUBE CSS compositions** ā Stack, cluster, flow
|
|
13
|
+
- š§ **Customizable** ā Your config, your design system
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install piloti
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### 1. Create your config
|
|
24
|
+
|
|
25
|
+
Create `piloti.config.json` in your project root:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"settings": {
|
|
30
|
+
"basePixel": 16,
|
|
31
|
+
"breakpoints": {
|
|
32
|
+
"sm": "640px",
|
|
33
|
+
"md": "768px",
|
|
34
|
+
"lg": "1024px"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"tokens": {
|
|
38
|
+
"color": {
|
|
39
|
+
"primary": "#4f46e5",
|
|
40
|
+
"secondary": "#e5e7eb",
|
|
41
|
+
"accent": "#f59e0b"
|
|
42
|
+
},
|
|
43
|
+
"spacing": {
|
|
44
|
+
"0": "0px",
|
|
45
|
+
"1": "4px",
|
|
46
|
+
"2": "8px",
|
|
47
|
+
"4": "16px",
|
|
48
|
+
"8": "32px"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"utilities": [
|
|
52
|
+
{
|
|
53
|
+
"prefix": "p",
|
|
54
|
+
"property": "padding",
|
|
55
|
+
"fromToken": "spacing",
|
|
56
|
+
"values": ["0", "1", "2", "4", "8"],
|
|
57
|
+
"responsive": true
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"prefix": "bg",
|
|
61
|
+
"property": "background-color",
|
|
62
|
+
"fromToken": "color",
|
|
63
|
+
"values": ["primary", "secondary", "accent"]
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. Generate CSS
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx piloti
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This creates `piloti-output/` with:
|
|
76
|
+
- `variables.css` ā CSS custom properties
|
|
77
|
+
- `utilities.css` ā Utility classes
|
|
78
|
+
- `responsive.css` ā Responsive utilities
|
|
79
|
+
- `index.css` ā Entry point
|
|
80
|
+
|
|
81
|
+
### 3. Import in your project
|
|
82
|
+
|
|
83
|
+
```css
|
|
84
|
+
@import url('./piloti-output/index.css');
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Or in JS:
|
|
88
|
+
```js
|
|
89
|
+
import './piloti-output/index.css';
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## CLI Options
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npx piloti [options]
|
|
96
|
+
|
|
97
|
+
Options:
|
|
98
|
+
-c, --config <path> Path to config file (default: ./piloti.config.json)
|
|
99
|
+
-o, --output <path> Output directory (default: ./piloti-output)
|
|
100
|
+
-h, --help Show help
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Examples
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Use default config location
|
|
107
|
+
npx piloti
|
|
108
|
+
|
|
109
|
+
# Custom config
|
|
110
|
+
npx piloti --config ./tokens/design-system.json
|
|
111
|
+
|
|
112
|
+
# Custom output
|
|
113
|
+
npx piloti --output ./src/styles/generated
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Config Reference
|
|
117
|
+
|
|
118
|
+
### Settings
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"settings": {
|
|
123
|
+
"basePixel": 16,
|
|
124
|
+
"breakpoints": {
|
|
125
|
+
"sm": "640px",
|
|
126
|
+
"md": "768px",
|
|
127
|
+
"lg": "1024px",
|
|
128
|
+
"xl": "1280px"
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Tokens
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"tokens": {
|
|
139
|
+
"color": { "primary": "#000", "accent": "#f00" },
|
|
140
|
+
"spacing": { "1": "4px", "2": "8px" },
|
|
141
|
+
"fontSize": { "sm": "14px", "base": "16px" },
|
|
142
|
+
"radius": { "sm": "2px", "full": "9999px" },
|
|
143
|
+
"shadow": { "md": "0 4px 6px rgba(0,0,0,0.1)" },
|
|
144
|
+
"lineHeight": { "tight": "1.25", "normal": "1.5" },
|
|
145
|
+
"zIndex": { "10": "10", "50": "50" }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Utilities
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"utilities": [
|
|
155
|
+
{
|
|
156
|
+
"prefix": "p",
|
|
157
|
+
"property": "padding",
|
|
158
|
+
"fromToken": "spacing",
|
|
159
|
+
"values": ["1", "2", "4"],
|
|
160
|
+
"responsive": true
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"prefix": "d",
|
|
164
|
+
"property": "display",
|
|
165
|
+
"values": ["flex", "grid", "block", "none"],
|
|
166
|
+
"responsive": true
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"prefix": "justify",
|
|
170
|
+
"property": "justify-content",
|
|
171
|
+
"values": ["start", "center", "between"],
|
|
172
|
+
"valueMap": {
|
|
173
|
+
"start": "flex-start",
|
|
174
|
+
"center": "center",
|
|
175
|
+
"between": "space-between"
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Generated Classes
|
|
183
|
+
|
|
184
|
+
With the default config, you get:
|
|
185
|
+
|
|
186
|
+
| Category | Examples |
|
|
187
|
+
|----------|----------|
|
|
188
|
+
| Display | `.d-flex`, `.d-grid`, `.d-none` |
|
|
189
|
+
| Flexbox | `.flex-row`, `.flex-col`, `.justify-center`, `.items-center` |
|
|
190
|
+
| Spacing | `.p-4`, `.m-2`, `.gap-4`, `.mx-auto` |
|
|
191
|
+
| Colors | `.text-primary`, `.bg-accent` |
|
|
192
|
+
| Typography | `.font-xl`, `.leading-tight` |
|
|
193
|
+
| Shadows | `.shadow-md`, `.shadow-lg` |
|
|
194
|
+
| Responsive | `.sm:d-flex`, `.md:w-1/2`, `.lg:gap-8` |
|
|
195
|
+
|
|
196
|
+
## Using with the Base Framework
|
|
197
|
+
|
|
198
|
+
For compositions (stack, cluster, flow) and reset styles, also import:
|
|
199
|
+
|
|
200
|
+
```css
|
|
201
|
+
@import url('node_modules/piloti/src/main.css');
|
|
202
|
+
@import url('./piloti-output/index.css'); /* Your custom tokens */
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## License
|
|
206
|
+
|
|
207
|
+
MIT
|
package/bin/piloti.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
// =============================================================================
|
|
7
|
+
// CLI ARGUMENT PARSING
|
|
8
|
+
// =============================================================================
|
|
9
|
+
|
|
10
|
+
const args = process.argv.slice(2);
|
|
11
|
+
let configPath = null;
|
|
12
|
+
let outputDir = null;
|
|
13
|
+
|
|
14
|
+
for (let i = 0; i < args.length; i++) {
|
|
15
|
+
if (args[i] === '--config' || args[i] === '-c') {
|
|
16
|
+
configPath = args[i + 1];
|
|
17
|
+
i++;
|
|
18
|
+
} else if (args[i] === '--output' || args[i] === '-o') {
|
|
19
|
+
outputDir = args[i + 1];
|
|
20
|
+
i++;
|
|
21
|
+
} else if (args[i] === '--help' || args[i] === '-h') {
|
|
22
|
+
console.log(`
|
|
23
|
+
Piloti - CSS Framework Build Tool
|
|
24
|
+
|
|
25
|
+
Usage:
|
|
26
|
+
npx piloti [options]
|
|
27
|
+
|
|
28
|
+
Options:
|
|
29
|
+
-c, --config <path> Path to custom config.json (default: ./piloti.config.json)
|
|
30
|
+
-o, --output <path> Output directory for generated CSS (default: ./piloti-output)
|
|
31
|
+
-h, --help Show this help message
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
npx piloti
|
|
35
|
+
npx piloti --config ./my-tokens.json
|
|
36
|
+
npx piloti --config ./tokens.json --output ./styles/generated
|
|
37
|
+
`);
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Resolve paths
|
|
43
|
+
const packageDir = path.dirname(__dirname);
|
|
44
|
+
const cwd = process.cwd();
|
|
45
|
+
|
|
46
|
+
// Default config: look in cwd first, then fallback to package config
|
|
47
|
+
const defaultConfigPath = fs.existsSync(path.join(cwd, 'piloti.config.json'))
|
|
48
|
+
? path.join(cwd, 'piloti.config.json')
|
|
49
|
+
: path.join(packageDir, 'config/config.json');
|
|
50
|
+
|
|
51
|
+
configPath = configPath
|
|
52
|
+
? path.resolve(cwd, configPath)
|
|
53
|
+
: defaultConfigPath;
|
|
54
|
+
|
|
55
|
+
outputDir = outputDir
|
|
56
|
+
? path.resolve(cwd, outputDir)
|
|
57
|
+
: path.join(cwd, 'piloti-output');
|
|
58
|
+
|
|
59
|
+
// Load config
|
|
60
|
+
if (!fs.existsSync(configPath)) {
|
|
61
|
+
console.error(`Error: Config file not found at ${configPath}`);
|
|
62
|
+
console.error('Create a piloti.config.json file or use --config to specify a path.');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const config = require(configPath);
|
|
67
|
+
|
|
68
|
+
console.log(`\nšļø Piloti CSS Framework`);
|
|
69
|
+
console.log(` Config: ${configPath}`);
|
|
70
|
+
console.log(` Output: ${outputDir}\n`);
|
|
71
|
+
|
|
72
|
+
// =============================================================================
|
|
73
|
+
// ENSURE OUTPUT DIRECTORIES
|
|
74
|
+
// =============================================================================
|
|
75
|
+
|
|
76
|
+
[outputDir].forEach(dir => {
|
|
77
|
+
if (!fs.existsSync(dir)) {
|
|
78
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// =============================================================================
|
|
83
|
+
// HELPERS
|
|
84
|
+
// =============================================================================
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Convert px to rem
|
|
88
|
+
*/
|
|
89
|
+
const toRem = (value) => {
|
|
90
|
+
const basePixel = config.settings?.basePixel || 16;
|
|
91
|
+
if (typeof value === 'string' && value.endsWith('px')) {
|
|
92
|
+
const px = parseInt(value);
|
|
93
|
+
return px === 0 ? '0' : `${px / basePixel}rem`;
|
|
94
|
+
}
|
|
95
|
+
return value;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Sanitize class names (handles special characters like /)
|
|
100
|
+
*/
|
|
101
|
+
const sanitizeClassName = (name) => {
|
|
102
|
+
return name.replace(/\//g, '\\/');
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// =============================================================================
|
|
106
|
+
// GENERATE VARIABLES CSS
|
|
107
|
+
// =============================================================================
|
|
108
|
+
|
|
109
|
+
console.log('Generating CSS variables...');
|
|
110
|
+
|
|
111
|
+
let variablesContent = `/* ============================================
|
|
112
|
+
CSS Variables - Auto-generated
|
|
113
|
+
Config: ${path.basename(configPath)}
|
|
114
|
+
============================================ */\n\n:root {\n`;
|
|
115
|
+
|
|
116
|
+
let variableCount = 0;
|
|
117
|
+
|
|
118
|
+
for (const [category, tokens] of Object.entries(config.tokens || {})) {
|
|
119
|
+
variablesContent += `\n /* ${category.charAt(0).toUpperCase() + category.slice(1)} */\n`;
|
|
120
|
+
|
|
121
|
+
for (const [key, value] of Object.entries(tokens)) {
|
|
122
|
+
const varName = `--${category}-${key}`;
|
|
123
|
+
const varValue = toRem(value);
|
|
124
|
+
variablesContent += ` ${varName}: ${varValue};\n`;
|
|
125
|
+
variableCount++;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
variablesContent += '}\n';
|
|
130
|
+
|
|
131
|
+
fs.writeFileSync(path.join(outputDir, 'variables.css'), variablesContent);
|
|
132
|
+
console.log(` ā ${variableCount} variables ā variables.css`);
|
|
133
|
+
|
|
134
|
+
// =============================================================================
|
|
135
|
+
// GENERATE UTILITIES CSS
|
|
136
|
+
// =============================================================================
|
|
137
|
+
|
|
138
|
+
console.log('Generating utilities...');
|
|
139
|
+
|
|
140
|
+
let utilitiesContent = `/* ============================================
|
|
141
|
+
Utility Classes - Auto-generated
|
|
142
|
+
Config: ${path.basename(configPath)}
|
|
143
|
+
============================================ */\n\n`;
|
|
144
|
+
|
|
145
|
+
let utilityCount = 0;
|
|
146
|
+
let warnings = [];
|
|
147
|
+
|
|
148
|
+
(config.utilities || []).forEach(util => {
|
|
149
|
+
utilitiesContent += `/* ${util.property} */\n`;
|
|
150
|
+
|
|
151
|
+
util.values.forEach(key => {
|
|
152
|
+
let valueStr;
|
|
153
|
+
|
|
154
|
+
if (util.fromToken) {
|
|
155
|
+
if (config.tokens[util.fromToken] && config.tokens[util.fromToken][key]) {
|
|
156
|
+
valueStr = `var(--${util.fromToken}-${key})`;
|
|
157
|
+
} else {
|
|
158
|
+
warnings.push(`Token '${key}' not found in '${util.fromToken}'`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
} else if (util.valueMap && util.valueMap[key]) {
|
|
162
|
+
valueStr = util.valueMap[key];
|
|
163
|
+
} else {
|
|
164
|
+
valueStr = key;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const className = `.${util.prefix}-${sanitizeClassName(key)}`;
|
|
168
|
+
utilitiesContent += `${className} { ${util.property}: ${valueStr}; }\n`;
|
|
169
|
+
utilityCount++;
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
utilitiesContent += '\n';
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
fs.writeFileSync(path.join(outputDir, 'utilities.css'), utilitiesContent);
|
|
176
|
+
console.log(` ā ${utilityCount} utilities ā utilities.css`);
|
|
177
|
+
|
|
178
|
+
// =============================================================================
|
|
179
|
+
// GENERATE RESPONSIVE UTILITIES
|
|
180
|
+
// =============================================================================
|
|
181
|
+
|
|
182
|
+
const breakpoints = config.settings?.breakpoints || {};
|
|
183
|
+
let responsiveCount = 0;
|
|
184
|
+
|
|
185
|
+
if (Object.keys(breakpoints).length > 0) {
|
|
186
|
+
console.log('Generating responsive utilities...');
|
|
187
|
+
|
|
188
|
+
let responsiveContent = `/* ============================================
|
|
189
|
+
Responsive Utilities - Auto-generated
|
|
190
|
+
Config: ${path.basename(configPath)}
|
|
191
|
+
============================================ */\n\n`;
|
|
192
|
+
|
|
193
|
+
for (const [bpName, bpValue] of Object.entries(breakpoints)) {
|
|
194
|
+
responsiveContent += `/* ${bpName}: (min-width: ${bpValue}) */\n`;
|
|
195
|
+
responsiveContent += `@media (min-width: ${bpValue}) {\n`;
|
|
196
|
+
|
|
197
|
+
(config.utilities || []).forEach(util => {
|
|
198
|
+
if (!util.responsive) return;
|
|
199
|
+
|
|
200
|
+
util.values.forEach(key => {
|
|
201
|
+
let valueStr;
|
|
202
|
+
|
|
203
|
+
if (util.fromToken) {
|
|
204
|
+
if (config.tokens[util.fromToken] && config.tokens[util.fromToken][key]) {
|
|
205
|
+
valueStr = `var(--${util.fromToken}-${key})`;
|
|
206
|
+
} else {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
} else if (util.valueMap && util.valueMap[key]) {
|
|
210
|
+
valueStr = util.valueMap[key];
|
|
211
|
+
} else {
|
|
212
|
+
valueStr = key;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const className = `.${bpName}\\:${util.prefix}-${sanitizeClassName(key)}`;
|
|
216
|
+
responsiveContent += ` ${className} { ${util.property}: ${valueStr}; }\n`;
|
|
217
|
+
responsiveCount++;
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
responsiveContent += '}\n\n';
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
fs.writeFileSync(path.join(outputDir, 'responsive.css'), responsiveContent);
|
|
225
|
+
console.log(` ā ${responsiveCount} responsive utilities ā responsive.css`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// =============================================================================
|
|
229
|
+
// GENERATE MAIN ENTRY FILE
|
|
230
|
+
// =============================================================================
|
|
231
|
+
|
|
232
|
+
let mainContent = `/* Piloti CSS Framework - Generated Entry Point */\n`;
|
|
233
|
+
mainContent += `@import url('./variables.css');\n`;
|
|
234
|
+
mainContent += `@import url('./utilities.css');\n`;
|
|
235
|
+
if (responsiveCount > 0) {
|
|
236
|
+
mainContent += `@import url('./responsive.css');\n`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
fs.writeFileSync(path.join(outputDir, 'index.css'), mainContent);
|
|
240
|
+
|
|
241
|
+
// =============================================================================
|
|
242
|
+
// WARNINGS
|
|
243
|
+
// =============================================================================
|
|
244
|
+
|
|
245
|
+
if (warnings.length > 0) {
|
|
246
|
+
console.log('\nā ļø Warnings:');
|
|
247
|
+
warnings.forEach(w => console.log(` - ${w}`));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// =============================================================================
|
|
251
|
+
// SUMMARY
|
|
252
|
+
// =============================================================================
|
|
253
|
+
|
|
254
|
+
console.log('\n' + '='.repeat(50));
|
|
255
|
+
console.log('ā
Build complete!');
|
|
256
|
+
console.log('='.repeat(50));
|
|
257
|
+
console.log(` Variables: ${variableCount}`);
|
|
258
|
+
console.log(` Utilities: ${utilityCount}`);
|
|
259
|
+
console.log(` Responsive: ${responsiveCount}`);
|
|
260
|
+
console.log(` Total: ${variableCount + utilityCount + responsiveCount}`);
|
|
261
|
+
console.log('='.repeat(50));
|
|
262
|
+
console.log(`\nImport in your project:`);
|
|
263
|
+
console.log(` @import url('${path.relative(cwd, outputDir)}/index.css');`);
|
|
264
|
+
console.log('');
|