@johnmorrisdotca/kyogijo-stadiums 0.10.2 → 0.11.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
@@ -7,6 +7,14 @@ All notable changes to this project are written here. The format follows
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.11.0] - 2026-10-19
11
+
12
+ ### Added
13
+
14
+ - **A ground with a running track offers the athletics stadium in the atlas:** `surfacesOf(place)` lists `{ sport: "athletics", ground }` for a ground whose `track` is set, and `surfaceOf(place, "athletics")` returns it; the six grounds Wikidata marks with a track are linked, no other place.
15
+ - **A `<title>` on every point and line when the marks are written** (`marks: true`, `pointer: true`): a datum with no `title` of its own is named from its outcome, its team and its place in the data ("goal, home, 3"), so a delegated listener, a hover and a screen reader reach every mark. Nothing is added without `marks`, so a drawing nobody points at is the same string as ever. Heat cells stay without one (size budget; their numbers are in the layer's table).
16
+ - **The IFAF's field is named and tested in the situation docs** (`docs/situation.md`): `sport: "ifaf"` takes a football situation on the NFL's 100-yard field, with four downs.
17
+
10
18
  ## [0.10.2] - 2026-10-14
11
19
 
12
20
  ### Fixed
package/README.md CHANGED
@@ -616,39 +616,67 @@ Using Kyogijo Stadiums in something? Open an *Add my project* issue and we will
616
616
  ### The family
617
617
 
618
618
  <!-- family:start (made by scripts/family-readme.mjs from scripts/family-template.mjs; change those, not this) -->
619
- Kyogijo Stadiums is one of twenty-eight packages, each made for the same site, each at
620
- [github.com/johnmorrisdotca](https://github.com/johnmorrisdotca). The code of every one is MIT.
619
+ Kyogijo Stadiums is one of thirty-two packages, each made for the same site, each at
620
+ [github.com/johnmorrisdotca](https://github.com/johnmorrisdotca) or [github.com/spxis](https://github.com/spxis). The code of every one is MIT.
621
621
 
622
- - [Korokoro](https://github.com/johnmorrisdotca/korokoro) (コロコロ): dice, with notation, exact odds, real sounds and the dice of many games. [Demo](https://johnmorrisdotca.github.io/korokoro/).
623
- - [Kyuubu](https://github.com/johnmorrisdotca/kyuubu) (キューブ): a turning cube for the browser, 2×2 to 7×7, with record solves to replay. [Demo](https://johnmorrisdotca.github.io/kyuubu/).
624
- - [Hitotsu](https://github.com/johnmorrisdotca/hitotsu) (一つ): a colour-card shedding game for two to eight, with the house rules people play. [Demo](https://johnmorrisdotca.github.io/hitotsu/).
625
- - [Toranpu](https://github.com/johnmorrisdotca/toranpu) (トランプ): a deck of playing cards, card games with computer players, and solitaires. [Demo](https://johnmorrisdotca.github.io/toranpu/).
626
- - [Tane](https://github.com/johnmorrisdotca/tane) (種): seeded random numbers and daily seeds, the same in every browser and on every server. [Demo](https://johnmorrisdotca.github.io/tane/).
622
+ **Games — board**
623
+
624
+ - [Domino](https://github.com/johnmorrisdotca/domino) (ドミノ): dominoes and Mexican Train. [Demo](https://johnmorrisdotca.github.io/domino/).
625
+ - [Gunjin](https://github.com/johnmorrisdotca/gunjin) (軍人): five hidden-rank strategy games with pass-the-device play. [Demo](https://johnmorrisdotca.github.io/gunjin/).
627
626
  - [Narabe](https://github.com/johnmorrisdotca/narabe) (並べ): one rules engine for abstract board games, from gomoku and Reversi to Go and checkers. [Demo](https://johnmorrisdotca.github.io/narabe/).
627
+ - [Sugoroku](https://github.com/johnmorrisdotca/sugoroku) (双六): backgammon and its variants, with the doubling cube and match play. [Demo](https://johnmorrisdotca.github.io/sugoroku/).
628
628
  - [Tenka](https://github.com/johnmorrisdotca/tenka) (天下): world conquest for two to six, on a map of the real world. [Demo](https://johnmorrisdotca.github.io/tenka/).
629
- - [Kumimoji](https://github.com/johnmorrisdotca/kumimoji) (組み文字): a crossword tile race, in English and Japanese kana. [Demo](https://johnmorrisdotca.github.io/kumimoji/).
630
- - [Tsunagi](https://github.com/johnmorrisdotca/tsunagi) (繋ぎ): a line-joining logic puzzle whose every level has exactly one answer. [Demo](https://johnmorrisdotca.github.io/tsunagi/).
629
+
630
+ **Games — cards & tiles**
631
+
632
+ - [Hitotsu](https://github.com/johnmorrisdotca/hitotsu) (一つ): a colour-card shedding game for two to eight, with the house rules people play. [Demo](https://johnmorrisdotca.github.io/hitotsu/).
631
633
  - [Jarajara](https://github.com/johnmorrisdotca/jarajara) (ジャラジャラ): mahjong tiles drawn as SVG, stacked layouts, and the matching solitaire Awase. [Demo](https://johnmorrisdotca.github.io/jarajara/).
632
- - [Suido](https://github.com/johnmorrisdotca/suido) (水道): a pipe puzzle: turn the pieces until the water reaches every drain. [Demo](https://johnmorrisdotca.github.io/suido/).
633
- - [Domino](https://github.com/johnmorrisdotca/domino) (ドミノ): dominoes and Mexican Train. [Demo](https://johnmorrisdotca.github.io/domino/).
634
- - [Kotoba](https://github.com/johnmorrisdotca/kotoba) (言葉): word lists and word-game rules in English, French, German and Japanese. [Demo](https://johnmorrisdotca.github.io/kotoba/).
635
- - [Sugoroku](https://github.com/johnmorrisdotca/sugoroku) (双六): backgammon and its variants, with the doubling cube and match play. [Demo](https://johnmorrisdotca.github.io/sugoroku/).
634
+ - [Kumimoji](https://github.com/johnmorrisdotca/kumimoji) (組み文字): a crossword tile race, in English and Japanese kana. [Demo](https://johnmorrisdotca.github.io/kumimoji/).
635
+ - [Toranpu](https://github.com/johnmorrisdotca/toranpu) (トランプ): a deck of playing cards, card games with computer players, and solitaires. [Demo](https://johnmorrisdotca.github.io/toranpu/).
636
+
637
+ **Games — puzzles**
638
+
639
+ - [Houseki](https://github.com/johnmorrisdotca/houseki) (宝石): gem and stone matching puzzles: falling triplets, stone collapse, colour chains and gem swap. [Demo](https://johnmorrisdotca.github.io/houseki/).
640
+ - [Jirai](https://github.com/johnmorrisdotca/jirai) (地雷): minesweeper on shaped grids with verified no-guess boards. [Demo](https://johnmorrisdotca.github.io/jirai/).
641
+ - [Karakuri](https://github.com/johnmorrisdotca/karakuri) (からくり): eight hyper-casual puzzle games, some of them physics: draw a shield, pull pins, cut ropes, slide blocks, pour tubes. [Demo](https://johnmorrisdotca.github.io/karakuri/).
636
642
  - [Kazu](https://github.com/johnmorrisdotca/kazu) (数): grid number puzzles: Sudoku and its variants, Futoshiki and Skyscrapers. [Demo](https://johnmorrisdotca.github.io/kazu/).
643
+ - [Kyuubu](https://github.com/johnmorrisdotca/kyuubu) (キューブ): a turning cube for the browser, 2×2 to 7×7, with record solves to replay. [Demo](https://johnmorrisdotca.github.io/kyuubu/).
637
644
  - [Meikyuu](https://github.com/johnmorrisdotca/meikyuu) (迷宮): mazes on squares, hexagons, triangles and circles, made from a seed and drawn through with a finger or the mouse. [Demo](https://johnmorrisdotca.github.io/meikyuu/).
645
+ - [Suido](https://github.com/johnmorrisdotca/suido) (水道): a pipe puzzle: turn the pieces until the water reaches every drain. [Demo](https://johnmorrisdotca.github.io/suido/).
646
+ - [Tobiishi](https://github.com/johnmorrisdotca/tobiishi) (飛び石): peg solitaire with nine boards and seeded solvable challenges. [Demo](https://johnmorrisdotca.github.io/tobiishi/).
647
+ - [Tsunagi](https://github.com/johnmorrisdotca/tsunagi) (繋ぎ): a line-joining logic puzzle whose every level has exactly one answer. [Demo](https://johnmorrisdotca.github.io/tsunagi/).
648
+
649
+ **Games — dice & random**
650
+
651
+ - [Korokoro](https://github.com/johnmorrisdotca/korokoro) (コロコロ): dice, with notation, exact odds, real sounds and the dice of many games. [Demo](https://johnmorrisdotca.github.io/korokoro/).
652
+ - [Tane](https://github.com/johnmorrisdotca/tane) (種): seeded random numbers and daily seeds, the same in every browser and on every server. [Demo](https://johnmorrisdotca.github.io/tane/).
653
+
654
+ **Language**
655
+
656
+ - [Bushu](https://github.com/johnmorrisdotca/bushu) (部首): find a kanji by the parts it is made of. [Demo](https://johnmorrisdotca.github.io/bushu/).
638
657
  - [Hikidashi](https://github.com/johnmorrisdotca/hikidashi) (引き出し): a drawer of small Japanese text tools: era dates, kanji numerals, readings and sentence difficulty. [Demo](https://johnmorrisdotca.github.io/hikidashi/).
658
+ - [Kotoba](https://github.com/johnmorrisdotca/kotoba) (言葉): word lists and word-game rules in English, French, German and Japanese. [Demo](https://johnmorrisdotca.github.io/kotoba/).
659
+
660
+ **Geography & data**
661
+
662
+ - [address-plus](https://github.com/johnmorrisdotca/address-plus) (アドレスプラス): parse and format addresses in the United States, Canada, Japan, Australia, France, Germany and the United Kingdom. [Demo](https://johnmorrisdotca.github.io/address-plus/).
639
663
  - [Chizu](https://github.com/johnmorrisdotca/chizu) (地図): maps of the world and of countries' regions, in English and Japanese, with a quiz and callouts. [Demo](https://johnmorrisdotca.github.io/chizu/).
640
- - [Bushu](https://github.com/johnmorrisdotca/bushu) (部首): find a kanji by the parts it is made of. [Demo](https://johnmorrisdotca.github.io/bushu/).
641
- - [Tobiishi](https://github.com/johnmorrisdotca/tobiishi) (飛び石): peg solitaire with nine boards and seeded solvable challenges. [Demo](https://johnmorrisdotca.github.io/tobiishi/).
642
- - [Jirai](https://github.com/johnmorrisdotca/jirai) (地雷): minesweeper on shaped grids with verified no-guess boards. [Demo](https://johnmorrisdotca.github.io/jirai/).
643
- - [Gunjin](https://github.com/johnmorrisdotca/gunjin) (軍人): five hidden-rank strategy games with pass-the-device play. [Demo](https://johnmorrisdotca.github.io/gunjin/).
644
- - [Karakuri](https://github.com/johnmorrisdotca/karakuri) (からくり): eight hyper-casual puzzle games, some of them physics: draw a shield, pull pins, cut ropes, slide blocks, pour tubes. [Demo](https://johnmorrisdotca.github.io/karakuri/).
645
- - [Houseki](https://github.com/johnmorrisdotca/houseki) (宝石): gem and stone matching puzzles: falling triplets, stone collapse, colour chains and gem swap. [Demo](https://johnmorrisdotca.github.io/houseki/).
646
- - [Kyogijo Stadiums](https://github.com/johnmorrisdotca/kyogijo-stadiums) (競技場): sports fields, rinks, courts and ballparks drawn to scale as SVG, with each team and the score on them. [Demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/).
647
- - [Taisen Brackets](https://github.com/johnmorrisdotca/taisen-brackets) (対戦): a sport-agnostic tournament bracket, drawn as SVG, from a list of ties. [Demo](https://johnmorrisdotca.github.io/taisen-brackets/).
664
+ - [Hata](https://github.com/johnmorrisdotca/hata) (旗): country and regional flags as SVG, at their true proportions and keyed by ISO code. [Demo](https://johnmorrisdotca.github.io/hata/).
665
+ - [Kuni](https://github.com/johnmorrisdotca/kuni) (国): countries and their regions, with ISO codes and names in English and Japanese. [Demo](https://johnmorrisdotca.github.io/kuni/).
666
+
667
+ **Sports**
668
+
648
669
  - [Dougu Sports](https://github.com/johnmorrisdotca/dougu-sports) (道具): sports emblems as SVG: trophies, medals, crests, and the ball and gear of every sport, with optional 3D turning. [Demo](https://johnmorrisdotca.github.io/dougu-sports/).
670
+ - [Kyogijo Stadiums](https://github.com/johnmorrisdotca/kyogijo-stadiums) (競技場): sports fields, rinks, courts and ballparks drawn to scale as SVG, with each team and the score on them. [Demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/).
649
671
  - [Senshu Jerseys](https://github.com/johnmorrisdotca/senshu-jerseys) (選手): sports jerseys, sweaters, kits, caps, helmets and retired-number banners as SVG, front and back, with optional 2.5D turning. [Demo](https://johnmorrisdotca.github.io/senshu-jerseys/).
672
+ - [Taisen Brackets](https://github.com/johnmorrisdotca/taisen-brackets) (対戦): a sport-agnostic tournament bracket, drawn as SVG, from a list of ties. [Demo](https://johnmorrisdotca.github.io/taisen-brackets/).
673
+
674
+ **Developer tools**
675
+
676
+ - [REST in Pieces](https://github.com/spxis/rest-in-pieces) (レスト・イン・ピーシーズ): seeded test data and REST APIs for frontend development, with latency and error drills. [Demo](https://spxis.github.io/rest-in-pieces/).
677
+
650
678
 
651
- **This package is Kyogijo Stadiums.** The demos of all twenty-eight share one header and footer, so each links the rest.
679
+ **This package is Kyogijo Stadiums.** The demos of all thirty-two share one header and footer, so each links the rest.
652
680
  <!-- family:end -->
653
681
 
654
682
  ## Development
package/dist/atlas.d.ts CHANGED
@@ -135,6 +135,9 @@ export type PlaceSurface = {
135
135
  } | {
136
136
  sport: "soccer";
137
137
  ground?: string;
138
+ } | {
139
+ sport: "athletics";
140
+ ground?: string;
138
141
  } | {
139
142
  sport: "nhl" | "nba";
140
143
  arena?: string;
@@ -158,7 +161,8 @@ export type SurfaceSport = PlaceSurface["sport"];
158
161
  export declare const hallSurfaces: (place: Place) => readonly HallSurface[];
159
162
  /**
160
163
  * Every surface a place offers, the place's own first: a park's fence (`mlb` with the park) or a ground's pitch at its size (`soccer` with the ground), then one for each
161
- * sport a tenant plays that the package draws (a CFL field for BC Place, an NFL field for MetLife Stadium, the rugby league field for Stadium Australia), each once. A ground
164
+ * sport a tenant plays that the package draws (a CFL field for BC Place, an NFL field for MetLife Stadium, the rugby league field for Stadium Australia), each once, and the
165
+ * athletics stadium (`athletics` with the ground) of a ground whose `track` is set. A ground
162
166
  * offers no ballpark, since its fence is not known; a park that a soccer club has played in offers the pitch at the international size, since its length and width are not known.
163
167
  * @example
164
168
  * ```ts
package/dist/atlas.js CHANGED
@@ -226,7 +226,8 @@ const hallOptions = (surface) => ({ sport: surface.sport, ...surface.options });
226
226
  export const hallSurfaces = (place) => (place.kind === "hall" ? (place.hall?.surfaces ?? []) : []);
227
227
  /**
228
228
  * Every surface a place offers, the place's own first: a park's fence (`mlb` with the park) or a ground's pitch at its size (`soccer` with the ground), then one for each
229
- * sport a tenant plays that the package draws (a CFL field for BC Place, an NFL field for MetLife Stadium, the rugby league field for Stadium Australia), each once. A ground
229
+ * sport a tenant plays that the package draws (a CFL field for BC Place, an NFL field for MetLife Stadium, the rugby league field for Stadium Australia), each once, and the
230
+ * athletics stadium (`athletics` with the ground) of a ground whose `track` is set. A ground
230
231
  * offers no ballpark, since its fence is not known; a park that a soccer club has played in offers the pitch at the international size, since its length and width are not known.
231
232
  * @example
232
233
  * ```ts
@@ -260,6 +261,9 @@ export function surfacesOf(place) {
260
261
  continue;
261
262
  out.push(surface);
262
263
  }
264
+ // A ground with a running track round its pitch (`track`) also offers the athletics stadium: the track and its field events round the ground's own pitch.
265
+ if (place.kind === "ground" && place.track === true && !out.some((have) => have.sport === "athletics"))
266
+ out.push({ sport: "athletics", ground: place.id });
263
267
  return out;
264
268
  }
265
269
  export function surfaceOf(place, sport) {
@@ -42,6 +42,16 @@ export interface Env {
42
42
  * `data-index` (its place in the layer's data) and `data-zone` (the zone it lies in, from `zoneAt`'s rules). The place is in feet in the drawing's wide frame.
43
43
  */
44
44
  export declare function markAttrs(env: Env, id: unknown, index: number, at?: readonly [number, number], name?: string): string;
45
+ /**
46
+ * The tooltip of a mark: the caller's own `title`, else (only when the layer writes `data-mark-id` and friends, for a listener that reads the mark's name)
47
+ * a short one made from what the datum says: its outcome, its team and its place in the data ("goal, home, 3"). Nothing is added when the marks are not
48
+ * written, so a drawing that nobody points at is the same text as ever.
49
+ */
50
+ export declare function markTitle(env: Env, d: {
51
+ title?: unknown;
52
+ outcome?: unknown;
53
+ team?: unknown;
54
+ }, index: number): string | undefined;
45
55
  /** What a layer made. */
46
56
  export interface Made {
47
57
  /** The elements of the layer, and its gradients as `defs`. */
@@ -40,6 +40,19 @@ export function markAttrs(env, id, index, at, name = "data-zone") {
40
40
  const zone = at === undefined ? undefined : env.zone(at[0], at[1]);
41
41
  return ` data-mark-id="${esc(given)}" data-index="${index}"${zone === undefined ? "" : ` ${name}="${esc(zone)}"`}`;
42
42
  }
43
+ /**
44
+ * The tooltip of a mark: the caller's own `title`, else (only when the layer writes `data-mark-id` and friends, for a listener that reads the mark's name)
45
+ * a short one made from what the datum says: its outcome, its team and its place in the data ("goal, home, 3"). Nothing is added when the marks are not
46
+ * written, so a drawing that nobody points at is the same text as ever.
47
+ */
48
+ export function markTitle(env, d, index) {
49
+ if (typeof d.title === "string" && d.title !== "")
50
+ return d.title;
51
+ if (!env.marks)
52
+ return undefined;
53
+ const word = (v) => (typeof v === "string" ? v.trim() : "");
54
+ return [word(d.outcome), word(d.team), String(index + 1)].filter((part) => part !== "").join(", ");
55
+ }
43
56
  export const toArray = (value, what) => {
44
57
  if (!Array.isArray(value))
45
58
  throw new TypeError(`${what} needs data: a list of marks`);
package/dist/layers.js CHANGED
@@ -11,7 +11,7 @@ import { FRAMES } from "./frames.js";
11
11
  import { KYOGIJO_HEAT_STYLE, KYOGIJO_TEAM_STYLE } from "./style.js";
12
12
  import { drawTeam, isTeamLayer, teamTables } from "./team.layers.js";
13
13
  import { drawHeat, drawLegend, heatTable } from "./heat.js";
14
- import { clamp01, envFor, isNum, markAttrs, newMade, opacityOf, own, place, toArray, unitOfLayer } from "./layers.env.js";
14
+ import { clamp01, envFor, isNum, markAttrs, markTitle, newMade, opacityOf, own, place, toArray, unitOfLayer } from "./layers.env.js";
15
15
  import { safeClass } from "./common.js";
16
16
  import { esc, num } from "./format.js";
17
17
  import { curveBetween, curvePath, headPath, isPolygon, polylinePath, rampColour, shapePath, taperPath, wavePath, } from "./layers.marks.js";
@@ -144,12 +144,12 @@ function drawPoints(layer, env) {
144
144
  const logo = shape === "logo" ? safeLogo(d.logo) ?? (role === undefined ? undefined : safeLogo(style.logos?.[role]) ?? env.logos[role]) : undefined;
145
145
  if (logo !== undefined) {
146
146
  const side = w * 1.8;
147
- out.push(titled("image", `${open} href="${esc(logo)}" x="${num(x - side / 2)}" y="${num(y - side / 2)}" width="${num(side)}" height="${num(side)}" preserveAspectRatio="xMidYMid meet"${env.back !== 0 ? ` transform="rotate(${num(env.back)} ${num(x)} ${num(y)})"` : ""}`, d.title));
147
+ out.push(titled("image", `${open} href="${esc(logo)}" x="${num(x - side / 2)}" y="${num(y - side / 2)}" width="${num(side)}" height="${num(side)}" preserveAspectRatio="xMidYMid meet"${env.back !== 0 ? ` transform="rotate(${num(env.back)} ${num(x)} ${num(y)})"` : ""}`, markTitle(env, d, i)));
148
148
  }
149
149
  else if (shape !== "logo" && isPolygon(shape))
150
- out.push(titled("path", `${open} d="${shapePath(shape, x, y, w, env.turned)}"`, d.title));
150
+ out.push(titled("path", `${open} d="${shapePath(shape, x, y, w, env.turned)}"`, markTitle(env, d, i)));
151
151
  else
152
- out.push(titled("circle", `${open} cx="${num(x)}" cy="${num(y)}" r="${num(w / 2)}"`, d.title));
152
+ out.push(titled("circle", `${open} cx="${num(x)}" cy="${num(y)}" r="${num(w / 2)}"`, markTitle(env, d, i)));
153
153
  });
154
154
  made.marks = out.join("");
155
155
  return made;
@@ -259,7 +259,8 @@ function drawLines(layer, env, index) {
259
259
  gradient = `stroke:url(#${gid})`;
260
260
  }
261
261
  const headEl = head === "" ? "" : `<path class="${headKind === "open" ? "ky-ln" : "ky-ah"}" d="${head}"${headKind === "open" ? ` stroke-width="${num(w)}"` : ""}/>`;
262
- const title = typeof d.title === "string" && d.title !== "" ? `<title>${esc(d.title)}</title>` : "";
262
+ const named = markTitle(env, d, i);
263
+ const title = named === undefined ? "" : `<title>${esc(named)}</title>`;
263
264
  const opacity = opacityOf(d.opacity);
264
265
  // Where it starts is its zone; where it ends is `data-zone-end`.
265
266
  const marks = env.marks ? markAttrs(env, d.id, i, first) + (env.zone(last[0], last[1]) === undefined ? "" : ` data-zone-end="${esc(env.zone(last[0], last[1]))}"`) : "";
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** The package's version: the same as package.json's, and a test holds them to each other. */
2
- export declare const VERSION = "0.10.2";
2
+ export declare const VERSION = "0.11.0";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** The package's version: the same as package.json's, and a test holds them to each other. */
2
- export const VERSION = "0.10.2";
2
+ export const VERSION = "0.11.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johnmorrisdotca/kyogijo-stadiums",
3
- "version": "0.10.2",
3
+ "version": "0.11.0",
4
4
  "description": "Kyogijo Stadiums 競技場, 39 sports' surfaces as SVG, drawn to scale from the rulebooks: NFL, CFL and IFAF football fields, an NHL rink, an NBA court, MLB ballparks, soccer pitches, curling sheets, athletics stadiums, speed skating, short track and figure skating rinks, swimming pools, water polo fields, diving wells, artistic swimming areas, and the courts and fields of tennis, volleyball (indoor, beach and sitting), badminton, pickleball, table tennis, padel, squash, racquetball, sepak takraw, teqball, roundnet, netball, lacrosse, rugby, Australian football, Gaelic games, cricket, field hockey and handball, and the budo of judo, karate, kendo, sumo and kyudo with a dojo of tatami laid as real units, with each team's name and logo on its side and the score in the middle, stands and a crowd, a pageant of flags, and a 3D view. Light and dark, accessible, a compact badge for lists, a helper for React and a custom element. Zero dependencies.",
5
5
  "keywords": [
6
6
  "sports",