A dame.is & pds.dad production bsky38.com

Give the ballot one home, and the campaign page a way in #85

Merged dame.is
targeting main from deleted fork:feat/ballot-campaign-ux
Merged opened by and assigned to dame.is
targeting main from deleted fork:feat/ballot-campaign-ux

Two pages claimed the word "ballot" and the louder one was wrong: /vote's heading said "Your ballot" over a meter and a search field, while the ballot itself — the picks, the shareable card — sat on /me. A voter who wanted to know who they had already chosen had to leave the page that promised it.

The ballot moves to the page that casts it. /vote grows an "On my ballot" section between the caster and the browser, so the page answers the questions in the order they are asked: how do I add one, what do I have, who else should I consider. It is skipped at nought votes, where the meter has just said the same thing and the field to fix it is directly above. All of it is a new MyBallot component rather than two hundred transcribed lines.

/me keeps what was actually there once the ballot left — how your page looks to voters, and whether you are in the running — and is renamed "My campaign". That page was undiscoverable before: on desktop the ballot was not in the tab row at all, and the campaign page was a 100px tile behind an unlabelled avatar square. Now the bar carries four tabs at every width (the header has a fixed 736px of content and four labels fit; a fifth does not), the account square has a caret behind a rule so it reads as a menu, and the campaign page is shown rather than linked — the face, the line, the blank state — because a page you can see is empty is one you want to fill in.

Three nudges key off one myCampaign.blank getter, which stays false until the repo has been read so nobody is told their page is empty on the strength of nobody having looked. They end themselves: write a line and they stop.

The participation card now renders in every state. It used to answer only the two where there was something to press, so an account nobody had voted for got no card at all, and unknown — where a failed check lands — rendered its error message inside the card that did not render. A did with no votes gets a working "Keep me out of the running": the server has always accepted it, and the two new tests pin that guarantee. Refresh now re-reads all three sources; it used to refresh the votes alone, so being voted for with the page open left it saying you were not in the running until a full reload.

Smaller things that were feeding the confusion: the ballot glyph sat on the Vote tab while the person glyph sat on "My ballot", so the mark for a ballot pointed at the one page without one. The share card's kicker said "MY BALLOT" inside a page that said it three more times, and told a screenshot's reader nothing; it is the voter's handle now.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

Commits 14

Say what the uncounted list is
Cut the commentary back to what the code cannot say itself
Name the bar's tab for the verb again
Ask for the ballot's profiles in one request, not ten
Send a logout home rather than into the login form
Send signed-out readers to the login page, not to a card about it
Take a vote off the ballot by tapping its slot
Give the provider buttons an edge in the dark, and Blacksky its neon
Put the handle under the ballot's title, not under its slots
Cut the ballot's second caption, and lift Share above the removal list
Bring back the slot grid as the ballot's picture
Make the ballot tab one compact list
Split the ballot card into casting and what has been cast
Give the ballot one home, and the campaign page a way in