Skip to content

Appbar MD3 Expressive improvements - #5075

Open
pawelkata wants to merge 20 commits into
callstack:mainfrom
pawelkata:appbar-improvements
Open

Appbar MD3 Expressive improvements#5075
pawelkata wants to merge 20 commits into
callstack:mainfrom
pawelkata:appbar-improvements

Conversation

@pawelkata

@pawelkata pawelkata commented Aug 25, 2026

Copy link
Copy Markdown

Motivation

Replace the legacy compound Appbar API with a prop-driven Material 3 implementation. This adds small, flexible, and search variants; filled actions; scroll-aware colors; safe-area handling; and improved accessibility. Documentation, examples, and downstream usages are updated accordingly.

Related issue

Related issue: #4934

Also based on comments on Appbar from here: #4949 (comment) .

Caveats

The search bar is dropped in verbatim as it needs a separate pool of work to properly handle the "search" variant. Deliberately skipped any animations related work at this stage due to lack of testing device and the PR already being quite big. Can be tackled at a later stage.

NOTE: The PR drops support for the bottom app bar variant, which is now - according to the official docs - part of the Toolbars.

Test plan

  • Run yarn test src/components/__tests__/Appbar.
  • Run yarn lint and yarn typecheck.
  • Verify all Appbar variants in the example app, including search, scrolling, centered headlines, actions, and safe areas.
  • Verify accessibility labels, element order, disabled states, and headline interactions with a screen reader.

/** Headline and subtitle alignment. */
headlineAlignment?: AppbarHeadlineAlignment;
/** Trailing actions. */
trailingActions?: AppbarTrailingActions;

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.

trailingActions takes config objects, so nothing can wrap an action any more. The examples in this PR show the cost - TooltipExample drops four Tooltips, and MenuExample, BottomNavigationExample and react-navigation.md all switch Menu from an element anchor to raw coordinates. Composition is what #4954 set out to fix.

contentStyle?: never;
};

export type Props = AppbarBaseProps &

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.

Running the docs generator produces four props for Appbar - headlineAlignment, isScrolled, testID, trailingActions - with no types and no descriptions. headline, variant, leadingButton, searchBar and the rest are missing because react-docgen can't read the union, so the published prop table is effectively empty.

/** Override for the automatic top safe-area inset. */
statusBarHeight?: number;
/** Safe-area inset overrides. Unspecified values use the detected insets. */
safeAreaInsets?: {

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.

safeAreaInsets.bottom is gone and children is no longer accepted, so the documented bottom app bar can't be built any more. Deliberate? The spec's replacement is the docked toolbar, which isn't here either.


const styles = StyleSheet.create({
button: {
margin: 4,

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.

margin: 4 on every button plus the bar's own paddingHorizontal: 4 gives 8dp to the edge and 8dp between actions. md.comp.app-bar.leading-space and trailing-space are 4dp and icon-button-space is 0dp, so the icons land 20dp from the edge instead of 16dp.

elevation={elevation}
ref={ref}
testID={`${testID}-root-layer`}
elevation={0}

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.

md.comp.app-bar.on-scroll.container.elevation resolves to level2, but elevation is pinned to 0 - isScrolled only changes the container colour. Intentional?

);
const resolvedSearchInputStyle = React.useMemo(
() => [
searchBar?.inputStyle,

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.

searchBar.inputStyle comes before styles.searchInput, so a consumer can't override textAlign: 'center'. Put the user's style last. Why is the search text centred at all?

variant: Exclude<AppbarHeadlineVariant, 'small'>;
};

type AppbarBaseProps = Omit<

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.

Why omit role, aria-label and accessible? Consumers can't label the app bar region any more.

<View
testID={`${testID}-headline-image`}
aria-hidden
importantForAccessibility="no-hide-descendants"

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.

Drop importantForAccessibility - aria-hidden already covers it, and #5005 migrated the library off the legacy props.

);
};

const hasEqualButtonProps = (

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.

Remove hasEqualButtonProps. It's a hand-rolled shallow compare with two as unknown as casts and no measurement behind it.

style,
subtitle,
subtitleProps,
testID = 'appbar',

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.

Drop the default testID.

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.

2 participants