There’s a 🇰🇷 Korean translation of this post by designer Kenny Hong.
Last update: May 1, 2018.
Combining Code and Design
With the launch of Dynamic Type, text blocks created in Framer Design became real Text Layers. In addition to updating the text in Code with template tags, you can now change how the text looks.
In this post I’ll give an overview of all Text Layer properties, show which ones can be animated, and share some tips and tricks.
If you still don’t have Framer but want to follow along, make sure to download the free 14-day trial.
By the way, you can, of course, still create Text Layers in Code as well. There’s a Snippet; see Layer > Text
in the Snippet menu.
myText = new TextLayer
text: "Hello World"
fontSize: 64
point: Align.center
Contents
1. Setting and changing the text
Changing the .text
With .text
you set the text for the layer — no surprises here. And, of course, many different languages and scripts are supported.
- Framer docs:
text.text
- Can be ✅ set in Framer Design
A few remarks about these projects
Almost all example Text Layers got a fixed size in Design. I enabled Auto Height so that they can resize to accommodate changes.
# Adjust the Text Layer's height automatically theTextLayer.autoHeight = yes
I also gave most of them a light gray background (another thing you have to do in Code), so that you can see the actual size of the layer.
# Visible background for the Text Layer
theTextLayer.backgroundColor = "#eee"
The example sentences are from Google Fonts. Apparently they’re quotes from different novels.
Multi-line text
When setting a "string"
you always enclose it in quotation marks. But what if you have more than just one line of text?
layer.text = "This is a new line. And here’s a second line!"
It’s simple: you use triple """
quotation marks.
layer.text = """
This is a new line.
Here’s a second line.
And even a third!
"""
… or you use the ‘newline’ character (\n
):
layer.text = "This is a new line.\nHere’s a second line.\nAnd even a third!"
And when the lines in the original text differ in styling, those styles will also be maintained.
Search and replace
You can search for a string in the text and replace it with something else.
layerA = new TextLayer
text: "Text Layers can be created in Framer Code."layerA.textReplace("Framer Code", "Framer Code AND Framer Design")
And when there are more than one occurrence of the same string, they will all be replaced.
text.textReplace("the", "da")
This textReplace()
function is built upon JavaScript’s String.replace()
, so you can also use Regular Expressions.
But before you dive in to the RegEx documentation, first take a look at Templating below!
- Framer docs:
text.textReplace
2. Templating
With templates you can pinpoint exactly which part of the text you want to replace by inserting tags. Template tags are wrapped in {}
and can be styled just like any other text.
theTextLayer.template =
pet: "🐙"
hobby: "🎺"
number: 10
gift: "🍪"
There’s a shortcut for when your Text Layer has just one tag, or when you only want to set the first tag:
theTextLayer.template = "🐙"
You can also animate
to a new value. It will not make a difference for text, but numbers will be animated. When you first animate to a number, it will count up from zero. Afterwards, it will count up or down to the new value.
theTextLayer.animate
template:
number: Utils.randomNumber 1, 20
(From the example prototype above: {number}
gets animated to a randomNumber
between 1
and 20
.)
- Framer docs:
text.template
Swapping between styles
Templating can (sometimes) be used as a way to switch between different styles. You can prepare different stylings for a text block and then swap them in and out.
For example, most of these example prototypes have a Text Layer named title
that contains {v}
and {d}
tags. The {v}
text is plain white, while the {d}
text is 60% transparent. I always hide one of them by giving it an empty string:
title.template =
v: ""
d: "«default»"
Template formatter
With the templateFormatter
you can set how numbers should appear (or animate). You’ll often use it to limit the amount of decimals.
In the formatter, you give your existing template tag a function that does something to the value
.
The first counter above doesn’t have a template formatter; the {s}
tag is simply animated to the number 5
(over a duration of five seconds). That’s why we also see the decimal values roll by.
textLayer1.animate
template:
s: 5
The second counter has two tags: {s}
and {ms}
.
- For the ‘seconds’ tag
{s}
the value is formatted to a number without decimals, usingUtils.Round
. - And the ‘milliseconds’ tag
{ms}
will show 1/1000th of every second (using the%
remainder operator).
textLayer2.templateFormatter =
s: (value) ->
Utils.round value
ms: (value) ->
Utils.round ( value * 1000 ) % 1000
The third counter’s template formatter takes the value in seconds and converts it into minutes and numbers, with a :
between them.
textLayer3.templateFormatter = m: (value) ->
minutes = Math.floor(value / 60)
seconds = Math.round(value % 60)
if seconds < 10
seconds = "0#{seconds}"
return "#{minutes}:#{seconds}"
It uses a return
statement to define what exactly should be returned by the function. (By default, CoffeeScript will return the last value.)
Template formatter also has a shortcut for when your Text Layer has just one tag (or you only want to format the first tag):
textLayer1.templateFormatter = (value) ->
Utils.round value
- Framer docs:
text.templateFormatter
3. Auto-sizing
When you make changes to the text (as in the former example projects), and also when you change the text’s size or line height (as in the upcoming examples), your Text Layer must be able to resize. (And that’s why all these example projects have resizing enabled.)
It’s best to enable auto-sizing in Code because that’s where you’ll have more options.
In Design, you can give a text block a fixed size …
… or set it to auto-size in both directions:
But in Code you can decide in which direction the text should resize.
Auto Height
When you enable autoHeight
the Text Layer will maintain the width you gave it, but change its height to make room for the text.
text.autoHeight = yes
To illustrate the difference, this is the behavior of a layer that was given a fixed width and height in Design:
Tapping the ‘more text’ button will add an extra paragraph:
# More text
buttonB.onTap ->
theTextLayer.text = """Then came the night of the first falling star. It was seen early in the morning, rushing over Winchester eastward, a line of flame high in the atmosphere. Hundreds must have seen it, and taken it for an ordinary falling star. Albin described it as leaving a greenish streak behind it that glowed for some seconds. Denning, our greatest authority on meteorites, stated that the height of its first appearance was about ninety or one hundred miles. It seemed to him that it fell to earth about one hundred miles east of him.I was at home at that hour and writing in my study; and although my French windows face towards Ottershaw and the blind was up (for I loved in those days to look up at the night sky), I saw nothing of it."""
… but nothing will change because the Text Layer is not set to adapt its height (nor its width).
(A Text Layer will mask the text it contains — its clip
is always on.)
And here’s what happens when autoHeight
is enabled:
Now that the text block resizes, I can also adjust the position of the — • — underneath it by looking at the Text Layer’s maxY
:
# Place the line under the text
lineUnderText.y = theTextLayer.maxY + 20
Auto Width
There’s also autoWidth
, for when the text block should stick to a certain height, and you only want its width to adapt.
text.autoWidth = yes
But there’s a limit: a Text Layer will never become wider than its parent layer, or when there’s no parent, wider than the screen.
Auto Everything
And if you want a text block to resize both vertically and horizontally, you can switch on its autoSize
…
text.autoSize = yes
… but that’s, of course, the same as the default behavior in Design:
One more thing: Setting a layer’s width
or height
will disable its auto-sizing for that direction. Remember that width and height are also changed when you set size
or frame
.
4. Padding
This is the space between the text itself and the edge of the layer. Adding more padding will make the Text Layer grow, in whichever direction it can.
So when using padding you should give the layer the possibility to resize, by enabling autoHeight
or autoWidth
.
You can set padding globally, for all four sides:
textA = new TextLayer
text: "Hello World"
padding: 20
… or for each side individually:
textB = new TextLayer
text: "Hello World"
padding:
top: 40
left: 20
bottom: 40
right: 20
… or use horizontal
to set left and right at the same time, and do the same for top and bottom with vertical
:
textC = new TextLayer
text: "Hello World"
padding:
horizontal: 80
vertical: 40
By using vertical and horizontal padding together with a background color you can convert the humble Text Layer into a button (see the examples below, under Background Color).
- Framer docs:
text.padding
- Can be ✅ animated
5. Truncation
So much text, too much text, it doesn’t fit …
Text Overflow
Setting textOverflow
to "ellipsis"
will signal overflowing text with an ‘…’. The other option, "clip"
, will just cut off the text at the first line.
You need to set a fixed height for this to work, so don’t use autoHeight
or text.autoSize
on a text block that you want to truncate.
The height of your layer defines how many lines will be shown. It can be tricky to find the correct height, though, so here’s a tip:
Pro tip: Use fontSize
and lineHeight
to calculate the correct height for a certain amount of lines:
textLayer.height = ( textLayer.fontSize * textLayer.lineHeight ) * 2
Truncate
There’s also truncate
; setting it to yes
will let the text overflow with an ellipsis.
textA = new TextLayer
text: "Very long text, that definitely needs some truncation, because it’ll never fit on this small mobile screen."
truncate: yes
- Framer docs:
text.textOverflow
andtext.truncate
6. Color
You set the color of the text itself on .color
, and just like any other color property in Framer, you can animate it.
The default color of a Text Layer created in Code is a light gray: "#888"
.
- Framer docs:
layer.color
- Can be ✅ set in Framer Design
- Can be ✅ animated
7. Background Color
Text Layers are transparent by default, but just like common layers you can give them a backgroundColor
.
Text Layers are perfect for making buttons with different states. You can give your buttons a fixed size using width
and height
, but know that you can use padding
and lineHeight
to create flexible buttons that resize automatically.
See the project below for examples of all three techniques: one with padding, one with line height, and a third one with a fixed size.
- Framer docs:
layer.backgroundColor
- Can be ✅ animated
And gradients!
As shown by the third button above, you can also set and animate gradients.
- Framer docs:
layer.gradient
- Can be ✅ animated
8. Font Size
Well, the size of the text … this stuff is easy, right?
Note that it’s a points value (so no px
, em
or %
values like in CSS).
Decimal values can be used, though, like halves (e.g., 10.5
) to change the font size by only a pixel.
- Framer docs:
text.fontSize
- Can be ✅ set in Framer Design
- Can be ✅ animated
9. Font Family
When you don’t set fontFamily
(on a Text Layer created in Code), Framer will use the platform’s default typeface: San Francisco on Apple devices, Roboto on Android, etc.
Just like in CSS, you can give alternatives for when a font is not available. In fact, Framer’s default for Google devices actually says:
fontFamily: "Roboto, Helvetica Neue"
So when Roboto is not installed it will fall back to Helvetica Neue.
Here’s an example of how Framer falls back to an alternative font: one of the examples in the above project says …
fontFamily: "Brush Script MT, cursive"
… but there’s no Brush Script on iOS devices. There’s a default cursive typeface, though, so that’s the one you’ll see.
- Framer docs:
text.fontFamily
- Can be ✅ set in Framer Design
Loading Google Fonts
You can load any font from Google’s catalogue by passing its name to Utils.loadWebFont
. This will work on all platforms.
fontFamily: Utils.loadWebFont "Cookie"
You can optionally pass in a second argument to load a certain font weight. In the example project above I used this to load the thinnest weight of Lato.
fontFamily: Utils.loadWebFont "Lato", 100
A font will load when the Text Layer becomes visible, but to avoid the slight delay this may give, you can pre-load a font and store it in a variable …
latoThin = Utils.loadWebFont "Lato", 100
… and then use that variable to set the font family:
title = new TextLayer
fontFamily: latoThin
- Framer docs:
Utils.loadWebFont
Loading other web fonts
Another utility, Utils.loadWebFontConfig
, gives access to the Web Font Loader library. With it, you can load fonts not only from Google Fonts, but also from Typekit, Fonts.com, Fontdeck, and even self-hosted web fonts.
To load your own custom fonts, for instance, you would do something like this, as explained in the library’s docs:
Utils.loadWebFontConfig
custom:
families: [“Some awesome font”, “and another”]
urls: [“location/of/font-face/css-file.css”]
But the library can also be used to pre-load multiple Google fonts …
Utils.loadWebFontConfig
google:
families: ["Oswald", "Cookie"]
… which you can then use by referring to their name.
myText = new TextLayer
text: "Hello World"
fontFamily: "Oswald"
Embedding a font file in your project
You can include TrueType and OpenType font files in your project folder and load them with a few lines of CSS.
First, with insertCSS
you insert an @font-face
at-rule to load the font(s).
# Loading the TTF files in the /fonts/ folder
# and giving them a unique font family name
Utils.insertCSS """
@font-face {
font-family: "Walt";
src: url("fonts/New Walt Disney.ttf");
}
@font-face {
font-family: "Walt UI";
src: url("fonts/New Walt Disney UI.ttf");
}
"""
You can then use the names you picked to set a Text Layer’s font family:
textLayerA.fontFamily = "Walt"textLayerB.fontFamily = "Walt UI"
10. Font Weight
In Code, you set the different weights with numbers, just as in CSS.
Here’s a list of what the different values stand for:
100
— Thin / Hairline200
— Extra Light / Ultra Light300
— Light400
— Normal (the default)500
— Medium600
— Semi Bold / Demi Bold700
— Bold800
— Extra Bold / Ultra Bold900
— Black / Heavy
Instead of 400
you can also write "normal”,
and when typing "bold”
you’ll get the 700
weight.
In Design, you can see which font weights a specific typeface actually has (and select them from the list).
Very few typefaces will have all these weights, though. Most only have Normal and Bold.
When a typeface doesn’t have the weight that you picked, Framer will use the closest available one. (So it makes sense to check in Design if a weight even exists.)
Sometimes a particular weight might be found in a separate typeface. To get the black version of "Arial"
, for instance, you’ll need to use the "Arial Black"
font family.
Note: Even though fontWeight
is a numeric value, it is not animatable. Animating to a new value will simply skip the in-between values.
- Framer docs:
text.fontWeight
- Can be ✅ set in Framer Design
11. Font Style
Italic and Oblique are the options here, but you could pick either one.
Well, in theory there should be a difference …
- An italic is a specially drawn, more cursive version of a typeface.
- And an oblique is simply a slanted version of the typeface.
… but in practice only a few fonts have both versions. (Lucida Grande seems to be one, but that’s not a web font.)
When there’s no italic version available, oblique will be used anyway (or created on the fly). That’s why I always pick "italic"
, so that when a font might have both I’ll at least get the attractive option.
The ‘Times’ typeface, at the top, has an italic version, but the (iOS default) ‘San Francisco’ at the bottom does not have one.
- Framer docs:
text.fontStyle
- Italic can be ✅ set in Framer Design in the weight menu (when a typeface has it)
12. Line Height
The lineHeight
property is not a points (or pixels) value, but a multiplier of fontSize
.
By default, it’s 1.25
(in Code): one and a quarter times the font size. So with a font size of 40
, the distance between two lines will be 50 points. (Although this currently seems to be off for small values.)
Pro tip: When you have only one line of text you can use line height to define the amount of vertical space around the text, for instance when making a button (see the example in Background Color above).
- Framer docs:
text.lineHeight
- Can be ✅ set in Framer Design (but with only 1 decimal:
1.2
,1.3
etc) - Can be ✅ animated
13. Font
With .font
you can set a bunch of properties at once, in one single line.
These are the possible properties, in their correct order:
"fontStyle fontWeight fontSize/lineHeight fontFamily"
But only two are required: fontSize
and fontFamily
.
textA = new TextLayer
font: "32px -apple-system"
(Yes, you do write px
for ‘pixels’, but, as with any fontSize
value in Framer, the value is actually in ‘points’.)
The remaining properties are optional. Here’s an example with all possible properties:
textA = new TextLayer
font: "italic 400 25px/1.5 Menlo"
(Don’t forget to type a /
before the line height.)
- Framer docs:
text.font
14. Text Align
The alignment of the text inside the Text Layer block.
Next to the CSS values "left”
, "right”
, and "center”
you can also use Framer’s Align
functions.
- Framer docs:
text.textAlign
- Can be ✅ set in Framer Design
15. Shadow Color
Setting this property on a standard layer would give the layer itself a shadow, but on a Text Layer, it affects the text.
Don’t forget to also set shadowX
and/or shadowY
to actually see a shadow appear.
- Framer docs:
text.shadowColor
- Can be ✅ set in Framer Design
- Can be ✅ animated
16. Shadow X
A shadow is really just a copy of the text placed underneath the text at a certain distance. Shadow X sets this distance to the right, or to the left when you use a negative value.
The distance is in points, but halves (e.g., 4.5
) can be used to add a pixel (on a @2x device).
- Framer docs:
text.shadowX
- Can be ✅ set in Framer Design
- Can be ✅ animated
17. Shadow Y
The same as Shadow X, but for the vertical axis. A positive number will move the shadow that many points downwards, and a negative value will produce a shadow from the top edge.
The distance is in points, but halves (e.g., 4.5
) can be used to add a pixel (on a @2x device).
- Framer docs:
text.shadowY
- Can be ✅ set in Framer Design
- Can be ✅ animated
18. Shadow Blur
The default value is 0
, which will produce a sharp ‘high noon’ shadow. Give it a higher number to soften the shadow with a Gaussian blur.
- Framer docs:
text.shadowBlur
- Can be ✅ set in Framer Design
- Can be ✅ animated
19. Letter Spacing
This property adds spacing between the letters of the text.
For example, when set to 3
it will add three points of additional spacing between every two letters (and will also add six points to every space).
Negative values will subtract this many points from the normal spacing.
Do you want to kern between letters? You can in Design. Select two letters to adjust the Letter Spacing between them.
- Framer docs:
text.letterSpacing
- Can be ✅ set in Framer Design
- Can be ✅ animated
20. Word Spacing
This property adds extra space to every … space.
So changing it to 10
, like in the prototype below, will add ten points of distance between any two words.
- Framer docs:
text.wordSpacing
- Can be ✅ animated
21. Text Transform
A quick way to make text all caps, or no caps at all, or turn the text into a title by capitalizing all the words.
- Framer docs:
text.textTransform
22. Text Decoration
For creating underline, overline, or strike-through text.
You can use combinations, so, for example, adding an underline and an overline (if you must) is possible:
textDecoration: "underline overline"
- Framer docs:
text.textDecoration
23. Text Indent
The textIndent
property sets the indentation of the first line of text.
It’s in points, and you can use a negative value to move the line to the left.
- Framer docs:
text.textIndent
- Can be ✅ animated
24. Direction
By default your text will run from ‘left to right’, just like the text you’re reading right now. So you’ll only change this property when working with Arabic, Hebrew, or other right-to-left languages.
Notice how switching to "right-to-left"
places the period at the correct end of the Arabic and Hebrew sentences.
(The prototype also changes textAlign
.)
- Framer docs:
text.direction
25. Setting other CSS properties
You can set most other CSS properties on a Text Layer (or any other layer), but you’ll have to add them to the style
property.
(You can type most of them in camelCase; here’s a page on MDN with an overview.)
In this example, I gave the layer a gradient background (by setting background
instead of backgroundColor
) and then used a webkit-specific property to clip (mask) that background with the text.
# Linear gradient: shades of gray
textLayerA.style =
background: "linear-gradient(#eee, #333)"
webkitBackgroundClip: "text"
For this to work we also have to remove the color
of the text by setting it to "transparent"
or null
:
textLayerA.color = null
The other three examples are the same, only with different gradient settings.
# Linear gradient: starts at 30% of the height and stops at 90%
background: "linear-gradient(rgb(98,125,77) 30%, rgb(31,59,8) 90%)"# Linear gradient: all the colors!
background: "linear-gradient(to right, red, orange, yellow, green, blue, indigo, violet)"# Radial gradient: from yellow in top right corner to orange
background: "radial-gradient(circle at top right, yellow, #f06d06)"
- Framer docs:
layer.style
I hope you 👏 liked this overview.
And if you did, check out my book — it has all the details about Framer Code, tutorials, and hundreds of example projects (276 Framer projects to be exact). And there’s a free preview version!