# Moiré rings

> Two sets of concentric rings drift over each other and create interference patterns that keep shifting.

- Category: [Backgrounds](https://library.aimran.es/components/patterns)
- Built with: HTML + CSS
- Live editor: https://library.aimran.es/blocks/pt-moire
- Library: AIMRAN Library (https://library.aimran.es), free, no sign-up, by Aimran Oukham

## Where to use it

Moiré is pure optical play, so it belongs in places that want to look bold and a little experimental: the homepage of an art gallery, a techno club’s event page or the site of a design conference. The pattern changes constantly as the rings slide, and you rarely see the same frame twice. A hypnotic moiré background in CSS.

Use it sparingly. Moiré can be uncomfortable for people who are sensitive to flicker, so keep it off pages meant for long reading and consider pausing it when someone has reduced motion turned on. Short headlines only.

## Code

### HTML

```html
<div class="pt-moire"><i></i><i></i></div>
```

### CSS

```css
.pt-moire {
  --c1: #0071e3;
  --c2: #8b5cf6;
  --c3: #14b8a6;
  --k: 1;
  /* velocidad */
  --d: 1;
  /* densidad / escala */
  --o: 1;
  /* opacidad */
  --bg: #05060a;
  --fg: #ffffff;
  --blend: screen;
  min-height: 100vh;
}

.pt-moire {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: var(--bg);
  container-type: size;
  isolation: isolate;
}

.pt-moire>* {
  opacity: var(--o);
}

.pt-moire i {
  position: absolute;
  inset: -50%;
  background: repeating-radial-gradient(circle at 50% 50%,transparent 0 calc(6px * var(--d)),var(--c1) calc(6px * var(--d)) calc(7.5px * var(--d)));
  will-change: transform;
  animation: pt-moire-a calc(12s / var(--k)) ease-in-out infinite alternate;
}

.pt-moire i:nth-child(2) {
  background: repeating-radial-gradient(circle at 50% 50%,transparent 0 calc(6px * var(--d)),var(--c2) calc(6px * var(--d)) calc(7.5px * var(--d)));
  mix-blend-mode: var(--blend,screen);
  animation-name: pt-moire-b;
}

.pt-moire::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle,transparent 30%,var(--bg) 90%);
}

@keyframes pt-moire-a {
  from {
    transform: translate(-8%,-4%);
  }
  to {
    transform: translate(6%,5%);
  }
}

@keyframes pt-moire-b {
  from {
    transform: translate(7%,6%) rotate(0);
  }
  to {
    transform: translate(-6%,-5%) rotate(8deg);
  }
}
```

### React

```tsx
// @ts-nocheck
import { useEffect, useRef } from "react";

const css = `
.pt-moire {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: var(--bg);
  container-type: size;
  isolation: isolate;
}

.pt-moire>* {
  opacity: var(--o);
}

.pt-moire i {
  position: absolute;
  inset: -50%;
  background: repeating-radial-gradient(circle at 50% 50%,transparent 0 calc(6px * var(--d)),var(--c1) calc(6px * var(--d)) calc(7.5px * var(--d)));
  will-change: transform;
  animation: pt-moire-a calc(12s / var(--k)) ease-in-out infinite alternate;
}

.pt-moire i:nth-child(2) {
  background: repeating-radial-gradient(circle at 50% 50%,transparent 0 calc(6px * var(--d)),var(--c2) calc(6px * var(--d)) calc(7.5px * var(--d)));
  mix-blend-mode: var(--blend,screen);
  animation-name: pt-moire-b;
}

.pt-moire::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle,transparent 30%,var(--bg) 90%);
}

@keyframes pt-moire-a {
  from {
    transform: translate(-8%,-4%);
  }
  to {
    transform: translate(6%,5%);
  }
}

@keyframes pt-moire-b {
  from {
    transform: translate(7%,6%) rotate(0);
  }
  to {
    transform: translate(-6%,-5%) rotate(8deg);
  }
}

`;

export default function PatternMoire() {
  const ref = useRef(null);
  return (
    <>
      <style>{css}</style>
      <div
        ref={ref}
        className="pt-moire"
        style={{
          "--c1": "#0071e3",
          "--c2": "#8b5cf6",
          "--c3": "#14b8a6",
          "--k": 1,
          "--d": 1,
          "--o": 1,
          "--bg": "#05060a",
          "--fg": "#ffffff",
          "--blend": "screen",
          minHeight: "100vh",
        }}
      >
        <i></i><i></i>
      </div>
    </>
  );
}
```

## Prompt for AI assistants

Paste this into ChatGPT, Claude, Cursor, v0 or Lovable to adapt the block to your stack (Tailwind, Vue, Svelte…):

````text
I want to add the “Moiré rings” block (Backgrounds) from AIMRAN Library to my project.

## What it is
Two sets of concentric rings drift over each other and create interference patterns that keep shifting.

## Chosen settings
- Color 1: #0071e3
- Color 2: #8b5cf6
- Color 3: #14b8a6
- Speed: 1x (from 0.2 to 3x)
- Density / scale: 1x (from 0.5 to 2x)
- Opacity: 1 (from 0.2 to 1)

## What I need
1. Add it to my project with the same look and animation as the reference code: same sizes, colours, radii, shadows, timings and easing.
2. It is built with plain HTML and CSS (the reference wraps it in a React component with its CSS). If my project uses something else (Vue, Svelte, Astro, plain HTML…), translate it without losing any behaviour. If I use Tailwind, turn the styles into classes; if not, keep the CSS in its own file.
3. Expose the settings above as props (or CSS variables) with those values as defaults.
4. Make it work well on phones: nothing that depends only on hover, touch targets of at least 44 px and no overflow on 320 px screens.
5. Make it look right in light and dark mode.
6. Keep it accessible: semantic HTML, roles and aria-* where needed, visible focus and keyboard support.
7. Respect prefers-reduced-motion: with reduced motion, no big animations but the same end result.

## Reference code
```tsx
// @ts-nocheck
import { useEffect, useRef } from "react";

const css = `
.pt-moire {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: var(--bg);
  container-type: size;
  isolation: isolate;
}

.pt-moire>* {
  opacity: var(--o);
}

.pt-moire i {
  position: absolute;
  inset: -50%;
  background: repeating-radial-gradient(circle at 50% 50%,transparent 0 calc(6px * var(--d)),var(--c1) calc(6px * var(--d)) calc(7.5px * var(--d)));
  will-change: transform;
  animation: pt-moire-a calc(12s / var(--k)) ease-in-out infinite alternate;
}

.pt-moire i:nth-child(2) {
  background: repeating-radial-gradient(circle at 50% 50%,transparent 0 calc(6px * var(--d)),var(--c2) calc(6px * var(--d)) calc(7.5px * var(--d)));
  mix-blend-mode: var(--blend,screen);
  animation-name: pt-moire-b;
}

.pt-moire::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(circle,transparent 30%,var(--bg) 90%);
}

@keyframes pt-moire-a {
  from {
    transform: translate(-8%,-4%);
  }
  to {
    transform: translate(6%,5%);
  }
}

@keyframes pt-moire-b {
  from {
    transform: translate(7%,6%) rotate(0);
  }
  to {
    transform: translate(-6%,-5%) rotate(8deg);
  }
}

`;

export default function PatternMoire() {
  const ref = useRef(null);
  return (
    <>
      <style>{css}</style>
      <div
        ref={ref}
        className="pt-moire"
        style={{
          "--c1": "#0071e3",
          "--c2": "#8b5cf6",
          "--c3": "#14b8a6",
          "--k": 1,
          "--d": 1,
          "--o": 1,
          "--bg": "#05060a",
          "--fg": "#ffffff",
          "--blend": "screen",
          minHeight: "100vh",
        }}
      >
        <i></i><i></i>
      </div>
    </>
  );
}
```

Give me the complete final code, ready to paste, and tell me in one line where to put it in my project.
````
