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 (generateNavDataTasksectionOrder), 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 cautiondanger and importantinfo 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