Skip to content

Commit 1a0230c

Browse files
authored
chore(elements): <TileRow /> element (#78)
<TileRow /> element Closes #66 Introduces <TileRow />, a unified element for the "compact row of text-label tiles" pattern that was duplicated across three character-creation screens. Before: HitDieOptions (step-class), ability badges (step-origin), and OriginBonusPicker's button grid each had their own ad-hoc markup and per-file CSS — all rendering the same visual primitive. After: a single element with two modes: - Read-only — role="list" + <span role="listitem"> per tile - Interactive — <button> per tile with aria-label, aria-pressed, aria-disabled; supports right-click via onUnpick ScorePicker is also refactored to delegate to TileRow, keeping only its toggle logic. ~220 lines of duplicated CSS removed. Sprite palette work - Normalize all sprites to 8 colors with a shared outline and main.palette.json - Unify skin/leather tones and outline color across all characters - Reduce barbarian, druid color counts; collapse druid near-duplicates - Add processed barbarian sprite - New sprite:candidates script - Add sorcerer sprite
2 parents 0561aee + ac0fc67 commit 1a0230c

40 files changed

Lines changed: 940 additions & 258 deletions
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
name: reduce-global-palette
3+
description: Iteratively reduce the global cross-sprite palette one color at a time. Use when the user wants to unify colors across character sprites or shrink the main palette.
4+
version: 1.1.0
5+
---
6+
7+
# Reduce Global Palette
8+
9+
## When to use this skill
10+
When the user wants to shrink the total number of unique colors across all sprites.
11+
Always process **one color at a time** — find one candidate, apply one merge, verify, done.
12+
13+
Invoke with: `/reduce-global-palette`
14+
15+
---
16+
17+
## Phase 1 — Find one color to remove
18+
19+
```
20+
pnpm sprite:candidates
21+
```
22+
23+
This prints a ranked table of cross-sprite color pairs sorted by RGB distance (smallest = safest):
24+
25+
```
26+
dist replace in from-hex → keep in into-hex
27+
9 fighter #BEC1C8 → rogue #B9BBC3
28+
18 barbarian #EA9364 → sorcerer #EA826A
29+
...
30+
```
31+
32+
**Pick the top row** (lowest distance). That is your target.
33+
- Distance < 30: safe to apply directly.
34+
- Distance 30–50: apply and do a visual check before confirming.
35+
36+
---
37+
38+
## Phase 2 — Remove one color
39+
40+
With the chosen row (`replace-in` sprite, `FROM` hex, `INTO` hex):
41+
42+
**Step 1 — Apply the merge**
43+
```
44+
pnpm sprite:merge-colors --input public/assets/sprites/<replace-in>.png --merge FROM:INTO
45+
```
46+
47+
**Step 2 — Upscale and display**
48+
```
49+
pnpm sprite:upscale --input public/assets/sprites/<replace-in>.png --scale 4
50+
```
51+
Read and display `<name>_4x.png`. Confirm no visible change.
52+
53+
**Step 3 — Rebuild main palette**
54+
```
55+
pnpm sprite:build-palette
56+
```
57+
Report the new total unique color count from `main.palette.json`.
58+
59+
---
60+
61+
## Done for this round
62+
One color removed. Stop here and wait for the user to confirm before continuing.

.claude/skills/sprite-pipeline/SKILL.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,13 @@ Show the final palette table with color roles (outline, skin, leather, cloak, et
5151

5252
### Step 5 — Upscale
5353
```
54-
pnpm sprite:upscale --input <file> --scale 2
54+
pnpm sprite:upscale --input <file> --scale 4
5555
```
56-
Use `--scale 2` (not 4) — AI-generated sprites arrive pre-upscaled and are already larger than the original 32×32 base. Using 4x would produce an oversized image.
57-
58-
After upscaling, read and display the `<name>_2x.png` result image for visual confirmation.
56+
After upscaling, read and display the `<name>_4x.png` result image for visual confirmation.
5957

6058
## Output
6159
- `<name>.png` — processed sprite (transparent background, 10 colors)
62-
- `<name>_2x.png` — upscaled version (the suffix follows `_<scale>x`, e.g. `_2x` for `--scale 2`)
60+
- `<name>_4x.png` — upscaled version (the suffix follows `_<scale>x`, e.g. `_4x` for `--scale 4`)
6361
- `<name>-palette.json` — final palette
6462

6563
## Notes

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
"sprite:merge-colors": "node scripts/reduce-palette.mjs",
4646
"sprite:remove-background": "node scripts/remove-background.mjs",
4747
"sprite:flip": "node scripts/flip-sprite.mjs",
48+
"sprite:build-palette": "node scripts/build-main-palette.mjs",
49+
"sprite:candidates": "node scripts/sprite-merge-candidates.mjs",
4850
"blue-ball": "pnpm lint && pnpm test && pnpm build",
4951
"postblue-ball": "pnpm test:coverage && ./.hooks/update-coverage-badge.sh"
5052
},

prompts/sprites/class-avatar.prompt.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
## Template
66

77
```
8-
A tiny {CLASS} character sprite in Game Boy pixel art style, 4 colors only using classic Game Boy
8+
A {CLASS} character sprite in Game Boy pixel art style, 4 colors only using classic Game Boy
99
{CLASS} palette ({PALETTE}), 32x32 pixels, front-facing idle pose, {CLOTHING}, {WEAPON},
1010
{COMPANION}, chunky pixelated outlines, no background, transparent background, retro RPG handheld
1111
game aesthetic, 1990s portable game sprite "no anti-aliasing, hard pixel edges, low resolution
@@ -48,4 +48,11 @@ Palette order: **darkest → dark → light → lightest**
4848
- Rogue (2026-03-29): v2 accepted (v1 rejected — lacking leather/color, too grey).
4949
> A tiny tall Tiefling Rogue character sprite in Game Boy pixel art style, 32x32 pixels, front-facing idle pose, 6-color palette: dark near-black outline (#030305), deep dark blue-grey (#181820), mid blue-grey (#404058), light silver-grey (#9090a8) for the cloak and armor, warm reddish-purple (#7a2040) for tiefling skin on face and hands, warm brown (#6b3a1a) for leather chest piece and bracers, dark hooded cloak pushed back revealing small curved horns, tight leather armor with visible belt and buckle details, slender tall frame, dagger in raised hand, second dagger at belt, a tiny rat on shoulder, thin devil tail curling from cloak hem, chunky pixelated outlines, no background, transparent background, retro RPG handheld game aesthetic, 1990s portable game sprite "no anti-aliasing, hard pixel edges, low resolution dithering", character facing right (3/4 right-facing view)
5050
51-
- Wizard (2026-03-29): prompt note — must specify character facing right (3/4 right-facing view).
51+
- Wizard (2026-03-29): v1 accepted. Prompt note: must specify character facing right.
52+
> A tiny wizard character sprite in Game Boy pixel art style, 4 colors only using classic Game Boy wizard palette (#020810 #0c2060 #3070d0 #c0d8ff), 32x32 pixels, front-facing idle pose, long robes with star pattern trim, pointed hat, tall staff with gem on top, spellbook at side, small cat familiar or floating scroll, chunky pixelated outlines, no background, transparent background, retro RPG handheld game aesthetic, 1990s portable game sprite "no anti-aliasing, hard pixel edges, low resolution dithering", character facing right (3/4 right-facing view)
53+
54+
- Sorcerer (2026-03-30): v1 good result, not exquisite — saved as baseline.
55+
> A tiny female Aasimar Sorcerer character sprite in Game Boy pixel art style, 32x32 pixels, front-facing idle pose, 6-color palette: dark near-black outline (#080010), deep dark purple (#380868), mid purple (#9040d0), soft pink-white (#f8d0ff) for robe highlights and wing feather tips, warm light skin (#e8c490) for face and hands, bright near-white (#f4f0e8) for large feathered aasimar wings spread slightly behind her, flowing robe with arcane rune trim, arcane orb held forward no staff, large feathered wings fanned slightly behind back — NO halo, small magical purple flames flickering around her body as an arcane aura — at her feet, along her arms, framing the wings — NO animal companion, chunky pixelated outlines, no background, transparent background, retro RPG handheld game aesthetic, 1990s portable game sprite "no anti-aliasing, hard pixel edges, low resolution dithering", character facing right (3/4 right-facing view)
56+
57+
- Sorcerer (2026-03-30): v2 accepted — red tones, aasimar wings, no halo, no animal companion.
58+
> A female Aasimar Sorcerer character sprite in Game Boy pixel art style, 32x32 pixels, front-facing idle pose, 6-color palette: dark near-black outline (#080010), deep crimson-dark (#3a0010), vivid red (#c02030) for flames and clothing details, warm rose-gold (#f8a080) for robe highlights and flame tips, warm light skin (#e8c490) for face and hands, bright near-white (#f4f0e8) for large feathered aasimar wings spread slightly behind her, gorgeous elegant flowing crimson sorcerer robes with ornate gold-red trim and arcane runes, low neckline, dramatic silhouette, arcane orb held forward no staff, large feathered wings fanned slightly behind back — NO halo, vivid red magical flames flickering around her body as an arcane aura — at her feet, along her arms, framing the wings — NO animal companion, chunky pixelated outlines, no background, transparent background, retro RPG handheld game aesthetic, 1990s portable game sprite "no anti-aliasing, hard pixel edges, low resolution dithering", character facing right (3/4 right-facing view)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[
2+
{
3+
"hex": "#030307",
4+
"pixels": 1256
5+
},
6+
{
7+
"hex": "#832B10",
8+
"pixels": 787
9+
},
10+
{
11+
"hex": "#EA9364",
12+
"pixels": 510
13+
},
14+
{
15+
"hex": "#B64826",
16+
"pixels": 390
17+
},
18+
{
19+
"hex": "#3B1F17",
20+
"pixels": 348
21+
},
22+
{
23+
"hex": "#EFB88B",
24+
"pixels": 288
25+
},
26+
{
27+
"hex": "#BA724D",
28+
"pixels": 284
29+
},
30+
{
31+
"hex": "#FFFFFE",
32+
"pixels": 16
33+
}
34+
]
3.73 KB
Loading
2.47 KB
Loading
Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,22 @@
11
[
2+
{
3+
"hex": "#030307",
4+
"pixels": 624
5+
},
26
{
37
"hex": "#8BAC0F",
4-
"pixels": 489
8+
"pixels": 494
59
},
610
{
711
"hex": "#38692C",
812
"pixels": 487
913
},
10-
{
11-
"hex": "#092907",
12-
"pixels": 460
13-
},
1414
{
1515
"hex": "#21491F",
1616
"pixels": 279
1717
},
1818
{
19-
"hex": "#062205",
20-
"pixels": 162
21-
},
22-
{
23-
"hex": "#433B15",
19+
"hex": "#3B1F17",
2420
"pixels": 72
2521
},
2622
{
@@ -36,15 +32,7 @@
3632
"pixels": 41
3733
},
3834
{
39-
"hex": "#CDFDB0",
35+
"hex": "#FFFFFE",
4036
"pixels": 12
41-
},
42-
{
43-
"hex": "#86A91D",
44-
"pixels": 4
45-
},
46-
{
47-
"hex": "#85AA3A",
48-
"pixels": 1
4937
}
5038
]

public/assets/sprites/druid.png

-2.96 KB
Loading

public/assets/sprites/druid_4x.png

-213 Bytes
Loading

0 commit comments

Comments
 (0)