Pure CSS Expandable/Collapsable "Tree" List

This technique uses the "checked" quality of a checkbox to tell its adjacent label and the label's adjacent UL of its state, which in is used to set the visibility of the UL. There is no JavaScript involved. The part that sets the visibility of the UL is literally just two lines. It looks like this:

input + label + ul { display:none; } input:checked + label + ul { display:block; }

Most of the CSS in this page is just to style the list with the "tree" lines. It uses the "::before" pseudo-element to put a small, absolutely positioned box with bottom and left borders to the left of each LI, aligned with a border on the left of the LI. It uses ":last-child" to prevent the last LI from having a border that continues on down (on the other LI's it is used to connect all of the positioned boxes together).

For demonstration purposes, the positioned, generated boxes have a different border color (blue-green) than the border of the LI's (powder blue). Normally they would be the same color in order to complete the illusion. Also for the demo, the checkboxes are there with 20% opacity, but in practice they can have "display:none" because the label can change their state even when the checkbox is not visible or in the flow. The labels can be given some padding on the left and a background image of a folder, plus-sign, etc. and the "input:checked + label" selector can change it based on the checkbox. The LI's can be given icons too, such as a small document icon as a background image.

Browser support

Safari 3 displays the page correctly in its initial state, according to the "checked" states of the checkboxes as set in the HTML, but it does not update based on the user's changes (even if you click directly on the checkbox instead off the label). FireFox 3 and Opera 9.26 both handle the checkbox state well to allow the tree lists to open and close, but they both have problems with the other styling: FireFox doesn't absolutely position the generated content (or take it out of the flow) as it was instructed to, but this is worked around with a CSS hack that feeds it a bunch of negative margins instead. Opera does not seem to understand "last-child", so the border ends up extending down too far on each branch of the tree. So FireFox 3 fares best (with the hack), Opera works fine but doesn't look as nice, and Safari looks correct after it loads but is non-functional after that.

Click on "Brad" below, to begin. Then click on its child elements. Rinse and repeat.

View source for the CSS. Note: This technique is similar to the tab box I created that uses radio buttons and their labels to activate each tab.

PS: The borders disappear in FireFox if you refresh the page, but come back if you hover the mouse over the first checkbox. The red and green help text is generated with "::after" content.