@salty-css/vite 0.1.0-alpha.3 → 0.1.0-alpha.31
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 +209 -0
- package/index.cjs +1 -1
- package/index.d.ts +8 -1
- package/index.js +12 -12
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -57,6 +57,8 @@ To get help with problems, [Join Salty CSS Discord server](https://discord.gg/R6
|
|
|
57
57
|
- [defineVariables](#variables) - create CSS variables (tokens) that can be used in any styling function
|
|
58
58
|
- [defineMediaQuery](#media-queries) - create CSS media queries and use them in any styling function
|
|
59
59
|
- [defineTemplates](#templates) - create reusable templates that can be applied when same styles are used over and over again
|
|
60
|
+
- [defineFont](#custom-fonts) - register custom fonts via `@font-face` (or a remote stylesheet) and expose them as a CSS variable
|
|
61
|
+
- [defineImport](#importing-additional-css) - pull in external CSS files (relative, public, node_modules, or URL)
|
|
60
62
|
- [keyframes](#keyframes-animations) - create CSS keyframes animation that can be used and imported in any styling function
|
|
61
63
|
|
|
62
64
|
### Styling helpers & utility
|
|
@@ -319,6 +321,213 @@ Example usage:
|
|
|
319
321
|
styled('div', { base: { textStyle: 'headline.large', card: '20px' } });
|
|
320
322
|
```
|
|
321
323
|
|
|
324
|
+
### Template variants
|
|
325
|
+
|
|
326
|
+
Static templates can opt into named variants by switching a node from a plain styles object to a "rich" shape with `base` and `variants` keys — the same authoring API as `styled`. Variants declared at a parent node are inherited by every descendant leaf, so one declaration of `weight` on `heading` flows down to `heading.large`, `heading.small`, etc.
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
// /styles/templates.css.ts
|
|
330
|
+
import { defineTemplates } from '@salty-css/core/factories';
|
|
331
|
+
|
|
332
|
+
export default defineTemplates({
|
|
333
|
+
textStyle: {
|
|
334
|
+
heading: {
|
|
335
|
+
// Rich node: variants declared here are available to every child leaf.
|
|
336
|
+
base: {
|
|
337
|
+
fontFamily: '{fontFamily.heading}',
|
|
338
|
+
lineHeight: '1.1em',
|
|
339
|
+
},
|
|
340
|
+
variants: {
|
|
341
|
+
weight: {
|
|
342
|
+
light: { fontWeight: 300 },
|
|
343
|
+
regular: { fontWeight: 500 },
|
|
344
|
+
heavy: { fontWeight: 800 },
|
|
345
|
+
},
|
|
346
|
+
italic: {
|
|
347
|
+
true: { fontStyle: 'italic' },
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
defaultVariants: {
|
|
351
|
+
weight: 'regular',
|
|
352
|
+
},
|
|
353
|
+
compoundVariants: [
|
|
354
|
+
// Applied when ALL listed axes match.
|
|
355
|
+
{ weight: 'heavy', italic: true, css: { letterSpacing: '-0.01em' } },
|
|
356
|
+
],
|
|
357
|
+
// Leaves can be plain styles…
|
|
358
|
+
small: { fontSize: '{fontSize.heading.small}' },
|
|
359
|
+
regular: { fontSize: '{fontSize.heading.regular}' },
|
|
360
|
+
// …or rich, with their own additional variants / overrides.
|
|
361
|
+
large: {
|
|
362
|
+
base: { fontSize: '{fontSize.heading.large}' },
|
|
363
|
+
variants: {
|
|
364
|
+
weight: {
|
|
365
|
+
// Override the inherited bundle just for `large`.
|
|
366
|
+
heavy: { fontWeight: 900, letterSpacing: '-0.02em' },
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Apply variants at the call site in either of two equivalent forms — string query or object:
|
|
376
|
+
|
|
377
|
+
```ts
|
|
378
|
+
styled('h1', {
|
|
379
|
+
base: {
|
|
380
|
+
// String form: `path@axis=value&axis=value&boolFlag`
|
|
381
|
+
textStyle: 'heading.large@weight=heavy&italic',
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
styled('h2', {
|
|
386
|
+
base: {
|
|
387
|
+
// Object form: `name` is the dot-path, the rest are axis values.
|
|
388
|
+
textStyle: { name: 'heading.large', weight: 'heavy', italic: true },
|
|
389
|
+
},
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// No variants — existing simple usage still works.
|
|
393
|
+
styled('p', { base: { textStyle: 'heading.regular' } });
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Behaviour worth knowing:
|
|
397
|
+
|
|
398
|
+
- **Inheritance is parent → leaf only.** A leaf sees variants from its ancestors; siblings and children are invisible.
|
|
399
|
+
- **Closest wins.** If the same axis/value bundle is declared at multiple levels, the deepest one replaces (not merges) the ancestor's bundle for that single call.
|
|
400
|
+
- **`defaultVariants` apply when the call site omits an axis.** Walked bottom-up, same closest-wins rule.
|
|
401
|
+
- **`compoundVariants` (AND) and `anyOfVariants` (OR) are accumulated top-down** across the path — every matching rule contributes.
|
|
402
|
+
- **Boolean axes accept a shorthand.** `@italic` is equivalent to `@italic=true`; in object form pass `italic: true`.
|
|
403
|
+
- **Reserved keys** inside a rich node: `base`, `variants`, `defaultVariants`, `compoundVariants`, `anyOfVariants`. Don't use `name` as an axis (reserved for the object call-site form).
|
|
404
|
+
- **Function templates** (e.g. `card: (v) => ({ … })`) don't support variants — keep them as plain functions.
|
|
405
|
+
|
|
406
|
+
## Custom fonts
|
|
407
|
+
|
|
408
|
+
Register custom fonts that will be emitted as `@font-face` declarations and exposed as a CSS variable. Mirrors the developer experience of Next.js / Astro font loaders, but generated at build time alongside the rest of your Salty CSS output.
|
|
409
|
+
|
|
410
|
+
The returned object stringifies to its `font-family` value and exposes helpers for explicit usage:
|
|
411
|
+
|
|
412
|
+
- `Font.variable` → CSS variable name (e.g. `--font-inter`)
|
|
413
|
+
- `Font.fontFamily` → final `font-family` string with fallbacks
|
|
414
|
+
- `Font.className` → class that sets the variable + applies the font on a subtree
|
|
415
|
+
- `Font.style` → object you can spread on a React `style` prop
|
|
416
|
+
|
|
417
|
+
```ts
|
|
418
|
+
// /styles/fonts.css.ts
|
|
419
|
+
import { defineFont } from '@salty-css/core/factories';
|
|
420
|
+
|
|
421
|
+
// 1. Local or self-hosted @font-face sources.
|
|
422
|
+
// URLs are passed through as-is (use a public-folder path or a CDN URL).
|
|
423
|
+
export const Inter = defineFont({
|
|
424
|
+
name: 'Inter', // CSS font-family value
|
|
425
|
+
variable: '--font-inter', // Optional — accepts 'font-inter' too; if omitted we derive `--font-<name>-<hash>` from the inputs
|
|
426
|
+
display: 'swap', // Optional default applied to variants without their own `display`
|
|
427
|
+
fallback: ['system-ui', 'sans-serif'], // Optional family fallbacks appended after `name`
|
|
428
|
+
variants: [
|
|
429
|
+
{
|
|
430
|
+
weight: 400,
|
|
431
|
+
style: 'normal',
|
|
432
|
+
// Shorthand: pass a string and the `format()` descriptor is auto-detected
|
|
433
|
+
// from the file extension (woff2, woff, ttf, otf, eot, svg, ttc).
|
|
434
|
+
src: '/fonts/inter-400.woff2',
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
weight: 700,
|
|
438
|
+
style: 'normal',
|
|
439
|
+
// Multiple sources can be a string array — first entry is preferred;
|
|
440
|
+
// the browser picks the first format it supports.
|
|
441
|
+
src: ['/fonts/inter-700.woff2', '/fonts/inter-700.ttf'],
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
weight: 400,
|
|
445
|
+
style: 'italic',
|
|
446
|
+
// Use the `{ url, format }` object form when the URL has no recognisable
|
|
447
|
+
// extension (signed CDN URLs, query-only endpoints, etc.). You can also
|
|
448
|
+
// mix strings and objects in the same array.
|
|
449
|
+
src: ['/fonts/inter-400-italic.woff2', { url: 'https://cdn.example.com/inter-italic', format: 'woff' }],
|
|
450
|
+
},
|
|
451
|
+
],
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
// 2. Remote stylesheet (Google Fonts, etc.). Emits `@import url(...)` and still
|
|
455
|
+
// registers the CSS variable so usage stays the same as the @font-face flow.
|
|
456
|
+
export const InterCdn = defineFont({
|
|
457
|
+
name: 'Inter',
|
|
458
|
+
variable: '--font-inter',
|
|
459
|
+
import: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap',
|
|
460
|
+
});
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
Example usage:
|
|
464
|
+
|
|
465
|
+
```tsx
|
|
466
|
+
import { Inter } from './fonts.css';
|
|
467
|
+
import { styled } from '@salty-css/react/styled';
|
|
468
|
+
|
|
469
|
+
// Apply the font globally by attaching its className high up in the tree.
|
|
470
|
+
// This sets `--font-inter` on the subtree and applies `font-family: var(--font-inter)`.
|
|
471
|
+
export const App = ({ children }) => <div className={Inter.className}>{children}</div>;
|
|
472
|
+
|
|
473
|
+
// `Inter` stringifies to its font-family value (with fallbacks), so it can be used directly.
|
|
474
|
+
export const Heading = styled('h1', {
|
|
475
|
+
base: {
|
|
476
|
+
fontFamily: `${Inter}`,
|
|
477
|
+
},
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// Or reference the CSS variable explicitly.
|
|
481
|
+
export const Body = styled('p', {
|
|
482
|
+
base: {
|
|
483
|
+
fontFamily: `var(${Inter.variable})`,
|
|
484
|
+
},
|
|
485
|
+
});
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
## Importing additional CSS
|
|
489
|
+
|
|
490
|
+
Use `defineImport` to pull in CSS that lives outside of Salty's authoring API — a reset stylesheet from npm, a Google Fonts URL, an asset in your app's `public/` folder, or a sibling `.css` file. The compiler turns each spec into an `@import` rule in the generated `saltygen/index.css`, so the imported stylesheets travel with the rest of your build.
|
|
491
|
+
|
|
492
|
+
```ts
|
|
493
|
+
// /styles/imports.css.ts
|
|
494
|
+
import { defineImport } from '@salty-css/core/factories';
|
|
495
|
+
|
|
496
|
+
export default defineImport(
|
|
497
|
+
// Relative to this file
|
|
498
|
+
'./reset.css',
|
|
499
|
+
// From node_modules (bare specifier — same as Vite / native CSS @import)
|
|
500
|
+
'modern-normalize/modern-normalize.css',
|
|
501
|
+
// From node_modules (~ prefix — same resolver, webpack-style)
|
|
502
|
+
'~normalize.css/normalize.css',
|
|
503
|
+
// From your app's public/ folder (served at the host root)
|
|
504
|
+
'/fonts/inter.css',
|
|
505
|
+
// External URL
|
|
506
|
+
'https://fonts.googleapis.com/css2?family=Inter&display=swap',
|
|
507
|
+
// Object form — attach media or supports() conditions
|
|
508
|
+
{ url: './print.css', media: 'print' },
|
|
509
|
+
{ url: './p3.css', supports: 'color(display-p3 1 1 1)' }
|
|
510
|
+
);
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
Path resolution:
|
|
514
|
+
|
|
515
|
+
| Pattern | Behaviour |
|
|
516
|
+
| --------------------------- | ---------------------------------------------------------------------------------------- |
|
|
517
|
+
| `http://`, `https://`, `//` | Emitted verbatim |
|
|
518
|
+
| Starts with `/` | Public-folder URL — emitted verbatim, the browser resolves it against your host |
|
|
519
|
+
| Starts with `./` or `../` | Resolved at build time relative to the file that called `defineImport` |
|
|
520
|
+
| `~package/file.css` | Stripped of the leading `~`, then resolved from `node_modules` and copied into the build |
|
|
521
|
+
| `package/file.css` (bare) | Same `node_modules` resolution as the `~` form |
|
|
522
|
+
|
|
523
|
+
All imports are placed inside a new `imports` cascade layer that sits **before** `reset`, `global`, `templates`, and your component styles. This means your own styles always win over third-party CSS you pull in — which is what most teams expect when they drop in something like `modern-normalize`.
|
|
524
|
+
|
|
525
|
+
The full layer order in the generated `index.css` is:
|
|
526
|
+
|
|
527
|
+
```css
|
|
528
|
+
@layer imports, reset, global, templates, l0, l1, l2, l3, l4, l5, l6, l7, l8;
|
|
529
|
+
```
|
|
530
|
+
|
|
322
531
|
## Keyframes animations
|
|
323
532
|
|
|
324
533
|
```ts
|
package/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var y=Object.create;var i=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var w=Object.getPrototypeOf,p=Object.prototype.hasOwnProperty;var f=(t,e,r,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of h(e))!p.call(t,a)&&a!==r&&i(t,a,{get:()=>e[a],enumerable:!(o=m(e,a))||o.enumerable});return t};var g=(t,e,r)=>(r=t!=null?y(w(t)):{},f(e||!t||!t.__esModule?i(r,"default",{value:t,enumerable:!0}):r,t));Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const c=require("@salty-css/core/compiler/helpers"),F=require("@salty-css/core/compiler/salty-compiler"),u=require("@salty-css/core/server"),S=async t=>{if(t==="react"||t===void 0)return(await import("@salty-css/react/transform-salty-file")).transformSaltyFile;throw new Error(`@salty-css/vite: framework "${t}" is not supported. Supported: react.`)},d=(t,e={})=>{const r=new F.SaltyCompiler(t,{mode:e.mode});let o;const a=()=>(o||(o=r.getFramework().then(S)),o);return{name:"stylegen",buildStart:async()=>await r.generateCss(),load:async s=>{if(c.isSaltyFile(s))return await(await a())(r,s)},handleHotUpdate:async({file:s,server:n})=>{await u.checkShouldRestart(s)&&n.restart()},watchChange:{handler:async(s,n)=>{c.isSaltyFile(s)&&n.event!=="delete"&&(await u.checkShouldRestart(s)||await r.generateFile(s))}}}};exports.default=d;exports.saltyPlugin=d;
|
package/index.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import { SaltyCompilerMode } from '@salty-css/core/compiler/salty-compiler';
|
|
1
2
|
import { PluginOption } from 'vite';
|
|
2
|
-
export
|
|
3
|
+
export interface SaltyVitePluginOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Explicit build mode. Defaults to NODE_ENV-based detection.
|
|
6
|
+
*/
|
|
7
|
+
mode?: SaltyCompilerMode;
|
|
8
|
+
}
|
|
9
|
+
export declare const saltyPlugin: (dir: string, options?: SaltyVitePluginOptions) => PluginOption;
|
|
3
10
|
export default saltyPlugin;
|
package/index.js
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { isSaltyFile as
|
|
1
|
+
import { isSaltyFile as i } from "@salty-css/core/compiler/helpers";
|
|
2
2
|
import { SaltyCompiler as c } from "@salty-css/core/compiler/salty-compiler";
|
|
3
|
-
import { checkShouldRestart as
|
|
4
|
-
const
|
|
3
|
+
import { checkShouldRestart as l } from "@salty-css/core/server";
|
|
4
|
+
const d = async (a) => {
|
|
5
5
|
if (a === "react" || a === void 0)
|
|
6
6
|
return (await import("@salty-css/react/transform-salty-file")).transformSaltyFile;
|
|
7
7
|
throw new Error(`@salty-css/vite: framework "${a}" is not supported. Supported: react.`);
|
|
8
|
-
},
|
|
9
|
-
const r = new c(a);
|
|
8
|
+
}, w = (a, o = {}) => {
|
|
9
|
+
const r = new c(a, { mode: o.mode });
|
|
10
10
|
let s;
|
|
11
|
-
const
|
|
11
|
+
const m = () => (s || (s = r.getFramework().then(d)), s);
|
|
12
12
|
return {
|
|
13
13
|
name: "stylegen",
|
|
14
14
|
buildStart: async () => await r.generateCss(),
|
|
15
15
|
load: async (t) => {
|
|
16
|
-
if (
|
|
17
|
-
return await (await
|
|
16
|
+
if (i(t))
|
|
17
|
+
return await (await m())(r, t);
|
|
18
18
|
},
|
|
19
19
|
handleHotUpdate: async ({ file: t, server: e }) => {
|
|
20
|
-
await
|
|
20
|
+
await l(t) && e.restart();
|
|
21
21
|
},
|
|
22
22
|
watchChange: {
|
|
23
23
|
handler: async (t, e) => {
|
|
24
|
-
|
|
24
|
+
i(t) && e.event !== "delete" && (await l(t) || await r.generateFile(t));
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
};
|
|
29
29
|
export {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
w as default,
|
|
31
|
+
w as saltyPlugin
|
|
32
32
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salty-css/vite",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.31",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"typings": "./dist/index.d.ts",
|
|
@@ -34,11 +34,11 @@
|
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@salty-css/core": "^0.1.0-alpha.
|
|
37
|
+
"@salty-css/core": "^0.1.0-alpha.31",
|
|
38
38
|
"vite": "^6.4.1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@salty-css/react": "^0.0.
|
|
41
|
+
"@salty-css/react": "^0.1.0-alpha.30"
|
|
42
42
|
},
|
|
43
43
|
"peerDependenciesMeta": {
|
|
44
44
|
"@salty-css/react": {
|