The CSS :target
pseudo-selector in CSS matches when the hash in the URL and the id of an element are the same.

<section id="voters">
Content
</section>
:target {
background: yellow;
}
While that URL is as it is, that section
element will have a yellow background, as per our CSS.
When would you use this?
One possibility is when you want style with “states.” When the page has a certain hash, it’s in that state. It’s not quite as versatile as manipulating class names (since there can only be one and it can only be related to one element) but it’s similar. Anything you could do changing a class to change state you could do when the element is in :target
. For instance: change colors, change position, change images, hide/show things, whatever.
I’d use these rules-of-thumb for when :target
is a good choice:
- When a “state” is needed
- When the jump-down behavior is acceptable
- When it’s acceptable to affect the browser history
We’ll touch on all these things in this article.
How do you get hashes in URLs?
The most common way is by a user clicking a link that includes a hash. Could be an internal (same-page) link or a fully qualified URL that happens to end with a hash and value. Examples:
<a href="#voters">Go To There</a>
<a href="http://example.com/#specific-part">Go To There</a>
Jumping Behavior
Regardless if it’s a same-page link or not, the browser behavior is the scroll the page until that element is at the top of the page. Or, as far as it can if it can’t scroll that far. This is rather important to know because it means exploiting this “stated” behavior is a bit tricky/limited.
For instance, I once tried a variety of techniques to replicate functional CSS tabs, but ultimately decided using the checkbox hack was a better idea because it avoids the page-jumping issues. Ian Hansson at CSS Science has some examples of tabs as well. His third example uses :target
, and absolutely positioned elements hidden above the top of the page to prevent page jumping behavior. It’s clever, but not really a solution, because that would mean the page would jump upwards should the tabs be down further on a page.
A perfect use: highlighting sections
Here’s a problem: When a hash-link sends you flying down the page to the relevant section, it will try and make that section snug against the top of the browser window.
But what if there isn’t enough room to scroll beneath that section? That section will be visible, but it won’t be snug against the top, which can be weird and confusing.

