Utopia https://utopia.fyi/ Elegantly scale type and space without breakpoints Tue, 23 Dec 2025 15:39:54 GMT https://validator.w3.org/feed/docs/rss2.html Nuxt and RSS en Utopia https://utopia.fyi/og.png https://utopia.fyi/ All rights reserved 2020, James Gilyead and Trys Mudford <![CDATA[Fluid responsive typography: easy when you know how]]> https://utopia.fyi/blog/utopian-typography-is-easy https://utopia.fyi/blog/utopian-typography-is-easy Thu, 30 Oct 2025 18:00:00 GMT I wrote a tiny CodePen to show how little code you need to get your typography scaling smoothly with the viewport: Simple Utopia typography demo

Resize your browser window and watch the heading and body text gradually change size.

You don’t need to be a hot-shot developer to get this working. Anybody who has access to their website’s CSS can do it. I’m not even a developer – I’m a designer. I rarely type any code at all. The Utopia type calculator generates the code for you to copy and paste into your website’s CSS file.

Unlike some other clamp solutions, Utopia abstracts the maths away from specific HTML elements. Instead we create a set of related step values which can then be referred to by multiple elements. It’s a more systematic approach.

I hope this shows how quick it can be to implement simple fluid responsive typography on your website. If you’d like to learn more about how fluid type scales work, watch our introduction to Utopia video.

]]>
[email protected] (James Gilyead)
<![CDATA[Utopian project kickstarter: designing a fluid responsive website in a static tool like Figma]]> https://utopia.fyi/blog/utopia-in-figma https://utopia.fyi/blog/utopia-in-figma Thu, 30 Oct 2025 00:00:00 GMT We’ve published a second video on our Utopia YouTube channel, walking through the project kickstarter file we maintain on Figma Community. I use the file as a starting point for practically all web design projects because it saves so much time and lets me focus on client-specific problems sooner.

This video goes into specifics about how the file’s set up, including the variables and tokens we’ve created to get new projects up and running quickly.

]]>
[email protected] (James Gilyead)
<![CDATA[Generate all pair permutations in Utopia SCSS]]> https://utopia.fyi/blog/generate-all-pairs https://utopia.fyi/blog/generate-all-pairs Mon, 19 May 2025 00:00:00 GMT A new allPairs parameter has been added to utopia-core-scss.

When this boolean paramater is applied to utopia.generateSpaceScale, all possible permutations of the positive and negative space steps will be generated. For a small set of steps, this convenient addition allows you to focus on painting with space, rather than remembering to keep your customSizes list up to date. To that end, if you're using this property, you can omit the customSizes property.

Be aware, however, that if you have a larger number of negativeSteps / positiveSteps, this will result in a much larger final CSS output. Consider using a tool such as PurgeCSS to remove any unused CSS custom properties at build time to get the best developer and user experience.

allPairs is also supplied as a new property in the result of utopia.calculateSpaceScale, should you wish to create your own custom generation of the space scale.

This is available in the latest version of Utopia Core SCSS, now at version 1.3.0.

]]>
[email protected] (Trys Mudford)
<![CDATA[Type scale graphs]]> https://utopia.fyi/blog/graph https://utopia.fyi/blog/graph Tue, 26 Nov 2024 00:00:00 GMT Communicating the power and simplicity of fluid typographic scales to Utopia newcomers continues to be a focus for us.

The concepts of fluid modular scales and their systematic benefits can be tricky to articulate. When we first began sharing the concepts of Utopia – long before this website existed – there was one image that reliably got the penny to drop:

The original graph of fluid typographic scales, plotting each scale step on a line graph, demonstrating the linear steps and modular scales

This graph has gone through a few iterations over the years, and has continued to be a useful deck slide when introducing colleagues and clients to fluid responsive design. For the latest iteration, we’d like to share a small – but hopefully powerful – new feature on our type scale calculator. Your type scale can be viewed in the original font size table, the scale visualiser, and now, the graph view:

A screenshot of the new graph view on the type scale calculator

The graph plots the font size of step 0, and a few surrounding steps, to help visualise the relationship between your two type scales at your min and max viewports. Rather than plot every step, which could get cramped, we’ve opted to focus on the key steps around the origin. If this doesn’t meet your use-case, we’d love to hear from you.

]]>
[email protected] (Trys Mudford)
<![CDATA[Figma plugin and kickstarter V2]]> https://utopia.fyi/blog/figma-plugin-v2 https://utopia.fyi/blog/figma-plugin-v2 Fri, 21 Jun 2024 00:00:00 GMT Our flagship Figma plugin and Utopian kickstarter have been rewritten to support the latest Figma features.

Typographic variables

With Figma recently adding support for typographic variables, it felt like the right time to strip the plugin back to the bones and rewrite it to make the most of the tool's latest features. This plugin generates several variable collections that dynamically drive the generated type, space and grid artboards.

A screenshot of a generated type and space system from the Utopia plugin. There are four main artboards: foundations, space, grid and typography

This fundamental shift in approach transforms these artboards into a reflection of the system, rather than a second source of truth, as they were before. They become disposable, which is a good thing! To that end, we've simplified the plugin so it will only create, not update existing artboards. If you add a new step to an existing type scale, you're free to delete the current type artboard and re-run the plugin, safe in the knowledge the variables will regenerate an accurate reflection of your type and space system.

Modes

If you have a paid Figma account, the plugin will now use modes. Modes allow variables to have multiple states, with state being determined by the parent artboard(s) they're used in. Our plugin treats the default mode for a variable collection as "min", and generates a new "max" mode. Then, rather than generating min and max versions of each step, it generates a single variable, with different values per mode. Modes allow variables to work much harder, slashing the number of variables required, and enabling instant toggling between multiple states of designed elements.

A screenshot of the Figma modes UI, showing the Fluid tokens collection with the min and max modes

If your Figma account tier doesn't include modes, the plugin will generate individual min and max variables.

Space pairs

One key feature of a Utopian space scale is the ability to jump between different sizes at min and max, eg S—XL. That's been missing in the plugin until now, but with the inclusion of modes, the plugin now generates any single step and custom pairs you have configured in the space calculator. This doesn't change anything about how designs are rendered in Figma but it can help us to communicate decisions more clearly.

A screenshot of single step and custom space sizes

Grid areas

The plugin now generates variables for commonly used grid areas: half, quarter, third etc, and demonstrates them on the grid artboard. These are useful when it comes to planning layouts and constraining components at sensible, systematic sizes.

A screenshot of grid areas at min and max grid sizes

Optional artboards

A common request for the Utopia plugin was to be able to opt in or out of the generated artboards. Each section in the plugin UI now includes a checkbox to create or omit the corresponding artboard.

Additionally, you can now supply different min and max base sizes to the space section of the plugin, should you wish to use a fixed or familiar methodology like the 8px space system, but still get the benefits of fluid typography and space scaling.

Kickstarter

The Kickstarter has been fully overhauled to support modes throughout, with every component hooked up to min and max modes, demonstrated on the components page. There's also a new opinionated approach to prose flow spacing and a page mockup to show how this can work. In short, the prose container has a flow space of S (small) between all items, and components can optionally include their own additional "breathing room" above and below. We plan to write more about this in future.

It's fully compatible with the Utopia plugin, making it a breeze to kickstart new fluid projects.

Dark mode

Oh, and one more thing, the plugin now supports dark mode ✨

A screenshot of the Utopia plugin, in dark mode

]]>
[email protected] (James & Trys)
<![CDATA[Readable clamp() with PostCSS Utopia]]> https://utopia.fyi/blog/postcss-utopia https://utopia.fyi/blog/postcss-utopia Tue, 06 Feb 2024 00:00:00 GMT Fast-following from the SCSS plugin, Utopia now has a PostCSS plugin. Thanks to the abstracted utopia-core calculations, creating library-specific generators is a breeze.

So when Stefan asked the question, it seemed rude not to create a PostCSS plugin.

Installation

  1. Install the package through npm
npm install postcss-utopia
  1. Add the plugin to your postcss.config.js file:
module.exports = {
  plugins: {
    'postcss-utopia': {},
  },
}
  1. (Optional tip) You can supply global minWidth and maxWidth viewport sizes in the configuration object and skip passing them to every method in your CSS:
module.exports = {
  plugins: {
    'postcss-utopia': {
      minWidth: 320,
      maxWidth: 1080
    },
  },
}

utopia.clamp()

The most exciting method is utopia.clamp(); a shorthand way to write readable clamp functions:

.my-class {
  margin-block-end: utopia.clamp(16, 24);

  /* Generates */
  margin-block-end: clamp(1rem, 0.7895rem + 1.0526vi, 1.5rem);

  /* If you've not set minWidth & maxWidth in your config, add them here */
  margin-block-end: utopia.clamp(16, 24, 320, 1080);
}

This function generates a fluid clamp that scales from 1rem (16px) to 1.5rem (24px) between the viewports of 320px and 1080px, clamping at either end.

@utopia typeScale()

This generates a complete Utopian type scale using CSS custom properties. Any config that's valid in utopia-core is also valid here, so relativeTo: 'container', usePx: true and prefix: 'custom-name' are all usable.

:root {
  @utopia typeScale({
    minWidth: 320,          /* Defaults to plugin minWidth */
    maxWidth: 1240,         /* Defaults to plugin maxWidth */
    minFontSize: 16,
    maxFontSize: 18,
    minTypeScale: 1.2,
    maxTypeScale: 1.25,
    positiveSteps: 5,
    negativeSteps: 2,
    relativeTo: 'viewport', /* Optional */
    prefix: 'step'          /* Optional */
  });

  /* Generates
  --step--2: clamp(...);
  --step--1: clamp(...); etc.
  */
}

