Skip to content
 
 

Latest commit

 

History

120 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

どくしょ ちょきんばこ(Reading-Books)

児童が読んだ本を記録し、冊数・ページ数・金額を「貯金」のようにためていく読書通帳アプリです。 本のバーコード(ISBN)をカメラで読み取るだけで記録できます。


できること

画面 できること
ホーム いままでの 冊数・ページ数・金額の合計、今月の目標に対する進み具合、記録の入口3つ(カメラ/ISBN/手入力)、さいきん きろくした本 3冊
リスト この月/この年/ぜんぶ の切りかえ、年月の選びなおし、本の名前と かいた人での検索、えらんだ範囲の 冊数・ページ数・金額
本の くわしく(リスト・ホームから) ページ数・ねだん・ISBN の確認、★(0〜5)の付けかえ、かんそうの書き足し、その1件の削除
スタンプ 月ごとの目標(1〜99さつ・はじめは10さつ)と、たまっていくスタンプ帳(絵柄は月ごとに変わります)
きろく ふりかえり(グラフ)、データの保存・読み込み、A4たてでの印刷、つかいかた、アプリとしてのインストール、音のオンオフ、記録のリセット、バージョンと学習ログ件数の表示

本を きろくする 3つの やりかた

やりかた 入口 中で おきること
カメラで よむ ホーム →「カメラで バーコードを よむ」 978 / 979 で始まる EAN-13 だけを受けつけ、読めたら そのまま書誌検索へ進みます。日本の本のねだんのバーコード(191 / 192 で始まるもの)を読んだときは、その場で読みなおしを うながします
ばんごうで さがす ホーム →「本のばんごう(ISBN)で さがす」/カメラ画面 →「ばんごうで いれる」 13けたでも 10けたでも 入れられます(10けたは 978 を付けた13けたに直します)。チェックディジットが 合わないものは はじきます
じぶんで かく ホーム →「じぶんで かいて きろくする」 検索をせずに、題名から手で入力します

記録に かならず要るのは「本の なまえ」と「ページ」の2つだけです。 かいた人・ねだん・★・かんそうは 空のままでも保存できます(かいた人が空のときは「ふめい」になります)。

  • すでに 同じ ISBN、または同じ題名の記録があるときは「おなじ本が あります」と出て、 それでも記録するか えらべます
  • リスト/スタンプで えらんでいる年月が「今月」でないときは、その月の記録になります (入力画面に「この きろくは ◯年◯月に なります」と金色で出ます)。前の月の分を あとから入れられます
  • 月の目標に はじめて とどいた月は、紙ふぶきとファンファーレでお祝いします (同じ月に2回は出ません。端末の「視差効果を減らす」がオンのときは 紙ふぶきを出しません)

本の情報の 自動取得

次の3つの書誌データベースを 同時に 呼び、そろったものから使います。 いずれも鍵の要らない公開APIで、送るのは ISBN の数字だけです。

出どころ 主に使う値
openBD 題名・著者・ページ数(ONIX の Extent)・ねだん
Google Books API 題名・著者・ページ数(pageCount)・ねだん
国立国会図書館サーチ(NDL) 題名・著者・ページ数(extent)・ねだん
  • ページ数は ページ数として はっきり書かれている項目を先に使い、それが1つも無いときだけ 紹介文から「◯◯ページ」を拾います(大きい数を むやみに ひろわないため)
  • ねだんは openBD → Google Books → NDL の順に えらびます
  • 題名・ページ数・ねだんが そろったら すぐ次へ進み、そろわなくても 6秒で打ちきります (1つの API が おそいだけで 児童を ながく待たせないため)
  • 3つとも だめだったときは「じぶんで かいて きろく できます」と出して 手入力へ ゆずります。 取れなかった らん(ページ・ねだん)には 赤い しるしが つきます

PWA(アプリとしてのインストール)

Chrome / Edge / Safari から ホーム画面に追加してアプリとして起動できます。

