56

I want text inside my div to remain same size in % percentage ratio to a parent div. I.E. I want my text to have font-size of 50% of parents div width. So when page size is changing, text always remains the same size in %.

Here Is what I'm talking about:

.counter-holder{
 display: block;
 position: absolute;
 width:90%;
 height:20%;
 top: 70%;
 left: 50%;
 /* bring your own prefixes */
 transform: translate(-50%, -50%);
}


.counter-element-box{
 position:relative;
 vertical-align: text-top;
 border-style: solid;
    border-width: 1px;
    width: 20%;
    height: 100%;
    float:left;
    margin: 6%;
}

.counter-element-text{
 position:absolute; 
 top:50%; 
 width: 50%;
 max-width:50%;
 display: inline-block;
 height:100%;
 margin-left:50%;
 font-size : 80%;overflow: hidden;
}

.counter-element-value{
 position:absolute; 
 top:50%; 
 width: 50%;
 max-width:50%;
 display: inline-block;
 height:100%;
 padding-left:30%;
 font-size : 80%;overflow: hidden;
}
<div class="counter-holder">
 <div class="counter-element-box">
  <div id="years" class="counter-element-value">
   0
  </div>
  <div class="counter-text counter-element-text" >
      Years
  </div> 
 </div>
 <div class="counter-element-box">
  <div id="missions" class="counter-element-value">
   0  
  </div>
  <div class="counter-text counter-element-text" >
      Missions
  </div>
 </div> 
 <div class="counter-element-box"> 
    <div id="team" class="counter-element-value">
      0 
    </div>
    <div class="counter-text counter-element-text" >
       Team
    </div>
 </div>       
</div>

Run this snippet in full screen and try resizing the page and see how text size is changing and not fitting inside the div borders. How can I prevent it from happening, so it always remains inside the borders?

Tachi
  • 2,042
  • 3
  • 27
  • 44

8 Answers8

26

The way I solved this problem was to use javascript to measure the container and then set the font size in px on that container. Once that baseline is set for the container then the relative font sizing of all the content will scale correctly using em or %.

I'm using React:

<div style={{ fontSize: width / 12 }} >
  ...
</div>

CSS:

div {
  font-size: 2em;
}
JamieR
  • 397
  • 3
  • 5
11

There's no way to achieve what this question is asking for in CSS. A font size can only be set in a certain number of ways, and any setting that is a percentage value is relative to the size of the font, not a width or height of a block.

So, while this doesn't offer a solution to your problem, it is an answer to the question of whether this can be done in CSS: it can't.

There are a lot of answers here which appear to answer the wrong question, and recommend the use of the vw unit, which is relative to the viewport and not the parent element's size. There are also some which resort to Javascript, which is doable if you don't mind that sort of thing. Then there is slaviboy's answer which advocates the use of CSS user variables which is a creative solution that I admire, even though it can't achieve the end effect asked for by the OP.

In many cases, when you want to set font size relative to the parent element's width, you have enough information to be able to do this with some math: work out how the parent element's width is calculated, and see if you can re-use a calculation like that in your font size setting.

For example, if parent element's width is the viewport width or a known percentage of it, then the answers based on vw will work, you just have to do the math to work out the right ratio. If parent element's width is some absolute value such as 500px, then that's even easier. Only if the parent element's width is completely unpredictable will this be difficult, though presumably the OP's situation is exactly that. For example, if the parent element's width is based on content, and content may be user-submitted or its size is otherwise not predictable.

thomasrutter
  • 114,488
  • 30
  • 148
  • 167
7

If you talk about responsive scaling, then you can implement it, but all depends on what you are trying to achieve. In the code below i have created a div container with ratio (height/width = 1/2). When you change the size of the browser window, container and text will scale responsively.Depending on the window size it will change the font according to VW(viewport width) and VH(viewport height). To add new font size you have to set it twice once according to the -vw and second time according to -vh. I have used CSS variables(var), that way its easier to understand the code.

Example

body {
    background-color: #000;
    padding: 0;
    margin:0;
}

/* position element in the middle */
.middle {
    position: absolute;
    top:50%;
    left: 50%;
    transform: translate(-50%, -50%);
} 


/* Ratio: height/width */ 
:root {
    --ratio: 0.5;
    --reverse-ratio: 2;
    --container-width: 50vw;
    --container-height: 50vh;

    --font1-sizeVW: 15vh;
    --font1-sizeVH: calc(var(--ratio) * 15vw);

    --font2-sizeVW: 6vh;
    --font2-sizeVH: calc(var(--ratio) * 6vw);
}