@utopia spaceScale()

Like typeScale above, spaceScale accepts utopia-core config, but generates three sets of custom properties:

  • Space units
  • One-up pairs
  • Custom sizes

These all map to the sizes generated on utopia.fyi:

:root {
  @utopia spaceScale({
    minWidth: 320,             /* Defaults to plugin minWidth */
    maxWidth: 1240,            /* Defaults to plugin maxWidth */
    minSize: 16,
    maxSize: 18,
    positiveSteps: [1.5, 2, 3],
    negativeSteps: [0.75, 0.5],
    customSizes: ['s-l'],
    relativeTo: 'viewport',    /* Optional */
    prefix: 'space',           /* Optional */
    usePx: false,              /* Optional */
  });

  /* Generates
  --space-2xs: clamp(...);
  --space-xs: clamp(...); etc.

  --space-2xs-xs: clamp(...); etc.

  --space-s-l: clamp(...); etc.
  */
}

@utopia clamps()

There's also method to generate multiple clamp custom properties in one go:

:root {
  @utopia clamps({
    minWidth: 320,         /* Defaults to plugin minWidth */
    maxWidth: 1240,        /* Defaults to plugin minWidth */
    pairs: [
      [16, 40]
    ],
    usePx: false,          /* Optional */
    prefix: 'space',       /* Optional */
    relativeTo: 'viewport' /* Optional */
  });

  /* Generates
  --space-16-40: clamp(...);
  */
}

Get your config

Finally, to save manually typing out your config (because who has time for that) you can grab the matching PostCSS configuration object from the type, space and clamp calculators.

]]>
[email protected] (Trys Mudford)
<![CDATA[Utopia SCSS library]]> https://utopia.fyi/blog/utopia-core-scss https://utopia.fyi/blog/utopia-core-scss Fri, 19 Jan 2024 00:00:00 GMT To assist developers using SCSS & Utopia, we’ve published utopia-core-scss. This library includes functions & mixins to calculate and generate Utopian type & space scales directly in SCSS.

Installation

  1. Install the package through npm
npm install utopia-core-scss
  1. Next, import the package into your SCSS file. The plugin won’t generate any code unless called. Use @use to namespace the functions and mixins.
@use 'node_modules/utopia-core-scss/src/utopia' as utopia;
  1. Finally, call the functions and mixins with the same values you know and love from utopia.fyi.
:root {
  @include utopia.generateTypeScale((
    "minWidth": 320,
    "maxWidth": 1240,
    "minFontSize": 18,
    "maxFontSize": 20,
    "minTypeScale": 1.2,
    "maxTypeScale": 1.25,
    "positiveSteps": 5,
    "negativeSteps": 2,
  ));

  $customClampValue: utopia.calculateClamp((
    "minWidth": 320,
    "maxWidth": 1240,
    "minSize": 16,
    "maxSize": 20,
  ));
}

Mixins

There are four mixins:

The prefix for the custom properties can be altered, but defaults to step- for typography, and space- for space units. Space mixins can also supply usePx: true if you wish to disconnect space from browser text zoom preferences.

All mixins can be relativeTo:

  • container (cqi)
  • viewport-width (vw)
  • viewport (vi) ← this is the default.

Full documentation can be viewed on GitHub.

Functions

The four functions mimic the above mixins, and in fact power each one under the hood. Where mixins generate (slightly) opinionated code, functions give you the raw values so you can totally customise the CSS output to your requirements.

Full documentation can be viewed on GitHub.

]]>
[email protected] (Trys Mudford)
<![CDATA[Clamp calculator]]> https://utopia.fyi/blog/clamp-calculator https://utopia.fyi/blog/clamp-calculator Tue, 03 Oct 2023 00:00:00 GMT Utopia offers a holistic approach to type & space systems. Rather than specifying every value, we encourage the use of a concise set of foundational values, implemented with modular scales and harmonious multipliers.

However, we also understand that this approach is not always easy to integrate into an existing product. As a developer looking to embrace fluidity, the use of a complete Utopian system may not be possible, particularly if you're looking for initial buy-in to this way of working.

We're not alone in thinking this; a number of fantastic calculators have been created to fill this gap. We particularly love the min-max interpolation calculator by 9elements. But between these single-token calculators, and our original suite of calculators, there is a middle ground to be filled.

Introducing the Utopia Clamp Calculator. This simple generator lets you define any number of custom fluid tokens between a set min/max viewport and export them as CSS.

A screenshot of the Utopia clamp calculator

Most importantly, the generated code also includes a custom URL taking you back to the source of your defined calculations. CSS clamp has a tricky syntax that can't be manually edited, so we've found it's essential to be able to have a way to tweak these values at a later date.

This calculator is the perfect way to dip your toes into the waters of fluid responsive design, or assist you in retrofitting breakpoint-free sizing in your existing projects, without adding a fully fledged type and space system.

]]>
[email protected] (Trys Mudford)
<![CDATA[Utopia Fluid Type + Space Calculator now supports Figma variables]]> https://utopia.fyi/blog/figma-variables https://utopia.fyi/blog/figma-variables Tue, 19 Sep 2023 00:00:00 GMT We're pleased to announce that the Utopia Fluid Type + Space calculator now supports Figma variables. Variables are a long-awaited Figma feature. Although still in beta, the benefits of using them are already clear and significant enough to incorporate them into our plugin.

When generating artboards through the plugin, three variable collections will be created: "Utopian size", "Space @min" and "Space @max". The first collection contains the raw pixel values generated by Utopia. You're unlikely to use these directly, so you're welcome to "hide from publishing" should you wish.

"Space @min/@max" are mapped to the raw values. When working on a design for a min/max viewport, grab the values from the respective collection and apply them to your work.

These variables are applied to the space components generated by the plugin, as well as the grid layouts demonstrations. Variables don't yet work with grid layout directly, so these values are handled by the plugin as before.

If you re-run the plugin, the variables will be updated and all connected components will automatically inherit the new sizes. This is a mixed blessing as not all Figma numerical inputs can accept variables, meaning that certain values still need to be manually updated. We're hopeful that these remaining parameters will be updated by Figma in time.

This plugin is also fully compatible with the Utopia Kickstarter file.

No modes?

We've chosen not to support modes just yet. Firstly, they can be a little temperamental, which we hope will be resolved as variables move towards a production release. Secondly, variables don't currently work with typographic values, where modes are most useful. Finally, using multiple modes is locked behind a paywall and we didn't want to exclude users who can't afford a paid subscription. We'll be keeping an eye on how modes develop and will look to support them appropriately for all users in the near future.

]]>
[email protected] (James & Trys)
<![CDATA[Designing a Utopian layout grid: Working with fluid responsive values in a static design tool.]]> https://utopia.fyi/blog/designing-a-utopian-layout-grid https://utopia.fyi/blog/designing-a-utopian-layout-grid Mon, 28 Nov 2022 00:00:00 GMT A layout grid is a useful aid for a designer organising information in a given space. Although these grids have their roots in print design, where dimensions are fixed, fluid layout grids still have some utility in responsive design.

<!-- excerpt -->

Fluid grid calculator

This post aims to contextualise the Utopia fluid grid calculator which helps you to define a layout grid by clicking a few buttons.

Layout grids: good for the broad strokes

Although each of our users' experiences will vary slightly according to their devices and settings, grids help us position our basic content areas predictably across pages, and consistently align certain aspects of an interface. For example, a main content area and a sidebar, or three equal-width columns of content.

With modern CSS, layouts like these can be achieved on-the-fly with display properties like flexbox and grid, without needing to first create persistent grids made of columns and gutters. In a design tool, though, it's helpful to set up an underlying grid for reference to keep our designs neat and consistent.

There's a good write-up at gridless.design describing why we might prefer flexible, proportional spacing over a series of fixed columns when we're building for the modern internet. For designers working in tools like Figma and Penpot, though, layout grids remain a helpful way to design with consistent spacing.

Declarative design

Utopia is a declarative design approach, where we describe some rules and let the browser interpret those rules according to its current situation. In 2022 our design tools are still based around fixed-size artboards, while we're trying to design products which scale gracefully to suit any screen. It's like trying to draw all six sides of a 3D cube on a flat surface: we can either draw the cube unfolded, or from multiple angles. Our communication options are limited by the medium. Our only option with current design tools is to draw our designs at multiple viewport widths because an artboard doesn't (yet) behave like a real browser viewport. This is true of breakpoint-based design as well as fluid responsive design, but because Utopia is declarative, we only need to visualise the smallest and largest states, which we call @min and @max. Everything in between will be displayed according to the rules we set.

<figure> <img src="/images/cubism.png" alt="A drawing of a die from two different angles and a flattened-out version"/> </figure>

Utopian projects include two layout grids, which are really the two poles of a single grid. We're looking at the same thing through two different viewports: one small, and one big.

Getting started: designing the grid

I'll describe my process in a series of steps although the reality is much messier. Reviewing typefaces, type scales, content, and components is an ongoing process. Over the course of a project we'll be looking to bring all of these related factors into balance.

We're aiming to design a grid that smoothly flexes between a small screen and a large screen like this:

