CSS3 viewport units give you several values tied to the browser window: vw, vh, vmin, and vmax. The first two are straightforward: 1vw is 1% of the viewport width, and 1vh is 1% of the viewport height. vmin takes the smaller of 1vw and 1vh; vmax takes the larger.

Compatibility is decent: Chrome 20+, Safari 6+, IE 10+, Firefox 19+, and iOS 6+.

A simple responsive-font setup looks like this:

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>Demo</title>
    <style type="text/css">
        #box {
            font-size: 4vw;
        }
    </style>
</head>
<body>
    <div id="box">
        我是Barret Lee
        我是Barret Lee
        我是Barret Lee
    </div>
</body>
</html>

In theory, font-size: 4vw should make the text scale with the window. In practice, when the browser window is resized, the text inside #box may not change at the expected ratio. The CSS3 spec says that viewport units scale when viewport height or width changes, so this behavior does not match the spec. Treat it as a bug.

A workaround is to trigger a repaint on resize:

window.onresize = function(){
    var box = document.getElementById("box");
    box.style["z-index"] = 1;
}

Setting z-index causes the corresponding element to be repainted.

A short repaint/reflow refresher: - Adding or deleting elements: reflow + repaint. - Hiding an element with display: none: reflow + repaint. With visibility: hidden: repaint only, no reflow. - Moving an element, for example changing top or left (jQuery's animate method does this, and not every top/left change necessarily triggers reflow), or moving an element into another parent: repaint + reflow. - Style operations: different properties have different effects. - User actions such as resizing the browser window or changing the browser's font size: reflow + repaint.

This code shows how individual style changes affect reflow and repaint:

var s = document.body.style;
s.padding = "2px"; // reflow + repaint
s.border = "1px solid red"; // reflow + repaint again
s.color = "blue"; // repaint again
s.backgroundColor = "#ccc"; // repaint again
s.fontSize = "14px"; // reflow + repaint again
// add a node: reflow + repaint again

There is a related side case that is worth thinking about. In the following markup, .box is 900px wide and .wrap is 100%:

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>CSS3 Demo</title>
    <style type="text/css">
        body, div {
            margin:0;
            padding: 0;
        }
        .wrap {
            background: blue;
            width: 100%;
        }
        .box {
            width: 900px;
            height: 200px;
        }
    </style>
</head>
<body>
    <div class="wrap"><div class="box"></div></div>
</body>
</html>

Shrink the browser window until it is narrower than 900px and a horizontal scrollbar appears. Scroll to the right and the blue area is no longer 100% wide.

Other approaches

1. CSS expression

This is inefficient and not recommended.

#box {
    star:expression(onresize = function(){
        var res = parseInt(this.style.width) / 20;
        res = res < 9 : "9px" ? res + "px";
        this.style.fontSize = res;
    });
}// P.S.: I did not test the code above, so I do not know whether there are mistakes.

It is less CSS and more JavaScript, and the JavaScript is not efficient.

2. Media queries

This works, but not especially well.

h2{
    font-size:25px
}
@media screen and (max-width: 850px){/* viewport is less than 850px wide; set a smaller font-size */
    h2{
        font-size:19px;
    }
}

Media-query font sizes change in steps, so the transition is not continuous. You may also need to write many @media blocks, which gets cumbersome.

3. Media queries plus -webkit-transition

This smooths out the change:

div{
    font-size: 40px;
    -webkit-transition:font-size 0.2s ease-out;
}
@media only screen and (max-width: 1200px) {
    div{ font-size: 39px; }
}
@media only screen and (max-width: 1100px) {
    div{ font-size: 38px; }
}
@media only screen and (max-width: 1000px) {
    div{ font-size: 37px; }
}
@media only screen and (max-width: 900px) {
    div{ font-size: 36px; }
}
@media only screen and (max-width: 800px) {
    div{ font-size: 35px; }
}
@media only screen and (max-width: 700px) {
    div{ font-size: 34px; }
}
@media only screen and (max-width: 600px) {
    div{ font-size: 33px; }
}
@media only screen and (max-width: 500px) {
    div{ font-size: 32px; }
}
@media only screen and (max-width: 400px) {
    div{ font-size: 31px; }
}
@media only screen and (max-width: 300px) {
    div{ font-size: 30px; }
}

Overall, this technique is not very useful in practice, and handling it in JavaScript is quite simple. There may be better approaches; if you have one, it is worth sharing.