card-theme
PURPOSE
Shared visual theme for every card surface in the game — gradients, badges, effect tiers, and drop shadow. Provides two coloring schemes: by rarity (artifacts, modifiers, passives, ships) and by weapon damage tag (weapon cards). Implements the medical-UI rollout from ADR 20260513-001, in which the rarity gradient is retained as an identity color carrier but applied as a header strip on a white panel surface rather than as the full card background.
OWNS
Raritytype — five-tier rarity union (common|uncommon|rare|epic|legendary).GradientStopsinterface —{ top, bottom, angleDeg }describing a linear-gradient tuple.RARITY_GRADIENT— saturated jewel-tone gradient per rarity, with rare as deep sapphire (distinct from electric energy-weapon blue).WEAPON_TAG_GRADIENT— gradient perDamageTag(bullet, energy, fire, bomb), kept for legacy consumers wanting a full damage-typed card background.RARITY_BADGE_LETTER— letter grade per rarity (D / C / B / A / S).RARITY_BADGE_FILL— solid corner-badge fill color per rarity.RARITY_ACCENT— neon accent color per rarity (stronger than badge fill) for glow and stroke effects.RARITY_EFFECT_TIER— 0-4 integer per rarity driving which animated effects layer on the card (0 none, 1 soft glow, 2 glow+stroke, 3 glow+stroke+particles, 4 glow+stroke+particles+lightning+flash). Common and uncommon are 0, rare is 1, epic is 2, legendary is 4.CARD_SHADOW— medical-UI drop shadow constants (offsetX,offsetY,blur,color) tuned to a cool-blue tinted soft blur replacing the V32 hard black shadow.CARD_BADGE— corner badge layout constants (radius,dangleX,dangleY).CARD_SHADOW_CSS— pre-built CSS box-shadow string derived fromCARD_SHADOW.rarityGradientCss(rarity)— CSSlinear-gradient(...)string for a rarity.weaponTagGradientCss(tag)— CSSlinear-gradient(...)string for a damage tag.paintCardGradient(ctx, x, y, w, h, stops)— canvas-side painter that converts the compass-bearingangleDeg(0° = top, clockwise) into canvas XY and fills acreateLinearGradientbetween top and bottom stops.
READS FROM
@starship-survivors/data/weapons/_types— imports theDamageTagtype used to keyWEAPON_TAG_GRADIENTandweaponTagGradientCss.
PUSHES TO
- DOM consumers via
rarityGradientCss/weaponTagGradientCss/CARD_SHADOW_CSS(React card components). - Canvas consumers via
paintCardGradient— writes a linear gradient fill to the suppliedCanvasRenderingContext2D.
DOES NOT
- Render cards or compose card layouts — only exposes theme primitives.
- Define the damage-tag pill palette — that lives in
damage-tag-pill.ts. - Define the medical panel surface itself — references
MedicalPalette.panelRaised,MedicalPalette.border, andMedicalShadow.panelonly conceptually in comments; the consumers wire those tokens. - Animate any effect —
RARITY_EFFECT_TIERis a data flag for consumers; no animation loop runs here. - Persist or mutate state — module is pure constants and pure functions.
Signals
None. No event emission, store writes, or telemetry.
Entry points
- In-game level-up reward cards via
hud.ts→drawRewardCardImpl(canvas). - Ship inventory grid via
CollectibleCard.tsx(DOM). - Ship inventory hero card via
SelectTab.tsx(DOM). - Weapon popover, which reuses
drawRewardCard(canvas).
Pattern notes
- Gradient angle is treated as a compass bearing (0° = top, clockwise), not the CSS convention (0° = bottom→top).
paintCardGradientsubtracts 90° before converting to radians so the sameangleDegvalue can flow into either CSS string builders or the canvas painter and produce a visually matching gradient. - The module is a pure constants-and-helpers theme file with no React or canvas dependency at the type level beyond
CanvasRenderingContext2Din one function signature. - The two coloring schemes (
RARITY_GRADIENT,WEAPON_TAG_GRADIENT) coexist intentionally so individual surfaces can pick the identity axis (rarity vs damage type) that matters for that view. - Rarity is the identity carrier post medical-UI rollout: gradient becomes a header strip on the white
panelRaisedsurface, rather than a full-bleed background. The constants themselves did not change shape — consumers changed how they apply them. - Effect tier mapping is deliberately non-linear: epic skips the particles tier (3) and legendary jumps straight to the full lightning+flash stack (4), so legendary reads as a distinct spectacle rather than an incremental step up from epic.