Stylised game-cover tiles in the game picker

- Each game renders as a two-tone tile split in its version colours
  (Scarlet/Violet, Sword/Shield, Ruby/Sapphire…), keyed by version group
  with a per-generation fallback (src/data/game-colors.js)
- Clean flat split with a bottom scrim for legibility — no gloss/bubble

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
chris 2026-08-27 13:40:18 -04:00
parent 61ceb6abf3
commit e61bf7ac6c
3 changed files with 114 additions and 3 deletions

View File

@ -6,6 +6,7 @@ import {
prettify,
} from '../data/pokedex-resolver.js';
import { settings } from '../store/settings.js';
import { gameColors } from '../data/game-colors.js';
let openInstance = null;
@ -101,12 +102,14 @@ export async function openGameSheet() {
for (const vg of versionGroups) {
const active = vg.key === st.versionGroup;
const dexCount = dexesForVersionGroup(snap, vg.key).length;
const [c1, c2] = gameColors(vg.key, generation.id);
grid.append(
el(
'button',
{
class: `gamecard${active ? ' is-active' : ''}`,
class: `gamecard gamecard--cover${active ? ' is-active' : ''}`,
type: 'button',
style: `--g1:${c1};--g2:${c2}`,
onclick: () => {
settings.set({ versionGroup: vg.key, pokedex: null });
if (dexesForVersionGroup(snap, vg.key).length <= 1) {
@ -118,8 +121,12 @@ export async function openGameSheet() {
},
},
el('span', { class: 'gamecard__name' }, prettify(vg.name || vg.key)),
el('span', { class: 'gamecard__meta' }, vg.versions.map(prettify).join(' / ')),
el('span', { class: 'gamecard__meta' }, `${dexCount} ${dexCount === 1 ? 'dex' : 'dexes'}`),
el(
'span',
{ class: 'gamecard__foot' },
el('span', { class: 'gamecard__meta' }, vg.versions.map(prettify).join(' / ')),
el('span', { class: 'gamecard__dex' }, `${dexCount} ${dexCount === 1 ? 'dex' : 'dexes'}`),
),
),
);
}

51
src/data/game-colors.js Normal file
View File

@ -0,0 +1,51 @@
/**
* Signature colours for each game, used to render a stylised "cover" tile in
* the game picker (real box art is Nintendo's and not in the API). Two
* colours = the paired versions; games without a listed entry fall back to
* a per-generation hue.
*/
const GAME = {
'red-blue': ['#e0362f', '#2f6cd8'],
'red-green-japan': ['#e0362f', '#39ab57'],
'blue-japan': ['#2f6cd8', '#3f8ad0'],
yellow: ['#f4c21a', '#f0a81f'],
'gold-silver': ['#d7a521', '#b9bcc6'],
crystal: ['#41a3c4', '#6fd0d8'],
'ruby-sapphire': ['#b4302e', '#2e5cb4'],
emerald: ['#12904a', '#26b56b'],
'firered-leafgreen': ['#e4552b', '#57b04b'],
'diamond-pearl': ['#7fb0e2', '#e0a3c8'],
platinum: ['#9aa0ab', '#c9ccd4'],
'heartgold-soulsilver': ['#d7a521', '#b9bcc6'],
'black-white': ['#26262b', '#b9b9c2'],
'black-2-white-2': ['#2b2b31', '#aeaeb8'],
'x-y': ['#4c7bc4', '#c43b3b'],
'omega-ruby-alpha-sapphire': ['#b4302e', '#2e5cb4'],
'sun-moon': ['#f0952b', '#4062b4'],
'ultra-sun-ultra-moon': ['#e3781e', '#34489e'],
'lets-go-pikachu-lets-go-eevee': ['#f4c21a', '#c08a52'],
'sword-shield': ['#00a0c6', '#c33b84'],
'brilliant-diamond-and-shining-pearl': ['#6f5ac4', '#d06fa6'],
'legends-arceus': ['#3e7c6a', '#b08d4a'],
'scarlet-violet': ['#c9352e', '#7c4da0'],
'the-teal-mask': ['#2e8b7a', '#39a892'],
'the-indigo-disk': ['#3b3ba0', '#5a5ac4'],
colosseum: ['#3a3a44', '#6e5aa0'],
xd: ['#4a2e6e', '#6a45a0'],
};
const GEN = {
1: ['#e0362f', '#e8564f'],
2: ['#d7a521', '#e0b94a'],
3: ['#12904a', '#2eb56b'],
4: ['#6e86c4', '#9ab0dc'],
5: ['#44444c', '#666670'],
6: ['#5b7bc4', '#7c9adc'],
7: ['#f0952b', '#f5b45a'],
8: ['#3aa6c6', '#5ec6dc'],
9: ['#c9352e', '#e0564f'],
};
export function gameColors(versionGroupKey, generation = 9) {
return GAME[versionGroupKey] || GEN[generation] || GEN[9];
}

View File

@ -621,6 +621,59 @@
text-transform: capitalize;
}
/* Cover tiles: a clean split of the two version colours, no gloss. */
.gamecard--cover {
position: relative;
overflow: hidden;
min-height: 88px;
justify-content: flex-end;
gap: 6px;
padding: 14px;
border: none;
color: #fff;
background: linear-gradient(105deg, var(--g1) 0%, var(--g1) 42%, var(--g2) 58%, var(--g2) 100%);
box-shadow: var(--shadow);
}
.gamecard--cover::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(to top, rgba(0, 0, 0, 0.28), transparent 55%);
}
.gamecard--cover > * {
position: relative;
}
.gamecard--cover:hover {
border: none;
transform: translateY(-3px);
}
.gamecard--cover.is-active {
background: linear-gradient(105deg, var(--g1) 0%, var(--g1) 42%, var(--g2) 58%, var(--g2) 100%);
box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
}
.gamecard--cover .gamecard__name {
font-size: 1rem;
line-height: 1.15;
}
.gamecard__foot {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.gamecard--cover .gamecard__meta {
color: rgba(255, 255, 255, 0.9);
}
.gamecard__dex {
font-size: 0.68rem;
font-weight: 700;
padding: 2px 8px;
border-radius: 6px;
background: rgba(255, 255, 255, 0.2);
color: #fff;
white-space: nowrap;
}
/* ================= Detail: type-themed hero + tabbed sheet ========= */
.pdetail {
--ink: #fff;