Skip to content

feat!: replace style-derived values with props - #5114

Merged
Doberjohn merged 10 commits into
callstack:mainfrom
waterim:refactor/remove-style-flatten
Nov 10, 2026
Merged

Doberjohn merged 10 commits into
callstack:mainfrom
waterim:refactor/remove-style-flatten

Conversation

@waterim

@waterim waterim commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

Values read from style with StyleSheet.flatten can't work with useAnimatedStyle, so they move to props, following Surface in #5078. ESLint now bans StyleSheet.flatten in src.

  • Appbar: backgroundColor and radius props, inset-aware height
  • Avatar: backgroundColor prop, contrast still derives from it
  • Button: iconSize prop, textColor also colors the icon
  • Card: borderColor prop, Card.Cover radius goes on the container
  • BottomNavigation.Bar: background moves to the outer view
  • Tooltip: anchors to its wrapper

Related issue

Reanimated migration for v6.

Screenshots / Videos

Test plan

Check Appbar, Avatar, Button and Card in the example app.

BREAKING CHANGE: style no longer sets Appbar's background or radius, Avatar's background or Card's outline color, use the new props. Appbar's height includes safeAreaInsets. Use iconSize and textColor for Button's icon, and a View around Tooltip for positioning.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved moderate issues remain in Avatar background-style precedence and Tooltip ref forwarding and measurement fallback.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Refactors v6 components to remove style flattening and improve Reanimated animated-style support.

Changes:

  • Replaces style-derived values with explicit component props.
  • Updates Tooltip measurement and ref handling.
  • Updates tests, snapshots, examples, linting, and migration documentation.
File summaries
File Summary
src/utils/splitStyles.ts Removes obsolete utility.
src/utils/__tests__/splitStyles.test.ts Removes obsolete tests.
src/components/Tooltip/utils.ts Supports direct measurement.
src/components/Tooltip/Tooltip.tsx Adds ref forwarding and measurement.
src/components/Surface.tsx Exports shared visual prop types.
src/components/DataTable/DataTablePagination.tsx Uses trailing icon positioning.
src/components/Card/utils.tsx Removes style-derived radius handling.
src/components/Card/CardCover.tsx Applies clipping radius to the container.
src/components/Card/Card.tsx Uses themed outline color.
src/components/Button/utils.tsx Uses static border-radius keys.
src/components/Button/Button.tsx Adds explicit icon positioning and styling.
src/components/BottomNavigation/BottomNavigationBar.tsx Moves background styling to the outer bar.
src/components/Avatar/AvatarText.tsx Adds an explicit background color prop.
src/components/Avatar/AvatarImage.tsx Adds an explicit background color prop.
src/components/Avatar/AvatarIcon.tsx Adds an explicit background color prop.
src/components/Appbar/utils.ts Removes border extraction.
src/components/Appbar/AppbarHeader.tsx Updates header styling and safe-area handling.
src/components/Appbar/Appbar.tsx Adds visual props and inset-aware sizing.
src/components/__tests__/Tooltip.test.tsx Tests ref forwarding.
src/components/__tests__/Card/Card.test.tsx Updates Card behavior tests.
src/components/__tests__/Card/__snapshots__/Card.test.tsx.snap Updates Card snapshots.
src/components/__tests__/Button.test.tsx Tests icon positioning.
src/components/__tests__/Avatar.test.tsx Uses background color props.
src/components/__tests__/Appbar/Appbar.test.tsx Tests Appbar visual props.
src/components/__tests__/Appbar/__snapshots__/Appbar.test.tsx.snap Updates Appbar snapshots.
src/components/__tests__/__snapshots__/DataTable.test.tsx.snap Updates pagination snapshot.
src/components/__tests__/__snapshots__/Button.test.tsx.snap Updates Button snapshot.
src/components/__tests__/__snapshots__/BottomNavigation.test.tsx.snap Updates navigation snapshots.
src/components/__tests__/__snapshots__/Avatar.test.tsx.snap Updates Avatar snapshots.
example/src/Examples/ButtonExample.tsx Migrates trailing icons.
example/src/Examples/AvatarExample.tsx Migrates background colors.
example/src/Examples/AppbarExample.tsx Migrates Appbar background colors.
eslint.config.mjs Bans source StyleSheet.flatten usage.
docs/6.x/docs/guides/migration.md Documents breaking API changes.
Review details

Suppressed comments (3)

src/components/Appbar/Appbar.tsx:226

  • This changes the Appbar layout contract by adding top and bottom safe-area values to the computed height, but the Appbar tests do not assert the resulting height for either Appbar or Appbar.Header (including the custom header-height case described in the migration guide). Add a regression case covering the inset calculation so a future refactor cannot drop or double-count these values.
  // The safe area insets are applied as padding, so they need to be included in the height
  const height =
    modeAppbarHeight[mode] +
    (safeAreaInsets?.top ?? 0) +
    (safeAreaInsets?.bottom ?? 0);

