DEV Community

Cover image for Folding the Flat with CSS
Mahmoud Elmahdi
Mahmoud Elmahdi

Posted on

3 1

Folding the Flat with CSS

A pure CSS approach that folds offscreen panel flat.

In this article we’ll create an offscreen panel with folds effect and elastic motion content from within.

I saw tons of cool demos over the years on the internet that has been done with Canvas, Threejs and all these heavy techniques . I’ve looking for a simpler way since then!

I had that task the other day to build a chat panel for the project am working on that appears by hitting the “Send a Message” button on the user’s profile, so users can communicate with each other. After getting the main functionality done and fully working, based on our workflow I had to forward to the QA team after getting done so they start testing! But I did not forward for some reasons 🤷‍♂️. Since I had enough time I wanted to make that chat a foldable animated panel with a less effort 🦸‍♂️. A few moments later after scratching my beard I decided to use a little bit CSS transition and clip-path polygon(...) to accomplish this feature/task.

Demo

I'm using a CRA boilerplate in this demo to create a React component. But you can stick with whatever stack you're comfortable with.

Demo Link: https://elmahdim.github.io/OffScreenPanel/

OffScreenPanel Component

import React, { useState } from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import styles from './OffScreenPanel.module.css';
export const OffScreenPanel = (props) => {
  const [open, toggleVisibility] = useState(false);
  const toggle = () => toggleVisibility(!open);
  return (
    <div className={classNames('offScreenPanel', {
      [styles.open]: open
    })}>
      <button type="button" className={styles.button} onClick={toggle}>
        ....
      </button>
      <div className={styles.panel}>
        <div className={styles.body}>{open && props.children}</div>
      </div>
      <div role="presentation" className={styles.overlay} onClick={toggle} />
    </div >
  );
};
OffScreenPanel.propTypes = {
  children: PropTypes.any,
};
export default OffScreenPanel;
Enter fullscreen mode Exit fullscreen mode

The code above represents a functional OffScreenPanel component that using Hooks and CSS Modules Stylesheet.

Plain HTML

The main elements we need to fold/unfold our panel without React.

<div class="offScreenPanel open">
  <div class="panel">
    <div class="body">...</div>
  </div>
  <div role="presentation" class="overlay"></div>
</div>
Enter fullscreen mode Exit fullscreen mode

The class open is toggleable (via JavaScript) on the offScreenPanel element. It has no associated styles.

The panel element is responsable to fold/unfold its layout. We have two options to achieve this effect: add two more extra elements, or use CSS pseudo element!

I'm going to pick the second option which is using pseudo element (::before, ::after). It makes our markup cleaner and less HTML codes.

The inner content will be wrapped by the element body.

Styles

/*
 * panel:
 * is out of screen (offset right) by default
 */
.panel {
  position: fixed;
  top: 0;
  right: 0;
  width: 450px;
  bottom: 0;
  z-index: 2;
  transform: translateX(450px);
}

/*
 * panel:
 * on open we set its horizontally offset to "0"
 */
.open .panel {
  transform: translateX(0);
  transition: all 400ms ease;
}

/*
 * panel - the folding element [[]]
 * make each element half width of its parent (panel) size
 */
.panel::before,
.panel::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 225px;
  transition: all 400ms ease-out;
}

/*
 * panel - the folding element []]
 */
.panel::before {
  clip-path: polygon(100% 10%, 100% 0, 100% 100%, 100% 90%);
  left: 0;
}

/*
 * panel - the folding element []]
 */
.panel::after {
  background: #f0f0f0 linear-gradient(to right, #f7f7f7 0%, #fff 100%);
  clip-path: polygon(100% 50%, 100% 0, 100% 100%, 100% 50%);
  right: 0;
}

/*
 * panel - the folding element []]
 */
.open .panel::before,
.open .panel::after {
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}

/*
 * panel - the folding element [[]
 * giving the left panel a paper book like background,
 * off-white and light grey
 */
.open .panel::before {
  transition-delay: 400ms;
  background: #f0f0f0 linear-gradient(to right, #f3f3f3 0%, #f1f1f1 48%, #f1f1f1 100%);
}

/*
 * body, one thin line centered by default
 */
.body {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  left: 0;
  background-color: #fff;
  transition: all 300ms cubic-bezier(0.22, 0.61, 0.36, 1);
  z-index: 1;
  clip-path: polygon(50% 50%, 50% 50%, 50% 50%, 50% 50%);
}

/*
 * body, folded and fits its parent with on open
 */
.open .panel .body {
  transition-delay: 0.8s;
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0% 100%);
}

/*
 * overlay, hidden by default. to overlap the content behind
 * and closes the panel on click outside
 */
.overlay {
  background-color: rgba(0, 0, 0, 0.2);
  position: fixed;
  visibility: hidden;
  top: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
  opacity: 0;
}

/*
 * overlay, visible on open
 */
.open .panel + .overlay {
  opacity: 1;
  visibility: visible;
  transition: all 400ms ease;
}
Enter fullscreen mode Exit fullscreen mode

Here's how it looks on both default and open states

Panels


LinkedIn | Twitter

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay