@debeshghorui/chaitailwind 0.1.0 → 0.1.2
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 +14 -16
- package/dist/index.browser.js +251 -0
- package/dist/index.js +3 -0
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -24,12 +24,23 @@ import { initChai } from "@debeshghorui/chaitailwind";
|
|
|
24
24
|
initChai();
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
## CDN Usage (Global)
|
|
28
|
+
|
|
29
|
+
```html
|
|
30
|
+
<script src="https://cdn.jsdelivr.net/npm/@debeshghorui/chaitailwind@0.1.0/dist/index.browser.js"></script>
|
|
31
|
+
<script>
|
|
32
|
+
window.initchai();
|
|
33
|
+
// Alias also available:
|
|
34
|
+
// window.initChai();
|
|
35
|
+
</script>
|
|
36
|
+
```
|
|
37
|
+
|
|
27
38
|
For local demo usage in this repository:
|
|
28
39
|
|
|
29
40
|
```html
|
|
30
41
|
<script type="module">
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
import { initChai } from "../src/index.js";
|
|
43
|
+
initChai();
|
|
33
44
|
</script>
|
|
34
45
|
```
|
|
35
46
|
|
|
@@ -48,19 +59,6 @@ For local demo usage in this repository:
|
|
|
48
59
|
- Percent shortcut is supported with pct suffix: chai-m-50pct -> margin: 50%
|
|
49
60
|
- Colors support common tokens (red, blue, gray-100, etc.) and hex formats (#fff, #ffffff, hex-ffffff)
|
|
50
61
|
|
|
51
|
-
## Scripts
|
|
52
|
-
|
|
53
|
-
- npm test: runs unit and integration tests with Vitest + jsdom
|
|
54
|
-
- npm run build: bundles src/index.js to dist/index.js using esbuild
|
|
55
|
-
- npm run prepublishOnly: runs test + build before publish
|
|
56
|
-
|
|
57
|
-
## Publish Checklist
|
|
58
|
-
|
|
59
|
-
1. Create and verify your npm account at npmjs.com.
|
|
60
|
-
2. Run npm login.
|
|
61
|
-
3. Run npm test && npm run build.
|
|
62
|
-
4. Publish with npm publish --access public.
|
|
63
|
-
|
|
64
62
|
## License
|
|
65
63
|
|
|
66
|
-
MIT
|
|
64
|
+
MIT
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
// src/utils.js
|
|
3
|
+
var COLOR_MAP = {
|
|
4
|
+
black: "#000000",
|
|
5
|
+
white: "#ffffff",
|
|
6
|
+
gray: "#6b7280",
|
|
7
|
+
"gray-100": "#f3f4f6",
|
|
8
|
+
"gray-500": "#6b7280",
|
|
9
|
+
"gray-900": "#111827",
|
|
10
|
+
red: "#ef4444",
|
|
11
|
+
"red-500": "#ef4444",
|
|
12
|
+
blue: "#3b82f6",
|
|
13
|
+
"blue-500": "#3b82f6",
|
|
14
|
+
green: "#22c55e",
|
|
15
|
+
"green-500": "#22c55e",
|
|
16
|
+
yellow: "#f59e0b"
|
|
17
|
+
};
|
|
18
|
+
var LENGTH_UNITS = ["px", "rem", "em", "%", "vh", "vw", "vmin", "vmax", "pt"];
|
|
19
|
+
function isNumeric(value) {
|
|
20
|
+
return /^-?\d+(\.\d+)?$/.test(value);
|
|
21
|
+
}
|
|
22
|
+
function normalizeLength(value) {
|
|
23
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const trimmed = value.trim().toLowerCase();
|
|
27
|
+
if (isNumeric(trimmed)) {
|
|
28
|
+
return `${trimmed}px`;
|
|
29
|
+
}
|
|
30
|
+
if (/^-?\d+(\.\d+)?pct$/.test(trimmed)) {
|
|
31
|
+
return `${trimmed.replace("pct", "")}%`;
|
|
32
|
+
}
|
|
33
|
+
if (LENGTH_UNITS.some((unit) => trimmed.endsWith(unit))) {
|
|
34
|
+
return trimmed;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
function resolveColor(value) {
|
|
39
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const token = value.trim().toLowerCase();
|
|
43
|
+
if (COLOR_MAP[token]) {
|
|
44
|
+
return COLOR_MAP[token];
|
|
45
|
+
}
|
|
46
|
+
if (/^#[0-9a-f]{3}$|^#[0-9a-f]{6}$/i.test(token)) {
|
|
47
|
+
return token;
|
|
48
|
+
}
|
|
49
|
+
if (/^hex-[0-9a-f]{3}$|^hex-[0-9a-f]{6}$/i.test(token)) {
|
|
50
|
+
return `#${token.slice(4)}`;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
function toKebabCase(property) {
|
|
55
|
+
return property.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/applyStyles.js
|
|
59
|
+
function applyStyles(element, styleMap) {
|
|
60
|
+
if (!element || !styleMap) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
Object.entries(styleMap).forEach(([property, value]) => {
|
|
64
|
+
if (value == null) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
element.style.setProperty(toKebabCase(property), String(value));
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/mapper.js
|
|
72
|
+
function mapUtilityToStyle(parsed) {
|
|
73
|
+
if (!parsed) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const { utility, value } = parsed;
|
|
77
|
+
switch (utility) {
|
|
78
|
+
case "p": {
|
|
79
|
+
const normalized = normalizeLength(value);
|
|
80
|
+
return normalized ? { padding: normalized } : null;
|
|
81
|
+
}
|
|
82
|
+
case "m": {
|
|
83
|
+
const normalized = normalizeLength(value);
|
|
84
|
+
return normalized ? { margin: normalized } : null;
|
|
85
|
+
}
|
|
86
|
+
case "fs": {
|
|
87
|
+
const normalized = normalizeLength(value);
|
|
88
|
+
return normalized ? { fontSize: normalized } : null;
|
|
89
|
+
}
|
|
90
|
+
case "rounded": {
|
|
91
|
+
const normalized = normalizeLength(value);
|
|
92
|
+
return normalized ? { borderRadius: normalized } : null;
|
|
93
|
+
}
|
|
94
|
+
case "bg": {
|
|
95
|
+
const color = resolveColor(value);
|
|
96
|
+
return color ? { backgroundColor: color } : null;
|
|
97
|
+
}
|
|
98
|
+
case "text": {
|
|
99
|
+
const color = resolveColor(value);
|
|
100
|
+
return color ? { color } : null;
|
|
101
|
+
}
|
|
102
|
+
case "border": {
|
|
103
|
+
const borderWidth = normalizeLength(value);
|
|
104
|
+
if (borderWidth) {
|
|
105
|
+
return { border: `${borderWidth} solid #000000` };
|
|
106
|
+
}
|
|
107
|
+
const borderColor = resolveColor(value);
|
|
108
|
+
if (borderColor) {
|
|
109
|
+
return { border: `1px solid ${borderColor}` };
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
case "center":
|
|
114
|
+
return { textAlign: "center" };
|
|
115
|
+
case "flex":
|
|
116
|
+
return { display: "flex" };
|
|
117
|
+
case "justify-center":
|
|
118
|
+
return { justifyContent: "center" };
|
|
119
|
+
case "items-center":
|
|
120
|
+
return { alignItems: "center" };
|
|
121
|
+
default:
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/parser.js
|
|
127
|
+
var STATIC_UTILITIES = /* @__PURE__ */ new Set([
|
|
128
|
+
"center",
|
|
129
|
+
"flex",
|
|
130
|
+
"justify-center",
|
|
131
|
+
"items-center"
|
|
132
|
+
]);
|
|
133
|
+
var VALUE_UTILITIES = /* @__PURE__ */ new Set([
|
|
134
|
+
"p",
|
|
135
|
+
"m",
|
|
136
|
+
"bg",
|
|
137
|
+
"text",
|
|
138
|
+
"fs",
|
|
139
|
+
"border",
|
|
140
|
+
"rounded"
|
|
141
|
+
]);
|
|
142
|
+
function parseChaiClass(className) {
|
|
143
|
+
if (!className || !className.startsWith("chai-")) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const payload = className.slice(5);
|
|
147
|
+
if (!payload) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
if (STATIC_UTILITIES.has(payload)) {
|
|
151
|
+
return {
|
|
152
|
+
raw: className,
|
|
153
|
+
utility: payload,
|
|
154
|
+
value: null
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const [utility, ...rest] = payload.split("-");
|
|
158
|
+
if (!VALUE_UTILITIES.has(utility) || rest.length === 0) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const value = rest.join("-");
|
|
162
|
+
if (!value) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
raw: className,
|
|
167
|
+
utility,
|
|
168
|
+
value
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function extractChaiClasses(classList) {
|
|
172
|
+
return Array.from(classList).filter((name) => name.startsWith("chai-"));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// src/scanner.js
|
|
176
|
+
function applyClassStyles(element) {
|
|
177
|
+
const chaiClasses = extractChaiClasses(element.classList);
|
|
178
|
+
const appliedClasses = [];
|
|
179
|
+
chaiClasses.forEach((className) => {
|
|
180
|
+
const parsed = parseChaiClass(className);
|
|
181
|
+
if (!parsed) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const styleMap = mapUtilityToStyle(parsed);
|
|
185
|
+
if (!styleMap) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
applyStyles(element, styleMap);
|
|
189
|
+
appliedClasses.push(className);
|
|
190
|
+
});
|
|
191
|
+
appliedClasses.forEach((cls) => element.classList.remove(cls));
|
|
192
|
+
}
|
|
193
|
+
function scanDOM(root = document) {
|
|
194
|
+
if (!root || !root.querySelectorAll) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const nodes = root.querySelectorAll("[class]");
|
|
198
|
+
nodes.forEach((node) => applyClassStyles(node));
|
|
199
|
+
}
|
|
200
|
+
function scanElement(element) {
|
|
201
|
+
if (!element || !element.classList) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
applyClassStyles(element);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// src/index.js
|
|
208
|
+
var observer = null;
|
|
209
|
+
function initChai(options = {}) {
|
|
210
|
+
const { root = document, observe = false } = options;
|
|
211
|
+
scanDOM(root);
|
|
212
|
+
if (observe && root && root.body) {
|
|
213
|
+
observer = new MutationObserver((mutations) => {
|
|
214
|
+
mutations.forEach((mutation) => {
|
|
215
|
+
if (mutation.type !== "childList") {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
mutation.addedNodes.forEach((node) => {
|
|
219
|
+
if (!(node instanceof Element)) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
scanElement(node);
|
|
223
|
+
if (node.querySelectorAll) {
|
|
224
|
+
scanDOM(node);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
observer.observe(root.body, {
|
|
230
|
+
childList: true,
|
|
231
|
+
subtree: true
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function stopChaiObserver() {
|
|
236
|
+
if (!observer) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
observer.disconnect();
|
|
240
|
+
observer = null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/index.browser.js
|
|
244
|
+
var globalTarget = typeof window !== "undefined" ? window : typeof globalThis !== "undefined" ? globalThis : null;
|
|
245
|
+
if (globalTarget) {
|
|
246
|
+
globalTarget.initChai = initChai;
|
|
247
|
+
globalTarget.initchai = initChai;
|
|
248
|
+
globalTarget.stopChaiObserver = stopChaiObserver;
|
|
249
|
+
globalTarget.stopchaiobserver = stopChaiObserver;
|
|
250
|
+
}
|
|
251
|
+
})();
|
package/dist/index.js
CHANGED
|
@@ -174,6 +174,7 @@ function extractChaiClasses(classList) {
|
|
|
174
174
|
// src/scanner.js
|
|
175
175
|
function applyClassStyles(element) {
|
|
176
176
|
const chaiClasses = extractChaiClasses(element.classList);
|
|
177
|
+
const appliedClasses = [];
|
|
177
178
|
chaiClasses.forEach((className) => {
|
|
178
179
|
const parsed = parseChaiClass(className);
|
|
179
180
|
if (!parsed) {
|
|
@@ -184,7 +185,9 @@ function applyClassStyles(element) {
|
|
|
184
185
|
return;
|
|
185
186
|
}
|
|
186
187
|
applyStyles(element, styleMap);
|
|
188
|
+
appliedClasses.push(className);
|
|
187
189
|
});
|
|
190
|
+
appliedClasses.forEach((cls) => element.classList.remove(cls));
|
|
188
191
|
}
|
|
189
192
|
function scanDOM(root = document) {
|
|
190
193
|
if (!root || !root.querySelectorAll) {
|
package/package.json
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@debeshghorui/chaitailwind",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "A lightweight utility-first CSS engine using chai-* classes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.js",
|
|
8
|
+
"browser": "dist/index.browser.js",
|
|
9
|
+
"unpkg": "dist/index.browser.js",
|
|
10
|
+
"jsdelivr": "dist/index.browser.js",
|
|
8
11
|
"exports": {
|
|
9
12
|
".": {
|
|
10
13
|
"import": "./dist/index.js"
|
|
11
|
-
}
|
|
14
|
+
},
|
|
15
|
+
"./browser": "./dist/index.browser.js"
|
|
12
16
|
},
|
|
13
17
|
"files": [
|
|
14
18
|
"dist",
|
|
@@ -18,7 +22,9 @@
|
|
|
18
22
|
"scripts": {
|
|
19
23
|
"test": "vitest run --environment jsdom",
|
|
20
24
|
"test:watch": "vitest --environment jsdom",
|
|
21
|
-
"build": "
|
|
25
|
+
"build": "npm run build:esm && npm run build:browser",
|
|
26
|
+
"build:esm": "esbuild src/index.js --bundle --format=esm --outfile=dist/index.js",
|
|
27
|
+
"build:browser": "esbuild src/index.browser.js --bundle --format=iife --outfile=dist/index.browser.js",
|
|
22
28
|
"prepublishOnly": "npm run test && npm run build"
|
|
23
29
|
},
|
|
24
30
|
"keywords": [
|