4

Only the top and right side columns are skewed

I've tried the perspective solution here How to transform each side of a shape separately? but can't get it to work probably due to the irregularness of the shape. Only the top and right side columns are slanted, vertical and bottom are straight. How can I do this with CSS?

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
Jjj
  • 762
  • 3
  • 8
  • 18
  • 5
    Yes...look into `transform:skew`... – Paulie_D Jul 25 '15 at 11:54
  • 2
    What you are looking for is something like [**this**](http://jsfiddle.net/nw92nmqp/) which is an adaptation of my answer [**here**](http://stackoverflow.com/questions/19958450/div-slanted-in-2-directions/30520927#30520927). Still, I would recommend SVG instead of CSS for such shapes. – Harry Jul 25 '15 at 12:15
  • Possible duplicate of http://stackoverflow.com/questions/12919398/perspective-transform-of-svg-paths-four-corner-distort (using SVG) – Alvin K. Jul 25 '15 at 20:15
  • 1
    A solution to this can be achieved using `transform:matrix3d` but a lot of people don't know how to use it. Here's a CSS generator: https://www.useragentman.com/matrix – nxasdf Nov 19 '21 at 22:23

5 Answers5

8

Using CSS borders you can create triangles and trapezoids.

You can achieve your shape joining a triangle and a trapezoid.

.triangle {
  border: 0 solid red;
  border-left-width: 500px;
  border-top-width: 30px;
  border-top-color: transparent;
}
.trapezoid {
  border: 0 solid red;
  width: 500px;
  border-bottom-width: 150px;
  border-right-width: 30px;
  border-right-color: transparent;
}
<div class="triangle"></div>
<div class="trapezoid"></div>
Community
  • 1
  • 1
Oriol
  • 274,082
  • 63
  • 437
  • 513
  • Thank you! Is there a way to add a text inside the box? When I try to add text the top part gets cut because they're borders I assume? – Jjj Jul 25 '15 at 13:13
  • 1
    @Jenny If you want text inside, better split the shape into a rectangle and two triangles: [demo](http://jsfiddle.net/g1tb3uk0/) – Oriol Jul 25 '15 at 14:23
  • @Jenny, you can also simply create a separate container div just for the text and move it over the shape with absolute positioning. This gives you full control over the text box, which you can style independently, and you don't have to touch any of the code for the shape. Btw, consider using `z-index` for your text box to ensure it always displays above the shape. – Michael Benjamin Jul 25 '15 at 19:22
  • @mbnyc: Using absolute positioning for the text could create extra trouble if the shape has to expand such that the full text fits within it. – Harry Jul 25 '15 at 21:06
  • @Harry, so many unknowns about how and where this shape will be used. We don't know anything about the surrounding context or code. I was just a presenting an option that may (or may not) be useful. – Michael Benjamin Jul 25 '15 at 21:12
  • @mbnyc: Yes, that's true and only OP knows best but I was just saying that using absolute positioning isn't always beneficial :) – Harry Jul 25 '15 at 21:22
7

Method 1: Clip path

You could make use of CSS clip-path feature to clip a rectangle into the required polygon shape.

div {
  box-sizing: border-box;
  height: 150px;
  width: 250px;
  background: red;
  padding: 10px;
  -webkit-clip-path: polygon(0% 0%, 90% 10%, 100% 100%, 0% 100%);
  clip-path: polygon(0% 0%, 90% 10%, 100% 100%, 0% 100%);
}
div#image {
  background: url(http://lorempixel.com/400/200);
}

/* Just for demo */

div{
  float: left;
  margin: 10px;
  transition: all 1s;
}
div:hover{
  height: 200px;
  width: 300px;
}
<div>Some text</div>
<div id="image"></div>

Pros:

  • Supports non-solid color fills inside the shape and also allow text to be present inside.
  • The shape is responsive and can adapt even if the container's dimensions change.

Cons:

div {
  box-sizing: border-box;
  height: 150px;
  width: 250px;
  padding: 10px;
  background: red;
  -webkit-clip-path: url(#clip);
  clip-path: url(#clip);
}
div#image {
  background: url(http://lorempixel.com/400/200);
}

/* Just for demo */

div{
  float: left;
  margin: 10px;
  transition: all 1s;
}
div:hover{
  height: 200px;
  width: 300px;
}
<svg width="0" height="0">
  <defs>
    <clipPath id="clip" clipPathUnits="objectBoundingBox">
      <path d="M0 0, 0.9 0.1, 1 1, 0 1z" />
    </clipPath>
  </defs>
</svg>
<div>
  Some text
</div>
<div id="image"></div>

Method 2: CSS Transforms

Generally it is better not to use transforms when there is going to be content like image or text inside the shape (or) when the shape's background is not going to be a solid color because then we would either have to (a) reverse transform the child elements separately to make them look normal or (b) use absolute positioning.

For this particular shape, having text inside the shape is not a problem even while using transforms but having non solid background colors would be.

Option 1: Using two pseudo-elements

You could use a couple of pseudo-elements with skew transforms, position one on the top and the other on the right to produce the required shape. Hover the shape in snippet to see how it is created.

div {
  position: relative;
  height: 150px;
  width: 250px;
  background: red;
  margin: 40px 40px 0px 0px;
}
div:after,
div:before {
  position: absolute;
  content: '';
  background: red;
  z-index: -1;
  backface-visibility: hidden;
}
div:before {
  height: 12.5%;
  width: 100%;
  top: 0px;
  left: 0px;
  transform-origin: right top;
  transform: skewY(3deg);
}
div:after {
  height: 100%;
  width: 12.5%;
  right: -1px;
  top: -1px;
  transform-origin: right top;
  transform: skewX(10deg);
}

/* Just for demo */

div{
  transition: all 1s;
}  
div:hover{
  height: 250px;
  width: 300px;
}
div:hover:after{
  background: blue;
}
div:hover:before{
  background: green;
}
<div>Some text</div>

Pros:

  • Shape can be created with a single element and can have text inside it without any trouble.

Cons:

  • Having gradients (or) images as background for the shape is complex because they would need reverse rotation as mentioned earlier.
  • Shape is not 100% scalable as dimensions of the container should change proportionately for the shape to be maintained (hover on the shape in the snippet to see what I mean). Reason is same as mentioned here.

Option 2: Using one pseudo-element

This is pretty similar to the previous option except that this uses a single pseudo-element along with a overflow: hidden on the parent.

div {
  position: relative;
  box-sizing: border-box;
  height: 200px;
  width: 300px;
  padding: 10px;
  overflow: hidden;
}
div:after {
  position: absolute;
  content: '';
  top: 0px;
  left: -20px;
  height: 100%;
  width: 100%;
  background: red;
  transform-origin: left bottom;
  transform: skewY(5deg) skewX(7.5deg);
  z-index: -1;
}
div:hover {
  height: 300px;
  width: 500px;
  transition: all 1s;
}
<div>Some text</div>

Pros:

  • Shape can be created with a single element and can have text inside it without any trouble.
  • Shape is responsive and can adapt even if the container's dimensions change .

Cons:

  • Same constraint as the previous option for gradient and image backgrounds.
  • Not suitable if the overflow: hidden on the parent is a constraint.
Community
  • 1
  • 1
Harry
  • 87,580
  • 25
  • 202
  • 214
2

A solution is:

div {
    width: 300px;
    height: 100px;
    margin:50px;
    background-color: yellow;
    border: 1px solid black;
}

.thisdiv {
    -ms-transform: skewX(-20deg); /* IE 9 */
    -webkit-transform: skewX(-20deg); /* Safari */
    transform: skewX(-20deg); /* Standard syntax */
}
<div class="thisdiv">
    This is the div I will skew
</div>

This is how to skew an element, if you want to make the shape you added, try using two overlaping div's, position, scale, rotate and skew, like this:

.outer-div{
    position:relative;
    margin:50px;
    width:200px;
    height:200px;
    border:2px black dashed;
}
.inner-one{
    position:absolute;
    left:0;
    bottom:0;
    width:180px;
    height:180px;
    background:red;
}
.inner-two{
    position:absolute;
    bottom:2px;
    right:0px;
    width:200px;
    height:195px;
    background:red;
    transform: rotate(7deg) skew(14deg) scale(0.905); /* Standard syntax */
}
<div class="outer-div">
    <div class="inner-one">
    </div>
    <div class="inner-two">
    </div>
</div>
web-tiki
  • 99,765
  • 32
  • 217
  • 249
vanntile
  • 2,727
  • 4
  • 26
  • 48
2

2 Triangle Solution for Irregular Quadrilateral in CSS

In looking at your image, I notice that the skew at the top and right are really just long, narrow triangles overlaying the rectangle.

enter image description here

So what I did was create triangles using CSS border properties and absolutely position them over the rectangle.

#rectangle {
  width: 400px;
  height: 200px;
  background-color: red;
  margin-top: 25px;
  position: relative;
}
#triangle-down {
  border-left: 30px solid red;
  border-right: 0;
  border-top: 200px solid transparent;
  position: absolute;
  right: -30px;
  top: 0;
}
#triangle-left {
  border-top: 0;
  border-bottom: 15px solid red;
  border-right: 400px solid transparent;
  position: absolute;
  right: 0;
  top: -15px;
}
<div id="rectangle">
  <div id="triangle-down"></div>
  <div id="triangle-left"></div>
</div>

jsFiddle demo

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
1

You could check out the clip-path property (see below from MDN), but support is very patchy. Chrome 24+ supports with prefix and FF, but only URL values, which reference a path in an SVG. You can read more about clip-path here.

Here's a basic pen - this will only work in Chrome.

From MDN

The clip-path CSS property prevents a portion of an element from getting displayed by defining a clipping region to be displayed i.e, only a specific region of the element is displayed. The clipping region is a path specified as a URL referencing an inline or external SVG, or shape method such as circle(). The clip-path property replaces the now deprecated clip property.

  • developer.mozilla.org/en-US/docs/Web/CSS/clip-path (sorry, not enough rep to link)
lukebm
  • 163
  • 11