Frederik Vig – ASP.NET developer

Follow me

Archive for the ‘Web design’ Category.

Guide to font sizing with CSS

Font sizing with CSS and browser support as always been a mystery to me. We used to use pixels, than we switched to ems and percent, then we switched back to pixels again?!.. In this post I’ve tried shedding some light on the matter and explaining the reason for this, and the different ways of doing font sizing on the web.

You have quite a few different measuring units to use in CSS for use with the font-size property (em, ex, px, in, cm, mm, pt, pc, %). We can group these lengths into two groups.

Relative lenghts

  • em: the ‘font-size’ of the relevant font
  • ex: the ‘x-height’ of the relevant font
  • px: pixels, relative to the viewing device (resolution and pixel density)

Absolute lengths

  • in: inches — 1 inch is equal to 2.54 centimeters.
  • cm: centimeters
  • mm: millimeters
  • pt: points — the points used by CSS 2.1 are equal to 1/72nd of an inch.
  • pc: picas — 1 pica is equal to 12 points.

Percentage values are always relative to a length or other value.

To be honest I only use four of these units. I use points (pt) in my print stylesheet, and px, em and % for my screen stylesheet.

Pixels

When using pixels for your font size you don’t have much to think about, 12px is 12px in every browser. What can be a problem, and what is the main reason that organizations like the W3C advice against using pixels for font size measurement, is the lack of support in browsers like Internet Explorer 6 when changing the browser’s default font size. The reason for this is that most browsers used to use text scaling for this, only enlarging the text on the page. However the newest versions of all the major browsers now use page zooming instead. What this does is increase (or decrease) all the elements on the page, not just the text, by zooming.

Lack of support for page zooming in Internet Explorer 6 can be a problem. If it is you should consider using ems and percent, which all browsers support. You can use ems/percent either for all browsers or by using a conditional comment stylesheet for Internet Explorer 6 and using pixels for the rest. I personally have started using pixels again, simple because it saves me a ton of work, and is much more reliable for measurement than using % and ems.

Ems and percent

If your audience still consists of a lot of Internet Explorer 6 users, you might want to stick with using ems or % for measurement. The rule to follow here is target ÷ context = result.

Example

If we give body a font size of 100% (roughly around 16px in most browsers), we can use that as the context. So if we want 12px in font size for normal text, and 20px for headings, this then becomes the target.

  • 12 ÷ 16 = 0,75
  • 20 ÷ 16 = 1,25
body {
    font-size: 100%;
}
p {
    font-size: .75em;
}
 
h1 {
    font-size: 1.25em;
}

If we’d used pixels we would have something like this.

p {
    font-size: 12px;
}
 
h1 {
    font-size: 20px;
}

Okay, say we have some text inside our paragraphs that we want to be 13px. The target becomes 13px, but the context changes, instead of using the body as the context, the paragraph becomes the context. So we get 13 ÷ 12 = 1,0833.

p strong {
    font-size: 1.0833em;
}

By using the target ÷ context = result rule, everything comes down to maths :) .

Here is the live example with ems and percent and here with pixels.

So, what are you using?

Better breadcrumb trail

I often install the Public Templates when setting up a new EPiServer project. They contain some good code that I reuse in various parts of a new site. One thing that I’ve copied and modified is the code for the breadcrumb (breadcrumb.ascx). By default the markup rendered looks something like this.

<a href="/en/" title="To start page">Start</a> / 
<a href="/en/Events/" title="Events">Events</a> / 
<a href="/en/Events/Conference/" title="Conference">Conference</a>

There are a number of problems with this code. First of the markup is not very semantic. Second the title attribute is redundant, since the same text gets repeated twice, and thus read by screen readers twice. Third the page we’re currently on should not be a link, since we’re already on it. It should also have some help text for screen reader users giving information that “Conference” in fact is the current page.

Better markup

Okay lets fix the semantics first. Lets put the links inside a list and remove the forward slash (/), which is presentational, something that should be handled by CSS.

<ul>
<li><a href="/en/" title="To start page">Start</a></li>
<li><a href="/en/Events/" title="Events">Events</a></li>
<li><a href="/en/Events/Conference/" title="Conference">Conference</a></li>
</ul>

