:target animation displays on page load


#1

I have the following fairly basic CSS for showing a menu using CSS :target

nav {
  position: absolute;
  height: 100vh;
  display: block;
  width: 20vw;
  margin-left: -30vw;
  transition: margin-left 0.5s ease-in;
  background-color: lightgrey;
}

nav:target {
  margin-left: 0vw;
}

and some sample HTML:

<html lang="en">
	<head>
		<link rel="stylesheet" href="test.css" />
	</head>


	<body>

		<nav id="menu">
			<a href="#">Close</a>
		</nav>

		<a href="#menu">Show menu</a>
	</body>
</html>

While this works, in Google Chrome, when the page loads, the menu slides out.

I’ve worked out that this only happens if it’s an eternal stylesheet. If I use <style> tags and embed the CSS on the page it works as expected: The menu is completely invisible until the link is clicked.

It seems that Google Chrome sees the change from no styles applied when the page load to applying the styles in the stylesheet as changing the margin-left property and displays the transition.

Is there any way to achieve the same result without using inline styles or a <style> tag? I can apply the transition property to nav:target instead of nav but then the menu is only animated when sliding in, it doesn’t slide out and just disappears.

I can’t provide a jsfiddle because jsfiddle uses stlye tags.


#2

I don’t have this behavior in Chrome. Have you verified you’re not running an extension that’s causing issues?


#3

I am running chromium on linux but I’d imagine the result is the same on Windows. I’ll give it a go!

On a real website I’m using SEO tools like Webmaster Tools that take a screenshot of the real website display the menu in the screenshot.


#4

I’m in the latest Chrome on Windows 10, and dont see it… maybe it’s a Chromium thing.


#5

I’ve uploaded it here for testing: https://r.je/test.html

It happens to me on Chrome on Windows as well.

edit: It’s only the first time I visit the page. Once the browser has cached the CSS it seems OK, try ctrl+f5.


#6

I’ve just found an incredibly stupid looking hack that seems to work.

body:hover nav {
	transition: margin-left 0.5s ease-in;
}

Because :hover isn’t triggered until you move the cursor, the transition property isn’t applied until you move the mouse, even if the mouse is hovering over the page as the page loads. The page loads, moves the nav off the screen, then when you move the mouse the transition property is applied.

This works but it feels like it might not be very consistent across browsers.


#7

Ah this is why i’m not seeing it. Running it localhost it’s holding the cache eternally.

Okay…my impulse is to change the style with Javascript, because it needs to understand the concept of an opening and a closing, which CSS doesn’t quite implement…


#8

You are transitioning the margin-left so the page will display and then the menu will move off screen. Whether or not you will see that will depend on how quickly things have loaded.

I would start with the menu off screen to start with and then use translate to bring it back into view. There will be no delay in the menu being off screen to start with and transforms are much smoother than margins and do not stutter.

e.g.

nav {
  position: absolute;
  height: 100vh;
  display: block;
  width: 20vw;
  left: -30vw;
  background-color: lightgrey;
  transition: transform 0.5s ease-in;
}

nav:target {
  transform:translateX(30vw);
}

#9

Actually my logic may be flawed :slight_smile:

Test and see if it makes a difference though :wink:

Edit:

I tested online with external stylesheets and it does seem to solve the problem for me when I test in Chrome. Your versions shows the menu sliding away on first load but the translate method loads already hidden.


#10

Thanks! This makes sense.

In my real-world use case the navigation is a drop down menu of indeterminate length as there are different menus with different content, browser font-sizes and number of links will affect the element.

What I really want to do is top: -100%. I can use translateY(-100%) and that works, but if the navigation is longer than the screen, using top, the bottom of the navigation is visible because 100% seems to evaluate to -100vh.

I had a similar problem when trying to animate height: 0 to height: auto, the only way I could get it to work was animating translateY(-100%) to translateY(0).


#11

Yes height is awkward to animate and animation doesn’t work to auto anyway (although there are some hacks but they are limited in use).

Yes 100% when using translate refers to 100% of the elements size but when you say top:100% then that refers to 100% of the containing block and not the height of the element.

You could use translateY(-100%) to move the menu fully above the top of the viewport but then you will probably get the menu sliding off the screen on first page load again. If you restrict the menu to 100vh height then you could use both and avoid seeing the menu but of course you would then need to add overflow auto to the menu for menus that are taller than 100vh.

It’s a bit of a conundrum :slight_smile:

Also note that absolute elements will scroll with the document so if you were looking for something to come from the top of the viewport you will need to use position:fixed and then you really will need to set overflow:auto as fixed positioned elements cannot be accessed if they stic k out of the viewport.


#12

I went with the body:hover hack in the end. It works fine in Chrome, Firefox and Edge. Any browser where it doesn’t work correctly will either see the bug from the original post or not see the animation.

The three main browsers are fine and I’m pretty happy with the result. Click on “Code” or “articles” on my site here to see it in action: https://r.je/


#13

Yes looks fine :slight_smile: