Skip to content

Components

All components are registered globally by enhanceApp() and are available in any Markdown page or Vue template without explicit imports. Composables must be imported explicitly from their source paths.

TypeScript-typed props give inline documentation in VS Code (Volar extension) and JetBrains IDEs — hover a prop in a template to see its description.


<ScavoldMenu>

Renders a <nav> element containing a nested list of page links derived from the site's page hierarchy. Nothing is rendered when the resolved item set is empty.

The component has three independent addressing modes — path-based, absolute (from root), and relative (from current page) — that cover all common navigation patterns without requiring knowledge of the current page's depth.

Props

PropTypeDefaultDescription
from-pathstringSelect the parent node by path. Supports {locale} (full BCP 47 tag, e.g. de-CH) and {lang} (primary language subtag only, e.g. de) placeholders, replaced with the current page's locale at runtime. Takes priority over from-root and from. Examples: "de/footer", "{lang}/footer".
from-rootnumberList items at this absolute depth from root. 1 = top-level pages, 2 = second level, etc. Ignored when from-path is set.
fromnumber0List items relative to the current page. 0 = siblings, 1 = children, -1 = aunt/uncle level. Ignored when from-root or from-path is set.
depthnumber0Additional levels to descend below the starting level. 0 = flat list, 1 = one level of children, -1 = unlimited.
active-onlybooleanfalseOnly expand children along the branch leading to the current page.
expandbooleanfalseExpand children of all nodes regardless of active branch. active-only takes precedence.
labelstringaria-label for the <nav> landmark. Required when more than one <ScavoldMenu> appears on the same page.

Rendered markup

html
<nav aria-label="…">
  <ul>
    <li class="active">          <!-- .active: current page or an ancestor -->
      <a href="/section/">Section</a>
      <ul>
        <li class="active current">   <!-- .current: exactly the current page -->
          <a href="/section/page/" aria-current="page">Page</a>
        </li>
      </ul>
    </li>
  </ul>
</nav>

Label resolution

Menu item labels are resolved in this order:

  1. label frontmatter — explicit navigation text
  2. title frontmatter — page title
  3. First # heading extracted at build time
  4. Last path segment (e.g. about from de/about.md)

Hiding pages

Set hide: menu (or hide: true) in a page's frontmatter to exclude it from menus. The page is still served and still appears in the hierarchy tree.

Examples

html
<!-- top-level pages — typical site header nav -->
<ScavoldMenu :from-root="1" label="Main navigation" />

<!-- second level of active section only -->
<ScavoldMenu :from-root="2" active-only />

<!-- children of the current page -->
<ScavoldMenu :from="1" />

<!-- siblings of the current page -->
<ScavoldMenu />

<!-- full site hierarchy (sitemap) -->
<ScavoldMenu :from-root="1" :depth="-1" />

<!-- locale-aware footer nav — works with path structures de/, en/, etc. -->
<ScavoldMenu from-path="{lang}/footer" label="Footer navigation" />

<ScavoldBreadcrumb>

Renders a <nav class="breadcrumb"> element with a flat list of links from root to the current page. Uses the same <ul>/<li>/<a> structure as <ScavoldMenu> so both can share CSS rules.

Props

PropTypeDefaultDescription
include-currentbooleantrueInclude the current page as the final crumb.
include-rootbooleanfalseInclude the root node as the first crumb.

Rendered markup

html
<nav class="breadcrumb">
  <ul>
    <li class="active"><a href="/de/">Deutsch</a></li>
    <li class="active current"><a href="/de/leistungen/">Leistungen</a></li>
  </ul>
</nav>

Examples

html
<!-- typical usage -->
<ScavoldBreadcrumb />

<!-- without current page, when the page heading serves as the final crumb -->
<ScavoldBreadcrumb :include-current="false" />

<!-- full trail including home link -->
<ScavoldBreadcrumb include-root />

<ScavoldLocaleMenu>

Renders a <nav class="locale-nav"> element listing links to the available translations of the current page. Nothing is rendered when the resolved link set is empty or (by default) when only a single locale is available.

Props

PropTypeDefaultDescription
detection"auto" | "explicit" | "inherited" | "global""auto"How to discover available locales. See Detection modes.
include-currentbooleanfalseInclude the current locale as a non-navigating item.
hide-if-singlebooleantrueRender nothing when only one (or zero) locale links are available.
labelstring"Language"aria-label for the <nav> landmark.

Detection modes

ModeDescription
"auto"Tries explicit, then inherited, then global; uses the first mode that yields at least one link.
"explicit"Only locales declared in the current page's own translations frontmatter.
"inherited"For each locale, links to the translation declared by the nearest ancestor with a translations entry. Section-level declarations cover all child pages that have no direct counterpart.
"global"Every locale present anywhere in the hierarchy. Links to the topmost page in that locale.

Rendered markup

html
<nav class="locale-nav" aria-label="Language">
  <ul>
    <li class="current" lang="de">
      <span aria-current="true">de</span>
    </li>
    <li lang="en">
      <a href="/en/" hreflang="en" aria-label="en">en</a>
    </li>
  </ul>