#container {

    background-color: pink;
    width: var(--container-width);
    height: calc(var(--ratio) * var(--container-width));

    max-width: calc(var(--reverse-ratio) * var(--container-height));  
    max-height: var(--container-height);
}

#span1 {
    font-size: var(--font1-sizeVW);   
}

#span2 {
    font-size: var(--font2-sizeVW);  
}

/* max-width: (reversRatio * 100vh) */
@media all and (max-width: 200vh) {  

    #container {
        background-color: green;
    }

    #span1 {  
        font-size: var(--font1-sizeVH); 
    } 

    #span2 {  
        font-size: var(--font2-sizeVH);
    } 
}
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <style>
    </style>
</head>
<body>
    <div id="container" class="middle">
        <span id="span1">Hello</span>
        <span id="span2">Hello again</span>
    </div>
</body>
</html>
slaviboy
  • 1,407
  • 17
  • 27
  • 9
    That is relative to the *viewport*, *not* the parent element. – John Sep 13 '19 at 06:42
  • Make sure you make your parent size relative to the viewport too. That way you can set ratio between the child and parent elements, and when page size changes both elements keep there ratio and scale responsively. – slaviboy Sep 13 '19 at 16:24
  • @slaviboy I feel that your above example might be the solution to my problem, but unfortunately I am missing something. I have an svg that scales on window resize, and some divs that don't. I want them to scale all together and keep their relative positions. I tried setting the `top` property like this `top: calc(var(--container-height) * 5%);` with no success. Is there a way to acheive this? Also can you explain, what is the purpose of the `transform` property on the `middle` class? Here is a blitz of what I am trying to do.. – gothaf Jan 13 '20 at 17:53
  • The way I see it is by calculation indeed. In my case, I use React, and it's easier, but the CSS variables provide the native way to do it. Nice answer! – Vladyn Sep 13 '22 at 08:01
4

You can use container query units: cqw, cqh, cqi, cqb, cqmin, cqmax. They are supported by all modern browsers.

This will allow to adjust font-size and other properties not only to the direct parent, but to any ancestor defined as a container, as well as target not only horizontal but also vertical size of the container if necessary.

.container {
  /* Define a container */
  /* Use `inline-size` instead of `size` if you are only
     interested in inline size, which is horizontal
     for Latin and many other layouts. */
  container-type: size;
  
  /* Other styles */
  height: 3em;
  border: solid 2px;
  overflow: hidden;
}

.horizontal {
  resize: horizontal;
}

.vertical {
  resize: vertical;
}

.both {
  resize: both;
}

.horizontal .child {
  font-size: 10cqw;
}

.vertical .child {
  font-size: 30cqh;
}

.both .child {
  font-size: 30cqmin;
}
<div class="container horizontal">
  <div class="child">Resize me horizontally!</div>
</div>

<div class="container vertical">
  <div class="child">Resize<br>me<br>vertically!</div>
</div>

<div class="container both">
  <div class="child">Resize me<br>in all<br>ways!</div>
</div>
Tigran
  • 2,800
  • 1
  • 19
  • 19
  • 2
    This is a God-send and should be the accepted answer! Every major browser supports it, minus IE, and it makes my components look so good without the need of an additional JavaScript framework like big-text.js – NessDan Jul 26 '23 at 21:33
  • This is almost what I'm looking for, but I can't get it to actually decrease the font size depending on the amount of text and the size of the parent container. For instance, if you add a lot of characters but no spaces to the `container horizontal` div, it will overflow and fail to resize the text to fit in the box. – Rontron Aug 19 '23 at 15:42
1

I don't know if that's helpful or not. It's maybe just crap. The idea is to manually take the width of the element and to calculate the font-size from it. I used some JavaScript because I don't know if maths are possible with CSS variables. Code in comment works but is not dynamic.

const resize_ob = new ResizeObserver(function(entries) {

let rect = entries[0].contentRect;

let currWidth = rect.width;    


let fontSize = rect.width / 10;

/*    

let testDiv = document.querySelector("#test_div");
let testDivStyle = getComputedStyle(testDiv);
let testDivRawWidth = testDivStyle.getPropertyValue('width')
let fontSize = parseInt(testDivRawWidth .substring(0, testDivRawWidth .length - 2)) / 10;
*/

testDiv.style.setProperty('--fontSize', fontSize+"px");
})
div {
background-color: lightblue;

height: 75vh;
width: 75vh;

font-size: var(--fontSize, 15vh);

}
<div id="test_div">

