@theme-registry/refract 0.1.2 → 0.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theme-registry/refract",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "The framework-agnostic, format-neutral theme compiler core — build a ThemeModel from a RawTheme, then lower it with any adapter package (CSS, SCSS, JSON, styled-components).",
5
5
  "author": {
6
6
  "name": "Petyo Stoyanov",
@@ -39,6 +39,9 @@ patch. Full narrative reference lives in [`docs/authoring.md`](https://github.co
39
39
 
40
40
  ## Route to the right guide
41
41
 
42
+ - **No theme yet, starting from nothing** → **theme-scaffold**. `refract create` generates a
43
+ contrast-checked starting palette, type scale and spacing ramp from one seed colour; don't
44
+ hand-write a first theme from a blank file. (It emits *tokens only* — no recipes.)
42
45
  - **Any recipe, states, `variants`, or `components` composition** → **recipes-and-composition**.
43
46
  Recipes work identically across every subsystem, so that vocabulary lives in one place.
44
47
  - **A specific token domain** (palettes, type scale, spacing, effects) → the domain guide above.
@@ -0,0 +1,130 @@
1
+ ---
2
+ name: theme-scaffold
3
+ description: Generate a starting RawTheme from one seed colour with `refract create`, or scaffold a whole publishable theme package with `npm create refract-theme`. Use when there is no theme yet and you need a defensible starting palette, type scale and spacing ramp rather than a blank file. Triggers: "refract create", "create a theme", "new theme", "scaffold a theme", "starter theme", "generate a palette", "npm create refract-theme", "new design system".
4
+ tier: core
5
+ ---
6
+
7
+ # Scaffolding a theme
8
+
9
+ Two commands turn a seed colour into a real `RawTheme`. Use them instead of hand-writing a first
10
+ theme from nothing — the output clears a contrast bar, which a hand-rolled first palette usually
11
+ doesn't.
12
+
13
+ | Situation | Command | Writes |
14
+ | --- | --- | --- |
15
+ | Project exists, needs a theme | `refract create` | `theme.raw.(ts\|js\|json)` |
16
+ | Nothing exists yet | `npm create refract-theme my-theme` | a whole publishable package |
17
+ | Tokens already exist elsewhere | `refract import tokens.json` | see **dtcg-import** |
18
+
19
+ Both run the **same interview** and the same generator. Neither changes the palette model: they call
20
+ the colour helpers the library already ships and write down what they return, **once**.
21
+
22
+ ## The commands
23
+
24
+ ```
25
+ refract create # interactive
26
+ refract create --yes # every default, asks nothing
27
+ refract create --seed "#4c6ef5" --colors 3 --scheme triadic --feel editorial --format json
28
+ ```
29
+
30
+ Then wire the build — `refract init` **detects** the theme file and imports it:
31
+
32
+ ```
33
+ refract init # finds theme.raw.*, writes a config that imports it
34
+ refract build
35
+ ```
36
+
37
+ Flags mirror every prompt: `--seed`, `--colors <n|list>`, `--scheme`, `--manual`, `--feel`,
38
+ `--ratio`, `--base-size`, `--contrast <AA|AAA|none>`, `--reset`, `--format <ts|js|json>`, `--out`,
39
+ `--no-semantics`, `--no-neutral`, `--no-shadows`, `--yes`, `--force`. Every prompt is
40
+ non-interactive-safe: with no TTY it takes the default rather than blocking, so this is safe in CI.
41
+
42
+ ## What you get — and what you don't
43
+
44
+ From one colour: brand palettes, semantic colours, a neutral ramp, shadow tints, a type scale with
45
+ derived leading and tracking, a spacing ramp, radius, elevation and motion — around 150 variables.
46
+
47
+ **No recipes.** This is the important one. The scaffold emits *tokens only*, so nothing composes
48
+ into a class list yet. Do not tell the user their buttons are themed — they have variables. Writing
49
+ recipes is the next step and it is theirs (or yours) to do: see **recipes-and-composition**.
50
+
51
+ Also absent by design: no `@keyframes` (nothing would reference one without a recipe), and no
52
+ components.
53
+
54
+ ## Decisions baked into the output
55
+
56
+ Know these before you edit a generated theme, or you'll "fix" things that are deliberate.
57
+
58
+ - **Brand colours 2–5 are top-level palettes, not variants.** `harmony` in a hand-authored theme
59
+ emits `primary-complement` as a single flat leaf; the scaffolder promotes each member to its own
60
+ family (`secondary`, `tertiary`, …) with a full `50…900` ladder, because a flat leaf can't carry a
61
+ UI.
62
+ - **`base` is the brand colour.** The ladder is absolute lightness, so the seed lands wherever it
63
+ falls (often between two stops) and is reachable only as `base`. The `50…900` stops are for
64
+ surfaces, borders and states. Nothing snaps — the hex the user typed survives.
65
+ - **Semantic colours are hue-anchored, not rotated.** Rotating off the seed would make "danger"
66
+ whatever lands at +150°; from a red seed that's green. success/info/warning/danger start from
67
+ fixed hues and borrow only the seed's character.
68
+ - **A contrast pass ran before the file was written.** Every text pairing was scored and failing
69
+ colours were darkened in OKLCH points until they cleared the bar. If you change a `base`, re-run
70
+ `refract audit` — it is easy to lose AA by hand.
71
+ - **Derived leading and tracking are named after the size step they were tuned for**
72
+ (`lineheight-4xl`, not `lineheight-tight`), so the pairing documents itself without a recipe:
73
+
74
+ ```css
75
+ font-size: var(--dt-typography-fontsize-4xl);
76
+ line-height: var(--dt-typography-lineheight-4xl);
77
+ letter-spacing: var(--dt-typography-letterspacing-4xl);
78
+ ```
79
+
80
+ ## Retuning a generated theme
81
+
82
+ The output is **literal where the value came from an opinion, declarative where the engine
83
+ synthesizes**. That's why retuning is usually one word, not a table:
84
+
85
+ | Want | Change |
86
+ | --- | --- |
87
+ | A different brand colour | `colors.primary.base` — every step re-derives |
88
+ | A tighter type scale | `typography.fontSize.ratio` → `"major-second"` |
89
+ | More generous spacing | the multipliers in `layout.spacing.steps` |
90
+ | Softer corners | `borders.radius.base` |
91
+
92
+ Do **not** replace `fontSize: { base, ratio }` with a hand-listed ladder, or `spacing: { base, step,
93
+ steps }` with literal pixels — that throws away the intent the engine uses to synthesize.
94
+
95
+ ## Gotchas
96
+
97
+ - **Re-running clobbers.** `refract create` refuses to overwrite an existing `theme.raw.*` without
98
+ `--force`. Don't pass `--force` over a theme the user has edited; generate to `--out` instead.
99
+ - **Format matters later, not now.** `.ts` · `.js` · `.json` compile to **byte-identical** output —
100
+ a scaffolded theme has no functions or recipes, so JSON loses nothing. It stops being true the
101
+ moment the theme needs a function-valued field (a custom scale `algorithm`); that requires `.ts`.
102
+ - **The spacing curve is linear on purpose.** `step: 4` keeps every stop on a 4px grid; the
103
+ geometric `ratio` curve gives `8 · 12 · 18 · 27 · 40.5` and is right for type, wrong for space.
104
+ - **`refract init` only skips its own starter palette when a theme file already exists.** In an
105
+ empty directory it still writes a self-contained config — so run `create` *before* `init`.
106
+
107
+ ## Programmatic use
108
+
109
+ The generator is exported, so tooling can call it without a TTY:
110
+
111
+ ```ts
112
+ import { scaffoldTheme, runCreate } from "@theme-registry/refract/build";
113
+
114
+ const { raw, report } = scaffoldTheme({ seed: "#4c6ef5", brandCount: 3, feel: "editorial" });
115
+ report.contrast; // per-palette: ratio before/after, the nudge applied, whether it cleared the bar
116
+ report.brand; // each brand palette with the hue rotation that produced it
117
+
118
+ runCreate({ seed: "#4c6ef5", cwd: ".", format: "ts" }); // generate + write in one call
119
+ ```
120
+
121
+ `scaffoldTheme` is pure — no filesystem, no prompting, no randomness — so the same answers always
122
+ produce the same theme. Its tunable curves (leading, tracking, semantic hue anchors, feel presets)
123
+ are named constants at the top of `build/scaffold.ts`; they are opinions, and they are meant to be
124
+ arguable.
125
+
126
+ ## Where to go next
127
+
128
+ **recipes-and-composition** to turn these tokens into classes · **colors** for the palette grammar
129
+ you're now editing · **build-config** for targets and emit modes · **overrides-and-child-themes** to
130
+ derive a second brand from the one you just generated.