Hardscrabble 🍫

By Maxwell Jacobson

See also: the archives and an RSS feed

devblog

March 19, 2013

Note: When I was first learning Ruby in late 2012, I made a small web app called “beefsteak” to act as my blog. I wrote several posts on that blog while I was exploring and adding features. I’ve since migrated my personal blog away from beefsteak and to Octopress, and then Jekyll. I decided to collapse those posts into one post because I think they’re kind of confusing now that they’re on a Jekyll site. I’m writing this note on July 10th, 2016. By the time you’re reading this, who knows what the blog is running on…

testing this little devblog thing

Originally posted December 1, 2012

So it’s like 6am and I guess I just made my first mini markdown blog engine.

I’m almost certainly too quick to congratulate myself. It has approximately zero features but it’s succeeding at putting HTML on the screen and I call that a win.

This is pretty much the whole app:

require 'sinatra'
require 'kramdown'

get '/' do
  @title = "devblog - max jacobson"
  the_html = String.new
  the_html << "<h1>#{@title}</h1>\n\n<hr />"
  posts = Dir.entries("posts")
  posts.each do |filename|
    if filename =~ /.md/
      the_text = File.read("posts/" + filename)
      the_html << Kramdown::Document.new(the_text).to_html + "<hr />"
    end
  end
  erb the_html
end

get '/css/style.css' do
  scss :style
end

It just checks all the files in a directory called “posts”, checks all of the ones with the .md file extension, and then renders them all one after the other. That’s it!

I wrote this in just a minute or two so I’ll be sure to add more blog-like features to this. Maybe design it up a bit. That mention of sass is more or less for show.

I feel smart right now so I’m comfortable admitting that I’d love to use Jekyll but just can’t figure out how the hell it works.

This is not one of those smartly static sites. It generates each page as you load it. Probably pretty fast, but still, not smart-smart.

Already with the updates

Originally posted December 1, 2012

There are already updates wow I am good.

Things I’ve added in the last small amount of time:

  • a config.rb file for helper methods that get the blog title and subtitle
    • so nothing in the main app file, web.rb is hardcoded and theoretically someone else could use this
  • permalinks based on the file name
  • option to see the markdown source of any page with a little link at the bottom. (alternately just add the .md to any page)
  • some small amount of style, mainly to do with displaying codeblocks
    • incidentally, I’m treating markdown source as “code”

Here’s what the tree of my project directory looks like right now:

[~/Dropbox/Sites/devblog] [maxjacobson] [07:41 AM]
ϡ tree
.
├── Gemfile
├── Gemfile.lock
├── Procfile
├── config.rb
├── posts
│   ├── 2012-12-01-already-updates.md
│   ├── 2012-12-01-gem-anxiety.md
│   └── 2012-12-01-the-first-post.md
├── views
│   ├── layout.erb
│   └── style.scss
└── web.rb

2 directories, 10 files

Off topic, but: I learned how to customize my bash prompt earlier today and that’s what it looks like.

added sorting

Originally posted December 1, 2012

I have added sorting and so I believe the posts on the homepage will be in the proper order. Kewl.

did i just implement search in like 5 minutes?

Originally posted December 1, 2012

I’m not sure but I think I just implemented search like it was no big deal.

It works well and fast but only because there are so few posts, I assume. I’m not searching the whole internet here or anything, but I do kinda feel like I Am Google or something.

That’s a theme for this system: it will get worse the more I use it. I’m kinda excited to ruin it.

man, sorting is hard when you use the twelve hour clock

Originally posted December 1, 2012

I have a TextExpander snippet for quickly inserting the current time. I write ttime and it inserts, say, 10:43 PM. In that format. If it were an hour earlier, it would say 9:43 PM. No leading zero.

This is what was screwing up my sorting method.

I want posts to be sorted based on both date and time, so if there are multiple posts in a day they’ll show up in the right order instead of just alphabetically.

I could have just written those leading zeros manually or I could’ve just used a twenty-four hour clock, but I like my snippet how it is and want my code to come to me rather than the other way around.

So I’ll share my method because I’m sure in as little as a month I’ll find it embarrassing and that’s very fun for me, being embarrassed.

The argument for this method, to_sort, is an array of hashes that stores all of the posts.

Each hash looks like this:

