Cratly
Zu Deutsch wechselnDENAV.openEditor

.cratly.config.yaml specification

This document is the living, framework-agnostic specification for the .cratly.config.yaml file format. It is owned by cratly (this repository), not by any single theme framework — Scavold is the reference adapter, but the format is meant for any adapter (Next.js, Astro, …). Update it whenever a key is added or changed.

Companion: the section-type schema & manifest convention specifies how the editor learns the typed properties each section kind accepts.

Background

"cratly" is an acronym for "a content repository and a template engine". It is an online editor for managing Markdown and media files stored in a content repository. Changes committed through cratly trigger an attached continuous delivery pipeline, which invokes a build tool - the template engine - to turn the Markdown and media files into a published website.

Purpose and design goals

.cratly.config.yaml describes a website — its content structure, media assets, and editorial metadata. It deliberately does not describe build tools, frameworks, or deployment targets. Any compliant tool can read it without knowledge of the underlying tech stack.

Two classes of tools are expected to consume this file:

  • Editors such as cratly use it to know where content lives, how to present frontmatter fields to authors (pickers, toggles, references to other pages), and what constitutes valid input.
  • Theme framework adapters such as Scavold (VitePress) use it at build time to locate source files, process assets, and apply content rules such as locale inheritance or field value fallback.

Third parties are explicitly welcome to build adapters for other frameworks (Next.js, Nuxt, Astro, …) against this same file. Keys should therefore remain semantic and content-oriented. If a concept only makes sense inside one specific build tool it does not belong here.

File location

The file must reside at the project root alongside package.json. Both editors and adapters resolve it relative to the working directory.

Top-level keys

version

yaml
version: 0

Declares which version of this specification the file conforms to. Tools should reject or warn on versions they do not support.

ValueMeaning
0Pre-stable. Breaking changes may occur at any time.

Optional while the spec is at version 0. Once a stable 1 is released this key becomes required and tools must validate it.


pages_folder

yaml
pages_folder: pages

Path to the folder that contains all Markdown page files, relative to the project root. Editors look here when browsing or linking pages. Adapters use it as the source root for building the page hierarchy and compiling redirects.

Required. No default — both editors and adapters must reject a configuration that omits this key.


media_folder

yaml
media_folder: media

Path to the folder that contains media files (images, videos, documents) referenced from Markdown, relative to the project root. Editors open this folder when a field with type: media-file is edited. Adapters resolve image paths against it.

Optional. When omitted, adapters may fall back to pages_folder.


static_folder

yaml
static_folder: public

Path to the folder that contains static theme assets (icons, fonts, brand images, manifest files) that should be served at the root of the web server but are not managed by content editors. Relative to the project root.

This folder is intentionally separate from media_folder and pages_folder: editors never browse or write to it, and its contents are owned by the theme or build configuration, not by the site's authors.

Adapter behaviour depends on the build tool:

  • Scavold / VitePress / Vite — maps to Vite's publicDir; files are copied verbatim into the build output and served at their path relative to the site root.
  • Other adapters — equivalent to whatever the underlying framework calls its "static" or "public" directory.

Optional. When omitted and pages_folder resolves to a subfolder, Scavold defaults to a public/ folder at the project root to keep static assets outside the editor-visible content tree. When the build tool has no equivalent concept the key is ignored.


image_widths

yaml
image_widths: [320, 640, 960, 1280, 1920]

List of pixel widths at which responsive image variants are generated during the build. Widths larger than the source image are silently skipped so images are never upscaled. The largest entry in the list acts as the cap; if the source is narrower than all entries only the source width is used.

Optional. Adapters apply a built-in default equivalent to the example above when this key is absent.


image_sizes

yaml
image_sizes: "100vw"

Default value for the HTML sizes attribute placed on every responsive image that does not carry a per-image override. Accepts any valid CSS sizes string.

Per-image overrides are written in the Markdown image title field:

markdown
![Alt text](/photo.jpg "sizes=50vw")

Optional. Adapters default to 100vw when absent.


locales

yaml
locales:
  - code: de
    flag: 🇩🇪
    label: Deutsch
  - code: de-AT
    flag: 🇦🇹
    label: Deutsch (Österreich)
  - code: en
    label: English

Declares the set of locales the site supports. Used by editors to populate locale pickers (for lang, translations, and redirect frontmatter fields) and by adapters to validate locale tags and drive locale-aware processing.

When this key is absent, editors fall back to a built-in list of common locales. When it is present, only the listed locales are offered.

Each entry is an object with the following fields:

FieldRequiredDescription
codeYesBCP 47 language tag. Simple tags (de, en) and region subtags (de-AT, zh-TW) are both valid.
flagNoUnicode flag emoji representing this locale (e.g. 🇦🇹). Editors display it next to the locale code. When absent, editors derive a flag from the primary language subtag if it is part of their built-in list, and fall back to a neutral placeholder otherwise.
labelNoHuman-readable name of the locale in its own language (e.g. Deutsch (Österreich)). Used as a tooltip or label in editors. When absent, editors display the code value.

Optional. When absent, editors use their own built-in locale list.


