skier. v1.0.0

Markdown & Frontmatter

Write content in Markdown with YAML frontmatter for metadata.


File Format

---
title: My First Post
date: 2024-01-15
category: Tech
tags:
  - javascript
  - web
---

# Hello World

This is my **first post** written in Markdown.

## Code Example

```javascript
console.log('Hello!');

More content here...


---

## Frontmatter

The YAML block at the top (between `---` markers) becomes template variables:

```handlebars
<h1>{{title}}</h1>
<time>{{date}}</time>
<span>{{category}}</span>

{{#each tags}}
  <span class="tag">{{this}}</span>
{{/each}}

Docs Site Navigation (section, order, subcategory)

This documentation site is built with Skier and its sidebar is generated by generateNavDataTask straight from frontmatter — there is no hand-maintained nav file. Three fields drive it, and every page in docs/ sets them so the information architecture stays predictable. These are the conventions any new docs page must follow.

Field Type Purpose
title string Sidebar label and page <title>. Falls back to the first # H1, then the filename.
section string Which top-level sidebar group the page belongs to. Must be one of the section names below — spelled and cased exactly.
order number Sort position within the section (or within its subcategory). Lower = earlier. 0 is conventionally the section's landing/overview page.
subcategory string (optional) Groups pages into a collapsible sub-list inside a section. Currently used only by Task Reference (Setup / Globals / Content / Feeds & SEO).

The sections (in sidebar order)

Section Contains
Getting Started Install, first build, configuration — the shortest path to a working site.
Guides Task-oriented how-tos and concepts: tasks, custom tasks, templates, frontmatter, architecture, migration, contributing.
Task Reference One page per built-in task, grouped by subcategory. Lives in docs/task-reference/.
API Reference Skier's programmatic surface (types, task contract, config interfaces). Lives in docs/api-reference/.
Recipes End-to-end, copy-paste project examples.
FAQ Common questions.

The order of the sections themselves is set once, in skier.tasks.cjs (generateNavDataTask → sectionOrder), not per page. Subcategory order lives next to it in subcategoryOrder. To add a new section you add pages with that section: value and give it a rank in sectionOrder; an unranked section still renders, but sorts last.

Adding a new docs page

  1. Create the .md file. Put it in the section's folder if it has one (task-reference/, api-reference/); otherwise the flat docs/ root is fine — section comes from frontmatter, not from the folder.
  2. Set title, section (exactly matching a name above), and order.
  3. Add subcategory only inside Task Reference.
  4. Rebuild — the sidebar updates itself. No nav file to touch.

A page with no section falls back to the defaultSection (Docs) and lands in a stray group, so always set it. Keeping these fields consistent is what lets later content PRs (quickstart, API/task reference) slot in without re-plumbing the nav.


Supported Features

Skier uses marked with GitHub Flavored Markdown:

  • Headings: # H1 through ###### H6
  • Emphasis: *italic*, **bold**, ~~strikethrough~~
  • Links: [text](url)
  • Images: ![alt](src)
  • Code blocks: Triple backticks with language
  • Tables: GFM table syntax
  • Task lists: - [ ] and - [x]
  • Blockquotes: >
  • Horizontal rules: ---

Syntax Highlighting

Code blocks are highlighted with highlight.js:

```javascript
const greeting = 'Hello';
console.log(greeting);
```

Include the highlight.js CSS in your template:

<link rel="stylesheet" href="https://unpkg.com/highlight.js@11/styles/github-dark.min.css">

Code block markup

Each fenced code block is rendered as a <figure class="code-block"> wrapper so a template's CSS and a small copy-to-clipboard handler have stable hooks — Skier exposes the metadata but does not hard-code a label chip or copy button (those belong to your template):

<figure class="code-block" data-language="ts" data-filename="skier.config.ts">
  <pre><code class="hljs language-ts">…highlighted…</code></pre>
</figure>
  • data-language — the fence language, present whenever one is given.
  • data-filename — present only when you set a filename (see below).
  • The <code> element's textContent is the original, un-highlighted source, so a copy handler can read it back verbatim with no duplicated payload.

Attach a label with CSS (.code-block[data-filename]::before { content: attr(data-filename); }) and a copy button with a few lines of vanilla JS that read data-* and the <code> text — no framework, no plugin.

Filenames / titles

Add a title= (or filename=) pair to the fence info string to label a block with the file it represents:

```ts title="skier.config.ts"
export default { outDir: 'public' };
```

Quotes are optional for single-token names (title=deploy.sh) and required for names with spaces (title="my notes.txt").


Callouts / admonitions

Draw attention with callout blocks using a ::: container. The keyword after the colons is the callout type; an optional title can follow on the same line:

:::note
A plain note. The body is full markdown — **bold**, links, lists, code.
:::

:::warning Heads up
A warning with a custom title.
:::

Supported types: note, tip, info, warning, danger (with caution → danger and important → info accepted as aliases). An unrecognised keyword is left as ordinary text, so ::: used for anything else is untouched.

Each block renders as class-driven, semantic markup for the template's CSS to theme per type — Skier hard-codes no colours or icons:

<div class="callout callout-warning" data-callout="warning">
  <p class="callout-title">Heads up</p>
  <div class="callout-body">…rendered body…</div>
</div>

The title defaults to the capitalised type name (Note, Warning, …) when you don't supply one.


Snippet transclusion (@include)

Pull a slice of a real source or example file into a page at build time, so code examples can't drift from the code they document. Write an @include directive on its own line, starting at column 0:

@include examples/quickstart.ts
@include src/config.ts region="setup"
@include src/config.ts lines="10-24"
@include "examples/with spaces.ts" lang="ts" title="config.ts"

The file is read at build time and rendered as a normal syntax-highlighted code block (the same markup as a fenced block, so copy buttons and the filename label work identically).

Modifiers (all optional):

Modifier Effect
region="name" Only the lines between #region name and #endregion markers (the markers are matched anywhere on a line, so any comment style works).
lines="a-b" A 1-indexed, inclusive line range (or a single line, lines="12").
lang="ts" Override the highlight language (default: inferred from the file extension).
title="…" Override the filename label (default: the include path).

Mark a region in the source file with matching comments:

// #region setup
const skier = createSkier({ outDir: 'public' });
// #endregion setup

Paths are resolved relative to the project working directory. A missing file, an unknown region, or an out-of-range line span fails the build with a clear error — a broken include never silently renders nothing. @include lines inside a fenced code block are left untouched, so this page can show the syntax without transcluding itself.

Live example

This page dogfoods the directive. The code block below is not hand-written — it is transcluded at build time from the snippet-error region of Skier's own renderer, src/utils/markdown.ts, which is covered by the renderer test suite. If that class changes or moves, this docs build fails, so the example here can never drift from the shipping code:

export class SnippetIncludeError extends Error {
  constructor(message: string) {
    super(`[skier] snippet include: ${message}`);
    this.name = 'SnippetIncludeError';
  }
}

Heading Anchors & Table of Contents

Every rendered heading (## through ######, and #) is given a slugged id, so any heading is directly linkable — .../my-page/#getting-started. Slugs are lower-cased with punctuation removed, and duplicates on the same page are made unique with -2, -3, … suffixes.

The page's headings are also exposed to templates as an ordered headings array ({ level, text, slug } per heading), so you can render an on-page table of contents. See generateItemsTask → Heading Anchors & On-Page TOC.


Excerpts

For post summaries, use a marker:

---
title: My Post
---

This is the excerpt that appears in lists.

<!--more-->

This is the full content that only appears on the detail page.

Configure in your task:

generateItemsTask({
  excerptFn: (content) => content.split('<!--more-->')[0],
  // ...
})

Common Fields

Field Type Usage
title string Page title
date string ISO date (2024-01-15)
description string Meta description / excerpt
tags array Categories/labels
featured boolean Highlight post
draft boolean Skip in build

Tips

  • Dates: Use ISO format (2024-01-15) for reliable parsing
  • No tabs: YAML requires spaces for indentation
  • Optional: Frontmatter block can be omitted if not needed
  • Custom fields: Add any field; it's available in templates

Learn More