@dloizides/ui-nav 1.27.0 → 1.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.28.0
4
+
5
+ - INVOICE-5 "Edit invoices before sending, shared background and menu hover" (AC-UI-3, AC-UI-6): `AppShell` draws `theme.backdrop` (the `@dloizides/theme-web` 1.5.0 `resolveBackdrop` shape, typed here as `AppShellBackdrop`): its colour and web image on the root, and for `horizon` a brand band behind header and nav that the content overlaps by `overlap.compact` (< 768 px) or `overlap.wide`. No `theme.backdrop` keeps today's flat background.
6
+
3
7
  ## 1.27.0
4
8
 
5
9
  - INVOICE-5 "Edit invoices before sending, shared background and menu hover" (AC-UI-2, AC-UI-6): `NavBar` takes `hoverFeedback` (`NavHoverFeedback`: `default`, `tint-pill`, `underline`, `lift`, `sliding-highlight`); the new phone `BottomTabBar` takes `tabFeedback` (`TabFeedback`: `default`, `indicator-pill`, `dim-spring`, `sliding-bar`, `icon-lift`). Timings come from the `@dloizides/ui-motion` tokens (<= 300 ms); reduced motion keeps every option still. `default` keeps today's look, so existing apps do not change.
package/dist/index.d.mts CHANGED
@@ -398,8 +398,7 @@ declare enum NavHoverFeedback {
398
398
  *
399
399
  * Contract discipline (same as the rest of `@dloizides/ui-nav`): NO FM, router,
400
400
  * icon set, or store imports. Labels are pre-localized strings, icons are render
401
- * slots, and every colour is read from the theme. This is purely additive —
402
- * `Sidebar`/`Topbar` are unchanged and the new overflow labels are optional.
401
+ * slots, and every colour is read from the theme.
403
402
  */
404
403
 
405
404
  interface NavBarProps {
@@ -463,6 +462,15 @@ interface NavBarProps {
463
462
  */
464
463
  declare const NavBar: ({ barTheme, ...props }: NavBarProps) => React.ReactElement;
465
464
 
465
+ interface BrandHomeLinkProps {
466
+ route: string;
467
+ label: string;
468
+ onNavigate: (route: string) => void;
469
+ children: React.ReactNode;
470
+ }
471
+ /** The app logo as a link to the app's home route, for `NavBar` and apps with their own header. */
472
+ declare const BrandHomeLink: ({ route, label, onNavigate, children }: BrandHomeLinkProps) => React.ReactElement;
473
+
466
474
  /**
467
475
  * `Nav` — ONE component that renders EITHER the horizontal top bar (`NavBar`) or the
468
476
  * vertical rail (`Sidebar`) from the same `NavItem[]`, chosen by a single
@@ -1085,6 +1093,21 @@ interface NavOverflowMenuProps {
1085
1093
  }
1086
1094
  declare const NavOverflowMenu: ({ items, pathname, onNavigate, colors, label, hint, testID, variant, }: NavOverflowMenuProps) => React.ReactElement;
1087
1095
 
1096
+ interface AppShellBackdropBand {
1097
+ color: string;
1098
+ text: string;
1099
+ overlap: {
1100
+ compact: number;
1101
+ wide: number;
1102
+ };
1103
+ }
1104
+ interface AppShellBackdrop {
1105
+ color: string;
1106
+ image: string | null;
1107
+ imageSize: string | null;
1108
+ band: AppShellBackdropBand | null;
1109
+ }
1110
+
1088
1111
  /**
1089
1112
  * Default testIDs for `@dloizides/ui-nav`. Kept as a small central map (mirrors
1090
1113
  * `@dloizides/ui-layout`'s `LAYOUT_TEST_IDS`) so the app-agnostic chrome exposes
@@ -1111,6 +1134,7 @@ declare const NAV_TEST_IDS: {
1111
1134
  readonly pillNavMenu: "pill-nav-menu";
1112
1135
  /** the optional middle slot of `Topbar` (rendered only when `center` is passed). */
1113
1136
  readonly topbarCenter: "topbar-center";
1137
+ readonly brandHome: "nav-brand-home";
1114
1138
  };
1115
1139
  /** Suffixes appended to an `AppShell`'s required `testID` to name its regions. */
1116
1140
  declare const APP_SHELL_SUFFIX: {
@@ -1132,6 +1156,7 @@ declare const APP_SHELL_SUFFIX: {
1132
1156
  readonly loading: "-loading";
1133
1157
  readonly error: "-error";
1134
1158
  readonly forbidden: "-forbidden";
1159
+ readonly band: "-backdrop-band";
1135
1160
  };
1136
1161
 
1137
1162
  /**
@@ -1628,4 +1653,4 @@ interface BottomTabBarProps {
1628
1653
  /** Phone tab bar; `tabFeedback` picks the press feedback, `Default` adds no motion. */
1629
1654
  declare const BottomTabBar: ({ items, pathname, onNavigate, tabFeedback, navigateHint, regionLabel, containerStyle, testID, }: BottomTabBarProps) => React.ReactElement;
1630
1655
 
1631
- export { ACTIVE_BORDER_RADIUS, ACTIVE_TINT_OPACITY, APP_SHELL_SUFFIX, type AccountPlan, type AccountState, AppShell, type AppShellProps, type AppShellWidth, BASE_INDENT, BottomTabBar, type BottomTabBarProps, CHEVRON_ICON_SIZE, CollapsedRail, type CollapsedRailProps, type CollapsedRailRange, type CommandItem, CommandPalette, type CommandPaletteLabels, type CommandPaletteProps, CommandPaletteTrigger, type CommandPaletteTriggerProps, DEFAULT_COLLAPSED_RAIL_MAX, DEFAULT_SEARCH_BREAKPOINT, DarkModeControl, type DarkModeControlProps, type DarkModeOption, type DarkModeVariant, HOVER_TINT_OPACITY, NAV_ICON_SIZE, NAV_LINK_GAP, NAV_ROW_RADIUS, NAV_TEST_IDS, Nav, NavBar, type NavBarProps, NavExpandableItem, type NavExpandableItemProps, NavHoverFeedback, type NavItem, type NavOrientation, NavOverflowMenu, type NavOverflowMenuProps, type NavProps, NavShell, type NavShellCollapsedRail, type NavShellLayout, type NavShellProps, type NavShellSideRail, type NavShellTopBar, type NavUser, PillNav, type PillNavProps, PillNavVariant, RAIL_FULL_BREAKPOINT, type RailMode, type ResolveRailModeArgs, type SearchAffordance, type ShellMessage, Sidebar, SidebarChevron, type SidebarChevronProps, type SidebarPaletteTriggerConfig, type SidebarProps, SidebarSearch, type SidebarSearchConfig, type SidebarSearchLabels, type SidebarSearchProps, TabFeedback, Topbar, type TopbarAction, type TopbarProps, type TopbarUser, accessibleNavItems, collapsedRailStyles, darkModeStyles, expandableStyles, filterCommands, filterNavItems, isFilterActive, isRouteActive, navStyles, pillNavStyles, resolveActiveRoute, resolveContentMaxWidth, resolvePillMenuLabel, resolveRailMode, resolveSearchAffordance, roleRoutesToNavItems, searchTokens, useCommandPaletteHotkey, useContentMaxWidth };
1656
+ export { ACTIVE_BORDER_RADIUS, ACTIVE_TINT_OPACITY, APP_SHELL_SUFFIX, type AccountPlan, type AccountState, AppShell, type AppShellBackdrop, type AppShellBackdropBand, type AppShellProps, type AppShellWidth, BASE_INDENT, BottomTabBar, type BottomTabBarProps, BrandHomeLink, type BrandHomeLinkProps, CHEVRON_ICON_SIZE, CollapsedRail, type CollapsedRailProps, type CollapsedRailRange, type CommandItem, CommandPalette, type CommandPaletteLabels, type CommandPaletteProps, CommandPaletteTrigger, type CommandPaletteTriggerProps, DEFAULT_COLLAPSED_RAIL_MAX, DEFAULT_SEARCH_BREAKPOINT, DarkModeControl, type DarkModeControlProps, type DarkModeOption, type DarkModeVariant, HOVER_TINT_OPACITY, NAV_ICON_SIZE, NAV_LINK_GAP, NAV_ROW_RADIUS, NAV_TEST_IDS, Nav, NavBar, type NavBarProps, NavExpandableItem, type NavExpandableItemProps, NavHoverFeedback, type NavItem, type NavOrientation, NavOverflowMenu, type NavOverflowMenuProps, type NavProps, NavShell, type NavShellCollapsedRail, type NavShellLayout, type NavShellProps, type NavShellSideRail, type NavShellTopBar, type NavUser, PillNav, type PillNavProps, PillNavVariant, RAIL_FULL_BREAKPOINT, type RailMode, type ResolveRailModeArgs, type SearchAffordance, type ShellMessage, Sidebar, SidebarChevron, type SidebarChevronProps, type SidebarPaletteTriggerConfig, type SidebarProps, SidebarSearch, type SidebarSearchConfig, type SidebarSearchLabels, type SidebarSearchProps, TabFeedback, Topbar, type TopbarAction, type TopbarProps, type TopbarUser, accessibleNavItems, collapsedRailStyles, darkModeStyles, expandableStyles, filterCommands, filterNavItems, isFilterActive, isRouteActive, navStyles, pillNavStyles, resolveActiveRoute, resolveContentMaxWidth, resolvePillMenuLabel, resolveRailMode, resolveSearchAffordance, roleRoutesToNavItems, searchTokens, useCommandPaletteHotkey, useContentMaxWidth };
package/dist/index.d.ts CHANGED
@@ -398,8 +398,7 @@ declare enum NavHoverFeedback {
398
398
  *
399
399
  * Contract discipline (same as the rest of `@dloizides/ui-nav`): NO FM, router,
400
400
  * icon set, or store imports. Labels are pre-localized strings, icons are render
401
- * slots, and every colour is read from the theme. This is purely additive —
402
- * `Sidebar`/`Topbar` are unchanged and the new overflow labels are optional.
401
+ * slots, and every colour is read from the theme.
403
402
  */
404
403
 
405
404
  interface NavBarProps {
@@ -463,6 +462,15 @@ interface NavBarProps {
463
462
  */
464
463
  declare const NavBar: ({ barTheme, ...props }: NavBarProps) => React.ReactElement;
465
464
 
465
+ interface BrandHomeLinkProps {
466
+ route: string;
467
+ label: string;
468
+ onNavigate: (route: string) => void;
469
+ children: React.ReactNode;
470
+ }
471
+ /** The app logo as a link to the app's home route, for `NavBar` and apps with their own header. */
472
+ declare const BrandHomeLink: ({ route, label, onNavigate, children }: BrandHomeLinkProps) => React.ReactElement;
473
+
466
474
  /**
467
475
  * `Nav` — ONE component that renders EITHER the horizontal top bar (`NavBar`) or the
468
476
  * vertical rail (`Sidebar`) from the same `NavItem[]`, chosen by a single
@@ -1085,6 +1093,21 @@ interface NavOverflowMenuProps {
1085
1093
  }
1086
1094
  declare const NavOverflowMenu: ({ items, pathname, onNavigate, colors, label, hint, testID, variant, }: NavOverflowMenuProps) => React.ReactElement;
1087
1095
 
1096
+ interface AppShellBackdropBand {
1097
+ color: string;
1098
+ text: string;
1099
+ overlap: {
1100
+ compact: number;
1101
+ wide: number;
1102
+ };
1103
+ }
1104
+ interface AppShellBackdrop {
1105
+ color: string;
1106
+ image: string | null;
1107
+ imageSize: string | null;
1108
+ band: AppShellBackdropBand | null;
1109
+ }
1110
+
1088
1111
  /**
1089
1112
  * Default testIDs for `@dloizides/ui-nav`. Kept as a small central map (mirrors
1090
1113
  * `@dloizides/ui-layout`'s `LAYOUT_TEST_IDS`) so the app-agnostic chrome exposes
@@ -1111,6 +1134,7 @@ declare const NAV_TEST_IDS: {
1111
1134
  readonly pillNavMenu: "pill-nav-menu";
1112
1135
  /** the optional middle slot of `Topbar` (rendered only when `center` is passed). */
1113
1136
  readonly topbarCenter: "topbar-center";
1137
+ readonly brandHome: "nav-brand-home";
1114
1138
  };
1115
1139
  /** Suffixes appended to an `AppShell`'s required `testID` to name its regions. */
1116
1140
  declare const APP_SHELL_SUFFIX: {
@@ -1132,6 +1156,7 @@ declare const APP_SHELL_SUFFIX: {
1132
1156
  readonly loading: "-loading";
1133
1157
  readonly error: "-error";
1134
1158
  readonly forbidden: "-forbidden";
1159
+ readonly band: "-backdrop-band";
1135
1160
  };
1136
1161
 
1137
1162
  /**
@@ -1628,4 +1653,4 @@ interface BottomTabBarProps {
1628
1653
  /** Phone tab bar; `tabFeedback` picks the press feedback, `Default` adds no motion. */
1629
1654
  declare const BottomTabBar: ({ items, pathname, onNavigate, tabFeedback, navigateHint, regionLabel, containerStyle, testID, }: BottomTabBarProps) => React.ReactElement;
1630
1655
 
1631
- export { ACTIVE_BORDER_RADIUS, ACTIVE_TINT_OPACITY, APP_SHELL_SUFFIX, type AccountPlan, type AccountState, AppShell, type AppShellProps, type AppShellWidth, BASE_INDENT, BottomTabBar, type BottomTabBarProps, CHEVRON_ICON_SIZE, CollapsedRail, type CollapsedRailProps, type CollapsedRailRange, type CommandItem, CommandPalette, type CommandPaletteLabels, type CommandPaletteProps, CommandPaletteTrigger, type CommandPaletteTriggerProps, DEFAULT_COLLAPSED_RAIL_MAX, DEFAULT_SEARCH_BREAKPOINT, DarkModeControl, type DarkModeControlProps, type DarkModeOption, type DarkModeVariant, HOVER_TINT_OPACITY, NAV_ICON_SIZE, NAV_LINK_GAP, NAV_ROW_RADIUS, NAV_TEST_IDS, Nav, NavBar, type NavBarProps, NavExpandableItem, type NavExpandableItemProps, NavHoverFeedback, type NavItem, type NavOrientation, NavOverflowMenu, type NavOverflowMenuProps, type NavProps, NavShell, type NavShellCollapsedRail, type NavShellLayout, type NavShellProps, type NavShellSideRail, type NavShellTopBar, type NavUser, PillNav, type PillNavProps, PillNavVariant, RAIL_FULL_BREAKPOINT, type RailMode, type ResolveRailModeArgs, type SearchAffordance, type ShellMessage, Sidebar, SidebarChevron, type SidebarChevronProps, type SidebarPaletteTriggerConfig, type SidebarProps, SidebarSearch, type SidebarSearchConfig, type SidebarSearchLabels, type SidebarSearchProps, TabFeedback, Topbar, type TopbarAction, type TopbarProps, type TopbarUser, accessibleNavItems, collapsedRailStyles, darkModeStyles, expandableStyles, filterCommands, filterNavItems, isFilterActive, isRouteActive, navStyles, pillNavStyles, resolveActiveRoute, resolveContentMaxWidth, resolvePillMenuLabel, resolveRailMode, resolveSearchAffordance, roleRoutesToNavItems, searchTokens, useCommandPaletteHotkey, useContentMaxWidth };
1656
+ export { ACTIVE_BORDER_RADIUS, ACTIVE_TINT_OPACITY, APP_SHELL_SUFFIX, type AccountPlan, type AccountState, AppShell, type AppShellBackdrop, type AppShellBackdropBand, type AppShellProps, type AppShellWidth, BASE_INDENT, BottomTabBar, type BottomTabBarProps, BrandHomeLink, type BrandHomeLinkProps, CHEVRON_ICON_SIZE, CollapsedRail, type CollapsedRailProps, type CollapsedRailRange, type CommandItem, CommandPalette, type CommandPaletteLabels, type CommandPaletteProps, CommandPaletteTrigger, type CommandPaletteTriggerProps, DEFAULT_COLLAPSED_RAIL_MAX, DEFAULT_SEARCH_BREAKPOINT, DarkModeControl, type DarkModeControlProps, type DarkModeOption, type DarkModeVariant, HOVER_TINT_OPACITY, NAV_ICON_SIZE, NAV_LINK_GAP, NAV_ROW_RADIUS, NAV_TEST_IDS, Nav, NavBar, type NavBarProps, NavExpandableItem, type NavExpandableItemProps, NavHoverFeedback, type NavItem, type NavOrientation, NavOverflowMenu, type NavOverflowMenuProps, type NavProps, NavShell, type NavShellCollapsedRail, type NavShellLayout, type NavShellProps, type NavShellSideRail, type NavShellTopBar, type NavUser, PillNav, type PillNavProps, PillNavVariant, RAIL_FULL_BREAKPOINT, type RailMode, type ResolveRailModeArgs, type SearchAffordance, type ShellMessage, Sidebar, SidebarChevron, type SidebarChevronProps, type SidebarPaletteTriggerConfig, type SidebarProps, SidebarSearch, type SidebarSearchConfig, type SidebarSearchLabels, type SidebarSearchProps, TabFeedback, Topbar, type TopbarAction, type TopbarProps, type TopbarUser, accessibleNavItems, collapsedRailStyles, darkModeStyles, expandableStyles, filterCommands, filterNavItems, isFilterActive, isRouteActive, navStyles, pillNavStyles, resolveActiveRoute, resolveContentMaxWidth, resolvePillMenuLabel, resolveRailMode, resolveSearchAffordance, roleRoutesToNavItems, searchTokens, useCommandPaletteHotkey, useContentMaxWidth };
package/dist/index.js CHANGED
@@ -883,7 +883,8 @@ var NAV_TEST_IDS = {
883
883
  /** the compact "Current ▾" trigger of `PillNav` with `variant={PillNavVariant.Menu}`. */
884
884
  pillNavMenu: "pill-nav-menu",
885
885
  /** the optional middle slot of `Topbar` (rendered only when `center` is passed). */
886
- topbarCenter: "topbar-center"
886
+ topbarCenter: "topbar-center",
887
+ brandHome: "nav-brand-home"
887
888
  };
888
889
  var APP_SHELL_SUFFIX = {
889
890
  content: "-content",
@@ -903,7 +904,8 @@ var APP_SHELL_SUFFIX = {
903
904
  pending: "-pending",
904
905
  loading: "-loading",
905
906
  error: "-error",
906
- forbidden: "-forbidden"
907
+ forbidden: "-forbidden",
908
+ band: "-backdrop-band"
907
909
  };
908
910
  var FocusableTouchable = ({
909
911
  onPress,
@@ -1389,6 +1391,7 @@ var NavBarInner = ({
1389
1391
  () => contentMaxWidth === void 0 ? null : { maxWidth: contentMaxWidth, width: "100%", alignSelf: "center" },
1390
1392
  [contentMaxWidth]
1391
1393
  );
1394
+ React.useEffect(() => setOpen(false), [pathname]);
1392
1395
  const toggleMenu = React.useCallback(() => setOpen((v) => !v), []);
1393
1396
  const handlePress = React.useCallback(
1394
1397
  (route) => {
@@ -1536,19 +1539,92 @@ var NavBar = ({ barTheme, ...props }) => {
1536
1539
  if (barTheme === void 0) return /* @__PURE__ */ jsxRuntime.jsx(NavBarInner, { ...props });
1537
1540
  return /* @__PURE__ */ jsxRuntime.jsx(uiFeedback.UiProvider, { navigate, t, theme: barTheme, children: /* @__PURE__ */ jsxRuntime.jsx(NavBarInner, { ...props }) });
1538
1541
  };
1542
+ var styles3 = reactNative.StyleSheet.create({
1543
+ hitBox: { minHeight: MIN_TOUCH_TARGET, minWidth: MIN_TOUCH_TARGET, justifyContent: "center" }
1544
+ });
1545
+ function anchorProps(route) {
1546
+ return { href: route };
1547
+ }
1548
+ function hasNewTabModifier(event) {
1549
+ const { ctrlKey, metaKey, shiftKey } = event.nativeEvent;
1550
+ return ctrlKey === true || metaKey === true || shiftKey === true;
1551
+ }
1552
+ var BrandHomeLink = ({ route, label, onNavigate, children }) => {
1553
+ const { theme } = uiFeedback.useUi();
1554
+ const [focused, setFocused] = React.useState(false);
1555
+ const handlePress = (event) => {
1556
+ if (hasNewTabModifier(event)) return;
1557
+ event.preventDefault();
1558
+ onNavigate(route);
1559
+ };
1560
+ return /* @__PURE__ */ jsxRuntime.jsx(
1561
+ reactNative.Pressable,
1562
+ {
1563
+ accessibilityLabel: label,
1564
+ accessibilityRole: "link",
1565
+ style: [styles3.hitBox, focusRingStyle(focused, theme.palette.primary["500"])],
1566
+ testID: NAV_TEST_IDS.brandHome,
1567
+ onBlur: () => setFocused(false),
1568
+ onFocus: () => setFocused(true),
1569
+ onPress: handlePress,
1570
+ ...anchorProps(route),
1571
+ children
1572
+ }
1573
+ );
1574
+ };
1539
1575
  var Nav = (props) => {
1540
1576
  if (props.orientation === "vertical") {
1541
1577
  return /* @__PURE__ */ jsxRuntime.jsx(Sidebar, { ...props });
1542
1578
  }
1543
1579
  return /* @__PURE__ */ jsxRuntime.jsx(NavBar, { ...props });
1544
1580
  };
1581
+ var RAIL_FULL_BREAKPOINT = uiLayout.LAYOUT_COLLAPSE_BREAKPOINT;
1582
+ var DEFAULT_COLLAPSED_RAIL_MAX = 1024;
1583
+ function resolveRailMode({ viewport, hasSidebar, hasCollapsed, range }) {
1584
+ if (!hasSidebar) return "none";
1585
+ if (hasCollapsed) {
1586
+ if (viewport >= range.max) return "full";
1587
+ if (viewport >= range.min) return "collapsed";
1588
+ return "drawer";
1589
+ }
1590
+ return viewport >= RAIL_FULL_BREAKPOINT ? "full" : "drawer";
1591
+ }
1592
+ var styles4 = reactNative.StyleSheet.create({
1593
+ band: { left: 0, position: "absolute", right: 0, top: 0 }
1594
+ });
1595
+ function readBackdrop(theme) {
1596
+ return theme.backdrop;
1597
+ }
1598
+ function rootStyle(backdrop, fallback) {
1599
+ if (backdrop === void 0) return { backgroundColor: fallback };
1600
+ const base = { backgroundColor: backdrop.color };
1601
+ if (!IS_WEB || backdrop.image === null) return base;
1602
+ const web = { backgroundImage: backdrop.image, backgroundSize: backdrop.imageSize ?? "auto" };
1603
+ return { ...base, ...web };
1604
+ }
1605
+ function resolveShellBackdrop(theme, viewport, contentPadding, testID) {
1606
+ const backdrop = readBackdrop(theme);
1607
+ const band = backdrop?.band ?? null;
1608
+ const root = rootStyle(backdrop, theme.colors.background);
1609
+ if (band === null) return { root, chrome: void 0, chromeText: theme.colors.text, band: null };
1610
+ const overlap = viewport < RAIL_FULL_BREAKPOINT ? band.overlap.compact : band.overlap.wide;
1611
+ const element = /* @__PURE__ */ jsxRuntime.jsx(
1612
+ reactNative.View,
1613
+ {
1614
+ pointerEvents: "none",
1615
+ style: [styles4.band, { backgroundColor: band.color, height: contentPadding + overlap }],
1616
+ testID: `${testID}${APP_SHELL_SUFFIX.band}`
1617
+ }
1618
+ );
1619
+ return { root, chrome: { backgroundColor: band.color }, chromeText: band.text, band: element };
1620
+ }
1545
1621
  var CARD_PADDING = 20;
1546
1622
  var CARD_BORDER_RADIUS = 12;
1547
1623
  var CARD_BORDER_WIDTH = 1;
1548
1624
  var CARD_TITLE_FONT_SIZE = 16;
1549
1625
  var CARD_MESSAGE_FONT_SIZE = 14;
1550
1626
  var CARD_TITLE_MARGIN_BOTTOM = 8;
1551
- var styles3 = reactNative.StyleSheet.create({
1627
+ var styles5 = reactNative.StyleSheet.create({
1552
1628
  // Loading is a TRANSITIONAL state: the spinner is an absolute overlay ON TOP of
1553
1629
  // stably-mounted children, never a replacement for them. Replacing children here
1554
1630
  // would unmount whatever they host (e.g. a navigator), and on remount React
@@ -1580,9 +1656,9 @@ function MessageCard({
1580
1656
  }) {
1581
1657
  const { theme } = uiFeedback.useUi();
1582
1658
  const colors = theme.colors;
1583
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles3.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
1584
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.cardTitle, { color: accentColor }], children: message.titleText }),
1585
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles3.cardMessage, { color: colors.textSecondary }], children: message.messageText })
1659
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles5.card, { backgroundColor: colors.surface, borderColor: accentColor }], testID, children: [
1660
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.cardTitle, { color: accentColor }], children: message.titleText }),
1661
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles5.cardMessage, { color: colors.textSecondary }], children: message.messageText })
1586
1662
  ] });
1587
1663
  }
1588
1664
  function useContentBody(state, children, testID) {
@@ -1594,12 +1670,12 @@ function useContentBody(state, children, testID) {
1594
1670
  return /* @__PURE__ */ jsxRuntime.jsx(MessageCard, { accentColor: errorColor, message: state.forbidden, testID: `${testID}${APP_SHELL_SUFFIX.forbidden}` });
1595
1671
  if (state.error)
1596
1672
  return /* @__PURE__ */ jsxRuntime.jsx(MessageCard, { accentColor: errorColor, message: state.error, testID: `${testID}${APP_SHELL_SUFFIX.error}` });
1597
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.loadingHost, children: [
1673
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.loadingHost, children: [
1598
1674
  children,
1599
1675
  state.loading === true ? /* @__PURE__ */ jsxRuntime.jsx(
1600
1676
  reactNative.View,
1601
1677
  {
1602
- style: [styles3.loadingOverlay, { backgroundColor: theme.colors.background }],
1678
+ style: [styles5.loadingOverlay, { backgroundColor: theme.colors.background }],
1603
1679
  testID: `${testID}${APP_SHELL_SUFFIX.loading}`,
1604
1680
  children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" })
1605
1681
  }
@@ -1626,7 +1702,7 @@ var DEFAULT_DRAWER_LABELS = {
1626
1702
  closeLabel: "Close menu",
1627
1703
  closeHint: "Close the navigation menu"
1628
1704
  };
1629
- var styles4 = reactNative.StyleSheet.create({
1705
+ var styles6 = reactNative.StyleSheet.create({
1630
1706
  overlay: { ...reactNative.StyleSheet.absoluteFillObject, zIndex: DRAWER_Z_INDEX },
1631
1707
  // The scrim's tap-to-close hit area is anchored to the RIGHT of the panel
1632
1708
  // (`left: DRAWER_WIDTH`), so it NEVER overlaps the panel: nav-item taps always
@@ -1661,10 +1737,10 @@ var MenuToggle = ({ label, hint, onOpen, testID }) => {
1661
1737
  accessibilityHint: hint,
1662
1738
  accessibilityLabel: label,
1663
1739
  ringColor: theme.palette.primary["500"],
1664
- style: styles4.menuToggle,
1740
+ style: styles6.menuToggle,
1665
1741
  testID,
1666
1742
  onPress: onOpen,
1667
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles4.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
1743
+ children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.menuToggleGlyph, { color: theme.colors.text }], children: MENU_GLYPH2 })
1668
1744
  }
1669
1745
  );
1670
1746
  };
@@ -1690,14 +1766,14 @@ var MobileDrawer = ({
1690
1766
  node.addEventListener("click", handleClick);
1691
1767
  return () => node.removeEventListener("click", handleClick);
1692
1768
  }, [onClose]);
1693
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles4.overlay, children: [
1769
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.overlay, children: [
1694
1770
  /* @__PURE__ */ jsxRuntime.jsx(
1695
1771
  reactNative.Pressable,
1696
1772
  {
1697
1773
  accessibilityHint: labels.closeHint,
1698
1774
  accessibilityLabel: labels.closeLabel,
1699
1775
  accessibilityRole: "button",
1700
- style: styles4.scrim,
1776
+ style: styles6.scrim,
1701
1777
  testID: scrimTestID,
1702
1778
  onPress: onClose
1703
1779
  }
@@ -1708,24 +1784,13 @@ var MobileDrawer = ({
1708
1784
  ref: panelRef,
1709
1785
  "aria-modal": true,
1710
1786
  role: "dialog",
1711
- style: [styles4.panel, { backgroundColor: theme.colors.surface }],
1787
+ style: [styles6.panel, { backgroundColor: theme.colors.surface }],
1712
1788
  testID: drawerTestID,
1713
1789
  children: renderTextSlot(sidebar, { color: theme.colors.text })
1714
1790
  }
1715
1791
  )
1716
1792
  ] });
1717
1793
  };
1718
- var RAIL_FULL_BREAKPOINT = uiLayout.LAYOUT_COLLAPSE_BREAKPOINT;
1719
- var DEFAULT_COLLAPSED_RAIL_MAX = 1024;
1720
- function resolveRailMode({ viewport, hasSidebar, hasCollapsed, range }) {
1721
- if (!hasSidebar) return "none";
1722
- if (hasCollapsed) {
1723
- if (viewport >= range.max) return "full";
1724
- if (viewport >= range.min) return "collapsed";
1725
- return "drawer";
1726
- }
1727
- return viewport >= RAIL_FULL_BREAKPOINT ? "full" : "drawer";
1728
- }
1729
1794
  function resolveContentMaxWidth(width, viewport) {
1730
1795
  if (width === "full") return "full";
1731
1796
  const wideMinViewport = width.wideMinViewport ?? Number.POSITIVE_INFINITY;
@@ -1737,7 +1802,7 @@ function useContentMaxWidth(width) {
1737
1802
  return resolveContentMaxWidth(width, viewport);
1738
1803
  }
1739
1804
  var DEFAULT_CONTENT_PADDING = 24;
1740
- var styles5 = reactNative.StyleSheet.create({
1805
+ var styles7 = reactNative.StyleSheet.create({
1741
1806
  root: { flex: 1 },
1742
1807
  // Auth-pending spinner: an opaque absolute overlay over the mounted shell, so
1743
1808
  // `children` (and any navigator they host) are never unmounted while the gate
@@ -1777,6 +1842,7 @@ var AppShell = ({
1777
1842
  const maxWidth = useContentMaxWidth(width);
1778
1843
  const body = useContentBody(state, children, testID);
1779
1844
  const { width: viewportWidth } = reactNative.useWindowDimensions();
1845
+ const look = resolveShellBackdrop(theme, viewportWidth, contentPadding, testID);
1780
1846
  const railRange = collapsedRailRange ?? { min: RAIL_FULL_BREAKPOINT, max: DEFAULT_COLLAPSED_RAIL_MAX };
1781
1847
  const railMode = resolveRailMode({
1782
1848
  viewport: viewportWidth,
@@ -1798,18 +1864,21 @@ var AppShell = ({
1798
1864
  }, [isUnauthenticated, gate]);
1799
1865
  const gatePending = gate?.pending === true;
1800
1866
  if (isUnauthenticated) return null;
1801
- const columnStyle = maxWidth === "full" ? styles5.columnFull : [styles5.columnCapped, { maxWidth }];
1802
- const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles5.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
1803
- const scroller = /* @__PURE__ */ jsxRuntime.jsx(
1867
+ const columnStyle = maxWidth === "full" ? styles7.columnFull : [styles7.columnCapped, { maxWidth }];
1868
+ const navInnerStyle = chromeAlignment === "content" && maxWidth !== "full" ? [styles7.chromeCapped, { maxWidth, paddingHorizontal: contentPadding }] : void 0;
1869
+ const scroller = /* @__PURE__ */ jsxRuntime.jsxs(
1804
1870
  reactNative.ScrollView,
1805
1871
  {
1806
- contentContainerStyle: [styles5.scrollContent, { padding: contentPadding }],
1807
- style: styles5.scroll,
1872
+ contentContainerStyle: [styles7.scrollContent, { padding: contentPadding }],
1873
+ style: styles7.scroll,
1808
1874
  testID: `${testID}${APP_SHELL_SUFFIX.content}`,
1809
- children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: columnStyle, children: body })
1875
+ children: [
1876
+ look.band,
1877
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: columnStyle, children: body })
1878
+ ]
1810
1879
  }
1811
1880
  );
1812
- const headerRegion = useDrawer ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.headerRow, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
1881
+ const headerRegion = useDrawer ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles7.headerRow, look.chrome], testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: [
1813
1882
  /* @__PURE__ */ jsxRuntime.jsx(
1814
1883
  MenuToggle,
1815
1884
  {
@@ -1819,14 +1888,14 @@ var AppShell = ({
1819
1888
  onOpen: openDrawer
1820
1889
  }
1821
1890
  ),
1822
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles5.headerFill, children: renderTextSlot(header, { color: theme.colors.text }) })
1823
- ] }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: renderTextSlot(header, { color: theme.colors.text }) });
1891
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles7.headerFill, children: renderTextSlot(header, { color: look.chromeText }) })
1892
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: look.chrome, testID: `${testID}${APP_SHELL_SUFFIX.header}`, children: renderTextSlot(header, { color: look.chromeText }) });
1824
1893
  const railRegion = railMode === "full" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.sidebar}`, children: renderTextSlot(sidebar, { color: theme.colors.text }) }) : railMode === "collapsed" ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.collapsedSidebar}`, children: renderTextSlot(collapsedSidebar, { color: theme.colors.text }) }) : null;
