[yelp-xsl/tmpl: 1/6] Put some CSS in separate tmpl files with simple substitutions




commit f26a7f86b10197d011f0084765564d332e19e14f
Author: Shaun McCance <shaunm redhat com>
Date:   Fri Oct 16 11:21:13 2020 -0400

    Put some CSS in separate tmpl files with simple substitutions
    
    This will make it a lot easier for people to work on the theming without
    digging into XSLT files.

 configure.ac                      |   1 +
 doc/yelp-xsl/C/db2html.page       |   1 +
 doc/yelp-xsl/C/db2xhtml.page      |   1 +
 doc/yelp-xsl/C/mal2html.page      |   1 +
 doc/yelp-xsl/C/mal2xhtml.page     |   1 +
 doc/yelp-xsl/C/tmpl.file.page     |  59 +++
 doc/yelp-xsl/C/tmpl.page          |  53 ++
 doc/yelp-xsl/C/tmpl.text.page     |  56 +++
 xslt/common/Makefile.am           |   3 +-
 xslt/common/css/Makefile.am       |   8 +
 xslt/common/css/core.css.tmpl     | 241 +++++++++
 xslt/common/css/elements.css.tmpl | 584 ++++++++++++++++++++++
 xslt/common/css/syntax.css.tmpl   |  51 ++
 xslt/common/html.xsl              | 994 +++-----------------------------------
 xslt/common/tmpl.xsl              | 162 +++++++
 xslt/dita/html/dita2xhtml.xsl     |   1 +
 xslt/docbook/html/db2xhtml.xsl    |   1 +
 xslt/mallard/html/mal2xhtml.xsl   |   1 +
 18 files changed, 1278 insertions(+), 941 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b4083651..5470bacc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -63,6 +63,7 @@ doc/yelp-xsl/Makefile
 po/Makefile.in
 xslt/Makefile
 xslt/common/Makefile
+xslt/common/css/Makefile
 xslt/common/domains/Makefile
 xslt/common/icons/Makefile
 xslt/docbook/Makefile
diff --git a/doc/yelp-xsl/C/db2html.page b/doc/yelp-xsl/C/db2html.page
index 947fd00e..6338afaf 100644
--- a/doc/yelp-xsl/C/db2html.page
+++ b/doc/yelp-xsl/C/db2html.page
@@ -6,6 +6,7 @@
   <link type="topic" xref="color" group="imports"/>
   <link type="topic" xref="icons" group="imports"/>
   <link type="topic" xref="html" group="imports"/>
+  <link type="topic" xref="tmpl" group="imports"/>
   <link type="topic" xref="utils" group="imports"/>
   <link type="topic" xref="db-chunk" group="imports"/>
   <link type="topic" xref="db-common" group="imports"/>
diff --git a/doc/yelp-xsl/C/db2xhtml.page b/doc/yelp-xsl/C/db2xhtml.page
index 7966de28..6be5e007 100644
--- a/doc/yelp-xsl/C/db2xhtml.page
+++ b/doc/yelp-xsl/C/db2xhtml.page
@@ -6,6 +6,7 @@
   <link type="topic" xref="color" group="imports"/>
   <link type="topic" xref="icons" group="imports"/>
   <link type="topic" xref="html" group="imports"/>
+  <link type="topic" xref="tmpl" group="imports"/>
   <link type="topic" xref="utils" group="imports"/>
   <link type="topic" xref="db-chunk" group="imports"/>
   <link type="topic" xref="db-common" group="imports"/>
diff --git a/doc/yelp-xsl/C/mal2html.page b/doc/yelp-xsl/C/mal2html.page
index 481f2699..ff738cb0 100644
--- a/doc/yelp-xsl/C/mal2html.page
+++ b/doc/yelp-xsl/C/mal2html.page
@@ -7,6 +7,7 @@
   <link type="topic" xref="icons" group="imports"/>
   <link type="topic" xref="html" group="imports"/>
   <link type="topic" xref="ttml" group="imports"/>
+  <link type="topic" xref="tmpl" group="imports"/>
   <link type="topic" xref="utils" group="imports"/>
   <link type="topic" xref="mal-gloss" group="imports"/>
   <link type="topic" xref="mal-if" group="imports"/>
diff --git a/doc/yelp-xsl/C/mal2xhtml.page b/doc/yelp-xsl/C/mal2xhtml.page
index 4ae4d1bc..47b08cab 100644
--- a/doc/yelp-xsl/C/mal2xhtml.page
+++ b/doc/yelp-xsl/C/mal2xhtml.page
@@ -7,6 +7,7 @@
   <link type="topic" xref="icons" group="imports"/>
   <link type="topic" xref="html" group="imports"/>
   <link type="topic" xref="ttml" group="imports"/>
+  <link type="topic" xref="tmpl" group="imports"/>
   <link type="topic" xref="utils" group="imports"/>
   <link type="topic" xref="mal-gloss" group="imports"/>
   <link type="topic" xref="mal-if" group="imports"/>
diff --git a/doc/yelp-xsl/C/tmpl.file.page b/doc/yelp-xsl/C/tmpl.file.page
new file mode 100644
index 00000000..5d4179b7
--- /dev/null
+++ b/doc/yelp-xsl/C/tmpl.file.page
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<page xmlns="http://projectmallard.org/1.0/"; style="xslt-template" id="tmpl.file">
+ <info>
+  <link type="guide" xref="tmpl" group="templates"/>
+  <link type="guide" xref="templates" group="tmpl"/>
+  <desc>Perform text substitutions on a file.</desc>
+  <revision version="40" date="FIXME" status="FIXME"/>
+ </info>
+ <title>tmpl.file</title>
+ <synopsis>
+  <title>Parameters</title>
+  <terms>
+   <item>
+    <title><code>$file</code></title>
+    <p>The filename of the file to process for substitutions.</p>
+   </item>
+   <item>
+    <title><code>$node</code></title>
+    <p>The node to create CSS for.</p>
+   </item>
+   <item>
+    <title><code>$direction</code></title>
+    <p>The directionality of the text, either <sys>ltr</sys> or <sys>rtl</sys>.</p>
+   </item>
+   <item>
+    <title><code>$left</code></title>
+    <p>The starting alignment, either <sys>left</sys> or <sys>right</sys>.</p>
+   </item>
+   <item>
+    <title><code>$right</code></title>
+    <p>The ending alignment, either <sys>left</sys> or <sys>right</sys>.</p>
+   </item>
+  </terms>
+ </synopsis>
+ <p>This template reads the file specified by the <code>$file</code> parameter and performs
+ text substitutions. Due to XSLT limitations, the file must be a well-formed
+ XML document. However, this template simply takes the string value of the
+ document, so it is sufficient to wrap the text in a dummy element and ensure
+ any <sys>&lt;</sys> and <sys>&amp;</sys> characters are escaped.</p>
+ <p>See <code xref="tmpl">tmpl</code> for information on the substitution syntax.</p>
+ <note style="plain">
+  <p>This template was added in version 40.</p>
+ </note>
+ <list style="compact">
+  <title>Calls Templates</title>
+  <item>
+   <p><link xref="l10n.align.end"/></p>
+  </item>
+  <item>
+   <p><link xref="l10n.align.start"/></p>
+  </item>
+  <item>
+   <p><link xref="l10n.direction"/></p>
+  </item>
+  <item>
+   <p><link xref="tmpl.text"/></p>
+  </item>
+ </list>
+</page>
diff --git a/doc/yelp-xsl/C/tmpl.page b/doc/yelp-xsl/C/tmpl.page
new file mode 100644
index 00000000..1715bc39
--- /dev/null
+++ b/doc/yelp-xsl/C/tmpl.page
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<page xmlns="http://projectmallard.org/1.0/"; type="guide" style="xslt-stylesheet" id="tmpl">
+ <info>
+  <link type="guide" xref="stylesheets" group="tmpl"/>
+  <desc>Perform simple substitutions in text files.</desc>
+  <revision version="40" date="FIXME" status="FIXME"/>
+ </info>
+ <title>Text Templates</title>
+ <p>This stylesheet contains templates to perform simple substitutions on text
+ and files containing text. The primary purpose of these templates is to allow
+ CSS and JavaScript to be maintained in separate files outside the XSLT, but
+ still allow those files to reference variables for things like color themes
+ and text directionality.</p>
+ <p>The substitution evaluates anything between <sys>{{</sys> and <sys>}}</sys>. Usually, this will
+ be a reference to a parameter or variable, but it can be any XPath expression.
+ For example, <sys>{{$color.fg}}</sys> will be replaced with the primary text color.</p>
+ <p>This syntax is similar to XSLT attribute value templates, except that it uses
+ double curly braces to avoid conflicts with the many curly braces used in CSS
+ and JavaScript files.</p>
+ <note style="plain">
+  <p>This stylesheet was added in version 40.</p>
+ </note>
+ <links type="topic" groups="imports" style="linklist">
+  <title>Imports Stylesheets</title>
+ </links>
+ <links type="topic" groups="includes" style="linklist">
+  <title>Includes Stylesheets</title>
+ </links>
+ <links type="topic" groups="params" style="linklist">
+  <title>Defines Parameters</title>
+ </links>
+ <links type="topic" groups="keys" style="linklist">
+  <title>Defines Keys</title>
+ </links>
+ <links type="topic" groups="templates" style="linklist">
+  <title>Defines Templates</title>
+ </links>
+ <links type="topic" groups="modes" style="linklist">
+  <title>Defines Modes</title>
+ </links>
+ <list style="compact">
+  <title>Calls Templates</title>
+  <item>
+   <p><link xref="l10n.align.end"/></p>
+  </item>
+  <item>
+   <p><link xref="l10n.align.start"/></p>
+  </item>
+  <item>
+   <p><link xref="l10n.direction"/></p>
+  </item>
+ </list>
+</page>
diff --git a/doc/yelp-xsl/C/tmpl.text.page b/doc/yelp-xsl/C/tmpl.text.page
new file mode 100644
index 00000000..4f4f5901
--- /dev/null
+++ b/doc/yelp-xsl/C/tmpl.text.page
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<page xmlns="http://projectmallard.org/1.0/"; style="xslt-template" id="tmpl.text">
+ <info>
+  <link type="guide" xref="tmpl" group="templates"/>
+  <link type="guide" xref="templates" group="tmpl"/>
+  <desc>Perform text substitutions on some text.</desc>
+  <revision version="40" date="FIXME" status="FIXME"/>
+ </info>
+ <title>tmpl.text</title>
+ <synopsis>
+  <title>Parameters</title>
+  <terms>
+   <item>
+    <title><code>$text</code></title>
+    <p>The text to process for substitutions.</p>
+   </item>
+   <item>
+    <title><code>$node</code></title>
+    <p>The node to create CSS for.</p>
+   </item>
+   <item>
+    <title><code>$direction</code></title>
+    <p>The directionality of the text, either <sys>ltr</sys> or <sys>rtl</sys>.</p>
+   </item>
+   <item>
+    <title><code>$left</code></title>
+    <p>The starting alignment, either <sys>left</sys> or <sys>right</sys>.</p>
+   </item>
+   <item>
+    <title><code>$right</code></title>
+    <p>The ending alignment, either <sys>left</sys> or <sys>right</sys>.</p>
+   </item>
+  </terms>
+ </synopsis>
+ <p>This template performs text substitutions on the text in <code>$text</code>. It is called
+ by <code xref="tmpl.file">tmpl.file</code>, and it calls itself recursively after each substitution.</p>
+ <p>See <code xref="tmpl">tmpl</code> for information on the substitution syntax.</p>
+ <note style="plain">
+  <p>This template was added in version 40.</p>
+ </note>
+ <list style="compact">
+  <title>Calls Templates</title>
+  <item>
+   <p><link xref="l10n.align.end"/></p>
+  </item>
+  <item>
+   <p><link xref="l10n.align.start"/></p>
+  </item>
+  <item>
+   <p><link xref="l10n.direction"/></p>
+  </item>
+  <item>
+   <p><link xref="tmpl.text"/></p>
+  </item>
+ </list>
+</page>
diff --git a/xslt/common/Makefile.am b/xslt/common/Makefile.am
index 33ab76c9..60230754 100644
--- a/xslt/common/Makefile.am
+++ b/xslt/common/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = domains icons
+SUBDIRS = css domains icons
 
 xsldir=$(datadir)/yelp-xsl/xslt/common
 