src/components/Tooltip/Tooltip.tsx:201

  • The wrapper fallback only runs when the ref is null. A wrapped class or forwardRef component can yield a non-null custom instance that does not implement React Native's measure, so this calls an undefined method and the tooltip crashes instead of falling back as documented. Check that the ref exposes measure before selecting it; otherwise use childrenWrapperRef.current.
    const target = childRef.current ?? childrenWrapperRef.current;

    target?.measure((_x, _y, width, height, pageX, pageY) => {

src/components/Tooltip/Tooltip.tsx:108

  • The ref merge only reads children.props.ref, but React 18 stores an element ref on the element rather than in props. Since this package declares an unrestricted React peer, wrapping a child with a ref under React 18 will replace the user's ref with setChildRef and never invoke the original ref, contrary to the forwarding behavior documented by this change. Use a React-version-compatible ref merge and add coverage for the supported React 18 path.
  const childOwnRef = isValidChild
    ? // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
      (children.props as TooltipChildProps).ref
    : undefined;

  // Keep a ref to the wrapped element so it can be measured directly,
  // while still forwarding the ref passed by the user (if any)
  const setChildRef = React.useCallback(
    (node: View | null) => {
      childRef.current = node;

      if (typeof childOwnRef === 'function') {
        childOwnRef(node);
      } else if (childOwnRef) {
        childOwnRef.current = node;
  • Files reviewed: 34/34 changed files
  • Comments generated: 4
  • Review effort level: Lite (auto)

Note

Copilot is running an experiment and ran this review at Lite.


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/components/Avatar/AvatarIcon.tsx Resolved
Comment thread src/components/Avatar/AvatarImage.tsx Resolved
Comment thread src/components/Avatar/AvatarText.tsx Resolved
Comment thread src/components/Appbar/Appbar.tsx Resolved

@satya164 satya164 left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review by Claude

See the inline comments. (via Claude, verify if it makes sense)

Comment thread src/components/Tooltip/Tooltip.tsx Resolved
Comment thread src/components/Button/utils.tsx Resolved
@Doberjohn Doberjohn added this to the 6.0.0 milestone Oct 30, 2026
@Doberjohn Doberjohn added the needs pickup Work that has been started but needs someone to pick it up and continue. label Nov 5, 2026
@lukewalczak lukewalczak self-assigned this Nov 5, 2026
@Doberjohn Doberjohn removed the needs pickup Work that has been started but needs someone to pick it up and continue. label Nov 5, 2026
…-flatten

# Conflicts:
#	src/components/Button/utils.tsx
#	src/components/Card/Card.tsx
#	src/components/Card/CardCover.tsx
#	src/components/Tooltip/utils.ts
@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Found potential problems with the pull request:

  • Screenshot or video evidence is missing. Make sure to include one if it affects the UI.

@Doberjohn
Doberjohn requested a review from lukewalczak November 6, 2026 12:25
# Conflicts:
#	docs/6.x/docs/guides/migration.md
#	example/src/Examples/ButtonExample.tsx
#	src/components/Button/Button.tsx
#	src/components/Button/utils.tsx
#	src/components/__tests__/Button.test.tsx
#	src/components/__tests__/__snapshots__/Banner.test.tsx.snap
#	src/components/__tests__/__snapshots__/Button.test.tsx.snap
#	src/components/__tests__/__snapshots__/DataTable.test.tsx.snap
#	src/components/__tests__/__snapshots__/Menu.test.tsx.snap
#	src/components/__tests__/__snapshots__/Snackbar.test.tsx.snap
@Doberjohn

Copy link
Copy Markdown
Collaborator

Thanks for the review, @lukewalczak. First pass, blockers only.

  1. Title. This changes what apps have to do: style.backgroundColor on Appbar, Appbar.Header and the Avatar components, the border radius in Appbar's style, style.borderColor on an outlined Card, positioning styles on a Tooltip's child and labelStyle.color for Button's icon are no longer applied, and Appbar's height now includes safeAreaInsets. Please type the title by what users see and mark it breaking, for example feat!: replace style-derived values with props.
  2. Squash message. Please add a BREAKING CHANGE: paragraph to the description with one sentence for users, for example:

BREAKING CHANGE: Appbar, Appbar.Header, Avatar and Card no longer read the background color, border radius or outline color from style, so use the new backgroundColor, border radius and borderColor props instead; color Button's icon with textColor, and put positioning styles on a View around Tooltip.

@Doberjohn Doberjohn left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@lukewalczak I posted the two blockers above, the following five items are worth fixing here, but are not introduced by this PR neither blocking the merge. Let's see what can be fixed now that we touched their files.

  1. Description. It's out of date, and the squash message is built from it. It still mentions iconPosition (that came with #5097), DataTable.Pagination, snapshots that are no longer in the diff, the closed #4943 and Tooltip ref code that's gone. It should also be under 1000 characters and have before and after screenshots, since the Appbar height and the Card.Cover clipping change visibly.
  2. Appbar.Header height (migration.md:302). style.height is now the total height, not the content row, and nothing warns about it: height: 80 with a 59pt inset leaves a 21pt row. Please add a before and after, e.g. height: 80 + top with useSafeAreaInsets(), plus a line in the style JSDoc.
  3. Avatar style type (AvatarText.tsx:18, AvatarIcon.tsx:13, AvatarImage.tsx:21). ViewProps & {...} still accepts backgroundColor, so the guide's own "before" snippet compiles and silently loses its color. Omit<ViewProps, 'style'> & {...}, as Card and Button do it, makes it a type error.
  4. Tooltip test (Tooltip.test.tsx:165). The new test also passes on main, so the real change, which is that the tooltip ignores the child's positioning and anchors to the wrapper, isn't tested. A test with an absolutely positioned child would cover it.
  5. Migration guide, Style section (migration.md:74). The new list repeats #5097's iconPosition and leaves out textColor for the icon color and the wrapping View for Tooltip. In the Button section, the labelStyle color and fontSize bullets need code changes, so they don't belong under "These need no code change".

@lukewalczak lukewalczak changed the title refactor: remove splitStyles and StyleSheet.flatten feat!: replace style-derived values with props Nov 9, 2026
@Doberjohn
Doberjohn removed the request for review from lukewalczak November 10, 2026 09:46
@Doberjohn

Copy link
Copy Markdown
Collaborator

Checked on Android and iOS (Appbar, Avatar, Button, Card, Bottom Navigation, Tooltip, Data Table): all good. Thanks, @lukewalczak!

@Doberjohn
Doberjohn merged commit af124d6 into callstack:main Nov 10, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants