Skip to content

allow subscribing to usePreventScroll state #4782

Open
@Talor-A

Description

@Talor-A

🙋 Feature Request

we have a header component with position:fixed overlaid on the main body content. when a modal, menu, or popover opens, padding is added to the <html> element to compensate for the removed scrollbar.

however, no padding is added to our fixed position <header>, so the content inside jumps a number of px equal to the size of the scrollbar. this means that menu triggers in the header move around, so they become much harder to toggle!

it would be great to have a way to know that scrolling has been locked, so that we could apply custom styles to any elements that need it besides the body.

🤔 Expected Behavior

make the state of usePreventScroll consumable from outside the context of the components that call the hook

😯 Current Behavior

there's no way to know whether scroll is being prevented. it's applied via inline styles onto the html element, and the state is stored in a locally scoped variable preventScrollCount

💁 Possible Solution

there are many ways to solve for this:

  1. apply a data-scroll-prevented attribute to the html element when locking scroll. this would allow using css selectors, event listeners, or observers to subscribe to the state.
  2. apply a custom class to the html element.
  3. expose a useIsScrollPrevented hook that allows subscribing to state in js-land. this would probably mean changing preventScrollCount to some kind of basic observable value / EventTarget, since it would be undesirable to use React.Context. I don't see an existing global state management solution in react-aria, but anything would work.

happy to help ship a feature if we can decide on the best path forward!

🔦 Context

💻 Examples

examples implementing the three suggestions above, respectively:

const Header = () => (
  <>
    <header className="site-header">{...}</header>
    <style>`
      html[data-scroll-prevented=true] header.site-header {
         marginRight: ${window.innerWidth - document.documentElement.clientWidth};
      }
      `
    </style>
  </>
)
const Header = () => (
  <>
    <header className="site-header">{...}</header>
    <style>`
      html.scroll-prevented header.site-header {
         marginRight: ${window.innerWidth - document.documentElement.clientWidth};
      }
      `
    </style>
  </>
)
const Header = () => {
  const isScrollPrevented = useIsScrollPrevented();

  return  (<>
    <header className={isScrollPrevented ? "site-header scroll-prevented" : "site-header"}>{...}</header>
    <style>`
      header.site-header.scroll-prevented {
         marginRight: ${window.innerWidth - document.documentElement.clientWidth};
      }
      `
    </style>
  </>)
}

🧢 Your Company/Team

https://replit.com

🎁 Tracking Ticket (optional)

(private: https://linear.app/replit/issue/I2C-125)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    • Status

      🩺 To Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions