# useMediaQuery

## Usage

SSR-safe media query hook that subscribes to `window.matchMedia` changes. Returns whether the given media query matches. The first value is read during setup whenever `matchMedia` exists, so a layout decision does not have to wait for mount; on the server the ref starts as `false`. Changes are followed while the component is alive, and the listener is removed on unmount.

The query argument is a plain string, read once per call. To follow a query that itself changes, call the hook again from wherever that value comes from.

```ts
import { useMediaQuery } from '@astryx-vue/core'
```

## Best practices

| Guidance | Practices |
| --- | --- |
| Do | Use for responsive layout switching based on viewport width, color scheme, or motion preferences. |
| Do | Prefer Astryx responsive tokens and component props over manual breakpoint logic when possible. |
| Don't | Use for server-rendered content that must match on first paint; on the server the hook always returns false. |

## Examples

Common configurations, variations, and states.

### useMediaQuery — Adaptive presentation

The media query is product policy and the presentation stays a deterministic override on top of it: the hook only answers whether the touch-oriented range matches.

```vue
<script setup lang="ts">
import { useMediaQuery } from '@astryx-vue/core'
import { SegmentedControl, SegmentedControlItem } from '@astryx-vue/core/SegmentedControl'
import { Text } from '@astryx-vue/core/Text'
import { computed, ref } from 'vue'

// Product policy: bottom sheet only on a narrow screen with no hover and a coarse pointer.
const TOUCH_RANGE = '(max-width: 1024px) and (pointer: coarse) and (hover: none)'

const isTouchRange = useMediaQuery(TOUCH_RANGE)
const override = ref<'auto' | 'dialog' | 'bottom-sheet'>('auto')

const presentation = computed(() => {
  if (override.value !== 'auto')
    return override.value
  return isTouchRange.value ? 'bottom-sheet' : 'dialog'
})
</script>

<template>
  <div class="media-query-preview">
    <Text type="supporting" color="secondary">
      {{ TOUCH_RANGE }}
    </Text>
    <code class="media-query-preview__value">
      matches: {{ isTouchRange }}
    </code>
    <SegmentedControl v-model="override" label="Presentation override">
      <SegmentedControlItem label="Auto" value="auto" />
      <SegmentedControlItem label="Dialog" value="dialog" />
      <SegmentedControlItem label="Bottom sheet" value="bottom-sheet" />
    </SegmentedControl>
    <Text type="body">
      Presentation: {{ presentation }}
    </Text>
  </div>
</template>

<style scoped>
.media-query-preview {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

.media-query-preview__value {
  padding: 2px 6px;
  border-radius: 6px;
  background: var(--astryx-color-background-muted);
  font-family: var(--astryx-font-family-code, ui-monospace, monospace);
  font-size: 0.75rem;
  color: var(--astryx-color-text-secondary);
}
</style>
```

## Parameters

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `query` | `string` | — | CSS media query string to evaluate, e.g. `'(max-width: 640px)'`. Read once per call, so pass a stable string. Required. |

## Returns

| Name | Type | Description |
| --- | --- | --- |
| `matches` | `Ref<boolean>` | Whether the media query currently matches. Read during setup on the client, and `false` on the server. |
