Monday, December 12, 2011

The Stair Effect: CSS Curved Text Wrap Using a Mock Sliced Image

CSS Mock Slicing an ImageTo view the slideshow which has the major steps of the tutorial, click here. So why are we going to pretend to divide up an image into smaller rectangles? Because I want to wrap text around an image following a curved path. Like many others have done before, including Eric Meyer in his blue book, you can also cut your image into real slices in Photoshop, which would simplify the CSS but it'd be labor intensive, and requires you to add img tags in the markup. I want to do the same using CSS (and a bit of markup) without actually editing the image.

Honestly, CSS was not what I had in mind when I started thinking about wrapping text around a PNG file which contained an unevenly shaped image. Using Javascript, you can probably check for blank pixels to determine the real contour of an image and then build a "staircase" based on that. That would require a bit of programming and writing of some image processing algorithms.

Being a CSS-centric blog, I want to share with you a CSS technique that approximates the same thing without any traditional programming. If you have a good grasp of CSS fundamentals and some patience, you can curve text around an image using only CSS and HTML. The types of topics that we'll cover during this exercise are Negative Margins, Relative and Absolute Positioning, The Flow, and Floats.

The markup for the first still in the animated gif has a div enveloping an img and a p element. The image goes first and because p is a block element, the text starts at a new line. The red border is for the enveloping div and the dotted black border belongs to the image. No CSS needed, the markup goes something like this:

<div id="stairs">
  <img src="images/gown.png">
  <p>An evening gown is a long flowing women's....</p>
</div>    

The output without any CSS looks like this. The borders are not turned on in this version because I have no CSS.

Since we want to pull the text to the same level with the image, we begin writing some CSS. Floating the image to the right should do the trick because this will sort of take the image out of the flow so as long as there's enough space the text and image will sit next to each other. Text will flow around the floated image but other than that, the image being there will not cause a block level element generate a br.

#stairs img { float: right; border: 1px dotted black; }
#stairs { border: 1px solid red; }

Let's add 20 pixels all around to get the image away from the corner of the browser and give the stairs div a width of 500 pixels to contain the text a bit. The third still is how we'll look at this point.

#stairs { 
   border: 1px solid red;
   margin: 20px;
   width: 500px;
   }
   
#stairs img { border: 1px dotted black; 
              float: right;}

The text is confined to a set width and is next to the image but moving the text more towards the image will benefit the design. To do this, we will use negative margins. Remember a left margin controls how far away the neighboring element to your left has to stay from you. You control how close someone gets to you. When you have a negative margin, you are in effect saying "come on in" so that the elements actually start overlapping. A left margin of minus 80 pixels looks okay.

#stairs { 
   border: 1px solid red;
   margin: 20px;
   width: 500px;
   }

#stairs img { 
   border: 1px dotted black;
   float: right;  
   margin-left: -80px;}

#stairs p   { text-align: justify; }

Here's the output of our CSS and HTML after the latest changes. I have the borders turned on for debugging.

STAIR EFFECT


With CSS, we do not have access to the internals of an image file so we are going to simulate the curve by stacking up rectangles on top of one another. This reminds me of how we used to compute an integral using a numerical method.

The markup will change. The finer the slices, the better the curve but for our purposes, four (4) should be sufficient to demonstrate the technique. Six (6) rectangles will make it perfect which would be a good exercise for you. All div's will be floated and will have nothing inside them so it's imperative that we declare width and height for each div. Without dimensions, an empty div will collapse in the flow. I'll take the negative margin out for now since we don't it for this case. The containing block for the image is csdiv, the wrapper div for the stair divs. I'm sure you can come up with a more descriptive name for your div, cs is short for container-stairs div.

/*the css*/
#stairs { 
   border: 1px solid red;
   margin: 20px;
   width: 460px;
   font: 13px/1.7 Georgia;
   }
   
#stairs img { 
      border: 1px dotted black; 
      position: absolute; 
      top:0; right:0;  }

#stairs p   { text-align: justify; }
#csdiv      { position: relative; }

.stair1, .stair2, .stair3, .stair4 { float: right; }   
.stair2, .stair3, .stair4 { clear: both; }

.stair1 { width: 181px;
   height: 190px;
   border: 1px solid blue;
   }
   
.stair2 { width:  208px;
   height: 76px;
   border: 1px solid black;
   }
   