{
  :text=>"the body of the post (in markdown) would go here, but it would be long so I'm leaving it out",
  :title=>"Introducing myself to the command line",
  :date=>"2012-02-26",
  :time=>"12:00 PM",
  :category=>"coding",
  :tags_array=>["command-line", "learning"],
  :filename=>"2012-02-26-introducing-myself-to-the-command-line"
}

And the method takes the info, specifically the date and time, and adds an additional parameter, :sorter, and then sorts the array based on that value of each hash, and returns the sorted array.

def sort_posts (to_sort)
  to_sort.each do |post|
    the_date = post[:date]
    the_time = post[:time]
    if the_time =~ /AM|am/
      if the_time =~ /^[0-9]:/ # aka ONE digit before the colon
        sorter = the_date.gsub(/-/,'') + ("0" + the_time).gsub(/:| |AM|am/,'')
      else
        sorter = the_date.gsub(/-/,'') + the_time.gsub(/:| |AM|am/,'')
      end
    end
    if the_time =~ /PM|pm/
      if the_time =~ /12:/
        sorter = the_date.gsub(/-/,'') + ((the_time.gsub(/:| |PM|pm/,'')).to_i).to_s
      else
        if the_time =~ /^[0-9]:/ # aka ONE digit before the colon
          sorter = the_date.gsub(/-/,'') + ((("0" + the_time).gsub(/:| |PM|pm/,'')).to_i + 1200).to_s
        else
          sorter = the_date.gsub(/-/,'') + ((the_time.gsub(/:| |PM|pm/,'')).to_i + 1200).to_s
        end
      end
    end
    post[:sorter] = sorter.to_i
  end
  to_sort.sort! { |a,b| b[:sorter] <=> a[:sorter]}
  return to_sort
end

So that’s that.

Edit (2012-12-02, 10:58 AM): There’s a bug in there! posts at 12:15am would sort newer than posts at 1:15am becuase twelve is a higher number than one. So I added some conditioanls to treat 12 like zero. Updated code below:

def sort_posts (to_sort)
  to_sort.each do |post|
    the_date = post[:date]
    the_time = post[:time]
    if the_time =~ /AM|am/
      if the_time =~ /^[0-9]:/ # aka ONE digit before the colon
        sorter = the_date.gsub(/-/,'') + ("0" + the_time).gsub(/:| |AM|am/,'')
      elsif the_time =~ /^12:/
        sorter = the_date.gsub(/-/,'') + ("00" + the_time).gsub(/:| |AM|am|12/,'')
      else
        sorter = the_date.gsub(/-/,'') + the_time.gsub(/:| |AM|am/,'')
      end
    end
    if the_time =~ /PM|pm/
      if the_time =~ /12:/
        sorter = the_date.gsub(/-/,'') + ((the_time.gsub(/:| |PM|pm/,'')).to_i).to_s
      else
        if the_time =~ /^[0-9]:/ # aka ONE digit before the colon
          sorter = the_date.gsub(/-/,'') + ((("0" + the_time).gsub(/:| |PM|pm/,'')).to_i + 1200).to_s
        else
          sorter = the_date.gsub(/-/,'') + ((the_time.gsub(/:| |PM|pm/,'')).to_i + 1200).to_s
        end
      end
    end
    post[:sorter] = sorter.to_i
  end

  to_sort.sort! { |a,b| b[:sorter] <=> a[:sorter]}
end

SEE?? Embarrassing. I love it. I’m sure there’s an easier way to do this. But it’s interesting, I think, that an idea (“hmm, i want to sort by both date and time, so why don’t I make a :sorter variable that takes those two strings and combines them into one an integer that represents when it was posted”) morphs and grows into this 28 line mess. I guess it’s time to refactor.

categories and tags

Originally posted December 1, 2012

I added some functionality for categories and tags. The difference between categories and tags, in my implementation, is that there can only be one category, but there can be a bunch of tags.

So be generous with your tags.

You can use spaces in your categories and tags and they’ll be converted to hyphens. They’re comma-delimited.

You can see the tags at the bottom of this post and you’ll see some hyphens. but if you look at the markdown source, you’ll see some spaces.

(You can see the markdown source by clicking the link at the bottom of each post or by simply replacing the / with .md at the end of a post url)

RSS is hard to implement and I’m not sure I’m doing it right

Originally posted December 1, 2012

I’m pretty sure I’m not doing this right but I’ve added an RSS feed so that’s fun, enjoy.

It’s available at /feed and ever time that “file” is queried, the sinatra app will generate an up-to-the-minute accurate feed, including all of the posts.