<figure class="embed"> <div class="embed-wrapper"> <video controls width="1280" loop="true"> <source src="/images/flexy.mp4" type="video/mp4"> </video> </div> </figure>

Step 0: define your body text size and generate a space palette

One of the first – and most important – design decisions on any project is what the majority of the text will look like. Depending on a unique combination of factors including content, audience, and style, a designer needs to specify a handful of properties for the main body text, including a suitable font, weight, size, line height, and perhaps letter spacing. This is the visual voice in which the majority of information will be communicated. The size of this text is the heart of the Utopian system: the centre of gravity around which everything else will orbit. It usually grows with the viewport width but it could also be a fixed size. In any case, we call this value "step 0".

<figure> <img src="/images/step-0-grey.png" alt="A typography sample annotated with its font family, weight, size, and line height properties"/> <figcaption>Some example properties of a step 0 type style.</figcaption> </figure>

The fluid type scale calculator takes that body text size and scales it up and down to generate a series of size tokens. I wrote a blog post about designing with fluid type scales.

The fluid space calculator takes that body text size and scales it up and down to generate a palette of related size tokens to use in your designs. I wrote in detail about designing with a fluid space palette in another blog post.

NOTE: Although this post is about the horizontal spacing of the layout grid, the same Utopian values can be employed to ensure a design's vertical rhythm stays in tune and the layout will look neat on any screen.

<figure> <img src="/images/space-palette-grey.png" alt="An example space palette generated by the Utopia space calculator"/> <figcaption>An example space palette, showing tokens with their respective sizes @min and @max viewport widths.</figcaption> </figure>

1. Choose @min viewport width

A good place to start designing a grid is to decide on a @min viewport width. This doesn't need to map to a particular device. Because Utopia will scale a design up to suit any viewport, it makes sense to design the @min screens as small as is practically possible. Lots of us used 320px phones for years without problems so I generally start around there, even though that's small by today's standards. If a design works at ~320px, it'll probably work everywhere it needs to.

<figure> <img src="/images/iphone-1.png" alt="A first-gen iPhone showing the Utopia home page"/> <figcaption>The 2007 iPhone was the device that really kicked off the responsive design revolution, with its 320 × 480px screen. Apple continued to make 320px iPhones until 2016.</figcaption> </figure>

NOTE: It's worth remembering that a small viewport doesn't necessarily mean a small device. A user might be looking at multiple windows on a tablet or an ultrawide desktop monitor. We're aiming to make our content accessible in as many situations as possible.

2. Choose a number of columns

Because we're designing two views of one grid, the number of columns will remain constant. How and when a design conforms to this is for you to decide. The number of columns will depend on the specifics of each project. If you're not sure, a 12-column grid is practical because it divides evenly into 2, 3, 4, and 6-column layouts. It also offers plenty of options for uneven layouts, for example a 9-column main content area with a 3-column sidebar. Almost all of my projects use a 12-column grid.

3. Choose @min gutter size from your space palette

Gutters are the spaces between the columns. How much space would look appropriate around and between the parts of your design? Would your content be better served by a dense, tightly-packed grid, or something more spacious and relaxed? Try doing some quick experiments in your design tool to get a feel for what works. You can always change your mind later when you have more content and components to work with. Most of the projects I work on use the step 0 value for the gutter size at the @min viewport width.

<figure> <img src="/images/min-grid.png" alt="An example grid for a small viewport"/> </figure>

4. Choose @max viewport gutter and column widths

When designing the large-screen end of the grid, the viewport size is unlikely to be a constraint. It's up to you to choose gutter and column sizes to best show off your project's content. Instead of deciding on an arbitrary container size and trying to fit a grid inside it, you can let your main content container width be the total of the space palette values you choose. When the site is built, the space palette CSS will ensure that in any viewport smaller than the container's max width, the spacing will be in tune.

<figure> <img src="/images/max-grid.png" alt="An example grid for a large viewport"/> </figure>

If you're already second-guessing your body text size decision, you're doing this right. It's all a balancing act. For example, to design your perfect line length you need to know your text size and how much space is available. To know how much space is available, you need to know your gutter size at the viewport width you're designing for. To know the gutter size you need to know your step 0 size. To know your step 0 size you need to know how much space is available. The way forward is to make some educated guesses and continue to review your decisions as your design progresses.

5. Calculate your design tool viewport widths

There's a good chance your column number and gutter size won't magically add up to exactly the width of your nominated @min viewport width. If that is the case, and you add these figures to Figma's Add layout grid panel, you'll end up with an uneven, lumpy grid. Figma splits the difference across all the columns to maintain whole-pixel values. That does make sense but it's not what we want, because the whole point of this grid is to make our designs neat and regular.

<figure> <img src="/images/round-up.png" alt="Two small grids side-by-side. One shows a fixed viewport of 320px with uneven columns. The other shows even columns and a 330px viewport that fits everything in"/> </figure>

Utopia is device agnostic and I'd prefer to design on an even grid with whole-pixel values, rather than an uneven grid in a specific viewport size. The way I achieve that is to round up all column sizes and accept that that will make my viewport width something close to – but not exactly – 320px. I then make my Figma artboards the rounded-up width. The fluid grid calculator will offer to round the @min column value up or down. It will then also offer to adjust your viewport width to match. It's up to you whether you use the original or the rounded-up number as your @min CSS value when it comes to building the site. In most cases it shouldn't make a significant difference.

For the @max artboard size, choose how much space the content container will sit within. It often looks neater to crop the artboard close to the sides of the main content area. In reality it's quite common for people using bigger screens to see the design with more space either side and developers will need to know how edge-to-edge components should scale when the browser window gets really wide.

Design stuff

Now you have the first version of the fluid container in which your designs will live. You can start testing your fonts to see what type scales play nicely with your grid. How big can your headings be at the small end before they start to wrap too much? What levers can you pull to address that? Font size? Letter spacing? Gutter size? How many type scale steps do you need to achieve the dynamics you want across the viewport range? How many columns should your primary content span? Are your line lengths reasonable? All of these decisions are part of the Utopian balancing act.

Each time you add or update an element in your @min design, make the same change (scaled, of course) to your @max design, and vice versa.

Don't think of @min and @max as two separate designs – but as two aspects of the same design.

<figure> <img src="/images/spinning-coin.gif" alt="A coin spinning forever, showing two aspects of a single item"/> </figure>

]]>
[email protected] (James Gilyead)
<![CDATA[Getting started with Utopia Figma Plugins]]> https://utopia.fyi/blog/get-started-with-utopia-figma-plugins https://utopia.fyi/blog/get-started-with-utopia-figma-plugins Wed, 16 Nov 2022 00:00:00 GMT Until now, the tooling for Utopia has been predominantly developer-focused, but we know that's only half the story. To start to address this, we've created a pair of Figma plugins to help designers set out Utopian project foundations.

To begin with, install the Utopia type + space calculator plugin from Figma Community, and create a new Figma design file. Then run the plugin from the menu:

Choose "Utopia / Fluid type + space calculator" from the the Plugins menu

Setting up your type and space scales

An info window will appear with some input fields that'll feel familiar if you've ever used the calculators on utopia.fyi. In fact, the first field allows you to paste a URL directly from one of the calculators, which will read any settings you've previously configured:

A screenshot of the Utopia plugin info window

Once you're happy with the values, click 'Create'. The plugin will generate five artboards in the document:

Five artboards generated by the plugin

Settings artboard

From left to right, the first is a settings artboard that acts as a source of truth for the Utopian values in the document. If you re-run the plugin at any point, it will read these values in as a starting point.

The settings artboard with the ten Utopian values

Space artboard

The next two artboards are your space scales, which are set out for your minimum & maximum viewports. Rather than use a background colour, these space units use the Layout Grids feature in Figma so will only show when the grids are active. Press [Shift+G] to toggle grid visibility within Figma:

Two space artboards with space components/variants

These components can be placed within a design to document space between elements, or used as a temporary reference guide when designing a component. The 'Size' parameter in the right side panel in Figma allows you to choose which space step to use within your design:

Space units within a design in Figma, with the variant menu open

Typography artboard

The last pair of artboards are your typographic scales, also presented according to your min and max viewports. You can configure the number of negative and positive steps within the plugin. There are three sets of type styles generated for both viewports. We typically use a Strong style for headings and other text that we want to visually punctuate our designs. The Prose style is useful for longer-form text which might benefit from a larger, more relaxed line height. The Default style is a catch-all for other snippets of text in our designs, like a standlone link, a short piece of explainer text, or perhaps an image caption. The number and variety of styles required will vary depending on your design's purpose and style but these three serve as a reasonable starting point.

Two typographic artboards, one for the smallest viewport, and one for the largest viewport

These type styles can be accessed from the right side panel in Figma:

Choosing type styles from the Figma styles menu

Updating typographic styles

Now you've generated a set of typographic styles, you'll probably want to change the typeface. Utopia uses Inter as the default font. Inter is an excellent chameleon-like typeface, but it might not be suitable for your design. As of November 2022, updating multiple text styles at once isn't natively supported in Figma, so we've created a utility plugin to do just that.

Once you've installed the plugin, go to the two newly generated typographic artboards and select them:

Selecting both typographic artboards

Run the plugin from the top menu; an info window will appear with six fields:

  • Font family
  • Font style
  • Line height
  • Letter spacing
  • Font size
  • Paragraph spacing

The plugin info window, with six fields for type style updates

