CSS Typographic Initiatives

Ruleset interpolation Explainer

Authors

Participate

Please leave feedback by filing issues on this page’s github repository.

Comment on the CSSWG issue and discussion

Proto implementation: Typetura

Table of contents

Introduction

There are times when it is ideal to have a gradation of change between viewport and component sizes as opposed to specific points defined in media queries and container queries. As more device types like watches and foldables enter the web ecosystem as well as more complex layouts being used the need for this level of control increases. This control is especially useful for typography and spacing within layout.

Currently some degree of this can be achieved with clamp(), but this doesn’t follow the container sizing mental model used in media/element queries, is limited to a single value, is limited to length typed units, and lacks easing control. A workaround to gain back all this functionality is possible by passing container width into the delay of an animation function. There are opportunities to make this cleaner and more clear.

Functionality goals

Example

Resize me

Proposed solutions

Below are three different proposals to add this functionality to CSS. I prefer the container-animation direction as I find it to be the most intuitive, but there are advantages to the other two directions as well. There may be aspects of the container-animation proposal that can be adapted to the animation-timeline proposal like using lengths as keyframes. At this stage I think it’s important to cast a wide net, assess advantages and disadvantages of differing approaches that we can consolidate and refine.

Container-animation (ideal solution)

This proposal creates a specific property focused on container interpolation as opposed to relying on animation. This sheds the baggage of time-based logic enabling container-keyframes to contain length based units. This proposal was originally discussed in this gist.

The biggest pro for this direction is the ability to use length values as the keyframes themselves, so it’s clear what context you are in and what is changing within that context. I think the added clarity of having length units in the keyframes is worth pursuing. CSS authors spend far too much time in online calculators to generate values that CSS itself could be making more plain. This is a huge problem with clamp() right now.

The biggest con for this direction is that it’s largely new CSS and a bit of a departure from scroll timeline and mix().

article {
  container-type: inline-size;
}

.headline {
  container-timeline-name: headline;
  container-timeline-axis: size(inline);
  container-timeline-timing-function: ease-in-out;
}

@container-keyframes headline {
  10rem {
    font-size: 1.2rem;
    font-weight: 900;
    color: hsl(330, 96%, 15%);
  }
  40rem {
    font-size: 3rem;
    font-weight: 600;
    color: hsl(330, 96%, 45%);
  }
}

Advantages

Disadvantages

Open questions

Mix timeline

Fantasai drafted this proposal with Miraim Suzanne that achieves most of the functionality goals outlined above. It expands upon the mix proposal for interpolating values.

The biggest pro for this direction is that styles can be easily overwritten in the cascade. This is an annoyance with styles in keyframes.

The biggest con for this direction is that it doesn’t meet the last two functionality goals. These are ideal and not must-haves, but still a concession on functionality.

article {
  container-type: inline-size;
}

@timeline headline {
  type: container;
  feature: width;
  from: 10rem;
  to: 40rem;
}

.headline {
  font-size: mix(headline ease-in-out; 1.2rem; 3rem);
  font-weight: mix(headline ease-in-out; 900; 600);
  color: mix(headline ease-in-out; hsl(330, 96%, 15%); hsl(330, 96%, 45%));
}

Advantages

Disadvantages

Animation timeline

This direction is more aligned with scroll-timeline. It satisfies all of the functionality goals.

The biggest pro for this direction is that it is so close to the existing scroll timeline spec. This direction feels clear and achievable.

The biggest con is that it’s more verbose than the other directions without adding as much clarity.

article {
  view-timeline-name: article;
  view-timeline-axis: size(inline);
}

.headline {
  animation: headline both ease-in-out;
  animation-timeline: article;
  animation-delay-start: inline-size 10rem;
  animation-delay-end: inline-size 40rem;
}

/* Standard animation keyframes */
@keyframes headline {
  from {
    font-size: 1.2rem;
    font-weight: 900;
    color: hsl(330, 96%, 15%);
  }
  to {
    font-size: 3rem;
    font-weight: 600;
    color: hsl(330, 96%, 45%);
  }
}

Advantages

Disadvantages

Open questions

Some details could be worked out around how animation-timeline and view-timeline work.

Workaround

A workaround is possible now according to the latest CSS spec and is waiting for browsers to implement unit division (the / 1px portions of animation delay) to function properly:

article {
  container-type: inline-size;
}
.headline {
    animation-name: headline;
    animation-timing-function: ease-in-out;
    --min: 10rem;
    --max: 40rem;

    animation-delay: calc(-1s * ((100cqi - var(--min, 0)) / 1px) / ((var(--max, 0) - var(--min, 0)) / 1px));
    animation-duration: 1s;
    animation-iteration-count: 1;
    animation-fill-mode: both;
    animation-play-state: paused;
}
@keyframes headline {
  from {
    font-size: 1.2rem;
    font-weight: 900;
    color: hsl(330, 96%, 15%);
  }
  to {
    font-size: 3rem;
    font-weight: 600;
    color: hsl(330, 96%, 45%);
  }
}

This is the same code Typetura uses, except Typetura uses JavaScript to attach resizeObservers to container elements, passing a var(--width) value into the animation delay function for better browser support.

Key scenarios

Reduce excessive breakpoints

Currently the following websites use numerous breakpoints to define how typesetting changes at different screen sizes: The Outline, HEX and Elliot Jay Stocks.

Consolidate styles

The Atlantic has a typographically rich home page on desktop with headlines of various sizes based on their container size. All of these headline styles could be consolidated into one interpolated ruleset for this layout with the additional bonus of working across viewport sizes. Here is a demo of The Atlantic with an interpolated headline style.

Changing typesetting around layout shifts

The Stripe home page changes its type styles for a layout shift. If these styles were interpolated and bound to the text container itself, then as the layout shift happens the text would automatically resize appropriately. Here is an example of what this type of layout might look like with interpolated styles.

Smooth layout shifts on resize

Lynn Fisher used Typetura to interpolate rulesets with CSS for her latest 2021 website redesign. This would all be possible natively in CSS with ruleset interpolation. Additionally more subtle applications of this can be applied, like the way the floating images slide in and out of the text column where there is space on this “Explore” demo.

Root sizing

As devices get increasingly small, controlling page scaling with the root font size is more useful. Because most websites don’t work well on watches, Apple adapts them for smaller screens unless <meta name="disable-adaptations" content="watch"> is in the <head>. The ideal solution here is interpolating the root font size with an ease-out timing function. You can see how the “Explore” demo scales to extremely small sizes.

Accessibility

Any time you change font size you run the risk of falling out of compliance with WCAG SC 1.4.4 and this still holds true with this feature. The success criteria may change in the future based on this open issue with the WCAG on 1.4.4. There is currently no way to identify and compensate for page scaling accessibility issues on desktop browsers (cmd +) and there is an open issue on how we might surface and solve for page scaling issues here.

Stakeholder Feedback / Opposition

References & acknowledgements

Additional thanks to support, feedback, and inspiration from

Changelog

2022.11.3

2022.10.31