Template code reference
テンプレートで使える特殊コード
KuroCMS のテンプレートは、[[...]] という特殊コードを含む「未展開の HTML 原本」です。公開ビルド時に CMS がこれらを実データへ展開します。テンプレートを自作・修正する人が使える表現を、役割とサンプルつきでまとめました。
先頭マーカー必須
HTML 原本の 1 行目は必ず <!-- kurocms-template-api:1 --> で始めます。未知の値は空文字に、オブジェクトの直接出力は不可、セクションの閉じ忘れは描画エラーになります。[[html:…]] は無加工出力なので信頼できる CMS データにのみ使ってください。
1. 基本構文 [[...]]
| 構文 | 動作 | 例 |
|---|---|---|
[[path]] / [[value:path]] | 値を HTML エスケープして出力 | [[site.name]] |
[[html:path]] | 無加工で HTML 出力(信頼できる CMS データのみ) | [[html:article.bodyHtml]] |
[[#if path]]…[[/if]] | 値が真のとき中身を出力 | [[#if page.isArticle]]…[[/if]] |
[[#unless path]]…[[/unless]] | 値が偽のとき中身を出力 | [[#unless article.coverUrl]]…[[/unless]] |
[[#each path]]…[[/each]] | 配列を反復(中は相対パス / [[this]]) | [[#each articles]][[title]][[/each]] |
[[#each type:{slug}:{N}]]…[[/each]] | 指定タイプの記事を新しい順に N 件(下の 3 章) | [[#each type:news:5]] |
2. データモデル(参照できるパス)
| パス | 内容 |
|---|---|
page.isHome / isStatic / isPrivacy / isTerms / isArticle / isType / isCategory / isRecipe | 現在ページの種別フラグ。記事下部の関連記事などは [[#if page.isArticle]] で囲む。isRecipe はレシピカードを持つ記事で true。固定ページ(About など)は slug を問わず isStatic で分岐する(下の 9 章) |
site.name / site.lang / site.dir / site.basePath | サイト名 / 表示言語 / 書字方向(rtl|ltr) / 公開 URL プレフィックス |
content.{key} | 管理画面「サイト文字編集」で編集するサイトテキスト(例 content.footer-text) |
navigation.types / navigation.categories | タイプ / カテゴリ配列(件数は含まない)。各項目の isCurrent は現在ページに対応する項目だけ true → ナビの現在地ハイライトに使う |
navigation.pages | ナビに出す固定ページの配列(slug / title / isCurrent)。About などのリンクは [[#each navigation.pages]] で作り、URL は直書きしない(下の 9 章) |
page.static.titleHtml / bodyHtml / summaryHtml / coverHtml (+ title は平文) | 表示中の固定ページの中身。slug に依存しないので、ページが増えてもテンプレートは無改修(下の 9 章) |
articles | 一覧ページの記事配列(slug, tid, title, summary, publishAt, coverUrl, categories) |
article | 記事詳細(上記 + type, bodyHtml, updatedAt)。記事ページ以外は null |
article.recipe | レシピカードの内容(下の 7 章)。カードが無い記事では null |
pagination | page / totalPages / prevUrl / nextUrl(旧ページング用。一覧では原則 null) |
type.id / type.name · category.id / category.name | 現在のタイプ / カテゴリ |
integrations.blueskyHandle | Bluesky ハンドル |
3. テンプレートから記事を引く(データ参照) KuroCMS v1.8.79+
articles はそのページのスコープの記事しか持ちません(TOP なら全タイプの最新が混ざります)。TOP に「お知らせ枠=news」「製品枠=products」のようなタイプ別の枠を作りたいときは、テンプレートから名指しで引きます。
| 記法 | 内容 | 例 |
|---|---|---|
[[#each type:{slug}:{N}]] | 指定タイプの記事を新しい順に N 件 | [[#each type:news:5]] |
[[#each category:{slug}:{N}]] | 指定カテゴリの記事を新しい順に N 件 | [[#each category:tips:3]] |
[[#each articles:latest:{N}]] | 全タイプ横断で最新 N 件 | [[#each articles:latest:8]] |
[[#each type:all]] | タイプ一覧(件数つき) | [[#each type:all]][[name]][[/each]] |
[[#each category:all]] | カテゴリ一覧(件数つき) | [[#each category:all]][[name]][[/each]] |
- 件数
Nは 1〜50(省略時 10) - 存在しない slug は空配列。
[[#each]]が何も出さないだけでテンプレートは壊れません - マークアップは 100% テンプレート側の責務です。CMS は HTML を組み立てず、記事カードのデータだけを渡します
- テンプレートに書いた参照だけを取得します。使わなければクエリは 1 本も増えません
配置例(トップページのタイプ別 2 枠)
<h2>更新情報・お知らせ</h2>
<ul>
[[#each type:news:5]]
<li><a href="[[site.basePath]]/[[tid]]/[[slug]]/"><time>[[date]]</time>[[title]]</a></li>
[[/each]]
</ul>
<h2>主な製品・サービス</h2>
[[#each type:products:3]]
<a class="card" href="[[site.basePath]]/[[tid]]/[[slug]]/">
[[#if coverUrl]]<img src="[[coverUrl]]" alt="">[[/if]]
<h3>[[title]]</h3><p>[[summary]]</p>
</a>
[[/each]] 4. カルーセルと広告 KuroCMS v1.8.79+
画像カルーセル [[carousel:{key}|{opts}]]
「どんな見た目・サイズか」はテンプレート、「どの画像を出すか」は運用者という分担です。テンプレートに画像 ID を直書きしません。
テンプレート
[[carousel:hero|100%x320,fade,arrows,5s]] サイトテキスト hero
img-100,img-101,img-102 - サイズ
100%/100%x320/640x360(数値だけなら px。既定 100%x320) - 切替
fade(既定) /slide - 矢印
arrows(既定) /noarrows - ドット
dots(既定) /nodots - 自動送り
5s(既定) /0s=自動送りなし - 順序は自由(
[[img-100|60%,right]]と同じカンマ区切り) - 画像 0 枚なら何も出力せず、1 枚なら矢印もドットも出しません
自動送りは prefers-reduced-motion とマウスホバーで停止します。ルートに class="kuro-carousel" が付くのでテンプレート側で装飾できます。
広告(Google AdSense)
テンプレートに [[ad]] を置くと AdSense の広告ユニットに展開します。預けるのは ID だけで、配布された広告コードを貼り付ける必要はありません(スクリプト本体は CMS が組み立てます)。
設定(サイトテキスト ad-adsense)
ca-pub-1234567890123456,1234567890,9876543210 1 行にカンマ区切りで書きます。先頭がクライアント、以降が枠 ID です。キーを分けないのは、分けると片方の設定漏れに気付けないためです。
[[ad]]=1 番目の枠、[[ad:2]]=2 番目の枠(1 始まり)- 未設定・形式不正・枠番号が範囲外のときは何も出力しません(壊れたタグを残さない)
- AdSense のローダーは実際に広告を出したページにだけ読み込まれます
- ユニットには
class="kuro-ad adsbygoogle"が付くので、.kuro-adをテンプレート側で自由に装飾できます
Amazon アソシエイトは未対応です。トラッキング ID は成果の帰属に使う識別子であって、それだけで広告ユニットを描画する仕組みではありません(ネイティブショッピングウィジェットには別途インスタンス ID が必要)。
5. サイトテキスト内で使える表現
管理画面「サイト文字編集」の各値(content.{key})の中では、テンプレートパーサーに渡す前に次が展開されます。
| 表現 | 内容 |
|---|---|
[[type:all]] / [[category:all]] | タイプ / カテゴリ一覧 JSON |
[[type:slug:N]] / [[category:slug:N]] | 指定タイプ / カテゴリの記事 N 件 |
[[articles:latest:N]] | 最新記事 N 件 |
[[article:slug]] | 指定記事のデータ |
[[mid]] | 画像・動画・音声タグへ展開(例 [[img-816]]) |
[[sid]] | 対応する SNS ウィジェットへ展開 |
[[lang]] | 言語スイッチャ・ウィジェットへ展開(ナビ等に配置) |
[[search]] | 記事検索ウィジェットへ展開(ナビの TYPE 左などに配置)。PC は入力欄+検索アイコン、スマホはアイコン→タップでダイアログ |
[[privacy]] / [[terms]] | プライバシーポリシー / 利用規約ページへのリンクに展開。対応するサイトテキストが空なら何も出ない(デッドリンクを作らない)ので、フッターに無条件で置いてよい |
[[ad]] / [[ad:2]] | Google AdSense の広告ユニット(下の 4 章) |
[[carousel:{key}|{opts}]] | 画像カルーセル。画像はサイトテキスト {key} にメディア ID をカンマ区切りで並べる(下の 4 章) |
6. 予約サイトテキスト slug
実体を保存しなくても、CMS がビルド時に自動生成する特別な content.* です。テンプレートに置くだけで機能します。
[[html:content.related-N]] 関連記事ストリップ。記事ページで、その記事と同じカテゴリの公開日が近い記事を N 枚並べます。中央の ←・→ が当記事で、左に古い記事 floor(N/2) 枚・右に新しい記事 ceil(N/2) 枚。端(最古/最新)では反対側から補充して常に N 枚を保ちます。画像とタイトルのみの小さなカードで、画面幅が狭くなっても折り返さず縮小して N 枚を維持します。
Nは 1〜20。枚数を変えるだけ:related-2/related-4/related-6- 記事ページ以外・カテゴリ未設定・近傍記事なしのときは空文字(何も出ません)
- データは書き換えず、表示専用。KuroCMS 本体が対応バージョン以外なら安全に無視されます
配置例(記事本文の直後 = 筆者表示の下)
[[#if page.isArticle]]
<article>
<div class="prose">[[html:article.bodyHtml]]</div>
[[html:content.related-4]]
</article>
[[/if]] [[html:archives]] | 月別アーカイブ切替の <select>。一覧ページで使用 |
7. レシピカード page.isRecipe
本文エディタの鍋アイコンから「レシピカード」(人数・時間・材料・手順)を挿入できます。カードを 1 枚置いた記事が、そのままレシピ記事になります — 専用の記事タイプは要りません。タイプが news でも blog でも同じように働きます。カードの中身はテンプレートからも構造化データとして取り出せるので、独自レイアウトで描き直せます。
page.isRecipe / article.recipe KuroCMS v1.8.78+ [[html:article.bodyHtml]] を出すだけでもカードはそのまま表示されます。下の値を使うのは、テンプレートの意匠に合わせて組み直したいときだけです。
page.isRecipe | レシピカードを持つ記事ページで true。タイプ名の文字列比較はせず、必ずこのフラグで分岐する |
article.recipe.yield | 人数(自由文字列。例「4人分」) |
article.recipe.prepTimeMinutes | 下準備(分)。未入力ならキーごと存在しない |
article.recipe.cookTimeMinutes | 調理(分)。未入力ならキーごと存在しない |
article.recipe.totalMinutes | 下準備+調理。どちらも未入力なら null |
article.recipe.ingredients | 材料の配列。[[#each]] の中で [[name]] / [[amount]](amount は省略可) |
article.recipe.instructions | 手順の配列。[[#each]] の中で [[text]] |
- 構造化データ(Schema.org
Recipe)は KuroCMS が自動で出します。テンプレート側で JSON-LD を書かないでください(二重になります) - 1 記事 = 1 レシピ。2 枚目はエディタ側で挿入できないようになっています(鍋アイコンがロックされます)。料理名・説明・完成画像・公開日はカードではなく記事共通のもの(
article.title/article.summary/article.coverUrl)を使います amountは省略できるので[[#if amount]]で囲みます- カードが無い / 壊れている記事では
page.isRecipeが false になり、通常記事としてそのまま描けます(構造化データもArticleに戻ります)
記事ページでの表示例
[[#if page.isRecipe]]
<section class="recipe">
<p class="meta">[[article.recipe.yield]][[#if article.recipe.totalMinutes]] ・ 合計 [[article.recipe.totalMinutes]] 分[[/if]]</p>
<h2>材料</h2>
<ul>
[[#each article.recipe.ingredients]]
<li>[[name]][[#if amount]] <span>[[amount]]</span>[[/if]]</li>
[[/each]]
</ul>
<h2>作り方</h2>
<ol>
[[#each article.recipe.instructions]]<li>[[text]]</li>[[/each]]
</ol>
</section>
[[/if]] 8. 記事ページの最小サンプル
<!-- kurocms-template-api:1 -->
<!doctype html>
<html lang="[[site.lang]]">
<body>
[[#if page.isArticle]]
<article>
[[#if article.coverUrl]]<img src="[[article.coverUrl]]" alt="">[[/if]]
<h1>[[article.title]]</h1>
<div class="prose">[[html:article.bodyHtml]]</div>
[[html:content.related-4]]
</article>
[[/if]]
</body>
</html> 9. 固定ページ(About・会社概要・FAQ …) page.isStatic
[[html:page.static.titleHtml]] を置くだけにし、自分で <h1> を書き足さないこと — 書き足すと装飾を変えるたびにテンプレート修正が要ります。見出しに指定し忘れた場合は CMS が h1 を補います。page.static.title は <title> 用の平文なので画面表示には使いません。記事ではない「置きっぱなしのページ」は、テンプレート冒頭の kurocms-pages 宣言で定義します。[[...]] には等値比較がありません。テンプレート側で「今 About か?」を判定する術が無いので、どのサイトテキストをどのページに出すかは宣言側が決め、テンプレートは slug に依存しない page.static.* で描くだけにします。
ページを増やしてもテンプレートは無改修
描画ブロックは [[#if page.isStatic]] の 1 つだけ。2 ページ目・3 ページ目は宣言に 1 行足すだけで増やせます。ナビも navigation.pages から自動生成されます。
やってはいけない書き方: content.about-body のように特定ページのキーを直書きする/page.isAbout で分岐する/<a href="/about/"> と URL を直書きする。いずれもページが増えた瞬間に破綻し、2 ページ目に About の中身が出ます。
宣言(HTML 原本の先頭マーカーの直後)
<!-- kurocms-template-api:1 -->
<!-- kurocms-pages: [
{"slug":"about","titleKey":"about-title","bodyKey":"about-body","nav":true,"summaryKey":"about-summary","coverKey":"about-cover"},
{"slug":"faq","titleKey":"faq-title","bodyKey":"faq-body","nav":true}
] --> | キー | 要否 | 内容 |
|---|---|---|
slug | 必須 | 公開 URL になる(about → /about/) |
titleKey | 必須 | 見出しに使うサイトテキストのキー |
bodyKey | 必須 | 本文に使うサイトテキストのキー |
nav | 必須 | true でナビ(navigation.pages)に載る |
summaryKey | 任意 | リード文のキー。省略すると page.static.summaryHtml は空文字 |
coverKey | 任意 | 表紙画像のキー。省略すると page.static.coverHtml は空文字 |
redirectFrom | 任意 | 旧 URL の配列。そのパスへ来た訪問者をこのページへ転送する |
summaryKey / coverKey は KuroCMS v1.9.31 以降。古い本体では単に空文字になるので、宣言に書いてあっても壊れません。
描画(この 1 ブロックで全固定ページを賄う)
<nav>
[[#each navigation.pages]]
<a href="[[site.basePath]]/[[slug]]/" class="[[#if isCurrent]]on[[/if]]">[[title]]</a>
[[/each]]
</nav>
[[#if page.isStatic]]
<article>
[[#if page.static.coverHtml]]<div class="cover">[[html:page.static.coverHtml]]</div>[[/if]]
[[html:page.static.titleHtml]]
[[#if page.static.summaryHtml]]<div class="lead">[[html:page.static.summaryHtml]]</div>[[/if]]
<div class="prose">[[html:page.static.bodyHtml]]</div>
</article>
[[/if]]