Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      The Case For Minimal WordPress Setups: A Contrarian View On Theme Frameworks

      May 19, 2025

      Sunshine And March Vibes (2025 Wallpapers Edition)

      May 19, 2025

      How To Fix Largest Contentful Paint Issues With Subpart Analysis

      May 19, 2025

      How To Prevent WordPress SQL Injection Attacks

      May 19, 2025

      My latest hands-on could be the best value AI laptop of the summer, but I still have questions

      May 19, 2025

      DOOM: The Dark Ages had the lowest Steam launch numbers in series history — Is it suffering from the ‘Game Pass Effect’?

      May 19, 2025

      Microsoft won’t be left exposed if something “catastrophic” happens to OpenAI — but may still be 3 to 6 months behind ChatGPT

      May 19, 2025

      Microsoft Copilot gets OpenAI’s GPT-4o image generation support — but maybe a day late and a dollar short for the hype?

      May 19, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      ES6: Set Vs Array- What and When?

      May 19, 2025
      Recent

      ES6: Set Vs Array- What and When?

      May 19, 2025

      Transform JSON into Typed Collections with Laravel’s AsCollection::of()

      May 19, 2025

      Deployer

      May 19, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      My latest hands-on could be the best value AI laptop of the summer, but I still have questions

      May 19, 2025
      Recent

      My latest hands-on could be the best value AI laptop of the summer, but I still have questions

      May 19, 2025

      DOOM: The Dark Ages had the lowest Steam launch numbers in series history — Is it suffering from the ‘Game Pass Effect’?

      May 19, 2025

      Microsoft won’t be left exposed if something “catastrophic” happens to OpenAI — but may still be 3 to 6 months behind ChatGPT

      May 19, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»How to make Storybook Interactions respect user motion preferences

    How to make Storybook Interactions respect user motion preferences

    November 20, 2024

    Recently, while browsing my company’s Storybook, I came across something that seemed broken: a flickering component that appeared to be re-rendering repeatedly. The open source tool that helps designers, developers, and others build and use reusable components was behaving weirdly. As I dug in, I realized I was seeing the unintended effects of the Storybook Interactions addon, which allows developers to simulate user interactions within a story, in action.

    Storybook Interactions can be a powerful tool, enabling developers to simulate and test user behaviors quickly. But if you’re unfamiliar with Interactions—especially if you’re just looking to explore available components—the simulated tests jumping around on the screen can feel disorienting.

    This can be especially jarring for users who have the prefers-reduced-motion setting enabled in their operating system. When these users encounter a story that includes an interaction, their preferences are ignored and they have no option to disable or enable it. Instead, the Storybook Interaction immediately plays on page load, regardless. These rapid screen movements can cause disorientation for users or in some cases can even trigger a seizure.

    Knowledge share

    Operating systems allow users to set a motion preference. Adhering to this setting can be critical for some users. For example, for users who have photosensitive epilepsy or vertigo, even a small animation can be life-threatening.

    This explicit preference for a reduced motion experience can be used by browsers, applications, and websites to reduce unnecessary animations and motions via the prefers-reduced-motion CSS media feature.

    At this time, Storybook does not have built-in capabilities to toggle interactions on or off. Until this feature can be baked in I am hoping this blog will provide you with an alternative way to make your work environment more inclusive. Now, let’s get into building an addon that respects user’s motion preferences and allows users to toggle interactions on and off.

    Goals

    1. Users with prefers-reduced-motion enabled MUST have interactions off by default.
    2. Users with prefers-reduced-motion enabled MUST have a way to toggle the feature on or off without altering their operating system user preferences.
    3. All users SHOULD have a way to toggle the feature on or off without altering their user preferences.

    Let’s get started

    Step 1: Build a Storybook addon

    Storybook allows developers to create custom addons. In this case, we will create one that will allow users to toggle Interactions on or off, while respecting the prefers-reduced-motion setting.

    Add the following code to a file in your project’s .storybook folder:

    import React, {useCallback, useEffect} from 'react'
    
    import {IconButton} from '@storybook/components'
    import {PlayIcon, StopIcon} from '@storybook/icons'
    
    export const ADDON_ID = 'toggle-interaction'
    export const TOOL_ID = `${ADDON_ID}/tool`
    
    export const INTERACTION_STORAGE_KEY = 'disableInteractions'
    
    export const InteractionToggle = () => {
      const [disableInteractions, setDisableInteractions] = React.useState(
           window?.localStorage.getItem(INTERACTION_STORAGE_KEY) === 'true',
      )
    
      useEffect(() => {
        const reducedMotion = matchMedia('(prefers-reduced-motion)')
    
        if (window?.localStorage.getItem(INTERACTION_STORAGE_KEY) === null && reducedMotion.matches) {
          window?.localStorage?.setItem(INTERACTION_STORAGE_KEY, 'true')
          setDisableInteractions(true)
        }
      }, [])
    
      const toggleMyTool = useCallback(() => {
        window?.localStorage?.setItem(INTERACTION_STORAGE_KEY, `${!disableInteractions}`)
        setDisableInteractions(!disableInteractions)
          // Refreshes the page to cause the interaction to stop/start
          window.location.reload()
    }, [disableInteractions, setDisableInteractions])
    
      return (
        <IconButton
          key={TOOL_ID}
          aria-label="Disable Interactions"
          onClick={toggleMyTool}
          defaultChecked={disableInteractions}
          aria-pressed={disableInteractions}
        >
          {disableInteractions ? <PlayIcon /> : <StopIcon />}
          Interactions
        </IconButton>
      )
    }
    

    Code breakdown

    This addon stores user preferences for Interactions using window.localStorage. When the addon first loads, it checks whether the preference is already set and, if so, it defaults to the user’s preference.

    const [disableInteractions, setDisableInteractions] = React.useState(
           window?.localStorage.getItem(INTERACTION_STORAGE_KEY) === 'true',
      )
    

    This useEffect hook checks if a user has their motion preferences set to prefers-reduced-motion and ensures that Interactions are turned off if the user hasn’t already set a preference in Storybook.

    useEffect(() => {
        const reducedMotion = matchMedia('(prefers-reduced-motion)')
    
        if (window?.localStorage.getItem(INTERACTION_STORAGE_KEY) === null && reducedMotion.matches) {
          window?.localStorage?.setItem(INTERACTION_STORAGE_KEY, 'true')
          setDisableInteractions(true)
        }
      }, [])
    

    When a user clicks the toggle button, preferences are updated and the page is refreshed to reflect the changes.

    const toggleMyTool = useCallback(() => {
        window?.localStorage?.setItem(INTERACTION_STORAGE_KEY, `${!disableInteractions}`)
        setDisableInteractions(!disableInteractions)
          // Refreshes the page to cause the interaction to stop/start
          window.location.reload()
      }, [disableInteractions, setDisableInteractions])
    

    Step 2: Register your new addon with Storybook

    In your .storybook/manager file, register your new addon:

    addons.register(ADDON_ID, () => {
      addons.add(TOOL_ID, {
        title: 'toggle interaction',
        type: types.TOOL as any,
        match: ({ viewMode, tabId }) => viewMode === 'story' && !tabId,
        render: () => <InteractionToggle />,
      })
    })
    

    This adds the toggle button to the Storybook toolbar, which will allow users to change their Storybook Interaction preferences.

    Step 3: Add functionality to check user preferences

    Finally, create a function that checks whether Interactions should be played and add it to your interaction stories:

    import {INTERACTION_STORAGE_KEY} from './.storybook/src/InteractionToggle'
    
    export const shouldInteractionPlay = () => {
      const disableInteractions = window?.localStorage?.getItem(INTERACTION_STORAGE_KEY)
      return disableInteractions === 'false' || disableInteractions === null
    }
    
    
     export const SomeComponentStory = {
      render: SomeComponent,
      play: async ({context}) => {
        if (shouldInteractionPlay()) {
    ...
        }
      })
     }
    

    Wrap-up

    With this custom addon, you can ensure your workplace remains accessible to users with motion sensitivities while benefiting from Storybook’s Interactions. For those with prefers-reduced-motion enabled, motion will be turned off by default and all users will be able to toggle interactions on or off.

    The post How to make Storybook Interactions respect user motion preferences appeared first on The GitHub Blog.

    Source: Read More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleInvoker Commands: Additional Ways to Work With Dialog, Popover… and More?
    Next Article Internal vs External Penetration Testing: Key Differences

    Related Posts

    Security

    Nmap 7.96 Launches with Lightning-Fast DNS and 612 Scripts

    May 19, 2025
    Common Vulnerabilities and Exposures (CVEs)

    CVE-2025-4915 – PHPGurukul Auto Taxi Stand Management System SQL Injection

    May 19, 2025
    Leave A Reply Cancel Reply

    Continue Reading

    Building SaaS Website #09: Mastering Schemas and Validation

    Development

    Laravel Development Environment Compared

    Development

    Best AI Tools in 2025

    Web Development

    The Code to Survival: Taiwan’s Cybersecurity Pivot Explained

    Development
    GetResponse

    Highlights

    Are you sick of AI? This iOS design app has pledged to never introduce it

    August 19, 2024

    If the continued rise of AI has you concerned that every creative app on the…

    Windows 11 Shutdown But User Stays Logged in: How to Fix it

    February 25, 2025

    Efficient Inference-Time Scaling for Flow Models: Enhancing Sampling Diversity and Compute Allocation

    March 29, 2025

    Cloudflare weaponizes AI against web crawlers

    March 22, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.