</nav>

Examples

html
<!-- show locales from current page or nearest ancestor with translations -->
<ScavoldLocaleMenu />

<!-- always show all known locales; link to their topmost page -->
<ScavoldLocaleMenu detection="global" />

<!-- show all locales including current, even when only one is listed -->
<ScavoldLocaleMenu detection="global" include-current :hide-if-single="false" />

<ScavoldLayout>

Base layout wrapper that intercepts pages with locale-conditional redirect frontmatter and renders <ScavoldLocaleRedirect> in their place. All other pages render the default slot.

Wrap your site shell in this component to inherit redirect handling automatically:

vue
<template>
  <ScavoldLayout>
    <div class="site-shell">
      <!-- header, nav, main, footer … -->
    </div>
  </ScavoldLayout>
</template>

Themes that need finer control can compose the building blocks directly using useRedirect() and <ScavoldLocaleRedirect>:

vue
<script setup>
import { useRedirect } from "scavold/composables/useRedirect.js";
import ScavoldLocaleRedirect from "scavold/components/ScavoldLocaleRedirect.vue";

const { isLocaleRedirect } = useRedirect();
</script>

<template>
  <ScavoldLocaleRedirect v-if="isLocaleRedirect" />
  <div v-else class="site-shell">…</div>
</template>

<ScavoldPageList>

Links a configurable number of the site's own pages — the latest posts of a blog, a teaser row of a section's sub-pages, a list of downloads. Rendered for the :::pagelist container block; anything written inside the block is rendered above the list.

Titles, dates, introductory texts and teaser images all come from the target pages, so authors maintain them where the content lives.

markdown
::: pagelist from=1 limit=5 sort=date teaser images dates heading-level=3 label="Recent posts"
## Recent posts
:::

Arguments

ArgumentTypeDefaultDescription
fromnumber1Level relative to the current page: 1 = its sub-pages, 0 = its siblings.
from-rootnumberAbsolute level from the site root. Takes precedence over from.
from-pathstringPath of the page whose sub-pages are listed, with {locale} / {lang} support. Takes precedence over both other selectors.
deepflagInclude every level below, flattened into one list.
limitnumber5, or 0 with per-pageMaximum number of entries; 0 lists all of them. A paginated list reaches every page it found unless a limit says otherwise.
offsetnumber0Entries to skip at the start.
sort"order" | "date" | "date-asc" | "title""order"order follows the site structure, date is newest first.
reverseflagReverse the selected order.
teaserflagShow each page's introductory text.
imagesflagShow each page's teaser image.
datesflagShow each page's date.
date-style"short" | "medium" | "long" | "full" | "iso""long"Style of the dates. The Intl.DateTimeFormat styles follow the page's language; iso writes 2020-10-20 in every language.
time-style"short" | "medium" | "long" | "full" | "iso"Style of the time of day. Left out, entries show their date only. iso writes 12:16.
timezonestring"UTC"IANA name of the zone the dates are read in, e.g. Europe/Berlin.
heading-levelnumber0Heading level of the titles; 0 renders a plain list of links.
image-sizesstring"(min-width: 60rem) 30rem, 90vw"CSS sizes descriptor for the teaser images.
labelstringAccessible label (aria-label) for the list.
per-pagenumber0Entries per page; 0 shows all. Paging happens in the browser and adds a pager.
paramstring"page"Query parameter the current page is remembered in, for pages carrying several paginated lists.

The data per entry is read from title, date, excerpt and image of the target page, with the page body as the fallback for the last two. A page opts out with hide: list.

Rendered markup

html
<div class="scavold-pagelist">
  <!-- slot content -->
  <ul class="scavold-pagelist__items" aria-label="Recent posts">
    <li class="scavold-pagelist__item">
      <a class="scavold-pagelist__link" href="/blog/newest">
        <picture class="scavold-pagelist__image">…</picture>
        <h3 class="scavold-pagelist__title">Newest post</h3>
        <time class="scavold-pagelist__date" datetime="2026-06-21T00:00:00.000Z">21 June 2026</time>
        <p class="scavold-pagelist__excerpt">Opening prose of the post…</p>
      </a>
    </li>
  </ul>
</div>

One link per entry, with the teaser image decorative inside it, so a screen reader announces each entry once. The component ships no CSS; the classes above are the theme's hooks.

per-page pages the list in the browser: only the current page's entries are rendered, the page shown is remembered as ?page=2 through history.replaceState, and the pager is a labelled <nav> with a status line and buttons. The pre-rendered HTML always holds page one, so a shared ?page=3 link shows it for an instant, and entries beyond page one are not in the HTML for a crawler to find — the sitemap and the feed are.


<ScavoldImage>

Renders a <picture> element with full srcset and sizes support. Scavold inserts this component automatically in place of every local <img> processed during the build — you typically do not use it directly in templates.

