htmlcssreactjschakra-ui

Wave design on a div border


I'm trying to clone some projects interfaces on dribble for study purposes and raise my css skills, i've selected the layout below:

enter image description here

As you can see it's a mobile interface but i'm using react dom to build it ( as i mention before, it's just for study purpose ).

My problem is about this wavy bordered div's on the top and the bottom of the layout, i have tried to follow some tutorials but i did not be able to do it.

What i've tried:

At first i have tried css to solve this, using position absulote an z-index to render divs to "hide" the purple background, but the final result was weird. Here is my code:

import { Flex, useBreakpointValue } from '@chakra-ui/react';
import { Box, Flex } from '@chakra-ui/react';


const Curve = () => (
    <Box
        position="absolute"
        height="33vh"
        width="100%"
        bottom="0"
        textAlign="center"
    >
        <Box
            content='""'
            display="block"
            position="absolute"
            borderRadius="100% 28%"
            width="69.5%"
            height="33vh"
            transform="translate(85%, 60%)"
            backgroundColor="#f8f9fc"
            top={5}
            left={-20}
        />
        <Box
            content='""'
            display="block"
            position="absolute"
            borderRadius="100% 45%"
            width="75%"
            height="33vh"
            backgroundColor="#3B3486"
            transform="translate(-4%, 40%)"
            zIndex="1"
            top={5}
            left={-20}
        />
    </Box>
);

export function Login() {
    return (
        <>
            <Box background="#3B3486" position="relative">
                <Flex pt="9rem" />
                <Curve />
            </Box>
            <Flex
                h={'70vh'}
                flex={1}
                justifyContent={'center'}
                alignItems={'center'}
            >
                Content
            </Flex>
        </>
    );
}

At last, i read that i can use SVG elements to handle it, but the final result was not responsive to other layouts. I would like to use css to solve it because it is the purpose of this clone. Is it possible to do it?


Solution

  • adsy's answer already show you the steps to use clip-path, so here's my attempt to recreate the path. (Click the "Full page" button and resize your windows to test responsiveness)

    :root {
      --bg: #3B3486;
    }
    
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    .svg {
      position: absolute;
      top: 0;
      left: 0;
      width: 0;
      height: 0;
    }
    
    body::before,
    body::after {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 33.33%;
      background-color: var(--bg);
      clip-path: url(#wavy-clip-path);
      z-index: -1;
    }
    
    body::after {
      top: unset;
      bottom: 0;
      scale: -1;
    }
    <svg class="svg">
      <clipPath id="wavy-clip-path" clipPathUnits="objectBoundingBox">
        <path d="M0,0 l1,0 c0,0.25 -0.15,0.33 -0.5,0.5 -0.33,0.15 -0.5,0.25 -0.5,0.5 z"></path>
      </clipPath>
    </svg>

    Alternatively, if you want pure CSS without SVG, you can try border-radius (the curve won't be exactly like your example though)

    :root {
      --bg: #3B3486;
    }
    
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    body {
      width: 100vw;
      height: 100vh;
      display: grid;
      grid-template-rows: 33.33% 33.33% 33.33%;
    }
    
    header,
    footer,
    main {
      position: relative;
    }
    
    header::before,
    footer::before,
    main::before,
    header::after,
    footer::after,
    main::after {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 50%;
      background-color: var(--bg);
      z-index: -1;
    }
    
    header::before {
      border-bottom-right-radius: 50% 100%;
    }
    
    header::after {
      top: unset;
      bottom: 0;
    }
    
    footer::before {
      border-top-left-radius: 50% 100%;
      top: unset;
      bottom: 0;
    }
    
    footer::after {
      z-index: -2;
    }
    
    main::before {
      border-top-left-radius: 50% 100%;
      top: unset;
      bottom: 100%;
      background-color: #fff;
    }
    
    main::after {
      border-bottom-right-radius: 50% 100%;
      top: 100%;
      background-color: #fff;
    }
    <header></header>
    <main></main>
    <footer></footer>