Skip to content

Commit bfdcdd5

Browse files
committed
material swatch convention v1.2: bicolor and tricolor as a 135° ramp
Bicolor (2 hard colours) and tricolor (3 hard colours) now render as a smooth 135° linear ramp, first colour top-left, no hard edge — matching the declared-gradient shape at the same length. Four or more hard colours still render as a camembert (N equal conic sectors). Rainbow and the catalogue's declared gradient type are unchanged. Updates the normative doc, its FR translation (+ sourceHash), the reference-renderer playground, glossary entries (EN/FR), and llms.txt.
1 parent 5413c77 commit bfdcdd5

6 files changed

Lines changed: 122 additions & 129 deletions

File tree

‎docs/developers/material-swatch-playground.html‎

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,13 @@ <h1>The material swatch — reference renderer</h1>
9292
./material-swatch.md — keep the two in step.
9393
═══════════════════════════════════════════════════════════════════════════ */
9494

95-
// Hard-edged colours. Exactly TWO → a diagonal split on the 135° axis (first
96-
// colour top-left, hard edge at 50 %) — the ramp's hard-edged twin. THREE or
97-
// more → N equal conic sectors, first colour at 12 o'clock, clockwise. One
95+
// Hard-edged colours. TWO or THREE → a smooth 135° ramp, no hard edge, first
96+
// colour top-left — same shape as a declared gradient of the same length.
97+
// FOUR or more → N equal conic sectors, first colour at 12 o'clock, clockwise
98+
// (a ramp through 4+ colours turns to mud, sectors stay recognisable). One
9899
// function, so every hard split (catalogue list or chip aspect) gets the rule.
99100
const pieSplit = colors => {
100-
if (colors.length === 2) return `linear-gradient(${RAMP_ANGLE}, ${colors[0]} 50%, ${colors[1]} 50%)`;
101+
if (colors.length <= 3) return `linear-gradient(${RAMP_ANGLE}, ${colors.join(', ')})`;
101102
const step = 360 / colors.length;
102103
return `conic-gradient(${colors.map((c, i) => `${c} ${i * step}deg ${(i + 1) * step}deg`).join(', ')})`;
103104
};
@@ -152,11 +153,11 @@ <h1>The material swatch — reference renderer</h1>
152153
}
153154
if (isTricolor) { // 8
154155
const [c1 = '#cccccc', c2 = '#888888', c3] = slots;
155-
return pieSplit([c1, c2, c3 || c1]);
156+
return pieSplit(c3 ? [c1, c2, c3] : [c1, c2]); // slot 3 missing → 2-colour ramp
156157
}
157158
if (isBicolor) { // 9
158159
const [c1 = '#cccccc', c2 = '#ffffff'] = slots;
159-
return pieSplit([c1, c2]); // → diagonal split
160+
return pieSplit([c1, c2]); // → ramp
160161
}
161162
return slots[0] || '#1c2030'; // 10
162163
}
@@ -197,23 +198,25 @@ <h1>The material swatch — reference renderer</h1>
197198
note:'The luminance trap: an unnormalised #000000FF read as "light" puts the black logo on a black spool.',
198199
doc:{ online_color_list:['000000FF'] } },
199200

