rastack 0.0.24 → 0.0.25

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/dist/rastack-design.d.ts +17 -0
  3. package/dist/rastack-design.js +134 -0
  4. package/dist/rastack-tokens.d.ts +15 -0
  5. package/dist/rastack-tokens.js +43 -0
  6. package/dist/rastack.d.ts +2 -0
  7. package/dist/rastack.js +12 -0
  8. package/dist/tokens/color.d.ts +22 -0
  9. package/dist/tokens/color.js +70 -0
  10. package/dist/tokens/compile.d.ts +34 -0
  11. package/dist/tokens/compile.js +103 -0
  12. package/dist/tokens/css.d.ts +30 -0
  13. package/dist/tokens/css.js +102 -0
  14. package/dist/tokens/define.d.ts +71 -0
  15. package/dist/tokens/define.js +98 -0
  16. package/dist/tokens/index.d.ts +17 -0
  17. package/dist/tokens/index.js +33 -0
  18. package/dist/tokens/resolve.d.ts +40 -0
  19. package/dist/tokens/resolve.js +135 -0
  20. package/dist/tokens/studio.d.ts +21 -0
  21. package/dist/tokens/studio.js +328 -0
  22. package/dist/tokens/theme.d.ts +55 -0
  23. package/dist/tokens/theme.js +139 -0
  24. package/dist/tokens/ts.d.ts +15 -0
  25. package/dist/tokens/ts.js +73 -0
  26. package/dist/tokens/types.d.ts +92 -0
  27. package/dist/tokens/types.js +35 -0
  28. package/jest.config.cjs +6 -0
  29. package/package.json +3 -2
  30. package/src/rastack-design.ts +117 -0
  31. package/src/rastack-tokens.ts +46 -0
  32. package/src/rastack.ts +12 -0
  33. package/src/tokens/color.ts +74 -0
  34. package/src/tokens/compile.ts +85 -0
  35. package/src/tokens/css.ts +138 -0
  36. package/src/tokens/define.ts +128 -0
  37. package/src/tokens/index.ts +18 -0
  38. package/src/tokens/resolve.ts +170 -0
  39. package/src/tokens/studio.ts +357 -0
  40. package/src/tokens/theme.ts +180 -0
  41. package/src/tokens/ts.ts +80 -0
  42. package/src/tokens/types.ts +125 -0
  43. package/test/tokens.spec.ts +302 -0
  44. package/theme/index.ts +9 -0
  45. package/theme/provider.tsx +157 -0
  46. package/tokens.ts +9 -0
  47. package/tsconfig.json +3 -0
