Plan
ID:SPEC-008Status:review

Unbuilt Runes — High-Level Spec

Runes listed in plan/spec/community-runes.md that do not yet have schemas in packages/runes/src/tags/


Overview

17 runes are referenced in the community runes spec that have no implementation. This document defines each rune's purpose, attributes, content model, and target package so they can be built out incrementally.

Conventions follow existing rune patterns:

  • Attributes are declared via @attribute decorators on the Model class
  • Content is reinterpreted using @group decorators to split header/body sections
  • Output uses createComponentRenderable with properties (meta tags) and refs (structural elements)
  • Schema.org extractors are added when a matching type exists

Core Runes (3)

These are universal primitives that belong in the built-in core, not in a package.

gallery

Purpose: Multi-image container with grid, carousel, or masonry layout and optional lightbox overlay.

Aliases:

Attributes:

NameTypeDefaultRequiredDescription
layoutString'grid'NoDisplay mode: grid, carousel, masonry
columnsNumber3NoGrid column count (grid/masonry only)
lightboxBooleantrueNoEnable click-to-enlarge overlay
gapString'md'NoSpacing between items: sm, md, lg
captionStringNoGallery-level caption

Content model:

  • Images (![alt](src)) become gallery items
  • Image alt text becomes the item caption
  • Paragraphs of text between images are ignored (or treated as section breaks in masonry)
  • Headings become gallery section titles (for grouped galleries)

Transform output:

  • typeof: Gallery
  • Tag: <figure>
  • Properties: layout, columns, lightbox, gap, caption
  • Refs: items (list of figure elements), caption (figcaption)

Behavior: Carousel mode needs JS for navigation (prev/next, swipe). Lightbox needs JS for overlay. Both are @refrakt-md/behaviors candidates.


stat

Purpose: Key metric display — a prominent number with label and optional trend indicator. Used for dashboards, KPI sections, and data highlights.

Aliases: metric

Attributes:

NameTypeDefaultRequiredDescription
valueStringYesThe metric value (e.g., "99.9%", "$4.2M", "1,247")
labelStringYesWhat the metric measures
trendStringNoTrend direction: up, down, flat
changeStringNoChange amount (e.g., "+12%", "-3 pts")
iconStringNoIcon name from theme icon registry

Content model:

  • Self-closing or minimal content. Most data comes from attributes.
  • Optional paragraph child becomes a description/context line.

Transform output:

  • typeof: Stat
  • Tag: <div>
  • Properties: value (span), label (span), trend, change (span), icon
  • Refs: description (p, if content provided)

Identity transform: Mostly declarative — modifiers for trend, structure injection for value/label/change display. No JS needed.


math

Purpose: Mathematical notation rendered from LaTeX/KaTeX syntax. Supports both inline and block (display) mode.

Aliases: equation, formula

Attributes:

NameTypeDefaultRequiredDescription
displayBooleantrueNoBlock display mode (centered, full-width) vs inline
labelStringNoEquation label for cross-referencing

Content model:

  • The text content inside the tag is treated as raw LaTeX/KaTeX source — not parsed as Markdown.
  • Similar to how {% diagram %} treats content as Mermaid source.

Transform output:

  • typeof: Math
  • Tag: <div> (display) or <span> (inline)
  • Properties: source (the raw LaTeX string), label
  • The actual rendering (LaTeX → MathML/SVG) happens either at build time (preferred, via a KaTeX transform similar to Shiki for code) or at runtime via a behavior/web component.

Implementation note: Rendering strategy mirrors the Diagram rune — either a build-time transform (like @refrakt-md/highlight does for code blocks) or a web component that initializes from the source attribute. Build-time is preferred for performance and SSR.


@refrakt-md/learning (6)

Educational and instructional content. These compose within a lesson page: objective at the top, concept runes introduce terminology, howto/recipe teach procedures, exercise provides practice, quiz tests retention.

concept

Purpose: Term definition with explanation, examples, and related concepts. The building block for glossaries and knowledge bases.

Aliases: definition

Schema.org: DefinedTerm

Attributes:

NameTypeDefaultRequiredDescription
termStringYesThe term being defined
idStringNoUnique identifier for cross-referencing (auto-derived from term if omitted)

