Web Design Memory Lane

One day, out of the blue, my best friend showed me 2Advanced's website. We were both blown away. We didn't really know what we were looking at or what 2Advanced was, but we knew it was cool as hell. It was a web site that turned you into Ethan Hunt, just by looking at it. It blinked, faded, flashed and everything else that Flash could possibly do at the time.

I wanted to make stuff like this.


I spent a lot of time making animations and UIs in Flash, but eventually got tired of it and moved on to HTML and CSS. Bartelme Design, a design studio run by Wolfgang Bartelme, became my new design obsession. Every little detail was in the right place. The colors were crisp and punchy. It had gradients and reflections that gave the web site life. It had cool, photorealistic elements like bubbles and grass. It was pixel-pushed to perfection.


Imitating Wolfgangs work taught me the ins and outs of Photoshop — especially the Shapes tool and the Layer Effects window. I learned to work with light sources and reflections and to imitate different materials. 

Soon thereafter, I discovered 31Three, a one-man digital studio run by Jesse Bennet-Chamberlain. Jesse created beautiful web sites that balanced typography, layout and visual effects perfectly. It had a level of sophistication that I had never encountered before on the web.


One my favorite items i Jesse's portfolio was this design for Campaign Monitor. After discovering it, I was unable to unsee it. This was good design, period. For the longest time, everything I did was blue and green, had a MacBook Pro in the splash and featured a punchy sans serif headline with an italic ampersand as the last character in the first line. You're welcome, clients.


Unlike me at the at the time, Jesse was really good at tailoring his designs to his clients' brands. I was amazed that the same person who created the cool all-caps-gotham buttons for Campaign Monitor were also able to design this elegant design for piano maker Steinway & Sons.


Jesse's designs taught me that web design was about more than just making cool buttons — it was about understanding that each client is different and that each site should communicate the client's brand, not the designer's personal style. I desperately needed that lesson.

Jesse also knew how to bring energy into a design. I would constantly revisit his design for Fitbit and marvel at Jesse's mix of turquoise and pink. This was before color theory was a big thing in web design circles so the color decisions in his designs seemed like black magic to me.


Later, while devouring typography books, I discovered Jason Santa Maria's work. Jason drew a lot of inspiration from print design. In this lecture, he talks about how disappointed he was to see imaginative print layouts become boring, default-layout articles when published online.

To prove his point, Jason created a series of articles on his web site, all with unique layouts, each tailored to the content and theme of the article. I have a large folder in my Dropbox with half finished, over-designed imitations of these.


When starting my own web agency in 2011, I dreamt of creating a Scandinavian Happy Cog. To me, they represented what web design should be like. They knew the rules of typography, layout and color so well that they could break them and still look good while doing it. Their designs broke out the traditional 960 grid and their headlines mixed fonts and sizes in creative ways.



The rest, as they say, is (modern) history. Responsive, mobile-first, atomic, flat…

Today, as I — and the rest of the design community — talk product design and design systems, I can I can find traces of all of these early designs in my work. All the stuff that I've stolen, refined and made my own is still very much present in my Sketch, Figma and Framer files. Thanks for the inspiration everyone.

(Ps. Please steal this blog post idea and share the stuff that shaped you as a designer.)

Six Months of Photography

Some six months ago, I bought a camera. I've owned cameras before, but this is my first professional camera. It has proper optics, manual settings and five million buttons. I'm officially a professional photographer.

I'm kidding of course. A good camera doesn't make a good photographer. Good football shoes doesn't make a good football player. (Obligatory parental rant #34.) But there is something special about having a really good camera. It expects you to be better. To take better photos and become a better photographer. Also, the $1500 lenses look at you very judgementally.

I've shot about 25 000 photos these first six months. I've photographed everything; cars, trees, buildings, people, my shoes, the night sky and Michelle — sitting, walking, eating, sleeping, working… For every good photo there are 50 bad ones. But that ratio is getting better. Fewer messed up exposures, poorly focused subjects and disastrous compositions. I'm learning.

“Photography is about light” is something that every photography tutorial on YouTube says. It sounds easy and intuitive, but it didn't really sink in for me until I set my camera in black and white mode. The art of photography becomes simple: everything is black unless there is light coming in. That's it. Colors trick you into thinking you have a good shot when it's really the motive that interesting, not your composition. Black and white mode asks you to create interesting photos, not just to photograph interesting subjects.

Here are some of the photos I've taken over the last six months. I'm releasing some of them on Unsplash (the blueberry one was featured, yay!). I'm also on Instagram. My camera is a Canon 5D Mark III.

Web Development on the iPad Pro

The iPad Pro deserves all the good reviews. I use it all the time, professionally and privately, and like it more than I ever expected to. I recently decided to take things one step further and try using it for web development as well.

