対象実装: arm/v1/src/os_lib/layout/flex_min.rs 単体試験:
arm/v1/tests/src/test_flex_min.rs
典拠(2026-07-28、ユーザ許可のうえ参照): CSS Flexible Box Layout Module Level 1 — https://www.w3.org/TR/css-flexbox-1/#algo-main-item (§4.5 Automatic Minimum Size of Flex Items、§9.7 Resolving Flexible Lengths)
www.sugi-lab.net の再現中、ヒーロー見出しが過剰に折り返された。
<h1 class="hero-title">IoTで未来の暮らしを創造する</h1> <!-- 改行なし・14文字 -->.hero-content { max-width: 800px }、font-size 48px
なので 14 文字 ≒ 672px は 800px に 1
行で収まる(実サイトでも 1 行)。ところが画面では 1 行あたり 約
5 文字(≒240px)で 3 行に折り返されていた。
折り返し幅は tree.rs:2476 付近で
そのテキストノードの親ボックスの
dimensions.content.width から取られる。 つまり
.hero-content 系のボックス幅が ≒240px まで縮んでいた。
構造は .hero(display:flex, center) >
.hero-overlay(display:flex, center) >
.hero-content(max-width:800px) >
h1.hero-title。
min-width の初期値は
auto であり、これは
自動最小サイズとして働く(§4.5 Automatic Minimum Size
of Flex Items)。flex-basis: auto かつ width: auto
のとき、flex base size は アイテムのコンテンツサイズで決まる。tree.rs の flex-shrink 処理は、超過分を shrink
係数で按分したうえで
child.dimensions.content.width = (child.dimensions.content.width - cut).max(0);と 0
までしか下限を設けていない。自動最小サイズ(コンテンツ由来の下限)も
min-width 指定も一切参照していないため、アイテムは理論上 0
まで縮む。 これが .hero-content が 240px
まで縮んだ原因。
min-width
が明示指定されていれば、それも下限として効く
(自動最小サイズと明示指定の両方があれば大きい方が下限)。flex_min.rs を作成し、tree.rs の
flex-shrink 経路を distribute_shrink()
へ置き換えた(単体試験 15 件・make test 536 件全通過、
cargo clippy --release エラー
0)。しかし実機の描画は一切変わらなかった。
→ .hero-content が 240px になる原因は
flex-shrink 経路ではない、 または flex-shrink
経路自体がこのページで実行されていない。
規格違反そのものは実在し修正の価値はあるが、今回の症状の原因ではなかった。
各段の dimensions.content.width / height
をログ([BOXW])した結果:
[BOXW] depth=3 tag=section id="home" w=924 h=529 ← ヒーロー全幅。正常
[BOXW] depth=6 tag=h1 id="_ta_51" w=640 h=19 ← 見出し。幅は十分、しかし高さ 19px
幅は 640px あり十分だった(240px
という当初の見立ては誤り)。 問題は高さ 19px。font-size
48px なら行高は約 57px のはずで、 19px はレイアウトが既定の 16px
でこの h1 を計算していることを意味する。
一方、描画側は以前の実測で fs=48 だった。
レイアウト: 14文字 × 16px ≒ 224px → 640px に収まる → 1 行・高さ 19px
描画: 14文字 × 48px ≒ 672px → 640px を超える → 3 行に折り返し
→
真因は「レイアウトと描画でフォントサイズが食い違っている」。
.hero-title { font-size: 3rem }
が描画側には効いているのに、 レイアウト側(tree.rs
の内在サイズ計算)には届いていない。 高さ 19px
しか確保されないため、描画された 2〜3
行目が後続要素へはみ出して重畳する。
次にやること: tree.rs がテキストの
font-size をどこから取っているかを特定し、 rem
を解決できているか確認する(css_units.rs::parse_font_size
は rem 対応済みだが、 レイアウト側が別経路で解決している可能性が高い)。
get_vector_string_wrapped_size_ext へ渡している
font_size の算出元を読むこと。
flex-shrink
の下限無視は別件の実在バグとして修正済み(規格違反であることは確か)。
本件とは無関係だったため、以降は上記の font-size 経路を追うこと。
実サイト www.sugi-lab.net のヘッダ再現で見つかった 4
件。 いずれも仕様上の明確な誤りで、この 1 ページに限らず
あらゆる flex レイアウトに影響する。
FB-1: 幅指定の無い flex アイテムの基準サイズは、
自身の max-content 幅(CSS Flexbox L1 §9.2)。 「幅
auto のブロックとしてレイアウトした結果」=親いっぱいの幅を
基準にしてはならない。全項目の基準が同じ値になると、縮小が
均等割りになる(実測: container=924 なのに
8 項目すべて base=924 → 全項目が一律 88px
に揃い、合計が描画幅を超えた)。
FB-2:
基準サイズを書き換えたら、その幅でレイアウトをやり直す。
幅だけ変えると子は親いっぱいの幅で組まれたまま取り残され、
折り返しも高さも位置も更新されない。縮小側(distribute_shrink
適用後)にも同じ手当てが要る。
FB-3: 横並びの flex
コンテナの内在幅は、子の総和(+ gap)。
ブロックの「子は縦に積むので最大値」という集約を使ってはならない。
実測では 8 項目のナビの max-content が最も広い 1 項目ぶん(80px)に
なり、本来の 656px と 8 倍以上食い違った。 flex-wrap: wrap
のときだけ、min-content は 1 項目ぶんまで詰められる。
FB-4(未解決・一度入れて戻した):
内在幅は「今どう組まれているか」に 依存してはならない。呼び出し側は min
なら 1、max なら十分大きい値を available
として渡すので、現在の箱の幅を優先してはならない。
優先すると min を求めても max を求めても同じ値(今の幅で折り返した
結果)が返る。 2026-09-05:
優先順位を入れ替えたところ、ロゴの重なりは 40px → 20px
に縮んだものの、ヒーローの見出しが 2 行に折り返す退行
(実サイトは 1 行)と、ナビの “Smart Home” と “Datamining” の間隔が
潰れる退行を招いたため戻した。
原因は、intrinsic_inline_size
が内在幅の算出以外からも呼ばれており、
そちらは現在の箱の幅を前提にしていること。
2026-09-05(対応済み): 内在幅専用の入口
intrinsic_inline_size_ext(avail, pure)
を分けた。pure が真のときだけ
現在の箱の幅を見ない。真を渡すのは block_intrinsic_widths
だけで、 他の 13 箇所の呼び出しは従来の挙動を保つ。
これで退行は起きない(描画はバイト単位で同一)。
ただしロゴとナビの重なりは解消しなかったため、
内在幅の汚染はロゴの経路では起きていなかったことになる。
重なりの原因は別(下記の残課題)。
FB-5:
縮小で子を組み直したら、親の交差軸サイズも計算し直す。
横並びなら高さ。縮小した子はその幅で組み直すため行数が増えることがあり、
高さを更新しないと 1
行ぶんのまま取り残されて次の要素と重なる。 実サイト
forms.htm の目次で、2 行に折り返した項目が次の項目に
重なって出ていた。高さが明示されている場合は指定を尊重する。
FB-2(子の再レイアウト)と対で必要——子を組み直すだけでは足りない。
レイアウトの試験(layout/selftest.rs)は
selftest フィーチャ限定で
通常ビルドでは走らず、ホスト側の試験にも木構造のレイアウトは無い。 この
4 件は実機の描画と要素の矩形で確認した。
数値で確かめる手段が薄いのは弱点として残っている。
IoT Programming Lab.)の文字が箱から 20〜40px
はみ出し、 ナビの先頭項目 “Top” と重なる。実サイトはロゴが 2
行に折り返して収まる。 文字の実幅は 280 だが箱は 240〜268
で、箱は縮んでいるのに 中の文字が折り返していない。
2026-09-05 の試み(失敗・差し戻し済み): 内在幅の
pure 指定が
入れ子要素への再帰で引き継がれていないことに気づき、引き継ぐようにした。
理屈の上ではこちらが正しいはずだが、実測では
ナビ項目の間隔が潰れる退行(“Smart Home” と
“Datamining” が
くっつく)が出たうえ、ロゴの重なりは解消しなかった——悪化のみ。
引き継いで壊れるということは、どこかが非 pure
の内在幅に依存している。
次はその依存箇所を突き止めること。依存が分からないまま
「正しい方」へ寄せても壊れるだけだと実測で分かっている。
2026-09-06 実測: ヘッダ(親幅
924)の子の内在幅を計測した。
min=196 max=308 ——文字の実幅 280
より大きく妥当。 ロゴ側の内在幅は壊れていない。min=168 max=280
——ここが誤り。 ナビは 8 項目で 656 あるはず。
つまり原因はロゴではなくナビの内在幅の過小評価だった
(箱が小さく見積もられ、配置が詰まってロゴと重なる)。
横並びの総和(FB-3)は ul には効くが、その外側の
nav 要素を 経由すると 280 に落ちている。nav
はブロックで、子の ul が FlexNode。ブロックの集約が
FlexNode の子から総和を受け取れて
いない疑いがある。次はそこを確かめること。