Today I Learned

A Hashrocket project

18 posts by camerondaigle @camerondaigle

Break long strings nicely with <wbr>

Are super-long strings (typically URLs) breaking out of an otherwise narrow column of text in your design? Sprinkle in some <wbr> tags; they don’t take up any space, but provide the browser with safe places to break the string to a new line.

For example, this super-long URL will look normal, but if it’s in a narrow area it’ll linebreak at any of the <wbr> points:

<a href="https://basecamp.com/188854/projects/1034065/messages/6033885">
  https://basecamp.co
  <wbr>
  m/188854/projects/1
  <wbr>
  034065/messages/603
  <wbr>
  3885
</a>

Learn all about the <wbr> at MDN.

The `<picture>` element is here to stay!

Safari 9.1 has officially added support for the <picture> element, allowing you to specify multiple image resources per viewport size. Now that Chrome and Safari both support it, go nuts (as long as you don’t need to support IE).

<picture>
    <source media="(min-width: 1000px)" srcset="picture_large.jpg">
    <source media="(min-width: 500px)" srcset="picture_small.jpg">
    <img src="picture_default.jpg">
</picture>

Read more about it in the release notes!

Max out your `vw` with Sass

A pretty big downside to vw units is that they are relative to the viewport width, regardless of the maximum width of your site itself. So if the user fullscreens your site on a big monitor, your vws will be huge while the rest of your site stops growing at some reasonable width. Here’s a handy Sass mixin to address that problem.

@mixin max-vw($attr, $value) {
  #{$attr}: #{$value}vw;
  @media screen and (min-width: 1400px) {
    #{$attr}: ($value/100) * 1400px;
}

Basically, since 1 vw is 1% of the viewport width, at a certain max breakpoint, we use an equivalent percentage of the max site width instead.

For example, this will assign a font-size of 10vw, but after the viewport enlarges past 1400px, the font-size will then set to a static 140px (10% of 1400px):

.foo {
  @include max-vw(font-size, 10)
}

Access Middleman template vars from a helper

Say you pass a local variable to a Middleman template via something like proxy:

proxy('/someplace/nice', 'nice.html', :locals => { :foo => 'boop' })

And you want to use :foo inside a helper without manually passing the variable as an argument. Turns out local variables are accessible in all helpers via current_page.metadata[:locals]:

helpers do
  def helpyhelp
    foo = current_page.metadata[:locals][:foo]
  end
end

Sample from an array in JavaScript

There’s no equivalent to Ruby’s array.sample, so here’s this:

Array.prototype.sample = function() {
  return this[Math.floor(Math.random() * this.length)];
}

I recently needed to pop a random item out of an array, changing the array in the process. That’s just the above plus a splice:

Array.prototype.randomSample = function() {
  return this.splice(Math.floor(Math.random() * this.length), 1);
}

(You don’t have to put them on the Array prototype, I’m just being cute here)

Animate with Transforms for Great CPU Success

I’m currently animating a bunch of clouds across a sky, for a thing, doesn’t matter. I was previously animating with:

@keyframes cloud {
  0% {
    margin-left: 100vw;
  }
  100% {
    margin-left: -30vw;
  }
}

10 clouds ate about 70-75% of my CPU. When I switched to transform instead, my CPU usage dropped to about 20%:

@keyframes cloud {
  0% {
    transform: translateX(100vw);
  }
  100% {
    transform: translateX(-30vw);
  }
}

So: animate with transforms whenever possible to save the planet.

Poke around in your `git stash`

For some reason it’s never occurred to me to see if there’s more to git stash than just pop until now. Turns out you can git stash list to see a big list of all your stashes across all branches:

stash@{0}: WIP on email-unsubscribe: ce66d0f Add missing "EXISTS" to data view
stash@{1}: WIP on master: 484855f Add name to email 'from' address
stash@{2}: WIP on nav_settings: cd3cc88 User revokes pending invitation
stash@{3}: WIP on new_home_integration: d6345a3 Style payment form Stripe errors

You can then git stash apply some stash other than the top one, if you’re into that, no matter what branch you’re on. Just use the name, e.g. git stash apply stash@{2}.

Same goes for git stash show stash@{2}, plus you can add -v if you’d like the line-by-line view.

[Addendum: you can also use git show stash@{2}, which is equivalent to git stash show stash@{2} -v. h/t @joshbranchaud]

Smarter CSS positioning with calc()

Let’s talk a specific use case: if you’re looking to position something a fixed amount from the bottom edge of an area, you’ll end up with something like this:

.foo {
  position: absolute;
  top: 100%;
}

… but if you want the element to be offset a fixed amount from the bottom edge, the standard methodology used to be to just add some margin:

.foo {
  position: absolute;
  top: 100%;
  margin-top: 10px;
}

However, with calc(), you can just throw a fixed amount onto your percentage:

.foo {
  top: calc(100% + 5px)
}

There are countless other uses, but for me that’s the one that comes up the most, so there you go. As always, check caniuse.com for browser caveats (you’re basically safe everywhere except <IE10).

Cmd-click a link open in a new tab with JavaScript

We have a little jQuery plugin that makes entire table rows clickable by catching a click event on a table row and assigning window.location to the href of the anchor in that row – here’s a simplified example:

$('table').on 'click', 'tbody tr', (e) ->
  $a = $(e.target).closest('tr').find('a')
  location.href = $a.attr('href')

… but this doesn’t account for users who might want to hold down command and click on a row to open it in a new tab. In order to do that, we check the e.metaKey property that jQuery has so helpfully provided:

$('table').on 'click', 'tbody tr', (e) ->
  $a = $(e.target).closest('tr').find('a')
  href = $a.attr('href')
  if e.metaKey
    open(href, '_blank')
  else
    location.href = href

Voila! (Disclaimer: If you use something like this in the wild, make sure to do some other checks, like for multiple links in rows, the existence of checkboxes, that sort of thing – but this is the core to get you started.)

thanks to Pavel for correcting my initial implementation, in which I failed to realize metaKey is available on any jQuery event type)