@@ -8,6 +8,7 @@ xsl_DATA =                      \
        l10n.xsl                \
        l10n-numbers.xsl        \
        html.xsl                \
+       tmpl.xsl                \
        ttml.xsl                \
        utils.xsl
 
diff --git a/xslt/common/css/Makefile.am b/xslt/common/css/Makefile.am
new file mode 100644
index 00000000..de375bd4
--- /dev/null
+++ b/xslt/common/css/Makefile.am
@@ -0,0 +1,8 @@
+cssdir=$(datadir)/yelp-xsl/xslt/common/css
+
+css_DATA =                     \
+       core.css.tmpl           \
+       elements.css.tmpl       \
+       syntax.css.tmpl
+
+EXTRA_DIST=$(css_DATA)
diff --git a/xslt/common/css/core.css.tmpl b/xslt/common/css/core.css.tmpl
new file mode 100644
index 00000000..daede214
--- /dev/null
+++ b/xslt/common/css/core.css.tmpl
@@ -0,0 +1,241 @@
+<tmpl>
+html { height: 100%; }
+body {
+  font-family: sans-serif;
+  margin: 0; padding: 0;
+  background-color: {{$color.bg}};
+  color: {{$color.fg}};
+  direction: {{$direction}};
+}
+article, aside, nav, header, footer, section {
+  display: block;
+  margin: 0;
+  padding: 0;
+}
+main {
+  display: flex;
+  flex-flow: row;
+}
+main > * {
+  flex: 0 0 220px;
+}
+main > div.page {
+  flex-grow: 1;
+  margin: 0;
+  display: flex;
+  flex-flow: column;
+  align-items: stretch;
+  justify-content: flex-start;
+  min-height: 100vh;
+}
+div.page > article { flex: 1 0 auto; }
+div.page > header, div.page > footer { flex: 0 1 auto; }
+.pagewide {
+  max-width: 940px;
+  margin-left: auto;
+  margin-right: auto;
+  padding-left: 10px;
+  padding-right: 10px;
+}
+aside.sidebar {
+  width: 300px;
+  padding: 20px 10px;
+  background: {{$color.bg.gray}}
+}
+aside.sidebar-right { order: 3; }
+aside.sidebar section { margin-top: 0; }
+aside.sidebar * { margin-bottom: 20px; }
+aside.sidebar section > div.inner > div.hgroup {
+  border-bottom: none;
+}
+aside.sidebar section h2 {
+  font-size: 1em;
+  margin-bottom: 0;
+}
+article {
+  padding-top: 10px;
+  padding-bottom: 10px;
+  min-height: 20em;
+  background-color: {{$color.bg}};
+}
+section {
+  margin-top: 2.4em;
+  clear: both;
+}
+section section {
+  margin-top: 1.44em;
+}
+.yelp-hash-highlight {
+  animation-name: yelp-hash-highlight;
+  animation-duration: 0.5s;
+  animation-fill-mode: forwards;
+}
+@keyframes yelp-hash-highlight {
+  from { transform: translateY(0px) }
+  25%  { transform: translateY(20px); }
+  50%  { transform: translateY(0); }
+  75%  { transform: translateY(10px); }
+  to   { transform: translateY(0px); }
+}
+div.trails {
+  margin: 0 -10px 0 -10px;
+  padding: 0.2em 10px;
+  background-color: {{$color.bg.gray}};
+}
+div.trail {
+  margin: 0.2em 0;
+  padding: 0 1em 0 1em;
+  text-indent: -1em;
+  color: {{$color.fg.dark}};
+}
+a.trail { white-space: nowrap; }
+div.hgroup {
+  margin-bottom: 0.5em;
+  color: {{$color.fg.dark}};
+}
+section > div.inner > div.hgroup {
+  margin-top: 0;
+  border-bottom: solid 1px {{$color.gray}};
+}
+section.links > div.inner > div.hgroup {
+  border-bottom: solid 2px {{$color.fg.blue}};
+}
+section section.links > div.inner > div.hgroup {
+  border: none;
+}
+h1, h2, h3, h4, h5, h6, h7 {
+  margin: 0; padding: 0;
+  font-weight: normal;
+}
+h1 { font-size: 2.4em; }
+h2 { font-size: 1.72em; }
+h3.title, h4.title, h5.title, h6.title, h7.title { font-size: 1.44em; }
+h3, h4, h5, h6, h7 { font-size: 1em; }
+p { line-height: 1.44em; }
+div, pre, p { margin: 0; padding: 0; }
+div.contents > * + *,
+th > * + *, td > * + *,
+dt > * + *, dd > * + *,
+li > * + * { margin-top: 1em; }
+p img { vertical-align: middle; }
+p.lead { font-size: 1.2em; }
+div.clear {
+  margin: 0; padding: 0;
+  height: 0; line-height: 0;
+  clear: both;
+}
+.center { text-align: center; }
+
+footer.about { margin: 0; }
+footer.about > div.inner > div.hgroup {
+  margin: 0; padding: 0;
+  text-align: center;
+  border: none;
+}
+footer.about > div.inner > div.hgroup > h2 {
+  margin: 0; padding: 0.2em;
+  font-size: inherit;
+}
+footer.about.ui-expander > div.inner > div.hgroup span.title:before {
+  content: "";
+}
+div.copyrights {
+  max-width: 700px;
+  text-align: center;
+  padding: 10px;
+  margin: 0 auto;
+}
+div.copyright { margin: 0; }
+div.credits {
+  display: flex;
+  flex-flow: row wrap;
+  align-items: stretch;
+  justify-content: flex-start;
+  max-width: 720px;
+  margin: 0 auto;
+}
+div.credits > * {
+  vertical-align: top;
+  text-align: left;
+  flex: 1 0 220px;
+  margin: 0;
+  padding: 10px;
+}
+div.credits > *:empty { padding: 0 10px; height: 0; }
+ul.credits, ul.credits li {
+  margin: 0; padding: 0;
+  list-style-type: none;
+}
+ul.credits li {
+  margin-{{$left}}: 1em;
+  text-indent: -1em;
+}
+div.license {
+  max-width: 700px;
+  margin: 0 auto;
+  padding: 10px;
+}
+
+table {
+  border-collapse: collapse;
+  border-color: {{$color.gray}};
+  border-width: 1px;
+}
+td, th {
+  padding: 0.5em;
+  vertical-align: top;
+  border-color: {{$color.gray}};
+  border-width: 1px;
+}
+thead td, thead th, tfoot td, tfoot th {
+  font-weight: bold;
+  color: {{$color.fg.dark}};
+  background-color: {{$color.bg.dark}};
+}
+th {
+  text-align: {{$left}};
+  font-weight: bold;
+  color: {{$color.fg.dark}};
+}
+
+ul, ol, dl { margin: 0; padding: 0; }
+li {
+  margin: 1em 0 0 0;
+  margin-{{$left}}: 2.4em;
+  padding: 0;
+}
+li:first-child { margin-top: 0; }
+@media (max-width: 480px) {
+  li {
+    margin-{{$left}}: 1.44em;
+  }
+}
+dt { margin-top: 1em; }
+dt:first-child { margin-top: 0; }
+dt + dt { margin-top: 0; }
+dd {
+  margin: 0.2em 0 0 0;
+  margin-{{$left}}: 1.44em;
+}
+dd + dd { margin-top: 1em; }
+ol.compact li { margin-top: 0.2em; }
+ul.compact li { margin-top: 0.2em; }
+ol.compact li:first-child { margin-top: 0; }
+ul.compact li:first-child { margin-top: 0; }
+dl.compact dt { margin-top: 0.2em; }
+dl.compact dt:first-child { margin-top: 0; }
+dl.compact dt + dt { margin-top: 0; }
+
+a {
+  text-decoration: none;
+  color: {{$color.fg.blue}};
+}
+a:visited { color: {{$color.fg.purple}}; }
+a:hover {
+  border-bottom: dotted 1px {{$color.fg.blue}};
+}
+p a {
+  border-bottom: dotted 1px {{$color.fg.blue}};
+}
+a img { border: none; }
+</tmpl>
diff --git a/xslt/common/css/elements.css.tmpl b/xslt/common/css/elements.css.tmpl
new file mode 100644
index 00000000..0432f80d
--- /dev/null
+++ b/xslt/common/css/elements.css.tmpl
@@ -0,0 +1,584 @@
+<tmpl>
+.yelp-svg-fill {
+  fill: {{$color.fg.dark}};
+}
+.yelp-svg-stroke {
+  stroke: {{$color.fg.dark}};
+}
+div.title {
+  margin: 0 0 0.2em 0;
+  font-weight: bold;
+  color: {{$color.fg.dark}};
+}
+div.title h1, div.title h2, div.title h3, div.title h4, div.title h5, div.title h6 {
+  margin: 0;
+  font-size: inherit;
+  font-weight: inherit;
+  color: inherit;
+}
+div.desc { margin: 0 0 0.2em 0; }
+div.contents + div.desc { margin: 0.2em 0 0 0; }
+pre.contents {
+  padding: 0.5em 1em 0.5em 1em;
+}
+div.links-center { text-align: center; }
+div.links .desc { color: {{$color.fg.gray}}; }
+div.links > div.inner > div.region > div.desc { font-style: italic; }
+div.links ul { margin: 0; padding: 0; }
+div.links ul ul {
+  margin-{{$left}}: 1em;
+}
+li.links {
+  margin: 0.5em 0 0.5em 0;
+  padding: 0;
+  list-style-type: none;
+}
+li.links-head {
+  margin-top: 1em;
+  color: {{$color.fg.gray}};
+  border-bottom: solid 1px {{$color.gray}};
+}
+div.sectionlinks {
+  display: inline-block;
+  padding: 0 1em 0 1em;
+  background-color: {{$color.bg.blue}};
+  border: solid 1px {{$color.fg.blue}};
+}
+div.sectionlinks ul { margin: 0; }
+div.sectionlinks li { padding: 0; }
+div.sectionlinks div.title { margin: 0.5em 0 0.5em 0; }
+div.sectionlinks div.sectionlinks {
+  display: block;
+  margin: 0.5em 0 0 0;
+  padding: 0;
+  border: none;
+}
+div.sectionlinks div.sectionlinks li {
+  padding-{{$left}}: 1.44em;
+}
+nav.prevnext { clear: both; }
+div.region > nav.prevnext, div.region + nav.prevnext { margin-top: 1em; }
+nav.prevnext > div.inner { float: {{$right}}; }
+nav.prevnext > div.inner > * {
+  background-color: {{$color.bg.gray}};
+  display: inline-block;
+  position: relative;
+  height: 1.44em;
+  padding: 0.2em 0.83em 0 0.83em;
+  margin-bottom: 1em;
+  border: solid 1px {{$color.gray}};
+}
+nav.prevnext > div.inner > span { visibility: hidden; }
+nav.prevnext > div.inner > a + a {
+  border-{{$left}}: none;
+}
+nav.prevnext > div.inner > a:first-child {
+  border-top-{{$left}}-radius: 2px;
+  border-bottom-{{$left}}-radius: 2px;
+}
+nav.prevnext > div.inner > a:last-of-type {
+  border-top-{{$right}}-radius: 2px;
+  border-bottom-{{$right}}-radius: 2px;
+}
+div.serieslinks {
+  display: inline-block;
+  padding: 0 1em 0 1em;
+  background-color: {{$color.bg.blue}};
+  border: solid 1px {{$color.fg.blue}};
+}
+div.serieslinks ul { margin: 0; }
+div.serieslinks li { padding: 0; }
+div.serieslinks div.title { margin: 0.5em 0 0.5em 0; }
+pre.numbered {
+  margin: 0;
+  padding: 0.5em;
+  float: {{$left}};
+  margin-{{$right}}: 0.5em;
+  text-align: {{$right}};
+  color: {{$color.fg.gray}};
+  background-color: {{$color.bg.yellow}};
+}
+div.code {
+  border: solid 1px {{$color.gray}};
+}
+div.example {
+  border-{{$left}}: solid 4px {{$color.gray}};
+  padding-{{$left}}: 1em;
+}
+div.example > div.inner > div.region > div.desc { font-style: italic; }
+div.figure {
+  display: inline-block;
+  max-width: 100%;
+  margin-{{$left}}: 1.72em;
+}
+div.figure > div.inner {
+  padding: 4px;
+  color: {{$color.fg.dark}};
+  border: solid 1px {{$color.gray}};
+  background-color: {{$color.bg.gray}};
+}
+@media (max-width: 960px) {
+  div.figure {
+    margin-{{$left}}: 0;
+  }
+}
+a.figure-zoom {
+  float: {{$right}};
+}
+a.figure-zoom:hover { border-bottom: none; }
+a.figure-zoom:hover .yelp-svg-fill { fill: {{$color.blue}}; }
+a.figure-zoom:hover .yelp-svg-stroke { stroke: {{$color.blue}}; }
+a.figure-zoom .figure-zoom-out { display: none; }
+a.figure-zoom.figure-zoomed .figure-zoom-in { display: none; }
+a.figure-zoom.figure-zoomed .figure-zoom-out { display: inline-block; }
+div.figure > div.inner > div.region > div.contents {
+  margin: 0;
+  padding: 0.5em 1em 0.5em 1em;
+  clear: both;
+  text-align: center;
+  color: {{$color.fg}};
+  border: solid 1px {{$color.gray}};
+  background-color: {{$color.bg}};
+}
+div.list > div.inner > div.title { margin-bottom: 0.5em; }
+div.listing > div.inner { margin: 0; padding: 0; }
+div.listing > div.inner > div.region > div.desc { font-style: italic; }
+div.note {
+  padding: 6px;
+  border: solid 1px {{$color.bg.dark}};
+  background-color: {{$color.bg.gray}};
+  display: flex;
+  flex-flow: row;
+}
+div.note > * { margin: 0 6px; padding: 0; min-height: 24px; min-width: 24px; }
+div.note-warning > svg .yelp-svg-fill {
+  fill: {{$color.red}};
+}
+div.note-danger {
+  border-color: {{$color.red}};
+}
+div.note-important > svg .yelp-svg-fill {
+  fill: {{$color.blue}};
+}
+div.note-danger > svg .yelp-svg-fill {
+  fill: {{$color.red}};
+  animation-name: yelp-note-danger;
+  animation-duration: 2s;
+  animation-fill-mode: forwards;
+  animation-iteration-count: infinite;
+}
+@keyframes yelp-note-danger {
+  from { fill: {{$color.red}} }
+  50%  { fill: {{$color.gray}} }
+  to   { fill: {{$color.red}} }
+}
+div.note-sidebar {
+  float: {{$right}};
+  max-width: 206px;
+  margin-{{$left}}: 20px;
+  padding: 6px;
+}
+div.quote {
+  padding: 0;
+  min-height: {{$icons.size.quote}}px;
+}
+<!-- There are rules to create the blockquote icon in html.xsl
+     that these simple text templates can't handle. -->
+div.quote > div.inner > div.title {
+  margin: 0;
+  margin-{{$left}}: {{$icons.size.quote}}px;
+}
+blockquote {
+  margin: 0; padding: 0;
+  margin-{{$left}}: {{$icons.size.quote}}px;
+}
+blockquote > *:first-child { margin-top: 0; }
+div.quote > div.inner > div.region > div.cite {
+  margin-top: 0.5em;
+  margin-{{$left}}: {{$icons.size.quote}}px;
+  color: {{$color.fg.gray}};
+}
+div.quote > div.inner > div.region > div.cite::before {
+  <!-- FIXME: i18n -->
+  content: '&#x2015; ';
+  color: {{$color.fg.gray}};
+}
+div.screen {
+  background-color: {{$color.bg.gray}};
+  border: solid 1px {{$color.gray}};
+}
+ol.steps, ul.steps {
+  padding: 0.5em 1em 0.5em 1em;
+  border: solid 1px {{$color.bg.gray}};
+  border-{{$left}}: solid 4px {{$color.yellow}};
+}
+ol.steps .steps {
+  padding: 0;
+  border: none;
+  background-color: unset;
+}
+li.steps { margin-{{$left}}: 1.44em; }
+li.steps li.steps { margin-{{$left}}: 2.4em; }
+div.synopsis > div.inner > div.region > div.contents,
+div.synopsis > div.contents, div.synopsis > pre.contents {
+  padding: 0.5em 1em 0.5em 1em;
+  border-top: solid 1px;
+  border-bottom: solid 1px;
+  border-color: {{$color.fg.blue}};
+  background-color: {{$color.bg.gray}};
+}
+div.synopsis > div.inner > div.region > div.desc { font-style: italic; }
+div.synopsis div.code {
+  background: unset;
+  border: none;
+  padding: 0;
+}
+div.synopsis div.code > pre.contents { margin: 0; padding: 0; }
+div.unknown > div.inner > div.region > div.desc { font-style: italic; }
+div.table > div.desc { font-style: italic; }
+tr.shade {
+  background-color: {{$color.bg.gray}};
+}
+td.shade {
+  background-color: {{$color.bg.gray}};
+}
+tr.shade td.shade {
+  background-color: {{$color.bg.dark}};
+}
+
+span.app { font-style: italic; }
+span.cmd {
+  font-family: monospace,monospace; font-size: 0.83em;
+  background-color: {{$color.bg.gray}};
+  padding: 0 0.2em 0 0.2em;
+}
+span.cmd span.cmd { background-color: unset; padding: 0; }
+pre span.cmd { background-color: unset; padding: 0; }
+span.code {
+  font-family: monospace,monospace; font-size: 0.83em;
+  border-bottom: solid 1px {{$color.bg.dark}};
+}
+span.code span.code { border: none; }
+pre span.code { border: none; }
+span.em { font-style: italic; }
+span.em-bold {
+  font-style: normal; font-weight: bold;
+  color: {{$color.fg.dark}};
+}
+a span.em-bold {
+  color: {{$color.fg.blue}};
+}
+pre span.error {
+  color: {{$color.fg.red}};
+}
+span.file { font-family: monospace,monospace; font-size: 0.83em; }
+span.gui, span.guiseq { color: {{$color.fg.dark}}; }
+a span.gui, a span.guiseq { color: {{$color.fg.blue}}; }
+span.input { font-family: monospace,monospace; font-size: 0.83em; }
+pre span.input {
+  font-weight: bold;
+  color: {{$color.fg.dark}};
+}
+kbd {
+  font-family: inherit;
+  font-size: inherit;
+  color: {{$color.fg.dark}};
+  background-color: {{$color.bg.gray}};
+  border: solid 1px {{$color.gray}};
+  border-radius: 2px;
+  margin: 0 0.2em 0 0.2em;
+  padding: 0.2em 0.5em 0 0.5em;
+  white-space: nowrap;
+}
+kbd.key-Fn {
+  font-weight: bold;
+  color: {{$color.fg.blue}};
+}
+span.key a {
+  border-bottom: none;
+}
+a kbd {
+  color: {{$color.fg.blue}};
+  border-color: {{$color.fg.blue}};
+}
+span.keyseq {
+  color: {{$color.fg.dark}};
+  white-space: nowrap
+}
+a span.keyseq { color: {{$color.fg.blue}}; }
+span.output { font-family: monospace,monospace; font-size: 0.83em; }
+pre span.output {
+  color: {{$color.fg}};
+}
+pre span.prompt {
+  color: {{$color.fg.dark}};
+}
+span.sys { font-family: monospace,monospace; font-size: 0.83em; }
+span.var { font-style: italic; }
+
+.ui-tile-img .media-controls { display: none; }
+span.media-audio, span.media-video { display: inline-block; }
+audio, video { display: block; margin: 0; }
+div.media > div.inner { display: inline-block; text-align: center; }
+.media-controls {
+  height: 30px;
+  margin: 0; padding: 0;
+  border-left: solid 1px {{$color.fg}};
+  border-right: solid 1px {{$color.fg}};
+  border-bottom: solid 1px {{$color.fg}};
+  background-color: {{$color.fg.dark}};
+  color: {{$color.bg}};
+  border-bottom-left-radius: 4px;
+  border-bottom-right-radius: 4px;
+  display: flex;
+  align-items: center;
+}
+.media-controls > * {
+  flex: 0 1 auto;
+}
+.media-controls > input.media-range {
+  flex: 1 0 auto;
+  background-color: {{$color.fg.dark}};
+  margin: 0 10px;
+  -webkit-appearance: none;
+}
+input.media-range::-webkit-slider-runnable-track {
+  height: 4px;
+  background: {{$color.fg.gray}};
+  border-radius: 2px;
+}
+input.media-range::-webkit-slider-thumb {
+  -webkit-appearance: none;
+  height: 16px;
+  width: 16px;
+  border-radius: 8px;
+  background: {{$color.bg.dark}};
+  border: solid 1px {{$color.fg.dark}};
+  margin-top: -6px;
+}
+input.media-range::-webkit-slider-thumb:hover,
+input.media-range::-webkit-slider-thumb:focus {
+  background: {{$color.bg.gray}};
+}
+input.media-range::-moz-range-track {
+  height: 4px;
+  background: {{$color.fg.gray}};
+  border-radius: 2px;
+}
+input.media-range::-moz-range-thumb {
+  -webkit-appearance: none;
+  height: 16px;
+  width: 16px;
+  border-radius: 8px;
+  background: {{$color.bg.dark}};
+  border: solid 1px {{$color.fg.dark}};
+  margin-top: -6px;
+}
+.media-controls-audio {
+  border-top: solid 1px {{$color.fg}};
+  border-radius: 4px;
+}
+button.media-play {
+  height: 30px;
+  padding: 0 6px 0 6px; line-height: 0;
+  background-color: {{$color.fg.dark}};
+  border: none;
+  border-{{$right}}: solid 1px {{$color.fg}};
+}
+button.media-play:hover, button.media-play:focus {
+  background-color: {{$color.fg.blue}};
+}
+button.media-play .yelp-svg-fill { fill: {{$color.bg.gray}}; }
+button.media-play .media-pause { display: none; }
+button.media-play-playing .media-play { display: none; }
+button.media-play-playing .media-pause { display: inline; }
+.media-time {
+  margin: 0;
+  font-size: 16px;
+  height: 30px;
+  line-height: 30px;
+}
+.media-time > span {
+  padding-{{$right}}: 8px;
+}
+.media-duration {
+  font-size: 12px;
+  color: {{$color.bg.dark}};
+  opacity: 0.8;
+}
+.media-controls-ttml {
+  min-width: 630px;
+  border-bottom-left-radius: 0;
+  border-bottom-right-radius: 0;
+}
+div.media-ttml {
+  margin: 0; padding: 6px 0;
+  background-color: {{$color.bg.gray}};
+  border: solid 1px {{$color.fg}};
+  min-height: 24px;
+  border-bottom-left-radius: 4px;
+  border-bottom-right-radius: 4px;
+}
+.media-ttml-pre { white-space: pre; }
+.media-ttml-nopre { white-space: normal; }
+div.media-ttml-div {
+  text-align: {{$left}};
+  display: none;
+  margin: 0; padding: 0;
+}
+div.media-ttml-p {
+  text-align: {{$left}};
+  display: none;
+  margin: 0 auto;
+  max-width: 560px;
+  line-height: 1.44em;
+}
+div.media-ttml-div > * + * { margin-top: 1em; }
+div.yelp-data { display: none; }
+.ui-expander > div.inner > div.title span.title,
+.ui-expander > div.inner > div.hgroup span.title {
+  cursor: default;
+}
+.ui-expander > div.inner > div.title span.title:before,
+.ui-expander > div.inner > div.hgroup span.title:before {
+  font-weight: bold;
+  content: "⌃";
+  display: inline-block;
+  margin: 0;
+  color: {{$color.fg.blue}};
+  transform: translateY(0.2em) rotate(0deg);
+  -webkit-transform: translateY(0.2em) rotate(0deg);
+  transition: transform 0.2s linear;
+  transform-origin: 50% 30%;
+  -webkit-transform-origin: 50% 30%;
+  -webkit-transition: -webkit-transform 0.2s linear;
+  margin: 0 0.2em;
+}
+.ui-expander-c > div.inner > div.hgroup { border-bottom: none; }
+<!-- There is a rotation on the arrow in html.xsl that these
+     simple text templates can't handle -->
+.ui-expander > div.inner > div.title:hover,
+.ui-expander > div.inner > div.hgroup:hover * {
+  color: {{$color.fg.blue}};
+}
+.ui-expander > div.inner > div.hgroup > .subtitle {
+  margin-{{$left}}: 2em;
+}
+.ui-expander-c > div.inner > div.region {
+  display: none;
+}
+.ui-expander-e > div.inner > div.region {
+  animation-name: yelp-ui-expander-e;
+  animation-duration: 0.2s;
+  animation-fill-mode: forwards;
+  transform-origin: 0 0;
+}
+@keyframes yelp-ui-expander-e {
+  from { transform: scaleY(0); }
+  to   { transform: scaleY(1); }
+}
+div.ui-expander-preview > div.inner > div.region {
+  transform-origin: 0 0;
+  transition: transform 0.2s linear, background-color 0.2s linear;
+  animation-name: none;
+}
+div.ui-expander-preview.ui-expander-c > div.inner {
+  max-height: 100px;
+  overflow: hidden;
+}
+div.ui-expander-preview.ui-expander-c > div.inner > div.region {
+  display: block;
+  transform: scaleY(0.4);
+  background-color: {{$color.bg.gray}};
+}
+div.ui-expander-preview.ui-expander-c > div.inner > div.region:hover {
+  background-color: {{$color.bg.blue}};
+  cursor: zoom-in;
+}
+div.ui-expander-preview.ui-expander-c > div.inner > div.region:hover * {
+  cursor: zoom-in;
+}
+div.ui-expander-preview > div.inner > div.region > * {
+  transform-origin: 0 0;
+  transition: transform 0.2s linear;
+}
+div.ui-expander-preview.ui-expander-c > div.inner > div.region > * {
+  transform: scaleX(0.4);
+}
+section.ui-expander-preview > div.inner > div.region > div.contents{
+  transform-origin: 0 0;
+  transition: transform 0.2s linear, background-color 0.2s linear;
+}
+section.ui-expander-preview.ui-expander-c > div.inner {
+  max-height: 140px;
+  overflow: hidden;
+}
+section.ui-expander-preview.ui-expander-c > div.inner > div.region {
+  display: block;
+}
+section.ui-expander-preview.ui-expander-c > div.inner > div.region > div.contents {
+  transform: scaleY(0.6);
+  background-color: {{$color.bg.gray}};
+}
+section.ui-expander-preview > div.inner > div.region > div.contents > * {
+  transform-origin: 0 0;
+  transition: transform 0.2s linear;
+}
+section.ui-expander-preview.ui-expander-c > div.inner > div.region > div.contents > * {
+  transform: scaleX(0.6);
+}
+section.ui-expander-preview.ui-expander-c > div.inner > div.region > div.contents:hover {
+  background-color: {{$color.bg.blue}};
+  cursor: zoom-in;
+}
+@media only screen and (max-width: 480px) {
+  article > div.region > div.contents > div.example,
+  article > div.region > section > div.inner > div.region > div.contents > div.example {
+    margin-left: -10px;
+    margin-right: -10px;
+  }
+  div.example {
+    padding-{{$left}}: 6px;
+    padding-{{$right}}: 10px;
+  }
+  article > div.region > div.contents > div.note,
+  article > div.region > section > div.inner > div.region > div.contents > div.note {
+    margin-left: -10px;
+    margin-right: -10px;
+    padding-left: 10px;
+    padding-right: 10px;
+  }
+  article > div.region > div.contents > div.note,
+  article > div.region > section > div.inner > div.region > div.contents > div.note {
+    border-left: none;
+    border-right: none;
+  }
+  div.note-sidebar {
+    float: none;
+    max-width: none;
+    margin-left: inherit;
+    margin-right: inherit;
+    padding-left: inherit;
+    padding-right: inherit;
+  }
+  div.note-sidebar > div.inner > div.title,
+  div.note-sidebar > div.inner > div.region > div.contents {
+    margin-left: 10px;
+    margin-right: 10px;
+  }
+  article > div.region > div.contents > div.steps,
+  article > div.region > section > div.inner > div.region > div.contents > div.steps {
+    margin-left: -10px;
+    margin-right: -10px;
+  }
+  div.steps > div.inner > div.title {
+    margin-left: 10px;
+    margin-right: 10px;
+  }
+  ol.steps, ul.steps {
+    padding: 0;
+    padding-{{$left}}: 6px;
+    padding-{{$right}}: 10px;
+  }
+}
+</tmpl>
diff --git a/xslt/common/css/syntax.css.tmpl b/xslt/common/css/syntax.css.tmpl
new file mode 100644
index 00000000..eac3f9d7
--- /dev/null
+++ b/xslt/common/css/syntax.css.tmpl
@@ -0,0 +1,51 @@
+<tmpl>
+.hljs a {
+  color: inherit;
+  border-bottom: dotted 1px {{$color.fg.blue}};
+}
+.hljs a:hover, .hljs a:hover * { color: {{$color.fg.blue}}; }
+.hljs-addition {
+  color: {{$color.fg.green}};
+  background-color: {{$color.bg.green}};
+}
+.hljs-deletion {
+  color: {{$color.fg.red}};
+  background-color: {{$color.bg.red}};
+}
+.hljs-emphasis  { font-style: italic; }
+.hljs-strong    { font-weight: bold; }
+.hljs-attr      { color: {{$color.fg.blue}}; }
+.hljs-attribute { color: {{$color.fg.yellow}}; }
+.hljs-built_in  { color: {{$color.fg.orange}}; }
+.hljs-bullet    { color: {{$color.fg.green}}; }
+.hljs-class     { }
+.hljs-code      { color: {{$color.fg.dark}}; }
+.hljs-comment   { color: {{$color.fg.gray}}; }
+.hljs-doctag    { }
+.hljs-formula   { color: {{$color.fg.dark}}; }
+.hljs-function  { }
+.hljs-keyword   { color: {{$color.fg.purple}}; }
+.hljs-link      { color: {{$color.fg.orange}}; }
+.hljs-literal   { color: {{$color.fg.orange}}; }
+.hljs-meta      { color: {{$color.fg.orange}}; }
+.hljs-name      { color: {{$color.fg.red}}; }
+.hljs-number    { color: {{$color.fg.orange}}; }
+.hljs-params    { color: {{$color.fg.orange}}; }
+.hljs-quote     { color: {{$color.fg.gray}}; }
+.hljs-regexp    { color: {{$color.fg.red}}; }
+.hljs-rest_arg  { }
+.hljs-section   { color: {{$color.fg.blue}}; }
+.hljs-string    { color: {{$color.fg.green}}; }
+.hljs-subst     { }
+.hljs-symbol    { color: {{$color.fg.green}}; }
+.hljs-tag       { color: {{$color.fg.red}}; }
+.hljs-title     { color: {{$color.fg.blue}}; }
+.hljs-type      { }
+.hljs-variable  { }
+.hljs-selector-attr  { }
+.hljs-selector-class { color: {{$color.fg.red}}; }
+.hljs-selector-id    { color: {{$color.fg.red}}; }
+.hljs-selector-tag   { color: {{$color.fg.purple}}; }
+.hljs-template-tag      { }
+.hljs-template-variable { }
+</tmpl>
diff --git a/xslt/common/html.xsl b/xslt/common/html.xsl
index 2dc349b1..453d205c 100644
--- a/xslt/common/html.xsl
+++ b/xslt/common/html.xsl
@@ -1239,254 +1239,13 @@ All parameters can be automatically computed if not provided.
       <xsl:with-param name="direction" select="$direction"/>
     </xsl:call-template>
   </xsl:param>
