chaiwind 2.1.1 → 3.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/src/engine.js ADDED
@@ -0,0 +1,144 @@
1
+ /**
2
+ * engine.js
3
+ * The core of chaiwind — 3 responsibilities:
4
+ *
5
+ * 1. parseClass(cls) — breaks 'chai-bg-piyush' into { utility, value }
6
+ * 2. applyClass(el, cls) — applies inline style + removes the class
7
+ * 3. init() — scans DOM on load + watches live with MutationObserver
8
+ */
9
+
10
+ import { handlers } from "./handlers.js";
11
+
12
+ // ─── 1. PARSER ────────────────────────────────────────────────────────────────
13
+ // Input: 'chai-bg-piyush-dark'
14
+ // Output: { utility: 'bg', value: 'piyush-dark' }
15
+ //
16
+ // Steps:
17
+ // - slice(5) removes the 'chai-' prefix → 'bg-piyush-dark'
18
+ // - indexOf('-') finds the first dash → index 2
19
+ // - slice(0, 2) gets the utility → 'bg'
20
+ // - slice(3) gets the value → 'piyush-dark'
21
+ //
22
+ // Edge case: 'chai-flex' has no value
23
+ // - indexOf('-') returns -1
24
+ // - utility = 'flex', value = ''
25
+
26
+ function parseClass(cls) {
27
+ const raw = cls.slice(5); // strip 'chai-'
28
+ const dashIdx = raw.indexOf("-"); // find first dash
29
+
30
+ const utility = dashIdx === -1 ? raw : raw.slice(0, dashIdx);
31
+ const value = dashIdx === -1 ? "" : raw.slice(dashIdx + 1);
32
+
33
+ return { utility, value };
34
+ }
35
+
36
+ // ─── 2. APPLY CLASS ───────────────────────────────────────────────────────────
37
+ // Takes one element and one class name
38
+ // Looks up the handler, calls it, removes the class
39
+
40
+ function applyClass(el, cls) {
41
+ const { utility, value } = parseClass(cls);
42
+
43
+ // look up the handler for this utility
44
+ const handler = handlers[utility];
45
+
46
+ if (handler) {
47
+ handler(el, value); // e.g. handlers['bg'](el, 'piyush')
48
+ }
49
+ // unknown utility — silently skip, just remove the class
50
+
51
+ // requirement: always remove chai- class after processing
52
+ el.classList.remove(cls);
53
+ }
54
+
55
+ // ─── 3. PROCESS ELEMENT ───────────────────────────────────────────────────────
56
+ // Takes one element, finds all chai- classes on it, applies each one
57
+ // We snapshot classList into an Array first — important!
58
+ // Because we're removing classes inside the loop,
59
+ // iterating el.classList live would skip some classes
60
+
61
+ function processElement(el) {
62
+ // only process actual HTML elements, skip text nodes / comment nodes
63
+ if (el.nodeType !== 1) return;
64
+
65
+ // snapshot into array so removals don't affect iteration
66
+ const classes = Array.from(el.classList);
67
+
68
+ classes.forEach((cls) => {
69
+ if (cls.startsWith("chai-")) {
70
+ applyClass(el, cls);
71
+ }
72
+ });
73
+ }
74
+
75
+ // ─── 4. SCAN ──────────────────────────────────────────────────────────────────
76
+ // Runs once — traverses the entire DOM
77
+ // Finds every element that has at least one chai- class
78
+
79
+ function scan() {
80
+ // querySelectorAll('[class]') — grabs every element that has a class attribute
81
+ // this is faster than grabbing ALL elements and checking each one
82
+ const elements = document.querySelectorAll("[class]");
83
+ elements.forEach((el) => processElement(el));
84
+ }
85
+
86
+ // ─── 5. MUTATIONOBSERVER ──────────────────────────────────────────────────────
87
+ // Watches the DOM live — any element added after page load also gets processed
88
+ // This handles: JS frameworks, dynamic content, setTimeout additions etc.
89
+ //
90
+ // mutation.addedNodes = list of nodes added in this mutation
91
+ // We process each added node + all its children (querySelectorAll inside it)
92
+
93
+ function watch() {
94
+ const observer = new MutationObserver((mutations) => {
95
+ for (const mutation of mutations) {
96
+ for (const node of mutation.addedNodes) {
97
+ // skip text nodes, comment nodes — only process elements
98
+ if (node.nodeType !== 1) continue;
99
+
100
+ // process the element itself
101
+ processElement(node);
102
+
103
+ // process any children inside it too
104
+ // e.g. a whole section added at once — scan everything inside
105
+ node
106
+ .querySelectorAll("[class]")
107
+ .forEach((child) => processElement(child));
108
+ }
109
+ }
110
+ });
111
+
112
+ // observe the entire document body
113
+ // childList: true → watch for elements being added or removed
114
+ // subtree: true → watch ALL descendants, not just direct children
115
+ observer.observe(document.body, {
116
+ childList: true,
117
+ subtree: true,
118
+ });
119
+ }
120
+
121
+ // ─── 6. INIT ──────────────────────────────────────────────────────────────────
122
+ // Called from index.js
123
+ // Handles two cases:
124
+ // - script in <head> → HTML not parsed yet → wait for DOMContentLoaded
125
+ // - script at <body> → HTML already parsed → run immediately
126
+
127
+ export function init() {
128
+ if (document.readyState === "loading") {
129
+ // DOM not ready yet — wait
130
+ document.addEventListener("DOMContentLoaded", () => {
131
+ scan();
132
+ watch();
133
+ });
134
+ } else {
135
+ // DOM already ready — run now
136
+ scan();
137
+ watch();
138
+ }
139
+
140
+ console.log(
141
+ "%c☕ chaiwind ready",
142
+ "color: #c8843a; font-weight: bold; font-size: 13px;",
143
+ );
144
+ }
@@ -0,0 +1,218 @@
1
+ /**
2
+ * handlers.js
3
+ * Maps every chai-* utility to a function that sets el.style directly
4
+ *
5
+ * Structure:
6
+ * 'utility': (el, value) => el.style.someProperty = resolvedValue
7
+ *
8
+ * How it's used in engine.js:
9
+ * handlers['bg'](el, 'piyush') → el.style.backgroundColor = '#ec4899'
10
+ */
11
+
12
+ import { colors, spacing, fontSizes, radii, shadows } from "./tokens.js";
13
+
14
+ export const handlers = {
15
+ // ── padding ───────────────────────────────────────────────────────────────
16
+ p: (el, v) => (el.style.padding = spacing[v] || v + "px"),
17
+ px: (el, v) => {
18
+ el.style.paddingLeft = spacing[v] || v + "px";
19
+ el.style.paddingRight = spacing[v] || v + "px";
20
+ },
21
+ py: (el, v) => {
22
+ el.style.paddingTop = spacing[v] || v + "px";
23
+ el.style.paddingBottom = spacing[v] || v + "px";
24
+ },
25
+ pt: (el, v) => (el.style.paddingTop = spacing[v] || v + "px"),
26
+ pb: (el, v) => (el.style.paddingBottom = spacing[v] || v + "px"),
27
+ pl: (el, v) => (el.style.paddingLeft = spacing[v] || v + "px"),
28
+ pr: (el, v) => (el.style.paddingRight = spacing[v] || v + "px"),
29
+
30
+ // ── margin ────────────────────────────────────────────────────────────────
31
+ m: (el, v) => (el.style.margin = spacing[v] || v + "px"),
32
+ mx: (el, v) => {
33
+ el.style.marginLeft = spacing[v] || v + "px";
34
+ el.style.marginRight = spacing[v] || v + "px";
35
+ },
36
+ my: (el, v) => {
37
+ el.style.marginTop = spacing[v] || v + "px";
38
+ el.style.marginBottom = spacing[v] || v + "px";
39
+ },
40
+ mt: (el, v) => (el.style.marginTop = spacing[v] || v + "px"),
41
+ mb: (el, v) => (el.style.marginBottom = spacing[v] || v + "px"),
42
+ ml: (el, v) => (el.style.marginLeft = spacing[v] || v + "px"),
43
+ mr: (el, v) => (el.style.marginRight = spacing[v] || v + "px"),
44
+
45
+ // ── gap ───────────────────────────────────────────────────────────────────
46
+ gap: (el, v) => (el.style.gap = spacing[v] || v + "px"),
47
+
48
+ // ── colors ────────────────────────────────────────────────────────────────
49
+ // chai-bg-chai, chai-bg-red, chai-bg-#ff0000
50
+ bg: (el, v) => (el.style.backgroundColor = colors[v] || v),
51
+ border: (el, v) => {
52
+ // chai-border → just adds a solid border
53
+ // chai-border-chai → solid border with chai color
54
+ if (!v) {
55
+ el.style.border = "1px solid currentColor";
56
+ } else {
57
+ el.style.border = "1px solid " + (colors[v] || v);
58
+ }
59
+ },
60
+
61
+ // ── text — handles 3 things: color, size, alignment ──────────────────────
62
+ // chai-text-chai → color
63
+ // chai-text-xl → font size
64
+ // chai-text-center → text align
65
+ text: (el, v) => {
66
+ if (colors[v]) el.style.color = colors[v];
67
+ else if (fontSizes[v]) el.style.fontSize = fontSizes[v];
68
+ else el.style.textAlign = v;
69
+ },
70
+
71
+ // ── font ──────────────────────────────────────────────────────────────────
72
+ font: (el, v) => {
73
+ const weightMap = {
74
+ thin: "100",
75
+ light: "300",
76
+ normal: "400",
77
+ medium: "500",
78
+ semibold: "600",
79
+ bold: "700",
80
+ black: "900",
81
+ };
82
+ if (weightMap[v]) el.style.fontWeight = weightMap[v];
83
+ },
84
+
85
+ // ── border radius ─────────────────────────────────────────────────────────
86
+ // chai-rounded-md, chai-rounded-full, chai-rounded-20 (raw px)
87
+ rounded: (el, v) => (el.style.borderRadius = radii[v] || v + "px"),
88
+
89
+ // ── shadow ────────────────────────────────────────────────────────────────
90
+ shadow: (el, v) => (el.style.boxShadow = shadows[v] || v),
91
+
92
+ // ── opacity ───────────────────────────────────────────────────────────────
93
+ // chai-opacity-50 → 0.5
94
+ opacity: (el, v) => (el.style.opacity = String(Number(v) / 100)),
95
+
96
+ // ── sizing ────────────────────────────────────────────────────────────────
97
+ w: (el, v) => {
98
+ if (v === "full") el.style.width = "100%";
99
+ else if (v === "screen") el.style.width = "100vw";
100
+ else if (v === "auto") el.style.width = "auto";
101
+ else el.style.width = spacing[v] || v + "px";
102
+ },
103
+ h: (el, v) => {
104
+ if (v === "full") el.style.height = "100%";
105
+ else if (v === "screen") el.style.height = "100vh";
106
+ else if (v === "auto") el.style.height = "auto";
107
+ else el.style.height = spacing[v] || v + "px";
108
+ },
109
+ "max-w": (el, v) => (el.style.maxWidth = spacing[v] || v + "px"),
110
+ "min-h": (el, v) =>
111
+ (el.style.minHeight = v === "screen" ? "100vh" : spacing[v] || v + "px"),
112
+ "min-w": (el, v) =>
113
+ (el.style.minWidth = v === "full" ? "100%" : spacing[v] || v + "px"),
114
+
115
+ // ── display ───────────────────────────────────────────────────────────────
116
+ flex: (el) => (el.style.display = "flex"),
117
+ grid: (el) => (el.style.display = "grid"),
118
+ block: (el) => (el.style.display = "block"),
119
+ "inline-block": (el) => (el.style.display = "inline-block"),
120
+ inline: (el) => (el.style.display = "inline"),
121
+ hidden: (el) => (el.style.display = "none"),
122
+
123
+ // ── flexbox ───────────────────────────────────────────────────────────────
124
+ "flex-col": (el) => (el.style.flexDirection = "column"),
125
+ "flex-row": (el) => (el.style.flexDirection = "row"),
126
+ "flex-wrap": (el) => (el.style.flexWrap = "wrap"),
127
+ "flex-1": (el) => (el.style.flex = "1"),
128
+ items: (el, v) => {
129
+ const map = {
130
+ center: "center",
131
+ start: "flex-start",
132
+ end: "flex-end",
133
+ stretch: "stretch",
134
+ };
135
+ el.style.alignItems = map[v] || v;
136
+ },
137
+ justify: (el, v) => {
138
+ const map = {
139
+ center: "center",
140
+ start: "flex-start",
141
+ end: "flex-end",
142
+ between: "space-between",
143
+ around: "space-around",
144
+ evenly: "space-evenly",
145
+ };
146
+ el.style.justifyContent = map[v] || v;
147
+ },
148
+
149
+ // ── grid ──────────────────────────────────────────────────────────────────
150
+ "grid-cols": (el, v) => (el.style.gridTemplateColumns = `repeat(${v}, 1fr)`),
151
+ "col-span": (el, v) => (el.style.gridColumn = `span ${v}`),
152
+
153
+ // ── position ──────────────────────────────────────────────────────────────
154
+ relative: (el) => (el.style.position = "relative"),
155
+ absolute: (el) => (el.style.position = "absolute"),
156
+ fixed: (el) => (el.style.position = "fixed"),
157
+ sticky: (el) => {
158
+ el.style.position = "sticky";
159
+ el.style.top = "0";
160
+ },
161
+
162
+ // ── overflow ──────────────────────────────────────────────────────────────
163
+ overflow: (el, v) => (el.style.overflow = v || "hidden"),
164
+ "overflow-x": (el, v) => (el.style.overflowX = v || "auto"),
165
+ "overflow-y": (el, v) => (el.style.overflowY = v || "auto"),
166
+
167
+ // ── z-index ───────────────────────────────────────────────────────────────
168
+ z: (el, v) => (el.style.zIndex = v),
169
+
170
+ // ── typography ────────────────────────────────────────────────────────────
171
+ uppercase: (el) => (el.style.textTransform = "uppercase"),
172
+ lowercase: (el) => (el.style.textTransform = "lowercase"),
173
+ capitalize: (el) => (el.style.textTransform = "capitalize"),
174
+ italic: (el) => (el.style.fontStyle = "italic"),
175
+ underline: (el) => (el.style.textDecoration = "underline"),
176
+ "line-through": (el) => (el.style.textDecoration = "line-through"),
177
+ "no-underline": (el) => (el.style.textDecoration = "none"),
178
+ truncate: (el) => {
179
+ el.style.overflow = "hidden";
180
+ el.style.textOverflow = "ellipsis";
181
+ el.style.whiteSpace = "nowrap";
182
+ },
183
+ leading: (el, v) => {
184
+ const map = {
185
+ none: "1",
186
+ tight: "1.25",
187
+ snug: "1.375",
188
+ normal: "1.5",
189
+ relaxed: "1.625",
190
+ loose: "2",
191
+ };
192
+ el.style.lineHeight = map[v] || v;
193
+ },
194
+ tracking: (el, v) => {
195
+ const map = {
196
+ tight: "-0.05em",
197
+ normal: "0",
198
+ wide: "0.05em",
199
+ wider: "0.1em",
200
+ widest: "0.2em",
201
+ };
202
+ el.style.letterSpacing = map[v] || v;
203
+ },
204
+
205
+ // ── cursor ────────────────────────────────────────────────────────────────
206
+ cursor: (el, v) => (el.style.cursor = v),
207
+
208
+ // ── misc ──────────────────────────────────────────────────────────────────
209
+ transition: (el, v) =>
210
+ (el.style.transition = v === "none" ? "none" : "all 150ms ease"),
211
+ "select-none": (el) => (el.style.userSelect = "none"),
212
+ "object-cover": (el) => (el.style.objectFit = "cover"),
213
+ "object-contain": (el) => (el.style.objectFit = "contain"),
214
+ "mx-auto": (el) => {
215
+ el.style.marginLeft = "auto";
216
+ el.style.marginRight = "auto";
217
+ },
218
+ };
package/src/index.js ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * index.js
3
+ * Entry point for chaiwind
4
+ *
5
+ * Usage with a bundler (Vite, webpack):
6
+ * import { initChai } from 'chaiwind'
7
+ * initChai()
8
+ *
9
+ * Usage in plain HTML:
10
+ * <script type="module">
11
+ * import { initChai } from './node_modules/chaiwind/src/index.js'
12
+ * initChai()
13
+ * </script>
14
+ */
15
+
16
+ import { init } from "./engine.js";
17
+
18
+ export function initChai() {
19
+ init();
20
+ }
package/src/tokens.js ADDED
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Hitesh sir — chai, adrak, masala, kulhad, tapri, dudh
3
+ * Piyush sir — piyush, rose, blush, fuschia, lipstick
4
+ * Akash sir — midnight, spacegray, silver, starlight, macos-*
5
+ * ChaiCode — chaicode, chaicode-dark
6
+ */
7
+
8
+ export const colors = {
9
+ // ── ChaiCode brand ────────────────────────────────────────────────────────
10
+ chaicode: "#f97316",
11
+ "chaicode-dark": "#1a1a2e",
12
+
13
+ // ── ☕ Hitesh sir — chai palette ──────────────────────────────────────────
14
+ chai: "#c8843a", // classic cutting chai
15
+ adrak: "#d4a056", // ginger gold
16
+ masala: "#8b4513", // deep masala brown
17
+ kulhad: "#b5651d", // clay kulhad
18
+ tapri: "#6b3a2a", // dark roasted tapri
19
+ dudh: "#f5f0e8", // milk chai cream
20
+
21
+ // ── 🍵 Tea Collection ─────────────────────────────────────────────────────
22
+ "green-tea": "#7fbf7f",
23
+ "chamomile-tea": "#f5e6a6",
24
+ "black-tea": "#3b2f2f",
25
+ "herbal-tea": "#a3c585",
26
+ "matcha-tea": "#6ea64d",
27
+ "white-tea": "#f2f2f2",
28
+ "masala-chai": "#c68642",
29
+ "darjeeling-tea": "#d4a373",
30
+ "irani-chai": "#b08968",
31
+ "oolong-tea": "#a47148",
32
+ "lemongrass-tea": "#c9e4a7",
33
+ "tandoori-chai": "#b7410e",
34
+ "chai-tea": "#8b5e3c",
35
+ "peach-tea": "#ffb07c",
36
+ "mate-tea": "#6b8e23",
37
+ "rooibos-tea": "#b22222",
38
+ "nilgiri-tea": "#5c4033",
39
+ "dark-tea": "#1c1c1c",
40
+
41
+ // ── 🩷 Piyush sir — pink palette ──────────────────────────────────────────
42
+ piyush: "#ec4899", // signature hot pink
43
+ "piyush-light": "#f9a8d4", // soft blush
44
+ "piyush-dark": "#be185d", // deep magenta
45
+ rose: "#fb7185", // rose
46
+ blush: "#fce7f3", // barely-there blush
47
+ fuschia: "#d946ef", // electric fuschia
48
+ lipstick: "#c2185b", // bold lipstick
49
+
50
+ // ── 🍎 Akash sir — Apple / Mac palette ────────────────────────────────────
51
+ midnight: "#1d1d1f", // Apple midnight black
52
+ spacegray: "#86868b", // Mac space gray
53
+ silver: "#e8e8ed", // Mac silver
54
+ starlight: "#f5f1eb", // MacBook starlight
55
+ "macos-blue": "#0071e3", // Apple blue
56
+ "macos-green": "#34c759", // Apple green
57
+ "macos-red": "#ff3b30", // Apple red
58
+ aluminum: "#d1d1d6", // aluminum body
59
+
60
+ // ── general colors ────────────────────────────────────────────────────────
61
+ white: "#ffffff",
62
+ black: "#000000",
63
+ transparent: "transparent",
64
+ red: "#ef4444",
65
+ orange: "#f97316",
66
+ yellow: "#eab308",
67
+ green: "#22c55e",
68
+ blue: "#3b82f6",
69
+ purple: "#a855f7",
70
+ pink: "#ec4899",
71
+ teal: "#14b8a6",
72
+ indigo: "#6366f1",
73
+ gray: "#6b7280",
74
+
75
+ // ── gray scale ────────────────────────────────────────────────────────────
76
+ "gray-50": "#f9fafb",
77
+ "gray-100": "#f3f4f6",
78
+ "gray-200": "#e5e7eb",
79
+ "gray-300": "#d1d5db",
80
+ "gray-400": "#9ca3af",
81
+ "gray-500": "#6b7280",
82
+ "gray-600": "#4b5563",
83
+ "gray-700": "#374151",
84
+ "gray-800": "#1f2937",
85
+ "gray-900": "#111827",
86
+ };
87
+
88
+ export const spacing = {
89
+ 0: "0px",
90
+ 1: "4px",
91
+ 2: "8px",
92
+ 3: "12px",
93
+ 4: "16px",
94
+ 5: "20px",
95
+ 6: "24px",
96
+ 7: "28px",
97
+ 8: "32px",
98
+ 9: "36px",
99
+ 10: "40px",
100
+ 12: "48px",
101
+ 14: "56px",
102
+ 16: "64px",
103
+ 20: "80px",
104
+ 24: "96px",
105
+ 32: "128px",
106
+ };
107
+
108
+ export const fontSizes = {
109
+ xs: "11px",
110
+ sm: "13px",
111
+ base: "16px",
112
+ lg: "18px",
113
+ xl: "20px",
114
+ "2xl": "24px",
115
+ "3xl": "30px",
116
+ "4xl": "36px",
117
+ "5xl": "48px",
118
+ "6xl": "64px",
119
+ };
120
+
121
+ export const radii = {
122
+ none: "0px",
123
+ sm: "4px",
124
+ md: "8px",
125
+ lg: "12px",
126
+ xl: "16px",
127
+ "2xl": "24px",
128
+ full: "9999px",
129
+ };
130
+
131
+ export const shadows = {
132
+ sm: "0 1px 3px rgba(0,0,0,0.10)",
133
+ md: "0 4px 12px rgba(0,0,0,0.12)",
134
+ lg: "0 8px 24px rgba(0,0,0,0.15)",
135
+ xl: "0 16px 48px rgba(0,0,0,0.20)",
136
+ none: "none",
137
+ // ChaiCode special shadows
138
+ chai: "0 4px 20px rgba(200,132,58,0.35)",
139
+ piyush: "0 4px 20px rgba(236,72,153,0.30)",
140
+ mac: "0 8px 32px rgba(29,29,31,0.25)",
141
+ };