Jekyll comes with a built in ‘Related Links’ feature, however - if you run your Jekyll blog on Github Pages then it’s just the latest N posts.
Related posts by common tags would take about 3 lines of ruby, but because Github Pages is so locked down1 I must do it in Liquid. This really isn’t what liquid is for, but it doesn’t have to be fast or pretty - it’s only run once per deploy.
Here is my Liquid-only related posts script. It reminds me of the dark horror-filled days of Smarty templates. *shudder*
I was going to go through and explain every line but liquid leaves me cold. I don’t understand it enough to know its limitations properly - such as why arithmetic works better with strings than numbers - I just do what it wants and be sad about the verbosity and inefficiency.
Often when you want anything more than the simplest of blog features and host on Github Pages you have to turn to the Octopress model and bake everything locally, pushing up pre-rendered html like an animal. Doing that felt like admitting defeat too early, and I don’t want to be restricted to only updating my blog from my own computer. ↑
Foundation 4 had a sections module that would be an accordion menu at narrow widths and a tab pane at wider widths.
I have set out to recreate this using CSS alone - based mostly on the wonderful orderflexbox property.
Don’t duplicate content.
Use no unnecessary HTML elements
Have a sensible/coherent source order
Flexible about the amount of content.
Don’t worry about browser support - polyfills are a wonder.
The HTML I ended up with initially was very simple:
<divclass="sections"><ahref="#one"id="one"class="tab">One</a><section><p>The first section</p></section><ahref="#two"id="two"class="tab">Two</a><section><p>The second section</p></section><ahref="#three"id="three"class="tab">Three</a><section><p>The third section</p></section></div>
The tabs target themselves so we can use the target pseudo-class .tab:target to select to the currently highlighted tab, and the sibling selector .tab:target+section to select its section.
But as I wrote this page and it got longer and became scrollabe the target was getting scrolled to the top. So, I’ll switch to using input[type="radio"] and :checked. woo.
<divclass="sections"><inputtype="radio"id="one"checked><labelclass="tab"for="one">One</label><section><p>The first section</p></section><inputtype="radio"id="two"><labelclass="tab"for="two">Two</label><section><p>The second section</p></section><inputtype="radio"id="three"><labelclass="tab"for="three">Three</label><section><p>The third section</p></section></div>
The css of the accordion menu is very straghtforward.
The selected tab’s content gets stacked on top. Because of the wonder of flexbox all the tab panes are flexibly the same height, and so the first can be simply overlapped.
And with all of this in place, we can slot everything into media queries and everything is wonderful, and easily add all the styles we could want in each case, without fighting with any js applied styles.
You may or may not have noticed that all my quote marks are outdented if they fall at the beginning of a line.
You may or may not think I’m crazy for not only caring, but writing a 50 line script to do it.
The typography nerds call this hanging punctuation.
The heart of any such script is, of course, a regular expression. I’m sorry.
The expression gets any series of quote marks (/[`'"“”‘’«»‹›]+/) that either begin at the start of a word (/^|\s/), or at the end of a word (/$|\s/). This successfully grabs all the quote marks and ignores all the apostrophes.
Next we wrap the quotes in something we can grab and manipulate. We don’t want to affect anything inside a code element, or a table, and we want this script to be safe to run repeatedly without wrapping the already-wrapped.
Because we’re going to outdent these quote marks by absolutely positioning them, we need a way of applying a positioning context to the parent without overriding whatever styling they had. We set position:only if something’s not already set.
With all those pieces in play, we can position the various quotes marks as necessary. First we find the elements we want and remove any styling they currently have. We get all the value we need using the aforementioned utility functions.
Then finally do the actual positioning. If the quote is against the edge, we first take it out of the document flow, and then check it’s still against the edge in case the reflow that we just triggered wrapped it to the previous line. We outdent it by its width.
If I want them to sit next to each other I could use float:left, though that can be annoying with the having to use clearfixes because it’s kind of a hack.
Maybe it would be better if I used display:inline-block and pretend they’re inline elements (with a bit of font-size:0 to collapse whitespace)
Or display:table; and display:table-cell; and pretend it’s an old-school table layout.
There is a new way. And the new way is flex-box. Behold!
All you need to do is give your wrapper element display:flex;.
Flexbox is a new CSS display model (similar in scope to the block, inline, float, and table display models). It’s a part of the CSS spec, natively supported by all the latest versions of browsers.
This is a outside block element that makes it all happen. It has display:flex or display:inline-flex. Everything happens because of this.
All the immediate children of the flex container are flex items. (Child text nodes are automagically wrapped in an anonymous flex item.)
Everything else in flexbox is defined relative to the flex direction, which is defined relative to the text direction of the current language (one of left-to-right, right-to-left, top-to-bottom, or bottom-to-top).
Suppose I want to change the display order of elements, without affecting source order?
Set setting order:n; on each element will work.
The elements will be sorted by order value, with 0 as the default.
like z-index, order doesn’t require the numbers be sequential.
look ma, no position:relative;
If you want the cells to be vertical then on the wrapping element you can set flex-direction:column; (the default value is row)
You can also set row-reverse or column-reverse which does what it says on the tin. These are relative to the text direction, so in an Arabic page the something with row will be look like row-reverse on an English page.
Perhaps we want our items centred. nightmarishly difficult, yes? no.
justify-content:center centres the elements along the flex-direction axis, while align-items:center works on the perpendicular axis:
Wow. That’s so easy. How much of my life have I wasted fighting CSS to center things, even in this very blog layout? never again (except all the times I need backwards compatibility)
The other options are flex-start and flex-end which align to the left or top, or right or bottom of the row or column, whichever makes the most sense based on the current flow direction.
justify-content: also has the option to space-around and space-between which spreads out the elements evenly. (space-between ignores the space on the ends). Wow. So many easy. So many simple.
If you have wrapped lines, then use align-content instead of align-items.
It’s also possible to set align-self: on an flex-item to give it a different alignment than all the other things, I can’t see why you’d want this, but maybe you’re a special snowflake.
Ladies and gentlement, this is the moment you’ve all been waiting for.
Flex is shorthand for flex-growflex-shrinkflex-basis so I’ll tackle them individually.
flex-basis:[length] (default is 0px). The length is used along the flex-direction axis. It’s basically the same as setting height or width.
if flex-grow is set to 0 then it won’t ever grow. It will be at most the flex-basis size. The equivalent for flex-shrink works also.
If the container is bigger than all the items lengths, then the flex-grow value is taken into account. This value represents its share of the remaining space.
In this example each flex item’s flex-grow value matches the number shown, so box 1 grows to fill 1/6 of the available space, box 2 grows to fill 1/3 of the available space, and box 3 grows to fill the final half of the available space.
If the container is smaller than all the items lengths, then the flex-shrink value is taken into account. This value is how much length is taken off, relative to other flex items flex-shrink value, to fit the row (relative to the length to be removed overall, not the length of the item. I think).
I haven’t got a grasp on the default values for flex yet, so I’ve been overly specific - the default value of the short-hand flex are different than the defaults for completely unspecified flex values.