The short version
- CSS patterns are drawn with gradients that use hard colour stops, so colours switch instantly instead of blending.
- The repeating-linear-gradient, repeating-radial-gradient and repeating-conic-gradient functions repeat their stops to fill the element, which draws stripes, rings and rays.
- background-size turns a single gradient into a tile, which is how dot grids, line grids, checkerboards and gingham are built.
- Stop positions written in pixels keep stripes crisp and even, while percentages can produce uneven, blurry edges.
- Patterns behind text should stay low in contrast, so the words remain easy to read.
On this page
CSS patterns are repeating backgrounds, such as stripes, grids, dots and checks, drawn entirely with gradient functions. Two ideas make them work. Hard colour stops, where two stops share a position, turn a gradient into a crisp edge. Repetition, either with the repeating-* gradient functions or with background-size tiling, turns one edge into a pattern. The result weighs a few bytes, scales perfectly and takes its colours from your CSS variables.
background.How hard stops make shapes
A gradient blends between neighbouring stops. If the next stop starts exactly where the last ended, there is no distance to blend over, so the colour switches in a hard line. The shorthand for this is a colour with two positions, such as #1e293b 0 10px, which holds that colour solid from 0 to 10 pixels. The CSS linear gradient guide covers stops, hints and hard stops in detail.
/* a soft blend */
background: linear-gradient(90deg, #1e293b, #334155);
/* a hard edge at 50% */
background: linear-gradient(90deg, #1e293b 50%, #334155 50%);
/* the same stripe pair, repeated every 20px */
background: repeating-linear-gradient(90deg, #1e293b 0 10px, #334155 10px 20px);Stripes with repeating-linear-gradient
repeating-linear-gradient() repeats its stop list along the gradient line until the element is full. The length of one repeat is the last stop's position, so 0 10px, 10px 20px repeats every 20 pixels. Change the angle for diagonals, and change the ratio of the two bands for pinstripes or bold awning stripes.
| Pattern | Stops | Effect |
|---|---|---|
| Even stripes | A 0 10px, B 10px 20px | Equal bands, 20px repeat |
| Pinstripe | A 0 23px, B 23px 24px | A 1px line every 24px |
| Awning | A 0 40px, B 40px 80px at 90deg | Wide vertical bands |
| Double line | A 0 20px, B 20px 22px, A 22px 26px, B 26px 28px | Two thin lines close together |
Grids, dots and tiles with background-size
Some patterns cannot be drawn by one repeating gradient: a dot grid needs repetition in two directions. The answer is to draw one tile, such as one dot or one corner of a grid, then set background-size to the tile size. Backgrounds repeat by default, so the tile fills the element.
.grid-lines {
background-color: #0f172a;
background-image:
linear-gradient(rgb(255 255 255 / 0.08) 1px, transparent 1px),
linear-gradient(90deg, rgb(255 255 255 / 0.08) 1px, transparent 1px);
background-size: 32px 32px;
}
.dots {
background-color: #fefce8;
background-image: radial-gradient(#ca8a04 1.5px, transparent 2px);
background-size: 20px 20px;
}
.checkerboard {
background: repeating-conic-gradient(#e2e8f0 0 25%, #ffffff 0 50%) 0 0 / 24px 24px;
}background-size.The grid is two gradients: one draws a 1px horizontal line at the top of each tile, the other a vertical line at its left edge. The dots are a small radial gradient that turns transparent after 2 pixels. The checkerboard uses a conic gradient split into quarters, which is why it needs only one layer.
Gingham, plaid and zigzags
Layering semi-transparent stripes makes woven patterns: where a horizontal and a vertical band cross, their colours stack and the square turns darker. That is gingham. Add a second, thinner set of stripes in another colour and it becomes plaid. Zigzags come from four triangular corners, each drawn with a 25% hard stop at a different angle.
.gingham {
background-color: #ffffff;
background-image:
linear-gradient(90deg, rgb(220 38 38 / 0.45) 50%, transparent 0),
linear-gradient(rgb(220 38 38 / 0.45) 50%, transparent 0);
background-size: 24px 24px;
}
.zigzag {
background-color: #e0f2fe;
background-image:
linear-gradient(135deg, #0ea5e9 25%, transparent 25%),
linear-gradient(225deg, #0ea5e9 25%, transparent 25%),
linear-gradient(315deg, #0ea5e9 25%, transparent 25%),
linear-gradient(45deg, #0ea5e9 25%, transparent 25%);
background-position: -16px 0, -16px 0, 0 0, 0 0;
background-size: 32px 32px;
}Moving stripes for progress bars
Animated diagonal stripes are the classic way to show that something is still working. Draw the stripes as a tile with background-size, then move background-position by exactly one tile width. Because the end frame matches the start frame, the loop has no visible jump. Keep it to small elements, and stop it for people who prefer reduced motion.
.progress {
background-color: #4f46e5;
background-image: linear-gradient(45deg, rgb(255 255 255 / 0.18) 25%, transparent 25% 50%, rgb(255 255 255 / 0.18) 50% 75%, transparent 75%);
background-size: 28px 28px;
animation: crawl 1s linear infinite;
}
@keyframes crawl {
to { background-position: 28px 0; }
}
@media (prefers-reduced-motion: reduce) {
.progress { animation: none; }
}Sizing, theming and scaling patterns
Write stop positions and tile sizes in pixels or em, not percentages. Percentages depend on the element's size, so stripes stretch unevenly as the element resizes and edges can land between pixels and blur. Using em makes a pattern scale with the text around it, which suits patterned badges and labels.
Custom properties make patterns reusable. Define the colours and size once, then vary them per component or per theme, for example a lighter pattern in light mode and a quieter one in dark mode.
.stripes {
--a: #1e293b;
--b: #334155;
--w: 10px;
background: repeating-linear-gradient(45deg, var(--a) 0 var(--w), var(--b) var(--w) calc(var(--w) * 2));
}
.stripes--warm {
--a: #fff7ed;
--b: #fed7aa;
--w: 14px;
}Patterns behind text
Avoid
- High-contrast stripes directly behind body text
- Fine patterns that shimmer when the page scrolls
- Patterns in the same colour as the text
- Several different patterns on one screen
Do
- Low-contrast patterns, two close shades of one colour
- Larger, calmer tiles on big surfaces
- A solid panel under text that sits on a busy pattern
- One pattern as a recognisable accent
Patterns are a kind of texture, and the same rules apply: subtle enough to feel rather than read. Background texture covers grain, paper and fabric; text on busy backgrounds covers keeping words legible on top.
Questions people ask
How do I make stripes in CSS?
Use repeating-linear-gradient() with hard stops, such as repeating-linear-gradient(45deg, #1e293b 0 10px, #334155 10px 20px). Change the angle for direction and the lengths for width.
How do I make a dot pattern in CSS?
Draw one dot with radial-gradient(#ca8a04 1.5px, transparent 2px) and repeat it with background-size: 20px 20px.
How do I make a checkerboard in CSS?
Use repeating-conic-gradient(#e2e8f0 0 25%, #ffffff 0 50%) with a background-size such as 24px 24px. Each tile holds four squares.
Why are my CSS stripes blurry or uneven?
Percentage positions can land between pixels. Use pixel or em lengths for stops and tile sizes, and leave half a pixel between stops on diagonal edges.
Are CSS gradient patterns better than image patterns?
For geometric patterns, usually yes: they need no download, stay sharp at every size and can be recoloured with CSS variables. Organic or photographic textures still need images.
Written by Gradiently
The team behind Gradiently, a design tool built around Marks: living gradients that make everything you design look like yours.
See our profile


