@pantoken/css 0.1.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/README.md +69 -0
- package/dist/index.d.mts +112 -0
- package/dist/index.mjs +2 -0
- package/dist/inject.d.mts +22 -0
- package/dist/inject.mjs +39 -0
- package/dist/src-DnTeei9H.mjs +181 -0
- package/dist/style.css +1 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# @pantoken/css
|
|
2
|
+
|
|
3
|
+
Emit Instructure design tokens as `@property`-typed CSS, with `light-dark()` theming and icon
|
|
4
|
+
data-URIs, from the pantoken IR. Concrete tokens register as typed `@property` rules; themed and
|
|
5
|
+
reference tokens stay as scoped declarations.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm i @pantoken/css
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Also available as `pantoken/css`.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { toCss, css } from "@pantoken/css";
|
|
19
|
+
import { byTheme } from "@pantoken/tokens";
|
|
20
|
+
|
|
21
|
+
css; // the ready-made rebrand stylesheet string
|
|
22
|
+
toCss(byTheme("canvas"), { scope: ":root" }); // build CSS for another theme
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Side-effect injection (browser):
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import "@pantoken/css/inject"; // appends <style data-pantoken> to document.head
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or a plain stylesheet: `@pantoken/css/style.css`.
|
|
32
|
+
|
|
33
|
+
A plugin's `css` hook can contribute or post-process CSS — for example, inject a focus ring that
|
|
34
|
+
isn't in the source tokens:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
toCss(tokens, {
|
|
38
|
+
plugins: [
|
|
39
|
+
{
|
|
40
|
+
name: "focus",
|
|
41
|
+
css: () => ({ append: ":focus-visible { outline: 2px solid var(--instui-focus-color); }" }),
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## API
|
|
48
|
+
|
|
49
|
+
- **`css: string`** — the ready-made `rebrand` stylesheet (also the default export).
|
|
50
|
+
- **`toCss(tokens, options?): string`** — build CSS for any token IR. `options.scope` sets the
|
|
51
|
+
declaration selector (default `":root"`); `options.plugins` runs `css` hooks after the base build.
|
|
52
|
+
- **`ToCssOptions`** — options for `toCss`.
|
|
53
|
+
- **`buildCssFile(args): string`** — the low-level builder: typed `@property` registrations followed
|
|
54
|
+
by scoped declaration sections.
|
|
55
|
+
- **`CssSection`** — one scoped block of custom properties, optionally wrapped in an `@layer`.
|
|
56
|
+
- **`inject(doc?): HTMLStyleElement | undefined`** (from `@pantoken/css/inject`) — inject the
|
|
57
|
+
stylesheet into `document.head` once; no-ops off the DOM.
|
|
58
|
+
- **`./inject`** — a DOM side-effect entry that injects the stylesheet on import.
|
|
59
|
+
- **`./style.css`** — the generated plain stylesheet, for a direct `<link>` or `@import`.
|
|
60
|
+
|
|
61
|
+
## Related
|
|
62
|
+
|
|
63
|
+
- Pairs with `@pantoken/tokens` for the underlying token IR.
|
|
64
|
+
- `@pantoken/scss`, `@pantoken/less`, and `@pantoken/stylus` emit the same tokens as preprocessor
|
|
65
|
+
variables.
|
|
66
|
+
|
|
67
|
+
## License
|
|
68
|
+
|
|
69
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { PantokenPlugin, PropertyRule, Token } from "@pantoken/model";
|
|
2
|
+
|
|
3
|
+
//#region src/to-css.d.ts
|
|
4
|
+
/** Options for {@link toCss}. */
|
|
5
|
+
interface ToCssOptions {
|
|
6
|
+
/** The selector scoped declarations are emitted under (default `":root"`). */
|
|
7
|
+
scope?: string;
|
|
8
|
+
/** Plugins whose `css` hooks run after the base CSS is built (default: none). */
|
|
9
|
+
plugins?: readonly PantokenPlugin[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Emit CSS for a token IR.
|
|
13
|
+
*
|
|
14
|
+
* @param tokens - The IR (e.g. from `@pantoken/tokens`).
|
|
15
|
+
* @param options - {@link ToCssOptions}.
|
|
16
|
+
* @returns The CSS string.
|
|
17
|
+
*
|
|
18
|
+
* @example Build the default stylesheet
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { toCss } from "@pantoken/css";
|
|
21
|
+
* import { tokens } from "@pantoken/tokens";
|
|
22
|
+
*
|
|
23
|
+
* const stylesheet = toCss(tokens); // declarations under :root
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @example Scope declarations to a class and build another theme
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { toCss } from "@pantoken/css";
|
|
29
|
+
* import { byTheme } from "@pantoken/tokens";
|
|
30
|
+
*
|
|
31
|
+
* toCss(byTheme("canvas"), { scope: '[class*="instui"]' });
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @example Post-process with a plugin css hook
|
|
35
|
+
* ```ts
|
|
36
|
+
* import { toCss } from "@pantoken/css";
|
|
37
|
+
* import { tokens } from "@pantoken/tokens";
|
|
38
|
+
*
|
|
39
|
+
* toCss(tokens, {
|
|
40
|
+
* plugins: [
|
|
41
|
+
* {
|
|
42
|
+
* name: "focus",
|
|
43
|
+
* css: () => ({ append: ":focus-visible { outline: 2px solid var(--instui-focus-color); }" }),
|
|
44
|
+
* },
|
|
45
|
+
* ],
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
declare function toCss(tokens: readonly Token[], options?: ToCssOptions): string;
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/emit.d.ts
|
|
52
|
+
/** One scoped block of custom properties, optionally wrapped in an `@layer`. */
|
|
53
|
+
interface CssSection {
|
|
54
|
+
/** Plain layer name (e.g. `"pantoken"`), no `@` prefix. Omit to skip the wrapper. */
|
|
55
|
+
layer?: string;
|
|
56
|
+
pairs: [string, string][];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build a CSS file string: typed `@property` registrations (top-level, theme-agnostic) followed by
|
|
60
|
+
* one or more scoped declaration sections (whose selector the consuming build may rewrite).
|
|
61
|
+
*
|
|
62
|
+
* @example Register one typed property and emit a scoped declaration
|
|
63
|
+
* ```ts
|
|
64
|
+
* import { buildCssFile } from "@pantoken/css";
|
|
65
|
+
*
|
|
66
|
+
* buildCssFile({
|
|
67
|
+
* comments: ["/* my tokens *\/"],
|
|
68
|
+
* scope: ":root",
|
|
69
|
+
* properties: [{ name: "--instui-color-brand", syntax: "<color>", value: "#0374b5" }],
|
|
70
|
+
* sections: [{ pairs: [["--instui-color-text-base", "var(--instui-color-brand)"]] }],
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* @example Wrap a section in a CSS cascade layer
|
|
75
|
+
* ```ts
|
|
76
|
+
* import { buildCssFile } from "@pantoken/css";
|
|
77
|
+
*
|
|
78
|
+
* buildCssFile({
|
|
79
|
+
* comments: [],
|
|
80
|
+
* scope: ":root",
|
|
81
|
+
* sections: [{ layer: "pantoken", pairs: [["--instui-spacing-space-md", "1rem"]] }],
|
|
82
|
+
* });
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
declare function buildCssFile({
|
|
86
|
+
comments,
|
|
87
|
+
scope,
|
|
88
|
+
properties,
|
|
89
|
+
sections,
|
|
90
|
+
inherits
|
|
91
|
+
}: {
|
|
92
|
+
comments: string[];
|
|
93
|
+
scope: string;
|
|
94
|
+
properties?: PropertyRule[];
|
|
95
|
+
sections: CssSection[];
|
|
96
|
+
inherits?: boolean;
|
|
97
|
+
}): string;
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/index.d.ts
|
|
100
|
+
/**
|
|
101
|
+
* The ready-made `rebrand` stylesheet string.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* import { css } from "@pantoken/css";
|
|
106
|
+
*
|
|
107
|
+
* document.head.insertAdjacentHTML("beforeend", `<style>${css}</style>`);
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
declare const css: string;
|
|
111
|
+
//#endregion
|
|
112
|
+
export { type CssSection, type ToCssOptions, buildCssFile, css, css as default, toCss };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/inject.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Inject the pantoken stylesheet into `document.head` once. Returns the created element, if any.
|
|
4
|
+
*
|
|
5
|
+
* @example Inject on the default document
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { inject } from "@pantoken/css/inject";
|
|
8
|
+
*
|
|
9
|
+
* const style = inject(); // <style data-pantoken> appended to document.head
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* @example Inject into a specific document (e.g. an iframe)
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { inject } from "@pantoken/css/inject";
|
|
15
|
+
*
|
|
16
|
+
* const frame = document.querySelector("iframe")!;
|
|
17
|
+
* inject(frame.contentDocument ?? undefined);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
declare function inject(doc?: Document | undefined): HTMLStyleElement | undefined;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { inject };
|
package/dist/inject.mjs
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { t as css } from "./src-DnTeei9H.mjs";
|
|
2
|
+
//#region src/inject.ts
|
|
3
|
+
/**
|
|
4
|
+
* `@pantoken/css/inject` — a DOM side-effect entry. Importing it injects the pantoken stylesheet
|
|
5
|
+
* into `document.head`. Safe to import in non-DOM environments (it no-ops).
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Inject the pantoken stylesheet into `document.head` once. Returns the created element, if any.
|
|
11
|
+
*
|
|
12
|
+
* @example Inject on the default document
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { inject } from "@pantoken/css/inject";
|
|
15
|
+
*
|
|
16
|
+
* const style = inject(); // <style data-pantoken> appended to document.head
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @example Inject into a specific document (e.g. an iframe)
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { inject } from "@pantoken/css/inject";
|
|
22
|
+
*
|
|
23
|
+
* const frame = document.querySelector("iframe")!;
|
|
24
|
+
* inject(frame.contentDocument ?? undefined);
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
function inject(doc = globalThis.document) {
|
|
28
|
+
if (!doc) return void 0;
|
|
29
|
+
const existing = doc.querySelector("style[data-pantoken]");
|
|
30
|
+
if (existing) return existing;
|
|
31
|
+
const style = doc.createElement("style");
|
|
32
|
+
style.setAttribute("data-pantoken", "css");
|
|
33
|
+
style.textContent = css;
|
|
34
|
+
doc.head.append(style);
|
|
35
|
+
return style;
|
|
36
|
+
}
|
|
37
|
+
inject();
|
|
38
|
+
//#endregion
|
|
39
|
+
export { inject };
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { tokens } from "@pantoken/tokens";
|
|
2
|
+
import { checkPlugins } from "@pantoken/plugin-kit";
|
|
3
|
+
//#region src/emit.ts
|
|
4
|
+
function renderSection(scope, { layer, pairs }) {
|
|
5
|
+
const declarations = [
|
|
6
|
+
` ${scope} {`,
|
|
7
|
+
...pairs.map(([name, val]) => ` ${name}: ${val.trim()};`),
|
|
8
|
+
" }"
|
|
9
|
+
];
|
|
10
|
+
return layer ? [
|
|
11
|
+
`@layer ${layer} {`,
|
|
12
|
+
...declarations,
|
|
13
|
+
"}"
|
|
14
|
+
].join("\n") : declarations.join("\n");
|
|
15
|
+
}
|
|
16
|
+
function renderProperty({ name, syntax, value }, inherits) {
|
|
17
|
+
return [
|
|
18
|
+
`@property ${name} {`,
|
|
19
|
+
` syntax: "${syntax}";`,
|
|
20
|
+
` inherits: ${inherits};`,
|
|
21
|
+
` initial-value: ${value.trim()};`,
|
|
22
|
+
`}`
|
|
23
|
+
].join("\n");
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build a CSS file string: typed `@property` registrations (top-level, theme-agnostic) followed by
|
|
27
|
+
* one or more scoped declaration sections (whose selector the consuming build may rewrite).
|
|
28
|
+
*
|
|
29
|
+
* @example Register one typed property and emit a scoped declaration
|
|
30
|
+
* ```ts
|
|
31
|
+
* import { buildCssFile } from "@pantoken/css";
|
|
32
|
+
*
|
|
33
|
+
* buildCssFile({
|
|
34
|
+
* comments: ["/* my tokens *\/"],
|
|
35
|
+
* scope: ":root",
|
|
36
|
+
* properties: [{ name: "--instui-color-brand", syntax: "<color>", value: "#0374b5" }],
|
|
37
|
+
* sections: [{ pairs: [["--instui-color-text-base", "var(--instui-color-brand)"]] }],
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @example Wrap a section in a CSS cascade layer
|
|
42
|
+
* ```ts
|
|
43
|
+
* import { buildCssFile } from "@pantoken/css";
|
|
44
|
+
*
|
|
45
|
+
* buildCssFile({
|
|
46
|
+
* comments: [],
|
|
47
|
+
* scope: ":root",
|
|
48
|
+
* sections: [{ layer: "pantoken", pairs: [["--instui-spacing-space-md", "1rem"]] }],
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
function buildCssFile({ comments, scope, properties = [], sections, inherits = true }) {
|
|
53
|
+
const blocks = [properties.map((p) => renderProperty(p, inherits)).join("\n\n"), ...sections.filter((s) => s.pairs.length > 0).map((s) => renderSection(scope, s))].filter(Boolean);
|
|
54
|
+
return [
|
|
55
|
+
`/* DO NOT EDIT — generated by @pantoken/css */`,
|
|
56
|
+
"",
|
|
57
|
+
...comments,
|
|
58
|
+
"",
|
|
59
|
+
...blocks,
|
|
60
|
+
""
|
|
61
|
+
].join("\n");
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/to-css.ts
|
|
65
|
+
/**
|
|
66
|
+
* `toCss` — turn the token IR into CSS: concrete tokens become typed `@property` registrations,
|
|
67
|
+
* themed/reference tokens become scoped declarations. Plugins can contribute or post-process CSS.
|
|
68
|
+
*
|
|
69
|
+
* @module
|
|
70
|
+
*/
|
|
71
|
+
/** True when a value cannot be a typed `@property` initial-value (`var()` / `light-dark()`). */
|
|
72
|
+
function isContextual(value) {
|
|
73
|
+
return /var\(|light-dark\(/.test(value);
|
|
74
|
+
}
|
|
75
|
+
function marked(marker, css) {
|
|
76
|
+
return marker ? `/* ${marker} */\n${css}` : css;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Emit CSS for a token IR.
|
|
80
|
+
*
|
|
81
|
+
* @param tokens - The IR (e.g. from `@pantoken/tokens`).
|
|
82
|
+
* @param options - {@link ToCssOptions}.
|
|
83
|
+
* @returns The CSS string.
|
|
84
|
+
*
|
|
85
|
+
* @example Build the default stylesheet
|
|
86
|
+
* ```ts
|
|
87
|
+
* import { toCss } from "@pantoken/css";
|
|
88
|
+
* import { tokens } from "@pantoken/tokens";
|
|
89
|
+
*
|
|
90
|
+
* const stylesheet = toCss(tokens); // declarations under :root
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @example Scope declarations to a class and build another theme
|
|
94
|
+
* ```ts
|
|
95
|
+
* import { toCss } from "@pantoken/css";
|
|
96
|
+
* import { byTheme } from "@pantoken/tokens";
|
|
97
|
+
*
|
|
98
|
+
* toCss(byTheme("canvas"), { scope: '[class*="instui"]' });
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
* @example Post-process with a plugin css hook
|
|
102
|
+
* ```ts
|
|
103
|
+
* import { toCss } from "@pantoken/css";
|
|
104
|
+
* import { tokens } from "@pantoken/tokens";
|
|
105
|
+
*
|
|
106
|
+
* toCss(tokens, {
|
|
107
|
+
* plugins: [
|
|
108
|
+
* {
|
|
109
|
+
* name: "focus",
|
|
110
|
+
* css: () => ({ append: ":focus-visible { outline: 2px solid var(--instui-focus-color); }" }),
|
|
111
|
+
* },
|
|
112
|
+
* ],
|
|
113
|
+
* });
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
function toCss(tokens, options = {}) {
|
|
117
|
+
const { scope = ":root", plugins = [] } = options;
|
|
118
|
+
const properties = [];
|
|
119
|
+
const declarations = [];
|
|
120
|
+
for (const token of tokens) if (isContextual(token.value)) declarations.push([token.name, token.value]);
|
|
121
|
+
else properties.push({
|
|
122
|
+
name: token.name,
|
|
123
|
+
syntax: token.syntax,
|
|
124
|
+
value: token.value
|
|
125
|
+
});
|
|
126
|
+
let css = buildCssFile({
|
|
127
|
+
comments: ["/* Instructure design tokens + icon set (pantoken) */", "/* Concrete tokens are registered with @property; themed/reference tokens are declarations. */"],
|
|
128
|
+
scope,
|
|
129
|
+
properties,
|
|
130
|
+
sections: [{ pairs: declarations }]
|
|
131
|
+
});
|
|
132
|
+
const prepends = [];
|
|
133
|
+
const appends = [];
|
|
134
|
+
for (const plugin of checkPlugins(plugins, "css")) {
|
|
135
|
+
const c = plugin.css?.({
|
|
136
|
+
tokens,
|
|
137
|
+
css
|
|
138
|
+
});
|
|
139
|
+
if (!c) continue;
|
|
140
|
+
appendContribution(c, prepends, appends);
|
|
141
|
+
}
|
|
142
|
+
return [
|
|
143
|
+
...prepends,
|
|
144
|
+
css,
|
|
145
|
+
...appends
|
|
146
|
+
].filter(Boolean).join("\n\n");
|
|
147
|
+
}
|
|
148
|
+
function appendContribution(c, prepends, appends) {
|
|
149
|
+
if (c.prepend) prepends.push(marked(c.marker, c.prepend));
|
|
150
|
+
const block = [
|
|
151
|
+
(c.properties ?? []).map((p) => `@property ${p.name} {\n syntax: "${p.syntax}";\n inherits: true;\n initial-value: ${p.value};\n}`).join("\n\n"),
|
|
152
|
+
c.declarations?.length ? `:root {\n${c.declarations.map(([n, v]) => ` ${n}: ${v};`).join("\n")}\n}` : "",
|
|
153
|
+
c.append
|
|
154
|
+
].filter(Boolean).join("\n\n");
|
|
155
|
+
if (block) appends.push(marked(c.marker, block));
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/index.ts
|
|
159
|
+
/**
|
|
160
|
+
* `@pantoken/css` — emit Instructure design tokens as `@property`-typed CSS.
|
|
161
|
+
*
|
|
162
|
+
* {@link toCss} turns any token IR into CSS; {@link css} is the ready-made `rebrand` stylesheet.
|
|
163
|
+
* A DOM side-effect entry lives at `@pantoken/css/inject`, and a static file at
|
|
164
|
+
* `@pantoken/css/style.css`.
|
|
165
|
+
*
|
|
166
|
+
* @module
|
|
167
|
+
* @beta
|
|
168
|
+
*/
|
|
169
|
+
/**
|
|
170
|
+
* The ready-made `rebrand` stylesheet string.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* import { css } from "@pantoken/css";
|
|
175
|
+
*
|
|
176
|
+
* document.head.insertAdjacentHTML("beforeend", `<style>${css}</style>`);
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
const css = toCss(tokens);
|
|
180
|
+
//#endregion
|
|
181
|
+
export { toCss as n, buildCssFile as r, css as t };
|