# How the @media (hover: hover) Guard Works for Hover Styles in Astryx

> Learn how Astryx uses @media (hover: hover) to apply :hover styles only on devices with true hover support, avoiding confusion on touchscreens.

- Repository: [Meta/astryx](https://github.com/facebook/astryx)
- Tags: deep-dive
- Published: 2026-07-02

---

**Astryx wraps every `:hover` style declaration inside an `@media (hover: hover)` media query to ensure hover effects only activate on devices that support true hovering, preventing confusing visual feedback on touchscreens.**

The `@media (hover: hover)` guard is a defensive CSS pattern implemented throughout the facebook/astryx repository to conditionally render hover states exclusively on mouse-capable devices. By nesting this media feature within `:hover` pseudo-classes using StyleX, the design system prevents unwanted overlays and background changes on smartphones and tablets while maintaining rich desktop interactions.

## What Is the @media (hover: hover) Guard?

The **`@media (hover: hover)`** CSS media feature evaluates to true only on devices with a primary pointing mechanism that supports hovering, such as a mouse or trackpad. On touch-only devices, this feature evaluates to **false**, causing browsers to ignore any rules nested inside the query. Astryx leverages this behavior to ensure that hover-specific visual effects—like background color changes or underlines—do not appear on mobile devices where users cannot hover, only tap.

## How Astryx Implements the Guard

Astryx builds its styling system on **StyleX**, which allows JavaScript objects to define complex CSS rules. The guard is implemented by defining a reusable constant and nesting it inside `:hover` declarations:

```tsx
// From packages/core/src/CommandPalette/CommandPaletteItem.tsx
const HOVER_HOVER = '@media (hover: hover)';

const styles = stylex.create({
  itemHover: {
    ':hover': {
      [HOVER_HOVER]: {
        backgroundColor: colorVars['--color-overlay-hover'],
      },
    },
    ':active': {
      backgroundColor: colorVars['--color-overlay-pressed'],
    },
  },
});

```

When StyleX compiles this object, it generates CSS that nests the media query inside the hover selector:

```css
.item:hover {
  @media (hover: hover) {
    background-color: var(--color-overlay-hover);
  }
}
.item:active {
  background-color: var(--color-overlay-pressed);
}

```

Because the `@media` block is nested within `:hover`, browsers only apply the background color change when both conditions are met: the user is hovering **and** the device supports hover.

## Where the Pattern Appears in the Source Code

The `HOVER_HOVER` guard appears consistently across Astryx core components:

- **CommandPaletteItem**: [`packages/core/src/CommandPalette/CommandPaletteItem.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/CommandPalette/CommandPaletteItem.tsx) (lines 29-55) defines the constant and demonstrates the nested structure.
- **Button**: [`packages/core/src/Button/Button.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Button/Button.tsx) (lines 77-84) applies the guard to all button variants, ensuring hover overlays only appear on desktop.
- **TopNavItem**: [`packages/core/src/TopNav/TopNavItem.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/TopNav/TopNavItem.tsx) (lines 65-70) uses the pattern for navigation item hover states.
- **Link**: [`packages/core/src/Link/Link.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Link/Link.tsx) (lines 62-68) wraps underline transitions in the guard to prevent persistent underlines on mobile taps.

## Benefits of the Guard

Implementing the `@media (hover: hover)` guard provides several advantages:

- **Accessibility**: Touch users avoid confusing "hover" cues they cannot trigger, reducing cognitive load and interface ambiguity.
- **Performance**: Browsers skip the nested rules on devices that do not support hover, slightly reducing style calculation overhead.
- **Design Consistency**: Hover effects automatically disable on mobile without requiring device detection logic in individual components.
- **Future-Proofing**: Convertible laptops or stylus-enabled tablets that advertise hover capability correctly receive hover effects when in laptop mode.

## How StyleX Compiles the Guard

Under the hood, StyleX processes the `stylex.create` call through three stages:

1. **Object Parsing**: StyleX identifies the `:hover` key and its nested object containing the `[HOVER_HOVER]` dynamic key.
2. **Rule Generation**: The compiler emits a CSS rule that nests the `@media` block inside the `:hover` pseudo-class selector.
3. **Runtime Injection**: The final CSS is injected into the document (or extracted during build), where browsers evaluate the media feature at viewport initialization.

This compilation step ensures the guard applies consistently across all components without runtime JavaScript overhead.

## Implementing the Guard in Custom Components

To add hover-safe styles to your own Astryx components, import the constant and nest it within your `:hover` declarations:

```tsx
import * as stylex from '@stylexjs/stylex';
import {colorVars, spacingVars} from '../theme/tokens.stylex';

const HOVER_HOVER = '@media (hover: hover)';

const styles = stylex.create({
  card: {
    padding: spacingVars['--spacing-3'],
    borderRadius: '4px',
    backgroundColor: 'transparent',
    cursor: 'pointer',
  },
  cardHover: {
    ':hover': {
      [HOVER_HOVER]: {
        backgroundColor: colorVars['--color-overlay-hover'],
      },
    },
  },
});

export function Card({children}: {children: React.ReactNode}) {
  return (
    <div {...stylex.props(styles.card, styles.cardHover)}>
      {children}
    </div>
  );
}

```

This approach ensures the hover background appears on desktops but remains disabled on smartphones, where only the `:active` state (if defined) would trigger.

## Summary

- **Astryx** uses the `@media (hover: hover)` guard to conditionally render hover effects only on devices that support true hovering.
- The pattern is implemented via **StyleX** by nesting the media query inside `:hover` pseudo-classes using the `HOVER_HOVER` constant.
- Key files implementing this guard include [`CommandPaletteItem.tsx`](https://github.com/facebook/astryx/blob/main/CommandPaletteItem.tsx), [`Button.tsx`](https://github.com/facebook/astryx/blob/main/Button.tsx), [`TopNavItem.tsx`](https://github.com/facebook/astryx/blob/main/TopNavItem.tsx), and [`Link.tsx`](https://github.com/facebook/astryx/blob/main/Link.tsx).
- This approach improves **accessibility**, **performance**, and **cross-device consistency** without requiring component-level device detection.

## Frequently Asked Questions

### What happens if I omit the @media (hover: hover) guard in Astryx?

Without the guard, hover styles will apply on all devices, including smartphones and tablets. On touch devices, this can cause the hover state to "stick" after a tap or appear when scrolling, creating a confusing user experience where visual feedback persists incorrectly.

### How does StyleX handle nested media queries inside pseudo-classes?

StyleX treats object keys starting with `@media` as media query rules and correctly nests them within the parent selector—in this case, `:hover`. During compilation, StyleX transforms the nested JavaScript object into valid CSS where the `@media` block resides inside the pseudo-class, ensuring proper specificity and cascade behavior.

### Does the @media (hover: hover) guard affect keyboard navigation or focus states?

No, the guard only affects the `:hover` pseudo-class. **Focus states** (triggered by keyboard navigation) are separate and typically use `:focus` or `:focus-visible` selectors. Astryx components often define distinct focus styles outside the hover guard to ensure keyboard users receive appropriate visual feedback regardless of device capabilities.

### Which Astryx components use this guard?

The pattern appears consistently across interactive components in `packages/core/src/`, including **CommandPaletteItem**, **Button**, **TopNavItem**, and **Link**. Any component that displays visual feedback on mouse hover—such as background color changes, underlines, or scale transforms—implements this guard to prevent those effects on touch-only devices.