Skip to content

Repository files navigation

〇〇市長、募集中! ポスタージェネレーター

住民が「どんな首長になってほしいか」をポスターの形で表現し、集まった言葉を並べて公開するウェブアプリ。 サーバー側のプログラムを持たない。 生成した静的ファイルを置くだけで動く。

横浜市長選にあわせ、Code for YOKOHAMA のメンバーを中心とした市民有志がつくったもの。 横浜の内容は設定ファイルに追い出してあり、どの自治体でもそのまま使えるように公開している。

動いているもの

横浜市長選にあわせて公開した、実際の稼働例。

試すだけなら「ギャラリーに公開する」のチェックを外したまま保存してください。 チェックを入れて保存すると、実際の投稿として記録されます。

できること

ビルドすると、2つのHTMLができる。

ジェネレーター 言葉・レイアウト・配色・背景写真を選ぶとポスターがその場で描かれる。PNGで保存し、SNSへシェアできる
ギャラリー 投稿された言葉を一覧する。言語・レイアウトで絞り込める

日本語・やさしい日本語・英語・中国語(簡体)・韓国語の5言語に対応し、 選択肢もポスターに描かれる文字もすべて切り替わる。

文書

誰向けか
docs/GUIDE.md はじめての人のための導入ガイド。 自分のまちで立ち上げたい非エンジニア向け。Node.js を入れるところから公開まで
docs/DESIGN.md なぜこう作ってあるか。 考え方と、割り切ったこと。改造・移植の前に読む
docs/SETUP.md 移植手順(技術者向け)。 要点だけを短く
docs/OPERATION.md 公開してからの運営。 荒らし対応、問い合わせ、承認制、終わり方
docs/USER-GUIDE.md 利用者向けの使い方。 そのまま配れる下敷き

どういう作りか

  • ポスターはすべて Canvas 2D で描いている。外部ライブラリを一切使わない。 CDNもnpmも不要で、生成される .html はそれ1枚で動く
  • 投稿はGoogleフォームにPOSTし、回答はスプレッドシートに溜まる。 データベースもサーバーサイドのコードも要らない
  • ギャラリーはそのスプレッドシートをCSVで読み、同じ描画コードでポスターを組み直す。 画像は保存していない
  • 背景写真はウィキメディア・コモンズの pageid だけを記録し、表示時にAPIで取り直す。 写真そのものは送信しない
  • 保存したPNGには投稿内容をメタデータとして焼き込んであり、生成側に読み戻して続きを編集できる

これらを選んだ理由と、その代わりに諦めたことは docs/DESIGN.md に書いてある。

動かす

必要なのは Node.js だけ(テストで使う jsdom の要件で 20.19 以上、22 系なら 22.13 以上)。 ビルド自体に依存ライブラリは無く、npm i はテストとOGP画像の生成に使う。

npm i        # 初回のみ
npm test     # dist/ を作り直してから、jsdom で2ページを動かして確認

npm test は、走る前に node build.js と OGP画像2枚の生成を自動で済ませる。 クローンした直後でも、この2つだけで最後まで通る。

自分のまちの設定を入れるときは、次を足す。

cp config.sample.json config.json  # 公開先URL・地名・フォーム・シートを書く
npm run build                      # src/ から dist/ を作り直す

config.json が無ければ config.sample.json(サンプル市・example.org のダミー)で動く。 設定の書き方は docs/SETUP.md。

ローカル確認は file:// だと通信が弾かれることがあるので、簡易サーバー経由がよい。

cd dist && python3 -m http.server 8000   # Windows なら python -m http.server 8000

SNSのカード画像(dist/ogp.png と dist/ogp-gallery.png)を作り直すときは次を叩く。 npm test の中でも同じものが走っている。

npm run ogp

dist/ の2つのHTMLとOGP画像2枚を、同じフォルダにアップロードすれば公開できる。

ファイル

config.sample.json 公開先URL・地名と首長の呼び名・フォームID・entry番号・シートID。複製して config.json にする
build.js src/ から dist/ を生成。設定を差し込むのもここ。依存ライブラリなし
place.js 地名から「市長/町長」「市民/町民」などを組み立てる。build.js と OGP生成が使う
src/i18n.js 5言語の辞書。文言・選択肢はすべてここ
src/app.tmpl ジェネレーター本体。ポスター描画コードの正本
src/gallery.tmpl ギャラリー
tools/mkqr.js QRを事前計算してJSONに落とす。書き出したQRは読み返して検証する
tools/mkogp.js / mkogp-gallery.js OGP画像の生成
test/ jsdom + @napi-rs/canvas で全ボタンを押して回すテスト

