@theme-registry/refract 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/LICENSE +21 -0
- package/dist/_createTheme-255ff48e.esm.js +2 -0
- package/dist/_createTheme-255ff48e.esm.js.map +1 -0
- package/dist/_createTheme-41c01033.cjs.js +2 -0
- package/dist/_createTheme-41c01033.cjs.js.map +1 -0
- package/dist/_errors-be768b25.cjs.js +2 -0
- package/dist/_errors-be768b25.cjs.js.map +1 -0
- package/dist/_errors-d75a5c74.esm.js +2 -0
- package/dist/_errors-d75a5c74.esm.js.map +1 -0
- package/dist/adapter-kit.cjs.js +2 -0
- package/dist/adapter-kit.cjs.js.map +1 -0
- package/dist/adapter-kit.d.ts +88 -0
- package/dist/adapter-kit.esm.js +2 -0
- package/dist/adapter-kit.esm.js.map +1 -0
- package/dist/build/auditCommand.d.ts +19 -0
- package/dist/build/buildCommand.d.ts +23 -0
- package/dist/build/cli.d.ts +2 -0
- package/dist/build/config.d.ts +58 -0
- package/dist/build/diff.d.ts +56 -0
- package/dist/build/diffCommand.d.ts +29 -0
- package/dist/build/emit.d.ts +20 -0
- package/dist/build/emitTheme.d.ts +39 -0
- package/dist/build/guide.d.ts +34 -0
- package/dist/build/importCommand.d.ts +42 -0
- package/dist/build/index.d.ts +33 -0
- package/dist/build/init.d.ts +32 -0
- package/dist/build/paths.d.ts +42 -0
- package/dist/build/skillsCommand.d.ts +53 -0
- package/dist/build/tokensCommand.d.ts +17 -0
- package/dist/build/vendor.d.ts +39 -0
- package/dist/build.cjs.js +2 -0
- package/dist/build.cjs.js.map +1 -0
- package/dist/build.esm.js +2 -0
- package/dist/build.esm.js.map +1 -0
- package/dist/cli.js +8250 -0
- package/dist/cli.js.map +1 -0
- package/dist/color-math.cjs.js +2 -0
- package/dist/color-math.cjs.js.map +1 -0
- package/dist/color-math.esm.js +2 -0
- package/dist/color-math.esm.js.map +1 -0
- package/dist/core/ThemeAdapter.d.ts +234 -0
- package/dist/core/createTheme.d.ts +96 -0
- package/dist/core/cssProperties.d.ts +8 -0
- package/dist/core/defineAdapter.d.ts +25 -0
- package/dist/core/derive/crossProperty.d.ts +31 -0
- package/dist/core/derive/index.d.ts +11 -0
- package/dist/core/derive/resolveTokens.d.ts +27 -0
- package/dist/core/errors.d.ts +24 -0
- package/dist/core/index.d.ts +16 -0
- package/dist/core/media/containers.d.ts +20 -0
- package/dist/core/media/defaults.d.ts +3 -0
- package/dist/core/media/descriptors.d.ts +12 -0
- package/dist/core/media/index.d.ts +11 -0
- package/dist/core/media/queries.d.ts +20 -0
- package/dist/core/model/buildModel.d.ts +116 -0
- package/dist/core/model/index.d.ts +14 -0
- package/dist/core/model/interpreted.d.ts +37 -0
- package/dist/core/model/mergeRuleSet.d.ts +68 -0
- package/dist/core/model/model.d.ts +260 -0
- package/dist/core/noopAdapter.d.ts +26 -0
- package/dist/core/normalize/index.d.ts +15 -0
- package/dist/core/normalize/properties.d.ts +20 -0
- package/dist/core/normalize/recipeResolver.d.ts +19 -0
- package/dist/core/normalize/recipes.d.ts +38 -0
- package/dist/core/normalize/responsive.d.ts +40 -0
- package/dist/core/normalize/types.d.ts +261 -0
- package/dist/core/rawTheme.d.ts +53 -0
- package/dist/core/ref.d.ts +13 -0
- package/dist/core/subsystem.d.ts +115 -0
- package/dist/core/types.d.ts +31 -0
- package/dist/core/units.d.ts +93 -0
- package/dist/dtcg/export.d.ts +48 -0
- package/dist/dtcg/import.d.ts +43 -0
- package/dist/dtcg/index.d.ts +21 -0
- package/dist/dtcg/parse.d.ts +6 -0
- package/dist/dtcg/types.d.ts +69 -0
- package/dist/dtcg.cjs.js +2 -0
- package/dist/dtcg.cjs.js.map +1 -0
- package/dist/dtcg.esm.js +2 -0
- package/dist/dtcg.esm.js.map +1 -0
- package/dist/index.cjs.js +2 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.esm.js +2 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/subsystems/animation/index.d.ts +13 -0
- package/dist/subsystems/animation/recipes.d.ts +20 -0
- package/dist/subsystems/animation/types.d.ts +52 -0
- package/dist/subsystems/borders/index.d.ts +10 -0
- package/dist/subsystems/borders/recipes.d.ts +29 -0
- package/dist/subsystems/borders/types.d.ts +50 -0
- package/dist/subsystems/colors/audit.d.ts +67 -0
- package/dist/subsystems/colors/colorInput.d.ts +17 -0
- package/dist/subsystems/colors/index.d.ts +12 -0
- package/dist/subsystems/colors/keywords.d.ts +9 -0
- package/dist/subsystems/colors/normalize.d.ts +26 -0
- package/dist/subsystems/colors/recipes.d.ts +21 -0
- package/dist/subsystems/colors/types.d.ts +98 -0
- package/dist/subsystems/colors/utils.d.ts +104 -0
- package/dist/subsystems/components/index.d.ts +13 -0
- package/dist/subsystems/components/recipes.d.ts +22 -0
- package/dist/subsystems/components/types.d.ts +46 -0
- package/dist/subsystems/effects/index.d.ts +9 -0
- package/dist/subsystems/effects/recipes.d.ts +24 -0
- package/dist/subsystems/effects/types.d.ts +63 -0
- package/dist/subsystems/effects/utils.d.ts +57 -0
- package/dist/subsystems/globals/index.d.ts +21 -0
- package/dist/subsystems/globals/presets.d.ts +33 -0
- package/dist/subsystems/globals/types.d.ts +48 -0
- package/dist/subsystems/index.d.ts +21 -0
- package/dist/subsystems/layout/index.d.ts +14 -0
- package/dist/subsystems/layout/normalize.d.ts +35 -0
- package/dist/subsystems/layout/recipes.d.ts +16 -0
- package/dist/subsystems/layout/structural.d.ts +37 -0
- package/dist/subsystems/layout/types.d.ts +154 -0
- package/dist/subsystems/typography/index.d.ts +9 -0
- package/dist/subsystems/typography/normalize.d.ts +17 -0
- package/dist/subsystems/typography/recipes.d.ts +21 -0
- package/dist/subsystems/typography/types.d.ts +49 -0
- package/package.json +83 -0
- package/skills/adapter-scaffold/SKILL.md +85 -0
- package/skills/adapter-usage/SKILL.md +75 -0
- package/skills/build-config/SKILL.md +77 -0
- package/skills/colors/SKILL.md +134 -0
- package/skills/consuming-the-output/SKILL.md +62 -0
- package/skills/dtcg-import/SKILL.md +76 -0
- package/skills/layout/SKILL.md +65 -0
- package/skills/overrides-and-child-themes/SKILL.md +70 -0
- package/skills/recipes-and-composition/SKILL.md +110 -0
- package/skills/theme-authoring/SKILL.md +107 -0
- package/skills/theme-foundations/SKILL.md +127 -0
- package/skills/troubleshooting/SKILL.md +97 -0
- package/skills/typography/SKILL.md +66 -0
- package/skills/visual-effects/SKILL.md +96 -0
- package/src/subsystems/colors/utils.ts +299 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: theme-foundations
|
|
3
|
+
description: The cross-cutting vocabulary shared by every refract subsystem — breakpoints, containers, the responsive/variant/target/modes/states override axes, and bare-string references. Read once; the domain guides (colors, typography, layout, …) build on it instead of re-explaining. Triggers: "breakpoints", "container queries / container sizes", "responsive override", "variant vs target", "dark mode / modes", "states (hover/disabled)", "how do refs work".
|
|
4
|
+
tier: core
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Theme foundations
|
|
8
|
+
|
|
9
|
+
Vocabulary reused across every subsystem. Learn it here so the domain guides can stay focused
|
|
10
|
+
on their own tokens. All of this is defined once in the core and validated on every reference.
|
|
11
|
+
|
|
12
|
+
## Literals vs references — the one rule
|
|
13
|
+
|
|
14
|
+
Whether a bare string is a token reference or a raw literal depends on the context:
|
|
15
|
+
|
|
16
|
+
- **Composition fields** — a recipe's cross-subsystem fields (`colors: "solid.brand"`) and a
|
|
17
|
+
colour recipe's declaration fields (`background: "brand"`, `color: "brand.text"`) — **compose
|
|
18
|
+
tokens**, so a bare string there is always a **reference** (lowers to `var(--…)`). Paths are
|
|
19
|
+
dotted and subsystem-scoped (`"colors.link"`); within a subsystem's own recipe the leading
|
|
20
|
+
segment is implied (`"brand"` inside a colors recipe).
|
|
21
|
+
- **The `css` block** and **`globals` element declarations** are **raw CSS**, so a bare string
|
|
22
|
+
there is a **literal** (`display: "flex"` just works). Mark the occasional token with the
|
|
23
|
+
**`ref()`** helper — `color: ref("colors.brand.text")` — or the JSON-safe **`{ ref: "…" }`**
|
|
24
|
+
object (for a `theme.raw.json`, which can't call a function).
|
|
25
|
+
- A plain **number** is always a literal.
|
|
26
|
+
- **Prefer `ref()` explicitly** when authoring — it makes the intent unmistakable. A `ref()` at a
|
|
27
|
+
token that doesn't exist fails loud at build; a bare literal is never validated.
|
|
28
|
+
- **Trust boundary:** a literal `css` value passes through to the output **verbatim** (refract
|
|
29
|
+
validates structure, not the content of raw declarations). Token values are re-serialized to a
|
|
30
|
+
canonical form, but the `css` block is passthrough — so if you compile an **untrusted** (user- or
|
|
31
|
+
agent-authored) theme and serve the result, sanitize its literal `css` values first, as you would
|
|
32
|
+
any untrusted CSS.
|
|
33
|
+
|
|
34
|
+
## Breakpoints — set them first
|
|
35
|
+
|
|
36
|
+
Top-level, min-width pixels:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
breakpoints: { xs: 0, sm: 576, md: 768, lg: 992, xl: 1280 } // the default set
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Every `responsive` entry references these **names**, so settle them before authoring anything
|
|
43
|
+
responsive. Threshold **units** (how `@media` widths render — `px | em | rem`) are a build-time
|
|
44
|
+
concern set on `createTheme({ media })` / the config, not in the raw theme.
|
|
45
|
+
|
|
46
|
+
## Containers — container queries
|
|
47
|
+
|
|
48
|
+
Top-level, opt-in. A container defines named sizes that recipe overrides target with
|
|
49
|
+
`{ container, size }` instead of `{ breakpoint }`:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
containers: {
|
|
53
|
+
card: { type: "inline-size", sizes: { sm: 320, md: 480 } },
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Use breakpoints for viewport-relative variation and containers for component-relative variation.
|
|
58
|
+
|
|
59
|
+
## The override axes
|
|
60
|
+
|
|
61
|
+
Both **properties** and **recipes** carry a `responsive: [...]` list. Each entry combines a
|
|
62
|
+
condition with a change:
|
|
63
|
+
|
|
64
|
+
- **Condition:** `breakpoint` (+ `query: "min" | "max" | "exact"`, default `min`),
|
|
65
|
+
`orientation`, or `container` + `size`.
|
|
66
|
+
- **Change (pick one):**
|
|
67
|
+
- a plain new value for the property/field;
|
|
68
|
+
- `variant: X` — reassign the base to reference sibling variant `X`;
|
|
69
|
+
- `target: X, …` — override variant `X`'s value specifically (not the base).
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
responsive: [
|
|
73
|
+
{ breakpoint: "md", query: "min", fontSize: "typography.fontSize.lg" }, // plain
|
|
74
|
+
{ breakpoint: "sm", query: "max", variant: "compact" }, // swap base → compact
|
|
75
|
+
{ breakpoint: "lg", target: "wide", padding: "layout.spacing.xl" }, // scope to variant "wide"
|
|
76
|
+
]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**variant vs target:** `variant` *swaps which variant the base points at*; `target` *edits one
|
|
80
|
+
named variant*. A recipe's `variants` map (see **recipes-and-composition**) desugars each variant
|
|
81
|
+
into a flat sibling, so `variant:` / `target:` references resolve against those siblings.
|
|
82
|
+
|
|
83
|
+
## modes — light/dark appearance
|
|
84
|
+
|
|
85
|
+
Two things share the name. A top-level **`modes` registry** declares which appearance modes exist
|
|
86
|
+
(`modes: ["dark", "light"]` — the default; declare more to use a custom mode like `hc`). And a
|
|
87
|
+
per-property **`modes` axis** (chiefly on colors) is a **LIST of override entries**, each
|
|
88
|
+
`{ mode, target?, …value }`: `mode` is the WHEN (validated against the registry), the optional
|
|
89
|
+
`target` scopes into a variant's var, and the value is a literal or a `{ ref?, modifiers }`
|
|
90
|
+
derivation. Adapters realize modes as conditional blocks (`prefers-color-scheme` media,
|
|
91
|
+
`[data-theme]` attribute, or both).
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
modes: ["dark", "light"], // top-level registry (optional; this is the default)
|
|
95
|
+
colors: {
|
|
96
|
+
surface: {
|
|
97
|
+
base: "#fff",
|
|
98
|
+
modes: [
|
|
99
|
+
{ mode: "dark", base: "#111" }, // a literal mode override
|
|
100
|
+
{ mode: "dark", modifiers: [{ darken: 10 }] }, // …or a derivation off the own base
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Because dark rides the **referenced token**, anything that refs `colors.surface` flips for free —
|
|
107
|
+
so `globals` element rules and recipes never need their own `modes` axis.
|
|
108
|
+
|
|
109
|
+
## states — recipe conditions
|
|
110
|
+
|
|
111
|
+
`states` is a **recipe-only** axis (hover, focus, disabled, …), a **LIST of override entries**,
|
|
112
|
+
each `{ state, target?, …deltas }`. The **allowed set is owned by the adapter** — an unknown state
|
|
113
|
+
throws at build. `state` is the WHEN; the optional `target` scopes the override onto a recipe
|
|
114
|
+
variant's sibling (`<item>-<variant>`), so two same-state entries can behave differently per
|
|
115
|
+
variant.
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
states: [
|
|
119
|
+
{ state: "hover", background: "brand.dark" },
|
|
120
|
+
{ state: "disabled", opacity: 0.5 },
|
|
121
|
+
{ state: "hover", target: "lg", background: "brand" }, // a hover just for the `lg` variant sibling
|
|
122
|
+
]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
modes / states / responsive are all **arrays of overrides over one spine** — WHEN
|
|
126
|
+
(`mode`/`state`/`breakpoint`) · WHERE (`target`) · WHAT (the value/deltas). See
|
|
127
|
+
**recipes-and-composition** for how states compose with variants and responsive entries.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: troubleshooting
|
|
3
|
+
description: Diagnose refract build errors — map createTheme's validation messages (unknown token/breakpoint/variant/state, cyclic or duplicate recipes, mode overrides nothing) to their cause and fix. Use when createTheme or refract build throws. Triggers: "createTheme throws", "references unknown token/breakpoint/variant", "state not allowed", "cyclic recipe", "duplicate recipe name", "overrides nothing".
|
|
4
|
+
tier: optional
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Troubleshooting
|
|
8
|
+
|
|
9
|
+
refract validates the theme up front and throws a message that **names the offending reference**.
|
|
10
|
+
Read the quoted name first — it points straight at the authoring mistake. Common errors and fixes:
|
|
11
|
+
|
|
12
|
+
Every authoring/build error is a **`RefractError`** with a stable machine `code` (`err.code`, e.g.
|
|
13
|
+
`REFRACT_E_COLOR_INPUT`, `REFRACT_E_STEPS`, `REFRACT_E_NAMING`) you can branch on without matching the
|
|
14
|
+
message text. Post-build reference validation **collects all failures** and throws them at once as
|
|
15
|
+
`REFRACT_E_VALIDATION` — `err.failures` is the array of every bad reference, so fix them in one pass:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { RefractError } from "@theme-registry/refract";
|
|
19
|
+
try { createTheme(raw, { adapter }); }
|
|
20
|
+
catch (e) {
|
|
21
|
+
if (e instanceof RefractError && e.code === "REFRACT_E_VALIDATION") console.error(e.failures);
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## `references unknown token '<ref>'`
|
|
26
|
+
|
|
27
|
+
> `components.<group>.<variant>: css '<prop>' references unknown token '<ref>' — check the token path, or use a bare string / number for a raw CSS value`
|
|
28
|
+
> `globals element '<sel>': '<prop>' references unknown token '<ref>' — …`
|
|
29
|
+
|
|
30
|
+
A `ref("…")` (or `{ ref: "…" }`) pointed at a token that doesn't exist. **Fix the path** — e.g.
|
|
31
|
+
`ref("colors.link")`, `ref("typography.fontSize.lg")`. (A raw CSS value doesn't need a ref at all
|
|
32
|
+
— inside a `css` block a bare string is already a literal: `cursor: "pointer"`.) See the
|
|
33
|
+
literals-vs-references rule in **theme-foundations**.
|
|
34
|
+
|
|
35
|
+
## `references unknown breakpoint "<name>"`
|
|
36
|
+
|
|
37
|
+
> `Responsive entry … references unknown breakpoint "<name>".`
|
|
38
|
+
> `Responsive entry … is missing a "breakpoint" value.`
|
|
39
|
+
|
|
40
|
+
A `responsive` entry names a breakpoint not in the top-level `breakpoints` map (or omits one).
|
|
41
|
+
Add the breakpoint, or fix the name. Set `breakpoints` first — see **theme-foundations**.
|
|
42
|
+
|
|
43
|
+
## `references unknown variant "<name>"` / `unknown target "<name>"`
|
|
44
|
+
|
|
45
|
+
A `variant:` / `target:` in a responsive entry points at a variant that doesn't exist on that
|
|
46
|
+
property/recipe. Define the variant, or fix the reference. Remember a recipe's `variants` map
|
|
47
|
+
desugars to siblings that `variant:`/`target:` can then reach — see **recipes-and-composition**.
|
|
48
|
+
|
|
49
|
+
## `references unknown state "<name>"`
|
|
50
|
+
|
|
51
|
+
> `Responsive recipe entry … references unknown state "<name>".`
|
|
52
|
+
|
|
53
|
+
The state isn't in the **adapter's** allowed set. Use a state the adapter supports (CSS knows
|
|
54
|
+
`hover`/`focus`/`disabled`/…), or choose an adapter that renders it. States are adapter-owned, not
|
|
55
|
+
authored — see **theme-foundations** → states.
|
|
56
|
+
|
|
57
|
+
## `Unknown recipe property "<prop>" in <path>`
|
|
58
|
+
|
|
59
|
+
> `Unknown recipe property "<prop>" in <path> — expected a CSS property or a reserved key (variant, target, state, breakpoint, query, orientation, container, size).`
|
|
60
|
+
|
|
61
|
+
A declaration key that's neither a real CSS property nor a reserved recipe key — almost always a
|
|
62
|
+
typo: `ref:` where you meant a `variant:` swap, or `colr:` for `color:`. refract rejects it so it
|
|
63
|
+
can't ship as a stray literal declaration. Use the CSS property, or the reserved key you meant.
|
|
64
|
+
|
|
65
|
+
## `Recipe variant "<name>" is not defined` / `Cyclic recipe reference`
|
|
66
|
+
|
|
67
|
+
> `Recipe variant "<name>" is not defined in "<group>".`
|
|
68
|
+
> `Cyclic recipe reference in "<group>": <cycle>`
|
|
69
|
+
|
|
70
|
+
A recipe references a sibling that doesn't exist, or two recipes reference each other in a loop.
|
|
71
|
+
Fix the composition graph so references point at real, acyclic recipes.
|
|
72
|
+
|
|
73
|
+
## `produced a duplicate recipe name "<name>"`
|
|
74
|
+
|
|
75
|
+
> `Recipe variant expansion … produced a duplicate recipe name "<name>" — a desugared "<recipe>-<variant>" collides with an existing sibling recipe.`
|
|
76
|
+
|
|
77
|
+
A `variants` entry desugars to `<recipe>-<variant>`, which collides with a hand-authored sibling.
|
|
78
|
+
Rename the variant or the sibling recipe.
|
|
79
|
+
|
|
80
|
+
## `Appearance mode "<path>" overrides nothing` / `Unable to resolve base value`
|
|
81
|
+
|
|
82
|
+
> `Appearance mode "<path>" overrides nothing.`
|
|
83
|
+
> `Unable to resolve base value…`
|
|
84
|
+
|
|
85
|
+
A `modes` block redefines a field the property never had, or a property has no resolvable base
|
|
86
|
+
value. Give the property a real base, and make each mode redefine an existing field — see
|
|
87
|
+
**theme-foundations** → modes.
|
|
88
|
+
|
|
89
|
+
## General
|
|
90
|
+
|
|
91
|
+
- The message's quoted path is the location — start there, fix the *theme*, never the engine.
|
|
92
|
+
- If the shape itself is wrong, the `satisfies RawTheme` typecheck usually catches it before
|
|
93
|
+
runtime — check the red squiggle first.
|
|
94
|
+
- **Validate against the real adapters.** With an MCP server connected, `validateTheme(candidate)`
|
|
95
|
+
builds the edit against every configured target and returns **all** problems at once (per target) —
|
|
96
|
+
it catches adapter-level rules (unknown state, naming collision) a bare core build misses. See
|
|
97
|
+
**theme-authoring → Agent tools**.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: typography
|
|
3
|
+
description: Author the `typography` slice of a refract theme — the open map of type properties (font families, weights, line-height, letter-spacing, text props) and the `fontSize` modular scale synthesized from a base + named ratio. Use when writing or tuning the type domain. Triggers: "type scale", "modular scale / ratio", "font sizes", "fontFamily / fontWeight / lineHeight", "typography recipe".
|
|
4
|
+
tier: core
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Typography
|
|
8
|
+
|
|
9
|
+
The `typography` key is an **open** map: each key is a type property holding a value, plus the
|
|
10
|
+
reserved `recipes` block. Cross-cutting axes (`responsive`, `variants`, refs) are in
|
|
11
|
+
**theme-foundations**; recipe mechanics are in **recipes-and-composition**. Exhaustive options live
|
|
12
|
+
in `docs/authoring.md`.
|
|
13
|
+
|
|
14
|
+
## Type properties
|
|
15
|
+
|
|
16
|
+
Properties are heterogeneous — a value is `string | number`, or an extended `{ base, … }`:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
typography: {
|
|
20
|
+
fontFamily: { base: "Inter, sans-serif", variants: { mono: "ui-monospace, monospace" } },
|
|
21
|
+
fontWeight: { base: 400, variants: { medium: 500, bold: 700 } },
|
|
22
|
+
lineHeight: { base: 1.5, variants: { tight: 1.25 } },
|
|
23
|
+
letterSpacing: { base: "0", variants: { wide: "0.05em" } },
|
|
24
|
+
// text props also live here: fontStyle, textTransform, textDecoration, textAlign
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Each `variants` entry is named and referenced elsewhere by bare name (see **recipes-and-composition**).
|
|
29
|
+
|
|
30
|
+
## fontSize — the modular scale
|
|
31
|
+
|
|
32
|
+
Give `fontSize` a `base` plus a named `ratio` and refract synthesizes the scale steps as variants —
|
|
33
|
+
each `base × ratio^step`, rounded to `precision`:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
fontSize: {
|
|
37
|
+
base: 16,
|
|
38
|
+
ratio: "minor-third", // one of the named ratios below
|
|
39
|
+
precision: 4, // decimal places (default 4)
|
|
40
|
+
baseFontSize: 16, // scale origin if it differs from `base` (defaults to `base`)
|
|
41
|
+
unit: "rem", // "px" | "rem"
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Generated step keys and their exponents: `xs` (−2), `sm` (−1), `md` (0), `lg` (1), `xl` (2), `2xl`
|
|
46
|
+
(3), `3xl` (4), `4xl` (5). Named ratios:
|
|
47
|
+
|
|
48
|
+
| Name | Factor | | Name | Factor |
|
|
49
|
+
|---|---|---|---|---|
|
|
50
|
+
| `minor-second` | 1.067 | | `augmented-fourth` | 1.414 |
|
|
51
|
+
| `major-second` | 1.125 | | `perfect-fifth` | 1.5 |
|
|
52
|
+
| `minor-third` | 1.2 | | `golden` | 1.618 |
|
|
53
|
+
| `major-third` | 1.25 | | | |
|
|
54
|
+
| `perfect-fourth` | 1.333 | | | |
|
|
55
|
+
|
|
56
|
+
Without a (recognized) `ratio`, `fontSize` passes through untouched. Author-declared `variants` win
|
|
57
|
+
over synthesized steps and **seed the chain** — a hand-pinned step becomes the origin the next
|
|
58
|
+
generated step compounds from.
|
|
59
|
+
|
|
60
|
+
## recipes — typography recipes
|
|
61
|
+
|
|
62
|
+
The reserved `recipes` block declares typographic properties (`fontFamily`, `fontSize`, `fontWeight`,
|
|
63
|
+
`lineHeight`, `letterSpacing`, `fontStyle`, `textTransform`, `textDecoration`, `textAlign`, plus any
|
|
64
|
+
extra passed through). Each value names a **variant** of the matching property (`fontSize: "3xl"`,
|
|
65
|
+
`fontWeight: "bold"`, or `"base"` for the base value). Recipe structure (states, responsive,
|
|
66
|
+
composition) is in **recipes-and-composition**.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: visual-effects
|
|
3
|
+
description: Author the three visual-effect slices of a refract theme — `effects` (shadows, transitions, blur, opacity, z-index), `borders` (stroke geometry: width/style/offset/radius), and `animation` (motion tokens + keyframes + animation-shorthand recipes). Use when writing shadows, transitions, strokes, or motion. Triggers: "box shadow", "transition tokens", "border / outline / radius", "keyframes", "animation recipe", "blur / opacity / z-index".
|
|
4
|
+
tier: core
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Visual effects — effects · borders · animation
|
|
8
|
+
|
|
9
|
+
Three sibling subsystems, each with its own top-level key. Cross-cutting axes (`responsive`,
|
|
10
|
+
`variants`, `modes`, refs) are in **theme-foundations**; recipe mechanics in
|
|
11
|
+
**recipes-and-composition**. Exhaustive options live in `docs/authoring.md`.
|
|
12
|
+
|
|
13
|
+
## effects
|
|
14
|
+
|
|
15
|
+
Open map. `shadow` and `transitions` are **structured object-leaf** properties — format-neutral flat
|
|
16
|
+
leaves, never raw CSS. `blur` / `opacity` / `zIndex` are plain scalars.
|
|
17
|
+
|
|
18
|
+
The flat leaf fields sit at the property top level and **are** the (single-layer) base — there is
|
|
19
|
+
**no `base` key**. A `variants` entry — and the value of each `modes` / `responsive` entry (both are
|
|
20
|
+
lists of `{ mode|breakpoint, …leaf }`, per **theme-foundations**) — is a full leaf value, or an
|
|
21
|
+
**array** of leaves for a multi-layer value. The keyword `"none"` means no shadow / no transition.
|
|
22
|
+
**Raw CSS strings are rejected** (only `"none"` is accepted).
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
effects: {
|
|
26
|
+
shadow: {
|
|
27
|
+
offsetY: 2, blur: 8, color: "colors.shadow", // top-level leaf = the base
|
|
28
|
+
variants: {
|
|
29
|
+
lg: [ // array = multi-layer
|
|
30
|
+
{ offsetY: 4, blur: 12, color: "colors.shadow" },
|
|
31
|
+
{ offsetY: 1, blur: 3, color: "colors.shadow.a20" },
|
|
32
|
+
],
|
|
33
|
+
none: "none",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
transitions: { property: "opacity", duration: 200, timingFunction: "ease-out", delay: 0 },
|
|
37
|
+
blur: 8, opacity: 1, zIndex: 10,
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Shadow leaf fields: `offsetX`, `offsetY`, `blur`, `spread` (a bare number is a **deferred** length
|
|
42
|
+
resolved by the unit pass; a `"1px"` / `"0.5rem"` string is **pinned**), `color` (a `colors.*`
|
|
43
|
+
**ref** — translucency comes from an `alpha` colour variant, e.g. `colors.shadow.a20`, never a shadow
|
|
44
|
+
field), and `inset` (boolean). Transition part fields: `property` (**required**), `duration` (ms),
|
|
45
|
+
`timingFunction` (keyword or `cubic-bezier(...)`), `delay` (ms).
|
|
46
|
+
|
|
47
|
+
Reserved `recipes` (EffectsRecipeProps): `boxShadow` (→ `shadow`), `transition` (→ `transitions`),
|
|
48
|
+
`blur` (lowers to `filter: blur(var(--…))`), `opacity`, `zIndex` — each naming a variant.
|
|
49
|
+
|
|
50
|
+
## borders
|
|
51
|
+
|
|
52
|
+
Open map of **stroke geometry only** — colour is never a borders token. Properties: `width` (px
|
|
53
|
+
number), `style` (string), `offset` (px number, outline only), `radius` (px number, or a string like
|
|
54
|
+
`"9999px"`).
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
borders: {
|
|
58
|
+
width: { base: 1, variants: { thick: 2 } },
|
|
59
|
+
style: { base: "solid", variants: { dashed: "dashed" } },
|
|
60
|
+
radius: { base: 4, variants: { pill: "9999px" } },
|
|
61
|
+
offset: { base: 2 },
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Reserved `recipes` (BordersRecipeProps): `as` (`"border"` default | `"outline"`) and `side`
|
|
66
|
+
(`top`/`right`/`bottom`/`left`, border only) are **modifiers** that route each aspect to its
|
|
67
|
+
longhand. The geometry aspects `width` / `style` / `offset` / `radius` name a **variant** of the
|
|
68
|
+
matching property by bare name (`"thick"` → `borders.width.thick`). `color` is a **value-level
|
|
69
|
+
`colors.*` ref** (`"colors.primary"`), never a borders token.
|
|
70
|
+
|
|
71
|
+
## animation
|
|
72
|
+
|
|
73
|
+
Motion tokens `duration` (ms number) / `easing` (string) / `delay` (ms number) are regular
|
|
74
|
+
properties (base + variants). The reserved `keyframes` key holds named definitions; `recipes`
|
|
75
|
+
composes them into an `animation:` shorthand.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
animation: {
|
|
79
|
+
duration: { base: 200, variants: { fast: 120, slow: 400 } },
|
|
80
|
+
easing: { base: "ease-out", variants: { spring: "cubic-bezier(.5,1.5,.5,1)" } },
|
|
81
|
+
keyframes: {
|
|
82
|
+
fadeInUp: {
|
|
83
|
+
from: { opacity: 0, transform: "translateY(20px)" }, // literal or { ref: "colors.surface" }
|
|
84
|
+
to: { opacity: 1, transform: "translateY(0)" },
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
A keyframe definition maps a step selector (`from` / `to` / `"0%"` / `"50%"` / grouped `"0%, 100%"`)
|
|
91
|
+
to declarations — kebab-case CSS property → a **literal** value or a **`{ ref }`** (resolved late so a
|
|
92
|
+
keyframe can animate a themed value). Authoring order is preserved.
|
|
93
|
+
|
|
94
|
+
Reserved `recipes` (AnimationRecipeProps): `keyframes` names a keyframe; `duration` / `easing` /
|
|
95
|
+
`delay` name a **variant** of the matching motion token; `iterationCount` / `direction` / `fillMode`
|
|
96
|
+
/ `playState` pass through — all composed into one `animation:` shorthand.
|