Now, it's not all about the iPad — the magic actually happens in the cloud. I use a $5/month Ubuntu server hosted by Digital Ocean. I could probably get away with less using AWS, but I like Digital Ocean's shiny interface. I'm only human.

Blink for iOS is a great shell that allows me to SSH into my development server. The IP address is available in Digital Ocean's control panel. Swedish 4G is quite good so there is no noticeable lag.


I host my projects on GitHub. To access them, I generated a new SSH key and hooked it up to my GitHub account. After that, I can clone/push/pull projects freely. When running my build scripts, I ran into a bunch of issues with packages, compilers and dependencies. After 30 minutes of Stack Overflow and apt-geting things all my build scripts were working smoothly. Development time!

My current project involves Jekyll which serves a static web site to a local url like localhost:4000. To make the site accessible from Safari I use Localtunnel which takes any local URL and gives it a random, publicly accessible URL.

Vim is my editor of choice on the Mac, so that's how I edit files on the iPad as well. It's a pain to learn, but it's worth it — being able to feel right at home on any unix machine is pretty neat. To make sure that my build script's output doesn't interrupt Vim I send the build script's output to /dev/null.

The iOS 11 beta has even better split screen support than previous generations of iOS. Windows can be arranged in custom 25%-50%-75% arrangements with another window floating on top.


All of this makes developing on the iPad Pro pretty great. I won't use it as my main development machine but I might settle for the iPad next time I leave town for a few days.

Stockholm → Berlin

On July 29, Michelle and I are going abroad for six months. We'll move around in Europe – a few weeks in Prague, short stops in Amsterdam and Paris and some longer stays in Berlin and maybe Rome. Michelle will be studying business and code and I will continue to study math, AI and robotics. Peak excitement!

Self Driving Lego Mindstorms Robot

I have a strange fascination with all kinds of autonomous robots. One of the main goals with my math and AI studies is to be able to build my own autonomous vehicle. But that's a few linear algebra courses away… In the mean time, I happily settle for LEGO Mindstorms.

The rotating part is a sensor which detects the distance to nearby obstacles. It looks left and right, left and right, left and right… It then analyzes the best course of action and continues in that direction. If it encounters a dead end it backs up, rotates and tries to find another path.

Managing Styles and CSS in Framer

The way Framer.js handles styling of layers can be confusing. Some parts are handled using Framer's built-in layout engine, other parts using the CSS you love and hate. By understanding the relationship between the two you can write cleaner and more manageable Framer code.

What’s a Framer layer anyway?

Framer layers are the basic components of all Framer prototypes. They can contain images, text, video, graphics imported from Sketch and much more. Every layer has properties that tells Framer what the layer looks like and what happens when you interact with it.

If you are a web developer, you may be tempted to think of Framer layers as HTML elements or DOM nodes. They have similarities, but are fundamentally different. Framer uses HTML to render it’s prototypes, but you’re not setting properties on the actual HTML element – Framer does that for you.

Style properties are set on the Layer

The design properties of your layer are added directly onto each Layer object. Where a JavaScript developer may be used to writing item.style.color = 'red' a Framer designer would simply write item.color = 'red'.

myLayer = new Layer

# Framer properties are added onto the layer
myLayer.x = 20
myLayer.y = 20
myLayer.width = 200
myLayer.height = 500
myLayer.borderRadius = 10

Well, not all properties…

Framer layers doesn’t support all CSS properties out of the box. Typographic styles for instance, can’t be added directly onto the layer — you need to use the styles property. So, while you can write layer.x and layer.width, you need to use the style property for layer.style.fontFamily and for some other properties.

myLayer = new Layer

# Framer properties
myLayer.x = 20
myLayer.y = 20
# …

# Actual CSS styles
myLayer.style =
	'font-family' : 'helvetica, sans-serif'
	'font-weight' : 'bold'
	'text-transform': 'uppercase'

Leave element positioning to Framer

Don’t try to micromanage the styling of layers. Forget what you know about position: absolute; and translate3d(). Framer takes care of positioning for you. Try not to think about how it’s done – just set the element’s x and y properties and be happy.

Tips for working with styles in Framer

At the top of my Framer file, I typically create a preferences object which holds the global settings for my Framer app. This is a good practice that will help you write DRY Framer code.

# My Framer app's settings live here
prefs = {}

Next, I add a few default properties that I want to use throughout my app.

prefs.margin = 20
prefs.borderRadius = 3
prefs.somethingElse = 100

When creating new layers, I simply reference these properties:

header = new Layer
	x: prefs.margin
	y: prefs.margin
	borderRadius: prefs.borderRadius

This makes the app much more maintainable. Whenever I want to change the default margin or border radius, I only have to do it once, in my prefs object. This saves time and ensures that the app’s design is consistent.

