Grow your CSS skills. Land your dream job.

Meet the Pseudo Class Selectors

Published by Chris Coyier

Pseudo class selectors are CSS selectors with a colon preceding them. You are probably very familiar with a few of them. Like hover:

a:hover {
  /* Yep, hover is a pseudo class */

They are immensely useful in a variety of situations. Some of them are CSS3, some CSS2... it depends on each particular one. Outside of IE, they have great browser support. In IE land, even IE8, support is pretty barren. However, the IE9 preview has full support of them. The link-related ones work but not much else. Let's take a brief look at each one of them. Don't worry, there isn't that many.

Link-related pseudo class selectors

:link - Perhaps the most confusion-causing link-related pseudo selector. Aren't all <a> links? Well not if they don't have an href attribute. This selects only those that do, thus is essentially the same as a[href]. This selector will become a lot more useful should any-element linking become reality.

:visited - Selects links that have already been visited by the current browser.

:hover - When the mouse cursor rolls over a link, that link is in it's hover state and this will select it.

:active - Selects the link while it is being activated (being clicked on or otherwise activated). For example, for the "pressed" state of a button-style link or to make all links feel more button-like.

There is a fun technique to remember all the link pseduo class selectors. Look at the first letter of each: LVHA ... LOVE HATE.

Input & link related pseudo class selectors

:focus - Defining hover styles for links is great, but it doesn't help out those who used keyboard navigation to get to the link. :focus will select links that are the current focus of the keyboard. This is not limited to links, but can be used (and really should be used) on inputs and textareas as well. Some would tell you to define a :focus style for anything that has a :hover style.

Form with a text input in focus. Yellow background is a focus style.
Form with a text input in focus. Yellow background is a focus style.

:target - The target pseudo class is used in conjunction with IDs, and match when the hash tag in the current URL matches that ID. So if you are at URL then the selector #home:target will match. That can be extremely powerful. For example, you can create a tabbed area where the tabs link to hash tags and then the panels "activate" by matching :target selectors and (for example) using z-index to move to the top.

:enabled - Selects inputs that are in the default state of enabled and ready to be used.

:disabled - Selects inputs that have the disabled attribute. A lot of browsers will make the input a faded out gray, you can control that with this selector.

Form using the :disabled attribute.

:checked - Selects checkboxes that are, wait for it, checked.

:indeterminate - Selects radio buttons that are in the purgatory state of neither chosen or unchosen (like when a page loads with radio button choices but no default is set).

Set of radio buttons in purgatory. Or more accurately, in their :indeterminate status.

Position/Number-based pseudo class selectors

:root - Selects the element that is at the root of the document. Almost certainly will select the <html> element, unless you are specifically working in some weird environment that somehow also allows CSS. Perhaps XML.

:first-child - Selects the first element within a parent.

:last-child - Selects the last element within a parent.

:nth-child(N) - Selects elements based on a simple provided algebraic expression (e.g. "2n" or "4n-1"). Has the ability to do things like select even/odd elements, "every third", "the first five", and things like that. Covered in more detail here with a tester tool.

:nth-of-type(N) - Works like :nth-child, but used in places where the elements at the same level are of different types. Like if inside a div you had a number of paragraphs and a number of images. You wanted to select all the odd images. :nth-child won't work there, you'd use div img:nth-of-type(odd). Particularly useful when working with definition lists and their alternating <dt> and <dd> elements.

:first-of-type - Selects the first element of this type within any parent. So if you have two divs, each had within it a paragraph, image, paragraph, image. Then div img:first-of-type would select the first image inside the first div and the first image inside the second div.

:last-of-type - Same as above, only would select the last image inside the first div and the last image inside the second div.

:nth-last-of-type(N) - Works like :nth-of-type, but it counts up from the bottom instead of the top.

:nth-last-child(N) - Works like :nth-child, but it counts up from the bottom instead of the top.

:only-of-type - Selects only if the element is the only one of its kind within the current parent.

Position/Number-based pseudo class selectors

Relational pseudo class selectors

:not(S) - Removes elements from an existing matched set that match the selector inside the parameter of :not(). So for example, all divs except those with a class of "music" = div:not(.music). The spec says that :not selectors cannot be nested, but they can be chained. Some browsers (Firefox) also support comma-separated selectors as the selector parameter, although chaining them would be a far safter bet. Also useful in conjunction with attribute selectors, e.g. input:not([disabled]).

:empty - Selects elements which contain no text and no child elements. e.g. <p></p>

Text-related pseudo class selectors / elements

::first-letter - Selects the first letter of the text in the element. Typical use: dropcaps.

::first-line - Selects the first line of text in the element. Typical use: setting the first sentence in small-caps as a typographical eye-catcher / lead-in.

:lang - This pseudo selector is in the CSS3 spec but only implemented in IE 8+. Will match anything that either has or is a descendant of an element with a matching lang attribute. For example, :lang(fr) will match any paragraph, even without a lang attribute, if the parent body had lang="fr" as an attribute.

Quick note

You can chain pseduo selectors just like you can chain class and ID selectors. This is particularly useful here while we are looking at :first-letter and :first-line. You probably wouldn't want to drop cap every single paragraph on the page, but just the first one, so, p:first-child:first-letter { }

Dropcap using :first-letter, which enlarges the font size and floats to the left.

Content-related pseudo "elements"

::before - Is able to add content before a certain element. For example, adding an opening quote before a blockquote or perhaps an preceding image to set apart a particular paragraph.

::after - Is able to add content after a certain element. For example, a closing quote to a blockquote. Also used commonly for the clearfix, where an empty space is added after the element which clears the float without any need for extra HTML markup.

Pseudo Elements vs Pseudo Selectors

These are appropriately called pseudo "elements" (not selectors) because they don't select any "real" element that exists on the page. This goes for these two, as well as the previous sections :first-letter and :first-line. Make sense? Like the first letter that ::first-letter selects isn't an element all to itself, it's just a part of an existing element, hence, pseudo element.

The double colons (::) make this distinction.

Tag Qualification

These selectors can be tag-qualified, meaning they will only apply if both the element (tag) and selector match. For instance:

p:first-child {
  color: red;

That will only match if the first child of another element is a `

`. If it's not, it won't match.


:contains() - As far as I know, this is gone. The current CSS3 spec has removed it. I don't know the story, let me know if you do. At a glance, it looks ridiculously useful (being able to select objects based on the textual content they contain). It may be because of problems, or having content in selectors being undesirable. My preference would be to have it select by elements rather than text, like p:contains(img), but alas, no such luck.

::selection - Allows the changing of style of selected text. It was drafted for CSS Selectors Level 3 but removed before it reached the Recommendation status. Despite this, it's implemented in some browsers, which will probably retain experimental support for it. For Firefox, you can use ::-moz-selection. More information here.

:required / :optional / :read-only / :read-write - Just use attribute selectors instead.

jQuery Usage

jQuery can use all of these in its selectors, which is awesome. Even awesomer, jQuery has additional pseudo class selectors available.

:first - Matches the first instance of the already matched set. This is different than :nth-child(1) which will only select if the selector matches and it's the first child. With :first, the selector matches, then it takes the first one it finds regardless of child position.

:eq(X) - jQuery doesn't support :nth-of-type as a part of it's selector engine, but this is very similar.It does now. This selects the Xth element from the already-matched set. It's also zero-indexed (0 is the first element) unlike :nth-child in which the first element is 1.

The above two concepts are kinda weird. Here's a demo.

:contains('text') - This is removed from CSS, but still works in jQuery.

:lt(X) - The same as :nth-child(-n+X), as in it selects the "first X elements"

:gt(X) - The same as :nth-child(n+X), as in it selects everything except the "first (X-1) elements"

:even - The same as :nth-child(even) or :nth-child(2n)

:odd - The same as :nth-child(odd) or :nth-child(2n+1)

:has(S) - Works like I wish CSS :contain did, where it tests if the element has a descendant of a certain selector before matching.

There are actually a whole bunch more, and all of them are clever and useful (or at least an improvement on readability) See the selector documentation for more.

NOTE: jQuery can't really help you with pseudo elements like :before and :after, but you can access their values in some browsers. E.g if a div had some :before generated content, you could get the value like:

var content = window
    .getComputedStyle($('div')[0], ':before')


Class selectors and pseudo class selectors have the same specificity weight:

li            {}  /* specificity = 0,0,0,1 */
li:first-line {}  /* specificity = 0,0,1,1 */  {}        /* specificity = 0,0,1,1 */

Typically they are used in conjunction or listed afterwards in CSS anyway, so hopefully it won't cause too many problems...

ul li.friend { margin: 0 0 5px 0; }
ul li:last-child { margin: 0; }

In that case the zeroing out of the margin would work (assuming it matched the same element), but only because the zeroing out is listed second (they have the same specificity). So... watch for that.


  1. Fascinating roundup – very useful, both as a reminder of things forgotten and as a source of new learning.
    I bet I’ll be using :target in the near future…

  2. Andre
    Permalink to comment#

    Thanks Chris for summing this up. Great job, as usual :-)

  3. Nice summary, thank you =)!

  4. I too haven’t used :target until now. Will have to try that in future. Very useful post, thanks.

  5. Chris
    Permalink to comment#

    Thanks! Did you made a test for browser support? A table that helps deceiding when I can use the selectors would be really appreciated :-)

  6. sebi kovacs
    Permalink to comment#

    i was looking for smth like this for a lot of time now. Thanks

  7. I didn’t actually realise there was this many of them!

    The link based selectors are standard, I would imagine everyone know what each of them does and use them on a regular basis.

    I have never really found myself needing to use a lot of them, but now I look at it and understand what they do, I might have to start using a couple… Especially :focus, :enabled & :disabled. Really would be good for adding that little extra to my contact forms.

    The text related ones are quite a cool as well, is :first-letter supported across all the browsers?

    Thanks for supplying all of these, just got to try and remember what they all do.

  8. Very interesting summary. Not being a CSS whiz I love these insights into what can be done.

  9. Michael
    Permalink to comment#

    very nice! The parent/child selectors are going to make some thorny javascript go byebye.

  10. visaap
    Permalink to comment#

    Nice summary indeed!
    There are a lot of them pseudo-classes I didn’t know of yet.
    Thanx again.

  11. Chris
    Permalink to comment#

    In reply to my first comment:

  12. Some pseudo-love!

  13. Permalink to comment#

    another very useful article, really appreciate the work you put in, thx chris

  14. Permalink to comment#

    Very useful! keep it up Chris!

  15. Permalink to comment#


    thank you for sharing such a nice summary.

    Maybe you should mention the difference between pseudo-classes and pseudo-elements and the use of double or single colons (::)


  16. Nice, Chris! I wasn’t aware of the :not and :empty psuedo classes. Too bad both of these are available in IE. DAMN IE!

  17. Great Post. I don’t tend to use :nth-last-child(N) and :nth-last-of-type(N) too often but may have to end up starting to use it more

  18. Nice round-up!

    Is the compatibility list for the browsers’ versions as simple as “it works in everything but IE version < 9"? I got a lot of clients trailing "browser-version-wise"… sadly.

    Oh, you got a little typo here :

    "You can chain pseduo selectors just like you can…”

  19. Permalink to comment#

    Great stuff. Pseudo classes are great…the only issue is browser compatibility. Thx for the list though!

  20. thinsoldier
    Permalink to comment#

    “Also used commonly for the clearfix, where an empty space is …”

    The clearfix HACK is not a good thing. CSS needs an official, documented way of clearing floats.

    Sure the clearfix HACK will be with us for the next 10 years minimum but that doesn’t mean we should just ignore this problem and not address is with a proper css attribute.

    • I like the ideas you’ve been posting around about a value for the overflow property for clearing floats.

      But, to be clear, the clearfix isn’t a hack. It’s perfectly valid CSS. It’s getting it to work in IE 6/7 that uses hacks.

    • thinsoldier
      Permalink to comment#

      Ok, you’re right. But it just doesn’t “feel” right to have to define all that stuff to get a simple, highly desirable behavior.

      Would you do an article on the reasoning behind why parent containers collapse when their children are floated. I can’t think of any scenario with any layout I’ve ever built where I _wanted_ a container to collapse. I think I prefer IE’s old behavior of always wrapping around floats. It’s just what makes the most sense 99.99999% of the time (in my layouts)

    • That’s a great idea. It’s weird… but there IS a reason it works that way. And actually, if it worked the other way, I think we would be complaining MORE.

    • thinsoldier
      Permalink to comment#

      I disagree but I’d have to see the scenarios where you think it would be more of a problem.

      If you have a lot of images on a page with lots of text and each image tag was with a paragraph tag and for some reason all paragraphs were set to clearfix then it would not look great if a lot of the images were taller than their pagraphs.

      But most of the time in my work when I use an image tag it’s in its own p/div/figure with a 1 sentence description. So, if that figure is floated it can run along side multiple paragraph blocks if needed.

    • Permalink to comment#

      It has nothing to do with whether or not you “want” a container to collapse. The reason that the container collapses is because when you add “float” to an element, it is then removed from the document flow. To quote SitePoint’s CSS reference on clearing floats: “A floated box is taken out of the flow, so it doesn’t affect the block-level boxes around it.” (which is the standard behavior and is required in order to make floats work properly.)

    • @Lee: yeah, if an image is floated left in a paragraph and the image is longer than the paragraph, you want the next paras to flow round the image too. I think that’s what float was designed for – it’s just a shame we have to use it for everything else when avoiding tables.

    • The official way is display: table and display: table-cell etc. But guess who does not support that thus making it useless in real life. Hint: It’s the same browser that also only supports a few of this pseudo-selectors.

  21. Permalink to comment#

    You forgot a:focus, which is what I usually use instead of a:active for tabbing support (highlight the current tabbed link).

  22. When are we going to get parent selectors? So, for example, if we want to set a property for the parent a tag wrapped around an img tag… which is almost always a bit of a pain. My latest solution is to just give linked images a class .imglink that overrides my other link styles so I don’t get a weird background or other odd behavior, but it would be so much simpler to set a css rule for img:parent a (or however the syntax would work – a:parent img?) Have you heard anything about this in CSS3? (or do we already have something like this that I’ve missed?)

    • I should say I give the parent “a” tag the imglink class… <a class=”imglink”><img…

    • thinsoldier
      Permalink to comment#

      I’ve asked the same question elsewhere. I once got a reply from someone claiming to be familiar with the code in both webkit and firefox and they said that 2 issues preventing it from happening were speed and the order in which the browser applies the css.

      I honestly can’t believe the speed excuse. Pretty much every javascript library allows you to find an element and then climb the dom tree to its parent and ancestors. If jquery & prototype can do it, why can’t the browser do it even faster?

  23. kn33ch41
    Permalink to comment#

    Good article. I haven’t found too many sites that explain the pseudo-class selectors that well. w3schools does not even cover more than the absolute basic ones. To be persnickety, though, you do also cover some pseudo-element selectors, which do not share the same name as pseudo-class. :)

    Nevertheless, this article has been bookmarked!

    For anyone who likes to explore the power CSS offers, this is another good article:

  24. Permalink to comment#

    Excellent article Chris.

  25. Weeeee
    Permalink to comment#

    And it is all works in IE6 / IE7 ? (IE8?)

  26. Awesome Post! I never knew about all the other pseudo selectors. Specially the one which enables us to select the last and the first element of an unordered list! It saves time and space to create extra classes for them (as and when required).

  27. As always, very nice and easy explaining. I’ll redirect “n00bs” to both w3schools and css-tricks ;-)

    I quote from the html specs:
    At all times, exactly one of the radio buttons in a set is checked. If none of the elements of a set of radio buttons specifies `CHECKED’, then the user agent must check the first radio button of the set initially.

    So how would that justify adding an indeterminate pseudo class in css?

    Also, p:empty{display:none;} which seemed like an easy fix to where users could remove data that would otherwise be displayed in a p element, did its work too good. In some weird cases it also set display:none to p elements that were absolutely not empty.
    So if you’re wondering why something is gone, skip this and just fix it server side ;-)

    :root didn’t seem to work too well either in my html5 page(luckily it didn’t matter, I just wanted to test it)

  28. Permalink to comment#

    Nice! Papa CSS sure does have a lot of “children.”

  29. Permalink to comment#


    Someone knows the real difference between pseudo-classes and pseudo-element ?

    Both are very different but I still can’t differentiate them.

    And I read over some website that there are pseudo-selector, pseudo-classes selector and pseudo-element selector. I’m so mixed !

    The only pseudo-element I use are a:link, a:visited, a:hover. And I though they were call pseudo-classes !

    Can someone can make it clear to me please ?

    Thanks !

  30. Very nice article Chris!
    I like the clearfix method and will probably implement it from now on. Thanks!

  31. Eamonn
    Permalink to comment#

    Love these posts that shed some light on underused and relatively unknown code. It’s like finding a door with a dust covered handle…

    Just hope there’s a floor on the other side ;)

  32. Permalink to comment#

    Using nth-child a lot lately. Didnt think of using :first-letter and :last-letter to create a stylized quote or blockquote. Really wish CSS3 offered a “current” pseudo class for elements like links in navigations :-)

  33. thank you,i’m a junior for css,usful for me

    am i right??

  34. through the study,i think i can do as say!

  35. Permalink to comment#

    Great round up Chris, some stuff i didn’t know, so this will be very handy.

  36. Permalink to comment#

    Nice round-up.

  37. Permalink to comment#

    Cris, as expected – Wonderful plus infornative article.

    I will certainly add p:first-child:first-letter {font-size:16px } to see if it has any positive effect on my readers at my blog.

    – Aery

    • Permalink to comment#

      This is not working in none Brwoser (FF, Safari, IE, …). What did i wrong?

      Whats works is p:first-letter, but thats not what i want.

  38. Chris this is a great explanation!

    It would be great if there was some sort of chart with these… and maybe even which browser support each? Anyone know if one exists? Even if it’s not as thorough as your article above?

  39. Tim
    Permalink to comment#

    It is always so interesting and much appreciated what knowledge this site offers! But honestly, as long IE does not play well it can’t be used in the next, what, 10 years?

    The necessary fallbacks for the mention browser make the use of all those great css handlings almost unnecessary.

  40. :target would actually be extremely useful and save on page load times. You would then only need to code a JavaScript fix for IE, while other browsers would only need to load CSS. Also being able to check for a hash is pretty cool, without the need of PHP or JS.

    Thanks for the article.

  41. Hey Chris, great article. I forget to apply :focus on my forms all the time, just one of those things I need to get in the habit of!

    Small typo, after the Quick Note header:

    “You can chain pseduo….” should be
    “You can chain pseudo….”

  42. Jonny Irving
    Permalink to comment#

    Another fantastic article. Making a note of a few of them now so I remember to include them in my next project. Thanks Chris.

  43. Eddy
    Permalink to comment#

    What do you recommend to use if you want to add the attribute of a self-closing element to the page? (This makes more sense in XML.) I can use :before or :after but there is nothing to be before or after. Hope you understand what I mean.

  44. Andrew
    Permalink to comment#

    Chris, awesome write up man! Can’t wait to go through it in detail!

  45. SitePoint CSS Reference ( says about calculating specificity:

    Count the class selectors (for example, .test), attribute selectors (for example, [type=”submit”]), and pseudo-classes (for example, :hover).

    To calculate c, count the number of other attributes and pseudo-classes in the selector.

    So I think the specificity of a pseudo class selector is NOT less than a regular class selector

    what is the truth?

  46. Permalink to comment#

    div id=asidegroup holding left and right asides can be set to opacity 0.2, then opacity 1.0 on hover… hover one and both pop into view. Here we style a physical div container for two objects in one physical location, and it works great!

    Now, say we want all Chickadee snaps scattered across a page to flutter and chirp when we hover any chickadee… how do we do that? Now, take it further, please. Say we want all girl Chickadee snaps to flutter and chirp when one girl is hovered, and ditto the boys when a boy is hovered naturally… Here we want to isolate certain page objects in one style group containing physically isolated likely class-associated objects (but it could be a named group of ID’s as well). How do we do that?

    I saw something like this on a Cornell Ornithology page, also on a Yellowstone Volcano page, but those were Mars Lander script. We want to explore style grouping using HTML5 and CSS3.

  47. Mark
    Permalink to comment#

    Chris, what you and TeamTom approach is at the programmatic scale a mix of very divergent word processor entities. For example, where :active traps yellow then :focus’ yellow here, then background sticks as a reminder of last accessed link until page surface is clicked – Internet Explorer. Whereas Chrome is unable to retain active focus instructions between literal instances, since it dives on primary, raw link-related focus implementation. Anyway, just as active imprints focus, setting active apart in the top reliability seat (top of this page, lol), well focus though less intrusive also consistently imprints the programmatic interface with the same reliability as, say, hover. I notice you have removed my older post here (guess it was not perfectly understood by all and sundry), where we challenged Nth of n using focus as a “delimiter”. In many ways focus is more reliable than hover, though its public profile is crudely scatted on by the likes of Google, rushing to implement as little as possible as quickly as possible. Let’s not pull the wool over our eyes, what? I mean, public interface we share here is a very small segment of what is Internet, each interface providing a unique set of programmatic interpolations. Where interface commanders (nor commandos) set the score, :focus would also be seated top-of-page. Is this too heady (or particular)?

  48. socialblogsite

    You have an error in the description of last-child.
    You said “…of its type…” but it should be “if the last element happens to be of that type” or so.

    and that’s the definition for nth-last-of-type, not last-child… and great, somebody catched it 4 years ago ¬¬ and already commented it above.

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".