Skip to content

Match Media

<quiet-match-media> stable since 4.2

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.

Mobile
Tablet
Desktop
Dark
Light
Landscape
Portrait
Mouse
Touch
Reduced motion
Motion OK
HiDPI
Standard DPI
<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.

Landscape Portrait
<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.

Right-click to open the context menu Long-press to open the context menu
Cut Copy Paste
<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.

Dark mode Light mode
<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.

OS prefers dark OS prefers light
<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 .

Animations off Animations on High contrast Standard contrast Dark mode Light mode
<!-- 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.

Mobile Tablet Desktop Widescreen
<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.

Hover capable No hover Fine pointer (mouse) Course pointer (touch)
<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>

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

Print this page
<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.

Installed app Browser
<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.

HiDPI display Standard display
<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.

High contrast Standard contrast
<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.

Desktop with mouse Touch or small screen
<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.

CDN Self-hosted

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)
Search this website Toggle dark mode View the code on GitHub Follow @quietui.org on Bluesky Follow @quiet_ui on X

    No results found