Any fields left blank are ignored when updating the type styles, so those properties will not be affected. For this example, we'll adjust the font family and style from Inter to Avenir Roman, and then hit update:

Both artboards with updated fonts in all columns

If you wish to alter a single column, for example all the Strong styles, you can use Figma's Deep Select feature to target only the styles you want to update. Hold the [Command] key (Mac) or [Control] key (Windows) and drag a selection box across those items before re-running the plugin:

The 'Strong' column selected with 'Avenir Black' as the chosen font & style

Next steps

In future posts, we'll discuss how to use these plugins alongside the Utopia Kickstarter, to help you bootstrap your future design endeavours.

]]>
[email protected] (James & Trys)
<![CDATA[A video introduction to Utopia]]> https://utopia.fyi/blog/an-introduction-video https://utopia.fyi/blog/an-introduction-video Tue, 08 Mar 2022 00:00:00 GMT We were recently invited to speak at UX Ghent for their frontend-themed evening. The meetup was held remotely, so we decided to pre-record the talk to see off any potential network glitches on the day.

The side benefit to pre-recording the talk is we can now share this video resource, explaining the origins of Utopia, and how you can begin to use it in your projects.

If you're new to Utopia, this video is a great place to start. Alternatively, if you prefer reading, we've written a longer-form piece for Smashing Magazine that covers the same beats.

<figure class="embed"> <div class="embed-wrapper"> <iframe src="//www.youtube-nocookie.com/embed/DDuGtN-GakA" allowfullscreen="1" frameborder="0" title="An introduction to Utopia"></iframe> </div> </figure>

]]>
[email protected] (James & Trys)
<![CDATA[Painting with a fluid space palette]]> https://utopia.fyi/blog/painting-with-a-fluid-space-palette https://utopia.fyi/blog/painting-with-a-fluid-space-palette Tue, 16 Mar 2021 11:00:00 GMT When it comes to design systems, it's all too easy to focus on the components, the chunky bricks that make up majority of the digital experiences we use. But personally, I'm more excited about the cement that holds it all together.

Gaps, margin, padding, gutters. Whatever you call it, space is everywhere on a website. It's the glue that holds components together, and dictates whether they sit in harmony, or feel disjointed. But in my experience, intentional, systemised and elegant placement of space is so often overlooked by developers and designers alike.

We developers tend to fall into two camps: the "eyeing it brigade", and the "treating the design as gospel clan". The first group see a gap between two components and think "well... that looks roughly like 2rem, let's go with that.". The other bunch get their rulers out and measure the precise gap, popping a 1.9375rem into the system. Neither is ideal. The first runs the risk of disregarding the nuanced decisions that go into a design. The second doesn't allow for the designer making an honest mistake. But importantly, both groups generate duplicate code, every time they set a gap. This issue is only accelerated when a larger team of designers & developers work on a project.

Preventing entropy

As I blogged recently, intentional friction points and shared language help prevent entropy in our design systems. We have a rule of thumb that applies to the big things (components), as well as the small (type and space):

"Before adding to a system, first consider whether anything similar exists, and if so, can the design can be adapted to use the system after all."

By designing from the same system, we can shortcut many of the fiddly micro-decisions about gaps, and as developers, feel empowered to roll out space intentionally on our sites.

A space palette

We're used to the concept of a colour palette. Brand guidelines surface the pre-agreed sets of colours the can be used, and their varying tints and shades, and designers <strike>have to</strike> are meant to stick to them. But what if we applied the same thinking to space?

Until now, Utopia has focused on harmonious and fluid typographic scales that help to ensure your text looks "at home" on any device. But we knew from the start that type was only one side of the coin. In our latest release, we've taken the foundations of a fluid type scale, and built a lightweight but extensible space system atop it, creating a "fluid space palette".

"Step 0" of our type scale becomes the t-shirt size "S", and acts as our origin for further spaces. We then multiply up and down to create a variety of intentional spaces. The multipliers are up to you and your specific design, but we tend to find you need more options close to the origin ("S"), and fewer further out.

A space matrix, comprising of individual fluid space values at their smallest and largest size

T-shirt sizing for space isn't a new concept—most systems map a "size" to a static value. But by building the space system on top of a fluid base value, each "size" is inherently fluid out of the box, rendering smaller on mobile screens, and gradually growing bigger on larger devices.

James has written a post documenting how a designer can arrive at these values, and use them within their design tool. For now, I'm going to carry on talking about how a developer can use this palette.

Interpolating between spaces

Now we have a set of individual space units, we can interpolate between them. "XS → S", "M → 3XL", and even reverse interpolations of "2XL → 2XS".

An interpolation of an XL to 2XL space

Out of the box, Utopia provides all the positive "single-step pairs" (2XS → XS, XS → S, S → M etc). Where an individual space value (M) grows a slight amount, these single-steps grow at a slightly steeper rate.

Pairs are perfect for handling internal component padding between small and large screens. Thanks to custom properties and CSS locks, you don't need to lock off these values every time you use them, it's a simple as:

.c-card {
  padding: var(--space-s-m); // 18px → 33px
}

Custom space pairs

To take it one step further, you can interpolate between any two individual space sizes, creating incredibly steep slopes (3XS → 4XL), perfect for handling spacing for hero slats, or even reverse slopes that get smaller as you expand the screen width (2XL → XS). These custom space pairs are fully configurable within the space calculator on Utopia.

A collection of custom space pairs

Using a fluid space palette

Utopia exposes these fluid spaces as custom properties, which makes them entirely opt-in. There are two main ways to use them:

In your CSS

As demonstrated with the earlier internal padding example, you can use fluid space values directly in your CSS. It's not limited to padding though, anything that can take a pixel value, can also use these fluid values. Margin, padding, grid-gap, clip-path, transform all work great with Utopia:

.o-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-gap: var(--space-s-l); // 18px → 44px
}

Utility classes

When used appropriately, utility classes can be a great way to 'paint' space onto a web page. I love the flow utility that Andy Bell documented, so I often start by creating a few versions of that with fluid space values:

.u-flow > * + * {
  margin-top: var(--space-s);
}

.u-flow--l > * + * {
  margin-top: var(--space-l); 
}

.u-flow--s-m > * + * {
  margin-top: var(--space-s-m); 
}

These can then be used in my HTML to provide bulletproof and fluid spacing between any number of items:

<article class="c-card u-flow">
  <img src="" alt="" />
  <h2>Card title</h2>
  <p>Card description</p>
</article>

Painting with space

The most empowering part of this system, is the control it gives me, as a developer, to make the call on how things should shrink or grow on different devices. In the past, I would be given a 'desktop' mockup, a 'mobile' mockup, and occasionally a 'tablet' sized mockup, and I'd have to try and shoehorn a system that worked between all three sizes. Now, with a fluid space palette, and a common language between design and development, I rarely <strike>get</strike> need more than a large screen design file. This leaves me to make the most appropriate call on how components should be spaced, or what the most appropriate gutter is to make the design feel at home on a small device.

I can't begin to explain how much of a difference it has made to my personal development process to be able to check a consistent design, and have the control at my fingertips to create a harmoniously spaced website. But more importantly, this shared language has significantly improved the way I communicate with the design team. And by spending time upfront deciding on the shared design foundations, we can each focus on what we’re best at.

Want to try it out?

If this has whetted your appetite to start building with fluid space, head on over to the fluid space calculator and create your own responsive and intuitive space system. The tool visualises the space system, and generates all the CSS you'll need to implement it in your project. Furthermore, we're working on a Figma plugin to make the designer's life easier too—watch this space.

]]>
[email protected] (Trys Mudford)
<![CDATA[Designing with a fluid space palette]]> https://utopia.fyi/blog/designing-with-a-fluid-space-palette https://utopia.fyi/blog/designing-with-a-fluid-space-palette Tue, 16 Mar 2021 10:00:00 GMT Space is the ultimate fundamental; the most ubiquitous “thing” there is. In fact our brains generally perceive space as the “nothing” between the “things” they prefer to fixate on. We move through this silent, invisible, three-dimensional emptiness with very little resistance and rarely think about it at all. So is it any wonder space is an often-overlooked element of design, appearing almost by accident between the bits we spend our time carefully crafting?

Thoughtful spacing is a key ingredient of effective design. It can group or separate content, help communicate your brand character, pack as many of your products as possible into a single viewport, or afford a beautiful, distraction-free reading experience.

In this world of many screens, we typically design digital layouts with tighter spacing for smaller screens and relax the spacing as more real estate becomes available on larger screens. There is, however, no single standardised approach to defining how space behaves from viewport to viewport.

In my experience, visual designers tend to deal with space on an ad-hoc basis, creating components and layouts that look “right” at a number of specific viewport widths. This can leave developers with the task of deciphering spatial relationships within and between components, either following the designer’s file with pixel-precision, or by adjusting and standardising spacing to ensure order and repeatability in the code. Since spacing is often not well documented in style guides, this can lead either to the faithful reproduction of unintentional decisions, or a thoughtful refactor which doesn’t follow a designer’s intent.

What we need is a way to make and communicate deliberate decisions to eradicate doubt and ensure we’re all singing from the same stylesheet…

Enter the matrix

During the last few projects Trys and I have worked on together, we’ve developed some ways to help streamline the relationship between design and development. Step one was to define a systematic approach to fluid responsive typography. Step two has been to formulate a similar approach to space, which I’ve been calling the “space matrix”. It starts off pretty much the same as our typographic scales:

  1. Define a type space scale for a small screen
  2. Define a type space scale for a large screen
  3. Tell the browser to interpolate between the two scales, based on the current viewport width.