I’m not just making that up. From personal experience, page-jumping links that don’t take me to somewhere where was I was linking to is exactly on the top, I get all out of sorts. I find it happens all to often on things like FAQ pages where the linked-to sections often aren’t very tall.
So let’s solve that!
One historical method was called the Yellow Fade Technique. It was employed by 37 signals in situations where new content is added to the page, and they were trying to draw the user’s attention to it. Jonathan Snook ported that idea to CSS and combined it with :target
.
Instead of yellow fade, we’ll indicate which section the link we just clicked was referring to by nudging it over to the right and flashing a red border. Instead of making you think, here you go:
The structure is a bit of navigation that links to sections by ID:
<nav>
<a href="#one">1</a>
<a href="#two">2</a>
<a href="#three">3</a>
</nav>
<section>
<div id="one"><h2>One</h2>Pellentesque habitant morbi ...</div>
<div id="two"><h2>Two</h2>Pellentesque habitant morbi ...</div>
<div id="three"><h2>Three</h2>Pellentesque habitant morbi ...</div>
</section>
When the sections become in :target, they scoot over to the right a bit via translateX
transform (prevents any weird text wrapping or anything we might get with padding) and a red border flashes on via keyframe animation.
:target {
animation: highlight 1s ease;
transform: translateX(20px);
}
@keyframes highlight {
0% { border-left-color: red; }
100% { border-left-color: white; }
}
section > div {
border-left: 40px solid white;
padding: 10px;
transition: all 0.5s ease;
padding-right: 50px;
margin-left: -20px;
}
That’s all there is too it really. I’d chalk this up under progressive enhancement, if you’re worried about browser support. As in, it’s just a nice touch, not vital.
Fighting the Jump!
Let’s say you like the idea of using :target
for states but dislike the page jumping behavior, you can change the hash link in a URL without a page jump.
Using jQuery, you could target all hash-links, prevent their default behavior, and use pushState (or replaceState, I suppose) to change the URL (which won’t move the page).
$("a[href^=#]").on("click", function(e) {
e.preventDefault();
history.pushState({}, "", this.href);
});
You could interchangeably use replaceState
there too, which would change the URL without adding an entry to the browser history. Sometimes you might want that, sometimes you might not. At least you have a choice here, which you don’t with the default behavior of clicking a hash link, which always adds.
But there is bad news
When the URL changes to a new hash, you’d think the current target would change and new CSS would take effect. It doesn’t (tested current WebKit and Firefox at time of this writing). It’s a bug.
Theoretically, you could measure and save the current scroll position of the page, let the link move it naturally, then set it back to where it was. But that just sounds so awful I couldn’t even bring myself to make a test page for it.
More
- The spec in Selectors Level 4 (no significant new stuff from what is above)
- A simple image gallery using :target (suffers from the page jump thing, good example of that) by Chris Heilmann
- Demo of yellow fade technique (although for existing content, not newly-added content) from Web Designer Notebook.
- A CSS Target, literally, by Caleb Ogden. (Offline now, but the Wayback Machine has a mangled version of it.)
Missing the usual mention of browser compatibility found in your other articles. When is it safe to use this technique and does it degrade gracefully?
You can use it in all modern browsers and IE9+. But IE has a problem:
From the quirksmode page
I used this method a while back to create a pure CSS click through slide show. It works pretty well. http://sitesbypalmer.com/themes/contrast/portfolio
off topic: i like your photos.
With regards the CSS Science tabs example, wouldn’t giving the hidden elements “position: fixed;” solve the jumping issue as they’d always be in the viewport then?
clevvvvver. Try making a demo?
I’m confused – isn’t the CSS Science page already using “position:fixed;” for the third example? There is no jumping as far as I can tell.
Ah yes, on inspecting his example the spans are in fact using “position: fixed;”.
I’ve created a fiddle on this: http://jsfiddle.net/pq9hx/1/embedded/result/
(very, very basic demo!!)
Wait, doesn’t that bug render the use of the pushState/replaceState technique practically unusable for now (and perhaps while bugged versions of those browsers persist)?
Err, though according to the bug report using location.hash does work, though I’m not exactly clear on the difference between that and pushState or replaceState at the moment.
(weirdly, paul irish seems to have reported tthat bug just earlier today. Oh that paul irish, always a step ahead of us all.)
I used to use “return false;” after any code to stop the page jump. For example (this was before I started using jquery) whenever I had an onclick event I would set href to “#” which would cause it to page jump. If I added “return false;” after my javascript code it wouldn’t do that (onclick=”example(‘test’); return false;”). Not sure if it would cause other issues though, didn’t appear to when I used it.
Great articles on target, previously I used in following these tutorials http://webstutorial.com/css3-tabs/css3 and http://webstutorial.com/html5-css3-toggle-slideup-slidedown/html-5
Previous tuts + your tut made me expert in :target
Thanks Chris
I saw a jQuery plugin not too long ago that made it so when you came from another website to the example page with a #hash in the link, it would stop the auto “throw you down the page instantly” thing and it would instead smoothly scroll from the top of the page down to that element. So people coming from different websites, clicking on a link that leads to the middle of the page in your website will be more oriented.
It didn’t really work though, it seemed to jump anyways. I’m wondering if anyone would know how to go about doing that with jQuery (the right way). I think you might have touched on something like that in this article but I’m not very great at jQuery so I wouldn’t know.
I’ve never seen anyone do this “right”, maybe it’s not really worth it though?
I’m not quite sure if you had my example in mind, because it does not jump, it really scrolls: http://osvaldas.info/incoming/examples/more-noticeable-html-bookmarks/#bookmark (article)
Another variation: I recently did a small demo using :target to hide content “off canvas” on smaller devices. See http://sandbox.webpro.nl/css3/adaptive-layout-pattern-off-canvas-variation.html (use small width to see it in effect).
Funny, I did something yesterday that abuses the :target selector excessively. I recommend viewing it in Opera, as in my tests it was the only browser to support transitions on transform: scaleY.
http://www.informatik.uni-freiburg.de/~dufners/gryth
I second the
position: fixed
way to make pages not to jump on:target
. I used it for popups there — http://kizu.ru/en/fun/popups/Played with :target few months ago http://rkrupinski.com/stuff/target/
I’d always wondered what it did exactly.
Now my mind is replete with monstrous creations that use the :target selector!
Mwahahahaha!!!
Hi !
I played with :target few month ago on http://creativejuiz.fr/trytotry/juizy-slideshow-full-css3-html5/ and I used the general siblings CSS selector combined to the :target pseudo-class in a “hidden” element (see the source). It works! No jump!
See also this CSS3 Parallax effect made with :target pseudo-class and transitions: http://www.creativejuiz.fr/trytotry/css3-parallax/
Thank you for the article ;)
In the article you mention that mine, the one on csscience, wouldn’t work if the tabs were further down the page.
The objects which are selected with :target have position fixed though, so that wouldn’t make a difference. I’ve purposely made the page long so you can scroll down a bit and check.
That’s flipping cool. Really useful for blogs and knowledge bases really.
This tutorial is interesting, this is using css animation, what I know is not all browser are support with css animation, but I like it, are its ok if using css animation?
After reading this I was wondering if you could use :target to create a CSS-only modal window (have seen a few examples of this, but none that use :target). Turns out you can…
http://jsfiddle.net/leonard_mcginty/SzhLq/
Haven’t done a great deal of testing on it, and of course it crashes and burns in the usual browsers, but always fun to experiment.
Ace tip as usual Mr Coyier :-)
Thanks for the data, There are a lot of things to learn, I haven’t heard about “:target” before, It is hard to keep up with everything
So happy I came across this script, I was a day or two away from adding a bunch of target links to a site i’ve been working on and this method will make the end result sickkk.
Pretty stoked, hope the webkit animation I have in mind degrades nicely in older browsers…
Hi there !
I’ve create an experiment with you technique, let’s see this !
http://cssdeck.com/item/307/animated-list-with-target
Hi Chris,
I played with :target a little after reading this post. Works great except on older IE, of course.
I used to use “return false;” after any code to stop the page jump. For example (this was before I started using jquery) whenever I had an onclick event I would set href to “#” which would cause it to page jump. If I added “return false;” after my javascript code it wouldn’t do that (onclick=”example(‘test’); return false;”). Not sure if it would cause other issues though, didn’t appear to when I used it.
How about to avoid Page Jumping Behaviour by javascript:void() ? i use it like this href=”javascript:void()”
I’ve used the :target pseudo-class a few different times in projects that were for modern browsers. Most recently, I’ve actually used it to create CSS-only accordions.
Granted, I know that jQuery can accomplish accordions, and that HTML5 has
summary
anddetails
. Butsummary
anddetails
doesn’t have wide browser support, while :target is pretty much IE9 and all modern browsers.So, if you find yourself in a situation where the very large corporate client doesn’t want you using JS libraries, but they’re ok with CSS3 (yes, it happened to me) it’s an approach worth considering. I wrote a blog post on the approach.
http://blog.frankmtaylor.com/2012/04/17/clever-css-tricks-using-target-to-create-accordions/
Chris, I believe I solved the problem you described in “Fighting the Jump!”
Instead of pushState/replaceState, using location.hash and location.replace to update the hash, respectively. These mechanisms will reevaluate the CSS correctly, bypassing the WebKit/Gecko bugs.
I wrote a blog post here:
http://www.zachleat.com/web/moving-target/
Ugh, nevermind—it is not a solution to Fight the Jump. Sorry Chris!
I was toying with the idea of non-JS pop-up (a friend at work asked if that’s possible) and, while working on it, I stumbled upon your text here. I’ve read that jumping of scrolling is unavoidable and it made me sad, but then I started trying to do that anyway. Eventually I came with the solution and it can be seen here: http://laboratorium.krazov.com/css-pop-up/. It has its flaws, of course, but that was just for fun.
(I noticed comments after I finished and quite possibly I don’t show anything new with my comment.)