shadcn.io is not affiliated with official shadcn/ui
Quick Poll Dialog Block for React
A team meeting agenda with a one-question poll shown open: four large rows to vote on, then the results in the same space with bars, counts in words and shares that add to 100, and a way to change the vote.
Installation
Overview
Settle a small decision without leaving the meeting notes, with this quick-poll dialog for React with Base UI. It opens from the agenda of a Lunchbell team meeting, a team lunch planner: the page's header holds an outline Vote Button, and the agenda, a list of numbered rows with one item, Decide: team lunch, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The Dialog asks where the team lunch should be and shows four large rows, Noodles, Salads, Tacos and Pizza, as one choice you move through with the arrow keys. Nothing else shows until you vote: the Vote button, the only Shine Button, swaps the rows for the results in the same space, a bar and a count in words for each place ("5 votes, 39%"), a check and Your vote on yours, and one line, "13 votes, closes in 2 days". Change vote takes your vote back out of the counts and brings the rows back. For preview the dialog is drawn open in place when the page loads; the header button opens the real Dialog, with the same rows and the same state. For team tools, meeting apps, community boards and any quick single-choice vote.
FAQ
Why is the dialog already open, and what do I keep?
So you can see it without a click. On load the dialog is a copy drawn in place over the page: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Vote button opens the real Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same PollBody component and share the block's state, so a choice made in the copy is there in the real one. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.
Why are there no results before I vote, and how do they hold the height?
So the answer isn't swayed by the count, and so the rows are the whole question. The rows and the results sit in one grid cell: the live one is in view and the other stays in place unseen, so the dialog is exactly as tall before and after the vote. Pressing Vote with nothing chosen says "Pick a place to vote." and puts focus on the rows; after a vote, focus goes to the results, which are named for the question.
How are the counts and shares worked out?
The place counts are typed, twelve votes in all, and your vote adds one to the place you chose, so the total is 13. Each share is the count over the total, and the four shares are rounded together by the largest-remainder rule, so they always add up to 100 (39, 23, 23 and 15 for Tacos, Noodles, Pizza and Salads). The count is written as words with the share ("5 votes, 39%"), the bar is a decoration with the same figures beside it, and in a right-to-left layout the bar still grows from the left. When two places tie for the lead, the line says so instead of naming one.
What does Change vote do, and what does the header button say afterwards?
It takes your vote out of the counts, so they go back to the typed twelve, and shows the rows again with your choice still selected, with a polite message that the vote was taken back. After you vote, the header button reads View results and opens the dialog on the results. Done closes the dialog and returns focus to that button.
Does it send my vote, and how do I wire it up?
No. The vote lives in this tab only: it is added to typed counts and a polite message says "Voted for Tacos in this demo" and "Nothing was sent". The drawn agenda row changes to "You voted for Tacos". In your app send the vote from submit, take the counts from the response (the server owns the tally), and keep the same swap. The closing day is worked out from two fixed dates, AS_OF and CLOSES, so replace them with your poll's own.
Is it accessible from the keyboard?
Yes. The real dialog is named by its question and described by its sentence, focus starts on the rows and stays inside until it closes, and Escape, Cancel and the close button all return focus to the header button. The trigger is passed to DialogTrigger through render, and the dialog is controlled with open and onOpenChange. The rows are native radio buttons in a radio group, so the arrow keys choose, the problem is announced as an alert, and one polite status message reports the result. The copy shown on load moves no focus; Escape inside it, Cancel and its close button close it and put focus on the header button.
Related
- Pro
Create Poll Dialog
Notes and editingA neighbourhood forum thread with a poll builder shown open: a question, two to six options, one answer or several, how long it stays open, and a live preview of the poll as voters will see it.
- Pro
Vote Results Bars Awards
Rankings and scoresHorizontal progress bars for six nominees sorted by votes, with a segmented control that swaps votes for share, a ringed winner row, a derived total and a fixed certification date.
- Pro
Meeting RSVP Dialog
Time and schedulingA team calendar's week with its reply dialog shown open: the event, a three-way Going, Maybe or Can't go answer, an optional note, and the guests by answer with their counts, moving as you choose.
- Pro
Feedback Rating Dialog
Feedback and reportsA help desk's ended chat with a one-tap satisfaction dialog shown open: five drawn faces from very bad to great that record the score at once, then an optional comment with a question that fits the score.
- Pro
Merge Accounts Dialog
Notes and editingA contacts page with its merge dialog shown open: two accounts that look like one person in a table, matching fields marked Same, a radio in each cell where they differ, a Result column that follows your choices, and Merge accounts waiting until every conflict is chosen.
- Pro
Quick Note Dialog
Notes and editingA sticky-note wall with its dialog shown open: a title, a note of up to 280 characters, an Idea, To-do, Reference or Later label, a reminder day on a calendar and a pin switch, and a new card that lands first on the wall.