Dai Codes

A software engineerʼs blog

A photograph of a pair a smartphone with HTML code on screen

Responsive CSS without media queries

Making a simple web page responsive without resorting to the complexity of media queries

I recently wrote a really trivial web page for sending to family and friends as a way of sharing a referral link for a UK energy supplier.

I wrote the content and some basic styling for a desktop browser first. The last step was to make it responsive for a reasonable appearance on mobile devices.

Just as I was about to start reaching for the common set of breakpoints I use for responsive design, I realised that actually media queries were way overkill for what I needed, and I could achieve the desired level of responsiveness without them.

Here's what I did.

Set the viewport width

Ok, so not actually CSS, but mentioning this for completeness: As always when implementing mobile-friendly layouts, the first thing to do is to add the viewport meta tag to the <head> section of your HTML page to set the viewport width to the device width:

content="width=device-width, initial-scale=1">

Stop images overflowing

Mobile devices come in a wide variety of dimensions. We want to make sure that images do not overflow the width of the viewport, and also that they make the most of the space available to them:

img {
max-width: 100%;
height: auto;

The max-width property ensures that images are never wider than their container, and the height: auto ensures that the image's aspect ratio (as defined by the width and height attributes of the img element) is maintained.

Ideally this should be used in combination with supplying various pre-scaled image sources using the picture and source elements.

Responsive font sizes

We can make font sizes responsive by using the vw unit, where 1vw equals 1% of the viewport width. This means as the viewport width changes, the font size will change proportionally.

We can also use the clamp function to set a minimum and maximum font size, so that the font size will never be too small or too large.

For example, the following will set the font size of h1 elements to 8% of the viewport width, but never less than 24px and never more than 48px.

h1 {
font-size: clamp(24px, 8vw, 48px);

For viewports 500 pixels wide, this means 500 / 100 * 8 = 40px, which is within the 24px-48px min-max range specified.

However, for viewports 1000 pixels wide, the calculated font size would be 1000 / 100 * 8 = 80px, which is greater than the 48px maximum, so the actual font size used will be 48px.

Avoid single dangling words in wrapped text

As the width of the viewport changes, text will wrap automatically onto multiple lines. But sometimes this will leave just one single word on the last line.

This can be particularly problematic with centrally-aligned text, as it can make it harder for the reader to follow the text flow and slow down their reading speed.

One plan to address this could have been testing at various widths and coming up with fixed-width containers at certain breakpoints to ensure dangling single words are avoided.

But this is tedious and requires media (or container) queries, which we're trying to avoid for simplicity.

Instead we can use the relatively new balance and pretty values for the text-wrap property.

Using text-wrap: balance ensures that the line widths within the same block of text are balanced as much as possible.

In my 'simple page' scenario, each block of text is small and all centrally aligned, so the balance value gives the best aesthetic across all viewport widths:

h1, p {
text-align: center;
text-wrap: balance;

In general though, we should use balance sparingly, as it's computationally expensive to calculate and browsers will ignore it for blocks of text that span too many lines (the max line count threshold varies by browser, but is around 3-4 lines). If you have a large amount of text or your paragraphs are long, prefer pretty:

p {
text-wrap: pretty;

Using text-wrap: pretty ensures that there are no orphaned words (a solitary word on a line) by moving the last word from the previous line onto the last line instead if necessary.

The interactive demo in the resizable iframe below illustrates how these work:


If you've found my blog useful, please consider buying me a coffee to say thanks:


Comments or questions? Find me on Twitter: @daiscog.