A pattern in CSS beats a pattern in a PNG
Every pattern here is gradients, so it costs no HTTP request, weighs a few hundred bytes rather than a few kilobytes, is crisp at any device pixel ratio, and recolours by changing one value. A tiling PNG is none of those things: it needs a request, it is fixed at one resolution, and recolouring it means opening an image editor. The status line above shows the actual byte count of the rule, which is the whole argument in one number.
The trick that makes them short is the double-position colour stop from CSS Images 4: #000 0 4px says "black from 0 to 4px" in one stop instead of two. So a stripe is repeating-linear-gradient(45deg, #000 0 4px, transparent 4px 16px) and not the four-stop version most generators still emit.
How each one is built
Stripes are one repeating linear gradient at an angle. Grid is two of them at right angles, and graph paper is four: fine lines at the tile size and heavier lines every fifth one. The heavy pair is written first, because the first layer in a background-image list paints on top. Isometric is three, at 30, 150 and 90 degrees, which are the isometric axes plus the vertical that closes the triangles.
Dots are a radial gradient tiled by background-size, and checkerboard is a single conic gradient with two of its four quadrants inked, which is by far the shortest checkerboard there is. Zigzag is four 45-degree gradients where two are offset by half a tile through background-position; without that offset they overlay into a crosshatch instead of meeting at the peaks.
Common problems
- The pattern shifts when the element moves. A background is positioned against the element's own padding box, so a pattern that must line up across two boxes needs the same
background-positionorigin on both, or one shared parent. - Thin lines shimmer while scrolling. A 1px line at a non-integer device pixel ratio lands between physical pixels and the browser resamples it every frame. Use a slightly thicker line, or a tile size that is a whole multiple of the ratio.
- Text over the pattern is hard to read. It will be: a repeating high-contrast pattern behind body copy fails contrast at some point on the tile no matter what the average says. Put the text on a solid panel, or take the ink opacity right down.
- The pattern looks like a flat fill. The line width reached the tile size, so there are no gaps. The slider stops half a pixel short of that for exactly this reason.
Frequently asked questions
Can I use a CSS variable for the colours?
Yes, and it is the reason to prefer this over an image. Replace the hex values with var(--pattern-ink) and the pattern follows your theme, including a dark-mode switch, with no second asset and no JavaScript.
Do these animate?
background-position animates cheaply and gives you a scrolling barber-pole stripe in three lines. Animating the gradient itself does not: gradients interpolate poorly and repaint the whole element. If you animate anything here, wrap it in a prefers-reduced-motion guard, because a moving repeating pattern is a strong vestibular trigger.
Why is the Tailwind output arbitrary properties?
bg-[image:…] exists but cannot carry a comma-separated layer list, and every pattern past stripes has one. The arbitrary-property form is one bracket per declaration and compiles to exactly the rule beside it.
Will a pattern hurt performance?
A static one, no: it paints once and is cached with the rest of the layer. The cost appears when a very small tile covers a very large area on a low-end device, which is worth checking if you are tiling something at 2px across a full-page hero.