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 t
Artistically, if you need to fit two or more lines of text within the same width regardless of their character count then you have nice options.
It's best to find a dynamical solution so whatever text is entered we end up with a nice display.
Let's see how we may approach.
var els = document.querySelectorAll(".divtext"),
refWidth = els[0].clientWidth,
refFontSize = parseFloat(window.getComputedStyle(els[0],null)
.getPropertyValue("font-size"));
els.forEach((el,i) => el.style.fontSize = refFontSize * refWidth / els[i].clientWidth + "px")
#container {
display: inline-block;
background-color: black;
padding: 0.6vw 1.2vw;
}
.divtext {
display: table;
color: white;
font-family: impact;
font-size: 4.5vw;
}
<div id="container">
<div class="divtext">THIS IS JUST AN</div>
<div class="divtext">EXAMPLE</div>
<div class="divtext">TO SHOW YOU WHAT</div>
<div class="divtext">YOU WANT</div>
</div>
All we do is to get the width (els[0].clientWidth
) and the font size (parseFloat(window.getComputedStyle(els[0],null).getPropertyValue("font-size"))
) of the first line as a reference and then just calculate the subsequent lines font size accordingly.
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 of vw
or vh
)vmax
(the larger or vw
or vh
)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
This may not be super practical, but if you want a font to be a direct function of the parent, without having any JavaScript that listens/loops (interval) to read the size of the div/page, there is a way to do it. Iframes.
Anything within the iframe will consider the size of the iframe as the size of the viewport. So the trick is to just make an iframe whose width is the maximum width you want your text to be, and whose height is equal to the maximum height * the particular text's aspect ratio.
Setting aside the limitation that viewport units can't also come along side parent units for text (as in, having the % size behave like everyone else), viewport units do provide a very powerful tool: being able to get the minimum/maximum dimension. You can't do that anywhere else - you can't say...make the height of this div be the width of the parent * something.
That being said, the trick is to use vmin, and to set the iframe size so that [fraction] * total height is a good font size when the height is the limiting dimension, and [fraction] * total width when the width is the limiting dimension. This is why the height has to be a product of the width and the aspect ratio.
For my particular example, you have
.main iframe{
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: calc(3.5 * 100%);
background: rgba(0, 0, 0, 0);
border-style: none;
transform: translate3d(-50%, -50%, 0);
}
The small annoyance with this method is that you have to manually set the CSS of the iframe. If you attach the whole CSS file, that would take up a lot of bandwidth for many text areas. So, what I do is attach the rule that I want directly from my CSS.
var rule = document.styleSheets[1].rules[4];
var iDoc = document.querySelector('iframe').contentDocument;
iDoc.styleSheets[0].insertRule(rule.cssText);
You can write small function that gets the CSS rule / all CSS rules that would affect the text area.
I cannot think of another way to do it without having some cycling/listening JavaScript. The real solution would be for browsers to provide a way to scale text as a function of the parent container and to also provide the same vmin/vmax type functionality.
JSFiddle: https://jsfiddle.net/0jr7rrgm/3/ (click once to lock the red square to the mouse, and click again to release)
Most of the JavaScript in the fiddle is just my custom click-drag function.
.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>
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>
This worked for me:
I try to approximate font-size based on a width/height got from setting `font-size: 10px`. Basically, the idea is "if I have 20 pixels width and 11 pixels height with `font-size: 10px`, so what would it be the maximum font-size to math a container of 50 pixels width and 30 pixels height?"
The answer is a double proportion system:
{ 20:10=50:X, 11:10=30:Y } = { X= (10*50)/20, Y= (10*30)/11 }
Now X is a font-size that will match width, and Y is a font-size that will match height; take the smallest value
function getMaxFontSizeApprox(el){
var fontSize = 10;
var p = el.parentNode;
var parent_h = p.offsetHeight ? p.offsetHeight : p.style.pixelHeight;
if(!parent_h)
parent_h = 0;
var parent_w = p.offsetHeight ? p.offsetWidth : p.style.pixelWidth;
if(!parent_w)
parent_w = 0;
el.style.fontSize = fontSize + "px";
var el_h = el.offsetHeight ? el.offsetHeight : el.style.pixelHeight;
if(!el_h)
el_h = 0;
var el_w = el.offsetHeight ? el.offsetWidth : el.style.pixelWidth;
if(!el_w)
el_w = 0;
// 0.5 is the error on the measure that JavaScript does
// if the real measure had been 12.49 px => JavaScript would have said 12px
// so we think about the worst case when could have, we add 0.5 to
// compensate the round error
var fs1 = (fontSize*(parent_w + 0.5))/(el_w + 0.5);
var fs2 = (fontSize*(parent_h) + 0.5)/(el_h + 0.5);
fontSize = Math.floor(Math.min(fs1,fs2));
el.style.fontSize = fontSize + "px";
return fontSize;
}
NB: the argument of the function must be a span element or an element which is smaller than its parent, otherwise if children and parent have both the same width/height function will fail.
In order to make font-size fit its container, rather than the window, see the resizeFont()
function I have shared in this question (a combination of other answers, most of which are already linked here). It is triggered using window.addEventListener('resize', resizeFont);
.
Vanilla JavaScript: Resize font-awesome to fit container
function resizeFont() {
var elements = document.getElementsByClassName('resize');
console.log(elements);
if (elements.length < 0) {
return;
}
_len = elements.length;
for (_i = 0; _i < _len; _i++) {
var el = elements[_i];
el.style.fontSize = "100%";
for (var size = 100; el.scrollHeight > el.clientHeight; size -= 10) {
el.style.fontSize = size + '%';
}
}
}
You could perhaps use vw/vh as a fallback, so you dynamically assign em
or rem
units using JavaScript, ensuring that the fonts do scale to the window if JavaScript is disabled.
Apply the .resize
class to all elements containing text you wish to be scaled.
Trigger the function prior to adding the window resize event listener. Then, any text which doesn't fit its container will be scaled down when the page loads, as well as when it is resized.
NOTE: The default font-size
must be set to either em
,rem
or %
to achieve proper results.