-  <xsl:text>
-html { height: 100%; }
-body {
-  font-family: sans-serif;
-  margin: 0; padding: 0;
-  background-color: </xsl:text>
-    <xsl:value-of select="$color.bg"/><xsl:text>;
-  color: </xsl:text>
-    <xsl:value-of select="$color.fg"/><xsl:text>;
-  direction: </xsl:text><xsl:value-of select="$direction"/><xsl:text>;
-}
-article, aside, nav, header, footer, section {
-  display: block;
-  margin: 0;
-  padding: 0;
-}
-main {
-  display: flex;
-  flex-flow: row;
-}
-main > * {
-  flex: 0 0 220px;
-}
-main > div.page {
-  flex-grow: 1;
-  margin: 0;
-  display: flex;
-  flex-flow: column;
-  align-items: stretch;
-  justify-content: flex-start;
-  min-height: 100vh;
-}
-div.page > article { flex: 1 0 auto; }
-div.page > header, div.page > footer { flex: 0 1 auto; }
-.pagewide {
-  max-width: 940px;
-  margin-left: auto;
-  margin-right: auto;
-  padding-left: 10px;
-  padding-right: 10px;
-}
-aside.sidebar {
-  width: 300px;
-  padding: 20px 10px;
-  background: </xsl:text><xsl:value-of select="$color.bg.gray"/><xsl:text>
-}
-aside.sidebar-right { order: 3; }
-aside.sidebar section { margin-top: 0; }
-aside.sidebar * { margin-bottom: 20px; }
-aside.sidebar section > div.inner > div.hgroup {
-  border-bottom: none;
-}
-aside.sidebar section h2 {
-  font-size: 1em;
-  margin-bottom: 0;
-}
-article {
-  padding-top: 10px;
-  padding-bottom: 10px;
-  min-height: 20em;
-  background-color: </xsl:text><xsl:value-of select="$color.bg"/><xsl:text>;
-}
-section {
-  margin-top: 2.4em;
-  clear: both;
-}
-section section {
-  margin-top: 1.44em;
-}
-.yelp-hash-highlight {
-  animation-name: yelp-hash-highlight;
-  animation-duration: 0.5s;
-  animation-fill-mode: forwards;
-}
-@keyframes yelp-hash-highlight {
-  from { transform: translateY(0px) }
-  25%  { transform: translateY(20px); }
-  50%  { transform: translateY(0); }
-  75%  { transform: translateY(10px); }
-  to   { transform: translateY(0px); }
-}
-div.trails {
-  margin: 0 -10px 0 -10px;
-  padding: 0.2em 10px;
-  background-color: </xsl:text><xsl:value-of select="$color.bg.gray"/><xsl:text>;
-}
-div.trail {
-  margin: 0.2em 0;
-  padding: 0 1em 0 1em;
-  text-indent: -1em;
-  color: </xsl:text><xsl:value-of select="$color.fg.dark"/><xsl:text>;
-}
-a.trail { white-space: nowrap; }
-div.hgroup {
-  margin-bottom: 0.5em;
-  color: </xsl:text><xsl:value-of select="$color.fg.dark"/><xsl:text>;
-}
-section > div.inner > div.hgroup {
-  margin-top: 0;
-  border-bottom: solid 1px </xsl:text>
-    <xsl:value-of select="$color.gray"/><xsl:text>;
-}
-section.links > div.inner > div.hgroup {
-  border-bottom: solid 2px </xsl:text>
-    <xsl:value-of select="$color.fg.blue"/><xsl:text>;
-}
-section section.links > div.inner > div.hgroup {
-  border: none;
-}
-h1, h2, h3, h4, h5, h6, h7 {
-  margin: 0; padding: 0;
-  font-weight: normal;
-}
-h1 { font-size: 2.4em; }
-h2 { font-size: 1.72em; }
-h3.title, h4.title, h5.title, h6.title, h7.title { font-size: 1.44em; }
-h3, h4, h5, h6, h7 { font-size: 1em; }
-p { line-height: 1.44em; }
-div, pre, p { margin: 0; padding: 0; }
-div.contents > * + *,
-th > * + *, td > * + *,
-dt > * + *, dd > * + *,
-li > * + * { margin-top: 1em; }
-p img { vertical-align: middle; }
-p.lead { font-size: 1.2em; }
-div.clear {
-  margin: 0; padding: 0;
-  height: 0; line-height: 0;
-  clear: both;
-}
-.center { text-align: center; }
-
-footer.about { margin: 0; }
-footer.about > div.inner > div.hgroup {
-  margin: 0; padding: 0;
-  text-align: center;
-  border: none;
-}
-footer.about > div.inner > div.hgroup > h2 {
-  margin: 0; padding: 0.2em;
-  font-size: inherit;
-}
-footer.about.ui-expander > div.inner > div.hgroup span.title:before {
-  content: "";
-}
-div.copyrights {
-  max-width: 700px;
-  text-align: center;
-  padding: 10px;
-  margin: 0 auto;
-}
-div.copyright { margin: 0; }
-div.credits {
-  display: flex;
-  flex-flow: row wrap;
-  align-items: stretch;
-  justify-content: flex-start;
-  max-width: 720px;
-  margin: 0 auto;
-}
-div.credits > * {
-  vertical-align: top;
-  text-align: left;
-  flex: 1 0 220px;
-  margin: 0;
-  padding: 10px;
-}
-div.credits > *:empty { padding: 0 10px; height: 0; }
-ul.credits, ul.credits li {
-  margin: 0; padding: 0;
-  list-style-type: none;
-}
-ul.credits li {
-  margin-</xsl:text><xsl:value-of select="$left"/><xsl:text>: 1em;
-  text-indent: -1em;
-}
-div.license {
-  max-width: 700px;
-  margin: 0 auto;
-  padding: 10px;
-}
-
-table {
-  border-collapse: collapse;
-  border-color: </xsl:text><xsl:value-of select="$color.gray"/><xsl:text>;
-  border-width: 1px;
-}
-td, th {
-  padding: 0.5em;
-  vertical-align: top;
-  border-color: </xsl:text><xsl:value-of select="$color.gray"/><xsl:text>;
-  border-width: 1px;
-}
-thead td, thead th, tfoot td, tfoot th {
-  font-weight: bold;
-  color: </xsl:text><xsl:value-of select="$color.fg.dark"/><xsl:text>;
-  background-color: </xsl:text><xsl:value-of select="$color.bg.dark"/><xsl:text>;
-}
-th {
-  text-align: </xsl:text><xsl:value-of select="$left"/><xsl:text>;
-  font-weight: bold;
-  color: </xsl:text><xsl:value-of select="$color.fg.dark"/><xsl:text>;
-}
-
-ul, ol, dl { margin: 0; padding: 0; }
-li {
-  margin: 1em 0 0 0;
-  margin-</xsl:text><xsl:value-of select="$left"/><xsl:text>: 2.4em;
-  padding: 0;
-}
-li:first-child { margin-top: 0; }
-@media (max-width: 480px) {
-  li {
-    margin-</xsl:text><xsl:value-of select="$left"/><xsl:text>: 1.44em;
-  }
-}
-dt { margin-top: 1em; }
-dt:first-child { margin-top: 0; }
-dt + dt { margin-top: 0; }
-dd {
-  margin: 0.2em 0 0 0;
-  margin-</xsl:text><xsl:value-of select="$left"/><xsl:text>: 1.44em;
-}
-dd + dd { margin-top: 1em; }
-ol.compact li { margin-top: 0.2em; }
-ul.compact li { margin-top: 0.2em; }
-ol.compact li:first-child { margin-top: 0; }
-ul.compact li:first-child { margin-top: 0; }
-dl.compact dt { margin-top: 0.2em; }
-dl.compact dt:first-child { margin-top: 0; }
-dl.compact dt + dt { margin-top: 0; }
-
-a {
-  text-decoration: none;
-  color: </xsl:text><xsl:value-of select="$color.fg.blue"/><xsl:text>;
-}
-a:visited { color: </xsl:text>
-  <xsl:value-of select="$color.fg.purple"/><xsl:text>; }
-a:hover {
-  border-bottom: dotted 1px </xsl:text>
-    <xsl:value-of select="$color.fg.blue"/><xsl:text>;
-}
-p a {
-  border-bottom: dotted 1px </xsl:text>
-    <xsl:value-of select="$color.fg.blue"/><xsl:text>;
-}
-a img { border: none; }
-</xsl:text>
+  <xsl:call-template name="tmpl.file">
+    <xsl:with-param name="file" select="'css/core.css.tmpl'"/>
+    <xsl:with-param name="node" select="$node"/>
+    <xsl:with-param name="direction" select="$direction"/>
+    <xsl:with-param name="left" select="$left"/>
+    <xsl:with-param name="right" select="$right"/>
+  </xsl:call-template>
 </xsl:template>
 
 
