Search code examples
reactjsnext.jsstyled-components

Switch theme based images on scroll in React App with Styled Components


I have a Next.js App with styled components that has a day/night mode (i.e. standard/dark mode) based on the time of day a user is visiting the site. So far, so good. On some pages, though I have a dark hero, so the navigation needs to be light no matter if the currently activated theme is day or night. Above that, the navigation is pretty complex and uses multiple nested elements, borders, and icons.

The way I am achieving this is having a state of const [navBarStyle, setNavBarStyle] = useState('light') that changes once a user reaches a certain scroll position. Inside my styled-components, I am updating based on props and said state, e.g.

  border-width: 1px;
  border-style: solid;
  border-color: ${
  props => props.hasDedicatedHeroNavBar && props.currentNavBarStyle === 'light' ? ThemeSettings.colors.light
        : props.hasDedicatedHeroNavBar && props.currentNavBarStyle === 'default' ? ThemeSettings.mainColor
        : ThemeSettings.mainColor
      };

As you can see here now I had to divide the border properties into multiple lines as I can't pass theme settings inside template strings as it returns a function: background-image: url(function(props) { return getThemeValue(name,props,values); }); (copied from my browser dev panel).

It's easy to do that for settings I can split up (e.g. borders) but how can I do that for background-images, as the following does not work:

  background-image: ${
        props => props.hasDedicatedHeroNavBar && props.currentNavBarStyle === 'light' ? `url('/images/account-icon-light.svg')`
        : props.hasDedicatedHeroNavBar && props.currentNavBarStyle === 'default' ? `url(${ThemeSettings.accountIcon})`
        : `url(${ThemeSettings.accountIcon})`
  };

Solution

  • You can leverage the abstraction up to component level where you can specify your styles per each icon or other component

    const UniversalIconImg = (props) => {
      return props.hasDedicatedHeroNavBar && props.currentNavBarStyle === "light" ? (
        <IconLight {...props} />
      ) : props.hasDedicatedHeroNavBar && props.currentNavBarStyle === "default" ? (
        <IconDefault {...props} />
      ) : (
        <IconElse {...props} />
      );
    };
    
    const IconLight = styled.div`
      background-image: url("/images/account-icon-light.svg");
    `;
    
    const IconDefault = styled.div`
      background-image: url("${ThemeSettings.accountIcon}");
    `;
    
    ...