From 4852f100a3191fd731ed2bc63d21516461725805 Mon Sep 17 00:00:00 2001
From: Ainsley Ellis <ainsleymae@proton.me>
Date: Sat, 31 May 2025 07:16:12 -0400
Subject: [PATCH] Add XSLT for CVs

---
 assets/css/all.css                 | 122 ++++++++++++++++++++++++++
 assets/css/print.css               |  16 ++++
 assets/css/screen.css              |   8 ++
 templates/cv.xsl                   | 132 +++++++++++++++++++++++++++++
 templates/fragments/header.xml     |  16 ++++
 templates/fragments/objectives.xml |  14 +++
 6 files changed, 308 insertions(+)
 create mode 100755 assets/css/all.css
 create mode 100755 assets/css/print.css
 create mode 100755 assets/css/screen.css
 create mode 100644 templates/cv.xsl
 create mode 100644 templates/fragments/header.xml
 create mode 100644 templates/fragments/objectives.xml

diff --git a/assets/css/all.css b/assets/css/all.css
new file mode 100755
index 0000000..8bef177
--- /dev/null
+++ b/assets/css/all.css
@@ -0,0 +1,122 @@
+body {
+	margin: 0;
+}
+
+h1,
+h2,
+h3,
+h4 {
+	margin: 0;
+}
+
+h1 {
+	text-align: center;
+}
+
+h2 {
+	margin-block-end: 0.25em;
+	margin-bottom: 0.25em;
+	border-block-end: 0.0625em solid currentColor;
+	border-bottom: 0.0625em solid currentColor;
+	font-variant: small-caps;
+}
+
+.headline {
+	margin-block-end: 1em;
+	margin-bottom: 1em;
+	text-align: center;
+}
+
+address {
+	margin-block-end: 1em;
+	margin-bottom: 1em;
+	display: flex;
+	justify-content: space-between;
+	flex-wrap: wrap;
+	gap: 0.5em;
+}
+
+address>ul {
+	margin: 0;
+	padding: 0;
+	display: inline-block;
+}
+
+address> :last-child {
+	text-align: end;
+}
+
+p {
+	margin: 0;
+}
+
+hr {
+	width: 50%;
+}
+
+main > section+section,
+article+article {
+	margin-block-start: 1em;
+	margin-top: 1em;
+}
+
+article>article {
+	margin-block-start: 0.5em;
+	margin-top: 0.5em;
+}
+
+article header {
+display: flex;
+justify-content: space-between;
+flex-wrap: wrap;
+gap: 0.5em;
+}
+
+article header p {
+	text-align: end;
+}
+
+article > p {
+	margin-block-start: 0.25em;
+	margin-top: 0.25em;
+}
+
+dl {
+	margin: 0;
+}
+
+dt {
+	font-weight: 700;
+}
+
+dd {
+	margin-inline-start: 2.5em;
+	margin-left: 2.5em;
+}
+
+#skills h3 {
+margin-block-start: 0.25em;
+margin-top: 0.25em;
+}
+
+#skills ul {
+	margin: 0;
+	padding: 0;
+	list-style-type: none;
+}
+
+#skills li {
+	display: inline;
+}
+
+#skills li::after {
+	content: ",";
+}
+
+#skills li:last-child::after {
+	content: none;
+}
+
+footer {
+text-align: center;
+}
diff --git a/assets/css/print.css b/assets/css/print.css
new file mode 100755
index 0000000..064ba1f
--- /dev/null
+++ b/assets/css/print.css
@@ -0,0 +1,16 @@
+@page {
+	size: 8.5in 11in;
+	margin: 0.5in;
+	padding: 0;
+}
+
+body { margin: 0; }
+
+article,
+footer {
+	break-inside: avoid-page;
+}
+
+a:visited {
+	color: blue;
+}
diff --git a/assets/css/screen.css b/assets/css/screen.css
new file mode 100755
index 0000000..6d4905d
--- /dev/null
+++ b/assets/css/screen.css
@@ -0,0 +1,8 @@
+body {
+	padding: 1em;
+}
+
+main {
+    margin: 0 auto;
+    max-width: 50em;
+}
diff --git a/templates/cv.xsl b/templates/cv.xsl
new file mode 100644
index 0000000..91a143b
--- /dev/null
+++ b/templates/cv.xsl
@@ -0,0 +1,132 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+	<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes" standalone="yes" doctype-system="about:legacy-compat" />
+	<xsl:param name="title" select="'Ainsley Ellis CV'"/>
+	<xsl:template name="company">
+		<article>
+			<h3><xsl:value-of select="./name/text()"/></h3>
+			<xsl:for-each select="./role">
+				<xsl:call-template name="role"/>
+			</xsl:for-each>
+		</article>
+	</xsl:template>
+	<xsl:template name="project">
+		<article>
+			<header>
+				<h3><xsl:value-of select="./name/text()" /></h3>
+				<p>
+					<xsl:value-of select="./activity/text()" />
+				</p>
+			</header>
+			<xsl:copy-of select="./description/node()" />
+		</article>
+	</xsl:template>
+	<xsl:template name="institution">
+		<article>
+			<h3><xsl:value-of select="./name/text()" /></h3>
+			<dl>
+				<xsl:for-each select="./degree">
+					<dt><xsl:value-of select="./level/text()" /> in <xsl:value-of select="./concentration/text()" /></dt>
+					<xsl:for-each select="./distinction">
+						<dd><xsl:value-of select="./text()" /></dd>
+					</xsl:for-each>
+				</xsl:for-each>
+			</dl>
+		</article>
+	</xsl:template>
+	<xsl:template name="skill-category">
+		<section>
+			<h3><xsl:value-of select="./name/text()" /></h3>
+			<ul role="list">
+				<xsl:for-each select="./item">
+					<li><xsl:value-of select="./text()" /></li>
+				</xsl:for-each>
+			</ul>
+		</section>
+	</xsl:template>
+	<xsl:template name="role">
+		<article>
+			<header>
+				<h4><xsl:value-of select="./title/text()" /></h4>
+				<p>
+					<time>
+						<xsl:attribute name="datetime">
+							<xsl:value-of select="./duration/start[@datetime]" />
+						</xsl:attribute>
+						<xsl:value-of select="./duration/start/text()" />
+					</time>
+						to 
+					<xsl:choose>
+						<xsl:when test="./duration/end">
+						<time>
+							<xsl:attribute name="datetime">
+								<xsl:value-of select="./duration/end[@datetime]" />
+							</xsl:attribute>
+							<xsl:value-of select="./duration/end/text()" />
+						</time>
+					</xsl:when>
+						<xsl:otherwise>Present</xsl:otherwise>
+					</xsl:choose>
+				</p>
+			</header>
+			<xsl:copy-of select="./description/node()" />
+		</article>
+	</xsl:template>
+	<xsl:template match="/cv" data-xmlns="http://www.w3.org/1999/xhtml">
+		<html lang="en" dir="ltr">
+			<!-- The Yesterweb is dead, long live the Yesterweb! -->
+			<head>
+				<meta charset="UTF-8" />
+				<meta name="viewport" content="width=device-width, initial-scale=1.0" />
+				<meta name="color-scheme" content="light dark" />
+				<title><xsl:value-of select="$title"/></title>
+				<link rel="stylesheet" href="../assets/css/all.css" />
+				<link rel="stylesheet" href="../assets/css/screen.css" media="screen" />
+				<link rel="stylesheet" href="../assets/css/print.css" media="print" />
+			</head>
+			<body>
+				<main>
+					<xsl:copy-of select="document('./fragments/header.xml')"/>
+					<!-- <xsl:copy-of select="/cv"/> -->
+					<section id="objective">
+						<h2>Objective</h2>
+						<!-- select specific objective from xml file -->
+						<xsl:copy-of select="document('./fragments/objectives.xml')/objectives/objective[@name='web-development']/node()"/>
+					</section>
+					<section id="work-history">
+						<h2>Work History</h2>
+						<xsl:for-each select="./work-history/company">
+							<xsl:call-template name="company"/>
+						</xsl:for-each>
+					</section>
+					<section id="open-source-contributions">
+						<h2>Open-source Contributions</h2>
+						<xsl:for-each select="./foss-contributions/project">
+							<xsl:call-template name="project"/>
+						</xsl:for-each>
+					</section>
+					<section id="education">
+						<h2>Education</h2>
+						<xsl:for-each select="./education/institution">
+							<xsl:call-template name="institution"/>
+						</xsl:for-each>
+					</section>
+					<section id="skills">
+						<h2>Skills</h2>
+						<xsl:for-each select="./skills/category">
+							<xsl:call-template name="skill-category"/>
+						</xsl:for-each>
+					</section>
+				</main>
+				<hr/>
+				<xsl:if test="$hash">
+					<footer>
+						<p>
+							<small>Built with XSLT (<a>
+									<xsl:attribute name="href">https://git.pub0.org/heyainsleymae/curriculum-vitae/src/commit/<xsl:value-of select="$hash" /></xsl:attribute>view commit</a>)</small></p>
+					</footer>
+				</xsl:if>
+			</body>
+		</html>
+	</xsl:template>
+</xsl:stylesheet>
diff --git a/templates/fragments/header.xml b/templates/fragments/header.xml
new file mode 100644
index 0000000..c4f30d1
--- /dev/null
+++ b/templates/fragments/header.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<header>
+	<h1>Ainsley Ellis</h1>
+	<p class="headline">Artist, Programmer, Digital Citizen</p>
+	<address>
+		<ul role="list">
+			<li>Email: <a href="mailto:ainsleymae@proton.me">Ainsley Ellis &lt;ainsleymae@proton.me&gt;</a>
+			</li>
+			<li>Website: <a href="https://ains.me">ains.me</a></li>
+			<li>
+				<code>heyainsleymae</code> on <a href="https://codeberg.org/heyainsleymae">Codeberg</a>, <a href="https://github.com/heyainsleymae">GitHub</a>, and <a href="https://npmjs.com/~heyainsleymae">npm</a>
+			</li>
+		</ul>
+		<p>America/New_York (NYC)<br />Remote or hybrid work, open to relocation</p>
+	</address>
+</header>
diff --git a/templates/fragments/objectives.xml b/templates/fragments/objectives.xml
new file mode 100644
index 0000000..9dedf6b
--- /dev/null
+++ b/templates/fragments/objectives.xml
@@ -0,0 +1,14 @@
+<objectives>
+	<objective name="web-development">
+		<p>
+			I am an accomplished full-stack developer with a passion for
+			accessibility and open web standards. I have experience building large, scalable
+			projects from scratch, improving internal processes and
+			design systems, and mentoring junior engineers. I hold a
+			<b>Section 508 Trusted Tester</b> certification from the United
+			States Department of Homeland Security. I want to work with a
+			diverse team to build accessible products that entertain,
+			inform, or assist the public.
+		</p>
+	</objective>
+</objectives>