Building Custom Templates
This guide covers template creation from scratch. For a quick reference of the built-in templates, see Templates Overview.
Template Files
Section titled “Template Files”Every Glyph template lives in a directory with three files:
templates/my-invoice/ template.html # HTML with Mustache placeholders styles.css # Template-specific styles (optional, can be inlined) schema.json # JSON Schema describing expected dataStep 1: Write the HTML
Section titled “Step 1: Write the HTML”Use standard HTML. Mark interactive sections with data-glyph-region attributes so users (and the AI) can target them:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <style> body { font-family: system-ui, sans-serif; margin: 0; padding: 0.75in; } .header { display: flex; justify-content: space-between; border-bottom: 2px solid #111; padding-bottom: 1rem; margin-bottom: 2rem; } table { width: 100%; border-collapse: collapse; } th, td { text-align: left; padding: 0.5rem; border-bottom: 1px solid #e5e5e5; } th { font-weight: 600; } .totals { margin-top: 2rem; text-align: right; } .totals .total { font-size: 1.25rem; font-weight: 700; } @page { size: letter; margin: 0; } @media print { body { padding: 0; } } </style></head><body>
<div class="header" data-glyph-region="header"> <div> <h1>{{branding.companyName}}</h1> <p>Invoice #{{meta.invoiceNumber}}</p> </div> {{#branding.logoUrl}} <img src="{{branding.logoUrl}}" alt="Logo" style="max-height:60px"> {{/branding.logoUrl}} </div>
<section data-glyph-region="client-info"> <h2>Bill To</h2> <p><strong>{{client.name}}</strong></p> {{#client.company}}<p>{{client.company}}</p>{{/client.company}} {{#client.email}}<p>{{client.email}}</p>{{/client.email}} </section>
<table data-glyph-region="line-items"> <thead> <tr><th>Description</th><th>Qty</th><th>Unit Price</th><th>Total</th></tr> </thead> <tbody> {{#lineItems}} <tr> <td>{{description}}</td> <td>{{quantity}}</td> <td>${{unitPrice}}</td> <td>${{total}}</td> </tr> {{/lineItems}} </tbody> </table>
<div class="totals" data-glyph-region="totals"> <p>Subtotal: ${{totals.subtotal}}</p> {{#totals.tax}}<p>Tax: ${{totals.tax}}</p>{{/totals.tax}} <p class="total">Total: ${{totals.total}}</p> </div>
</body></html>Mustache Syntax
Section titled “Mustache Syntax”| Syntax | Purpose |
|---|---|
{{variable}} | Output escaped value |
{{{raw}}} | Output unescaped HTML |
{{#section}}...{{/section}} | Conditional block or array loop |
{{^section}}...{{/section}} | Inverted block (renders when value is falsy or array is empty) |
Region Guidelines
Section titled “Region Guidelines”- Each
data-glyph-regionvalue must be unique within the template. - Regions define what users can click to modify. Keep them semantic:
header,line-items,footer,terms. - Regions can be nested, but the AI targets the most specific match.
Step 2: Define the Schema
Section titled “Step 2: Define the Schema”schema.json validates data passed to the template and documents expected fields:
{ "$schema": "http://json-schema.org/draft-07/schema#", "title": "InvoiceData", "type": "object", "required": ["client", "lineItems", "totals"], "properties": { "client": { "type": "object", "required": ["name"], "properties": { "name": { "type": "string" }, "company": { "type": "string" }, "email": { "type": "string", "format": "email" } } }, "lineItems": { "type": "array", "minItems": 1, "items": { "type": "object", "required": ["description", "quantity", "unitPrice", "total"], "properties": { "description": { "type": "string" }, "quantity": { "type": "number" }, "unitPrice": { "type": "number" }, "total": { "type": "number" } } } }, "totals": { "type": "object", "required": ["subtotal", "total"], "properties": { "subtotal": { "type": "number" }, "tax": { "type": "number" }, "total": { "type": "number" } } }, "branding": { "type": "object", "properties": { "companyName": { "type": "string" }, "logoUrl": { "type": "string", "format": "uri" } } }, "meta": { "type": "object", "properties": { "invoiceNumber": { "type": "string" }, "date": { "type": "string" }, "dueDate": { "type": "string" } } } }}Step 3: Register the Template
Section titled “Step 3: Register the Template”Self-hosted API
Section titled “Self-hosted API”Place the template directory inside your templates/ folder and restart the API:
templates/ quote-modern/ my-invoice/ <-- your new template template.html schema.json styles.cssThen reference it in the SDK or API calls:
<glyph-editor template="my-invoice" ...></glyph-editor>curl -X POST https://api.glyph.you/v1/preview \ -H "Authorization: Bearer gk_demo_playground_2024" \ -H "Content-Type: application/json" \ -d '{ "template": "my-invoice", "data": { "client": { "name": "Test" }, "lineItems": [...], "totals": { "subtotal": 100, "total": 100 } } }'Glyph Cloud
Section titled “Glyph Cloud”Upload via the dashboard under Templates > Upload Template.
Step 4: Test
Section titled “Step 4: Test”- Preview — Call
/v1/previewwith sample data and verify the rendered HTML. - Modify — Try AI modifications on each region: “make the header blue”, “add a watermark”.
- Generate — Call
/v1/generateand check the PDF output for page breaks, color accuracy, and font rendering. - Edge cases — Test with missing optional fields, very long text, large numbers, and empty arrays.
Print CSS Tips
Section titled “Print CSS Tips”- Use
@page { size: letter; margin: 0.5in; }to control page dimensions. - Add
print-color-adjust: exact;and-webkit-print-color-adjust: exact;to preserve background colors in PDF output. - Avoid
position: fixed— it does not work in paged media. - Use
page-break-inside: avoid;on rows you do not want split across pages.
Next Steps
Section titled “Next Steps”- Templates Overview — see built-in templates
- Custom Templates Reference — additional details and examples
- POST /v1/preview — API reference for rendering templates