- Changing the Appearance of Server Controls
- Using Themes and Skins
- Master Pages
- User Controls
- Summary
- Exercises
- Key Concepts
- References
Using Themes and Skins
The previous section illustrates how you can customize your controls by setting the style properties of the controls themselves. ASP.NET 2.0 introduced the theme mechanism. This mechanism allows the developer to style the appearance of Web server controls on a site-wide basis. Like CSS, ASP.NET themes allow you to separate Web server control styling from the pages themselves, but have the additional benefit of having a complete object model that can be manipulated programmatically. Themes still allow you to use CSS for the majority of your visual formatting, and because theme support is built in to ASP.NET, you can dramatically alter the appearance of your Web site with a single line of programming (or a single line in the Web.config file). For instance, Figure 6.2 illustrates how a single Web Form's appearance can be radically transformed using three different themes.
Figure 6.2 Same page—three different themes
An ASP.NET Web application can define multiple themes. Each theme resides in its own folder within the App_Themes folder in the root of your application. Within each theme folder, there are one or more skin files, as well as optional subfolders, CSS files, and image files (see Figure 6.3).
Figure 6.3 Theme file structure
Defining Skins
A skin describes the appearance of one or more control types. For example, a skin file might look like the following.
<asp:Label runat="server" ForeColor="Blue" Font-Size="10pt" Font-Name="Verdana" />
Notice that a skin simply contains a control definition without the id attribute. A given skin file can contain multiple control definitions. Alternately, many developers have a separate skin file for each control type (a theme can contain any number of skin files).
Not all properties can be skinned. Generally speaking, only properties relating to appearance (i.e., the properties in Table 6.1 plus additional properties depending upon the control) can be specified in a skin. Referencing a property that is not themeable in a skin file generates an error. As well, certain controls, such as the Repeater, are not themeable, generally because they do not inherit from the WebControl class.
There is no Visual Studio designer support for creating skins. That is, the only way to create and modify a skin file is directly in Source view within Visual Studio. Even worse, Visual Studio's Intellisense is not available in Source view when modifying a skin. As an alternative, you could create a temporary Web Form, use the Design view or Source view as needed to add controls and set up their properties, copy and paste the markup to your skin file, and then remove the id attribute from each of the controls.
Creating Themes in Visual Studio
Themes reside in separate folders within the App_Themes folder within your site. You can create this folder yourself in Visual Studio by right-clicking the Web project in the Solution Explorer and choosing Add ASP.NET Folder → Theme option, as shown in Figure 6.4.
Figure 6.4 Adding a theme folder
Alternately, Visual Studio can automatically create a theme folder for you when you add a skin file via the Add New Item menu option (see Figure 6.5). In general, you probably want to avoid this approach because Visual Studio names the theme folder the same as the skin file.
Figure 6.5 Automatically adding a theme folder
Walkthroughs 6.1 and 6.2 demonstrate how to create a theme and a skin.
Applying a Theme
After a theme has been created (that is, after you've created one or more skin files), you can apply a theme to a page. This can be done in a few different ways. One way is to simply assign the theme using the Theme=themeName in the Page directive, for instance:
<%@ Page ... Theme="Cool" %>
You can also set the theme for all pages in a site via the Web.config file. To do so, simply specify the theme via the theme attribute of the pages element within the system.web element, as shown in the following.
<system.web> ... <pages theme="Cool" /> ... </system.web>
A page's theme can also be set programmatically. To do so, you can set the Theme property (defined in the Page base class) for a form in its code-behind class. This is covered in more detail later in the chapter. Finally, another, less common way to set the theme is to set it for all sites on a machine via the machine.config file. This file is located at [windows]\Microsoft.NET\Framework\[version]\CONFIG. Just as with setting the theme for a site via the Web.config file shown earlier, you can set the global theme for the machine as a whole via the Theme attribute of the pages element within the system.web element of this machine.config file. The specified theme folder and its contents must be located in the global theme space for the machine, located at [windows]\Microsoft.NET\Framework\[version]\ ASP.NETClientFiles\Themes.
How Themes Work
Now that you have seen how to create and use themes, let us peek under the hood and examine what ASP.NET does to make themes work. Like everything in ASP.NET, it all begins with a request. When a request arrives for a resource from an ASP.NET application that uses themes, the runtime parses and compiles all the skins in each theme. Recall from Chapter 2 that the markup in an aspx page is parsed into a class and then compiled into an assembly; an analogous thing happens with the skin files—each theme is parsed and compiled into a separate assembly.
Each theme is realized as a concrete subclass of the PageTheme class. This class maintains a collection of ControlSkinDelegate objects. This delegate represents or "points to" the actual method that applies the correct skin to the control; this method exists in the class file generated for the skin.
When the runtime executes a page that has a theme, it iterates through the page's Controls collection, and if there is a delegate for the control type, it calls the delegated method (in the generated skin class) which then decorates (i.e., changes the properties specified in the skin) the specific control object.
Overriding Themes
As previously mentioned, skin definitions for a control type override any settings for that control type made within a given page. For instance, consider the following skin definition.
<asp:Label runat="server" ForeColor="Green" />
Now imagine that you use this skin in a page that contains the following markup.
<asp:Label runat="server" id="labOne" Text="Hello" /> <asp:Label runat="server" id="labTwo" ForeColor="Blue" Text="World" />
What text color will the content of these two Label controls have when rendered by the browser? In fact, both will be green, because skin definitions override page definitions. You can have a control ignore a skin setting via the EnableTheming property, as in the following.
<asp:Label id="labTwo" ... EnableTheming="false" ForeColor="Blue"/>
There is another way to have properties defined within individual controls override skin settings. You can do so by changing the Page directive of the form and use the StyleSheetTheme attribute rather than the Theme attribute. Unlike those applied by the Theme attribute, the properties applied by the StyleSheetTheme are overridden by control properties defined within the page. As such, the StyleSheetTheme behaves in a manner more akin to the cascade within CSS. For instance, in the following example, the "World" text is blue.
<%@ Page ... StyleSheetTheme="Cool" %> ... <body> <asp:Label runat="server" id="labOne" Text="Hello" /> <asp:Label runat="server" id="labTwo" ForeColor="Blue" Text="World" /> </body> </html>
You can also set the StyleSheetTheme via the Web.config file. To set it in the Web.config file, you would use the styleSheetTheme attribute (rather than the Theme attribute), as in the following.
<system.web> ... <pages styleSheetTheme="Cool" /> ... </system.web>
Other than the fact that one allows skin properties to be overridden and the other does not, what else is different between the Theme and StyleSheetTheme attributes? A Theme is applied after the properties are applied to the server-side control, which is why the properties set by the Theme override those of the control. A StyleSheetTheme is applied before the properties from the server-side control, and are therefore overridden by the properties on the control. As well, the Visual Studio designer displays skins set by the StyleSheetTheme, but not by the Theme. If you specify both a Theme and StyleSheetTheme, the Theme takes precedence (i.e., control properties are overridden by the theme).
So which should you use? StyleSheetTheme is probably ideal during development because the Visual Studio designer displays the skins, and you can make quick changes to the page's Web server control appearance properties as part of your debugging and development. But because one of the primary benefits of themes is that you can change the entire appearance of a site through one simple theme change, it probably makes sense to use the Theme property when your site is ready for deployment; by then, you will no longer need the designer support and you will probably want to override any page properties by the formatting specified by the individual skins.
Named Skins
If you need to override the appearance of a skinned control, perhaps a better approach than using StyleSheetTheme is to use named skins. For instance, you might not want all of your Label controls to have the same appearance. You can thus define alternate skin definitions for the Label control by giving the different skins separate SkinID values. You can then reference this SkinID in your Web Form's Label controls. For instance, let's define a skin file with the following content.
<asp:Label runat="server" ForeColor="Green" Font-Size="10pt" /> <asp:Label runat="server" ForeColor="Red" Font-Size="14pt" Font-Name="Verdana" Font-Bold="True" SkinID="Quote" />
To use the named skin in any of your Web Forms, you simply need to add the reference to the SkinID in the controls that will use the named skin, for instance:
<asp:Label ID="labQuote" runat="server" Text="Hello" SkinID="Quote"/> <asp:Label runat="server" id="labMsg" ForeColor="Blue" Text="World" />
In this case, the word "Hello" appears in bold, 14pt red Verdana, whereas the word "World" appears as 10pt green text.
Themes and Images
One of the more interesting features of themes is that a given theme folder can also contain images and CSS files. You can thus radically transform a Web page by substituting different images and different style sheets. For instance, different themes could use a different set of images for bullets, image buttons, or for the icons used by the TreeView control. The only requirement is that the skin must use a relative URL for the image. This means that the image files must exist somewhere inside the same themes folder as the skin file itself.
For instance, the following skin defines two controls. The first is a named skin that displays the masthead image for the site; the second defines the look for all BulletedList controls. The relative path for the images indicates that the files are contained in a subfolder named images within this particular theme folder.
<asp:Image runat="server" ImageUrl="images/logo.gif" AlternateText="Masthead Image" SkinID="logo" /> <asp:BulletedList runat="server" BulletStyle="CustomImage" BulletImageUrl="images/bullet.gif" DisplayMode="HyperLink" />
To use this skin, your Web Form might look like that shown here. Notice how the resulting code in the Web Form is quite simple, because the additional properties are contained in the skin rather than in the Web Form. Figure 6.6 illustrates how the visual appearance of this form might vary simply by having different images for logo.gif and bullet.gif in two different themes containing the exact same skin.
Figure 6.6 Using images in theme skins
<asp:Image runat="server" ID="imgLogo" SkinID="logo" /> <asp:BulletedList ID="blstSample" runat="server"> <asp:ListItem Value="">Home</asp:ListItem> <asp:ListItem Value="">Browser</asp:ListItem> <asp:ListItem Value="">About</asp:ListItem> </asp:BulletedList>
Themes and CSS
There is a certain amount of overlap between CSS and ASP.NET skins. Both allow you to specify consistent site-wide formatting. For example, if you want all of your text boxes to have a certain border style and color scheme, you could do so via a skin, as in the following.
<asp:TextBox runat="server" BackColor="Beige" ForeColor="DarkKhaki" BorderColor="LightGray" BorderWidth="1px" />
You could achieve the same effect by defining a CSS class in the theme's style sheet.
.myTextBoxes { border: 1px solid LightGray; color: DarkKhaki; background-color: Beige; }
You could then reference this class in the skin.
<asp:TextBox runat="server" CssClass="myTextBoxes" />
So which approach is better? In general, try to place as much appearance formatting as possible into a theme's CSS files. Many Web sites are created in conjunction with a Web designer, who undoubtedly is familiar already with CSS. A designer can easily modify and even swap the CSS files in any given theme without knowing anything about ASP.NET. If all of a site's formatting is contained within skin files, formatting changes will instead require the intervention of an ASP.NET developer (which is perhaps a good thing if your sole concern is to boost the employment of ASP.NET developers). As well, CSS provides more control over the appearance of a page than is possible with skins, which are limited to those appearance properties exposed by the Web server controls. If you use appearance properties to define the look of the controls in a theme's skin, the downloaded size of the resulting rendered markup will be larger than the CSS approach, because these properties will be emitted in each HTML element. Finally, even with skins, CSS probably is still necessary to format plain HTML text.
Ultimately, then, your site is much more maintainable if as much formatting as possible is contained within CSS. If your site design requires a change in the font or color, it is much more preferable to change just one file (the style sheet), rather than having to change both a style sheet and multiple skins. Finally, another benefit of CSS is that if your Web Forms use CSS rather than HTML tables for layout, different themes could completely change the layout of the site, as was illustrated back in Figure 6.2.
Thankfully, you do not have to choose between CSS and themes. Each theme can in fact contain multiple CSS files. ASP.NET automatically links all of a theme's CSS files into a page by adding the appropriate <link> element for each CSS file into the header of a page. Note that each page must have the runat="server" attribute in the <head> for this to occur, as shown here.
<head runat="server"> <title>Some title here</title> </head>
Despite the benefits of CSS, there are several things that skins can do, which CSS cannot. Some server control properties that are themeable have no CSS equivalent. For instance, you can specify the textual format to display the days of the week in a Calendar control or the locations for the hot spots in an ImageMap control. As well, because themes are applied on the server side, it is possible to dynamically specify a theme based on user input or configuration information without the bother of Javascript-based CSS file swapping.
In addition, the more complex templated controls, such as Calendar, GridView, MultiView, Wizard, and so on, are probably styled more easily through skins, because it is up to ASP.NET to determine exactly what markup to use when rendering these controls. Yet even here, there still is a role for CSS. For example, in the following GridView skin definition (we cover the GridView control later in Chapter 10), the three style templates contain appearance properties.
<asp:GridView runat="server" GridLines="Vertical" > <HeaderStyle BackColor="#FF9900" ForeColor="#FFFFFF" /> <RowStyle BackColor="#FFFFFF" ForeColor="#333333" /> <AlternatingRowStyle BackColor="#F2F2F2" ForeColor="#333333"/> </asp:GridView>
A better approach is to define the template styling information via CSS classes.
.gridHeader { background-color: #FF9900; color: #FFFFFF; } .gridRow { background-color: #FFFFFF; color: #333333; } .gridAltRow { background-color: #F2F2F2; color: #333333; }
With the CSS classes defined, you could then simply reference these classes in the skin, as shown here.
<asp:GridView runat="server" GridLines="Vertical" > <HeaderStyle CssClass="gridHeader" /> <RowStyle CssClass="gridRow" /> <AlternatingRowStyle CssClass="gridAltRow" /> </asp:GridView>
Again, the benefit of this approach is that the typical Web designer, who probably does not know ASP.NET, can still make changes to the visual appearance of the ASP.NET application by modifying the appropriate CSS files.
Dynamically Setting the Theme
One of the key benefits that themes provide to the Web developer is the ability to programmatically change a page's theme. A page's theme can be set programmatically in the code-behind class. However, you may recall from Figure 2.3 in Chapter 2 that theme skins are applied before the controls and page are initialized. Thus, you must set the page's Theme property in the PreInit event handler for the page, as shown here.
protected void Page_PreInit(object o, EventArgs e) { // Set theme for this page this.Theme = "Cool"; }
The PreInit event is new to ASP.NET 2.0. It is raised just before the Init event of the page, but after the controls for the page have been instantiated. Of course, with the simple example shown earlier, it makes much more sense to simply set the theme in the Web.config or in the Page directive. The real advantage of using the programmatic approach is that the page's theme can be set dynamically based on user preferences. This preference can even be persisted in a user profile (covered in Chapter 14), in a session (covered in Chapter 12), or in a database.
Like with the Theme property, you can also set the StyleSheetTheme property programmatically. However, unlike with setting the Theme property, the programmatic setting of the StyleSheetTheme property is not done in the PreInit method, but is achieved by overriding the property in your code-behind class, as in the following.
public override String StyleSheetTheme { get { return "Cool"; } }
Setting the theme dynamically based on user input is not quite so straightforward. The problem lies in the fact that the theme needs to be set in the PreInit event of the page, which is before any of the controls have had their values loaded from the view state or the form input data. For example, imagine a page with a drop-down list by which the user can select the theme for the page. The list would have an event handler that would process the user's theme selection, but unfortunately you cannot set the page theme in this event handler. It has to be set before the postback event handling in the page's PreInit event. Yet during the PreInit event, you cannot yet know what the user selected!
The solution to this conundrum is that you need some way to store the user's theme selection in a way that is available to the PreInit event, and then reload the page so that the PreInit event is invoked again. How then can you store the user's theme selection? Later in Chapter 14, you will learn about the user profile system, which could be used for this problem. An alternative we will use here is to use ASP.NET session state (refer to Chapter 12 for more detail).
Session state allows you to store and retrieve values for a browser session as the user moves from page to page in the site. Recall that HTTP is a stateless protocol. This means that a Web server treats each HTTP request for a page as an independent request and retains no knowledge of code-behind data members or form values used during previous requests. ASP.NET session state provides a way to identify requests received from the same browser during a limited period of time and provides the ability to persist values for the duration of that session.
ASP.NET session state can be accessed from an ASP.NET Web Form via the Session property of the Page base class. This property references an object collection that is indexed by name. When you save an object in the session collection, you identify it by using a name, which can be any text string that you want. You can thus use session state to save the user's theme choice in the list event handler, as shown here.
protected void drpThemes_SelectedIndexChanged(object o, EventArgs e) { // Save theme name in session. You identify this value // using "theme" but you could use any name Session["theme"] = drpThemes.SelectedItem.Text; // Re-execute page so PreInit event can be re-triggered Server.Transfer(Request.Path); }
The PreInit event handler can now retrieve the user's choice using the following.
protected void Page_PreInit(object sender, EventArgs e) { // Retrieve theme from session string theme = (string)Session["theme"]; // Must make sure that session exists if (theme != null) { // Set the page theme this.Page.Theme = theme; } else { // Set default theme if nothing in session because no theme // has been selected yet (or the session has timed-out) this.Page.Theme = "Cool"; } }
Notice that retrieving the theme name from the session collection requires a casting operation (from object to string). Also, the method cannot assume that the session collection actually contains a theme name. It might be the first time the page has been requested and thus the theme does not exist yet in the session. Alternately, the session may have timed out and thus is empty. For this reason, any time you retrieve an item from session state, you must always verify that the item does exist, typically by comparing it to null.
Creating a Sample Page with Two Themes
To finish our coverage of themes, let's examine an example that demonstrates the dynamic selection of themes. The example page contains a drop-down list that allows the user to select one of two different themes to be applied to that page. The markup for this page is shown in Listing 6.3. Notice that the page contains no appearance markup, only structured content. All formatting is contained in the theme skins and CSS files.
Listing 6.3. ThemeTester.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ThemeTester.aspx.cs" Inherits="ThemeTester"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Themes Tester</title> </head> <body> <form id="form1" runat="server"> <div id="container"> <div id="header"> <asp:Image runat="server" ID="imgLogo" SkinID="logo"/> </div> <div id="menu"> <asp:BulletedList ID="blstSample" runat="server" > <asp:ListItem Value="">Home</asp:ListItem> <asp:ListItem Value="">Products</asp:ListItem> <asp:ListItem Value="">About</asp:ListItem> </asp:BulletedList> </div> <div id="content"> <h1>Technical Books</h1> <p> Microsoft. Cisco. IBM. Hewlett Packard. Intel. Adobe. Macromedia. There's a reason the top technology companies choose Pearson Education as their publisher partners. Publishing over a thousand new titles each year, much of our content is also available online - so busy professionals and students can access it from any computer, day or night. </p> <div id="register"> <fieldset> <legend>Contact Us</legend> <asp:Label ID="labName" runat="server" AssociatedControlID="txtName" Text="<u>N</u>ame" /> <br /> <asp:TextBox ID="txtName" runat="server" AccessKey="n" TabIndex="1" /><br /> <asp:Label ID="labEmail" runat="server" AssociatedControlID="txtEmail" Text="E<u>m</u>ail" /><br /> <asp:TextBox ID="txtEmail" runat="server" AccessKey="m" TabIndex="2" /><br /> <asp:Label ID="labZip" runat="server" AssociatedControlID="txtZip" Text="<u>Z</u>ip" /><br /> <asp:TextBox ID="txtZip" runat="server" AccessKey="z" TabIndex="3" /><br /> <asp:Button ID="btnSubmit" runat="server" Text="Send" TabIndex="4" AccessKey="d" /> </fieldset> </div> <p> <asp:DropDownList ID="drpThemes" runat="server" AutoPostBack="True" OnSelectedIndexChanged= "drpThemes_SelectedIndexChanged" > <asp:ListItem>Pick a theme</asp:ListItem> <asp:ListItem>Cool</asp:ListItem> <asp:ListItem>Professional</asp:ListItem> </asp:DropDownList> </p> </div> </div> </form> </body> </html>
The markup contains a drop-down list that allows the user to select the theme. The code-behind for the page performs this processing, as shown in Listing 6.4.
Listing 6.4. ThemeTester.aspx.cs
public partial class ThemeTester : System.Web.UI.Page { /// <summary> /// Set the page's theme based on the user's selection /// </summary> protected void Page_PreInit(object sender, EventArgs e) { // Retrieve theme from session string theme = (string)Session["theme"]; // Must make sure that session exists if (theme != null) { this.Page.Theme = theme; } else { // Set default theme this.Page.Theme = "Cool"; } } /// <summary> /// Process the user's theme choice by saving it in /// session state /// </summary> protected void drpThemes_SelectedIndexChanged(object sender, EventArgs e) { // Ignore the first item ("pick a theme") in list if (drpThemes.SelectedIndex != 0) { // Save theme in session Session["theme"] = drpThemes.SelectedItem.Text; // Re-request page Server.Transfer(Request.Path); } } }
By keeping your markup devoid of appearance formatting, it can be quite radically transformed by your themes. This example has two different themes: one called Cool and the other called Professional. Figure 6.7 illustrates how this page appears in its two themes (the Cool theme is the one on the right with two layout columns).
Figure 6.7 ThemeTester.aspx with its two themes
The skins for these two themes (shown in Listings 6.5 and 6.6) are quite similar as well as quite straightforward. Notice that both skins delegate the actual formatting to each theme's CSS file.
Listing 6.5. Cool.skin
<asp:Image runat="server" ImageUrl="images/cool.gif" AlternateText="Masthead Image" SkinID="logo" /> <asp:BulletedList runat="server" BulletStyle="CustomImage" BulletImageUrl="images/bullet.gif" DisplayMode="HyperLink" /> <asp:TextBox runat="server" CssClass="txtBox" /> <asp:Button runat="server" CssClass="myButton"/>
Listing 6.6. Professional.skin
<asp:Image runat="server" ImageUrl="images/prof.gif" AlternateText="Masthead Image" SkinID="logo" /> <asp:BulletedList runat="server" BulletStyle="NotSet" DisplayMode="HyperLink"/> <asp:TextBox runat="server" CssClass="txtBox" /> <asp:Button runat="server" CssClass="myButton"/>
As mentioned, the majority of the heavy lifting for these themes is done by the CSS file for each theme. Listings 6.7 and 6.8 contain the content of each CSS file. The two CSS files are similar in that they use class, ID, and descendant selectors to specify the text formatting and the positioning for the different elements in your Web Form. The professionalStyles.css file is the easier of the two to understand because it contains no positioning styles. The only complexity in this style sheet is how the unordered list items are turned into a horizontal menu—that is, by turning off the bullets (list-item: none), keeping the list items together on a single line (display: inline), and by styling the link and hover pseudo elements.
Listing 6.7. professionalStyles.css
body { background-color: White; font-size: small; } h1 { margin-top: 3em; font-family: Georgia, Times New Roman, serif; font-size: 1.2em; padding-bottom: 4px; border-bottom: 1px solid #cc0000; color: #666666; text-transform: uppercase; letter-spacing: 0.4em; } p { font: normal .8em/2.0em Verdana, Arial , sans-serif; } #container { position: relative; top: 30px; left: 30px; width: 400px; } /* style the list as a horizontal menu */ #menu { font-family: Verdana, Arial , sans-serif; font-size: 0.9em; } #blstSample { margin-left: 0; padding-left: 0; white-space: nowrap; } #blstSample li { display: inline; list-style: none; text-transform: uppercase; } #blstSample a { font-weight: bold; padding: 3px 10px 3px 20px; margin-right: 2px; } #blstSample a:link, #blstSample a:visited { text-decoration: none; color: white; background-color: Gray; } #blstSample a:hover { color: #cc0000; border-bottom: 4px solid #cc0000; padding-bottom: 2px; text-decoration: none; background: url(images/bullet.gif) no-repeat 0 50%; background-color: white; } /* style the form elements */ fieldset { border: 1px solid #cc0000; padding: 1em; } .txtBox { border:1px solid #666666; margin: 0.2em 0 0.8em 0.2em; } .myButton { border:1px solid gray; color:#FFFFFF; background-color:#FF9900; font-size:1em; font-weight: bold; margin: 0.2em 0 0.8em 0.2em; } label { font: normal 0.8em Verdana, Arial , sans-serif; margin: 0.2em 0 0 0.2em; } legend { font-family: Georgia, Times New Roman, serif; font-size: 1em; font-weight: bold; color: #666666; text-transform: uppercase; letter-spacing: 0.2em; }
The coolStyles.css file is a bit more complex. It uses CSS positioning and margins to place the menu <div> and the content <div> into two separate columns. The menu <div> is floated to the left (float: left) of the subsequent content in the form (the content <div>) and given a specified width. The content <div> is given a left margin that places all of the content to the right of the menu <div>. One interesting feature of this CSS file is the use of the so-called Tan Hack to deal with a bug with Internet Explorer 6.0 and earlier. In this case, Internet Explorer is adding the content <div> left margin setting to the form's <input> elements (the TextBox and Button controls). To fix this problem, the style sheet uses the * html selector (which is only supported by IE) to override the correct margin settings and apply a negative margin. The rest of the CSS is simple text formatting.
Listing 6.8. coolStyles.css
body { background-color: #0A4581; font-size: small; } h1 { margin-top: 0; padding: 0.2em; font-family: Verdana, Tahoma, Helvetica, Arial, sans-serif; font-size: 1.2em; background-color: #ABC6EE; color: #666666; } P { font: normal .9em/2.2em Verdana, Helvetica, Arial, sans-serif; } /* style each of the principle div elements */ #container { width: 90%; margin: 10px auto; background-color: #ffffff; color: #333333; border: 1px solid gray; } #content { margin-left: 16em; border-left: 1px solid gray; padding: 1em; } #header { padding: 0; height: 92px; background: url(images/header_background.gif); } /* style the list as a vertical list of links */ #menu { float: left; color: black; width: 9em; margin: 1em; padding: 0.5em; border: 1px solid #000; font-family: Verdana, Tahoma, Helvetica, Arial, sans-serif; background-color: #ABC6EE; } #blstSample li { padding-top: 0.4em; } #blstSample a:link, #blstSample a:visited { text-decoration: underline; color: blue; } #blstSample a:hover { color: #cc0000; border-bottom: 3px solid #cc0000; text-decoration: none; } /* style the form elements */ #register { font-family: Verdana, Tahoma, Helvetica, Arial, sans-serif; } fieldset { border: 1px solid #666666; padding: 1em; } legend { font-size: 1em; font-weight: bold; color: #666666; text-transform: uppercase; letter-spacing: 0.2em; background-color: #ABC6EE; border: 1px solid #666666; } .txtBox { border:1px solid #666666; margin: 0.3em 0 0.8em 0.2em; } /* IE 6 adds the #content margin to the text box and button so we must give the text boxes and buttons a negative margin using a CSS format only understood by IE (which will override the margin set in the .txtBox rule just above). */ * html .txtBox { margin-left: -16em; } * html .myButton { margin-left: -16em; } label { font: normal 0.8em Verdana, Arial , sans-serif; }
One other feature worth mentioning about these two themes is their use of em units for sizing. By avoiding the use of pixels, these two layouts still work even if users change the display size of the text in their browser (see Figure 6.8). We could have achieved a similar result as well by using percent units (e.g., font-size: 90%;) for sizing.
Figure 6.8 Cool theme with different user-selected font sizes
The em unit is the size of the character box for the element's parent. If no other font sizes are specified in other containers, a font size of 1em is equivalent to the default font size (determined by the browser). The principle used in these styles is to set the default font size in the body to small, and then make all other dimensions relative to whatever the browser sets for the small font size.
body { font-size: small; } h1 { font-size: 1.2em; } p { font-size: 0.8em; }
If the browser's font size for small is 14px, the h1 is displayed at around 17px (14 x 1.2) and the p is displayed at around 11px (14 x 0.8). If the user increases the size of her text through browser preferences, so that small is now 18px, the h1 and the p are 21 and 14 pixels, respectively (18 x 1.2 and 18 x 0.8).