# How the DESIGN.md Parser Handles YAML Front Matter vs Markdown Body

> Learn how the DESIGN.md parser unifies YAML front matter and markdown body into a single AST. Discover its tokenization and extraction process for comprehensive content analysis.

- Repository: [Google Labs Code/design.md](https://github.com/google-labs-code/design.md)
- Tags: how-to-guide
- Published: 2026-06-28

---

**The DESIGN.md parser treats YAML front matter and fenced YAML code blocks as interchangeable token sources while preserving the surrounding markdown content as discrete document sections, extracting both into a unified AST before merging them into a single token object.**

The `ParserHandler` class in the [`google-labs-code/design.md`](https://github.com/google-labs-code/design.md/blob/main/google-labs-code/design.md) repository processes DESIGN.md files through a three-phase pipeline that separates machine-readable YAML tokens from human-readable markdown content. Unlike standard markdown parsers that simply skip front matter, this implementation extracts both `---` delimited front matter and ` ```yaml ` fenced blocks, validates them for duplicate keys, and maps the remaining markdown body into navigable sections based on level-2 headings.

## Three-Phase Parsing Architecture

The parser operates through distinct phases defined in `packages/cli/src/linter/parser/handler.ts`. Each phase transforms the raw file into structured data that downstream linting and export tools consume.

### Phase 1: AST Construction with Unified

The parser initializes a **unified** processor chain using `remark-parse` and `remark-frontmatter` to recognize both standard markdown and YAML front matter delimiters. This configuration happens at lines 30-34 of `handler.ts`, preparing the parser to identify `"yaml"` nodes separately from the markdown body.

```typescript
// From handler.ts L30-L34
const processor = unified()
  .use(remarkParse)
  .use(remarkFrontmatter, ['yaml']);

```

### Phase 2: YAML Extraction

During AST traversal, the parser identifies and extracts two distinct YAML sources:

- **Front matter blocks**: Nodes of type `"yaml"` representing content between `---` delimiters (lines 42-50)
- **Fenced code blocks**: Nodes of type `"code"` where the `lang` property equals `"yaml"` or `"yml"` (lines 52-60)

Each extracted block stores its source line number and a flag indicating whether it originated from front matter or a numbered code block. If no YAML blocks are found, the parser immediately returns a `NO_YAML_FOUND` error (lines 112-120).

### Phase 3: Markdown Body Extraction

Simultaneously, the parser captures every **level-2 heading** (`## ...`) to segment the document. It records the heading text and line number (lines 64-73), then uses these positions to slice the original file into **document sections**—including a pre-lude section before the first heading and individual sections for each heading (lines 76-104).

## Distinguishing Front Matter from Fenced Blocks

While both sources contain YAML tokens, the parser tracks their provenance differently. Front matter blocks receive special handling as the primary token declaration site, whereas fenced YAML blocks are indexed incrementally via a `blockIndex` counter. This distinction enables precise **source mapping**—the parser maintains a `sourceMap` that correlates specific lines in the file to their corresponding YAML blocks.

The extraction logic in `handler.ts` lines 42-60 demonstrates this dual-path handling:

```typescript
// Conceptual representation of the visit logic
visit(ast, (node) => {
  if (node.type === 'yaml') {
    // Front matter extraction
    blocks.push({ type: 'frontmatter', value: node.value, line: node.position.start.line });
  } else if (node.type === 'code' && (node.lang === 'yaml' || node.lang === 'yml')) {
    // Fenced block extraction with incremental indexing
    blocks.push({ type: 'code', index: blockIndex++, value: node.value, line: node.position.start.line });
  }
});

```

## Building Document Sections from Headings

The markdown body reconstruction relies on the **heading boundaries** captured during AST traversal. Using the line numbers of each `##` heading, the parser extracts raw markdown substrings from the original file content. This approach preserves the original formatting, whitespace, and any nested content that might not appear in the AST.

The resulting `documentSections` array contains objects with the heading title and raw content, enabling tools to render or diff specific sections without re-parsing the entire document.

## Merging and Validating YAML Tokens

After extraction, the parser processes each YAML block using the `yaml` library (lines 45-48). All parsed objects are merged into a single token tree representing the design system. However, the parser enforces strict uniqueness: if the same top-level key appears in both front matter and a fenced code block, it returns a `DUPLICATE_SECTION` error (lines 61-75).

This validation ensures token definitions remain unambiguous across the file. The final output is a `ParsedDesignSystem` instance containing:

- The merged **token tree**
- A **sourceMap** for error reporting
- The ordered list of **sections** (headings)
- The raw **documentSections** for content rendering

## Implementation Example

The following example demonstrates direct usage of the `ParserHandler` to process a DESIGN.md file containing both front matter and markdown body:

```typescript
import { ParserHandler } from '@google/design.md/linter/parser/handler';
import { ParserInputSchema } from '@google/design.md/linter/parser/spec';

const designMd = `---
name: Horizon
colors:
  primary: "#112233"
---

## Overview

The Horizon theme focuses on deep blues.

## Typography

All headings use Inter font.
`;

const handler = new ParserHandler();
const result = handler.execute({ content: designMd });

if (result.success) {
  console.log('Tokens:', result.data.name, result.data.colors?.primary);
  // Output: Tokens: Horizon #112233
  
  console.log('Headings:', result.data.sections);
  // Output: Headings: ["Overview", "Typography"]
  
  console.log('First section body:', result.data.documentSections?.[1].content);
  // Output: The Horizon theme focuses on deep blues.
} else {
  console.error('Parse error:', result.error.code, result.error.message);
}

```

The test suite in `packages/cli/src/linter/parser/handler.test.ts` provides additional coverage for edge cases including empty front matter, malformed YAML, and duplicate key detection across block types.

## Summary

- **Dual extraction**: The parser extracts YAML from both `---` front matter delimiters and ` ```yaml ` fenced code blocks, treating them as equivalent token sources.
- **Provenance tracking**: Each block is flagged by source type (front matter vs. code) and indexed to support precise error reporting via the `sourceMap`.
- **Document sectioning**: Level-2 headings (`##`) slice the markdown body into discrete sections, preserving raw content for downstream rendering.
- **Validation**: Duplicate top-level keys across blocks trigger a `DUPLICATE_SECTION` error, while missing YAML returns `NO_YAML_FOUND`.
- **Unified output**: The `ParsedDesignSystem` combines merged tokens, section headings, and raw document content for comprehensive design system tooling.

## Frequently Asked Questions

### What happens if no YAML is found in the file?

If the parser traverses the entire AST and finds no `yaml` nodes (front matter) or `code` nodes with YAML language identifiers, it immediately returns a `NO_YAML_FOUND` error. This check occurs at lines 112-120 of [`handler.ts`](https://github.com/google-labs-code/design.md/blob/main/handler.ts) before any further processing, ensuring the design system always has a valid token definition to work with.

### How does the parser distinguish between front matter and fenced YAML blocks?

The parser uses the AST node `type` property to distinguish sources. Front matter appears as nodes with `type: "yaml"` created by `remark-frontmatter`, while fenced blocks appear as `type: "code"` nodes with a `lang` property of `"yaml"` or `"yml"`. Both are extracted into a unified `blocks` array, but front matter blocks are flagged differently from incremental code block indices to maintain source traceability.

### Can the same token key appear in both front matter and a code block?

No. The parser explicitly checks for duplicate top-level keys across all extracted YAML blocks during the merge phase (lines 140-176 of [`handler.ts`](https://github.com/google-labs-code/design.md/blob/main/handler.ts)). If a key defined in the front matter section also appears in a fenced YAML code block, the parser returns a `DUPLICATE_SECTION` error, preventing ambiguous token definitions in the design system.

### How are markdown headings used to structure the document body?

The parser captures every level-2 heading (`##`) during AST traversal, recording its text and line number. Using these line positions, it slices the original file content into an array of `documentSections`, where index 0 represents the pre-lude (content before the first heading) and subsequent indices map to each heading's content. This preserves the original markdown formatting while enabling section-based navigation and diffing.