12

I'd like to create a sigmoid curve-like shape for a full-screen layout that would reveal decorative patterned background on one side and have the solid color background on the other side, for text to be placed on top of it.

The goal is to have a full-screen page with a sigmoid-like top-left side filled with pattern, and the rest of the page just to have white background.

JSFiddle: Unfinished sigmoid curve

#container {
  padding-top: 10%;
  padding-bottom: 10%;
  background: white url(http://famok.com/wp-content/uploads/2016/10/WhiteOnWhite.jpg) top left / 26px 32px repeat;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
#parallelogram {
  margin-left: 35%;
  width: 100%;
  height: 900px;
  -webkit-transform: skew(-15deg);
  -moz-transform: skew(-15deg);
  -o-transform: skew(-15deg);
  transform: skew(-15deg);
  background: white;
  -moz-border-radius: 100px;
  -webkit-border-radius: 100px;
  border-radius: 100px;
  -moz-box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
  -webkit-box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
  box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
}
<div id="container">
  <div id="parallelogram">
  </div>
</div>

I can't figure out how to create (or simulate) inverted rounded corner near lower left corner.

Or perhaps there's a conceptually different (better) solution available?

Update: I figured out how to create the shape I need entirely with CSS.

#container {
  padding-top: 100px;
  background: red;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
#parallelogram {
  margin-left: 400px;
  width: 100%;
  height: 900px;
  -webkit-transform: skew(-15deg);
  -moz-transform: skew(-15deg);
  -o-transform: skew(-15deg);
  transform: skew(-15deg);
  background: white;
  -moz-border-top-left-radius: 100px;
  -webkit-top-left-border-radius: 100px;
  border-top-left-radius: 100px;
}
#bottom {
  height: 200px;
  width: 100%;
  background: white;
}
#bottom-corner {
  height: 100px;
  width: 300px;
  margin-left: -34px;
  background: red;
  -moz-border-bottom-right-radius: 100px;
  -webkit-bottom-right-border-radius: 100px;
  border-bottom-right-radius: 100px;
  -webkit-transform: skew(-15deg);
  -moz-transform: skew(-15deg);
  -o-transform: skew(-15deg);
  transform: skew(-15deg);
}
<div id="container">
  <div id="parallelogram">
  </div>

  <div id="bottom">
    <div id="bottom-corner">
    </div>
  </div>
</div>

However, this is still not the final solution because the shape doesn't allow me to use the kind of background effects I have in mind. Here's what happens when I try: fiddle.

#container {
  padding-top: 100px;
  background: white url(http://famok.com/wp-content/uploads/2016/10/WhiteOnWhite.jpg) bottom left / 26px 32px repeat;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
#parallelogram {
  margin-left: 400px;
  width: 100%;
  height: 900px;
  -webkit-transform: skew(-15deg);
  -moz-transform: skew(-15deg);
  -o-transform: skew(-15deg);
  transform: skew(-15deg);
  background: white;
  -moz-border-top-left-radius: 100px;
  -webkit-top-left-border-radius: 100px;
  border-top-left-radius: 100px;
  -moz-box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
  -webkit-box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
  box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
}
#bottom {
  height: 200px;
  width: 100%;
  background: white;
  -moz-box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
  -webkit-box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
  box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
}
#bottom-corner {
  height: 100px;
  width: 300px;
  margin-left: -34px;
  background: white url(http://famok.com/wp-content/uploads/2016/10/WhiteOnWhite.jpg) top left / 26px 32px repeat;
  -moz-border-bottom-right-radius: 100px;
  -webkit-bottom-right-border-radius: 100px;
  border-bottom-right-radius: 100px;
  -webkit-transform: skew(-15deg);
  -moz-transform: skew(-15deg);
  -o-transform: skew(-15deg);
  transform: skew(-15deg);
  -moz-box-shadow: 0 0 15px rgba(0, 0, 0, .4);
  -webkit-box-shadow: 0 0 15px rgba(0, 0, 0, .4);
  box-shadow: 0 0 15px rgba(0, 0, 0, .4);
}
<div id="container">
  <div id="parallelogram">
  </div>

  <div id="bottom">
    <div id="bottom-corner">
    </div>
  </div>
