@pikku/skills 0.12.32 → 0.12.34

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.
@@ -53,6 +53,128 @@ Then **write the direction into `knowledge/decisions/design/`** — the words th
53
53
  user gave you, what you chose, and what it rules out. The JSON records what the
54
54
  theme is; only the note records why.
55
55
 
56
+ ## The colours the theme has no field for
57
+
58
+ `brand` is the product's accent. It is not the only colour a screen needs, and
59
+ the missing ones are why "don't hardcode colours per component" gets broken by
60
+ the same agent that wrote it down.
61
+
62
+ A screen has to say *covered* and *still open*, *fine* and *needs attention* —
63
+ and those are not the accent. Using the accent for them is worse than a stray
64
+ hex: the brand colour stops meaning "this product" and starts meaning "good", so
65
+ it means nothing. But there is no `brand.covered` field, so the value lands
66
+ inline as `#3f7d5c`, once per component, slightly different each time.
67
+
68
+ Give them a home. A small stylesheet of custom properties, imported once beside
69
+ the Mantine styles, is enough:
70
+
71
+ ```css
72
+ :root:root:root {
73
+ --app-covered: #3f7d5c; --app-covered-bg: #e6f1ea;
74
+ --app-open: #a8701a; --app-open-bg: #fbeedb;
75
+ --app-sunk: #fdf7f4; /* a recessed surface, for forms and asides */
76
+ --app-hairline: #ecdfd9; /* NOT var(--mantine-color-gray-2) — see below */
77
+ }
78
+ :root:root[data-mantine-color-scheme='dark'] {
79
+ --app-covered: #7fc09a; --app-covered-bg: #1e2f26;
80
+ --app-open: #e0ab5c; --app-open-bg: #33271a;
81
+ --app-sunk: #241b18;
82
+ --app-hairline: #392b26;
83
+ }
84
+ ```
85
+
86
+ Those values are one app's warm direction, not a palette to copy — derive your
87
+ own from yours.
88
+
89
+ ## Choose the neutrals; do not inherit them
90
+
91
+ The two details in that snippet that look like typos are the two things most
92
+ likely to make your app look like every other app.
93
+
94
+ **The hairline is a literal, not `var(--mantine-color-gray-2)`.** Mantine's grey
95
+ ramp is blue-biased — `#f8f9fa`, `#dee2e6`, `#868e96` are all cool — and it is
96
+ what draws card borders, dividers, the shell's edges and every disabled control.
97
+ The theme JSON has `brand` and `structure` and **no neutral field at all**, so
98
+ unless you choose otherwise, every app built from this skill runs its accent on
99
+ somebody else's greys.
100
+
101
+ If the accent is not itself blue, that mismatch lands on every screen at once:
102
+ warm content ruled off in cold lines, off everywhere and wrong nowhere in
103
+ particular, which is the hardest kind of wrong to find. It survives a careful
104
+ critique because no single screen is broken.
105
+
106
+ So bias the whole ramp toward the accent — not just the tokens with obvious
107
+ names. Keep Mantine's lightness steps so contrast behaviour and every component
108
+ that picks a step by number are unchanged; move only the hue:
109
+
110
+ ```css
111
+ /* The ramps: hue only. Mantine picks a step by number in either scheme, so
112
+ these are scheme-independent and belong in the unscoped block. */
113
+ :root:root:root {
114
+ --mantine-color-gray-0: #faf7f5; --mantine-color-gray-5: #b8a49d;
115
+ --mantine-color-gray-1: #f5efec; --mantine-color-gray-6: #93807a;
116
+ --mantine-color-gray-2: #efe6e2; --mantine-color-gray-7: #574a45;
117
+ --mantine-color-gray-3: #e6dad5; --mantine-color-gray-8: #3d332f;
118
+ --mantine-color-gray-4: #d8c8c2; --mantine-color-gray-9: #2a1f1b;
119
+ --mantine-color-dark-0: #f3e9e4; --mantine-color-dark-5: #4a3a34;
120
+ --mantine-color-dark-1: #cdbdb6; --mantine-color-dark-6: #392b26;
121
+ --mantine-color-dark-2: #a8938c; --mantine-color-dark-7: #241b18;
122
+ --mantine-color-dark-3: #7a655e; --mantine-color-dark-8: #1b1512;
123
+ --mantine-color-dark-4: #5c4a44; --mantine-color-dark-9: #120d0b;
124
+ }
125
+
126
+ /* Ground, text and border are a different colour in each scheme, so each one
127
+ is set in the scheme it belongs to. A scheme-dependent token left in the
128
+ block above is the classic unreadable-in-dark bug. */
129
+ :root:root:root[data-mantine-color-scheme='light'] {
130
+ --mantine-color-body: #fdfaf8;
131
+ --mantine-color-text: #2a1f1b;
132
+ --mantine-color-default-border: #ecdfd9;
133
+ --mantine-color-dimmed: #7a625c; /* keep AA: ~5.4:1 on the body above */
134
+ --mantine-color-placeholder: #826a64; /* ~4.8:1 */
135
+ }
136
+ :root:root:root[data-mantine-color-scheme='dark'] {
137
+ --mantine-color-body: #1b1512;
138
+ --mantine-color-text: #f3e9e4;
139
+ --mantine-color-default-border: #392b26;
140
+ --mantine-color-dimmed: #a8938c; /* ~5.6:1 on the body above */
141
+ --mantine-color-placeholder: #8c7a74; /* ~4.7:1 */
142
+ }
143
+ ```
144
+
145
+ Check the four text tokens against your own grounds rather than copying these —
146
+ `dimmed` is the most-used text colour in the app and the easiest to drop below
147
+ 4.5:1 while making it prettier, and it has to clear the bar on *both* grounds.
148
+
149
+ **The selector is tripled on purpose.** Mantine's `cssVariablesResolver` injects
150
+ its own `:root` block into `<head>` at runtime, which lands *after* your
151
+ stylesheet and wins on source order at equal specificity. A plain `:root` here is
152
+ silently reverted: the file reads correct, the app renders Mantine's defaults,
153
+ and nothing errors. Repeating the pseudo-class raises specificity without adding
154
+ an element to the selector.
155
+
156
+ This one costs a whole pass if you meet it without knowing: you diagnose the
157
+ colours correctly, write the right values, reload, and see no change — so you
158
+ assume the diagnosis was wrong. **The only thing that catches it is looking at a
159
+ screenshot and disbelieving the CSS.**
160
+
161
+ Name them for what they *mean* in this product, never for the colour — `covered`,
162
+ not `green`. The name is the whole value: it survives a change of palette, and it
163
+ is the thing that makes the second use agree with the first. Define both colour
164
+ schemes at once; a token defined only in light is the classic unreadable-in-dark
165
+ bug, and Mantine will happily render it.
166
+
167
+ The same file is where a couple of other things belong that the theme JSON has no
168
+ field for and every screen otherwise re-invents: the hairline that separates rows
169
+ in a list, the recessed surface a form sits on so it does not carry the same
170
+ weight as the content it adds to, and the one animation the product is allowed
171
+ (behind `prefers-reduced-motion`). Two or three rules, not a framework.
172
+
173
+ If the app ships template screens you did not write — the error and not-found
174
+ pages usually — read them before you call the palette done. They arrive with the
175
+ scaffold's colours hardcoded, and a stock blue accent on an app whose direction
176
+ says warm is the single loudest contradiction in the build.
177
+
56
178
  **Set the theme once, don't hardcode colours per component.** A screen full of
