Poll
A question, a few answers, and how the votes fell once one is chosen.
On this page
A poll is a question, a few answers, and how the votes fell. Each answer is a <label> around a native radio, so the group keeps the arrow keys and a form keeps the value. Before a vote the answers are flat faces on one contour. Once one is chosen, every answer fills from its start with a tint as long as its share and its figure appears at the end. The checked input is all that drives it: there is no script.
Poll
<div class="card w-full max-w(360)"> <fieldset class="poll"> <legend class="poll-title">Which theme should be the default?</legend> <label class="poll-option" style="--value: 58"> <input class="poll-input" type="radio" name="theme" value="moonstone"> <span class="poll-label">Moonstone</span> <span class="poll-value">58%</span> </label> <label class="poll-option" style="--value: 31"> <input class="poll-input" type="radio" name="theme" value="obsidian"> <span class="poll-label">Obsidian</span> <span class="poll-value">31%</span> </label> <label class="poll-option" style="--value: 11"> <input class="poll-input" type="radio" name="theme" value="system"> <span class="poll-label">Follow the system</span> <span class="poll-value">11%</span> </label> <p class="poll-meta">214 votes · 2 days left</p> </fieldset></div>#How the result shows
Each poll-option carries its share of the votes as --value, from 0 to 100, and the same figure as text in poll-value. While no input is checked, the shares have no width and the figures are hidden, from assistive technology too. When an answer is checked, :has() sees it on the group: every share grows to its width and every figure fades in.
The chosen answer takes an ink contour, a stronger tint and a check after its words. Choosing another answer moves all three; the shares stay.
The page supplies the numbers. If the result should include the vote just cast, update --value and the text when the vote is recorded: the shares ease to their new widths.
#A poll that is over
Disable the inputs when voting has ended. The result shows from the start, nothing can be chosen, and the answers stay as legible as before, since a result is there to be read. Keep checked on the answer the reader chose.
Closed
<div class="card w-full max-w(360)"> <fieldset class="poll"> <legend class="poll-title">Which theme should be the default?</legend> <label class="poll-option" style="--value: 61"> <input class="poll-input" type="radio" name="theme-closed" value="moonstone" disabled> <span class="poll-label">Moonstone</span> <span class="poll-value">61%</span> </label> <label class="poll-option" style="--value: 29"> <input class="poll-input" type="radio" name="theme-closed" value="obsidian" disabled checked> <span class="poll-label">Obsidian</span> <span class="poll-value">29%</span> </label> <label class="poll-option" style="--value: 10"> <input class="poll-input" type="radio" name="theme-closed" value="system" disabled> <span class="poll-label">Follow the system</span> <span class="poll-value">10%</span> </label> <p class="poll-meta">380 votes · Final result</p> </fieldset></div>#Several answers
Use checkboxes when a reader may choose more than one. The result shows as soon as one is checked, each chosen answer takes the contour and the check, and the shares no longer add up to 100.
Several answers
<div class="card w-full max-w(360)"> <fieldset class="poll"> <legend class="poll-title">Which frameworks do you ship with?</legend> <label class="poll-option" style="--value: 72"> <input class="poll-input" type="checkbox" name="framework" value="react" checked> <span class="poll-label">React</span> <span class="poll-value">72%</span> </label> <label class="poll-option" style="--value: 34"> <input class="poll-input" type="checkbox" name="framework" value="svelte" checked> <span class="poll-label">Svelte</span> <span class="poll-value">34%</span> </label> <label class="poll-option" style="--value: 41"> <input class="poll-input" type="checkbox" name="framework" value="vue"> <span class="poll-label">Vue</span> <span class="poll-value">41%</span> </label> <label class="poll-option" style="--value: 18"> <input class="poll-input" type="checkbox" name="framework" value="none"> <span class="poll-label">None: plain HTML</span> <span class="poll-value">18%</span> </label> <p class="poll-meta">Choose all that apply</p> </fieldset></div>#Poll or choice
A poll asks a question whose answer is everyone's: choosing shows the result. A choice is an option of a form that explains itself in a sentence, and shows nothing afterwards. A result nobody votes on is a chart with chart-horizontal.
#Accessibility
- Use a
<fieldset>with the question in its<legend>, so the question is announced with each answer. Radios in it share aname. - Each answer is a
<label>around its input, so the whole face is the target, 38px tall and 44px on a touch screen. The focus ring is drawn around the answer. - The figures are text. They are hidden with
visibilityuntil the result shows, so they are not read early, and they are in the document for a reader who cannot see the tint. - The check is the sign of the chosen answer for a reader who does not see the contour; in forced colors the chosen answer takes the system highlight.
#Classes
| Class | Description |
|---|---|
poll | The group, on a fieldset or an element with role="group": answers 8px apart |
poll-title | The question, on the legend or a heading |
poll-option | One answer, on a label: a flat face 38px tall that fills from its start with a tint as long as its share; chosen, it takes an ink contour and a check |
poll-input | The native radio or checkbox inside the answer. It covers the answer and is not drawn |
poll-label | The answer's words |
poll-value | The answer's share as text, at the end; hidden until the result shows |
poll-meta | A quiet line under the answers: how many voted, how long is left |
#Custom properties
| Property | Default | Description |
|---|---|---|
--value | The answer's share of the votes, from 0 to 100. On poll-option | |
--poll-color | var(--color-surface-inverse) | The ink the shares are tinted with |
--poll-radius | var(--radius-control) | An answer's corner |