Match Media
<quiet-match-media>
Conditionally renders content based on a CSS media query.
Conditionally renders content based on a CSS
media query. The default slot is shown when the query matches, and the optional fallback slot is shown
when it doesn't. Try toggling dark mode or changing your browser settings to see the values update.
<div id="match__overview"> <!-- Mobile --> <quiet-match-media query="(max-width: 599px)"> <div> <quiet-icon name="device-mobile"></quiet-icon> <small>Mobile</small> </div> </quiet-match-media> <!-- Tablet --> <quiet-match-media query="(min-width: 600px) and (max-width: 1023px)"> <div> <quiet-icon name="device-tablet"></quiet-icon> <small>Tablet</small> </div> </quiet-match-media> <!-- Desktop --> <quiet-match-media query="(min-width: 1024px)"> <div> <quiet-icon name="device-desktop"></quiet-icon> <small>Desktop</small> </div> </quiet-match-media> <!-- Color scheme --> <quiet-match-media query="(quiet-color-scheme: dark)"> <div> <quiet-icon name="moon"></quiet-icon> <small>Dark</small> </div> <div slot="fallback"> <quiet-icon name="sun"></quiet-icon> <small>Light</small> </div> </quiet-match-media> <!-- Orientation --> <quiet-match-media query="(orientation: landscape)"> <div> <quiet-icon name="crop-landscape"></quiet-icon> <small>Landscape</small> </div> <div slot="fallback"> <quiet-icon name="crop-portrait"></quiet-icon> <small>Portrait</small> </div> </quiet-match-media> <!-- Pointer type --> <quiet-match-media query="(hover: hover)"> <div> <quiet-icon name="mouse"></quiet-icon> <small>Mouse</small> </div> <div slot="fallback"> <quiet-icon name="hand-click"></quiet-icon> <small>Touch</small> </div> </quiet-match-media> <!-- Motion preference --> <quiet-match-media query="(prefers-reduced-motion: reduce)"> <div> <quiet-icon name="eye-off"></quiet-icon> <small>Reduced motion</small> </div> <div slot="fallback"> <quiet-icon name="sparkles"></quiet-icon> <small>Motion OK</small> </div> </quiet-match-media> <!-- Display resolution --> <quiet-match-media query="(min-resolution: 2dppx)"> <div> <quiet-icon name="photo"></quiet-icon> <small>HiDPI</small> </div> <div slot="fallback"> <quiet-icon name="photo"></quiet-icon> <small>Standard DPI</small> </div> </quiet-match-media> </div> <style> #match__overview { display: flex; flex-wrap: wrap; gap: 0.5rem; quiet-match-media { div { display: flex; flex-direction: column; align-items: center; text-align: center; min-width: 140px; gap: 0.5rem; padding: 1rem; border-radius: var(--quiet-border-radius-md); background: var(--quiet-neutral-fill-softer); } quiet-icon { font-size: 2rem; } } } </style>
Examples Jump to heading
Basic usage Jump to heading
Set the query attribute to any valid CSS media query. The default slot will be shown when the
query matches and hidden when it doesn't.
<div id="match__basic"> <!-- Landscape --> <quiet-match-media query="(orientation: landscape)"> <quiet-icon name="crop-landscape"></quiet-icon> <small>Landscape</small> </quiet-match-media> <!-- Portrait --> <quiet-match-media query="(orientation: portrait)"> <quiet-icon name="crop-portrait"></quiet-icon> <small>Portrait</small> </quiet-match-media> </div> <style> #match__basic { display: flex; justify-content: center; padding: 2rem; quiet-match-media { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; } quiet-icon { font-size: 3rem; } } </style>
Use the fallback slot to provide content that shows when the query does not match. This lets
you swap between two different sets of content declaratively.
<div id="overview__fallback" tabindex="0"> <quiet-match-media query="(hover: hover)"> <!-- Hover-capable devices --> Right-click to open the context menu <!-- Other devices --> <span slot="fallback">Long-press to open the context menu</span> </quiet-match-media> </div> <!-- Example dropdown --> <quiet-dropdown context-menu="overview__fallback"> <quiet-dropdown-item value="cut"> <quiet-icon slot="icon" name="scissors"></quiet-icon> Cut </quiet-dropdown-item> <quiet-dropdown-item value="copy"> <quiet-icon slot="icon" name="clipboard-copy"></quiet-icon> Copy </quiet-dropdown-item> <quiet-dropdown-item value="paste"> <quiet-icon slot="icon" name="clipboard-plus"></quiet-icon> Paste </quiet-dropdown-item> </quiet-dropdown> <style> #overview__fallback { padding: 3em 2em; border: dashed var(--quiet-border-width) var(--quiet-neutral-stroke-soft); border-radius: var(--quiet-border-radius-md); background-color: var(--quiet-paper-color); color: var(--quiet-neutral-text-colorful); font-weight: var(--quiet-font-weight-semibold); text-align: center; &:focus { outline: none; } &:focus-visible { outline: var(--quiet-focus-ring); outline-offset: calc(var(--quiet-border-width) * -1); } } </style>
Detecting the color scheme Jump to heading
Use the custom quiet-color-scheme query to detect the current color scheme in your app. It
tracks Quiet's class-based theming, so it reacts to in-app dark mode toggles. Try toggling dark mode on this
page to see it update.
<div id="match__color-scheme"> <!-- Quiet color scheme --> <quiet-match-media query="(quiet-color-scheme: dark)"> <quiet-icon name="moon"></quiet-icon> <small>Dark mode</small> <span slot="fallback"> <quiet-icon name="sun"></quiet-icon> <small>Light mode</small> </span> </quiet-match-media> </div> <style> #match__color-scheme { display: flex; justify-content: center; padding: 2rem; quiet-match-media { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; [slot="fallback"] { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; } } quiet-icon { font-size: 3rem; } } </style>
If you need to detect the operating system's color scheme instead, use the standard
prefers-color-scheme query.
<div id="match__prefers-color-scheme"> <!-- OS color scheme --> <quiet-match-media query="(prefers-color-scheme: dark)"> <quiet-icon name="moon"></quiet-icon> <small>OS prefers dark</small> <span slot="fallback"> <quiet-icon name="sun"></quiet-icon> <small>OS prefers light</small> </span> </quiet-match-media> </div> <style> #match__prefers-color-scheme { display: flex; justify-content: center; padding: 2rem; quiet-match-media { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; [slot="fallback"] { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; } } quiet-icon { font-size: 3rem; } } </style>
Detecting user preferences Jump to heading
You can detect various user preferences with media queries such as
prefers-reduced-motion
and
prefers-contrast.
<!-- Motion --> <quiet-match-media query="(prefers-reduced-motion: reduce)"> <quiet-badge> <quiet-icon name="eye-off"></quiet-icon> Animations off </quiet-badge> <quiet-badge slot="fallback" variant="primary"> <quiet-icon name="sparkles"></quiet-icon> Animations on </quiet-badge> </quiet-match-media> <!-- Contrast --> <quiet-match-media query="(prefers-contrast: more)"> <quiet-badge> <quiet-icon name="contrast-2"></quiet-icon> High contrast </quiet-badge> <quiet-badge slot="fallback" variant="primary"> <quiet-icon name="contrast-2"></quiet-icon> Standard contrast </quiet-badge> </quiet-match-media> <!-- Quiet color scheme --> <quiet-match-media query="(quiet-color-scheme: dark)"> <quiet-badge variant="primary"> <quiet-icon name="moon"></quiet-icon> Dark mode </quiet-badge> <quiet-badge slot="fallback" variant="primary"> <quiet-icon name="sun"></quiet-icon> Light mode </quiet-badge> </quiet-match-media>
You can emulate these preferences in your browser's developer tools to test different states. In Chrome, open DevTools and look for the "Rendering" panel.
Responsive breakpoints Jump to heading
Use viewport-based media queries like min-width and max-width to show different
content at different breakpoints. Try resizing your browser window to see the content change.
<div id="match__breakpoints"> <!-- Mobile --> <quiet-match-media query="(max-width: 599px)"> <quiet-icon name="device-mobile"></quiet-icon> <small>Mobile</small> </quiet-match-media> <!-- Tabler --> <quiet-match-media query="(min-width: 600px) and (max-width: 1023px)"> <quiet-icon name="device-tablet"></quiet-icon> <small>Tablet</small> </quiet-match-media> <!-- Desktop --> <quiet-match-media query="(min-width: 1024px) and (max-width: 1439px)"> <quiet-icon name="device-desktop"></quiet-icon> <small>Desktop</small> </quiet-match-media> <!-- Widescreen --> <quiet-match-media query="(min-width: 1440px)"> <quiet-icon name="viewport-wide"></quiet-icon> <small>Widescreen</small> </quiet-match-media> </div> <style> #match__breakpoints { display: flex; justify-content: center; padding: 2rem; quiet-match-media { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; } quiet-icon { font-size: 3rem; } } </style>
Input detection Jump to heading
Detect how the user interacts with the page using
hover
and
pointer
media queries.
<div id="match__input"> <!-- Hover --> <quiet-match-media query="(hover: hover)"> <quiet-icon name="mouse"></quiet-icon> <small>Hover capable</small> <span slot="fallback"> <quiet-icon name="mouse-off"></quiet-icon> <small>No hover</small> </span> </quiet-match-media> <!-- Pointer --> <quiet-match-media query="(pointer: fine)"> <quiet-icon name="pointer"></quiet-icon> <small>Fine pointer (mouse)</small> <span slot="fallback"> <quiet-icon name="hand-click"></quiet-icon> <small>Course pointer (touch)</small> </span> </quiet-match-media> </div> <style> #match__input { display: flex; justify-content: center; gap: 2rem; padding: 2rem; quiet-match-media { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; [slot="fallback"] { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; } } quiet-icon { font-size: 3rem; } } </style>
Print detection Jump to heading
The print media type matches when the page is being printed or previewed for print. This is
useful for swapping a print button with print-only content like a timestamp.
Printed on
<quiet-match-media query="print"> <!-- What the printer will print --> <p>Printed on <quiet-date></quiet-date></p> <!-- What screens will show --> <span slot="fallback"> <quiet-button> <quiet-icon slot="start" name="printer"></quiet-icon> Print this page </quiet-button> </span> </quiet-match-media>
PWA detection Jump to heading
Use
display-mode
to detect when your app is running as an installed PWA. You can show different content or features depending
on whether the user is in the browser or the standalone app.
<div id="match__pwa"> <quiet-match-media query="(display-mode: standalone)"> <quiet-icon name="apps"></quiet-icon> <small>Installed app</small> <span slot="fallback"> <quiet-icon name="browser"></quiet-icon> <small>Browser</small> </span> </quiet-match-media> </div> <style> #match__pwa { display: flex; justify-content: center; padding: 2rem; quiet-match-media { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; [slot="fallback"] { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; } } quiet-icon { font-size: 3rem; } } </style>
High-resolution displays Jump to heading
Detect HiDPI (retina) screens with
min-resolution. This can be useful for conditionally loading higher-quality images or showing resolution-aware content.
<div id="match__hidpi"> <quiet-match-media query="(min-resolution: 2dppx)"> <quiet-icon name="photo"></quiet-icon> <small>HiDPI display</small> <span slot="fallback"> <quiet-icon name="photo"></quiet-icon> <small>Standard display</small> </span> </quiet-match-media> </div> <style> #match__hidpi { display: flex; justify-content: center; padding: 2rem; quiet-match-media { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; [slot="fallback"] { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; } } quiet-icon { font-size: 3rem; } } </style>
Forced colors Jump to heading
Forced colors mode is an accessibility feature, most commonly seen as Windows High Contrast. Use this query to inform users or adjust content when forced colors are active.
<div id="match__forced-colors"> <quiet-match-media query="(forced-colors: active)"> <quiet-icon name="contrast-2"></quiet-icon> <small>High contrast</small> <span slot="fallback"> <quiet-icon name="contrast-2"></quiet-icon> <small>Standard contrast</small> </span> </quiet-match-media> </div> <style> #match__forced-colors { display: flex; justify-content: center; padding: 2rem; quiet-match-media { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; [slot="fallback"] { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; } } quiet-icon { font-size: 3rem; } } </style>
Combining queries Jump to heading
Combine multiple conditions with and to create more specific matches. This example detects a
desktop-sized viewport with a hover-capable input, which is a strong signal that the user is on a
traditional desktop computer with a mouse.
<div id="match__combining"> <quiet-match-media query="(min-width: 1024px) and (hover: hover)"> <quiet-icon name="device-desktop"></quiet-icon> <small>Desktop with mouse</small> <span slot="fallback"> <quiet-icon name="hand-click"></quiet-icon> <small>Touch or small screen</small> </span> </quiet-match-media> </div> <style> #match__combining { display: flex; justify-content: center; padding: 2rem; quiet-match-media { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; [slot="fallback"] { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; } } quiet-icon { font-size: 3rem; } } </style>
Listening for changes Jump to heading
The component doesn't dispatch events, but you can use
window.matchMedia()
directly to listen for changes in JavaScript. The native
MediaQueryListEvent
provides the matches property directly.
const mq = window.matchMedia('(prefers-reduced-motion: reduce)'); mq.addEventListener('change', event => { console.log('Reduced motion:', event.matches); });
Useful queries Jump to heading
Here's a reference of commonly used queries you can use with this component.
Custom queries Jump to heading
Custom queries are prefixed with quiet- and are handled by the component rather than the
browser's matchMedia API.
| Query | Description |
|---|---|
(quiet-color-scheme: dark) |
Quiet UI's local color scheme is dark |
(quiet-color-scheme: light) |
Quiet UI's local color scheme is light |
Standard media queries Jump to heading
These are standard CSS media queries passed directly to
window.matchMedia().
| Media Query | Description |
|---|---|
(prefers-color-scheme: dark) |
OS-level color scheme preference (does not react to in-app theme toggles) |
(prefers-reduced-motion: reduce) |
User prefers less motion and animation |
(prefers-contrast: more) |
User prefers higher contrast |
(prefers-reduced-transparency: reduce) |
User prefers less transparency |
(forced-colors: active) |
Forced colors mode is active (e.g., Windows High Contrast) |
(hover: hover) |
Primary input supports hover |
(pointer: fine) |
Primary input is a fine pointer (e.g., mouse) |
(pointer: coarse) |
Primary input is a coarse pointer (e.g., touch) |
(orientation: portrait) |
Viewport is taller than it is wide |
(orientation: landscape) |
Viewport is wider than it is tall |
(display-mode: standalone) |
App is running as an installed PWA |
(min-resolution: 2dppx) |
HiDPI / retina display |
(min-width: Npx) |
Viewport is at least N pixels wide |
(max-width: Npx) |
Viewport is at most N pixels wide |
print |
Document is being printed or previewed for print |
API Jump to heading
Importing Jump to heading
The autoloader is the recommended way to import components but, if you prefer to do it manually, the following code snippets will be helpful.
To manually import <quiet-match-media> from the CDN, use the following code.
import 'https://cdn.quietui.org/v5.0.0/components/match-media/match-media.js';
To manually import <quiet-match-media> from a self-hosted distribution, use the
following code. Remember to replace /path/to/quiet with the appropriate local path.
import '/path/to/quiet/components/match-media/match-media.js';
Slots Jump to heading
Match Media supports the following slots. Learn more about using slots
| Name | Description |
|---|---|
| (default) | Content to show when the media query matches. |
fallback
|
Content to show when the media query does not match. |
Properties Jump to heading
Match Media has the following properties that can be set with corresponding attributes. In many cases, the attribute's name is the same as the property's name. If an attribute is different, it will be displayed after the property. Learn more about attributes and properties
| Property | Description | Reflects | Type | Default |
|---|---|---|---|---|
query
|
The CSS media query to evaluate. Also supports the custom query
(quiet-color-scheme: dark|light) to detect Quiet's local color scheme instead of the
system-level prefers-color-scheme.
|
|
string
|
''
|
isMatch
|
Whether the media query currently matches. |
|
boolean
|
false
|
Custom States Jump to heading
Match Media has the following custom states. You can target them with CSS using the selectors shown below. Learn more about custom states
| Name | Description | CSS selector |
|---|---|---|
matches |
Applied when the media query matches. Target with quiet-match-media:state(matches).
|
:state(matches)
|