Posts tagged css
Building on Tags and Pages, I've added one more navigation tool. Now, if you hover over Blog in the header, you'll get a drop down menu of some popular tags. Since I don't really have any readers, by popular, I mean what I think is important.
Implementation-wise, I store the list of tags that I want to display in Redis as a JSON array. I can edit that list by going to Application Settings without doing a redeploy.
For the front-end, there's no JavaScript. The HTML looks something like
<ul id="header-menu">
<li class="header-link blog"><a href="/" class="header-link">Blog</a>
<ul class="header-dropdown-menu">
<li class="header-dropdown-link header-link blog"><a href="http://www.phillypham.com?tag=math" class="header-dropdown-link header-link">Math</a></li>
<li class="header-dropdown-link header-link blog"><a href="http://www.phillypham.com?tag=cooking" class="header-dropdown-link header-link">Cooking</a></li>
<li class="header-dropdown-link header-link blog"><a href="http://www.phillypham.com?tag=algorithm" class="header-dropdown-link header-link">Algorithms</a></li>
<li class="header-dropdown-link header-link blog"><a href="http://www.phillypham.com?tag=stat" class="header-dropdown-link header-link">Statistics</a></li>
<li class="header-dropdown-link header-link blog"><a href="http://www.phillypham.com?tag=meta" class="header-dropdown-link header-link">Meta</a></li>
</ul>
</li>
</ul>
As for the CSS, the key part is
ul.header-dropdown-menu {
position: absolute;
list-style: none;
padding: 0px;
margin: 0px;
overflow: hidden;
max-height: 0px;
transition: max-height 0.5s ease;
background: rgba(254, 254, 254, 0.9);
}
li.header-link:hover > ul {
max-height: 252px;
}
We have that the position
is set to absolute
, so it displays on top of everything without affecting the layout of the rest of the page. We set overflow
to hidden
and max-height
to 0px
, so that it's not displayed. Then, we use a child selector, so when we hover over the parent element, li.header-link
, we select the child ul
and adjust its max-height
. The transition
property just pretties things up.
Here's the full CSS.
ul#header-menu {
align: bottom;
list-style: none;
padding: 0px;
margin: 0px;
}
li.header-link {
display: inline-block;
line-height: 28px;
padding-top: 0px;
padding-bottom: 0px;
margin: 0px;
min-width: 140px;
}
a.header-link {
color: #003b52;
display: block;
margin: 2px;
font-variant: small-caps;
text-decoration: none;
font-size: 125%;
}
a.header-link:hover {
color: #d02027;
outline: 2px solid #d02027;
}
ul.header-dropdown-menu {
position: absolute;
list-style: none;
padding: 0px;
margin: 0px;
overflow: hidden;
max-height: 0px;
transition: max-height 0.5s ease;
background: rgba(254, 254, 254, 0.9);
}
li.header-link:hover > ul {
max-height: 252px;
}
li.header-dropdown-link {
display: block;
width: 100%;
}
a.header-dropdown-link {
outline: 1px solid rgb(34, 34, 34);
}
See how it smoothly expands and contracts. Very cool, right? I think so. Even the animation is done in CSS. Since the number of items in the drop down is dynamic, I would like to set the height
to auto
. Unfortunately, CSS transitions don't work in that case, so I instead set the max-height
to a number large enough. This has the disadvantage that the time of the transition is based on the max-height
, so it will transition faster than desired.
Perhaps the funnest part about this blog was writing the commenting system. One may want to comment on a comment. I found a way to support this behaviour with recursion.
In the layout, using Jade mixins, we have something like
mixin commentView(comment)
- var children = comments.filter(function(child) { return comment.id === child.commentId; })
.wmd-preview!= comment.bodyHtml
.children
.inner-children
each child in children
+commentView(child)
We need the inner-children
div to create the smooth transitions when you expand the sub-comments. Apparently, CSS transitions only work when the height is specified, so you can calculate the new height of children
with inner-children
.
We can also expand comments recursively with JavaScript:
function expandParent(node) {
if (node.classList.contains('comments')) return; // we've reached the top level
if (node.classList.contains('children')) {
node.parentNode.querySelector('.reply-expander').classList.add('expanded');
node.classList.add('expanded');
}
expandParent(node.parentNode);
}
I've left some sample comments below to show off the features. Apparently, transitions are only smooth in Chrome and Internet Explorer. Leave a comment below and try it out!