{% endif %}
+{{ body }}
+
diff --git a/site/themes/driller/shortcodes/card.yaml b/site/themes/driller/shortcodes/card.yaml
new file mode 100644
index 0000000..30716a1
--- /dev/null
+++ b/site/themes/driller/shortcodes/card.yaml
@@ -0,0 +1,23 @@
+# Component manifest for the card, one of the canonical components.
+#
+# A self-contained block of content: an optional image, a title that can link
+# somewhere, and a body. Every conforming theme implements `card` with the
+# `title`, `href` and `image` attributes.
+name: card
+description: A block of content with an optional image, a title, and a link.
+params:
+ - name: title
+ type: string
+ required: false
+ description: The card's heading.
+ - name: href
+ type: reference
+ required: false
+ description: Where the title links to.
+ - name: image
+ type: media
+ required: false
+ description: >-
+ An image shown above the title. It is decorative and carries no alt
+ text, so describe anything the reader needs in the body.
+body: markdown
diff --git a/site/themes/driller/shortcodes/image.html.jinja b/site/themes/driller/shortcodes/image.html.jinja
new file mode 100644
index 0000000..1b6e135
--- /dev/null
+++ b/site/themes/driller/shortcodes/image.html.jinja
@@ -0,0 +1,5 @@
+{#- The image, as a figure so a caption stays attached to it. `src` goes
+ through `url()` so a sub-path install serves it with the prefix.
+ `data-canonical` and `data-part` are the markup contract every conforming
+ theme's image carries. -#}
+{% if attrs.caption %}{{ attrs.caption }}{% endif %}
diff --git a/site/themes/driller/shortcodes/image.yaml b/site/themes/driller/shortcodes/image.yaml
new file mode 100644
index 0000000..870ccd1
--- /dev/null
+++ b/site/themes/driller/shortcodes/image.yaml
@@ -0,0 +1,23 @@
+# Component manifest for the image, one of the canonical components.
+#
+# An image with alt text and an optional caption, written self-closing:
+# `{% image src="/media/diagram.png" alt="The request flow" /%}`. Every
+# conforming theme implements `image` with the `src`, `alt` and `caption`
+# attributes.
+name: image
+description: An image with alt text and an optional caption.
+params:
+ - name: src
+ type: media
+ required: true
+ description: The image file.
+ - name: alt
+ type: string
+ required: true
+ description: >-
+ What the image shows, for readers who cannot see it. Use an empty string
+ only for a purely decorative image.
+ - name: caption
+ type: string
+ required: false
+ description: Visible text shown under the image.
diff --git a/site/themes/driller/shortcodes/tab.html.jinja b/site/themes/driller/shortcodes/tab.html.jinja
new file mode 100644
index 0000000..ba6f437
--- /dev/null
+++ b/site/themes/driller/shortcodes/tab.html.jinja
@@ -0,0 +1 @@
+{{ body }}
\ No newline at end of file
diff --git a/site/themes/driller/shortcodes/tab.yaml b/site/themes/driller/shortcodes/tab.yaml
new file mode 100644
index 0000000..6bbda13
--- /dev/null
+++ b/site/themes/driller/shortcodes/tab.yaml
@@ -0,0 +1,23 @@
+# Component manifest for one tab panel.
+#
+# A `tab` is normally read by its `tabs` parent rather than rendered on its own:
+# the parent builds the strip and the panels from `children`. This template is
+# what a `tab` written outside a `tabs` renders as, and it emits the body
+# unchanged -- the same thing a stray `[tab]` did.
+name: tab
+description: One panel of a tabbed switcher. Belongs inside `tabs`.
+params:
+ - name: label
+ type: string
+ required: false
+ description: The text on the tab button.
+ - name: id
+ type: string
+ required: false
+ description: >-
+ The panel's identifier, used for the anchor and for remembering the
+ selection. Defaults to the slugified label.
+body: markdown
+# `[tab "macOS"]` wrote the label as a bare word.
+migrate:
+ positional: [label]
diff --git a/site/themes/driller/shortcodes/tabs.html.jinja b/site/themes/driller/shortcodes/tabs.html.jinja
new file mode 100644
index 0000000..a23da56
--- /dev/null
+++ b/site/themes/driller/shortcodes/tabs.html.jinja
@@ -0,0 +1,29 @@
+{#- The tab container.
+
+ Both loops read `children`, not `body`: by the time this template runs, the
+ body is one string of rendered markup and a label cannot be recovered from
+ it. `children` carries each `{% tab %}` with its attributes still resolved
+ and its own body separate, which is what makes the strip constructible.
+
+ The panels are emitted flush against each other -- no blank line between a
+ `
` and the next `
` while the panels themselves stay one HTML block.
+
+ `tabs.js` handles switching, group synchronisation, keyboard navigation and
+ persistence. The first tab is marked active here so the page is correct
+ before any script runs.
+
+ `data-canonical` and `data-part` are the markup contract every conforming
+ theme's tabs carry: the tab list, each tab and each panel.
+-#}
+{%- set group = attrs.group | default("default") -%}
+
+{%- for c in children if c.name == "tab" %}{% set id = c.attrs.id | default(c.attrs.label | slugify) %}{% endfor -%}
+
+{%- for c in children if c.name == "tab" %}{% set id = c.attrs.id | default(c.attrs.label | slugify) %}
+{{ c.body }}
+
{% endfor -%}
+
diff --git a/site/themes/driller/shortcodes/tabs.yaml b/site/themes/driller/shortcodes/tabs.yaml
new file mode 100644
index 0000000..a274463
--- /dev/null
+++ b/site/themes/driller/shortcodes/tabs.yaml
@@ -0,0 +1,16 @@
+# Component manifest for the tab container.
+#
+# The one shipped component that composes its children rather than wrapping its
+# body: the tab strip is a button per `{% tab %}` child, carrying that child's
+# label, and it has to be emitted before any panel. The template reads
+# `children` for exactly that.
+name: tabs
+description: A tabbed switcher whose panels are its `tab` children.
+params:
+ - name: group
+ type: string
+ required: false
+ description: >-
+ Tab groups with the same name switch together, and the selection is
+ remembered per group. Defaults to `default`.
+body: markdown
diff --git a/site/themes/driller/shortcodes/tag.html.jinja b/site/themes/driller/shortcodes/tag.html.jinja
new file mode 100644
index 0000000..45141b9
--- /dev/null
+++ b/site/themes/driller/shortcodes/tag.html.jinja
@@ -0,0 +1,5 @@
+{#- The tag. Inline, so its body is a label spliced flush into the line. The
+ class is the one page tags already use, so both look alike.
+ `data-canonical` is the markup contract every conforming theme's tag
+ carries. -#}
+{% if attrs.href %}{{ body }}{% else %}{{ body }}{% endif %}
diff --git a/site/themes/driller/shortcodes/tag.yaml b/site/themes/driller/shortcodes/tag.yaml
new file mode 100644
index 0000000..7cf08e5
--- /dev/null
+++ b/site/themes/driller/shortcodes/tag.yaml
@@ -0,0 +1,15 @@
+# Component manifest for the tag, one of the canonical components.
+#
+# A small label written inside a sentence, optionally a link. It is declared
+# `inline: true`, so it stays inside the paragraph, heading or list item it is
+# written in. Every conforming theme implements `tag` with the `href`
+# attribute.
+name: tag
+description: A small inline label, optionally a link.
+inline: true
+params:
+ - name: href
+ type: reference
+ required: false
+ description: Where the label links to.
+body: inline
diff --git a/site/themes/driller/templates/base.html.jinja b/site/themes/driller/templates/base.html.jinja
index 8f9a675..1a94721 100644
--- a/site/themes/driller/templates/base.html.jinja
+++ b/site/themes/driller/templates/base.html.jinja
@@ -19,6 +19,7 @@
+
@@ -61,13 +62,13 @@
·Issues·
- Built with Accent CMS
+ Built with Accent CMS{{ accent.version }} ({{ accent.git_hash }})
{% for js in theme.assets.js %}
-
+
{% endfor %}
-