2-10 / Series
2-10 № 10 · 2026

中身は AsciiDoc、
外枠だけ HTML と CSS。

中身は AsciiDoc と Mermaid、外枠は最小限の HTML と CSS

Web サイトは、二層に分けて作る。

2-09: 会議とカレンダーを自分の側に ── Jitsi と CalDAV で、集まりと予定を自分の側に置いた。ここでは外に見せる Web を、自分の手元で組み立てる。中身は AsciiDoc と Mermaid、外枠は HTML と CSS と最小限の JavaScript、両者を Python が繋ぐ。中身の形式は 2-07 と同じで、Markdown でも同じことができる。焼き上がった HTML をどこに置くかは、次の章が受け持つ。

React の疲れは、道具の数から来る

過去 10 年、Web 開発はフレームワークの軍拡だった。

jQuery、Backbone、Angular、React、Vue、Svelte。ビルドツールは Grunt、Gulp、Webpack、 Rollup、Vite、Turbopack。CSS は Sass、Less、PostCSS、Tailwind、CSS-in-JS。サーバー側の描画は Next.js、Nuxt、Remix、SvelteKit、Astro。

どれも何かの問題を解いてきた。しかし、解いた問題と新しく生まれた問題を並べると、割に合わない場面が増えた。

これは技術そのものの複雑さではなく、自分たちで足した複雑さだ。素の HTML と CSS と JavaScript に戻ると、この五つが一度に片づく。ブラウザはこの三つを直接読むので、間に変換の層が要らない。そして AI が最も安定して書けるのも、この三つと、文字の原稿だ。

WordPress には四つの問題がある

「React は使っていない、うちは WordPress だ」── 多くの人がそう思っているはずだ。

それは事実に合っている。世界の Web サイトの 40.1% が WordPress で動いている(W3Techs、 2026-10-05)。日本でも企業の公式サイト、個人ブログ、ニュースメディア、自治体の Web ── あらゆる場所にある。

ただし WordPress には WordPress の問題がある。React の問題とは質が違い、深刻さは同じか、それ以上だ。

問題 1: 中身が MySQL に入る

WordPress の記事は、テキストファイルではなく、MySQL の中の HTML 混じりのレコードとして保存される。同じ記事を PDF にしたい、別のサイトに移したい、AI に渡して分析したい ── どの道もエクスポート作業から始まる。

しかもエクスポート形式は .xml(WordPress 独自の WXR)で、HTML タグや独自の短縮記法 ([gallery] など)が混ざっている。中身が WordPress の側に残る。これは 2-03: 土台を据える ── SQLite・PostgreSQL・pgvector・DuckDB・Polars で見た「Excel に入ったままのデータ」と同じ形だ。

問題 2: プラグインが攻撃面を増やす

WordPress の機能拡張は、世界中の有志が作ったプラグインに頼る。一つのサイトに何十ものプラグインが入っているのは珍しくない。それぞれが定期的に脆弱性を出す。サイト全体が攻撃面の集合になる。世界の Web の四割が同じ装置で動くということは、攻撃する側から見れば、四割の標的が同じ装置だということでもある。

問題 3: 更新が互いを止める

WordPress 本体、テーマ、プラグイン ── これらが互いに依存している。一つを更新すると、別のものが動かなくなる。「更新を当てたらサイトが落ちた」は、WordPress の運用でよく起きることだ。

問題 4: PHP より Python のほうが、AI の出力が安定する

WordPress のテーマとプラグインは PHP で書かれている。AI は PHP も書けるが、出力の安定度は Python のほうが高い。AI と組んで道具を作るなら、Python の側に寄せたほうが速い。

とくに急ぐのは、ネットショップだ。決済と顧客の個人情報を扱うため、価値の高い標的になる ── カード情報の抜き取り、個人情報の流出。被害が直接、お金と信用に響く。 WordPress のネットショップは、まっ先に静的にするとよい。決済は Stripe などの外部の決済ページに任せ、カード情報を自分で持たない。動く PHP・データベース・管理ログインを手放せば、攻撃面ごと消える。

WordPress からは、七つの手順で抜ける

WordPress を使っているなら、抜け道は決まっている。 2-12: API を作る ── FastAPI で基幹のロジックを出す で扱う 並行稼働 と同じ形で移る。

  1. 既存の WordPress を動かしたまま、中身を文字(Markdown か AsciiDoc)にエクスポートする(プラグインや CLI ツールで自動化できる。変換コードは AI が書く)
  2. 新しいサイトを文字の原稿 + 最小限の HTML/CSS + Python で組む
  3. 同じ URL 構造を保つ(検索の評価を引き継ぐ)
  4. ステージング環境で動きを確認し、検索エンジンへのインデックス申請を用意する
  5. DNS を切り替える日を決める
  6. 切り替えの後、WordPress を読み取り専用にして 1 か月動かす
  7. 問題が出なければ WordPress を止め、ホスティング契約を解く

費用も変わる。WordPress のホスティングには月額がかかるが、焼いた HTML は 2-02 の一台に置けば追加の費用が無い(置き場は 2-11)。

WordPress も並行稼働で抜ける。文字へエクスポートし、外枠だけ HTML と CSS で書き、 Python で生成して、静的に配る。これで WordPress を止められる。

作り直す時間が無ければ、もっと速い手もある ── 今あるサイトを丸ごと静的に写し取る。ヘッドレスブラウザ(Playwright)で各ページを開き、HTML と、読み込む全ファイル (CSS・JS・画像・フォント)を保存し、参照を相対パスに書き換える。既定では JavaScript を実行せず、サーバーが返す素の HTML を保存する ── 広告や解析ウィジェットが描画時に足す膨大な DOM が入らない、軽い HTML になる。本文を JS で組み立てるサイトのときだけ、描画を有効にすればいい。どちらにせよブラウザはページが実際に読み込むファイルを残らず辿って落とすので、requests や wget のような取りこぼしがない。これを静的配信に載せれば、動く WordPress を攻撃面ごと止められる。Mac の SiteSucker(有料)と同じことが、 AI に頼めば Python で書ける(このサイトのリポジトリには tools/mirror_site.py がある)。

中身と外枠を、二層に分ける

Web サイトの中身は、二つに分けられる。

種類 何を書くか 道具
中身 文章、表、引用、コード、図 AsciiDoc + Mermaid(Markdown でも同じ)
外枠 ヘッダー、ナビ、フッター、レイアウト、配色 HTML + CSS + 最小限の JavaScript
接続 中身を外枠に流し込んで HTML を生成 Python

これを混ぜて書くのが、これまでの Web 制作の問題だった。React のコンポーネントには、文章と書式とロジックが一緒に入っている。WordPress の記事には、HTML タグと文章が一緒に入っている。後から書き換えるとき、この混在が効いてくる。

AI ネイティブな分け方は、中身と外枠を完全に離すことだ。中身は AsciiDoc と Mermaid だけで書き、HTML タグは置かない。外枠は HTML と CSS で書き、各記事の中身には触らない。Python が機械的に繋ぐ。