This is better, but we’re not quite there yet. An unordered list is not the correct list type here. The meaning of the list changes when we change the order of the links, we should therefor use an ordered list instead. Lets update the list to be ordered, and lets also get rid of all the title attributes.

<ol>
<li><a href="/en/">Start</a></li>
<li><a href="/en/Events/">Events</a></li>
<li><a href="/en/Events/Conference/">Conference</a></li>
</ol>

That’s better! Now we only have the “Conference” link left. Lets remove the anchor, and add some helper text for our screen reader users.

...
<li>Conference <span>(this page)</span></li>
...

A little styling

Great, now with a little CSS we can transform this to a nice, user friendly breadcrumb trail.

#breadcrumb {
    list-style: none;
}
 
#breadcrumb li {
    display: inline;
    margin: 0;
    padding: 0;
}
 
#breadcrumb a
{
    color: #3e3e3e;
	float: left;
	margin-right: .5em;
	padding-right: 1em;
	background: url(images/separator.gif) no-repeat right center;
}
 
#breadcrumb span {
    position: absolute;
    left: -9999px;
}

Nothing particular here, instead of adding extra markup for the separator, we’re using a background image. And for the helper text we’re using a technique for pushing the span element all the way to the left (off screen), hiding it from all except screen reader users. Why not use display: none instead? Well because some screen readers don’t read text that has the display property set to none.

The result


Nothing exciting here, but we now have a much more semantic breadcrumb, that is very easy to style to our needs.

Implementation in EPiServer

This is code based on the Public Templates breadcrumb.ascx, modified for our purpose.

using System;
using EPiServer.Core;
using System.Text;
 
namespace EPiServer.Templates.Public.Units.Static
{
    public partial class BreadCrumbs : UserControlBase
    {
        private const string _link = "<li><a href=\"{0}\">{1}</a></li>";
        private int _maxLength = 60;
        private string _breadcrumbId = "breadcrumb";
 
        protected override void OnPreRender(EventArgs e)
        {
            Breadcrumbs.Text = GenerateBreadCrumbs(CurrentPage);
        }
 
        private string GenerateBreadCrumbs(PageData page)
        {
            // Initiate a string builder based on max length. The generated html is considerably longer than the visible text.
            var breadCrumbsText = new StringBuilder(8 * MaxLength);
 
            // Initiate a counter holding the visible length of the bread crumbs with the length of the start page link text.
            int breadCrumbsLength = Translate("/navigation/startpage").Length;
 
            while (page != null && !page.PageLink.CompareToIgnoreWorkID(PageReference.StartPage))
            {
                breadCrumbsLength += page.PageName.Length;
                if (breadCrumbsLength > MaxLength)
                {
                    breadCrumbsText.Insert(0, "...");
                    break;
                }
 
                // Insert the link at beginning of the bread crumbs string 
                breadCrumbsText.Insert(0, this.GetLink(page));
 
                // Get next visible parent
                page = this.GetParentPageData(page);
            }
 
            // Generate the start page link 
            string startPageLinkUrl = string.Empty;
            if (string.IsNullOrEmpty(startPageLinkUrl))
            {
                startPageLinkUrl = Server.UrlPathEncode(GetPage(PageReference.StartPage).LinkURL);
            }
 
            string startPageLink = string.Format(_link, startPageLinkUrl, Translate("/navigation/startpage"));
            breadCrumbsText.Insert(0, startPageLink);
 
            string listStart = string.Format("<ol id=\"{0}\"", _breadcrumbId);
 
            breadCrumbsText.Insert(0, listStart);
            breadCrumbsText.Append("</ol>");
            return breadCrumbsText.ToString();
        }
 
        /// <summary>
        /// Get the next visible parent page of the supplied <see cref="PageData"/>. 
        /// </summary>
        /// <param name="page"></param>
        /// <returns>The <see cref="PageData"/> object or    </returns>
        private PageData GetParentPageData(PageData pageData)
        {
            // Don't return a PageData object for start page or root page.
            if (pageData == null || pageData.ParentLink == PageReference.StartPage || pageData.ParentLink == PageReference.RootPage)
            {
                return null;
            }
 
            // Get Page data for parent page
            pageData = GetPage(pageData.ParentLink);
            if (pageData != null && pageData.VisibleInMenu)
            {
                return pageData;
            }
            // Step up to next parent
            return GetParentPageData(pageData);
        }
 