地名は src/ に直接書かず、config.json の place から差し込んでいる。 "full": "葉山町" と書けば、ページも辞書も「葉山町長・町民・役場・町長選」に変わる (市・町・村・区・都道府県に対応。英語・中国語・韓国語は自動では決められないので設定に書く)。

描画関数は src/app.tmpl にしかなく、ビルド時にギャラリーへ機械的にコピーされる。 ポスターのデザインを変えれば両方に反映される。dist/ を手で直しても次のビルドで消える。

config.json と、そこから作るQRコード(tools/qr*.json)、dist/ は .gitignore 済み。 共有するのは *.sample.json のほう。

テスト

ブラウザを使わず、jsdom + @napi-rs/canvas で dist/*.html を実際に動かす。 node build.js のあとに npm test を実行する。

ページ内の例外は jsdom が黙って飲み込むので、test/env.js が拾って必ず失敗にしている。 通信はすべて止め、Googleフォームとコモンズは差し替えて中身を検査する。

見ているのは、5言語 × 6レイアウト × 3サイズの描画、地名の差し替え、フォーム送信の13項目、 ギャラリーのフィルタ、コモンズのライセンス絞り込み、保存PNGの焼き込みと読み戻し、 下書きと履歴、XSS、壊れた記録への耐性、localStorage が使えない端末での動作、 そして2ページとも全ボタンを機械的にクリックしても例外が出ないこと。

初期化順を入れ替えただけでページの半分が動かなくなることがあるので、 UIに手を入れたら全ボタンを押すテストまで通すこと。

使うときに知っておくこと

コードは MIT なので、改変も再配布も商用利用も自由。 連絡も報告も要らない。 以下はお願いではなく、外すと事故になるところの共有。理由は docs/DESIGN.md。

写真まわりは、外すと他人の権利の問題になる

  • 背景写真はウィキメディア・コモンズから、CC0・パブリックドメイン・CC BY だけを検索している。 SA は継承条件ができあがったポスター全体に及び、ND はトリミング・色調変更が許されないため外してある。 絞り込みを広げるなら、できたポスターのライセンスをどうするかまで含めて決めること
  • 撮影者・作品名・ライセンス・URL・改変の有無は、画像に自動で焼き込んでいる。 CC BY の写真でこの表示を外すと、ライセンス違反になる。 消せるように改造するなら、同時に CC BY を検索対象から外す必要がある

画面に書いてあることと、実際の動きをずらさない

いまの画面は、投稿する人にこう約束している。約束の中身は運営する人が決めてよい。 ただし文言と実装のどちらか片方だけを変えると、投稿者への嘘になる。

画面での約束 それを支えている実装
チェックを入れたときだけ送信する 「ギャラリーに公開する」(consent)が ON のときだけ POST する
写真そのものは送らない コモンズの pageid だけを記録し、表示時にAPIで取り直す
下書きと履歴は端末から出ない localStorage のみ。消す導線(「この端末の記録を消す」)も置いてある
投稿された言葉は CC0 で公開する 注意書き n4 に明記。集計・公開の前提

集めているのはニックネーム・年代・性別・地域(すべて任意)。項目を増やすときは、 注意書き(n4b)と、保存した画像に焼き込まれる内容も一緒に直すこと。

最初に作った版での判断(従う義務はない)

  • ポスターに載る統計(src/i18n.js の stats)は、出典を確認してから入れた。 他都市・他国との比較は、統計年次が変わっても逆転しない差のものを選んでいる
  • 選挙が絡む時期には公職選挙法の制限があり、生成物をどう使うかは使う人の責任になる。 そのため画面に「選挙運動には使わないでください」と書き、特定の候補者・政党には触れなかった

ライセンス

このリポジトリのコードは MIT License(LICENSE)。 Copyright (c) 2026 Code for YOKOHAMA。

改変も再配布も商用利用も自由だが、著作権表示とライセンス文は残すこと。 生成される dist/*.html にはこの表示が自動で入る(build.js が差し込む)。

投稿された言葉のCC0とは別の話。 あれは集まったデータを誰でも分析できるようにするための 取り決めで、コードのライセンスとは対象が違う。

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages