DEV Community

loading...
Cover image for One media query you didn't know about! + Comparison with similar queries

One media query you didn't know about! + Comparison with similar queries

Gautam Tiwari
I'm a Front-end developer who likes to develop pixel-perfect components using only HTML and CSS or build a reusable component in React.js.
Originally published at animperfectcoder.hashnode.dev ・5 min read

Do you want to know when the user has an input device that can hover over elements? Want to display the hover state without hovering when there's no hovering device? Have you made an element invisible that shows up on hover, but you are stuck with it on a mobile device?

Do you know what? There's a CSS only solution for this!

Hi guys! I am Gautam Tiwari, from Uttarakhand, India. I am a front-end developer, learner, and dev blogger.

gautamtiwari image

Understanding the problem

I made a card component for my portfolio website (working on it). It shows an image, and the text content shows up on top of it. I gave the text content an opacity of 0. Now the text content becomes visible only when the user either hover over it or focus on it.

I settled with that until I realised that a mobile user would have a poor UX as they can't hover over the element.

I thought there must be a media query for this, so I Googled it and WUOLAH! I find a media query to solve this kind of problem and found three more similar media queries (which I'll explain in a few).

Here's the HTML code for the card component:

 <a
    rel="noopener noreferrer"
    target="_blank"
    href="https://bit.ly/35eecgK"
    class="recentlypublished__post"
>
    <img 
        src="https://bit.ly/2RUHcr6"
        alt="cover photo"
        class="recentlypublished__img"
    />
    <div class="recentlypublished__textbody">
        <p class="recentlypublished__date">May 30, 2021</p>
        <h3 class="recentlypublished__title">
            You are a developer just like me. No?
        </h3>
        <p class="recentlypublished__description">
            In the field of development, you will thrive if you keep
            learning, write good performing clean code, and stay away
            from...
        </p>
        <i
            class="
            fa fa-external-link-alt
            recentlypublished__externallinkicon
            "
        ></i>
    </div>
</a>
Enter fullscreen mode Exit fullscreen mode

Here's the CSS code (without the media query), which you can safely ignore as it is just for styling it:

*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  margin: 2rem;
  line-height: 1.6;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
 Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}

h1,
h2,
h3,
h4,
h5,
h6,
p {
  margin: 0;
}

img {
  display: block;
  width: 100%;
}

.recentlypublished__post {
  display: block;
  width: 100%;
  min-width: 200px;
  max-width: 600px;
  text-decoration: none;
  color: inherit;
  border-radius: 2rem;
  transition: box-shadow 200ms linear, transform 200ms ease-in-out;
  position: relative;
  color: #fefefe;
}

.recentlypublished__post:hover {
  transform: scale(1.05);
  box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.5);
}

.recentlypublished__post:focus {
  outline: 0;
  transform: scale(1.05);
  box-shadow: 0 0px 0 10px rgba(0, 0, 0, 0.19);
}

.recentlypublished__textbody {
  position: absolute;
  top: 0rem;
  left: 0rem;
  padding: 2rem 1.5rem;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 80ms linear;
  z-index: -1;
}

.recentlypublished__textbody > * {
  transition: transform 250ms ease-out;
  transform: translateY(2rem);
  text-shadow: 2px 0px 15px #00331c;
}

.recentlypublished__textbody::before,
.recentlypublished__textbody::after {
  content: "";
  position: absolute;
  border-radius: 2rem;
  z-index: -1;
  left: 0;
  width: 100%;
  height: 100%;
}

.recentlypublished__img {
  border-radius: 2rem;
}

.recentlypublished__title, 
.recentlypublished__description {
  line-height: 1;
}

.recentlypublished__description {
  position: absolute;
  bottom: 10%;
  width: 90%;
}

.recentlypublished__externallinkicon {
  position: absolute;
  top: 0;
  right: 0;
  padding: 1rem;
}

.recentlypublished__post:hover > .recentlypublished__textbody,
.recentlypublished__post:focus > .recentlypublished__textbody {
  z-index: 2;
  opacity: 1;
}

.recentlypublished__post:hover > .recentlypublished__textbody::before,
.recentlypublished__post:focus > .recentlypublished__textbody::before {
  top: 0;
  background-image: linear-gradient(
    180deg, 
    #232323cf, 
    #202020bf 20%, 
    transparent 60%
  );
}

