Font scaling based on width of container
I'm having a hard time getting my head around font scaling.
I currently have a website with a body font-size
of 100%. 100% of what though? This seems to compute out at 16 pixels.
I was under the impression that 100% would somehow refer to the size of the browser window, but apparently not because it's always 16 pixels whether the window is resized down to a mobile width or full-blown widescreen desktop.
How can I make the text on my site scale in relation to its container? I tried using em
, but this doesn't scale either.
My reasoning is that things like my menu become squished when you resize, so I need to reduce the px
font-size
of .menuItem
among other elements in relation to the width of the container. (For example, in the menu on a large desktop, 22px
works perfectly. Move down to tablet width and 16px
is more appropriate.)
I'm aware I can add breakpoints, but I really want the text to scale as well as having extra breakpoints, otherwise, I'll end up with hundreds of breakpoints for every 100pixels decrease in width to control the text.
EDIT: If the container is not the body CSS Tricks covers all of your options in Fitting Text to a Container.
If the container is the body, what you are looking for is Viewport-percentage lengths:
The viewport-percentage lengths are relative to the size of the initial containing block. When the height or width of the initial containing block is changed, they are scaled accordingly. However, when the value of overflow on the root element is auto, any scroll bars are assumed not to exist.
The values are:
-
vw
(% of the viewport width) -
vh
(% of the viewport height) -
vi
(1% of the viewport size in the direction of the root element's inline axis) -
vb
(1% of the viewport size in the direction of the root element's block axis) -
vmin
(the smaller ofvw
orvh
) -
vmax
(the larger orvw
orvh
)
1 v* is equal to 1% of the initial containing block.
Using it looks like this:
p {
font-size: 4vw;
}
As you can see, when the viewport width increases, so do the font-size
, without needing to use media queries.
These values are a sizing unit, just like px
or em
, so they can be used to size other elements as well, such as width, margin, or padding.
Browser support is pretty good, but you'll likely need a fallback, such as:
p {
font-size: 16px;
font-size: 4vw;
}
Check out the support statistics: http://caniuse.com/#feat=viewport-units.
Also, check out CSS-Tricks for a broader look: Viewport Sized Typography
Here's a nice article about setting minimum/maximum sizes and exercising a bit more control over the sizes: Precise control over responsive typography
And here's an article about setting your size using calc() so that the text fills the viewport: http://codepen.io/CrocoDillon/pen/fBJxu
Also, please view this article, which uses a technique dubbed 'molten leading' to adjust the line-height as well. Molten Leading in CSS
But what if the container is not the viewport (body)?
This question is asked in a comment by Alex under the accepted answer.
That fact does not mean vw
cannot be used to some extent to size for that container. Now to see any variation at all one has to be assuming that the container in some way is flexible in size. Whether through a direct percentage width
or through being 100% minus margins. The point becomes "moot" if the container is always set to, let's say, 200px
wide--then just set a font-size
that works for that width.
Example 1
With a flexible width container, however, it must be realized that in some way the container is still being sized off the viewport. As such, it is a matter of adjusting a vw
setting based off that percentage size difference to the viewport, which means taking into account the sizing of parent wrappers. Take this example:
div {
width: 50%;
border: 1px solid black;
margin: 20px;
font-size: 16px;
/* 100 = viewport width, as 1vw = 1/100th of that
So if the container is 50% of viewport (as here)
then factor that into how you want it to size.
Let's say you like 5vw if it were the whole width,
then for this container, size it at 2.5vw (5 * .5 [i.e. 50%])
*/
font-size: 2.5vw;
}
Assuming here the div
is a child of the body
, it is 50%
of that 100%
width, which is the viewport size in this basic case. Basically, you want to set a vw
that is going to look good to you. As you can see in my comment in the above CSS content, you can "think" through that mathematically with respect to the full viewport size, but you don't need to do that. The text is going to "flex" with the container because the container is flexing with the viewport resizing. UPDATE: here's an example of two differently sized containers.
Example 2
You can help ensure viewport sizing by forcing the calculation based off that. Consider this example:
html {width: 100%;} /* Force 'html' to be viewport width */
body {width: 150%; } /* Overflow the body */
div {
width: 50%;
border: 1px solid black;
margin: 20px;
font-size: 16px;
/* 100 = viewport width, as 1vw = 1/100th of that
Here, the body is 150% of viewport, but the container is 50%
of viewport, so both parents factor into how you want it to size.
Let's say you like 5vw if it were the whole width,
then for this container, size it at 3.75vw
(5 * 1.5 [i.e. 150%]) * .5 [i.e. 50%]
*/
font-size: 3.75vw;
}
The sizing is still based off viewport, but is in essence set up based off the container size itself.
Should Size of the Container Change Dynamically...
If the sizing of the container element ended up changing dynamically its percentage relationship either via @media
breakpoints or via JavaScript, then whatever the base "target" was would need recalculation to maintain the same "relationship" for text sizing.
Take example #1 above. If the div
was switched to 25%
width by either @media
or JavaScript, then at the same time, the font-size
would need to adjust in either the media query or by JavaScript to the new calculation of 5vw * .25 = 1.25
. This would put the text size at the same size it would have been had the "width" of the original 50%
container been reduced by half from viewport sizing, but has now been reduced due to a change in its own percentage calculation.
A Challenge
With the CSS3 calc()
function in use, it would become difficult to adjust dynamically, as that function does not work for font-size
purposes at this time. So you could not do a pure CSS 3 adjustment if your width is changing on calc()
. Of course, a minor adjustment of width for margins may not be enough to warrant any change in font-size
, so it may not matter.
Solution with SVG:
.resizeme {
resize: both;
margin: 0;
padding: 0;
height: 75px;
width: 500px;
background-color: lightblue;
overflow: hidden;
}
<div class="resizeme">
<svg
width="100%"
height="100%"
viewBox="0 0 500 75"
preserveAspectRatio="xMinYMid meet"
style="background-color:green"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<text
x="0"
y="75"
font-size="75"
fill="black"
>█Resize This█</text>
</svg>
</div>
Solution with SVG and text-wrapping using foreignObject
:
.resizeme {
resize: both;
margin: 0;
padding: 0;
height: 200px;
width: 500px;
background-color: lightblue;
overflow: hidden;
}
<div class="resizeme">
<svg
width="100%"
height="100%"
viewBox="0 0 500 200"
preserveAspectRatio="xMinYMin meet"
>
<foreignObject width="100%" height="100%" xmlns="http://www.w3.org/1999/xhtml">
<div xmlns="http://www.w3.org/1999/xhtml" style="background-color:lightgreen;">
<h1>heading</h1>
<p>Resize the blue box.</p>
</div>
</foreignObject>
</svg>
</div>
In one of my projects I use a "mixture" between vw and vh to adjust the font size to my needs, for example:
font-size: calc(3vw + 3vh);
I know this doesn't answer the OP's question, but maybe it can be a solution to anyone else.