Design pass: the client becomes Eric's table
The design system is the physical set itself, taken from the scans: card-stock cream, warm ink, and the six wizard marker colors, laid on a dark leather tabletop. Cards in your hand are now the actual cards — vertical corner rails (ATTACK / L.O.S. / ADJACENT), condensed gothic titles, brown body text on cream, number cards as big bordered numerals with per-value border styles; selecting lifts a card from the fan. The board matches the physical tiles: ink-outlined white brick walls on cracked flagstone, plank doors, eight-point home stars in each wizard's color, swirled treasure discs, warp arrows. The sidebar is paper: a turn slip, the rulebook's own hand-scrawled life-points score sheet (Caveat over ruled lines), and a typewritten chronicle that scrolls itself. The lobby is the box lid. Type: Oswald condensed display / Archivo Narrow body / Courier Prime log. Buttons are ink stamps. Board height-caps so board, actions, and the full hand share one screen; responsive stack below 900px; reduced motion respected. Verified with live screenshots at 1440x900. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
c9524a37db
commit
9501a85be4
@@ -0,0 +1,199 @@
|
||||
<script lang="ts">
|
||||
import { cardDef } from "@wizwar/engine";
|
||||
import type { CardInstance } from "@wizwar/engine";
|
||||
|
||||
let {
|
||||
card,
|
||||
selected = false,
|
||||
attached = false,
|
||||
marked = false,
|
||||
displayed = false,
|
||||
charges = null,
|
||||
onclick,
|
||||
}: {
|
||||
card: CardInstance;
|
||||
selected?: boolean;
|
||||
attached?: boolean;
|
||||
marked?: boolean;
|
||||
displayed?: boolean;
|
||||
charges?: number | null;
|
||||
onclick?: () => void;
|
||||
} = $props();
|
||||
|
||||
const def = $derived(cardDef(card.cardId));
|
||||
const isNumber = $derived(def.cardType === "number");
|
||||
|
||||
const leftRail = $derived.by(() => {
|
||||
switch (def.cardType) {
|
||||
case "attack": return "ATTACK";
|
||||
case "neutral": return "NEUTRAL";
|
||||
case "counteraction": return "COUNTERACTION";
|
||||
case "neutral/counteraction": return "NEUTRAL/COUNTERACTION";
|
||||
case "trap": return "TRAP";
|
||||
default: return "";
|
||||
}
|
||||
});
|
||||
const rightRail = $derived(def.los ? "L.O.S." : def.adjacent ? "ADJACENT" : "");
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="card"
|
||||
class:selected
|
||||
class:attached
|
||||
class:marked
|
||||
class:number={isNumber}
|
||||
{onclick}
|
||||
title={def.text ?? def.name}
|
||||
>
|
||||
{#if isNumber}
|
||||
<span class="corner tl">{def.value}</span>
|
||||
<span class="numeral" data-value={def.value}>{def.value}</span>
|
||||
<span class="brand">Wiz-War</span>
|
||||
<span class="corner br">{def.value}</span>
|
||||
{:else}
|
||||
{#if leftRail}<span class="rail left">{leftRail}</span>{/if}
|
||||
{#if rightRail}<span class="rail right">{rightRail}</span>{/if}
|
||||
<span class="name">{def.name}</span>
|
||||
{#if def.text}<span class="text">{def.text}</span>{/if}
|
||||
{/if}
|
||||
{#if displayed}
|
||||
<span class="badge">DISPLAYED{charges != null ? ` · ${charges}` : ""}</span>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<style>
|
||||
.card {
|
||||
position: relative;
|
||||
width: 8.2rem;
|
||||
height: 11.4rem;
|
||||
flex: 0 0 auto;
|
||||
background:
|
||||
radial-gradient(ellipse at 30% 20%, rgba(255, 252, 240, 0.5), transparent 60%),
|
||||
#e9e1cb;
|
||||
border: 1px solid #b3a687;
|
||||
border-radius: 7px;
|
||||
box-shadow:
|
||||
0 1px 0 rgba(255, 255, 255, 0.5) inset,
|
||||
0 3px 8px rgba(10, 8, 4, 0.45);
|
||||
color: #5f4a33;
|
||||
padding: 0.55rem 1.05rem 0.5rem;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
transition: transform 120ms ease, box-shadow 120ms ease;
|
||||
}
|
||||
.card:hover {
|
||||
transform: translateY(-0.55rem);
|
||||
box-shadow: 0 8px 18px rgba(10, 8, 4, 0.55);
|
||||
z-index: 3;
|
||||
}
|
||||
.card:focus-visible {
|
||||
outline: 2px solid #d8b23a;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
.card.selected {
|
||||
transform: translateY(-0.8rem);
|
||||
box-shadow:
|
||||
0 0 0 2.5px #d8b23a,
|
||||
0 10px 20px rgba(10, 8, 4, 0.6);
|
||||
}
|
||||
.card.attached {
|
||||
transform: translateY(-0.5rem);
|
||||
box-shadow: 0 0 0 2.5px #2e7d32, 0 8px 16px rgba(10, 8, 4, 0.55);
|
||||
}
|
||||
.card.marked {
|
||||
box-shadow: 0 0 0 2.5px #b3372b, 0 4px 10px rgba(10, 8, 4, 0.5);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.rail {
|
||||
position: absolute;
|
||||
top: 0.55rem;
|
||||
font-family: "Oswald", sans-serif;
|
||||
font-size: 0.42rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.14em;
|
||||
color: #4a3a26;
|
||||
writing-mode: vertical-lr;
|
||||
text-orientation: upright;
|
||||
line-height: 1;
|
||||
max-height: 9.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
.rail.left { left: 0.28rem; }
|
||||
.rail.right { right: 0.28rem; }
|
||||
|
||||
.name {
|
||||
font-family: "Oswald", sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 0.78rem;
|
||||
line-height: 1.05;
|
||||
letter-spacing: 0.02em;
|
||||
text-transform: uppercase;
|
||||
color: #43331f;
|
||||
margin: 0.35rem 0 0.3rem;
|
||||
}
|
||||
.text {
|
||||
font-family: "Archivo Narrow", sans-serif;
|
||||
font-size: 0.56rem;
|
||||
line-height: 1.28;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 9;
|
||||
line-clamp: 9;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
/* Number cards: a big numeral in a bordered plate, like the originals. */
|
||||
.numeral {
|
||||
font-family: "Oswald", sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 3.4rem;
|
||||
line-height: 1;
|
||||
color: #43331f;
|
||||
margin-top: 1.6rem;
|
||||
padding: 0.4rem 1rem 0.5rem;
|
||||
border: 2px solid #5f4a33;
|
||||
}
|
||||
.numeral[data-value="3"] { border-style: dashed; }
|
||||
.numeral[data-value="4"] { border-radius: 12px; }
|
||||
.numeral[data-value="5"] { border-style: double; border-width: 4px; }
|
||||
.numeral[data-value="6"] { border-style: none; outline: 2px dotted #5f4a33; outline-offset: 4px; }
|
||||
.brand {
|
||||
font-family: "Oswald", sans-serif;
|
||||
font-size: 0.6rem;
|
||||
letter-spacing: 0.06em;
|
||||
margin-top: 0.45rem;
|
||||
}
|
||||
.corner {
|
||||
position: absolute;
|
||||
font-family: "Oswald", sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.corner.tl { top: 0.3rem; left: 0.45rem; }
|
||||
.corner.br { bottom: 0.3rem; right: 0.45rem; transform: rotate(180deg); }
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
bottom: 0.3rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: #43331f;
|
||||
color: #e9e1cb;
|
||||
font-family: "Oswald", sans-serif;
|
||||
font-size: 0.45rem;
|
||||
letter-spacing: 0.12em;
|
||||
padding: 0.12rem 0.4rem;
|
||||
border-radius: 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.card { transition: none; }
|
||||
.card:hover, .card.selected, .card.attached { transform: none; }
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user