jit-colors 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 +49 -0
- package/jit-colors.js +235 -0
- package/package.json +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# JIT Colors
|
|
2
|
+
|
|
3
|
+
A lightweight, Just-In-Time (JIT) CSS utility generator. It watches your DOM for specific class patterns and generates the corresponding CSS on the fly.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install jit-colors
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Import and start the engine in your main JavaScript entry point.
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
import jitColors from "jit-colors";
|
|
17
|
+
|
|
18
|
+
// Start the JIT engine
|
|
19
|
+
jitColors.start();
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Options
|
|
23
|
+
|
|
24
|
+
You can pass options to the `start` method:
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
jitColors.start({
|
|
28
|
+
separator: "_", // Default is '_'
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- **Colors**: `bg-red`, `text-blue` (supports hex codes like `bg-#ff0000` or `bg-ff0000`)
|
|
35
|
+
- **Spacing**: `m-10px`, `p-20` (default px), `mx-auto`, `my-5%`
|
|
36
|
+
- **Typography**: `fs-16px`, `fw-bold`, `tw-wrap`
|
|
37
|
+
- **Dimensions**: `w-100%`, `h-100vh`, `minw-300px`
|
|
38
|
+
- **Borders**: `border-1px_solid_red`, `border-top-5px`
|
|
39
|
+
- **Position**: `top-0`, `left-50%`, `z-10`
|
|
40
|
+
- **Breakpoints**: `sm`, `md`, `lg`, `xl`, `xxl` (e.g., `md_bg-red`)
|
|
41
|
+
|
|
42
|
+
## Example
|
|
43
|
+
|
|
44
|
+
```html
|
|
45
|
+
<div class="bg-f0f0f0 p-20px rounded-8px">
|
|
46
|
+
<h1 class="text-333 fs-24px fw-bold">Hello World</h1>
|
|
47
|
+
<p class="mt-10px text-666">This is styled with JIT Colors.</p>
|
|
48
|
+
</div>
|
|
49
|
+
```
|
package/jit-colors.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
start(options = {}) {
|
|
3
|
+
const separator = options.separator || '_';
|
|
4
|
+
const styleId = 'jit_styles';
|
|
5
|
+
|
|
6
|
+
// ---------------- Create <style> early ----------------
|
|
7
|
+
let styleTag = document.getElementById(styleId);
|
|
8
|
+
if (!styleTag) {
|
|
9
|
+
styleTag = document.createElement('style');
|
|
10
|
+
styleTag.id = styleId;
|
|
11
|
+
document.head.appendChild(styleTag);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const breakpoints = {
|
|
15
|
+
sm: '576px',
|
|
16
|
+
md: '768px',
|
|
17
|
+
lg: '992px',
|
|
18
|
+
xl: '1200px',
|
|
19
|
+
xxl: '1400px',
|
|
20
|
+
};
|
|
21
|
+
const breakpointOrder = ['base', 'sm', 'md', 'lg', 'xl', 'xxl'];
|
|
22
|
+
|
|
23
|
+
const cssBuckets = {
|
|
24
|
+
base: '',
|
|
25
|
+
sm: '',
|
|
26
|
+
md: '',
|
|
27
|
+
lg: '',
|
|
28
|
+
xl: '',
|
|
29
|
+
xxl: '',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const addedClasses = new Set();
|
|
33
|
+
const esc = (cls) => CSS.escape(cls);
|
|
34
|
+
|
|
35
|
+
const parseValue = (raw) => {
|
|
36
|
+
if (/^-?\d*\.?\d+[a-z%]+$/i.test(raw)) return raw;
|
|
37
|
+
if (/^-?\d*\.?\d+$/.test(raw)) return raw + 'px';
|
|
38
|
+
return null;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const normalizeColor = (raw) => {
|
|
42
|
+
if (raw.startsWith('#')) return raw;
|
|
43
|
+
if (/^[0-9A-Fa-f]{3,8}$/.test(raw)) return '#' + raw;
|
|
44
|
+
return raw;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const generateCSS = (className) => {
|
|
48
|
+
if (addedClasses.has(className)) return;
|
|
49
|
+
|
|
50
|
+
const parts = className.split(separator);
|
|
51
|
+
const utility = parts[0];
|
|
52
|
+
|
|
53
|
+
let bp = null;
|
|
54
|
+
let values = [];
|
|
55
|
+
|
|
56
|
+
if (breakpoints[parts[1]]) {
|
|
57
|
+
bp = parts[1];
|
|
58
|
+
values = parts.slice(2);
|
|
59
|
+
} else {
|
|
60
|
+
values = parts.slice(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!utility || !values.length) return;
|
|
64
|
+
const cls = esc(className);
|
|
65
|
+
let cssRule = '';
|
|
66
|
+
|
|
67
|
+
/* ---------- COLORS ---------- */
|
|
68
|
+
if (utility === 'bg' || utility === 'text') {
|
|
69
|
+
const colorRaw = values.join('_');
|
|
70
|
+
const color = normalizeColor(colorRaw);
|
|
71
|
+
cssRule = utility === 'bg'
|
|
72
|
+
? `.${cls}{background-color:${color}!important;}`
|
|
73
|
+
: `.${cls}{color:${color}!important;}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* ---------- TEXT WRAP ---------- */
|
|
77
|
+
else if (utility === 'tw') {
|
|
78
|
+
const map = {
|
|
79
|
+
wrap: 'white-space:normal',
|
|
80
|
+
nowrap: 'white-space:nowrap',
|
|
81
|
+
pre: 'white-space:pre',
|
|
82
|
+
pre_wrap: 'white-space:pre-wrap',
|
|
83
|
+
pre_line: 'white-space:pre-line',
|
|
84
|
+
break: 'word-break:break-word',
|
|
85
|
+
break_all: 'word-break:break-all',
|
|
86
|
+
keep_all: 'word-break:keep-all',
|
|
87
|
+
};
|
|
88
|
+
const key = values.join('_');
|
|
89
|
+
if (!map[key]) return;
|
|
90
|
+
cssRule = `.${cls}{${map[key]}!important;}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* ---------- Z-INDEX ---------- */
|
|
94
|
+
else if (utility === 'z') {
|
|
95
|
+
cssRule = `.${cls}{z-index:${values[0]}!important;}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* ---------- POSITION ---------- */
|
|
99
|
+
else if (['top', 'bottom', 'left', 'right'].includes(utility)) {
|
|
100
|
+
const value = parseValue(values[0]);
|
|
101
|
+
if (!value) return;
|
|
102
|
+
cssRule = `.${cls}{${utility}:${value}!important;}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* ---------- BORDERS ---------- */
|
|
106
|
+
else if (utility === 'border') {
|
|
107
|
+
let pos = null, size = null, style = null, colorRaw = null;
|
|
108
|
+
|
|
109
|
+
const sideMap = {
|
|
110
|
+
top: 'top', bottom: 'bottom',
|
|
111
|
+
start: 'left', end: 'right',
|
|
112
|
+
left: 'left', right: 'right'
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const isSide = (val) => Object.keys(sideMap).includes(val);
|
|
116
|
+
|
|
117
|
+
if (values.length === 2 && isSide(values[0])) {
|
|
118
|
+
[pos, size] = values;
|
|
119
|
+
} else if (values.length === 1) {
|
|
120
|
+
[size] = values;
|
|
121
|
+
} else if (values.length === 3) {
|
|
122
|
+
[size, style, colorRaw] = values;
|
|
123
|
+
} else if (values.length === 4) {
|
|
124
|
+
[pos, size, style, colorRaw] = values;
|
|
125
|
+
} else return;
|
|
126
|
+
|
|
127
|
+
const side = (pos && sideMap[pos]) ? `-${sideMap[pos]}` : '';
|
|
128
|
+
const sizeVal = parseValue(size);
|
|
129
|
+
if (!sizeVal) return;
|
|
130
|
+
|
|
131
|
+
if (parseFloat(sizeVal) === 0) {
|
|
132
|
+
cssRule = `.${cls}{border${side}:${sizeVal}!important;}`;
|
|
133
|
+
} else {
|
|
134
|
+
if (!style || !colorRaw) return;
|
|
135
|
+
cssRule = `.${cls}{border${side}:${sizeVal} ${style} ${normalizeColor(colorRaw)}!important;}`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* ---------- SIZE / SPACING / FONT ---------- */
|
|
140
|
+
else {
|
|
141
|
+
const value = parseValue(values[0]);
|
|
142
|
+
if (!value) return;
|
|
143
|
+
|
|
144
|
+
const map = {
|
|
145
|
+
m: 'margin', mt: 'margin-top', mb: 'margin-bottom',
|
|
146
|
+
ms: 'margin-left', me: 'margin-right',
|
|
147
|
+
p: 'padding', pt: 'padding-top', pb: 'padding-bottom',
|
|
148
|
+
ps: 'padding-left', pe: 'padding-right',
|
|
149
|
+
w: 'width', h: 'height',
|
|
150
|
+
minw: 'min-width', maxw: 'max-width',
|
|
151
|
+
minh: 'min-height', maxh: 'max-height',
|
|
152
|
+
fs: 'font-size', fw: 'font-weight',
|
|
153
|
+
rounded: 'border-radius', gap: 'gap',
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
if (utility === 'mx') {
|
|
157
|
+
cssRule = `.${cls}{margin-left:${value}!important;margin-right:${value}!important;}`;
|
|
158
|
+
}
|
|
159
|
+
else if (utility === 'my') {
|
|
160
|
+
cssRule = `.${cls}{margin-top:${value}!important;margin-bottom:${value}!important;}`;
|
|
161
|
+
}
|
|
162
|
+
else if (utility === 'px') {
|
|
163
|
+
cssRule = `.${cls}{padding-left:${value}!important;padding-right:${value}!important;}`;
|
|
164
|
+
}
|
|
165
|
+
else if (utility === 'py') {
|
|
166
|
+
cssRule = `.${cls}{padding-top:${value}!important;padding-bottom:${value}!important;}`;
|
|
167
|
+
}
|
|
168
|
+
else if (map[utility]) {
|
|
169
|
+
if (utility === 'fw') {
|
|
170
|
+
cssRule = `.${cls}{font-weight:${values[0]}!important;}`;
|
|
171
|
+
} else {
|
|
172
|
+
cssRule = `.${cls}{${map[utility]}:${value}!important;}`;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!cssRule) return;
|
|
181
|
+
if (bp) cssBuckets[bp] += cssRule + '\n';
|
|
182
|
+
else cssBuckets.base += cssRule + '\n';
|
|
183
|
+
addedClasses.add(className);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const renderStyles = () => {
|
|
187
|
+
let css = cssBuckets.base + '\n';
|
|
188
|
+
for (const bp of breakpointOrder.slice(1)) {
|
|
189
|
+
if (cssBuckets[bp]) {
|
|
190
|
+
css += `@media (min-width:${breakpoints[bp]}){\n${cssBuckets[bp]}}\n`;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
styleTag.textContent = css;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const preload = () => {
|
|
197
|
+
document.querySelectorAll('*').forEach(el =>
|
|
198
|
+
el.classList.forEach(generateCSS)
|
|
199
|
+
);
|
|
200
|
+
renderStyles();
|
|
201
|
+
document.documentElement.classList.add('jit-ready');
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
preload();
|
|
205
|
+
|
|
206
|
+
const observer = new MutationObserver((mutations) => {
|
|
207
|
+
let changed = false;
|
|
208
|
+
for (const m of mutations) {
|
|
209
|
+
if (m.type === 'attributes' && m.attributeName === 'class') {
|
|
210
|
+
m.target.classList.forEach(generateCSS);
|
|
211
|
+
changed = true;
|
|
212
|
+
}
|
|
213
|
+
if (m.type === 'childList') {
|
|
214
|
+
m.addedNodes.forEach((node) => {
|
|
215
|
+
if (node.nodeType === 1) {
|
|
216
|
+
node.classList.forEach(generateCSS);
|
|
217
|
+
node.querySelectorAll('*').forEach(el =>
|
|
218
|
+
el.classList.forEach(generateCSS)
|
|
219
|
+
);
|
|
220
|
+
changed = true;
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (changed) renderStyles();
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
observer.observe(document.body, {
|
|
229
|
+
childList: true,
|
|
230
|
+
subtree: true,
|
|
231
|
+
attributes: true,
|
|
232
|
+
attributeFilter: ['class'],
|
|
233
|
+
});
|
|
234
|
+
},
|
|
235
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jit-colors",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A lightweight JIT color and utility class generator for styling.",
|
|
5
|
+
"main": "jit-colors.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"css",
|
|
12
|
+
"jit",
|
|
13
|
+
"colors",
|
|
14
|
+
"utility",
|
|
15
|
+
"styling"
|
|
16
|
+
],
|
|
17
|
+
"author": "",
|
|
18
|
+
"license": "ISC"
|
|
19
|
+
}
|