grailwind 0.6.0 → 0.8.0
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 +48 -1
- package/README.md +3 -3
- package/data.js +62 -9
- package/divisions.js +150 -26
- package/grailwind.css +21 -8
- package/ordinaries.js +341 -51
- package/package.json +12 -4
- package/paths.js +848 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,51 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.8.0] - 2026-11-12
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Wavy ordinaries, e.g. `fess-wavy-or` and `three-bars-wavy-argent`.
|
|
14
|
+
- Indented and embattled ordinaries, e.g. `chief-indented-azure` and
|
|
15
|
+
`cross-embattled-gules`. A fess, bar, bend or chevron embattled has
|
|
16
|
+
battlements on its upper edge only. The chevron, cross, saltire, pall and
|
|
17
|
+
pile take them with hand-drawn corners, as do the wavy chevron and pall.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Ordinary proportions move towards Fox-Davies: the chevron is wider (a fifth
|
|
22
|
+
of the shield), the cross's limbs are equal, the saltire is wider and crosses
|
|
23
|
+
at the fess point (as does `per-saltire-*`), and the pile is wider. The
|
|
24
|
+
pall's arms and foot are equal, and its arms meet above the fess point.
|
|
25
|
+
- The engrailed cross and pile are hand-drawn, with clean corners and tips.
|
|
26
|
+
- `@grailwind/eslint-plugin` 0.1.0-beta.4 needs `grailwind` 0.8.x.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- `gyronny-*` crosses at the fess point, as `per-saltire-*` does, so a
|
|
31
|
+
counterchanged saltire lines up with it.
|
|
32
|
+
- Charges `on` an engrailed or indented ordinary are smaller, and charges
|
|
33
|
+
`between` a lined bend, pall, or embattled chevron move clear of its edge.
|
|
34
|
+
|
|
35
|
+
## [0.7.0] - 2026-11-09
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- `per-chevron-*`: a field divided along a chevron's centre line, the first
|
|
40
|
+
tincture in chief.
|
|
41
|
+
- `bendy-*`, `bendy-sinister-*`, `chevronny-*` (each with `-of-eight-*`) and
|
|
42
|
+
`chequy-*`: six or eight pieces, or five squares by six.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- `@grailwind/eslint-plugin` 0.1.0-beta.3 needs `grailwind` 0.7.x.
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- On a shield, `quarterly` and two-part `per-fess` split at the fess point, as
|
|
51
|
+
the divisions do, not halfway down.
|
|
52
|
+
- No seam down the centre of `per-saltire-*` at odd widths.
|
|
53
|
+
|
|
9
54
|
## [0.6.0] - 2026-11-06
|
|
10
55
|
|
|
11
56
|
### Added
|
|
@@ -138,7 +183,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
138
183
|
Initial release: tinctures, divisions, ordinaries, charges, strict mode and
|
|
139
184
|
custom SVG charges.
|
|
140
185
|
|
|
141
|
-
[Unreleased]: https://github.com/pafirmin/grailwind/compare/v0.
|
|
186
|
+
[Unreleased]: https://github.com/pafirmin/grailwind/compare/v0.8.0...HEAD
|
|
187
|
+
[0.8.0]: https://github.com/pafirmin/grailwind/compare/v0.7.0...v0.8.0
|
|
188
|
+
[0.7.0]: https://github.com/pafirmin/grailwind/compare/v0.6.0...v0.7.0
|
|
142
189
|
[0.6.0]: https://github.com/pafirmin/grailwind/compare/v0.5.0...v0.6.0
|
|
143
190
|
[0.5.0]: https://github.com/pafirmin/grailwind/compare/v0.4.0...v0.5.0
|
|
144
191
|
[0.4.0]: https://github.com/pafirmin/grailwind/compare/v0.3.0...v0.4.0
|
package/README.md
CHANGED
|
@@ -57,11 +57,11 @@ tinctures replace Tailwind's colours entirely.
|
|
|
57
57
|
- **The shield:** `escutcheon`, plus `bordure-*`, `inescutcheon` (a small shield at the centre)
|
|
58
58
|
and `canton` (a ninth of the shield in dexter chief), each with its own field and charges.
|
|
59
59
|
- **Divided fields:** `per-pale-or-gules`, `quarterly-azure-argent`,
|
|
60
|
-
`gyronny-or-sable`, `barry-argent-azure
|
|
61
|
-
layout.
|
|
60
|
+
`gyronny-or-sable`, `barry-argent-azure`, `bendy-argent-gules`, `chequy-or-azure` and more, or
|
|
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/data.js
CHANGED
|
@@ -13,6 +13,7 @@ export const DIVISIONS = [
|
|
|
13
13
|
"per-fess",
|
|
14
14
|
"per-bend",
|
|
15
15
|
"per-bend-sinister",
|
|
16
|
+
"per-chevron",
|
|
16
17
|
"per-saltire",
|
|
17
18
|
"quarterly",
|
|
18
19
|
"gyronny",
|
|
@@ -21,6 +22,13 @@ export const DIVISIONS = [
|
|
|
21
22
|
"barry-of-ten",
|
|
22
23
|
"paly",
|
|
23
24
|
"paly-of-eight",
|
|
25
|
+
"bendy",
|
|
26
|
+
"bendy-of-eight",
|
|
27
|
+
"bendy-sinister",
|
|
28
|
+
"bendy-sinister-of-eight",
|
|
29
|
+
"chevronny",
|
|
30
|
+
"chevronny-of-eight",
|
|
31
|
+
"chequy",
|
|
24
32
|
];
|
|
25
33
|
|
|
26
34
|
// Ordinaries that can be reversed, e.g. `pile-reversed-or`: the same shape upside down.
|
|
@@ -72,7 +80,10 @@ export const ROUNDELS = {
|
|
|
72
80
|
};
|
|
73
81
|
|
|
74
82
|
// Lines of partition for an ordinary's edge, e.g. `fess-engrailed-or`.
|
|
75
|
-
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;
|
|
76
87
|
|
|
77
88
|
// Ordinaries that can be voided, e.g. `cross-voided-argent`.
|
|
78
89
|
export const VOIDABLE = [
|
|
@@ -135,15 +146,18 @@ export const BETWEEN = {
|
|
|
135
146
|
"bend-sinister": { size: 24, points: [[30, 26], [70, 68]], counts: {
|
|
136
147
|
6: { size: 14, points: [[52, 9], [24, 12], [16, 38], [91, 48], [82, 72], [62, 84]] } } },
|
|
137
148
|
chevron: { size: 22, points: [[24, 22], [76, 22], [50, 70]], counts: {
|
|
138
|
-
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]] } } },
|
|
139
150
|
cross: { size: 18, points: [[20, 17], [80, 17], [24, 72], [76, 72]] },
|
|
140
|
-
saltire: { size: 16, points: [[50, 13], [14,
|
|
141
|
-
2: { points: [[14,
|
|
151
|
+
saltire: { size: 16, points: [[50, 13], [14, 45], [86, 45], [50, 84]], counts: {
|
|
152
|
+
2: { points: [[14, 45], [86, 45]] } } },
|
|
142
153
|
pall: { size: 18, points: [[50, 11], [18, 56], [82, 56]], counts: {
|
|
143
154
|
2: { points: [[18, 56], [82, 56]] } } },
|
|
144
155
|
pile: { size: 20, points: [[18, 55], [82, 55]] },
|
|
145
156
|
};
|
|
146
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
|
+
|
|
147
161
|
// Where charges go for "on", along the ordinary's centre line, by number of charges.
|
|
148
162
|
// prettier-ignore
|
|
149
163
|
export const ON = {
|
|
@@ -151,10 +165,10 @@ export const ON = {
|
|
|
151
165
|
fess: { size: 22, counts: { 1: [[50, 45]], 2: [[30, 45], [70, 45]], 3: [[20, 45], [50, 45], [80, 45]] } },
|
|
152
166
|
bend: { size: 18, counts: { 1: [[50, 50]], 2: [[33, 33], [67, 67]], 3: [[24, 24], [50, 50], [76, 76]] } },
|
|
153
167
|
"bend-sinister": { size: 18, counts: { 1: [[50, 50]], 2: [[67, 33], [33, 67]], 3: [[76, 24], [50, 50], [24, 76]] } },
|
|
154
|
-
chevron: { size:
|
|
168
|
+
chevron: { size: 15, counts: { 1: [[50, 36]], 3: [[20, 65], [50, 36], [80, 65]] } },
|
|
155
169
|
cross: { size: 15, counts: { 1: [[50, 45]], 5: [[50, 15], [18, 45], [50, 45], [82, 45], [50, 80]] } },
|
|
156
|
-
saltire: { size:
|
|
157
|
-
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]] } },
|
|
158
172
|
pile: { size: 14, counts: { 1: [[50, 25]], 3: [[50, 14], [50, 34], [50, 54]] } },
|
|
159
173
|
};
|
|
160
174
|
|
|
@@ -179,6 +193,45 @@ for (const ordinary of REVERSIBLE) {
|
|
|
179
193
|
// prettier-ignore
|
|
180
194
|
BETWEEN["chevron-reversed"] = {
|
|
181
195
|
...BETWEEN["chevron-reversed"],
|
|
182
|
-
points: [[
|
|
183
|
-
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
|
+
},
|
|
184
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 });
|
|
@@ -22,12 +26,64 @@ const stripes = (to, pieces) => (a, b) => {
|
|
|
22
26
|
};
|
|
23
27
|
};
|
|
24
28
|
|
|
29
|
+
// How much of the corner-to-corner extent the shield covers: the base corner is cut away by the
|
|
30
|
+
// border-radius of 0 0 50% 50% / 0 0 45% 45% and the 5:6 aspect ratio in grailwind.css. It runs
|
|
31
|
+
// from the chief corner the bands start in to the tangent on the base curve.
|
|
32
|
+
const BENDY_VISIBLE = (() => {
|
|
33
|
+
const [width, height] = [5, 6];
|
|
34
|
+
const [radiusX, radiusY] = [width / 2, 0.45 * height];
|
|
35
|
+
const [centreX, centreY] = [width / 2, height - radiusY];
|
|
36
|
+
const tangent = Math.sqrt((height * radiusX) ** 2 + (width * radiusY) ** 2);
|
|
37
|
+
return (width * height - height * centreX + width * centreY + tangent) / (2 * width * height);
|
|
38
|
+
})();
|
|
39
|
+
|
|
40
|
+
// Diagonal bands parallel to per bend, `pieces` equal ones across the visible part of the shield,
|
|
41
|
+
// the last running to the edge. Explicit stops, not a repeating gradient, so a canton's square
|
|
42
|
+
// corners don't show the first tincture again.
|
|
43
|
+
const bendy = (to, pieces) => (a, b) => {
|
|
44
|
+
const stops = [];
|
|
45
|
+
for (let i = 0; i < pieces; i++) {
|
|
46
|
+
const start = i === 0 ? 0 : ((i * BENDY_VISIBLE * 100) / pieces).toFixed(2);
|
|
47
|
+
const end = i === pieces - 1 ? 100 : (((i + 1) * BENDY_VISIBLE * 100) / pieces).toFixed(2);
|
|
48
|
+
stops.push(`${i % 2 === 0 ? a : b} ${start}% ${end}%`);
|
|
49
|
+
}
|
|
50
|
+
return layer(`linear-gradient(to ${to}, ${stops.join(", ")})`);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// The direction of `to bottom right` on the shield (aspect-ratio 5 / 6 in grailwind.css), so
|
|
54
|
+
// chevronny's lines run parallel to per chevron's, the field's diagonals.
|
|
55
|
+
const CHEVRON_ANGLE = 180 - (Math.atan(6 / 5) * 180) / Math.PI;
|
|
56
|
+
|
|
57
|
+
// A half box is 75% as long as the whole along this angle (45/sqrt(61) over 60/sqrt(61)).
|
|
58
|
+
// Plain, the sinister half sits over a full-width dexter layer, which hides the centre seam.
|
|
59
|
+
// With a fur the layers can't overlap, as the transparent stripes would show the layer below.
|
|
60
|
+
const chevronny = (pieces) => (a, b, fur) => {
|
|
61
|
+
const stripe = (angle, length) =>
|
|
62
|
+
`repeating-linear-gradient(${angle}deg, ${a} 0 calc(${length}% / ${pieces}), ${b} 0 calc(${2 * length}% / ${pieces}))`;
|
|
63
|
+
const sinister = stripe(-CHEVRON_ANGLE, 100);
|
|
64
|
+
|
|
65
|
+
return fur
|
|
66
|
+
? {
|
|
67
|
+
"background-image": [stripe(CHEVRON_ANGLE, 100), sinister].join(", "),
|
|
68
|
+
"background-size": "50% 100%",
|
|
69
|
+
"background-position": "left, right",
|
|
70
|
+
"background-repeat": "no-repeat",
|
|
71
|
+
}
|
|
72
|
+
: {
|
|
73
|
+
"background-image": [sinister, stripe(CHEVRON_ANGLE, 75)].join(", "),
|
|
74
|
+
"background-size": "50% 100%, 100% 100%",
|
|
75
|
+
"background-position": "right, left",
|
|
76
|
+
"background-repeat": "no-repeat",
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
|
|
25
80
|
const DIVISIONS = {
|
|
26
81
|
// Vertical split: first on the dexter (viewer's left).
|
|
27
82
|
"per-pale": (a, b) => layer(`linear-gradient(to right, ${a} 50%, ${b} 50%)`),
|
|
28
83
|
|
|
29
84
|
// Horizontal split through the fess point: first in chief.
|
|
30
|
-
"per-fess": (a, b) =>
|
|
85
|
+
"per-fess": (a, b) =>
|
|
86
|
+
layer(`linear-gradient(to bottom, ${a} ${FESS_POINT}%, ${b} ${FESS_POINT}%)`),
|
|
31
87
|
|
|
32
88
|
// Line from dexter chief to sinister base: first in the upper half.
|
|
33
89
|
// With a corner-to-corner gradient, the 50% line joins the other two corners.
|
|
@@ -36,18 +92,55 @@ const DIVISIONS = {
|
|
|
36
92
|
// Line from sinister chief to dexter base: first in the upper half.
|
|
37
93
|
"per-bend-sinister": (a, b) => layer(`linear-gradient(to bottom right, ${a} 50%, ${b} 50%)`),
|
|
38
94
|
|
|
39
|
-
|
|
40
|
-
|
|
95
|
+
"per-chevron": (a, b) =>
|
|
96
|
+
a === "transparent"
|
|
97
|
+
? {
|
|
98
|
+
"background-image": [
|
|
99
|
+
`linear-gradient(to bottom right, ${a} 50%, ${b} 50%)`,
|
|
100
|
+
`linear-gradient(to bottom left, ${a} 50%, ${b} 50%)`,
|
|
101
|
+
`linear-gradient(${b}, ${b})`,
|
|
102
|
+
].join(", "),
|
|
103
|
+
"background-size": "50% 50%, 50% 50%, 100% 15%",
|
|
104
|
+
"background-position": "left 70%, right 70%, bottom",
|
|
105
|
+
"background-repeat": "no-repeat",
|
|
106
|
+
}
|
|
107
|
+
: {
|
|
108
|
+
"background-image": [
|
|
109
|
+
`linear-gradient(to bottom right, ${a} 42.5%, transparent 42.5%)`,
|
|
110
|
+
`linear-gradient(to bottom left, ${a} 42.5%, ${b} 42.5%)`,
|
|
111
|
+
].join(", "),
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
// Both lines of the saltire, from the top corners through the fess point to the flanks at 90%:
|
|
115
|
+
// first in chief and base. Four tiles, each half the width and 45% high, with each half-line
|
|
116
|
+
// as a tile's diagonal, and the base below them in the first tincture, 2px taller so Chromium
|
|
117
|
+
// leaves no seam where it meets them. Two strips under the tiles fill the sub-pixel seam where
|
|
118
|
+
// they meet: the first tincture down, the second across.
|
|
41
119
|
"per-saltire": (a, b) => ({
|
|
42
|
-
"background-color": b,
|
|
43
120
|
"background-image": [
|
|
44
121
|
`linear-gradient(to bottom left, ${a} 50%, ${b} 50%)`,
|
|
45
122
|
`linear-gradient(to bottom right, ${a} 50%, ${b} 50%)`,
|
|
46
123
|
`linear-gradient(to bottom left, ${b} 50%, ${a} 50%)`,
|
|
47
124
|
`linear-gradient(to bottom right, ${b} 50%, ${a} 50%)`,
|
|
125
|
+
`linear-gradient(${a}, ${a})`,
|
|
126
|
+
`linear-gradient(${a}, ${a})`,
|
|
127
|
+
`linear-gradient(${b}, ${b})`,
|
|
128
|
+
].join(", "),
|
|
129
|
+
"background-size": [
|
|
130
|
+
...Array(4).fill(`50% ${FESS_POINT}%`),
|
|
131
|
+
`100% calc(${100 - 2 * FESS_POINT}% + 2px)`,
|
|
132
|
+
"2px 100%",
|
|
133
|
+
"100% 2px",
|
|
134
|
+
].join(", "),
|
|
135
|
+
"background-position": [
|
|
136
|
+
"left top",
|
|
137
|
+
"right top",
|
|
138
|
+
`right ${BASE_TILES}%`,
|
|
139
|
+
`left ${BASE_TILES}%`,
|
|
140
|
+
"bottom",
|
|
141
|
+
"center",
|
|
142
|
+
`center ${FESS_POINT}%`,
|
|
48
143
|
].join(", "),
|
|
49
|
-
"background-size": "50% 50%",
|
|
50
|
-
"background-position": "left top, right top, right bottom, left bottom",
|
|
51
144
|
"background-repeat": "no-repeat",
|
|
52
145
|
}),
|
|
53
146
|
|
|
@@ -56,14 +149,15 @@ const DIVISIONS = {
|
|
|
56
149
|
// With a fur, the bands can't overlap, or the fur's transparent half shows the band below.
|
|
57
150
|
quarterly: (a, b, fur) => ({
|
|
58
151
|
"background-image": `linear-gradient(to right, ${a} 50%, ${b} 50%), linear-gradient(to right, ${b} 50%, ${a} 50%)`,
|
|
59
|
-
"background-size": `100% ${FESS_POINT}
|
|
152
|
+
"background-size": `100% ${FESS_POINT}%, 100% ${fur ? `calc(100% - ${FESS_POINT}%)` : "100%"}`,
|
|
60
153
|
"background-position": fur ? "top, bottom" : "top, top",
|
|
61
154
|
"background-repeat": "no-repeat",
|
|
62
155
|
}),
|
|
63
156
|
|
|
64
|
-
// Gyronny of eight:
|
|
65
|
-
// is in the gyron just dexter of the top line.
|
|
66
|
-
// any sub-pixel
|
|
157
|
+
// Gyronny of eight: quarterly and per saltire, all through the fess point. The first tincture
|
|
158
|
+
// is in the gyron just dexter of the top line. Per saltire's four tiles, then the base below
|
|
159
|
+
// them split per pale, 2px taller so no seam shows. The background colour fills any sub-pixel
|
|
160
|
+
// seam between tiles.
|
|
67
161
|
gyronny: (a, b) => ({
|
|
68
162
|
"background-color": b,
|
|
69
163
|
"background-image": [
|
|
@@ -71,9 +165,19 @@ const DIVISIONS = {
|
|
|
71
165
|
`linear-gradient(to bottom right, ${b} 50%, ${a} 50%)`,
|
|
72
166
|
`linear-gradient(to bottom left, ${b} 50%, ${a} 50%)`,
|
|
73
167
|
`linear-gradient(to bottom right, ${a} 50%, ${b} 50%)`,
|
|
168
|
+
`linear-gradient(to right, ${b} 50%, ${a} 50%)`,
|
|
169
|
+
].join(", "),
|
|
170
|
+
"background-size": [
|
|
171
|
+
...Array(4).fill(`50% ${FESS_POINT}%`),
|
|
172
|
+
`100% calc(${100 - 2 * FESS_POINT}% + 2px)`,
|
|
173
|
+
].join(", "),
|
|
174
|
+
"background-position": [
|
|
175
|
+
"left top",
|
|
176
|
+
"right top",
|
|
177
|
+
`right ${BASE_TILES}%`,
|
|
178
|
+
`left ${BASE_TILES}%`,
|
|
179
|
+
"bottom",
|
|
74
180
|
].join(", "),
|
|
75
|
-
"background-size": "50% 50%",
|
|
76
|
-
"background-position": "left top, right top, right bottom, left bottom",
|
|
77
181
|
"background-repeat": "no-repeat",
|
|
78
182
|
}),
|
|
79
183
|
|
|
@@ -83,22 +187,42 @@ const DIVISIONS = {
|
|
|
83
187
|
"barry-of-ten": stripes("bottom", 10),
|
|
84
188
|
paly: stripes("right", 6),
|
|
85
189
|
"paly-of-eight": stripes("right", 8),
|
|
190
|
+
|
|
191
|
+
// Bendy has the first tincture in the sinister chief corner, bendy sinister in the dexter.
|
|
192
|
+
bendy: bendy("bottom left", 6),
|
|
193
|
+
"bendy-of-eight": bendy("bottom left", 8),
|
|
194
|
+
"bendy-sinister": bendy("bottom right", 6),
|
|
195
|
+
"bendy-sinister-of-eight": bendy("bottom right", 8),
|
|
196
|
+
|
|
197
|
+
// The first tincture in the chief corners.
|
|
198
|
+
chevronny: chevronny(6),
|
|
199
|
+
"chevronny-of-eight": chevronny(8),
|
|
200
|
+
|
|
201
|
+
// 5 across and 6 down, the first tincture in the dexter chief square. Conic starts at the top.
|
|
202
|
+
// The repeat must be explicit, as with stripes.
|
|
203
|
+
chequy: (a, b) => ({
|
|
204
|
+
"background-image": `conic-gradient(${b} 0 25%, ${a} 0 50%, ${b} 0 75%, ${a} 0)`,
|
|
205
|
+
"background-size": "40% calc(100% / 3)",
|
|
206
|
+
"background-repeat": "repeat",
|
|
207
|
+
}),
|
|
86
208
|
};
|
|
87
209
|
|
|
88
210
|
// A division's background rules as one { image, size, position, repeat } per layer.
|
|
89
211
|
const toLayers = (rules) => {
|
|
90
|
-
return rules["background-image"]
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
212
|
+
return rules["background-image"]
|
|
213
|
+
.split(/, (?=(?:repeating-)?(?:linear|conic)-gradient)/)
|
|
214
|
+
.map((gradient, i) => {
|
|
215
|
+
const size = (rules["background-size"] ?? "100% 100%").split(", ");
|
|
216
|
+
const position = (rules["background-position"] ?? "0 0").split(", ");
|
|
217
|
+
const repeat = (rules["background-repeat"] ?? "no-repeat").split(", ");
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
image: gradient,
|
|
221
|
+
size: size[i % size.length],
|
|
222
|
+
position: position[i % position.length],
|
|
223
|
+
repeat: repeat[i % repeat.length],
|
|
224
|
+
};
|
|
225
|
+
});
|
|
102
226
|
};
|
|
103
227
|
|
|
104
228
|
// A division with one fur: the fur's part is left transparent, over the fur's own layers.
|
package/grailwind.css
CHANGED
|
@@ -135,10 +135,18 @@
|
|
|
135
135
|
grid-auto-flow: row;
|
|
136
136
|
grid-auto-rows: 1fr;
|
|
137
137
|
|
|
138
|
-
/*
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
/* On a shield, two parts split at the fess point, FESS_POINT in divisions.js.
|
|
139
|
+
* More parts stay equal. */
|
|
140
|
+
&:is(.escutcheon, .escutcheon *) {
|
|
141
|
+
&:has(> :last-child:nth-child(2)) {
|
|
142
|
+
grid-template-rows: 45fr 55fr;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* See per-pale. */
|
|
146
|
+
& > * {
|
|
147
|
+
position: relative;
|
|
148
|
+
contain: layout;
|
|
149
|
+
}
|
|
142
150
|
}
|
|
143
151
|
}
|
|
144
152
|
|
|
@@ -155,10 +163,15 @@
|
|
|
155
163
|
--outer-inset: 0px;
|
|
156
164
|
}
|
|
157
165
|
|
|
158
|
-
/*
|
|
159
|
-
&:is(.escutcheon, .escutcheon *)
|
|
160
|
-
|
|
161
|
-
|
|
166
|
+
/* On a shield, split at the fess point, as per-fess. */
|
|
167
|
+
&:is(.escutcheon, .escutcheon *) {
|
|
168
|
+
grid-template-rows: 45fr 55fr;
|
|
169
|
+
|
|
170
|
+
/* See per-pale. */
|
|
171
|
+
& > * {
|
|
172
|
+
position: relative;
|
|
173
|
+
contain: layout;
|
|
174
|
+
}
|
|
162
175
|
}
|
|
163
176
|
}
|
|
164
177
|
|