blades✨
Minimal CSS Framework for Semantic HTML
Blades CSS is a modern drop‑in successor to Pico CSS.
Just switch pico.css to blades.css— and voilà!
Quick start
There are 4 ways to get started:
A. Install manually
Download CSS archive and link css/blades.css in the <head> of your website.
<link rel="stylesheet" href="css/blades.css" />
B. Usage from CDN
<link rel="stylesheet" href="
https://cdn.jsdelivr.net/npm/@anyblades/blades@3/css/blades.min.css
"/>
Live example: https://github.com/pallets/website/blob/main/src/pallets/templates/layout.html
Full list of CSS files available on CDN: https://cdn.jsdelivr.net/npm/@anyblades/blades@3/css/
C. Install with NPM
npm install @anyblades/blades
Then, @import Blades into your CSS file:
@import "@anyblades/blades";
Alternatively, import only the parts you need, e.g. per page or per component. They come wrapped in @layer blades, so your unlayered styles win over them:
@import "@anyblades/blades/variables";
@import "@anyblades/blades/layout/document";
@import "@anyblades/blades/content/typography";
@import "@anyblades/blades/components/card";
@import "@anyblades/blades/components/modal";
variablesandlayout/documentare the minimum; keep the same order as src/pico/_index.css.- Some parts build on others, e.g.
components/modalexpectscomponents/card, andcomponents/groupandcomponents/dropdownexpectforms/basicsandcontent/button. - See Layered modules for the available parts, and for importing them from JavaScript in bundlers and component frameworks (SvelteKit, Next.js, Nuxt, Astro, …).
- See package.json for a list of all exported parts.
D. Official starters
- https://github.com/buildawesome-one/starter
- https://github.com/buildawesome-one/examples
- https://github.com/getgrav/grav-theme-quark2
- https://github.com/getgrav/grav-plugin-devtools
Starter HTML template
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="color-scheme" content="light dark" />
<link rel="stylesheet" href="css/blades.css" />
<title>Hello world!</title>
</head>
<body>
<main class="container">
<h1>Hello world!</h1>
</main>
</body>
</html>
Using Nunjucks, Twig or Liquid? Check out the built-in Base HTML templates for each engine, ready to be used in your project.
Documentation
If you know Pico — you already know Blades:
Dark mode without a flash
With the Pico-based builds, Blades follows the OS setting by default, with no JavaScript and no flash (see Dark scheme). To force a theme, set data-theme="light" or "dark" on <html>. On a nested element it works when the element paints its own background, such as <article>; otherwise add background-color: var(--pico-background-color); color: var(--pico-color);.
If you persist the user's choice, e.g. under the localStorage key theme, set the attribute before the first paint with a small inline script in <head>, e.g. in SvelteKit's src/app.html:
<script>
try {
const theme = localStorage.getItem("theme");
if (theme === "light" || theme === "dark") document.documentElement.dataset.theme = theme;
} catch {}
</script>
- Setting it later, e.g. in
onMountoruseEffect, renders the OS theme first and then switches. - In a Next.js App Router layout, pass the same code as a template literal,
<script dangerouslySetInnerHTML={{ __html: `try { … } catch {}` }} />, and addsuppressHydrationWarningto<html>, because the attribute changes before hydration.
Compact sizing (opt-in)
Add <html data-compact> for the whole page, or put the attribute on a single element, e.g. <form data-compact> for one form. Without the attribute nothing changes.
- Where it ships:
- included in
blades.css - also in
blades.layered.css - with per-part imports, add
@anyblades/blades/compactafter the other parts - with
pico.conditional.css, add@anyblades/blades/src/compactafter it
- included in
- What changes:
- smaller control padding and 1.5rem block spacing
- a 0.5rem radius and a 3px focus ring
- 500-weight labels and buttons, 600-weight headings
- a white field background in light mode
- a 1px card ring instead of the blurred shadow, with no grey card header/footer bands
- Root font size: on
<html>, it also keeps the root font size at 100% instead of growing with the viewport. At 1280px wide, controls are then 38px instead of 62px. On a single element they are about 48px, because everything is sized inremand the root stays as it is. - Field text size: form controls stay at 16px, because below that iOS Safari zooms in on focus.
Trusted by
- 🐍 Flask & Jinja community website ↗
- Grav CMS v2 default theme ↗
- FastHTML Pico components ↗