grailwind 0.7.0 → 0.8.1
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/CHANGELOG.md +39 -1
- package/README.md +1 -1
- package/charges.js +14 -2
- package/data.js +54 -9
- package/divisions.js +137 -47
- package/grailwind.css +36 -0
- package/ordinaries.js +355 -55
- package/package.json +5 -3
- package/paths.js +848 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,42 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.8.1] - 2026-11-13
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Counterchanged charges `on` a counterchanged ordinary showed the ordinary's
|
|
14
|
+
swapped field and vanished. They now show the field's own tinctures.
|
|
15
|
+
- `chevronny-*` in a part of a split shield (a `per-pale` half, a `per-fess`
|
|
16
|
+
chief) broke at the centre line. It now follows the part, as `per-chevron`
|
|
17
|
+
does, and the arms meet.
|
|
18
|
+
|
|
19
|
+
## [0.8.0] - 2026-11-12
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Wavy ordinaries, e.g. `fess-wavy-or` and `three-bars-wavy-argent`.
|
|
24
|
+
- Indented and embattled ordinaries, e.g. `chief-indented-azure` and
|
|
25
|
+
`cross-embattled-gules`. A fess, bar, bend or chevron embattled has
|
|
26
|
+
battlements on its upper edge only. The chevron, cross, saltire, pall and
|
|
27
|
+
pile take them with hand-drawn corners, as do the wavy chevron and pall.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- Ordinary proportions move towards Fox-Davies: the chevron is wider (a fifth
|
|
32
|
+
of the shield), the cross's limbs are equal, the saltire is wider and crosses
|
|
33
|
+
at the fess point (as does `per-saltire-*`), and the pile is wider. The
|
|
34
|
+
pall's arms and foot are equal, and its arms meet above the fess point.
|
|
35
|
+
- The engrailed cross and pile are hand-drawn, with clean corners and tips.
|
|
36
|
+
- `@grailwind/eslint-plugin` 0.1.0-beta.4 needs `grailwind` 0.8.x.
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- `gyronny-*` crosses at the fess point, as `per-saltire-*` does, so a
|
|
41
|
+
counterchanged saltire lines up with it.
|
|
42
|
+
- Charges `on` an engrailed or indented ordinary are smaller, and charges
|
|
43
|
+
`between` a lined bend, pall, or embattled chevron move clear of its edge.
|
|
44
|
+
|
|
9
45
|
## [0.7.0] - 2026-11-09
|
|
10
46
|
|
|
11
47
|
### Added
|
|
@@ -157,7 +193,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
157
193
|
Initial release: tinctures, divisions, ordinaries, charges, strict mode and
|
|
158
194
|
custom SVG charges.
|
|
159
195
|
|
|
160
|
-
[Unreleased]: https://github.com/pafirmin/grailwind/compare/v0.
|
|
196
|
+
[Unreleased]: https://github.com/pafirmin/grailwind/compare/v0.8.1...HEAD
|
|
197
|
+
[0.8.1]: https://github.com/pafirmin/grailwind/compare/v0.8.0...v0.8.1
|
|
198
|
+
[0.8.0]: https://github.com/pafirmin/grailwind/compare/v0.7.0...v0.8.0
|
|
161
199
|
[0.7.0]: https://github.com/pafirmin/grailwind/compare/v0.6.0...v0.7.0
|
|
162
200
|
[0.6.0]: https://github.com/pafirmin/grailwind/compare/v0.5.0...v0.6.0
|
|
163
201
|
[0.5.0]: https://github.com/pafirmin/grailwind/compare/v0.4.0...v0.5.0
|
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ tinctures replace Tailwind's colours entirely.
|
|
|
61
61
|
`per-pale`, `per-fess` and `quarterly` for layout.
|
|
62
62
|
- **Ordinaries:** `fess-or`, `chevron-gules`, `cross-argent`, `chief-azure`,
|
|
63
63
|
`two-bars-or` and more, with `fimbriated-*` for a narrow border,
|
|
64
|
-
`fess-engrailed-or` and the like for an engrailed or
|
|
64
|
+
`fess-engrailed-or` and the like for an engrailed, invected, wavy, indented or embattled edge,
|
|
65
65
|
`cross-voided-argent` and the like to show the field through it,
|
|
66
66
|
`pile-reversed-or` (also the chevron and pall) to turn one upside down,
|
|
67
67
|
`chief-ermine` and the like in a fur, or `chevron-counterchanged` and the like
|
package/charges.js
CHANGED
|
@@ -149,17 +149,28 @@ const inheritCounterchange = Object.fromEntries(
|
|
|
149
149
|
COUNTERCHANGE.map((n) => [`--counterchange-${n}`, "inherit"]),
|
|
150
150
|
);
|
|
151
151
|
|
|
152
|
+
// What a charge on a counterchanged ordinary is counterchanged against: the field's own
|
|
153
|
+
// tinctures, handed down by the ordinary to `on` containers only.
|
|
154
|
+
const inheritOnCounterchange = Object.fromEntries(
|
|
155
|
+
COUNTERCHANGE.map((n) => [`--on-counterchange-${n}`, "inherit"]),
|
|
156
|
+
);
|
|
157
|
+
|
|
152
158
|
// A layer the size of the shield, as the escutcheon is the containing block
|
|
153
159
|
// for fixed positioning. The charge's mask cuts it to shape.
|
|
154
160
|
const counterchanged = {
|
|
155
161
|
...inheritCounterchange,
|
|
162
|
+
...inheritOnCounterchange,
|
|
156
163
|
"&::before": {
|
|
157
164
|
content: '""',
|
|
158
165
|
position: "fixed",
|
|
159
166
|
inset: "0",
|
|
160
167
|
...inheritCounterchange,
|
|
168
|
+
...inheritOnCounterchange,
|
|
161
169
|
...Object.fromEntries(
|
|
162
|
-
COUNTERCHANGE.map((n) => [
|
|
170
|
+
COUNTERCHANGE.map((n) => [
|
|
171
|
+
`background-${n}`,
|
|
172
|
+
`var(--on-counterchange-${n}, var(--counterchange-${n}))`,
|
|
173
|
+
]),
|
|
163
174
|
),
|
|
164
175
|
},
|
|
165
176
|
};
|
|
@@ -279,7 +290,8 @@ export default plugin(({ addUtilities, matchUtilities }) => {
|
|
|
279
290
|
};
|
|
280
291
|
}
|
|
281
292
|
}
|
|
282
|
-
|
|
293
|
+
// Nested, so Tailwind keeps these apart from the per-count rules above.
|
|
294
|
+
utilities[".on"] = { "&": inheritOnCounterchange, ...on };
|
|
283
295
|
|
|
284
296
|
for (const name of Object.keys(POINTS)) {
|
|
285
297
|
utilities[`.in-${name}`] = { ".charges > &": atPoint(name) };
|
package/data.js
CHANGED
|
@@ -80,7 +80,10 @@ export const ROUNDELS = {
|
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
// Lines of partition for an ordinary's edge, e.g. `fess-engrailed-or`.
|
|
83
|
-
export const LINES = ["engrailed", "invected"];
|
|
83
|
+
export const LINES = ["engrailed", "invected", "wavy", "indented", "embattled"];
|
|
84
|
+
|
|
85
|
+
// The ordinaries that take a line: every one, the cornered ones drawn by hand (064).
|
|
86
|
+
export const lineOrdinaries = () => ORDINARIES;
|
|
84
87
|
|
|
85
88
|
// Ordinaries that can be voided, e.g. `cross-voided-argent`.
|
|
86
89
|
export const VOIDABLE = [
|
|
@@ -143,15 +146,18 @@ export const BETWEEN = {
|
|
|
143
146
|
"bend-sinister": { size: 24, points: [[30, 26], [70, 68]], counts: {
|
|
144
147
|
6: { size: 14, points: [[52, 9], [24, 12], [16, 38], [91, 48], [82, 72], [62, 84]] } } },
|
|
145
148
|
chevron: { size: 22, points: [[24, 22], [76, 22], [50, 70]], counts: {
|
|
146
|
-
6: { size: 12, points: [[18, 14], [50, 11], [82, 14], [
|
|
149
|
+
6: { size: 12, points: [[18, 14], [50, 11], [82, 14], [40, 72], [60, 72], [50, 84]] } } },
|
|
147
150
|
cross: { size: 18, points: [[20, 17], [80, 17], [24, 72], [76, 72]] },
|
|
148
|
-
saltire: { size: 16, points: [[50, 13], [14,
|
|
149
|
-
2: { points: [[14,
|
|
151
|
+
saltire: { size: 16, points: [[50, 13], [14, 45], [86, 45], [50, 84]], counts: {
|
|
152
|
+
2: { points: [[14, 45], [86, 45]] } } },
|
|
150
153
|
pall: { size: 18, points: [[50, 11], [18, 56], [82, 56]], counts: {
|
|
151
154
|
2: { points: [[18, 56], [82, 56]] } } },
|
|
152
155
|
pile: { size: 20, points: [[18, 55], [82, 55]] },
|
|
153
156
|
};
|
|
154
157
|
|
|
158
|
+
// `on` charges shrink on lines that cut deep into the ordinary, by line.
|
|
159
|
+
export const ON_SCALE = { engrailed: 0.8, indented: 0.9 };
|
|
160
|
+
|
|
155
161
|
// Where charges go for "on", along the ordinary's centre line, by number of charges.
|
|
156
162
|
// prettier-ignore
|
|
157
163
|
export const ON = {
|
|
@@ -159,10 +165,10 @@ export const ON = {
|
|
|
159
165
|
fess: { size: 22, counts: { 1: [[50, 45]], 2: [[30, 45], [70, 45]], 3: [[20, 45], [50, 45], [80, 45]] } },
|
|
160
166
|
bend: { size: 18, counts: { 1: [[50, 50]], 2: [[33, 33], [67, 67]], 3: [[24, 24], [50, 50], [76, 76]] } },
|
|
161
167
|
"bend-sinister": { size: 18, counts: { 1: [[50, 50]], 2: [[67, 33], [33, 67]], 3: [[76, 24], [50, 50], [24, 76]] } },
|
|
162
|
-
chevron: { size:
|
|
168
|
+
chevron: { size: 15, counts: { 1: [[50, 36]], 3: [[20, 65], [50, 36], [80, 65]] } },
|
|
163
169
|
cross: { size: 15, counts: { 1: [[50, 45]], 5: [[50, 15], [18, 45], [50, 45], [82, 45], [50, 80]] } },
|
|
164
|
-
saltire: { size:
|
|
165
|
-
pall: { size:
|
|
170
|
+
saltire: { size: 16, counts: { 1: [[50, 45]], 5: [[22, 19.8], [78, 19.8], [50, 45], [22, 70.2], [78, 70.2]] } },
|
|
171
|
+
pall: { size: 16, counts: { 1: [[50, 40]], 3: [[26, 20.8], [74, 20.8], [50, 75]] } },
|
|
166
172
|
pile: { size: 14, counts: { 1: [[50, 25]], 3: [[50, 14], [50, 34], [50, 54]] } },
|
|
167
173
|
};
|
|
168
174
|
|
|
@@ -187,6 +193,45 @@ for (const ordinary of REVERSIBLE) {
|
|
|
187
193
|
// prettier-ignore
|
|
188
194
|
BETWEEN["chevron-reversed"] = {
|
|
189
195
|
...BETWEEN["chevron-reversed"],
|
|
190
|
-
points: [[
|
|
191
|
-
counts: { 6: { size: 12, points: [[26, 78], [50, 89], [74, 78], [
|
|
196
|
+
points: [[26, 76], [74, 76], [50, 30]],
|
|
197
|
+
counts: { 6: { size: 12, points: [[26, 78], [50, 89], [74, 78], [40, 28], [60, 28], [50, 16]] } },
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// `between` overrides where a line's edge hits a charge, by ordinary and line; the rest falls
|
|
201
|
+
// back to the plain ordinary's. The sinister bend and the reversed pall are mirrored from these.
|
|
202
|
+
// prettier-ignore
|
|
203
|
+
export const BETWEEN_LINED = {
|
|
204
|
+
bend: {
|
|
205
|
+
engrailed: { points: [[70, 26], [24, 72]] },
|
|
206
|
+
invected: { points: [[70, 26], [24, 72]] },
|
|
207
|
+
wavy: { points: [[70, 26], [24, 72]] },
|
|
208
|
+
indented: { points: [[70, 26], [24, 72]] },
|
|
209
|
+
},
|
|
210
|
+
chevron: {
|
|
211
|
+
embattled: { counts: { 6: { size: 10, points: [[16, 13], [50, 8], [84, 13], [40, 72], [60, 72], [50, 84]] } } },
|
|
212
|
+
},
|
|
213
|
+
"chevron-reversed": {
|
|
214
|
+
embattled: { points: [[26, 76], [74, 76], [50, 24]] },
|
|
215
|
+
},
|
|
216
|
+
pall: {
|
|
217
|
+
invected: { points: [[50, 8], [18, 56], [82, 56]] },
|
|
218
|
+
wavy: { points: [[50, 8], [18, 56], [82, 56]] },
|
|
219
|
+
indented: { points: [[50, 8], [18, 56], [82, 56]] },
|
|
220
|
+
embattled: { points: [[50, 8], [18, 56], [82, 56]] },
|
|
221
|
+
},
|
|
192
222
|
};
|
|
223
|
+
|
|
224
|
+
const mirrorX = (points) => points.map(([x, y]) => [100 - x, y]);
|
|
225
|
+
const mirrorLined = (entry, flip) => ({
|
|
226
|
+
...entry,
|
|
227
|
+
...(entry.points && { points: flip(entry.points) }),
|
|
228
|
+
...(entry.counts && {
|
|
229
|
+
counts: mapValues(entry.counts, (count) => ({ ...count, points: flip(count.points) })),
|
|
230
|
+
}),
|
|
231
|
+
});
|
|
232
|
+
BETWEEN_LINED["bend-sinister"] = mapValues(BETWEEN_LINED.bend, (entry) =>
|
|
233
|
+
mirrorLined(entry, mirrorX),
|
|
234
|
+
);
|
|
235
|
+
BETWEEN_LINED["pall-reversed"] = mapValues(BETWEEN_LINED.pall, (entry) =>
|
|
236
|
+
mirrorLined(entry, mirror),
|
|
237
|
+
);
|
package/divisions.js
CHANGED
|
@@ -5,8 +5,12 @@ import plugin from "tailwindcss/plugin";
|
|
|
5
5
|
import { FURS, TINCTURES } from "./data.js";
|
|
6
6
|
import { furLayers } from "./furs.js";
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
|
|
8
|
+
// The fess point, in % down the shield: horizontal lines of partition pass through it, as the
|
|
9
|
+
// cross and fess do.
|
|
10
|
+
const FESS_POINT = 45;
|
|
11
|
+
|
|
12
|
+
// The background-position that puts a tile FESS_POINT% high just below the fess point.
|
|
13
|
+
const BASE_TILES = ((100 * FESS_POINT) / (100 - FESS_POINT)).toFixed(2);
|
|
10
14
|
|
|
11
15
|
// Each division maps (first, second) tincture colours to background rules.
|
|
12
16
|
const layer = (image) => ({ "background-image": image });
|
|
@@ -46,31 +50,36 @@ const bendy = (to, pieces) => (a, b) => {
|
|
|
46
50
|
return layer(`linear-gradient(to ${to}, ${stops.join(", ")})`);
|
|
47
51
|
};
|
|
48
52
|
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// A
|
|
54
|
-
// Plain, the
|
|
55
|
-
//
|
|
53
|
+
// Four tiles, each half the box, so the lines follow the box's own proportions: a part of the
|
|
54
|
+
// shield is the whole one squeezed to fit, as per chevron's are. A tile's lines run parallel to its
|
|
55
|
+
// diagonal, dexter tiles `to bottom right` and sinister ones `to bottom left`.
|
|
56
|
+
// The period is 300/n of a tile's gradient line, which keeps a stripe's width on the whole shield.
|
|
57
|
+
// A lower tile starts half a gradient line further on, so its lines continue the upper tile's.
|
|
58
|
+
// Plain, the dexter and lower tiles overlap the others by 1px, hiding the seams. With a fur the
|
|
59
|
+
// transparent stripes would show the tile beneath, so they butt.
|
|
56
60
|
const chevronny = (pieces) => (a, b, fur) => {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
61
|
+
const period = 300 / pieces;
|
|
62
|
+
const lowerStart = (period - (50 % period)) % period;
|
|
63
|
+
const stripe = (to, start) =>
|
|
64
|
+
`repeating-linear-gradient(to bottom ${to}, ${a} ${start}% ${start + period / 2}%, ${b} 0 ${start + period}%)`;
|
|
65
|
+
const overlap = fur ? "50%" : "calc(50% + 1px)";
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
"background-image": [
|
|
69
|
+
stripe("left", 0),
|
|
70
|
+
stripe("left", lowerStart),
|
|
71
|
+
stripe("right", 0),
|
|
72
|
+
stripe("right", lowerStart),
|
|
73
|
+
].join(", "),
|
|
74
|
+
"background-size": [
|
|
75
|
+
`50% 50%`,
|
|
76
|
+
`50% ${overlap}`,
|
|
77
|
+
`${overlap} 50%`,
|
|
78
|
+
`${overlap} ${overlap}`,
|
|
79
|
+
].join(", "),
|
|
80
|
+
"background-position": "right top, right bottom, left top, left bottom",
|
|
81
|
+
"background-repeat": "no-repeat",
|
|
82
|
+
};
|
|
74
83
|
};
|
|
75
84
|
|
|
76
85
|
const DIVISIONS = {
|
|
@@ -78,7 +87,8 @@ const DIVISIONS = {
|
|
|
78
87
|
"per-pale": (a, b) => layer(`linear-gradient(to right, ${a} 50%, ${b} 50%)`),
|
|
79
88
|
|
|
80
89
|
// Horizontal split through the fess point: first in chief.
|
|
81
|
-
"per-fess": (a, b) =>
|
|
90
|
+
"per-fess": (a, b) =>
|
|
91
|
+
layer(`linear-gradient(to bottom, ${a} ${FESS_POINT}%, ${b} ${FESS_POINT}%)`),
|
|
82
92
|
|
|
83
93
|
// Line from dexter chief to sinister base: first in the upper half.
|
|
84
94
|
// With a corner-to-corner gradient, the 50% line joins the other two corners.
|
|
@@ -106,9 +116,11 @@ const DIVISIONS = {
|
|
|
106
116
|
].join(", "),
|
|
107
117
|
},
|
|
108
118
|
|
|
109
|
-
// Both
|
|
110
|
-
//
|
|
111
|
-
//
|
|
119
|
+
// Both lines of the saltire, from the top corners through the fess point to the flanks at 90%:
|
|
120
|
+
// first in chief and base. Four tiles, each half the width and 45% high, with each half-line
|
|
121
|
+
// as a tile's diagonal, and the base below them in the first tincture, 2px taller so Chromium
|
|
122
|
+
// leaves no seam where it meets them. Two strips under the tiles fill the sub-pixel seam where
|
|
123
|
+
// they meet: the first tincture down, the second across.
|
|
112
124
|
"per-saltire": (a, b) => ({
|
|
113
125
|
"background-image": [
|
|
114
126
|
`linear-gradient(to bottom left, ${a} 50%, ${b} 50%)`,
|
|
@@ -116,10 +128,24 @@ const DIVISIONS = {
|
|
|
116
128
|
`linear-gradient(to bottom left, ${b} 50%, ${a} 50%)`,
|
|
117
129
|
`linear-gradient(to bottom right, ${b} 50%, ${a} 50%)`,
|
|
118
130
|
`linear-gradient(${a}, ${a})`,
|
|
131
|
+
`linear-gradient(${a}, ${a})`,
|
|
119
132
|
`linear-gradient(${b}, ${b})`,
|
|
120
133
|
].join(", "),
|
|
121
|
-
"background-size":
|
|
122
|
-
|
|
134
|
+
"background-size": [
|
|
135
|
+
...Array(4).fill(`50% ${FESS_POINT}%`),
|
|
136
|
+
`100% calc(${100 - 2 * FESS_POINT}% + 2px)`,
|
|
137
|
+
"2px 100%",
|
|
138
|
+
"100% 2px",
|
|
139
|
+
].join(", "),
|
|
140
|
+
"background-position": [
|
|
141
|
+
"left top",
|
|
142
|
+
"right top",
|
|
143
|
+
`right ${BASE_TILES}%`,
|
|
144
|
+
`left ${BASE_TILES}%`,
|
|
145
|
+
"bottom",
|
|
146
|
+
"center",
|
|
147
|
+
`center ${FESS_POINT}%`,
|
|
148
|
+
].join(", "),
|
|
123
149
|
"background-repeat": "no-repeat",
|
|
124
150
|
}),
|
|
125
151
|
|
|
@@ -128,14 +154,15 @@ const DIVISIONS = {
|
|
|
128
154
|
// With a fur, the bands can't overlap, or the fur's transparent half shows the band below.
|
|
129
155
|
quarterly: (a, b, fur) => ({
|
|
130
156
|
"background-image": `linear-gradient(to right, ${a} 50%, ${b} 50%), linear-gradient(to right, ${b} 50%, ${a} 50%)`,
|
|
131
|
-
"background-size": `100% ${FESS_POINT}
|
|
157
|
+
"background-size": `100% ${FESS_POINT}%, 100% ${fur ? `calc(100% - ${FESS_POINT}%)` : "100%"}`,
|
|
132
158
|
"background-position": fur ? "top, bottom" : "top, top",
|
|
133
159
|
"background-repeat": "no-repeat",
|
|
134
160
|
}),
|
|
135
161
|
|
|
136
|
-
// Gyronny of eight:
|
|
137
|
-
// is in the gyron just dexter of the top line.
|
|
138
|
-
// any sub-pixel
|
|
162
|
+
// Gyronny of eight: quarterly and per saltire, all through the fess point. The first tincture
|
|
163
|
+
// is in the gyron just dexter of the top line. Per saltire's four tiles, then the base below
|
|
164
|
+
// them split per pale, 2px taller so no seam shows. The background colour fills any sub-pixel
|
|
165
|
+
// seam between tiles.
|
|
139
166
|
gyronny: (a, b) => ({
|
|
140
167
|
"background-color": b,
|
|
141
168
|
"background-image": [
|
|
@@ -143,9 +170,19 @@ const DIVISIONS = {
|
|
|
143
170
|
`linear-gradient(to bottom right, ${b} 50%, ${a} 50%)`,
|
|
144
171
|
`linear-gradient(to bottom left, ${b} 50%, ${a} 50%)`,
|
|
145
172
|
`linear-gradient(to bottom right, ${a} 50%, ${b} 50%)`,
|
|
173
|
+
`linear-gradient(to right, ${b} 50%, ${a} 50%)`,
|
|
174
|
+
].join(", "),
|
|
175
|
+
"background-size": [
|
|
176
|
+
...Array(4).fill(`50% ${FESS_POINT}%`),
|
|
177
|
+
`100% calc(${100 - 2 * FESS_POINT}% + 2px)`,
|
|
178
|
+
].join(", "),
|
|
179
|
+
"background-position": [
|
|
180
|
+
"left top",
|
|
181
|
+
"right top",
|
|
182
|
+
`right ${BASE_TILES}%`,
|
|
183
|
+
`left ${BASE_TILES}%`,
|
|
184
|
+
"bottom",
|
|
146
185
|
].join(", "),
|
|
147
|
-
"background-size": "50% 50%",
|
|
148
|
-
"background-position": "left top, right top, right bottom, left bottom",
|
|
149
186
|
"background-repeat": "no-repeat",
|
|
150
187
|
}),
|
|
151
188
|
|
|
@@ -193,21 +230,70 @@ const toLayers = (rules) => {
|
|
|
193
230
|
});
|
|
194
231
|
};
|
|
195
232
|
|
|
196
|
-
|
|
197
|
-
|
|
233
|
+
/**
|
|
234
|
+
* A division with one fur: the fur's part is left transparent, over the fur's own layers.
|
|
235
|
+
* @param {string} division The division's name.
|
|
236
|
+
* @param {string} first The first tincture or fur.
|
|
237
|
+
* @param {string} second The second tincture or fur; one of the two is a fur.
|
|
238
|
+
* @param {Function} theme Tailwind's theme function, for the spots' colour.
|
|
239
|
+
* @returns {{image: string, size: string, position: string, repeat: string}[]} The layers, top first.
|
|
240
|
+
*/
|
|
241
|
+
const furredLayers = (division, first, second, theme) => {
|
|
198
242
|
const fur = FURS[first] ? first : second;
|
|
199
243
|
const paint = (t) => (t === fur ? "transparent" : `var(--color-${t})`);
|
|
200
|
-
|
|
244
|
+
return [
|
|
201
245
|
...toLayers(DIVISIONS[division](paint(first), paint(second), true)),
|
|
202
246
|
...furLayers(fur, theme),
|
|
203
247
|
];
|
|
204
|
-
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Layers joined into one comma-separated value per property.
|
|
252
|
+
* @param {{image: string, size: string, position: string, repeat: string}[]} layers
|
|
253
|
+
* @param {string} prefix The property prefix, such as "background" or "--field".
|
|
254
|
+
* @returns {Object<string, string>} `<prefix>-image`, `-size`, `-position` and `-repeat`.
|
|
255
|
+
*/
|
|
256
|
+
const joinLayers = (layers, prefix) =>
|
|
257
|
+
Object.fromEntries(
|
|
205
258
|
["image", "size", "position", "repeat"].map((p) => [
|
|
206
|
-
|
|
207
|
-
|
|
259
|
+
`${prefix}-${p}`,
|
|
260
|
+
layers.map((layer) => layer[p]).join(", "),
|
|
208
261
|
]),
|
|
209
262
|
);
|
|
210
|
-
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* A division with one fur, as background rules.
|
|
266
|
+
* @param {string} division The division's name.
|
|
267
|
+
* @param {string} first The first tincture or fur.
|
|
268
|
+
* @param {string} second The second tincture or fur; one of the two is a fur.
|
|
269
|
+
* @param {Function} theme Tailwind's theme function, for the spots' colour.
|
|
270
|
+
* @returns {Object<string, string>} The four background-* properties.
|
|
271
|
+
*/
|
|
272
|
+
const withFur = (division, first, second, theme) =>
|
|
273
|
+
joinLayers(furredLayers(division, first, second, theme), "background");
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* The field's own layers as --field-* variables, for charges on a counterchanged ordinary.
|
|
277
|
+
* A background colour becomes a last opaque layer, as the variables carry no colour.
|
|
278
|
+
* @param {{image: string, size: string, position: string, repeat: string}[]} layers
|
|
279
|
+
* @param {string} [color] The division's background-color, if it has one.
|
|
280
|
+
* @returns {Object<string, string>} The four --field-* variables.
|
|
281
|
+
*/
|
|
282
|
+
const field = (layers, color) =>
|
|
283
|
+
joinLayers(
|
|
284
|
+
color
|
|
285
|
+
? [
|
|
286
|
+
...layers,
|
|
287
|
+
{
|
|
288
|
+
image: `linear-gradient(${color}, ${color})`,
|
|
289
|
+
size: "100% 100%",
|
|
290
|
+
position: "0 0",
|
|
291
|
+
repeat: "no-repeat",
|
|
292
|
+
},
|
|
293
|
+
]
|
|
294
|
+
: layers,
|
|
295
|
+
"--field",
|
|
296
|
+
);
|
|
211
297
|
|
|
212
298
|
// The field with its tinctures swapped, for counterchanged ordinaries to paint.
|
|
213
299
|
const counterchange = (rules) =>
|
|
@@ -230,8 +316,10 @@ export default plugin(({ addUtilities, theme }) => {
|
|
|
230
316
|
|
|
231
317
|
const a = `var(--color-${first})`;
|
|
232
318
|
const b = `var(--color-${second})`;
|
|
319
|
+
const unswapped = background(a, b);
|
|
233
320
|
utilities[`.${division}-${first}-${second}`] = {
|
|
234
|
-
...
|
|
321
|
+
...unswapped,
|
|
322
|
+
...field(toLayers(unswapped), unswapped["background-color"]),
|
|
235
323
|
...counterchange(background(b, a)),
|
|
236
324
|
"--field-first": a,
|
|
237
325
|
};
|
|
@@ -247,9 +335,11 @@ export default plugin(({ addUtilities, theme }) => {
|
|
|
247
335
|
[fur, tincture],
|
|
248
336
|
[tincture, fur],
|
|
249
337
|
]) {
|
|
338
|
+
const layers = furredLayers(division, first, second, theme);
|
|
250
339
|
// Only a tincture can be "of the first"; a fur first leaves it unset.
|
|
251
340
|
utilities[`.${division}-${first}-${second}`] = {
|
|
252
|
-
...
|
|
341
|
+
...joinLayers(layers, "background"),
|
|
342
|
+
...field(layers),
|
|
253
343
|
...counterchange(withFur(division, second, first, theme)),
|
|
254
344
|
...(!FURS[first] && { "--field-first": `var(--color-${first})` }),
|
|
255
345
|
};
|
package/grailwind.css
CHANGED
|
@@ -42,6 +42,42 @@
|
|
|
42
42
|
syntax: "*";
|
|
43
43
|
inherits: false;
|
|
44
44
|
}
|
|
45
|
+
@property --field-image {
|
|
46
|
+
syntax: "*";
|
|
47
|
+
inherits: false;
|
|
48
|
+
}
|
|
49
|
+
@property --field-size {
|
|
50
|
+
syntax: "*";
|
|
51
|
+
inherits: false;
|
|
52
|
+
}
|
|
53
|
+
@property --field-position {
|
|
54
|
+
syntax: "*";
|
|
55
|
+
inherits: false;
|
|
56
|
+
}
|
|
57
|
+
@property --field-repeat {
|
|
58
|
+
syntax: "*";
|
|
59
|
+
inherits: false;
|
|
60
|
+
}
|
|
61
|
+
@property --on-counterchange-color {
|
|
62
|
+
syntax: "*";
|
|
63
|
+
inherits: false;
|
|
64
|
+
}
|
|
65
|
+
@property --on-counterchange-image {
|
|
66
|
+
syntax: "*";
|
|
67
|
+
inherits: false;
|
|
68
|
+
}
|
|
69
|
+
@property --on-counterchange-size {
|
|
70
|
+
syntax: "*";
|
|
71
|
+
inherits: false;
|
|
72
|
+
}
|
|
73
|
+
@property --on-counterchange-position {
|
|
74
|
+
syntax: "*";
|
|
75
|
+
inherits: false;
|
|
76
|
+
}
|
|
77
|
+
@property --on-counterchange-repeat {
|
|
78
|
+
syntax: "*";
|
|
79
|
+
inherits: false;
|
|
80
|
+
}
|
|
45
81
|
/* A field's first tincture. The escutcheon copies it to --the-first, which inherits,
|
|
46
82
|
* so a canton's own field can't change it. */
|
|
47
83
|
@property --field-first {
|