200-
{ t:'Bicolor — chip aspect 252', rule:'rule 9 — diagonal split, 135°',
201-
note:'Look at all five boxes: the hard edge runs top-right → bottom-left through the centre of the circle, the square, the banner, the column and the clipped bar — first colour top-left, like the ramp.',
201+
{ t:'Bicolor — chip aspect 252', rule:'rule 9 — ramp, 135°',
202+
note:'Look at all five boxes: the blend runs top-left → bottom-right across the circle, the square, the banner, the column and the clipped bar, first colour top-left, no hard edge.',
202203
doc:{ color_r:224, color_g:36, color_b:36, color_r2:36, color_g2:99, color_b2:224,
203204
color_r3:0, color_g3:0, color_b3:0, id_aspect2:ASPECT.BICOLOR } },
204-
{ t:'Tricolor — chip aspect 24', rule:'rule 8 — 3 sectors',
205+
{ t:'Tricolor — chip aspect 24', rule:'rule 8 — ramp, 135°',
205206
doc:{ color_r:224, color_g:36, color_b:36, color_r2:36, color_g2:99, color_b2:224,
206207
color_r3:34, color_g3:160, color_b3:107, id_aspect2:ASPECT.TRICOLOR } },
207208

208-
{ t:'Two colours in the list', rule:'rule 3',
209+
{ t:'Two colours in the list', rule:'rule 3 — ramp',
209210
doc:{ online_color_list:['e02424','2463e0'] } },
210-
{ t:'Four colours in the list (multi)', rule:'rule 3',
211+
{ t:'Three colours in the list', rule:'rule 3 — still a ramp',
212+
doc:{ online_color_list:['e02424','2463e0','22a06b'] } },
213+
{ t:'Four colours in the list (multi)', rule:'rule 3 — camembert from here up',
211214
doc:{ online_color_list:['e02424','f0a020','2463e0','22a06b'], online_color_type:'multi' } },
212215

213216
{ t:'Declared type: gradient', rule:'rule 2 — ramp 135°',
214217
doc:{ online_color_list:['e02424','f0a020','2463e0'], online_color_type:'gradient' } },
215-
{ t:'Declared type: gradient, TWO colours', rule:'rule 2 — still a ramp',
216-
note:'Same axis as the bicolor split, but blended: the catalogue asked for a ramp, not a hard edge.',
218+
{ t:'Declared type: gradient, TWO colours', rule:'rule 2 — same shape as an undeclared 2-colour list',
219+
note:'The declared type only changes anything once the list reaches 4 colours — below that, a hard list already renders as this same ramp.',
217220
doc:{ online_color_list:['e02424','2463e0'], online_color_type:'gradient' } },
218221
{ t:'Declared type: conic_gradient', rule:'rule 1 — catalogue only, never on a chip',
219222
doc:{ online_color_list:['e02424','f0a020','22a06b','2463e0'], online_color_type:'conic_gradient' } },

‎docs/developers/material-swatch.md‎

Lines changed: 46 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ protocol has yet to gain. The convention is written against `id_type` in
1313
general: nothing below reads the product type, so a swatch is produced the same
1414
way whatever the material is.
1515

16-
It is normative. A bicolor filament that shows a diagonal split in one app and a
17-
vertical one in another is a bug in whichever one departed from this page
16+
It is normative. A bicolor filament that shows a smooth ramp in one app and a
17+
hard diagonal split in another is a bug in whichever one departed from this page
1818
— not a matter of taste. If you cannot reproduce a rule exactly (a platform
1919
with no conic gradient, say), implement the closest equivalent described in
2020
[Non-CSS platforms](#non-css-platforms) and say so; do not invent a different
2121
picture.
2222

23-
- **Convention version:** 1.1 — bicolor is a **diagonal** split (1.0 split it vertically)
23+
- **Convention version:** 1.2 — bicolor and tricolor are a **135° ramp** (1.1 drew bicolor as a
24+
hard diagonal split and tricolor as a camembert; 1.0 split bicolor vertically)
2425
- **Reference renderer:** [`material-swatch-playground.html`](./material-swatch-playground.html) —
2526
open it in any browser, no server, no dependency. Every case, every box shape,
2627
live colour pickers, and the exact CSS it produces.
@@ -31,30 +32,28 @@ picture.
3132

3233
| Shape | When | Geometry |
3334
|---|---|---|
34-
| **Diagonal split** | Exactly **two** hard-edged colours — bicolor | Two halves along the **135°** axis, hard edge through the centre: first colour **top-left**, second **bottom-right** |
35-
| **Camembert** (pie) | **Three or more** hard-edged colours — tricolor, any list of N ≥ 3 | N equal conic sectors, first colour starting at **12 o'clock**, sweeping **clockwise** |
36-
| **Ramp** | Rainbow, and the catalogue's declared `gradient` type | A smooth linear ramp at **135°** — pointing to the bottom-right, so the first colour sits top-left |
37-
38-
Said plainly: **two hard colours split on the diagonal, more make a camembert,
39-
smooth colours make a ramp** — and there is exactly one angle in the whole system,
40-
135°, shared by the split and the ramp.
41-
42-
**Bicolor is the diagonal split — the ramp's hard-edged twin.** Same axis, same
43-
order (first colour top-left), but a hard edge at 50 % instead of a blend. The
44-
edge runs top-right → bottom-left through the centre of *any* box: a round
45-
swatch, a square tile, a wide thumbnail, a frame around a photo, a
46-
partially-filled bar. Implement it once, in the function that splits a list of
47-
hard colours (N = 2 → diagonal, N ≥ 3 → pie), so every caller gets it; never draw
48-
it mirrored.
49-
50-
Why a diagonal for two colours: a vertical split read as two separate objects
51-
standing side by side — on a thumbnail, and even more on the colour frame round a
52-
product photo, where the photo hid the centre and left a left bar and a right
53-
bar. A diagonal reads as one two-tone material. Why a pie from three colours up:
54-
sector boundaries are angular, measured from the box centre, so N sectors stay
55-
recognisable whatever the box's aspect ratio, where N diagonal bands would not.
56-
Why 135°: it is the one angle of the system — a bicolor and a two-colour ramp
57-
share the axis, and differ only by the hard edge.
35+
| **Ramp** | **Two or three** hard-edged colours — bicolor, tricolor — plus rainbow and the catalogue's declared `gradient` type | A smooth linear ramp at **135°**, no hard edge — pointing to the bottom-right, so the first colour sits top-left |
36+
| **Camembert** (pie) | **Four or more** hard-edged colours — any list of N ≥ 4 | N equal conic sectors, first colour starting at **12 o'clock**, sweeping **clockwise** |
37+
38+
Said plainly: **two or three hard colours blend into a ramp, four or more make a
39+
camembert** — and there is exactly one angle in the whole system, 135°, shared
40+
by every ramp in it.
41+
42+
**Bicolor and tricolor are a ramp — the same shape as a declared gradient.**
43+
Same axis, same order (first colour top-left), no hard edge anywhere: 2 or 3
44+
hard colours blend exactly like a catalogue `gradient` of the same length.
45+
Implement it once, in the function that splits a list of colours (N ≤ 3 →
46+
ramp, N ≥ 4 → pie), so every caller gets it; never draw it mirrored.
47+
48+
Why a ramp for two or three colours: a hard edge still read as two or three
49+
separate objects — on a thumbnail, and even more on the colour frame round a
50+
product photo, where the photo hid the centre and left disjoint coloured bars.
51+
A ramp reads as one multi-tone material on any box shape. Why a pie from four
52+
colours up: a ramp through four or more colours turns to mud, while sector
53+
boundaries stay angular and recognisable — measured from the box centre, so N
54+
sectors stay legible whatever the box's aspect ratio. Why 135°: it is the one
55+
angle of the system, shared by every ramp, whether it has 2, 3 or more smooth
56+
stops.
5857

5958
---
6059

@@ -80,8 +79,8 @@ authoritative `color_count`):
8079

8180
| id | label | `color_count` | Shape |
8281
|---|---|---|---|
83-
| `252` | Bicolor | 2 | **Diagonal split**, 135° |
84-
| `24` | Tricolor | 3 | Camembert, 3 sectors |
82+
| `252` | Bicolor | 2 | **Ramp**, 135° |
83+
| `24` | Tricolor | 3 | **Ramp**, 135° |
8584
| `145` | Rainbow | 3 | Ramp, 135° |
8685

8786
Every other aspect (`Silk`, `Matt`, `Glitter`, …) has `color_count` ≤ 1 and does
@@ -121,14 +120,14 @@ Let `LIST` = `online_color_list` after [normalisation](#normalisation), `TYPE` =
121120
| # | Condition | Result |
122121
|---|---|---|
123122
| 1 | `LIST ≥ 2` and `TYPE == "conic_gradient"` | Smooth conic sweep, closing on the first colour |
124-
| 2 | `LIST ≥ 2` and `TYPE == "gradient"` | Ramp — **even on two colours**; the catalogue asked for a ramp, so it does not become a hard bicolor split |
125-
| 3 | `LIST ≥ 2` | Hard split of `LIST`: **diagonal** when 2 colours, **camembert** of `LIST.length` sectors when 3 or more |
123+
| 2 | `LIST ≥ 2` and `TYPE == "gradient"` | Ramp — same shape `LIST` would get at 2-3 colours anyway; the declared type only matters once `LIST.length ≥ 4` |
124+
| 3 | `LIST ≥ 2` | **Ramp** of `LIST` when 2 or 3 colours, **camembert** of `LIST.length` sectors when 4 or more |
126125
| 4 | `LIST == 1` | Solid colour — **outranks the chip colour** |
127126
| 5 | aspect **Rainbow** *and* **Tricolor** | Ramp, 3 stops |
128127
| 6 | aspect **Rainbow** *and* **Bicolor** | Ramp, 2 stops |
129128
| 7 | aspect **Rainbow** | Ramp over `SLOTS`; 1 slot → solid; 0 slots → the 6-colour default |
130-
| 8 | aspect **Tricolor** | **Camembert**, 3 sectors over `SLOTS` (slot 3 missing → repeat slot 1) |
131-
| 9 | aspect **Bicolor** | **Diagonal split**, 135°, over slots 1 and 2 |
129+
| 8 | aspect **Tricolor** | **Ramp**, 135°, over `SLOTS` (slot 3 missing → a 2-colour ramp over slots 1 and 2, it does not repeat slot 1) |
130+
| 9 | aspect **Bicolor** | **Ramp**, 135°, over slots 1 and 2 |
132131
| 10 | otherwise | Solid slot 1; nothing at all → `#1c2030` |
133132

134133
Defaults when an aspect carries no usable colour:
@@ -138,7 +137,7 @@ Defaults when an aspect carries no usable colour:
138137
| Rainbow, no colours | `#ff0000 #ff8800 #ffff00 #00cc00 #0000ff #8b00ff` |
139138
| Rainbow + Tricolor | `#ff4d4d #ffd93d #4da3ff` |
140139
| Rainbow + Bicolor | `#ff7a00 #8a2be2` |
141-
| Tricolor | `#cccccc #888888` (+ slot 1 repeated) |
140+
| Tricolor | `#cccccc #888888` (slot 3 missing → 2-colour ramp, slot 1 is not repeated) |
142141
| Bicolor | `#cccccc #ffffff` |
143142
| Nothing | `#1c2030` |
144143

@@ -168,15 +167,13 @@ components are numbers.
168167
With `c1…cN` the normalised colours and `step = 360 / N`:
169168

170169
```css
171-
/* Diagonal split — exactly two hard colours: rule 3 with 2 colours, rule 9 */
172-
linear-gradient(135deg, c1 50%, c2 50%)
170+
/* Ramp — 2 or 3 hard colours: rule 3 with 2-3 colours, rules 5-9. Same shape
171+
as a declared gradient, one angle for every ramp in the system. */
172+
linear-gradient(135deg, c1, c2, …)
173173

174-
/* Camembert — three or more hard colours: rule 3 with ≥ 3 colours, rule 8 */
174+
/* Camembert — four or more hard colours: rule 3 with ≥ 4 colours */
175175
conic-gradient(c1 0deg <step>deg, c2 <step>deg <2·step>deg, …)
176176

177-
/* Ramp — rules 2, 5, 6, 7. One angle for every ramp in the system. */
178-
linear-gradient(135deg, c1, c2, …)
179-
180177
/* Catalogue-declared conic gradient — rule 1 (first colour repeated to close) */
181178
conic-gradient(from 0deg, c1, c2, …, c1)
182179

@@ -192,10 +189,11 @@ Any implementation must reproduce these exactly.
192189
|---|---|
193190
| `{online_color_list:["FF5722"]}` | `#FF5722` |
194191
| `{online_color_list:["000000FF"]}` | `#000000` |
195-
| `{online_color_list:["e02424","2463e0"]}` | `linear-gradient(135deg, #e02424 50%, #2463e0 50%)` |
196-
| `{online_color_list:["e02424","2463e0","22a06b"]}` | `conic-gradient(#e02424 0deg 120deg, #2463e0 120deg 240deg, #22a06b 240deg 360deg)` |
192+
| `{online_color_list:["e02424","2463e0"]}` | `linear-gradient(135deg, #e02424, #2463e0)` |
193+
| `{online_color_list:["e02424","2463e0","22a06b"]}` | `linear-gradient(135deg, #e02424, #2463e0, #22a06b)` |
194+
| `{online_color_list:["e02424","2463e0","22a06b","f0a020"]}` | `conic-gradient(#e02424 0deg 90deg, #2463e0 90deg 180deg, #22a06b 180deg 270deg, #f0a020 270deg 360deg)` |
197195
| `{online_color_list:["e02424","2463e0"],online_color_type:"gradient"}` | `linear-gradient(135deg, #e02424, #2463e0)` |
198-
| `{color_r:224,color_g:36,color_b:36,color_r2:36,color_g2:99,color_b2:224,id_aspect2:252}` | `linear-gradient(135deg, #e02424 50%, #2463e0 50%)` |
196+
| `{color_r:224,color_g:36,color_b:36,color_r2:36,color_g2:99,color_b2:224,id_aspect2:252}` | `linear-gradient(135deg, #e02424, #2463e0)` |
199197
| `{id_aspect1:145}` | `linear-gradient(135deg, #ff0000, #ff8800, #ffff00, #00cc00, #0000ff, #8b00ff)` |
200198
| `{}` | `#1c2030` |
201199

@@ -243,16 +241,12 @@ clockwise.**
243241
clockwise, with hard stops at each `k · 360/N` degrees. Flutter:
244242
`SweepGradient` with `transform: GradientRotation(-pi/2)`. Do not approximate
245243
it with pie slices drawn as paths unless the tile is square — the sector
246-
boundaries must follow the box.
247-
- **Diagonal split** — the ramp's geometry with a hard edge at the middle:
248-
`c1@0, c1@0.5, c2@0.5, c2@1` from the **top-left** to the **bottom-right**.
249-
Flutter: `LinearGradient(begin: Alignment.topLeft, end: Alignment.bottomRight,
250-
colors: [c1, c1, c2, c2], stops: [0, .5, .5, 1])`.
244+
boundaries must follow the box. Only for **four or more** hard colours.
251245
- **Ramp** — a linear gradient from the **top-left** corner to the
252-
**bottom-right**. Flutter: `Alignment.topLeft → Alignment.bottomRight`.
253-
- **Hard stops** — repeat each colour at both ends of its band
254-
(`c1@0, c1@0.5, c2@0.5, c2@1`), which is how a platform without CSS's
255-
two-position syntax gets an edge instead of a blend.
246+
**bottom-right**, evenly spaced stops, no hard edge. Used for **two or
247+
three** hard colours (bicolor, tricolor), for rainbow, and for the
248+
catalogue's declared `gradient` type. Flutter: `Alignment.topLeft →
249+
Alignment.bottomRight`.
256250

257251
---
258252

‎docs/glossary.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ owns the full story.
3434
| **Twin Tag** | The two chips of one spool, written together as a pair and kept identical for the spool's whole life — always counted as **one** spool. |
3535
| **Reference database** | The shared ID tables (brands, materials, aspects, types, diameters, units) served from `cdn.tigertag.io`, so a chip encoded by one tool reads identically in every other. See [Universal filament identity](./concepts/universal-filament-identity.md). |
3636
| **Aspect** | The reference-database field describing a filament's colour and finish; the [material swatch](./developers/material-swatch.md) is painted from it. |
37-
| **Material swatch** | The normative convention for turning a spool's colour *data* into the same *picture* on every surface — three shapes only (diagonal split, camembert, ramp), with a reference renderer to check an implementation against. See [The material swatch](./developers/material-swatch.md). |
37+
| **Material swatch** | The normative convention for turning a spool's colour *data* into the same *picture* on every surface — two shapes only (ramp for 2-3 hard colours, camembert for 4+), with a reference renderer to check an implementation against. See [The material swatch](./developers/material-swatch.md). |
3838
| **`.ttag` file** | The portable, offline interchange file carrying one or more inventory materials (TigerData, TigerData+, TigerTag or TigerTag+) between tools. See [the `.ttag` format](./developers/ttag-format.md). |
3939
| **Second life** | Re-encoding a chip for a refilled or re-purposed spool, or converting it to plain NDEF for any other NFC use — a chip should never become e-waste. See [Second life](./philosophy/second-life.md). |
4040
| **Smartphone bridge** | Using a phone (or a desktop reader) to identify a spool for a printer that has no RFID reader of its own. See [The smartphone bridge](./philosophy/smartphone-bridge.md). |

0 commit comments

Comments
 (0)