size-section-css
"A section's compiled CSS exceeds its byte budget."
Venue: check — A section’s compiled CSS exceeds its byte budget.
A section’s compiled cascade layer must gzip to 50 KB or less. What is measured is everything that lands in the layer:
- the section’s hand-written CSS, and
- every package stylesheet its module graph imports — the
import "swiper/css"in a component or an island.
The budget is visible in template-kit check’s configuration and is not
author-overridable. A workspace cannot raise it, and there is no per-section exemption.
Reason
Section titled “Reason”The output is a real page for a real buyer, standing in a driveway on a phone — and a stylesheet is the worst thing to be heavy, because CSS blocks the first paint. Nothing can schedule it out of the way: the browser paints when the layer arrives and not before. That is why this budget is one hard number, where the renderer and island bundles get a guideline and a far higher ceiling.
“Including package CSS” is not a technicality; it is the failure this rule exists to catch.
Your own stylesheet is almost never the problem — it is a few kilobytes of overrides. The
package’s is. A one-line import "some-ui/dist.css" can drag in thousands of rules for
components you never render — resets, theme variants, every widget’s styles — and they all
land in your compiled layer. Nothing in your source looks large; the compiled layer is
400 KB.
So the number this check reports is frequently the first time anyone finds out what a
one-line CSS import actually cost. (What this budget does not count is asset bytes: a
url()-referenced webfont or icon file resolves to a cached URL and is weighed separately —
see size-assets.)
Three moves, in order of how often they work:
- Import the slimmest entry the package offers. Most CSS-shipping packages have a core stylesheet plus opt-in modules, and a fat “everything” bundle that is the one you get from copying its starter snippet. Take the core, plus only the modules you use.
- Drop the icon font. If the package’s stylesheet declares an icon font and you use
three of its glyphs, replace them with inline SVG — or with a family the template’s theme
already declares in
fonts(--tr-font-<name>), which costs nothing because it is already loaded. (The font file is weighed undersize-assets, but its@font-facerules and every.icon-*class still land in this layer.) - Drop the package and hand-write the residue. If you use one component from a large widget library, its stylesheet is paying for components you never render.
Before
Section titled “Before”"use client";
import Swiper from "swiper";import "swiper/swiper-bundle.css"; // ← every module Swiper ships, plus its icon font
export default function Carousel({ slides }: { slides: { id: string; url: string }[] }) { // …uses navigation arrows and pagination dots, and nothing else}"use client";
import Swiper from "swiper";import "swiper/css"; // ← the core layout onlyimport "swiper/css/navigation"; // ← the two modules this carousel actually usesimport "swiper/css/pagination";
export default function Carousel({ slides }: { slides: { id: string; url: string }[] }) { // …}The arrows and dots that came from the bundle’s icon font are drawn as inline SVG in the
component instead, so the layer no longer carries that font’s @font-face and icon-class
rules (and the font file no longer shows up under size-assets either).
See also
Section titled “See also”size-renderer-bundle— the section’s JavaScript, held to a guideline at this number and a ceiling well above it.size-assets— the separate, warn-only report on the static asset files (fonts, images) a stylesheet or component references.no-bare-css-import— why a package’s stylesheet is imported from code and never@imported from CSS.TokenTheme— the fonts and tokens the page already loads, which cost you nothing to reuse.