Aside: you could factor that out by making an <xsl:template match="figure"> that does the choose on element, then your repeated code can just expand that template. The immense power in XSLT comes from xpath to make it easy to match on things like "all figures that contain a <name series=1/>":
<table>
<tr><th>Skylanders figure</th><th>Note</th></tr>
<xsl:apply-templates select="skylanders/figure[name/@series=1]">
<xsl:sort select="name"/>
</xsl:apply-templates>
</table>
If you further refactor the XML, you could do e.g.
<skylanders>
<figure name="Hijinx" element="undead" series="3" note=""/>
<figure name="Eye Small" element="undead" series="3" note=""/>
<figure name="Air Screamer" element="air" series="3" note="Storm Warning"/>
...
<figure name="Blast Zone" element="fire" series="2" note="Bottom only"/>
<series name="Skylanders Giants" id="1"/>
<series name="Skylanders SWAP Force" id="2"/>
<series name="Skylanders Trap Team" id="3"/>
<series name="Skylanders Superchargers" id="4"/>
<series name="Skylanders Imaginators" id="5"/>
</skylanders>
And then you can entirely eliminate the verbosity in your XSL. The templates become:
<xsl:template match="series">
<h2><xsl:value-of select="@name"/></h2>
<table>
<tr><th>Skylanders figure</th><th>Note</th></tr>
<xsl:apply-templates select="/skylanders/figure[@series=current()/@id]"><xsl:sort select="name"/></xsl:apply-templates>
</table>
</xsl:template>
<xsl:template match="figure">
<tr class="element-{ @element }">
<td><xsl:value-of select="@name" /></td>
<td><xsl:value-of select="@note" /></td>
</tr>
</xsl:template>
...
<style>
.element-air td { background: skyblue; color: black; }
.element-dark td { background: dimgrey; color: black; }
.element-earth td { background: saddlebrown; color: white; }
.element-fire td { background: firebrick; color: white; }
.element-life td { background: darkgreen; color: white; }
.element-light td { background: ivory; color: black; }
.element-magic td { background: purple; color: white; }
.element-tech td { background: orangered; color: white; }
.element-undead td { background: midnightblue; color: white; }
.element-water td { background: blue; color: white; }
.element-none td { background: black; color: white; }
</style>
...
Then in your body area:
<xsl:apply-templates select="skylanders/series"><xsl:sort select="id"/></xsl:apply-templates>
You can actually use XSL to do the XML refactor too! ChatGPT happily obliges a template to do so:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/skylanders">
<skylanders>
<xsl:apply-templates select="figure"/>
</skylanders>
</xsl:template>
<xsl:template match="figure">
<figure>
<xsl:attribute name="name">
<xsl:value-of select="name"/>
</xsl:attribute>
<xsl:attribute name="element">
<xsl:call-template name="convert-element">
<xsl:with-param name="code" select="name/@element"/>
</xsl:call-template>
</xsl:attribute>
<xsl:attribute name="series">
<xsl:value-of select="name/@series"/>
</xsl:attribute>
<xsl:attribute name="note">
<xsl:value-of select="normalize-space(note)"/>
</xsl:attribute>
</figure>
</xsl:template>
<xsl:template name="convert-element">
<xsl:param name="code"/>
<xsl:choose>
<xsl:when test="$code = 0">air</xsl:when>
<xsl:when test="$code = 1">dark</xsl:when>
<xsl:when test="$code = 2">earth</xsl:when>
<xsl:when test="$code = 3">fire</xsl:when>
<xsl:when test="$code = 4">life</xsl:when>
<xsl:when test="$code = 5">light</xsl:when>
<xsl:when test="$code = 6">magic</xsl:when>
<xsl:when test="$code = 7">tech</xsl:when>
<xsl:when test="$code = 8">undead</xsl:when>
<xsl:when test="$code = 9">water</xsl:when>
<xsl:when test="$code = 10">none</xsl:when>
<xsl:otherwise>unknown</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Then `xsltproc refactor.xsl skylanders.xml > skylanders-refactored.xml`
As I've said elsewhere, I like XSL for its beginner-approachability, so not doing a bunch of factoring is fine, but I also like it for its power: such factoring into simple templates is possible once you wrap your head around the idea (as with CSS). Using for-each or choose should be a sign you're doing it wrong. Ideally if you did your data model well, you just do simple template expansions everywhere.