Slide 1

Slide 1 text

HRC 速さを追い求めたら、HTMLになった Studioのデザインデータから、公開サイトのHTMLを生成するエンジン。 菅原 孝則 フロントエンドカンファレンス福岡 2026

Slide 2

Slide 2 text

自己紹介 菅原 孝則 Studioの古参エンジニア コロナ禍に海外で2年間暮らして。 コロナ明けに福岡に引越し。 2 x.com/oligin020

Slide 3

Slide 3 text

コードを書かずに、 Webサイトを 作って公開。 デザインから公開まで、 ひとつのツールで。 3

Slide 4

Slide 4 text

今日のお話 速さを追い求めた、 エンジン刷新の2年半。 作り直す決断 設計と互換性の試行錯誤 ヘルシーなインターネットへ 4 HRC Studioのデザインデータを HTML / CSSに変換する。 デザインデータ JSON ↓ HTML / CSS 公開サイト

Slide 5

Slide 5 text

20万超 Studioがホスティングする規模 サイトごとに違う、 見た目と動きを守る。 公開サイト 現在のホスティング規模 基盤の変更が、 個々のサイトに影響する。 5 公開サイトの基盤

Slide 6

Slide 6 text

実際に、 Studioを操作してみます。 6

Slide 7

Slide 7 text

Studioが掲げてきた志 グローバルに通用する ツールを作る。 7 第1章|作り直す決断

Slide 8

Slide 8 text

10年も機能追加してたら 流石に公開サイトが重くなる 作る側 表現力が増える 見る側は、待つ 8

Slide 9

Slide 9 text

2021年、 公共サイトの保存で困った 公開サイト SPAは自動アーカイブの対象外、と説明を受けた 9 Webアーカイブ

Slide 10

Slide 10 text

本文を読む前に、 ブラウザの仕事がある 受信したHTML JSON + JavaScript 解釈・組み立て 本文を表示 10

Slide 11

Slide 11 text

白紙でも、80点くらい。 Lighthouseで測った、当時の公開サイト 80 500KB超 表示前に読むJavaScript 11 点くらい

Slide 12

Slide 12 text

いろいろあって、 高速化&MPA化に チャレンジしてみた。 12

Slide 13

Slide 13 text

最初の高速化は、6か月で凍結 6 開始 13 完了の目処が立たない ほかに任せたい仕事がある か月 凍結

Slide 14

Slide 14 text

やっぱ遅いよな 日本のサーバーは遠い。 CDNの大切さを実感した。 14

Slide 15

Slide 15 text

やっぱり遅い アンテナが、 1本立つかどうかで
 SPAは無理ゲー 15

Slide 16

Slide 16 text

2024年 作り直すチャンスが やってきた! 16

Slide 17

Slide 17 text

世界一速くする。 17

Slide 18

Slide 18 text

世界一の基準を、社内に示す プロジェクトの 北極星は、 阿部寛のホームページ。 18

Slide 19

Slide 19 text

Webサイトの本質だけで、できている HTML 速さ 19 魚拓での再現 クローラー

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

こうすればいいのは分かりきったこと でもなかなかそうもいかない 21

Slide 22

Slide 22 text

JSONをHTMLにするだけなんやが JSON フレームワーク ライブラリ 更新に約2か月 数人で対応。しかも、更新後の挙動を保証しきれない。 22 HTML

Slide 23

Slide 23 text

E2Eに頼っても、 最後は目視確認だった 実装 E2E 気づけずに、またバグを埋め込む 23 目視確認

Slide 24

Slide 24 text

書きやすくて速くないと、 カバレッジを上げる気にならない! 素直なunit testを書いて、すぐ確かめたい。 plainなobject { title: "記事A" } f 変換する関数 24 期待するHTML 結果を直接確かめる

Slide 25

Slide 25 text

何を残すかは、 自分たちで決めたい 依存先の都合 自分たちの要件 削れないファイル 必要なものだけ残す 25

Slide 26

Slide 26 text

巨人の肩に乗っていては、 阿部寛のホームページには 絶対に辿り着けない!

Slide 27

Slide 27 text

技術選定の判断軸 テストの書きやすさ 実行速度 開発速度 CIの速度 シンプルに作るために、 外部ライブラリを 徹底排除する。 27

Slide 28

Slide 28 text

2年間、同じ議論で 立ち止まりたくない 「ライブラリを入れよう」 開発開始 28 「ライブラリを入れよう」 「ライブラリを入れよう」 2年の見込み

Slide 29

