Grow your CSS skills. Land your dream job.

Set Font Size Based On Word Count

Published by Chris Coyier

In my little Quotes on Design project, some of the quotes are longer than others. I thought it might be kind of nifty to beef up the font-size on the shorter quotes and trim down the size for the longer quotes so they all match a bit better in terms of the size they take up on the screen.

Rather than do this with a hard-coded number in each of the quotes, I did it with JavaScript. This allows for easier adjustments down the road and nicely keeps content and presentation separate.

The Plan

  • Set an object of the paragraph you are targetting
  • Split the paragraph into an array of words, breaking at every space
  • Count the length of the array
  • Set the font-size based on that count.

Doing it with MooTools

window.addEvent('domready',function() {
	
	$quote = $$('.post p')[0];
    
        var $numWords = $quote.get('text').split(' ').length; 
    
        if (($numWords >= 1) && ($numWords < 10)) {
                $quote.setStyle('font-size','36px');
        }
        else if (($numWords >= 10) && ($numWords < 20)) {
                $quote.setStyle('font-size','32px');
        }
        else if (($numWords >= 20) && ($numWords < 30)) {
                $quote.setStyle('font-size','28px');
        }
        else if (($numWords >= 30) && ($numWords < 40)) {
                $quote.setStyle('font-size','24px');
            }
        else {
                $quote.setStyle('font-size','20px');
        };
});

Doing it with jQuery

$(function(){

    var $quote = $(".post p:first");
    
    var $numWords = $quote.text().split(" ").length;
    
    if (($numWords >= 1) && ($numWords < 10)) {
        $quote.css("font-size", "36px");
    }
    else if (($numWords >= 10) && ($numWords < 20)) {
        $quote.css("font-size", "32px");
    }
    else if (($numWords >= 20) && ($numWords < 30)) {
        $quote.css("font-size", "28px");
    }
    else if (($numWords >= 30) && ($numWords < 40)) {
        $quote.css("font-size", "24px");
    }
    else {
        $quote.css("font-size", "20px");
    }    
	
});

Examples

Quotes on Design is using the jQuery version. MooTools example here (note some slight changes in the code to accommodate two quotes on one page). This is the first time I've every played with MooTools really. I'm excited to learn more. I am obviously pretty jQuery-centric around here, but it's definitely not the only kid on the block. Thanks @mootools!

