Grow your CSS skills. Land your dream job.

Fold Out Popups

Published by Chris Coyier

The trick with using hidden checkboxes/radio buttons, the :checked pseudo class selector, and adjacent sibling (~) combinators really enables some neat functional possibility with pure CSS1. If you need a primer, we recently used this to build a functional tabbed area.

Let's exploit it again to build "fold out popups". That is, links/buttons you can click to reveal tooltip-like rich HTML popup content.


View Demo   Download Files

HTML Structure

We need a checkbox for the on/off clickable functionality (don't worry, it's hidden). Then right after it is a label. Like any good label, the for attribute matches the inputs id attribute. That way we can click it to toggle the checkbox.

The label contains within it a number of spans. The .box span is the container for the rich HTML popup. In other words, put whatever you want in there. Well, whatever you want that are inline elements. It's tempting to use a div and header tags and stuff, but some browsers don't dig that and will render those things outside of the label instead of within. Making the .box outside of the label wouldn't be too big of a deal, as we could chain ~ selectors to get it to hide/show anyway, but having them within means we'll be able to position the popup relative to the position of the label, like a "popup" should.

<input type="checkbox" id="popup" class="popUpControl">
<label for="popup">
   Click me!
   <span class="box">
       Super cool popup content
   </span>
</label> 

CSS Basics

Our goal with the .box is to absolutely position it relative to the label it's within, so it pops up attached to it. By default, it will be hidden by having zero opacity. We'll also scale it down to nothing, skew it, and add transitions. Then when we reveal it by way of the checkbox becoming checked, we'll scale it back up, remove the skew, and make it visible by bringing the opacity back up to 1.

.box {
	position: absolute;
	left: 0;
	top: 100%;
	z-index: 100;
	
	/* Prevent some white flashing in Safari 5.1 */
	-webkit-backface-visibility: hidden;

	-moz-border-radius:    20px; 
	-webkit-border-radius: 20px; 
	border-radius:         20px; 
	
	width: 260px; 
	padding: 20px;
	opacity: 0;
			    
	-webkit-transform: scale(0) skew(50deg);
	-moz-transform:    scale(0) skew(50deg);
	-ms-transform:     scale(0) skew(50deg);
	-o-transform:      scale(0) skew(50deg);
	
	-webkit-transform-origin: 0px -30px;
	-moz-transform-origin:    0px -30px;
	-ms-transform-origin:     0px -30px;
	-o-transform-origin:      0px -30px;
	
	-webkit-transition: -webkit-transform ease-out .35s, opacity ease-out .4s;
	-moz-transition:    -moz-transform    ease-out .35s, opacity ease-out .4s;
	-ms-transition:     -ms-transform     ease-out .35s, opacity ease-out .4s;
	-o-transition:      -o-transform      ease-out .35s, opacity ease-out .4s;
}

.popUpControl { 
	display: none; 
}
.popUpControl:checked ~ label > .box {
	opacity: 1;
	-webkit-transform: scale(1) skew(0deg);
	-moz-transform:    scale(1) skew(0deg);
	-ms-transform:     scale(1) skew(0deg);
	-o-transform:      scale(1) skew(0deg);
}

The actual CSS for the box has some additional styling, but this the important functional stuff. Snag the download that accompanies this tutorial for the complete set of CSS.

Changing Text of Button When Open

This isn't vital to the idea here, but another neat trick in this demo is changing the text of the button when the popup is open. This works by actually hiding the text inside the button when :checked and inserting new text. The basics:

<input type="checkbox">
<label>
   <span>Default text</span>
</label>
[type=checkbox]:checked ~ label span { 
   display: none; 
}
[type=checkbox]:checked ~ label:before { 
   content: "Text to show when checked";
}