        /// <summary>
        /// Returns a anchor based on a <see cref="PageData"/> object.
        /// </summary>
        private string GetLink(PageData page)
        {
            string pageName = page.Property["PageName"].ToWebString();
 
            if (page.PageLink.CompareToIgnoreWorkID(CurrentPage.PageLink))
            {
                return string.Format("<li>{0} <span>(this page)</span></li>", pageName);
            }
 
            return string.Format(_link, Server.UrlPathEncode(page.LinkURL), pageName);
        }
 
        /// <summary>
        /// Sets the max length on the visible breadcrumb text (default = 60)
        /// </summary>
        public int MaxLength
        {
            get { return _maxLength; }
            set { _maxLength = value; }
        }
 
        public string BreadcrumbId
        {
            get { return _breadcrumbId; }
            set { _breadcrumbId = value; }
        }
    }
}

One thing to note here is that I’ve hard coded the text (this page). This should be placed in a language file instead.

Other resources

Font resizing and printing with jQuery

You’ve probably seen the three A’s on various websites, especially public sector websites; that you can click on to increase the font size on the web site. I’m personally not a huge fan of those. In my opinion it is much better to explain to the user how they can use the built in browser functionality to do this instead. This will also increase the font size on all websites not just the one you created. You could do this by linking to a page with an explanation and screenshots of how to do this in the most popular browsers.

However sometimes I still need to create those three A’s. Below is a little jQuery plugin that does this.

See the demo

Font resizing plugin

This plugin will create an ordered list with three links to increase the font size. The plugin adds three different classes to the body element, which then can be used to set the base font size.

body.small {
    font-size: 80%;
}
 
body.medium {
    font-size: 120%;
}
 
body.large {
    font-size: 140%;
}

The plugin code.

(function($) {
    $.fn.fontresizing = function(customOptions) {
        var options = $.extend({}, $.fn.fontresizing.defaultOptions, customOptions);
        var bodyClasses = '' + options.smallClass + ' ' + options.mediumClass + ' ' + options.largeClass + '';
        return this.each(function() {
            $(this).append('<ol class="' + options.fontresizingClass + '"><li><a href="" class="' + options.smallClass + '">A</a></li><li><a href="" class="' + options.mediumClass + '">A</a></li><li><a href="" class="' + options.largeClass + '">A</a></li></ol>');
 
            $('ol.' + options.fontresizingClass + ' a').click(function() {
                var cssClass = $(this).attr('class');
                $('body').removeClass(bodyClasses).addClass(cssClass);
                createCookie('fontresizingClass', cssClass, options.cookieDuration);
 
                return false;
            });
 
            var fontresizingClass = readCookie('fontresizingClass');
            if (fontresizingClass == options.smallClass || fontresizingClass == options.mediumClass || fontresizingClass == options.largeClass) {
                $('body').removeClass(bodyClasses).addClass(fontresizingClass);
            }
 
            // cookie functions http://www.quirksmode.org/js/cookies.html
            function createCookie(name, value, days) {
                if (days) {
                    var date = new Date();
                    date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
                    var expires = "; expires=" + date.toGMTString();
                }
                else var expires = "";
                document.cookie = name + "=" + value + expires + "; path=/";
            }
 
            function readCookie(name) {
                var nameEQ = name + "=";
                var ca = document.cookie.split(';');
                for (var i = 0; i < ca.length; i++) {
                    var c = ca[i];
                    while (c.charAt(0) == ' ') c = c.substring(1, c.length);
                    if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
                }
 
                return null;
            }
        });
    };
 
    $.fn.fontresizing.defaultOptions = {
        smallClass: 'small',
        mediumClass: 'medium',
        largeClass: 'large',
        fontresizingClass: 'font-resizing',
        cookieDuration: 365
    };
})(jQuery);

Breaking it down

We start by creating a private scope for our jQuery code. The reason for this is so we don’t have to worry about conflicts with other libraries.

(function($) {
...
})(jQuery);

We then add a new method to the jQuery.fn object

(function($) {
    $.fn.fontresizing = function(customOptions) {
    ...
    };
})(jQuery);

Combine the custom options with the default options.