The space scales we’ve been working with so far have been multiplied directly from the body font size of each project, without defining different scale multipliers for min and max viewports. It’s important to assign size names to these values to make them easy to refer to. We use t-shirt sizes: (S)mall, (M)edium, (L)arge, etc. For example:

<figure> <img src="/images/space-values-individual.png" alt="Although I keep track of decimal places for documentation, I round all values to the nearest pixel when working with them in Figma"> <figcaption>Although I keep track of decimal places for documentation, I round all values to the nearest pixel when working with them in Figma.</figcaption> </figure>

The multiplier values in this example have emerged from real-world projects as reasonable steps to provide enough spatial contrast for grouping or separating content, padding inside a button, or big, confident spacing for hero slats. These values can of course be adjusted to suit any project, before or during the design process. We’ve found that we need more nuanced space options nearer the base font size, and fewer options at the larger end of the scale. Hence the smaller increments are 0.25 and the larger increments are whole numbers.

Correctly implemented, these individual space values are already fluidly responsive – an (S) space flexes depending on the viewport width: In this example, 16px @min, through to 18px @max, and a proportional size within that range at any viewport width in between. Admittedly, a 2px flex is pretty underwhelming. To make our space palette work even harder though, we can pair up these values to add a dimension of attitude, increasing the variance between the space @min and @max:

<figure> <img src="/images/space-values-pairs-custom.png" alt="Individual space values are subtly responsive by default. Space value pairs can offer much more dynamic space options"> <figcaption>Individual space values are subtly responsive by default. Space value pairs can offer much more dynamic space options.</figcaption> </figure>

These new space pairs flex with much more drama than the individual space values:

<figure> <div class="flexi-card root" id="flexicard" style="--fluid-screen: 45.625rem;"> <div class="flexi-card__wrapper"> <article class="example-card"> <a href="#"> <p>Content type</p> <h2>Card title</h2> <p>Content lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> </a> </article> </div> <label for="viewport" class="screen-reader-only">Viewport</label> <form class="flexi-form"> <span class="u-style-label">@min</span> <input type="range" min="20" max="71.25" value="45.625" id="viewport" oninput="document.getElementById('flexicard').style.setProperty('--fluid-screen', this.value + 'rem')" /> <span class="u-style-label">@max</span> </form> <form class="flexi-form"> <div class="c-checkbox"> <input type="checkbox" id="show-space" checked class="screen-reader-only" oninput="document.getElementById('flexicard').style.setProperty('--opacity', this.checked ? 0.5 : 0)" /> <label for="show-space" class="u-style-label">Show space</label> </div> </form> </div> <figcaption>The space between the card’s items is defined as an individual space value, which only changes subtly between viewports. The card’s padding is defined as a space pair, which changes more dramatically.</figcaption> </figure>

With the space matrix we can simply define those space pairs and use them anywhere that particular size and attitude is appropriate.

In this way we can build up a palette of fluid space sizes to repeat throughout our design. Because these sizes are calculated from the current viewport width, the layout will simply “breathe” to make the most of the available space, and we can forget about breakpoint-based space rules. We don’t need to worry about the latest iPad size or the new Google hugePhone. Our design will automatically look proportional on any device regardless of its exact viewport width. There’s no limit to how many spaces you might define, although I prefer to work with the fewest possible.

"Because these sizes are calculated from the current viewport width, we can forget about breakpoint-based space rules"

KISS: Keep it simple and systematic

In the same way a layout grid helps guide us to design in an orderly fashion without second guessing every size and space decision, the space matrix can help us to create consistent designs which adapt gracefully to look at home on any screen. We can make certain design decisions much more quickly: What size should my grid’s gutters be? What about its columns? Button padding? Elements within a card? What about the space between cards? In all of these cases we can now pick a suitable size from the space palette. If nothing comes close enough, we can always add a size.

<figure> <div class="image-stack"> <img src="/images/space-matrix-off.png" alt=""> <img src="/images/space-matrix-on.png" alt=""> </div> <figcaption>Every space in our designs can now be described by name.</figcaption> </figure>

Creating a bespoke space matrix does introduce some cognitive overhead early in the design process but it can dramatically speed things up once the project gets rolling. Think of it as engineering a custom set of rails to design on. I start practically every design project by defining the font and the body copy size. Once I know that magic number I can step up and down through my default multiplier increments to identify my space matrix values and start mapping out layout grids and generic components. A developer can start setting up the design foundations in code at the same time because we already have a common language and a shared approach. In other words, we’ve agreed on a set of – malleable – project-specific constraints to ensure we’ll be building the same thing in the same way.

Get started

To help you incorporate a space matrix in your next project, we’ve designed a tool to quickly calculate a set of space values for you. This tool also generates CSS ready to be included in your project’s code. We’re also working on a Figma plugin to automate some of the initial asset generation and speed up the first stages of the designer’s project setup.

If you’re looking for more details on the CSS magic that makes this work in the browser, Trys has written an excellent blog post about that.

]]>
[email protected] (James Gilyead)
<![CDATA[Clamp]]> https://utopia.fyi/blog/clamp https://utopia.fyi/blog/clamp Fri, 25 Sep 2020 00:00:00 GMT CSS has some exciting new features for fluid scaling. clamp(), min(), and max() are functions that cap and scale values as the browser grows and shrinks.

Much like their Math. equivalents in JavaScript, min() and max() return the respective minimum and maximum values at any given time.

Clamp is a superset of the two, letting you pass in a minimum, maximum, and 'preferred' size (this is a suggested size for the browser to use). A straightforward usage would be: clamp(1rem, 1vw + 1rem, 2rem), where the browser will scale between 1rem and 2rem, trying to always be 1vw + 1rem, assuming it doesn't drop below the minimum, or exceed the maximum values.

While this is good, it doesn't provide an obvious way to cap where the minimum and maximums should be, and therefore how aggressively it should interpolate betweeen the two. It is possible, but isn't as clear as a CSS lock. Without some reasonably heavy mathematics, clamp() seems to be most useful when you want broadly fluid typography, without being 100% specific about the relationship between the varying sizes.

If you need more precision for a single typographic size, Pedro Rodriguez has written a fantastic article that gives you all the maths, and explains how to use clamp() to interpolate between two viewports and sizes. It includes a calculator to work out the angle and intersection point of the slope, required to accurately scale typography.

Preparing clamp() for typographic scales

Utopia strives for precision, flexibility, and ease of use, helping you create harmonious and fluid typographic scales. We therefore wanted to see how Utopia could incorporate clamp() most effectively. For this example, we're going to interpolate between:

Min Font Size Max Font Size Min Viewport Max Viewport
1rem 2rem 320px 1440px

The equation

Here's the equation (thanks to Pedro) for calculating the clamp:

Slope = (MaxSize - MinSize) / (MaxWidth - MinWidth)
yIntersection = (-1 * MinWidth) * Slope + MinSize
font-size: clamp(MinSize[rem], yIntersection[rem] + Slope * 100vw, MaxSize[rem])

As with every abstraction, there's a trade-off between verbosity and flexibility. Here are three examples on that scale:

1. Move the calculation into CSS

:root {
  --f-max-w: 90; // 1440px in REM
  --f-min-w: 20; // 320px in REM
  --f-minus: (-1 * var(--f-min-w)); // Precalcuation for the -MinWidth we need
  --f-w: (var(--f-max-w) - var(--f-min-w)); // Precalculation for the (MaxWidth - MinWidth) we need

  /* Per step size */
  --f-0-min: 1; // Min font size
  --f-0-max: 2; // Max font size
  --f-0-slope: (var(--f-0-max) - var(--f-0-min)) / (var(--f-max-w) - var(--f-min-w));
  --f-0-intersection: ((-1 * var(--f-min-w)) * var(--f-slope) + var(--f-0-min));
  --step-0: clamp(var(--f-0-min) * 1rem, var(--f-0-intersection) * 1rem + var(--f-0-slope) * 100vw, var(--f-0-max) * 1rem);
}

We can recreate that calculation directly in CSS using Custom Properties. The first aim is to expose the four parameters: Min & Max font size, and Min & Max viewport. Given Utopia is concerned about the relationship between multiple typographic sizes, we will separate the viewport sizing, and the steps themselves, to avoid duplication.

Each step calculates the slope and intersection as per the equation above, but with substituted custom properties.

2. One-line the calculation

:root {
  --f-max-w: 90; // 1440px in REM
  --f-min-w: 20; // 320px in REM
  --f-minus: (-1 * var(--f-min-w)); // Precalcuation for the -MinWidth we need
  --f-w: (var(--f-max-w) - var(--f-min-w)); // Precalculation for the (MaxWidth - MinWidth) we need
  
  /* Per step size */
  --f-0-min: 1; // Min font size
  --f-0-max: 2; // Max font size
  --step-0: clamp(var(--f-0-min) * 1rem, ((var(--f-minus) * ((var(--f-0-max) - var(--f-0-min)) / var(--f-w)) + var(--f-0-min)) * 1rem) + ((var(--f-0-max) - 1) / var(--f-w) * 100vw), var(--f-0-max) * 1rem);
}

It's possible to 'one-line' option 1. Not hugely readable, but vertically succinct, and still flexible.

3. Fully pre-calculated

:root {
  --step-0: clamp(1rem, 0.7143rem + 1.4286vw, 2rem);
}