package/theme/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `rastack/theme` — the React runtime that applies a design-token set: a
3
+ * `<ThemeProvider>` that injects the token CSS variables and manages light/dark
4
+ * (and any custom) modes, plus `useTheme` / `useToken` / `useThemeMode` hooks.
5
+ *
6
+ * Tokens are authored with `rastack/tokens` and compiled by `rastack tokens`;
7
+ * this is where they light up the UI.
8
+ */
9
+ export * from "./provider";
@@ -0,0 +1,157 @@
1
+ import React, {
2
+ createContext,
3
+ ReactNode,
4
+ useContext,
5
+ useEffect,
6
+ useMemo,
7
+ useState,
8
+ } from "react";
9
+ import {
10
+ emitCss,
11
+ formatCssValue,
12
+ cssVarName,
13
+ resolveTheme,
14
+ ThemedTokens,
15
+ } from "../src/tokens";
16
+
17
+ /**
18
+ * `rastack/theme` — apply a design-token set to a React app.
19
+ *
20
+ * The counterpart to `rastack tokens`: where the CLI emits static CSS, this
21
+ * provider injects the same CSS custom properties at runtime and manages the
22
+ * active **mode** (light / dark / …). Because every value is a CSS variable,
23
+ * flipping mode re-cascades instantly with no re-render of consuming
24
+ * components — exactly like `RAStackProvider` reconfiguring the API in place.
25
+ *
26
+ * ```tsx
27
+ * import tokens from "../tokens/theme.tokens"; // defineTokens(...)
28
+ * import { ThemeProvider, useToken, useThemeMode } from "rastack/theme";
29
+ *
30
+ * <ThemeProvider tokens={tokens}>
31
+ * <App />
32
+ * </ThemeProvider>
33
+ *
34
+ * const accent = useToken("color.accent"); // "var(--color-accent)"
35
+ * const [mode, setMode] = useThemeMode(); // "light" | "dark" | …
36
+ * ```
37
+ */
38
+
39
+ export interface ThemeContextValue {
40
+ /** The active mode, or `undefined` for the base (mode-less) theme. */
41
+ mode: string | undefined;
42
+ /** Switch mode; unknown modes fall back to the base. */
43
+ setMode: (mode: string | undefined) => void;
44
+ /** The modes this theme defines. */
45
+ modes: string[];
46
+ /** `token("color.accent")` → `"var(--color-accent)"`. */
47
+ token: (path: string) => string;
48
+ /**
49
+ * The resolved *literal* value of a token in the current mode
50
+ * (`"#4F6BFF"`), for contexts without CSS variables (React Native, canvas).
51
+ */
52
+ value: (path: string) => string | undefined;
53
+ }
54
+
55
+ const ThemeContext = createContext<ThemeContextValue | null>(null);
56
+
57
+ export interface ThemeProviderProps {
58
+ /** A `defineTokens(...)` result, or a bare `{ tokens, modes }` document. */
59
+ tokens: ThemedTokens;
60
+ /** Initial mode. Defaults to the token set's `defaultMode`. */
61
+ mode?: string;
62
+ /** Prefix every CSS variable (`prefix="rs"` → `--rs-color-accent`). */
63
+ prefix?: string;
64
+ /**
65
+ * The attribute set on `<html>` for the active mode. Default `data-theme`,
66
+ * matching the emitted CSS's `[data-theme="dark"]` selectors.
67
+ */
68
+ modeAttribute?: string;
69
+ /**
70
+ * Where to scope the CSS. `document` (default) injects a `<style>` and sets
71
+ * the mode attribute on `<html>`. Pass an element to scope to a subtree.
72
+ */
73
+ target?: HTMLElement | "document";
74
+ children: ReactNode;
75
+ }
76
+
77
+ export function ThemeProvider({
78
+ tokens,
79
+ mode: initialMode,
80
+ prefix = "",
81
+ modeAttribute = "data-theme",
82
+ target = "document",
83
+ children,
84
+ }: ThemeProviderProps): JSX.Element {
85
+ const resolved = useMemo(() => resolveTheme(tokens), [tokens]);
86
+ const css = useMemo(() => emitCss(tokens, { prefix }), [tokens, prefix]);
87
+ const [mode, setMode] = useState<string | undefined>(
88
+ initialMode ?? resolved.defaultMode,
89
+ );
90
+
91
+ // Inject (and keep in sync) the generated CSS as a single <style> element.
92
+ useEffect(() => {
93
+ if (typeof document === "undefined") return;
94
+ const style = document.createElement("style");
95
+ style.setAttribute("data-rastack-theme", "");
96
+ style.textContent = css;
97
+ document.head.appendChild(style);
98
+ return () => {
99
+ style.remove();
100
+ };
101
+ }, [css]);
102
+
103
+ // Reflect the active mode onto the target element's attribute.
104
+ useEffect(() => {
105
+ if (typeof document === "undefined") return;
106
+ const el =
107
+ target === "document" ? document.documentElement : (target as HTMLElement);
108
+ const prev = el.getAttribute(modeAttribute);
109
+ if (mode) el.setAttribute(modeAttribute, mode);
110
+ else el.removeAttribute(modeAttribute);
111
+ return () => {
112
+ if (prev != null) el.setAttribute(modeAttribute, prev);
113
+ else el.removeAttribute(modeAttribute);
114
+ };
115
+ }, [mode, modeAttribute, target]);
116
+
117
+ const ctx = useMemo<ThemeContextValue>(() => {
118
+ const active = (mode && resolved.modes[mode]) || resolved.base;
119
+ const literals = new Map(active.map((t) => [t.path, formatCssValue(t)]));
120
+ return {
121
+ mode,
122
+ setMode,
123
+ modes: Object.keys(resolved.modes),
124
+ token: (path) => `var(${cssVarName(path, prefix)})`,
125
+ value: (path) => literals.get(path),
126
+ };
127
+ }, [mode, resolved, prefix]);
128
+
129
+ return <ThemeContext.Provider value={ctx}>{children}</ThemeContext.Provider>;
130
+ }
131
+
132
+ /** The full theme context (mode, setMode, token/value lookups). */
133
+ export function useTheme(): ThemeContextValue {
134
+ const ctx = useContext(ThemeContext);
135
+ if (!ctx) throw new Error("useTheme must be used within a <ThemeProvider>.");
136
+ return ctx;
137
+ }
138
+
139
+ /** `[mode, setMode]` — the active mode and a switcher. */
140
+ export function useThemeMode(): [string | undefined, (mode: string | undefined) => void] {
141
+ const { mode, setMode } = useTheme();
142
+ return [mode, setMode];
143
+ }
144
+
145
+ /** A token's CSS-variable reference: `useToken("color.accent")` → `"var(--color-accent)"`. */
146
+ export function useToken(path: string): string {
147
+ return useTheme().token(path);
148
+ }
149
+
150
+ /**
151
+ * A token's resolved *literal* value in the active mode (`"#4F6BFF"`). Use on
152
+ * platforms without CSS variables (React Native); on the web prefer
153
+ * {@link useToken} so mode switches don't re-render.
154
+ */
155
+ export function useTokenValue(path: string): string | undefined {
156
+ return useTheme().value(path);
157
+ }
package/tokens.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `rastack/tokens` — the design-system authoring DSL and pure token engine.
3
+ *
4
+ * Author your design system in TypeScript with {@link defineTokens} + {@link t},
5
+ * following the industry-standard W3C Design Tokens (DTCG) format. `rastack
6
+ * tokens` compiles it to CSS custom properties + a typed theme; `rastack/theme`
7
+ * applies it at runtime. Filesystem-free, so it also runs in the browser.
8
+ */
9
+ export * from "./src/tokens";
package/tsconfig.json CHANGED
@@ -11,6 +11,9 @@
11
11
  ],
12
12
  "outDir": "dist",
13
13
  "rootDir": "src",
14
+ "paths": {
15
+ "rastack/tokens": ["./tokens.ts"]
16
+ },
14
17
  "declaration": true,
15
18
  "esModuleInterop": true,
16
19
  "forceConsistentCasingInFileNames": true,