Add logical inset utilities (inset-s, inset-e, inset-bs, inset-be)#19613
Add logical inset utilities (inset-s, inset-e, inset-bs, inset-be)#19613RobinMalfait merged 4 commits intomainfrom
Conversation
WalkthroughThe PR adds support for block-axis inset properties by inserting 🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Tip 🧪 Unit Test Generation v2 is now available!We have significantly improved our unit test generation capabilities. To enable: Add this to your reviews:
finishing_touches:
unit_tests:
enabled: trueTry it out by using the Have feedback? Share your thoughts on our Discord thread! Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/tailwindcss/src/utilities.ts (1)
519-571:⚠️ Potential issue | 🟡 MinorAutocomplete suppression is incomplete for deprecated spacing utilities.
skipSuggestionsonly prevents the extra DEFAULT_SPACING_SUGGESTIONS, butfunctionalUtilitystill registers suggestions (theme keys + static values). That means deprecatedstart-*/end-*will still appear in completions. Consider plumbingskipSuggestionsthroughfunctionalUtilityand guarding its suggestion registration so deprecated utilities are fully hidden.Proposed fix (guard suggestions in functionalUtility)
type UtilityDescription = { supportsNegative?: boolean supportsFractions?: boolean themeKeys?: ThemeKey[] defaultValue?: string | null staticValues?: Record<string, AstNode[]> + skipSuggestions?: boolean handleBareValue?: (value: NamedUtilityValue) => string | null handleNegativeBareValue?: (value: NamedUtilityValue) => string | null handle: (value: string, dataType: string | null) => AstNode[] | undefined } function functionalUtility(classRoot: string, desc: UtilityDescription) { ... - suggest(classRoot, () => [ - { - supportsNegative: desc.supportsNegative, - valueThemeKeys: desc.themeKeys ?? [], - hasDefaultValue: desc.defaultValue !== undefined && desc.defaultValue !== null, - supportsFractions: desc.supportsFractions, - }, - ]) - - if (desc.staticValues && Object.keys(desc.staticValues).length > 0) { - let values = Object.keys(desc.staticValues) - suggest(classRoot, () => [{ values }]) - } + if (!desc.skipSuggestions) { + suggest(classRoot, () => [ + { + supportsNegative: desc.supportsNegative, + valueThemeKeys: desc.themeKeys ?? [], + hasDefaultValue: desc.defaultValue !== undefined && desc.defaultValue !== null, + supportsFractions: desc.supportsFractions, + }, + ]) + + if (desc.staticValues && Object.keys(desc.staticValues).length > 0) { + let values = Object.keys(desc.staticValues) + suggest(classRoot, () => [{ values }]) + } + } } spacingUtility(...){ functionalUtility(name, { ... staticValues, + skipSuggestions, }) if (!skipSuggestions) { suggest(name, () => [ ... ]) } }
Add new logical property-based inset utilities: - inset-s-* → inset-inline-start (alias for start-*) - inset-e-* → inset-inline-end (alias for end-*) - inset-bs-* → inset-block-start (new) - inset-be-* → inset-block-end (new) These utilities support the same value set as other inset utilities including spacing scale values, arbitrary values, auto, full, and negative values. https://claude.ai/code/session_01JcYXVAMawRuntKatjku1WZ
…inset-e-* Add `skipSuggestions` option to `spacingUtility` to control whether utilities appear in autocomplete suggestions. Use this to soft-deprecate the `start-*` and `end-*` utilities - they still work but won't be suggested in autocomplete, encouraging users to use `inset-s-*` and `inset-e-*` instead. https://claude.ai/code/session_01JcYXVAMawRuntKatjku1WZ
We already had legacy utilities in that file. I think I want to eventually keep those in the utilities file directly and introduce some deprecated flag and suggestion for the replacement.
5bd2b0f to
e4c3036
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@CHANGELOG.md`:
- Around line 23-29: Update the deprecation line that currently references the
replacements for start-* and end-* to point to the correct new inset utilities:
replace the mention of inline-s-* and inline-e-* with inset-s-* and inset-e-* so
the deprecation reads that start-* and end-* are deprecated in favor of
inset-s-* and inset-e-* (refer to the symbols start-*, end-*, inset-s-*,
inset-e-* to locate and update the text).
In `@packages/tailwindcss/src/compat/legacy-utilities.ts`:
- Around line 127-140: The returned function in handleInset currently checks
candidate.modifier only in the no-value and arbitrary branches, allowing classes
like start-4/foo to slip through; add a top-level guard at the start of the
returned function (inside handleInset's returned arrow) that returns immediately
if candidate.modifier is truthy so modifiers are rejected consistently for
legacy start/end utilities (keep existing negative handling and the rest of the
branches unchanged).
- Around line 149-151: The fraction handling currently only checks
isPositiveInteger on both parts and can accept a zero denominator (e.g., "1/0");
update the block that splits with segment(candidate.value.fraction, '/') to also
detect and reject a zero denominator (check rhs !== '0' or parseInt(rhs,10) !==
0) before computing value = `calc(${candidate.value.fraction} * 100%)`; ensure
the function returns early when rhs is zero so invalid CSS is not emitted
(references: segment, candidate.value.fraction, isPositiveInteger, value).
| - Add `pbs-*`, `pbe-*`, `mbs-*`, `mbe-*`, `scroll-pbs-*`, `scroll-pbe-*`, `scroll-mbs-*`, `scroll-mbe-*`, `border-bs-*`, `border-be-*` utilities for `padding-block-start`, `padding-block-end`, `margin-block-start`, `margin-block-end`, `scroll-padding-block-start`, `scroll-padding-block-end`, `scroll-margin-block-start`, `scroll-margin-block-end`, `border-block-start`, and `border-block-end` ([`#19601`](https://github.com/tailwindlabs/tailwindcss/pull/19601)) | ||
| - Add `inline-*`, `min-inline-*`, `max-inline-*`, `block-*`, `min-block-*`, `max-block-*` utilities for `inline-size`, `min-inline-size`, `max-inline-size`, `block-size`, `min-block-size`, and `max-block-size` ([#19612](https://github.com/tailwindlabs/tailwindcss/pull/19612)) | ||
| - Add `inset-s-*`, `inset-e-*`, `inset-bs-*`, `inset-be-*` utilities for `inset-inline-start`, `inset-inline-end`, `inset-block-start`, and `inset-block-end` ([#19613](https://github.com/tailwindlabs/tailwindcss/pull/19613)) | ||
|
|
||
| ### Deprecated | ||
|
|
||
| - Deprecate `start-*` and `end-*` utilities in favor of `inline-s-*` and `inline-e-*` ([#19613](https://github.com/tailwindlabs/tailwindcss/pull/19613)) |
There was a problem hiding this comment.
Deprecation note should reference inset-s-* / inset-e-*.
Line 29 currently points to inline-s-*/inline-e-*, but the replacements for start-*/end-* are the new inset utilities.
Suggested fix
-- Deprecate `start-*` and `end-*` utilities in favor of `inline-s-*` and `inline-e-*` ([`#19613`](https://github.com/tailwindlabs/tailwindcss/pull/19613))
+- Deprecate `start-*` and `end-*` utilities in favor of `inset-s-*` and `inset-e-*` ([`#19613`](https://github.com/tailwindlabs/tailwindcss/pull/19613))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - Add `pbs-*`, `pbe-*`, `mbs-*`, `mbe-*`, `scroll-pbs-*`, `scroll-pbe-*`, `scroll-mbs-*`, `scroll-mbe-*`, `border-bs-*`, `border-be-*` utilities for `padding-block-start`, `padding-block-end`, `margin-block-start`, `margin-block-end`, `scroll-padding-block-start`, `scroll-padding-block-end`, `scroll-margin-block-start`, `scroll-margin-block-end`, `border-block-start`, and `border-block-end` ([`#19601`](https://github.com/tailwindlabs/tailwindcss/pull/19601)) | |
| - Add `inline-*`, `min-inline-*`, `max-inline-*`, `block-*`, `min-block-*`, `max-block-*` utilities for `inline-size`, `min-inline-size`, `max-inline-size`, `block-size`, `min-block-size`, and `max-block-size` ([#19612](https://github.com/tailwindlabs/tailwindcss/pull/19612)) | |
| - Add `inset-s-*`, `inset-e-*`, `inset-bs-*`, `inset-be-*` utilities for `inset-inline-start`, `inset-inline-end`, `inset-block-start`, and `inset-block-end` ([#19613](https://github.com/tailwindlabs/tailwindcss/pull/19613)) | |
| ### Deprecated | |
| - Deprecate `start-*` and `end-*` utilities in favor of `inline-s-*` and `inline-e-*` ([#19613](https://github.com/tailwindlabs/tailwindcss/pull/19613)) | |
| - Add `pbs-*`, `pbe-*`, `mbs-*`, `mbe-*`, `scroll-pbs-*`, `scroll-pbe-*`, `scroll-mbs-*`, `scroll-mbe-*`, `border-bs-*`, `border-be-*` utilities for `padding-block-start`, `padding-block-end`, `margin-block-start`, `margin-block-end`, `scroll-padding-block-start`, `scroll-padding-block-end`, `scroll-margin-block-start`, `scroll-margin-block-end`, `border-block-start`, and `border-block-end` ([`#19601`](https://github.com/tailwindlabs/tailwindcss/pull/19601)) | |
| - Add `inline-*`, `min-inline-*`, `max-inline-*`, `block-*`, `min-block-*`, `max-block-*` utilities for `inline-size`, `min-inline-size`, `max-inline-size`, `block-size`, `min-block-size`, and `max-block-size` ([`#19612`](https://github.com/tailwindlabs/tailwindcss/pull/19612)) | |
| - Add `inset-s-*`, `inset-e-*`, `inset-bs-*`, `inset-be-*` utilities for `inset-inline-start`, `inset-inline-end`, `inset-block-start`, and `inset-block-end` ([`#19613`](https://github.com/tailwindlabs/tailwindcss/pull/19613)) | |
| ### Deprecated | |
| - Deprecate `start-*` and `end-*` utilities in favor of `inset-s-*` and `inset-e-*` ([`#19613`](https://github.com/tailwindlabs/tailwindcss/pull/19613)) |
🤖 Prompt for AI Agents
In `@CHANGELOG.md` around lines 23 - 29, Update the deprecation line that
currently references the replacements for start-* and end-* to point to the
correct new inset utilities: replace the mention of inline-s-* and inline-e-*
with inset-s-* and inset-e-* so the deprecation reads that start-* and end-* are
deprecated in favor of inset-s-* and inset-e-* (refer to the symbols start-*,
end-*, inset-s-*, inset-e-* to locate and update the text).
| function handleInset({ negative }: { negative: boolean }) { | ||
| return (candidate: Extract<import('../candidate').Candidate, { kind: 'functional' }>) => { | ||
| if (!candidate.value) { | ||
| if (candidate.modifier) return | ||
| let value = designSystem.theme.resolve(null, ['--inset', '--spacing']) | ||
| if (value === null) return | ||
| return [decl(property, negative ? `calc(${value} * -1)` : value)] | ||
| } | ||
|
|
||
| if (candidate.value.kind === 'arbitrary') { | ||
| if (candidate.modifier) return | ||
| let value = candidate.value.value | ||
| return [decl(property, negative ? `calc(${value} * -1)` : value)] | ||
| } |
There was a problem hiding this comment.
Reject modifiers for legacy start/end utilities.
Line 128: modifiers like start-4/foo can still generate CSS because the modifier check only exists in the no-value/arbitrary branches. A top-level guard keeps invalid classes from leaking through and matches the behavior of other functional utilities.
Suggested fix
function handleInset({ negative }: { negative: boolean }) {
return (candidate: Extract<import('../candidate').Candidate, { kind: 'functional' }>) => {
+ if (candidate.modifier) return
if (!candidate.value) {
- if (candidate.modifier) return
let value = designSystem.theme.resolve(null, ['--inset', '--spacing'])
if (value === null) return
return [decl(property, negative ? `calc(${value} * -1)` : value)]
}
if (candidate.value.kind === 'arbitrary') {
- if (candidate.modifier) return
let value = candidate.value.value
return [decl(property, negative ? `calc(${value} * -1)` : value)]
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function handleInset({ negative }: { negative: boolean }) { | |
| return (candidate: Extract<import('../candidate').Candidate, { kind: 'functional' }>) => { | |
| if (!candidate.value) { | |
| if (candidate.modifier) return | |
| let value = designSystem.theme.resolve(null, ['--inset', '--spacing']) | |
| if (value === null) return | |
| return [decl(property, negative ? `calc(${value} * -1)` : value)] | |
| } | |
| if (candidate.value.kind === 'arbitrary') { | |
| if (candidate.modifier) return | |
| let value = candidate.value.value | |
| return [decl(property, negative ? `calc(${value} * -1)` : value)] | |
| } | |
| function handleInset({ negative }: { negative: boolean }) { | |
| return (candidate: Extract<import('../candidate').Candidate, { kind: 'functional' }>) => { | |
| if (candidate.modifier) return | |
| if (!candidate.value) { | |
| let value = designSystem.theme.resolve(null, ['--inset', '--spacing']) | |
| if (value === null) return | |
| return [decl(property, negative ? `calc(${value} * -1)` : value)] | |
| } | |
| if (candidate.value.kind === 'arbitrary') { | |
| let value = candidate.value.value | |
| return [decl(property, negative ? `calc(${value} * -1)` : value)] | |
| } |
🤖 Prompt for AI Agents
In `@packages/tailwindcss/src/compat/legacy-utilities.ts` around lines 127 - 140,
The returned function in handleInset currently checks candidate.modifier only in
the no-value and arbitrary branches, allowing classes like start-4/foo to slip
through; add a top-level guard at the start of the returned function (inside
handleInset's returned arrow) that returns immediately if candidate.modifier is
truthy so modifiers are rejected consistently for legacy start/end utilities
(keep existing negative handling and the rest of the branches unchanged).
| let [lhs, rhs] = segment(candidate.value.fraction, '/') | ||
| if (!isPositiveInteger(lhs) || !isPositiveInteger(rhs)) return | ||
| value = `calc(${candidate.value.fraction} * 100%)` |
There was a problem hiding this comment.
Guard against zero denominators in fractions.
start-1/0 currently passes isPositiveInteger and emits invalid CSS (calc(1/0 * 100%)). Add an explicit zero check.
Suggested fix
- if (!isPositiveInteger(lhs) || !isPositiveInteger(rhs)) return
+ if (!isPositiveInteger(lhs) || !isPositiveInteger(rhs) || Number(rhs) === 0) return🤖 Prompt for AI Agents
In `@packages/tailwindcss/src/compat/legacy-utilities.ts` around lines 149 - 151,
The fraction handling currently only checks isPositiveInteger on both parts and
can accept a zero denominator (e.g., "1/0"); update the block that splits with
segment(candidate.value.fraction, '/') to also detect and reject a zero
denominator (check rhs !== '0' or parseInt(rhs,10) !== 0) before computing value
= `calc(${candidate.value.fraction} * 100%)`; ensure the function returns early
when rhs is zero so invalid CSS is not emitted (references: segment,
candidate.value.fraction, isPositiveInteger, value).
I noticed this when reading the changelog to migrate a project to the new version. I'm not sure whether retroactively changing the changelog is considered acceptable behaviour in this project or whether appending a notice in the next changelog would be better. Funnily this was actually guessed in #19613, though hard to notice among the other many false guesses.
TailwindCSS v4.2.0 changelog: Deprecate start-* and end-* utilities in favor of inset-s-* and inset-e-* utilities tailwindlabs/tailwindcss#19613 Despite the start-0 change to inset-s-0, it seems that Flowbite will still add a left-0 on the main-menu element when reviewing the HTML in the inspector. We only had a single instance of start-* in a template file. The other is just for our test suite.
Summary
This PR adds support for logical inset utilities that map to CSS logical properties:
inset-s→inset-inline-startinset-e→inset-inline-endinset-bs→inset-block-startinset-be→inset-block-endThese utilities complement the existing
inset-x(inline) andinset-y(block) utilities, providing more granular control over positioning in a direction-aware manner. This aligns with CSS logical properties and improves support for internationalization (RTL/LTR languages).Changes
inset-block-startandinset-block-endto the property ordering list to ensure consistent cascade orderingTest plan
All changes are covered by unit tests in
utilities.test.ts:inset-stest: 8 valid classes + 13 invalid class assertionsinset-etest: 8 valid classes + 13 invalid class assertionsinset-bstest: 8 valid classes + 13 invalid class assertionsinset-betest: 8 valid classes + 13 invalid class assertionsEach test verifies correct CSS output generation and proper rejection of malformed utilities.
https://claude.ai/code/session_01JcYXVAMawRuntKatjku1WZ