Extensible Stylesheets

XML Logo
XML Logo

The browser has a peculiar dimensionality that natively supports moving around complexity between different data and language domains. One of those domains is XML (Extensible Markup Language) and it comes practically in the form of RSS (Really Simple Syndication) Feeds, Atom Feeds, Site Maps, OPML (Outline Processor Markup Language) Outlines and other various XML representations.

XSLT (Extensible Stylesheet Language Transformations) is a See the XSL Transformations Version 3.0 specification. that transforms XML into different output formats. You can expose and transform big or tiny blobs of atom.xml, rss.xml, sitemap.xml, and opml.xml files into Think plain text or PDF (Portable Document Format) but that’s outside the scope of this article. presentation formats.

In some respects, XSLT is considered “dead” technology, but take the word dead with a X is dead, and Y killed it” is a common trope on the Internet. You can find articles and comments for any X of your choosing. of salt. It’s not uncommon to right click a site, and lo and behold see a DTD (Document Type Definition) for XHTML (EXtensible HyperText Markup Language) in its source generated from XML — you’d be surprised.

XSLT operates as an XML templating language and a rather verbose one at that. If you go deep enough, the verbosity gets unwieldy and like all programming shenanigans it’s a perpetual rabbit hole. Here’s my practical notes for working with XML and XSLT in a web context for adding style and presentation while maintaining a bit of sanity.

Formats, File Extensions, and MIME types

The XSLT transformations discussed here will be limited to XHTML output. Raw XML in the browser has no styles associated with it (example) so styles are added with XSLT (example).

The MIME (Multipurpose Internet Mail Extensions) type definition for XSLT is application/xslt+xml. A file extension ending in .xsl or .xslt is the commonly accepted and used form.

The mimetype definition for XHTML is application/xhtml+xml but it’s usually served using the text/html content type for browsers to assume HTML instead of XML parsing. XHTML has HTML vs XHTML is an epic and historic flame war. Think tabs vs. spaces, self–closing tags vs. non self–closing tags or any other versus trope you can imagine. from HTML which you can take a look at in this XHTML in a nutshell article.

XML Validation and Formatting

You can validate and check an XML document for well formedness using xmllint from the libxml2 Check your Linux distribution repositories. W3C (The World Wide Web Consortium) offers an online feed validation service, but an offline validator sets up a better feedback loop and is a lot more robust and If you’re behind a CGNAT like me, the Internet is effectively a captcha game. CaptchaNET™.

XML has multiple validation grammars in the form of schemas. RELAX NG (REgular LAnguage for XML Next Generation) is one of those schema language formats. Schema examples can be found in RFCs (Request for Comments) or in niche places around the web — for example here’s a RSS rng file, an ATOM rnc file, and an ATOM rng file. The catch is that these validation schema files may have differing use cases or may be out of spec due to time, but they’re still worth looking at.

RELAX NG has both a standard xml.rng syntax and a compact xml.rnc syntax. Offline validation with xmllint does not According to the xmllint manual it supports RELAX NG, WXS (W3C XML Schema), and Schematron. rnc compact schema syntax — but rng works. Schema As seen in this blog post on validating ATOM feeds locally. between rnc and rng can be achieved with the Java program trang (usually goes by the name jing-trang in package repositories).

Trang converts between different schema languages for XML. RELAX NG (XML syntax), RELAX NG compact syntax, XML 1.0 DTDs and W3C XML Schema (WXS).

In my case, and maybe yours, it’s easier to run trang on an already well specified and well formed XML document. This produces a basic rng schema file for validation and adding more rules.

trang rss.xml rss.rng
trang atom.xml atom.rng
trang opml.xml opml.rng

Validate XML using the rng file with xmllint and the --relaxng flag. The --noout flag disables printing the output to the command line.

$ xmllint --noout --relaxng rss.rng rss.xml
rss.xml validates

If it fails to validate it will return the error message defined by the schema’s grammar.

$ xmllint --noout --relaxng rss.rng rss.xml
rss.xml:25: element description: Relax-NG validity error : Did not expect element description there
rss.xml fails to validate

Pretty print XML with --pretty 1 for basic formatting or --pretty 2 for “one attribute per line” white space formatting.

xmllint --pretty 1 rss.xml
xmllint --pretty 2 rss.xml

Stylesheet Processing and Validation

The command line XSLT processor xsltproc can be used to process stylesheets offline and works only on stylesheets up to version 1.1. If using xsltproc as a validation tool for xsl files, you’ll have to downgrade the version declaration from version 3.0 to version 1.1 and Not that it matters much — you’ll find that version 1.0 is the version that that most browsers support. a few features.

xsltproc rss.xsl
If nothing returns the xsl file is validated
xsltproc rss.xsl rss.xml
Transform rss.xml using rss.xsl. The data transforms from XML → XHTML

Other processors like Xalan–Java supports XSLT up to version 1.0 and Saxon up to version 3.0.

Stylesheet Boilerplate and Transformations

Below is one variation of a stylesheet that transforms XML to XHTML. A typical XHTML document skeleton is embedded within along with XSLT elements for processing and transformation.

<?xml version="1.0" encoding="utf-8"?>
  <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml">
        <title>XHTML Document</title>
          content="text/html; charset=utf-8"
          content="width=device-width, initial-scale=1, maximum-scale=1"
A basic template for transforming RSS, OPML, ATOM → XHTML

In the above, namespace attributes in the form xmlns:itunes extend the document. You could think of them as imports for extending features and avoiding naming conflicts. The URL points to the “allowed” Specs are meant to be broken after all. specified by the namespace.

For example, the Atom Activity Streams namespace could be added under xmlns:activity and extend the stylesheet with an understanding of Activity Streams related vocabulary. Namespaces can also be used to extend processing instructions like xmlns:xsl for XSLT processors that support them.

Namespace in the XSLT stylesheet
Somewhere in an XML document

Drop the xsl stylesheet inside a XML document with the xml-stylesheet declaration and the browser handles the rest.

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<?xml-stylesheet href="/rss.xsl" type="text/xsl"?>

XSLT works in conjunction with XPath (the XML Path Language) and is somewhat similar to CSS (Cascading Style Sheets) selectors. Command line programs like xmlstarlet make use of XPath expressions for selecting data from parts of an XML document.

<xsl:value-of select="/rss/channel/atom:link[@rel='previous']/@href"/>

The XPath expression from the select attribute above gets the href value from the <link> tag in the atom namespace which is equal to https://example.com/page/2/rss.xml.

<atom:link rel="next" href="https://example.com/page/2/rss.xml" />

If you’re familiar with CSS, then you’re in luck. Cheat sheets for XPath are everywhere across the Internet in a “CSS to XPath” format. Test expressions locally with xsltproc, xmlstarlet or an online Xpath expression test bed.

