@pixerate/editor 0.223.6 → 0.223.8
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/dist/tools/skill-evals.d.cts +23 -1
- package/dist/tools/skill-evals.d.ts +23 -1
- package/package.json +1 -1
- package/skills/README.md +8 -3
- package/skills/pixerate-accessibility-audit/SKILL.md +98 -0
- package/skills/pixerate-accessibility-audit/evals/button-label-contrast.json +97 -0
- package/skills/pixerate-accessibility-audit/evals/distinct-for-cvd.json +160 -0
- package/skills/pixerate-accessibility-audit/evals/icon-non-text-contrast.json +109 -0
- package/skills/pixerate-brand-application/SKILL.md +99 -0
- package/skills/pixerate-brand-application/evals/apply-palette.json +148 -0
- package/skills/pixerate-brand-application/evals/readable-on-brand.json +104 -0
- package/skills/pixerate-brand-application/evals/snap-to-palette.json +148 -0
- package/skills/pixerate-color/SKILL.md +8 -0
- package/skills/pixerate-effect-authoring/SKILL.md +7 -0
- package/skills/pixerate-effect-look-development/SKILL.md +96 -0
- package/skills/pixerate-effect-look-development/evals/fade-in.json +55 -0
- package/skills/pixerate-effect-look-development/evals/seamless-loop.json +66 -0
- package/skills/pixerate-effect-look-development/evals/subtle-preset.json +49 -0
- package/skills/pixerate-generation/SKILL.md +7 -0
- package/skills/pixerate-generation-budgeted-batch/SKILL.md +99 -0
- package/skills/pixerate-generation-budgeted-batch/evals/drafts-within-budget.json +101 -0
- package/skills/pixerate-generation-budgeted-batch/evals/right-model-for-format.json +124 -0
- package/skills/pixerate-generation-budgeted-batch/evals/vary-earlier.json +101 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pixerate-brand-application
|
|
3
|
+
description: The brand application workflow across Pixerate editors - applying a brand palette, type and tokens to a design, chart or document, mapping each element to its brand role (background, primary, accent, text), snapping near-miss colours to the palette, keeping text readable on brand colours, deriving tints and dark-mode variants without leaving the brand, and running the house-style checks. Use when asked to brand, rebrand, apply a brand, style guide, palette, theme or design tokens, make something on-brand, snap colours to a palette, or check brand consistency, or when check_color_contrast, check_color_palette, blend_colors or a check_*_style tool is available. Load with pixerate-color, which lists the colour tools, and the skill for the editor that holds the design.
|
|
4
|
+
metadata:
|
|
5
|
+
kind: workflow
|
|
6
|
+
reference: pixerate-color
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Applying a brand
|
|
10
|
+
|
|
11
|
+
A brand is a small set of decisions: a palette with roles (background, primary, accent, text),
|
|
12
|
+
type, and rules for using them. Applying it means mapping every element of a design to a role
|
|
13
|
+
and giving it that role's value exactly, not something close. Colours outside the palette are
|
|
14
|
+
either snapped to the nearest brand colour or derived from one deliberately (a tint, a dark-mode
|
|
15
|
+
variant), and every text pairing still meets contrast.
|
|
16
|
+
|
|
17
|
+
This skill is the method. `pixerate-color` has the colour tools; the design's editor applies the
|
|
18
|
+
values (`vector.setStyle`, `update_chart_series`), design tokens live in `pixerate-tokens`, type
|
|
19
|
+
in `pixerate-typography`, and a host's house style in `check_*_style` tools.
|
|
20
|
+
|
|
21
|
+
## Goal and quality bar
|
|
22
|
+
|
|
23
|
+
- Every colour in the design is a brand colour, or a documented derivative of one (a tint, a
|
|
24
|
+
shade, a dark-mode pair).
|
|
25
|
+
- Each element has the colour of its role: backgrounds, primary surfaces, accents, text.
|
|
26
|
+
- Text on every brand surface meets WCAG AA (see `pixerate-accessibility-audit`).
|
|
27
|
+
- Values are exact (`#1d3557`, not `#1f3759`), referenced from tokens where the editor supports
|
|
28
|
+
them.
|
|
29
|
+
- Layout, geometry and content are unchanged by the rebrand.
|
|
30
|
+
- House-style checks pass where the host provides them.
|
|
31
|
+
|
|
32
|
+
## Inputs to confirm
|
|
33
|
+
|
|
34
|
+
- **The palette with roles**, or a tokens set (`pixerate-tokens`), and the type.
|
|
35
|
+
- **Which elements take which role** when it is not obvious.
|
|
36
|
+
- **What to do when a pairing cannot meet contrast** (another palette colour, larger text, or an
|
|
37
|
+
approved derivative).
|
|
38
|
+
|
|
39
|
+
## Working method
|
|
40
|
+
|
|
41
|
+
1. **Read the design** with its editor: every element's fills, strokes and text colours.
|
|
42
|
+
**Check:** each element has a role.
|
|
43
|
+
2. **Map roles to brand values.** Write the mapping (background → `#f1faee`, panel → primary,
|
|
44
|
+
badge → accent) before changing anything.
|
|
45
|
+
3. **Apply exactly**, in one batch where the editor allows it (`run_commands` of
|
|
46
|
+
`vector.setStyle`, one `update_chart` call).
|
|
47
|
+
**Check:** reading the design again shows only brand values, and geometry unchanged.
|
|
48
|
+
4. **Snap near-misses.** For colours close to the palette, pick the nearest brand colour by
|
|
49
|
+
perceptual distance (OKLab): `inspect_color` gives each colour's OKLCH for comparison.
|
|
50
|
+
5. **Readable text.** `check_color_contrast` for each text-on-surface pair; when white fails on a
|
|
51
|
+
light brand colour, use the darkest brand colour for the text.
|
|
52
|
+
**Check:** every text pair passes AA.
|
|
53
|
+
6. **Derivatives.** Tints and shades with `blend_colors` in OKLab from a brand colour; dark-mode
|
|
54
|
+
pairs with `derive_dual_color`. Name and report them.
|
|
55
|
+
7. **House style.** Run the host's `check_*_style` tools (for example `check_chart_style`) and fix
|
|
56
|
+
what they report.
|
|
57
|
+
|
|
58
|
+
## Decision table
|
|
59
|
+
|
|
60
|
+
| Situation | Approach |
|
|
61
|
+
| --- | --- |
|
|
62
|
+
| A colour close to a brand colour | Snap to the nearest by OKLab distance |
|
|
63
|
+
| A needed colour not in the palette | Tint or shade a brand colour (`blend_colors`), and say so |
|
|
64
|
+
| White text fails on a light brand colour | The darkest brand colour for text |
|
|
65
|
+
| No palette colour passes on a surface | Report it; propose larger text or another surface colour |
|
|
66
|
+
| A dark mode is needed | `derive_dual_color` from each brand colour |
|
|
67
|
+
| Charts | Series colours from the palette in its order, checked with `check_chart_colors` |
|
|
68
|
+
| Tokens available | Apply token references, not raw values |
|
|
69
|
+
|
|
70
|
+
## Recipes
|
|
71
|
+
|
|
72
|
+
**Apply a palette.** Read element ids → one `run_commands` with a `vector.setStyle` per element
|
|
73
|
+
and its brand `fill` → read again to confirm values and bounds.
|
|
74
|
+
|
|
75
|
+
**Snap to the palette.** `inspect_color` each off-brand colour and each brand colour; pick the
|
|
76
|
+
smallest OKLab distance; apply.
|
|
77
|
+
|
|
78
|
+
**Readable badge text.** `check_color_contrast` white and the darkest brand colour on the badge;
|
|
79
|
+
apply the one that passes.
|
|
80
|
+
|
|
81
|
+
## Pitfalls
|
|
82
|
+
|
|
83
|
+
- **Close is not on-brand.** `#1f3759` beside `#1d3557` looks the same and fails every audit.
|
|
84
|
+
- **Inventing colours** to fix contrast leaves the brand; use a palette colour or an approved
|
|
85
|
+
derivative.
|
|
86
|
+
- **Some pairings can never pass**: no text colour reaches 4.5:1 on a mid-tone red or blue.
|
|
87
|
+
Report it instead of breaking the brand silently.
|
|
88
|
+
- **Rebranding moves things**: change only colours and type, not geometry or content.
|
|
89
|
+
- **Accent overuse**: accents mark what matters; most of a design is background and primary.
|
|
90
|
+
- **Hue-only palettes** for charts fail colour-blind readers; check them.
|
|
91
|
+
|
|
92
|
+
## Tools used
|
|
93
|
+
|
|
94
|
+
- `inspect_color`: parse colours and compare them in OKLCH
|
|
95
|
+
- `check_color_contrast`: text on brand surfaces
|
|
96
|
+
- `check_color_palette`: brand colours used for meaning, under colour-vision deficiencies
|
|
97
|
+
- `blend_colors`: tints and shades of brand colours
|
|
98
|
+
- `derive_dual_color`, `check_dual_color_contrast`: dark-mode variants
|
|
99
|
+
- `suggest_color_harmony`: supporting colours when the palette has gaps
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Apply our brand palette to this layout: the background is #f1faee, the big panel is primary #1d3557, and the small badge is accent #e63946.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "vector",
|
|
5
|
+
"width": 200,
|
|
6
|
+
"height": 120,
|
|
7
|
+
"colorTools": true,
|
|
8
|
+
"setup": [
|
|
9
|
+
{
|
|
10
|
+
"id": "vector.addShape",
|
|
11
|
+
"params": {
|
|
12
|
+
"kind": "rect",
|
|
13
|
+
"x": 0,
|
|
14
|
+
"y": 0,
|
|
15
|
+
"width": 200,
|
|
16
|
+
"height": 120,
|
|
17
|
+
"name": "Background",
|
|
18
|
+
"fill": "#ffffff",
|
|
19
|
+
"stroke": null
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "vector.addShape",
|
|
24
|
+
"params": {
|
|
25
|
+
"kind": "rect",
|
|
26
|
+
"x": 20,
|
|
27
|
+
"y": 20,
|
|
28
|
+
"width": 160,
|
|
29
|
+
"height": 80,
|
|
30
|
+
"name": "Panel",
|
|
31
|
+
"fill": "#334455",
|
|
32
|
+
"stroke": null
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "vector.addShape",
|
|
37
|
+
"params": {
|
|
38
|
+
"kind": "ellipse",
|
|
39
|
+
"x": 150,
|
|
40
|
+
"y": 10,
|
|
41
|
+
"width": 30,
|
|
42
|
+
"height": 30,
|
|
43
|
+
"name": "Badge",
|
|
44
|
+
"fill": "#ff6600",
|
|
45
|
+
"stroke": null
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
"checks": [
|
|
51
|
+
{
|
|
52
|
+
"why": "each part in its brand colour",
|
|
53
|
+
"tool": "run_command",
|
|
54
|
+
"args": {
|
|
55
|
+
"id": "vector.list"
|
|
56
|
+
},
|
|
57
|
+
"expect": {
|
|
58
|
+
"result": {
|
|
59
|
+
"nodes": {
|
|
60
|
+
"$expr": "(() => { const f = (n) => (v.find((x) => x.name === n).fill || '').toLowerCase(); return f('Background') === '#f1faee' && f('Panel') === '#1d3557' && f('Badge') === '#e63946'; })()"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"why": "nothing moved",
|
|
67
|
+
"tool": "run_command",
|
|
68
|
+
"args": {
|
|
69
|
+
"id": "vector.list"
|
|
70
|
+
},
|
|
71
|
+
"expect": {
|
|
72
|
+
"result": {
|
|
73
|
+
"nodes": {
|
|
74
|
+
"$some": {
|
|
75
|
+
"name": "Badge",
|
|
76
|
+
"bounds": {
|
|
77
|
+
"x": 150,
|
|
78
|
+
"y": 10,
|
|
79
|
+
"width": 30,
|
|
80
|
+
"height": 30
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
"solution": [
|
|
89
|
+
{
|
|
90
|
+
"tool": "run_command",
|
|
91
|
+
"args": {
|
|
92
|
+
"id": "vector.list"
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"tool": "run_commands",
|
|
97
|
+
"args": {
|
|
98
|
+
"steps": [
|
|
99
|
+
{
|
|
100
|
+
"id": "vector.setStyle",
|
|
101
|
+
"params": {
|
|
102
|
+
"ids": [
|
|
103
|
+
{
|
|
104
|
+
"$read": "run_command",
|
|
105
|
+
"args": {
|
|
106
|
+
"id": "vector.list"
|
|
107
|
+
},
|
|
108
|
+
"path": "result.nodes.0.id"
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"fill": "#f1faee"
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"id": "vector.setStyle",
|
|
116
|
+
"params": {
|
|
117
|
+
"ids": [
|
|
118
|
+
{
|
|
119
|
+
"$read": "run_command",
|
|
120
|
+
"args": {
|
|
121
|
+
"id": "vector.list"
|
|
122
|
+
},
|
|
123
|
+
"path": "result.nodes.1.id"
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
"fill": "#1d3557"
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"id": "vector.setStyle",
|
|
131
|
+
"params": {
|
|
132
|
+
"ids": [
|
|
133
|
+
{
|
|
134
|
+
"$read": "run_command",
|
|
135
|
+
"args": {
|
|
136
|
+
"id": "vector.list"
|
|
137
|
+
},
|
|
138
|
+
"path": "result.nodes.2.id"
|
|
139
|
+
}
|
|
140
|
+
],
|
|
141
|
+
"fill": "#e63946"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "The white text on our light-blue badge fails contrast. Fix it using a colour from our palette (#1d3557, #457b9d, #a8dadc, #f1faee, #e63946), keeping the badge light blue.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "vector",
|
|
5
|
+
"width": 120,
|
|
6
|
+
"height": 60,
|
|
7
|
+
"colorTools": true,
|
|
8
|
+
"setup": [
|
|
9
|
+
{
|
|
10
|
+
"id": "vector.addShape",
|
|
11
|
+
"params": {
|
|
12
|
+
"kind": "rect",
|
|
13
|
+
"x": 10,
|
|
14
|
+
"y": 10,
|
|
15
|
+
"width": 100,
|
|
16
|
+
"height": 40,
|
|
17
|
+
"name": "Badge",
|
|
18
|
+
"fill": "#a8dadc",
|
|
19
|
+
"stroke": null
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "vector.addShape",
|
|
24
|
+
"params": {
|
|
25
|
+
"kind": "rect",
|
|
26
|
+
"x": 30,
|
|
27
|
+
"y": 24,
|
|
28
|
+
"width": 60,
|
|
29
|
+
"height": 12,
|
|
30
|
+
"name": "Text",
|
|
31
|
+
"fill": "#ffffff",
|
|
32
|
+
"stroke": null
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"checks": [
|
|
38
|
+
{
|
|
39
|
+
"why": "the text uses a palette colour that reads on the badge at 4.5:1 or more",
|
|
40
|
+
"tool": "run_command",
|
|
41
|
+
"args": {
|
|
42
|
+
"id": "vector.list"
|
|
43
|
+
},
|
|
44
|
+
"expect": {
|
|
45
|
+
"result": {
|
|
46
|
+
"nodes": {
|
|
47
|
+
"$expr": "(() => { const L = (h) => { const c = [1, 3, 5].map((i) => parseInt(h.replace('#', '').padEnd(6, '0').slice(i - 1, i + 1), 16) / 255).map((x) => (x <= 0.04045 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4)); return 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]; }; const cr = (a, b) => { const [x, y] = [L(a), L(b)].sort((p, q) => q - p); return (x + 0.05) / (y + 0.05); }; const t = v.find((n) => n.name === 'Text').fill.toLowerCase(); return ['#1d3557', '#457b9d', '#a8dadc', '#f1faee', '#e63946'].includes(t) && cr(t, v.find((n) => n.name === 'Badge').fill) >= 4.5; })()"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"why": "the badge stays light blue",
|
|
54
|
+
"tool": "run_command",
|
|
55
|
+
"args": {
|
|
56
|
+
"id": "vector.list"
|
|
57
|
+
},
|
|
58
|
+
"expect": {
|
|
59
|
+
"result": {
|
|
60
|
+
"nodes": {
|
|
61
|
+
"$some": {
|
|
62
|
+
"name": "Badge",
|
|
63
|
+
"fill": "#a8dadc"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
"solution": [
|
|
71
|
+
{
|
|
72
|
+
"tool": "check_color_contrast",
|
|
73
|
+
"args": {
|
|
74
|
+
"foreground": "#ffffff",
|
|
75
|
+
"background": "#a8dadc"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"tool": "check_color_contrast",
|
|
80
|
+
"args": {
|
|
81
|
+
"foreground": "#1d3557",
|
|
82
|
+
"background": "#a8dadc"
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"tool": "run_command",
|
|
87
|
+
"args": {
|
|
88
|
+
"id": "vector.setStyle",
|
|
89
|
+
"params": {
|
|
90
|
+
"ids": [
|
|
91
|
+
{
|
|
92
|
+
"$read": "run_command",
|
|
93
|
+
"args": {
|
|
94
|
+
"id": "vector.list"
|
|
95
|
+
},
|
|
96
|
+
"path": "result.nodes.1.id"
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
"fill": "#1d3557"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "These colours are almost our brand colours but not exact. Snap each shape to the nearest colour in the palette: #1d3557, #457b9d, #a8dadc, #e63946.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "vector",
|
|
5
|
+
"width": 160,
|
|
6
|
+
"height": 40,
|
|
7
|
+
"colorTools": true,
|
|
8
|
+
"setup": [
|
|
9
|
+
{
|
|
10
|
+
"id": "vector.addShape",
|
|
11
|
+
"params": {
|
|
12
|
+
"kind": "rect",
|
|
13
|
+
"x": 0,
|
|
14
|
+
"y": 0,
|
|
15
|
+
"width": 40,
|
|
16
|
+
"height": 40,
|
|
17
|
+
"name": "A",
|
|
18
|
+
"fill": "#1f3759",
|
|
19
|
+
"stroke": null
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "vector.addShape",
|
|
24
|
+
"params": {
|
|
25
|
+
"kind": "rect",
|
|
26
|
+
"x": 40,
|
|
27
|
+
"y": 0,
|
|
28
|
+
"width": 40,
|
|
29
|
+
"height": 40,
|
|
30
|
+
"name": "B",
|
|
31
|
+
"fill": "#4a7fa0",
|
|
32
|
+
"stroke": null
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "vector.addShape",
|
|
37
|
+
"params": {
|
|
38
|
+
"kind": "rect",
|
|
39
|
+
"x": 80,
|
|
40
|
+
"y": 0,
|
|
41
|
+
"width": 40,
|
|
42
|
+
"height": 40,
|
|
43
|
+
"name": "C",
|
|
44
|
+
"fill": "#aadcde",
|
|
45
|
+
"stroke": null
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "vector.addShape",
|
|
50
|
+
"params": {
|
|
51
|
+
"kind": "rect",
|
|
52
|
+
"x": 120,
|
|
53
|
+
"y": 0,
|
|
54
|
+
"width": 40,
|
|
55
|
+
"height": 40,
|
|
56
|
+
"name": "D",
|
|
57
|
+
"fill": "#e43b48",
|
|
58
|
+
"stroke": null
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
"checks": [
|
|
64
|
+
{
|
|
65
|
+
"why": "each shape in its nearest brand colour",
|
|
66
|
+
"tool": "run_command",
|
|
67
|
+
"args": {
|
|
68
|
+
"id": "vector.list"
|
|
69
|
+
},
|
|
70
|
+
"expect": {
|
|
71
|
+
"result": {
|
|
72
|
+
"nodes": {
|
|
73
|
+
"$expr": "v.map((n) => (n.fill || '').toLowerCase()).join() === '#1d3557,#457b9d,#a8dadc,#e63946'"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
"solution": [
|
|
80
|
+
{
|
|
81
|
+
"tool": "run_commands",
|
|
82
|
+
"args": {
|
|
83
|
+
"steps": [
|
|
84
|
+
{
|
|
85
|
+
"id": "vector.setStyle",
|
|
86
|
+
"params": {
|
|
87
|
+
"ids": [
|
|
88
|
+
{
|
|
89
|
+
"$read": "run_command",
|
|
90
|
+
"args": {
|
|
91
|
+
"id": "vector.list"
|
|
92
|
+
},
|
|
93
|
+
"path": "result.nodes.0.id"
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
"fill": "#1d3557"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"id": "vector.setStyle",
|
|
101
|
+
"params": {
|
|
102
|
+
"ids": [
|
|
103
|
+
{
|
|
104
|
+
"$read": "run_command",
|
|
105
|
+
"args": {
|
|
106
|
+
"id": "vector.list"
|
|
107
|
+
},
|
|
108
|
+
"path": "result.nodes.1.id"
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"fill": "#457b9d"
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"id": "vector.setStyle",
|
|
116
|
+
"params": {
|
|
117
|
+
"ids": [
|
|
118
|
+
{
|
|
119
|
+
"$read": "run_command",
|
|
120
|
+
"args": {
|
|
121
|
+
"id": "vector.list"
|
|
122
|
+
},
|
|
123
|
+
"path": "result.nodes.2.id"
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
"fill": "#a8dadc"
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"id": "vector.setStyle",
|
|
131
|
+
"params": {
|
|
132
|
+
"ids": [
|
|
133
|
+
{
|
|
134
|
+
"$read": "run_command",
|
|
135
|
+
"args": {
|
|
136
|
+
"id": "vector.list"
|
|
137
|
+
},
|
|
138
|
+
"path": "result.nodes.3.id"
|
|
139
|
+
}
|
|
140
|
+
],
|
|
141
|
+
"fill": "#e63946"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
]
|
|
148
|
+
}
|
|
@@ -7,6 +7,14 @@ description: How to inspect colours, check and optimize WCAG contrast and access
|
|
|
7
7
|
|
|
8
8
|
The colour module provides DOM-free colour tools for inspection, WCAG accessibility validation, colour-vision deficiency simulation, palette audits, colour harmony suggestions, and perceptual blending.
|
|
9
9
|
|
|
10
|
+
## Workflow skills
|
|
11
|
+
|
|
12
|
+
For these jobs, load the workflow skill too: it has the method, the checks between steps and
|
|
13
|
+
the pitfalls, and uses the tools below.
|
|
14
|
+
|
|
15
|
+
- `pixerate-accessibility-audit`: contrast, colour-blind-safe meaning, both modes, fixes and a report.
|
|
16
|
+
- `pixerate-brand-application`: brand roles, exact palette values, readable text, derivatives.
|
|
17
|
+
|
|
10
18
|
## Working method
|
|
11
19
|
|
|
12
20
|
1. **Inspect and convert.** Run `inspect_color` to parse any hex, `rgb()`, `hsl()`, `oklch()`, or CSS named colour into standard formats (hex, rgb, hsl, oklch) and relative luminance, and to check if it fits within the sRGB gamut.
|
|
@@ -17,6 +17,13 @@ preview frames.
|
|
|
17
17
|
|
|
18
18
|
For general error reporting and undo conventions, see `pixerate-agent-tools`.
|
|
19
19
|
|
|
20
|
+
## Workflow skills
|
|
21
|
+
|
|
22
|
+
For these jobs, load the workflow skill too: it has the method, the checks between steps and
|
|
23
|
+
the pitfalls, and uses the tools below.
|
|
24
|
+
|
|
25
|
+
- `pixerate-effect-look-development`: tune a look, save presets, eased keyframes, seamless loops.
|
|
26
|
+
|
|
20
27
|
## Working method
|
|
21
28
|
|
|
22
29
|
1. **Inspect first.** Run `effect_authoring_describe` to inspect the effect name, kind, source
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pixerate-effect-look-development
|
|
3
|
+
description: The look development workflow for Pixerate effects - reading an effect's controls, finding the look with a few deliberate value changes, saving looks as named presets, animating controls with keyframes and easing, setting duration and looping for seamless cycles, previewing at key moments, and editing shader source only when controls cannot get there. Use when asked to design, tune, tweak or animate an effect, shader, filter or procedural look, make a preset or variant of a look, make an effect fade in or out, pulse or loop, or when effect_authoring_describe, effect_authoring_set_values, effect_authoring_set_keyframe or effect_authoring_save_preset are available. Load with pixerate-effect-authoring, which lists every tool.
|
|
4
|
+
metadata:
|
|
5
|
+
kind: workflow
|
|
6
|
+
reference: pixerate-effect-authoring
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Developing a look
|
|
10
|
+
|
|
11
|
+
An effect is a set of controls over a shader or procedure. A look is a set of values for those
|
|
12
|
+
controls, kept as a named preset; an animated look adds keyframed tracks over a duration. Find
|
|
13
|
+
the look with a few deliberate moves (start from a preset, change one control at a time, look),
|
|
14
|
+
save it, and animate only what should change over time.
|
|
15
|
+
|
|
16
|
+
This skill is the method. `pixerate-effect-authoring` has the full tool reference.
|
|
17
|
+
|
|
18
|
+
## Goal and quality bar
|
|
19
|
+
|
|
20
|
+
- Every value is within its control's range (`effect_authoring_describe` lists `min`, `max`,
|
|
21
|
+
`default`).
|
|
22
|
+
- The look is saved as a preset with a clear name, so it can be applied again.
|
|
23
|
+
- Animation uses as few keyframes as the motion needs, with easing (`ease-out` to arrive,
|
|
24
|
+
`ease-in-out` for a pulse); duration fits the use.
|
|
25
|
+
- A loop is seamless: `loop` on, and the animated values match at the start and the end.
|
|
26
|
+
- `issues` is empty and `previewError` is null.
|
|
27
|
+
|
|
28
|
+
## Inputs to confirm
|
|
29
|
+
|
|
30
|
+
- **The look** in words (subtle, strong, warm, glitchy) or a reference.
|
|
31
|
+
- **Static or animated**, and the duration and looping for animation.
|
|
32
|
+
- **Whether to keep** the result as a preset, and its name.
|
|
33
|
+
|
|
34
|
+
## Working method
|
|
35
|
+
|
|
36
|
+
1. **Read.** `effect_authoring_describe`: controls with ranges and defaults, current values,
|
|
37
|
+
presets, tracks, duration, loop, issues.
|
|
38
|
+
**Check:** you know which control does what.
|
|
39
|
+
2. **Start close.** `effect_authoring_apply_preset` when a preset is near the look;
|
|
40
|
+
`effect_authoring_reset_values` to start clean.
|
|
41
|
+
3. **Tune.** `effect_authoring_set_values` with only the controls you change, a few at a time;
|
|
42
|
+
`effect_authoring_view` to look.
|
|
43
|
+
**Check:** `values` in `effect_authoring_describe` are what you set; no `issues`.
|
|
44
|
+
4. **Save.** `effect_authoring_save_preset` with a descriptive `name` ("Subtle", "Warm glow").
|
|
45
|
+
**Check:** the name is in `presets`.
|
|
46
|
+
5. **Animate.** `effect_authoring_set_timing` for `duration` and `loop`; then
|
|
47
|
+
`effect_authoring_set_keyframe` per control at the times that matter, with `easing` into the
|
|
48
|
+
next keyframe. For a loop, the last keyframe's value equals the first's.
|
|
49
|
+
**Check:** `tracks` list the control with the keyframe count you meant.
|
|
50
|
+
6. **Preview** with `effect_authoring_seek` to key times and `effect_authoring_view`.
|
|
51
|
+
7. **Source last.** `effect_authoring_get_source` / `effect_authoring_set_source` only when no
|
|
52
|
+
control can produce the look; read the returned issues and fix them.
|
|
53
|
+
|
|
54
|
+
## Decision table
|
|
55
|
+
|
|
56
|
+
| Want | Approach |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| A softer or stronger version of a look | Scale the main control (strength, amount); save as a preset |
|
|
59
|
+
| A starting point | `effect_authoring_apply_preset` |
|
|
60
|
+
| Back to defaults | `effect_authoring_reset_values` |
|
|
61
|
+
| Fade in | Keyframes from the neutral value to the look, `ease-out` |
|
|
62
|
+
| Pulse or breathe | Three keyframes (rest, peak, rest), `ease-in-out`, loop on |
|
|
63
|
+
| A one-off change at a moment | Two keyframes around the moment |
|
|
64
|
+
| Remove animation from a control | `effect_authoring_clear_track` |
|
|
65
|
+
| Something no control does | Edit the source |
|
|
66
|
+
|
|
67
|
+
## Recipes
|
|
68
|
+
|
|
69
|
+
**Quarter-strength preset.** `effect_authoring_set_values` `{ values: { amount: 0.25 } }` →
|
|
70
|
+
`effect_authoring_save_preset` `{ name: "Subtle" }`.
|
|
71
|
+
|
|
72
|
+
**Fade in over 1.5 s in a 2 s effect.** `effect_authoring_set_timing` `{ duration: 2000 }` →
|
|
73
|
+
`effect_authoring_set_keyframe` `{ control: "amount", time: 0, value: 0 }` → `{ control:
|
|
74
|
+
"amount", time: 1500, value: 1, easing: "ease-out" }`.
|
|
75
|
+
|
|
76
|
+
**Seamless 3 s pulse.** `effect_authoring_set_timing` `{ duration: 3000, loop: true }` → keyframes
|
|
77
|
+
at 0 (0), 1500 (1, `ease-in-out`) and 3000 (0, `ease-in-out`).
|
|
78
|
+
|
|
79
|
+
## Pitfalls
|
|
80
|
+
|
|
81
|
+
- **Values out of range** are clamped or refused; read the control's `min` and `max`.
|
|
82
|
+
- **Many keyframes** where three would do make motion lumpy and hard to edit.
|
|
83
|
+
- **Linear easing** on organic motion looks mechanical.
|
|
84
|
+
- **Loops that jump**: the end value must equal the start value.
|
|
85
|
+
- **Overwriting a preset** by saving under an existing name; choose a new name unless replacing
|
|
86
|
+
is intended.
|
|
87
|
+
- **Editing source first**: controls are safer and keep presets working.
|
|
88
|
+
|
|
89
|
+
## Tools used
|
|
90
|
+
|
|
91
|
+
- `effect_authoring_describe`, `effect_authoring_view`, `effect_authoring_seek`: read, look and preview
|
|
92
|
+
- `effect_authoring_set_values`, `effect_authoring_reset_values`, `effect_authoring_apply_preset`: find the look
|
|
93
|
+
- `effect_authoring_save_preset`: keep the look
|
|
94
|
+
- `effect_authoring_set_timing`, `effect_authoring_set_keyframe`, `effect_authoring_remove_keyframe`, `effect_authoring_clear_track`: animate
|
|
95
|
+
- `effect_authoring_get_source`, `effect_authoring_set_source`: edit the shader when needed
|
|
96
|
+
- `effect_authoring_undo`, `effect_authoring_redo`: take back a step
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Animate the effect fading in: no effect at the start, full strength by 1.5 seconds, in a 2-second effect.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "effect",
|
|
5
|
+
"effectId": "fake.invert"
|
|
6
|
+
},
|
|
7
|
+
"checks": [
|
|
8
|
+
{
|
|
9
|
+
"why": "a 2-second effect",
|
|
10
|
+
"tool": "effect_authoring_describe",
|
|
11
|
+
"expect": {
|
|
12
|
+
"duration": 2000
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"why": "the amount is keyframed (at least a start and an end)",
|
|
17
|
+
"tool": "effect_authoring_describe",
|
|
18
|
+
"expect": {
|
|
19
|
+
"tracks": {
|
|
20
|
+
"$some": {
|
|
21
|
+
"control": "amount",
|
|
22
|
+
"keyframes": {
|
|
23
|
+
"$min": 2
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"solution": [
|
|
31
|
+
{
|
|
32
|
+
"tool": "effect_authoring_set_timing",
|
|
33
|
+
"args": {
|
|
34
|
+
"duration": 2000
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"tool": "effect_authoring_set_keyframe",
|
|
39
|
+
"args": {
|
|
40
|
+
"control": "amount",
|
|
41
|
+
"time": 0,
|
|
42
|
+
"value": 0
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"tool": "effect_authoring_set_keyframe",
|
|
47
|
+
"args": {
|
|
48
|
+
"control": "amount",
|
|
49
|
+
"time": 1500,
|
|
50
|
+
"value": 1,
|
|
51
|
+
"easing": "ease-out"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|