CSS background pattern generator — stripes, checkerboard, grid, dots and zigzag in pure CSS
How every pattern in our CSS pattern generator is built with repeating-linear-gradient, radial-gradient and conic-gradient — with the actual CSS, scaling, animation, theming and performance notes.
A background pattern does not need a PNG, an SVG sprite or a data URI. Every
classic texture — diagonal stripes, a checkerboard, graph paper, a dot grid,
zigzag, crosshatch — is a small gradient tiled with background-size. Our
CSS pattern generator builds seven of them
interactively: pick a pattern, set two colors, drag the size, thickness and
angle sliders, copy the declaration. This article explains what that output
is actually doing, so you can modify it or write your own.
Patterns at a glance
| Pattern | Gradient function | Tiled with background-size? | Controls in the generator |
|---|---|---|---|
| Checkerboard | one conic-gradient | yes | size |
| Dots | one radial-gradient | yes | size, thickness |
| Grid (graph paper) | two linear-gradients | yes | size, thickness |
| Diagonal stripes | one repeating-linear-gradient | no (self-repeating) | size, angle |
| Vertical stripes | one repeating-linear-gradient | no | size |
| Zigzag | four linear-gradients | yes, with background-position offsets | size |
| Crosshatch | two repeating-linear-gradients | no | size, thickness |
Two ideas power all seven. First, a hard color stop — two stops at the
same position, written color 25%, other 25% or with the shorthand
other 0 50% — turns a smooth gradient into a crisp edge. Second,
background-size tiles that one small image across the element, so a
20-pixel square becomes an infinite texture.
Checkerboard
A conic-gradient sweeps around the center of the tile. Splitting the sweep
into four 25% quarters with alternating colors gives a 2×2 checker in one
tile:
background-color: #3a3f4f;
background-image: conic-gradient(#2a2d3a 25%, #3a3f4f 0 50%, #2a2d3a 0 75%, #3a3f4f 0);
background-size: 40px 40px;
The 0 after each color means “start this stop where the previous one
ended”, which is what produces the hard edge. Each checker square is half of
background-size, so 40px tiles give 20px squares. The older two-gradient
trick with background-position offsets still works, but the single
conic-gradient is shorter and has no seam. Live version:
checkerboard pattern.
Dots
One radial-gradient with a hard stop paints a filled circle in the center
of each tile; everything beyond the radius is transparent so the background
color shows through:
background-color: #0f1117;
background-image: radial-gradient(#3a3f4f 2px, transparent 2px);
background-size: 22px 22px;
2px is the dot radius (the thickness slider), 22px is the spacing. Add
0.5px to the transparent stop (transparent 2.5px) if the dots look
jagged — that half-pixel ramp acts as anti-aliasing. Live version:
dotted grid background.
Grid (graph paper)
Two linear gradients, one horizontal and one rotated 90°, each painting a thin line at the start of the tile and transparent after it:
background-color: #0f1117;
background-image:
linear-gradient(#2a2d3a 1px, transparent 1px),
linear-gradient(90deg, #2a2d3a 1px, transparent 1px);
background-size: 24px 24px;
The first gradient draws a 1px line along the top of every tile, the second
along the left edge. Stack a second pair with a larger background-size and
a bolder color for the classic graph-paper major/minor grid — see the
graph paper grid snippet, which layers a 100px
major grid over a 20px minor one.
Diagonal stripes (css diagonal stripes)
repeating-linear-gradient repeats its own stop list along the gradient
line, so no background-size is needed. Two hard-edged bands of equal width
give even stripes:
background-color: #12151c;
background-image: repeating-linear-gradient(45deg, #6b46ff 0 12px, #12151c 12px 24px);
The angle is the stripe direction; 12px is the width of one band measured
perpendicular to the stripes. Because the gradient line runs through the
element’s center, the stripes never show a tile seam at any angle — the
reason this is preferable to a rotated tiled gradient. The generator’s
angle slider rotates it; 135deg mirrors the direction. Live version:
diagonal stripes.
Vertical stripes (and horizontal)
The same declaration at 90deg. Set 0deg (or 180deg) for horizontal
stripes:
background-color: #12151c;
background-image: repeating-linear-gradient(90deg, #6b46ff 0 12px, #12151c 12px 24px);
Use unequal bands (0 4px, … 4px 24px) for pinstripes, or three colors for
a hazard-tape look.
Zigzag
Zigzag is the trickiest: four linear-gradients at 45°, 135°, 225° and
315°, each painting a triangle in one corner of the tile, with two of them
shifted by half a tile so the triangles join into a chevron:
background-color: #12151c;
background-image:
linear-gradient(135deg, #6b46ff 25%, transparent 25%),
linear-gradient(225deg, #6b46ff 25%, transparent 25%),
linear-gradient(45deg, #6b46ff 25%, transparent 25%),
linear-gradient(315deg, #6b46ff 25%, transparent 25%);
background-position: 12px 0, 12px 0, 0 0, 0 0;
background-size: 24px 24px;
The background-position list is positional: the first two gradients are
offset by half the tile width (size / 2), the last two are not. If you
change background-size, update the offsets to stay at exactly half or the
chevrons tear. Live version: zigzag pattern.
Crosshatch
Two self-repeating diagonal gradients at +45° and −45°, each drawing a thin line then a transparent gap:
background-color: #0f1117;
background-image:
repeating-linear-gradient(45deg, #2a2d3a 0 1px, transparent 1px 16px),
repeating-linear-gradient(-45deg, #2a2d3a 0 1px, transparent 1px 16px);
1px is the line thickness, 16px the period. Make the two periods
different (16px and 24px) for a woven, less mechanical texture. Live
version: crosshatch pattern.
How background-size controls scale
For tiled patterns, background-size is the pattern scale: the gradient is
defined in percentages of the tile, so changing the size scales everything
proportionally. For the self-repeating ones (repeating-linear-gradient) the
scale lives inside the stop list instead, which is why the generator rewrites
the pixel values rather than setting background-size.
Three practical notes:
- Use whole pixels. A
23.5pxtile makes every other tile land on a half pixel and you get a faint moiré. The generator only emits integers. - Tiles start at the top-left corner. If the element’s size is not a
multiple of the tile, the last row/column is cut. Usually invisible; if it
matters, set
background-position: centerto center the cut instead. - Line widths do not scale with the tile.
1pxstays 1px at any size, which is what you want for grids. For dots, scale the radius with the gap (about 10% of the gap looks balanced).
Animating a pattern
Tiled patterns animate by moving background-position. Shift by exactly one
tile per loop and the animation is seamless:
.moving-stripes {
background-image: repeating-linear-gradient(45deg, #6b46ff 0 12px, #12151c 12px 24px);
background-size: 34px 34px; /* 24px * sqrt(2) — one diagonal period */
animation: slide 1s linear infinite;
}
@keyframes slide {
to { background-position: 34px 0; }
}
For a self-repeating gradient the tile is undefined, so give it an explicit
background-size equal to one period (for 45° stripes, the period along the
x-axis is band × 2 × √2). This is exactly how the
striped progress bar snippet runs its
barber-pole effect. Animate background-position, not the gradient itself —
gradients are not interpolable without @property, and moving the
position is a cheap compositor-friendly change.
Theme-aware patterns with custom properties
Every snippet on the site drives its colors through :root variables, and
you should do the same so one rule switches the pattern for dark mode:
:root {
--pattern-fg: #2a2d3a;
--pattern-bg: #0f1117;
--pattern-size: 24px;
}
[data-theme="light"] {
--pattern-fg: #e2e5ee;
--pattern-bg: #ffffff;
}
.grid-bg {
background-color: var(--pattern-bg);
background-image:
linear-gradient(var(--pattern-fg) 1px, transparent 1px),
linear-gradient(90deg, var(--pattern-fg) 1px, transparent 1px);
background-size: var(--pattern-size) var(--pattern-size);
}
Low-contrast patterns work best as page textures: keep the foreground within
10–15% lightness of the background (#0f1117 on #2a2d3a, not white on
black), or readers will fight the lines. For patterns under text, the
contrast checker tells you whether your text
still passes against the brightest color in the pattern.
Performance: gradients vs PNG vs SVG
| Approach | Bytes | Scales with DPR | Themable | Repaint cost |
|---|---|---|---|---|
| CSS gradients | ~100–300 B of CSS | yes, always crisp | one variable swap | rasterized once per tile size, then tiled by the compositor |
| Tiled PNG | 1–20 KB per pattern, one request | blurry unless you ship 2× and 3× | needs one file per theme | cheap |
| Inline SVG data URI | 200 B–2 KB | yes | needs URL-encoding the color into the URI | re-decoded when the URI changes |
Gradients win on every column that matters for a UI texture. Two caveats:
browsers rasterize the gradient tile when it is first painted and again
whenever its size or colors change, so animate background-position, not
colors or size. And very large tiles (a 2000px background-size on a
full-screen hero) rasterize a big bitmap — keep tiles under a few hundred
pixels.
Conic gradients have been in every major browser since 2020 (Chrome 69,
Firefox 83, Safari 12.1), and repeating-linear-gradient with the two-position
stop shorthand (#000 0 12px) since Chrome 71, Firefox 64 and Safari 12.1.
If you need to support anything older, write the double stop the long way:
#000 0, #000 12px.
FAQ
How do I make a CSS checkerboard background?
One conic-gradient with four hard 25% stops, tiled with background-size:
conic-gradient(#000 25%, #fff 0 50%, #000 0 75%, #fff 0) at
background-size: 40px 40px gives 20px squares. The
pattern generator lets you set both colors and
the tile size and copies the declaration.
How do I make diagonal stripes in CSS?
Use repeating-linear-gradient(45deg, #6b46ff 0 12px, #12151c 12px 24px). The angle
sets the direction, the pixel values set the band width, and no
background-size is needed because the gradient repeats itself.
Why does my pattern have a visible seam?
Either the tile size is a fraction of a pixel (round it), the
background-position offsets in a multi-gradient pattern like zigzag are
not exactly half the tile, or you rotated a tiled gradient with transform
instead of using the gradient’s own angle. Self-repeating gradients never
seam.
Can I use a pattern with an image or gradient underneath?
Yes — background-image takes a comma-separated list and paints the first
one on top. Put the pattern first and the photo or gradient last, and use
transparent (not the background color) for the pattern’s gaps so the lower
layer shows through.
Is a CSS pattern better than an SVG background?
For simple geometry (stripes, grids, dots, checks) CSS is smaller, scales perfectly and themes with a variable. Use SVG when the motif is a real shape — icons, hexagons, organic blobs — that gradients cannot draw.
Related
- CSS pattern generator — build all seven patterns interactively and copy the code.
- Four CSS background patterns — the original snippet announcement with live editable previews.
- Snippets: checkerboard, diagonal stripes, dotted grid, graph paper, zigzag, crosshatch.
- Gradient generator — for the smooth kind of gradient, with multiple stops and OKLCH interpolation.