ブラウザ 手順
Chrome / Edge(Android・PC) 「きろく」→「アプリとして インストール」。
この行はブラウザが beforeinstallprompt を出したときだけ表示されるので、
出ていないときはメニュー(⋮)→「アプリをインストール」
Safari(iPhone・iPad) 「きろく」→「アプリとして インストール」で手順の案内が出ます。
実際の操作は 共有 ⬆️ →「ホーム画面に追加」

Service Worker がアプリ本体を端末に保存するため、2回目以降はオフラインでも起動します。 (書誌データの検索だけはインターネットが必要です。)

  • manifest.json … アプリ名・アイコン・表示モード・ショートカット
  • sw.js … オフライン用のキャッシュ
  • offline.html … アプリ本体すら取れないときの案内ページ
  • icons/ … アイコン(192 / 512 / maskable / apple-touch-icon)

アイコンの長押しで出るショートカットと、URL の ?screen= は次の画面を直接ひらきます。

URL ひらく画面
?screen=scan カメラで よみとる
?screen=home / list / stamp / data ホーム/リスト/スタンプ/きろく

id / scope / start_url公開の置き場所から始まる絶対パスにします。 このアプリは reading-books.giga-school.com という専用ドメインの直下で公開しているので、 3つとも / から始めます(quality.config.jsonbasePath が正解の値で、 品質ゲートと実機テストはこれと突きあわせます)。

gigayama.github.io/Reading-Books/ で公開していたころは /Reading-Books/ でした。 ドメインを移したのに /Reading-Books/ のままにすると、開いているページ (https://reading-books.giga-school.com/)が scope の外になり、ブラウザは manifest ごと丸ごと無視します。 その結果、インストールできず、すでにホーム画面に 入れてある PWA も起動しなくなります。gigayama.github.io のように1つのオリジンを 数十個のアプリで共有する置き方に戻すなら、basePath/リポジトリ名/ にして manifest の3つも書きかえてください(相対パスのままだと URL の書き方ひとつで 別アプリ扱いになったり、似た構成の別アプリと取りちがえられます)。

このリポジトリをコピーして新しいアプリを作るときは、CNAMEbasePath・ manifest の3つを最初に書きかえてください。

キャッシュに入れる前に Content-Encoding を落としています

サーバー(GitHub Pages を含む)は HTML や JS を gzip / brotli で縮めて返します。 fetch は中身をもどしてから渡してくれますが、「縮めてあります」という Content-Encoding ヘッダーだけは Response に残ります。 これを そのまま Cache API に入れて、圏外のときにページとして返すと、 ブラウザはヘッダーを信じてもう一度ほどこうとして失敗し、真っ白なエラー画面になりますsw.jsstripEncodingHeaders() で、中身はそのまま・ヘッダーだけ外して保存しています。

更新の反映

sw.jsjs/app.jsAPP_VERSION を、リリースごとに必ず上げてください (この2つが食いちがうと npm run check の E9 が落ちます。package.jsonversion も あわせておくと、あとから追いやすくなります)。 新しい版が用意できると、児童の画面に「あたらしい バージョンが あります」 「さいしんに する」「あとで」のバーが出ます。 黙って入れかえないのは、かんそうを入力している途中に再読み込みが走ると 書いたものが消えてしまうためです。


画面の階層と「戻る」操作

かいそう0(下のナビゲーションバー = タブ)
├── ホーム
│   ├── かいそう1  カメラで よみとる
│   │   └── かいそう2  本を きろくする
│   ├── かいそう1  本を きろくする   ← ISBN検索・手入力から じかに
│   └── かいそう1  本の くわしく     ← さいきん きろくした本 から
├── リスト
│   └── かいそう1  本の くわしく
├── スタンプ
└── きろく
    ├── かいそう1  どくしょの ふりかえり
    ├── かいそう1  データを のこす
    ├── かいそう1  つかいかた
    └── かいそう1  ぜんぶ けす

「つかいかた」は、いちばん上の画面のときだけ出る**ヘッダー右上の「?」**からも ひらけます。

下の階層にいるとき、画面下部のナビゲーションバーが「◀ もどる」に変わります。 また、画面の左右どちらかの端から中央へスワイプしても1つ前の階層に戻れます。

戻る操作は History API を通して1本化してあり、次を保証しています。

  • 端末やブラウザの「戻る」も、アプリの階層を1つずつさかのぼる
  • いちばん上の画面で戻ってもアプリは終了しない(履歴の底に見張りの履歴を1つ置いています)
  • スワイプ操作とブラウザの戻るが同時に起きて2階層戻ることがない (端末がジェスチャーを引き取った場合は touchcancel を受け取り、アプリ側は何もしません)

デザイン(GIGA山 共通)

骨組みは Qalc と同じ設計(6トークン・太い輪郭・段差の影)にそろえたまま、 色みをアプリアイコン(本のちょきんばこ)に合わせた、深い藍+金の落ち着いた配色にしてあります。 色は次の6つのトークンだけで組み立てます。新しい画面を足すときも、この6つの外の色を増やさないでください。

トークン 役わり
--bg #F3F0E9 画面の下じき(アイコンのまわりの紙いろ)
--panel #FFFFFF カード・ボタンの面
--text #14283C 文字とすべての輪郭線(すみ色)
--primary #1F4C72 このアプリの色(アイコンの深い藍)
--secondary #2E7D8C 決定・進む・もどる(落ち着いた青みどり)
--accent #D9AE58 ごほうび・強調(アイコンの金)

文字のこさ(3段階)

本文は --text、ひかえめな説明は --ink-soft、 いちばんひかえめなもの(プレースホルダ・からっぽの表示)は --ink-faint を使います。 この3つは いちばん暗い地(--secondary-tint)にのせても 4.5:1 以上 でそろえてあります。 Chromebook の液晶は視野角もコントラストも弱く、うすい灰色はななめから見るとほとんど読めません。

トークン いちばん暗い地での比
--ink-soft #4C5A69 6.06:1
--ink-faint #5C6C7E 4.60:1

値を変えたら npm run check が D8 で見張ります(quality.config.jsoncontrast)。

かたちの決まり

  • 輪郭は太く(カード・ボタン 3px、小さい部品 2px)、色は --text
  • 影は「段差」(カード 4px 4px 0 / ボタン 0 4px 0)。ぼかし影は使わない
  • ボタンは押すと影が消えて translateY(4px) 沈む
  • 角丸は 20px(カード・ボタン)/16px(本の行・目標の入力)/12px(入力らん)/999px(チップ・セグメント・丸い部品)
  • グラデーション・文字の影は使わない
  • 書体は Zen Maru Gothic。fonts/ から自分で配ります(CDN は使いません)。 太さは実際に使っている 700 と 900 だけを配布します(500 は未使用)

アイコン(絵文字は使わない)

画面の中のアイコンはすべて自前の SVG です。絵文字は端末ごとに絵柄も色も変わるため使いません。 index.html の先頭にある <svg class="icon-sprite"><symbol id="i-…"> を並べ、 使う場所からは次の形で呼び出します。

<svg class="ic"><use href="#i-camera"></use></svg>
ic('i-camera')            // JavaScript から組み立てるときのヘルパー
ic('i-star', 'ic solid')  // solid を付けると塗りつぶし(★など)
  • 大きさは親の font-size1em)、色は currentColor にそろいます
  • 線は 24×24 のビューボックスで太さ 2・角丸の端。新しいアイコンもこの形に合わせてください
  • 月ごとのスタンプの絵柄は i-m1i-m12MONTH_STAMPS)です

端の「戻る」スワイプは Qalc と同じ寸法・同じ見た目です (はしの幅 32px/戻る距離 72px/たてのぶれ 48px まで/56px の半円チップ)。