Content model:

  • First paragraph → the definition
  • ## Examples heading → examples section
  • ## Related heading → list of related term references
  • Other headings → additional sections (etymology, usage notes, etc.)

Transform output:

  • typeof: Concept
  • Tag: <article>
  • Properties: term (dt), id
  • Refs: definition (dd), examples (div), related (ul of links)

Glossary integration: The glossary rune (below) collects all concept definitions and the build pipeline auto-links term occurrences across the site.


exercise

Purpose: Practice problem with a prompt, optional hints, and a revealable solution. Encourages active learning.

Aliases:

Attributes:

NameTypeDefaultRequiredDescription
difficultyString'medium'Noeasy, medium, hard
pointsNumberNoPoint value (for graded contexts)
typeString'open'Noopen (free response), code (expects code), multiple-choice

Content model:

  • First paragraph/section → the problem prompt
  • ## Hints or ## Hint heading → progressive hints (each child is one hint, revealed sequentially)
  • ## Solution heading → the solution (hidden by default, revealed on click)
  • Code fences in the solution section get syntax highlighting

Transform output:

  • typeof: Exercise
  • Tag: <article>
  • Properties: difficulty, points, type
  • Refs: prompt (div), hints (ol, each li is a hint), solution (div, initially hidden)

Behavior: Hint revelation (show one at a time) and solution toggle. Candidate for @refrakt-md/behaviors.


quiz

Purpose: Assessment with multiple questions, answer options, and scoring. Supports multiple-choice, true/false, and fill-in-the-blank.

Aliases:

Schema.org: Quiz

Attributes:

NameTypeDefaultRequiredDescription
titleStringNoQuiz title
passingScoreNumberNoMinimum score to pass (percentage)
shuffleBooleanfalseNoRandomize question order

Content model:

  • Each ## heading → a question
  • Unordered list under a question → answer options
  • List items starting with * or marked with [x] → correct answer(s)
  • Blockquotes under a question → explanation shown after answering
  • Paragraphs → question context

Example:

{% quiz title="Module 1 Assessment" passingScore=70 %}

## What does the identity transform produce?

- [ ] React components
- [x] BEM-classed HTML elements
- [ ] Raw Markdown
- [ ] JSON data

> The identity transform adds BEM classes, data attributes, and structural elements to produce framework-agnostic HTML.

## True or false: Runes modify the Markdoc parser.

- [ ] True
- [x] False

> Runes work within the standard Markdoc tag system. They reinterpret Markdown children, not the parser itself.

{% /quiz %}

Transform output:

  • typeof: Quiz
  • Tag: <form>
  • Properties: title, passingScore, shuffle
  • Refs: questions (ol), each containing prompt, options (ul of radio/checkbox inputs), explanation (blockquote)

Behavior: Interactive form with score calculation, answer checking, and result display. Significant JS — likely a web component or full behavior.


glossary

Purpose: Collection of terms with definitions, rendered as a navigable index. At build time, auto-links term occurrences across the site.

Aliases:

Schema.org: DefinedTermSet

Attributes:

NameTypeDefaultRequiredDescription
autoLinkBooleantrueNoEnable site-wide auto-linking of terms
groupByString'letter'NoGrouping: letter (alphabetical), category, none

Content model:

  • Child {% concept %} runes → individual terms
  • Or: definition list (term\n: definition) syntax → auto-converted to concept entries
  • Headings → category group labels (when groupBy="category")

Transform output:

  • typeof: Glossary
  • Tag: <section>
  • Properties: autoLink, groupBy
  • Refs: terms (dl or grouped divs), index (alphabetical jump links)

Implementation note: The auto-linking feature requires build pipeline integration beyond standard rune transforms. During the content build, the pipeline collects all glossary terms and rewrites matching text nodes across other pages into links. This is similar to how toc collects headings — but cross-page. This should be implemented as a post-build pass in @refrakt-md/content.


prerequisite

Purpose: Declares dependencies between content pages. "Complete X before starting this lesson." Themes can render these as a learning path or dependency graph.

Aliases:

Attributes:

NameTypeDefaultRequiredDescription
pathStringYesPath to the prerequisite page (relative URL)
labelStringNoDisplay label (defaults to the target page's title)
requiredBooleantrueNoWhether this is a hard requirement or a recommendation

Content model:

  • Self-closing tag (no children). All data comes from attributes.
  • Multiple {% prerequisite %} tags can appear on a page.

Transform output:

  • typeof: Prerequisite
  • Tag: <a> or <div>
  • Properties: path, label, required

Implementation note: Like glossary, the full power of this rune comes from cross-page awareness at build time. The content pipeline collects all prerequisite declarations and builds a dependency graph. Themes can render this as a progress tracker, learning path visualization, or simple prerequisite list. The rune itself is simple — the complexity is in the build pipeline integration.


objective

Purpose: Learning outcome statement. "After this lesson, you will be able to..." Typically placed at the top of a lesson page.

Aliases: learning-outcome

Attributes:

NameTypeDefaultRequiredDescription
verbStringNoBloom's taxonomy verb (e.g., understand, apply, analyze) — used for metadata, not rendering

Content model:

  • Unordered list → individual learning objectives
  • Paragraphs → introductory context

Example:

{% objective %}
After completing this lesson, you will be able to:

- Explain the difference between core and community runes
- Install and configure a community rune package
- Write a custom local rune for your project
{% /objective %}

Transform output:

  • typeof: Objective
  • Tag: <aside> (semantic — this is supplementary info about the lesson)
  • Properties: verb
  • Refs: objectives (ul)

Identity transform: Declarative — block, optional icon injection (target/goal icon), content wrapper.


@refrakt-md/business (2)

partner

Purpose: Logo grid of partners, clients, investors, or sponsors with optional links. Common on company about pages and landing pages.

Aliases: client

Attributes:

NameTypeDefaultRequiredDescription
layoutString'grid'Nogrid (fixed columns) or marquee (scrolling)
columnsNumber4NoGrid column count
grayscaleBooleantrueNoDisplay logos in grayscale (color on hover)

Content model:

  • Images → partner logos. Alt text becomes the partner name.
  • Links wrapping images → partner logos with clickthrough URLs.
  • Headings → section titles (for grouping: "Platinum Sponsors", "Gold Sponsors")
  • Header group: heading + paragraph → section eyebrow/headline/blurb

Example:

{% partner columns=5 %}

## Our Partners

[![Acme Corp](/logos/acme.svg)](https://acme.com)
[![Globex](/logos/globex.svg)](https://globex.com)
[![Initech](/logos/initech.svg)](https://initech.com)

{% /partner %}

Transform output:

  • typeof: Partner
  • Tag: <section> with property: 'contentSection'
  • Properties: eyebrow, headline, blurb, layout, columns, grayscale
  • Refs: logos (ul of li elements, each containing an img or a>img)

job

Purpose: Job listing with structured metadata. For careers pages and job boards.

Aliases: posting

Schema.org: JobPosting

Attributes:

NameTypeDefaultRequiredDescription
titleStringYesJob title
departmentStringNoDepartment or team
locationStringNoLocation (e.g., "Remote", "San Francisco, CA")
typeString'full-time'Nofull-time, part-time, contract, internship
salaryStringNoSalary range (e.g., "$120k–$160k")
applyUrlStringNoApplication URL
postedStringNoDate posted (ISO 8601)

Content model:

  • First paragraph → job summary/description
  • ## Responsibilities or ## What You'll Do heading → responsibilities list
  • ## Requirements or ## Qualifications heading → requirements list
  • ## Nice to Have or ## Preferred heading → preferred qualifications list
  • ## Benefits heading → benefits list

Transform output:

  • typeof: Job
  • Tag: <article>
  • Properties: title (h-element), department (span), location (span), type, salary (span), applyUrl, posted
  • Refs: description (div), responsibilities (ul), requirements (ul), preferred (ul), benefits (ul), applyButton (a)

SEO extractor: Generates schema.org/JobPosting with title, datePosted, employmentType, jobLocation, baseSalary, description.


@refrakt-md/media (6)

Time-based media content. The existing music-playlist and music-recording runes are music-specific. The spec generalizes them into type-polymorphic runes that handle music, podcasts, audiobooks, video, and talks via a type attribute.

Migration from existing runes

ExistingNewAttribute changes
music-recordingtrack with type="song"byArtistartist, copyrightYearyear. listItem stays.
music-playlistplaylist with type="album"Remove trackFields, split, mirror. Add artist. audio stays.

The existing runes continue to work as aliases during the transition period. New content should use track and playlist.

Design notes: compact list syntax

Playlists support two ways to define tracks:

  1. Explicit tags{% track artist="Queen" duration="PT5M55S" %} for full control
  2. Compact list syntax — pipe-delimited list items parsed into track attributes

The compact syntax uses the playlist's type to determine field order. The first pipe segment is always the track name; subsequent segments are type-dependent:

TypePipe order after nameExample
album (default)duration- Bohemian Rhapsody | PT5M55S
mixartist, duration- Bohemian Rhapsody | Queen | PT5M55S
podcastduration, date- The Pilot | PT45M00S | 2024-01-15
audiobookduration- Chapter 1 | PT32M00S
seriesduration, date- Episode 1 | PT22M00S | 2024-03-01

This follows the same pattern as budget (hardcoded description | $amount regex) and cast (hardcoded name — role regex) — the rune defines the parsing, not the author. The type attribute provides just enough flexibility for different media domains without introducing a novel fields configuration convention.

Why not fields? The original music-playlist had a trackFields attribute letting authors declare the pipe-segment mapping. This was unique to one rune, positional (fragile to reorder), and misleadingly named. Type-derived defaults are simpler, discoverable, and consistent with how other runes work. For non-standard field needs, use explicit {% track %} child tags with named attributes.

track

Purpose: Single media item — a song, podcast episode, audiobook chapter, talk, or video.

Aliases:

Schema.org: Varies by type: MusicRecording, PodcastEpisode, AudioObject, VideoObject

Attributes:

NameTypeDefaultRequiredDescription
typeString'song'Nosong, episode, chapter, talk, video
artistStringNoCreator/performer name
albumStringNoParent collection (album, show, book)
durationStringNoISO 8601 duration (e.g., PT3M45S) — formatted to 3:45 display
yearNumberNoRelease year
numberNumberNoTrack/episode/chapter number
urlStringNoLink to the media (streaming service, host platform)
dateStringNoPublication date (ISO 8601, for episodes/series)
listItemBooleanfalseNoRender as <li> instead of <div>

Content model:

  • Heading → track name/title
  • Other children ignored (data comes from attributes)

Transform output:

  • typeof: Track
  • Tag: <div> or <li>
  • Properties: name (h-element), artist (span), album (span), duration (span, formatted), year (span), number (span), url (a), date (span), type

Identity transform: Declarative — modifiers for type, structure injection for metadata display (artist, duration, number). Presence-based conditional display (same pattern as event): fields only render when non-empty.


playlist

Purpose: Ordered collection of tracks — an album tracklist, podcast feed, video series, audiobook table of contents.

Aliases:

Schema.org: Varies by type: MusicPlaylist, PodcastSeries, ItemList

Attributes:

NameTypeDefaultRequiredDescription
typeString'album'Noalbum, podcast, audiobook, series, mix
artistStringNoDefault artist inherited by all child tracks
audioStringNoAudio file URL for embedded playback

Content model:

  • Header group: heading, paragraphs, images (cover art, description)
  • Body: {% track %} children or list items parsed via compact syntax (pipe-delimited, type-derived field order)
  • When artist is set on the playlist, parsed list-item tracks inherit it as their default artist

Examples:

{% playlist type="album" artist="Pink Floyd" %}
# The Dark Side of the Moon
![Cover](/images/dsotm.jpg)

- Speak to Me | PT1M13S
- Breathe | PT2M43S
- On the Run | PT3M36S
{% /playlist %}
{% playlist type="mix" %}
# Road Trip Mix

- Bohemian Rhapsody | Queen | PT5M55S
- Hotel California | Eagles | PT6M30S
- Stairway to Heaven | Led Zeppelin | PT8M02S
{% /playlist %}
{% playlist type="podcast" %}
# Tech Weekly
![Podcast Art](/images/techweekly.jpg)

A weekly podcast about emerging technology.

- The AI Revolution | PT45M00S | 2024-01-15
- Quantum Computing 101 | PT38M00S | 2024-01-22
{% /playlist %}

Transform output:

  • typeof: Playlist
  • Tag: <section>
  • Properties: eyebrow, headline, image, blurb, type, artist (span)
  • Refs: tracks (ol of track items)

album

Purpose: Grouped release — a music album, podcast season, video season, lecture series. Higher-level grouping than playlist.

Aliases:

Schema.org: Varies by type: MusicAlbum, PodcastSeason, TVSeason

Attributes:

NameTypeDefaultRequiredDescription
typeString'music'Nomusic, podcast, video, lectures
artistStringNoPrimary creator
yearNumberNoRelease year
labelStringNoRecord label, network, or publisher
genreStringNoGenre or category

Content model:

  • Header group: heading (album title), paragraph (description), image (cover art)
  • Body: {% playlist %} or {% track %} children, or list items for tracks
  • Headings in body → disc/side/part separators

Transform output:

  • typeof: Album
  • Tag: <article> with property: 'contentSection'
  • Properties: eyebrow, headline, image, blurb, artist (span), year (span), label (span), genre (span), type
  • Refs: tracklist (ol or grouped ols)

artist

Purpose: Creator profile — musician, podcaster, narrator, filmmaker, speaker. Structured biography with discography/body of work.

Aliases:

Schema.org: MusicGroup or Person (based on content)

Attributes:

NameTypeDefaultRequiredDescription
nameStringYesArtist/creator name
genreStringNoPrimary genre or field
activeStringNoActive years (e.g., "2015–present")
originStringNoLocation/origin

Content model:

  • Header group: heading (name, auto-extracted), image (photo/avatar), paragraphs (bio)
  • ## Discography or ## Works heading → list of works (links to album/playlist pages)
  • ## Links heading → external links (streaming profiles, website, social)
  • Other headings → additional bio sections

Transform output:

  • typeof: Artist
  • Tag: <article> with property: 'contentSection'
  • Properties: eyebrow, headline, image, blurb, name, genre (span), active (span), origin (span)
  • Refs: works (ul), links (ul), bio (div)

video

Purpose: Self-hosted video player with poster image, captions, subtitles, and responsive sizing. For video that you host yourself — use embed for YouTube/Vimeo.

Aliases:

Schema.org: VideoObject

Attributes:

NameTypeDefaultRequiredDescription
srcStringYesVideo file URL
posterStringNoPoster/thumbnail image URL
captionsStringNoWebVTT captions file URL
subtitlesStringNoWebVTT subtitles file URL (if different from captions)
aspectString'16:9'NoAspect ratio: 16:9, 4:3, 1:1, 9:16 (vertical)
autoplayBooleanfalseNoAutoplay (muted)
loopBooleanfalseNoLoop playback
titleStringNoVideo title
durationStringNoISO 8601 duration

Content model:

  • Paragraph → video description/caption
  • Self-closing is common: {% video src="/video.mp4" poster="/thumb.jpg" /%}

Transform output:

  • typeof: Video
  • Tag: <figure>
  • Properties: src, poster, captions, subtitles, aspect, autoplay, loop, title, duration
  • Refs: player (video element), caption (figcaption)

Implementation: Web component (rf-video) similar to the Diagram/Sandbox/Map migration pattern. Handles play controls, caption track loading, and responsive sizing.


audio

Purpose: Self-hosted audio player with waveform visualization, chapters, and transcript. For podcasts, music, audiobooks, and sound design hosted on your own server.

Aliases:

Schema.org: AudioObject

Attributes:

NameTypeDefaultRequiredDescription
srcStringYesAudio file URL
titleStringNoTrack title
artistStringNoArtist/speaker name
durationStringNoISO 8601 duration
waveformBooleantrueNoShow waveform visualization
chaptersStringNoWebVTT chapters file URL

Content model:

  • Paragraph → description/show notes
  • Ordered list → chapter markers (if no WebVTT file): 1. 00:00 — Introduction
  • Blockquotes → transcript excerpts

Transform output:

  • typeof: Audio
  • Tag: <figure>
  • Properties: src, title, artist (span), duration (span, formatted), waveform, chapters
  • Refs: player (audio element), chapterList (ol), transcript (div), caption (figcaption)

Implementation: Web component (rf-audio). Waveform visualization requires either a pre-computed waveform data file or client-side Web Audio API analysis. Chapter markers sync with playback position.

Relationships