@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 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
- import { initChai } from "../src/index.js";
32
- initChai();
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.0",
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": "esbuild src/index.js --bundle --format=esm --outfile=dist/index.js",
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": [