学習ログ study.v1

本アプリは **学習ログ共通スキーマ仕様書 study.v1(1.9)**に準拠しています。 仕様書 §3.8 が本アプリの定義です(採点を伴わない活動を本スキーマに載せる事例)。

項目
出力スキーマ版 study.v1
appId reading-books
studyLog.js ロジック版 1.1(グローバル/IIFE 形態)
保存先キー study.records.v1

⚠️ study.records.v1 は複数アプリ共通の学習ログです。 このアプリ専用のキーではないため、リセット処理やクリーンアップの対象に含めないでください。

アプリ内の「きろくを ぜんぶ けす」で削除するのは、次の3つのキーだけです。

reading_record_main_v1_logs    よんだ本の記録
reading_record_main_v1_goals   月ごとの目標
reading_record_main_v1_meta    お祝い済みの月・音の設定

localStorage.clear() は使用しません。Service Worker の activate でも、 reading-books- で始まる自アプリのキャッシュ以外は削除しません。

記録内容とスキーマとの対応は docs/study-log.md を参照してください。

記録は「まなびクエスト」へ送れます

このアプリは保存だけを行い、外部への送信は一切しません。送信するのは、 gamification.giga-school.com に置かれた学習ポータルGamification/manabi-portal/)です。

専用ドメインへ移したあと、どうやってポータルへ渡しているか

学習ログ(study.records.v1)は localStorage に入るため、オリジン(ドメイン)ごとに 別々です。このアプリが reading-books.giga-school.com へ移ったことで、 ポータルが自分の localStorage を読むだけでは、この記録は見えなくなりました。

そこで、読み取り専用の受け渡し口を置いています。

ファイル 役割
records-export.html 受け渡し口のページ(児童が開く画面ではない)
js/records-export.js study.records.v1 を読んで postMessage で返す

ポータルがこのページを同一サイトの iframe で開き、postMessage で問い合わせます。 サブドメイン同士は同一サイト(eTLD+1 が giga-school.com)なので、ブラウザの third-party ストレージ分割の対象にならず、iframe の中でも第一者と同じ localStorage が見えます。

  • 読むだけ。書き込みも削除もしません。 集計側の不具合でこのアプリの記録が 壊れることが、原理的に起きない形にしてあります
  • 渡す相手は giga-school.com とそのサブドメインだけです ポータルの「きろくを おくる」を押すと、端末にたまった学習ログが まなびクエスト(学習記録・ゲーミフィケーションのアプリ)へ届きます。

まなびクエスト側の読書記録の手入力フォームは廃止され、読書の記録はこのアプリだけで行います。 届いたレコードは冊数・ページ数として、ランキング(どくしょ王)・バッジ・ミッション・ 学習ポートフォリオPDFに反映されます。

送るのは次の項目だけです。児童を見分けられる情報(氏名・出席番号・端末情報)は含みません (出席番号はポータル側で付きます)。

