<tagbrush>

A tiny, no-build CSS library that makes plain semantic HTML look clean and modern. One file. No classes required. Themeable with CSS variables, with dark mode built in.

Get started View on GitHub
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tagbrush@1/tagbrush.min.css">

Why TagBrush.css?

No build step

Plain CSS. No PostCSS, no bundler, no config. Drop in a <link> and you're done.

Classless by default

Headings, forms, tables, buttons and code blocks are styled out of the box. Write HTML, not class soup.

Themeable with variables

Colors, fonts, spacing and radius are semantic CSS custom properties. Override a few to make it yours.

Easy to override

Everything lives in a @layer, so your own CSS always wins. No !important, no specificity battles.

Dark mode built in

Follows the visitor's system setting automatically, or force it with data-theme="dark" on any element.

Tiny

About 7 KB gzipped, with zero dependencies and zero JavaScript.

Works with any framework

Import it once in your layout and every page picks it up. Astro, Next.js, React, Vue, Nuxt, Angular, SvelteKit, or a plain static site.

Open source

Apache 2.0 licensed. Fork it, tweak it, ship it.

Build your theme

Every component on this page reads from a handful of CSS variables. Change them here and the whole page updates live, then copy the CSS into your site or share a link to your theme. Hover colors, focus rings, and button text are derived from --color-primary automatically: buttons switch between white and black text, and links are adjusted to stay readable.

A link
/* Using the defaults */

Dark mode

TagBrush.css follows the visitor's system preference. Use the switcher at the top of the page, or set data-theme on any element to force a scheme for just that part of the page:

data-theme="light"

Always light, whatever the system setting.

data-theme="dark"

Always dark, whatever the system setting.

What you get

Everything below is plain HTML with no classes, styled entirely by TagBrush.css.

Typography

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6

Body text is set in your system font with a comfortable line height. It supports links, bold, italics and inline code.

Inline elements are covered too: highlighted text, keyboard shortcuts like Ctrl + K, CSS abbreviations, H2O, E = mc2, sample output like Build succeeded, and small print.

Edits show up as removed and inserted text, outdated text is muted, a definition stands out, and inline quotes get curly quote marks. 42 is an <output>.

A heading with a subtitle

Wrap them in <hgroup> and the subtitle is muted automatically.

Blockquotes are great for callouts, notes, and quotes worth highlighting.

Someone wise
Kovir Labs
123 Example Street
hello@example.com
// Code blocks scroll horizontally when they overflow, and highlighted
// code from Prism, highlight.js, Hugo, Jekyll, or Shiki gets theme colors
function greet(name) {
  console.log(`Hello, ${name}!`);
  return 42;
}

Lists

  • Unordered item
  • Another item
    • Nested item
    • Nested item
  • Last item
  1. First step
  2. Second step
  3. Third step
Description list
Terms and their definitions.
Classless
No class names needed.

Navigation and menus

Lists inside <nav> become a horizontal row. Mark the current page with aria-current="page":

Label a <nav> "Breadcrumb" and its list gets separators:

Use <menu> for a toolbar of actions:

Articles and asides

An article

Long-form content goes in an <article>. An <aside> inside it becomes a callout:

Media

A blue to violet gradient
Images scale down to fit their container. Wrap them in <figure> to add a caption.
Inline SVG scales down the same way and can use theme variables, so it follows your colors and dark mode.

Buttons

Link styled as button Disabled link

Set aria-busy="true" while something loads. It shows a spinner and tells screen readers to wait:

Loading results…

Wrap buttons in <div role="group"> to lay them out in a row. It works for inline forms too:

Forms

Contact us
Help text: put a <small> right after any field.
More inputs
Please enter a full URL, including https://
That username is available.

Progress and meter

70%

Meters turn green, amber, or red based on their low, high and optimum attributes:

40%
70%
95%

Disclosure and dialogs

What is a <details> element?

A native, accessible collapsible section with no JavaScript. Great for FAQs.

Neighboring <details> join into an accordion. Give them the same name so only one opens at a time:

Does it need any classes?

Nope. Just write the HTML.

Does it need JavaScript?

No. The browser handles opening and closing.

Does it work with a keyboard?

Yes, and with screen readers.

Native popover. Opened with the popovertarget attribute, no JavaScript needed. Click outside or press Esc to close.

Native <dialog>

Opened with showModal(). A <header> with a button puts the close button beside the title, and a <footer> lines its buttons up on the right. Press Esc to close.

Article grids

A <section> of two or more <article>s becomes a grid of cards, with no classes. Headings and paragraphs in the section span the full width:

Blog posts

A list of posts on a home page.

Products

Plans, products, or pricing tiers.

Features

Anything you'd show side by side.

App patterns

Dashboards and admin pages need no classes either. A <dl> of <div>s becomes stat tiles, a <header> with a heading and a button puts them on one row, and <search> lays out filters. An <aside> next to <main> becomes a sidebar layout.

Orders

Revenue
$84,312
+12.4% vs. last month
Orders
1,148
Conversion
2.86%

Layout helpers

A few optional classes cover the layouts almost every site needs: .grid for responsive columns, .card for boxed content, and role="group" for rows of buttons.

.card

Cards sit in a .grid that wraps to one column on small screens.

Tunable

Set --grid-min-column on a grid to change when it wraps.

Themeable

--card-padding and --card-radius adjust every card at once.

Tables

A few of the theme variables. Wrap a table in a figure to make it scroll on small screens.
Variable Default Used for
--color-primary#2563ebLinks, buttons, focus states
--color-surfacelight gray, tinted by the primaryCode, blockquotes, cards
--font-family-basesystem-ui, …All body text
--border-radius0.375remButtons, inputs, code
See the README for the full list.

Get started

1. Add the stylesheet

Use the CDN, or download tagbrush.css and host it yourself.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tagbrush@1/tagbrush.min.css">

2. Write semantic HTML

<header><nav><strong>My site</strong> <a href="/about">About</a></nav></header>
<main>
  <h1>Hello, world</h1>
  <p>It already looks good.</p>
  <button>Click me</button>
</main>
<footer>© 2026 Me</footer>

No classes needed: the content is centered and the footer stays at the bottom of short pages. Or start from starter.html, a complete page to copy.

3. Make it yours (optional)

<style>
  :root {
    --color-primary: #10b981;
    --font-family-base: "Inter", sans-serif;
    --border-radius: 0.5rem;
  }
</style>

Because TagBrush.css lives in its own cascade layer, any rule you write wins, even a plain button { … } selector. No !important needed.