(function($) {
    $.fn.fontresizing = function(customOptions) {
        var options = $.extend({}, $.fn.fontresizing.defaultOptions, customOptions);
        ...
    };
 
    $.fn.fontresizing.defaultOptions = {
        smallClass: 'small',
        mediumClass: 'medium',
        largeClass: 'large',
        fontresizingClass: 'font-resizing',
        cookieDuration: 365
    };
})(jQuery);

Next we iterate over the jQuery object or jQuery wrapper set, and return “this” (the current jQuery object), so we don’t break the jQuery chaining functionality.

(function($) {
    $.fn.fontresizing = function(customOptions) {
        var options = $.extend({}, $.fn.fontresizing.defaultOptions, customOptions);
        var bodyClasses = '' + options.smallClass + ' ' + options.mediumClass + ' ' + options.largeClass + '';
        return this.each(function() {
        ...
        });
    };
 
    $.fn.fontresizing.defaultOptions = {
        smallClass: 'small',
        mediumClass: 'medium',
        largeClass: 'large',
        fontresizingClass: 'font-resizing',
        cookieDuration: 365
    };
})(jQuery);

Now we come to the actual code for creating the links and attaching click events to them.

(function($) {
    $.fn.fontresizing = function(customOptions) {
        var options = $.extend({}, $.fn.fontresizing.defaultOptions, customOptions);
        var bodyClasses = '' + options.smallClass + ' ' + options.mediumClass + ' ' + options.largeClass + '';
        return this.each(function() {
            $(this).append('<ol class="' + options.fontresizingClass + '"><li><a href="" class="' + options.smallClass + '">A</a></li><li><a href="" class="' + options.mediumClass + '">A</a></li><li><a href="" class="' + options.largeClass + '">A</a></li></ol>');
 
            $('ol.' + options.fontresizingClass + ' a').click(function() {
                var cssClass = $(this).attr('class');
                $('body').removeClass(bodyClasses).addClass(cssClass);
                createCookie('fontresizingClass', cssClass, options.cookieDuration);
 
                return false;
            });
            ...
        });
    };
 
    $.fn.fontresizing.defaultOptions = {
        smallClass: 'small',
        mediumClass: 'medium',
        largeClass: 'large',
        fontresizingClass: 'font-resizing',
        cookieDuration: 365
    };
})(jQuery);

Lastly we have the cookie functions to read and create the cookie to hold the font size choices the user has made.

...
            var fontresizingClass = readCookie('fontresizingClass');
            if (fontresizingClass == options.smallClass || fontresizingClass == options.mediumClass || fontresizingClass == options.largeClass) {
                $('body').removeClass(bodyClasses).addClass(fontresizingClass);
            }
 
            // cookie functions from http://www.quirksmode.org/js/cookies.html
            function createCookie(name, value, days) {
                if (days) {
                    var date = new Date();
                    date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
                    var expires = "; expires=" + date.toGMTString();
                }
                else var expires = "";
                document.cookie = name + "=" + value + expires + "; path=/";
            }
 
            function readCookie(name) {
                var nameEQ = name + "=";
                var ca = document.cookie.split(';');
                for (var i = 0; i < ca.length; i++) {
                    var c = ca[i];
                    while (c.charAt(0) == ' ') c = c.substring(1, c.length);
                    if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
                }
 
                return null;
            }
...

Usage

To use the plugin simply add a reference to the jQuery library file, and copy the code into your page. After you’ve done that you can use it by calling the fontresizing method

// With default options
jQuery(function($) {
    $('#tools').fontresizing();
});
 
// With custom options
jQuery(function($) {
    $('#tools').fontresizing({
        smallClass: 'small',
        mediumClass: 'medium',
        largeClass: 'large',
        fontresizingClass: 'font-resizing',
        cookieDuration: 365
    });
});

Print plugin

Another thing that many clients ask for is a print link. Below is a little jQuery plugin that creates the link and attaches a click event that triggers the browsers print dialog.

(function($) {
    $.fn.print = function(customOptions) {
        var options = $.extend({}, $.fn.print.defaultOptions, customOptions);
 
        return this.each(function() {
            $(this).append('<a href="" class="' + options.printClass + '">' + options.printText + '</a>');
 
            $('a.' + options.printClass + '').click(function() {
                window.print();
                return false;
            });
        });
    };
 
    $.fn.print.defaultOptions = {
        printClass: 'print',
        printText: 'Print'
    };
})(jQuery);