送る値 使われ方
本の題名(unit.title 読書記録・みんなの本だな
ページ数・値段・★(ext.pages / priceYen / rating 読書量・手ごたえの把握
かんそう(ext.memo ★の高いものが「みんなの本だな」のおすすめになります
ISBN(ext.isbn 同じ本をまとめるための鍵
記録した月・月の冊数・目標・達成(ext.monthKey / monthlyBooks / monthlyGoal / goalAchieved 目標に対する進み具合
通算の冊数・ページ数(ext.totalBooks / totalPages その時点までの読書量
記録のしかた(ext.lookupauto / manual / none 自動で引けない本(古い蔵書など)の割合
記録の開始・終了時刻と かかった時間(startedAt / endedAt / elapsedMs / activeMs 読書時間ではありません。「記録する操作」にかかった時間で、学習時間には加算されません

かんそうは「本を きろくする」画面で書いたものが送られます。 あとから「本の くわしく」で書き足したかんそうは、このアプリの中には残りますが、 送信ずみの学習ログには反映されません(学習ログは書きかえられない追記専用の記録のためです)。


ファイル構成

index.html                 画面の HTML(マークアップのみ。スクリプトとスタイルは持ちません)
css/style.css              スタイル一式(@font-face・画面・印刷・ハイコントラスト)
css/offline.css            offline.html だけで使うスタイル
js/pwa-early.js            <head> 最上部で読む。インストールの合図を取りこぼさないため
js/app.js                  アプリ本体(画面・操作・書誌検索・学習ログの組み立て/読み出し)
js/offline.js              offline.html の「もういちど ためす」
offline.html               圏外のときの案内(アプリと同じ配色・同じフォント)
studyLog.js                学習ログ共通モジュール(全アプリ共通・不変。ロジック版 1.1)
manifest.json              PWA マニフェスト
sw.js                      Service Worker(オフライン対応)
fonts/zen-maru-gothic/     Zen Maru Gothic woff2(700 / 900・unicode-range 分割 244本)
icons/                     アプリアイコン一式(192 / 512 / maskable / apple-touch / favicon / icon.svg)
vendor/quagga.min.js       バーコード読取ライブラリ QuaggaJS 0.12.1(MIT)
vendor/quagga.LICENSE.txt  QuaggaJS のライセンス本文
docs/study-log.md          学習ログの設計メモ
AUDIT.md                   GIGA Standard v4 による現状監査
MANUAL.md                  先生向けの手引き
ROLLOUT.md                 他のリポジトリへ持っていく知見
scripts/check-project.mjs  品質ゲート(静的・依存なし)
quality.config.json        品質ゲートの設定(コントラストの組み合わせ・例外の理由)
tests/browser.test.mjs     実機(Chromium)での検査
tests/server.mjs           検査用の静的サーバー(/Reading-Books/ の下で配信する)
package.json               npm スクリプト(check / test / serve)と playwright の指定
.github/workflows/deploy.yml  GitHub Actions(検査だけを行い、公開には手を出しません)
.github/dependabot.yml     GitHub Actions の更新通知(月1回)

外部 CDN には一切依存していません。 Web フォントも fonts/ から自分で配ります (学校のネットワークで外部ドメインが遮断されても、オフラインでも、同じ字体で表示されます)。 バーコードの読み取りは、対応端末では標準の BarcodeDetector を使い、 非対応の端末(iOS Safari など)では同梱の QuaggaJS に自動で切り替わります。


🔐 セキュリティ設計

サーバーを持たない静的アプリ(A型)です。認証も、児童を見分ける情報もありません。

項目 設計
個人情報 持ちません。 氏名・出席番号・メールアドレスを入力させる箇所がありません
保存場所 児童の端末の localStorage のみ。サーバーへ送りません
API キー 使いません(openBD / Google Books / NDL はいずれも鍵なしの公開API)
外へ出る通信 書誌検索の3件のみ。CSP の connect-src でそれ以外を遮断しています
CSP index.html<meta http-equiv> で宣言。script-src 'self' / style-src 'self'(インラインの script も style も無し)
クリックジャッキング frame-ancestors<meta> では効かず、GitHub Pages はヘッダーを足せないため、
js/pwa-early.jswindow.top !== window.self を見張ります
localStorage.clear() 使いません。 自アプリの3キーだけを消します
Service Worker localStorage に一切触れません。reading-books- で始まる自分のキャッシュだけを掃除します

'unsafe-inline' は script 側にも style 側にもありません。 style="..." の直書きはすべて css/style.css の補助クラス(.mt-4 .txt-danger など)に 置きかえてあります。新しい画面を足すときも style="..." を書かないでください (CSP に弾かれて、その指定だけが無視されます)。

大きさがそのつど変わるところ(グラフの棒の高さ・進み具合のバー)は JavaScript から element.style.height = … で動かしています。 これは CSSOM の操作なので CSP の対象外です。

⚠️ 制限とクォータ

制限 内容 対処
localStorage の容量 オリジンあたり 5MB 前後。専用ドメインなのでこのアプリだけで使えます(旧 gigayama.github.io に保存したぶんは別オリジンのため引きつげません) 記録1件は数百バイト。数千冊でも問題ありませんが、書き出し機能を用意しています
学習ログの上限 study.records.v1500件studyLog.js)。超えると古いものから消えます ポータルから定期的に送ってください
iOS Safari の ITP 7日間使わないと保存データが消されます ホーム画面に追加してもらう(MANUAL.md 2章)
Chromebook のタブ破棄 メモリ不足でタブが捨てられ、入力中のデータが消えることがあります pagehide で確定保存しています
書誌API いずれも公開API。学校のフィルタで遮断されることがあります 3系統フォールバック。すべて失敗したら手入力へ誘導します
フォント fonts/ は 244本・合計 約3.9MB ありますが、unicode-range で分割されており
初回に落ちるのは数十KBだけです
先読み(precache)の対象には入れていません
一度読まれたぶんは Service Worker が持つので、2回目からはオフラインでも同じ字体で出ます

開発

ビルドは不要です。ローカルで確認する場合は任意の静的サーバーで配信してください。

npx http-server -p 8099 -c-1 .

Service Worker とカメラは https:// または http://localhost でのみ動作します。

manifest.jsonscope/Reading-Books/ のため、 リポジトリ名のディレクトリの下で配信してください(例:site/Reading-Books/)。

検査

npm run check          # 静的な検査(依存パッケージ不要)
npm run test:browser   # 実機(Chromium)での検査。playwright が要る
npm test               # 両方

npm run test:browser は Chromium を立ちあげて、次を実際に確かめます。

見るもの 中身
横スクロール 320 / 375 / 810 / 1366 / 1920px
console・CSP 全画面を回ってエラー0件・Refused to 0件
空振り検知 検査用の記録がリストに出ているか(出ていないと以下が素通りする)
タップ領域 44px 未満の押せる要素が無いか(::after で広げた分も数える)
コントラスト 描画された文字色と、重なりを解決した地の色から計算。プレースホルダ(疑似要素)と「からっぽの表示」も含む
印刷 印刷シートが組み立てられ、画面用の要素が出ないか
maskable 絵が中央80%のまるに収まっているか(四角で見ると甘くなる)
オフライン サーバーを本当に止めてアプリが起動するか
manifest id / scope / start_url がリポジトリ名の絶対パスか

npm run check のほうは、LICENSE や手引きの有無・CSP・秘密情報の直書き・localStorage.clear()safe-area-inset・印刷CSS・トークンのコントラスト・manifest とアイコン・sw.js の作法・ APP_VERSION の一致・1ファイルの大きさ・初回に読む JS の量を見ます。

GitHub Actions

.github/workflows/deploy.yml が、main への push と すべての Pull Request で npm run check(静的)と npm run test:browser(Chromium)を回します。 公開そのものには手を出しません。 GitHub Pages は「ブランチからデプロイ」の設定で main に入った時点で公開されます(ワークフローが壊れても公開は止まりません)。

変更したら確認すること

  • 320px 幅で横スクロールが出ない(DevTools のレスポンシブモード)
  • コンソールにエラー0件、Refused to(CSP違反)0件
  • Application タブで manifest が読め、インストール可能と表示される
  • サーバーを止めてから再読み込みしても起動する
  • 「きろく」→「データを のこす」→「いんさつ」で A4たての記録が出る
  • APP_VERSION を上げた(sw.jsjs/app.js の両方。npm run check が食いちがいを見ます)
  • npm test が通る

©2026 どくしょ ちょきんばこ/ICT支援の杜

Releases

Packages

Contributors

Languages