Buttons and roles
Every <button> in Pico is already styled: the primary color, rounded corners, padding, a visible focus ring. Variants come from a few classes, and special behavior from accessibility attributes: role tells the browser and screen readers what an element is, and aria-* tells them what state it is in.
Button variants
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
<button>Subscribe</button>
<button class="secondary">Later</button>
<button class="contrast">Resume</button>
<button class="outline">All projects</button>
<button class="outline secondary">Cancel</button>
<button disabled>Unavailable</button>The preview shows a blue, a grey and a dark button, two outlined buttons and a faded disabled one. Classes combine: outline secondary gives a grey outline.
| Class | When to use |
|---|---|
| no class | The main action on the screen |
secondary | A secondary action |
contrast | The action that must stand out most |
outline | A quieter version of any variant |
A link that looks like a button
When a button leads to another page, it is a link, not a <button>. The role="button" attribute gives the link a button look while it stays a link:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
<a href="#" role="button">Read the blog</a>
<a href="#" role="button" class="outline">Portfolio</a>Two links look like buttons. The same variant classes work here too.
Button group and loading
The role="group" attribute on a wrapper joins the buttons into one strip. The aria-busy="true" attribute shows a spinner — that is how a button says an action is in progress:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
<div role="group">
<button>All</button>
<button class="secondary">CSS</button>
<button class="secondary">HTML</button>
</div>
<button id="save">Save draft</button>
<script>
const save = document.querySelector('#save');
save.addEventListener('click', () => {
save.setAttribute('aria-busy', 'true');
save.textContent = 'Saving…';
setTimeout(() => {
save.removeAttribute('aria-busy');
save.textContent = 'Saved';
console.log('Draft saved');
}, 1500);
});
</script>At the top there are three filter buttons in one strip. Click "Save draft": for a second and a half a spinner turns in the button, then the label changes and a message appears under the example.
Tooltip
The data-tooltip attribute shows a tooltip on hover, and data-placement="bottom" moves it below:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
<p style="margin-top: 3rem">
<button data-tooltip="PDF file, 120 KB">Download resume</button>
</p>Hover over the button and a dark label with the text appears above it. The top margin gives the tooltip room in the preview.
Common mistake
Making a button out of a <div> with a click handler and role="button". It looks like a button but cannot be used from the keyboard: you cannot Tab to it and press Enter. Use <button> for actions, <a href> for navigation, and put role="button" only on links.