layout: html
本文をそのまま単体の HTML 文書として出力するレイアウト。ヘッダーもフッターも
AppDock もなく、サイトの CSS もフォントも Nuxt のランタイムも一切載らない。
書いた HTML がそのままブラウザに届く。
---
content_id: 01K...
layout: html
visibility: public
---
<style>
body { margin: 0; background: #000 }
</style>
<canvas id="c" width="800" height="800"></canvas>
<script>
const c = document.querySelector('#c')
// …
</script>何をしているか
server/middleware/html-layout.ts が Nuxt のレンダラより手前で応答する。page.body(=ブラウザが描画するのと同じ AST)を utils/markdown/renderPageHtml.ts
が HTML 文字列に直列化し、最小限の骨格を被せて返す。アプリのバンドルも
hydration も発生しない。
GET /sketch → <!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="…">
<title>Sketch</title>
<style>…</style> ← 本文の <style> がここへ上がる
</head>
<body>
<canvas id="c"></canvas>
<script>…</script> ← <script> は本文中の位置のまま
</body>
</html><!doctype html><html><head>… まで含めたフル HTML を書いてもよい。Markdown
パーサは生 HTML をフラグメントとして読むので <html> / <head> / <body> の
ラッパは落ち、中身だけがトップレベルに並ぶ。renderPageHtml はそのうち<title> <meta> <link> <base> <style> を <head> に引き上げ直す。<script> だけは引き上げない — 参照する要素より先に走ってしまうため、本文中の
位置を保つ。
charset / viewport / <title> は既定で補われるが、本文が自分で書いていれば
そちらが優先される。draft: false かつ visibility: public でないページにはnoindex, nofollow の robots meta が付く(通常ページの pages/singular.vue と
同じ規則)。
Markdown もそのまま使える
パイプラインは通常ページとまったく同じなので、# 見出し も **強調** も
Wikilink も効く。「HTML しか書けない」のではなく「HTML が素通しで通る」レイアウト。
制約
- インラインイベントハンドラは消える。
onclick=などのon*属性は MDC の
パーサ(validateProps)が安全のために剥がす。<script>の中でaddEventListenerを使う。 - ブロック要素でないタグは
<p>に包まれる。 Markdown の HTML ブロック判定は
CommonMark のタグ一覧に従うので、<canvas>や<custom-element>を単独行に
書くと<p><canvas …></canvas></p>になる。リセット CSS が無い分、<p>の
デフォルトマージンがそのまま効く。<div>で包むか、CSS 側でp { margin: 0 }
を書く。 - MDC コンポーネントは描画されない。
::heroや:date[…]は Vue
コンポーネントであり、ここには Vue がいない。<hero>のような不活性なカスタム
要素として出力される。 - 公開ページ専用の経路。 バイパスが効くのは
visibilityがpublic/unlistedのページだけ。private/protectedはアクセス判定が
アプリ側にあるので通常の SSR に落ち、記事として表示される。
編集する
素の HTML 文書には AppDock もコマンドパレットもないので、?edit を付けると
同じ URL で通常のアプリに戻れる。
/sketch → 素の HTML
/sketch?edit → いつもの Nuxt アプリ(エディタが開ける)サイト内リンクから layout: html のページへ SPA 遷移した場合は、pages/singular.vue がブラウザに URL を投げ直して本物のドキュメントリクエストに
切り替える。この再読み込みは「1 タブ・1 パスにつき 1 回」に制限してある
(utils/pages/bareHtmlLayout.ts のコメント参照)— まだレプリケーションされて
いないページなど、サーバが応答できないケースでループしないため。