1825
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles5.root, { backgroundColor: theme.colors.background }], testID, children: [
1894
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: [styles7.root, look.root], testID, children: [
1826
1895
  headerRegion,
1827
- nav !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.nav}`, children: navInnerStyle !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: navInnerStyle, children: renderTextSlot(nav, { color: theme.colors.text }) }) : renderTextSlot(nav, { color: theme.colors.text }) }) : null,
1896
+ nav !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: look.chrome, testID: `${testID}${APP_SHELL_SUFFIX.nav}`, children: navInnerStyle !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: navInnerStyle, children: renderTextSlot(nav, { color: look.chromeText }) }) : renderTextSlot(nav, { color: look.chromeText }) }) : null,
1828
1897
  banner !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { testID: `${testID}${APP_SHELL_SUFFIX.banner}`, children: renderTextSlot(banner, { color: theme.colors.text }) }) : null,
1829
- railRegion !== null ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles5.bodyRow, children: [
1898
+ railRegion !== null ? /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles7.bodyRow, children: [
1830
1899
  railRegion,
1831
1900
  scroller
1832
1901
  ] }) : scroller,
@@ -1843,7 +1912,7 @@ var AppShell = ({
1843
1912
  gatePending ? /* @__PURE__ */ jsxRuntime.jsx(
1844
1913
  reactNative.View,
1845
1914
  {
1846
- style: [styles5.pendingOverlay, { backgroundColor: theme.colors.background }],
1915
+ style: [styles7.pendingOverlay, { backgroundColor: theme.colors.background }],
1847
1916
  testID: `${testID}${APP_SHELL_SUFFIX.pending}`,
1848
1917
  children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.ActivityIndicator, { color: primary, size: "large" })
1849
1918
  }
@@ -2210,7 +2279,7 @@ var KEY_DOWN = "ArrowDown";
2210
2279
  var KEY_UP = "ArrowUp";
2211
2280
  var KEY_ENTER = "Enter";
2212
2281
  var KEY_ESCAPE = "Escape";
2213
- var styles6 = reactNative.StyleSheet.create({
2282
+ var styles8 = reactNative.StyleSheet.create({
2214
2283
  overlay: { ...reactNative.StyleSheet.absoluteFillObject, zIndex: OVERLAY_Z_INDEX, alignItems: "center" },
2215
2284
  scrim: { ...reactNative.StyleSheet.absoluteFillObject, backgroundColor: SCRIM_COLOR2 },
2216
2285
  panel: {
@@ -2239,14 +2308,14 @@ var PaletteRow = ({ item, selected, onChoose }) => {
2239
2308
  accessibilityLabel: item.label,
2240
2309
  accessibilityRole: "button",
2241
2310
  accessibilityState: { selected },
2242
- style: [styles6.row, selected ? { backgroundColor: colors.border } : void 0],
2311
+ style: [styles8.row, selected ? { backgroundColor: colors.border } : void 0],
2243
2312
  testID: item.testID ?? item.key,
2244
2313
  onPress: () => onChoose(item),
2245
2314
  children: [
2246
2315
  typeof item.renderIcon === "function" ? item.renderIcon(colors.textSecondary, NAV_ICON_SIZE) : null,
2247
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.rowText, children: [
2248
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.rowLabel, { color: colors.text }], children: item.label }),
2249
- item.hint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.rowHint, { color: colors.textSecondary }], children: item.hint }) : null
2316
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles8.rowText, children: [
2317
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles8.rowLabel, { color: colors.text }], children: item.label }),
2318
+ item.hint ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles8.rowHint, { color: colors.textSecondary }], children: item.hint }) : null
2250
2319
  ] })
2251
2320
  ]
2252
2321
  }
@@ -2286,14 +2355,14 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
2286
2355
  [results, selected, choose, onClose]
2287
2356
  );
2288
2357
  if (!open) return null;
2289
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles6.overlay, children: [
2358
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles8.overlay, children: [
2290
2359
  /* @__PURE__ */ jsxRuntime.jsx(
2291
2360
  reactNative.Pressable,
2292
2361
  {
2293
2362
  accessibilityHint: labels.closeHint,
2294
2363
  accessibilityLabel: labels.closeLabel,
2295
2364
  accessibilityRole: "button",
2296
- style: styles6.scrim,
2365
+ style: styles8.scrim,
2297
2366
  testID: `${testID ?? "command-palette"}-scrim`,
2298
2367
  onPress: onClose
2299
2368
  }
@@ -2304,7 +2373,7 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
2304
2373
  "aria-modal": true,
2305
2374
  "aria-label": labels.regionLabel,
2306
2375
  role: "dialog",
2307
- style: [styles6.panel, { backgroundColor: colors.surface, borderColor: colors.border }],
2376
+ style: [styles8.panel, { backgroundColor: colors.surface, borderColor: colors.border }],
2308
2377
  testID: testID ?? "command-palette",
2309
2378
  children: [
2310
2379
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2314,14 +2383,14 @@ var CommandPalette = ({ open, items, onClose, labels, testID }) => {
2314
2383
  accessibilityLabel: labels.placeholder,
2315
2384
  placeholder: labels.placeholder,
2316
2385
  placeholderTextColor: colors.textSecondary,
2317
- style: [styles6.input, { color: colors.text, borderBottomColor: colors.border }],
2386
+ style: [styles8.input, { color: colors.text, borderBottomColor: colors.border }],
2318
2387
  testID: `${testID ?? "command-palette"}-input`,
2319
2388
  value: query,
2320
2389
  onChangeText: setQuery,
2321
2390
  onKeyPress
2322
2391
  }
2323
2392
  ),
2324
- results.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles6.empty, { color: colors.textSecondary }], children: labels.emptyText }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.ScrollView, { keyboardShouldPersistTaps: "handled", style: styles6.list, children: results.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(PaletteRow, { item, selected: index === selected, onChoose: choose }, item.key)) })
2393
+ results.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles8.empty, { color: colors.textSecondary }], children: labels.emptyText }) : /* @__PURE__ */ jsxRuntime.jsx(reactNative.ScrollView, { keyboardShouldPersistTaps: "handled", style: styles8.list, children: results.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(PaletteRow, { item, selected: index === selected, onChoose: choose }, item.key)) })
2325
2394
  ]
2326
2395
  }
2327
2396
  )
@@ -2426,7 +2495,7 @@ var TAB_MIN_HEIGHT = 56;
2426
2495
  var LABEL_SIZE = 12;
2427
2496
  var LABEL_GAP = 2;
2428
2497
  var TAB_PADDING = 4;
2429
- var styles7 = reactNative.StyleSheet.create({
2498
+ var styles9 = reactNative.StyleSheet.create({
2430
2499
  tab: {
2431
2500
  flex: 1,
2432
2501
  minHeight: TAB_MIN_HEIGHT,
@@ -2463,31 +2532,31 @@ var BottomTab = ({
2463
2532
  accessibilityLabel: item.label,
2464
2533
  accessibilityRole: "link",
2465
2534
  accessibilityState: { selected: isActive },
2466
- style: [styles7.tab, { opacity: feedback.opacity }],
2535
+ style: [styles9.tab, { opacity: feedback.opacity }],
2467
2536
  testID: item.testID ?? item.key,
2468
2537
  onPress: () => onPress(item.route),
2469
2538
  onPressIn: () => setPressed(true),
2470
2539
  onPressOut: () => setPressed(false),
2471
2540
  ...ariaCurrentProps3(isActive),
2472
2541
  children: [
2473
- /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Animated.View, { style: [styles7.iconWrap, { transform: [{ scale }] }], children: [
2542
+ /* @__PURE__ */ jsxRuntime.jsxs(reactNative.Animated.View, { style: [styles9.iconWrap, { transform: [{ scale }] }], children: [
2474
2543
  feedback.pillScale === null ? null : /* @__PURE__ */ jsxRuntime.jsx(
2475
2544
  reactNative.View,
2476
2545
  {
2477
2546
  pointerEvents: "none",
2478
- style: [styles7.pill, { backgroundColor: colors.pill, transform: [{ scale: feedback.pillScale }] }, motion]
2547
+ style: [styles9.pill, { backgroundColor: colors.pill, transform: [{ scale: feedback.pillScale }] }, motion]
2479
2548
  }
2480
2549
  ),
2481
2550
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [{ transform: feedback.iconTransform }, motion], children: item.renderIcon?.(color, ICON_SIZE) })
2482
2551
  ] }),
2483
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles7.label, { color }], children: item.label })
2552
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles9.label, { color }], children: item.label })
2484
2553
  ]
2485
2554
  }
2486
2555
  );
2487
2556
  };
2488
2557
  var DEFAULT_TEST_ID = "bottom-tab-bar";
2489
2558
  var SLIDER_HEIGHT = 3;
2490
- var styles8 = reactNative.StyleSheet.create({
2559
+ var styles10 = reactNative.StyleSheet.create({
2491
2560
  bar: { flexDirection: "row", borderTopWidth: reactNative.StyleSheet.hairlineWidth },
2492
2561
  slider: { position: "absolute", top: 0, height: SLIDER_HEIGHT }
2493
2562
  });
@@ -2514,14 +2583,14 @@ var BottomTabBar = ({
2514
2583
  {
2515
2584
  "aria-label": regionLabel,
2516
2585
  role: "navigation",
2517
- style: [styles8.bar, { backgroundColor: theme.colors.surface, borderTopColor: theme.colors.border }, containerStyle],
2586
+ style: [styles10.bar, { backgroundColor: theme.colors.surface, borderTopColor: theme.colors.border }, containerStyle],
2518
2587
  testID,
2519
2588
  children: [
2520
2589
  bar === null ? null : /* @__PURE__ */ jsxRuntime.jsx(
2521
2590
  reactNative.View,
2522
2591
  {
2523
2592
  pointerEvents: "none",
2524
- style: [styles8.slider, { left: bar.left, width: bar.width, backgroundColor: primary }, webTransition("left", barMs, reducedMotion)]
2593
+ style: [styles10.slider, { left: bar.left, width: bar.width, backgroundColor: primary }, webTransition("left", barMs, reducedMotion)]
2525
2594
  }
2526
2595
  ),
2527
2596
  items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -2548,6 +2617,7 @@ exports.APP_SHELL_SUFFIX = APP_SHELL_SUFFIX;
2548
2617
  exports.AppShell = AppShell;
2549
2618
  exports.BASE_INDENT = BASE_INDENT;
2550
2619
  exports.BottomTabBar = BottomTabBar;
2620
+ exports.BrandHomeLink = BrandHomeLink;
2551
2621
  exports.CHEVRON_ICON_SIZE = CHEVRON_ICON_SIZE;
2552
2622
  exports.CollapsedRail = CollapsedRail;
2553
2623
  exports.CommandPalette = CommandPalette;