rss > channel > link[rel="previous"][href] {
  display: inline;

Value selections, for loops, and, switch statements are the more commonly used XSLT elements.

Attributes and Value Selection

Create attributes with the xsl:attribute element. The attributes are added to the parent tag. Select values with the xsl:value-of element.

  <xsl:attribute name="href">
    <xsl:value-of select="/rss/channel/atom:link[@rel='next']/@href"/>

<!--  Output: <a href="https://example.com/page/2/rss.xml"></a> -->
Anchor attribute selection
  <xsl:attribute name="alt"><xsl:value-of select="/rss/channel/category"/></xsl:attribute>
  <xsl:attribute name="title"><xsl:value-of select="/rss/channel/category"/></xsl:attribute>
  <xsl:attribute name="src"><xsl:value-of select="/rss/channel/image/url"/></xsl:attribute>

<!--  Output: <img alt="image" title="image" src="/image"></img> -->
Image attribute selection

For Each

A typical for each construction executes over a range of XML tags with the xsl:for-each element.

<xsl:for-each select="/rss/channel/item">
    <xsl:value-of select="title" />

Switch Statements

A switch statement construction is executed with a combination of the xsl:choose, xsl:otherwise, and xsl:when elements. The test attribute on xsl:when contains the condition.


  <xsl:when test="/rss/channel/atom:link[@rel='previous']/@href">
    <xsl:attribute name="href">
      <xsl:value-of select="/rss/channel/atom:link[@rel='previous']/@href"/>

    <xsl:attribute name="href">/</xsl:attribute>


View the XSLT elements and function reference for the complete list of instructions.


There you have it — a basic overview and approach to working with XML, XSLT, and XHTML.

30 November 2022 — Written
30 November 2022 — Updated
Thedro Neely — Creator
extensible-stylesheets.md — Article

More Content


Web Ring



  1. https://thedroneely.com/git/
  2. https://thedroneely.com/
  3. https://thedroneely.com/posts/
  4. https://thedroneely.com/projects/
  5. https://thedroneely.com/about/
  6. https://thedroneely.com/contact/
  7. https://thedroneely.com/abstracts/
  8. https://ko-fi.com/thedroneely
  9. https://thedroneely.com/tags/xml/
  10. https://thedroneely.com/posts/extensible-stylesheets/#isso-thread
  11. https://thedroneely.com/posts/rss.xml
  12. https://thedroneely.com/images/extensible-stylesheets.png
  13. https://developer.mozilla.org/en-US/docs/Web/XSLT
  14. https://www.w3.org/TR/xslt-30/
  15. https://lists.w3.org/Archives/Public/public-forms/2013Oct/0013.html
  16. https://thedroneely.com/posts/extensible-stylesheets/#formats-file-extensions-and-mime-types
  17. https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/XHTML
  18. https://thedroneely.com/post/rss.xml
  19. https://micro.thedroneely.com/m/tdro/rss.xml
  20. https://www.w3.org/TR/xslt20#xslt-mime-definition
  21. https://blog.whatwg.org/xhtml5-in-a-nutshell
  22. https://thedroneely.com/posts/extensible-stylesheets/#xml-validation-and-formatting
  23. https://man.archlinux.org/man/xmllint.1
  24. https://repology.org/project/libxml2/versions
  25. https://repology.org/repositories/statistics
  26. https://validator.w3.org/feed/
  27. https://en.wikipedia.org/wiki/Carrier-grade_NAT
  28. https://relaxng.org/
  29. https://www.w3.org/2002/09/rss-rng/rss.rng
  30. https://gist.github.com/tommorris/3725394#file-atom-rnc
  31. https://gist.github.com/tommorris/3725394#file-atom-rng
  32. https://relaxng.org/compact-tutorial-20030326.html
  33. https://www.w3.org/XML/Schema
  34. https://www.schematron.com/
  35. https://cweiske.de/tagebuch/atom-validation.htm
  36. https://en.wikipedia.org/wiki/Java_%28programming_language%29
  37. https://relaxng.org/jclark/trang.html
  38. https://repology.org/project/jing-trang/versions
  39. https://thedroneely.com/posts/extensible-stylesheets/#code-block-3e0ecb3
  40. https://thedroneely.com/posts/extensible-stylesheets/#code-block-db47cac
  41. https://thedroneely.com/posts/extensible-stylesheets/#code-block-0fdc249
  42. https://thedroneely.com/posts/extensible-stylesheets/#code-block-c359f49
  43. https://thedroneely.com/posts/extensible-stylesheets/#stylesheet-processing-and-validation
  44. https://man.archlinux.org/man/xsltproc.1.en
  45. https://www.w3.org/TR/xslt-10/
  46. https://thedroneely.com/posts/extensible-stylesheets/#code-block-4da9f02
  47. https://thedroneely.com/posts/extensible-stylesheets/#code-block-a96b607
  48. https://xml.apache.org/xalan-j/
  49. https://www.saxonica.com/documentation11/index.html#!using-xsl/xslt30
  50. https://thedroneely.com/posts/extensible-stylesheets/#stylesheet-boilerplate-and-transformations
  51. https://thedroneely.com/posts/extensible-stylesheets/#code-block-be0c4a6
  52. https://en.wikipedia.org/wiki/XML_namespace
  53. https://activitystrea.ms/specs/atom/1.0/
  54. https://www.w3.org/TR/activitystreams-core/
  55. https://thedroneely.com/posts/extensible-stylesheets/#code-block-b5f1fb0
  56. https://thedroneely.com/posts/extensible-stylesheets/#code-block-de8dfab
  57. https://www.w3.org/TR/1999/REC-xpath-19991116/
  58. https://man.archlinux.org/man/xmlstarlet.1.en
  59. https://thedroneely.com/posts/extensible-stylesheets/#code-block-60ec880
  60. https://thedroneely.com/posts/extensible-stylesheets/#code-block-c680eed
  61. https://devhints.io/xpath
  62. https://man.archlinux.org/man/xmlstarlet.1
  63. http://www.whitebeam.org/library/guide/TechNotes/xpathtestbed.rhtm
  64. https://thedroneely.com/posts/extensible-stylesheets/#code-block-4c4b0f0
  65. https://thedroneely.com/posts/extensible-stylesheets/#attributes-and-value-selection
  66. https://thedroneely.com/posts/extensible-stylesheets/#code-block-1fdecd5
  67. https://thedroneely.com/posts/extensible-stylesheets/#code-block-92872aa
  68. https://thedroneely.com/posts/extensible-stylesheets/#for-each
  69. https://thedroneely.com/posts/extensible-stylesheets/#code-block-657e459
  70. https://thedroneely.com/posts/extensible-stylesheets/#switch-statements
  71. https://thedroneely.com/posts/extensible-stylesheets/#code-block-e195613
  72. https://developer.mozilla.org/en-US/docs/Web/XSLT/Element
  73. https://thedroneely.com/posts/extensible-stylesheets/#conclusion
  74. https://www.thedroneely.com/posts/extensible-stylesheets.md
  75. https://thedroneely.com/posts/extensible-stylesheets/#more-content
  76. https://thedroneely.com/posts/making-web-pages/
  77. https://thedroneely.com/posts/a-few-links/
  78. https://thedroneely.com/projects/personal-portfolio/
  79. https://git.sr.ht/~sircmpwn/openring
  80. https://thedroneely.com/posts/extensible-stylesheets/#web-ring
  81. https://drewdevault.com/2022/11/12/In-praise-of-Plan-9.html
  82. https://drewdevault.com/
  83. https://mxb.dev/blog/the-indieweb-for-everyone/
  84. https://mxb.dev/
  85. https://www.taniarascia.com/simplifying-drag-and-drop/
  86. https://www.taniarascia.com/
  87. https://thedroneely.com/posts/extensible-stylesheets/#comments
  88. https://thedroneely.com/sitemap.xml
  89. https://thedroneely.com/index.json
  90. https://thedroneely.com/resume/
  91. https://gitlab.com/tdro
  92. https://github.com/tdro
  93. https://codeberg.org/tdro
  94. https://thedroneely.com/analytics
  95. https://thedroneely.com/posts/extensible-stylesheets/#
  96. https://creativecommons.org/licenses/by-sa/2.0/
  97. https://thedroneely.com/git/thedroneely/thedroneely.com
  98. https://opensource.org/licenses/GPL-3.0
  99. https://www.thedroneely.com/