Part 2 of this series describes the team's techniques for understanding the nature of the audience for the Web site of our fictitious company, International Business Council (IBC). Using various techniques, such as paper prototypes, we tested page layouts and user interaction. This stage provided a basis for combining visual elements with the general layout, allowing us to start creating basic styling to present the information. In this and the next article, you'll learn about our approach to constructing a custom theme for the IBC Web site.
The information in this article should not be interpreted as a rigid set of development guidelines, but as a place to start when creating the structure for your own Web site.
In this article, follow along as we create a theme in Drupal and focus on methods that helped to enable:
- Templates for content fragments
- An independent home page layout
- Web site section identification
- Dynamically updating navigation
- Catering to nontraditional browsers
- Contextually placed actions
- Highlighted content areas
While many of these ideas are based on existing practices and current standards-based Web design, we hope this article gives you ideas to enhance the way you approach content structure within the context of Drupal.
When we started creating a custom theme, we weren't aware of many of the features available. We often started with a quick and dirty solution, only to find later that there was a much better way of doing the same thing. We're still learning. This is a powerful concept since you can usually assemble a Web site "look" very quickly and refine it as you become more familiar with Drupal.
After we discuss our methods, you'll learn about our rationale as we moved from initial solutions to better, more systematic approaches.
Many books and Web sites talk to the current trends of standards-based Web design. An essential part of this philosophy is the proper application of XHTML and cascading style sheets (CSS). The flexible nature of the Drupal theming system allows Web site designers to easily adopt these standards.
Using a standards-based approach to the structure and style of the IBC Web site let us create a robust design that:
- Works across modern Web browsers, screen readers, and wireless devices
- Reduces the amount of time for a page to be downloaded and rendered by the browser
- Allows a flexible layout for users to increase or decrease the size of the browser window or font size
- Creates an acceptable printed rendition of any content on the Web site
- Allows content to degrade gracefully when displayed in older browsers
Our approach to creating the IBC Web site embraces standards-based Web design, but it is not the aim of this article to explain those standards. Now, on with creating a new custom theme.
To start our new theme, the team copied the Bluemarine directory inside the themes directory and renamed it ibc. This is a default phptemplate engine theme that comes with Drupal and contains the basis on which you can create your own custom theme. The new directory is shown in our Eclipse environment in Figure 1.
Figure 1. Navigator view, new IBC theme directory
Other theme engines are available. By definition, if you use .tpl.php files in your theme directory, Drupal will know to use the phptemplate engine. If you use .xtmpl files, Drupal will know to use the xtemplate engine. You could also use a .theme file in which you can override any PHP theme functions. To read more on this topic, see the Drupal theming overview.
To make this the active theme for our Web site, enable this theme through the admin/themes page. Drupal lists all themes found in the Themes directory. The ibc theme can be enabled and selected as the default, as shown in Figure 2.
Figure 2. Selecting IBC theme
The thumbnail image representing a screen shot of the look of the theme is controlled by the screenshot.png file inside the themes directory. You can create your own image for your theme, but probably only when you are ready with a look you want to present.
The Configure tab in Figure 2 lets the administrator change settings for themes in general, or for a particular theme. For a specific theme, certain content elements, provided by Drupal to be used by a theme, can be enabled or disabled.
Figure 3 lists a number of the content elements under the Configure tab in the Toggle display group that can be enabled or disabled. The mission statement is one element that is defined by the administrator in the General Settings of the admin/settings page. The theme engine will then deliver this string of text, if enabled, as a variable ($mission) to the page template (described in Listing 1). It is up to the theme designer to decide how to render these page elements, or not, when creating a custom theme.
Figure 3. Configuration page for the IBC theme
Rather like painting a room, much of the work is in the preparation. The more you do to fill in the holes and cracks, then sand it, the easier it is to paint -- and the better the room looks. So, before we style the Web site, we want to ensure that the content is well structured. The rest of this article describes how we build the XHTML in our theme so that styling with CSS is relatively easy.
The basic layout of your Web site is controlled by the page.tpl.php file in the theme directory you created in Figure 1. Here we can start to define the skeleton structure that will wrap our content fragments defined by the node template files described later. Listing 1 shows how we started a layout in the page.tpl.php file. We'll be referring back to this as we talk more about the basic content structure for the IBC theme.
Listing 1. A basic page layout in page.tpl.php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
lang="<?php print $language ?>;"
xml:lang="<?php print $language ?>">
<head>
<title><?php print ( $is_front ? "Home | International Business Council (IBC)"
: $head_title ); ?></title>
<?php print $head ?>
<link rel="stylesheet" type="text/css" media="screen"
href="themes/ibc/c/screen.css" />
</head>
<body>
<div id="wrap">
<div id="header">
<div id="banner">
<h1>International Business Council</h1>
</div>
<div id="nav_bar">
</div>
</div>
<div id="content">
<div class="tabs"><?php print $tabs ?></div>
<?php print $help ?>
<?php print $messages ?>
<?php if ($is_front) { : ?>
<?php print announcement_all(); ?>
<?php print discussions_all(); ?>
<?php else : ?>
<?php print $content; ?>
<?php endif; ?>
</div>
<div id="sidebar">
<?php print $sidebar_right; ?>
</div>
<div id="footer">
</div>
</div>
<?php print $closure;?>
</body>
</html>
|
It is good preparation to make sure you structure your XHTML so that all the elements you want to style are accessible using CSS selectors. Of course, this doesn't mean you should wrap everything in DIV or SPAN elements. Employing the ideas of semantic markup, we tried to use the proper XHTML elements for their proper purpose with the content. We use DIV and SPAN elements only where we need to define a logical separation in the content. In Listing 1, you should be able to identify the containers for the header, content, sidebar and footer.
We use ID attributes mainly for layout and class attributes, for repeated elements, or elements we know will be hard to identify using a CSS selector. In Listing 1 notice that the main logical blocks of content use identifiers to classify their purpose in the layout; for example, <div id="content">. <div class="tab"> uses a class attribute where you might imagine an ID attribute. This is an existing style value from the Bluemarine theme that we chose to keep.
To identify blocks of content generated by specific template files, we use a class attribute value that describes the content fragment in the containing element. For example, we determined that the content defined by announcement_compact.tpl.php would be wrapped in a DIV, whose class attribute value is announcement_summary. This approach proved to be a good debugging aid when trying to track down which template controlled which content. We will show this later when we discuss specific templates within a theme.
In Listing 1, you can see the use of variables with names such as $language, $is_front, $title_head, and so on. When the phptemplate engine calls a template file, it provides variables to this template, which can be defined within the Drupal settings page and enabled and disabled in the theme configuration as shown previously. The available variables can be inserted at suitable places in the template.
Regions
are areas into which themed blocks and node content are rendered by Drupal. $sidebar_$left, $sidebar_right, $content, $header, and $footer_message are variables that contain the content for the standard regions. Custom regions can also be defined using the theme _regions hook. These collections of themed content provide convenient containers that can then be positioned in the page layout by the theme designer.
As with most Web sites, the home page, or front page, usually presents a different layout when compared to other pages. Using the $is_front variable in the page.tpl.php file, we can make decisions about various displayed elements, such as the slight alteration of the title element, as shown in Listing 2.
Listing 2. Altering title of a page based on $is_front variable
<title><?php print ( $is_front ? "Home | International Business Council (IBC)"
: $head_title ); ?></title>
|
For the IBC Web site, we needed to show a summary list of announcements and the most recent entries in the forum. To distinguish the layout of this content from that of other pages, we used a conditional statement in the body of the page, like the one in Listing 3.
Listing 3. Altering content of a page based on $is_front variable
<?php if ($is_front) { : ?>
<?php print announcement_all(); ?>
<?php print discussions_all (); ?>
<?php else : ?>
<?php print $content ?>
<?php endif; ?>
|
This way, we control which content to present based on whether the page template was being used for the home page or not. If $is_front is true, we call the correct module functions, like those in the announcements module, to display a summary list of announcements. If $is_front is false, then the $content variable is displayed, which contains the collection of themed nodes that Drupal has assembled based on the node-building sequence described in an earlier article.
One of the requirements of the IBC site is to force users to authenticate before any content can be displayed. Typically, a closed community site or extranet site like this presents an unauthorized user with a login page instead of the home page.
Since we're talking about the page.tpl.php file, it may be useful to explain our initial solution to this problem. It doesn't take into consideration session expiration, and it's not particularly elegant, but it works. (We will describe a better method in a future article, but thought it might be interesting to show the initial solution here.)
The code in Listing 4 is placed before the DOCTYPE element in the page.tpl.php file shown in Listing 1. The $user->uid variable is only set when the current user is authenticated. We use this as a check to determine whether to include another template file to render the login page and then jump out of the page.tpl.php file. The login.tpl.php file renders the login page with a login form copied from the user login block.
Listing 4. Checking for user authentication or show the login page
<?php
global $user;
if (!$user->uid) {
include('login.tpl.php');
return;
}
?>
|
Maintaining the basic layout of your Web site page from one file is useful, but what happens when you want to identify different sections of your Web site? For instance, you might want to provide the user with visual clues of the current section. We used the URL to set a class value in the body element so that we could style the page appropriately. One way of doing this is to print the Drupal arg function straight into the class attribute value, as shown in Listing 5. However, this didn't give us the control we needed.
Listing 5. Adding a class value to the body element
<body class="<?php print arg(0); ?>" >
|
We resorted to using a simple switch statement in order to define the class value with more control. In Listing 6, the switch statement determines the value to use based on the first part of the URL path. This provided us with a CSS class we could use to vary the presentation of the page based on the section of the Web site being displayed.
Listing 6. Adding defined class value to the body element
<?php
switch(arg(0)) {
case 'workgroups':
$page_type = 'workgroups';
break;
case 'action_items':
$page_type = 'workgroups';
break;
case 'conferences':
$page_type = 'conferences';
break;
case 'agenda_items':
$page_type = 'conferences';
break;
case 'members':
$page_type = 'members';
break;
default:
$page_type = 'home';
}
?>
<body class="<?php print $page_type; ?> <?php print arg(0); ?>" >
|
Of course, we had clean URLS configured in the admin/settings page and modules for each section of the Web site that registered URLs with the Drupal menu system, so we could reliably detect sections of the site from the URL. Another way of doing this, which we'll talk about in a future article, is to use the taxonomy module to determine what section is being displayed.
The structure of our primary navigation needed to change based on the database content of the Web site. For example, under the Workgroups section, we needed to list only the active workgroups, and under the Conferences section we needed to list only those conferences from the last two years. Listing 7 shows the unordered list created to structure the content for the primary navigation based on these requirements. This is used within the nav_bar DIV, shown in Listing 1. You can see from Listing 7 that for the Workgroups and Conferences sections, a function from the workgroups and conference modules provide a list of items that are rendered as list items.
Listing 7. Structuring the primary navigation
<ul id="pri_nav">
<li id="t_home"><a href="" accesskey="1"
title="The IBC home page">Home</a></li>
<li id="t_workgroups"><a href="workgroups"
title="IBC Workgroups and status">Workgroups</a>
<ul>
<li><a href="workgroups" title="View all Workgroups">All Workgroups</a></li>
<?php
$workgroups = workgroup_nav_list();
foreach ($workgroups as $wg) : ?>
<li><a href="workgroups/<?php print $wg->nid; ?>"
title="Jump to $wg->title"><?php print $wg->title; ?></a></li>
<?php endforeach; ?>
</ul>
</li>
<li id="t_conferences"><a href="conferences/" title="IBC Conference
information">Conferences</a>
<ul>
<li><a href="conferences/" title="View all Conferences">All Conferences</a></li>
<?php
$conferences = conference_nav_list();
foreach ($conferences as $c) : ?>
<li><a href="conferences/<?php print $c->nid; ?>"
title="Jump to $c->title"><?php print $c->title; ?></a></li>
<?php endforeach; ?>
</ul>
</li>
<li id="t_members"><a href="members" title="IBC Members and contact
information">Members</a>
<ul>
<li><a href="members" title="All Members">All Members</a></li>
<li><a href="members/bycompany" title="By Company">By Company</a></li>
<li><a href="members/byconference" title="By Conference">By Conference</a></li>
<li><a href="members/byworkgroup" title="By Workgroup">By Workgroup</a></li>
<li><a href="members/workgroupleaders" title="Workgroup Leaders">Workgroup
Leaders</a></li>
<li><a href="members/abcstaff" title="IBC Staff">IBC Staff</a></li>
</ul>
</li>
</ul>
|
As you can see from Listing 7, additional nested lists are embedded into the list item elements for Workgroups and Conferences. These contain the dynamic content generated with the help of some PHP. The next article explains how we style this structure to make a drop-down menu for the primary navigation.
Content for nontraditional browsers
To adapt to situations where the IBC content may not be seen through a traditional browser, we included extra content structure for wider accessibility. We added access keys, links to certain major sections of the content layout, and headings to identify these major sections.
To document the access keys used within the Web site page layout, we added a container of explanatory text at the top of the page, just after the BODY element in Listing 1. Listing 8 below shows this block extra content structure.
Listing 8. Explanatory text for ALT keys placed just after the BODY element
<div id="access-info">
<p class="access" >The access keys for this page are:</p>
<ul class="access">
<li>ALT plus 1 links to the IBC home page.</li>
<li>ALT plus 2 links to the site map for the IBC web site.</li>
<li>ALT plus 3 skips to the start of the content on this page.</li>
<li>ALT plus 9 links to the contact page.</li>
</ul>
</div>
|
The implementation of the access keys, with the accesskey attribute, was added to the associated links. Listing 9 shows examples of how these are used.
Listing 9. Examples of access keys in the page template
<li><a href="sitemap" accesskey="2"
title="An index to the whole of this web site">Site map</a></li>
<li><a href="contact" accesskey="9"
title="Contact information for the IBC web site">Contact</a></li>
|
Listing 10 shows examples of how to insert extra content structure to identify major sections of the page layout and add a link to the start of the page content. Notice the class value access, which allows the CSS to hide these extra content structures for traditional browsers.
Listing 10. Examples of headings to help identify sections in the page template
<div class="access">
<p><a href="<?php print request_uri(); ?>#page-content"
accesskey="3">Jump to the start of the content on this page</a></p>
</div>
.
.
.
<h2 class="access">Start of main page navigation</h2>
.
.
.
<div id="content">
<h2 id="page-content" class="access">Start of the main page content</h2>
.
.
.
|
Of course other structural techniques, such as including alt attributes in an IMG element to help explain the use of the image, should be used. Placing a title attribute within an anchor element (<A>) can be achieved in Drupal by including it in the attributes array of the l function, as shown in Listing 11.
Listing 11. Adding a title attribute to a link
$links[] = l(t('Edit'),
"announcement/$node->nid/edit",
array('title' => t('Display the edit form for this announcement')));
|
As for accessibility within forms, Drupal makes sure form elements are preceded with LABEL elements when using the form API.
So, how do we customize the structure of the content rendered for these pieces of content inserted into the page template? We use the content fragment templates.
The phptemplate engine uses the node.tpl.php as a generic template for a theme designer to mold any generated node data into a content fragment. For sites that use multiple node types, theme designers can control the rendering of the data for a particular node type using a node-<node-type>.tpl.php template file.
In Part 6 of this series, you learned how our custom modules for the IBC Web site follow a pattern resulting in data being rendered in three distinct contexts: a detailed layout, a summary representation, and a block. Figures 4 and 5 show examples of these contexts, which are inserted into the page template. When the node-building sequence knows to build an announcement page from the URL, as shown in Figure 4, the various node fragments required in the page are assembled and themed by Drupal and then placed into the page template.
In Figure 4, the box marked A outlines the content fragment built by the node system using the node-announcement.tpl.php template (described in Listing 14) to structure the announcement data. Area C outlines the content built by the announcement module block hook using the announcement_block_list.tpl.php template to structure the block data.
Figure 4. Announcement page context, including detailed layout and announcement block layout
In Figure 5, the outlined content areas marked B show the announcement summary content fragments built by the announcement_all function in the announcement module using the announcement_compact.tpl.php template.
Figure 5. Home page context, including summary layouts and an announcement block layout
As shown in Figure 5, we used templates to separate the structure and styling of these contexts from the modules creating the data. To render the detail view we used the node-<node-type>.tpl.php template file. However, to create a template to theme the summary and block contexts in their own template files for each node type, we used a method described in Part 6.
When you theme data in Drupal, you use the theme(<theme_function_name>, <data>) function, where the theme_function_name is a string used to identify a theme function. Drupal uses a process of elimination to decide what function it should use to pass this data so that a themed output can be generated. Figure 6 shows this process.
Figure 6. Drupal method for finding a theme function
A module developer can thus override the default theme function with a phptemplate engine-specific theme function. Now, within this phptemplate theme function we use the _phptemplate_callback function to tell the Drupal theme system to use a template file constructed using the theme_function_name. Therefore, a theme call, such as the one in Listing 12, will use the template file announcement_compact.tpl.php.
Listing 12. Theming an announcement summary in announcement.module
$page_content[] = theme('announcement_compact', $announcement);
|
In Part 6, in the hook_nodeapi section, we discussed how to let module developers create <theme_function_name>-<node-type>.tpl.php for any theme_function_name. With this method you could use block-<node-type>.tpl.php files if you wanted to.
Let's take a look at what these templates might look like for our three contexts using the announcements module described in Part 6.
We use the node-announcement.tpl.php template file to structure the resulting content fragment for the full announcement. This content fragment appears on the page that displays a single announcement (Figure 4, section A). The Drupal node-building system passes the node object to this template supplying the variables that can be inserted into the XHTML structure.
To examine what these variables are, you can use the print_r function in the template, as shown in Listing 13. Save the template file and then view the Web page that contains the template. The contents of $node will be displayed. This function is a rudimentary, yet helpful, debugging tool when working with PHP.
Listing 13. Displaying the node object contents
<?php print print_r($node); ?>
|
In Listing 14 you can see how a full announcement is built. The PHP code used within a template is used exclusively to transform the data into a well-structured XHTML content fragment that is easily styled using CSS. (The styling will be described in more detail in the next article in the series.)
Listing 14. node-announcement.tpl.php template
<?php
global $user;
$published = format_date($node->publish_date,'custom','j M, Y');
$expires = format_date($node->expiration_date,'custom','j M, Y');
?>
<?php if ($page or $page == 0): ?>
<h2 class='category'>Announcement</h2>
<?php endif; ?>
<div class="announcement_detail">
<h3><?php print $title ?><?php print $links ?></h3>
<p class="date">Posted on <?php print $published; ?>
<?php
if ($user->uid == $node->uid or user_access('edit announcement')) {
if ($node->expiration_date < time()) print " and expired on ".$expires;
else print " and going to expire on ".$expires;
}
?>
</p>
<?php if ($node->abstract) : ?>
<div class="abstract"><?php print $node->abstract ?></div>
<?php endif; ?>
<?php if ($node->body) : ?>
<div><?php print $node->body ?></div>
<?php endif; ?>
<?php
if ($node->attachments != NULL) {
print filegallery_attachments_display($node);
}
?>
</div>
|
The listing begins by converting the publish and expiration dates to be used later in the template. It then prints out <h2 class='category'>Announcement</h2>, a small introductory title for this part of the content.
Next, a DIV element is presented as an effective container for the fragment and identified by a class attribute value called announcement_detail, as mentioned earlier. This gives us a class name with which to style the content within the fragment.
The title is then printed along with the $links content, generated by the announcement_link function. This displays what we call the "action links" beside the heading of the announcement. Rather than relying on the default Drupal tabbed interface that provides users with a way to apply actions to a node, we wanted a method of placing these actions (such as Add, Edit, and Delete) next to the content they affect, as shown in red in Figure 7.
Figure 7. Action links next to the announcement heading
You might have noticed that we present some extra information in the
Posted on
line. In Listing 14, a condition statement is used to display this extra expiration information. It tests to see whether the current user is the author of the announcement or has edit access privileges. The global user data object ($user) and the user_access function are used to accomplish this. We include it as an example of how to use different data objects and helper functions within a template file.
The remainder of the listing prints out the abstract, body, and any attachments to the announcement. The XHTML is structured so it is valid semantically as well as structurally. If a piece of data is not available, then its whole containing XHTML element is excluded from the resulting content fragment. For example, wrapping the DIV element that contains the abstract text within the PHP conditional statement, <?php if ($node->abstract) : ?>, makes sure this content is displayed only when $node->abstract exists.
We use the announcement_compact.tpl.php template file to structure the resulting content fragment for the announcement summary. This content fragment is used when listing multiple announcements, such as on the home page or announcements page, as shown in Figure 5 section B.
In Listing 15, we have an effective container for the object identified by a class attribute value of announcement_summary. You'll notice that there are two other possible class attribute value inclusions here: sticky and hl_yellow.
Listing 15. announcement_compact.tpl.php template
<?php global $user; ?>
<div class="announcement_summary<?php print ($node->sticky) ? " sticky" : ""; ?>
<?php print ($user->uid == $node->uid) ? " hl_yellow" : ""; ?>">
<h3>
<a href="<?php print $node->url ?>"
title="Read more about this announcement"><?php print $title ?></a>
<?php print $links ?>
</h3>
<p class="date">Posted on <?php print $published; ?>
<?php
if ($user->uid == $node->uid or user_access('edit announcement')) {
if ($node->expiration_date < time()) print " and expired on ".$expires;
else print " and going to expire on ".$expires;
}
?>
</p>
<?php if ($abstract) : ?>
<div class="abstract">
<?php print $abstract ?>
<a class="more" href="<?php print request_uri(); ?>/<?php print $node->nid ?>"
title="Read more">more »</a>
</div>
<?php endif; ?>
</div>
|
When editing an announcement, the Publishing options section of the form allows an administrator to set the sticky flag on a node. Using this in the announcement module, we can override the chronological order of the listed announcements and have sticky ones placed at the top. Here we identify sticky announcements with a class attribute value in the containing DIV element. The $node->sticky variable is passed through to this template in the phptemplate_announcement_compact function (shown in Part 6).
The hl_yellow class is included if the current user's ID is the same as the announcements associated user ID. This lets us highlight any announcements authored by the current user.
We use the announcement_block_list.tpl.php template file to structure the content fragment for the announcement block. This content fragment is used when listing the last few announcements in the right sidebar on every page, as shown in Figure 4 section C, and Figure 5 section C.
The template in Listing 16 customizes the data created in the announcement_block function. It builds a tabulated list of announcements for use in the announcement block content variable. Since the announcement block uses the default block layout, this could also be altered using the block.tpl.php template.
Listing 16. announcement_block_list.tpl.php template
<?php global $user; ?>
<?php $i = 1; ?>
<table cellspacing="0">
<tbody>
<?php foreach($announcements as $a): ?>
<tr class="<?php print ($i%2) ? "" : " alt"; ?>
<?php print ($a->uid == $user->uid) ? " hl_yellow" : "" ; ?>">
<td class="col1"><a href="/announcements/<?php print $a->nid; ?>"
title="Read more about '<?php print $a->title ?>'">
<?php print $a->title; ?></a></td>
<td class="col2">
<?php print format_date($a->publish_date,'custom','j M y'); ?></td>
</tr>
<?php $i++; ?>
<?php endforeach; ?>
</tbody>
</table>
<p class="more"><a href="announcements/">View all the Announcements »</a></p>
|
The code in this table is a little more complicated because it constructs a table row for each announcement. Each row contains two columns. The first column contains the announcement title, which is constructed as an XHTML anchor element that links to the announcement's detail page. The second column contains the publish date of the announcement. Another anchor element is built after the table to allow the user to link to the page that lists all the announcements. As with the summary layout, a class attribute value, hl_yellow, is included when an announcement is owned by the current user.
In Part 6 we describe how the $announcement_block_max_list_count variable is used to control how many announcements to display in the block. This logic is contained within the announcement_block function so that the proper number of announcements are passed to this template.
In Part 5 we mention the use of the template.php file. This file can contain theme functions that override existing theme functions that are applied globally across the theme, not just to specific node types. A simple example of this is the need to wrap the actions links (described above) with a container that helps us style these anchor elements to separate them visually from the rest of the content.
The /inc/theme.inc file provided with the Drupal distribution defines the theme_links function. This function is used to theme the $links variable that the phpengine passes to the template files. So, using the flow as described in Figure 6, we should be able to override this using a function called phptemplate_links. Listing 17 shows this function, which was placed in the template.php file.
Listing 17. Overriding the default theme_links function in the template.php file
function phptemplate_links($links, $delimiter = " ") {
return '<span class="action">'.implode($delimiter, $links).'</span>';
}
|
As you can see, this function simply wraps the links with a SPAN element identified with a class attribute.
In this article you learned how to start creating a new theme as well as methods to construct structured XHTML within the theme for our Web site. We described a few methods for structuring the content of the Web site, which include the overall page layout, the home page content, the primary navigation, and the identification of Web site sections.
Stay tuned for the next article, where you'll discover how the team styles the structured IBC Web site using CSS.
Learn
-
Learn more about this series, where the Internet Technology Group team describes a fictitious organization that requires a customized Web site that includes, among other things, document storage, discussion groups, specialized workgroups, conference scheduling, and schedule session descriptions.
-
The following are some useful resources on the Web to help with structuring and styling your Web site:
- Designing with Web Standards, 2nd Edition : The book by Jeffrey Zeldman that changed how Web sites are designed.
- Web Standard Solutions: The Markup and Style Handbook : by Dan Cederholm.
- CSS Mastery: Advanced Web Standards Solutions : by Budd, Moll & Collison is a guide to leading-edge CSS development.
- A List Apart: Loads of topics for people who make Web sites.
- CSS Discuss: A mailing list devoted to talking about CSS and ways to use it in the real world.
-
RSS feed for this series. (Find out more about RSS.)
-
Stay current with developerWorks technical events and webcasts.
-
Browse the
technology bookstore for books on these and other technical topics.
Get products and technologies
- Part 14 provides the complete announcement module download.
Discuss
- Participate in the discussion forum.
-
Participate in developerWorks
blogs and get involved in the developerWorks community.

Alister Lewis-Bowen is a senior software engineer in IBM's Internet Technology Group. He has worked on Internet and Web technologies as an IBM UK employee since 1993. Alister was brought to the U.S. to work on the Web sites for the IBM-sponsored sports events, then as senior Webmaster for ibm.com. He is currently helping create semantic Web prototypes.

Stephen Evanchik is a software engineer in IBM's Internet Technology Group. He has been a contributor to many open source software projects, the most notable being his IBM TrackPoint driver in the Linux kernel. Stephen is currently working with emerging semantic Web technologies.

Louis Weitzman is a senior software engineer in IBM's Internet Technology Group. For 30 years he has worked at the intersection of design and computation. He helped develop an XML, fragment-based content management system in use by ibm.com, and currently is involved with bringing the design process to emerging projects.

