Journal

A fluid CSS methodology

Fluid CSS methodologies have completely transformed my approach to web integration by improving the responsive aspect and simplifying maintenance. I find this topic deceptively simple yet incredibly powerful and still relevant today.

A fluid css methodology cover

When I work with a new team, I'm always curious to discover their layout integration methodologies, as this will partly determine the project's complexity. Especially for content websites, the number of design system components to integrate doesn't allow me to spend too much time on each one. So, when I'm not using my own tools and frameworks, I spend a day or two setting up the grid system and this crucial integration methodology to ensure efficiency in my work.

That's why I decided to compile the best practices from all the team methodologies I've had the chance to work with and create my own. Here we go.

Pixel unit everywhere

Writing about this CSS subject, definitely makes me want to reflet on my beginning in web development. I discovered CSS through Le zen des CSS, a Book written by Dave Shea, the creator of csszengarden.com. Any of the layouts described on this book were built using the px unit on each elements. The web was more static back then, and devices mostly had the same screen size. The main centered column was often 960px width. (Ho! and who remember 960 grid system?). Mobile integration wasn't a priority at that time. Because smartphones didn't exist.

CSS Zen Garden - https://csszengarden.com/208/

With the arrival of new devices with portrait screens, CSS specifications have adapted by including media queries. Big revolution for webmasters! We could then integrate designs based on min or max breakpoint sizes. Today, breakpoints are still relevant; however, it's about adapting to a multitude of device sizes. We can't set a breakpoint every 10px, as that would be unmanageable.

Then came the notion of fluid design. The content of our web pages must adapt to the screen size, rather than waiting to exceed a specific breakpoint to modify the layout. We started using units like em, rem, and vw.

With the web becoming powerful enough to accommodate apps and games of all kinds, web design has taken liberties. The needs in UI development have completely changed.

A modern approach

Let's start with an example to describe the need. On this game interface, there is no scrolling possible; the design is centered, regardless of the screen size. The goal is to ensure that all elements on the screen are always visible, whether resizing the width or the height.

INCA Cancer Fighter home screen, developed with Cher-ami

Height adjustment really depends on the project. We don't always need it. I am deliberately presenting the most comprehensive example. So, how does it work?

Fluid calculation basis

We need a function that converts an absolute value to a relative value based on the viewing context, specifically the viewport size. To achieve this, we first need the width of our viewport, the width of our design mockup, and the value to be transformed.

We calculate a ratio of viewport-width / design-width, then multiply the value to be rationalized by this ratio: ratio * value. In CSS, it could look like this.

Arbitrarily, the design width is set to 600, but this can vary depending on your needs. (I use the same size in the module example below). If we apply this calculation to the font-size of an HTML page, here is the result.

Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. Vivamus consectetuer hendrerit lacus. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Etiam rhoncus. Fusce a quam. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. Vivamus consectetuer hendrerit lacus. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Etiam rhoncus. Fusce a quam.

fontSize:
W
(300/600*16)

The behavior is as expected. The font size increases as the width of our viewport grows. Note that the font-size reaches 16px when the viewport width equals the design mockup value.

Adding a limiting ratio

The problem is that the calculation changes the font-size too quickly. So I added a vw-ratio to limit how fast the calculated value grows. This brings us to a more practical and real world use case.

The vw-ratio determines the percentage of viewport width used in the fluid integration. If it is set to 1, 100% of the value is based on the viewport width. If it is set to 0.5, 50% of the value is based on the viewport width, and 50% is based on the browser's native font-size.

And here is what it looks like in use. Note that the vw-ratio does not affect the font-size when the viewport size matches the design size. This is the expected behavior.

Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. Vivamus consectetuer hendrerit lacus. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Etiam rhoncus. Fusce a quam. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. Vivamus consectetuer hendrerit lacus. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Etiam rhoncus. Fusce a quam.

fontSize:
W
vw-ratio
(300/600*(16*1))+(1-1*16)

