@debeshghorui/chaitailwind 0.1.0 → 0.1.1
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 +248 -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,248 @@
|
|
|
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
|
+
chaiClasses.forEach((className) => {
|
|
179
|
+
const parsed = parseChaiClass(className);
|
|
180
|
+
if (!parsed) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const styleMap = mapUtilityToStyle(parsed);
|
|
184
|
+
if (!styleMap) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
applyStyles(element, styleMap);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
function scanDOM(root = document) {
|
|
191
|
+
if (!root || !root.querySelectorAll) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const nodes = root.querySelectorAll("[class]");
|
|
195
|
+
nodes.forEach((node) => applyClassStyles(node));
|
|
196
|
+
}
|
|
197
|
+
function scanElement(element) {
|
|
198
|
+
if (!element || !element.classList) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
applyClassStyles(element);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/index.js
|
|
205
|
+
var observer = null;
|
|
206
|
+
function initChai(options = {}) {
|
|
207
|
+
const { root = document, observe = false } = options;
|
|
208
|
+
scanDOM(root);
|
|
209
|
+
if (observe && root && root.body) {
|
|
210
|
+
observer = new MutationObserver((mutations) => {
|
|
211
|
+
mutations.forEach((mutation) => {
|
|
212
|
+
if (mutation.type !== "childList") {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
mutation.addedNodes.forEach((node) => {
|
|
216
|
+
if (!(node instanceof Element)) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
scanElement(node);
|
|
220
|
+
if (node.querySelectorAll) {
|
|
221
|
+
scanDOM(node);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
observer.observe(root.body, {
|
|
227
|
+
childList: true,
|
|
228
|
+
subtree: true
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function stopChaiObserver() {
|
|
233
|
+
if (!observer) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
observer.disconnect();
|
|
237
|
+
observer = null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// src/index.browser.js
|
|
241
|
+
var globalTarget = typeof window !== "undefined" ? window : typeof globalThis !== "undefined" ? globalThis : null;
|
|
242
|
+
if (globalTarget) {
|
|
243
|
+
globalTarget.initChai = initChai;
|
|
244
|
+
globalTarget.initchai = initChai;
|
|
245
|
+
globalTarget.stopChaiObserver = stopChaiObserver;
|
|
246
|
+
globalTarget.stopchaiobserver = stopChaiObserver;
|
|
247
|
+
}
|
|
248
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@debeshghorui/chaitailwind",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
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": [
|