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

  • Rarity type — five-tier rarity union (common | uncommon | rare | epic | legendary).
  • GradientStops interface — { 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 per DamageTag (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 from CARD_SHADOW.
  • rarityGradientCss(rarity) — CSS linear-gradient(...) string for a rarity.
  • weaponTagGradientCss(tag) — CSS linear-gradient(...) string for a damage tag.
  • paintCardGradient(ctx, x, y, w, h, stops) — canvas-side painter that converts the compass-bearing angleDeg (0° = top, clockwise) into canvas XY and fills a createLinearGradient between top and bottom stops.

READS FROM

  • @starship-survivors/data/weapons/_types — imports the DamageTag type used to key WEAPON_TAG_GRADIENT and weaponTagGradientCss.

PUSHES TO

  • DOM consumers via rarityGradientCss / weaponTagGradientCss / CARD_SHADOW_CSS (React card components).
  • Canvas consumers via paintCardGradient — writes a linear gradient fill to the supplied CanvasRenderingContext2D.

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, and MedicalShadow.panel only conceptually in comments; the consumers wire those tokens.
  • Animate any effect — RARITY_EFFECT_TIER is 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.tsdrawRewardCardImpl (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). paintCardGradient subtracts 90° before converting to radians so the same angleDeg value 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 CanvasRenderingContext2D in 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 panelRaised surface, 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.