containers

yaml
containers:
  <name>: <string>             # shorthand — component name only
  <name>:                      # full object form
    [component: <string>]
    [label: <string>]
    [props: {<prop-name>: {type: <type>, ...}, ...}]
    [flags: [<flag-name>, ...]]

Declares the set of named Markdown containers the site uses, beyond the built-in containers which are always available without declaration:

  • HTML sectioning elements: section, aside, article, header, footer, nav, main
  • video

Editors use this list to offer a constrained pick-list of container types when inserting or editing a container block, rather than accepting free-form names. Each declared container may advertise a set of typed properties (see props); the editor renders an appropriate control for each (text input, checkbox, number, media picker, page picker, select) in its section editing UI.

The set of section kinds and their props is resolved by the adapter and emitted into the website repository as a machine-readable section-type manifest that the editor reads — see the section-type schema & manifest convention. The declarations here are merged on top of the adapter's built-in section definitions; a site need only declare custom sections and overrides, never restate built-in props.

Built-in containers can be declared here purely to add a label, props, or flags for the editor — the component mapping is already set by the adapter and does not need to be repeated unless a custom component is desired.

Each entry under containers is keyed by the container name as it appears in Markdown after the opening :::. Two value forms are accepted:

String shorthand

yaml
containers:
  hero: MyHeroComponent

A plain string is treated as an explicit component name by adapters. Editors that do not recognise the string form fall back to the raw container name as the label. Use this form when no editor metadata (label, props, flags) is needed.

Object form

yaml
containers:
  hero:
    component: MyHeroComponent
    label: "Hero section"
    flags:
      - dark
      - centered

The full object form supports all properties described below. All properties are optional; an empty object {} is valid and registers the container with the adapter's default component-naming convention (Scavold{Name}).

component

yaml
component: MyHeroComponent

Overrides the Vue component the adapter uses to render this container. When absent, adapters apply their default naming convention (e.g. heroScavoldHero).

Optional.

label

yaml
label: "Hero section"

Human-readable name shown in the editor's container type picker. When absent, editors fall back to the raw container name.

Optional.

props

yaml
props:
  image:
    type: media-file
    label: "Background image"
  columns:
    type: number
    label: "Columns"
    default: 2
  variant:
    type: enum
    label: "Variant"
    values: [ plain, boxed, dark ]
    default: plain
  dark:
    type: boolean
    label: "Dark mode"

Map of typed properties the section accepts. Each entry is keyed by the property name an author writes on the container's opening line and uses the same type vocabulary as frontmatter_fields, plus enum:

typeEditor widgetMarkdown serialization
textsingle-line inputname="value"
textareamulti-line inputname="value"
booleancheckboxbare flag token when true
numbernumeric inputname="value"
media-filemedia pickername="path" (resolved against media_folder)
page-refpage-tree pickername="path" (resolved against pages_folder)
enumselectname="value"

Per-prop attributes: label, hint, required, default, and — for enum only — values (a list of strings, or {value, label} objects). A string shorthand is accepted for the type alone (image: media-file).

Serialization rule: a boolean prop is written as a bare flag on the opening line; every other type is written as a name=value pair:

markdown
::: hero dark image=/media/banner.jpg variant=boxed
Content here.
:::

Optional. See the section-type schema for the authoritative field list and the manifest the editor consumes.

flags

yaml
flags:
  - dark
  - centered
  - fullwidth

Shorthand for a set of boolean props — each listed name becomes a checkbox in the editor. Equivalent to declaring each under props with type: boolean. Authors may still write any flag by hand in Markdown; this list only controls what the editor surfaces. When the same name appears in both props and flags, the props entry wins.

Optional.

Example

yaml
containers:
  # shorthand — just register the component, no editor metadata needed
  teaser: TeaserCard

  # full form — typed props plus component override
  hero:
    component: SiteHero
    label: "Hero section"
    props:
      image: { type: media-file, label: "Background image" }
      dark:  { type: boolean, label: "Dark variant" }

  # editor metadata only — adapter uses default ScavoldCallout component
  callout:
    label: "Callout box"
    flags:
      - warning
      - info

  # override a built-in — replace ScavoldVideo with a custom player
  video:
    component: MyVideoPlayer
    label: "Video"

In Markdown a container with props looks like (boolean props as bare flags, all other types as name=value pairs):

markdown
::: hero dark image=/media/banner.jpg
Content here.
:::

frontmatter_fields

yaml
frontmatter_fields:
  <field-name>:
    type: <type>
    [inherits: true]
    [label: <string>]
    [hint: <string>]

Declares custom frontmatter fields that both editors and adapters treat specially. Fields not declared here are still valid frontmatter — they are simply passed through without any special handling.

Each entry under frontmatter_fields is keyed by the exact frontmatter property name used in Markdown files. The value is an object with the following properties:

type

Determines how editors present the field and how adapters process its value.

ValueEditor widgetAdapter behaviour
textSingle-line text inputPassed through as-is
textareaMulti-line text inputPassed through as-is
booleanToggle / checkboxCoerced to boolean
numberNumeric inputCoerced to number
media-fileMedia folder file pickerPath resolved against media_folder
page-refPage tree pickerPath resolved against pages_folder

