Today I Learned

日々の仕事と学びの振り返りです。何をして、何を学んだかを記録します。

20268

2026-08-23

再利用可能なテーブルコンポーネントを型で固める

カラム配列からソートキーの union を導出し、ドメインごとに異なる行の型を合成コンポーネントの各パーツまで届けながら学んだこと — そしてその過程で何度も顔を出した変性の話

  • 値から型を導出する書き方と const 型パラメータはセットです。どちらか一方が欠けると、もう一方もエラーなく静かに無意味になります
  • const 宣言には型パラメータを付けられないので、ジェネリックな context は関数のクロージャの中で作る必要があります
2026-08-14

実装より先に正解の基準を固定する

コード生成ツールに作業を任せる際、判定基準を先に文書化し、コード・画面・レビュー意見を証拠へ格下げし、確信が足りなければ止める設計を整理した

  • 判定基準の出どころを人と承認済み仕様に限定すると、実装を仕様と取り違える失敗が消える
  • 完了の定義をテスト通過の後ろへ動かすと、assertion を緩める理由がなくなる
2026-08-09

ブラウザ拡張機能のリクエスト応答と変更通知を分けて追跡する

分離された拡張機能の実行空間でメッセージベース RPC によって保存を依頼し、変更信号と query 無効化で別画面が単一の原本を読み直す流れを整理した

  • Chrome メッセージ API は転送手段であり、リクエスト検証・ルーティング・エラー応答はアプリケーションが定義する
  • 命令応答と別画面への変更通知は目的が異なるため分離する
2026-08-07

中間層は何を通し、何を推測するのか — プロキシ・入力確定・画像比率

rewrite がアップストリームの 3xx をそのまま流して起きた無限リダイレクト、変換が終わる前に確定された日本語入力、16:9 と推測されていた画像比率 — 中間層の素通しと推測を点検して学んだこと

  • rewrite はリダイレクトではなくプロキシなので、アップストリームの 3xx をそのままブラウザへ渡す
  • 日本語入力のバグは値の正規化ではなく、まずコミット境界の問題として切り分ける

20267

2026-07-31

旧ブラウザ互換は babel 一つではない — 構文・ポリフィル・条件ロード・スモークの多層契約

5 年超の旧ブラウザでアプリが丸ごと死ぬバグを直しながら学んだこと — 複数の機能が同時に死んだらハイドレーションを疑い、構文ダウンレベルとランタイムポリフィルを分離し、ポリフィルは feature-detect で旧ブラウザにだけロードし、スモークは実際のインタラクションを実行する

  • 複数の UI 機能が同時に死んだら、個別のバグではなくハイドレーション/バンドルのパースといった共通の根本原因をまず疑う
  • 構文ダウンレベル(babel)とランタイム API(ポリフィル)は別々のレイヤーである — トランスパイラは構文は変えても存在しない関数は作らない
2026-07-25

初回ペイントスプラッシュとオフラインファースト — ISR を守りつつ即時反映する

ネイティブアプリのようなローディングスプラッシュを初回ペイントから表示しつつボットには隠すインラインスクリプト技法、そしてオフラインファーストで『自分の書き込みの即時反映』を staleTime ではなく invalidate と durable キューで作る方法

  • 初回ペイント前の表示 + ボット隠蔽はインラインブロッキングスクリプト注入 — UA ゲートは headers() で ISR を dynamic に壊す
  • react-query で「自分の書き込みの即時反映」は staleTime を下げるのではなく書き込み後の invalidateQueries で作る(invalidate は staleTime を無視)
2026-07-24

LCP のボトルネックが画像ではなく hydration のとき、そして節度あるスクロールモーション

ランディング性能を改善して学んだこと — LCP が遅いのに画像が小さいなら render delay(hydration)を疑いクライアント JS を外す、派手なスクロールモーションより進入スタッガーリビール、構造化データは画面と 1:1

  • LCP が遅いのに TBT が 0 で画像が小さければ element render delay(=クライアント境界の hydration)を疑い、そのサーフェスのクライアント JS を外す
  • 派手なスクロール従属モーション(scroll-jacking)より短いビューポート進入スタッガーリビールが汎用的な正解であることが多い
2026-07-23

断続的に崩れる画像 — objectURL レース、WebP フォールバック、サービスワーカーシェル

リロードのたびに見えたり見えなかったりする画像バグを追って学んだ三つ — objectURL のライフタイムレース、ブラウザ専用 lib のテスト hang 防止、サービスワーカーのシェルフォールバックを navigation リクエストに限定すること

  • サーバーが URL で渡す画像は objectURL blob で包まず直接レンダリングする — createObjectURL のライフタイムレースを根本から取り除く
  • ブラウザ専用(canvas)lib をテスト経路に入れるときは、実行前の同期 capability ゲートで jsdom の hang を防ぐ
2026-07-22

無限リスト機能をコードなしで移す — クリーンルーム契約と keyset ページネーション

仮想化された無限リスト+スクロール復元機能を別スタックへ移しながら学んだこと — 実装コードではなく見た目の振る舞いを契約として書くクリーンルーム方式、そして無限スクロールの安定性を左右する offset と keyset ページネーション

  • 機能を移す文書は「使ったライブラリ」ではなく「外からどう振る舞い、失敗時に何を保証するか」を契約として書く
  • 無限スクロールリストの安定性は大半がページネーション方式で決まる — offset は挿入/削除で重複・欠落、keyset は値基準なので安定
2026-07-20

公開面・分析ファサード・モーションゲートで学んだこと

構造化データと可視 FAQ の一致、auth 解決後の fetch ゲート、分析イベントのファンアウトファサード、prefers-reduced-motion JS 遷移ゲートを汎用概念として整理した TIL

  • 構造化データ(FAQPage など)は、画面上に表示されるテキストと同じソースから出すべきである
  • 保護 API はセッションローディングが終わった後にだけ有効にし、ローディング中は !user だけで匿名・fetch を決定しない
2026-07-14

開発環境を整備していて出会った汎用概念:シェルの引用符・モノレポ公開・CIランナー・ガード設計

ツール・インフラを整備しながら遭遇したシェルの単語分割、workspace公開の書き換え、CIランナーの不一致、fail-openガードを汎用概念として整理したTIL

  • シェル変数に入ったパスは空白があると引用符なしで使うと複数の引数に分割され、コマンドが静かに壊れる
  • モノレポ内部の依存はworkspaceプロトコルでリンクし、公開時に実際のバージョンへ置換するマネージャーで上げるべきだ
2026-07-13

星明かりグラフ・SW キャッシュ・Playwright triage・ESLint guardrail

Canvas 星ノード、Service Worker SWR・generation セキュリティ、CI flaky 分類、デザインシステム ESLint guardrail を一日で整理した TIL

  • Canvas ノード装飾は ID ハッシュで安定化し、globalAlpha・shadowBlur は描画直後に必ず復元する
  • Playwright CI が赤いときは retry 結果で flaky と deterministic failed を先に分ける
2026-07-11

ホームヒーローの scroll-driven ナラティブ演出

GSAP なしで sticky scrolly·CSS keyframes·view-timeline を使い、TIL→Knowledge 昇格デモとパイプラインセクションを実装した一日

  • sticky scrolly はラッパーの高さぶんスクロールの時間軸を作り、phase は進行度から派生させると JS が薄くなる
  • ヒーローのライブデモは clip-path steps ワイプ + animation-delay だけでループを作れる
2026-07-10

Modal peek スタックと CSS layer パッケージ

単一 dialog 内の document push と @layer ベースの editor CSS 分離で、peek UX とテーマのカスタマイズを合わせた一日

  • gallery→peek は dialog を新しく開くより、同じ shell で documentId だけ替えるほうが連続的だ
  • async resolve は (newId, oldContent) 中間フレームを render-time reset で防がねばならない
2026-07-09

階層型ドキュメントナビと viewer バンドル分離

page push/peek navigator と read-only viewer entry の分離で、ポートフォリオのデモ深度とバンドルを同時に合わせた一日

  • ドキュメントグラフは page ブロック + push/peek navigator 契約で flat editor と分離するほうがよい
  • peek·embed は editor と DOM は同じでも import graph が違ってこそ viewer バンドルが小さくなる
2026-07-08

モバイルのポインタジェスチャとサーバ画像プロキシ

タッチのドラッグ・リサイズの安定化と、unoptimized 環境で画像プロキシにより転送バイトを減らした一日

  • タッチで引いてもページが一緒にスクロールしないようドラッグ動作を直した
  • 画像は原本まるごとではなく必要なサイズだけ送るようプロキシを付けた
2026-07-07

ネストされたUIの再レンダーをO(1)へ — read-model・external store・レンダー回数テスト

イミュータブルなツリーエディタで一行直しただけで全体が再描画される問題を、まずテストで固定してから、正規化projectionとid別購読で行・コンテナの再レンダーを減らした過程

  • イミュータブルなツリーpatchは祖先も新しいrefを受け取り、propsで下ろすとO(depth)の再レンダーになる — 正規化entry + id別購読でO(1)に減らせる
  • entryキャッシュは入力refではなく出力の値-等価で安定化すべきで、外部storeのsyncはsetState直前のchoke pointで同期実行する
2026-07-06

バンドルの shared vendor、クロスブロック選択、フローティング UI 座標

webpack splitChunks の罠、ハイブリッドエディタのクロスブロック範囲演算、selectionchange·scroll anchor パターン、Context 購読の分離

  • catch-all vendor に chunks:all と固定 name を一緒に使うと、dynamic import が shared チャンクへ引き上げられる
  • クロスブロックのテキスト演算は範囲 state の代わりに getSelection からの無状態派生 + 境界 split の単一化が安全だ
2026-07-05

ブロックエディタの順序・協業・キーボード契約

fractional ordering, optimistic rebase, IME defer, keyboard undo 委譲, scoped popover, Jest ESM shim など汎用パターンの整理

  • sibling の順序は integer index より fractional position key + neighbor anchor + tie-break が concurrent insert に有利だ
  • optimistic UI は inverse patch なしには rebase できず、remote seq が merge の権威でなければならない
2026-07-04

ドキュメントは host SoT、エディタは focused guest

ブロックドキュメント編集器における host/guest 境界、React 外部ストアのスナップショット、undo の2スタック、HTML5 ドラッグ・選択/コピーを、初めて読む人向けに整理

  • ドキュメントの意味は host のパッチが所有し、リッチテキストエンジンはフォーカスブロックの guest としてのみ置く
  • useSyncExternalStore のスナップショットに ms タイムスタンプだけを使うと、同一 ms の mutation で再レンダーが落ちる — 単調な revision が必要