<section class="gallery-container"></section>
$black: #222;

*, *:before, *:after { box-sizing: border-box; }

html {
  font-size: 16px;
}

body {
  font-family: helvetica;
  position: relative;
  font-size: 100%;
  background: #D1913C;
  background: -webkit-linear-gradient(to right, #FFD194, #D1913C);
  background: linear-gradient(to right, #FFD194, #D1913C);
}
.gallery-container {
  padding: .9375rem 0;
}
.gallery-container h1 {
  margin: 2rem 0;
  padding: 0;
  text-align: center;
  color: #fff;
  text-transform: uppercase;
  font-size: 6.5vw;
  font-weight: lighter;
}
.gallery-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 8px;
  max-width: 1200px;
  width: 100%;
  margin: 0 auto;
  
  @include bp( x-small ) {
    grid-template-columns: repeat(1, 1fr);
  }	  
  @include bp( small ) {
    grid-template-columns: repeat(2, 1fr);
  }	
  // When above our large breakpoint, make sure we have 3 columns
  @include bp( large ) {
    grid-template-columns: repeat(3, 1fr);
  }	
  img {
    width: 100%;
    border: 5px solid #fff;
  }
}
.gallery-grid div {
  position: relative;
  cursor: pointer;
  &:before, &:after {
    transition: .3s opacity ease;
    opacity: 0;
  }
  &:after {
    content: '\02194';
    font-size: 80px;
    position: absolute;
    transform: translate3d(-50%, -50%, 0) rotate(-45deg);
    color: #fff;
    left: 50%;
    top: 50%;
    display: block;
  }
  &:before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 4px;
    left: 0;
    right: 0;
    background: rgba(#222, 0.5);
    display: block;
  }
  &:hover {
    &:before, &:after {
      opacity: 1;
      transition: .3s opacity ease;
    }
  }
}

.modal {
  position: fixed;
  z-index: 999;
  width: 50%;
  max-width: 800px;
  top: 50%;
  left: 50%;
  transform: translate3d(-50%, -50%, 0);
  @include bp( x-small ) {
    width: 95%;
  }	  
  @include bp( small ) {
    width: 80%;
  }	
  @include bp( large ) {
    width: 60%;
  }	
  img {
    width: 100%;
    border: 5px solid #fff;
  }
}

.modal-overlay {
  position: fixed;
  z-index: 1;
  height: 100%;
  width: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  top: 0;
  left: 0;
}
.modal-body a {
  position: absolute;
  display: inline;
  color: $black;
  text-decoration: none;
  line-height: 36px;
  font-size: 30px;
  font-weight: lighter;
  background: #fff;
  border-radius: 5px;
  height: 40px; width: 40px;
  text-align: center;
}
.modal-body .modal-close {
  right: 0; top: 0;
  border-radius: 0 0 0 5px;
}
.modal-body .modal-next,
.modal-body .modal-prev {
  right: 0; top: calc(50% - 25px);
  border-radius: 5px 0 0 5px;
  height: 50px;
  line-height: 40px;
  font-size: 60px;
}
.modal-body .modal-prev {
  left: 0;
  right: auto;
  border-radius: 0 5px 5px 0;
}
.modal-body {
  position: relative;
}
View Compiled
const imgUrls = ['https://source.unsplash.com/PC_lbSSxCZE/800x600','https://source.unsplash.com/lVmR1YaBGG4/800x600','https://source.unsplash.com/5KvPQc1Uklk/800x600','https://source.unsplash.com/GtYFwFrFbMA/800x600','https://source.unsplash.com/Igct8iZucFI/800x600','https://source.unsplash.com/M01DfkOqz7I/800x600','https://source.unsplash.com/MoI_cHNcSK8/800x600','https://source.unsplash.com/M0WbGFRTXqU/800x600','https://source.unsplash.com/s48nn4NtlZ4/800x600','https://source.unsplash.com/E4944K_4SvI/800x600','https://source.unsplash.com/F5Dxy9i8bxc/800x600','https://source.unsplash.com/iPum7Ket2jo/800x600'
];

class Gallery extends React.Component {
  constructor(props) {
    super(props);
    this.state = { currentIndex: null };
    this.closeModal = this.closeModal.bind(this);
    this.findNext = this.findNext.bind(this);
    this.findPrev = this.findPrev.bind(this);
    this.renderImageContent = this.renderImageContent.bind(this);
  }
  renderImageContent(src, index) {
    return (
      <div onClick={(e) => this.openModal(e, index)}>
        <img src={src} key={src} />
      </div>
    ) 
  }
  openModal(e, index) {
    this.setState ({ currentIndex: index });
  }
  closeModal(e) {
    if (e != undefined) {
      e.preventDefault();
    }
    this.setState ({ currentIndex: null });
  }
  findPrev(e) {
    if (e != undefined) {
      e.preventDefault();
    }
    this.setState(prevState => ({
      currentIndex: prevState.currentIndex -1
    }));
  }
  findNext(e) {
    if (e != undefined) {
      e.preventDefault();
    }
    this.setState(prevState => ({
      currentIndex: prevState.currentIndex + 1
    }));
  }
  render() {
    return (
      <div className="gallery-container">
        <h1>🔥 This Gallery Is Lit 🔥</h1>
        <div className="gallery-grid">
          {imgUrls.map(this.renderImageContent)}
        </div>
        <GalleryModal 
          closeModal={this.closeModal} 
          findPrev={this.findPrev} 
          findNext={this.findNext} 
          hasPrev={this.state.currentIndex > 0} 
          hasNext={this.state.currentIndex + 1 < imgUrls.length} 
          src={imgUrls[this.state.currentIndex]} 
        />
      </div>
    )
  }
}

class GalleryModal extends React.Component {
  constructor() {
    super();
    this.handleKeyDown = this.handleKeyDown.bind(this);
  }
  componentDidMount() {
    document.body.addEventListener('keydown', this.handleKeyDown);
  }  
  componentWillUnMount() {
    document.body.removeEventListener('keydown', this.handleKeyDown);
  }
  handleKeyDown(e) {
    if (e.keyCode === 27)
      this.props.closeModal();
    if (e.keyCode === 37 && this.props.hasPrev)
      this.props.findPrev();
    if (e.keyCode === 39 && this.props.hasNext)
      this.props.findNext();
  }
  render () {
    const { closeModal, hasNext, hasPrev, findNext, findPrev, src } = this.props;
    if (!src) {
      console.log('whut')
      return null;
    }
    return (
      <div>
        <div className="modal-overlay" onClick={closeModal}></div>
        <div isOpen={!!src} className="modal">
          <div className='modal-body'>
            <a href="#" className='modal-close' onClick={closeModal} onKeyDown={this.handleKeyDown}>&times;</a>
            {hasPrev && <a href="#" className='modal-prev' onClick={findPrev} onKeyDown={this.handleKeyDown}>&lsaquo;</a>}
            {hasNext && <a href="#" className='modal-next' onClick={findNext} onKeyDown={this.handleKeyDown}>&rsaquo;</a>}
            <img src={src} />
          </div>
        </div>
      </div>
    )
  }
}

ReactDOM.render(<Gallery />, document.querySelector('.gallery-container'));
View Compiled
Run Pen

External CSS

  1. https://codepen.io/tvweinstock/pen/OgEOWQ.scss

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react-dom.min.js