The 0 Buffer

Here’s a Vim 101 thing that I went without knowing for far too long, so maybe it’ll help you as well:

Say you have 3 lines of code …

T-2000
John Connor's Foster Mom
Random Policeman

And you want to replace the second two lines with the first. You might type yy to yank T-2000, then type shift-v and p on line 2 to replace John Connor's Foster Mom with T-2000:

T-2000
T-2000
Random Policeman

But if you go to line 3 and type shift-v and p again, you’ll paste John Connor's Foster Mom, because Vim replaces the default buffer with the previously removed line.

Fortunately, T-2000 is still available in that scenario – it’s automatically placed into the 0 buffer. To continue to paste T-2000, just type:

"0 p

to paste from the 0 buffer instead of the default buffer.

VW FTW

If you haven’t used CSS viewport units like vw and vh yet, you’re missing out on some really fun ways to solve responsive problems. A vw and a vh are equal to 1% of the viewport width and height, respectively.

  • use vw for padding to scale your padding responsively without having to use @media breakpoints
  • set a font-size in vw to allow fonts to scale with the viewport (the TODAY I LEARNED page heading is set using vw)
  • use vw for box-shadow to let your box-shadows scale down with the page (also a feature of the TIL site)

For more sweet viewport unit tricks, check out Rye Mason’s post on the Hashrocket blog.

Generating subclasses with Sass Maps

Say you have a set of colors that correspond to class names:

$category-colors: (
  "bananas": "yellow",
  "oranges": "orange",
  "blue-milk": "cyan",
  "cabbage": "lime",
  "sour-warheads": "magenta"
);

And sometimes those class names recolor a background, but sometimes they might override something else, like a border or text color. Try this mixin on for size:

@mixin category-colors($attr:color)
  @each $class, $color in $category-colors
    &.#{$class}
      #{$attr}: #{$color}

So this call will output subclasses of .widget.bananas, etc., each with the appropriate text color …

.widget
  @include category-colors

And this one will output subclasses with the appropriate background color (or whatever other attribute you’d like).

.widget
  @include category-colors(background-color)

Multiline strings in YAML

If you’re using Middleman data files (which are awesome), you might run into an issue where you want to store text intended for Markdown processing within a .yaml file. To do so, just use |:

widget:
  content: |
    # This is some markdown content in YAML that will be output as an <h1>.

    This will be output as a paragraph tag.

    So will this!

Technically you can use > instead of |, but > does not output linebreaks when the YAML is parsed, which can create issues when the text is processed through Markdown.