@@ -1524,211 +1283,21 @@ All parameters can be automatically computed if not provided.
       <xsl:with-param name="direction" select="$direction"/>
     </xsl:call-template>
   </xsl:param>
-  <xsl:text>
-.yelp-svg-fill {
-  fill: </xsl:text><xsl:value-of select="$color.fg.dark"/><xsl:text>;
-}
-.yelp-svg-stroke {
-  stroke: </xsl:text><xsl:value-of select="$color.fg.dark"/><xsl:text>;
-}
-div.title {
-  margin: 0 0 0.2em 0;
-  font-weight: bold;
-  color: </xsl:text><xsl:value-of select="$color.fg.dark"/><xsl:text>;
-}
-div.title h1, div.title h2, div.title h3, div.title h4, div.title h5, div.title h6 {
-  margin: 0;
-  font-size: inherit;
-  font-weight: inherit;
-  color: inherit;
-}
-div.desc { margin: 0 0 0.2em 0; }
-div.contents + div.desc { margin: 0.2em 0 0 0; }
-pre.contents {
-  padding: 0.5em 1em 0.5em 1em;
-}
-div.links-center { text-align: center; }
-div.links .desc { color: </xsl:text><xsl:value-of select="$color.fg.gray"/><xsl:text>; }
-div.links > div.inner > div.region > div.desc { font-style: italic; }
-div.links ul { margin: 0; padding: 0; }
-div.links ul ul {
-  margin-</xsl:text><xsl:value-of select="$left"/><xsl:text>: 1em;
-}
-li.links {
-  margin: 0.5em 0 0.5em 0;
-  padding: 0;
-  list-style-type: none;
-}
-li.links-head {
-  margin-top: 1em;
-  color: </xsl:text><xsl:value-of select="$color.fg.gray"/><xsl:text>;
-  border-bottom: solid 1px </xsl:text><xsl:value-of select="$color.gray"/><xsl:text>;
-}
-div.sectionlinks {
-  display: inline-block;
-  padding: 0 1em 0 1em;
-  background-color: </xsl:text>
-    <xsl:value-of select="$color.bg.blue"/><xsl:text>;
-  border: solid 1px </xsl:text>
-    <xsl:value-of select="$color.fg.blue"/><xsl:text>;
-}
-div.sectionlinks ul { margin: 0; }
-div.sectionlinks li { padding: 0; }
-div.sectionlinks div.title { margin: 0.5em 0 0.5em 0; }
-div.sectionlinks div.sectionlinks {
-  display: block;
-  margin: 0.5em 0 0 0;
-  padding: 0;
-  border: none;
-}
-div.sectionlinks div.sectionlinks li {
-  padding-</xsl:text><xsl:value-of select="$left"/><xsl:text>: 1.44em;
-}
-nav.prevnext { clear: both; }
-div.region > nav.prevnext, div.region + nav.prevnext { margin-top: 1em; }
-nav.prevnext > div.inner { float: </xsl:text><xsl:value-of select="$right"/><xsl:text>; }
-nav.prevnext > div.inner > * {
-  background-color: </xsl:text><xsl:value-of select="$color.bg.gray"/><xsl:text>;
-  display: inline-block;
-  position: relative;
-  height: 1.44em;
-  padding: 0.2em 0.83em 0 0.83em;
-  margin-bottom: 1em;
-  border: solid 1px </xsl:text><xsl:value-of select="$color.gray"/><xsl:text>;
-}
-nav.prevnext > div.inner > span { visibility: hidden; }
-nav.prevnext > div.inner > a + a {
-  border-</xsl:text><xsl:value-of select="$left"/><xsl:text>: none;
-}
-nav.prevnext > div.inner > a:first-child {
-  border-top-</xsl:text><xsl:value-of select="$left"/><xsl:text>-radius: 2px;
-  border-bottom-</xsl:text><xsl:value-of select="$left"/><xsl:text>-radius: 2px;
-}
-nav.prevnext > div.inner > a:last-of-type {
-  border-top-</xsl:text><xsl:value-of select="$right"/><xsl:text>-radius: 2px;
-  border-bottom-</xsl:text><xsl:value-of select="$right"/><xsl:text>-radius: 2px;
-}
-div.serieslinks {
-  display: inline-block;
-  padding: 0 1em 0 1em;
-  background-color: </xsl:text>
-    <xsl:value-of select="$color.bg.blue"/><xsl:text>;
-  border: solid 1px </xsl:text>
-    <xsl:value-of select="$color.fg.blue"/><xsl:text>;
-}
-div.serieslinks ul { margin: 0; }
-div.serieslinks li { padding: 0; }
-div.serieslinks div.title { margin: 0.5em 0 0.5em 0; }
-pre.numbered {
-  margin: 0;
-  padding: 0.5em;
-  float: </xsl:text><xsl:value-of select="$left"/><xsl:text>;
-  margin-</xsl:text><xsl:value-of select="$right"/><xsl:text>: 0.5em;
-  text-align: </xsl:text><xsl:value-of select="$right"/><xsl:text>;
-  color: </xsl:text><xsl:value-of select="$color.fg.gray"/><xsl:text>;
-  background-color: </xsl:text>
-    <xsl:value-of select="$color.bg.yellow"/><xsl:text>;
-}
-div.code {
-  border: solid 1px </xsl:text>
-    <xsl:value-of select="$color.gray"/><xsl:text>;
-}
-div.example {
-  border-</xsl:text><xsl:value-of select="$left"/><xsl:text>: solid 4px </xsl:text>
-    <xsl:value-of select="$color.gray"/><xsl:text>;
-  padding-</xsl:text><xsl:value-of select="$left"/><xsl:text>: 1em;
-}
-div.example > div.inner > div.region > div.desc { font-style: italic; }
-div.figure {
-  display: inline-block;
-  max-width: 100%;
-  margin-</xsl:text><xsl:value-of select="$left"/><xsl:text>: 1.72em;
-}
-div.figure > div.inner {
-  padding: 4px;
-  color: </xsl:text>
-    <xsl:value-of select="$color.fg.dark"/><xsl:text>;
-  border: solid 1px </xsl:text>
-    <xsl:value-of select="$color.gray"/><xsl:text>;
-  background-color: </xsl:text>
-    <xsl:value-of select="$color.bg.gray"/><xsl:text>;
-}
-@media (max-width: 960px) {
-  div.figure {
-    margin-</xsl:text><xsl:value-of select="$left"/><xsl:text>: 0;
-  }
-}
-a.figure-zoom {
-  float: </xsl:text><xsl:value-of select="$right"/><xsl:text>;
-}
-a.figure-zoom:hover { border-bottom: none; }
-a.figure-zoom:hover .yelp-svg-fill { fill: </xsl:text>
-  <xsl:value-of select="$color.blue"/><xsl:text>; }
-a.figure-zoom:hover .yelp-svg-stroke { stroke: </xsl:text>
-  <xsl:value-of select="$color.blue"/><xsl:text>; }
-a.figure-zoom .figure-zoom-out { display: none; }
-a.figure-zoom.figure-zoomed .figure-zoom-in { display: none; }
-a.figure-zoom.figure-zoomed .figure-zoom-out { display: inline-block; }
-div.figure > div.inner > div.region > div.contents {
-  margin: 0;
-  padding: 0.5em 1em 0.5em 1em;
-  clear: both;
-  text-align: center;
-  color: </xsl:text>
-    <xsl:value-of select="$color.fg"/><xsl:text>;
-  border: solid 1px </xsl:text>
-    <xsl:value-of select="$color.gray"/><xsl:text>;
-  background-color: </xsl:text>
-    <xsl:value-of select="$color.bg"/><xsl:text>;
-}
-div.list > div.inner > div.title { margin-bottom: 0.5em; }
-div.listing > div.inner { margin: 0; padding: 0; }
-div.listing > div.inner > div.region > div.desc { font-style: italic; }
-div.note {
-  padding: 6px;
-  border: solid 1px </xsl:text>
-    <xsl:value-of select="$color.bg.dark"/><xsl:text>;
-  background-color: </xsl:text>
-    <xsl:value-of select="$color.bg.gray"/><xsl:text>;
-  display: flex;
-  flex-flow: row;
-}
-div.note > * { margin: 0 6px; padding: 0; min-height: 24px; min-width: 24px; }
-div.note-warning > svg .yelp-svg-fill {
-  fill: </xsl:text><xsl:value-of select="$color.red"/><xsl:text>;
-}
-div.note-danger {
-  border-color: </xsl:text><xsl:value-of select="$color.red"/><xsl:text>;
-}
-div.note-important > svg .yelp-svg-fill {
-  fill: </xsl:text><xsl:value-of select="$color.blue"/><xsl:text>;
-}
-div.note-danger > svg .yelp-svg-fill {
-  fill: </xsl:text><xsl:value-of select="$color.red"/><xsl:text>;
-  animation-name: yelp-note-danger;
-  animation-duration: 2s;
-  animation-fill-mode: forwards;
-  animation-iteration-count: infinite;
-}
-@keyframes yelp-note-danger {
-  from { fill: </xsl:text><xsl:value-of select="$color.red"/><xsl:text> }
-  50%  { fill: </xsl:text><xsl:value-of select="$color.gray"/><xsl:text> }
-  to   { fill: </xsl:text><xsl:value-of select="$color.red"/><xsl:text> }
-}
-div.note-sidebar {
-  float: </xsl:text><xsl:value-of select="$right"/><xsl:text>;
-  max-width: 206px;
-  margin-</xsl:text><xsl:value-of select="$left"/><xsl:text>: 20px;
-  padding: 6px;
-}
-div.quote {
-  padding: 0;
-  min-height: </xsl:text>
-    <xsl:value-of select="$icons.size.quote"/><xsl:text>px;
-}
-div.quote > div.inner:before {
-  float: </xsl:text><xsl:value-of select="$left"/><xsl:text>;
-  content: '</xsl:text>
+  <!-- Almost everything is in the text template file, except there's
+       these two bits that aren't because XPath alone can't handle
+       them. We could precompute and pass params, but I'm hesitant
+       to add special-purpose params, which are API. -->
+  <xsl:call-template name="tmpl.file">
+    <xsl:with-param name="file" select="'css/elements.css.tmpl'"/>
+    <xsl:with-param name="node" select="$node"/>
+    <xsl:with-param name="direction" select="$direction"/>
+    <xsl:with-param name="left" select="$left"/>
+    <xsl:with-param name="right" select="$right"/>
+  </xsl:call-template>
+  <!-- FIXME: I don't like this at all. We use a character as the
+       blockquote icon, which requires a whole font, and which random
+       internet users might not have. Let's make SVGs and use the same
+       icon embedding we use for notes. -->
   <xsl:variable name="quote">
     <xsl:for-each select="$node[1]">
       <xsl:call-template name="l10n.gettext">
@@ -1738,6 +1307,9 @@ div.quote > div.inner:before {
     </xsl:for-each>
   </xsl:variable>
   <xsl:variable name="quotc" select="substring(concat($quote, '“'), 1, 1)"/>
+  <xsl:text>div.quote > div.inner:before { float: </xsl:text>
+  <xsl:value-of select="$left"/>
+  <xsl:text>content: '</xsl:text>
   <xsl:choose>
     <xsl:when test="contains('«‹', $quotc)">
       <xsl:text>«</xsl:text>
@@ -1751,10 +1323,10 @@ div.quote > div.inner:before {
     <xsl:otherwise>
       <xsl:text>“</xsl:text>
     </xsl:otherwise>
-  </xsl:choose><xsl:text>';
+  </xsl:choose>
+  <xsl:text>';
   font-family: "Century Schoolbook L";
-  font-size: </xsl:text>
-    <xsl:value-of select="$icons.size.quote"/><xsl:text>px;
+  font-size: {{$icons.size.quote}}px;
   font-weight: bold;
   line-height: </xsl:text>
   <xsl:choose>
@@ -1766,441 +1338,25 @@ div.quote > div.inner:before {
     </xsl:otherwise>
   </xsl:choose><xsl:text>em;
   margin: 0; padding: 0;
-  height: </xsl:text>
-    <xsl:value-of select="$icons.size.quote"/><xsl:text>px;
-  width: </xsl:text>
-    <xsl:value-of select="$icons.size.quote"/><xsl:text>px;
+  height: {{$icons.size.quote}}px;
+  width: {{$icons.size.quote}}px;
   text-align: center;
-  color: </xsl:text>
-    <xsl:value-of select="$color.bg.dark"/><xsl:text>;
-}
-div.quote > div.inner > div.title {
-  margin: 0;
-  margin-</xsl:text><xsl:value-of select="$left"/><xsl:text>: </xsl:text>
-    <xsl:value-of select="$icons.size.quote"/><xsl:text>px;
-}
-blockquote {
-  margin: 0; padding: 0;
-  margin-</xsl:text><xsl:value-of select="$left"/><xsl:text>: </xsl:text>
-    <xsl:value-of select="$icons.size.quote"/><xsl:text>px;
-}
-blockquote > *:first-child { margin-top: 0; }
-div.quote > div.inner > div.region > div.cite {
-  margin-top: 0.5em;
-  margin-</xsl:text><xsl:value-of select="$left"/><xsl:text>: </xsl:text>
-    <xsl:value-of select="$icons.size.quote"/><xsl:text>px;
-  color: </xsl:text><xsl:value-of select="$color.fg.gray"/><xsl:text>;
-}
-div.quote > div.inner > div.region > div.cite::before {
-  <!-- FIXME: i18n -->
-  content: '&#x2015; ';
-  color: </xsl:text><xsl:value-of select="$color.fg.gray"/><xsl:text>;
-}
-div.screen {
-  background-color: </xsl:text>
-    <xsl:value-of select="$color.bg.gray"/><xsl:text>;
-  border: solid 1px </xsl:text>
-    <xsl:value-of select="$color.gray"/><xsl:text>;
-}
-ol.steps, ul.steps {
-  padding: 0.5em 1em 0.5em 1em;
-  border: solid 1px </xsl:text><xsl:value-of select="$color.bg.gray"/><xsl:text>;
-  border-</xsl:text><xsl:value-of select="$left"/><xsl:text>: solid 4px </xsl:text>
-    <xsl:value-of select="$color.yellow"/><xsl:text>;
-}
-ol.steps .steps {
-  padding: 0;
-  border: none;
-  background-color: unset;
-}
-li.steps { margin-</xsl:text><xsl:value-of select="$left"/><xsl:text>: 1.44em; }
-li.steps li.steps { margin-</xsl:text><xsl:value-of select="$left"/><xsl:text>: 2.4em; }
-div.synopsis > div.inner > div.region > div.contents,
-div.synopsis > div.contents, div.synopsis > pre.contents {
-  padding: 0.5em 1em 0.5em 1em;
-  border-top: solid 1px;
-  border-bottom: solid 1px;
-  border-color: </xsl:text>
-    <xsl:value-of select="$color.fg.blue"/><xsl:text>;
-  background-color: </xsl:text>
-    <xsl:value-of select="$color.bg.gray"/><xsl:text>;
-}
-div.synopsis > div.inner > div.region > div.desc { font-style: italic; }
-div.synopsis div.code {
-  background: unset;
-  border: none;
-  padding: 0;
-}
-div.synopsis div.code > pre.contents { margin: 0; padding: 0; }
-div.unknown > div.inner > div.region > div.desc { font-style: italic; }
-div.table > div.desc { font-style: italic; }
-tr.shade {
-  background-color: </xsl:text><xsl:value-of select="$color.bg.gray"/><xsl:text>;
-}
-td.shade {
-  background-color: </xsl:text><xsl:value-of select="$color.bg.gray"/><xsl:text>;
-}
-tr.shade td.shade {
-  background-color: </xsl:text><xsl:value-of select="$color.bg.dark"/><xsl:text>;
-}
-
-span.app { font-style: italic; }
-span.cmd {
-  font-family: monospace,monospace; font-size: 0.83em;
-  background-color: </xsl:text>
-    <xsl:value-of select="$color.bg.gray"/><xsl:text>;
-  padding: 0 0.2em 0 0.2em;
-}
-span.cmd span.cmd { background-color: unset; padding: 0; }
-pre span.cmd { background-color: unset; padding: 0; }
-span.code {
-  font-family: monospace,monospace; font-size: 0.83em;
-  border-bottom: solid 1px </xsl:text><xsl:value-of select="$color.bg.dark"/><xsl:text>;
-}
-span.code span.code { border: none; }
-pre span.code { border: none; }
-span.em { font-style: italic; }
-span.em-bold {
-  font-style: normal; font-weight: bold;
-  color: </xsl:text><xsl:value-of select="$color.fg.dark"/><xsl:text>;
-}
-a span.em-bold {
-  color: </xsl:text><xsl:value-of select="$color.fg.blue"/><xsl:text>;
-}
-pre span.error {
-  color: </xsl:text><xsl:value-of select="$color.fg.red"/><xsl:text>;
-}
-span.file { font-family: monospace,monospace; font-size: 0.83em; }
-span.gui, span.guiseq { color: </xsl:text>
-  <xsl:value-of select="$color.fg.dark"/><xsl:text>; }
-a span.gui, a span.guiseq { color: </xsl:text>
-  <xsl:value-of select="$color.fg.blue"/><xsl:text>; }
-span.input { font-family: monospace,monospace; font-size: 0.83em; }
-pre span.input {
-  font-weight: bold;
-  color: </xsl:text><xsl:value-of select="$color.fg.dark"/><xsl:text>;
-}
-kbd {
-  font-family: inherit;
-  font-size: inherit;
-  color: </xsl:text>
-    <xsl:value-of select="$color.fg.dark"/><xsl:text>;
-  background-color: </xsl:text>
-    <xsl:value-of select="$color.bg.gray"/><xsl:text>;
-  border: solid 1px </xsl:text>
-    <xsl:value-of select="$color.gray"/><xsl:text>;
-  border-radius: 2px;
-  margin: 0 0.2em 0 0.2em;
-  padding: 0.2em 0.5em 0 0.5em;
-  white-space: nowrap;
-}
-kbd.key-Fn {
-  font-weight: bold;
-  color: </xsl:text>
-    <xsl:value-of select="$color.fg.blue"/><xsl:text>;
-}
-span.key a {
-  border-bottom: none;
-}
-a kbd {
-  color: </xsl:text><xsl:value-of select="$color.fg.blue"/><xsl:text>;
-  border-color: </xsl:text><xsl:value-of select="$color.fg.blue"/><xsl:text>;
-}
-span.keyseq {
-  color: </xsl:text>
-    <xsl:value-of select="$color.fg.dark"/><xsl:text>;
-  white-space: nowrap
-}
-a span.keyseq { color: </xsl:text>
-  <xsl:value-of select="$color.fg.blue"/><xsl:text>; }
-span.output { font-family: monospace,monospace; font-size: 0.83em; }
-pre span.output {
-  color: </xsl:text><xsl:value-of select="$color.fg"/><xsl:text>;
-}
-pre span.prompt {
-  color: </xsl:text><xsl:value-of select="$color.fg.dark"/><xsl:text>;
-}
-span.sys { font-family: monospace,monospace; font-size: 0.83em; }
-span.var { font-style: italic; }
-
-.ui-tile-img .media-controls { display: none; }
-span.media-audio, span.media-video { display: inline-block; }
-audio, video { display: block; margin: 0; }
-div.media > div.inner { display: inline-block; text-align: center; }
-.media-controls {
-  height: 30px;
-  margin: 0; padding: 0;
-  border-left: solid 1px </xsl:text><xsl:value-of select="$color.fg"/><xsl:text>;
-  border-right: solid 1px </xsl:text><xsl:value-of select="$color.fg"/><xsl:text>;
-  border-bottom: solid 1px </xsl:text><xsl:value-of select="$color.fg"/><xsl:text>;
-  background-color: </xsl:text><xsl:value-of select="$color.fg.dark"/><xsl:text>;
-  color: </xsl:text><xsl:value-of select="$color.bg"/><xsl:text>;
-  border-bottom-left-radius: 4px;
-  border-bottom-right-radius: 4px;
-  display: flex;
-  align-items: center;
-}
-.media-controls > * {
-  flex: 0 1 auto;
-}
-.media-controls > input.media-range {
-  flex: 1 0 auto;
-  background-color: </xsl:text><xsl:value-of select="$color.fg.dark"/><xsl:text>;
-  margin: 0 10px;
-  -webkit-appearance: none;
-}
-input.media-range::-webkit-slider-runnable-track {
-  height: 4px;
-  background: </xsl:text><xsl:value-of select="$color.fg.gray"/><xsl:text>;
-  border-radius: 2px;
-}
-input.media-range::-webkit-slider-thumb {
-  -webkit-appearance: none;
-  height: 16px;
-  width: 16px;
-  border-radius: 8px;
-  background: </xsl:text><xsl:value-of select="$color.bg.dark"/><xsl:text>;
-  border: solid 1px </xsl:text><xsl:value-of select="$color.fg.dark"/><xsl:text>;
-  margin-top: -6px;
-}
-input.media-range::-webkit-slider-thumb:hover,
-input.media-range::-webkit-slider-thumb:focus {
-  background: </xsl:text><xsl:value-of select="$color.bg.gray"/><xsl:text>;
-}
-input.media-range::-moz-range-track {
-  height: 4px;
-  background: </xsl:text><xsl:value-of select="$color.fg.gray"/><xsl:text>;
-  border-radius: 2px;
-}
-input.media-range::-moz-range-thumb {
-  -webkit-appearance: none;
-  height: 16px;
-  width: 16px;
-  border-radius: 8px;
-  background: </xsl:text><xsl:value-of select="$color.bg.dark"/><xsl:text>;
-  border: solid 1px </xsl:text><xsl:value-of select="$color.fg.dark"/><xsl:text>;
-  margin-top: -6px;
-}
-.media-controls-audio {
-  border-top: solid 1px </xsl:text><xsl:value-of select="$color.fg"/><xsl:text>;
-  border-radius: 4px;
-}
-button.media-play {
-  height: 30px;
-  padding: 0 6px 0 6px; line-height: 0;
-  background-color: </xsl:text><xsl:value-of select="$color.fg.dark"/><xsl:text>;
-  border: none;
-  border-</xsl:text><xsl:value-of select="$right"/><xsl:text>: solid 1px </xsl:text>
-    <xsl:value-of select="$color.fg"/><xsl:text>;
-}
-button.media-play:hover, button.media-play:focus {
-  background-color: </xsl:text><xsl:value-of select="$color.fg.blue"/><xsl:text>;
-}
-button.media-play .yelp-svg-fill { fill: </xsl:text>
-  <xsl:value-of select="$color.bg.gray"/><xsl:text>; }
-button.media-play .media-pause { display: none; }
-button.media-play-playing .media-play { display: none; }
-button.media-play-playing .media-pause { display: inline; }
-.media-time {
-  margin: 0;
-  font-size: 16px;
-  height: 30px;
-  line-height: 30px;
-}
-.media-time > span {
-  padding-</xsl:text><xsl:value-of select="$right"/><xsl:text>: 8px;
-}
-.media-duration {
-  font-size: 12px;
-  color: </xsl:text><xsl:value-of select="$color.bg.dark"/><xsl:text>;
-  opacity: 0.8;
-}
-.media-controls-ttml {
-  min-width: 630px;
-  border-bottom-left-radius: 0;
-  border-bottom-right-radius: 0;
-}
-div.media-ttml {
-  margin: 0; padding: 6px 0;
-  background-color: </xsl:text><xsl:value-of select="$color.bg.gray"/><xsl:text>;
-  border: solid 1px </xsl:text><xsl:value-of select="$color.fg"/><xsl:text>;
-  min-height: 24px;
-  border-bottom-left-radius: 4px;
-  border-bottom-right-radius: 4px;
-}
-.media-ttml-pre { white-space: pre; }
-.media-ttml-nopre { white-space: normal; }
-div.media-ttml-div {
-  text-align: </xsl:text><xsl:value-of select="$left"/><xsl:text>;
-  display: none;
-  margin: 0; padding: 0;
-}
-div.media-ttml-p {
-  text-align: </xsl:text><xsl:value-of select="$left"/><xsl:text>;
-  display: none;
-  margin: 0 auto;
-  max-width: 560px;
-  line-height: 1.44em;
-}
-div.media-ttml-div > * + * { margin-top: 1em; }
-div.yelp-data { display: none; }
-.ui-expander > div.inner > div.title span.title,
-.ui-expander > div.inner > div.hgroup span.title {
-  cursor: default;
-}
-.ui-expander > div.inner > div.title span.title:before,
-.ui-expander > div.inner > div.hgroup span.title:before {
-  font-weight: bold;
-  content: "⌃";
-  display: inline-block;
-  margin: 0;
-  color: </xsl:text><xsl:value-of select="$color.fg.blue"/><xsl:text>;
-  transform: translateY(0.2em) rotate(0deg);
-  -webkit-transform: translateY(0.2em) rotate(0deg);
-  transition: transform 0.2s linear;
-  transform-origin: 50% 30%;
-  -webkit-transform-origin: 50% 30%;
-  -webkit-transition: -webkit-transform 0.2s linear;
-  margin: 0 0.2em;
-}
-.ui-expander-c > div.inner > div.hgroup { border-bottom: none; }
-.ui-expander-e > div.inner > div.title span.title:before,
-.ui-expander-e > div.inner > div.hgroup span.title:before {</xsl:text>
-<xsl:choose>
-<xsl:when test="$direction = 'rtl'">
-  transform: translateY(0.2em) rotate(-180deg);
-  -webkit-transform: translateY(0.2em) rotate(-180deg);
-</xsl:when>
-<xsl:otherwise>
-  transform: translateY(0.2em) rotate(180deg);
-  -webkit-transform: translateY(0.2em) rotate(180deg);
-</xsl:otherwise>
-</xsl:choose><xsl:text>}
-.ui-expander > div.inner > div.title:hover,
-.ui-expander > div.inner > div.hgroup:hover * {
-  color: </xsl:text><xsl:value-of select="$color.fg.blue"/><xsl:text>;
-}
-.ui-expander > div.inner > div.hgroup > .subtitle {
-  margin-</xsl:text><xsl:value-of select="$left"/><xsl:text>: 2em;
-}
-.ui-expander-c > div.inner > div.region {
-  display: none;
-}
-.ui-expander-e > div.inner > div.region {
-  animation-name: yelp-ui-expander-e;
-  animation-duration: 0.2s;
-  animation-fill-mode: forwards;
-  transform-origin: 0 0;
-}
-@keyframes yelp-ui-expander-e {
-  from { transform: scaleY(0); }
-  to   { transform: scaleY(1); }
-}
-div.ui-expander-preview > div.inner > div.region {
-  transform-origin: 0 0;
-  transition: transform 0.2s linear, background-color 0.2s linear;
-  animation-name: none;
-}
-div.ui-expander-preview.ui-expander-c > div.inner {
-  max-height: 100px;
-  overflow: hidden;
-}
-div.ui-expander-preview.ui-expander-c > div.inner > div.region {
-  display: block;
-  transform: scaleY(0.4);
-  background-color: </xsl:text><xsl:value-of select="$color.bg.gray"/><xsl:text>;
-}
-div.ui-expander-preview.ui-expander-c > div.inner > div.region:hover {
-  background-color: </xsl:text><xsl:value-of select="$color.bg.blue"/><xsl:text>;
-  cursor: zoom-in;
-}
-div.ui-expander-preview.ui-expander-c > div.inner > div.region:hover * {
-  cursor: zoom-in;
-}
-div.ui-expander-preview > div.inner > div.region > * {
-  transform-origin: 0 0;
-  transition: transform 0.2s linear;
-}
-div.ui-expander-preview.ui-expander-c > div.inner > div.region > * {
-  transform: scaleX(0.4);
-}
-section.ui-expander-preview > div.inner > div.region > div.contents{
-  transform-origin: 0 0;
-  transition: transform 0.2s linear, background-color 0.2s linear;
-}
-section.ui-expander-preview.ui-expander-c > div.inner {
-  max-height: 140px;
-  overflow: hidden;
-}
-section.ui-expander-preview.ui-expander-c > div.inner > div.region {
-  display: block;
-}
-section.ui-expander-preview.ui-expander-c > div.inner > div.region > div.contents {
-  transform: scaleY(0.6);
-  background-color: </xsl:text><xsl:value-of select="$color.bg.gray"/><xsl:text>;
-}
-section.ui-expander-preview > div.inner > div.region > div.contents > * {
-  transform-origin: 0 0;
-  transition: transform 0.2s linear;
-}
-section.ui-expander-preview.ui-expander-c > div.inner > div.region > div.contents > * {
-  transform: scaleX(0.6);
-}
-section.ui-expander-preview.ui-expander-c > div.inner > div.region > div.contents:hover {
-  background-color: </xsl:text><xsl:value-of select="$color.bg.blue"/><xsl:text>;
-  cursor: zoom-in;
-}
-@media only screen and (max-width: 480px) {
-  article > div.region > div.contents > div.example,
-  article > div.region > section > div.inner > div.region > div.contents > div.example {
-    margin-left: -10px;
-    margin-right: -10px;
-  }
-  div.example {
-    padding-</xsl:text><xsl:value-of select="$left"/><xsl:text>: 6px;
-    padding-</xsl:text><xsl:value-of select="$right"/><xsl:text>: 10px;
-  }
-  article > div.region > div.contents > div.note,
-  article > div.region > section > div.inner > div.region > div.contents > div.note {
-    margin-left: -10px;
-    margin-right: -10px;
-    padding-left: 10px;
-    padding-right: 10px;
-  }
-  article > div.region > div.contents > div.note,
-  article > div.region > section > div.inner > div.region > div.contents > div.note {
-    border-left: none;
-    border-right: none;
-  }
-  div.note-sidebar {
-    float: none;
-    max-width: none;
-    margin-left: inherit;
-    margin-right: inherit;
-    padding-left: inherit;
-    padding-right: inherit;
-  }
-  div.note-sidebar > div.inner > div.title,
-  div.note-sidebar > div.inner > div.region > div.contents {
-    margin-left: 10px;
-    margin-right: 10px;
-  }
-  article > div.region > div.contents > div.steps,
-  article > div.region > section > div.inner > div.region > div.contents > div.steps {
-    margin-left: -10px;
-    margin-right: -10px;
-  }
-  div.steps > div.inner > div.title {
-    margin-left: 10px;
-    margin-right: 10px;
-  }
-  ol.steps, ul.steps {
-    padding: 0;
-    padding-</xsl:text><xsl:value-of select="$left"/><xsl:text>: 6px;
-    padding-</xsl:text><xsl:value-of select="$right"/><xsl:text>: 10px;
-  }
-}
-</xsl:text>
+  color: {{$color.bg.dark}};
+}</xsl:text>
+  <!-- Surely we should be able to figure out a way to conditionalize
+       the rotation direction in the text templates, or handle this a
+       different way. -->
+  <xsl:text>.ui-expander-e > div.inner > div.title span.title:before, </xsl:text>
+  <xsl:text>.ui-expander-e > div.inner > div.hgroup span.title:before {</xsl:text>
+  <xsl:choose>
+    <xsl:when test="$direction = 'rtl'">
+      <xsl:text>transform: translateY(0.2em) rotate(-180deg);</xsl:text>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:text>transform: translateY(0.2em) rotate(180deg);</xsl:text>
+    </xsl:otherwise>
+  </xsl:choose>
+  <xsl:text>}</xsl:text>
 </xsl:template>
 
 
@@ -2239,56 +1395,14 @@ All parameters can be automatically computed if not provided.
     </xsl:call-template>
   </xsl:param>
   <xsl:if test="$html.syntax.highlight">
-.hljs a {
-  color: inherit;
-  border-bottom: dotted 1px <xsl:value-of select="$color.fg.blue"/>;
-}
-.hljs a:hover, .hljs a:hover * { color: <xsl:value-of select="$color.fg.blue"/>; }
-.hljs-addition {
-  color: <xsl:value-of select="$color.fg.green"/>;
-  background-color: <xsl:value-of select="$color.bg.green"/>;
-}
-.hljs-deletion {
-  color: <xsl:value-of select="$color.fg.red"/>;
-  background-color: <xsl:value-of select="$color.bg.red"/>;
-}
-.hljs-emphasis  { font-style: italic; }
-.hljs-strong    { font-weight: bold; }
-.hljs-attr      { color: <xsl:value-of select="$color.fg.blue"/>; }
-.hljs-attribute { color: <xsl:value-of select="$color.fg.yellow"/>; }
-.hljs-built_in  { color: <xsl:value-of select="$color.fg.orange"/>; }
-.hljs-bullet    { color: <xsl:value-of select="$color.fg.green"/>; }
-.hljs-class     { }
-.hljs-code      { color: <xsl:value-of select="$color.fg.dark"/>; }
-.hljs-comment   { color: <xsl:value-of select="$color.fg.gray"/>; }
-.hljs-doctag    { }
-.hljs-formula   { color: <xsl:value-of select="$color.fg.dark"/>; }
-.hljs-function  { }
-.hljs-keyword   { color: <xsl:value-of select="$color.fg.purple"/>; }
-.hljs-link      { color: <xsl:value-of select="$color.fg.orange"/>; }
-.hljs-literal   { color: <xsl:value-of select="$color.fg.orange"/>; }
-.hljs-meta      { color: <xsl:value-of select="$color.fg.orange"/>; }
-.hljs-name      { color: <xsl:value-of select="$color.fg.red"/>; }
-.hljs-number    { color: <xsl:value-of select="$color.fg.orange"/>; }
-.hljs-params    { color: <xsl:value-of select="$color.fg.orange"/>; }
-.hljs-quote     { color: <xsl:value-of select="$color.fg.gray"/>; }
-.hljs-regexp    { color: <xsl:value-of select="$color.fg.red"/>; }
-.hljs-rest_arg  { }
-.hljs-section   { color: <xsl:value-of select="$color.fg.blue"/>; }
-.hljs-string    { color: <xsl:value-of select="$color.fg.green"/>; }
-.hljs-subst     { }
-.hljs-symbol    { color: <xsl:value-of select="$color.fg.green"/>; }
-.hljs-tag       { color: <xsl:value-of select="$color.fg.red"/>; }
-.hljs-title     { color: <xsl:value-of select="$color.fg.blue"/>; }
-.hljs-type      { }
-.hljs-variable  { }
-.hljs-selector-attr  { }
-.hljs-selector-class { color: <xsl:value-of select="$color.fg.red"/>; }
-.hljs-selector-id    { color: <xsl:value-of select="$color.fg.red"/>; }
-.hljs-selector-tag   { color: <xsl:value-of select="$color.fg.purple"/>; }
-.hljs-template-tag      { }
-.hljs-template-variable { }
-</xsl:if>
+    <xsl:call-template name="tmpl.file">
+      <xsl:with-param name="file" select="'css/syntax.css.tmpl'"/>
+      <xsl:with-param name="node" select="$node"/>
+      <xsl:with-param name="direction" select="$direction"/>
+      <xsl:with-param name="left" select="$left"/>
+      <xsl:with-param name="right" select="$right"/>
+    </xsl:call-template>
+  </xsl:if>
 </xsl:template>
 
 
diff --git a/xslt/common/tmpl.xsl b/xslt/common/tmpl.xsl
new file mode 100644
index 00000000..4df8e1ed
--- /dev/null
+++ b/xslt/common/tmpl.xsl
@@ -0,0 +1,162 @@
+<?xml version='1.0' encoding='UTF-8'?><!-- -*- indent-tabs-mode: nil -*- -->
+<!--
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program; see the file COPYING.LGPL. If not, see
+<http://www.gnu.org/licenses/>.
+-->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+                xmlns:dyn="http://exslt.org/dynamic";
+                xmlns:str="http://exslt.org/strings";
+                exclude-result-prefixes="dyn str"
+                version="1.0">
+
+<!--!!==========================================================================
+Text Templates
+Perform simple substitutions in text files.
+@revision[version=40 date=FIXME status=FIXME]
+
+This stylesheet contains templates to perform simple substitutions on text
+and files containing text. The primary purpose of these templates is to allow
+CSS and JavaScript to be maintained in separate files outside the XSLT, but
+still allow those files to reference variables for things like color themes
+and text directionality.
+
+The substitution evaluates anything between `{{` and `}}`. Usually, this will
+be a reference to a parameter or variable, but it can be any XPath expression.
+For example, `{{$color.fg}}` will be replaced with the primary text color.
+
+This syntax is similar to XSLT attribute value templates, except that it uses
+double curly braces to avoid conflicts with the many curly braces used in CSS
+and JavaScript files.
+
+[note .plain]
+This stylesheet was added in version 40.
+-->
+
+
+<!--**==========================================================================
+tmpl.file
+Perform text substitutions on a file.
+@revision[version=40 date=FIXME status=FIXME]
+
+[xsl:params]
+$file: The filename of the file to process for substitutions.
+$node: The node to create CSS for.
+$direction: The directionality of the text, either `ltr` or `rtl`.
+$left: The starting alignment, either `left` or `right`.
+$right: The ending alignment, either `left` or `right`.
+
+This template reads the file specified by the $file parameter and performs
+text substitutions. Due to XSLT limitations, the file must be a well-formed
+XML document. However, this template simply takes the string value of the
+document, so it is sufficient to wrap the text in a dummy element and ensure
+any `<` and `&` characters are escaped.
+
+See {tmpl} for information on the substitution syntax.
+
+[note .plain]
+This template was added in version 40.
+-->
+<xsl:template name="tmpl.file">
+  <xsl:param name="file"/>
+  <xsl:param name="node" select="."/>
+  <xsl:param name="direction">
+    <xsl:call-template name="l10n.direction"/>
+  </xsl:param>
+  <xsl:param name="left">
+    <xsl:call-template name="l10n.align.start">
+      <xsl:with-param name="direction" select="$direction"/>
+    </xsl:call-template>
+  </xsl:param>
+  <xsl:param name="right">
+    <xsl:call-template name="l10n.align.end">
+      <xsl:with-param name="direction" select="$direction"/>
+    </xsl:call-template>
+  </xsl:param>
+
+  <xsl:for-each select="document($file)">
+    <xsl:call-template name="tmpl.text">
+      <xsl:with-param name="text" select="string(.)"/>
+      <xsl:with-param name="node" select="$node"/>
+      <xsl:with-param name="direction" select="$direction"/>
+      <xsl:with-param name="left" select="$left"/>
+      <xsl:with-param name="right" select="$right"/>
+    </xsl:call-template>
+  </xsl:for-each>
+</xsl:template>
+
+
+<!--**==========================================================================
+tmpl.text
+Perform text substitutions on some text.
+@revision[version=40 date=FIXME status=FIXME]
+
+[xsl:params]
+$text: The text to process for substitutions.
+$node: The node to create CSS for.
+$direction: The directionality of the text, either `ltr` or `rtl`.
+$left: The starting alignment, either `left` or `right`.
+$right: The ending alignment, either `left` or `right`.
+
+This template performs text substitutions on the text in $text. It is called
+by {tmpl.file}, and it calls itself recursively after each substitution.
+
+See {tmpl} for information on the substitution syntax.
+
+[note .plain]
+This template was added in version 40.
+-->
+<xsl:template name="tmpl.text">
+  <xsl:param name="text" select="''"/>
+  <xsl:param name="node" select="."/>
+  <xsl:param name="direction">
+    <xsl:call-template name="l10n.direction"/>
+  </xsl:param>
+  <xsl:param name="left">
+    <xsl:call-template name="l10n.align.start">
+      <xsl:with-param name="direction" select="$direction"/>
+    </xsl:call-template>
+  </xsl:param>
+  <xsl:param name="right">
+    <xsl:call-template name="l10n.align.end">
+      <xsl:with-param name="direction" select="$direction"/>
+    </xsl:call-template>
+  </xsl:param>
+  <xsl:choose>
+    <xsl:when test="contains($text, '{{')">
+      <xsl:variable name="aft" select="substring-after($text, '{{')"/>
+      <xsl:choose>
+        <xsl:when test="contains($aft, '}}')">
+          <xsl:value-of select="substring-before($text, '{{')"/>
+          <xsl:value-of select="dyn:evaluate(substring-before($aft, '}}'))"/>
+          <xsl:call-template name="tmpl.text">
+            <xsl:with-param name="text" select="substring-after($aft, '}}')"/>
+            <xsl:with-param name="node" select="$node"/>
+            <xsl:with-param name="direction" select="$direction"/>
+            <xsl:with-param name="left" select="$left"/>
+            <xsl:with-param name="right" select="$right"/>
+          </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:value-of select="$text"/>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:when>
+    <xsl:otherwise>
+      <xsl:value-of select="$text"/>
+    </xsl:otherwise>
+  </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/xslt/dita/html/dita2xhtml.xsl b/xslt/dita/html/dita2xhtml.xsl
index 869b0010..e18c8cff 100644
--- a/xslt/dita/html/dita2xhtml.xsl
+++ b/xslt/dita/html/dita2xhtml.xsl
@@ -30,6 +30,7 @@ REMARK: Describe this module
 <xsl:import href="../../common/icons.xsl"/>
 <xsl:import href="../../common/html.xsl"/>
 <xsl:import href="../../common/ttml.xsl"/>
+<xsl:import href="../../common/tmpl.xsl"/>
 <xsl:import href="../../common/utils.xsl"/>
 
 <xsl:import href="../common/dita-map.xsl"/>
diff --git a/xslt/docbook/html/db2xhtml.xsl b/xslt/docbook/html/db2xhtml.xsl
index bc12a788..a6238984 100644
--- a/xslt/docbook/html/db2xhtml.xsl
+++ b/xslt/docbook/html/db2xhtml.xsl
@@ -32,6 +32,7 @@ DocBook documents into XHTML. This stylesheet sets the parameter
 <xsl:import href="../../common/color.xsl"/>
 <xsl:import href="../../common/icons.xsl"/>
 <xsl:import href="../../common/html.xsl"/>
+<xsl:import href="../../common/tmpl.xsl"/>
 <xsl:import href="../../common/utils.xsl"/>
 
 <xsl:import href="../common/db-chunk.xsl"/>
diff --git a/xslt/mallard/html/mal2xhtml.xsl b/xslt/mallard/html/mal2xhtml.xsl
index 9f54a663..f84ca976 100644
--- a/xslt/mallard/html/mal2xhtml.xsl
+++ b/xslt/mallard/html/mal2xhtml.xsl
@@ -37,6 +37,7 @@ and {ttml.features}.
 <xsl:import href="../../common/icons.xsl"/>
 <xsl:import href="../../common/html.xsl"/>
 <xsl:import href="../../common/ttml.xsl"/>
+<xsl:import href="../../common/tmpl.xsl"/>
 <xsl:import href="../../common/utils.xsl"/>
 
 <xsl:import href="../common/mal-gloss.xsl"/>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]