Declutter the challenge panel and obstacle titles (v4)

Per playtest feedback:
- Drop the per-challenge 'Plays:' line — redundant with the card column and the
  event log (also removes the now-unused chPlays const).
- Drop the 'Attempted by … drag a card … assist' how-to paragraph; players know
  the basics, and the challenge header already names who's challenged.
- Replace the 'Red/Black Obstacle · match to draw' tag with the original card in
  the title (e.g. '5♣ — Loitering Mermaid'), and color the title by suit. The
  left border already marks color; the dead .obstacle-color-tag CSS is removed.

Suit glyphs in the title use plain text symbols (not SUIT_EMOJI) so they take
the theme-aware suit color and stay readable on the dark panel. Verified in
light and dark.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 09:56:07 -07:00
parent 922fc38c0a
commit e4c78391fc
4 changed files with 21 additions and 58 deletions

View File

@@ -241,43 +241,6 @@
box-shadow: 0 0 0 2px var(--danger); box-shadow: 0 0 0 2px var(--danger);
} }
/* Obstacle color (red/black) — fixed by the original card; matching it draws back */
.obstacle-color-tag {
display: inline-flex;
align-items: center;
gap: 0.35rem;
margin-bottom: 0.4rem;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.03em;
padding: 0.12rem 0.5rem;
border-radius: 999px;
cursor: help;
}
.obstacle-color-tag::before {
content: '';
width: 0.6rem;
height: 0.6rem;
border-radius: 50%;
display: inline-block;
}
.obstacle-color-tag.red {
color: var(--suit-red);
background: color-mix(in srgb, var(--suit-red) 14%, transparent);
}
.obstacle-color-tag.red::before { background: var(--suit-red); }
.obstacle-color-tag.black {
color: var(--suit-black);
background: color-mix(in srgb, var(--suit-black) 14%, transparent);
}
.obstacle-color-tag.black::before { background: var(--suit-black); }
.suit-badge { .suit-badge {
font-weight: 900; font-weight: 900;
font-size: 1.1rem; font-size: 1.1rem;
@@ -298,6 +261,13 @@
font-size: 1.15rem; font-size: 1.15rem;
} }
/* The title and its "K♦" card-code prefix take the Obstacle's fixed suit color
(red border on the left already marks color, so the old tag is gone). */
.suit-h .obstacle-details h4, .suit-d .obstacle-details h4 { color: var(--suit-red); }
.suit-c .obstacle-details h4, .suit-s .obstacle-details h4 { color: var(--suit-black); }
.obs-card-code { opacity: 0.8; }
.obstacle-details .in-challenge-tag { .obstacle-details .in-challenge-tag {
color: var(--deep); color: var(--deep);
font-size: 0.8rem; font-size: 0.8rem;

View File

@@ -95,7 +95,6 @@
{/if} {/if}
{#each openChallenges as ch} {#each openChallenges as ch}
{@const chPlays = JSON.parse(ch.plays || '[]')}
{@const chObstacleIds = JSON.parse(ch.obstacle_ids || '[]')} {@const chObstacleIds = JSON.parse(ch.obstacle_ids || '[]')}
<div class="challenge-item"> <div class="challenge-item">
<div class="challenge-head"> <div class="challenge-head">
@@ -127,10 +126,6 @@
Temporary Obstacle: <strong use:tooltip={{ html: cardTooltipHtml(ch.temp_card, $obstacleTable) }}>{getCardDisplay(ch.temp_card)}</strong>{playerName(ch.acting_player_id)} must answer it! Temporary Obstacle: <strong use:tooltip={{ html: cardTooltipHtml(ch.temp_card, $obstacleTable) }}>{getCardDisplay(ch.temp_card)}</strong>{playerName(ch.acting_player_id)} must answer it!
</p> </p>
{:else} {:else}
<p class="info-text" style="margin: 0.5rem 0 0 0;">
Attempted by <strong>{playerName(ch.acting_player_id)}</strong>{ch.acting_player_id !== ch.target_player_id ? ` (took it over via ${ch.tax_type === 'gat' ? 'Gat' : 'Name'} Tax)` : ''}.
Drag a card onto an Obstacle below to play it. Other Pi-Rats may assist (assistants don't draw back).
</p>
<div class="challenge-obstacles"> <div class="challenge-obstacles">
{#each chObstacleIds as oid} {#each chObstacleIds as oid}
{@const obs = state.obstacles.find(o => o.id === oid)} {@const obs = state.obstacles.find(o => o.id === oid)}
@@ -142,12 +137,6 @@
{/each} {/each}
</div> </div>
{/if} {/if}
{#if chPlays.length > 0}
<p class="info-text" style="margin: 0.5rem 0 0 0; font-size: 0.85rem;">
Plays: {chPlays.map(p => `${p.player_name}: ${getCardDisplay(p.card)} ${p.success ? '✔' : '✘'}`).join(' · ')}
</p>
{/if}
<!-- Tax: pending request aimed at me --> <!-- Tax: pending request aimed at me -->
{#if ch.tax_state === 'requested'} {#if ch.tax_state === 'requested'}
{#if myTaxRequest && myTaxRequest.id === ch.id} {#if myTaxRequest && myTaxRequest.id === ch.id}

View File

@@ -1,6 +1,6 @@
<script> <script>
import { apiRequest } from '../../lib/api'; import { apiRequest } from '../../lib/api';
import { isJoker } from '../../lib/cards'; import { isJoker, cardValue } from '../../lib/cards';
import { tooltip } from '../../lib/tooltip'; import { tooltip } from '../../lib/tooltip';
import Card from '../Card.svelte'; import Card from '../Card.svelte';
@@ -8,9 +8,9 @@
export let obs; export let obs;
export let embedded = false; // rendered inside the Challenge panel — skip the "in challenge" flag export let embedded = false; // rendered inside the Challenge panel — skip the "in challenge" flag
const COLOR_HINT = "An Obstacle's color is fixed by its original card and never changes, no matter what's played on it. " // Plain (text-presentation) suit glyphs so they inherit the title's suit color,
+ 'Beat the difficulty with a card of any suit to pass — but play a card of the matching color (red/black) ' // unlike SUIT_EMOJI which renders as fixed-color emoji.
+ 'to also draw one back into your hand.'; const SUIT_CHAR = { C: '♣', S: '♠', H: '♥', D: '♦' };
const VALUE_HINT = 'The number to beat. Set by the most recent card played on the column — or the original card ' const VALUE_HINT = 'The number to beat. Set by the most recent card played on the column — or the original card '
+ 'until one is played. (Color stays fixed to the original.)'; + 'until one is played. (Color stays fixed to the original.)';
@@ -22,7 +22,7 @@
$: success_count = column_cards.filter(c => c.success).length; $: success_count = column_cards.filter(c => c.success).length;
$: is_completed = success_count >= state.players.length; $: is_completed = success_count >= state.players.length;
$: in_challenge = openChallenges.some(c => JSON.parse(c.obstacle_ids || '[]').includes(obs.id)); $: in_challenge = openChallenges.some(c => JSON.parse(c.obstacle_ids || '[]').includes(obs.id));
$: is_red = ['H', 'D'].includes(obs.suit); $: cardCodeLabel = `${cardValue(obs.original_card)}${SUIT_CHAR[obs.suit] || ''}`;
$: is_face_active = ['J', 'Q', 'K'].includes(active_card_code.slice(0, -1)); $: is_face_active = ['J', 'Q', 'K'].includes(active_card_code.slice(0, -1));
// A face-card Obstacle is worth the challenged Rat's Rank. When this Obstacle is // A face-card Obstacle is worth the challenged Rat's Rank. When this Obstacle is
@@ -82,10 +82,7 @@
{/each} {/each}
</div> </div>
<div class="obstacle-details"> <div class="obstacle-details">
<h4>{obs.title} {#if in_challenge && !embedded}<span class="in-challenge-tag">⚔️ In Challenge</span>{/if}</h4> <h4><span class="obs-card-code">{cardCodeLabel}</span>{obs.title} {#if in_challenge && !embedded}<span class="in-challenge-tag">⚔️ In Challenge</span>{/if}</h4>
<span class="obstacle-color-tag {is_red ? 'red' : 'black'}" use:tooltip={COLOR_HINT}>
{is_red ? 'Red' : 'Black'} obstacle · match to draw
</span>
<p class="desc">{obs.description}</p> <p class="desc">{obs.description}</p>
</div> </div>

View File

@@ -6,10 +6,17 @@
// - Add a CHANGELOG entry only when a commit changes something players can // - Add a CHANGELOG entry only when a commit changes something players can
// see. Skip refactors, tests, and tooling. Keep wording player-facing. // see. Skip refactors, tests, and tooling. Keep wording player-facing.
export const VERSION = 3; export const VERSION = 4;
// Newest first. Each entry: { version, date: 'YYYY-MM-DD', changes: [string, ...] }. // Newest first. Each entry: { version, date: 'YYYY-MM-DD', changes: [string, ...] }.
export const CHANGELOG = [ export const CHANGELOG = [
{
version: 4,
date: '2026-06-15',
changes: [
'Tidied the Challenge panel: dropped the redundant “Plays” list and the how-to-play blurb. Each Obstacle now leads with its card (e.g. “K♦”) in the title, colored by suit.',
],
},
{ {
version: 3, version: 3,
date: '2026-06-15', date: '2026-06-15',