Skip to content

Add safe area utilities for mobile app usage#19921

Open
riderx wants to merge 4 commits into
tailwindlabs:mainfrom
riderx:codex/add-safe-area-utilities
Open

Add safe area utilities for mobile app usage#19921
riderx wants to merge 4 commits into
tailwindlabs:mainfrom
riderx:codex/add-safe-area-utilities

Conversation

@riderx
Copy link
Copy Markdown

@riderx riderx commented Apr 9, 2026

Summary

  • add built-in safe-area utilities for inset, margin, padding, scroll margin, and scroll padding
  • add safe and safe-none control utilities so safe-area values can be scoped or reset via CSS custom properties
  • add viewport-safe height utilities for screen, svh, lvh, and dvh

Why

  • Tailwind users currently need external plugins or ad hoc utilities for safe-area handling on notch and home-indicator devices
  • this brings the most common safe-area patterns into core with the existing spacing and fraction syntax

Impact

  • users can write classes like pt-safe-4, -top-safe-px, scroll-p-safe, and h-dvh-safe
  • safe-none style utilities let component subtrees opt out without redefining the spacing helpers

Validation

  • pnpm exec vitest run packages/tailwindcss/src/utilities.test.ts packages/tailwindcss/src/intellisense.test.ts packages/tailwindcss/src/candidate.test.ts packages/tailwindcss/src/prefix.test.ts

@riderx riderx requested a review from a team as a code owner April 9, 2026 13:11
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 51763ed4f5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tailwindcss/src/utilities.ts Outdated
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 9, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7d90e3a2-27f7-4378-8fe3-6907de2ebcdd

📥 Commits

Reviewing files that changed from the base of the PR and between 132ec1a and 893fe55.

⛔ Files ignored due to path filters (1)
  • packages/tailwindcss/src/__snapshots__/intellisense.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (2)
  • packages/tailwindcss/src/utilities.test.ts
  • packages/tailwindcss/src/utilities.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/tailwindcss/src/utilities.test.ts
  • packages/tailwindcss/src/utilities.ts

Walkthrough

Adds safe-area helpers and registers utilities that expose safe-area CSS variables and apply them to inset, margin, padding, scroll-margin, scroll-padding, and viewport-height height utilities. Implements a resolver for spacing-like functional values (theme keys, arbitrary values, fractions, multipliers), emits static safe* and safe*-none variables, and wires negative/fractional variants where applicable. Adds tests validating generated CSS snapshots and runtime class handling for both spacing-based and viewport-height safe-area variants.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main change—adding safe area utilities for mobile app usage—which matches the primary focus of the changeset.
Description check ✅ Passed The description is directly related to the changeset, providing clear context on safe-area utilities, motivation, and validation approach for the implementation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/tailwindcss/src/utilities.ts (1)

598-625: Extract the spacing resolution path instead of copying it.

resolveSpacingUtilityValue now reimplements the same theme/bare/fraction resolution that spacingUtility already owns. Any future change to spacing candidate handling will have to be updated in both places, which is an easy drift point for core utilities.

Also applies to: 627-666

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/tailwindcss/src/utilities.ts` around lines 598 - 625,
resolveSpacingUtilityValue duplicates the spacing resolution logic already
implemented by spacingUtility; extract that shared resolution path into a single
helper (e.g., resolveSpacingCandidate or reuse spacingUtility's internal
resolver) and have resolveSpacingUtilityValue call it instead of reimplementing
theme.resolve/fraction/bare/multiplier logic. Refactor so the new helper accepts
the same inputs used here (candidate, themeKeys, supportsFractions) and
internally uses theme.resolve, segment/isPositiveInteger, and
isValidSpacingMultiplier to produce the final value; update both
resolveSpacingUtilityValue and the original spacingUtility call sites to use the
shared helper to avoid duplication and future drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/tailwindcss/src/utilities.test.ts`:
- Around line 319-437: The test currently covers 'safe', 'safe-x-none', and
'safe-b' but misses the primary control utility 'safe-none'; update the
candidate list in the utilities.test.ts test (the array alongside 'safe',
'safe-x-none', 'safe-b') to include 'safe-none' so the snapshot generation also
asserts the .safe-none rule (alongside existing .safe, .safe-x-none, .safe-b)
that sets all --tw-safe-area-* variables to 0; run the test to refresh the
inline snapshot if necessary.

In `@packages/tailwindcss/src/utilities.ts`:
- Around line 775-808: The safe-axis utilities currently use physical sides
(left/right/top/bottom) which breaks logical-axis consistency; update the
entries passed to safeAreaSpacingUtility so that 'inset-x-safe' and any other
"*-x-safe" use the inline-axis pair (inline-start, inline-end) and
'inset-y-safe' and "*-y-safe" use the block-axis pair (block-start, block-end),
and change single-side safe entries (e.g., 'left-safe', 'right-safe',
'top-safe', 'bottom-safe') to their logical equivalents where appropriate
(inline-start/inline-end/block-start/block-end) so the safe variants match
Tailwind’s logical-axis semantics; locate and modify the arrays in the for-loop
that calls safeAreaSpacingUtility (names like 'inset-safe', 'inset-x-safe',
'inset-y-safe', 'top-safe', 'right-safe', 'bottom-safe', 'left-safe') and mirror
the same logical-axis replacement in the other similar blocks noted in the
review.