I don’t necessarily recommend subscribing because I might change where it’s located soon. Right now the blog is online at http://devblog.maxjacobson.net but I may move it to blog. or beef. or beefsteak. or something else entirely.

going open source

Originally posted December 2, 2012

Just for fun I’m sharing the source code for this blog in full on github. I don’t know if anyone else will or should use this but maybe they’ll want to and I won’t stop them.

It’s surprisingly easy having two remote destinations for this code (heroku, where my blog is hosted, and now github where the source code is hosted). I make changes, commit them, push to one, then push to the other. Easy peasy.

If someone else wants to use it, they’ll have to delete my stuff and replace it with their stuff. I’m not giving them a blank slate. Because I’m not sure how to maintain two separate versions like that. But they’ll figure it out, I think.

I guess I’ve been planning to do this, because I’ve refrained from hardcoding my information (name, blog title, etc) into the code. Instead it’s in a separate config.rb file (which you can see on github).

I’m cold on this balcony in San Diego. I am proud that I made this in like two days. I will keep working on it. A small part of me just got a tingling urge to hurl my laptop down into the parking lot like it’s my punk rock guitar.

I’m worried that it will be a whole hassle if I ever want to change the address of this blog. I already kinda feel limited by “devblog”.

Right now my Top Level website, http://maxjacobson.net is mostly just a front for my Pinboard public links. maybe I’ll merge that idea into this one and put the whole thing at that address. I glanced over the Pinboard gem and I could probably whip something up myself instead of using the linkroll widget. That would give me some more flexibility to display multimedia stuff, Layabout-style and do some more color-coding without having to use jquery like I’m currently doing to emphasize posts with the max_jacobson tag.

I’m just daydreaming here.

some new features here

Originally posted December 13, 2012

Things I’ve added to beefsteak tonight:

more feeds

Like, for individual categories, tags, and search queries. Try clicking on a tag/cat or searching something, and you’ll see a link to the feed at the bottom.

A fun one might be: http://devblog.maxjacobson.net/tag/gush/feed or http://devblog.maxjacobson.net/search/confess/feed if you like me at my gushiest or most confessional.

pages

My about page is now at http://devblog.maxjacobson.net/~about. I made another one mostly to test what it looked like with two. It doesn’t look great.

You can view the markdown source of pages the same as posts. Just add the .md suffix or click the link at the bottom. I don’t know why I offer this. It doesn’t work amazingly. In some posts with code, I see underscores disappear. It’s weird.

favicon and apple touch icon

I just threw in some all-black-errything squares for now. I figure if anyone else uses this they can replace with something they like.


Here’s what the tree for this site looks like right now:

(notice the new pages directory, mostly)

[~/Dropbox/Sites/devblog] [max] [08:23 PM]
 ϡ tree
.
├── config.rb
├── Gemfile
├── Gemfile.lock
├── helpers.rb
├── pages
│   ├── about.md
│   └── projects.md
├── posts
│   ├── 2011-03-12-un-americano.md
│   ├── 2012-02-26-introducing-myself-to-the-command-line.md
│   ├── 2012-08-26-favblogging.md
│   ├── 2012-12-01-added-sorting.md
│   ├── 2012-12-01-already-updates.md
│   ├── 2012-12-01-categories-and-tags.md
│   ├── 2012-12-01-gem-anxiety.md
│   ├── 2012-12-01-sleep.md
│   ├── 2012-12-01-sorting.md
│   ├── 2012-12-01-the-first-post.md
│   ├── 2012-12-02-erb-and-indendation.md
│   ├── 2012-12-02-on-an-airplane.md
│   ├── 2012-12-02-open-source.md
│   ├── 2012-12-02-RSS-is-hard.md
│   ├── 2012-12-02-search.md
│   ├── 2012-12-04-seal-attack.md
│   └── 2012-12-13-meetups.md
├── Procfile
├── public
│   └── img

│       ├── 2012-12-04-seal-1.jpg
│       ├── 2012-12-04-seal-2.jpg
│       ├── 2012-12-04-seal-3.jpg
│       ├── apple-touch-icon.png
│       └── favicon.ico
├── README.md
├── views
│   ├── 404.erb
│   ├── 500.erb
│   ├── layout.erb
│   └── style.scss
└── web.rb

5 directories, 35 files

fuzzy search

Originally posted December 14, 2012

I just made the search a little smarter on this blog. I think. I made it so that when you search a multi-word query, it’ll include posts that match any of the words, not just posts that match all of the words. They’re sorted chronologically, not based on relevance.