Using Pedro's formula, we can precalculate the clamp()—it's pretty darn elegant. One line, and total fluid type without a media query. The only complaint: the 'preferred' value isn't at all clear to read, and impossible to work out without a calculator. Changing the preferred value, or the 1rem/2rem will also break the function.

Adding clamp to Utopia

We've added the option to generate Utopian scales using clamp(), opting for the precalculated option. A pre-calculated clamp is considerably more succinct than a CSS lock, even if the result is less readable. If you're happy with that trade-off and are aware of the potential accessibility impacts, you can generate and download the clamp() version of Utopia with the generator.

The dangers of clamp

Adrian Roselli quite rightly warns that clamp can have a knock-on effect to the maximum font-size when the user explicitly sets a browser text zoom preference. As with any feature affecting typography, ensure you test thoroughly before using in production.

]]>
[email protected] (Trys Mudford)
<![CDATA[Utopian CSS generator, an iteration]]> https://utopia.fyi/blog/a-second-generator https://utopia.fyi/blog/a-second-generator Wed, 09 Sep 2020 00:00:00 GMT We've just published a second generator for Utopia.

The initial controls are the same as the first, you control the viewport, base size and type scale for your minimum and maximum target screens, and the generator will provide project-ready CSS.

Originally, the idea of leaving the configurable options within the <code>:root</code> declaration seemed ideal; it allowed you to tweak the parameters without recalculating each time. And for type scale purists, or for when you're starting out on a project, that may well still be the best option - we don't plan on sunsetting that generator.

But having applied these Utopian principles to a few more projects, we were finding we wanted a little more control over each step in the scale.

Take this scale:

Step Min Max
0 19.00 22.00
1 22.80 26.40
2 27.36 31.68
3 32.83 38.02
4 39.40 45.62
5 47.28 54.74
6 56.73 65.69
7 68.08 78.83

It uses a variable base size of 19-22px, but a fixed minor third type scale of 1.2. This gives us subtle shifts between font sizes; step 5 only shifts 7px between the two breakpoints. This is perfect if you need a slow ramp up to larger sizes, but does have a downside. If you're after a nice chunky heading size (let's say step 7 rounding out at 78.83px), you have to accept that it'll be 68px on the smallest screen. That's generally too large on a mobile device, leaving you with overflowing or wrapping words.

The original generator baked those two values into a complex calculation, making it hard to unpick. The new generator -calculates the values for each step within the Utopia website, but leaves them tweakable in your codebase:

--f-7-min: 68.08;
--f-7-max: 78.83;
--step-7: calc(((var(--f-7-min) / 16) * 1rem) + (var(--f-7-max) - var(--f-7-min)) * var(--fluid-bp));

Overriding the steps

With the new generator, you can now bring that minimum value down to something a bit more reasonable. Be aware, by doing this, you are in effect changing the type scale for this step, so the rhythm between each step will be altered.

The first option is to manually change the minimum, say to 50px:

--f-7-min: 50;
--f-7-max: 78.83;
--step-7: calc(((var(--f-7-min) / 16) * 1rem) + (var(--f-7-max) - var(--f-7-min)) * var(--fluid-bp));

But that's pretty arbitrary. It would be better if we could keep things systematic. Thanks to custom properties, you can feed the 'output' of another step, into the input of this step:

/* Start at the same point as step 5 */
--f-7-min: var(--f-5-min);

/* Start at step 4's end point */
--f-7-min: var(--f-4-max);

Why another generator?

The original generator prioritised the integrity of the type scales, which still works well for certain projects. This new generator introduces a layer of flexibility, removing restrictions without sacrificing the entire system. Utopia is an 'opinionated' methodology, that sets sensible foundations for your responsive web projects; this iteration exposes those opinions for you to tweak to taste.

]]>
[email protected] (Trys Mudford)
<![CDATA[Dealing with negativity in fluid type scales]]> https://utopia.fyi/blog/dealing-with-negativity-in-fluid-type-scales https://utopia.fyi/blog/dealing-with-negativity-in-fluid-type-scales Mon, 17 Feb 2020 00:00:00 GMT A quirk of composing type to a scale is that a larger ratio will translate to larger font size jumps both above and below your body copy size. This means any text smaller than your base size might end up smaller on a large screen than on a small screen, which is generally undesirable.

Type scales play an important role in systematic design, helping us to achieve harmonious and consistent designs by defining a set of related sizes to choose from throughout our project. We often base our type scale on the body copy size, stepping up and down from there to create sizes for various headings, blockquotes, tags, etc. Since Trys and I have started thinking in Utopian terms at Clearleft, we’ve been naming our type sizes by their step on the scale; step -1, step 0, step 1, etc, with step 0 being our body copy size.

In most cases this approach works well, since we generally choose a body copy size for our smallest viewport, and increase this size as the viewport becomes wider. Coupled with the fact that many projects don’t call for more than a few type sizes below step 0, this means we don’t need to worry about large ratios resulting in tiny text on desktop displays.

Where this could cause problems is if your project uses a static body copy size – ie one that does not vary according to viewport width – and a large scale ratio at max width:

<figure> <img src="/images/fluid-type-scale-visualisation-with-static-step-0.png" alt="A fluid type scale visualisation with a static step 0."> </figure>

This will give you completely impractical negative step sizes at your max width:

<figure> <img src="/images/static-step-0-fluid-type-scale-example.png" alt="A card mockup with a type scale ratio of 2, demonstrating unusably small font sizes."> </figure>

I’ve used a huge scale ratio of 2 to illustrate my point but this also happens with much more conservative scales. In some cases the effect is negligible. In others you will find it unacceptable. It could even be desirable – for instance it could be employed to scale a link hit area to be larger on smaller screens.

Utopia makes it trivial to arrive at a set of values to use in your designs but it doesn’t prescribe how you should use those values. In a situation like this you might decide to unhook small text from your main Utopian scale. You could then either set its size manually, or design a separate scale specifically for a range of small text sizes. Another option would be to simply build your scale around your smallest text size rather than your body copy. You could also consider increasing your overall number of steps and skipping some of them at the larger end of your scale.

This is all part of the art of designing with scales. It’s not always easier but it does encourage us to think systematically, which is a good thing.

]]>
[email protected] (James Gilyead)
<![CDATA[Fluid custom properties]]> https://utopia.fyi/blog/fluid-custom-properties https://utopia.fyi/blog/fluid-custom-properties Mon, 03 Feb 2020 00:00:00 GMT A core theme of the seminal introduction to responsive web design was an acceptance of the 'ebb and flow' found on our inherently fluid web. But despite a broadly accepted view that device-based breakpoints are flawed, and a myriad of solutions proposed to solve the problem, it's fair to say we still mostly think in terms of pre-defined breakpoints.

Writing truly isolated components and tailored breakpoints; where we reevaluate our original CSS decisions as the viewport gets to a size that breaks the component, is great in theory, but challenging in practice. Ubiquitous language in a team, and a natural desire to consolidate magic numbers tends to lead to either a set of easy to remember numbers: 30em, 40em, 50em, or more generic 'small, medium, large' mixin breakpoints. Both approaches create jarring 'breakpoint jumps' rather than a natural easing as a screen changes.

When there is shared styling between components, our instinct is to group the components in some respect. This tends to fall into one of three camps:

  • Writing large selectors, or relying on Sass @extends, despite its flaws and horizontal coupling.
  • Peppering our HTML with utility classes like .mt18 and .pb24.
  • Duplicating the common styles and accepting the performance hit.

These all work at a single width, but begin to fall apart as more screen sizes get involved. Here are some of the most common next steps to patch the issue:

  • Adding @media breakpoints to the utility classes and renaming them to something more generic.
  • Create multiple utility classes with breakpoint-specific suffixes, adding all of them to an element.
  • Continuing the duplication with identical @media breakpoints in each component.

None of these are ideal – not only are we duplicating code or coupling horizontally, we're still thinking about device-specific breakpoints. It's a problem, and it affects all aspects of our work – spacing, rhythm, layout and typography.

We need to think fluidly.

A proposal

Fluid custom properties combine CSS Locks, CSS Custom Properties, and the concept of hills (yes, hills). They allow us to write fluid CSS without writing any breakpoints.

A fluid custom property is a font-size representation of a gradient or slope, set between two screen sizes, and stored as a global CSS custom property.

With a predefined set of fluid custom properties at the heart of a project, we can hook onto them to create natural, breakpoint-less spacing and typography that gradually interpolates across screen sizes.

Relying on these global rules brings consistency across a project, and helps to ensure every component looks 'just right' on all screens. There are no nasty 'breakpoint jumps', just buttery smooth interpolation.

They significantly reduce code duplication and keep code succinct and readable. Rather than coupling horizontally, shared styles are linked vertically to these global, project-specific constants.

All the complicated maths is abstracted away, leaving you to work with natural numbers, browser text zoom preferences are respected, and they work naturally with ems.

They're also entirely opt-in; the brilliance of custom properties is that they do nothing to your webpage until you reference them. This makes it a great way to retrospectively add fluid sizing to an existing site.

Let's dig into the three concepts in a little more detail:

CSS Locks and interpolation

Linear interpolation is a mathematical technique used to calculate the value at a position between two points. In the CSS and animation world, Interpolating or 'tweening' is the process of smoothly changing a value between the two points over two screen sizes. We can achieve this effect with CSS locks, a technique created by Mike Riethmuller.