.stair3 { width:  245px;  
   height:  70px;
   border: 1px solid red;
   }
   
.stair4 { width:  278px;
   height: 112px;
   border: 1px solid green;
   }


<!-- The Markup -->
<div id="stairs">
  <div id="csdiv">
    <img src="images/gown.png">
    <div class="stair1"></div>
    <div class="stair2"></div>
    <div class="stair3"></div>
    <div class="stair4"></div>
  </div>
  
  <p>
  An evening gown is a long flowing women's dress usually worn....
  </p>
</div>

CSS Stair Effect

CSS3 BORDER PROPERTIES


You can easily see the different elements when we turn the borders on. The text is nicely flowing around our image. We can dress up the border in Firefox by thickening the border, rounding its corners and dropping a shadow. The -webkit- prefix covers Safari/Chrome/Konqueror. I am not including it in the code here but you should:

#stairs {
border: 9px solid #f8d0d4;
-moz-border-radius: 12px;
-moz-box-shadow: -2px 2px 5px 1px #888;
box-shadow: -2px 2px 5px 1px #888;
border-radius: 12px;
......
}

A LITTLE MARGIN GOES A LONG WAY


With the addition of the thick border for the stairs div, the first thing that strikes you is the lack of padding/margin which we can take care of easily.

The text runs right up to the red border so one way to fix this is to give some padding or margin to one of the elements. I'm going to add margin to the p element with margin: 40px 0 40px 40px; statement. There will be no margin for the right side because that could create problems by keeping the text away from our "stairs".

CSS3 Border
Let's get the text and the image away from the border:

#stairs p {
text-align: justify;
margin: 40px 0 40px 40px;
}

A SET OF STAIRS KEEP THE TEXT AT BAY


Just so we are on the same page - if you are thinking the image itself is playing any role in arranging the text, you'd be wrong. It's taken out of the flow by the absolute positioning and is only there for displaying purposes. The 4 rectangles are now the "image". As long as we overlay the image where the rectangles are stacked up, we'll fool the eye.

#stairs {
margin: 50px;
width: 360px;
font: 13px/1.7 Georgia;
border: 9px solid #f8d0d4;
-moz-border-radius: 12px;
-moz-box-shadow: -2px 2px 5px 1px #888;
border-radius: 12px;
box-shadow: -2px 2px 5px 1px #888;
}

I am going to nudge the image up a little by absolutely positioning the top by -20 pixels. This will move the image up a little above the border.

#stairs img {
position: absolute;
top:-20px; right:0;
}

RELATIVE POSITIONING LEAVES A HOLE IN THE FLOW


#csdiv {
position: relative;
right: -165px;
}

To see the effects of the relative (moving the stairs to the right) and absolute positioning (moving the image up), take a look here. csdiv is relatively positioned. It's still in the flow but we have moved it to the right by 165 pixels. We are doing this because we want the image to pop out of its container and sit on top of the border. But doing so will leave a "hole" behind where csdiv was going to be. As far as the flow is concerned, it still is.

We are going fill in the yellow strip - leftover from the relative positioning - by bringing the text over to the right.

NEGATIVE MARGIN TO PULL THE TEXT TO THE RIGHT


The curve will stay the same. Every stair will get the same negative margin of minus 162 pixels. I can turn off my debugging borders simply by setting all borders to none.

/*negative margin to pull the text over*/
.stair1, .stair2, .stair3, .stair4 {
float: right;
margin-left: -162px;
border: none;
}

Notice how we have moved the image over the border of its containing div in the final version. I thought we were done then I remembered that I had replaced the first letter of the paragraph with a dropcap.

At the time I wrote the CSS Dropcap post, I didn't think a non-floated version was going to help anyone but maybe calm down people's fears about floats. Well, as it turns out, a non-floated dropcap, "stylized_olho-a" in this case, is exactly what I needed for this example to work.

THE CSS & THE MARKUP


If you want to see the entire CSS and markup based on this version, click here. HTML version here. The introductory gif shows the text lighter than I have in the image below. If you prefer it that way, update the paragraph selector with: #stairs p { color: #999999; }.

For comparing the image below with what we started out, see here. Once again, the slideshow has the major steps of the tutorial.

CSS Curved Text Wrap

0 comments:

Post a Comment