</div>

Later update: after a bit of trial and error I ended up with a ridiculously crude hack solution that achieves the visual result I needed:

#container {
  padding-top: 100px;
  background: white url(http://famok.com/wp-content/uploads/2016/10/WhiteOnWhite.jpg) top left / 26px 32px repeat;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
#parallelogram {
  margin-left: 385px;
  width: 100%;
  height: 900px;
  -webkit-transform: skew(-15deg);
  -moz-transform: skew(-15deg);
  -o-transform: skew(-15deg);
  transform: skew(-15deg);
  background: white;
  -moz-border-top-left-radius: 100px;
  -webkit-top-left-border-radius: 100px;
  border-top-left-radius: 100px;
  -moz-box-shadow: inset 0 15px rgba(0, 0, 0, .4);
  -webkit-box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
  box-shadow: inset 0 0 15px rgba(0, 0, 0, .4);
}
#bottom-rounded-corner {
  height: 122px;
  position: relative;
  width: 200%;
  z-index: 1000;
  margin-top: -80px;
  margin-left: -185px;
  background: url(http://famok.com/wp-content/uploads/2016/11/CornerAndMask.png) top left no-repeat;
}
#bottom-white {
  height: 100px;
  width: 100%;
  background: white;
}
<div id="container">
  <div id="parallelogram">
  </div>
  <div id="bottom-rounded-corner">

  </div>
  <div id="bottom-white">
  </div>
</div>

Try as hard as I could to implement a conceptually better alternative suggested by Harry below, I was unable to use it to create the effect I wanted. I'd still be grateful if someone could help, either by showing how to do it, or by proposing optimizations to my solution.

Thank you in advance!

Harry
  • 87,580
  • 25
  • 202
  • 214
Dimitri Vorontzov
  • 7,834
  • 12
  • 48
  • 76
  • 1
    No, don't use CSS for such effects. They're very tricky. You'd be better off using SVG. There a couple of CSS methods to create a wave like pattern (or a sigmoid curve) in this thread - http://stackoverflow.com/questions/27777470/wave-or-shape-with-border-on-css3/27780572#27780572 but there is also one SVG solution. A quick comparison would tell you which one is better to use :) – Harry Nov 01 '16 at 05:56
  • Thank you very much @Harry! SVG appears to be exactly what I needed. Could you please help me further? Considering that I need full-screen sigmoid shape - horizontal line on the lower left, curved angle and rising toward upper right, and then curved angle and horizontal line on the right - how can this be achieved with SVG? Can you please post it as an answer, so that I could accept? – Dimitri Vorontzov Nov 01 '16 at 11:27
  • The SVG answer that I linked in the earlier thread does have the horizontal line and the curves + SVGs are scaleable by default. I'd suggest you to try adapting that SVG to your case and post the SVG code in the answer if you run into a brick wall. – Harry Nov 01 '16 at 14:04
  • Thank you @Harry. Another question - how does one add the inset box-shadows to SVG? The solution offered in that separate thread doesn't seem to allow for that, unless I'm mistaken. – Dimitri Vorontzov Nov 01 '16 at 14:33
  • Sorry, I don't have time to draft an answer now but you can have a look at this thread for creating inset shadows in SVG - http://stackoverflow.com/questions/20778568/how-to-make-an-inset-drop-shadow-in-svg – Harry Nov 01 '16 at 14:38

1 Answers1

12

Use SVG for complex shapes and not CSS:

As I had mentioned in my comments, please do not use CSS for creating such complex shapes. SVG is the recommended tool for such complex stuff. SVGs are easy to create, maintain and they are also responsive (scaleable) by default and so it has a lot of advantages.


Creating the sigmoid shape:

Creating the sigmoid curve shape with SVG itself is pretty simple and just needs one path element:

  • M0,750 moves the imaginary pen close to the bottom-left of the SVG element (co-ordinates are set slightly lower than the SVG's height so that there is a gap at the bottom where the shadow will be visible).
  • L250,750 produces a horizontal Line from the point (0,768) to (250,768)
  • C650,730 500,154 1024,154 creates the actual curve. Here the first two coordinates are control points of the curve ((650,730), (500,154)) and the third one is the end point (1024,154). Curvature of the curve can be adjusted by modifying the control points.
  • L1024,0 0,0 0,750 is for completing the shape. The shape needs to be complete for fill to work.

body {
  margin: 0;
}
svg {
  width: 100%;
  height: 100vh;
}
<svg viewBox='0 0 1024 768' preserveAspectRatio='none'>

  <!-- For the shadow -->
  <defs>
    <filter id="dropShadow">
      <feGaussianBlur in="SourceAlpha" stdDeviation="6" />
      <feOffset dx="3" dy="3" result="offsetBlur" />
      <feFlood flood-color="#AAA" flood-opacity="1" result="offsetColor" />
      <feComposite in="offsetColor" in2="offsetBlur" operator="in" result="offsetBlur" />
      <feMerge>
        <feMergeNode />
        <feMergeNode in="SourceGraphic" />
      </feMerge>
    </filter>
  </defs>
  <!-- End of shadow -->

  <!-- For filling the top-left with pattern -->
  <pattern id='dots' patternUnits='userSpaceOnUse' width='25' height='25'>
    <polygon points='0,0 0,25 25,25 25,0' fill='yellowgreen' />
    <circle cx='12.5' cy='12.5' r='4' fill='rebeccapurple' />
  </pattern>
  <!-- End of pattern -->

  <!-- Actual sigmoid curve -->
  <path d='M0,750 L250,750 C650,730 500,154 1024,154 L1024,0 0,0 0,750' fill='url(#dots)' filter='url(#dropShadow)' />

</svg>

Applying Pattern to the Shape:

In the above demo, I had created a polka dot pattern using the polygon and circle elements but it is not mandatory to create it within SVG itself, we can also use image element and fill the shape with the image pattern.

If you want to change the background image (pattern) into another image of your choice, just specify the URL of your image in the xlink:href attribute of the image tag like in the below snippet. Based on your needs and image, you may have to change the height and width of pattern and image.

<pattern id='dots' patternUnits='userSpaceOnUse' width='25' height='25'>
  <image xlink:href='https://yourwebsite.com/yourpath' x='0' y='0' width='15' height='15' />
</pattern>

body {
  margin: 0;
}
svg {
  width: 100%;
  height: 100vh;
}
<svg viewBox='0 0 1024 768' preserveAspectRatio='none'>
  <defs>
    <filter id="dropShadow">
      <feGaussianBlur in="SourceAlpha" stdDeviation="6" />
      <feOffset dx="3" dy="3" result="offsetBlur" />
      <feFlood flood-color="#AAA" flood-opacity="1" result="offsetColor" />
      <feComposite in="offsetColor" in2="offsetBlur" operator="in" result="offsetBlur" />
      <feMerge>
        <feMergeNode />
        <feMergeNode in="SourceGraphic" />
      </feMerge>
    </filter>
    <pattern id='dots' patternUnits='userSpaceOnUse' width='36.6' height='46'>
      <image xlink:href='http://famok.com/wp-content/uploads/2016/10/WhiteOnWhite.jpg' x='0' y='0' width='36.6' height='46' />
    </pattern>
  </defs>
  <path d='M0,750 L250,750 C650,730 500,154 1024,154 L1024,0 0,0 0,750' fill='url(#dots)' filter='url(#dropShadow)' />
</svg>

Note: The image used in the above demo is not my own. It was taken from the internet.


The Shadow:

The shadow effect is created by using SVG filter element along with feGaussianBlur, feOffset and the feMerge elements. The feGaussianBlur element blurs the source graphic (our sigmoid) by the specified standard deviation value and the feOffset offsets the resulting image by the dx, dy values. The original image and the blurred one are the merged using feMerge. The feFlood and the feComposite are added just in case you want to give the shadow a different color. The colors can be specified using the flood-color and flood-opacity attributes. (The method for changing the SVG drop-shadow's color was taken from this answer by Joe W.)


Adding Text:

Now this is the really tricky bit of the whole thing. If you need to place the text only on the solid colored area of the page then you would need to use positioning attributes carefully. If the text is small or just a single line of text then we could use SVG text element itself like in the demo that I linked earlier. If it is not then you'd have to make sure that the container box of the text doesn't overlap onto the sigmoid shape's area.

body {
  margin: 0;
}
div.container {
  position: relative;
  width: 100%;
  height: 100vh;
}
svg {
  position: absolute;
  top: 0px;
  left: 0px;
  height: 100%;
  width: 100%;
}
.container div {
  position: absolute;
  top: 50%;
  right: 0px;
  height: 30vh;
  width: 33.33%;
  font-size: 20px;
}
<div class='container'>
  <svg viewBox='0 0 1024 768' preserveAspectRatio='none'>

    <defs>
      <!-- For the shadow -->
      <filter id="dropShadow">
        <feGaussianBlur in="SourceAlpha" stdDeviation="6" />
        <feOffset dx="3" dy="3" result="offsetBlur" />
        <feFlood flood-color="#AAA" flood-opacity="1" result="offsetColor" />
        <feComposite in="offsetColor" in2="offsetBlur" operator="in" result="offsetBlur" />
        <feMerge>
          <feMergeNode />
          <feMergeNode in="SourceGraphic" />
        </feMerge>
      </filter>
      <!-- End of shadow -->

      <!-- For filling the top-left with pattern -->
      <pattern id='dots' patternUnits='userSpaceOnUse' width='25' height='25'>
        <polygon points='0,0 0,25 25,25 25,0' fill='yellowgreen' />
        <circle cx='12.5' cy='12.5' r='4' fill='rebeccapurple' />
      </pattern>
      <!-- End of pattern -->
    </defs>

    <!-- Actual sigmoid curve -->
    <path d='M0,750 L250,750 C650,730 500,154 1024,154 L1024,0 0,0 0,750' fill='url(#dots)' filter='url(#dropShadow)' />

  </svg>
  <div>Hello! Here is some text that is placed on the solid colored area of the page.</div>
</div>

We can try using the CSS shape-outside property but the browser support for that is pretty poor at the moment. Here is a demo using this shape-outside property. Could not host it in-site because it needed creating a separate SVG file. The demo is an adapted version of the one provided in the W3C CSS Shapes Spec.


Alternate Approach: (apply the pattern to the container instead of SVG)

Since you don't want the image to get squished or stretched, an alternate approach would be to do the following:

  • Create the SVG shape such that it is only the solid colored part (and not the patterned area)
  • Apply the pattern to the div.container and then place the SVG absolutely on top of the element. The SVG shape (which has a white colored fill) will prevent the pattern from being visible on the other side.
  • Change the shadow on the SVG from a normal drop shadow to an inset shadow. (Inset Shadow's code is completely taken from here.

body {
  margin: 0;
}
div.container {
  position: relative;
  background: white url(http://famok.com/wp-content/uploads/2016/10/WhiteOnWhite.jpg) top left / 26px 32px repeat;
  width: 100%;
  height: 100vh;
}
svg {
  position: absolute;
  top: 0px;
  left: 0px;
  height: 100%;
  width: 100%;
}
.container div {
  position: absolute;
  top: 50%;
  right: 0px;
  height: 30vh;
  width: 33.33%;
  font-size: 20px;
}
<div class='container'>
  <svg viewBox='0 0 1024 768' preserveAspectRatio='none'>
    <defs>
      <filter id="dropShadow" x="-50%" y="-50%" width="200%" height="200%">
        <feComponentTransfer in=SourceAlpha>
          <feFuncA type="table" tableValues="1 0" />
        </feComponentTransfer>
        <feGaussianBlur stdDeviation="6" />
        <feOffset dx="2" dy="2" result="offsetblur" />
        <feFlood flood-color="#AAA" result="color" />
        <feComposite in2="offsetblur" operator="in" />
        <feComposite in2="SourceAlpha" operator="in" />
        <feMerge>
          <feMergeNode in="SourceGraphic" />
          <feMergeNode />
        </feMerge>
      </filter>
    </defs>
    <path d='M0,750 L250,750 C650,730 500,154 1024,154 L1024,768 0,768 0,750' fill='white' filter='url(#dropShadow)' />
  </svg>
  <div>Hello! Here is some text that is placed on the solid colored area of the page.</div>
</div>

Here is Plunker Demo for the alternate approach. This is a bit more complex than the previous one because here we need one SVG to produce the solid colored area (used as img) and another SVG which is the inverse (the patterned area) to be used with shape-outside.

Community
  • 1
  • 1
Harry
  • 87,580
  • 25
  • 202
  • 214
  • Thank you Harry - great answer! I have three clarifying questions: if I needed to change the angle of the diagonal line, how can that be achieved? (I need precisely 33 degrees angle). Also, how can the shadow be extended to below the horizontal line at the bottom left? Finally, how can I use specifically the background image that I need to use? Could you please help further? – Dimitri Vorontzov Nov 03 '16 at 12:45
  • @DimitriVorontzov: (1) I am not a Math expert so can't really help on that bit. I will try and see if I can find anything but can't guarantee. You can play around with the control point coordinates to get whatever is closest to your expectation. (2) I will update my answer for this one. We just need to move the shape coords up a bit so that the shadow can be visible inside the SVG's box. (3) Use the second snippet in the code and in the `image` tag's `xlink:href` attribute provide the URL of your image. That should do it. – Harry Nov 03 '16 at 12:49
  • Could you please show the shadow and the background image in the answer, Harry? – Dimitri Vorontzov Nov 03 '16 at 12:52
  • Amazing. Thank you @Harry - but it doesn't work with my image: https://jsfiddle.net/CybArt/myh6e1hc/ . Which makes the solution unfortunately unusable for me, as great as it is. How can it be made to work with the background that I need it to work with? Could you please show me? – Dimitri Vorontzov Nov 03 '16 at 13:17
  • @DimitriVorontzov: Your fiddle works, you just had to set the height and width of the image to the `pattern` and the `image` tag. Here is the [updated demo](https://jsfiddle.net/myh6e1hc/1/). – Harry Nov 03 '16 at 13:40
  • Not quite, unfortunately. The image is stretched vertically. Compare this: https://jsfiddle.net/myh6e1hc/1/ with this hack solution: https://jsfiddle.net/CybArt/wn7zjoco/2/ Also - please compare the drop shadow - what I need is a subtle and elegant effect, like in my fiddle. Could you please help? – Dimitri Vorontzov Nov 03 '16 at 13:44
  • @DimitriVorontzov: How about [this](https://jsfiddle.net/xmzu1ayf/)? A change in approach here. I have set the pattern on the container element instead of inside SVG and modified the shape + shadow. I hope this is what you need :) – Harry Nov 03 '16 at 14:09
  • 1
    That is the ONE! Thank you @Harry! – Dimitri Vorontzov Nov 03 '16 at 14:53
  • @DimitriVorontzov: Thanks for the bounty :) I am going to add SVG tag to the question as it may help others in future. Let me know in case you have any concerns. – Harry Nov 04 '16 at 06:58
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/127378/discussion-between-dimitri-vorontzov-and-harry). – Dimitri Vorontzov Nov 04 '16 at 12:59