Bad typography on the Web doesn't exactly kill people (even if it is believed to be 95% of what web design is) but it can still make widows - typographic widows, that is. To quote Shaun Inman:

For those who don’t know, in typesetting, a widow is a single word on a line by itself at the end of a paragraph and is considered bad style.

Compare these:

This is a widow
Now with Widon't!

Helping website owners to fix the most frequent errors automagically will hopefully make online texts more readable and stylish as well. Mr. Inman came up with a simple yet perfectly working solution: replace the regular whitespace between last two words of a title with a non-breaking space (i.e.   entity) - and released it in a form of an easy-to-use Wordpress plugin. It is now also ported for Movable Type, Expression Engine, Textpattern etc.

Today I make Widon't available for Symphony and other XML/XSLT-based engines via an XSL template. Read on for full source.

Edit your Entries utility (or any other file that you want to make use of this method) to include this template:

          <xsl:template name="widont-title">
    <xsl:param name="temp"/>
    <xsl:param name="text"/>

    <xsl:choose>
        <xsl:when test="contains($text, ' ')">
            <xsl:variable name="before" select="concat($temp,' ',substring-before($text,' '))"/>
            <xsl:variable name="after" select="substring-after($text, ' ')"/>
            <xsl:choose>
                <xsl:when test="contains($after, ' ')">
                    <xsl:call-template name="widont-title">
                        <xsl:with-param name="temp" select="$before"/>
                        <xsl:with-param name="text" select="$after"/>
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="concat($before, '&#160;', $after)" />
                </xsl:otherwise>
            </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$text"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

        

Update: By request from Josh I've modified the source to drop the non-breaking space if the combined length of two last words is longer than desired so that it won't break the layout in a bad way. The default is 35 characters but it can be modified either by editing the template or using an optional maxlen parameter. Download the updated template if you prefer this behavior.

N.B. &#160; entity is a proper way to place a non-breaking space in a generic XML document because &nbsp; is only defined in (X)HTML!

Call this template where appropriate, e.g. if you use Symphony stock data source and template, replace these lines in Entries utility's source:

          <a href="{$root}/entries/{@handle}/">
    <xsl:value-of select="fields/title"/>
</a>

        

with the following (don't forget to modify both "brief" and "full" modes):

          <a href="{$root}/entries/{@handle}/">
    <xsl:call-template name="widont-title">
        <xsl:with-param name="text" select="normalize-space(fields/title)" />
    </xsl:call-template>
</a>

        

Notice how I used normalize-space() function to remove any extra spaces that may occur in your string. That's important: a stray trailing space can make this algorithm fail since non-breaking space will be placed after the last word!


XSLT code used in this article is based on the work of XSLT standard library project and is in turn licensed under a GNU Lesser General Public License.