CustomizationThemes

Pick one of 20 color themes, build one from a single color with the CLI or the @pico config, or derive one at run time with relative color syntax.

On this page

A theme sets the primary color tokens. Everything else, the neutral light and dark colors included, stays the same. Azure is the default.

Ready-made themes

Twenty themes ship as separate files: amber, azure, blue, cyan, fuchsia, green, grey, indigo, jade, lime, orange, pink, pumpkin, purple, red, sand, slate, violet, yellow and zinc. Each swatch below shows the primary background of a theme, with the text color that sits on it.

The 20 themes
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
  <button type="button" style="background-color: #ffbf00; border-color: #ffbf00; color: #000000;">amber</button>
  <button type="button" style="background-color: #0172ad; border-color: #0172ad; color: #ffffff;">azure</button>
  <button type="button" style="background-color: #2060df; border-color: #2060df; color: #ffffff;">blue</button>
  <button type="button" style="background-color: #047878; border-color: #047878; color: #ffffff;">cyan</button>
  <button type="button" style="background-color: #c1208b; border-color: #c1208b; color: #ffffff;">fuchsia</button>
  <button type="button" style="background-color: #398712; border-color: #398712; color: #ffffff;">green</button>
  <button type="button" style="background-color: #ababab; border-color: #ababab; color: #000000;">grey</button>
  <button type="button" style="background-color: #524ed2; border-color: #524ed2; color: #ffffff;">indigo</button>
  <button type="button" style="background-color: #007a50; border-color: #007a50; color: #ffffff;">jade</button>
  <button type="button" style="background-color: #a5d601; border-color: #a5d601; color: #000000;">lime</button>
  <button type="button" style="background-color: #d24317; border-color: #d24317; color: #ffffff;">orange</button>
  <button type="button" style="background-color: #d92662; border-color: #d92662; color: #ffffff;">pink</button>
  <button type="button" style="background-color: #ff9500; border-color: #ff9500; color: #000000;">pumpkin</button>
  <button type="button" style="background-color: #9236a4; border-color: #9236a4; color: #ffffff;">purple</button>
  <button type="button" style="background-color: #c52f21; border-color: #c52f21; color: #ffffff;">red</button>
  <button type="button" style="background-color: #ccc6b4; border-color: #ccc6b4; color: #000000;">sand</button>
  <button type="button" style="background-color: #525f7a; border-color: #525f7a; color: #ffffff;">slate</button>
  <button type="button" style="background-color: #7540bf; border-color: #7540bf; color: #ffffff;">violet</button>
  <button type="button" style="background-color: #f2df0d; border-color: #f2df0d; color: #000000;">yellow</button>
  <button type="button" style="background-color: #646b79; border-color: #646b79; color: #ffffff;">zinc</button>
</div>

Use the theme menu in the header of this site to switch the stylesheet of every live example on the page.

The azure theme is the plain file. Any other theme adds its name before .min.css.

HTML
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@rusticstack/pico-reforged@2/css/pico.jade.min.css" />

The file names are the ones of Pico CSS v2.1.1. The two classless conditional builds have no azure file, as in v2.1.1, so for azure build them yourself. Every other combination exists in all 20 themes.

BuildAzure (default)Jade
Classpico.min.csspico.jade.min.css
Classlesspico.classless.min.csspico.classless.jade.min.css
Conditional (scoped to .pico)pico.conditional.min.csspico.conditional.jade.min.css
Classless conditionalnone, build itpico.classless.conditional.jade.min.css
Fluid classlesspico.fluid.classless.min.csspico.fluid.classless.jade.min.css
Fluid classless conditionalnone, build itpico.fluid.classless.conditional.jade.min.css

Drop the .min for the readable file. See Building a custom bundle for the builds that have no ready-made file.

Pick a theme with the CLI or the config

Terminal
npx pico-reforged themes
npx pico-reforged build --theme jade --minify --out public/pico.css

In a Vite or PostCSS project, name it in the stylesheet that imports the package.

CSS
@import "@rusticstack/pico-reforged";

@pico {
  theme: jade;
}

A theme from one color

The compiler turns one color into a full 19-step OKLCH scale. It keeps the hue and chroma of your color, borrows the lightness curve of the azure scale, and clamps the chroma to what the sRGB screen can show. Then it maps the steps onto the primary tokens like a built-in theme. The text color on the primary background is black or white, whichever has more contrast.

With the CLI

Terminal
npx pico-reforged build --theme "#e5484d" --minify --out public/pico.css

--theme takes a theme name, a CSS color (a hex value, or rgb(), hsl(), oklch() and the like) or the path of a CSS file.

With the @pico config

With the Vite or PostCSS integration, the color goes next to the import.

CSS
@import "@rusticstack/pico-reforged";

@pico {
  theme: #e5484d;
}

A quoted path works too, relative to the stylesheet: theme: "./brand.css";. The compiler builds with the default theme first, then adds your file, so the file only needs the tokens you change.

With a config file or the API

pico.config.json, or a pico.config.ts that exports the options, takes the color or a hue and chroma pair.

TypeScript
// pico.config.ts
export default {
  theme: { color: "#e5484d" },
  // or: theme: { hue: 25, chroma: 0.19 },
  minify: true,
};
TypeScript
import { compile } from "@rusticstack/pico-reforged/compiler";

const { css } = await compile({ theme: { hue: 25, chroma: 0.19 }, minify: true });

A theme at run time

Without a build step, derive the primary tokens from one color with relative color syntax. Change --brand and every primary token follows. The slider below changes its hue.

Primary tokens from one brand color
<style>
  :root,
  [data-theme] {
    --brand: oklch(0.62 0.19 25);

    --pico-primary: light-dark(oklch(from var(--brand) 0.5 c h), oklch(from var(--brand) 0.75 c h));
    --pico-primary-background: oklch(from var(--brand) 0.5 c h);
    --pico-primary-underline: light-dark(
      oklch(from var(--brand) 0.5 c h / 0.5),
      oklch(from var(--brand) 0.75 c h / 0.5)
    );
    --pico-primary-hover: light-dark(oklch(from var(--brand) 0.42 c h), oklch(from var(--brand) 0.82 c h));
    --pico-primary-hover-background: light-dark(
      oklch(from var(--brand) 0.45 c h),
      oklch(from var(--brand) 0.56 c h)
    );
    --pico-primary-focus: light-dark(
      oklch(from var(--brand) 0.62 c h / 0.5),
      oklch(from var(--brand) 0.75 c h / 0.375)
    );
    --pico-primary-inverse: oklch(1 0 0);
    --pico-text-selection-color: light-dark(
      oklch(from var(--brand) 0.62 c h / 0.25),
      oklch(from var(--brand) 0.75 c h / 0.2)
    );
  }
</style>

<label>
  Hue
  <input type="range" id="hue" min="0" max="360" value="25" />
</label>
<p>
  <button>Primary</button>
  <button class="outline">Outline</button>
  <a href="#brand">A link</a>
</p>
<input type="text" placeholder="Focus this field" aria-label="Sample field" />

<script>
  document.getElementById("hue").addEventListener("input", (event) => {
    document.documentElement.style.setProperty("--brand", "oklch(0.62 0.19 " + event.target.value + ")");
  });
</script>

The same page is in the repository as examples/custom-theme.html. This only restyles the tokens that a theme sets, so the secondary and contrast colors stay neutral.

Two features carry the browser requirements here. Relative color syntax is native in Chrome and Edge 119, Firefox 128 and Safari 16.4, and light-dark() in your own CSS in Chrome and Edge 123, Firefox 120 and Safari 17.5. Your own stylesheet is not compiled, so a browser below those versions drops the declaration. Pico Reforged itself supports Chrome and Edge 111, so if you need the whole floor, make the theme with the CLI. The compiler lowers light-dark() and OKLCH for the floor in the files it builds. See Browser support.

Override tokens in CSS

A theme is only a set of custom properties, so you can also override them by hand. Your CSS is not in a cascade layer, so it wins over the theme file.

Overriding tokens on one element
<article style="--pico-primary-background: oklch(0.55 0.2 25); --pico-primary-border: oklch(0.55 0.2 25); --pico-primary-hover-background: oklch(0.48 0.2 25); --pico-primary-hover-border: oklch(0.48 0.2 25); --pico-border-radius: 1rem;">
  <h3>A red, rounder card</h3>
  <p>The tokens are scoped to this element only.</p>
  <button>Primary</button>
</article>

Some tokens are aliases of others, and an alias is resolved where it is declared, on :root. --pico-primary-border is var(--pico-primary-background) there, so when you change the background on a single element you must set the matching border token too, as above (and --pico-primary-hover-border for the hover state). On :root the alias follows your override, so one declaration is enough.

Tokens a theme sets

--pico-primary
light-dark(#0172ad, #01aaff)
Links and accents.
--pico-primary-background
#0172ad
Primary button background.
--pico-primary-underline
light-dark(oklch(0.53 0.1254 242 / 0.5), oklch(0.707 0.1679 242.1 / 0.5))
Link underline color.
--pico-primary-hover
light-dark(#015887, #79c0ff)
Link color on hover.
--pico-primary-hover-background
light-dark(#02659a, #017fc0)
Primary button background on hover.
--pico-primary-focus
light-dark(#017fc0, #01aaff)
Focus ring color.
--pico-primary-inverse
#ffffff
Text on a primary background.
--pico-text-selection-color
light-dark(oklch(0.658 0.1564 242.2 / 0.25), oklch(0.707 0.1679 242.1 / 0.1875))
Background of selected text.
--pico-switch-thumb-box-shadow
0 0.0625rem 0.125rem 0 oklch(0 0 0 / 0.2)
Shadow of the switch thumb.

Nested schemes

Theme tokens are declared on :root, :host and every [data-theme] element, so a dark section inside a light page recomputes its own colors. See Color schemes.