3.3 KiB
Pico CSS — Gotchas & Notes
[role=button] clobbers --pico-color
Pico's stylesheet includes this rule:
[role=button], [type=button], button, ... {
--pico-color: var(--pico-primary-inverse);
...
}
In light mode, --pico-primary-inverse is #fff. This means any element with role="button" will have white text by default, regardless of its background. Children inherit this, making text invisible on white/light card backgrounds.
Fix: On the element itself, reset the variable and set an explicit color:
.your-element {
--pico-color: initial;
color: var(--pico-contrast); /* #181c25 light / #dfe3eb dark — always legible */
}
Alternative: Remove role="button" and use tabindex="0" + keyboard handlers only, avoiding the Pico button rule entirely. Only do this if button semantics aren't needed for accessibility.
Plain buttons disappear on hover in light/Dan mode
Pico's button hover rule sets --pico-color: var(--pico-primary-inverse) (i.e. #fff) on hover, making the text white. If your button has a transparent or white background — e.g. a "plain" / ghost-style button — the text becomes invisible.
This affects any <button> element, not just [role=button]. The plain class (or similar resets) strips Pico's background/border, but does not prevent Pico from overriding the text color on hover.
Symptom: button text vanishes on hover in light or Dan mode, visible in dark mode (where the background is already dark).
Fix: Use !important on hover to override Pico's specificity, and also explicitly reset the background so you're sure nothing sneaks in:
.my-plain-button:not(:disabled):hover {
color: var(--pico-color) !important;
background: transparent;
}
Or, if you want a specific non-body text color on hover:
.my-plain-button:not(:disabled):hover {
color: var(--pico-contrast) !important; /* always legible */
background: transparent;
}
Do not rely on Pico's hover cascade for plain/ghost buttons — always override explicitly.
Palette overrides must be scoped to light mode
Pico defines its palette twice: once for light (:root:not([data-theme=dark])) and once for dark (@media (prefers-color-scheme: dark) + [data-theme=dark]). If you override Pico vars at bare :root, you'll clobber the dark mode values.
Wrong:
:root {
--pico-color: #2C2C2C; /* kills dark mode */
}
Correct:
@media (prefers-color-scheme: light) {
:root:not([data-theme]) { --pico-color: #2C2C2C; }
}
[data-theme=light] { --pico-color: #2C2C2C; }
Theme toggling pattern
Set data-theme="light" or data-theme="dark" on <html>. Pico reads this attribute for its palette selectors. Persist to localStorage. Read OS preference (prefers-color-scheme) on first visit when no stored value exists.
--pico-card-background-color in light mode
In light mode, Pico sets:
--pico-card-background-color: var(--pico-background-color) → #fff
If you want custom card backgrounds, override --pico-card-background-color directly (scoped to light mode only — see above).
CSS variables that Pico does NOT define
--color-label(app-defined) — must be set for both light and dark explicitly- Any
--shadow-*vars — define at:root(theme-neutral)