Files
bsky-social-app/src/components/Layout/README.md
T
Eric Bailey 143e2c802d [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>
2024-12-05 18:59:26 -06:00

5.4 KiB

Layout

This directory contains our core layout components. Use these when creating new screens, or when supplementing other components with functionality like centering.

Usage

If we aren't talking about the shell components, layouts on individual screens look like more or less like this:

<Outer>
  <Header>...</Header>
  <Content>...</Content>
</Outer>

I'll map these words to real components.

Layout.Screen

Provides the "Outer" functionality for a screen, like taking up the full height of the screen. All screens should be wrapped with this component, probably as the outermost component.

Note

On web, Layout.Screen also provides the side borders on our central content column. These borders are fixed position, 1px outside our center column width of 600px.

What this effectively means is that nothing inside the center content column needs (or should) define left/right borders. That is now handled in one place: within Layout.Screen.

Layout.Header.*

The Layout.Header component actually contains multiple sub-components. Use this to compose different versions of the header. The most basic version looks like this:

<Layout.Header.Outer>
  <Layout.Header.BackButton /> {/* or <Layout.Header.MenuButton /> */}

  <Layout.Header.Content>
    <Layout.Header.TitleText>Account</Layout.Header.TitleText>

    {/* Optional subtitle */}
    <Layout.Header.SubtitleText>Settings for @esb.lol</Layout.Header.SubtitleText>
  </Layout.Header.Content>

  <Layout.Header.Slot />
</Layout.Header.Outer>

Note the additional Slot component. This is here to keep the header balanced and provide correct spacing on all platforms. The Slot is 34px wide, which matches the BackButton and MenuButton.

If anyone has better ideas, I'm all ears, but this was simple and the small amount of boilerplate is only incurred when creating a new screen, which is infrequent.

It can also function as a "slot" for a button positioned on the right side. See the Hashtag screen for an example, abbreviated below:

<Layout.Header.Slot>
  <Button size='small' shape='round'>...</Button>
</Layout.Header.Slot>

If you need additional customization, simply use the components that are helpful and create new ones as needed. A good example is the SavedFeeds screen, which looks roughly like this:

<Layout.Header.Outer>
  <Layout.Header.BackButton />

  {/* Override to align content to the left, making room for the button */}
  <Layout.Header.Content align='left'>
    <Layout.Header.TitleText>Edit My Feeds</Layout.Header.TitleText>
  </Layout.Header.Content>

  {/* Custom button, wider than 34px */}
  <Button size='small'>...</Button>
</Layout.Header.Outer>

Tip

The Header should be outside the Content component in order to be fixed on scroll on native. Placing it inside will make it scroll with the rest of the page.

Layout.Content

This provides the "Content" functionality for a screen. This component is actually an Animated.ScrollView, and accepts props for that component. It provides a little default styling as well. On web, it also centers the content inside our center content column of 600px.

Note

What about flatlists or pagers? Those components are not colocated here (yet). But those components serve the same purpose of "Content".

Examples

The most basic layout available to us looks like this:

<Layout.Screen>
  <Layout.Header.Outer>
    <Layout.Header.BackButton /> {/* or <Layout.Header.MenuButton /> */}

    <Layout.Header.Content>
      <Layout.Header.TitleText>Account</Layout.Header.TitleText>

      {/* Optional subtitle */}
      <Layout.Header.SubtitleText>Settings for @esb.lol</Layout.Header.SubtitleText>
    </Layout.Header.Content>

    <Layout.Header.Slot />
  </Layout.Header.Outer>

  <Layout.Content>
    ...
  </Layout.Content>
</Layout.Screen>

For List views, you'd sub in List for Layout.Content and it will function the same. See Feeds screen for an example.

For Pager views, including PagerWithHeader, do the same. See Hashtag screen for an example.

Utilities

Layout.Center

This component behaves like our old CenteredView component.

Layout.SCROLLBAR_OFFSET and Layout.SCROLLBAR_OFFSET_POSITIVE

Provide a pre-configured CSS vars for use when aligning fixed position elements. More on this below.

Scrollbar gutter handling

Operating systems allow users to configure if their browser always shows scrollbars not. Some OSs also don't allow configuration.

The presence of scrollbars affects layout, particularly fixed position elements. Browsers support scrollbar-gutter, but each behaves differently. Our approach is to use the default scrollbar-gutter: auto. Basically, we start from a clean slate.

This handling becomes particularly thorny when we need to lock scroll, like when opening a dialog or dropdown. Radix uses the library react-remove-scroll internally, which in turn depends on react-remove-scroll-bar. We've opted to rely on this transient dependency. This library adds some utility classes and CSS vars to the page when scroll is locked.

It is this CSS variable that we use in SCROLLBAR_OFFSET values. This ensures that elements do not shift relative to the screen when opening a dropdown or dialog.

These styles are applied where needed and we should have very little need of adjusting them often.