# Flashing banners and videos that can trigger seizures

Source: https://easeweb.dev/learn/flashing-content
Topics: Time limits and motion > Nothing flashes fast or brightly enough to cause a seizure (WCAG 2.3.1)
The fix: Either stop the flashing and let the banner stand out by its colour and size, or slow it to one flash a second and let it end after three. Keeping the fast strobe to a small badge also passes 2.3.1, while the badge stays under a quarter of a 341 × 256 pixel field at every zoom; video and animated images are fixed in the edit, checked with a flash analysis tool.
Test with: flash analysis, reduced motion, zoom
References: https://www.w3.org/WAI/WCAG22/Understanding/three-flashes-or-below-threshold.html https://www.w3.org/TR/WCAG22/#dfn-general-flash-and-red-flash-thresholds https://www.w3.org/WAI/WCAG22/Techniques/general/G19 https://www.w3.org/WAI/WCAG22/Techniques/general/G176 https://www.w3.org/WAI/WCAG22/Techniques/general/G15 https://trace.umd.edu/peat/ https://github.com/electronicarts/IRIS https://epilepsysociety.org.uk/about-epilepsy/epileptic-seizures/seizure-triggers/photosensitive-epilepsy

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

## What happens

A clothing shop runs a one-day sale. Across the top of the home page, a full-width banner reads "Flash sale: 50% off everything until midnight", and to make the words "flash sale" literal, the banner flashes: black with yellow text, then yellow with black text, ten times a second, so it goes dark and light again five times a second. It starts as the page loads and keeps going for as long as the page is open. The same failure turns up as a "glitch" effect on a hero heading, a game or product trailer with strobe lighting that plays by itself, a "lightning" GIF in a promotion, or an error state that flashes the whole screen red.

## Why it fails

Success criterion 2.3.1 Three Flashes or Below Threshold allows a page to flash more than three times in any one second only if the flash is below both the general flash and the red flash thresholds. A general flash is a pair of opposing changes in relative luminance of 10% or more of white, where the darker state is below 0.80; a red flash is a pair of changes to and from a saturated red. The threshold is passed when the flashing covers more than a quarter of a 10-degree visual field, which W3C estimates as a 341 × 256 CSS pixel area. The banner fails on every count. Black is close to 0 in relative luminance and the yellow is about 0.68, so every swap is a general flash; it flashes five times a second; and a banner 110 pixels tall across the page covers about 43% of that area. 2.3.1 is one of the few criteria that applies to everything on the page, even content a conforming alternative version exists for: W3C lists it under conformance requirement 5, non-interference, since the flash reaches people before they could ever choose another version.

## Who is affected

Around 3% of people with epilepsy are photosensitive, most of them developing it between the ages of 7 and 19, and someone who has never had a seizure has no reason to know they are. A shop's home page is also where people browse on a phone held close to the face, which makes the banner cover even more of what they see.

## What automation and AI miss

The audit now measures flashing it can read from the page: it steps every CSS and Web Animations API animation through its first seconds, watches for scripts that swap a class on a timer, reads the colours each time, counts flashes in any one second, and measures the area against the 341 × 256 field. It reports the banner above as a finding with its rate, colours and size. It can't see inside video, animated GIFs, Lottie files or canvas, and it can't know the brightness of a banner blinking over a photo; those need a flash analysis tool. axe and linters have no flash rule at all. AI tools usually answer a flashing banner by wrapping it in a reduced-motion query or adding a Pause button, and neither passes. "Flash sale" and "glitch text" snippets online often use `steps()` timing a tenth of a second long, so AI-written CSS brings the failure with it.

## Before: a banner that strobes

```html
<div class="flash-sale">
  <p>Flash sale: 50% off everything until midnight</p>
</div>
```

```css
.flash-sale {
  padding: 2rem 1rem;
  background: #111;
  color: #ffd400;
  font-size: 2rem;
  font-weight: 800;
  text-align: center;
  animation: strobe 0.2s steps(1) infinite;
}
@keyframes strobe {
  50% { background: #ffd400; color: #111; }
}
```

`steps(1)` jumps straight from black to yellow and back with no fade, every 0.1 seconds: five flashes a second, from dark to bright, over a large part of the screen, without end.

## After: a still banner

```css
.flash-sale {
  padding: 2rem 1rem;
  border-block: 6px solid #111;
  background: #ffd400;
  color: #111;
  font-size: 2rem;
  font-weight: 800;
  text-align: center;
}
```

Nothing changes, so nothing flashes, and there is nothing to measure. The banner keeps the sale's colours, black on bright yellow with a heavy black edge above and below, and it still stands out as the brightest thing on the page. It also passes 2.3.2 and 2.2.2 with nothing to keep in check.

## After: one flash a second, then still

```css
.flash-sale {
  padding: 2rem 1rem;
  background: #111;
  color: #ffd400;
  font-size: 2rem;
  font-weight: 800;
  text-align: center;
}
/* One change each way a second, three times: three seconds, then still. */
@media (prefers-reduced-motion: no-preference) {
  .flash-sale { animation: strobe 1s steps(1) 3; }
}
@keyframes strobe {
  50% { background: #ffd400; color: #111; }
}
```

Two values change: `0.2s` becomes `1s` and `infinite` becomes `3`. The banner goes yellow and back once a second, three times, and rests after three seconds, so no second ever holds more than one flash, well within three. WCAG notes that no tool is needed below that rate. Ending within five seconds also meets 2.2.2. It is written inside `no-preference` because it costs nothing and spares people who have asked for less motion, but the rate is what passes 2.3.1: the slow blink is safe for people who never set that preference.

## After: a fast flash only on a small badge

```html
<div class="flash-sale">
  <p><span class="badge">Now</span> Flash sale: 50% off everything until midnight</p>
</div>
```

```css
.flash-sale {
  padding: 2rem 1rem;
  background: #111;
  color: #ffd400;
  font-size: 2rem;
  font-weight: 800;
  text-align: center;
}
.badge {
  display: inline-block;
  padding: 0.125rem 0.5rem;
  background: #ffd400;
  color: #111;
  font-size: 0.875rem;
  vertical-align: middle;
  animation: strobe-badge 0.2s steps(1) 10;
}
@keyframes strobe-badge {
  50% { background: #111; color: #ffd400; }
}
```

The banner stays still and only the "Now" badge strobes, ten cycles in two seconds. At about 50 × 24 pixels it covers around 1% of the 341 × 256 field, far under the quarter 2.3.1 allows; even at 400% zoom it covers under a quarter. This holds only under conditions outside the snippet. The badge must stay that small at the largest zoom people use. Nothing else may flash in the same part of the screen, because flashes side by side add up. And it must stay out of red, since a red flash counts on its own. It still flashes five times a second, which fails 2.3.2 at Level AAA, and people who are highly sensitive can still react to it.

## Which option to use

All three pass 2.3.1. The still banner needs no measuring and passes every flash and motion criterion; what it gives up is the flash itself, so the design has to earn attention with colour and size. One flash a second keeps a blink as the page opens, at a rate below which seizures are uncommon, by changing two values; choose it when the brief asks for movement. The small badge keeps a fast strobe, but it passes only while it stays small and alone, and fails 2.3.2; choose it only when a fast flash is part of a brand you can't change, and plan to slow it when you can.

## Implementation decisions

Count flashes as pairs. A swap to light and back is one flash, so an animation that changes once each way per cycle flashes once per `animation-duration`. With `animation-direction: alternate`, each cycle is only half a flash, so `0.2s alternate` flashes 2.5 times a second. Keep every cycle at one second or longer and the count never needs care.

Some changes look like fixes and are not. Each leaves the flash in place for someone:

- A seizure warning before the page or the video: by the time it is read, the flash is on screen, and someone who has never had a seizure doesn't know to heed it.
- A Pause or Stop button: W3C notes that turning the flash off is not an option, because a seizure can come faster than most people could turn it off.
- The reduced-motion query alone: most people with photosensitive epilepsy have never set it. Write motion inside `no-preference` as well, never instead of a safe rate.

A change between two light colours is not a general flash when the darker of them has a relative luminance of 0.80 or more, such as a pale yellow flickering to a paler one. It passes on paper, but the next brand colour can break it, and a change to or from a saturated red counts as a red flash even with no change in brightness. Red is counted on its own because red flashing is especially likely to set off seizures: the red and blue flashing in a 1997 anime episode sent hundreds of viewers in Japan to hospital. Treat a pale flicker as the small badge is treated, and confirm it with a tool.

Video, animated GIFs, Lottie files and canvas animations hold the flash in their frames, so CSS can't fix them. Run each through a flash analysis tool at the size the page shows it, such as PEAT, a free Windows tool from the Trace Center, or IRIS, an open-source library from EA that checks video for luminance flashes, red flashes and patterns. Then fix the edit: cut or slow the strobe, darken it, or shrink it in the frame. A third-party ad or embed counts too, because 2.3.1 applies to everything on the page.

Flashing is not the only movement on a sale page. A banner that pulses or glows without end, a ticker and a countdown each have their own guides: [pulsing buttons, glowing badges and animations that never stop](/learn/endless-animations) and [tickers, carousels and animations that can't be paused](/learn/moving-content). A small badge or dot that blinks fast is the subject of [badges and icons that blink more than three times a second](/learn/fast-blinking).

## Verify the fix

1. List everything on the page that changes by itself: CSS animations, script timers, SVG animations, video, animated images, canvas, ads and embeds.
2. For each, count the flashes in its busiest second: slow the browser's Animations panel to 10%, or step through a screen recording frame by frame. Three or fewer passes; anything faster goes to step 3.
3. For anything that flashes faster, measure the flashing area at the largest zoom people use, together with anything else flashing beside it: under a quarter of a 341 × 256 CSS pixel area, and not red.
4. Run every video, animated image and canvas animation through a flash analysis tool at the size the page shows it, and confirm no general or red flash fails.
5. Turn Reduce motion off in the operating system and repeat steps 1 and 2: the page must pass for people who never set it.

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the design, a campaign banner, a video or the animation library changes.

## Limits

This is a starting pattern, not a guarantee for every application, and not medical advice. The thresholds lower the risk of a seizure but don't remove it: some people react below three flashes a second, and to still patterns of stripes or checks that 2.3.1 doesn't cover. The audit measures flashes it can read from the page, not video, animated images or canvas, and it doesn't add up small flashes that sit side by side. A flash analysis tool and a person still need to check what it can't see.