Adding defaults to layers

If you’re absolutely sure that every layer inside your Framer app should have certain styles, you can set global defaults that apply to all Framer layers. Generally however, I would advise against this. It tends to add bloat to the prototype and confuse other developers.

Framer.Defaults.Layer.backgroundColor = 'red'

Adding complex CSS to the prefs object

As I mentioned earlier, some style properties are added as properties on the Layerobject. Others are added as sub-properties of the Layer’s style property. The latter is true for all typography styles. To manage this, I set up groups of CSS styles in my main prefs object:

prefs.styles = {}
prefs.styles.bodyText =
	fontFamily: 'helvetica, sans-serif'
	fontSize: '16px'
	lineHeight: '20px'

This allows me to reference this style whenever I create a new Layer:

comment = new Layer
	x: prefs.margin
	y: prefs.margin
	style: prefs.styles.bodyText

Mixing style objects

Sometimes you want to mix two different CSS objects in a single layer:

prefs.styles.largeText =
	fontSize: '44px'

prefs.styles.boldText =
	fontWeight: 'bold'

largeAndBold = new Layer
	style: /* Wanted: a mix between largeText and boldText */

To solve this, I often use the native JavaScript method Object.assign() which mixes two or more JavaScript objects to create a new object.

callout = new Layer
	style: Object.assign(prefs.style.largeText, prefs.style.boldText)

This will create a mixed object containing properties from both largeText and boldText and assign it to the new layer’s style property.

Another way of achieving the same result is to assign values to the style property twice:

callout = new Layer

callout.style = prefs.style.largeText
callout.style = prefs.style.boldText

Somewhat surprisingly, Framer does not replace the first value with the second one. Instead, it combines the largeText and the boldText styles. Unintuitive but useful.

Styling inline elements

In Framer Studio, inline elements are created by adding a html property to the layer with the inline elements added as part of the string.

myElement = new Layer
	html: 'Your friend <span>@upperdog</span> added a new photo.'

As a JavaScript developer, you may feel the urge to use document.querySelector() to access the child element directly. Try to avoid this. Accessing DOM elements in Framer will cause a bunch of headaches in the long run.

Instead, use inline styles to apply the styles:

myElement = new Element
	html: 'Your friend <span style="font-weight: bold">@upperdog</span> added a new photo.'

Using our prefs object with inline styles

Hard-coding inline styles works well, but it doesn’t scale. The minute you start adding more inline styles to your app, things get messy. The best way to solve this is to use our trusted prefs.style object. Ideally, what you would want is something like this:

myElement = new Element
	html: 'Your friend <span style="'
		+ prefs.styles.highlight
		+ '">@upperdog</span> added a new photo.'

This won’t work however, since the JavaScript object prefs.styles.highlight will be converted to [object Object] when used inside a string. To solve this, you need a function that converts your JavaScript styles to a CSS string:

styleToString = (obj) ->
	str = ''
	for key, value of obj
		str += key + ': ' + value + ';'
	str = str.replace(/"/g, "'")
	return str

Now, simply use that function to when adding styles to your inline elements.

myElement = new Element
	html: 	'Your friend <span style="'
		+ styleToString(prefs.styles.highlight)
		+ '">@upperdog</span> added a new photo.'

Again, this lets you update the prototype’s CSS in one place instead of spreading it out throughout the code.


  • Some properties are defined on the layer (layer.x), others on the style property (layer.style.fontFamily)
  • You don’t have to think about position: absolute; or translate3d(). Framer handles that for you.
  • Put commonly used design patterns in a global prefs object. This makes your design more consistent and your code more maintainable.

Using the Device Orientation API to Estimate Proximity

The Device Orientation API of HTML5 tells us the orientation of the user’s device at any given moment. However, in some controlled environments it can also be used to guess a device’s position in the room.

A while back, I was recently tasked with designing and developing an iPad web app for a client. It was to be used as a sales tool at conventions and exhibitions and when not in the hand of a sales representative it would be mounted on the walls of the exhibition booth.

The idea was to turn the app’s own “screensaver” on as soon as the iPad was mounted on the wall, but since the iPad doesn’t have a RFID or NFC chip, how can we tell when it’s mounted on the wall? HTML5′s Device Orientation API.

The angle of the books differ slightly. Using the Device Orientation API we can tell which book the iPhone is lying on. The colors are pre-defined.

The solution is not perfect and it’s just qualified guessing, but it works remarkably well. By adding a slight delay to the screensaver we can make sure that it’s not turned on as part of a larger hand motion.

This is in no way a replacement to proper near field communication, but in our controlled environment — the exhibition booth — it was all we needed to tell if the iPad was mounted on the wall or not.