grailwind 0.2.0 → 0.3.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 CHANGED
@@ -4,6 +4,26 @@ All notable changes to this project are documented here. The format is based
4
4
  on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
5
5
  adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [Unreleased]
8
+
9
+ ## [0.3.0] - 2026-11-01
10
+
11
+ ### Added
12
+
13
+ - Counterchanged ordinaries: `<ordinary>-counterchanged` swaps the tinctures
14
+ of a divided field, e.g. `per-pale-argent-gules chevron-counterchanged`.
15
+ - Counterchanged charges: `counterchanged` on a charge, or on the `charges`
16
+ container for all of them.
17
+
18
+ ### Changed
19
+
20
+ - The horizontal line of `per-fess-*` and `quarterly-*` fields now passes
21
+ through the fess point, 45% down the shield, where the fess and cross are
22
+ centred. It was at 50%.
23
+ - Charges are centred with negative margins instead of `translate`, so
24
+ Tailwind's `translate-*` on a charge now moves it from its place.
25
+ - `escutcheon` sets `contain: layout`.
26
+
7
27
  ## [0.2.0] - 2026-11-01
8
28
 
9
29
  ### Added
@@ -23,5 +43,7 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
23
43
  Initial release: tinctures, divisions, ordinaries, charges, strict mode and
24
44
  custom SVG charges.
25
45
 
46
+ [Unreleased]: https://github.com/pafirmin/grailwind/compare/v0.3.0...HEAD
47
+ [0.3.0]: https://github.com/pafirmin/grailwind/compare/v0.2.0...v0.3.0
26
48
  [0.2.0]: https://github.com/pafirmin/grailwind/compare/v0.1.0...v0.2.0
27
49
  [0.1.0]: https://github.com/pafirmin/grailwind/releases/tag/v0.1.0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Grailwind CSS
2
2
 
