@bizrk/leancss 0.1.4 → 0.2.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/dist/index.js CHANGED
@@ -29,6 +29,58 @@ var leancss = (opts = {}) => {
29
29
  postcssPlugin: "leancss",
30
30
  Once(root, { result }) {
31
31
  const sets = /* @__PURE__ */ new Map();
32
+ const mediaThresholds = /* @__PURE__ */ new Map([
33
+ ["sm", "640px"],
34
+ ["md", "768px"],
35
+ ["lg", "1024px"],
36
+ ["xl", "1280px"],
37
+ ["2xl", "1536px"]
38
+ ]);
39
+ const containerThresholds = /* @__PURE__ */ new Map([
40
+ ["sm", "24rem"],
41
+ ["md", "36rem"],
42
+ ["lg", "48rem"],
43
+ ["xl", "64rem"],
44
+ ["2xl", "80rem"]
45
+ ]);
46
+ const seenMediaTokens = /* @__PURE__ */ new Set();
47
+ const seenContainerTokens = /* @__PURE__ */ new Set();
48
+ const lengthRegex = /^[+-]?(\d+|\d*\.\d+)(px|rem|em|vh|vw|vmin|vmax|ch|ex|cm|mm|in|pt|pc)$/i;
49
+ root.walkRules(":root", (rule) => {
50
+ rule.walkDecls((decl) => {
51
+ const prop = decl.prop;
52
+ const val = decl.value.trim();
53
+ if (prop.startsWith("--media-")) {
54
+ const key = prop.replace("--media-", "");
55
+ if (mediaThresholds.has(key)) {
56
+ if (seenMediaTokens.has(key)) {
57
+ decl.warn(result, `LeanCSS warning: Multiple values detected for ${prop} in :root. Using the first valid value in source order: ${mediaThresholds.get(key)}.`);
58
+ } else {
59
+ if (lengthRegex.test(val)) {
60
+ mediaThresholds.set(key, val);
61
+ seenMediaTokens.add(key);
62
+ } else {
63
+ decl.warn(result, `LeanCSS warning: Invalid value for ${prop}: ${val}. Falling back to default: ${mediaThresholds.get(key)}.`);
64
+ }
65
+ }
66
+ }
67
+ } else if (prop.startsWith("--container-")) {
68
+ const key = prop.replace("--container-", "");
69
+ if (containerThresholds.has(key)) {
70
+ if (seenContainerTokens.has(key)) {
71
+ decl.warn(result, `LeanCSS warning: Multiple values detected for ${prop} in :root. Using the first valid value in source order: ${containerThresholds.get(key)}.`);
72
+ } else {
73
+ if (lengthRegex.test(val)) {
74
+ containerThresholds.set(key, val);
75
+ seenContainerTokens.add(key);
76
+ } else {
77
+ decl.warn(result, `LeanCSS warning: Invalid value for ${prop}: ${val}. Falling back to default: ${containerThresholds.get(key)}.`);
78
+ }
79
+ }
80
+ }
81
+ }
82
+ });
83
+ });
32
84
  root.walkAtRules(/^(set|drop)$/, (atRule) => {
33
85
  const name = atRule.params.trim();
34
86
  if (!name) {
@@ -142,6 +194,20 @@ var leancss = (opts = {}) => {
142
194
  atRule.remove();
143
195
  }
144
196
  });
197
+ root.walkAtRules((atRule) => {
198
+ if (["sm", "md", "lg", "xl", "2xl"].includes(atRule.name)) {
199
+ const width = mediaThresholds.get(atRule.name);
200
+ atRule.name = "media";
201
+ atRule.params = `(width >= ${width})`;
202
+ if (atRule.raws) atRule.raws.afterName = " ";
203
+ } else if (["c-sm", "c-md", "c-lg", "c-xl", "c-2xl"].includes(atRule.name)) {
204
+ const key = atRule.name.substring(2);
205
+ const width = containerThresholds.get(key);
206
+ atRule.name = "container";
207
+ atRule.params = `(width >= ${width})`;
208
+ if (atRule.raws) atRule.raws.afterName = " ";
209
+ }
210
+ });
145
211
  }
146
212
  };
147
213
  };