Below is a CSS lock that interpolates between a font-size of 1em and 2em between the two screen sizes of 20em (320px) and 50em. The locking is handled by the media query directly below it, without it the growth would continue at the same rate forever.

p {
  font-size: calc(1em + (2 - 1) * ((100vw - 20em)/(50 - 20)));
}

@media screen and (min-width: 50em) {
  p {
    font-size: 2em;
  }
}

Writing a lock by hand is pretty verbose, so Sass mixins are regularly turned to. This has the huge advantage of making your life as a developer easier, but the distinct disadvantage, like all pre-processor features, of distancing yourself from the final CSS output. Once you've been bitten by the fluid bug and seen its virtues, it's very easy to end up with several hundred CSS locks, and thus several hundred media queries. That's a lot of code.

CSS custom properties

There are plenty of wonderful guides to CSS custom properties, so I shan't go into too much detail. Here's a CSS custom property definition and usage example.

:root {
  --brand: #FF4757;
}

a {
  color: var(--brand);
}

Not only are they a great way to extract common values out to a central location, they can be overridden using the cascade, and used in calc() functions. Using custom properties for typography and vertical rhythm has been well documented, but they can be used for so much more. We can combine CSS custom properties with locks to great effect...

Refactoring the lock

Let's rewrite the CSS lock we used earlier, harnessing the descriptive power of custom properties. We start by extracting the configurable parts of the lock into a :root definition. The vast majority of CSS locks run from 20em/320px, so we'll keep that in the lock for brevity. Then we can substitute the values within the declaration and media query, multiplying the appropriate values by 1em:

:root {
  --max-value: 2;
  --min-value: 1;
  --max-screen: 50;
}

p {
  font-size: calc(
    (var(--min-value) * 1em) + (var(--max-value) - var(--min-value)) *
      ((100vw - 20em) / (var(--max-screen) - 20))
  );
}

@media screen and (min-width: 50em) {
  p {
    font-size: calc(var(--max-value) * 1em);
  }
}

Sadly, we can't use custom properties in the media query definition, so we have to repeat the 50em. But ignoring that, we've extracted all the other 'bits' of the calculation into a single source of truth. The CSS lock now looks even more unwieldy than it did before but – crucially – the bits we actually need to access are much easier to read.

Even more refactoring

With traditional CSS locks, you need a media query for every lock, but as fluid custom properties rely on cascading custom properties, we can solve this really elegantly in one line.

CSS locks use the 100vw unit to represent the varying screen size, but this doesn't have to be the case. We can extract that value into its own custom property: --f-screen.

When we've reached the 'lock point', rather than update all the CSS locks we have on the page, we can update the value of --f-screen to be the width of our --max-screen. This one line change holds every lock in its maximum state.

:root {
  --max-value: 2;
  --min-value: 1;
  --max-screen: 75;

  --f-screen: 100vw;
  --f-bp: (var(--f-screen) - 20em)/(var(--max-screen) - 20);
}

p {
  font-size: calc((var(--min-value) * 1em) + (var(--max-value) - var(--min-value)) * var(--f-bp));
}

@media screen and (min-width: 75em) {
  :root {
    --f-screen: calc(var(--max-screen) * 1em);
  }
}

This is a rather neat refactor, but it's still only working at a selector-level - we can still step it up a notch or two. But before we can talk about that, we need to talk about hills.

Hills, grades & slopes

When travelling by road, we can refer to the steepness of a hill by a gradient or grade. They're often given in terms of a ratio: 2:1 or a percentage: 30%. The higher the percentage, the steeper the incline, and the more likely you'll need to get off your bike and walk up the hill.

A CSS lock can also be visualised as a hill. The screen sizes define the where the hill starts and ends (or the foot and summit), and the two values (say, 1em and 2em) dictate the gradient. When plotted onto a graph, it looks a little like this:

<figure> <img src="/images/lock-graph.png" alt="A graph demonstrating a CSS lock"> <figcaption>A CSS lock, visualised</figcaption> </figure>

In this example, we're interpolating between two specific values: 1em and 2em, a relationship of 2:1. This is great, but a bit limiting. What if we wanted to interpolate between 2em and 4em. Fluid custom properties encapsulate that relationship into a fluid multiplier that lets you re-use that angle in various ways across a project.

The implementation

Below is the CSS for four fluid custom properties than run between 320px and 1200px.

:root {
  --f-summit: 1200;

  --f-screen: 100vw;
  --f-foot: 1 / 16;
  --f-hill: (var(--f-screen) - 20rem) / (var(--f-summit) / 16 - 20) + var(--f-foot) * 1rem;

  --f-1-25: ((1.25 / 16 - var(--f-foot)) * var(--f-hill));
  --f-1-5: ((1.5 / 16 - var(--f-foot)) * var(--f-hill));
  --f-2: ((2 / 16 - var(--f-foot)) * var(--f-hill));
  --f-3: ((3 / 16 - var(--f-foot)) * var(--f-hill));
}

@media screen and (min-width: 1200px) {
  :root {
    --f-screen: calc(var(--f-summit) * 1px);
  }
}

Let's break it down section by section.

--f-summit: 1200;

This property denotes the largest screen size in px. This gets converted to rems internally to ensure text zoom preferences are respected.

--f-screen: 100vw;
--f-foot: 1 / 16;
--f-hill: (var(--f-screen) - 20rem) / (var(--f-summit) / 16 - 20) + var(--f-foot) * 1rem;

--f-screen holds the width of screen (100vw) until we reach the summit. Extracting this make sets us up to be able to succinctly lock all the properties in one go. All fluid custom properties are ratios based off of 1, and --f-foot represents that.

--f-hill is the media query part of the lock, running from 320px to our --f-summit. By extracting this out from the back end of the CSS lock, and into its own CSS custom property, we can cap all the custom properties in one go - more on that later.

It's worth noting I've intentionally baked in the assumption of that start point. Extracting that out to another custom property is perfectly valid if it fits your use-case better.

--f-1-25: ((1.25 / 16 - var(--f-foot)) * var(--f-hill));
--f-1-5: ((1.5 / 16 - var(--f-foot)) * var(--f-hill));
--f-2: ((2 / 16 - var(--f-foot)) * var(--f-hill));
--f-3: ((3 / 16 - var(--f-foot)) * var(--f-hill));

These are the fluid custom properties themselves. --f-1-25 represents a gradient of 1.25:1. The names are down to personal preference, I like the clarity of exposing the gradient angle in the variable, but you may prefer more generic names like --f-shallow or --f-steep. Equally, you may find a name like --f-gutter would be more appropriate.

Side-note: custom properties aren't evaluated until they are used, so there's no need to wrap each one in a calc().

@media screen and (min-width: 1200px) {
  :root {
    --f-screen: calc(var(--f-summit) * 1px);
  }
}

Finally, we have the aforementioned screen width lock to prevent the values from growing to silly levels.

Using fluid custom properties

The actual values stored in fluid custom properties are tiny, so they need to be multiplied up to useful numbers. The multiplier you choose represents the pixel size of the value at 320px. You can calculate the final size by multiplying it against the gradient.

Let's look at a specific example, setting the font-size on the document body.

body {
  font-size: calc(var(--f-1-25) * 16);
}

This declaration will interpolate between 16px and 20px (16 * 1.25 = 20), without a breakpoint jump. All screens will get an appropriate font-size somewhere in between those two values.

Now we've written that, we can use ems in the normal way to get relative fluid sizing off the body.

h3 {
  font-size: 1.5em;
}

This will size h3 tags to be 24px on small screens, gradually changing up to 30px on larger screens.

Working with steeper gradients

Here's an example for a hero banner. These are normally pretty painful to write, involving multiple padding breakpoint jumps as the screen expands. But when we use a fluid custom properties at a steeper gradient, we can achieve it in one line:

.hero {
  padding: calc(var(--f-5) * 40) 0;
}

This gradient of 5:1 interpolates the vertical padding between 40px and 200px as the screen gets larger.

The flexibility of different gradients give us a multitude of options to build with. If you're after tight spacing on mobile and ample on larger screens, choose a steeper gradient multiplied by a smaller number. If you want similar spacing on both, increasing ever so slightly, take a shallower gradient and multiply it by a larger number. You can even use negative gradients to make reductions on larger screens!

Fluid custom properties can be applied to margins, border-widths, padding, font-size, grid-gaps, transforms and all manner of other properties.

Common patterns can be consolidated in other CSS custom properties to reduce the number of calc() function calls. There's also no reason why they can't be applied to design tokens or utility classes. These common calculations can then be surfaced in a design system to ensure maximum usage and understanding on a project.

]]>
[email protected] (Trys Mudford)
<![CDATA[CSS-only fluid modular type scales]]> https://utopia.fyi/blog/css-modular-scales https://utopia.fyi/blog/css-modular-scales Sun, 02 Feb 2020 00:00:00 GMT A modular scale is a mathematical rule that one can use to create intentional and harmonious typography sizing. A scale is represented as a number that gets multiplied against a base size again and again, creating 'steps'.

If we take a base unit of 16px and multiply it by a scale of 1.618 (the golden section), we get 25.88px - our first 'step'. Then we can multiply that by 1.618 to get our second step: 41.88px. And again to calculate the third step as 67.77px. We can apply those sizes to our HTML elements to get beautifully sized typography.

<figure> <img src="/images/golden-section-scales.png" alt="Golden section in typographic form"> <figcaption>Golden section in typographic form</figcaption> </figure>