.recentlypublished__post:hover > .recentlypublished__textbody::after,
.recentlypublished__post:focus > .recentlypublished__textbody::after {
  bottom: 0;
  background-image: linear-gradient(
    0deg, 
    #232323cf, 
    #202020bf 20%, 
    transparent 60%
  );
}

.recentlypublished__post:hover > .recentlypublished__textbody > *,
.recentlypublished__post:focus > .recentlypublished__textbody > * {
  transform: translateY(0rem);
}
Enter fullscreen mode Exit fullscreen mode

We have built a card component that shows text content on the :hover and :focus state. Cool!

Here's a preview that shows :hover and :focus state:
hover and focus demonstration

But what happens on a mobile or tablet?

Most mobile or tablets have a primary input of touch and have no support for hovering. For those mobile users, it would always look like this:

Mobile Preview

THAT is a bad UX. The user would not know the context of that article or link.

So, we shall now make the text visible to the user whenever the user has no hovering device.

Note: Users can tap and hold the card to activate the hover state, but they have no idea about that invisible content.

Introducing the any-hover media query

any-hover is the media query that checks if ANY available input mechanism allows the user to hover over the elements.

If they do allow hovering, then any-hover: hover holds true.

If they do NOT allow hovering, then any-hover: none holds true.

Using any-hover: none

Just add this code at the end of the previous CSS code.

/* Just for a responsive behaviour */
@media (max-width: 460px) {
  .recentlypublished__date {
    font-size: calc(0.3rem + 2vw);
  }
  .recentlypublished__title {
    font-size: calc(0.38rem + 2vw);
  }
  .recentlypublished__description {
    font-size: calc(0.2rem + 2vw);
  }
}

/* Just for a responsive behaviour */
@media (max-width: 280px) {
  .recentlypublished__description {
    display: none;
  }
}

/* -----any-hover implementation----- */
/* if any input device does not have
hover capability then: */
@media (any-hover: none) {
  .recentlypublished__textbody {
    opacity: 1;
    z-index: 2;
  }
  .recentlypublished__textbody::before {
    top: 0;
    background-image: linear-gradient(
      180deg,
      #232323cf,
      #202020bf 20%,
      transparent 60%
    );
  }
  .recentlypublished__textbody::after {
    bottom: 0;
    background-image: linear-gradient(
      0deg,
      #232323cf,
      #202020bf 20%,
      transparent 60%
    );
  }
  .recentlypublished__date,
  .recentlypublished__title,
  .recentlypublished__description {
    transform: translateY(0rem);
  }
}

Enter fullscreen mode Exit fullscreen mode

Completed Codepen below:

We added a media query any-hover: none to check if ANY of the available input mechanism does not support hovering (e.g. in a mobile phone there is no hover capable input device OR when a laptop has support for touch then any-hover: none and any-hover: hover both holds true). Then, we display the text content if it's true.

THAT'S IT!

Remember: In CSS, whatever comes later gets applied (if it has the same specificity). So, if you mess up with the order of media queries, you may get unexpected results.

BONUS: Three similar queries and their explanation

hover: hover|none: Same as any-hover but inquiries only for primary input mechanism.

pointer: fine|coarse|none: Is the primary input mechanism (provided by the browser) a pointing device, and if so, how accurate is it? (Coarse corresponds to a pointing device with limited accuracy)

any-pointer: fine|coarse|none: Same as pointer but tests for all input devices and holds true if anyone device satisfies the query.

Table that summarises it all

property/situation touch only cursor touch + cursor cursor + touch
hover none hover none hover
any-hover none hover hover hover
pointer coarse fine coarse fine
any-pointer coarse fine & coarse coarse & fine fine & coarse

Not for IE and Opera Mini

Browser support is a huge thing when talking about such features.
In 2021, interaction media features (of which any-hover is a part of) has fairly good support among the browsers.

Browser support screenshot from caniuse dot com

Conclusion

Here's an article that explains all this in more detail by Patrick H. Lauke. It literally covers almost everything you need to know more about these queries.

Subscribe to my newsletter and get articles like this directly in your inbox every Sunday.

Keep coding imperfectly. Keep coding experimentally.

Thanks for reading. I hope you learned something.

Discussion (0)