Yet another "hack" (bad, as generated content isn't very accessible or selectable). But hey, we're still having fun right?

1 Yeah but... JavaScript

This definitely walks "separation of concerns" line. Even though I've created this in "pure" CSS, I actually think this type of behavior is probably better suited to JavaScript. And by "use JavaScript", this is what I would actually do:

  1. Toggle a class name for "open" and "closed" on the .box, rather than use the checkbox.
  2. Change the text through the JavaScript, not use CSS generated content.

But everything else should stay in the CSS. The styling, the transitions, the transforms... That stuff is style not functionality.

Boy I'm a hypocrite hey?

Credit

Thanks to Victor Coulon who sent me this idea that I riffed on for this.

Comments

  1. Permalink to comment#

    I was really expecting to see dogs :(

  2. The demo says more dogs and a cat shows up! I can only think of a billion uses for something like this – I haven’t checked but do you know how this works/doesn’t work in older browsers?

  3. Chris
    Permalink to comment#

    Awesome as usual!

  4. Permalink to comment#

    Awesome Effect! Thanks for the share!

  5. DJ
    Permalink to comment#

    Chris… I notice that the pop-up (out) on the link eases in both directions (out and in); but, the photograph just triggers on then eases out. Is that how it is meant to be or is FF6.0 still missing some of your intended functionality?

  6. nott
    Permalink to comment#

    Great tips!

  7. Great effect. The source code is less than I think. Thanks for sharing.

  8. I think that you should have a
    cursor: pointer;
    int the CSS, because it just makes the link look un-clickable.
    I also thing that a :hover on the button would be good, just for nicer visual effects.

  9. Permalink to comment#

    Sadly it doesn’t work on iOS devices with mobile Safari.

  10. Permalink to comment#

    Great Chris ;)

  11. Very Nice Effect!

  12. Permalink to comment#

    Neat. In Windows, Chrome both reveals and hides the popup with the effect. Interestingly, Firefox will pop it open instantly (no effect) but will ease it back in on hide.

    • Yeah… super weird. Looking at that now. I don’t see why it would be doing that (tested Firefox 7). Maybe the generated content change? Weirdddd.

  13. Permalink to comment#

    Great as usual, but there is something I don’t understand in this line of code:

    .popUpControl:checked ~ label > .box {

    What the “~” and the “>” do?

    • Looks like this article from CSS Tricks might help you understand what “~” and “>” do: Child and Sibling Selectors.

      If you have any more questions, I’ll be happy to help :)

    • Thanks for sharing that post Aaron! Understanding that is paramount to understanding this tutorial. Essentially, it allows us to select a different element when a preceding element is in a different state (in this case).

  14. Brian Howard
    Permalink to comment#

    That’s odd. For me browsing the example in Safari 5.0.6 the “bubble” popup from the link example is underlined in blue (like the link is).

    • Yeah I forgot to mention that but I left it in there on purpose. The text-decoration: underline is propagating to the child elements even though the children have explicitly turned it off. Just one of those weird things. The CSS3 spec has some ways around that but there are no implementations yet.

  15. Hi, i have a question what if i want to use this effect on more than one link? when i do this, all the links open the first pop up

    • Permalink to comment#

      This may be because you did not change the id’s for each checkbox and the labels.
      Check out this example for more (I’ve striped some CSS – for webkit-only CSS3 – just to keep it a bit more handy).

  16. Jenny
    Permalink to comment#

    I have to click twice to get it to pup-out and click twice to get it to pup back in… (Chrome)

  17. Adam
    Permalink to comment#

    It didn’t work on my iPad… :(
    I didn’t get to see the cat

  18. Permalink to comment#

    Perfect – other than the cat it is exactly what i needed – the angle that it skews is slightly odd but i guess this can change?

  19. It doesn’t work. Only cat. Shit sample :)

  20. DJ
    Permalink to comment#

    Chris… the button does NOT animate open in FF6; however, it does animate closed. (The anchor link DOES animate both directions) – is that a known issue in FF6, a peculiarity to the code you wrote, or ?

    [I can't get over how good your new site style looks! To me at least, the most user friendly and appealing one since I've been stalking your site, lo these many years! I hope you do a screencast or posts about specifics of how you pulled it off... soon]

  21. Nick
    Permalink to comment#

    One things for sure, if any animal makes it to heaven, it would have to be cats. I mean, where else are we gonna get the strings for the harps? One time, I ran over a cat on purpose… I felt bad when i woke up the next morning, but i don’t feel bad anymore. Reason being: he was probably homeless, and I helped him find a better home: heaven… I probably shouldn’t have said this.

  22. Permalink to comment#

    I was thinking to make something that does this. I can’t and don’t like to work with JS so that was no option for me. the best I could come up with was using the hover selector (see my demo).
    then this post showed up.
    because I wanted multiple buttons and only one opened at the time I’ve used radiobuttons. and added one extra to make close button.

    just check my demo site:

    http://tuinderijdevork.nl/demo/

    works fine in: opera 11.51 and ff 6.0

    the closebutton is broken in midori 0.4.0 and chromium 12.0.742.112

    I didn’t try to fix it in webkit browsers yet, so suggestions (and other test results) are welcome.

  23. Justin
    Permalink to comment#

    I’m new to the whole transition / transform properties, but perhaps the reason a few of us are not seeing the animation effects in Firefox is due to a lack of ease-in declarations?

    Something like this:

    
    .popUpControl:checked ~ label > .box {
            -webkit-transition: -webkit-transform ease-in .35s, opacity ease-in .4s;
            -moz-transition:    -moz-transform    ease-in .35s, opacity ease-in .4s;
            -ms-transition:     -ms-transform     ease-in .35s, opacity ease-in .4s;
            -o-transition:      -o-transform      ease-in .35s, opacity ease-in .4s;
    }
    

    I implemented a variation of this in the javascript triggered version I built, and that did the trick; it now animates in and out in Firefox (6.01), Chrome, and Safari.

    Curious that Chrome / Safari still animate it even without those declarations though.

    Sweet tut, and amazing site, Chris. This is fast becoming a first resource for when I’m troubleshooting some new (to me) form of CSS. Excellent work.

    By the way, when I was checking out the demo page, my wife walked past my computer. She likes the cat. :)

  24. Marty
    Permalink to comment#

    This example doesn’t work in IE8 and the layout of the whole re-designed website doesn’t work in IE7 or 8, it’s all a mess with elements overlapping etc.

    • de vries
      Permalink to comment#

      Forget about IE. Maybe in IE9 some of the new stuff works, but I think its a waste of money and time to work around all IE’s shortcomings. I’m for the idea to show IE visitors a plain text version and attached to that the advice to switch to a real browser. (and maybe some explanation why you’re doing this…)

  25. Awesome. I’m going to try to put this to use in one of my next projects. Thanks.

  26. PvF
    Permalink to comment#

    Hello,

    I am quite new on CSS / HTML so please do not lough because of my dumb question :)
    Is it possible to have this functionality with link or even on image with mapping (i have a such solution on my case)? I have no idea how to change the code instead of INPUT tag.

    Thanks in advance!

  27. PvF
    Permalink to comment#

    Any ideas how to make CSS popup when i press or move mouse cursor over specific AREA of mapped image? I have few areas of the image that should call different popups.

    i have looked to ‘de vries’ posted demo link and tried to make something, but it seems that my skills are to low for this.

    • Permalink to comment#

      the first part of my demo (were the popup shows when the mouse moves over) is probably the easiest to understand. the code for that is in the css file (http://tuinderijdevork.nl/demo/css/de_vork.css) under /* FANCY MENU */. the code under /* FANCY MENU 2.0 */ is for the second part of the demo.

      the css code can be a bit messy because it is a part of a joomla-template i’m working on.

      ps. I still didn’t figure out why its not working with webkit. When I open a popup and then make the browser re-render the page by chancing any css code on the page it’s fixed. (if I only could expect my visitors to do that…) Ideas??, anyone?

  28. Andrew Roberts
    Permalink to comment#

    “But everything else should stay in the CSS. The styling, the transitions, the transforms… That stuff is style not functionality.”

    HTML = Content, CSS = Presentation, JS = Behavior

    I love the new stuff we have available in CSS3, but am I the only one that feels that interaction like tooltips and things like this fold out popup should be left to JS. To me, that IS functionality, behavior, and user interaction. I can see arguments for both sides, but I just feel like CSS is going to go beyond it’s purpose if we keep adding in more features like animation.

    Just a discussion starter :)

  29. Permalink to comment#

    Chris. Which plugin do you use for syntax highlighting?

  30. Mohammed Elkholy
    Permalink to comment#
  31. Permalink to comment#

    Thanks for the add. I was looking for something like that!

  32. pahnin
    Permalink to comment#

    Hey awesome effect

  33. Permalink to comment#

    Very neat !
    Will have to dissect the code to fully understand it though.

    Thanks !

  34. Nicely done with the CSS – like you say, there are always places where Javascript might be more suitable, just depends on what you’re trying to achieve I suppose.

    Animating is still going to be a problem with html/css for some time I think…

    Many Thanks,
    Darren.

  35. Nice CSS, but if you have tutorial about javascript notice me please..
    Many Thanks,
    Faris

  36. HarryErps
    Permalink to comment#

    so the example is great and works well but is there any way to apply it to a youtube video considering the embed uses a block-level element?

    ive tried encasing in a p but alas it just displays beneath the code as mentioned in the tutorial

  37. HolyMolly
    Permalink to comment#

    Thanks for the code, Chris! :) It’ s been almost 3 years, but still useful.

    Instead of ‘span’ nested in ‘label’ (as in your code), i’m trying to nest a ‘div class=”popout-box” ‘ that includes many things (lots of code). So ‘/label’ gets to be way down in the code which is not semantic.. If I close label before the div class, the pop-out-box doesn’t work.. Any suggestions how I can make it semantic and still get it to pop-out?

    Will be happy for any help, comments or advice :)

    Click

                        Content
                        &gt;
                        +
    
    
                        <a href="#" rel="nofollow">Notes </a>
                        <a href="#" rel="nofollow">Published</a>
                        <a href="#" rel="nofollow">Books</a>
    
    
                        <a href="#" rel="nofollow">
                                ooo
                                aaa
                            </a>
    

Leave a Comment

Current day month ye@r *

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