57
179
  inline `color="blue"` and one-off hex values is why apps look templated. Change
58
180
  the theme, not the components — and keep it theme-aware for light and dark.
@@ -266,7 +266,7 @@ Two things about the output matter if you are driving it:
266
266
  `options` is empty when the answer is free text, and an empty list means offer free
267
267
  text — never invent choices to fill it.
268
268
 
269
- `hold` means a profile's own gate is holding the milestone and no seat this loop knows
269
+ `hold` means a profile's own gate is holding the milestone and nothing this loop knows
270
270
  about can clear it. It names the hold and the notes it is about; what to do then
271
271
  belongs to that profile, not here.
272
272
 
@@ -282,7 +282,7 @@ pikku knowledge plan progress <milestone> # what it still owes, read fr
282
282
  pikku knowledge plan defer <milestone> <item> -r "<why>"
283
283
  ```
284
284
 
285
- `progress` reconciles the plan against pikku's generated meta — set membership, never anyone's status — and exits non-zero while the first pass is short, or while anything already built contradicts the plan. Unbuilt work in a later pass is reported, not blocked; a function that shipped wide open against a planned permission rule blocks from any pass, because that is a hole rather than a backlog. Writing a plan is its own seat: read `pikku-architect`. Building against one is `pikku-build`.
285
+ `progress` reconciles the plan against pikku's generated meta — set membership, never anyone's status — and exits non-zero while the first pass is short, or while anything already built contradicts the plan. Unbuilt work in a later pass is reported, not blocked; a function that shipped wide open against a planned permission rule blocks from any pass, because that is a hole rather than a backlog. How a plan is written is `pikku-architect`; building against one, and the order plan-then-build, is `pikku-build`.
286
286
 
287
287
  ### A finished milestone is a tombstone
288
288