Skip to content
PolyFish CSS

Documentation

Five files, one required. Start with installation, then read the module you're actually using.

Start here

Three ways in

Depending on whether you want to ship something today, understand the architecture, or look up a class.

Quick start

Three lines and a container

Core defines the tokens every other file reads, so it always loads first. Utilities loads last so its helpers win a specificity tie.

  • No npm, no bundler, no config file
  • Class names are prefixed pf- and collide with nothing
  • Fonts are optional — every stack falls back to a system face
index.htmlhtml
<link rel="stylesheet" href="/PolyFishCSS-Core.css?v=c0428852">
<link rel="stylesheet" href="/PolyFishCSS-Headers.css?v=c6809083">
<link rel="stylesheet" href="/PolyFishCSS-Content.css?v=6048cac5">

<section class="pf-section">
  <div class="pf-container"></div>
</section>

Load order matters

Core, then Headers, Content and Footers in any order, then Utilities. Loading Utilities early is the single most common mistake — its helpers stop overriding component rules.

Interactive

Most of these pages do something

Eleven panels across the documentation, all of them radio inputs, checkboxes and :has(). No script runs any of it.

How the pieces fit

Which files does my page need?

Core is the only required file. Everything else maps to a part of a page, so the answer is usually visible from the design.

Typical loads. Past eight modules the bundle is smaller and one request — see installation.
Page you are building Files to link Minified Why
Sign-in or error screenCore, Content29 KBOne panel, no navigation, no footer
Marketing or landing pageCore, Headers, Content, Footers40 KBHero, sections, column footer
Documentation pageCore, Headers, Content, Footers, Space60 KBAdds sidebar navigation and spacing helpers
Application screenCore, Headers, Content, Forms, Components61 KBTables, tabs, menus, validation states
Anything using most of itThe bundle108 KBFewer requests and smaller gzipped past eight modules

The two-layer token idea

A palette names colours. Roles name jobs — background, surface, text, accent, border. Components only ever read roles, so a theme is eleven redeclared properties rather than a fork of the framework.

That indirection is why light mode is a palette swap with no component rule touched, and why the playground can retint a live page from radio inputs.

What is deliberately missing

No carousel, no toast queue, no combobox, no scroll-animation library. Anything that only works when a script runs would break the rule the optional layer is built on: every component functions without it.

A horizontally snapping .pf-scroller replaces the carousel. It has no autoplay and no hidden slides, and every card is reachable by scroll, keyboard and screen reader.

Baseline

What it needs from a browser

FeatureUsed forFromWithout it
Custom propertiesEvery token2016Nothing works — this is the floor
Grid and gapAll layout primitives2017Nothing works
clamp()Fluid type and rhythmChrome 79Type stops scaling; layout holds
:has()Option cards, playgroundChrome 105, Safari 15.4Selected-state styling is skipped
@containerContainer-query helpersChrome 105, Safari 16Components keep their stacked layout
animation-timelineScroll revealChrome 115Content is simply visible
color-mix()Frosted panels, brand tintsChrome 111, Safari 16.2Falls back to a solid colour

Roughly Chrome 105, Safari 15.4 and Firefox 103. Every feature past that floor is behind @supports or has a declared fallback, so an older engine does less rather than breaking.