Files
dickloads/PICO_CSS.md

3.8 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/ghost buttons disappear on hover in light/Dan mode

Pico's button hover rule sets both CSS custom properties and computed values:

button:is(:hover,:active,:focus) {
  --pico-background-color: var(--pico-primary-hover-background);
  --pico-border-color:     var(--pico-primary-hover-border);
  --pico-color:            var(--pico-primary-inverse);  /* #fff in light mode */
}

If your button has a transparent or white background — e.g. a ghost/plain-style button — the text becomes invisible because --pico-primary-inverse is #fff in light mode.

Important: Overriding only color: ... !important is not enough, because Pico sets --pico-color as a CSS variable that other rules may read. You must override all three custom properties and the corresponding computed properties simultaneously.

Symptom: ghost button text vanishes on hover in light or Dan mode (background goes blue, text goes white). Works fine in dark mode because the dark background makes white text readable.

Fix: Override all Pico button hover variables explicitly:

.my-ghost-button:not(:disabled):hover {
  --pico-color:            var(--pico-contrast) !important; /* near-black in light, near-white in dark */
  --pico-background-color: transparent !important;
  --pico-border-color:     var(--pico-contrast) !important;
  color:                   var(--pico-contrast) !important;
  background:              transparent !important;
  border-color:            var(--pico-contrast) !important;
}

Use --pico-contrast (not --pico-color) as the hover text color — it's #181c25 in light mode and #dfe3eb in dark mode, so it's always legible against the page background regardless of theme.

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)