Comments

  1. PHP variables, why? Also, you can use $$(“.post p:first”) in MooTools.

  2. Lewis Walsh
    Permalink to comment#

    Nice idea. I wonder though if it wouldn’t be more prudent to use your counting methods to count the words when the quote is put in to the list and store that number. Simply use the code again to update the number again if the quote is updated.

    This way you only run the count code once rather than every time a page is refreshed.

  3. Good to see a little MooTools love on CSS-Tricks!

  4. Permalink to comment#

    Neat little trick, Chris!

  5. cssProdigy
    Permalink to comment#

    I can’t remember the last time I saw MooTools on CSS Tricks.

  6. Bogdan
    Permalink to comment#

    Chris,

    Would it be too much to ask for a back button for “Quotes on design”. You could easily end up clicking “Another” over and over, and once you saw a nice cool quota, you accidentally click another, and darn, you cannot go back to the previous cool one.

  7. polosatus
    Permalink to comment#

    I just don’t get it: why not use php and why counting words instead of characters? What’s the difference?

    • Paul Grill
      Permalink to comment#

      I agree: There is no sense in counting words in a quote that doesn’t change everytime it is shown, so why not just count using php and let php determine what to do?

      <?php
      function determineFontSize($quote) {
      $numWords=str_word_count($quote);
      if (($numWords >= 1) && ($numWords < 10)) {
      return 36;
      }elseif (($numWords >= 10) && ($numWords < 20)) {
      return 32;
      }elseif (($numWords >= 20) && ($numWords < 30)) {
      return 28;
      }elseif (($numWords >= 30) && ($numWords < 40)) {
      return 24;
      }else {
      return 20;
      }
      ?>

    • dat_paulchen
      Permalink to comment#

      uh-oh there’s a curly bracket missing at the end of my little snippet

    • Permalink to comment#

      I would use the strlen() function, and use switch(){ case : } instead of ifs.

  8. Nice work!

    If you really want to get a feel for how MooTools works (and is different from jQuery – not necessarily better, but different), your code should be a class. Like this:

    var QuoteSizer = new Class({
        Implements: Options,
        options: {
            grid: {
                10: 36,
                20: 32,
                30: 28,
                40: 24
            },
            minimumSize: 20
        }
        initialize: function(element, options) {
            this.element = $(element);
            this.setOptions(options);
            var style = this.calculate(this.options.grid);
            this.style(style);
        },
        calculate: function(grid){
            var numWords = $(this).get('text').split(' ').length;
            var value;
            $each(grid, function(size, count){
                if (numWords < count) {
                    sizeSet = size;
                }
            }, this);
            return value;
        },
        style: function(size) {
            $(this).setStyle('font-size', size || this.options.minimumSize);
        }
    });
    
    window.addEvent('domready',function() {
        new QuoteSizer($$('.post p')[0]);
    });
    

    What’s cool about this is that you can then extend it with greater functionality later, if you like:

    var QuoteColors = new Class({
        Extends: QuoteSizer,
        options: {
            grid: {
                10: 'red',
                20: 'blue',
                30: 'green',
                40: 'yellow'
            },
            defaultColor: 'black'
        }
        style: function(color) {
            $(this).setStyle('color', color || this.options.defaultColor);
        }
    });
    
    • Wow, thanks for taking the time to help out and make this a lot smarter Aaron!

    • No problem! I like seeing people learn the library. Translating a jQuery design to a MooTools design is pretty easy to do (as you found out), but the real magic in MooTools comes when you learn MooTools design, which is quite different from jQuery’s (again, different, not necessarily better).

    • styx
      Permalink to comment#

      From what i’ve learn and what uou show, it seems that you wrote a plugin-like.
      I mean With jquery you can achieve almost the same goal:

      (function($){
      $.fn.quoteSizer = function(options) {
      var settings = $.extend({
      grid: {10:36,20:32,30:28,40:24},
      minimumSize: 20
      }, options);
      [...code here...]
      };
      })(jQuery);

      And then call the script like this:
      $(‘.post’).quoteSizer();

      And even extend the basic function with:
      $.quoteSizer.extend()

      Seems like the difference is not as wide as you believed.

  9. I’m a noob with noob questions.

    What do you do with this code? Where does it go? Can I use it with WordPress? How do I use it with WordPress?

    Found you through @andysowards.

  10. Vince
    Permalink to comment#

    This is just awesome! I wish I thought of this!

  11. Permalink to comment#

    Great idea, but i suggest to add some HTML tags wipeout method before counting the words.

  12. Nice class there, Aaron. When I first read what was happening, I thought of a different way of calculating the right size. There’s a pattern in the original concept, every 10 words, lower the font size by 4. So I just wrote a simple formula of division and multiplication. The differences I would make to the class.

    options: {
        maxSize: 36,
        fontDiff:4,
        wordDiff:10,
        minSize: 20
    }
    calculate: function(grid){
        var numWords = $(this).get('text').split(' ').length;
        var wordDiff = this.options.wordDiff, fontDiff = this.options.fontDiff, minSize = this.options.minSize;
    
        var size = this.options.maxSize;
        size -= (numWords / wordDiff).toInt() * fontDiff;
        return (size > minSize ? size : minSize);
    }
    

    Granted, this provides a little less control, since it requires a pattern. But it allows defining a larger range with more ease, so you don’t have to provide values for 10-100 words or whichever. shrug

    • Why you always gotta show me up Sean? WHY?

    • Though now that I think of it, your version wouldn’t allow you to extend it as I did mine…

    • That’s true… Though if you wanted to get fancy, you could extend what I did and rewrite the calculate function using a pattern still and calculate rgb values. I couldn’t think of a quick example…

      Though I did admit the function I thought of relied on a pattern, and gave less specific control :)

  13. Permalink to comment#

    nice! you could simplify the code a little, tho (MooTools version):

    window.addEvent('domready',function() {
    
      $quote = $$('.post p')[0];
    
      var $numWords = $quote.get('text').split(' ').length; 
      var size = 20;
    
      if ($numWords >= 1 && $numWords < 10) {
        size = 36;
      }
      else if ($numWords >= 10 && $numWords < 20) {
        size = 32;
      }
      else if ($numWords >= 20 && $numWords < 30) {
        size = 28;
      }
      else if ($numWords >= 30 && $numWords < 40) {
        size = 24;
      }
    
      $quote.setStyle('font-size', size +'px');
    
    });
    
  14. Jordan
    Permalink to comment#

    Nice trick.

    Since all of the font sizes seem to be, consistently, 88% of the previous, you could set a default size (36px) and default modifier (0.88), and for every 10 words, multiply the default size by modifier^($numWords / 10), instead of having to setup multiple different if/else blocks.

    Something like (but after a lot more optimization, obvs):

    $fontSize = 36;
    $modifier = 0.88;
    $quotes.css("font-size", Math.min(Math.round($fontSize * (Math.pow($modifier, Math.round($numWords / 10)))), 20) + "px");

    Basically, you’re rounding the number of words divided by ten (x), raising the modifier to the power of x, rounding that, multiplying it by the default font-size, rounding it, and, to maintain a minimum size of 20px, running it through min(). And I’m sure there’s some obscure javascript function that does all that in half the time of what I just wrote, but I sure don’t know what that would be :p

    I don’t know what the speed would be like, though. That’s probably the biggest drawback. I imagine your if/else is probably quite a bit faster. But it might be easier, if you ever decide to change anything, to just change the default size and the modifier, rather than having to change a different size in each if block.

    Or maybe I’m just weird and like to throw things in equations for no reason. Totally a possibility :)

    • Jordan
      Permalink to comment#

      Oh, wow, maybe I should f5 before replying to posts that have been opened in a tab for 6 hours…

  15. This is really cool. Thanks for sharing!

  16. Jarrod
    Permalink to comment#

    This reminds me of the iPhone text resize in fields based upon the amount of text in applications. I wish I could figure out how to do it based upon character count instead of words. It would be potentially handy in a mobile app. Pretty cool and powerful, nonetheless.

    • Just simply take the length of the string, instead of splitting it by whitespace like he is doing.

      $('#element').get('text').length

  17. Permalink to comment#

    Cool concept. I like the practical usage of that. One quick thing:

    print("window.addEvent('domready',function() {

    $quote = $$('.post p')[0];

    var $numWords = $quote.get('text').split(' ').length;

    if (($numWords >= 1) && ($numWords < 10)) {
    $quote.setStyle('font-size','36px');
    }
    else if (($numWords >= 10) && ($numWords < 20)) {
    $quote.setStyle('font-size','32px');
    }
    else if (($numWords >= 20) && ($numWords < 30)) {
    $quote.setStyle('font-size','28px');
    }
    else if (($numWords >= 30) && ($numWords < 40)) {
    $quote.setStyle('font-size','24px');
    }
    else {
    $quote.setStyle('font-size','20px');
    };

    });”);

    In this code, you are performing unnecessary calculations. I would optimize it by taking out the first condition in all of the if statements. (Assuming no negative/non-zero word counts) As you roll through the statements, the first condition will always hold true if the second is true.

    print("window.addEvent('domready',function() {

    $quote = $$('.post p')[0];

    var $numWords = $quote.get('text').split(' ').length;

    if ($numWords < 10) {
    $quote.setStyle('font-size','36px');
    }
    else if ($numWords < 20) {
    $quote.setStyle('font-size','32px');
    }
    else if ($numWords < 30) {
    $quote.setStyle('font-size','28px');
    }
    else if ($numWords < 40) {
    $quote.setStyle('font-size','24px');
    }
    else {
    $quote.setStyle('font-size','20px');
    };

    });”);

    • I don’t believe that will work Brian. Take the example of if the $numWords was “5″. That would meet all four of the first conditions and end up setting the font size to 24px when you really want it to be 36px. Maybe you could put a “break” in there of some kind, but as written it doesn’t look like it would work logically.

    • It actually would work that way. Because he’s using else if clauses. So the second condition only gets evaluated if the first is false, and so on.

  18. Permalink to comment#

    That is is a nifty little snippet of code.

  19. Permalink to comment#

    I just love finding these snippets, good idea and thanks for the share.

  20. Permalink to comment#

    Thanks for the MooTools goodness! :)

  21. Spencer
    Permalink to comment#

    If you’re going to use javascript, then why not set a height for the quote and increase the font until you hit it? It’s quick enough (to be unnoticeable), is font-family, -weight, line-height, etc. independent, and ensures the portion of screen you want filled, filled.

    Spencer

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".