package/dist/index.mjs CHANGED
@@ -7,6 +7,58 @@ var leancss = (opts = {}) => {
7
7
  postcssPlugin: "leancss",
8
8
  Once(root, { result }) {
9
9
  const sets = /* @__PURE__ */ new Map();
10
+ const mediaThresholds = /* @__PURE__ */ new Map([
11
+ ["sm", "640px"],
12
+ ["md", "768px"],
13
+ ["lg", "1024px"],
14
+ ["xl", "1280px"],
15
+ ["2xl", "1536px"]
16
+ ]);
17
+ const containerThresholds = /* @__PURE__ */ new Map([
18
+ ["sm", "24rem"],
19
+ ["md", "36rem"],
20
+ ["lg", "48rem"],
21
+ ["xl", "64rem"],
22
+ ["2xl", "80rem"]
23
+ ]);
24
+ const seenMediaTokens = /* @__PURE__ */ new Set();
25
+ const seenContainerTokens = /* @__PURE__ */ new Set();
26
+ const lengthRegex = /^[+-]?(\d+|\d*\.\d+)(px|rem|em|vh|vw|vmin|vmax|ch|ex|cm|mm|in|pt|pc)$/i;
27
+ root.walkRules(":root", (rule) => {
28
+ rule.walkDecls((decl) => {
29
+ const prop = decl.prop;
30
+ const val = decl.value.trim();
31
+ if (prop.startsWith("--media-")) {
32
+ const key = prop.replace("--media-", "");
33
+ if (mediaThresholds.has(key)) {
34
+ if (seenMediaTokens.has(key)) {
35
+ decl.warn(result, `LeanCSS warning: Multiple values detected for ${prop} in :root. Using the first valid value in source order: ${mediaThresholds.get(key)}.`);
36
+ } else {
37
+ if (lengthRegex.test(val)) {
38
+ mediaThresholds.set(key, val);
39
+ seenMediaTokens.add(key);
40
+ } else {
41
+ decl.warn(result, `LeanCSS warning: Invalid value for ${prop}: ${val}. Falling back to default: ${mediaThresholds.get(key)}.`);
42
+ }
43
+ }
44
+ }
45
+ } else if (prop.startsWith("--container-")) {
46
+ const key = prop.replace("--container-", "");
47
+ if (containerThresholds.has(key)) {
48
+ if (seenContainerTokens.has(key)) {
49
+ decl.warn(result, `LeanCSS warning: Multiple values detected for ${prop} in :root. Using the first valid value in source order: ${containerThresholds.get(key)}.`);
50
+ } else {
51
+ if (lengthRegex.test(val)) {
52
+ containerThresholds.set(key, val);
53
+ seenContainerTokens.add(key);
54
+ } else {
55
+ decl.warn(result, `LeanCSS warning: Invalid value for ${prop}: ${val}. Falling back to default: ${containerThresholds.get(key)}.`);
56
+ }
57
+ }
58
+ }
59
+ }
60
+ });
61
+ });
10
62
  root.walkAtRules(/^(set|drop)$/, (atRule) => {
11
63
  const name = atRule.params.trim();
12
64
  if (!name) {
@@ -120,6 +172,20 @@ var leancss = (opts = {}) => {
120
172
  atRule.remove();
121
173
  }
122
174
  });
175
+ root.walkAtRules((atRule) => {
176
+ if (["sm", "md", "lg", "xl", "2xl"].includes(atRule.name)) {
177
+ const width = mediaThresholds.get(atRule.name);
178
+ atRule.name = "media";
179
+ atRule.params = `(width >= ${width})`;
180
+ if (atRule.raws) atRule.raws.afterName = " ";
181
+ } else if (["c-sm", "c-md", "c-lg", "c-xl", "c-2xl"].includes(atRule.name)) {
182
+ const key = atRule.name.substring(2);
183
+ const width = containerThresholds.get(key);
184
+ atRule.name = "container";
185
+ atRule.params = `(width >= ${width})`;
186
+ if (atRule.raws) atRule.raws.afterName = " ";
187
+ }
188
+ });
123
189
  }
124
190
  };
125
191
  };