And you can put a query in quotes if you want it to match all of the words, in that order.

Here’s how I did that (with some stuff paraphrased):

if query =~ /^".+"$/
  query_array = [query.gsub!(/"/,'')]
else
  query_array = query.split(' ')
end
# ... each post |the_text|
query_array.each do |q|
  if the_text =~ Regexp.new(q, true)
    # then include the post
    break
  end
end

So, first I check if the query is in quotes (I’m coming to really love using regular expressions), and if so I remove the quotes and put that whole string thru. If it’s not in quotes, I split the string into an array of strings, using a space as the delimeter. Then I go through each post and check it against each item in the array. If the query was in quotes, the array only has one item in it. Once there’s a match, I push it thru. The break makes it so once a post matches the query, we move on to the next post rather than keep checking other words in the query against it.

I wonder if I should allow single quotes too. Note to self, allow single quotes.

I wonder what new bugs I just introduced.

heave ho

Originally posted December 22, 2012

So I am happy enough with beefsteak that I am moving this to my proper homepage, http://www.maxjacobson.net.

That space was mainly occupied by my pinboard public links, and I’ve integrated those into beefsteak now.

I considered integrating a pinboard gem but for now I’m sticking with the same old linkroll widget I’ve been using. It’s not super flexible but it has an important feature the main pinboard gem lacks: a distinction between private and public links. It’s not that I have such private stuff in there, it’s just that I have so much stuff in there. As previously described I use IFTTT to send all kinds of stuff automatically, and then I occasionally make a small minority of those links public. And maybe there’s some private stuff in there.

Who is sleepier than I? No one is.

The project is still mostly useable by other people, I think. The only thing is the small jquery script that modifies the DOM so that links with the tag max_jacobson are made red. I tweaked it so it lets you specify what that tag will be, but it’s not working. I’m going to fix that later, maybe.

I create links just to kill them at this point. Old feeds and links have a short shelf life.

beefsteak v2

Originally posted March 19, 2013

This version of beefsteak is brought to you by Justin Timberlake.

I put his new album on repeat eleven or twelve hours ago and starting working.

I realized people might be looking at my GitHub and realized I was embarrassed of the way this blog worked. In the few months from the last update, I learned a lot. I hope I’ll be embarrassed of this in a few months.

Heck I already kind of am.1

The first several posts I wrote for this blog were, naturally, about this blog and the process of building it and excitement of discovery. A lot of it is really dumb but I’m leaving it.

So here’s what’s new:

  • syntax highlighting for code blocks, using my fav colorscheme, cobalt
  • switched from erb to haml – now writing far less HTML by hand
  • appending .md to posts or pages takes you to a text file instead of embedding within a page, which displayed poorly (missing underscores for some reason)
  • some (goofy?) animations on page loads. It picks randomly from a list
  • oh, and when you click to see a footnote, the right one will wiggle. see? 2
  • much better config file; in case anyone besides me uses this system, it should be relatively easy for them to plug in their info and start blogging
  1. or will be in the morning, anyway 

  2. and now I’ve just made it so the return button makes the source from within the blog wiggle, too. I want to learn to write my own CSS3 animations. Right now I’m leaning heavily on Dan Eden. 

click to ugh

January 21, 2013

It’s time for You’re Doing it Wrong, my sometimes series where I complain about blogs whose HTML or CSS bugs me!

Today’s offending blog is The AV Club, which I generally really like and read all the time. Here’s an example post: Jonathan Coulton says Glee ripped off his cover of “Baby God Back”.

How the heck did they come up with this embed code for a YouTube video:

<embed width="425" height="344" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" src="http://www.youtube.com/v/Yww4BLjReEk%26hl=en%26fs=1%26rel=0">

Had they copied the embed code from beneath the YouTube video, they would have gotten this:

<iframe width="560" height="315" src="http://www.youtube.com/embed/Yww4BLjReEk" frameborder="0" allowfullscreen></iframe>

I don’t know where The AV Club got their embed code, but exhibit A for it’s outdatedness is the macromedia reference. Try going to that page. Because Macromedia doesn’t exist anymore, you’ll be redirected to Flash’s new owner, Adobe. This post is from three days ago; Adobe acquired Macromedia seven years ago.

Also: every video they embed is given the dimensions 425x344, without regard to the video’s aspect ratio. YouTube’s provided embed code is dimensions-aware and doesn’t introduce unnecessary letterboxing.

So why do I care? The videos still play and it’s pretty much fine I guess. But it screws up ClickToPlugin.

ClickToPlugin is an awesome Safari Extension that blocks plugins like Flash from running until you click on them. It also, when it can, replaces embedded media with a vanilla HTML5 video player. When I typically encounter a YouTube or Vimeo video embedded on a blog, I don’t see their custom Flash player, I see this:

beyonce video

This example is from an AbsolutePunk.net post, Beyoncé Performs Star Spangled Banner [Video]. It behaves much like a regular, Flash YouTube embed – it doesn’t play until you click the triangle although you have preferences like autoplay or auto-preload – it uses Safari’s implementation of the HTML5 video player. Among the benefits: YouTube hasn’t yet figured out how to include ads in the HTML5 videos, it feels faster, and on youtube.com, videos don’t autoplay so I can open six tabs of Taylor Swift videos without causing a terrible cacophany.

But when I visit that AV Club article, I see this:

avclub doesn't have a still frame

Which is fine, I guess. It’s what I signed up for, using this extension. But c’mon, don’t overthink things and get all fancy, stuff’ll get broken.

(AbsolutePunk is doing something custom too. Its code looks like this:

<div align="center"><object width="472" height="389" bgcolor="#f7f7f7"><param name="movie" value="http://www.youtube.com/v/Z-DSFrGnQrk&amp;fs=1"></param><param name="allowFullScreen" value="true"></param><param name="wmode" value="transparent"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/Z-DSFrGnQrk&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="472" height="389" bgcolor="#f7f7f7" wmode="transparent"></embed></object></div>

I don’t really understand that either, but I don’t need to, because it behaves the way I expect it to. It looks like all AbsolutePunk video embeds use the same aspect ratio (472x389) but at least it works with ClickToPlugin.)

On Layabout, my web app which is embedding videos all day every day, I see this:

here there is a still frame

Look at that aspect ratio – no black bars! And this is all without manually copying and pasting the embed code from YouTube (or Vimeo in that case). These sites support oEmbed, a protocol for taking a URL and supplying an embed code that works.

Unfortunatley it’s not as well-documented as it could be. Vimeo’s oEmbed API is better in that regard and supports the most important feature: maxwidth. I suspect this is at the root of the hardcoded aspect ratios; designers don’t want videos bursting out of their layouts. Vimeo’s page is very useful and clear. YouTube, as far as I can tell, has only provided one brief blog post on the subject.

Maybe that’s the problem?

using mobile web inspector

January 15, 2013

I’ve just figured out why infinite scrolling wasn’t working on Layabout on my iPod Touch.

I’m using jQuery to detect when the user has reached the bottom of the page. There’s no hardcoded event for that, so you have to do some math using the methods that are given. It goes something like this:

If the user is scrolling, check if the current window height plus the amount of stuff above it is greater than or equal to the total height of the document. This works on the desktop (in the browsers I tested anyway) and on the iPad, but not on my iPod Touch.

$(window).scroll(function() {
  if($(window).scrollTop() + $(window).height() >= $(document).height()) {
    // ...
  }
});

On the web, a nice and easy way to figure out problems like these is to use the console and log some variables. So I found myself adding in some stuff like this:

var scroll_top = $(window).scrollTop();
var window_height = $(window).height();
var document_height = $(document).height();
var current_position = scroll_top + window_height;
console.log("scroll top: " + scroll_top);
console.log("window height: " + window_height);
console.log("current position: " + current_position);
console.log("document height: " + document_height);

But it’s not very easy to access the console on an iOS device, so these messages were getting logged to nowhere.

Long story short I followed these instructions and managed to get access to the info I needed. Here’s what the page logged at the bottom of a page:

scroll top: 41628
window height: 1360
current position: 42988
document height: 43173

Huh! No wonder further pages weren’t loading, we were never (by this logic) truly reaching the end of the page!

So, to fix it, I just changed the logic to add 200 pixels of wiggle room, so now it triggers when you get near the bottom of the page rather than hitting it exactly.

So it works and I’m happy. But before we wrap things up too tightly, let’s try to figure out what’s going on here.

Let’s look at some numbers:

thing pixels amount
5th gen iPod Touch screen height 1136
Window height as logged by Mobile Safari ($(window).height()) 1360
The difference between those two 224

What’s the deal with that? Maybe it’s a points-and-pixels thing beyond my comprehension, but this feels kinda wrong to me. Is it possibly a bug in Mobile Safari? Why else would the window height be bigger than the total height of the screen? I expected the opposite; the window height should be less than the screen height, considering that the window doesn’t include browser chrome like the navigation bar along the bottom and sometimes (at the top of pages) the title and address bar.

Let’s revisit the numbers from the earlier log example:

(Note: other than window height, these values would be different on a longer or shorter page)

thing value
scroll top 41628
window height 1360
current position 42988
document height 43173
difference between those last two 185

So if I’m right that Mobile Safari is mis-reporting its height by 224 pixels, how come my earlier logic was falling short by 185 pixels? I think that difference (39 pixels) must be the height of the navigation toolbar?

Maybe!

writing prose with marked

January 12, 2013

Yesterday I got the urge to write some fiction. This has been a decreasingly frequent urge for me, so I seized on it.

I did write a little but I ended up spending more time creating a Marked theme to help me write. Ain’t that how it goes. I set up a GitHub repo to share the theme and any others I may make down the line.

The goal is for me to be able to write in my programmy, vimmy text editor on one half of the screen and for the other half to look as much like a book as I could manage. Also, it prints out nicely (not that I print things, except to PDF). Some of the design features:

  • smart paragraph indenting – follows the physical book’s convention of not unnecessarily indenting the first paragraph in a group of paragraphs
  • kind of musty old book yellow color (but not in printouts)
  • uses google web fonts for a little extra personality
  • classy horizontal rule

A screenshot and the code over on GitHub.

twice

January 10, 2013

From a few weeks ago:

I had no idea what he meant. But I should have, because he was making a reference to an essay I had shared with him and which we had then discussed called Fish. In the essay, Robin Sloan suggests, among other things, the value of revisiting things we like over constantly checking what’s new. I hadn’t meant to reference it and hadn’t thought much about it since reading it, but that idea must have kept with me.

Earlier tonight I watched this video three times:

Do you know that feeling of going to a concert for a band you love, where none of your friends like that band, and you sort of feel like a weirdo for liking them, only to discover that tons of other people like them too and you’re fine? That’s the feeling I got while watching this video, and I didn’t even need to leave the house to get it.

the horizontal rule

December 31, 2012

On a sexier and perhaps better blog, this post title would be a euphemism.

Instead, this post is a bit of a follow up to an earlier post, so this second entry might as well mark the beginning of a series.

It’s time for You’re doing it wrong!, the yearly column where I criticize people’s blog’s CSS even though I’m not an authority on that subject!1

Let’s talk about lines.

There aren’t that many HTML tags, and markdown only supports the small subset that are relevant to writers, so it makes sense to make use of them.

Here’s how you make a horizontal rule in markdown: * * *2. This becomes the following HTML:

<hr>

Which typically comes across as a horizontal line, depending on the CSS, and typically is meant to mark a new section. On this blog, in a browser, at this time of writing, it looks like two thin lines on top of one another. The CSS I used for that looks like this:

hr {
  padding: 0;
  border: none;
  border-top: medium double #333;
}

Edit: Actually, I’ve already changed it. The horizontal rules outside of the post body are still that, but the ones within post bodies are now stars as described below.

CSS is really flexible. You can even style an hr as an image. The blog post that I’m constantly googling for is this one by Marek Prokop which gives a great introduction to the different ways you can style hrs. Heres another, from which I more or less cribbed their last example.

Considering how good hrs are, I don’t understand why bloggers like Shawn Blanc and Stephen Hackett (whom I generally like), don’t use them.

They get the appeal of a nice separating line but instead of using an hr, which is easy to make with markdown, which I think they both use, they do this:

<div align="center">* * *</div>

or:

<p style="text-align:center">* * *</p>

Both commit the cardinal sin of embedding CSS in the middle of an HTML tag. You’re not supposed to do that! Even if you don’t want to use an hr, the correct move would be to separate content and presentation by assigning a class and then selecting that class with the CSS, like so:

The HTML:

<div class="separator">* * *</div>

The CSS:

.separator {
  align: center;
}

I assume they do it this way with the hope that it will be more portable. These days, people often read blog posts in their RSS reader or read later app, far out of reach of their blog’s CSS. In these contexts, the post is subject to the reader app’s CSS, and a div with a class will be treated as unstyled text, but a div with inline CSS might still be styled.

Sometimes.

If I’m reading a post outside of a browser, it’s probably in Reeder or Instapaper (links unnecessary, right?). In Reeder, the rat tactic works and the stars are centered and more or less convey what the authors want them to. In Instapaper, the CSS is totally overridden and it’s just a couple of asterisks. Same for Safari Reader, Readability, probably others.

Had they used an hr, each individual reader would style it as they see fit, but they would understand what it is and work to convey your meaning.

Besides, it’s not that semantic is it?

Blanc’s p tag is most egregious in this regard, because p means paragraph, which this is not. It may have been chosen because the blog’s CSS properties for paragraphs also applied to separators, but that does not make this a paragraph.

styling the hr with an image

Here’s the rub: as flexible as CSS is, I have no idea how to style an hr so that it looks the way these guys seem to want it to look without embedding a small image at every hr, which introduces its own set of problems.

  • According to Prokop, an image hr has visual bugs in IE and Opera, so he resorts to a bit of a hack, namely wrapping the hr in a div with some additional rules, which is a bit of a nonstarter for markdown users – we need something that automatically expands from * * * and looks right.
  • I’ve tried this in the past and it looked kind of lo-res and not great on a retina display

I think these problems are surmountable by:

  1. just ignoring those browsers
  2. researching hi-res images and how to do it right (on my to do list)

So what would that look like?

The HTML:

<hr>

The CSS:

hr {
  height: 13px;
  background: url(hr.png) no-repeat scroll center;
  border: 0;
}

The image could be anything but here’s one I just whipped up in pixelmator with a transparent background to play nice with various sites. Keep in mind: the height property corresponds to the image’s height, so if you use a different image, adjust accordingly.

Edit: styling the hr with pseudo-elements

Edit 2: I love writing posts like this because I end up learning a lot. I especially love realizing how little I understood it at the beginning. I’ve removed the image-based-stars and replaced them with something different, something better.

Now no images are required at all! I realized while loading the dishes that I had cited an example earlier that used the :after pseudo-element to insert a glyph into an hr, so why couldn’t we do the same with Hackett and Blanc’s beloved asterisks? We can! It looks like this:

The HTML:

<hr>

The CSS:

hr {
  padding: 0;
  margin: 0;
  border: none;
  text-align: center;
  color: black;
}
hr:after {
  content: "* * *";
  position: relative;
  top: -0.5em;
}

This is a thinly-modified take on Harry Robert’s Glyph style from that earlier link (example eight).

Isn’t that great! You can use a standard markdown (or HTML or anything) hr to make some centered asterisks show up automatically.

Of course I wanted to experiment and try inserting some other characters in there. This is my first experience with the :after CSS rule and unsurprisingly it’s a can of worms. I attempted to replace content: "* * *"; with content: "✿";, pasting the unicode black florette character directly into the CSS, and there were bonkers errors. The sass compiler just freaked out and killed the whole stylesheet. So, looking at this terriffic HTML entity reference, I went for the familiar-looking code decimal correspondent, &#10047;, wearing those comfy ampersand-semicolon mittens (etsy, get on that). That totally didn’t work either so I ignored the third column and went googling. I came upon this great stack overflow answer which set me straight.

Now my CSS looks like this (and I promise to walk away and stop changing it for a day or two):

hr {
  padding: 0;
  margin: 0;
  border: none;
  text-align: center;
  color: black;
}
hr:after {
  content: "\273F\a0 \273F\a0 \273F";
  position: relative;
  top: -0.5em;
}

If you’re like me you’re like the fuck is that.

That’s the third column I ignored, hex code! \273F corresponds to that black florette and \a0 corresponds to a space. Isn’t that terrifically fussy?? You must pull out the hex number and add a leading \ as an escape to avoid errors.

Regarding using CSS to insert content into an hr, Chris Coyier writes:

Note that in some of these examples, generated content is used (:before/:after). This isn’t supposed to work, as far as I know, because <hr>s are no-content style elements (they have no closing tag). But as you can see, it does. If you are concerned about uber-long-term design fidelity, stay away from those.

To which I say: kewl bruh whatever tho.

Here’s an hr with this style:


This opens you up to use actual stars (or any other unicode character) instead of asterisks! I call that an upgrade. And if you turn CSS off or Instapaper it, it degrades nicely to a plain old horizontal rule3, which isn’t really so bad. It’s good enough for John Gruber anyway. His horizontal rule looks like three pale centered dots.

His CSS:

hr {
  height: 1px;
  margin: 2em 1em 4em 0;
  text-align: center;
  border-color: #777;
  border-width: 0;
  border-style: dotted;
  }

I don’t really understand how that becomes three pale dots but then I don’t really understand CSS.

Edit 3: I was wrong about instapaper

Completely wrong! I made some bad assumptions and now I’m really confused.

In the first draft of this post, I had this scattered throughout my paragraphs anywhere you see “hr” or “hrs” above:

<code>&lt;hr /&gt;</code>

Which is what my markdown processor, Kramdown, generates when I write this:

`<hr />`

It wraps it in the code tags and replaces the angle brackets with their HTML entities, with the goal to make sure it’s not recognized as a horizontal rule, but as anonymous, quoted code. Despite these precautions, Instapaper rendered those as horizontal rules, awkwardly breaking up paragraphs instead of displaying the code’s text inline.

That behavior may make sense in some contexts, but not really in this one. So I went through and removed the brackets, despite it looking kind of goofy without them. But this post was ostensibly about writing blog posts with things like Instapaper in mind, and I wanted it to be readable in there.

What’s even more baffling is that hrs that weren’t inline with paragraphs wouldn’t display at all. Just regular plain old hrs. Not on Daring Fireball and not on here. For a parser that is so aggressive as to forcibly render hrs that don’t want to be, it’s bizarre that it ignores the ones with their hand raised.

So, I dunno. I’m pleased that I managed to find a CSS replacement for Blanc and Hackett’s vibe, but now I’m not sure if they were doing it wrong at all. At least theirs show up.

  1. I’m inspired in part by the dear, departed podcast Hypercritical, which good-naturedly criticized all kinds of stuff with the hopes that things might improve. I miss that show! This drawing is sweet as hell. 

  2. or - - - or *** etc 

  3. Edit: or maybe not? Keep reading… 

rdoc is magic

December 29, 2012

I keep learning new things as I slowly make this ruby gem. I had sent a copy to a friend and wondered if he would be able to unpack it and see the source code, and that led me to the command gem server which starts up a local server so you can go to localhost:8808 in your browser and read about all of the gems installed on your machine, including the included documentation.

Now, writing some documentation has been on my to do list. I ain’t done that yet. In fact I hadn’t gotten around to adding it to my to do list. But I was delighted to see that my gem is listed along with all the others, and – magically – there’s some documentation there.

What!?

It pulls out all of your class methods presents them as a collapsed excerpt. In some instances it pulled out my comments. Those were functioning as notes-to-self but now I know to groom them for others to read by the time I’m close to sharing.

This is cool as shit.

my first gem

December 20, 2012

One of the projects I’ve been intermittently working on is called smashcut. I’ve been keeping it sort of vague and secret because I’m imagining tickling a wild beast of an audience that isn’t necessarily real and that tickles me.

I’m planning to release it as a gem soon enough. I’ve just learned how to do that and it was shockingly easy. A real delight. It’s even quite easy for the gem to be a command line tool. I am using it locally by running smashcut and it is doing things. I am thrilled.

I learned this from the first google result for the query “how to make a gem” (http://guides.rubygems.org/make-your-own-gem/) which I’m sure you could just as easily have found.

I need to learn more about testing and also sleep more.

i gave myself a hair cut

December 14, 2012

It was something like five or six months and I needed one anyway. I’ve always had hair and I’ve always threatened to get rid of it. It was just a matter of getting bored enough, and now it’s gone.

It’s already growing back.

If I weren’t pretty sure it’s a kind of racist thing to think, I’d be more comfortable admitting that my motivation is to look like a buddhist monk. A lot of western people, myself included, have a shallow appreciation of Buddhism. I don’t meditate, but I feel like I would meditate. I aspire toward being zen but don’t really know what that means. I took one class in Eastern religion in college, to fulfill a requirement, and ended up loving it mainly because the professor had a great sense of humor.

So now my hair is short and I clog less the shower drain. I feel very self-conscious. I feel like I look weird or creepy. I’m nervous to take a Christmas photo with my sisters. Who’s the scary baby?

I’m glad I did it. I feel like a male GI Jane.

I was thinking it would imbue me with a seriousness and I would be able to think more clearly. Is there a fog surrounding your head? It may or may not be your hair.

seal attack

December 4, 2012

a video from my travels:

Some additional photos via my instagram (http://instagram.com/maxjacobson):

Seal swims near old lady

Guy jumps by seal

Stoic seal