---

Nitpick comments:
In `@packages/tailwindcss/src/utilities.ts`:
- Around line 598-625: resolveSpacingUtilityValue duplicates the spacing
resolution logic already implemented by spacingUtility; extract that shared
resolution path into a single helper (e.g., resolveSpacingCandidate or reuse
spacingUtility's internal resolver) and have resolveSpacingUtilityValue call it
instead of reimplementing theme.resolve/fraction/bare/multiplier logic. Refactor
so the new helper accepts the same inputs used here (candidate, themeKeys,
supportsFractions) and internally uses theme.resolve, segment/isPositiveInteger,
and isValidSpacingMultiplier to produce the final value; update both
resolveSpacingUtilityValue and the original spacingUtility call sites to use the
shared helper to avoid duplication and future drift.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6749e52a-559b-41ef-9a78-aab6eaa3f876

📥 Commits

Reviewing files that changed from the base of the PR and between aad6017 and 51763ed.

⛔ Files ignored due to path filters (1)
  • packages/tailwindcss/src/__snapshots__/intellisense.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (2)
  • packages/tailwindcss/src/utilities.test.ts
  • packages/tailwindcss/src/utilities.ts

Comment thread packages/tailwindcss/src/utilities.test.ts
Comment thread packages/tailwindcss/src/utilities.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 246f79e9ce

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/tailwindcss/src/utilities.ts
@riderx riderx changed the title [codex] Add safe area utilities Add safe area utilities for mobile app usage Apr 10, 2026
@riderx
Copy link
Copy Markdown
Author

riderx commented Apr 16, 2026

hey @adamwathan i would love your review on that one, it's easy win

@riderx
Copy link
Copy Markdown
Author

riderx commented May 12, 2026

made the missing doc website PR tailwindlabs/tailwindcss.com#2491

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 16, 2026

Confidence Score: 4/5

The implementation is functionally correct and well-tested; the main concerns are a duplicated value-resolution helper and a missing set of width viewport utilities rather than any broken behaviour.

The CSS custom property fallback mechanism works correctly, all tested class combinations produce the expected output, and invalid combinations are properly rejected. The duplicated resolveSpacingUtilityValue logic is a future maintenance concern rather than a current defect.

The resolveSpacingUtilityValue function in utilities.ts and the absence of w-*-safe width viewport utilities are the areas worth a second look.

Reviews (1): Last reviewed commit: "Merge branch 'main' into codex/add-safe-..." | Re-trigger Greptile

Comment on lines +601 to +631
function resolveSpacingUtilityValue(
candidate: Extract<Candidate, { kind: 'functional' }>,
themeKeys: ThemeKey[],
supportsFractions = false,
) {
if (!candidate.value) return null

if (candidate.value.kind === 'arbitrary') {
if (candidate.modifier) return null
return candidate.value.value
}

if (candidate.modifier && (!candidate.value.fraction || !supportsFractions)) return null

let value = theme.resolve(candidate.value.fraction ?? candidate.value.value, themeKeys)

if (value === null && supportsFractions && candidate.value.fraction) {
let [lhs, rhs] = segment(candidate.value.fraction, '/')
if (!isPositiveInteger(lhs) || !isPositiveInteger(rhs)) return null
value = `calc(${lhs} / ${rhs} * 100%)`
}

if (value === null) {
let multiplier = theme.resolve(null, ['--spacing'])
if (multiplier && isValidSpacingMultiplier(candidate.value.value)) {
value = `calc(${multiplier} * ${candidate.value.value})`
}
}

return value
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Duplicated value-resolution logic

resolveSpacingUtilityValue re-implements the same theme/spacing resolution that already lives inside the handleFunctionalUtility closure of functionalUtility. If the core resolution path is ever extended (e.g., new CSS math keywords, new spacing syntax in isValidSpacingMultiplier), this copy would need to be updated separately, creating a silent drift risk. Consider extracting the shared resolution logic from functionalUtility into a standalone helper that both paths can call.

Comment on lines +1254 to +1269
for (let [name, property] of [
['h', 'height'],
['min-h', 'min-height'],
['max-h', 'max-height'],
] as const) {
for (let [viewport, value] of [
['screen', '100vh'],
['svh', '100svh'],
['lvh', '100lvh'],
['dvh', '100dvh'],
] as const) {
staticUtility(`${name}-${viewport}-safe`, [
[property, `calc(${value} - (${safeAreaValue('top')} + ${safeAreaValue('bottom')}))`],
])
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing width safe-area viewport utilities

The existing framework already ships w-dvw, w-svw, w-lvw, and w-screen (see lines 1225 and 1247). The PR adds h-*-safe height variants but no equivalent w-dvw-safe, w-svw-safe, w-lvw-safe, or w-screen-safe utilities. On landscape-oriented iOS/iPadOS devices the safe areas appear on the left and right edges, so calc(100dvw - (safe-left + safe-right)) is a common need. Without these utilities users fall back to ad-hoc workarounds — the same situation this PR is trying to solve.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant