# prefers_contrast

> The user's contrast preference from the prefers-contrast media query: more, less, custom or none.

Source: https://whiskerjs.com/signals/prefers-contrast/ (last updated 2026-10-26)

**prefers_contrast at a glance (from the library code, signalsVersion 9)**

| Field | Value |
| --- | --- |
| Component name | `prefers_contrast` |
| In the visitorId | yes |
| structuredId group | [prefs (display and accessibility preferences)](https://whiskerjs.com/docs/identifiers/#structuredid) |
| Hashing | plain value; a digest only when longer than 128 characters or not printable ASCII |
| Excluded under | no browser class |
| Inner timeout | 1000 ms |
| Added in | [signalsVersion 1](https://whiskerjs.com/whats-new/) |

> **In plain words:**
>
> Operating systems let people ask for more contrast (or less) to make screens easier to read. Browsers pass that preference on to web pages so they can adapt. WhiskerJS records the preference: `more`, `less`, `custom` or `none` for no preference.

## What it reads

The CSS `prefers-contrast` media feature through `window.matchMedia()`.

## How the technique works

| Order | Query | Value reported |
| --- | --- | --- |
| 1 | `(prefers-contrast: more)` | `more` |
| 2 | `(prefers-contrast: less)` | `less` |
| 3 | `(prefers-contrast: custom)` | `custom` |
| 4 | `(prefers-contrast: no-preference)` | `none` |

The first match is reported; none matching gives `unavailable` with `no_match`.

## Why it helps

Most users have no preference, but those who set one keep it. That makes the uncommon values stable and distinctive, while the common value is shared by most devices.

On your server, read it together with the other four preference components. They share the `prefs` part of the [structuredId](https://whiskerjs.com/glossary/#structured-id), so a result where only that part moved usually means a display or accessibility setting changed, not a different device. See [Comparing two results](https://whiskerjs.com/docs/identifiers/#comparing-two-results).

## Stability

Very stable: it follows a system accessibility setting that people rarely toggle.

## Browser support and what blocks it

Current [Chrome](https://whiskerjs.com/browsers/chrome-desktop/), [Edge](https://whiskerjs.com/browsers/edge/), [Firefox](https://whiskerjs.com/browsers/firefox/), [Safari](https://whiskerjs.com/browsers/safari-macos/) and [iOS browsers](https://whiskerjs.com/browsers/safari-ios/) answer `prefers-contrast`; a browser version that does not know the feature reports `no_match`. No browser class excludes it.

## Privacy

An accessibility preference. `more` can hint at visual needs. WhiskerJS uses it only as a device characteristic and never acts on it. Drop it with `collect({ exclude: ['prefers_contrast'] })` if your purpose does not need it (this changes ids). Describe it in your [privacy notice](https://whiskerjs.com/privacy/privacy-notice/).

## How WhiskerJS handles noise and failures

| Status | ruleId | When |
| --- | --- | --- |
| `ok` |   | `more`, `less`, `custom` or `none` |
| `unavailable` | `no_api` | no `matchMedia` |
| `unavailable` | `no_match` | no query matched |
| `blocked` | `threw`, `security_error` | `matchMedia` throws |
| `excluded` | `caller_excluded` | you excluded it |
| `timed_out` | `timeout`, `aborted` | budget or abort |

## Example value

All three headless test browsers in the container reported `none`.

## Questions and answers

### What does custom mean?

The user has a contrast setting that is neither clearly more nor clearly less, for example a forced colour palette with medium contrast. Browsers report it as custom.

### Why is none the most common value?

none is reported when prefers-contrast is no-preference, which is the default for most users.

## Related pages

- [forced_colors](https://whiskerjs.com/signals/forced-colors/): Forced colour modes.
- [reduced_motion](https://whiskerjs.com/signals/reduced-motion/): The motion preference.
- [Privacy](https://whiskerjs.com/docs/privacy/): Data minimisation and the exclude option.
