@pixerate/editor 0.223.7 → 0.223.9
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 +10 -1
- package/dist/tools/skill-evals.d.ts +10 -1
- package/package.json +1 -1
- package/skills/README.md +7 -2
- 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-canvas/SKILL.md +8 -0
- package/skills/pixerate-canvas-design-system-components/SKILL.md +103 -0
- package/skills/pixerate-canvas-design-system-components/evals/add-variant.json +172 -0
- package/skills/pixerate-canvas-design-system-components/evals/combine-variants.json +172 -0
- package/skills/pixerate-canvas-design-system-components/evals/make-component.json +103 -0
- package/skills/pixerate-canvas-layout/SKILL.md +95 -0
- package/skills/pixerate-canvas-layout/evals/align-tops.json +98 -0
- package/skills/pixerate-canvas-layout/evals/distribute-evenly.json +98 -0
- package/skills/pixerate-canvas-layout/evals/order-and-lock.json +110 -0
- package/skills/pixerate-color/SKILL.md +8 -0
|
@@ -20,7 +20,7 @@ export interface SkillEvalCheck extends SkillEvalToolCall {
|
|
|
20
20
|
}
|
|
21
21
|
export interface SkillEval {
|
|
22
22
|
prompt: string;
|
|
23
|
-
fixture: SpreadsheetEvalFixture | ImageEvalFixture | VectorEvalFixture | PaintEvalFixture | VideoEvalFixture | ChartEvalFixture | DiagramEvalFixture | RichTextEvalFixture | AnimationEvalFixture | GenerationEvalFixture | EffectEvalFixture;
|
|
23
|
+
fixture: SpreadsheetEvalFixture | ImageEvalFixture | VectorEvalFixture | PaintEvalFixture | VideoEvalFixture | ChartEvalFixture | DiagramEvalFixture | RichTextEvalFixture | AnimationEvalFixture | GenerationEvalFixture | EffectEvalFixture | CanvasEvalFixture;
|
|
24
24
|
checks: SkillEvalCheck[];
|
|
25
25
|
solution: SkillEvalToolCall[];
|
|
26
26
|
}
|
|
@@ -54,6 +54,8 @@ export interface VectorEvalFixture {
|
|
|
54
54
|
id: string;
|
|
55
55
|
params?: Record<string, unknown>;
|
|
56
56
|
}>;
|
|
57
|
+
/** Also register the colour tools (`check_color_contrast`, `check_color_palette`, ...). */
|
|
58
|
+
colorTools?: boolean;
|
|
57
59
|
}
|
|
58
60
|
export interface PaintEvalFixture {
|
|
59
61
|
editor: 'paint';
|
|
@@ -107,6 +109,13 @@ export interface EffectEvalFixture {
|
|
|
107
109
|
/** Tool calls that prepare the session, run in order. */
|
|
108
110
|
setup?: SkillEvalToolCall[];
|
|
109
111
|
}
|
|
112
|
+
export interface CanvasEvalFixture {
|
|
113
|
+
editor: 'canvas';
|
|
114
|
+
/** The starting nodes (React Flow style: `id`, `type`, `position`, `width`, `height`, `data`, `parentId`). */
|
|
115
|
+
nodes: Array<Record<string, unknown>>;
|
|
116
|
+
/** Tool calls that prepare the canvas (components, instances), run in order. */
|
|
117
|
+
setup?: SkillEvalToolCall[];
|
|
118
|
+
}
|
|
110
119
|
/** One eval file of a skill. */
|
|
111
120
|
export interface SkillEvalFile {
|
|
112
121
|
skill: string;
|
|
@@ -20,7 +20,7 @@ export interface SkillEvalCheck extends SkillEvalToolCall {
|
|
|
20
20
|
}
|
|
21
21
|
export interface SkillEval {
|
|
22
22
|
prompt: string;
|
|
23
|
-
fixture: SpreadsheetEvalFixture | ImageEvalFixture | VectorEvalFixture | PaintEvalFixture | VideoEvalFixture | ChartEvalFixture | DiagramEvalFixture | RichTextEvalFixture | AnimationEvalFixture | GenerationEvalFixture | EffectEvalFixture;
|
|
23
|
+
fixture: SpreadsheetEvalFixture | ImageEvalFixture | VectorEvalFixture | PaintEvalFixture | VideoEvalFixture | ChartEvalFixture | DiagramEvalFixture | RichTextEvalFixture | AnimationEvalFixture | GenerationEvalFixture | EffectEvalFixture | CanvasEvalFixture;
|
|
24
24
|
checks: SkillEvalCheck[];
|
|
25
25
|
solution: SkillEvalToolCall[];
|
|
26
26
|
}
|
|
@@ -54,6 +54,8 @@ export interface VectorEvalFixture {
|
|
|
54
54
|
id: string;
|
|
55
55
|
params?: Record<string, unknown>;
|
|
56
56
|
}>;
|
|
57
|
+
/** Also register the colour tools (`check_color_contrast`, `check_color_palette`, ...). */
|
|
58
|
+
colorTools?: boolean;
|
|
57
59
|
}
|
|
58
60
|
export interface PaintEvalFixture {
|
|
59
61
|
editor: 'paint';
|
|
@@ -107,6 +109,13 @@ export interface EffectEvalFixture {
|
|
|
107
109
|
/** Tool calls that prepare the session, run in order. */
|
|
108
110
|
setup?: SkillEvalToolCall[];
|
|
109
111
|
}
|
|
112
|
+
export interface CanvasEvalFixture {
|
|
113
|
+
editor: 'canvas';
|
|
114
|
+
/** The starting nodes (React Flow style: `id`, `type`, `position`, `width`, `height`, `data`, `parentId`). */
|
|
115
|
+
nodes: Array<Record<string, unknown>>;
|
|
116
|
+
/** Tool calls that prepare the canvas (components, instances), run in order. */
|
|
117
|
+
setup?: SkillEvalToolCall[];
|
|
118
|
+
}
|
|
110
119
|
/** One eval file of a skill. */
|
|
111
120
|
export interface SkillEvalFile {
|
|
112
121
|
skill: string;
|
package/package.json
CHANGED
package/skills/README.md
CHANGED
|
@@ -65,6 +65,10 @@ request like "clean up this export" finds it.
|
|
|
65
65
|
| [`pixerate-animation-logo-reveal`](pixerate-animation-logo-reveal/SKILL.md) | Animation (`pixerate-animation-editor`) | A short reveal built from the logo's parts, landing on the beat and ending on the held, finished logo. |
|
|
66
66
|
| [`pixerate-generation-budgeted-batch`](pixerate-generation-budgeted-batch/SKILL.md) | Generation (`pixerate-generation`) | The right model for the format, plan and estimate before spending, drafts cheap and finals once, safe retries, variations from the history. |
|
|
67
67
|
| [`pixerate-effect-look-development`](pixerate-effect-look-development/SKILL.md) | Effects (`pixerate-effect-authoring`) | Tune a look in a few moves, save it as a preset, animate with eased keyframes, seamless loops. |
|
|
68
|
+
| [`pixerate-accessibility-audit`](pixerate-accessibility-audit/SKILL.md) | Cross-domain (`pixerate-color`) | Text and non-text contrast, colour-blind-safe meaning, both modes, smallest fixes and a report, in any editor. |
|
|
69
|
+
| [`pixerate-brand-application`](pixerate-brand-application/SKILL.md) | Cross-domain (`pixerate-color`) | Brand roles mapped to exact palette values, near-misses snapped, readable text on brand colours, derivatives and house-style checks. |
|
|
70
|
+
| [`pixerate-canvas-design-system-components`](pixerate-canvas-design-system-components/SKILL.md) | Canvas (`pixerate-canvas`) | Main components and linked instances, component sets with named variant properties, overrides kept linked. |
|
|
71
|
+
| [`pixerate-canvas-layout`](pixerate-canvas-layout/SKILL.md) | Canvas (`pixerate-canvas`) | Align, distribute, order and lock with the arrange commands, changing only the intended axis. |
|
|
68
72
|
| [`pixerate-talking-head-edit`](pixerate-talking-head-edit/SKILL.md) | Video (`pixerate-video-editor`) | Talking-head cleanup by transcript: fillers, long pauses and stutters found in one pass and cut as one plan reviewed before it applies. Owns the dialogue tools. |
|
|
69
73
|
| [`pixerate-bgm-alignment`](pixerate-bgm-alignment/SKILL.md) | Video (`pixerate-video-editor`) | Background music: beats and cuts on the beat, ducking under speech, loudness in LUFS. Owns the music tools. |
|
|
70
74
|
|
|
@@ -128,12 +132,13 @@ same change.
|
|
|
128
132
|
`packages/core/tests/agent-skill-evals.test.ts`. An eval has a `prompt` (the task, in the
|
|
129
133
|
user's words), a `fixture` (the starting document: `{ "editor": "spreadsheet", "rows" }`,
|
|
130
134
|
`{ "editor": "image", "width", "height", "background", "shapes" }` or `{ "editor":
|
|
131
|
-
"vector", "width", "height", "setup" }` with commands that draw the starting art,
|
|
135
|
+
"vector", "width", "height", "setup", "colorTools" }` with commands that draw the starting art,
|
|
132
136
|
`{ "editor": "paint", "width", "height", "setup" }` `{ "editor": "video", "setup" }` or `{ "editor":
|
|
133
137
|
"chart", "setup" }` `{ "editor": "diagram", "setup" }` with tool calls,
|
|
134
138
|
`{ "editor": "rich-text", "html" }`,
|
|
135
139
|
`{ "editor": "animation", "setup" }`, `{ "editor": "generation", "models", "prices", "history" }`
|
|
136
|
-
(a fake host that records each generation)
|
|
140
|
+
(a fake host that records each generation) `{ "editor": "effect", "effectId", "setup" }` or `{ "editor": "canvas",
|
|
141
|
+
"nodes", "setup" }` (a headless canvas with the component, arrange and document tools)), `checks` (a read-only
|
|
137
142
|
tool, or `run_command` with a read-only command such as `vector.list`, its `args`, what its result must match in `expect`, and `why`) and a `solution` (the
|
|
138
143
|
tool calls an expert would make). The test proves each eval: some check fails on the fixture,
|
|
139
144
|
and all pass after the solution. A model harness runs the same file with the model in place
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pixerate-accessibility-audit
|
|
3
|
+
description: The accessibility audit workflow across Pixerate editors - checking text contrast (WCAG AA 4.5:1, large text 3:1), non-text contrast for icons, borders and controls (3:1), colour-only meaning and palettes under colour-vision deficiencies, light and dark modes, type readability, and fixing what fails with the smallest change, then reporting what was checked and changed. Use when asked to audit, check or fix accessibility, contrast, legibility, readability or colour-blind safety of a design, chart, icon, document, UI or brand palette, or to meet WCAG, or when check_color_contrast, check_color_palette or check_dual_color_contrast are 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
|
+
# Auditing accessibility
|
|
10
|
+
|
|
11
|
+
An accessibility audit finds what some readers cannot see or tell apart, fixes it with the
|
|
12
|
+
smallest change that keeps the design's intent, and reports both. Measure, never eyeball:
|
|
13
|
+
every text and background pair gets a contrast ratio, every set of colours that carries meaning
|
|
14
|
+
gets a colour-vision check, and every fix is measured again.
|
|
15
|
+
|
|
16
|
+
This skill is the method. `pixerate-color` has the colour tools; read colours from the design
|
|
17
|
+
with its editor's tools (`vector.list` in the vector editor, `get_chart` and
|
|
18
|
+
`check_chart_colors` for charts, `measure_region` for an image).
|
|
19
|
+
|
|
20
|
+
## Goal and quality bar
|
|
21
|
+
|
|
22
|
+
- Normal text at least 4.5:1 against its background, large text (24 px, or 18.5 px bold) at
|
|
23
|
+
least 3:1 (`check_color_contrast`, with `large: true` for large text).
|
|
24
|
+
- Icons, borders, focus rings, chart lines and control outlines at least 3:1 against what they
|
|
25
|
+
sit on.
|
|
26
|
+
- Colours that carry meaning (status, series, categories) distinct under protanopia,
|
|
27
|
+
deuteranopia and tritanopia (`check_color_palette` `ok: true`), and not the only cue (add a
|
|
28
|
+
label, shape or pattern where meaning matters).
|
|
29
|
+
- Both light and dark modes pass when the design has them (`check_dual_color_contrast`).
|
|
30
|
+
- Fixes change as little as possible: the text colour rather than the brand background, a
|
|
31
|
+
darker shade of the same hue rather than a new hue.
|
|
32
|
+
- A report lists each pair checked, its ratio before and after, and what changed.
|
|
33
|
+
|
|
34
|
+
## Inputs to confirm
|
|
35
|
+
|
|
36
|
+
- **The standard:** WCAG 2 AA (the default) or AAA.
|
|
37
|
+
- **What may change:** usually text and icon colours; brand backgrounds and logos only with
|
|
38
|
+
approval.
|
|
39
|
+
- **Modes:** light, dark or both.
|
|
40
|
+
|
|
41
|
+
## Working method
|
|
42
|
+
|
|
43
|
+
1. **Inventory.** Read every foreground and background pair from the design's editor: text on
|
|
44
|
+
surfaces, icons on cards, lines on plots, status colours.
|
|
45
|
+
**Check:** each pair has a role (normal text, large text, graphic, decorative).
|
|
46
|
+
2. **Measure.** `check_color_contrast` for each pair (`large: true` for large text);
|
|
47
|
+
`check_color_palette` for sets of meaningful colours; `check_dual_color_contrast` for dual
|
|
48
|
+
colours.
|
|
49
|
+
**Check:** a list of failures with their ratios.
|
|
50
|
+
3. **Fix the smallest thing.** Use the `suggestion` the contrast check returns (a lightness that
|
|
51
|
+
passes), or a darker or lighter shade of the same hue; for palettes, replace the clashing
|
|
52
|
+
colours with ones that differ in lightness as well as hue (blue and orange rather than red and
|
|
53
|
+
green). Apply with the editor's own tools (`vector.setStyle`, `update_chart_series`).
|
|
54
|
+
**Check:** measure the pair again; it passes.
|
|
55
|
+
4. **Meaning without colour.** Where a palette carries meaning, make sure a label, icon or shape
|
|
56
|
+
carries it too.
|
|
57
|
+
5. **Report** pairs, ratios before and after, and anything left that needs a decision (a brand
|
|
58
|
+
colour that cannot pass with any text colour).
|
|
59
|
+
|
|
60
|
+
## Decision table
|
|
61
|
+
|
|
62
|
+
| Element | Requirement | Tool |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| Body text, labels | 4.5:1 | `check_color_contrast` |
|
|
65
|
+
| Large or bold headings | 3:1 | `check_color_contrast` with `large: true` |
|
|
66
|
+
| Icons, borders, focus rings, chart lines | 3:1 | `check_color_contrast` |
|
|
67
|
+
| Status or series colours | Distinct under CVD | `check_color_palette` |
|
|
68
|
+
| Light and dark themes | Both pass | `check_dual_color_contrast` |
|
|
69
|
+
| Decorative graphics, disabled controls, logos | No requirement | Note them as exempt |
|
|
70
|
+
|
|
71
|
+
## Recipes
|
|
72
|
+
|
|
73
|
+
**Fix a button label.** `check_color_contrast` `{ foreground: label, background: button }` →
|
|
74
|
+
use its suggested lightness or a dark shade of the button's hue → apply → check again.
|
|
75
|
+
|
|
76
|
+
**Colour-blind-safe status colours.** `check_color_palette` with the three colours → replace with
|
|
77
|
+
a set that varies in lightness (`#0072b2`, `#e69f00`, `#000000`) → check again; keep the labels.
|
|
78
|
+
|
|
79
|
+
**A faint icon.** `check_color_contrast` icon stroke against the card; darken the stroke until it
|
|
80
|
+
reaches 3:1.
|
|
81
|
+
|
|
82
|
+
## Pitfalls
|
|
83
|
+
|
|
84
|
+
- **Eyeballing contrast.** Screens and eyes vary; measure every pair.
|
|
85
|
+
- **Changing the brand colour** to fix text; change the text first.
|
|
86
|
+
- **Large-text allowance on small text.** 3:1 applies only to large or bold-large text.
|
|
87
|
+
- **Translucent colours** are measured composited over their real backdrop; pass the opaque
|
|
88
|
+
backdrop.
|
|
89
|
+
- **Red and green only** for meaning fails the most common colour-vision deficiency.
|
|
90
|
+
- **Fixing one mode** and breaking the other in a dual theme.
|
|
91
|
+
|
|
92
|
+
## Tools used
|
|
93
|
+
|
|
94
|
+
- `check_color_contrast`: text and graphic contrast, with a suggested passing lightness
|
|
95
|
+
- `check_color_palette`: distinctness under colour-vision deficiencies
|
|
96
|
+
- `check_dual_color_contrast`, `derive_dual_color`: light and dark modes
|
|
97
|
+
- `inspect_color`: parse and convert a colour
|
|
98
|
+
- `blend_colors`: shades and tints of a hue
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Our 'Buy now' button fails accessibility review: the label is hard to read on the button. Fix the label colour so it meets WCAG AA for normal text, keeping the button's brand blue.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "vector",
|
|
5
|
+
"width": 200,
|
|
6
|
+
"height": 80,
|
|
7
|
+
"colorTools": true,
|
|
8
|
+
"setup": [
|
|
9
|
+
{
|
|
10
|
+
"id": "vector.addShape",
|
|
11
|
+
"params": {
|
|
12
|
+
"kind": "rect",
|
|
13
|
+
"x": 20,
|
|
14
|
+
"y": 20,
|
|
15
|
+
"width": 160,
|
|
16
|
+
"height": 40,
|
|
17
|
+
"name": "Button",
|
|
18
|
+
"fill": "#4da3ff",
|
|
19
|
+
"stroke": null
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "vector.addShape",
|
|
24
|
+
"params": {
|
|
25
|
+
"kind": "rect",
|
|
26
|
+
"x": 60,
|
|
27
|
+
"y": 32,
|
|
28
|
+
"width": 80,
|
|
29
|
+
"height": 16,
|
|
30
|
+
"name": "Label",
|
|
31
|
+
"fill": "#ffffff",
|
|
32
|
+
"stroke": null
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"checks": [
|
|
38
|
+
{
|
|
39
|
+
"why": "label on button at least 4.5:1",
|
|
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); }; return cr(v.find((n) => n.name === 'Label').fill, v.find((n) => n.name === 'Button').fill) >= 4.5; })()"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"why": "the button keeps its blue",
|
|
54
|
+
"tool": "run_command",
|
|
55
|
+
"args": {
|
|
56
|
+
"id": "vector.list"
|
|
57
|
+
},
|
|
58
|
+
"expect": {
|
|
59
|
+
"result": {
|
|
60
|
+
"nodes": {
|
|
61
|
+
"$some": {
|
|
62
|
+
"name": "Button",
|
|
63
|
+
"fill": "#4da3ff"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
"solution": [
|
|
71
|
+
{
|
|
72
|
+
"tool": "check_color_contrast",
|
|
73
|
+
"args": {
|
|
74
|
+
"foreground": "#ffffff",
|
|
75
|
+
"background": "#4da3ff"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"tool": "run_command",
|
|
80
|
+
"args": {
|
|
81
|
+
"id": "vector.setStyle",
|
|
82
|
+
"params": {
|
|
83
|
+
"ids": [
|
|
84
|
+
{
|
|
85
|
+
"$read": "run_command",
|
|
86
|
+
"args": {
|
|
87
|
+
"id": "vector.list"
|
|
88
|
+
},
|
|
89
|
+
"path": "result.nodes.1.id"
|
|
90
|
+
}
|
|
91
|
+
],
|
|
92
|
+
"fill": "#0b1f33"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "These three status dots (OK, warning, error) must be told apart by colour-blind users too. Fix their colours.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "vector",
|
|
5
|
+
"width": 120,
|
|
6
|
+
"height": 40,
|
|
7
|
+
"colorTools": true,
|
|
8
|
+
"setup": [
|
|
9
|
+
{
|
|
10
|
+
"id": "vector.addShape",
|
|
11
|
+
"params": {
|
|
12
|
+
"kind": "ellipse",
|
|
13
|
+
"x": 10,
|
|
14
|
+
"y": 10,
|
|
15
|
+
"width": 20,
|
|
16
|
+
"height": 20,
|
|
17
|
+
"name": "OK",
|
|
18
|
+
"fill": "#2ca02c",
|
|
19
|
+
"stroke": null
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "vector.addShape",
|
|
24
|
+
"params": {
|
|
25
|
+
"kind": "ellipse",
|
|
26
|
+
"x": 50,
|
|
27
|
+
"y": 10,
|
|
28
|
+
"width": 20,
|
|
29
|
+
"height": 20,
|
|
30
|
+
"name": "Warning",
|
|
31
|
+
"fill": "#bcbd22",
|
|
32
|
+
"stroke": null
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "vector.addShape",
|
|
37
|
+
"params": {
|
|
38
|
+
"kind": "ellipse",
|
|
39
|
+
"x": 90,
|
|
40
|
+
"y": 10,
|
|
41
|
+
"width": 20,
|
|
42
|
+
"height": 20,
|
|
43
|
+
"name": "Error",
|
|
44
|
+
"fill": "#d62728",
|
|
45
|
+
"stroke": null
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
"checks": [
|
|
51
|
+
{
|
|
52
|
+
"why": "the three colours pass the colour-vision check",
|
|
53
|
+
"tool": "check_color_palette",
|
|
54
|
+
"args": {
|
|
55
|
+
"colors": {
|
|
56
|
+
"$read": "run_command",
|
|
57
|
+
"args": {
|
|
58
|
+
"id": "vector.list"
|
|
59
|
+
},
|
|
60
|
+
"path": "result.nodes.*.fill"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"expect": {
|
|
64
|
+
"ok": true
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"why": "still three dots",
|
|
69
|
+
"tool": "run_command",
|
|
70
|
+
"args": {
|
|
71
|
+
"id": "vector.list"
|
|
72
|
+
},
|
|
73
|
+
"expect": {
|
|
74
|
+
"result": {
|
|
75
|
+
"nodes": {
|
|
76
|
+
"$length": 3
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
"solution": [
|
|
83
|
+
{
|
|
84
|
+
"tool": "check_color_palette",
|
|
85
|
+
"args": {
|
|
86
|
+
"colors": [
|
|
87
|
+
"#2ca02c",
|
|
88
|
+
"#bcbd22",
|
|
89
|
+
"#d62728"
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"tool": "run_command",
|
|
95
|
+
"args": {
|
|
96
|
+
"id": "vector.setStyle",
|
|
97
|
+
"params": {
|
|
98
|
+
"ids": [
|
|
99
|
+
{
|
|
100
|
+
"$read": "run_command",
|
|
101
|
+
"args": {
|
|
102
|
+
"id": "vector.list"
|
|
103
|
+
},
|
|
104
|
+
"path": "result.nodes.0.id"
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
"fill": "#0072b2"
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"tool": "run_command",
|
|
113
|
+
"args": {
|
|
114
|
+
"id": "vector.setStyle",
|
|
115
|
+
"params": {
|
|
116
|
+
"ids": [
|
|
117
|
+
{
|
|
118
|
+
"$read": "run_command",
|
|
119
|
+
"args": {
|
|
120
|
+
"id": "vector.list"
|
|
121
|
+
},
|
|
122
|
+
"path": "result.nodes.1.id"
|
|
123
|
+
}
|
|
124
|
+
],
|
|
125
|
+
"fill": "#e69f00"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"tool": "run_command",
|
|
131
|
+
"args": {
|
|
132
|
+
"id": "vector.setStyle",
|
|
133
|
+
"params": {
|
|
134
|
+
"ids": [
|
|
135
|
+
{
|
|
136
|
+
"$read": "run_command",
|
|
137
|
+
"args": {
|
|
138
|
+
"id": "vector.list"
|
|
139
|
+
},
|
|
140
|
+
"path": "result.nodes.2.id"
|
|
141
|
+
}
|
|
142
|
+
],
|
|
143
|
+
"fill": "#000000"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"tool": "check_color_palette",
|
|
149
|
+
"args": {
|
|
150
|
+
"colors": {
|
|
151
|
+
"$read": "run_command",
|
|
152
|
+
"args": {
|
|
153
|
+
"id": "vector.list"
|
|
154
|
+
},
|
|
155
|
+
"path": "result.nodes.*.fill"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "The outline icon on this card is too faint. Make sure it meets the WCAG contrast for non-text graphics (3:1) against the card.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "vector",
|
|
5
|
+
"width": 120,
|
|
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": 120,
|
|
16
|
+
"height": 120,
|
|
17
|
+
"name": "Card",
|
|
18
|
+
"fill": "#ffffff",
|
|
19
|
+
"stroke": null
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "vector.addShape",
|
|
24
|
+
"params": {
|
|
25
|
+
"kind": "ellipse",
|
|
26
|
+
"x": 40,
|
|
27
|
+
"y": 40,
|
|
28
|
+
"width": 40,
|
|
29
|
+
"height": 40,
|
|
30
|
+
"name": "Icon",
|
|
31
|
+
"fill": null,
|
|
32
|
+
"stroke": "#c8c8c8",
|
|
33
|
+
"strokeWidth": 2
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"checks": [
|
|
39
|
+
{
|
|
40
|
+
"why": "the icon stroke against the card is at least 3:1",
|
|
41
|
+
"tool": "check_color_contrast",
|
|
42
|
+
"args": {
|
|
43
|
+
"foreground": {
|
|
44
|
+
"$read": "run_command",
|
|
45
|
+
"args": {
|
|
46
|
+
"id": "vector.list"
|
|
47
|
+
},
|
|
48
|
+
"path": "result.nodes.1.stroke"
|
|
49
|
+
},
|
|
50
|
+
"background": {
|
|
51
|
+
"$read": "run_command",
|
|
52
|
+
"args": {
|
|
53
|
+
"id": "vector.list"
|
|
54
|
+
},
|
|
55
|
+
"path": "result.nodes.0.fill"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"expect": {
|
|
59
|
+
"ratio": {
|
|
60
|
+
"$min": 3
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"why": "the card is unchanged",
|
|
66
|
+
"tool": "run_command",
|
|
67
|
+
"args": {
|
|
68
|
+
"id": "vector.list"
|
|
69
|
+
},
|
|
70
|
+
"expect": {
|
|
71
|
+
"result": {
|
|
72
|
+
"nodes": {
|
|
73
|
+
"$some": {
|
|
74
|
+
"name": "Card",
|
|
75
|
+
"fill": "#ffffff"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
"solution": [
|
|
83
|
+
{
|
|
84
|
+
"tool": "check_color_contrast",
|
|
85
|
+
"args": {
|
|
86
|
+
"foreground": "#c8c8c8",
|
|
87
|
+
"background": "#ffffff"
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"tool": "run_command",
|
|
92
|
+
"args": {
|
|
93
|
+
"id": "vector.setStyle",
|
|
94
|
+
"params": {
|
|
95
|
+
"ids": [
|
|
96
|
+
{
|
|
97
|
+
"$read": "run_command",
|
|
98
|
+
"args": {
|
|
99
|
+
"id": "vector.list"
|
|
100
|
+
},
|
|
101
|
+
"path": "result.nodes.1.id"
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
"stroke": "#767676"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
@@ -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
|