Here we also generate the link with JavaScript, so that users who don’t have JavaScript wont see it – since they cannot use it anyway. When you click on the link you’ll see the print dialog; use this with CSS to create a printer friendly version of your page.

To create CSS rules for print either use the @media

@media print {
  /* CSS rules here */
}

or link to an external CSS file and add the media=”print” attribute.

<link rel="stylesheet" href="styles/print.css" media="print" type="text/css" />

Usage

// With default options
jQuery(function($) {
    $('#tools').print();
});
 
// With custom options
jQuery(function($) {
    $('#tools').print({
        printText: 'Print this page'
    });
});

Download the code or see the demo

Creating a mobile version of a web site

When building web sites we has developers or designers have to take into consideration all the different types of devices that can be used to access the web sites we create. Not just PC or Mac with Internet Explorer, Firefox, Safari, Opera, Jaws, or any other browser. But also mobile devices, like iPhones, iTouch, Nintendo Wii.

Especially in recent years, after the launch of the iPhone, accessing online information through a mobile device has become more common. This is something that is going to increase even more in the next few years.

Below I’ve listed some resources for creating a mobile version of a web site that I hope you’ll find useful.

Mobify

Mobify is a free online service that lets you convert an existing web site into a mobile version. This is done by selecting the content you wish to display for mobile users, customizing it by changing the display order and tweaking the CSS. The last step is to publish it by creating an url and adding a little JavaScript to your page.

Check out How to implement a mobile version of your blog in three simple steps for a quick introduction.

Mobify is quick and easy way to great a mobile version, but you cannot change the HTML code and you’re stuck with the options the service offers. It can sometimes be better to create a mobile version from scratch.

Update 05.10.2009

Igor Faletski, founder of Mobify, was kind enough to send me an email explaining the use of HTML blocks to add more static content. While this is a great feature I still miss the ability to update my existing markup and easily see what classes and ids that the mobile version uses. Now I have to test and tweak a little before I get the right id or class when changing my CSS.

Creating a mobile version with ASP.NET

Although services like Mobify are great, sometimes you need full control. On mobify.me it says that Mobify supports over 4500+ different mobile devices. This is a lot of different devices to support! Fortunately there is an open source project on CodePlex that has done most of the work for us. It is called Mobile Device Browser File and consists of a browser file with information about what the different mobile devices support (Java, JavaScript, Images, Flash etc). This project is pretty active with new devices added all the time.

You simply download the browser file and add it to your projects App_Browser folder. After you’ve done that you’ll be able to use Request.Browser["IsMobileDevice"] to detect if the device accessing your page is actually a mobile device. There are also 64 other capabilities that offers a lot of different detection functionality. You can for instance check if the device supports Flash, JavaScript, Color, CSS background images etc.

If you’re using ASP.NET MVC check out Scott Hanselman’s post about how he implemented this with Nerd Dinner.

If you’re using ASP.NET web forms there are a couple of approaches you could take. You could use a different master page for the mobile version by adding some detection code to your base page and switching the master pages in the Init method. Or you could register a custom HttpModule that redirects the user to a different page if it is a mobile device.

Recently I stumbled over another library to detect the mobile device, it is called 51Degrees.mobi. Here is the article that describes how to use it.

iPhone

Apple iPhone uses the Safari browser as its default browser. The great thing about that is that CSS3 stuff like border-radius, multiple background images, canvas and more are supported.

If you like to simply target iPhone users you can do so by simply adding a custom stylesheet with this code.

<link media="only screen and (max-device-width: 480px)" 
    href="iPhone.css" type="text/css" rel="stylesheet" />

For more information I recommend reading A List Aparts Put Your Content in My Pocket and checking out the iPhone Dev Center.

Wordpress

For Wordpress you have a nice plugin called WPtouch, that will help you easily add a mobile view for iPhone, iPod, Android, Storm and Pre. You just need to install and activate the plugin, and you’re ready! You can easily customize it to your needs as well.

Other resources

More and more resources are being added each day. Below are some I’ve bookmarked and that have helped me in the past.