package/package.json CHANGED
@@ -1,55 +1,55 @@
1
- {
2
- "name": "@bizrk/leancss",
3
- "version": "0.1.4",
4
- "description": "CSS-first utility composition with @set and @lift that provides a clean, layer-aware alternative to Sass mixins and Tailwind @apply for atomic compositions.",
5
- "main": "dist/index.js",
6
- "bin": {
7
- "leancss": "./dist/cli/index.js"
8
- },
9
- "module": "dist/index.mjs",
10
- "types": "dist/index.d.ts",
11
- "exports": {
12
- ".": {
13
- "import": "./dist/index.mjs",
14
- "require": "./dist/index.js",
15
- "types": "./dist/index.d.ts"
16
- }
17
- },
18
- "directories": {
19
- "test": "tests"
20
- },
21
- "scripts": {
22
- "build": "tsup src/index.ts src/cli/index.ts --format cjs,esm --dts",
23
- "test": "vitest run",
24
- "test:watch": "vitest"
25
- },
26
- "keywords": [
27
- "css",
28
- "postcss",
29
- "postcss-plugin",
30
- "utility",
31
- "atomic",
32
- "composition",
33
- "design-system",
34
- "leancss",
35
- "set",
36
- "lift"
37
- ],
38
- "files": [
39
- "dist",
40
- "readme.md"
41
- ],
42
- "author": "Adam Birkner",
43
- "license": "MIT",
44
- "peerDependencies": {
45
- "postcss": "^8.0.0"
46
- },
47
- "devDependencies": {
48
- "@types/node": "^20.12.7",
49
- "fast-glob": "^3.3.3",
50
- "postcss": "^8.4.38",
51
- "tsup": "^8.5.1",
52
- "typescript": "^5.4.5",
53
- "vitest": "^1.5.0"
54
- }
1
+ {
2
+ "name": "@bizrk/leancss",
3
+ "version": "0.2.1",
4
+ "description": "CSS-first utility composition with @set and @lift that provides a clean, layer-aware alternative to Sass mixins and Tailwind @apply for atomic compositions.",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "leancss": "./dist/cli/index.js"
8
+ },
9
+ "module": "dist/index.mjs",
10
+ "types": "dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.mjs",
14
+ "require": "./dist/index.js",
15
+ "types": "./dist/index.d.ts"
16
+ }
17
+ },
18
+ "directories": {
19
+ "test": "tests"
20
+ },
21
+ "scripts": {
22
+ "build": "tsup src/index.ts src/cli/index.ts --format cjs,esm --dts",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest"
25
+ },
26
+ "keywords": [
27
+ "css",
28
+ "postcss",
29
+ "postcss-plugin",
30
+ "utility",
31
+ "atomic",
32
+ "composition",
33
+ "design-system",
34
+ "leancss",
35
+ "set",
36
+ "lift"
37
+ ],
38
+ "files": [
39
+ "dist",
40
+ "readme.md"
41
+ ],
42
+ "author": "Adam Birkner",
43
+ "license": "MIT",
44
+ "peerDependencies": {
45
+ "postcss": "^8.0.0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^20.12.7",
49
+ "fast-glob": "^3.3.3",
50
+ "postcss": "^8.4.38",
51
+ "tsup": "^8.5.1",
52
+ "typescript": "^5.4.5",
53
+ "vitest": "^1.5.0"
54
+ }
55
55
  }
package/readme.md CHANGED
@@ -101,6 +101,7 @@ LeanCSS is intentionally simple.
101
101
  • CSS-first authoring
102
102
  • reusable style bundles via `@set` and `@drop`
103
103
  • composition via `@lift`
