Custom Documentation with Handlebars Templates
Overviewโ
Extend your documentation with custom Handlebars templates when you need more control than the built-in calm-widgets provide.
Learning objectivesโ
By the end of this tutorial, you will:
- Know when to use raw Handlebars instead of (or alongside) calm-widgets
- Use core Handlebars syntax:
{{#each}},{{#if}},{{#unless}} - Use CALM-specific helpers such as
eq,kebabToTitleCase,join,json - Create a node inventory template that filters by node type
- Create a relationship details template with conditional formatting
- Combine widgets and raw Handlebars in the same template
Prerequisitesโ
Complete Custom Documentation with CALM Widgets first.
Step-by-Step Guideโ
1. When to Use Raw Handlebarsโ
In the Custom Documentation with CALM Widgets lesson you learned calm-widgets provide ready-made components. Use raw Handlebars when you need to:
- Create custom filtering logic (e.g., only show nodes of a certain type)
- Format data in ways widgets don't support
- Build complex conditional documentation
- Generate non-markdown outputs (CSV, HTML, etc.)
2. Handlebars Basicsโ
Handlebars is a simple templating language:
| Syntax | Description | Example |
|---|---|---|
{{property}} | Output a value | {{name}} |
{{#each array}}...{{/each}} | Loop over arrays | {{#each nodes}}{{name}}{{/each}} |
{{#if condition}}...{{/if}} | Conditional | {{#if description}}...{{/if}} |
{{#unless condition}}...{{/unless}} | Negative conditional | {{#unless @last}}, {{/unless}} |
{{@index}} | Current index in loop | {{@index}}. {{name}} |
{{@last}} | Is last item in loop | For comma-separated lists |
{{this}} | Current item | {{#each tags}}{{this}}{{/each}} |
3. CALM-Specific Helpersโ
The docify command includes custom helpers:
| Helper | Description | Example |
|---|---|---|
eq | Equality check | {{#if (eq node-type "service")}} |
lookup | Access property by name | {{lookup this 'unique-id'}} |
json | Output as JSON | {{json metadata}} |
kebabToTitleCase | Format text | {{kebabToTitleCase node-type}} |
kebabCase | Convert to kebab-case | {{kebabCase name}} |
isObject | Check if object | {{#if (isObject data)}} |
isArray | Check if array | {{#if (isArray items)}} |
join | Join array elements | {{join tags ", "}} |
4. Create a Node Inventory Templateโ
Create a template that filters nodes by type โ something the widgets don't do directly:
File: templates/node-inventory.md
5. Preview in VSCodeโ
Just like in the Custom Documentation with CALM Widgets lesson, you can preview this template in VSCode:
- Open
templates/node-inventory.mdin VSCode - Open the CALM preview pane (
Ctrl+Shift+C/Cmd+Shift+C) to see the rendered output
6. Create a Relationship Details Templateโ
File: templates/relationship-details.md
7. Generate Static Files with CLIโ
For CI/CD or static site generation, use the docify CLI:
calm docify \
--architecture architectures/ecommerce-platform.json \
--template templates/node-inventory.md \
--output docs/generated/node-inventory.md
calm docify \
--architecture architectures/ecommerce-platform.json \
--template templates/relationship-details.md \
--output docs/generated/relationship-details.md
8. Combine Widgets and Handlebarsโ
You can mix calm-widgets with raw Handlebars in the same template:
File: templates/combined-view.md
Don't forget to commit your progress with git. Capturing a snapshot here keeps your change history clean and meaningful.
Key conceptsโ
When to Use Each Approachโ
| Approach | Best For |
|---|---|
| calm-widgets | Quick visualisations, standard documentation |
| Raw Handlebars | Custom filtering, complex logic, non-standard formats |
| Combined | Best of both worlds |
Filtering Nodes by Typeโ
The eq helper combined with {{#if}} is the primary filtering mechanism:
Debugging Templatesโ
Add {{json nodes}} to output the raw data structure and discover available property names before using them in your template logic.
Next stepsโ
In the next tutorial, you'll use CALM Chat mode as an expert architecture advisor to identify and fix resilience weaknesses in your e-commerce platform!