Defining keyframe animations inside media queries

Viewed 26580

I'm trying to get objects to "slide in" from the bottom of the screen, but since I can't get the screen height as a unit in CSS, I'm trying to do this with media queries, like so:

@media(max-height:500px) {
  @keyframe slideUp {
    0%    { transform: translate3d(0,500px,0); }
    100%  { transform: translate3d(0,0,0); }
  }
}
@media(max-height:750px) {
  @keyframe slideUp {
    0%    { transform: translate3d(0,750px,0); }
    100%  { transform: translate3d(0,0,0); }
  }
}
/* etc. */

This doesn't work (it uses the first version of slideUp regardless of height), so I assume keyframes, once defined, cannot be overwritten or reassigned based on media queries? Is there any way to achieve this effect (short of having many different keyframe setups and using a media query to assign the appropriate one to the class)?

5 Answers

Nowadays you can solve this using native CSS variables.

In your case:

@media(max-height:500px) {
    :root {
        --slide-up-y: 500px
    }
}

@media(max-height:750px) {
    :root {
        --slide-up-y: 750px
    }
}

@keyframes slideUp {
    0%    { transform: translate3d(0,var(--slide-up-y),0); }
    100%  { transform: translate3d(0,0,0); }
  }
}

A way of doing a slide up animation regardless of screen or element height is to use position: fixed:

.box {
    position: fixed;
    animation: slideUp 1s forwards;
}

@keyframes slideUp {
    from { top: 100%; }
    to { top: 0; }
}

Demo: http://jsfiddle.net/myajouri/Kvtd2/

If you want to slide relative to a parent element and not the viewport, use position: absolute instead.

Long time since this question was asked. But I'm seeing that nobody has answered the solution that I'll give you, and this one, in my opinion, is easier than creating different media-queries for different screen sizes.

@myajouri proposed you to use a fixed position and you discarded this solution because you need to use 3d transforms to get hardware acceleration. But you can still use 3d transforms with a fixed position. With CSS transformations, if you use percentages, they will be relative to the size of the element itself. This will allow you to move the element from outside the screen no matter what size it has, so, only one keyframe animation is needed. Check the next snippet:

body {
  margin: 0;
  padding: 0;
}

.element {
  animation: slideUp 1s ease-in-out infinite alternate;
  background: #CCC;
  border: 5px solid gray;
  box-sizing: border-box;
  height: 100%;
  position: fixed;
  width: 100%;
}

@keyframes slideUp {
  from {
    transform: translate3d(0, 100%, 0);
  }
  to {
    transform: translate3d(0, 0, 0);
  }
}
<div class="element" />

If you have to use translate3d to get hardware acceleration on mobile devices (as you mentioned), and given that you can't use at-rules inside @media:

You could define one @keyframes with large translateX (say 5000px) and change the animation-duration based on the screen height to ensure the speed is more or less the same across the different heights.

I would also define height ranges (max-height and min-height) as opposed to upper limits (max-height only) to prevent unwanted style overrides.

@keyframes slideUp {
    from { transform: translate3d(0,5000px,0); }
    to   { transform: translate3d(0,0,0); }
}

.box { animation: slideUp 5s forwards; }

@media (min-height: 0) and (max-height: 500px) {
    .box { animation-duration: 0.5s; }
}

@media (min-height: 501px) and (max-height: 750px) {
    .box { animation-duration: 0.75s; }
}

@media (min-height: 751px) and (max-height: 1000px) {
    .box { animation-duration: 1s; }
}

DEMO: http://jsfiddle.net/myajouri/9xLyf/

Related