about summary refs log tree commit diff
path: root/src/screens
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2024-12-05 18:59:26 -0600
committerGitHub <noreply@github.com>2024-12-05 18:59:26 -0600
commit143e2c802d1d8d8498e6658c174ed1e657c4ec12 (patch)
treecbe937bec7e0a241774060ade7428180c4fe0aaf /src/screens
parent8467dfd452b4cb1b62214b3abe87fd90d23a183b (diff)
downloadvoidsky-143e2c802d1d8d8498e6658c174ed1e657c4ec12.tar.zst
[Layout] Base (#6907)
* Add common gutter styles as hook

* Add computed scrollbar gutter CSS vars

* Add new layout components

* Replace layout components in settings screens

* Remove old back button

* Invert web border logic for easier migration

* Clean up Slot API

* Port over FF handling of scrollbar offset

* Trade boilerplate for ease of use

* Limit to one line

* Allow two lines, fix wrapping

* Fix alignment

* sticky headers

* set max with on header and center

* [Layout] Notifications Header (#6910)

* Replace notifications screen header

* fix cropped indicator

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>

* Replace Hashtag header (#6928)

* [Layout] ChatList header (#6929)

* Replace ChatList header

* update chat settings as well

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>

* Add web borders to Chat settings

* Remove unused var

* Move ChatList header outside center

* Replace empty chat layout

* fix breakpoints

* [Layout] Scrollbar gutters (#6908)

* Fix sidebar alignment

* Make sure scrollbars don't hide

* Gift left nav more space

* Use stable one-edge, update logic in RightNav

* Ope

* Increase width

* Reset

* Add transform to sidebars

* Remove bg in sidebars

* Handle shifts in layout components

* Replace scroll-removal handling

* Make react-remove-scroll an explicit dep

* Remove unused script

* use correct scroll insets (#6950)

* [Layout] Feeds headers (#6913)

* Replace ViewHeader internals, duplicate old ViewHeader

* Replace Feeds header

* Replace SavedFeeds header

* Visual alignment

* Uglier but clear

* Use old ViewHeader for SavedFeeds

* use Layout.Center instead of Layout.Content

* use left-aligned header for feed edit

* delete unused old view header

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>

* [Layout] Every other screen (#6953)

* attempt to fix double borders on every other screen

* delete ListHeaderDesktop

* delete `SimpleViewHeader` and fix screens (#6956)

* Make Layout.Center not full height

* Refactor List to use Layout.Center, remove built-in borders

* Fix Home screen

* Refactor PagerWithHeader to use Layout components

* Replace components in ProfileFeed and ProfileList

* Borders on Profile

* Search screen replacements

* use new header for profile subpage header (#6958)

* Search AutocompleteResults

* use new header for starter pack wizard (#6957)

* Fix post thread

* Enable borders by default

* Moderation muted and blocked accounts

* Fix scrollbar offset on Labeler

* Remove ScrollView from Moderation

* Remove ScrollView from Deactivated

* Remove ScrollView from onboarding

* Remove ScrollView from SignupQueued

* Mark deprecations

* fix lint

* Fix double borders on profile load

* Remove unneeded CenteredView from noty Feed

* Remove double Center layout on Notifications screen

* Remove double Center layout on ChatList screen

* Handle scrollbar offset in chat

* Use new atom for other scrollbar offsets

* Remove borders from old views

* Better doc

* Remove temp migration prop

* Fix new atom usage on native

* Clean up Hashtag screen

* Layout docs

* Clarify usage in Pager

* Handle nested offset contexts

* Clean up Layout

* fix feeds page

* asymmetric header on native (#6969)

* Reusable header const

* Fix up home header

* Add back button to convo

* Add hitslop to header buttons

* Comment

* Better handling on native for new atom

* Format

* Fix nested flatlist on mod screens

* Use react-remove-scroll-bar directly

* Fix notification count overflow on web

* Clarify doc

---------

Co-authored-by: Samuel Newman <mozzius@protonmail.com>
Diffstat (limited to 'src/screens')
-rw-r--r--src/screens/Deactivated.tsx21
-rw-r--r--src/screens/Hashtag.tsx71
-rw-r--r--src/screens/Messages/ChatList.tsx160
-rw-r--r--src/screens/Messages/Conversation.tsx9
-rw-r--r--src/screens/Messages/Settings.tsx16
-rw-r--r--src/screens/Moderation/index.tsx47
-rw-r--r--src/screens/Onboarding/Layout.tsx6
-rw-r--r--src/screens/Post/PostLikedBy.tsx10
-rw-r--r--src/screens/Post/PostQuotes.tsx2
-rw-r--r--src/screens/Post/PostRepostedBy.tsx2
-rw-r--r--src/screens/Profile/KnownFollowers.tsx29
-rw-r--r--src/screens/Profile/Sections/Labels.tsx7
-rw-r--r--src/screens/Settings/AboutSettings.tsx10
-rw-r--r--src/screens/Settings/AccessibilitySettings.tsx10
-rw-r--r--src/screens/Settings/AccountSettings.tsx10
-rw-r--r--src/screens/Settings/AppIconSettings.tsx12
-rw-r--r--src/screens/Settings/AppPasswords.tsx10
-rw-r--r--src/screens/Settings/AppearanceSettings.tsx10
-rw-r--r--src/screens/Settings/ContentAndMediaSettings.tsx10
-rw-r--r--src/screens/Settings/ExternalMediaPreferences.tsx14
-rw-r--r--src/screens/Settings/FollowingFeedPreferences.tsx10
-rw-r--r--src/screens/Settings/LanguageSettings.tsx10
-rw-r--r--src/screens/Settings/NotificationSettings.tsx10
-rw-r--r--src/screens/Settings/PrivacyAndSecuritySettings.tsx10
-rw-r--r--src/screens/Settings/Settings.tsx10
-rw-r--r--src/screens/Settings/ThreadPreferences.tsx10
-rw-r--r--src/screens/SignupQueued.tsx3
-rw-r--r--src/screens/StarterPack/Wizard/index.tsx81
28 files changed, 305 insertions, 305 deletions
diff --git a/src/screens/Deactivated.tsx b/src/screens/Deactivated.tsx
index 36b96cacd..4fcb42854 100644
--- a/src/screens/Deactivated.tsx
+++ b/src/screens/Deactivated.tsx
@@ -17,13 +17,13 @@ import {
 } from '#/state/session'
 import {useSetMinimalShellMode} from '#/state/shell'
 import {useLoggedOutViewControls} from '#/state/shell/logged-out'
-import {ScrollView} from '#/view/com/util/Views'
 import {Logo} from '#/view/icons/Logo'
 import {atoms as a, useTheme} from '#/alf'
 import {AccountList} from '#/components/AccountList'
 import {Button, ButtonIcon, ButtonText} from '#/components/Button'
 import {Divider} from '#/components/Divider'
 import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
+import * as Layout from '#/components/Layout'
 import {Loader} from '#/components/Loader'
 import {Text} from '#/components/Typography'
 
@@ -104,24 +104,17 @@ export function Deactivated() {
   }, [_, agent, setPending, setError, queryClient])
 
   return (
-    <View style={[a.util_screen_outer, a.flex_1, t.atoms.bg]}>
-      <ScrollView
-        style={[
-          a.h_full,
-          a.w_full,
+    <View style={[a.util_screen_outer, a.flex_1]}>
+      <Layout.Content
+        contentContainerStyle={[
           a.px_2xl,
           {
             paddingTop: isWeb ? 64 : insets.top + 16,
             paddingBottom: isWeb ? 64 : insets.bottom,
           },
-        ]}
-        contentContainerStyle={[
-          a.w_full,
-          a.flex_row,
-          a.justify_center,
-          {borderWidth: 0},
         ]}>
-        <View style={[a.w_full, {maxWidth: COL_WIDTH}]}>
+        <View
+          style={[a.w_full, {marginHorizontal: 'auto', maxWidth: COL_WIDTH}]}>
           <View style={[a.w_full, a.justify_center, a.align_center, a.pb_5xl]}>
             <Logo width={40} />
           </View>
@@ -218,7 +211,7 @@ export function Deactivated() {
             </>
           )}
         </View>
-      </ScrollView>
+      </Layout.Content>
     </View>
   )
 }
diff --git a/src/screens/Hashtag.tsx b/src/screens/Hashtag.tsx
index adf5f0080..a0fc3707c 100644
--- a/src/screens/Hashtag.tsx
+++ b/src/screens/Hashtag.tsx
@@ -1,5 +1,5 @@
 import React from 'react'
-import {ListRenderItemInfo, Pressable, View} from 'react-native'
+import {ListRenderItemInfo, View} from 'react-native'
 import {PostView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
@@ -13,16 +13,15 @@ import {shareUrl} from '#/lib/sharing'
 import {cleanError} from '#/lib/strings/errors'
 import {sanitizeHandle} from '#/lib/strings/handles'
 import {enforceLen} from '#/lib/strings/helpers'
-import {isNative, isWeb} from '#/platform/detection'
 import {useSearchPostsQuery} from '#/state/queries/search-posts'
 import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell'
 import {Pager} from '#/view/com/pager/Pager'
 import {TabBar} from '#/view/com/pager/TabBar'
 import {Post} from '#/view/com/post/Post'
 import {List} from '#/view/com/util/List'
-import {ViewHeader} from '#/view/com/util/ViewHeader'
-import {CenteredView} from '#/view/com/util/Views'
-import {ArrowOutOfBox_Stroke2_Corner0_Rounded} from '#/components/icons/ArrowOutOfBox'
+import {atoms as a, web} from '#/alf'
+import {Button, ButtonIcon} from '#/components/Button'
+import {ArrowOutOfBox_Stroke2_Corner0_Rounded as Share} from '#/components/icons/ArrowOutOfBox'
 import * as Layout from '#/components/Layout'
 import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
 
@@ -110,46 +109,36 @@ export default function HashtagScreen({
 
   return (
     <Layout.Screen>
-      <CenteredView sideBorders={true}>
-        <ViewHeader
-          showOnDesktop
-          title={headerTitle}
-          subtitle={author ? _(msg`From @${sanitizedAuthor}`) : undefined}
-          canGoBack
-          renderButton={
-            isNative
-              ? () => (
-                  <Pressable
-                    accessibilityRole="button"
-                    onPress={onShare}
-                    hitSlop={HITSLOP_10}>
-                    <ArrowOutOfBox_Stroke2_Corner0_Rounded
-                      size="lg"
-                      onPress={onShare}
-                    />
-                  </Pressable>
-                )
-              : undefined
-          }
-        />
-      </CenteredView>
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>{headerTitle}</Layout.Header.TitleText>
+          {author && (
+            <Layout.Header.SubtitleText>
+              {_(msg`From @${sanitizedAuthor}`)}
+            </Layout.Header.SubtitleText>
+          )}
+        </Layout.Header.Content>
+        <Layout.Header.Slot>
+          <Button
+            label={_(msg`Share`)}
+            size="small"
+            variant="ghost"
+            color="primary"
+            shape="round"
+            onPress={onShare}
+            hitSlop={HITSLOP_10}
+            style={[{right: -3}]}>
+            <ButtonIcon icon={Share} size="md" />
+          </Button>
+        </Layout.Header.Slot>
+      </Layout.Header.Outer>
       <Pager
         onPageSelected={onPageSelected}
         renderTabBar={props => (
-          <CenteredView
-            sideBorders={true}
-            // @ts-ignore web only
-            style={
-              isWeb
-                ? {
-                    position: isWeb ? 'sticky' : '',
-                    top: 0,
-                    zIndex: 1,
-                  }
-                : undefined
-            }>
+          <Layout.Center style={web([a.sticky, a.z_10, {top: 0}])}>
             <TabBar items={sections.map(section => section.title)} {...props} />
-          </CenteredView>
+          </Layout.Center>
         )}
         initialPage={0}>
         {sections.map((section, i) => (
diff --git a/src/screens/Messages/ChatList.tsx b/src/screens/Messages/ChatList.tsx
index 4f2bd251f..1a87a2ac5 100644
--- a/src/screens/Messages/ChatList.tsx
+++ b/src/screens/Messages/ChatList.tsx
@@ -16,8 +16,6 @@ import {MESSAGE_SCREEN_POLL_INTERVAL} from '#/state/messages/convo/const'
 import {useMessagesEventBus} from '#/state/messages/events'
 import {useListConvosQuery} from '#/state/queries/messages/list-converations'
 import {List} from '#/view/com/util/List'
-import {ViewHeader} from '#/view/com/util/ViewHeader'
-import {CenteredView} from '#/view/com/util/Views'
 import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
 import {Button, ButtonIcon, ButtonText} from '#/components/Button'
 import {DialogControlProps, useDialogControl} from '#/components/Dialog'
@@ -49,7 +47,6 @@ export function MessagesScreen({navigation, route}: Props) {
   const {_} = useLingui()
   const t = useTheme()
   const newChatControl = useDialogControl()
-  const {gtMobile} = useBreakpoints()
   const pushToConversation = route.params?.pushToConversation
 
   // Whenever we have `pushToConversation` set, it means we pressed a notification for a chat without being on
@@ -81,21 +78,6 @@ export function MessagesScreen({navigation, route}: Props) {
     }, [messagesBus, isActive]),
   )
 
-  const renderButton = useCallback(() => {
-    return (
-      <Link
-        to="/messages/settings"
-        label={_(msg`Chat settings`)}
-        size="small"
-        variant="ghost"
-        color="secondary"
-        shape="square"
-        style={[a.justify_center]}>
-        <SettingsSlider size="md" style={[t.atoms.text_contrast_medium]} />
-      </Link>
-    )
-  }, [_, t])
-
   const initialNumToRender = useInitialNumToRender({minItemHeight: 80})
   const [isPTRing, setIsPTRing] = useState(false)
 
@@ -144,28 +126,11 @@ export function MessagesScreen({navigation, route}: Props) {
     [navigation],
   )
 
-  const onNavigateToSettings = useCallback(() => {
-    navigation.navigate('MessagesSettings')
-  }, [navigation])
-
   if (conversations.length < 1) {
     return (
       <Layout.Screen>
-        <CenteredView sideBorders={gtMobile} style={[a.h_full_vh]}>
-          {gtMobile ? (
-            <DesktopHeader
-              newChatControl={newChatControl}
-              onNavigateToSettings={onNavigateToSettings}
-            />
-          ) : (
-            <ViewHeader
-              title={_(msg`Messages`)}
-              renderButton={renderButton}
-              showBorder
-              canGoBack={false}
-            />
-          )}
-
+        <Header newChatControl={newChatControl} />
+        <Layout.Center>
           {isLoading ? (
             <View style={[a.align_center, a.pt_3xl, web({paddingTop: '10vh'})]}>
               <Loader size="xl" />
@@ -227,7 +192,7 @@ export function MessagesScreen({navigation, route}: Props) {
               )}
             </>
           )}
-        </CenteredView>
+        </Layout.Center>
 
         {!isLoading && !isError && (
           <NewChat onNewChat={onNewChat} control={newChatControl} />
@@ -238,14 +203,7 @@ export function MessagesScreen({navigation, route}: Props) {
 
   return (
     <Layout.Screen testID="messagesScreen">
-      {!gtMobile && (
-        <ViewHeader
-          title={_(msg`Messages`)}
-          renderButton={renderButton}
-          showBorder
-          canGoBack={false}
-        />
-      )}
+      <Header newChatControl={newChatControl} />
       <NewChat onNewChat={onNewChat} control={newChatControl} />
       <List
         data={conversations}
@@ -254,12 +212,6 @@ export function MessagesScreen({navigation, route}: Props) {
         refreshing={isPTRing}
         onRefresh={onRefresh}
         onEndReached={onEndReached}
-        ListHeaderComponent={
-          <DesktopHeader
-            newChatControl={newChatControl}
-            onNavigateToSettings={onNavigateToSettings}
-          />
-        }
         ListFooterComponent={
           <ListFooter
             isFetchingNextPage={isFetchingNextPage}
@@ -276,67 +228,65 @@ export function MessagesScreen({navigation, route}: Props) {
         windowSize={11}
         // @ts-ignore our .web version only -sfn
         desktopFixedHeight
+        sideBorders={false}
       />
     </Layout.Screen>
   )
 }
 
-function DesktopHeader({
-  newChatControl,
-  onNavigateToSettings,
-}: {
-  newChatControl: DialogControlProps
-  onNavigateToSettings: () => void
-}) {
-  const t = useTheme()
+function Header({newChatControl}: {newChatControl: DialogControlProps}) {
   const {_} = useLingui()
-  const {gtMobile, gtTablet} = useBreakpoints()
+  const {gtMobile} = useBreakpoints()
 
-  if (!gtMobile) {
-    return null
-  }
+  const settingsLink = (
+    <Link
+      to="/messages/settings"
+      label={_(msg`Chat settings`)}
+      size="small"
+      variant="ghost"
+      color="secondary"
+      shape="square"
+      style={[a.justify_center]}>
+      <ButtonIcon icon={SettingsSlider} size="md" />
+    </Link>
+  )
 
   return (
-    <View
-      style={[
-        t.atoms.bg,
-        a.flex_row,
-        a.align_center,
-        a.justify_between,
-        a.gap_lg,
-        a.px_lg,
-        a.pr_md,
-        a.py_sm,
-        a.border_b,
-        t.atoms.border_contrast_low,
-      ]}>
-      <Text style={[a.text_2xl, a.font_bold]}>
-        <Trans>Messages</Trans>
-      </Text>
-      <View style={[a.flex_row, a.align_center, a.gap_sm]}>
-        <Button
-          label={_(msg`Message settings`)}
-          color="secondary"
-          size="small"
-          variant="ghost"
-          shape="square"
-          onPress={onNavigateToSettings}>
-          <SettingsSlider size="md" style={[t.atoms.text_contrast_medium]} />
-        </Button>
-        {gtTablet && (
-          <Button
-            label={_(msg`New chat`)}
-            color="primary"
-            size="small"
-            variant="solid"
-            onPress={newChatControl.open}>
-            <ButtonIcon icon={Plus} position="left" />
-            <ButtonText>
-              <Trans>New chat</Trans>
-            </ButtonText>
-          </Button>
-        )}
-      </View>
-    </View>
+    <Layout.Header.Outer>
+      {gtMobile ? (
+        <>
+          <Layout.Header.Content>
+            <Layout.Header.TitleText>
+              <Trans>Messages</Trans>
+            </Layout.Header.TitleText>
+          </Layout.Header.Content>
+
+          <View style={[a.flex_row, a.align_center, a.gap_sm]}>
+            {settingsLink}
+            <Button
+              label={_(msg`New chat`)}
+              color="primary"
+              size="small"
+              variant="solid"
+              onPress={newChatControl.open}>
+              <ButtonIcon icon={Plus} position="left" />
+              <ButtonText>
+                <Trans>New chat</Trans>
+              </ButtonText>
+            </Button>
+          </View>
+        </>
+      ) : (
+        <>
+          <Layout.Header.MenuButton />
+          <Layout.Header.Content>
+            <Layout.Header.TitleText>
+              <Trans>Messages</Trans>
+            </Layout.Header.TitleText>
+          </Layout.Header.Content>
+          <Layout.Header.Slot>{settingsLink}</Layout.Header.Slot>
+        </>
+      )}
+    </Layout.Header.Outer>
   )
 }
diff --git a/src/screens/Messages/Conversation.tsx b/src/screens/Messages/Conversation.tsx
index a2157d2b9..b8b0bfe0d 100644
--- a/src/screens/Messages/Conversation.tsx
+++ b/src/screens/Messages/Conversation.tsx
@@ -17,7 +17,6 @@ import {useCurrentConvoId} from '#/state/messages/current-convo-id'
 import {useModerationOpts} from '#/state/preferences/moderation-opts'
 import {useProfileQuery} from '#/state/queries/profile'
 import {useSetMinimalShellMode} from '#/state/shell'
-import {CenteredView} from '#/view/com/util/Views'
 import {MessagesList} from '#/screens/Messages/components/MessagesList'
 import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
 import {useDialogControl} from '#/components/Dialog'
@@ -97,7 +96,7 @@ function Inner() {
 
   if (convoState.status === ConvoStatus.Error) {
     return (
-      <CenteredView style={[a.flex_1]} sideBorders>
+      <Layout.Center style={[a.flex_1]}>
         <MessagesListHeader />
         <Error
           title={_(msg`Something went wrong`)}
@@ -105,12 +104,12 @@ function Inner() {
           onRetry={() => convoState.error.retry()}
           sideBorders={false}
         />
-      </CenteredView>
+      </Layout.Center>
     )
   }
 
   return (
-    <CenteredView style={[a.flex_1]} sideBorders>
+    <Layout.Center style={[a.flex_1]}>
       {!readyToShow && <MessagesListHeader />}
       <View style={[a.flex_1]}>
         {moderationOpts && recipient ? (
@@ -140,7 +139,7 @@ function Inner() {
           </View>
         )}
       </View>
-    </CenteredView>
+    </Layout.Center>
   )
 }
 
diff --git a/src/screens/Messages/Settings.tsx b/src/screens/Messages/Settings.tsx
index 50b1c4cc9..f37e7a9ba 100644
--- a/src/screens/Messages/Settings.tsx
+++ b/src/screens/Messages/Settings.tsx
@@ -10,8 +10,6 @@ import {useUpdateActorDeclaration} from '#/state/queries/messages/actor-declarat
 import {useProfileQuery} from '#/state/queries/profile'
 import {useSession} from '#/state/session'
 import * as Toast from '#/view/com/util/Toast'
-import {ViewHeader} from '#/view/com/util/ViewHeader'
-import {ScrollView} from '#/view/com/util/Views'
 import {atoms as a} from '#/alf'
 import {Admonition} from '#/components/Admonition'
 import {Divider} from '#/components/Divider'
@@ -57,8 +55,16 @@ export function MessagesSettingsScreen({}: Props) {
 
   return (
     <Layout.Screen testID="messagesSettingsScreen">
-      <ScrollView stickyHeaderIndices={[0]}>
-        <ViewHeader title={_(msg`Chat Settings`)} showOnDesktop showBorder />
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>
+            <Trans>Chat Settings</Trans>
+          </Layout.Header.TitleText>
+        </Layout.Header.Content>
+        <Layout.Header.Slot />
+      </Layout.Header.Outer>
+      <Layout.Content>
         <View style={[a.p_lg, a.gap_md]}>
           <Text style={[a.text_lg, a.font_bold]}>
             <Trans>Allow new messages from</Trans>
@@ -142,7 +148,7 @@ export function MessagesSettingsScreen({}: Props) {
             </>
           )}
         </View>
-      </ScrollView>
+      </Layout.Content>
     </Layout.Screen>
   )
 }
diff --git a/src/screens/Moderation/index.tsx b/src/screens/Moderation/index.tsx
index 5f340cd56..6b4dd06bc 100644
--- a/src/screens/Moderation/index.tsx
+++ b/src/screens/Moderation/index.tsx
@@ -1,6 +1,5 @@
-import React from 'react'
+import {Fragment, useCallback} from 'react'
 import {Linking, View} from 'react-native'
-import {useSafeAreaFrame} from 'react-native-safe-area-context'
 import {LABELS} from '@atproto/api'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
@@ -19,8 +18,6 @@ import {
 import {isNonConfigurableModerationAuthority} from '#/state/session/additional-moderation-authorities'
 import {useSetMinimalShellMode} from '#/state/shell'
 import {ViewHeader} from '#/view/com/util/ViewHeader'
-import {CenteredView} from '#/view/com/util/Views'
-import {ScrollView} from '#/view/com/util/Views'
 import {atoms as a, useBreakpoints, useTheme, ViewStyleProp} from '#/alf'
 import {Button, ButtonText} from '#/components/Button'
 import * as Dialog from '#/components/Dialog'
@@ -37,6 +34,7 @@ import {Person_Stroke2_Corner0_Rounded as Person} from '#/components/icons/Perso
 import * as LabelingService from '#/components/LabelingServiceCard'
 import * as Layout from '#/components/Layout'
 import {InlineLinkText, Link} from '#/components/Link'
+import {ListMaybePlaceholder} from '#/components/Lists'
 import {Loader} from '#/components/Loader'
 import {GlobalLabelPreference} from '#/components/moderation/LabelPreference'
 import {Text} from '#/components/Typography'
@@ -75,35 +73,22 @@ function ErrorState({error}: {error: string}) {
 export function ModerationScreen(
   _props: NativeStackScreenProps<CommonNavigatorParams, 'Moderation'>,
 ) {
-  const t = useTheme()
   const {_} = useLingui()
   const {
     isLoading: isPreferencesLoading,
     error: preferencesError,
     data: preferences,
   } = usePreferencesQuery()
-  const {gtMobile} = useBreakpoints()
-  const {height} = useSafeAreaFrame()
 
   const isLoading = isPreferencesLoading
   const error = preferencesError
 
   return (
     <Layout.Screen testID="moderationScreen">
-      <CenteredView
-        testID="moderationScreen"
-        style={[
-          t.atoms.border_contrast_low,
-          t.atoms.bg,
-          {minHeight: height},
-          ...(gtMobile ? [a.border_l, a.border_r] : []),
-        ]}>
-        <ViewHeader title={_(msg`Moderation`)} showOnDesktop />
-
+      <ViewHeader title={_(msg`Moderation`)} showOnDesktop />
+      <Layout.Content>
         {isLoading ? (
-          <View style={[a.w_full, a.align_center, a.pt_2xl]}>
-            <Loader size="xl" fill={t.atoms.text.color} />
-          </View>
+          <ListMaybePlaceholder isLoading={true} sideBorders={false} />
         ) : error || !preferences ? (
           <ErrorState
             error={
@@ -114,7 +99,7 @@ export function ModerationScreen(
         ) : (
           <ModerationScreenInner preferences={preferences} />
         )}
-      </CenteredView>
+      </Layout.Content>
     </Layout.Screen>
   )
 }
@@ -169,7 +154,7 @@ export function ModerationScreenInner({
   } = useMyLabelersQuery()
 
   useFocusEffect(
-    React.useCallback(() => {
+    useCallback(() => {
       setMinimalShellMode(false)
     }, [setMinimalShellMode]),
   )
@@ -183,7 +168,7 @@ export function ModerationScreenInner({
   const ageNotSet = !preferences.userAge
   const isUnderage = (preferences.userAge || 0) < 18
 
-  const onToggleAdultContentEnabled = React.useCallback(
+  const onToggleAdultContentEnabled = useCallback(
     async (selected: boolean) => {
       try {
         await setAdultContentPref({
@@ -201,13 +186,7 @@ export function ModerationScreenInner({
   const disabledOnIOS = isIOS && !adultContentEnabled
 
   return (
-    <ScrollView
-      contentContainerStyle={[
-        a.border_0,
-        a.pt_2xl,
-        a.px_lg,
-        gtMobile && a.px_2xl,
-      ]}>
+    <View style={[a.pt_2xl, a.px_lg, gtMobile && a.px_2xl]}>
       <Text
         style={[a.text_md, a.font_bold, a.pb_md, t.atoms.text_contrast_high]}>
         <Trans>Moderation tools</Trans>
@@ -420,7 +399,7 @@ export function ModerationScreenInner({
         <View style={[a.rounded_sm, t.atoms.bg_contrast_25]}>
           {labelers.map((labeler, i) => {
             return (
-              <React.Fragment key={labeler.creator.did}>
+              <Fragment key={labeler.creator.did}>
                 {i !== 0 && <Divider />}
                 <LabelingService.Link labeler={labeler}>
                   {state => (
@@ -457,12 +436,12 @@ export function ModerationScreenInner({
                     </LabelingService.Outer>
                   )}
                 </LabelingService.Link>
-              </React.Fragment>
+              </Fragment>
             )
           })}
         </View>
       )}
-      <View style={{height: 200}} />
-    </ScrollView>
+      <View style={{height: 150}} />
+    </View>
   )
 }
diff --git a/src/screens/Onboarding/Layout.tsx b/src/screens/Onboarding/Layout.tsx
index 54821532c..059cdfd5c 100644
--- a/src/screens/Onboarding/Layout.tsx
+++ b/src/screens/Onboarding/Layout.tsx
@@ -1,13 +1,11 @@
 import React from 'react'
-import {View} from 'react-native'
-import Animated from 'react-native-reanimated'
+import {ScrollView, View} from 'react-native'
 import {useSafeAreaInsets} from 'react-native-safe-area-context'
 import {msg} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
 import {isWeb} from '#/platform/detection'
 import {useOnboardingDispatch} from '#/state/shell'
-import {ScrollView} from '#/view/com/util/Views'
 import {Context} from '#/screens/Onboarding/state'
 import {
   atoms as a,
@@ -36,7 +34,7 @@ export function Layout({children}: React.PropsWithChildren<{}>) {
   const {gtMobile} = useBreakpoints()
   const onboardDispatch = useOnboardingDispatch()
   const {state, dispatch} = React.useContext(Context)
-  const scrollview = React.useRef<Animated.ScrollView>(null)
+  const scrollview = React.useRef<ScrollView>(null)
   const prevActiveStep = React.useRef<string>(state.activeStep)
 
   React.useEffect(() => {
diff --git a/src/screens/Post/PostLikedBy.tsx b/src/screens/Post/PostLikedBy.tsx
index 6fc485f34..d35d33243 100644
--- a/src/screens/Post/PostLikedBy.tsx
+++ b/src/screens/Post/PostLikedBy.tsx
@@ -5,13 +5,10 @@ import {useFocusEffect} from '@react-navigation/native'
 
 import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
 import {makeRecordUri} from '#/lib/strings/url-helpers'
-import {isWeb} from '#/platform/detection'
 import {useSetMinimalShellMode} from '#/state/shell'
 import {PostLikedBy as PostLikedByComponent} from '#/view/com/post-thread/PostLikedBy'
 import {ViewHeader} from '#/view/com/util/ViewHeader'
-import {CenteredView} from '#/view/com/util/Views'
 import * as Layout from '#/components/Layout'
-import {ListHeaderDesktop} from '#/components/Lists'
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostLikedBy'>
 export const PostLikedByScreen = ({route}: Props) => {
@@ -28,11 +25,8 @@ export const PostLikedByScreen = ({route}: Props) => {
 
   return (
     <Layout.Screen>
-      <CenteredView sideBorders={true}>
-        <ListHeaderDesktop title={_(msg`Liked By`)} />
-        <ViewHeader title={_(msg`Liked By`)} showBorder={!isWeb} />
-        <PostLikedByComponent uri={uri} />
-      </CenteredView>
+      <ViewHeader title={_(msg`Liked By`)} />
+      <PostLikedByComponent uri={uri} />
     </Layout.Screen>
   )
 }
diff --git a/src/screens/Post/PostQuotes.tsx b/src/screens/Post/PostQuotes.tsx
index 71dd8ad8d..2cd6be879 100644
--- a/src/screens/Post/PostQuotes.tsx
+++ b/src/screens/Post/PostQuotes.tsx
@@ -11,7 +11,6 @@ import {PostQuotes as PostQuotesComponent} from '#/view/com/post-thread/PostQuot
 import {ViewHeader} from '#/view/com/util/ViewHeader'
 import {CenteredView} from '#/view/com/util/Views'
 import * as Layout from '#/components/Layout'
-import {ListHeaderDesktop} from '#/components/Lists'
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostQuotes'>
 export const PostQuotesScreen = ({route}: Props) => {
@@ -29,7 +28,6 @@ export const PostQuotesScreen = ({route}: Props) => {
   return (
     <Layout.Screen>
       <CenteredView sideBorders={true}>
-        <ListHeaderDesktop title={_(msg`Quotes`)} />
         <ViewHeader title={_(msg`Quotes`)} showBorder={!isWeb} />
         <PostQuotesComponent uri={uri} />
       </CenteredView>
diff --git a/src/screens/Post/PostRepostedBy.tsx b/src/screens/Post/PostRepostedBy.tsx
index c1e8b2987..304e70808 100644
--- a/src/screens/Post/PostRepostedBy.tsx
+++ b/src/screens/Post/PostRepostedBy.tsx
@@ -11,7 +11,6 @@ import {PostRepostedBy as PostRepostedByComponent} from '#/view/com/post-thread/
 import {ViewHeader} from '#/view/com/util/ViewHeader'
 import {CenteredView} from '#/view/com/util/Views'
 import * as Layout from '#/components/Layout'
-import {ListHeaderDesktop} from '#/components/Lists'
 
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'PostRepostedBy'>
 export const PostRepostedByScreen = ({route}: Props) => {
@@ -29,7 +28,6 @@ export const PostRepostedByScreen = ({route}: Props) => {
   return (
     <Layout.Screen>
       <CenteredView sideBorders={true}>
-        <ListHeaderDesktop title={_(msg`Reposted By`)} />
         <ViewHeader title={_(msg`Reposted By`)} showBorder={!isWeb} />
         <PostRepostedByComponent uri={uri} />
       </CenteredView>
diff --git a/src/screens/Profile/KnownFollowers.tsx b/src/screens/Profile/KnownFollowers.tsx
index 7e396c350..d6dd15c69 100644
--- a/src/screens/Profile/KnownFollowers.tsx
+++ b/src/screens/Profile/KnownFollowers.tsx
@@ -15,14 +15,22 @@ import {ProfileCardWithFollowBtn} from '#/view/com/profile/ProfileCard'
 import {List} from '#/view/com/util/List'
 import {ViewHeader} from '#/view/com/util/ViewHeader'
 import * as Layout from '#/components/Layout'
-import {
-  ListFooter,
-  ListHeaderDesktop,
-  ListMaybePlaceholder,
-} from '#/components/Lists'
+import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
 
-function renderItem({item}: {item: AppBskyActorDefs.ProfileViewBasic}) {
-  return <ProfileCardWithFollowBtn key={item.did} profile={item} />
+function renderItem({
+  item,
+  index,
+}: {
+  item: AppBskyActorDefs.ProfileViewBasic
+  index: number
+}) {
+  return (
+    <ProfileCardWithFollowBtn
+      key={item.did}
+      profile={item}
+      noBorder={index === 0}
+    />
+  )
 }
 
 function keyExtractor(item: AppBskyActorDefs.ProfileViewBasic) {
@@ -93,6 +101,7 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
   if (followers.length < 1) {
     return (
       <Layout.Screen>
+        <ViewHeader title={_(msg`Followers you know`)} />
         <ListMaybePlaceholder
           isLoading={isDidLoading || isFollowersLoading}
           isError={isError}
@@ -100,6 +109,8 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
           emptyMessage={_(msg`You don't follow any users who follow @${name}.`)}
           errorMessage={cleanError(resolveError || error)}
           onRetry={isError ? refetch : undefined}
+          topBorder={false}
+          sideBorders={false}
         />
       </Layout.Screen>
     )
@@ -116,9 +127,6 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
         onRefresh={onRefresh}
         onEndReached={onEndReached}
         onEndReachedThreshold={4}
-        ListHeaderComponent={
-          <ListHeaderDesktop title={_(msg`Followers you know`)} />
-        }
         ListFooterComponent={
           <ListFooter
             isFetchingNextPage={isFetchingNextPage}
@@ -130,6 +138,7 @@ export const ProfileKnownFollowersScreen = ({route}: Props) => {
         desktopFixedHeight
         initialNumToRender={initialNumToRender}
         windowSize={11}
+        sideBorders={false}
       />
     </Layout.Screen>
   )
diff --git a/src/screens/Profile/Sections/Labels.tsx b/src/screens/Profile/Sections/Labels.tsx
index 67c827d90..6c76d7b15 100644
--- a/src/screens/Profile/Sections/Labels.tsx
+++ b/src/screens/Profile/Sections/Labels.tsx
@@ -15,10 +15,11 @@ import {isLabelerSubscribed, lookupLabelValueDefinition} from '#/lib/moderation'
 import {useScrollHandlers} from '#/lib/ScrollContext'
 import {isNative} from '#/platform/detection'
 import {ListRef} from '#/view/com/util/List'
-import {CenteredView, ScrollView} from '#/view/com/util/Views'
+import {ScrollView} from '#/view/com/util/Views'
 import {atoms as a, useTheme} from '#/alf'
 import {Divider} from '#/components/Divider'
 import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
+import * as Layout from '#/components/Layout'
 import {Loader} from '#/components/Loader'
 import {LabelerLabelPreference} from '#/components/moderation/LabelPreference'
 import {Text} from '#/components/Typography'
@@ -75,7 +76,7 @@ export const ProfileLabelsSection = React.forwardRef<
   }, [isFocused, scrollElRef, setScrollViewTag])
 
   return (
-    <CenteredView style={{flex: 1, minHeight}} sideBorders>
+    <Layout.Center style={{flex: 1, minHeight}}>
       {isLabelerLoading ? (
         <View style={[a.w_full, a.align_center]}>
           <Loader size="xl" />
@@ -95,7 +96,7 @@ export const ProfileLabelsSection = React.forwardRef<
           headerHeight={headerHeight}
         />
       )}
-    </CenteredView>
+    </Layout.Center>
   )
 })
 
diff --git a/src/screens/Settings/AboutSettings.tsx b/src/screens/Settings/AboutSettings.tsx
index 8019a20f9..02976bb3c 100644
--- a/src/screens/Settings/AboutSettings.tsx
+++ b/src/screens/Settings/AboutSettings.tsx
@@ -21,7 +21,15 @@ export function AboutSettingsScreen({}: Props) {
 
   return (
     <Layout.Screen>
-      <Layout.Header title={_(msg`About`)} />
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>
+            <Trans>About</Trans>
+          </Layout.Header.TitleText>
+        </Layout.Header.Content>
+        <Layout.Header.Slot />
+      </Layout.Header.Outer>
       <Layout.Content>
         <SettingsList.Container>
           <SettingsList.LinkItem
diff --git a/src/screens/Settings/AccessibilitySettings.tsx b/src/screens/Settings/AccessibilitySettings.tsx
index 6ab0131d9..ee26697d2 100644
--- a/src/screens/Settings/AccessibilitySettings.tsx
+++ b/src/screens/Settings/AccessibilitySettings.tsx
@@ -39,7 +39,15 @@ export function AccessibilitySettingsScreen({}: Props) {
 
   return (
     <Layout.Screen>
-      <Layout.Header title={_(msg`Accessibility`)} />
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>
+            <Trans>Accessibility</Trans>
+          </Layout.Header.TitleText>
+        </Layout.Header.Content>
+        <Layout.Header.Slot />
+      </Layout.Header.Outer>
       <Layout.Content>
         <SettingsList.Container>
           <SettingsList.Group contentContainerStyle={[a.gap_sm]}>
diff --git a/src/screens/Settings/AccountSettings.tsx b/src/screens/Settings/AccountSettings.tsx
index 2495a0f2f..634c9d3f7 100644
--- a/src/screens/Settings/AccountSettings.tsx
+++ b/src/screens/Settings/AccountSettings.tsx
@@ -38,7 +38,15 @@ export function AccountSettingsScreen({}: Props) {
 
   return (
     <Layout.Screen>
-      <Layout.Header title={_(msg`Account`)} />
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>
+            <Trans>Account</Trans>
+          </Layout.Header.TitleText>
+        </Layout.Header.Content>
+        <Layout.Header.Slot />
+      </Layout.Header.Outer>
       <Layout.Content>
         <SettingsList.Container>
           <SettingsList.Item>
diff --git a/src/screens/Settings/AppIconSettings.tsx b/src/screens/Settings/AppIconSettings.tsx
index 1dd87d45f..18fcd5e30 100644
--- a/src/screens/Settings/AppIconSettings.tsx
+++ b/src/screens/Settings/AppIconSettings.tsx
@@ -1,7 +1,7 @@
 import React from 'react'
 import {Alert, View} from 'react-native'
 import {Image} from 'expo-image'
-import {msg} from '@lingui/macro'
+import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import * as AppIcon from '@mozzius/expo-dynamic-app-icon'
 import {NativeStackScreenProps} from '@react-navigation/native-stack'
@@ -20,7 +20,15 @@ export function AppIconSettingsScreen({}: Props) {
 
   return (
     <Layout.Screen>
-      <Layout.Header title={_('App Icon')} />
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>
+            <Trans>App Icon</Trans>
+          </Layout.Header.TitleText>
+        </Layout.Header.Content>
+        <Layout.Header.Slot />
+      </Layout.Header.Outer>
       <Layout.Content
         contentContainerStyle={[a.py_2xl, a.px_xl, {paddingBottom: 100}]}>
         <Text style={[a.text_lg, a.font_heavy]}>Defaults</Text>
diff --git a/src/screens/Settings/AppPasswords.tsx b/src/screens/Settings/AppPasswords.tsx
index 1ea0bd1b3..630d26ba7 100644
--- a/src/screens/Settings/AppPasswords.tsx
+++ b/src/screens/Settings/AppPasswords.tsx
@@ -44,7 +44,15 @@ export function AppPasswordsScreen({}: Props) {
 
   return (
     <Layout.Screen testID="AppPasswordsScreen">
-      <Layout.Header title={_(msg`App Passwords`)} />
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>
+            <Trans>App Passwords</Trans>
+          </Layout.Header.TitleText>
+        </Layout.Header.Content>
+        <Layout.Header.Slot />
+      </Layout.Header.Outer>
       <Layout.Content>
         {error ? (
           <ErrorScreen
diff --git a/src/screens/Settings/AppearanceSettings.tsx b/src/screens/Settings/AppearanceSettings.tsx
index 82c4ef97e..48c4a2d85 100644
--- a/src/screens/Settings/AppearanceSettings.tsx
+++ b/src/screens/Settings/AppearanceSettings.tsx
@@ -79,7 +79,15 @@ export function AppearanceSettingsScreen({}: Props) {
   return (
     <LayoutAnimationConfig skipExiting skipEntering>
       <Layout.Screen testID="preferencesThreadsScreen">
-        <Layout.Header title={_(msg`Appearance`)} />
+        <Layout.Header.Outer>
+          <Layout.Header.BackButton />
+          <Layout.Header.Content>
+            <Layout.Header.TitleText>
+              <Trans>Appearance</Trans>
+            </Layout.Header.TitleText>
+          </Layout.Header.Content>
+          <Layout.Header.Slot />
+        </Layout.Header.Outer>
         <Layout.Content>
           <SettingsList.Container>
             <AppearanceToggleButtonGroup
diff --git a/src/screens/Settings/ContentAndMediaSettings.tsx b/src/screens/Settings/ContentAndMediaSettings.tsx
index b3fb8c174..17f8fa506 100644
--- a/src/screens/Settings/ContentAndMediaSettings.tsx
+++ b/src/screens/Settings/ContentAndMediaSettings.tsx
@@ -32,7 +32,15 @@ export function ContentAndMediaSettingsScreen({}: Props) {
 
   return (
     <Layout.Screen>
-      <Layout.Header title={_(msg`Content and Media`)} />
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>
+            <Trans>Content & Media</Trans>
+          </Layout.Header.TitleText>
+        </Layout.Header.Content>
+        <Layout.Header.Slot />
+      </Layout.Header.Outer>
       <Layout.Content>
         <SettingsList.Container>
           <SettingsList.LinkItem
diff --git a/src/screens/Settings/ExternalMediaPreferences.tsx b/src/screens/Settings/ExternalMediaPreferences.tsx
index f7e081429..ae859295f 100644
--- a/src/screens/Settings/ExternalMediaPreferences.tsx
+++ b/src/screens/Settings/ExternalMediaPreferences.tsx
@@ -1,7 +1,6 @@
 import {Fragment} from 'react'
 import {View} from 'react-native'
-import {msg, Trans} from '@lingui/macro'
-import {useLingui} from '@lingui/react'
+import {Trans} from '@lingui/macro'
 
 import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
 import {
@@ -23,10 +22,17 @@ type Props = NativeStackScreenProps<
   'PreferencesExternalEmbeds'
 >
 export function ExternalMediaPreferencesScreen({}: Props) {
-  const {_} = useLingui()
   return (
     <Layout.Screen testID="externalMediaPreferencesScreen">
-      <Layout.Header title={_(msg`External Media Preferences`)} />
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>
+            <Trans>External Media Preferences</Trans>
+          </Layout.Header.TitleText>
+        </Layout.Header.Content>
+        <Layout.Header.Slot />
+      </Layout.Header.Outer>
       <Layout.Content>
         <SettingsList.Container>
           <SettingsList.Item>
diff --git a/src/screens/Settings/FollowingFeedPreferences.tsx b/src/screens/Settings/FollowingFeedPreferences.tsx
index 089491dd0..ea9455ab1 100644
--- a/src/screens/Settings/FollowingFeedPreferences.tsx
+++ b/src/screens/Settings/FollowingFeedPreferences.tsx
@@ -46,7 +46,15 @@ export function FollowingFeedPreferencesScreen({}: Props) {
 
   return (
     <Layout.Screen testID="followingFeedPreferencesScreen">
-      <Layout.Header title={_(msg`Following Feed Preferences`)} />
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>
+            <Trans>Following Feed Preferences</Trans>
+          </Layout.Header.TitleText>
+        </Layout.Header.Content>
+        <Layout.Header.Slot />
+      </Layout.Header.Outer>
       <Layout.Content>
         <SettingsList.Container>
           <SettingsList.Item>
diff --git a/src/screens/Settings/LanguageSettings.tsx b/src/screens/Settings/LanguageSettings.tsx
index a44e2fcec..096f92566 100644
--- a/src/screens/Settings/LanguageSettings.tsx
+++ b/src/screens/Settings/LanguageSettings.tsx
@@ -64,7 +64,15 @@ export function LanguageSettingsScreen({}: Props) {
 
   return (
     <Layout.Screen testID="PreferencesLanguagesScreen">
-      <Layout.Header title={_(msg`Languages`)} />
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>
+            <Trans>Languages</Trans>
+          </Layout.Header.TitleText>
+        </Layout.Header.Content>
+        <Layout.Header.Slot />
+      </Layout.Header.Outer>
       <Layout.Content>
         <SettingsList.Container>
           <SettingsList.Group iconInset={false}>
diff --git a/src/screens/Settings/NotificationSettings.tsx b/src/screens/Settings/NotificationSettings.tsx
index c5f7078c4..1c77b3148 100644
--- a/src/screens/Settings/NotificationSettings.tsx
+++ b/src/screens/Settings/NotificationSettings.tsx
@@ -33,7 +33,15 @@ export function NotificationSettingsScreen({}: Props) {
 
   return (
     <Layout.Screen>
-      <Layout.Header title={_(msg`Notification Settings`)} />
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>
+            <Trans>Notification Settings</Trans>
+          </Layout.Header.TitleText>
+        </Layout.Header.Content>
+        <Layout.Header.Slot />
+      </Layout.Header.Outer>
       <Layout.Content>
         {isQueryError ? (
           <Error
diff --git a/src/screens/Settings/PrivacyAndSecuritySettings.tsx b/src/screens/Settings/PrivacyAndSecuritySettings.tsx
index d695f830d..870ece4bf 100644
--- a/src/screens/Settings/PrivacyAndSecuritySettings.tsx
+++ b/src/screens/Settings/PrivacyAndSecuritySettings.tsx
@@ -29,7 +29,15 @@ export function PrivacyAndSecuritySettingsScreen({}: Props) {
 
   return (
     <Layout.Screen>
-      <Layout.Header title={_(msg`Privacy and Security`)} />
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>
+            <Trans>Privacy and Security</Trans>
+          </Layout.Header.TitleText>
+        </Layout.Header.Content>
+        <Layout.Header.Slot />
+      </Layout.Header.Outer>
       <Layout.Content>
         <SettingsList.Container>
           <SettingsList.Item>
diff --git a/src/screens/Settings/Settings.tsx b/src/screens/Settings/Settings.tsx
index 126a1bc88..7a4ad6f20 100644
--- a/src/screens/Settings/Settings.tsx
+++ b/src/screens/Settings/Settings.tsx
@@ -73,7 +73,15 @@ export function SettingsScreen({}: Props) {
 
   return (
     <Layout.Screen>
-      <Layout.Header title={_(msg`Settings`)} />
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>
+            <Trans>Settings</Trans>
+          </Layout.Header.TitleText>
+        </Layout.Header.Content>
+        <Layout.Header.Slot />
+      </Layout.Header.Outer>
       <Layout.Content>
         <SettingsList.Container>
           <View
diff --git a/src/screens/Settings/ThreadPreferences.tsx b/src/screens/Settings/ThreadPreferences.tsx
index d29daa58b..b1547e495 100644
--- a/src/screens/Settings/ThreadPreferences.tsx
+++ b/src/screens/Settings/ThreadPreferences.tsx
@@ -38,7 +38,15 @@ export function ThreadPreferencesScreen({}: Props) {
 
   return (
     <Layout.Screen testID="threadPreferencesScreen">
-      <Layout.Header title={_(msg`Thread Preferences`)} />
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>
+            <Trans>Thread Preferences</Trans>
+          </Layout.Header.TitleText>
+        </Layout.Header.Content>
+        <Layout.Header.Slot />
+      </Layout.Header.Outer>
       <Layout.Content>
         <SettingsList.Container>
           <SettingsList.Group>
diff --git a/src/screens/SignupQueued.tsx b/src/screens/SignupQueued.tsx
index ed261f29e..f1c36a69c 100644
--- a/src/screens/SignupQueued.tsx
+++ b/src/screens/SignupQueued.tsx
@@ -1,5 +1,5 @@
 import React from 'react'
-import {Modal, View} from 'react-native'
+import {Modal, ScrollView, View} from 'react-native'
 import {useSafeAreaInsets} from 'react-native-safe-area-context'
 import {StatusBar} from 'expo-status-bar'
 import {msg, plural, Trans} from '@lingui/macro'
@@ -9,7 +9,6 @@ import {logger} from '#/logger'
 import {isIOS, isWeb} from '#/platform/detection'
 import {isSignupQueued, useAgent, useSessionApi} from '#/state/session'
 import {useOnboardingDispatch} from '#/state/shell'
-import {ScrollView} from '#/view/com/util/Views'
 import {Logo} from '#/view/icons/Logo'
 import {atoms as a, native, useBreakpoints, useTheme, web} from '#/alf'
 import {Button, ButtonIcon, ButtonText} from '#/components/Button'
diff --git a/src/screens/StarterPack/Wizard/index.tsx b/src/screens/StarterPack/Wizard/index.tsx
index b0d71b929..b42b753e3 100644
--- a/src/screens/StarterPack/Wizard/index.tsx
+++ b/src/screens/StarterPack/Wizard/index.tsx
@@ -1,5 +1,5 @@
 import React from 'react'
-import {Keyboard, TouchableOpacity, View} from 'react-native'
+import {Keyboard, View} from 'react-native'
 import {KeyboardAwareScrollView} from 'react-native-keyboard-controller'
 import {useSafeAreaInsets} from 'react-native-safe-area-context'
 import {Image} from 'expo-image'
@@ -10,13 +10,12 @@ import {
   ModerationOpts,
 } from '@atproto/api'
 import {GeneratorView} from '@atproto/api/dist/client/types/app/bsky/feed/defs'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {msg, Plural, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 import {useFocusEffect, useNavigation} from '@react-navigation/native'
 import {NativeStackScreenProps} from '@react-navigation/native-stack'
 
-import {HITSLOP_10, STARTER_PACK_MAX_SIZE} from '#/lib/constants'
+import {STARTER_PACK_MAX_SIZE} from '#/lib/constants'
 import {useEnableKeyboardControllerScreen} from '#/lib/hooks/useEnableKeyboardController'
 import {createSanitizedDisplayName} from '#/lib/moderation/create-sanitized-display-name'
 import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types'
@@ -29,7 +28,7 @@ import {
   parseStarterPackUri,
 } from '#/lib/strings/starter-pack'
 import {logger} from '#/logger'
-import {isAndroid, isNative, isWeb} from '#/platform/detection'
+import {isNative} from '#/platform/detection'
 import {useModerationOpts} from '#/state/preferences/moderation-opts'
 import {useAllListMembersQuery} from '#/state/queries/list-members'
 import {useProfileQuery} from '#/state/queries/profile'
@@ -147,7 +146,6 @@ function WizardInner({
 }) {
   const navigation = useNavigation<NavigationProp>()
   const {_} = useLingui()
-  const t = useTheme()
   const setMinimalShellMode = useSetMinimalShellMode()
   const [state, dispatch] = useWizardState()
   const {currentAccount} = useSession()
@@ -283,45 +281,24 @@ function WizardInner({
 
   return (
     <CenteredView style={[a.flex_1]} sideBorders>
-      <View
-        style={[
-          a.flex_row,
-          a.pb_sm,
-          a.px_md,
-          a.border_b,
-          t.atoms.border_contrast_medium,
-          a.gap_sm,
-          a.justify_between,
-          a.align_center,
-          isAndroid && a.pt_sm,
-          isWeb && [a.py_md],
-        ]}>
-        <View style={[{width: 65}]}>
-          <TouchableOpacity
-            testID="viewHeaderDrawerBtn"
-            hitSlop={HITSLOP_10}
-            accessibilityRole="button"
-            accessibilityLabel={_(msg`Back`)}
-            accessibilityHint={_(msg`Go back to the previous step`)}
-            onPress={() => {
-              if (state.currentStep === 'Details') {
-                navigation.pop()
-              } else {
-                dispatch({type: 'Back'})
-              }
-            }}>
-            <FontAwesomeIcon
-              size={18}
-              icon="angle-left"
-              color={t.atoms.text.color}
-            />
-          </TouchableOpacity>
-        </View>
-        <Text style={[a.flex_1, a.font_bold, a.text_lg, a.text_center]}>
-          {currUiStrings.header}
-        </Text>
-        <View style={[{width: 65}]} />
-      </View>
+      <Layout.Header.Outer>
+        <Layout.Header.BackButton
+          label={_(msg`Back`)}
+          accessibilityHint={_(msg`Go back to the previous step`)}
+          onPress={evt => {
+            if (state.currentStep !== 'Details') {
+              evt.preventDefault()
+              dispatch({type: 'Back'})
+            }
+          }}
+        />
+        <Layout.Header.Content>
+          <Layout.Header.TitleText>
+            {currUiStrings.header}
+          </Layout.Header.TitleText>
+        </Layout.Header.Content>
+        <Layout.Header.Slot />
+      </Layout.Header.Outer>
 
       <Container>
         {state.currentStep === 'Details' ? (
@@ -463,17 +440,17 @@ function Footer({
                 <Trans>
                   <Text style={[a.font_bold, textStyles]}>You</Text> and
                   <Text> </Text>
-                  <Text style={[a.font_bold, textStyles]}>
+                  <Text style={[a.font_bold, textStyles]} emoji>
                     {getName(items[1] /* [0] is self, skip it */)}{' '}
                   </Text>
                   are included in your starter pack
                 </Trans>
               ) : items.length > 2 ? (
                 <Trans context="profiles">
-                  <Text style={[a.font_bold, textStyles]}>
+                  <Text style={[a.font_bold, textStyles]} emoji>
                     {getName(items[1] /* [0] is self, skip it */)},{' '}
                   </Text>
-                  <Text style={[a.font_bold, textStyles]}>
+                  <Text style={[a.font_bold, textStyles]} emoji>
                     {getName(items[2])},{' '}
                   </Text>
                   and{' '}
@@ -504,29 +481,29 @@ function Footer({
               {
                 items.length === 1 ? (
                   <Trans>
-                    <Text style={[a.font_bold, textStyles]}>
+                    <Text style={[a.font_bold, textStyles]} emoji>
                       {getName(items[0])}
                     </Text>{' '}
                     is included in your starter pack
                   </Trans>
                 ) : items.length === 2 ? (
                   <Trans>
-                    <Text style={[a.font_bold, textStyles]}>
+                    <Text style={[a.font_bold, textStyles]} emoji>
                       {getName(items[0])}
                     </Text>{' '}
                     and
                     <Text> </Text>
-                    <Text style={[a.font_bold, textStyles]}>
+                    <Text style={[a.font_bold, textStyles]} emoji>
                       {getName(items[1])}{' '}
                     </Text>
                     are included in your starter pack
                   </Trans>
                 ) : items.length > 2 ? (
                   <Trans context="feeds">
-                    <Text style={[a.font_bold, textStyles]}>
+                    <Text style={[a.font_bold, textStyles]} emoji>
                       {getName(items[0])},{' '}
                     </Text>
-                    <Text style={[a.font_bold, textStyles]}>
+                    <Text style={[a.font_bold, textStyles]} emoji>
                       {getName(items[1])},{' '}
                     </Text>
                     and{' '}