3
- ![Eight coats of arms drawn with Grailwind: chevrons, quarters, fleurs-de-lis, a saltire, gyronny, a chief, a bend and a bordure](https://pafirmin.github.io/grailwind/preview.png)
3
+ ![Eight coats of arms drawn with Grailwind: chevrons, quarters, fleurs-de-lis, a saltire, gyronny, a chief, a bend and a bordure](https://grailwind.dev/preview.png)
4
4
 
5
5
  Heraldic blazon for [Tailwind CSS](https://tailwindcss.com). Draw coats of
6
6
  arms, or style anything else, in the language heralds have used for 800 years:
@@ -15,10 +15,10 @@ arms, or style anything else, in the language heralds have used for 800 years:
15
15
  </div>
16
16
  ```
17
17
 
18
- That's *Azure, a chevron Or between three mullets Argent*: a blue shield, a
18
+ That's _Azure, a chevron Or between three mullets Argent_: a blue shield, a
19
19
  gold chevron, and three silver stars around it.
20
20
 
21
- **[Read the documentation](https://pafirmin.github.io/grailwind/)** for every class, with live examples.
21
+ **[Read the documentation](https://grailwind.dev/)** for every class, with live examples.
22
22
 
23
23
  > Grailwind is a light-hearted project at version 0.x, so class names may
24
24
  > still change.
@@ -53,11 +53,12 @@ tinctures replace Tailwind's colours entirely.
53
53
  `gyronny-or-sable` and more, or `per-pale`, `per-fess` and `quarterly` for
54
54
  layout.
55
55
  - **Ordinaries:** `fess-or`, `chevron-gules`, `cross-argent`, `chief-azure`
56
- and more, with `fimbriated-*` for a narrow border.
56
+ and more, with `fimbriated-*` for a narrow border, or `chevron-counterchanged`
57
+ and the like on a divided field.
57
58
  - **Charges:** `mullet`, `fleur-de-lis`, `bezant` and more, arranged by
58
59
  number, `in-pale`, `in-fess`, `in-bend`, `on` or `between` the ordinary, or
59
- placed at a named point such as `in-dexter-chief`, and sized with
60
- `charge-size-*`.
60
+ placed at a named point such as `in-dexter-chief`, sized with
61
+ `charge-size-*`, and `counterchanged` on a divided field.
61
62
  - **Your own charges:** any SVG, via `charge-[url(…)]`.
62
63
 
63
64
  ## Development
@@ -65,6 +66,7 @@ tinctures replace Tailwind's colours entirely.
65
66
  ```sh
66
67
  npm install
67
68
  npm test # checks both the default and strict builds
69
+ npm run format # formats with Prettier; CI runs format:check
68
70
  npm run dev:watch # rebuilds the docs site on change and serves it at http://localhost:8000
69
71
  ```
70
72
 
package/charges.js CHANGED
@@ -41,6 +41,7 @@ const ROUNDELS = {
41
41
 
42
42
  // Points as [left%, top%] of the shield, plus a charge size as % of its width.
43
43
  // The default for each number of charges, e.g. three are placed 2 and 1.
44
+ // prettier-ignore
44
45
  const DEFAULTS = {
45
46
  1: { size: 50, points: [[50, 45]] },
46
47
  2: { size: 30, points: [[50, 24], [50, 66]] },
@@ -66,6 +67,7 @@ const POINT_SIZE = 22;
66
67
  // With a chief on the shield, these points sit on it (see ordinaries.js).
67
68
  const CHIEF_POINTS = new Set(["dexter-chief", "chief", "sinister-chief"]);
68
69
 
70
+ // prettier-ignore
69
71
  const ARRANGEMENTS = {
70
72
  "in-pale": {
71
73
  2: { size: 30, points: [[50, 24], [50, 66]] },
@@ -96,9 +98,30 @@ const maskRules = (image) => ({
96
98
  const place = ([left, top], size) => ({
97
99
  left: `${left}%`,
98
100
  top: `${top}%`,
99
- width: `var(--charge-size, ${size}%)`,
101
+ "--charge-width": `var(--charge-size, ${size}%)`,
100
102
  });
101
103
 
104
+ // The field with its tinctures swapped, from the shield's division (see divisions.js).
105
+ const COUNTERCHANGE = ["color", "image", "size", "position", "repeat"];
106
+ const inheritCounterchange = Object.fromEntries(
107
+ COUNTERCHANGE.map((n) => [`--counterchange-${n}`, "inherit"]),
108
+ );
109
+
110
+ // A layer the size of the shield, as the escutcheon is the containing block
111
+ // for fixed positioning. The charge's mask cuts it to shape.
112
+ const counterchanged = {
113
+ ...inheritCounterchange,
114
+ "&::before": {
115
+ content: '""',
116
+ position: "fixed",
117
+ inset: "0",
118
+ ...inheritCounterchange,
119
+ ...Object.fromEntries(
120
+ COUNTERCHANGE.map((n) => [`background-${n}`, `var(--counterchange-${n})`]),
121
+ ),
122
+ },
123
+ };
124
+
102
125
  const hasCount = (n) => `:has(> :nth-child(${n}):last-child)`;
103
126
 
104
127
  export default plugin(({ addUtilities, matchUtilities }) => {
@@ -124,17 +147,20 @@ export default plugin(({ addUtilities, matchUtilities }) => {
124
147
  inset: "var(--field-top, 0%) 0 0",
125
148
  // Above the chief, which is painted last, so charges can sit on it.
126
149
  "z-index": "1",
150
+ ...inheritCounterchange,
127
151
  "& > *": {
128
152
  position: "absolute",
129
153
  "aspect-ratio": "1",
130
- translate: "-50% -50%",
154
+ // Centred with margins, not translate: a transform would stop a
155
+ // counterchanged charge's fixed layer from lining up with the shield.
156
+ width: "var(--charge-width)",
157
+ margin: "calc(var(--charge-width) / -2) 0 0 calc(var(--charge-width) / -2)",
131
158
  "object-fit": "contain",
132
159
  },
133
160
  };
134
161
  for (const [count, { size, points }] of Object.entries(DEFAULTS)) {
135
162
  points.forEach((point, i) => {
136
- charges[`&:where(${hasCount(count)}) > :where(:nth-child(${i + 1}))`] =
137
- place(point, size);
163
+ charges[`&:where(${hasCount(count)}) > :where(:nth-child(${i + 1}))`] = place(point, size);
138
164
  });
139
165
  }
140
166
  utilities[".charges"] = charges;
@@ -155,7 +181,7 @@ export default plugin(({ addUtilities, matchUtilities }) => {
155
181
  between[`& > :nth-child(${i})`] = {
156
182
  left: `var(--between-${i}-x)`,
157
183
  top: `var(--between-${i}-y)`,
158
- width: "var(--charge-size, var(--between-size))",
184
+ "--charge-width": "var(--charge-size, var(--between-size))",
159
185
  };
160
186
  }
161
187
  utilities[".between"] = between;
@@ -168,11 +194,13 @@ export default plugin(({ addUtilities, matchUtilities }) => {
168
194
  for (let i = 1; i <= count; i++) {
169
195
  const [left, top] = fallback?.points[i - 1] ?? [];
170
196
  const at = (axis, value) =>
171
- value === undefined ? `var(--on-${count}-${i}-${axis})` : `var(--on-${count}-${i}-${axis}, ${value}%)`;
197
+ value === undefined
198
+ ? `var(--on-${count}-${i}-${axis})`
199
+ : `var(--on-${count}-${i}-${axis}, ${value}%)`;
172
200
  on[`&${hasCount(count)} > :nth-child(${i})`] = {
173
201
  left: at("x", left),
174
202
  top: at("y", top),
175
- width: `var(--charge-size, var(--on-size, ${fallback?.size ?? 16}%))`,
203
+ "--charge-width": `var(--charge-size, var(--on-size, ${fallback?.size ?? 16}%))`,
176
204
  };
177
205
  }
178
206
  }
@@ -184,11 +212,17 @@ export default plugin(({ addUtilities, matchUtilities }) => {
184
212
  ? {
185
213
  left: `${left}%`,
186
214
  top: `var(--chief-points-y, ${top}%)`,
187
- width: `var(--charge-size, var(--chief-points-size, ${POINT_SIZE}%))`,
215
+ "--charge-width": `var(--charge-size, var(--chief-points-size, ${POINT_SIZE}%))`,
188
216
  }
189
217
  : place([left, top], POINT_SIZE),
190
218
  };
191
219
  }
192
220
 
221
+ // On a charge, or on `charges` for all of them.
222
+ utilities[".counterchanged"] = {
223
+ ".charges > &": counterchanged,
224
+ "&.charges > *": counterchanged,
225
+ };
226
+
193
227
  addUtilities(utilities);
194
228
  });
package/divisions.js CHANGED
@@ -5,6 +5,9 @@ import plugin from "tailwindcss/plugin";
5
5
 
6
6
  const TINCTURES = ["or", "argent", "gules", "azure", "vert", "purpure", "sable"];
7
7
 
8
+ // Horizontal lines of partition pass through the fess point, as the cross and fess do.
9
+ const FESS_POINT = "45%";
10
+
8
11
  // Each division maps (first, second) tincture colours to background rules.
9
12
  const layer = (image) => ({ "background-image": image });
10
13
 
@@ -12,22 +15,21 @@ const DIVISIONS = {
12
15
  // Vertical split: first on the dexter (viewer's left).
13
16
  "per-pale": (a, b) => layer(`linear-gradient(to right, ${a} 50%, ${b} 50%)`),
14
17
 
15
- // Horizontal split: first in chief.
16
- "per-fess": (a, b) => layer(`linear-gradient(to bottom, ${a} 50%, ${b} 50%)`),
18
+ // Horizontal split through the fess point: first in chief.
19
+ "per-fess": (a, b) => layer(`linear-gradient(to bottom, ${a} ${FESS_POINT}, ${b} ${FESS_POINT})`),
17
20
 
18
21
  // Line from dexter chief to sinister base: first in the upper half.
19
22
  // With a corner-to-corner gradient, the 50% line joins the other two corners.
20
23
  "per-bend": (a, b) => layer(`linear-gradient(to bottom left, ${a} 50%, ${b} 50%)`),
21
24
 
22
25
  // Line from sinister chief to dexter base: first in the upper half.
23
- "per-bend-sinister": (a, b) =>
24
- layer(`linear-gradient(to bottom right, ${a} 50%, ${b} 50%)`),
26
+ "per-bend-sinister": (a, b) => layer(`linear-gradient(to bottom right, ${a} 50%, ${b} 50%)`),
25
27
 
26
28
  // Quarters 1 and 4 (dexter chief, sinister base) are the first tincture.
27
29
  // Linear gradients rather than conic, which render jagged edges in some browsers.
28
30
  quarterly: (a, b) => ({
29
31
  "background-image": `linear-gradient(to right, ${a} 50%, ${b} 50%), linear-gradient(to right, ${b} 50%, ${a} 50%)`,
30
- "background-size": "100% 50%, 100% 100%",
32
+ "background-size": `100% ${FESS_POINT}, 100% 100%`,
31
33
  "background-position": "top, top",
32
34
  "background-repeat": "no-repeat",
33
35
  }),
@@ -49,6 +51,15 @@ const DIVISIONS = {
49
51
  }),
50
52
  };
51
53
 
54
+ // The field with its tinctures swapped, for counterchanged ordinaries to paint.
55
+ const counterchange = (rules) =>
56
+ Object.fromEntries(
57
+ Object.entries(rules).map(([property, value]) => [
58
+ property.replace("background-", "--counterchange-"),
59
+ value,
60
+ ]),
61
+ );
62
+
52
63
  export default plugin(({ addUtilities }) => {
53
64
  const utilities = {};
54
65
 
@@ -57,10 +68,12 @@ export default plugin(({ addUtilities }) => {
57
68
  for (const second of TINCTURES) {
58
69
  if (first === second) continue;
59
70
 
60
- utilities[`.${division}-${first}-${second}`] = background(
61
- `var(--color-${first})`,
62
- `var(--color-${second})`,
63
- );
71
+ const a = `var(--color-${first})`;
72
+ const b = `var(--color-${second})`;
73
+ utilities[`.${division}-${first}-${second}`] = {
74
+ ...background(a, b),
75
+ ...counterchange(background(b, a)),
76
+ };
64
77
  }
65
78
  }
66
79
  }
package/grailwind.css CHANGED
@@ -21,6 +21,26 @@
21
21
  syntax: "*";
22
22
  inherits: false;
23
23
  }
24
+ @property --counterchange-color {
25
+ syntax: "*";
26
+ inherits: false;
27
+ }
28
+ @property --counterchange-image {
29
+ syntax: "*";
30
+ inherits: false;
31
+ }
32
+ @property --counterchange-size {
33
+ syntax: "*";
34
+ inherits: false;
35
+ }
36
+ @property --counterchange-position {
37
+ syntax: "*";
38
+ inherits: false;
39
+ }
40
+ @property --counterchange-repeat {
41
+ syntax: "*";
42
+ inherits: false;
43
+ }
24
44
 
25
45
  /* ---------------------------------------------------------------------------
26
46
  * Tinctures
@@ -41,13 +61,27 @@
41
61
  /* ---------------------------------------------------------------------------
42
62
  * Bare tinctures
43
63
  * ------------------------------------------------------------------------- */
44
- @utility or { background-color: var(--color-or); }
45
- @utility argent { background-color: var(--color-argent); }
46
- @utility gules { background-color: var(--color-gules); }
47
- @utility azure { background-color: var(--color-azure); }
48
- @utility vert { background-color: var(--color-vert); }
49
- @utility purpure { background-color: var(--color-purpure); }
50
- @utility sable { background-color: var(--color-sable); }
64
+ @utility or {
65
+ background-color: var(--color-or);
66
+ }
67
+ @utility argent {
68
+ background-color: var(--color-argent);
69
+ }
70
+ @utility gules {
71
+ background-color: var(--color-gules);
72
+ }
73
+ @utility azure {
74
+ background-color: var(--color-azure);
75
+ }
76
+ @utility vert {
77
+ background-color: var(--color-vert);
78
+ }
79
+ @utility purpure {
80
+ background-color: var(--color-purpure);
81
+ }
82
+ @utility sable {
83
+ background-color: var(--color-sable);
84
+ }
51
85
 
52
86
  /* ---------------------------------------------------------------------------
53
87
  * Field divisions
@@ -97,6 +131,8 @@
97
131
  overflow: hidden;
98
132
  border-radius: 0 0 50% 50% / 0 0 45% 45%;
99
133
  box-shadow: 0 0 0 2px var(--color-sable);
134
+ /* The containing block for counterchanged charges' fixed layers. */
135
+ contain: layout;
100
136
  }
101
137
 
102
138
  /* ---------------------------------------------------------------------------
package/ordinaries.js CHANGED
@@ -5,6 +5,7 @@ import plugin from "tailwindcss/plugin";
5
5
 
6
6
  const TINCTURES = ["or", "argent", "gules", "azure", "vert", "purpure", "sable"];
7
7
 
8
+ // prettier-ignore
8
9
  const ORDINARIES = {
9
10
  pale: [[35, 0], [65, 0], [65, 100], [35, 100]],
10
11
  fess: [[0, 30], [100, 30], [100, 60], [0, 60]],
@@ -33,6 +34,10 @@ const polygon = (vertices) => `polygon(${vertices.map(point).join(", ")})`;
33
34
 
34
35
  const PSEUDO = { chief: "&::after" };
35
36
 
37
+ // A y in % of the field, as % of the whole shield when a chief takes the top of it.
38
+ const belowChief = (y) =>
39
+ y === 100 ? "100%" : `calc(${num(y)}% + var(--field-top, 0%) * ${num(1 - y / 100)})`;
40
+
36
41
  // Fimbriation: the ordinary's clip is widened and filled with the fimbriation
37
42
  // tincture, and the ordinary's own tincture is painted over it as gradient
38
43
  // bands.
@@ -99,6 +104,7 @@ const widenedPolygon = (vertices) => {
99
104
  const S = 12;
100
105
  const band = (kind, from, to, tile) => ({ kind, from, to, tile });
101
106
  const stripe = (kind, from, to, tile) => ({ kind, from, to, tile, stripe: true });
107
+ // prettier-ignore
102
108
  const BANDS = {
103
109
  pale: [band("x", 35, 65)],
104
110
  fess: [band("y", 30, 60)],
@@ -123,7 +129,12 @@ const BANDS = {
123
129
  ],
124
130
  };
125
131
 
126
- const DIRECTION = { x: "to right", y: "to bottom", "u-v": "to top right", "u+v": "to bottom right" };
132
+ const DIRECTION = {
133
+ x: "to right",
134
+ y: "to bottom",
135
+ "u-v": "to top right",
136
+ "u+v": "to bottom right",
137
+ };
127
138
 
128
139
  // A band's edges as % along its gradient, within its tile.
129
140
  const stop = ({ kind, tile: [x0, y0, x1, y1] = [0, 0, 100, 100] }, value) => {
@@ -131,10 +142,14 @@ const stop = ({ kind, tile: [x0, y0, x1, y1] = [0, 0, 100, 100] }, value) => {
131
142
  const h = y1 - y0;
132
143
  if (kind.length > 1 && w !== h) throw new Error("diagonal bands need a square tile");
133
144
  switch (kind) {
134
- case "x": return ((value - x0) * 100) / w;
135
- case "y": return ((value - y0) * 100) / h;
136
- case "u-v": return ((value - (x0 - y0)) * 100) / w / 2 + 50;
137
- case "u+v": return ((value - x0 - y0) * 100) / w / 2;
145
+ case "x":
146
+ return ((value - x0) * 100) / w;
147
+ case "y":
148
+ return ((value - y0) * 100) / h;
149
+ case "u-v":
150
+ return ((value - (x0 - y0)) * 100) / w / 2 + 50;
151
+ case "u+v":
152
+ return ((value - x0 - y0) * 100) / w / 2;
138
153
  }
139
154
  };
140
155
 
@@ -152,19 +167,24 @@ const tilePosition = (start, size) => (size === 100 ? "0" : pct((start * 100) /
152
167
 
153
168
  const bands = (layers, tincture) => ({
154
169
  "background-image": layers
155
- .map((l) => gradient(l, l.stripe ? "var(--fimbriation, transparent)" : `var(--color-${tincture})`))
170
+ .map((l) =>
171
+ gradient(l, l.stripe ? "var(--fimbriation, transparent)" : `var(--color-${tincture})`),
172
+ )
156
173
  .join(", "),
157
174
  "background-size": layers
158
175
  .map(({ tile: [x0, y0, x1, y1] = [0, 0, 100, 100] }) => `${pct(x1 - x0)} ${pct(y1 - y0)}`)
159
176
  .join(", "),
160
177
  "background-position": layers
161
- .map(({ tile: [x0, y0, x1, y1] = [0, 0, 100, 100] }) =>
162
- `${tilePosition(x0, x1 - x0)} ${tilePosition(y0, y1 - y0)}`)
178
+ .map(
179
+ ({ tile: [x0, y0, x1, y1] = [0, 0, 100, 100] }) =>
180
+ `${tilePosition(x0, x1 - x0)} ${tilePosition(y0, y1 - y0)}`,
181
+ )
163
182
  .join(", "),
164
183
  "background-repeat": "no-repeat",
165
184
  });
166
185
 
167
186
  // Where charges go for "between", as [left%, top%], plus their size as % of width.
187
+ // prettier-ignore
168
188
  const BETWEEN = {
169
189
  pale: { size: 18, points: [[18, 42], [82, 42]] },
170
190
  fess: { size: 20, points: [[30, 15], [70, 15], [50, 78]] },
@@ -177,6 +197,7 @@ const BETWEEN = {
177
197
  };
178
198
 
179
199
  // Where charges go for "on", along the ordinary's centre line, by number of charges.
200
+ // prettier-ignore
180
201
  const ON = {
181
202
  pale: { size: 20, counts: { 1: [[50, 45]], 2: [[50, 28], [50, 62]], 3: [[50, 18], [50, 45], [50, 72]] } },
182
203
  fess: { size: 22, counts: { 1: [[50, 45]], 2: [[30, 45], [70, 45]], 3: [[20, 45], [50, 45], [80, 45]] } },
@@ -218,37 +239,59 @@ export default plugin(({ addUtilities }) => {
218
239
  };
219
240
  }
220
241
 
242
+ const ordinaryRules = (ordinary, pseudo) => ({
243
+ position: "relative",
244
+ ...(BETWEEN[ordinary] && betweenVars(BETWEEN[ordinary])),
245
+ ...(ON[ordinary] && onVars(ON[ordinary])),
246
+ // With a chief, everything else is drawn in the field below it.
247
+ ...(ordinary === "chief" && {
248
+ "--field-top": "30%",
249
+ // The chief's centre line, relative to the charges container below it.
250
+ "--chief-points-y": "calc(-15% / 0.7)",
251
+ "--chief-points-size": "18%",
252
+ }),
253
+ [PSEUDO[ordinary] ?? "&::before"]: { content: '""', position: "absolute", ...pseudo },
254
+ });
255
+
221
256
  for (const [ordinary, vertices] of Object.entries(ORDINARIES)) {
257
+ // Covers the whole shield, so the swapped field lines up with the real one,
258
+ // and the clip moves the ordinary below any chief instead.
259
+ utilities[`.${ordinary}-counterchanged`] = ordinaryRules(ordinary, {
260
+ inset: "0",
261
+ // Registered not to inherit, so a shield inside another can't pick up its swap.
262
+ ...Object.fromEntries(
263
+ ["color", "image", "size", "position", "repeat"].map((n) => [
264
+ `--counterchange-${n}`,
265
+ "inherit",
266
+ ]),
267
+ ),
268
+ "background-color": "var(--counterchange-color, transparent)",
269
+ "background-image": "var(--counterchange-image, none)",
270
+ "background-size": "var(--counterchange-size, auto)",
271
+ "background-position": "var(--counterchange-position, 0 0)",
272
+ "background-repeat": "var(--counterchange-repeat, repeat)",
273
+ "clip-path":
274
+ ordinary === "chief"
275
+ ? polygon(vertices)
276
+ : `polygon(${vertices.map(([x, y]) => `${pct(x)} ${belowChief(y)}`).join(", ")})`,
277
+ });
278
+
222
279
  for (const tincture of TINCTURES) {
223
- utilities[`.${ordinary}-${tincture}`] = {
224
- position: "relative",
225
- ...(BETWEEN[ordinary] && betweenVars(BETWEEN[ordinary])),
226
- ...(ON[ordinary] && onVars(ON[ordinary])),
227
- // With a chief, everything else is drawn in the field below it.
228
- ...(ordinary === "chief" && {
229
- "--field-top": "30%",
230
- // The chief's centre line, relative to the charges container below it.
231
- "--chief-points-y": "calc(-15% / 0.7)",
232
- "--chief-points-size": "18%",
233
- }),
234
- [PSEUDO[ordinary] ?? "&::before"]: {
235
- content: '""',
236
- position: "absolute",
237
- inset: ordinary === "chief" ? "0" : "var(--field-top, 0%) 0 0",
238
- ...(BANDS[ordinary]
239
- ? {
240
- "--fimbriated": "inherit",
241
- "--fimbriation": "inherit",
242
- "background-color": `var(--fimbriation, var(--color-${tincture}))`,
243
- ...bands(BANDS[ordinary], tincture),
244
- "clip-path": widenedPolygon(vertices),
245
- }
246
- : {
247
- "background-color": `var(--color-${tincture})`,
248
- "clip-path": polygon(vertices),
249
- }),
250
- },
251
- };
280
+ utilities[`.${ordinary}-${tincture}`] = ordinaryRules(ordinary, {
281
+ inset: ordinary === "chief" ? "0" : "var(--field-top, 0%) 0 0",
282
+ ...(BANDS[ordinary]
283
+ ? {
284
+ "--fimbriated": "inherit",
285
+ "--fimbriation": "inherit",
286
+ "background-color": `var(--fimbriation, var(--color-${tincture}))`,
287
+ ...bands(BANDS[ordinary], tincture),
288
+ "clip-path": widenedPolygon(vertices),
289
+ }
290
+ : {
291
+ "background-color": `var(--color-${tincture})`,
292
+ "clip-path": polygon(vertices),
293
+ }),
294
+ });
252
295
  }
253
296
  }
254
297
 
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "grailwind",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "scripts": {
5
5
  "build": "tailwindcss -i docs/app.css -o docs/site.css",
6
6
  "dev": "tailwindcss -i docs/app.css -o docs/site.css --watch",
7
7
  "test": "node test/modes.mjs",
8
- "dev:watch": "tailwindcss -i docs/app.css -o docs/site.css --watch=always & python3 -m http.server ${PORT:-8000} -d docs"
8
+ "dev:watch": "tailwindcss -i docs/app.css -o docs/site.css --watch=always & python3 -m http.server ${PORT:-8000} -d docs",
9
+ "format": "prettier --write .",
10
+ "format:check": "prettier --check ."
9
11
  },
10
12
  "keywords": [
11
13
  "tailwindcss",
@@ -19,6 +21,7 @@
19
21
  "description": "Heraldic blazon for Tailwind CSS: tinctures, divisions, ordinaries and charges",
20
22
  "devDependencies": {
21
23
  "@tailwindcss/cli": "^4.3.3",
24
+ "prettier": "^3.9.10",
22
25
  "tailwindcss": "^4.3.3"
23
26
  },
24
27
  "type": "module",
@@ -45,7 +48,7 @@
45
48
  "peerDependencies": {
46
49
  "tailwindcss": "^4.0.0"
47
50
  },
48
- "homepage": "https://pafirmin.github.io/grailwind/",
51
+ "homepage": "https://grailwind.dev/",
49
52
  "repository": {
50
53
  "type": "git",
51
54
  "url": "git+https://github.com/pafirmin/grailwind.git"