Commit db237c5a authored by Aral Balkan's avatar Aral Balkan
Browse files

Initial add

parents
# Archive of Ind.ie Labs (https://ind.ie/labs)
This is a static archive of [Ind.ie Labs](https://ind.ie/labs) ([source](https://source.small-tech.org/project/indie-labs)), which was rendered by running the Ind.ie Labs Node project at http://localhost:300 and executing the following command:
```shell
wget --recursive --domains localhost --no-parent --page-requisites http://localhost:3000
```
It is meant to be served via [Site.js](https://sitejs.org), using its [archival cascade](https://github.com/small-tech/site.js/blob/master/README.md#the-archival-cascade) feature, alongside [the archived 2018 version of the site](https://source.small-tech.org/site/www) and [the archived 2013-2017 version of the site](https://source.small-tech.org/site/www-archive-1) under the `/labs` route.
## Like this? Fund us!
[Small Technology Foundation](https://small-tech.org) is a tiny, independent not-for-profit.
We exist in part thanks to patronage by people like you. If you share [our vision](https://small-tech.org/about/#small-technology) and want to support our work, please [become a patron or donate to us](https://small-tech.org/fund-us) today and help us continue to exist.
## Copyright
_Copyright ⓒ 2013-present [Small Technology Foundation](https://small-tech.org) (formerly Ind.ie)._
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1' />
<title>Ind.ie Labs - Blog - Animating Cocoa Window Size When Using Auto Layout</title>
<!--[if lte IE 8]>
<script src='https://ind.ie/assets/js/html5shiv.js'></script>
<![endif]-->
<link rel='shortcut icon' href='/favicon.ico'>
<link rel='apple-touch-icon' href='https://ind.ie/assets/images/ios-180.png'>
<link rel='apple-touch-icon' sizes='76x76' href='https://ind.ie/assets/images/ios-76.png'>
<link rel='apple-touch-icon' sizes='120x120' href='https://ind.ie/assets/images/ios-120.png'>
<link rel='apple-touch-icon' sizes='152x152' href='https://ind.ie/assets/images/ios-152.png'>
<link rel='apple-touch-icon' sizes='152x152' href='https://ind.ie/assets/images/ios-180.png'>
<link rel='alternate' type='application/rss+xml' title='Ind.ie Labs Blog' href='https://ind.ie/labs/blog/rss/index.xml' />
<link rel='stylesheet' href='https://ind.ie/assets/css/lab-styles.css'/>
<link rel="stylesheet" href="../css/highlight/kimbie.dark.css">
<link rel='stylesheet' href='../css/styles.css'/>
<script src="../js/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
</head>
<body class='labs labs-blog'>
<nav class="site-navigation">
<ul class="site-navigation--list">
<li class="home-link">
<a href="https://ind.ie/">Home</a>
</li>
<li class="about-link">
<a href="https://ind.ie/about">About</a>
</li>
<li class="labs-link">
<a href="https://ind.ie/labs">Labs</a>
</li>
<li class="forum-link">
<a href="https://forum.ind.ie">Forum</a>
</li>
<li class="videos-link">
<a href="https://ind.ie/videos">Videos</a>
</li>
<li class="news-link">
<a href="https://ind.ie/blog">News</a>
</li>
<li class="fund-link">
<a href="https://ind.ie/fund">Fund</a>
</li>
</ul>
</nav>
<nav class='sub-navigation labs-navigation'>
<ul class='sub-navigation--list'>
<li class='projects-link'><a href='../../'>Projects</a></li>
<li class='blog-link'><a href='../'>Blog</a></li>
</ul>
</nav>
<div class='main h-entry'>
<h1 class='p-name long-title'>Animating Cocoa Window Size When Using Auto Layout</h1>
<p class='post-date dt-published' datetime='2015-04-06 15:15:00'>6th April, 2015 — <span class='p-author'>Aral Balkan</span></p>
<div class='e-content'>
<figure>
<img src='images/window-screenshot.png' style='width:448px; margin:auto; display:block;' alt='Screenshot of the example project.'/>
<figcaption>The button cries out to be pushed.</figcaption>
</figure>
<h2>Window animations with Auto Layout</h2>
<p>The one thing you hear over and over again about Apple’s Auto Layout in Cocoa is that you should never, ever directly manipulate the frame of your views when using it. This makes perfect sense since Auto Layout manages the frames of your views for you based on the constraints you use. What you don’t get told nearly as often is the caveat: <em>unless you want to change the size of your window</em>.</p>
<p>Until I realised this, I was hitting my head on the wall trying to alter the Window size by setting my constraints so that their priority was higher than that of my window and then animating the constant to, in effect, push the window out to expand it or pull it in to contract it. Needless to say, this was giving me some odd results (like the Window size jumping to the final dimensions directly and the view itself animating within the set duration to fill it up eventually.)</p>
<h2>A little cartography</h2>
<p>When working in a complicated project, I frequently jump out of it and create little spikes to test out a feature or a troubleshoot an issue I’m having in isolation. While troubleshooting this particular issue, I also took the opportunity to try out the <a href='https://github.com/robb/Cartography'>Cartography autolayout library</a> by <a href='https://github.com/robb'>Robert Böhnke</a>.</p>
<p>It’s an absolute joy to work with.</p>
<h2>Walking through the example</h2>
<p>If you want to jump right in and play with the example, you can <a href='downloads/CartographySpike.zip'>download the source here.</a> <small><em>(CartographySpike.zip, 118KB)</em></small></p>
<p><strong>Update:</strong> And here’s one that shows you how to do the same thing in a project that uses an NSSplitViewController. <small><em>(<a href='downloads/CartographySplitViewSpike.zip'>CartographySplitViewSpike.zip</a>, 163KB)</em></small></p>
<p>Let’s go through the code for the example to see how it works.</p>
<p>First, let’s make sure that we have outlets created for the various boxes we’re going to lay out. I find image views with one-pixel colors set to scale <em>axis independently</em> a quick and easy way to prototype layouts. That’s what I’ve used here.</p>
<p>(While we’re at it, we’ll also create a couple of properties to hold the constraint groups we’ll be creating using Cartography. Although we don’t make use of them in this example, you can animate constraint changes by replacing the constraints in constraint groups and calling <code>layoutSubtreeIfNeeded()</code> on your view from within an animation block.)</p>
<p>In <em>ViewController.swift</em>:</p>
<pre>Swift: <code>import Cocoa
class ViewController: NSViewController
{
@IBOutlet weak var background: NSImageView!
@IBOutlet weak var longBox: NSImageView!
@IBOutlet weak var shortBox: NSImageView!
@IBOutlet weak var animateButton: NSButton!
var backgroundConstraintGroup:ConstraintGroup?
var contentsConstraintGroup:ConstraintGroup?
// …
</code></pre>
<p>Next, let’s create the layout constraints.</p>
<p>When working with Auto Layout, I have a personal preference that might seem rather odd at first: I want to use Interface Builder (IB) to lay out my views and set my constraints but I also want to specify my constraints in code. This allows me to take advantage of the quick, visual prototyping offered by IB (as well as its excellent preview tools for localisation, etc.) and it means that I also know exactly which constraints are being added and have them all in one place in code for easy maintenance. If this sounds like a bit of repitition, it is, but I find that it is worth it to have the best of both worlds.</p>
<p>So, to start off, I lay out my view in IB. For this quick spike, I didn’t actually set any constraints in IB but normally I would have so I could visually fine-tune its responsiveness. Regardless, IB will add some constraints automatically, and I don’t want these, so we start by removing them in <code>viewWillAppear</code>:</p>
<pre>Swift: <code>override func viewWillAppear()
{
super.viewWillAppear()
// Remove constraints added by
// Interface Builder while prototyping.
self.view.removeConstraints(self.view.constraints)
// …</code></pre>
<p>Next, let’s set up the constraints using Cartography.</p>
<p>Did I mention that Cartography is a beauty?</p>
<p>I also looked at <a href='https://github.com/smileyborg/PureLayout'>PureLayout</a> (which led me to try <a href='https://github.com/Masonry/Masonry'>Masonry</a> and <a href='https://github.com/iMartinKiss/KeepLayout'>KeepLayout</a> as well), but Cartography is the one I found most intuitive.</p>
<p>Continuing with <code>viewWillAppear()</code>:</p>
<pre>Swift: <code>backgroundConstraintGroup = layout(background)
{
/* with */ background in
// Make the background hug the edges of its superview exactly.
background.top == background.superview!.top
background.bottom == background.superview!.bottom
background.left == background.superview!.left
background.right == background.superview!.right
}
contentsConstraintGroup = layout(animateButton, longBox, shortBox)
{
/* with */ animateButton, longBox, shortBox in
animateButton.center == animateButton.superview!.center
longBox.width >= 274.0 ~ 1000 // Specify a minimum width for
// the long box and allow it to
// expand (all constraints at
// required — 1,000 — priority).
longBox.height == 34.0 ~ 1000 // Static height.
// Inset the long box by 20 points from the left and
// bottom edges of its superview.
longBox.left == longBox.superview!.left + 20.0 ~ 1000
longBox.bottom == longBox.superview!.bottom - 20.0 ~ 1000
// The short box has a static width and height and is aligned
// to the right of the long box by the default Cocoa distance.
// Its inset from the bottom and right of its superview by
// 20 points.
shortBox.width == 78.0 ~ 1000
shortBox.height == 34.0 ~ 1000
shortBox.left == longBox.right + 8.0 ~ 1000
shortBox.right == shortBox.superview!.right - 20.0 ~ 1000
shortBox.bottom == shortBox.superview!.bottom - 20.0 ~ 1000
}
println(self.view.constraints)
}
</code></pre>
<p>Finally, let’s create an action for the Animate button which, when pressed, animates the size of the Window to make it expand by 200 points in both dimensions while keeping it centred on the same spot by moving its origin to compensate.</p>
<p>Continuing in <em>ViewController.swift:</em></p>
<pre>Swift: <code>@IBAction func animateButtonPressed(sender: NSButton)
{
// Animate the window’s frame.
NSAnimationContext.runAnimationGroup(
{
/* with */ (context: NSAnimationContext!) -> Void in
context.duration = 0.33
context.allowsImplicitAnimation = true
var windowFrame:NSRect = self.view.window!.frame
windowFrame.size.width += 200.0
windowFrame.size.height += 200.0
windowFrame.origin.x -= 100.0
windowFrame.origin.y -= 100.0
self.view.window!.setFrame(windowFrame, display: true, animate: true)
},
completionHandler:
{
() -> Void in
println("Animation complete. Window is now \(self.view.window!.frame.size.width) × \(self.view.window!.frame.size.height) points.")
}
)
}
</code></pre>
<p>And that’s basically it.</p>
<p>The key thing to remember is that while you shouldn’t be setting the frame of your views directly when using Auto Layout, this doesn’t apply for window size changes.</p>
<p>When you want to smoothly change the size of your window, it’s perfectly permissible to animate its frame and your views that use Auto Layout will animate along beautifully while satisfying their constraints.</p>
<p>While I won’t go into it here, you can also cause the size of the window to change by expanding/collapsing panels in a split view. Find out more about that technique, as well as the window resize method I use here, in the Best Practices for Cocoa Animation session from <a href='https://developer.apple.com/videos/wwdc/2013/'>WWDC 2013</a> (<a href='http://asciiwwdc.com/2013/sessions/213'>transcript</a>).</p>
</div>
<div id="discourse-comments"></div>
</div>
<!-- Ind.ie footer -->
<aside class='fund-us'>
<h2>We need your support</h2>
<p><a href='/about'>We won’t take venture capital</a>. We need your individual support to help us <a href='https://ind.ie/fund'>until we reach sustainability</a>.</p>
<a class='button' href='https://ind.ie/fund'>Fund us</a>
</aside>
<footer class='site-footer'>
<div class='footer-wrap'>
<aside class='ethical-design-badge'>
<a href='https://ind.ie/ethical-design'><img alt='' src='https://ind.ie/ethical-design/images/ethical-design-badge-tiny.svg'></a>
<p>We practice <strong>Ethical</strong> Design.</p>
</aside>
<small class='copyright'><strong>Ind.ie</strong> <abbr title='Copyright'>©</abbr> <a href='/about/#trademarks'>Article 12</a>. All content is <a href='http://creativecommons.org/licenses/by/4.0/'>Creative Commons Attribution 4.0 International</a>, unless otherwise stated. <a class='view-source' href='https://source.ind.ie/project/ind-ie-site/tree/master'>View source</a></small>
</div>
</footer>
<script type='text/javascript'>
var discourseUrl = 'https://forum.ind.ie/',
discourseEmbedUrl = 'https://ind.ie/labs/blog/animating-cocoa-window-size-when-using-auto-layout/';
(function() {
var d = document.createElement('script'); d.type = 'text/javascript'; d.async = true;
d.src = discourseUrl + 'javascripts/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(d);
})();
</script>
<script src='https://ind.ie/assets/js/modernizr-min.js'></script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1' />
<title>Ind.ie Labs - Blog - App Sandbox: Updating NSUserDefaults fails after deleting app’s container directory</title>
<!--[if lte IE 8]>
<script src='https://ind.ie/assets/js/html5shiv.js'></script>
<![endif]-->
<link rel='shortcut icon' href='/favicon.ico'>
<link rel='apple-touch-icon' href='https://ind.ie/assets/images/ios-180.png'>
<link rel='apple-touch-icon' sizes='76x76' href='https://ind.ie/assets/images/ios-76.png'>
<link rel='apple-touch-icon' sizes='120x120' href='https://ind.ie/assets/images/ios-120.png'>
<link rel='apple-touch-icon' sizes='152x152' href='https://ind.ie/assets/images/ios-152.png'>
<link rel='apple-touch-icon' sizes='152x152' href='https://ind.ie/assets/images/ios-180.png'>
<link rel='alternate' type='application/rss+xml' title='Ind.ie Labs Blog' href='https://ind.ie/labs/blog/rss/index.xml' />
<link rel='stylesheet' href='https://ind.ie/assets/css/lab-styles.css'/>
<link rel="stylesheet" href="../css/highlight/kimbie.dark.css">
<link rel='stylesheet' href='../css/styles.css'/>
<script src="../js/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
</head>
<body class='labs labs-blog'>
<nav class="site-navigation">
<ul class="site-navigation--list">
<li class="home-link">
<a href="https://ind.ie/">Home</a>
</li>
<li class="about-link">
<a href="https://ind.ie/about">About</a>
</li>
<li class="labs-link">
<a href="https://ind.ie/labs">Labs</a>
</li>
<li class="forum-link">
<a href="https://forum.ind.ie">Forum</a>
</li>
<li class="videos-link">
<a href="https://ind.ie/videos">Videos</a>
</li>
<li class="news-link">
<a href="https://ind.ie/blog">News</a>
</li>
<li class="fund-link">
<a href="https://ind.ie/fund">Fund</a>
</li>
</ul>
</nav>
<nav class='sub-navigation labs-navigation'>
<ul class='sub-navigation--list'>
<li class='projects-link'><a href='../../'>Projects</a></li>
<li class='blog-link'><a href='../'>Blog</a></li>
</ul>
</nav>
<div class='main h-entry'>
<h1 class='p-name long-title'>App Sandbox: Updating NSUserDefaults fails after deleting app’s container directory</h1>
<p class='post-date dt-published' datetime='2015-02-26 11:48:00'>26th February, 2015 — <span class='p-author'>Aral Balkan</span></p>
<div class='e-content'>
<p class='tldr'><strong>TL; DR:</strong> You forgot to empty the trash.</p>
<p>This is a simple thing but it had me scratching my head for a bit last night: If you delete the container folder of your sandboxed app, you might notice that user defaults at <code>NSUserDefaults.standardUserDefaults()</code> are not being persisted. This is the same whether or not you call <code>synchronize()</code> directly (as of 10.8 and later, <a href='http://stackoverflow.com/questions/1725852/nsuserdefaults-are-not-being-written-to-disk#comment28473973_1726001'>you shouldn’t be calling it directly anyway</a>)</p>
<p>The reason for it is simple: you didn’t empty the trash and the app has a dangling reference to the <code>.plist</code> file in the trash.</p>
<p>The solution is also simple: empty the trash.</p>
<p>You can see for yourself by noticing that your application’s property list file is absent as your app runs before you’ve emptied the trash. When you empty the trash and run your app again, it will be created the first time you write to the standard user defaults:</p>
<pre> File: <code>~/Library/Containers/your.identifier.AppName/Data/Library/Preferences/your.identifier.AppName.plist</code></pre>
</div>
<div id="discourse-comments"></div>
</div>
<!-- Ind.ie footer -->
<aside class='fund-us'>
<h2>We need your support</h2>
<p><a href='/about'>We won’t take venture capital</a>. We need your individual support to help us <a href='https://ind.ie/fund'>until we reach sustainability</a>.</p>
<a class='button' href='https://ind.ie/fund'>Fund us</a>
</aside>
<footer class='site-footer'>
<div class='footer-wrap'>
<aside class='ethical-design-badge'>
<a href='https://ind.ie/ethical-design'><img alt='' src='https://ind.ie/ethical-design/images/ethical-design-badge-tiny.svg'></a>
<p>We practice <strong>Ethical</strong> Design.</p>
</aside>
<small class='copyright'><strong>Ind.ie</strong> <abbr title='Copyright'>©</abbr> <a href='/about/#trademarks'>Article 12</a>. All content is <a href='http://creativecommons.org/licenses/by/4.0/'>Creative Commons Attribution 4.0 International</a>, unless otherwise stated. <a class='view-source' href='https://source.ind.ie/project/ind-ie-site/tree/master'>View source</a></small>
</div>
</footer>
<script type='text/javascript'>
var discourseUrl = 'https://forum.ind.ie/',
discourseEmbedUrl = 'https://ind.ie/labs/blog/app-sandbox-updating-nsuserdefaults-fails-after-deleting-apps-container-directory/';
(function() {
var d = document.createElement('script'); d.type = 'text/javascript'; d.async = true;
d.src = discourseUrl + 'javascripts/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(d);
})();
</script>
<script src='https://ind.ie/assets/js/modernizr-min.js'></script>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1' />
<title>Ind.ie Labs - Blog - Better Styles Through ems</title>
<!--[if lte IE 8]>
<script src='https://ind.ie/assets/js/html5shiv.js'></script>
<![endif]-->
<link rel='shortcut icon' href='/favicon.ico'>
<link rel='apple-touch-icon' href='https://ind.ie/assets/images/ios-180.png'>
<link rel='apple-touch-icon' sizes='76x76' href='https://ind.ie/assets/images/ios-76.png'>
<link rel='apple-touch-icon' sizes='120x120' href='https://ind.ie/assets/images/ios-120.png'>
<link rel='apple-touch-icon' sizes='152x152' href='https://ind.ie/assets/images/ios-152.png'>
<link rel='apple-touch-icon' sizes='152x152' href='https://ind.ie/assets/images/ios-180.png'>
<link rel='alternate' type='application/rss+xml' title='Ind.ie Labs Blog' href='https://ind.ie/labs/blog/rss/index.xml' />
<link rel='stylesheet' href='https://ind.ie/assets/css/lab-styles.css'/>
<link rel="stylesheet" href="../css/highlight/kimbie.dark.css">
<link rel='stylesheet' href='../css/styles.css'/>
<script src="../js/highlight.pack.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
</head>
<body class='labs labs-blog'>
<nav class="site-navigation">
<ul class="site-navigation--list">
<li class="home-link">
<a href="https://ind.ie/">Home</a>
</li>
<li class="about-link">
<a href="https://ind.ie/about">About</a>
</li>
<li class="labs-link">
<a href="https://ind.ie/labs">Labs</a>
</li>
<li class="forum-link">
<a href="https://forum.ind.ie">Forum</a>
</li>
<li class="videos-link">
<a href="https://ind.ie/videos">Videos</a>
</li>
<li class="news-link">
<a href="https://ind.ie/blog">News</a>
</li>
<li class="fund-link">
<a href="https://ind.ie/fund">Fund</a>
</li>
</ul>
</nav>
<nav class='sub-navigation labs-navigation'>
<ul class='sub-navigation--list'>
<li class='projects-link'><a href='../../'>Projects</a></li>
<li class='blog-link'><a href='../'>Blog</a></li>
</ul>
</nav>
<div class='main h-entry'>
<h1 class='p-name long-title'>Better Styles Through&nbsp;ems</h1>
<p class='post-date dt-published' datetime='2016-11-29 11:55:00'>29th November, 2016 — <span class='p-author'>Laura Kalbag</span></p>
<div class='e-content'>
<p>CSS is vital to <a href="https://better.fyi">Better</a>. The Better apps (<a href="https://itunes.apple.com/us/app/better-by-ind.ie/id1080964978?ls=1&mt=8">iOS</a> and <a href="https://itunes.apple.com/us/app/better/id1121192229?ls=1&mt=12">Mac</a>) are native apps that use web pages for their content. They’re the same pages you see on the <a href="https://better.fyi">Better site</a>, but with specialised templates. Still, the content you see on the apps is largely HTML and CSS.</p>
<p>The pre-compiled architecture is fairly straight-forward:</p>
<ul>
<li>a <a href="https://source.ind.ie/better/themes/blob/master/common/static/styles/common.css">common CSS file</a> for styles that cover both the site and the apps</li>
<li>a <a href="https://source.ind.ie/better/themes/blob/master/site/static/styles/postfix.css">site CSS file</a> that covers site-specific design elements such as the navigation, footer, and site homepage</li>
<li>an <a href="https://source.ind.ie/better/themes/blob/master/app/static/styles/postfix.css">app CSS file</a> that covers rendering differences between the app(s) and site</li>
</ul>
<p>Each of the CSS files is compiled into <a href="https://data.better.fyi/generated/site/blob/master/styles/global.css">a global CSS file</a> during the build process using first the common CSS file, and then either the app or site CSS files.</p>
<h2>The opportunity</h2>
<p>When we first launched Better, the iOS app had a coloured frame around the content (see below). The frame was in the native app, and meant the content viewport size was slightly different than when viewing the same content on the site in a web browser on the same device. It also meant the CSS required specific media queries to ensure more complex design elements, such as the statistics, would fit the viewport and still look good on narrow devices.</p>
<figure>
<img src="images/border-to-borderless.jpg" alt="Two screenshots of the statistics on the Cloud of Shame on iPhone 6" width="750" height="667" style="margin-left:auto;margin-right:auto;height:auto;border:1px #ccc solid;">
<figcaption>
<p>Left: the app with the coloured frame, right: the app without the coloured frame.</p>
</figcaption>
</figure>
<p>In September, Aral removed the coloured frame from the app interface. Firstly, this gave the interface a much cleaner and more spacious feel. It also meant the viewport widths were exactly the same whether you were looking at Better content in the app or in a web browser on the same device. This gave me the opportunity to overhaul the CSS, removing excess rules and repetition mostly caused by a gazillion media queries.</p>
<h2>Changing the way I design for the web</h2>
<p>In a yet-to-be-releasable redesign of <a href="https://laurakalbag.com">my own site</a>, I’d started experimenting with a new approach to units and measurements in my design and overall CSS. I started using <code>em</code>s for every unit. Using <code>em</code>s turned out to be easy, and made my CSS clean and simple, especially as I was writing the CSS from scratch. Using <code>em</code>s isn’t new or revelatory in the web world. But it was always an approach I shied away from before.</p>
<p>I’m pretty good at CSS. You don’t get away with being this bad at JavaScript without knowing CSS very well. Still, it’s taken me a long time to relinquish control over the pixel precision-based thinking learned through years of designing in bitmap graphic software. One can take the Photoshop away from the woman, but you can’t take the Photoshop mindset out of the woman. Even though I hadn’t designed in Photoshop in nearly a decade, had used vector graphic software, had jumped on responsive web design as soon as I heard about it, my brain still thought, and designed, in pixels. When I read anything new about responsive design and other development approaches, I used those techniques, but I still envisioned my work as pixels mapped in the canvas space. I used <code>rem</code>s and media queries, but these were usually compiled from pixel units. I abstracted away my inflexible pixel brain.</p>
<h2>To a flexible everything with ems</h2>
<p>As Better no longer needed so many media queries, the leap from <code>rem</code>s and <code>px</code> to <code>em</code>s was straight-forward. Though from a visual perspective, it was hard to visualise the units as relationships between the type and space without starting from scratch. In order to better visualise the design in flexible units, it turned into a complete refactor.</p>
<p>I started by converting all <code>font-size</code>s to <code>em</code>s, then removing any subsequent media queries relating to those font sizes, instead using two media queries on the root:</p>
<figure><pre><code>html
{
/* ↓ root size */
font-size: 14px;
line-height: 1.5;
}
@media only screen and (min-width: 460px)
{
html
{
font-size: 16px;
}
}
@media only screen and (min-width: 600px)
{
html
{
font-size: 18px;
}
}</code></pre>
<figcaption>
<p>This makes all font sizes scale up proportionally at 460px and 600px viewport widths.</p>
</figcaption>
</figure>
<p>With all font sizes scaling up from the root at 460px and 600px-wide viewports, every other rule using <code>em</code>s will scale up too. Thanks to the cascade, the scaling is always in proportion to the font size. This makes most media queries redundant, with exceptions only when tweakpoints are required between or above these viewport sizes. Scaling with <code>em</code>s requires fewer rules, and makes every rule proportional. It’s building on the existing flexibility of the web.</p>
<p>Then I took all the paddings, margins, max-widths, and anything else related to space or layout, and converted those units to <code>em</code>s, removing their media queries too. I generally used <a href="https://abookapart.com/products/responsive-web-design">the old <code>target/context = result</code> rule</a>. A <code>margin: 12px;</code>, with a root <code>font-size</code> of <code>14px</code> becomes <code>margin: 0.857142857em;</code>. The decimals are ugly, and harder to visualise, so I rounded units as I re-factored. This makes the <code>margin: 0.857142857em;</code> a much tidier <code>margin: 0.85em;</code>.</p>
<p>With barely any media queries, the CSS was easier to manage. It meant I could have a fresh look at the relationships between the type and the space, adjusting consistently across all the elements. When writing CSS, I first style based on the naked elements (<code>h1, h2… ul… p</code> etc) themselves. (<a href="https://www.smashingmagazine.com/2016/11/css-inheritance-cascade-global-scope-new-old-worst-best-friends/">Heydon recently wrote a great article on the benefits of this approach</a>.) Styling without classes is also necessary as most of our content is generated directly from markdown.</p>
<p>The few modules that rely on classes for more complex layouts and styles (such as the statistics below) didn’t need a million breakpoints either. The core typography and spacing of these modules were already adjusting to the available space derived from the base element CSS, so when refactoring, the rules only occasionally needed minor tweakpoints.</p>
<figure>
<img src="images/statistics.png" alt="After Better statistics with icons and text flowing side-by-side" width="521" height="369" style="margin-left:auto;margin-right:auto;height:auto;border:1px #ccc solid;"></p>
<p>Once the rest of the layouts were relying on <code>em</code> units, I could make any icons (which are SVG background images) use <code>em</code>s for their sizing too. Using <code>em</code>s for SVG icons make perfect sense, as they’re vector anyway, and means they can then scale to the text around them.</p>
<p>The <a href="https://better.fyi/spotlight/">Spotlight page</a> has its own CSS applied on top of the global styles, as it has a custom design for each issue. In refactoring to use <code>em</code>s, I again removed a load of CSS (100 lines exactly) that wasn’t necessary now the base elements were more flexible. Cascade FTW.</p>
<h2>A bit of vw</h2>
<p>In the spirit of refactor, I went all-out. On the current <a href="https://better.fyi/spotlight/">Spotlight page</a>, we have a big “Bait!” headline, filling the width of the main container. It was a perfect opportunity to replace a load of media queries with <code>vw</code> units for the <code>font-size</code>. The only additional CSS needed was a media query to ensure it doesn&#39;t exceed the width of the container when the viewport is wider than the container.</p>
<pre><code>body.spotlight h1.headline
{
/* ↓ fallback */
font-size: 8em;
font-size: 36vw;
line-height: 1;
}
@media only screen and (min-width: 600px)
{
body.spotlight h1.headline
{
font-size: 13.2em;
}
}</code></pre>
<h2>Better SVGs</h2>
<p>Getting all overexcited with stripping out unnecessary CSS, I decided to do the same with our graphics too. Last month I read <a href="https://abookapart.com/products/practical-svg">Chris Coyier’s Practical SVG</a> which helped me understand the ins-and-outs of SVG much better. To help grok as I was learning, I created a <a href="https://source.ind.ie/better/themes/snippets/23">little find-and-replace script</a> to scrub all the junk out of a generated SVG file to keep it nice and small. One of Chris’ simplest tips was removing excess decimals from the path points. This alone can halve the size of an SVG.</p>
<figure>
<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;svg width="25px" height="28px" viewBox="0 0 25 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"&gt;
&lt;!-- Generator: Sketch 41.1 (35376) - http://www.bohemiancoding.com/sketch --&gt;
&lt;title&gt;resources-small&lt;/title&gt;
&lt;desc&gt;Created with Sketch.&lt;/desc&gt;
&lt;defs&gt;&lt;/defs&gt;
&lt;g id="Icons-and-Clouds" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"&gt;
&lt;g id="table" transform="translate(0.000000, -93.000000)" stroke="#787878"&gt;
&lt;g id="note" transform="translate(4.000000, 99.000000)"&gt;
&lt;polygon id="paper" stroke-linecap="round" stroke-linejoin="round" points="0.5 0.5 11.5622559 0.5 16.5 5.49975586 16.5 20.5 0.5 20.5"&gt;&lt;/polygon&gt;
&lt;polyline id="corner" points="11.5 0.5 11.5 5.50014165 16.5 5.5"&gt;&lt;/polyline&gt;
&lt;/g&gt;
&lt;/g&gt;
&lt;/g&gt;
&lt;/svg&gt;</code></pre>
<figcaption>
<p>SVG icon generated by Sketch (which is pretty clean anyway,) 903 characters</p>
</figcaption>
</figure>
<figure>
<pre><code>&lt;svg width="25px" height="28px" viewBox="0 0 25 28" xmlns="http://www.w3.org/2000/svg"&gt;
&lt;defs&gt;&lt;/defs&gt;
&lt;g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"&gt;
&lt;g class="table" transform="translate(0.00, -93.00)" stroke="#787878"&gt;