CSS tricks – part 1

Here are a few CSS tricks I’ve picked up along the way.

Always visible vertical scrollbar

When you have a fixed width centered web site open you’ve probably noticed that the page sometimes jumps a little. This is because the vertical scrollbar only is visible if the content is longer than the viewport, if no vertical scrollbar is visible in the browser. To always have the vertical scrollbar visible you can use the CSS hack below.

html {
    overflow-y: scroll;	
}

CSS reset styles

To make our lives as web developers easier we have a few css reset style rules which will help make things more consistent across browsers. It started out with simply setting the padding and margin of all elements to 0 with this CSS code.

* {
    margin: 0;
    padding: 0;
}

Though this caused a few problems with form elements. After a few years of using the technique above I started using a CSS reset stylesheet.

100% min-height

#wrapper {
    min-height: 600px;
    background-color: #000;
}
* html #wrapper {
    height: 600px;
}

Internet Explorer 6 has problems understanding the min-height property. Therefor I use the star selector hack to target IE6 and set the height property (IE6 uses the height property the same way as min-height, so if the content is bigger the page will still grow, like with min-height).

More information about the min-height property

Star selector hack

Applying * html {selector here} will only target Internet Explorer 5.5 and 6. Which makes this a nice way to still use valid CSS and only target those two browsers. Like in the min-height example above.

More information: Workarounds and Filters

Internet Explorer 6 double margin bug

When setting the float property to either left or right and having a margin set in the same direction as the float (either margin-left or margin-right) will give IE6 users twice the margin.

#sidebar {
    float: left;
    margin-left: 10px;
}

This will give IE6 users a left margin of 20px. Fortunately it is easy to fix. Simply add the display property and set it to inline.

#sidebar {  
    float: left;
    margin-left: 10px;
    display: inline;
}

This has no negative effect on other browsers. Also, when floating inline elements (a, span, em, etc), the element automatically becomes the same as when using display:block.

More on floats

hasLayout in Internet Explorer

“Layout” is an IE/Win proprietary concept that determines how elements draw and bound their content, interact with and relate to other elements, and react on and transmit application/user events.

This quality can be irreversibly triggered by some CSS properties. Some HTML elements have “layout” by default.

Microsoft developers decided that elements should be able to acquire a “property” (in an object-oriented programming sense) they referred to as hasLayout, which is set to true when this rendering concept takes effect.

Source: satzansatz.de

I mostly come across this when I have no width or height applied. Like in the code below.

#wrapper {
    overflow: hidden;
}
#sidebar {
    float: left;
    width: 200px;
}
#content {
    float: left;
    width: 600px;
}

Overflow: hidden will clear the floats in all browsers except for in IE6. I could fix this by giving #wrapper a width or by floating it. But that is not always possible. Instead I can use the star selector and give IE6 a height of 1% to just trigger hasLayout.

#wrapper {
    overflow: hidden;
}
* html #wrapper {
    height: 1%;
}
#sidebar {
    float: left;
    width: 200px;
}
#content {
    float: left;
    width: 600px;
}

More information on hasLayout

Displaying a pointer cursor for button, label and select elements

I always add this code when I start a new project.

input[type=submit], button, label, select { 
    cursor: pointer; 
}

Just to make it easier for users to see where they can click.

Transparent PNGs

This is mostly for my own reference, but hopefully useful for others as well. I always forget to add a pngfix for Internet Explorer 6 when using transparent PNGs.
There are a few fixes out there.

The one I’ve mostly used is SuperSleight – mostly because it is so easy to setup. You only have to download the JavaScript file and transparent gif, include it in your project, update the path to the transparent gif in the JavaScript file, and then include it inside the head section of your page with IE conditional comments.

<!--[if lte IE 6]>
<script type="text/javascript" src="supersleight-min.js"></script>
<![endif]-->

If you’re using jQuery there is also a jQuery version of SuperSleight. You include it in the same way as the regular JavaScript version, though remember to add the jQuery library file before. Then you can call it like this.

// Checks that the SuperSleight plugin is loaded
if (jQuery().supersleight) {
    // Here you can use regular jQuery selectors to select where you want to add SuperSleight
    $('body').supersleight();
}

© Copyright Frederik Vig. Based on Fluid Blue theme