flowchart LR subgraph Content["中身(別の出口にも回せる)"] MD["記事 *.adoc
(AsciiDoc)"] MMD["図 *.mmd
(Mermaid)"] end subgraph Frame["外枠"] HTML["template.html"] CSS["style.css"] JS["main.js(最小)"] end Py(("Python
ビルド")) MD --> Py MMD --> Py HTML --> Py CSS --> Py JS --> Py Py --> Site["静的 HTML サイト"] MD -.->|そのまま| PDF["PDF / 印刷"] MD -.->|そのまま| AI[("AI 分析")] MD -.->|そのまま| Book["電子書籍"] classDef good fill:#e8f5e9,stroke:#7a9a6d,color:#3a4d34 classDef bad fill:#fef3e7,stroke:#c89559,color:#5a3f1a class MD,MMD good class HTML,CSS,JS bad

中身を文字で持つと、出口が増える

中身を AsciiDoc と Mermaid で書く最大の理由は、同じデータが Web 以外の出口にも回ることだ。

同じ原稿のファイルから、

中身が Web の側に残らない。WordPress や Wix で書いた中身は、サービスが終わるとそこまでだ。 Notion で書いた中身は、Notion の形式の中にある。文字の原稿は、どこにも属さない。

これは 2-07: 文書を取り戻す ── 読む物は adoc、触る表は格子、刷る紙はテンプレート で見た「中身は素のテキストで持つ」考え方の、Web 版だ。入口・中身・出口を分ける。

外枠は最小限で書く

外枠 ── ヘッダー、ナビ、フッター、レイアウト、配色 ── は、HTML と CSS で直接書く。最小限で足りる。

これらは年に数回しか触らない。外枠に時間を掛けすぎない。骨格は骨格として置き、時間は中身に使う。

手元に置く物

要らなくなる物

残るのは、素の Web 標準と文字の原稿だけだ。HTML / CSS / JS には変換の層が無く、 AsciiDoc と Markdown は AI がよく扱う記法だ。どちらも、AI が出した物をそのまま置ける。

npm を離れるもう一つの理由は、サプライチェーンにある

ビルドが遅いこと、依存が重いことだけが理由ではない。npm を離れるもう一つの理由は、 レジストリを通して他人のコードが実行される という構造にある。

npm レジストリでは、サプライチェーンを狙う攻撃が繰り返し起きている。似た名前のパッケージで誤インストールを誘うタイポスクワッティング、依存パッケージ経由のコード注入、メンテナーのアカウント侵害によるパッケージの乗っ取り、ポストインストールスクリプトでの秘密情報の流出 ── 確かめられる代表例だけでも、こう並ぶ。

ほぼ毎年、大きな事件が報告されている。個別の事件は、 3-02: 物語を確かめる ── ベンダーの語りを一次情報で検算する の手順で、各自が一次情報に当たって確かめてほしい。

この構造は、npm の設計から来ている

問題は、npm の生態系の設計そのものにある。

このうち一つが乗っ取られれば、それを取り込んだ全部のプロジェクトに届く。一つのパッケージの乗っ取りが、それを知らずに使っている全員に及ぶという形は、npm を使う限り付いてくる。この構造がどう効いてくるかは 3-05: ロックイン問題 の話と噛み合う。

これに対して、この章のスタックは依存が少ない。AsciiDoc + HTML + CSS + JS は外部依存ゼロ(ブラウザ標準だけ)、Python のビルドの依存は一桁だ。一桁なら、全てのソースを AI と一緒に読み通せる。怪しい挙動があれば気付ける。何百何千の依存は、人の目では追い切れない。この章の決めは、依存を一桁に保つことだ。侵害される表面積が、そのぶん小さい。

追い切れない依存に AI を入れるということ

AI 時代に、もう一つ重い点がある。

AI のエージェントは、エディタの裏でファイルを読み、コマンドを実行する。開発機の node_modules のどこかに悪意のあるパッケージが潜んでいれば、*AI 経由でその実行が引き起こされる* 可能性がある。

追い切れない数の依存を抱えるプロジェクトに AI を入れることは、信頼できない他人をその数だけ、自分の作業空間に招き入れることと同じだ。

依存の無いスタックなら、この心配がない。HTML、CSS、文字の原稿は、ただのテキストだ。実行されるコードは、自分と AI が書いた物だけになる。「軽い」「速い」だけでなく、「侵害される入口が無い」── これが依存を減らすもう一つの理由だ。

Python が中身と外枠を繋ぐ

AsciiDoc を HTML に、Mermaid の図を SVG か画像に、そして外枠のテンプレートに流し込む ── これは Python スクリプトの仕事だ。

articles/foo.adoc   ──→  Python  ──→  html/foo/index.html
                              ↑
                      tools/templates/article.html(外枠)

このスクリプトを自分で書く必要は無い。AI に頼んで書いてもらう。pyasciidoc で AsciiDoc を HTML にし、Jinja2 でテンプレートに流し込む。短いスクリプトで済む。

依存は一桁で収まる。

uv add pyasciidoc jinja2 pillow を一度打てば、あとは git clone && uv sync で他の機械でも動く。node_modules は出てこない。

このサイト ── aiseed.dev ── のビルドスクリプト(tools/build_article.py)も、ほとんど AI が書いた。ビルドツールを自分の手元に持てる時代だ。フレームワークの規約に従う代わりに、自分の規約で書く。図と資料の作り方は 2-13: 図と資料を作る ── Mermaid・Marp・そのほかの道具 に続く。

動く部分は、FastAPI を一つ選ぶ

「動的なサイトは作れないのでは」と思うかもしれない。動く部分はサーバー側に置く。 Python の FastAPI、これ一つでいい。

Flask、Django、Go、Rust、Ruby ── 選択肢は山ほどある。しかし選択肢を増やすと、組織はそのぶん分かれる。「FastAPI で書こう」と決めれば、そこから先の議論が終わる。 選んだら、選んだことを忘れる。これが道具立ての考え方だ。

なぜ FastAPI か。

そして、その FastAPI も最小限に保つ。

一つの関数の形は、こうなる。

@app.get("/items", response_class=HTMLResponse)
async def items():
    rows = await conn.fetch("SELECT name, price FROM items")
    return render("items.html", rows=rows)

リクエストを受け、SQL を実行し、HTML を返す。ここから先は、これ以上の何が要るのかを問い続ける。AI が直接 SQL を書ける以上、ORM の抽象化を挟まなくても届く。書き方は AI が知っているので、人が渡すのは決めたことのほうだ。

クライアント側の JavaScript も最小限でいい。リンク遷移は <a>、フォーム送信は <form> で足りる。部分的な更新が要るなら HTMX(数 KB のライブラリで、フレームワークではない)。WebSocket は本当に必要になったときに足す。最初から SPA を組まない。

FastAPI の書き方と、基幹のロジックを API として出す手順は 2-12: API を作る ── FastAPI で基幹のロジックを出す が受け持つ。ここでは、選ぶ理由までで足りる。

この形で作れるもの

「中身は文字、外枠は HTML と CSS、Python が繋ぐ」は、実例で見るのが速い。

共通するのは、依存が一桁で、ビルドが短く、追加の費用がほぼ無く、コードのほとんどを AI が書くことだ。作るのも保守するのも軽い。このサイト(aiseed.dev)自身が、この構成で動いている。

この組み合わせは、10 年後も動く

フレームワークは、大きな版が上がると書き方が変わり、古い書き方が通らなくなる。ビルドツールの設定も、版に合わせて書き直しが要る。

HTML、CSS、JavaScript の基本仕様は、後方互換が保たれてきた。1999 年の HTML 4.01 で書いたファイルは、今のブラウザでも開く。Markdown は 2004 年の初版から、AsciiDoc は 2002 年の初版から、ほとんど変わっていない。この先も同じファイルが読める。

フレームワークは時代に依存する。Web 標準と文字の原稿は時代を超える。

数字で見る

決めるときに効く数字は、依存の数だ。このサイト(aiseed.dev)で数えると、Python の依存は requirements.txt に 9 個、焼いた頁は 443、ビルドのスクリプトは 7,928 行 (2026-10-05、公開リポジトリ aiseed-dev/website で数えられる)。React・Next.js・ TypeScript はゼロ。AsciiDoc と Mermaid と最小限の HTML/CSS だけだ。9 個の依存は、一つ残らず読める。

決めたことを、そのまま渡す

この章の決定を一覧にしておく。AI に作らせるときは、これを渡せば足りる。書き方は AI が知っているので、人が渡すのは決めたことのほうだ。

決める所 決めたこと
中身の形式 AsciiDoc + Mermaid(Markdown でも同じ)。中身に HTML タグは書かない
外枠 HTML テンプレート 1〜数ファイル、CSS 1 ファイル、JavaScript は数十行
繋ぐ所 Python。pyasciidoc + Jinja2(画像が要るときだけ Pillow)
置き場 中身は articles/、外枠は tools/templates/、出力は html/。すべて Git の中に置く
置かない物 JS フレームワーク、ビルドツール、TypeScript、CSS フレームワーク、npm と node_modules
動く部分 FastAPI。ORM は積まず SQL を直接書く。テンプレートは Jinja2、返すのは HTML
部分更新 要るときだけ HTMX。SPA にはしない
公開 焼いた静的ファイルを配る。自分の一台か、Cloudflare Pages か(2-11)

確かめ方

この章は、次の五つができていれば済みだ。

  1. 記事一本が AsciiDoc のファイルとして置いてあり、その中に HTML タグが一つも入っていない
  2. 一つのコマンドでビルドが走り、html/ の下にページが出て、数秒で終わる
  3. できた HTML をブラウザで開くと、外枠(ヘッダー・ナビ・フッター)と本文が出て、Mermaid の図が描かれている
  4. 依存パッケージの一覧が数行に収まり、node_modules がどこにも無い
  5. 同じ原稿から PDF を作ってみて、中身がそのまま出る
uv add pyasciidoc jinja2 pillow
uv run python -c "import pyasciidoc, jinja2; print('ok')"

人が持つ物

人が渡す値

AI が「やる前に言う」操作

確かめた版と日付

まとめ

Web を作る道具を、二層に分ける。

ここまでで、公開できる HTML が手元にできた。次章では、その html/ を公開する。置き場は二つ、2-02 の自分の一台(Caddy)か、Cloudflare Pages か。


関連記事