Krafter

Sign inGet started

Template authoring guide

April 12, 2026

Community templates in Krafter are created entirely through the web editor — no code deployment required. A template has two parts: a CSS file that controls visual presentation, and a Handlebars file (.hbs) that generates the HTML structure. Both are written in the browser.

Creating a template

Go to My Templates in the dashboard and click New Template. Enter a name and an optional description. After creation you are taken directly to the template editor.

The template editor

The editor has two code tabs alongside a live preview:

  • Handlebars — the template that generates the resume HTML
  • CSS — all visual styling

The live preview updates as you type. Use the Standard / Minimal / Extended buttons above the preview to switch between sample datasets and see how your template handles different amounts of content.

The preview renders using your Handlebars file directly — what you see is exactly what users will see when they apply your template to a resume.

Writing CSS

Every CSS rule must be scoped to your template's data-template attribute selector. The key is derived from your template name — lowercased with spaces replaced by hyphens. A template named "Clean Slate" uses the key clean-slate.

Krafter provides tk-* utility classes (from _utils.css) that wire CSS design tokens to standard resume elements. Your template CSS only needs to override the visual distinctives — you don't need to redeclare all token bindings.

.resume[data-template="clean-slate"] {
  padding: var(--margin-top, 30px) var(--margin-right, 30px)
           var(--margin-bottom, 30px) var(--margin-left, 30px);
}

/* Override section heading style — tk-section-heading already
   binds --size-label, --weight-label, --color-label from tokens */
.resume[data-template="clean-slate"] .tk-section-heading {
  border-bottom: 1px solid var(--color-rule, #cccccc);
  padding-bottom: 2pt;
  margin-bottom: 6pt;
}

/* Item date styling */
.resume[data-template="clean-slate"] .tk-item-date {
  font-style: italic;
}

Krafter injects CSS custom properties for typography, spacing, and colors onto the .resume root element. Use var(--name, fallback) to respect the user's settings. The tk-* classes already consume these tokens — only override what your template needs to look distinct. See the CSS Variables Reference doc for the full list.

Writing Handlebars

The Handlebars file receives the resume data and outputs HTML. It is used for the live preview, PDF export, and HTML export — all rendering goes through your Handlebars file.

Use tk-* class names on your HTML elements so the _utils.css utility layer can wire design tokens automatically. Your template CSS then overrides only the visual distinctives via [data-template] specificity.

A minimal starting point:

<div class="tk-header">
  {{#if fullName}}<div class="tk-resume-name">{{fullName}}</div>{{/if}}
  {{#if jobTitle}}<p class="tk-subtitle">{{jobTitle}}</p>{{/if}}
  {{#if contactItems}}
  <div class="tk-contact">
    {{#each contactItems}}
      <span class="tk-contact-item" data-type="{{type}}">
        {{#unless @first}}<span class="tk-separator">|</span>{{/unless}}{{value}}
      </span>
    {{/each}}
  </div>
  {{/if}}
  {{#if links}}
  <div class="tk-links">
    {{#each links}}<span>{{#unless @first}}<span class="tk-separator">|</span>{{/unless}}<a class="tk-link" data-type="{{title}}" href="{{href}}">{{title}}</a></span>{{/each}}
  </div>
  {{/if}}
</div>

{{#if summary}}
<div class="tk-section-heading" data-section-type="summary">Summary</div>
<div class="tk-section-heading-rule"></div>
<div class="tk-section-content tk-rich-text" data-section-type="summary">{{{summary}}}</div>
{{/if}}

{{#each sections}}
<div class="tk-section-heading" data-section-type="{{type}}" data-category="{{category}}">{{name}}</div>
<div class="tk-section-heading-rule"></div>

{{#if historyItems}}
  {{#each historyItems}}
  <div class="tk-history-item{{#unless isFirst}} tk-history-gap{{/unless}}">
    {{#if hasHeader}}
    <div class="tk-item-header">
      <div class="tk-item-title">{{heading}}</div>
      {{#if duration}}<div class="tk-item-date">{{duration}}</div>{{/if}}
    </div>
    {{/if}}
    {{#if subtitleParts}}<div class="tk-item-subtitle">{{join subtitleParts ", "}}</div>{{/if}}
  </div>
  {{#each descriptionBlocks}}<div class="tk-item-body tk-rich-text">{{{this}}}</div>{{/each}}
  {{/each}}

{{else if tagItems}}
  <div data-section-type="{{type}}" data-category="tags">
    <div class="tk-section-content tk-tag-list">
      {{#each tagItems}}<span>{{#unless @first}}<span class="tk-separator"></span>{{/unless}}<span class="tk-badge tag-item">{{display}}</span></span>{{/each}}
    </div>
  </div>

{{else if languageItems}}
  <div data-section-type="{{type}}" data-category="languages">
    <div class="tk-section-content">{{join languageItems "display" ", "}}</div>
  </div>

{{else if referenceItems}}
  <div data-section-type="{{type}}" data-category="references">
    <div class="tk-section-content">
      {{#each referenceItems}}
      <div class="tk-reference">
        <div class="tk-reference-name">{{name}}</div>
        {{#if details}}<div class="tk-reference-detail">{{details}}</div>{{/if}}
        {{#if contact}}<div class="tk-reference-contact">{{contact}}</div>{{/if}}
      </div>
      {{/each}}
    </div>
  </div>
{{/if}}

{{/each}}

See the Handlebars Variables Reference for the complete data object, all section types, and the two custom helpers (join and where) registered in Krafter.

The working copy

Everything you write in the editor is the working copy — a single mutable draft. You can apply it to your own resumes from the Render Settings drawer before cutting a version — it appears under My Templates with a Draft badge when no published version exists yet.

  • Click Save to persist the draft. There is no autosave and no undo history — saving overwrites the previous state.
  • The preview always reflects the current (unsaved) state in the editor — you do not need to save to see changes.

When a resume is using a Draft template, the renderer reads from the live working copy — any change you Save is immediately reflected in the resume preview, PDF, and HTML export. Once you cut a version (see below), the working copy is cleared and those resumes will need to be updated to the new version.

Publishing a version

When you are satisfied with the template, click Cut Version. This takes an immutable snapshot of the current CSS and Handlebars, assigns the next version number (v1, v2, v3, …), and clears the working copy. Any resume that was using the Draft will show an Update available prompt in the template picker — click it to switch the resume to the new stable version.

You can cut as many versions as you like. All previous versions are kept in the version history on the template page.

Sharing with the community

Templates are private by default. Click Share at the top of the template page to make it publicly visible in the community gallery at /templates. Other users can then browse your template and create resumes from it. Unsharing removes it from the gallery without affecting resumes already using it.

Deleting a template

Click Delete on the template page and confirm by typing the template name. All published versions are removed permanently. Resumes that were using the template will lose their preview: the thumbnail on the home page will show a Preview not available message, and the compose editor will show a blank page. Switch affected resumes to a different template before deleting.

Limitations

The following are not supported for community templates:

  • Multi-column (sidebar) layouts — these require code-level changes and are not available in the web editor.
  • Remote font imports — CSS @import of external URLs is blocked. Fonts are provided via the user's selection in the Render Settings drawer (curated catalog or any of 1,700+ Google Fonts) through the --font-body and --font-display tokens.
  • JavaScript or event handlers<script> tags, javascript: URLs, and on* event attributes are blocked in both CSS and Handlebars.
  • Custom scoping key — the CSS key is always derived from the template name and cannot be set manually. Renaming the template would break existing CSS selector rules.
  • No autosave or undo — the editor is a plain textarea. Save manually and save often.