Learn how to achieve exceptional typography in your app's user interface that enhances legibility, accessibility, and consistency across Apple platforms. Get up to speed on the latest advancements to the San Francisco font family including the move to variable fonts for accommodating optical sizes and weights. We'll also share tips about how to get the most out of systems fonts, support dynamic type with custom fonts.
For a refresher on the principles behind the San Francisco font family, catch up on “Introducing the New System Fonts” from WWDC15.
Hi, my name is Loïc Sander. I’m a type designer on the Apple design team. In this session with my colleague Jiang we'll introduce you to the details that matter for great UI typography and the tools that will allow you to achieve that for your apps. Overall, all the topics we'll cover are really about understanding the dynamic behavior of text on our platforms. So first I’ll talk about fonts and their aspects that are relevant to UI design.
Then, Jiang will show you around useful APIs and best practices when dealing with typographic styles and layouts. For most people, dealing with type is as simple as picking a font and choosing a point size, and it’s really how it should be. But with our first topic, I’d like to show you how changing the size of text actually has many subtle consequences. Learning about optical sizes is key to a better understanding of legibility, which is something you should always try to care for. Right after opticals sizes, I’ll talk a bit about variable fonts. This is a format that’s letting us refine the behavior of text on our platforms, and can improve the typography of your apps, too. Then, I’ll get into a topic that affects UI design directly: tracking and leading. They’re essential parts of text layout. So it’s definitely worth taking time to become more familiar with them. Last but not least, Jiang will delve into text styles and Dynamic Type: two of our core typographic tools to achieve great UI design. It’ll be a refresher since some of those APIs are not new, but we do also have some updates on the topic. So, let’s get started. To describe what “optical sizes“ are, I'd like to go back a few years when our main system font, San Francisco, was first designed. Our team put a lot of effort in making sure that SF would look great at any point size. And we achieved that by introducing two variants that you’re probably familiar with. We designed SF text for small sizes below 20 points, and SF Display for 20 points and above. These variants are what we call “optical sizes”. They allow us to better control the details that affect legibility. For instance, the space between letters in SF Text and Display is adapted to the fact that one design will be used small, and the other one, large. The vertical proportions between the two designs also differ slightly, so that SF Text can appear a little bit bigger at the same point size, and therefore be more legible when used small. When we introduced this design, it felt like a small revolution. But there’s something I find amusing about the word “revolution”: we usually assume it means forward-motion or progress, when essentially, it means “going back to the start.” Because you see, optical sizes have been a thing ever since typography was invented — and remained a printing process involving physical object that barely changed for centuries. It is true that digital technology gave us a lot more flexibility in handling text, but in one way, it made typography a bit more crude than it used to be. So to make my point, I'd like to show you optical sizes in their natural habitat. As a type and history nerd, I happen to have this old book from the 18th century at home. This is what we call a “type specimen” and it contains samples of typography at various sizes, of which I want to show you two examples. In these pages, we have the same design at a different size — which is to say two different fonts, a bit like a SF Text and Display. They’re about 8 and 42 points in today’s printing units. Now I’d like you to picture that for each letter you see here, there was a metallic stamp of the exact same size that was made by hand.
These so-called “stamps” were designed through a process that involves cutting letterforms and punches of steel. And these punches of steel were then used at the start of a molding process that resulted in a “sort”, which is what these metallic stamps are actually called. Looking at the details of these letterforms reveals how much this manual process was influenced by physical scale. The 8 point letter looks rough because of its smaller dimension, and that says something about optical sizes: they’re entangled with resolution. Which, for people cutting punches, meant designing against the challenges of printing with metal and ink on paper fibers. While for us, it means dealing with the challenges of vector graphics being rasterized on a pixel grid. But regardless of the kind of output you design for, the changes in optical sizes are meant to balance legibility and detail.
And I say this as if they were independent. But legibility is defined by the details. As with SF Text and Display, a key detail here is the space between letters. When type becomes smaller, letters tend to need more space between them, so that the eye can more easily tell them apart. But there are more meaningful details in the letterforms themselves, of course.
Like the thinner parts of the letters that sometimes need to become sturdier as the letters become smaller. Then, some details might strike you as accidents, like the position of the dot on the letter “i” for instance. But that’s intentionally done to make sure an “i” won’t be confused with an “l” at small sizes. Because the smaller gap gets, the easier it is to fill: be it with ink or pixels. So since relative size is relevant here, let’s observe two initial words at their intended scale ratio: the difference between the two designs now feels much less pronounced. Which is the purpose of optical sizes: they balance legibility with a aesthetic qualities, to make the design feel comfortable and consistent across scales. And for people making fonts centuries ago, adapting a design to its scale was a fairly direct process, since they always work at actual size. But on the other hand, digital type design has been faced with an unfortunate challenge. Because you can scale vector graphics infinitely, virtually all digital fonts only contain a single drawing for each glyph.
So type designers have to pick an ideal size to design for. And that means that most fonts you use were really meant to perform best around a certain size.
Of course, type designers can always decide to create multiple fonts, adapted to multiple sizes. But it’s not a decision we take lightly, because it’s a lot more work, and more importantly, it becomes a font family that’s less convenient to use. So when SF was initially designed, we considered that very carefully, and we eventually decided on having optical sizes, but just two of them. For us, this decision was a bit easier to make because the OS could switch between designs automatically. Then we embrace the need to teach everyone about the existence of SF Text and SF Display. And for years now, that’s pretty much been the state of the art on our platforms, but we kept wishing we could refine this behavior more. After all, wouldn’t it be great if a single font would automatically adapt to each size you picked, without you having to switch between Text and Display. Well, that’s exactly what became possible a few years ago. Variable fonts were introduced in 2016, as an evolution of the OpenType specification. The spec was updated in response to the increased popularity of web fonts, and the fact that this technology was really testing the limits of existing font formats. The format fundamentally changes the way glyphs are stored in a font. A glyph is no longer limited to being a single static drawing, it can also describe the way each point moves to produce, a related, but different glyph. Each of these motions, or deltas, are tied to design axes that let software control their behavior. Among these axes, there is one that is of particular interest to us because it is dedicated to the implementation of optical sizes. This has allowed us to update SF Pro and refine existing optical sizes. But this new dynamic behavior really shows its potential with our system serif — New York — where the different adaptations to size are much more noticeable.
Thanks to variable optical sizes, we’re now able to design typefaces that can adapt to scale perfectly. And coming full circle, we’re recovering what was good about physical type. This is why, this year, we’re starting to move away from optical sizes as separate fonts. And it’s not just optical sizes that are now merged together, but weights too. This means that from now on, most of our system fonts will be downloadable as single variable fonts.
This being said, we’ll still publish Text and Display fonts this year to ease the transition. In design tools, you should expect to find new controls for variable fonts. Mostly in the form of sliders that will expose a font’s public axes. But note that they’re an optional control. You can still use that font the usual way, through its predefined instances. In fact, when you’re working with our system fonts, it’s usually best to keep to these predefined instances.
If the variable font you’re using supports optical sizes, you can look for a dedicated slider that will cover a specific range of point sizes. In principle, design apps should take care of keeping the value of that slider in sync with the point size you’re using. But it’s not always the case, and depending on the app, changing the point size may not automatically update the control. So if you notice the point size you use doesn’t match the value on the slider, then you need to set the slider yourself. And it’s totally okay if the point that you’re using is out of range of the slider, just set the slider at the closest value you can. If you’re using a design tool on a previous OS, it is possible that variable fonts won’t work as expected, in that case, it is safer to revert to using the separate optical sizes we still provide, like SF Text and Display. In code, this behavior is entirely automatic: we take care of keeping the optical size and point size values in sync.
This applies to system fonts of course, but also to any custom variable font you might use, as long as it has an optical size axis. So after catching up with the latest developments in size-specific typography, I’d like to touch on two fundamentals of UI text layout: tracking and leading. Tracking is an essential part of how we achieve great size-specific typography. I’ve just discussed optical sizes at length, but there is one thing I didn’t mention, and that’s the fact that optical sizes work better when you pair them with tracking. But first, what do I mean by “tracking”? Well, let’s start by looking at the glyphs of a font first, they already incorporate a certain amount of space in them. And that space is what we call “sidebearings.” It is designed as the glyphs are drawn, and defines the default spacing of text.
But as I pointed out earlier, text requires letter-spacing to vary so it can always look good at any point size. And when you do that, and compensate that initial space that’s what we call “tracking”. It is somewhat synonymous with letter-spacing, as in CSS, but “tracking” specifically refers to the action of adding space between glyphs in text layout. Now, I’m sure some of you were puzzled by what I just said, and thought: “Wait, I thought this was kerning…“ The confusion is common, but tracking and kerning are different things. Kerning is a micro-correction of spacing that is only applied between certain pairs. Kerning is also created by type designers, and the vast majority of times, you shouldn’t need to modify it at all.
But back to tracking, let me show you how and why we use it with SF.
Even when you are designing fonts with size-specific spacing like we did with SF text and Display it’s still good to use tracking if you want to achieve fine-tuned results. With no tracking, the difference in native spacing between SF Text and Display would have been an issue when we switch from one font to the other 20 points. Notice how the last blue, and first orange lines are almost the same length, in spite of the difference in point size. That’s because of their default spacing being different, and that abrupt change is the reason why we use tracking with our optical sizes.
It really is an essential part of the behavior of system fonts, and the reason why we published tracking tables as part of the Apple Design Resources.
Because if you want to accurately reproduce the system font’s behavior, you need to know how much tracking to apply for each point size. This year, with SF Pro becoming a variable font, there is no hard break around 20 points anymore and the design now transitions from Text to Display between 17 and 28 points.
Because of that underlying change in behavior, we’ve had to update the tracking tables.
So note that when you’re using the new SF Pro in your design comps, you’ll need to apply a new set of tracking values between 17 and 28 points.
But while we’re looking at tracking curves, there’s one last thing I’d like to point out. A font can actually contain multiple tracking tables, and we’ve used that to add tracking values that can help with the tightening of strings for instance. So say you’re in the tricky situation, with a truncated string.
You might reach for this very common solution using the kerning API. But as I pointed out earlier, using kerning here is not ideal. It would be better to use a tracking API, because it’s semantically correct, for one, but more importantly: it allows the OS to deactivate typographic features that can clash with tracking — such as ligatures. Because if you’re tracking out a word that contains a ligature with the kerning API, that ligature remains and breaks the rhythm of the word. On the other hand, with the tracking API we take care of disassembling the ligature for you, which makes that word spacing more even. However, the best and preferred solution to make a string fit is to allow it to be tighten automatically. And that’s when we use that tight tracking table. With this API, the system will try to make that string fit, within a range that’s reasonable for legibility — because there is such a thing as too much tracking. And for those cases it’s just better to let strings truncate. One last reason to use this API is that it applies size-specific tracking, which you would have to implement manually if you applied tracking on your own. OK, so let me wrap up on the topic of tracking with a little announcement. This year, we’re enabling tracking for third-party fonts.
This means that font vendors can start adding tracking tables to their own fonts. That embedded tracking will be applied on our platforms; provided that the fonts also contains a STAT table. Such fonts can also be used with a tracking table on older OSes if you apply the kCTFontOpticalSizeAttribute.
Alright, so we’ve looked at the space between glyphs, but let’s now consider another kind of typographic space: between lines. Here, again, I’d like to propose a vocabulary interlude to disambiguate two terms. So let’s first talk about line-height. By default, “line-height” is the height of a font’s vertical limits. You can also measure it as the distance between two baselines, but regardless of the approach, the distance remains the same. Now, when the distance between two lines increases, the space between them is referred to as “leading”. And if trivia helps your memory: the name comes from the days of metal type when this gap corresponded to an actual piece of lead between two lines of text. One important thing to note here is that when there is leading between lines, the line-height includes the leading. So the two concepts are dependent: if you change one, you change the other. Most of the time, you don’t need to concern yourself with leading or line-height, because we’ve already defined them in most of our UI components. But let me show you two examples in which we do modify leading. The Arabic script is a writing system that can feature a lot of ascending and descending parts. And on a given line, they can encroach on the visual space of surrounding lines.
So it’s usually beneficial to add some leading in such situations. For that reason, we’ve made our platforms apply more leading for Arabic and other locales that benefit from a taller line-height. On the other hand, it’s sometimes helpful to tighten the leading, and make things more vertically compact.
On watchOS for instance, we apply tight leading in many places, to maximize the amount of information that can be shown on screen. Such modifications are done through the use of text styles, that Jiang will be covering next. These two examples provide a good closing demonstration of how we strive to design APIs and typographic features. By using pre-existing system APIs, you’ll benefit from the design improvements we’re making each year.
As for the new ones, we try and make it easy for you to consider adopting them, which allows your app to become better with time. This gives me the perfect opportunity now to hand off to Jiang. He will show you more about the powerful font and text APIs we have for you to use.
Thank you, Loïc. After an excursion into the finer details of typography let’s talk about text styles and Dynamic Type, two important system tools that will help you create beautiful and consistent typography in your apps. Text styles are at the core of how we build great UIs at Apple. They provide a framework that enables flexible and consistent typography with a clear hierarchy and enough stylistic range to cover most UI needs. Text styles are a set of predefined combinations of a system font weight, a point size value, and the leading value. They are built in a system that provides typographic hierarchy and makes it easier for you to achieve clear and legible layouts in your apps. First of all, while the text styles define offer a wide range of font sizes to represent the information hierarchy, sometimes, the sizes alone are not enough to highlight more subtle differences. Like here the header of the year and month in Calendar, you can see they’re using a heavier weight from the standard text styles.
Emphasized text styles like these allow you to use the same text styles sizes with a different weight. This is widely used by our system apps. And of course, you have seen the large title navigation bar in Mail. This is using the emphasized large title text style. Now, I’m going to show you how to use it in your own app. Let’s say we have a label using the title1 font. I found the title needs a little bit more “punch”, so why not use a bold weight? Well, emphasize title1 is exactly that.
Here, instead of using the UIFont.preferredFont (withTextStyle:) API, we are using the UIFontDescriptor API to get a descriptor with title1 text style, then we apply of bold symbolic trait to it. With the bold trait you are getting emphasized title1 text style, which maps to SF Pro Bold. You can apply bold symbolic trait to any NSFont, UIFont or SwiftUI Font to create the emphasis variant of that font. While the name of that symbolic trait is “bold”, but the actual weight of the variant depends on the text style, it can be medium, semibold, bold or heavy. As we talked about earlier, text styles come with line heights that we found to be appropriate for most of the cases.
However, sometimes you are working with a more constrained space, so saving spaces between lines can increase the information density. An example here is the Fitness app. Or, when presenting a large amount of text, you might want to give the content a bit more breathing room, to improve the reading experience, like here with Maps. In those cases, you can use the tight and loose leading variants of text styles. In iOS and macOS, tight leading decreases to line height by 2 points, while loose leading increases line spacing by 2 points. In watchOS, the adjustment is reduced to 1 point. Now in this case, we have a long paragraph text like this, and it is using the body text style. For for now, it has 22 point line height. And let’s just say we want to experiment and see how it will look like with a tighter line height. I think you’re now getting the hang of it. Once we apply the tight leading symbolic trait, the text will look like this. There you go. Now it has 20 point of line height. But that does seem to be too tight for such a long paragraph text.
So what about loose leading, how would that look? Now let’s try the loose leading trait to get 24 point line height. OK, that does look much better. So it is actually very similar to what we just covered with the emphasized variants. Only this time we are using the .tightLeading and .looseLeading symbolic traits. And new in this year, you can do leading adjustments for text style fonts in SwiftUI as well. Throughout the system, we have used New York and SF Pro Rounded in our apps, like here with the Books and Reminders app. But it is worth noticing that they don’t just work as standalone typefaces, they work really well with our text styles too. Now let’s say we want to build a label just like Reminders. Let’s combine what we just learned about emphasize text style. We start with a text such as “Today” using emphasized large titled text.
For now it’s using SF Pro Bold. Let’s try to apply rendered design using the fontDescriptor.withDesign() API. You can see now the font has switched to SF Pro Rounded. So we have introduced a really flexible API to allow you to apply these designs to any system UI fonts: for AppKit and UIKit, The font descriptor “withDesign” API can be applied to any UI font. For SwiftUI, the API is a little bit different, you should pass the design when constructing a font to begin with, instead of using a modifier to convert an existing font. When picking a text style with a design, everything else besides the design stays the same, including the font size, weight and line-height.
I know many of you work with web pages or have web views embedded in your app.
A few years ago, WebKit introduced the -apple-system font family so that you can use it to use San Francisco fonts in CSS. This has been widely adopted.
Now the -apple prefixed version has a standard name, system-ui. And earlier this year we brought a new set of CSS font family names like ui-rounded, ui-serif and ui-monospace to Apple platforms. I’m happy to announce that this year text style is a fully supported API in macOS, we are introducing a new set of API to AppKit for all Cocoa apps. This new AppKit API will support the full range of text styles, just like iOS.
The font sizes are optimized to match macOS control sizes. Although there is no Dynamic Type support, you do get all the other benefits of text styles we have just covered. Last year, we shared this chart for Catalyst app text styles sizes, which is really iOS sizes scaled at 77 percent. And this year with Catalyst apps with interface optimized for Mac, you’ll get a new set of text style sizes like this. You see, they are pretty well aligned, so that your apps should only require minimal change to adapt. If you pick “Scale Interface to Match iPad“, it will continue to follow the iOS sizes at 77 percent.
But if you want the new behavior picking, the “Optimized Interface for Mac“ option in Xcode will give you that. As you may already be aware, in iOS there’s another dimension to text style: Dynamic Type. The font you picked will scale automatically according to preferences.
Dynamic Type lets people control how large or small text styles should be for them. It makes text easier to read for a lot of people, so you really should consider supporting Dynamic Type in your apps. By using the system font APIs and text styles together, you are opting into this behavior automatically on iOS. But you can also support Dynamic Type when using custom fonts, and I will cover how to do that in this part of the talk. Alright, the chart we have just shown is a lot to take in, one thing I would like you to notice is that different text styles may have different scaling behaviors.
And here we can see how body, title1 and footnote text styles scale with Dynamic Type. When you’re working on Dynamic Type support with custom fonts, you should also pay attention to this. As we know, typography is critical for brand identity. And many of you are building apps using custom fonts designed for your app and brand, but you still want to support Dynamic Type as that is critical for accessibility.
You want your app to react accordingly when text size preference change, just like iOS system apps do. How would you do that? We have covered this in past talks, by this still deserves a revisit as it is such a common question throughout the years. Since iOS 11, we introduced the new UIFontMetrics class that captures the Dynamic Type capability from text styles, and lets you apply it to any arbitrary font. To use it let’s look at this example. First, I start with a custom font as usual with the standard UIFont API, then I can create a UIFontMetrics from Body text style. With that bodyMetrics object, the font generated by scaleFont API will scale just like how body text style scales when text size preference change.
And it is worth noticing that you can also use it to scale a constant value for custom layouts, like here the spacing between the two labels. We have received great feedback from our SwiftUI developers since the release last year. And this year in SwiftUI, we are making it possible for you to do the same font scaling as in UIKit. To understand this, let’s start by having a piece of Text in our SwiftUI ContentView. So far so good, everything scales accordingly when text size preference changes.
Now let’s say I want to use Avenir font, medium weight, 34 point for this text. I know how to do that, and as you can see it is using the font we intended. However, in iOS 13, the problem is that once you start using custom font, it stops supporting Dynamic Type.
In iOS 14 it will automatically scale, but just to make sure it has the correct scaling behavior as title, I’m going to set the relativeTo parameter, so that it will scale relative to title text style. As you can see, the title text is scaling automatically now. Let’s move on to add the body text.
Similar to how we at the title text, we declare a paragraph text and assign it to Text in a VStack. And I don’t even need to use the relativeTo parameter, since custom font will scale relative to body text style by default now in iOS 14. Both the title and body text are scaling automatically now. But it looks like we can benefit from some padding around the body text, because the text is just too close to the edges and to the title. Let’s add some padding, we start with the fixed padding of 20 points first. Much better.
Now, wouldn’t it be nice if the padding can also scale relative to text size changes? Here we can leverage the new @ScaledMetric property wrapper to define a padding with a value of 20, but it can also scale relative to body text style. Let’s see if that did trick. You can see the padding around the text are scaling in proportion to the text size, instead of fixed size.
OK, here is a recap of what we just showed. The Font.custom() API has a new optional parameter, relativeTo, to specify which text style you want it to scale relative to. If you skip that, it will scale relative to body. And this is a behavior change in iOS 14. You can still create a custom font that is a fixed size and doesn’t scale at all with the fixedSize parameter.
In addition to that, you can also scale a constant just like UIFontMetrics does with the @ScaledMetric property wrapper. Well, we have covered a lot of ground today. Here are some of the basic principles I want to remind you of. We have offered a wide range of carefully designed system fonts in a variety of styles, such as SF Pro, SF Pro Rounded, SF Mono and New York, we encourage you to give them a try. For good typography, hierarchy is important, and text style is a great tool for building such hierarchy. Even when working with custom fonts, you should have plenty of options to support Dynamic Type, which is great for accessibility. We’ve cover many important details of typography, such as tracking and leading, you should only override the default system behavior in exceptional cases, and let the system do the right thing for you for rest of them. If you have to work with custom tracking in your UI, remember that it should be size-specific, otherwise the tracking you provided for small sizes may not look good on large sizes.
I hope you now have a better understanding of the behavior of texts on our platforms, and the text and font API at your disposal to achieve great
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.