diff --git a/src/components/blocks/VoteWidget.astro b/src/components/blocks/VoteWidget.astro new file mode 100644 index 0000000..a3b99ad --- /dev/null +++ b/src/components/blocks/VoteWidget.astro @@ -0,0 +1,146 @@ +--- +// VoteWidget — the "which draft would you ship?" reader poll. +// +// Static markup only. The fetch to vote-service, the localStorage voter id, +// and the click handler are task 16's job. The button toggle state lives +// here so the layout matches the legacy page at first render. +// +// CSS hooks asserted by scripts/verify.mjs that must survive in the new +// architecture: +// .vote-widget — the outer wrapper +// .vote-buttons — the button row +// [aria-pressed=true] — the selected state +// +// The aria-label / role="group" on the inner cluster carries the state to +// assistive tech — colour alone is not enough. This is asserted in the +// task brief and lives in the same hook surface. + +interface VoteTally { + original: number; + improved: number; +} + +interface Props { + /** Skill id this widget votes for. */ + skillId: string; + /** Current vote tallies. Zeros render as "0 · 0%". */ + tally: VoteTally; + /** The current visitor's vote, if any. */ + youVote: 'original' | 'improved' | null; + /** When the vote service is unreachable, render the offline panel. */ + unavailable?: boolean; +} + +const { skillId, tally, youVote, unavailable = false } = Astro.props; +const total = (tally.original || 0) + (tally.improved || 0); +const share = (count) => (total ? Math.round((count / total) * 100) : 0); +--- + +{ + unavailable ? ( +
+ READER VOTE +

Voting is offline right now — the vote service is not configured or unreachable.

+
+ ) : ( +
+ WHICH DRAFT WOULD YOU SHIP? +
+ + +
+

+ {youVote + ? `You voted ${youVote === 'original' ? 'original' : 'improved draft'}. Pick the other option to change it.` + : 'One vote per visitor, tracked by network source.'} +

+
+ ) +} + +