testing just a thing...

</div>
-3

I found this to work pretty well, and it has the advantage of simplicity:

font-size: min(3vh,1.7vw);

just tweak the proportions according to your font.

This works because it takes the shorter side of your viewport as the limiting factor in sizing your font. You can also set this on a parent div and then reference it with em units in its children.

like so

.parent{
  font-size: min(3vh,1.7vw);
}
.child {
  font-size: 2.2em;
}
<div class="parent">
  <div class="child">
    <p>Hello there</p>
  </div>
</div>
Valentino
  • 465
  • 6
  • 17
  • 1
    Instead of taking a min(), you can use the `vmin` (viewport min) unit, which is another unit like `vw` and `vh` but is based on the shorter of the width and height of the viewport. – thomasrutter Jul 29 '21 at 13:08
  • 1
    My guess is because the question is asking for something different - OP wants to have a text string, and size the text relative to the parent element's width. While you can use vw to size the text according to the viewpoint width, you can't do it according to the parent element width. So I don't think what OP wants is possible without modifying their approach, as most answers are suggesting. But I think they're downvoted because they don't provide exactly what OP wanted. – thomasrutter Sep 01 '21 at 07:28
-7

Responsive Font Size/ Text responsive according to the browser window :-

The text size can be set with a vw unit, which means the "viewport width".

That way the text size will follow the size of the browser window:

For Example

<div>
<h2 style="font-size:10vw;">Ganesh Pandey</h2>
</div>

Resize the browser window to see how the text size scales.

-18

Using font-size: x% is not relative to the width of the parent but the font-size the element would normally have.

So if your element's parent sets font-size: 12px then font-size: 50% in element means that element has a font-size of 6px

What you want to use is viewport percentage: vw

for example: font-size: 2vw

.counter-holder{
 display: block;
 position: absolute;
 width:90%;
 height:20%;
 top: 70%;
 left: 50%;
 /* bring your own prefixes */
 transform: translate(-50%, -50%);
}


.counter-element-box{
 position:relative;
 vertical-align: text-top;
 border-style: solid;
    border-width: 1px;
    width: 20%;
    height: 100%;
    float:left;
    margin: 6%;
}

.counter-element-text{
 position:absolute; 
 top:50%; 
 display: inline-block;
 margin-left:40%;
 font-size : 2vw;overflow: hidden;
}

.counter-element-value{
 position:absolute; 
 top:50%; 
 width: 50%;
 max-width:50%;
 display: inline-block;
 height:100%;
 padding-left:30%;
 font-size : 2vw;overflow: hidden;
}
<div class="counter-holder">
   
   <div class="counter-element-box">
    <div id="years" class="counter-element-value">
     0
       </div>
       <div class="counter-text counter-element-text" >
        Years
       </div> 
      </div>
      <div class="counter-element-box">
       <div id="missions" class="counter-element-value">
     0  
       </div>
       <div class="counter-text counter-element-text" >
        Missions
       </div>
      </div> 
      <div class="counter-element-box"> 
       <div id="team" class="counter-element-value">
     0 
       </div>
       <div class="counter-text counter-element-text" >
        Team
       </div>
      </div>       
</div>
Mathieu David
  • 4,706
  • 3
  • 18
  • 28
  • Great explanation, never heard of ```vw``` before. – Tachi Jun 07 '15 at 13:36
  • 119
    Isn't `vw` and `vh` the size of the viewport window, not it's parent? – bryan Nov 09 '16 at 03:33
  • 19
    Indeed, this answer should not have been marked as valid! I'm guessing you would need some javascript to size text relative to a parent, but viewport units are definitely not the way to achieve this. – MrMerrick Apr 03 '17 at 18:17
  • Absolutely agree - I can't imagine a real example when it's necessary to have text size relative to the size of a browser viewport. It's much more common to have a situation when text size should be relative to a size of a parent block. And one of the solutions is to use JavaScript similar to https://github.com/davatron5000/FitText.js – Serg Sep 20 '17 at 13:52
  • 1
    I have a perfectly good use case for this - I have an element that can be configurable by an admin for display on an ordinary user's page. For the user, this element is the width of the screen, scaling using vw and vh works great. I would like the admin to have a smaller preview of this which may be some variable proportion of the width of the page, for example 6 bootstrap columns, switching responsively to full width at the medium breakpoint. Scaling to a percentage of the enclosing box's width would be ideal here. – stephan.com Jan 17 '18 at 20:22