Menu

A Confusion of Styles

January 28, 2004

John E. Simpson

Q: How do I style a custom element's content?

I want two elements (QUESTION and ANSWER) to be declared in an external DTD. The QUESTION element's data is to be displayed in red, and the ANSWER element's data is to be displayed in green. For this, an external stylesheet needs to be used. How do I include the DTD in my HTML document?

Here's the relevant code:

queans.dtd:
<?xml-stylesheet type="text/css" href="clr.css"?>
<!ELEMENT QUESTION (#PCDATA)>
<!ELEMENT ANSWER (#PCDATA)>

clr.css:
<STYLE>
QUESTION {font-family:arial;font-size:20pt;color:#ff0000}
ANSWER {font-family:arial;font-size:20pt;color:#00aa00}
</STYLE>

sample.htm:
<!DOCTYPE SYSTEM "queans.dtd">
<HTML>
<BODY>
<QUESTION>What is your favorite web site?</QUESTION>
<ANSWER>My favorite web site is www.xml.com.</ANSWER>
</BODY>
</HTML>

A: As you've no doubt discovered, if you open this sample.htm in your favorite browser, you'll see the content of the QUESTION and ANSWER elements all right but displayed in the browser's default font (family, size, and color). You can fix this, but you'll have to straighten out some major misunderstandings first.

To recap, you've got a DTD which defines a couple of elements; a Cascading Style Sheet which contains a STYLE element; and an HTML document which (via the DOCTYPE declaration) points to the DTD and includes both elements defined there, plus a couple of others (HTML and BODY). Among the numerous confusions at work here:

  • First, you don't need a DTD if all you want is to style an XML element's content when viewed in a browser. (I'll show you how to do this later.)
  • Second, you really can't customize an HTML document by adding non-HTML elements (such as QUESTION and ANSWER), using a DTD or by any other means. Browsers employ a variety of techniques to determine what sort of file or document they're asked to present to the user. (The simplest of these, also arguably the most common, is to look at the file's extension.) If a browser thinks it's reading an HTML document, it will display "known" HTML elements according to its default settings (for font, color, and so on) for that element -- unless overridden by a stylesheet. When it encounters an unknown element, however, all bets are off: it just displays the content in the same font, etc., used for plain old text (such as text contained in a p element). Thus, your sample.htm file isn't a true HTML document; it's not even an XHTML document, but a document employing a hybrid, HTML-like XML vocabulary.
  • Third, if for some other reason you want to use a DTD, it needs to spell out everything which can be encountered in a document conforming to the DTD. If a document referencing the DTD includes HTML and BODY elements, then those elements must be declared in the DTD.
  • Fourth, the association between a stylesheet and the content to be displayed is never made in a DTD, or anywhere else for that matter, but in the document where the content is found. Note that this is true for both XML documents -- including XHTML ones -- and garden-variety HTML ones.
    • If you're working with a non-(X)HTML vocabulary, use an xml-stylesheet processing instruction just like the one you've mistakenly placed in your DTD.
    • If you're working with (X)HTML, place the reference to the external stylesheet in a LINK element -- again, in the document where the content resides (sample.htm, in this case). (If you're using XHTML-with-an-X, all element and attribute names must be lowercase. Thus LINK becomes link, BODY becomes body, and so on.)
  • Finally, in a CSS stylesheet, there's never anything but the style specifications: you especially don't include anything that looks like XML or HTML (like the STYLE element you've placed in clr.css).

If contemplating all of this hasn't completely exhausted you, here are some alternative solutions to your problem.

Simple customized display of an XML document

With this approach, as I said, you don't need a DTD at all. Just point the XML document to the right stylesheet. Corrected versions of your documents would then look as follows:

clr.css:
QUESTION {font-family:arial;font-size:20pt;color:#ff0000}
ANSWER {font-family:arial;font-size:20pt;color:#00aa00}

sample.xml:
<?xml-stylesheet type="text/css" href="clr.css"?>
<HTML>
<BODY>
<QUESTION>What is your favorite web site?</QUESTION>
<ANSWER>My favorite web site is www.xml.com.</ANSWER>
</BODY>
</HTML>

Results when viewed in Mozilla:

Sample document (corrected) viewed in Mozilla

(The Internet Explorer display is identical, except for the browser "chrome" of course.)

Simple display of an XHTML document

This is a little more complex, but still simple. The stylesheet remains the same as above. In the document itself, as I said, change all uppercase element and attribute names to lowercase. Furthermore, while it's not absolutely essential in the almost-anything-goes world of browsers, you should formally associate your document with the XHTML vocabulary in two ways: (1) use a DOCTYPE declaration which points to the XHTML DTD of your choice, and (2) use an xmlns attribute -- that is, a namespace declaration -- to assert which of the document's elements are in the XHTML namespace. (Typically, all elements in an "XHTML" document are in the XHTML namespace, but this needn't strictly be the case.) And finally, of course, you must add a link element to connect your document to clr.css.

At this point, sample.htm will now resemble the following (assuming you want to use the XHTML "transitional" vocabulary), with the most significant changes in boldface:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/TR/1999/REC-html-in-xml">
<link rel="stylesheet" type="text/css" href="clr.css" />
<body>
<QUESTION>What is your favorite web site?</QUESTION>
<ANSWER>My favorite web site is www.xml.com.</ANSWER>
</body>
</html>

In this case, browser behavior diverges: Mozilla's display of the content matches that of the previous solution. Internet Explorer, however, displays the question and answer in the browser's default font (family, size, and color). The "problem," I think, is that IE still doesn't recognize your QUESTION and ANSWER elements as XHTML. (Understandably, I might add. This seems to be a more correct behavior than Mozilla's.)

The only way to fix this is to replace your QUESTION and ANSWER elements with true XHTML elements, differentiating between them using class attributes. Something like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/TR/1999/REC-html-in-xml">
<link rel="stylesheet" type="text/css" href="clr.css" />
<body>
<span class="QUESTION">What is your favorite web site?</span>
<span class="ANSWER">My favorite web site is www.xml.com.</span>
</body>
</html>

The implication of this change is that you must also change clr.css's selectors; here's a general solution:

.QUESTION {font-family:arial;font-size:20pt;color:#ff0000}
.ANSWER {font-family:arial;font-size:20pt;color:#00aa00}

(The dot preceding each selector associates that style with any element which has a class attribute with the indicated value.)

Now both browsers again behave identically.

Also in XML Q&A

From English to Dutch?

Trickledown Namespaces?

From XML to SMIL

From One String to Many

Getting in Touch with XML Contacts

Getting fancier...

I don't mean "fancier" in terms of the display; I mean it in terms of how to attain your objective. This solution builds on the previous one; it assumes that you truly do need those non-(X)HTML elements in your document, and you want to use the uppercase element names. It simplifies some things, such as sample.htm itself (which returns to something very like its original form). The complexity comes from the addition of a second stylesheet -- this one in XSLT. Your document would now look something like this:

<?xml-stylesheet type="text/xsl" href="clr.xsl"?>
<HTML>
<BODY>
<QUESTION>What is your favorite web site?</QUESTION>
<ANSWER>My favorite web site is www.xml.com.</ANSWER>
</BODY>
</HTML>

The XSLT stylesheet in question would specify the transformation of this simplified sample.htm into a result tree which more or less matches the version of sample.htm in the previous solution. Here's an XSLT stylesheet (just one of many approaches, depending on how rigorous you need it to be) to accomplish this:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/TR/1999/REC-html-in-xml">

<xsl:output method="xml"
version="1.0"
doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" />

<xsl:template match="HTML">
<html>
<link rel="stylesheet" type="text/css" href="clr.css" />
<xsl:apply-templates />
</html>
</xsl:template>

<xsl:template match="BODY">
<body><xsl:apply-templates /></body>
</xsl:template>

<xsl:template match="QUESTION | ANSWER">
<span class="{name()}"><xsl:apply-templates /></span>
</xsl:template>

</xsl:stylesheet>

(I'll leave readers to extract from this example those bits which most interest them.)

And how, you might wonder, do the browsers treat sample.htm now? Sadly, they ignore -- at least under Windows -- the result tree from this transformation; indeed they don't attend to the xml-stylesheet declaration at all. I suspect this is because of that operating system's stubborn reliance on filename extensions to determine how to treat the document: .htm (or .html) means the document is an (X)HTML document and damn the consequences. Accordingly, sample.htm displays exactly as it did way back at the beginning of this answer, in default fonts and colors.

(Rather than shake your head in misery at this point, you might try changing the extension to .xml. Test it in both browsers. You'll find something else to shake your head over.)

For what it's worth, if you run this most recent version of sample.htm through a standalone XSLT engine like Saxon, you'll see the result tree you need:

<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/TR/1999/REC-html-in-xml">
<link rel="stylesheet" type="text/css" href="clr.css"/>
<body>
<span class="QUESTION">What is your favorite web site?</span>
<span class="ANSWER">My favorite web site is www.xml.com.</span>
</body>
</html>

If you save this result tree to a separate file, the browsers handle it just fine (and identically).

You may find this last alternative -- even though it's in some ways the most correct, requiring the least modification to your original source document -- a bit out of reach for now. Don't despair. Just keep asking questions and doing research.