Step Element Size Gap
0 Body 16px
1 H4 25.88px 9.88px
2 H3 41.88px 16px
3 H2 67.77px 25.88px
4 H1 109.65px 41.88px

On a large device, a 110px H1 might be just what we're after, but on a small screen, it almost certainly wouldn't be. One solution would be to reduce the base unit, but then our body copy would become illegible. Another solution would be to reduce the scale multiplier, but then we'd lose the intentionally large heading size.

It might seem tempting to throw out this scale, or the concept of scales altogether. But fear not, there's a solution if we combine some CSS fluidity with modular scales.

Fluid type scales

My colleague at Clearleft, James Gilyead, has worked on the idea of fluidly interpolating between two modular scales, one for smaller screens, and one for larger screens. His post outlines the huge benefits of relying on these scales within a design system.

James and I have also put together a page demonstrating these fluid scales in action. Resize the page, and watch the type breathe into the space.

It uses a combination of CSS locks, CSS custom properties and modular scales:

:root {
  --fluid-min-width: 320;
  --fluid-max-width: 1500;
  --fluid-min-size: 17;
  --fluid-max-size: 20;
  --fluid-min-ratio: 1.2;
  --fluid-max-ratio: 1.33;
}

:root {
  --fluid-screen: 100vw;
  --fluid-bp: calc((var(--fluid-screen) - ((var(--fluid-min-width) / 16) * 1rem)) / ((var(--fluid-max-width) / 16) - (var(--fluid-min-width) / 16)));
}

@media screen and (min-width: 1500px) {
  :root {
    --fluid-screen: calc(var(--fluid-max-width) * 1px);
  }
}

:root {
  --fluid-min-scale-0: var(--fluid-min-ratio);
  --fluid-min-scale-1: var(--fluid-min-scale-0) * var(--fluid-min-ratio);
  --fluid-min-scale-2: var(--fluid-min-scale-1) * var(--fluid-min-ratio);

  --fluid-max-scale-0: var(--fluid-max-ratio);
  --fluid-max-scale-1: var(--fluid-max-scale-0) * var(--fluid-max-ratio);
  --fluid-max-scale-2: var(--fluid-max-scale-1) * var(--fluid-max-ratio);

  --fluid-min-size-0: (var(--fluid-min-size)) / 16;
  --fluid-min-size-1: (var(--fluid-min-size) * var(--fluid-min-scale-0)) / 16;
  --fluid-min-size-2: (var(--fluid-min-size) * var(--fluid-min-scale-1)) / 16;

  --fluid-max-size-0: (var(--fluid-max-size)) / 16;
  --fluid-max-size-1: (var(--fluid-max-size) * var(--fluid-max-scale-0)) / 16;
  --fluid-max-size-2: (var(--fluid-max-size) * var(--fluid-max-scale-1)) / 16;

  --fluid-0: calc(((var(--fluid-min-size-0) * 1rem) + (var(--fluid-max-size-0) - var(--fluid-min-size-0)) * var(--fluid-bp)));
  --fluid-1: calc(((var(--fluid-min-size-1) * 1rem) + (var(--fluid-max-size-1) - var(--fluid-min-size-1)) * var(--fluid-bp)));
  --fluid-2: calc(((var(--fluid-min-size-2) * 1rem) + (var(--fluid-max-size-2) - var(--fluid-min-size-2)) * var(--fluid-bp)));
}

That's a lot of code! Let's break the key sections down:

Configuration

:root {
  --fluid-min-width: 320;
  --fluid-max-width: 1500;
  --fluid-min-size: 17;
  --fluid-max-size: 20;
  --fluid-min-ratio: 1.2;
  --fluid-max-ratio: 1.33;
}

These are the settings for the fluid scales. We define a small and large breakpoint, in this case: 320px and 1500px.

Next up are the two base units; 17px for the smallest breakpoint, and 20px for the largest. These are our body type sizes, which we’ll refer to as scale step 0.

Finally, we set out the two modular scales: 1.2 and 1.33.

CSS Locks

:root {
  --fluid-screen: 100vw;
  --fluid-bp: calc((var(--fluid-screen) - ((var(--fluid-min-width) / 16) * 1rem)) / ((var(--fluid-max-width) / 16) - (var(--fluid-min-width) / 16)));
}

@media screen and (min-width: 1500px) {
  :root {
    --fluid-screen: calc(var(--fluid-max-width) * 1px);
  }
}

This section of the code handles the responsive, screen-width dependant stuff. On the back of a CSS lock, there's a calculation used to scale the font-size based on viewport width. It's capped off with a media query to prevent the scaling going on forever.

Traditionally, every CSS lock needs that media query, which can lead to an awful lot of duplicate code. This section extracts that query into a CSS custom property that caps every CSS lock in one line!

Fluid scales

The remaining code is broadly boilerplate; it calculates the steps along the modular scale, and can be duplicated to add additional scale steps. At the time of writing, mathematical powers aren't possible in CSS. So there is a lot of repetitive calculation to do. We've made a generator that creates as many steps as you require.

The most important lines are the final few, where we surface our three fluid scale steps: --fluid-0, --fluid-1 and --fluid-2.

Using fluid type scales

Applying these scales are entirely opt-in, and used in the same way as any other CSS custom property:

body {
  font-size: var(--fluid-0);
}

h3 {
  font-size: var(--fluid-2);
}

These scales are intended to be referenced across the design and development phase. They work equally well both for typography as well as spacing.

When setting up symbol libraries, the design team can choose the two modular scales and base units. It can be made clear in documentation that, for example, H2's in prose should follow the second step of the scale. Achieving that in code is as follows:

.prose h2 {
  font-size: var(--fluid-2);
}

That one declaration gives us fully fluid heading styles without writing a breakpoint!

Pixel perfection and design intention

We've been told that pixel perfection is a myth, and to Kellie Kowalski's definition, I would agree. The plethora of screen sizes and resolutions out in the wild makes it impossible to account for every device eventuality. Chasing that goal is fruitless. Each device will render things ever so slightly differently, and accepting that is an important step to embracing truly responsive design.

Building upon fluid type scales is a radically different approach for those who still aim match a design to the pixel. It requires a relinquishing of control from specific values and an embrace of fluidity. But by 'letting go' of tedious and flawed breakpoint control, and passing it back to the browser, we actually gain a solidity and trust that every device that loads your page will be inherently served a tailored experience.

This approach has been coined 'Intrinsic Web Design' by Jen Simmons, and we feel this approach feeds into that embrace of the 'ebb and flow' of our fluid web.

Many designers already think in terms of modular scales. A system like this give us the opportunity to synchronise our language, and better understand the design intention in the flat files we receive.

]]>
[email protected] (Trys Mudford)
<![CDATA[Designing with fluid type scales]]> https://utopia.fyi/blog/designing-with-fluid-type-scales https://utopia.fyi/blog/designing-with-fluid-type-scales Sat, 01 Feb 2020 00:00:00 GMT Breakpoint-based type sizing has always felt a bit arbitrary to me. It seems like equal parts guesswork and compromise, where the better we want it to work, the more stuff we need to design. It strikes me as inelegant and inefficient.

Over the past few years I’ve been lucky enough to work with some very smart designers and developers who have helped me hone my thoughts and ideas into a more tangible approach to fluid responsive design – particularly with regard to typography.

Although every project has different requirements, type size variation on a phone is usually relatively conservative, given that there’s limited horizontal space available. On a large laptop or desktop display it’s often desirable to use much more dramatic type scales to make the most of the extra space. The question is how to accommodate both screen sizes while respecting everything in between.

The big idea that triggered this train of thought now seems very simple:

  1. Define a type scale for a small screen
  2. Define a type scale for a large screen
  3. Tell the browser to interpolate between the two scales, based on the current viewport width

This results in a set of type sizes which is always “in tune” with itself and feels at home on any device, without needing to manually specify sizes for x number of arbitrary breakpoints. For example we can just say H2s are always “size 4” and trust the calculation to generate a suitable size heading for everybody.

<figure> <img src="/images/fluid-type-scale-visualisation.png" alt="A graph of fluid typographic scales"> <figcaption>Visualising what this means</figcaption> </figure>

In the example above, I defined a typographic scale of 1.2x at 320px (mobile-ish) and 1.333x at 1500px (desktop-ish). At any viewport width in between, the set of type sizes is proportional, although I haven’t needed to manually specify any additional values. I dropped a line at 1024px to show the automatically-calculated font size values at that viewport size. For this example I generated the values using a Google Sheet but this can be elegantly replicated in CSS as described in this excellent blog post by Clearleft developer Trys.

Here’s how these values can translate into design:

<figure> <img src="/images/the-result.png" alt="A graph of fluid typographic scales"> <figcaption>A simple example showing typography automatically “breathing” into the available space in a tablet-ish viewport size.</figcaption> </figure>

Trys has created a live version of the above example so you can see it in action: Watch the type breathe as you resize your viewport.

Exactly how you define your two type scales will depend on your product’s fonts, visual style, content, layout, audience, etc. This approach is not a substitute for good design, rather it encourages focusing design effort into the creation of a robust system. This should help to speed up future activity and decision making, as well as bake in consistency by establishing a “palette” of related type sizes.

The user benefit to this approach is that typography will always look “right”, regardless of the device being used. This is not always the case with the standard approach, where your brand new tablet might land you on the wrong side of the nearest breakpoint.

]]>
[email protected] (James Gilyead)