React-modal: overlayClassName and styled-components not playing nicely

Created on 6 Feb 2018  Â·  9Comments  Â·  Source: reactjs/react-modal

I'm running into a problem trying to encapsulate my Modal styles with styled-components. The following approach fails to work and the defaultProps() route is a no go as well. After looking at how react-modal generates the modal elements outside of the <App/> root in the DOM, it's not surprising this is causing problems. It looks like buildClassName() is correctly adding the 'modal-overlay' className, yet even the second approach doesn't work. I suspect styled-components doesn't inject the CSS as it doesn't see any of it's children, or any of the <App/> children using it. Any ideas on how to get this to work to avoid using a global css class for the overlay?

const overlayClassName = 'modal-overlay'
const StyledModal = styled(Modal).attrs({
  overlayClassName
})`
  styles: here;

  &.${overlayClassName} {
    doesnt: work;
  }
  .${overlayClassName} {
    also-doesnt: work;
  }
`
combining libraries styling

Most helpful comment

I solved the problem without using .attrs().

function ReactModalAdapter ({ className, ...props }) {
  const contentClassName = `${className}__content`;
  const overlayClassName = `${className}__overlay`;
  return (
    <Modal
      portalClassName={className}
      className={contentClassName}
      overlayClassName={overlayClassName}
      {...props}
    />
  )
}

const StyledModal = styled(ReactModalAdapter)`

  &__overlay {
    position: fixed;
    top: 0px;
    left: 0px;
    right: 0px;
    bottom: 0px;
    background-color: rgba(255, 255, 255, 0.75);
  }

  &__content {
    position: absolute;
    top: 40px;
    left: 40px;
    right: 40px;
    bottom: 40px;
    border: 1px solid #ccc;
    background: #fff;
    overflow: auto;
    -webkit-overflow-scrolling: touch;
    borderRadius: 4px;
    outline: none;
    padding: 20px;
  }
`;

All 9 comments

For anyone searching, I solved this using a wrapper function, assuggested by @exogen

function ReactModalAdapter ({ className, modalClassName, ...props }) {
  return (
    <ReactModal
      className={modalClassName}
      portalClassName={className}
      {...props}
    />
  )
}

const StyledModal = styled(ReactModalAdapter).attrs({
  overlayClassName: 'Overlay',
  modalClassName: 'Modal'
})`
  .Modal {
    styles: here;
  }
  .Overlay {
    styles: here;
  }
`

@NathanielHill Were you getting this to work without using injectGlobal?

@christinechou I use this method, and yes, it works without any injectGlobal necessary.

The only thing you should need injectGlobal for is if you want the optional body styling like:

// Remove scroll on the body when react-modal is open.
injectGlobal`
  .ReactModal__Body--open {
    overflow: hidden;
  }
`;

(because that class is applied to the <body>, not our modal component)

Got it - so the reason this works is that it's overwriting the React Modal's portalClassName with the className being generated by styled components and attached to the component. I had it wrong (I was passing down the same className from the adapter wrapper into the ReactModal className). Thanks!

Exactly! :)

I needed to go one step further than the example above to get full control of styles:

const StyledModal = styled(ReactModalAdapter).attrs({
  overlayClassName: {
    base: "Overlay",
    afterOpen: "Overlay--after-open",
    beforeClose: "Overlay--before-close"
  },
  modalClassName: {
    base: "Modal",
    afterOpen: "Modal--after-open",
    beforeClose: "Modal--before-close"
  }
})`
  .Modal {
    &--after-open {
    }

    &--before-close {
    }
  }

  .Overlay {
    &--after-open {
    }

    &--before-close {
    }
  }
`;
`

Hope this helps someone :)

I solved the problem without using .attrs().

function ReactModalAdapter ({ className, ...props }) {
  const contentClassName = `${className}__content`;
  const overlayClassName = `${className}__overlay`;
  return (
    <Modal
      portalClassName={className}
      className={contentClassName}
      overlayClassName={overlayClassName}
      {...props}
    />
  )
}

const StyledModal = styled(ReactModalAdapter)`

  &__overlay {
    position: fixed;
    top: 0px;
    left: 0px;
    right: 0px;
    bottom: 0px;
    background-color: rgba(255, 255, 255, 0.75);
  }

  &__content {
    position: absolute;
    top: 40px;
    left: 40px;
    right: 40px;
    bottom: 40px;
    border: 1px solid #ccc;
    background: #fff;
    overflow: auto;
    -webkit-overflow-scrolling: touch;
    borderRadius: 4px;
    outline: none;
    padding: 20px;
  }
`;

I elaborated on @smagch's code for TypeScript. There is probably still some room for improvement for handling the transition classes:

import React from "react";
import styled from "styled-components";
import ReactModal from "react-modal";

interface Props extends ReactModal.Props {
  className?: string;
}

const ReactModalAdapter: React.SFC<Props> = ({ className, ...props }: Props) => {
  const contentClassName = `${className}__content`;
  const overlayClassName = `${className}__overlay`;
  return (
    <ReactModal
      portalClassName={className}
      className={contentClassName}
      overlayClassName={overlayClassName}
      {...props}
    />
  );
};

export const StyledModal = styled(ReactModalAdapter)`
  &__overlay {
    …
    &.ReactModal__Overlay--after-open {
      …
    }
    &.ReactModal__Overlay--before-close {
      …
    }
  }

  &__content {
    …
    &.ReactModal__Content--after-open {
      …
    }
    &.ReactModal__Content--before-close {
      …
    }
  }
`;

And if you're like me and you want to compulsively turn everything into a HoC, here's the recompose solution:

``ts const enhance = mapProps(({ className, ...rest }) => ({ portalClassName: className, className:${className}__content, overlayClassName:${className}__overlay`,
...rest
} as ReactModal.Props));

const Modal = styled(enhance(ReactModal))`
&__content {
....
}

&__overlay {
    ...
}

`;

Was this page helpful?
0 / 5 - 0 ratings

Related issues

CXJoyce picture CXJoyce  Â·  4Comments

gavmck picture gavmck  Â·  3Comments

petertdinh picture petertdinh  Â·  4Comments

emwee picture emwee  Â·  4Comments

jrock17 picture jrock17  Â·  4Comments