A gradient has no intrinsic dimensions, according to MDN, so background-size alone defines the tile. Leave it out and the gradient stretches across the whole element. Nothing repeats.
That one detail sits behind most CSS background patterns. The browser draws a single tile and repeats it along both axes until the element is covered. Gradient layers or an inline SVG data URI can build the tile, and sometimes a plain image file does, so the pattern ships as code or a small file instead of a full-size image. Gradients alone can draw stripes, dots, checkerboards, grid lines and zigzags.
Front-end developers mostly put patterns on section backgrounds and cards, with the occasional faint texture behind body text. That last use is where things go wrong, because readability depends on the contrast between pattern and text.
What Are CSS Background Patterns?
Nothing about a background pattern is a single image stretched over an element. It is a tile, set up with background-image, gradients and background-size, and the browser repeats it. No raster image file is needed, and the whole thing stays editable in code.
It also isn’t the SVG pattern element, which fills shapes inside an SVG document, and it isn’t an animation, although you can animate a static tile afterward.
Gradient layers do most of the work. Hard color stops in linear-gradient, radial-gradient or conic-gradient draw stripes, dots and grids. An SVG embedded in the stylesheet as text works too, and so does a PNG, JPG or SVG file loaded with url().
All of it runs on plain CSS, so the pattern ships with the stylesheet and changes through a few values.
CSS Properties That Control a Background Pattern Tile
Four properties decide how a tile behaves.
background-image supplies the layer. It takes a gradient, an SVG data URI or a url() reference, and because gradients count as images, they never go in background-color.
background-size sets the tile dimensions. One value sets the width and leaves the height at auto, while two values set both. Without it, a gradient stretches over the element, and that’s the most common reason a pattern “doesn’t work”.
background-repeat defaults to repeat, with repeat-x, repeat-y and no-repeat as variants. The space value adds gaps between whole tiles, and round rescales tiles to fit a whole number.
background-position offsets a layer, usually by half a tile when two layers have to interlock for a checkerboard or zigzag.
background-size has been Baseline widely available since July 2015, according to MDN, so tile sizing carries no support risk.
Percentage sizes resolve against the background positioning area. With background-attachment set to fixed, that area becomes the viewport.
Stacking multiple backgrounds in one declaration
Comma-separated values stack layers, and the first layer listed sits on top. MDN notes that only the last layer can carry a background color.
.layers {
background-color: #f4f1ea;
background-image:
radial-gradient(circle, #c9b99a 2px, transparent 2px),
linear-gradient(#e2dccd 1px, transparent 1px);
background-size: 24px 24px, 48px 48px;
}Each sub-property is matched by position, so the first background-size value belongs to the first layer.
Mixed tile sizes like 24px and 48px put a dense dot field over wider ruled lines.
How Gradients Build Patterns
Put two color stops at the same position and the gradient draws a hard edge instead of a blend. Repeat that edge with background-size and you have a pattern.
The CSS Images specification defines the rule, and Lea Verou’s early pattern posts built on it. Stops at the same position switch instantly from the first color to the last.
- Set a base color with background-color.
- Add linear-gradient(to right, #4a7bd8 50%, transparent 50%), so both stops sit at 50%.
- Set background-size to the stripe period, for example 24px 100%.
- Leave background-repeat on its default so the tile repeats along the x-axis.
.stripes {
background-color: #fefefe;
background-image: linear-gradient(to right, #4a7bd8 50%, transparent 50%);
background-size: 24px 100%;
}Each stripe is 12px wide, because the stop sits at the halfway point of a 24px tile.
Stripes, grid lines and triangles come from linear-gradient. Dots and rings come from radial-gradient, and conic-gradient handles checkerboards and anything angular. The repeating variants tile inside a single layer, so diagonal stripes need no background-size.
MDN lists conic-gradient under CSS Images Module Level 4, while linear and radial gradients come from Level 3. A repeating-conic-gradient() repeats its color stops around the full 360-degree turn, which is how a checkerboard fits in one declaration.
Stop positions take trial and error, and a visual CSS gradient generator speeds up the first draft.
Diagonal edges drawn with hard stops can look jagged in some browsers. Separating the two stops by about 1px softens the edge.
How to Code Stripes, Dots, Checkerboards, Grids and Zigzags
What separates these patterns is mostly layer count. The table puts all five side by side.
| Pattern | Gradient function | Layers | Tile size logic |
|---|---|---|---|
| Stripes | linear-gradient, repeating-linear-gradient | 1 | One full stripe cycle |
| Dots | radial-gradient | 1 | Spacing between dot centers |
| Checkerboard | repeating-conic-gradient | 1 | Two squares across and down |
| Grid lines | linear-gradient | 2 | One grid cell |
| Zigzag | linear-gradient | 4 | Two zigzag steps |
Stripes
One layer is enough. Vertical stripes reuse the linear-gradient from the previous section, and diagonal stripes switch to repeating-linear-gradient with an angle.
.diagonal {
background: repeating-linear-gradient(
deg, #4a7bd8, #4a7bd8 12px, #fefefe 12px, #fefefe 24px
);
}No background-size here. The repetition happens inside the gradient itself.
Dots
A dot is a radial-gradient with a hard stop at the dot radius. The stop position sets that radius (2px below), background-size sets the spacing between dots, and the first stop sets the color.
.dots {
background-color: #fefefe;
background-image: radial-gradient(circle, #4a7bd8 2px, transparent 2px);
background-size: 24px 24px;
}Stick to whole-pixel tile sizes, since fractional values can produce visible seams between tiles.
Checkerboards
Until conic-gradient support reached every major browser in November 2020 (MDN), a checkerboard meant stacking several linear-gradient layers. That is the approach in Lea Verou’s CSS3 Patterns Gallery, which CSS-Tricks covered in April 2011.
The gap lasted more than nine years.
.checker {
background: repeating-conic-gradient(#222 0% 25%, #eee 0% 50%) 0 0 / 40px 40px;
}Ana Tudor’s CSS-Tricks article rebuilt gallery patterns with conic-gradient to shorten the code in the same way.
Grid lines
.grid {
background-image:
linear-gradient(#ccc 1px, transparent 1px),
linear-gradient(90deg, #ccc 1px, transparent 1px);
background-size: 24px 24px;
}Two layers share one background-size. The first draws horizontal lines, and the second rotates by 90 degrees for the vertical ones.
Line thickness is the stop position, so 1px gives hairline rules and 2px gives heavier lines.
Zigzags
A zigzag stacks four triangle layers at 135, 225, 45 and 315 degrees, with the first two shifted by half the tile.
.zigzag {
background-color: #fefefe;
background-image:
linear-gradient(135deg, #d9534f 25%, transparent 25%),
linear-gradient(225deg, #d9534f 25%, transparent 25%),
linear-gradient(45deg, #d9534f 25%, transparent 25%),
linear-gradient(315deg, #d9534f 25%, #fefefe 25%);
background-position: 20px 0, 20px 0, 0 0, 0 0;
background-size: 40px 40px;
}Four layers make the declaration long. Honestly, this is where I’d stop and switch to an SVG tile, since it reads cleaner.
For more variants, Temani Afif’s css-pattern.com gives every pattern its own URL, with size and color controls.
How to Build a Pattern With an Inline SVG Data URI
Write a small SVG straight into background-image as a data URI and the tile loads with the stylesheet. No extra request.
Reach for an SVG tile when the shape is a curve, a cross or an icon that would take many gradient layers.
- Write the SVG with an xmlns attribute and width and height set to the tile size.
- Encode the characters that break a URL, at minimum # as %23.
- Place the markup inside url(“data:image/svg+xml,…”) on background-image.
- Set background-size to the same tile size.
.crosses {
background-color: #f6f6f6;
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24'><path d='M12 8v8M8 12h8' stroke='%23999' stroke-width='1.5'/></svg>");
background-size: 24px 24px;
}Chris Coyier reported in January 2023 that modern browsers accept unencoded SVG in a data URI as long as the markup contains no # character. A hex color needs %23 in place of #, or the image breaks.
Trim the string before inlining it, because every character adds to stylesheet weight. Optimizing the SVG markup with SVGO shortens it, and postcss-svgo minifies data URI SVGs at build time.
An explicit background-size makes the browser ignore the SVG’s preserveAspectRatio setting, according to MDN.
If the tile size differs in ratio from the SVG’s own width and height, the shape stretches.
Gradient, SVG or Image Tile: Which Method Fits Which Job?
Straight lines, circles and squares belong to gradient layers. Curves and icon tiles suit an inline SVG data URI, and photographic or noisy texture calls for an image tile.
| Method | Size and requests | Editing | Best fit |
|---|---|---|---|
| Gradient layers | Text in the stylesheet, no extra request | Change values in CSS | Stripes, dots, grids, checkerboards |
| Inline SVG data URI | Text in the stylesheet, no extra request | Edit the string | Curves, crosses, icon tiles |
| Image tile | One extra file, cached on its own | Re-export the file | Noise, paper, photographic texture |
If the shape has straight edges or circles and needs two layers or fewer, use gradients. Once it has curves, or the gradient recipe reaches four layers, switch to inline SVG. Anything photographic, noisy or hand-drawn goes to an image tile.
Generator sites often claim CSS-only patterns win on performance because image files slow loading, a point 10015.io makes on its pattern generator page. That holds for a one-layer stripe and weakens as the layer count grows.
Gradient patterns
There’s no extra request and no encoding step, and every color and size is a plain CSS value. Resolution doesn’t matter either.
The price is code length, which grows with each layer. Diagonal hard stops can look jagged, and curved shapes need a pile of layers.
In CSS Secrets, Lea Verou found the SVG version of a checkerboard to be 40 characters shorter than the gradient version, and less repetitive.
Inline SVG patterns
Like gradients, inline SVG needs no request, and it can draw curves, crosses and icon tiles in a shorter string.
The limit is weight. Colors are baked into the string, and every character adds to render-blocking CSS.
Base64 adds roughly 33% because 3 bytes become 4 characters, DebugBear reports. SVG is already text, so keep the URL-encoded form and skip Base64.
Ready-made sources save drawing time.
- Steve Schoger’s Hero Patterns is a library of repeatable SVG patterns with adjustable color and opacity, plus copyable CSS.
- Pattern Monster lets you adjust colors, stroke width and angle, then export SVG or PNG along with copyable CSS.
- Haikei generates SVG scenes such as layered waves, which suit one large background (background-size: cover, no-repeat) better than a tile.
Image tiles
Image tiles win when the texture has no geometric rule.
A tiny tiled image at low opacity works everywhere, according to 21st.dev, while an SVG turbulence filter avoids the image but is expensive to rasterize at full-screen size.
The browser caches the file apart from the stylesheet, and the tile can hold any texture you can export. The downsides are one extra request and a fixed pixel size that needs a 2x file for dense screens.
A PNG tile blurs when scaled, which is where SVG and PNG files differ most for backgrounds.
How to Make a Background Pattern Customizable With CSS Custom Properties
Put the color, tile size and line width into custom properties, reference them with var() inside the gradient and background-size, then override the variables wherever a variant is needed.
- Declare –tile, –ink and –paper on the selector, or on :root for a site-wide default.
- Reference the variables with var() in the gradient stops and in background-size.
- Derive dot radius or stripe width from –tile with calc(), so one value rescales the whole pattern.
- Redefine the variables on a modifier class or theme selector for each variant.
.pattern {
--tile: 24px;
--ink: #4a7bd8;
--paper: #fefefe;
background-color: var(--paper);
background-image: radial-gradient(
circle, var(--ink) calc(var(--tile) / 12), transparent calc(var(--tile) / 12)
);
background-size: var(--tile) var(--tile);
}
.pattern--dark {
--ink: #9bb7f0;
--paper: #14171c;
}css-pattern.com works the same way, exposing size and color controls on top of CSS variables for each pattern.
One catch. var() does not work inside an unquoted url(). The parser reads the value as a legacy url token and invalidates the whole declaration, according to Stefan Judis.
You can move the entire url() value into the custom property, or apply an SVG shape as a mask-image on a pseudo-element and color it with background-color: var(–ink).
Do Background Patterns Hurt Text Readability and Accessibility?
They can. Contrast between text and pattern has to stay at or above the WCAG minimum of 4.5:1 for body text or 3:1 for large text, per W3C Success Criterion 1.4.3.
The ratio applies against the pattern itself, so test the pattern color closest in luminance to the text, not the average of the tile. W3C also warns that a nominal pass can read much worse in practice, because authors do not control font smoothing.
Keeping the ink and paper colors within a narrow lightness range helps the tile stay quiet. Better still, set body text on a solid panel and let the pattern fill the margins. Save high-contrast patterns for headings and large text, where 3:1 applies.
Run each pattern and text pairing through a color contrast check, since the lightest and darkest pixels of the tile both count.
CSS images carry no alternative text, so screen readers never announce them, as MDN notes in its conic-gradient accessibility guidance.
Anything a reader needs to understand the page belongs in HTML, which is a basic web accessibility rule.
Animated patterns and reduced motion
A scrolling pattern pans a large area, and MDN lists panning large objects among vestibular motion triggers.
@media (prefers-reduced-motion: reduce) {
.moving-pattern {
animation: none;
}
}Reduced motion does not have to mean no animation, BOIA advises. A fade or a static tile keeps the design intact.
BOIA’s position is that the media query counts as a sufficient mechanism for WCAG conformance, and that a visible pause control can serve some users better.
When Do CSS Background Patterns Fail?
Mostly in two documented ways. The browser lacks the gradient function, or an animated layer repaints on every frame.
conic-gradient first shipped in Chrome 69, Edge 79, Safari 12.1 and Firefox 83 (MDN browser compatibility data, Can I use). On the performance side, animating mask-position on a full-viewport gradient layer dropped one developer’s frame rate from 120fps to 40fps (Andrico, DEV Community). Moving the same layer with translate cut its paint count from 7941 to 2 (same post).
Browser support and fallback color
Firefox was the last of the four major browsers to add conic-gradient, and before version 83 cross-browser use meant a polyfill such as Lea Verou’s, CSS-Tricks noted.
A browser that drops the declaration shows whatever plain background-color sits above it. Keep that color outside the shorthand, because an unsupported value invalidates the whole shorthand declaration, color included.
.checker {
background-color: #ddd;
}
@supports (background: conic-gradient(#000, #fff)) {
.checker {
background: repeating-conic-gradient(#222 0% 25%, #eee 0% 50%) 0 0 / 40px 40px;
}
}The PostCSS Conic Gradient plugin adds fallbacks automatically, and the fallback belongs on your list of cross-browser compatibility checks.
Repaint cost in animated patterns
Andrico traced the 120fps to 40fps drop to a paint count that grew on every frame, then fixed it by translating the layer instead.
web.dev explains that animations triggering paint or layout need the main thread, which is why transform and opacity are the cheap properties to animate.
A static pattern costs one paint. The cost appears when the layer moves.
For a looping pattern, animate a layer one tile larger than the viewport with translate, and reset after exactly one tile.
Debugging repaints in Chrome DevTools
- Open DevTools and switch to the Rendering tab.
- Check Paint flashing, then scroll or trigger the animation. Chrome flashes repainted areas green.
- Check Layer borders to outline compositor layers in orange and olive, with tiles in cyan.
Chrome’s documentation says to investigate when the whole screen flashes green, or when areas flash that should not repaint.
CSS background patterns examples
Circle Transparency Magic

Emerald Dreams

Hearty Patterns

Triad of Patterns

Soothing Gradient Moves

Geo-Rainbows Everywhere

Patterned Illusions

Nostalgic Windows Vibes

Heartful Trunks

Raindrops Keep Falling

Pie in the Sky

Solo Card Magic

Zen & Dazzle: Yin Yang + Bokeh

Deep Dive: Underwater Vibe

Surf’s Up: Waves Everywhere

Subtle Sass: Caterpillar Edge

Heartfelt: Children in Need

School Days: Blueprint Dream

Simple & Sleek: Go-To Pattern

Glow & Flow: Patterns and Shades

Art in the Shadows: Box Delights

Top-Notch: Granjamar Glam

Tropical Bliss: Pineapple Paradise

Anime Vibes: Nezuko Kamado

Keeping it Minimal: Just CSS Things

Alive & Kicking: Simple Patterns

Quote Me On That: Jason’s Touch

Clean & Pure: Halftone Wonders

Blue Bliss: Squared Away

Textured Times: Living Design

CSS Background Patterns FAQ
How can I make a repeating pattern look random?
Give each gradient layer a different prime background-size, such as 17px, 37px and 53px, so the layers rarely line up. A prime repeat unit maximizes the appearance of organic randomness, Lea Verou writes.
The Cicada Principle dates to Alex Walker’s 2011 article, and it only works when the layer parameters are independent.
Does background-blend-mode work on patterns, and how does it differ from an opacity overlay?
Yes. background-blend-mode blends a pattern layer with the layers beneath it and with the background color, and it has been available across browsers since January 2020, according to MDN.
Opacity set on the element fades its text too, so apply opacity to a pseudo-element that carries the pattern.
Can Tailwind CSS hold a background pattern?
Yes. Write the gradient as an arbitrary value, using underscores for spaces, or install the tailwindcss-bg-patterns plugin.
The plugin adds utility classes for pattern color, opacity and size, and it is inspired by MagicPattern’s CSS Background Patterns collection.
Which media query switches a pattern to dark mode?
Use prefers-color-scheme: dark and redefine the pattern’s custom properties inside it, so the gradient itself stays untouched.
Re-test text contrast afterward, because a pairing that passes in light mode can fail in dark mode.
Does the CSS Houdini Paint API work for patterns?
Yes, in Chromium browsers only. registerPaint() defines the drawing, and background-image: paint(name) applies it.
MDN lists support from Chrome 65 and Edge 79, with none in Firefox or Safari, so keep a url() fallback behind @supports.
Building and Testing a First Pattern Before It Ships
A single-layer gradient tile is the right first build for CSS background patterns, because one declaration isolates tile size, color stops and text contrast.
Start there, with a solid fallback color behind it. Move to an inline SVG tile only when the design needs curves or irregular shapes, and to an image tile for noise and photographic grain.
Each step away from the gradient gives up easy CSS editing in exchange for shapes a gradient cannot draw. A pattern that stays a gradient remains editable with one custom property.
Test the finished tile at 100% and 200% zoom on one real phone and one desktop browser, with the actual text it will sit behind.
Position verified on October 14, 2026. Can I use lists the CSS Painting API as unsupported in Firefox and disabled by default in Safari, so a static fallback tile stays required until both browsers ship it.
- What is Backend in Web Development? - September 13, 2026
- What is Frontend Development? - September 11, 2026
- How to Make a Button in Figma: Design Best Practices - September 10, 2026











