Frontend: prepinac vzhladu kariet (znaky vs suit-icon)

Toggle v hlavicke prepina znaky farieb medzi textovymi glyfmi a suit-icon obrazkami; volba perzistuje v localStorage. Gula ma v icon rezime hnedy popis, stredove ikonky srdca/gule/listu jemne zmensene aby sa neprekryvali s rohmi.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
timandClaude Opus 4.8 committed 2026-07-12 19:37:57 +02:00
1 parent 97c71cf6e1
commit 04185a0d97
16 files changed
+145 -18

No files matched your search

@@ -0,0 +1,23 @@
import { useCardSkin } from '../lib/cardSkin';
/** Small header button that flips the deck between the classic vector cards and
* the illustrated "sedmové" art. Persists via the cardSkin store. */
export default function CardSkinToggle({ className = '' }: { className?: string }) {
const skin = useCardSkin((s) => s.skin);
const toggle = useCardSkin((s) => s.toggle);
const illustrated = skin === 'illustrated';
return (
<button
onClick={toggle}
title="Prepnúť vzhľad kariet"
aria-label="Prepnúť vzhľad kariet"
aria-pressed={illustrated}
className={`text-[13px] whitespace-nowrap transition-colors ${
illustrated ? 'text-gold hover:text-gold-bright' : 'text-[#7a7058] hover:text-gold'
} ${className}`}
>
{illustrated ? '🂠 Sedmové' : '🂠 Klasické'}
</button>
);
}
+43 -4
View File
@@ -1,4 +1,5 @@
import type { Card } from '../types';
import { useCardSkin, suitIcon } from '../lib/cardSkin';
const SUIT_SYMBOL: Record<string, string> = {
HEARTS: '♥',
@@ -19,6 +20,15 @@ const VALUE_LABEL: Record<string, string> = {
LOWER: 'J', UPPER: 'Q', KING: 'K', ACE: 'A',
};
// The bell, leaf and heart icons are a touch taller than the acorn, so in the
// centre they sometimes bleed into the corner marks — nudge those three down.
const CENTER_ICON_SCALE: Record<string, number> = {
HEARTS: 0.85,
LEAVES: 0.85,
BELLS: 0.85,
ACORNS: 1,
};
// Per-size geometry. sm/md sit on the table, lg/xl are hand cards.
const DIMS = {
sm: { w: 38, h: 54, radius: 4, inset: 3, label: 9, suitSm: 7, suitLg: 18 },
@@ -37,26 +47,45 @@ interface Props {
}
export default function CardView({ card, onClick, disabled = false, highlight = false, size = 'md' }: Props) {
const skin = useCardSkin((s) => s.skin);
const symbol = SUIT_SYMBOL[card.color];
const color = SUIT_COLOR[card.color];
const label = VALUE_LABEL[card.value];
const d = DIMS[size];
const interactive = !disabled && !!onClick;
// 'illustrated' skin keeps the classic card but swaps the ♥♠♣♦ glyphs for the
// suit-icon artwork; 'classic' uses the plain text glyphs.
const useIcons = skin === 'illustrated';
// With the icon artwork the bell suit reads brown, so tint its value label to
// match instead of the classic blue.
const labelColor = useIcons && card.color === 'BELLS' ? '#8a5a1c' : color;
const corner = (rotated: boolean) => (
<span
className="absolute flex flex-col items-center leading-none"
// Icons align to the number's left edge so a wide label (VIII) doesn't shove
// the centred icon toward the middle; classic text stays centred as before.
className={`absolute flex flex-col leading-none ${useIcons ? 'items-start' : 'items-center'}`}
style={
rotated
? { bottom: d.inset, right: d.inset, transform: 'rotate(180deg)' }
: { top: d.inset, left: d.inset }
}
>
<span style={{ color, fontSize: d.label, fontFamily: 'Georgia,serif', fontWeight: 700, lineHeight: 1 }}>
<span style={{ color: labelColor, fontSize: d.label, fontFamily: 'Georgia,serif', fontWeight: 700, lineHeight: 1 }}>
{label}
</span>
<span style={{ color, fontSize: d.suitSm, lineHeight: 1.2 }}>{symbol}</span>
{useIcons ? (
<img
src={suitIcon(card.color, 'small')}
alt={symbol}
draggable={false}
style={{ height: d.suitSm * 1.25, width: 'auto', marginTop: 1 }}
className="select-none"
/>
) : (
<span style={{ color, fontSize: d.suitSm, lineHeight: 1.2 }}>{symbol}</span>
)}
</span>
);
@@ -76,7 +105,17 @@ export default function CardView({ card, onClick, disabled = false, highlight =
>
{corner(false)}
<span className="absolute inset-0 flex items-center justify-center">
<span style={{ color, fontSize: d.suitLg, lineHeight: 1 }}>{symbol}</span>
{useIcons ? (
<img
src={suitIcon(card.color, 'large')}
alt={symbol}
draggable={false}
style={{ height: d.suitLg * CENTER_ICON_SCALE[card.color], width: 'auto' }}
className="select-none"
/>
) : (
<span style={{ color, fontSize: d.suitLg, lineHeight: 1 }}>{symbol}</span>
)}
</span>
{corner(true)}
</button>