# detect-media

Live media-query facts as attributes — what CSS knows, Quark can now select on. One `<detect-media>` per query; `is-matched` follows `window.matchMedia` and flips as the query does.


```html
<div>
  <detect-media media-query="(prefers-color-scheme: dark)"></detect-media>
  <p>Dark color scheme — <code>is-matched</code> is set.</p>
  <p role="status">Light color scheme — <code>is-matched</code> is absent.</p>
  <small role="note">Live — switch your OS / browser color scheme (or emulate <code>prefers-color-scheme</code> in DevTools) to flip it.</small>
  <style>
    #demo-detect-media-simple {
      &:has(detect-media[is-matched]) p[role="status"] {
        display: none;
      }
      &:has(detect-media:not([is-matched])) p:not([role="status"]) {
        display: none;
      }
    }
  </style>
</div>
```


## Features

- **Live `is-matched`** Follows `MediaQueryList.matches` and its `change` event
- **Any media query** Color scheme, pointer type, motion, width — whatever `matchMedia` accepts
- **CSS / Quark selectable** Gate content on `detect-media[is-matched]`, no listeners
- **Provision + event** `.provision` is `{ mediaQuery, isMatched }`; `detect-media-change` fires on every flip

## Installation


`@excom/detect-media` v0.1.8

```bash
pnpm add @excom/detect-media
```

```bash
npm install @excom/detect-media
```

```bash
yarn add @excom/detect-media
```

### Import

```ts
import "@excom/detect-media";
```



## Usage

One element per query. `media-query` takes anything `window.matchMedia` does.

```html
<detect-media media-query="(pointer: coarse)"></detect-media>
```

Select on `is-matched` from CSS:

```css
body:has(detect-media[media-query="(pointer: coarse)"][is-matched]) .hover-hint {
  display: none;
}
```

Or from Quark — the attribute, or the provision:

```quark
detect-media[media-query="(pointer: coarse)"][is-matched] ~ nav {
  data-is-touch: "";
}
detect-media[media-query="(pointer: coarse)"]:not([is-matched]) ~ nav {
  data-is-touch: none;
}
detect-media[media-query="(prefers-color-scheme: dark)"] {
  $app-is-dark: prop("provision").isMatched;
}
```

Missing / empty `media-query` leaves `is-matched` unset and `provision` `null`; changing it re-subscribes. `detect-media-change` fires on every flip after mount, never on mount — react to the attribute for the initial state.

### API Reference


#### Attributes

| Name | Surface | Type | Default | Values | Description |
| --- | --- | --- | --- | --- | --- |
| `media-query` | option | `string` |  |  | Media query to evaluate, in `window.matchMedia` syntax: `(prefers-color-scheme: dark)`, `(pointer: coarse)`, `(width < 600px)`. Required — missing / empty leaves `is-matched` unset and `provision` `null`. Changing it re-subscribes. |
| `is-matched` | state | `boolean` |  |  | Present while `media-query` matches. Live — follows the query's `change` event. |

#### Provision

| Name | Type | Description |
| --- | --- | --- |
| `provision` | `DetectMediaProvision` (`{ mediaQuery: string; isMatched: boolean; }`) | `{ mediaQuery, isMatched }` — a new object on first evaluation and on every change; `null` without a query. Not reflected as an attribute. |

#### Fires

| Name | Type | Description |
| --- | --- | --- |
| `detect-media-change` | `DetectMediaChangeEvent` (`CustomEvent & { type: "detect-media-change"; detail: { mediaQuery: string; isMatched: boolean; }; bubbles: true; cancelable: true; composed: true }`) | Dispatched every time the query flips (not on mount). `event.detail` is the `.provision` payload. |



### Examples

#### Touch vs pointer views

Quark flips `is-active` on two `<include-content>` hosts from `(pointer: coarse)` — a true render, so the inactive view is not in the document. Toggle device emulation in DevTools to switch live.


```html
<div>
  <detect-media media-query="(pointer: coarse)">
    <include-content bind-touch>
      <template>
        <p>Touch layout</p>
      </template>
    </include-content>
    <include-content bind-pointer>
      <template>
        <p>Pointer layout</p>
      </template>
    </include-content>
  </detect-media>
  <quark-sheet>
    detect-media[is-matched] include-content[bind-touch] {
      is-active: "";
    }
    detect-media[is-matched] include-content[bind-pointer] {
      is-active: none;
    }
    detect-media:not([is-matched]) include-content[bind-pointer] {
      is-active: "";
    }
    detect-media:not([is-matched]) include-content[bind-touch] {
      is-active: none;
    }
  </quark-sheet>
</div>
```

## Release notes

### 0.1.1 (2026-09-23)

- Ship only dist (and declared extras) in the npm tarball; drop build logs, tests and sources
