From 813303e90fa1d452dcfcf27b85403632ca63aac3 Mon Sep 17 00:00:00 2001 From: Alexander van Elsas <58037137+avanelsas@users.noreply.github.com> Date: Tue, 9 Jun 2026 12:03:01 +0200 Subject: [PATCH] fix(x-table): integer columns shorthand yields truly equal columns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `columns="N"` expanded to `repeat(N,1fr)`, but a bare `1fr` track keeps a `min-content` floor — a column holding wide/unbreakable content (a long string, a button) bulges past its 1/N share and the columns come out uneven, which reads as the attribute being ignored. Expand the integer shorthand to `repeat(N,minmax(0,1fr))` instead. The `minmax(0,1fr)` floor of 0 removes the intrinsic-minimum constraint, so the N tracks are genuinely equal and content shrinks/ellipsises rather than overflowing — which is what `columns="N"` is meant to express. Explicit template strings are still passed through unchanged for per-column sizing. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/x-table.md | 8 +++++--- src/baredom/components/x_table/model.cljs | 9 +++++++-- test/baredom/components/x_table/model_test.cljs | 10 +++++----- test/baredom/components/x_table/x_table_test.cljs | 3 ++- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/docs/x-table.md b/docs/x-table.md index 5b49271d..86e115a1 100644 --- a/docs/x-table.md +++ b/docs/x-table.md @@ -26,7 +26,7 @@ A stateless Web Component that acts as the grid container for `x-table-row` and | Attribute | Type | Default | Description | |-----------|------|---------|-------------| -| `columns` | string | absent | CSS `grid-template-columns` value. A positive integer (e.g. `"4"`) expands to `repeat(4,1fr)`. Any other string is used as-is. Absent → no explicit column template. | +| `columns` | string | absent | CSS `grid-template-columns` value. A positive integer (e.g. `"4"`) expands to `repeat(4,minmax(0,1fr))` (equal-width columns). Any other string is used as-is. Absent → no explicit column template. | | `caption` | string | absent | Visible caption text rendered before the first row. Also sets `aria-label` on the host. | | `selectable` | `"none"\|"single"\|"multi"` | `"none"` | Row selection mode. `"single"` and `"multi"` switch `role` to `"grid"` and enable automatic selection management via `x-table-row-click` events. `"multi"` also adds `aria-multiselectable="true"`. | | `striped` | boolean | absent | Applies an alternating background to even-indexed `x-table-row` children. | @@ -182,8 +182,8 @@ x-table { `x-table` is `display:grid`. Its `grid-template-columns` value comes from the `columns` attribute: ```css -/* Integer shorthand */ - → grid-template-columns: repeat(4,1fr) +/* Integer shorthand — equal-width columns */ + → grid-template-columns: repeat(4,minmax(0,1fr)) /* CSS value */ → grid-template-columns: 2fr 1fr 120px @@ -192,6 +192,8 @@ x-table { → grid-template-columns: (not set, auto) ``` +The integer shorthand expands to `minmax(0,1fr)`, not plain `1fr`. A bare `1fr` track keeps a `min-content` floor, so a column holding wide or unbreakable content (a long string, a button) bulges past its share and the columns come out uneven. `minmax(0,1fr)` removes that floor, giving genuinely equal columns whose content shrinks/ellipsises instead of overflowing — which is what `columns="N"` is meant to express. Pass an explicit template string when you want per-column sizing. + Each `x-table-row` child has `grid-template-columns:subgrid; grid-column:1/-1` — it inherits the column tracks from `x-table` and its `x-table-cell` children align perfectly across rows. The slot inside `x-table`'s shadow DOM has `display:contents`, making `x-table-row` elements direct grid items of the host grid. diff --git a/src/baredom/components/x_table/model.cljs b/src/baredom/components/x_table/model.cljs index f1208f9c..d7657765 100644 --- a/src/baredom/components/x_table/model.cljs +++ b/src/baredom/components/x_table/model.cljs @@ -50,7 +50,12 @@ (defn parse-columns "Parse `columns` attribute to a CSS grid-template-columns string. - A positive integer string (e.g. \"4\") becomes \"repeat(4,1fr)\". + A positive integer string (e.g. \"4\") becomes \"repeat(4,minmax(0,1fr))\" + — equal-width tracks that can shrink below their content's intrinsic + minimum. Plain `1fr` keeps a `min-content` floor, so a wide/unbreakable + cell makes its column bulge past its share and the columns come out + uneven; `minmax(0,1fr)` is what authors actually mean by \"N equal + columns\". For per-column sizing, pass an explicit template string instead. Any other non-empty string is returned as-is. Nil or blank → nil (no explicit template)." [s] @@ -61,7 +66,7 @@ (if (and (not (js/isNaN n)) (pos? n) (= t (str n))) - (str "repeat(" n ",1fr)") + (str "repeat(" n ",minmax(0,1fr))") t)))))) (defn parse-selectable diff --git a/test/baredom/components/x_table/model_test.cljs b/test/baredom/components/x_table/model_test.cljs index 9056b158..f7e87f9a 100644 --- a/test/baredom/components/x_table/model_test.cljs +++ b/test/baredom/components/x_table/model_test.cljs @@ -4,10 +4,10 @@ ;; ── parse-columns ──────────────────────────────────────────────────────────── (deftest parse-columns-integer-test - (testing "integer string → repeat(N,1fr)" - (is (= "repeat(4,1fr)" (model/parse-columns "4"))) - (is (= "repeat(1,1fr)" (model/parse-columns "1"))) - (is (= "repeat(12,1fr)" (model/parse-columns "12"))))) + (testing "integer string → repeat(N,minmax(0,1fr)) for truly equal columns" + (is (= "repeat(4,minmax(0,1fr))" (model/parse-columns "4"))) + (is (= "repeat(1,minmax(0,1fr))" (model/parse-columns "1"))) + (is (= "repeat(12,minmax(0,1fr))" (model/parse-columns "12"))))) (deftest parse-columns-css-string-test (testing "CSS string passed through" @@ -77,7 +77,7 @@ (is (nil? (:row-count m))))) (deftest normalize-columns-test - (is (= "repeat(3,1fr)" (:columns (model/normalize {:columns-raw "3"})))) + (is (= "repeat(3,minmax(0,1fr))" (:columns (model/normalize {:columns-raw "3"})))) (is (= "2fr 1fr" (:columns (model/normalize {:columns-raw "2fr 1fr"}))))) (deftest normalize-selectable-test diff --git a/test/baredom/components/x_table/x_table_test.cljs b/test/baredom/components/x_table/x_table_test.cljs index 9d863237..6a590f26 100644 --- a/test/baredom/components/x_table/x_table_test.cljs +++ b/test/baredom/components/x_table/x_table_test.cljs @@ -64,7 +64,8 @@ (deftest columns-integer-test (let [^js el (append! (make-table {:columns "3"}))] ;; grid-template-columns should be set on host style - (is (= "repeat(3, 1fr)" (.. el -style -gridTemplateColumns))))) + ;; Browser serialises 0 → 0px when reading back the computed style value. + (is (= "repeat(3, minmax(0px, 1fr))" (.. el -style -gridTemplateColumns))))) (deftest columns-css-value-test (let [^js el (append! (make-table {:columns "2fr 1fr"}))]