Slide 29 text

一人で。 誰にも相談しない、と決めた。 29 アーキテクチャ 大枠の実装

Slide 30

Slide 30 text

もちろん、リスクはある。 受け入れられない 運用できない 時間がかかりすぎる 打ち切られる 30

Slide 31

Slide 31 text

組織が落ち着くまでに、形にする 組織改編期 チャンス期間 組織が落ち着く 進捗悪いと 最悪凍結 一定レベルまで形にする 31

Slide 32

Slide 32 text

でも、やるんだよ CTOと自分の信用貯金をここで使い切る 32

Slide 33

Slide 33 text

まずはここから始めました 33 Bun Bun test Biome 実行環境 テスト Lint・フォーマット

Slide 34

Slide 34 text

まず、動くことを証明する JSON 試作 公開サイト 34

Slide 35

Slide 35 text

別のエンジンで、公開サイトが動いた 2 35 JSON → HTML 週間 コンパクトな変換実装

Slide 36

Slide 36 text

第2章 ここから、 考古学が始まる。 36

Slide 37

Slide 37 text

一つの型に、26種類のBox 既存の型定義(抜粋) interface StudioDom { type?: StudioDomType content?: StudioDomContent style?: StudioDomStyle state?: StudioDomState action?: StudioDomAction children?: StudioDom[] // ... } 正しい組み合わせは、型から分からない。 37 第2章|既存仕様の考古学 種類・スタイル・状態・挙動

Slide 38

Slide 38 text

コードからは読み解かない。 出力データと、画面の動き。 この二つから仕様を割り出した。 38 第2章|既存仕様の考古学

Slide 39

Slide 39 text

第3章 同じ動きを、 シンプルな仕組みで。 39

Slide 40

Slide 40 text

まず、全部MPAで処理できないか。 サーバー側で処理 40 データの解釈 HTML生成 スタイル 動作の再現 第3章|処理の境界

Slide 41

Slide 41 text

ブラウザでしかできない部分を、切り出す 41 サーバー ブラウザ データの解釈 HTML・CSSの生成 操作 状態の更新 第3章|処理の境界

Slide 42

Slide 42 text

属性と本文には、値を直接割り当てる データ url: "/articles/a" title: "記事A" 42 第3章|スタイル 生成したHTML 記事A

Slide 43

Slide 43 text

ここで、 ちょっとした 発明があった。 43 第3章|スタイル

Slide 44

Slide 44 text

共通スタイルを持ち、colorだけを渡す。 Editorで、見出しの色をバインディング masterの共通スタイル .article-title { font-weight: 700; color: var(--title-color); } 親インスタンスの差分

記事A

CSS変数は、子孫要素にも伝わる。 44 第3章|スタイル

Slide 45

Slide 45 text

Componentも、Listも、Carouselも。 masterの共通スタイル Component List Carousel CSS変数の差分 CSS変数の差分 CSS変数の差分 45 第3章|スタイル

Slide 46

Slide 46 text

追加した要素も、同じCSSで表示できる 読み込み済みのHTML .article-title { font-weight: 700; color: var(--title-color); }

記事A

親に渡したCSS変数を、追加した子孫要素も参照できる。 46 第3章|スタイル + 新しいインスタンス

記事B

Slide 47

Slide 47 text

第4章 試行錯誤。 47

Slide 48

Slide 48 text

Carouselという魔境 表現力が無限大はテストケースも無限大 48 第4章|試行錯誤・カルーセル

Slide 49

Slide 49 text

データの中に、条件分岐も関数も。 独自DSL 判定 関数定義 関数呼び出し 49 第4章|試行錯誤・カルーセル 独自パーサー 関数化 Vue.js ライフサイクル computed

Slide 50

Slide 50 text

当然、自作する。 既存ライブラリ 細かな制御が合わない 50 第4章|試行錯誤・カルーセル 自作 評価と処理順まで合わせる

Slide 51

Slide 51 text

Vue.jsの内部処理順にまで踏み込む 独自DSL 独自DSL Vue.js ブラウザのイベント carousel-renderer DOM computed 要素 条件式 DSL由来の関数 ライフサイクル 評価結果を受け取る 初期化 関数定義 inlineStyle 条件判定 依存する値 状態 関数呼び出し transition スタイル更新 条件判定 値の参照 ユーザー定義変数 ユーザー定義の値 トリガー処理 評価結果 更新処理 データ トリガー ユーザー定義の値 別の値を参照 描画処理 独自パーサー 表示状態 タイミング調整 再評価 次の更新 生成された関数 要素の入れ替え 処理を再開 値の受け渡し 要素の制御 処理順が1つでも狂うと、既存の動作とズレる。 51 タイマー・同期 第4章|試行錯誤・カルーセル 処理を予約 setTimeout 待機 処理を再開 要素を入れ替える 次の処理へ

Slide 52

Slide 52 text

Reactiveな挙動をトレースするのとても辛い 4 カルーセルだけで、たぶん。 52 か月 第4章|試行錯誤・カルーセル

Slide 53

Slide 53 text

サイズ別画像切り替え 画像読むのにjsなんで使わにゃならんのだ 53 第4章|試行錯誤・画像

Slide 54

Slide 54 text

画像のJSも消せるんちゃうか? 画像を切り替える基準は、viewportではなくcontainerのサイズ。 同じviewport 広いcontainer 大きい画像を選ぶ 画面幅が同じでも、必要な画像サイズは違う。 54 第4章|試行錯誤・画像 狭いcontainer 小さい画像を選ぶ JSで画像を選択 ↓ この処理を CSSだけに できないか?

Slide 55

Slide 55 text

画像選択、CSSだけでいけた! やったーーー! SVG / ラッパー img 小 Container Queryで選択 55 第4章|試行錯誤・画像 img 中 img 大 loading="lazy"

Slide 56

Slide 56 text

横を直すと、縦が壊れる。なんでや。 3月10日 同日 3月16日 横並びで 縮まない 縦並びで 位置がズレる 幅の指定を 組み直す 修正 撤回 再修正 56 第4章|試行錯誤・画像

Slide 57

Slide 57 text

2年くらい粘ったが敗北しJSへ戻した SVG / ラッパー img img img img CustomElementで妥協 57 第4章|試行錯誤・画像

Slide 58

Slide 58 text

第5章 2年半たった現状 58

Slide 59

Slide 59 text

248,725 開発の節目とコード総量 各月末の総行数 行 · 2026/6/12 リリース時点 千行 300 1 9 3 8 200 2 4 7 5 6 100 1 2 3 4 6 5 7 8 0 2024/3 2024/9 2025/3 2025/9 実装・テスト・開発ツール。依存コード・データ・生成物を除外。 第5章|開発の思い出と成果 59 2026/3 6/12 9 2024.08.16 型と生成処理を再編 2025.01頃 Clineを使い始める 2025.03頃 Claude Codeを使い始める 2025.07.03 カルーセルを再設計 2025.09.22 生成とサーバーを分離 2025.11頃 バグ修正にラストスパート 2026.02.25 RSSをIsland化 2026.04.22 取得とHTML構築を分離 2026.06.12 リリース 2・3・6は本人の回想

Slide 60

Slide 60 text

行カバレッジは81.4%に。 行カバレッジ|両方とも現在のコードで計測 Studio Preview HRC(全パッケージ込み) 未実行ファイルを含む。テスト・モック・生成データを除外。 2026.09.10 計測|実行基盤が異なるため参考比較 60 第5章|開発の思い出と成果 32.8% 81.4%

Slide 61

Slide 61 text

13 外部ライブラリ 個 監視用を含む 61 第5章|開発の思い出と成果

Slide 62

Slide 62 text

58 CI時間 62 第5章|開発の思い出と成果 秒

Slide 63

Slide 63 text

モバイルでも100点を取れるようになりました モバイル Lighthouse|公式記事の計測例 新基盤への切り替えに加え、実装もチューニングした比較。 出典:Studio公式発表 · 2026.01.29 第5章|開発の思い出と成果 63

Slide 64

Slide 64 text

できてみたら 誰からも文句を言われませんでした 64

Slide 65

Slide 65 text

第6章 ヘルシーな インターネットへ。 65

Slide 66

Slide 66 text

細い回線でも、 本文を読める ようにしたい。 66 第6章|ヘルシーなインターネット

Slide 67

Slide 67 text

本文がHTMLにある。 人が読める 本文 機械が読める アーカイブできる 67 第6章|ヘルシーなインターネット

Slide 68

Slide 68 text

今の実行環境がなくなっても、 情報は残したい。 今の実行環境 情報 68 第6章|ヘルシーなインターネット

Slide 69

Slide 69 text

スペシャルサンクス HRC全般 HRC全般 VRT anatoo hanayama mizchi x.com/anatoo 69 github.com/hanatyan128 x.com/mizchi

Slide 70

Slide 70 text

ご清聴ありがとうございました