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.
This did horrible things with javascript - absolutely positioning the tabs using style attributes, after doing maths on them - making the tabs difficult to style differently than the supposed blank-slate foundation provides, which is probably why this is gone from later versions of foundation.
I heavily modified the module to copy the heading links into a separate div so I could use normal styling rather than javascript maths, but it was still very unsatisfying.
I have set out to recreate this using CSS alone - based mostly on the wonderful orderflexbox property.
The rules:
Don’t duplicate content.
Use no unnecessary HTML elements
Have a sensible/coherent source order
No JavaScript!
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>
Accordion styles
The css of the accordion menu is very straghtforward.
For the tabs we set the container to be display:flex, then we are able to pull all the tabs to the top of the container using order:1 for the tabs and order:2 for the content.
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.
.sections:checked+.tab+section{z-index:2;}
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.
The next most important part of the process is a way to grab all the text nodes in a document. You’d think jQuery would just give you this, but it’s only 5 lines of coffeescript to roll our own.
This recursively steps through nodes, building up a jQuery object of just the text (nodeType==3), until we have all the contiguous strings in the document and none of the HTML. Booya.
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.
Now that all our quote marks are surrounded by <spanclass="hq"></span> we can find out if they’re at the edge of their margin, and then we can outdent them.
Before that we’ll need yet another utility function, this to determine which edge/margin we want. This returns 'left', or 'right' based on the text alignment, and/or the language direction1.
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.
All & that remains is to assign & some events, so these scripts run when they need to. We would also need to run hangQuotes() after fonts have loaded, and wrapQuotes() after ajax has ajaxed.
(wow it’s like I’m writing an actual programming blog, with a post about some obscure hack that a total of 3 people will ever need).
“Which methods?” you ask. update_columns and reload at least. ↑
“What kind of sneaky things?” you ask. Well, if you use non-unique ids and determine out which record with that id you want in the defualt_scope it becomes kind of necessary.3↑
“Ok… Wait… What? Why would you do that?” you ask, quite reasonably. I run away. ↑
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.
<style>.box{float:left;}.boxes::after{content:'Hello I am a clearfix';clear:left;display:block;}</style><divclass="boxes"><divclass="red box">1</div><divclass="blue box">2</div><divclass="green box">3</div></div>
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)
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.
Flex container
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.)
<style>.boxes{display:flex;}</style><divclass="boxes"><divclass="red box"><divclass="text">Flex item</div></div>
Text pretending to be a flex item.
</div>
Direction
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).
Reorder
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.
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.
Alignment.
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.
Flex
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.