• #理科
  • #Vue
  • #Nuxt3
  • #Miller Column
  • #教育コンテンツ
  • #クイズ
  • #Web Audio API
mdx-playgroundメモ

やったこと概要

小学理科の学習コンテンツを、Miller Column レイアウトの専用ページとして新規構築した。中学受験向け参考書の全100トピックを目次として登録し、そのうち5トピック分のインタラクティブコンテンツを実装。クイズシステム、復習機能、オシロスコープ風の波形表示UIまで一日で作り切った。

Miller Column レイアウト

macOS の Finder でおなじみの、カラムが横に並ぶナビゲーションを採用した。理科コンテンツは階層が深い(大カテゴリ → 中カテゴリ → セクション → コンテンツ本体)ため、ツリー構造を一覧しながら掘り下げていける Miller Column が合っていた。

4カラム構成:

  1. 大カテゴリ: 水溶液と気体、物の燃え方、熱・光・音、力とエネルギーなど
  2. 中カテゴリ(トピック): ロウソクの燃え方、水の三態変化、音の性質など
  3. セクション: 基本のしくみ、くわしく知ろう、チャレンジ問題など
  4. コンテンツ本体: Vue コンポーネントまたはクイズ

ready 状態のトピックだけがクリック可能で、未実装のものはグレーアウト表示になる。categories.tssections: [] と書いておけば自動的に「準備中」扱いになる仕組み。

100トピックの目次登録

参考書の全100トピックを categories.ts に登録した。大カテゴリは10個前後に分けている。

水溶液と気体(1-10)/ 実験器具(11-12)/ 物の燃え方(13-17)
熱・光・音(18-28)/ 力とエネルギー(29-40)/ ...

大半は sections: [] の空状態だが、全体の見通しが立つことで「次にどこを埋めるか」が明確になった。100トピック全てを一度に作るのは現実的ではないので、子供が興味を持ったトピックから順に埋めていく方針。

React JSX から Vue SFC への変換

既存の React 実装(JSX)を Vue SFC に変換するスラッシュコマンド /convert-science-content を作った。

変換の要点:

  • useStateref / reactive
  • useEffectwatch / onMounted
  • JSX の className → template の class
  • イベントハンドラの記法変換(onClick@click

手動で書き直すと同じパターンの繰り返しになるため、スキル化して効率を上げた。入力として React コンポーネントを渡すと、Vue SFC に変換して出力する。

変換した5トピック

今回コンテンツを実装したのは以下の5つ。

トピック主なコンテンツ
ロウソクの燃え方炎の3層構造、温度分布の図解
水の三態変化状態変化ダイアグラム、融点・沸点の比較表
音の性質波形の基礎、モノコードの法則
音の秘密音速計算、波形の性質比較
てこ3種のてこ分類、つり合いの計算

各トピックは複数のセクション(3〜5個)に分かれていて、セクションごとに Vue コンポーネント1つが対応する。最後のセクションはクイズ。

クイズシステム

basic-english で実装済みだったスクロールリスト型のクイズパターンを踏襲した。

動作:

  1. 問題と選択肢を表示
  2. 選択すると即時フィードバック(正解/不正解 + 解説)
  3. 全問終了後にスコア表示
  4. 間違えた問題だけをリトライできるボタン

クイズデータは quiz-data/ ディレクトリに TypeScript ファイルとして分離。candle-quiz.tsstate-change-quiz.ts のようにトピックごとにファイルを分けている。

復習機能

useScienceQuizHistory composable で、間違えた問題の履歴を localStorage に保存する仕組みを作った。

  • クイズ完了時に間違えた問題を記録
  • 3回連続正解でその問題を復習リストから除外
  • Miller Column 内に「復習」モードとして統合

復習モードに入ると、全トピックの間違い履歴を横断して出題する。トピックをまたいだ弱点克服ができる。

オシロスコープ風 波形表示 UI

音のトピックで作った SoundPlayground.vue が今回のハイライト。

見た目:

  • CRT モニタ風の暗い背景(黒に近いグレー)
  • 蛍光グリーンの波形描画(Canvas 2D)
  • グリッド線も薄い緑で表示

機能:

  • 周波数スライダー(220〜880Hz)
  • 振幅スライダー
  • 倍音合成スライダー(基本波〜第7倍音まで個別調整)
  • プリセット: サイン波、矩形波、三角波、のこぎり波

Web Audio API の OscillatorNode を倍音の数だけ生成し、加法合成で音を作る。Canvas にはリアルタイムで合成波形を描画する。倍音スライダーを動かすと波形の形が変わり、同時に音色も変わる。

プリセットの実装は単純で、倍音スライダーの値を配列で持っているだけ。

const presets = [
  { label: 'サイン波', values: [100, 0, 0, 0, 0, 0, 0] },
  { label: '矩形波', values: [100, 0, 33, 0, 20, 0, 14] },  // 奇数倍音 1/n
  { label: 'のこぎり波', values: [100, 50, 33, 25, 20, 17, 14] },  // 全倍音 1/n
]

矩形波が「奇数次倍音だけ」というのは物理の話だが、スライダーで実際に音を聞きながら確認できるので理解しやすい。

スタイリング方針の転換

最初はカラフルな装飾(色付き背景、アイコン多用、ボーダー装飾)で作っていたが、途中でシンプル路線に切り替えた。

変更前: 各セクションに独自の背景色、太いボーダー、大きなアイコン 変更後: グレー背景のハイライトボックスに統一、装飾は最小限

理由は、コンテンツが増えるほど装飾が邪魔になること。100トピック分のスタイルを個別に管理するのは現実的ではない。ScienceSectionBlockScienceInfoCard という共通コンポーネントにまとめ、見た目を統一した。

/generate-science-content スキル

/convert-science-content が既存の React コードを変換するスキルだったのに対し、/generate-science-content はゼロからコンテンツを生成するスキル。トピック名を指定すると、セクション構成・Vue コンポーネント・クイズデータを一括で生成する。

その他の実装

  • インデックスページへの理科カード追加: トップページから /science への導線を追加
  • 矢印キーナビゲーション: セクション間を上下矢印キーで移動できるようにした。Miller Column の3列目(セクション一覧)にフォーカスがある状態で上下キーを押すと、前後のセクションに切り替わる
  • Chrome DevTools での表示確認: MCP 経由でリモートデバッグ用 Chrome を起動し、レイアウト崩れや Canvas 描画の確認を行った

振り返り

一日でここまで作れたのは、既存のクイズシステム(basic-english)とスラッシュコマンドによる変換自動化が大きい。React → Vue の変換は手作業だと面倒だが、パターンが決まっているのでスキル化すると速い。

100トピック中5つしか埋まっていないが、枠組みが完成したことで「あとはコンテンツを入れるだけ」の状態になった。子供が「てこ」の問題を解いて間違えた後に復習モードで再挑戦している様子を見ると、作った甲斐がある。