From bb28b5691d7dcc6ebf716178cd0325b30030fda0 Mon Sep 17 00:00:00 2001 From: Bartosz-Do Date: Mon, 7 Sep 2026 20:46:40 +0200 Subject: [PATCH] Emit block-axis logical properties from spacing and border utilities (#2985) --- .changeset/logical-block-axis-utilities.md | 5 ++++ BOOTSTRAP-V6-MIGRATION.md | 33 ++++++++++++++++++++++ core/scss/_extends.scss | 4 +-- core/scss/_utilities.scss | 30 +++++++++++--------- core/scss/_variables.scss | 6 ---- core/scss/bootstrap/_spinners.scss | 2 +- 6 files changed, 58 insertions(+), 22 deletions(-) create mode 100644 .changeset/logical-block-axis-utilities.md diff --git a/.changeset/logical-block-axis-utilities.md b/.changeset/logical-block-axis-utilities.md new file mode 100644 index 000000000..7dfb24a94 --- /dev/null +++ b/.changeset/logical-block-axis-utilities.md @@ -0,0 +1,5 @@ +--- +"@tabler/core": patch +--- + +Updated spacing and border utilities to emit block-axis logical properties like `margin-block-start` and `border-block-end`. diff --git a/BOOTSTRAP-V6-MIGRATION.md b/BOOTSTRAP-V6-MIGRATION.md index 4689df178..b40d73c20 100644 --- a/BOOTSTRAP-V6-MIGRATION.md +++ b/BOOTSTRAP-V6-MIGRATION.md @@ -271,6 +271,39 @@ which validates the direction. **Gate:** RTL build comparison; `html-diff` zero diff. +**Status:** #2974 landed the block axis of the spacing and border utilities (`mt` / `mb` / `my`, +`pt` / `pb` / `py`, `.border-top` / `.border-bottom` / `.border-y`, negative `mt` / `mb` / `my`) plus +the non-geometry physical leftovers (`_extends.scss` markdown-table padding, `_spinners.scss` border). +The inline axis was already logical. `tabler.css` changed by property rename only; `tabler.rtl.css` +changed by the same renames plus the three inline-axis sweep lines, which rtlcss no longer flips +because the logical property now resolves the direction itself — rendered output is unchanged. + +**Why rtlcss and `--dir` stay.** Logical properties cover box-model sides (margin, padding, border, +inset, size) but there is no logical form for `transform`, so a physical `translateX(-50%)` cannot be +mirrored by the browser. Tabler bridges that with `--tblr-dir` (`1` in LTR, `-1` in RTL, set in +`_props.scss`) multiplied into the `calc()` of every direction-sensitive translate — `.translate-middle` +in `_utilities.scss`, `$form-floating-label-transform` in `_variables.scss` — each carrying a +`/*rtl:ignore*/` so rtlcss (which would otherwise negate the `calc()` a second time) leaves it alone. +rtlcss is still run in `.build/build-css.ts` for everything `--dir` does not reach. + +**What would let us drop rtlcss.** Every remaining `rtl:` marker under `core/scss` has to be gone +first — `grep -rn 'rtl:' core/scss --include='*.scss'` is the live checklist. They fall into: + +- *Transforms* — `.translate-middle`, `$form-floating-label-transform`, and the `translateX` / + `rotate` uses in `ui/_steps.scss`, `ui/_switch-icon.scss`, `ui/_buttons.scss`, `ui/_badges.scss`, + `ui/_loaders.scss`, `ui/_dropdowns.scss`, `layout/_navbar.scss`, `bootstrap/_carousel.scss`, + `bootstrap/_offcanvas.scss`, `bootstrap/_spinners.scss`. These need the `--dir` multiplier applied + the same way, or a future CSS logical transform. +- *Popover / tooltip arrow geometry* (`bootstrap/_popover.scss`, `_tooltip.scss`) — physical + `border-*` triangles on absolutely positioned arrows; the Floating UI work (phase 5) is expected to + replace this with placement-driven values. +- *`rtl:raw` blocks* in `bootstrap/_reboot.scss` and `ui/_breadcrumbs.scss`, and the + `rtl:begin:remove` block in `mixins/bootstrap/_utilities.scss` — hand-written RTL overrides that + would need a logical-property equivalent. + +Once the checklist is empty, `rtlcss` and the `tabler.rtl.css` build come out and one stylesheet +serves both directions, matching Bootstrap v6. + ### Phase 5 — JavaScript runtime - ESM only. Drop the UMD build from `core/package.json` (`js:build:standalone`, `js:min:*`); no diff --git a/core/scss/_extends.scss b/core/scss/_extends.scss index 94d9d8200..5d92f6b98 100644 --- a/core/scss/_extends.scss +++ b/core/scss/_extends.scss @@ -60,12 +60,12 @@ th:first-child, td:first-child { - padding-left: 0; + padding-inline-start: 0; } th:last-child, td:last-child { - padding-right: 0; + padding-inline-end: 0; } tr:last-child > * { diff --git a/core/scss/_utilities.scss b/core/scss/_utilities.scss index 873063c79..b2bf51c67 100644 --- a/core/scss/_utilities.scss +++ b/core/scss/_utilities.scss @@ -6,11 +6,13 @@ @use 'maps' as *; @use 'mixins/functions' as *; +// Border utility values. Uses the `--border-color-translucent` custom property +// (dark-mode aware) rather than the light-only Sass token. $border-values: ( null: var(--border-width) var(--border-style) var(--border-color-translucent), wide: $border-width-wide var(--border-style) var(--border-color-translucent), 0: 0, -); +) !default; $utilities-border-colors: map-loop( ( @@ -188,7 +190,8 @@ $utilities: map.merge( values: $border-values, ), 'border-top': ( - property: border-top, + class: border-top, + property: border-block-start, values: $border-values, ), 'border-end': ( @@ -197,7 +200,8 @@ $utilities: map.merge( values: $border-values, ), 'border-bottom': ( - property: border-bottom, + class: border-bottom, + property: border-block-end, values: $border-values, ), 'border-start': ( @@ -212,7 +216,7 @@ $utilities: map.merge( ), 'border-y': ( class: border-y, - property: border-top border-bottom, + property: border-block-start border-block-end, values: $border-values, ), 'border-color': ( @@ -432,7 +436,7 @@ $utilities: map.merge( ), 'margin-y': ( responsive: true, - property: margin-top margin-bottom, + property: margin-block-start margin-block-end, class: my, values: map.merge( $spacers, @@ -443,7 +447,7 @@ $utilities: map.merge( ), 'margin-top': ( responsive: true, - property: margin-top, + property: margin-block-start, class: mt, values: map.merge( $spacers, @@ -465,7 +469,7 @@ $utilities: map.merge( ), 'margin-bottom': ( responsive: true, - property: margin-bottom, + property: margin-block-end, class: mb, values: map.merge( $spacers, @@ -500,13 +504,13 @@ $utilities: map.merge( ), 'negative-margin-y': ( responsive: true, - property: margin-top margin-bottom, + property: margin-block-start margin-block-end, class: my, values: $negative-spacers, ), 'negative-margin-top': ( responsive: true, - property: margin-top, + property: margin-block-start, class: mt, values: $negative-spacers, ), @@ -518,7 +522,7 @@ $utilities: map.merge( ), 'negative-margin-bottom': ( responsive: true, - property: margin-bottom, + property: margin-block-end, class: mb, values: $negative-spacers, ), @@ -543,13 +547,13 @@ $utilities: map.merge( ), 'padding-y': ( responsive: true, - property: padding-top padding-bottom, + property: padding-block-start padding-block-end, class: py, values: $spacers, ), 'padding-top': ( responsive: true, - property: padding-top, + property: padding-block-start, class: pt, values: $spacers, ), @@ -561,7 +565,7 @@ $utilities: map.merge( ), 'padding-bottom': ( responsive: true, - property: padding-bottom, + property: padding-block-end, class: pb, values: $spacers, ), diff --git a/core/scss/_variables.scss b/core/scss/_variables.scss index b6273def6..8b2a49518 100644 --- a/core/scss/_variables.scss +++ b/core/scss/_variables.scss @@ -734,12 +734,6 @@ $border-radiuses: ( null: var(--border-radius-md), ) !default; -$border-values: ( - null: var(--border-width) var(--border-style) $border-color-translucent, - wide: $border-width-wide var(--border-style) $border-color-translucent, - 0: 0, -); - $icon-color: var(--gray-400) !default; // Code diff --git a/core/scss/bootstrap/_spinners.scss b/core/scss/bootstrap/_spinners.scss index 2b273643b..b34db2dd7 100644 --- a/core/scss/bootstrap/_spinners.scss +++ b/core/scss/bootstrap/_spinners.scss @@ -35,7 +35,7 @@ // scss-docs-end spinner-border-css-vars border: var(--spinner-border-width) solid currentColor; - border-right-color: transparent; + border-inline-end-color: transparent; } .spinner-border-sm {