Required on every frontmatter_fields entry.

inherits

yaml
inherits: true

When true, adapters walk the page hierarchy upward from the current page until a ancestor is found that carries a non-empty value for this field. The resolved (possibly inherited) value is what components and templates receive.

This allows a value set on a parent or section index page to apply automatically to all subordinated pages unless they override it explicitly. Useful for fields such as a header image, a default author, or a content category.

Optional. Defaults to false.

label

yaml
label: "Header image"

Human-readable label shown by editors next to the input. When absent, editors may derive a label from the field name.

Optional.

hint

yaml
hint: "Shown at the top of the page and in link previews."

Short explanatory text shown below the input in editors.

Optional.

Built-in frontmatter conventions

The following frontmatter properties are part of the core specification and are understood by all compliant tools without any declaration in frontmatter_fields.

title

yaml
title: About us

Display title for the page. Used in navigation menus, breadcrumbs, the browser <title> element, and social previews. When absent, adapters fall back first to the first ATX heading (# …) in the page body, then to the path segment.

Optional.


label

yaml
label: About

Short navigation label shown in menus and breadcrumbs instead of title. Useful when the full title is too long for compact navigation. Falls back to title and then the path segment when absent.

Optional.


order

yaml
order: 3

Controls the sort position of this page among its siblings. Pages are sorted by order ascending; pages that omit order follow at the end in filename order. Negative values are permitted.

Optional.


url

yaml
url: /custom-path/

Declares a root-relative alias URL for the page. The source file stays in its original location in pages_folder but is compiled to this path in the build output. Adapters detect conflicts at build time. Editors that present a page list should resolve this alias rather than the default path when linking to the page.

Optional.


lang / locale

yaml
locale: de

Declares the language or locale of a page. Adapters inherit this value downward through the page hierarchy: a value set on a section's index.md applies to all pages in that section unless overridden. Standard BCP 47 language tags are expected (de, en, en-US, …). locale is the preferred key; lang is accepted as a deprecated alias and is ignored when locale is also present.

translations

yaml
translations:
  en: en/about_us.md
  fr: fr/a-propos.md

Maps locale codes to the path of the equivalent page in that locale, relative to pages_folder. Editors present this as a page picker per locale. Adapters use the mapping to generate hreflang link elements and to power locale-aware navigation.

hide

yaml
# Hidden everywhere
hide: true

# Hidden from navigation menus only
hide: menu

# Hidden from breadcrumbs only
hide: breadcrumb

Controls visibility in navigation. Accepts a boolean or one of two string values:

ValueEffect
absent / falseVisible in menus and breadcrumbs (default)
trueHidden from menus and breadcrumbs; page still served at its URL
"menu"Hidden from navigation menus only
"breadcrumb"Hidden from breadcrumbs only

Optional. Defaults to visible when absent.


redirect

yaml
# Unconditional redirect
redirect: other-page.md

# Locale-conditional redirects
redirect:
  de: de/index.md
  en: en/index.md

# Locale-conditional with a catch-all fallback
redirect:
  de: de/index.md
  en: en/index.md
  "*": fallback/index.md

Declares that visiting this page should redirect the visitor elsewhere. A plain string is an unconditional redirect. An object maps locale codes to target paths, allowing the same URL to redirect differently depending on the visitor's detected locale (browser language preference or previously selected locale).

When the object form is used, the special key "*" serves as a catch-all fallback: adapters redirect to this target when no locale-specific entry matches the visitor's detected locale. If the object contains only a "*" key, adapters may treat it as equivalent to the plain-string form.

Paths are resolved relative to the current page file using the same rules as Markdown links. Only paths within pages_folder are accepted; absolute external URLs are rejected.

Annotated example

yaml
version: 0

# Where content and media live
pages_folder: pages
media_folder: media
static_folder: public

# Responsive image generation
image_widths: [320, 640, 960, 1280, 1920]
image_sizes: "100vw"

# Custom frontmatter fields
frontmatter_fields:

  image:
    type: media-file
    inherits: true
    label: "Page image"
    hint: "Used at the top of the page and in social previews. Inherited by sub-pages when not set."

  featured:
    type: boolean
    label: "Featured"
    hint: "Highlight this page in listings."

  author:
    type: text
    inherits: true
    label: "Author"

# Named containers beyond the built-in sectioning elements and video
containers:

  hero:
    label: "Hero section"
    flags:
      - dark
      - centered

  callout:
    label: "Callout box"
    flags:
      - warning
      - info

  # shorthand: just a component override, no editor metadata
  teaser: TeaserCard

Versioning and extensibility

The version key tracks the version of this specification. The current version is 0, meaning the format is pre-stable and breaking changes may occur. All keys are considered experimental until version 1 is declared.

Tool-specific extension keys should be namespaced to avoid clashes, for example:

yaml
_scavold:
  some_option: value

_crate:
  some_option: value

Compliant tools must ignore keys they do not recognise rather than erroring.