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.

Summary

  • 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.