104
+ • responsive shorthands (`@md`, `@c-lg`)
104
105
  • alias sets
105
106
  • cascade layer friendly
106
107
  • works with `.css` and `.scss`
@@ -154,7 +155,17 @@ Create `.vscode/leancss.css-data.json`:
154
155
  {
155
156
  "name": "@lift",
156
157
  "description": "LeanCSS: Expands one or more sets into the current selector rule."
157
- }
158
+ },
159
+ { "name": "@sm", "description": "LeanCSS responsive media query for small viewports (--media-sm)." },
160
+ { "name": "@md", "description": "LeanCSS responsive media query for medium viewports (--media-md)." },
161
+ { "name": "@lg", "description": "LeanCSS responsive media query for large viewports (--media-lg)." },
162
+ { "name": "@xl", "description": "LeanCSS responsive media query for extra large viewports (--media-xl)." },
163
+ { "name": "@2xl", "description": "LeanCSS responsive media query for 2x extra large viewports (--media-2xl)." },
164
+ { "name": "@c-sm", "description": "LeanCSS responsive container query for small containers (--container-sm)." },
165
+ { "name": "@c-md", "description": "LeanCSS responsive container query for medium containers (--container-md)." },
166
+ { "name": "@c-lg", "description": "LeanCSS responsive container query for large containers (--container-lg)." },
167
+ { "name": "@c-xl", "description": "LeanCSS responsive container query for extra large containers (--container-xl)." },
168
+ { "name": "@c-2xl", "description": "LeanCSS responsive container query for 2x extra large containers (--container-2xl)." }
158
169
  ]
159
170
  }
160
171
  ```
@@ -208,6 +219,69 @@ This does two things:
208
219
 
209
220
  * * *
210
221
 
222
+ # Responsive Shorthands
223
+
224
+ LeanCSS supports responsive shorthand at-rules that quickly compile into concrete viewport and container queries natively.
225
+
226
+ Viewport shorthands: `@sm`, `@md`, `@lg`, `@xl`, `@2xl`
227
+ Container shorthands: `@c-sm`, `@c-md`, `@c-lg`, `@c-xl`, `@c-2xl`
228
+
229
+ You can nest them anywhere: inside `@set` definitions, `@drop` definitions, or standard CSS classes.
230
+
231
+ ```css
232
+ @set app-card {
233
+ padding: 1rem;
234
+
235
+ @md {
236
+ padding: 2rem;
237
+ }
238
+
239
+ @c-lg {
240
+ flex-direction: row;
241
+ }
242
+ }
243
+ ```
244
+
245
+ Compiles to native nested queries:
246
+
247
+ ```css
248
+ .app-card {
249
+ padding: 1rem;
250
+
251
+ @media (width >= 768px) {
252
+ padding: 2rem;
253
+ }
254
+
255
+ @container (width >= 48rem) {
256
+ flex-direction: row;
257
+ }
258
+ }
259
+ ```
260
+
261
+ ### Configuration
262
+
263
+ LeanCSS ships with built-in breakpoint defaults. If you wish to override them, define `--media-{size}` or `--container-{size}` custom properties directly in your `:root`. No separate JSON config is required.
264
+
265
+ ```css
266
+ :root {
267
+ --media-sm: 640px;
268
+ --media-md: 768px;
269
+ --media-lg: 1024px;
270
+ --media-xl: 1280px;
271
+ --media-2xl: 1536px;
272
+
273
+ --container-sm: 24rem;
274
+ --container-md: 36rem;
275
+ --container-lg: 48rem;
276
+ --container-xl: 64rem;
277
+ --container-2xl: 80rem;
278
+ }
279
+ ```
280
+
281
+ LeanCSS treats these properties as compile-time values and substitutes them gracefully without ever emitting invalid `var()` boundaries into output at-rules.
282
+
283
+ * * *
284
+
211
285
  # CLI Tools
212
286
 
213
287
  LeanCSS is reversible and comes with maintenance tools to keep your project clean.