# CSS patterns with repeating gradients

[Canonical HTML page](https://gradiently.design/guide/css-patterns)

Hard colour stops turn gradients into shapes, and background-size turns shapes into tiles. With those two ideas you can draw most classic patterns in a few lines, with no image files.

## 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.

**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.

- Diagonal stripes: `repeating-linear-gradient(45deg, #1e293b 0 10px, #334155 10px 20px)`
- Pinstripe: `repeating-linear-gradient(90deg, #0f172a 0 23px, #475569 23px 24px)`
- Rings: `repeating-radial-gradient(circle at 50% 50%, #fef3c7 0 8px, #fcd34d 8px 16px)`
- Rays: `repeating-conic-gradient(#0f766e 0 15deg, #115e59 15deg 30deg)`
- Candy stripe: `repeating-linear-gradient(-45deg, #fdf2f8 0 14px, #f9a8d4 14px 18px)`
- Ruled paper: `repeating-linear-gradient(0deg, #eff6ff 0 27px, #bfdbfe 27px 28px)`

Six patterns, each a single repeating gradient. Copy any of them straight into `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](https://gradiently.design/guide/css-linear-gradient) guide covers stops, hints and hard stops in detail.

```css
/* 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);
```

From blend to edge to pattern, in three steps.

## 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 |

Stripe recipes. A and B are your two colours; the last stop sets the repeat length.

> **Crisp diagonals** Hard edges at angles can look jagged. Leaving half a pixel between stops, as in `#1e293b 0 10px, #334155 10.5px 20px`, gives each edge a hair of anti-aliasing and reads as smoother.

## 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.

```css
.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;
}
```

A blueprint grid, a dot grid and a checkerboard. Each is one tile, repeated by `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](https://gradiently.design/guide/css-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.

```css
.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;
}
```

Red gingham and a sky blue zigzag. In the zigzag, the first two layers are shifted half a tile so the triangles interlock.

## 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](https://gradiently.design/guide/reduced-motion).

```css
.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; }
}
```

Stripes that crawl along a progress bar. The animation distance equals the tile width, so the loop is seamless.

## 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](https://gradiently.design/guide/dark-mode-design).

```css
.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;
}
```

One pattern, themed by changing three variables.

## 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](https://gradiently.design/guide/background-texture) covers grain, paper and fabric; [text on busy backgrounds](https://gradiently.design/guide/type-on-busy-backgrounds) covers keeping words legible on top.

## FAQ

### 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.
