模写上級 #007は、イタリアンレストランの1ページ完結型LPを、HTML・CSSと軽量CSSフレームワーク「Lism CSS」で再現する課題です。この課題で身につくのは、フレームワークが用意したレイアウト部品の「効く範囲」と「効かない範囲」を見分け、狭い画面で崩れたときに原因を1箇所に特定する力です。
見た目を似せるだけの課題ではありません。今回のデモページは、スマートフォン幅で実際に横スクロールが発生します。その原因を自分で突き止めて直せたかどうかを合格ラインに置いています。以下の数値はすべて2026年8月2日にヘッドレスChromeで実測したものです。
| 難易度 | 上級 |
| 所要時間 | 目安6〜10時間(実装量から見積もった目安で、計測値ではありません) |
| 使う技術 | HTML / CSS(Grid・Flexbox) / Lism CSS / IntersectionObserver |
このLPで作るもの
題材は南イタリア料理のトラットリア「Trattoria LUCE」です。ヒーローから予約フォームまでを1ページに収めた、実際の飲食店サイトとほぼ同じ情報量を持つLPになっています。まずはPCとスマートフォンの両方で通してスクロールし、次の表の「観察ポイント」を意識して見てください。
| セクション | 観察ポイント | 使う技術 |
|---|---|---|
| ヒーロー | 画面いっぱいの背景と大きな見出し | 背景グラデーション+clamp() |
| CONCEPT | 画像とテキストの2カラム、数値3つの横並び | Grid / Flexbox |
| MENU | 3カラムのカード。狭い画面でも3列のまま | Grid |
| CHEF | 左右を入れ替えた2カラム | Grid |
| COURSE | コース名と価格を左右に振り分ける行 | Flexbox |
| ACCESS / 予約 | 入力欄の2カラムと、日時・人数のプルダウン | Grid / フォーム要素 |
予約セクションは、時間と人数を <select> のプルダウンで選ぶ実装です。ボタンを並べて選ばせる形式ではないので、状態管理のJavaScriptは必要ありません。デモに入っているJavaScriptは、スクロールで要素をふわりと出す処理・ヘッダーに影を付ける処理・コピーライトの年を入れる処理の3つだけで、全体で30行ほどです。ページ内リンクの滑らかなスクロールもJavaScriptではなくCSSの scroll-behavior: smooth で実現されています。
デモは外部の配信元に3つ依存しています。CSSフレームワークのLism CSS、Google FontsのNoto Serif JP・Noto Sans JP・Cormorant Garamond、そして写真5点のUnsplashです。フォントが当たっていないだけで印象は大きく変わるため、余白を測り直す前にこの3つの読み込み状況を確認してください。
Lism CSSはバージョンを固定して読み込む
Lism CSSは、レイアウト用のクラスを組み合わせてページの骨組みを作る「レイアウトファースト」設計のCSSフレームワークです。CSSファイルを1つ読み込むだけで導入でき、ビルド作業は要りません。npmのレジストリで確認したところ、2026年8月2日時点の最新版は0.24.0(公開日2026年7月5日)、初回公開は2025年5月20日で、これまでに73バージョンが公開されています。開発は止まっていません。
出典はnpmレジストリのパッケージ情報とLism CSS公式ドキュメントのインストール手順(いずれも2026年8月2日取得)です。
読み込みは1行、ただしバージョンを書く
CDNから読み込む場合、バージョン部分に @latest と書くこともできますが、この課題では使わないでください。@latest は「今この瞬間の最新版」を指すため、フレームワーク側が更新された日を境に、昨日まで動いていたページが何の予告もなく崩れます。公式ドキュメントのインストール手順も、@latest ではなく具体的なバージョン番号を書いた例を示しています。
<!-- デモと同じ見え方にしたいとき(デモが読み込んでいるのはこのバージョン) -->
<link href="https://cdn.jsdelivr.net/npm/lism-css@0.0.13/dist/css/main.css" rel="stylesheet" />
<!-- 最新の設計で作り直したいとき -->
<link href="https://cdn.jsdelivr.net/npm/lism-css@0.24.0/dist/css/main.css" rel="stylesheet" />デモページのHTMLを見ると、読み込んでいるのは lism-css@0.0.13 です。最新の0.24.0とは71バージョン離れています。デモのソースをそのまま写しながら読み込みだけを最新版にすると、見た目が合わない原因がフレームワーク側にあることに気づけません。
バージョンを上げると実際に何が止まるか
デモのHTMLは一切変えず、読み込むCSSのバージョンだけを0.0.13から0.24.0に差し替えて、同じ要素の計算後スタイルを比較しました。デモが使っている25個のユーティリティクラスのうち9個が0.24.0には存在せず、次のように無効化されます。エラーもコンソール警告も出ないため、崩れた理由が見えないのが厄介なところです。
| クラス | 0.0.13での実測値 | 0.24.0での実測値 |
|---|---|---|
-c:main | 色 rgb(201, 168, 76) | rgb(255, 255, 255)(金色が消える) |
-jc:sb | space-between | normal(左右振り分けが崩れる) |
-td:n | none | underline(リンクに下線が戻る) |
-ta:c | center | start(中央寄せが解除) |
-ai:c | center | normal(縦位置が揃わない) |
-ov:h | hidden | visible(はみ出しが隠れない) |
これらは削除されたのではなく、短縮形から正式名へ書き換えられています。0.24.0では -ai:c が -ai:center、-jc:sb が -jc:between、-ta:c が -ta:center、-ov:h が -ov:hidden、-td:n が -td:none になります。-container:m は is--container 系のトレイトクラスへ、色の -c:main は -c:brand や -c:accent へ整理されました。最新版で作り直すなら、この対応を先にまとめてから着手すると手戻りが減ります。
一方で、レイアウトの中心である l--stack l--flex l--columns l--center は両バージョンで同じ定義のまま残っています。骨組みは安定していて、細かい装飾用のクラス名が動いた、という理解でおおむね合っています。
Lism CSSを使わずに進めることもできる
2026年8月2日時点で、CDNのCSSファイルは正常に取得できます(HTTP 200・約29KB)。ただし将来配信が止まった場合や、フレームワークを使わずに実力を測りたい場合は、素のHTMLとCSSだけで進めても構いません。Lism CSSのクラスは、次のように置き換えれば同じ結果になります。
l--stackはdisplay: flex; flex-direction: column;だけの定義ですl--flexはdisplay: flex;だけで、折り返しの指定は含まれていませんl--columnsはdisplay: grid; grid-template-columns: repeat(var(--cols), minmax(0, 1fr));ですl--centerはdisplay: grid; place-content: center; place-items: center;です
この4つを自前のCSSに書けば、フレームワークなしでもデモとほぼ同じ骨組みが組めます。l--columns の minmax(0, 1fr) は特に重要なので、写すときに 1fr へ省略しないでください。理由は次の節で扱います。
完成条件(合格ライン)
上級課題の合否は「なんとなく似ている」では判定できません。この課題では、幅を変えたときにレイアウトが崩れないことを合格ラインに置きます。ブラウザの開発者ツールでデバイスツールバーを開き、幅を1280px・768px・375px・320pxの4段階に変えながら、次の4項目を確認してください。
| 判定項目 | 合格ライン | 外れたら疑うこと |
|---|---|---|
| 横スクロール | 4段階すべてで発生しない | 折り返し指定のない横並び(l--flex 相当)に大きな gap が付いていないか |
| カラム幅 | 2カラム・3カラムの各列が等幅を保つ | Gridの列指定を 1fr で書いていないか(minmax(0, 1fr) にする) |
| ヘッダー | 80px以上スクロールすると背景が濃くなる | スクロール監視でクラスが付いていない、または position: fixed が外れていないか |
| スクロール演出 | 各セクションが画面に入ると浮き上がる | 監視対象のクラス名が要素側と一致しているか |
1つ目と2つ目は目視ではなく数値で判定できます。開発者ツールのコンソールに次の2行を貼れば、その場で答えが出ます。
// 0 なら合格。1以上ならその数値ぶん横にはみ出している
document.documentElement.scrollWidth - document.documentElement.clientWidth;
// 各グリッドの列幅。ひとつの行の中で数値がバラついていたら不合格
[...document.querySelectorAll('[style*="--cols"]')]
.map(el => getComputedStyle(el).gridTemplateColumns);参考までに、デモページを実測した結果は次のとおりです。1280pxと768pxでははみ出し0px、375pxで23px、320pxで50pxのはみ出しが出ました。カラム幅のほうは8つのグリッドすべてが4段階とも等幅を保っています。つまりデモ自身が狭い画面で横に溢れている状態で、あなたの模写が320pxで0pxになれば、デモより良い出来ということになります。
つまずきポイント
以下は、実際にデモのコードを手元で動かして再現できたものだけを載せています。
| 症状 | 原因 | 直し方 |
|---|---|---|
| スマホ幅で横スクロールが出る | 横並びの行に折り返し指定がなく、gap が広すぎる | その行に flex-wrap: wrap を足す |
| 2カラムの左右が不揃いになる | 列指定が 1fr で、中身の最小幅に押し広げられている | minmax(0, 1fr) に書き換える |
| remで書いた文字サイズが合わない | デモのフレームワークが狭い画面でルート文字サイズを縮めている | 実測値をpxで確認してから換算する |
| クラスを写したのに効かない | 読み込んでいるバージョンにそのクラスが無い | デモと同じ0.0.13を指定する |
| 雰囲気が似ない | Webフォントか写真が読めていない | ネットワークタブで3つの外部読み込みを確認する |
横スクロールの犯人はCONCEPTの数値3つ
320px幅ではみ出している要素を全部拾うと、CONCEPTセクションにある「12 Years / 4.8 Rating / 28 Seats」の横並びに絞り込めました。この行は入れ物の幅が119pxしかないのに、中身は192px必要としています。差の73pxがそのままページ全体の横スクロールにつながっていました。
原因は2つ重なっています。1つは、この行が使っている l--flex が display: flex だけの定義で、折り返しを含んでいないこと。もう1つは、要素間の隙間が36.75pxずつ、2箇所で73.5px確保されていることです。入れ物が119pxしかない場所で、隙間だけで73.5pxを使ってしまっています。
/* 修正前: 折り返さないので横に溢れる */
.stat-row { display: flex; gap: 36.75px; }
/* 修正後: 狭いときだけ折り返す */
.stat-row { display: flex; flex-wrap: wrap; gap: 12px; }この修正を当てて再計測したところ、行のはみ出しもページ全体のはみ出しも0pxになりました。注意したいのは、隙間を狭めるだけでは直らない点です。折り返しを付けずに隙間を36.75pxから12pxへ縮めただけでは、はみ出しは残ったままでした。効いているのは flex-wrap: wrap のほうで、隙間の調整は見栄えの微調整にすぎません。この順序を取り違えると、数値をいじり続けて時間を失います。
remの実測値がずれるのはルート文字サイズのせい
デモが読み込んでいる0.0.13は、ルート文字サイズを clamp(0.875rem, 0.625rem + 1vw, 1rem) で可変にしています。実測すると、320pxと375pxの幅では14px、768pxと1280pxでは16pxでした。つまり狭い画面では 1rem が16pxではなく14pxになります。
デモの見出しは clamp(3rem, 8vw, 6rem) ですが、320pxでの実測値は48pxではなく42pxでした。開発者ツールで測った数値をそのまま自分のページにrem指定で写すと、ここで6pxずれます。フレームワークを使わずに作る場合は、pxの実測値を基準にするか、ルート文字サイズも同じ式で揃えてください。なお最新の0.24.0はこの可変指定をやめており、全幅で16pxのままです。
進め方
上から順に作るのではなく、骨組みを全部通してから装飾に入るほうが早く終わります。装飾を先に詰めると、レイアウトを直すたびに調整をやり直すことになるためです。
- デモを1280pxと320pxの両方で見て、セクションの並びと各セクションのカラム数だけをメモする
- Lism CSSをバージョン指定で読み込み、全セクションの空箱をHTMLだけで並べる
- この段階で320pxのはみ出しを0pxにしておく(後から直すより圧倒的に楽)
- テキストと画像を流し込み、色とフォントを当てる
- スクロール演出とヘッダーの背景変化を実装する
- 完成条件の4項目を4段階の幅で再確認する
完成したら店名・写真・コース内容を差し替えて、自分のポートフォリオ作品に仕立て直してください。デモと同じ内容のまま公開するより、題材を変えたほうが実力の証明になります。作り始める前に構成の決め方を整理しておきたい場合は、デザインカンプを作る前に決めておくルールが参考になります。
次に取り組む課題
この課題は模写上級シリーズの最後にあたります。1つ前の課題は模写上級 #006 SaaS系LPの模写です。今回が「余白と色で世界観を作る」課題なのに対し、#006は情報を整理して伝える方向の課題なので、両方こなすと引き出しが増えます。
他のレベルの課題に進む場合は模写コーディング課題の一覧から選んでください。初級から上級、Figmaを使う課題までまとめてあります。
今回つまずいたのがCSSの書き方そのものだった場合は、CSSのテクニックまとめで該当箇所を補強してから戻ってくると進みが変わります。Lism CSSそのものをもう少し知りたい場合は、Lism CSSの特徴を解説した記事も用意しています。
よくある質問
Q. Lism CSSを使わずに素のHTMLとCSSだけで作ってもいいですか?
構いません。この課題で使っているLism CSSのレイアウトクラスは4つだけで、いずれも数行のCSSに置き換えられるため、自前のCSSで書いても完成条件は同じように満たせます。
Q. デモと同じ写真やフォントは使えますか?
写真はUnsplash、フォントはGoogle Fontsから読み込んでいるため、それぞれの利用規約の範囲で使えます。ポートフォリオとして公開するなら、写真は自分で選び直したほうが作品としての印象が良くなります。
Q. 予約フォームは実際に送信できるようにする必要がありますか?
この課題では不要です。デモも送信時の既定動作を止めるだけで、サーバーへは何も送っていません。見た目と入力体験の再現に集中してください。
Q. デモと同じバージョンではなく最新のLism CSSで作ってもいいですか?
問題ありませんが、装飾用のクラス名が変わっているため、デモのソースをそのまま写すと一部が効かなくなります。最新版で作るなら、本文の対応表を見ながらクラス名を読み替えてください。
Q. 模写したページをポートフォリオに載せてもいいですか?
「模写作品」であることを明記し、元のデモページへのリンクを併記すれば問題ありません。オリジナル作品として提示するのは避けてください。
Q. どのくらいの実力があれば取り組めますか?
HTMLとCSSでGridとFlexboxを一通り書ける状態が目安です。JavaScriptは30行ほどしか出てこないため、DOM操作の基本が分かっていれば足ります。