Site-specific layout components that need to control the sizes value for a particular context should pass it explicitly:

vue
<ScavoldImage src="/photo.jpg" alt="A photo" sizes="(min-width: 60rem) 50vw, 100vw" />

Props

PropTypeDefaultDescription
srcstringImage path, resolved against media_folder.
altstring""Alt text. Empty string marks the image as decorative (role="presentation").
titlestringCaption or title text.
sizesstringsite defaultCSS sizes descriptor for the <source> element. Overrides the site-level image_sizes default.
widthsnumber[]site defaultExplicit list of variant widths. Overrides the site-level image_widths default.

<ScavoldVideo>

Container component for embedded video. Accepts all options via the useVideo composable.

Props

See videoProps from scavold/composables/useVideo.js for the full list. Common props:

PropTypeDescription
srcstringVideo source URL or path.
posterstringPoster image path.
autoplaybooleanAutoplay the video (muted).
loopbooleanLoop the video.

Sectioning wrappers

Seven thin wrapper components map to their corresponding HTML sectioning elements. They exist primarily to serve as Markdown container targets so that :::section, :::aside, etc. produce the correct element with the container arguments wired as props.

ComponentElement
<ScavoldSection><section>
<ScavoldAside><aside>
<ScavoldArticle><article>
<ScavoldHeader><header>
<ScavoldFooter><footer>
<ScavoldNav><nav>
<ScavoldMain><main>

Each accepts containerProps (see useContainer below) and any additional props or slots specific to its element type.

<ScavoldContainer>

Fallback used for any container name that has no registered component. Renders as the matching HTML sectioning element if the name is one of the seven above, otherwise as a <div>. Adds the container name to the element's class list.


Composables

useHierarchy()

js
import { useHierarchy } from "scavold/composables/hierarchy";

const { hierarchy, current } = useHierarchy();
Return valueTypeDescription
hierarchyRef<HierarchyNode>The full page tree root.
currentRef<HierarchyNode>The node for the page currently being viewed.

HierarchyNode properties:

PropertyTypeDescription
pathstringPath of the Markdown source file.
isPagebooleanWhether this node represents an actual page.
titlestring?Display title (frontmatter or first heading).
labelstring?Navigation label (frontmatter label, falls back to title).
urlstring?Alias output path when url is declared in frontmatter.
localestring?Inherited locale (BCP 47) — see locale.
excerptstring?Introductory text — excerpt or the page's opening prose.
imagestring?Lead image reference — image or the first image in the page.
imageDataobject?Responsive variant data (src, srcset, webpSrcset) for image, resolved at build time.
datestring?Publication date as an ISO 8601 timestamp — see date.
frontmatterobject?Full frontmatter data for this page.
parentHierarchyNode?Parent node (non-enumerable, not serialised).
subsobject?Child nodes keyed by path segment, sorted by order.

useContainer() / containerProps

Used inside custom container components to consume the arguments parsed from the opening ::: line. Container names and their argument declarations are configured in .cratly.config.yamlcontainers.

js
import { useContainer, containerProps } from "scavold/composables/useContainer.js";

const props = defineProps( {
    ...containerProps,
    background: String,   // custom KV arg: background=/media/hero.jpg
} );

const { containerName, classes, dataAttrs } = useContainer( props );

useContainer return values:

ValueTypeDescription
containerNamestringThe container name (e.g. "hero").
classesstring[]Flag arguments as class names.
dataAttrsobjectKey–value arguments as data-* attributes.

containerProps:

PropTypeDescription
containerArgsstringRaw argument string from the ::: opening line.

usePageList() / pageListProps

Used inside custom page-list components to turn the arguments of a :::pagelist block into ready-to-render entries — the same selection, ordering and formatting <ScavoldPageList> uses.

js
import { usePageList, pageListProps } from "scavold/composables/usePageList.js";

const props = defineProps( pageListProps );

const { items, headingTag, teaser, images, dates, imageSizes, ariaLabel, formatDate } =
    usePageList( props );
Return valueTypeDescription
itemsRef<PageListItem[]>Entries with node, href, title, date, excerpt and image resolved.
headingTagRef<string | null>Heading element for the titles, null for a plain list of links.
teaser / images / datesRef<boolean>Whether the respective flag is set.
imageSizesRef<string>CSS sizes descriptor for the teaser images.
ariaLabelRef<string | undefined>Accessible label for the list.
formatDate(iso: string) => stringFormats a timestamp in the page's locale, in UTC.

useRedirect()

js
import { useRedirect } from "scavold/composables/useRedirect.js";

const { isLocaleRedirect, redirect } = useRedirect();
Return valueTypeDescription
isLocaleRedirectRef<boolean>true when the current page has a locale-conditional redirect in frontmatter.
redirectRef<object | string>The raw redirect frontmatter value.

useVideo() / videoProps

Used inside custom video container components.

js
import { useVideo, videoProps } from "scavold/composables/useVideo.js";

const props = defineProps( videoProps );
const { attrs } = useVideo( props );