Adding height to the calculation

In the INCA video screen presented previously, we observe the elements centered in the viewport regardless of its width, but also its height. To achieve this, I had to improve the calculation seen previously. This time, the added part refers to the viewport height or vh.

Dividing the sum of the width contribution and the height contribution by two gives a "weight" equal to the scaling factor of these two parts of the calculation. At this point, no limiting ratio is involved. Here is the result when resizing the height.

Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. Vivamus consectetuer hendrerit lacus. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Etiam rhoncus. Fusce a quam. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. Vivamus consectetuer hendrerit lacus. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Etiam rhoncus. Fusce a quam.

fontSize:
W
H
(
(300/600*16)
+
(400/400*16)
)/2

My font-size is now mapped to a value that scales based on the viewport size, including its height!

Width, height & ratios

Finally, just like with the width calculation, I apply a ratio to both parts. This allows me to control the scaling on each axis. I introduce a vh-ratio, and with that, we will be done with this complex calculation.

Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. Vivamus consectetuer hendrerit lacus. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Etiam rhoncus. Fusce a quam. Sed magna purus, fermentum eu, tincidunt eu, varius ut, felis. Vivamus consectetuer hendrerit lacus. Donec mi odio, faucibus at, scelerisque quis, convallis in, nisi. Etiam rhoncus. Fusce a quam.

fontSize:
W
H
vw-ratio
vh-ratio
(
(300/600*(16*0.5))+(1-0.5*16)
+
(400/400*(16*0.5))+(1-0.5*16)
)/2

This sandbox performs the same calculations as the module above. You can update the CSS variable values and observe how the font-size changes when resizing the sandbox. The updates will be applied dynamically.

Real world usage

Of course, the idea isn't just to use this for font-size. If we want the entire interface to be fluid, we should apply this calculation to any CSS property as needed. And this is where it gets interesting.

Dividing the calculation by 16 gives us a pseudo-pixel value for use. "Pseudo" because it's now a relative value. Let's say --fluid represents "1px in a relative viewport ratio". This is very convenient for my front-end tasks, as I can simply integrate Figma values into my CSS to achieve a "relative pixel-perfect" design.

And since these are just CSS variables, we can even test different vw and vh ratios directly in the inspector. I took a screenshot of my last project where I modified these values.

Julien Jussey website, resized with & without vh-ratio, julienjussey.com

I often notice that I am asked not to apply size variation on viewport height resize. This is a specific use case that generally works on interfaces without scroll, a bit like a poster, where the design is closed by screen borders.

To conclude, the method described above remains quite basic. Like most front-end developers, I usually use CSS preprocessors, such as Sass, which no longer need to be introduced. In this specific case, it allows me to put this calculation in a function and reset --fluid on demand, within a restricted scope.

A future approach

A more modern approach might be worth considering. Miriam Suzanne explains this in her post Reimagining Fluid Typography.

At first glance, this calculation is based on a property related to width rather than height. This doesn't cover all the cases explored in this article, so I'll probably need some time to think about it. Perhaps this will be an opportunity to revisit the topic.

If you've read this far, thank you to Pierre and Manu, with whom I've often discussed and tested these concepts. They'll recognize themselves. 👋

Next article.

Interpol

About

I'm Willy from France. With over 12 years of experience in design-driven front-end development, I specialize in creating design systems and interactive experiences.

Previously lead front-end developer at cher-ami.tv, I now work as a freelancer, collaborating with engineering teams and studios to balance the big picture with the finer details, ensuring that projects align perfectly with client needs and personality.

Stack

  • typescript
  • vanilla JS
  • node JS
  • PHP
  • webgl
  • preact
  • nuxt
  • vite
  • esbuild
  • ogl
  • use-gesture
  • preact signals
  • gsap
  • interpol
  • debug
  • low-router
  • docker
  • github action
  • gitlab CI

I'm Available for collaborations & projects! Feel free to reach out for any inquiries.