Wednesday, June 6, 2018

Fun with CSS

I recently had to figure out how to do a bunch of stuff with CSS that I'd normally do with flexbox, but without flexbox. You may have read my other post about generating a PDF from HTML. In that post I used PhantomJS to render the HTML to generate the PDF and Phantom doesn't play well with flexbox (or at least I think that was the problem I was having, but it may have been something else). At any rate, I couldn't use flexbox, but I needed to imitate flexbox. Here are the various ways I did what I needed to do to get the result I wanted without flexbox.

Sticky-ish Footer

Stick a div to the bottom of the page, regardless of whether there is enough content to push the footer to the bottom. I didn't want to (or maybe I wasn't able to) use position: fixed so I used this alternative I found here via this Stack Overflow answer.

There are only a couple of things I'd change about the solution. When I set the container div's margin to -330px the footer ends up being slightly below the end of the body, which causes the page to scroll vertically even when there isn't enough content. Changing the margin on the container div to -300px resolved that problem. The other change is that you don't need the clearfooter div anymore. That may have been necessary in 2008 when that article was written, but it's not anymore.

Here's the markup I ended up using to demonstrate this nifty little capability:
<html>
  <head>
    <style>
      html, body {
        height: 100%;
        margin: 0;
      }
      
      #container {
        min-height: 100%;
        margin-bottom: -300px;
        position: relative;
      }
      
      #footer {
        height: 300px;
        position: relative;
        background-color: red;
      }
    </style>
  </head>
  <body>
    <div id="container">
      <div id="header">Header</div>
      <div id="nav">
        <ul>
          <li><a href="#">Home</a></li>
          <li><a href="#">Page 1</a></li>
          <li><a href="#">Page 2</a></li>
        </ul>
      </div>
      <div id="content">
        Content Here.
      </div>
    </div>
    <div id="footer">Footer Here.</div>
  </body>
</html>

Equal Height Columns

The next issue I had to tackle was having two columns right next to each other taking up the exact same amount of vertical space even if one of their contents was significantly larger than the other. I found this answer on Stack Overflow, which was amazing and simple and so much fun to implement.

The markup:
<html>
  <head>
    <style>
      #container {
        overflow: hidden;
        width: 100%;
      }
      
      #left-col {
        float: left;
        width: 50%;
        background-color: orange;
        padding-bottom: 500em;
        margin-bottom: -500em;
      }
      #right-col {
        float: left;
        width: 50%;
        margin-right: -1px;
        border-left: 1px solid black;
        background-color: red;
        padding-bottom: 500em;
        margin-bottom: -500em;
      }
    </style>
  </head>
  <body>
    <div id="container">
      <div id="left-col">
        <p>Test Content</p>
        <p>longer</p>
      </div>
      <div id="right-col">
        <p>Test Content</p>
      </div>
    </div>
  </body>
</html>

Forcing a Page Break with Flexbox

This one isn't really a solution as it is a pointer to an answer on Stack Overflow explaining why I couldn't use Flexbox in the first place: I was trying to force a page break and you can't do that when any of the ancestors are flexboxes.

Non-Bullet Bullets

The final issue I had to resolve was to have bullets that didn't use the <li> tag. I don't remember the entirety of the problem, but it had to do with the way list items align themselves with the bullets and how multi-line items are displayed when you use list-style-position: inside. What I ultimately wanted was a non-indented list where the bullets were always aligned with each other and the text was always aligned with itself, no matter how many times the text wrapped to a new line. I used this answer on Stack Overflow as a starting-point, and then made some minor changes to get a solution I was happy with.

\2022 is the ASCII code for a bullet and \00a0 is the ASCII code for a non-breaking space.
<html>
  <head>
    <style>
      .assumptions {
        float: left;
        width: 50%;
        margin-right: -1px;
      }
      
      .assumption {
        overflow: hidden;
        width: 100%;
      }
      
      .assumption > .bullet {
        float: left;
        width: 1em;
      }
      
      .assumption > .bullet:before {
        content: "\2022 \00a0";
        text-align: right;
        font-weight: bold;
      }
      
      .assumption > .text {
        float: left;
        width: 90%;
        margin-right: -1px;
      }
    </style>
  </head>
  <body>
    <div class="assumptions">
      <div class="assumption">
        <div class="bullet"></div>
        <div class="text">Something</div>
      </div>
      <div class="assumption">
        <div class="bullet"></div>
        <div class="text">Something else</div>
      </div>
      <div class="assumption">
        <div class="bullet"></div>
        <div class="text">Another thing</div>
      </div>
      <div class="assumption">
        <div class="bullet"></div>
        <div class="text">A really really really really really really really really really really really really really really really really really really really really really really long thing</div>
      </div>
    </div>
    <div class="assumptions">
      <div class="assumption">
        <div class="bullet"></div>
        <div class="text">Something</div>
      </div>
      <div class="assumption">
        <div class="bullet"></div>
        <div class="text">Something else</div>
      </div>
      <div class="assumption">
        <div class="bullet"></div>
        <div class="text">Another thing</div>
      </div>
    </div>
  </body>
</html>

Upon further review (and actually writing this blog post) I can see that all of my problems weren't really caused by flexbox or Phantom. It was more a case of having to figure out how to do everything in new and different ways for this specific project. But, whatever, I made it work. And now I've written about it so I don't forget.

Oh, here's the markup using all of these approaches together.
<html>
  <head>
    <style>
      html, body {
        height: 100%;
        margin: 0;
      }
      
      #container {
        min-height: 100%;
        margin-bottom: -346px;
        position: relative;
      }
      
      #footer {
        height: 330px;
        position: relative;
        background-color: red;
      }
      
      #content {
        overflow: hidden;
        width: 100%;
      }
      
      #left-col {
        float: left;
        width: 50%;
        background-color: yellow;
        padding-bottom: 500em;
        margin-bottom: -500em;
      }
      
      #right-col {
        float: left;
        width: 50%;
        background-color: blue;
        padding-bottom: 500em;
        margin-bottom: -500em;
      }

      .list-container {
        float: left;
        width: 50%;
        margin-right: -1px;
      }
      
      .list-item {
        overflow: hidden;
        width: 100%;
      }
      
      .list-item > .bullet {
        float: left;
        width: 1em;
      }
      
      .list-item > .bullet:before {
        content: "\2022 \00a0";
        text-align: right;
        font-weight: bold;
      }
      
      .list-item > .text {
        float: left;
        width: 90%;
        margin-right: -1px;
      }
    </style>
  </head>
  <body>
    <div id="container">
      <div id="header">Header</div>
      <div id="nav">
        <ul>
          <li><a href="#">Home</a></li>
          <li><a href="#">Page 1</a></li>
          <li><a href="#">Page 2</a></li>
        </ul>
      </div>
      <div id="content">
        <div class="list-container" id="left-col">
          <div class="list-item">
            <div class="bullet"></div>
            <div class="text">Something</div>
          </div>
          <div class="list-item">
            <div class="bullet"></div>
            <div class="text">Something else</div>
          </div>
          <div class="list-item">
            <div class="bullet"></div>
            <div class="text">Another thing</div>
          </div>
          <div class="list-item">
            <div class="bullet"></div>
            <div class="text">A really really really really really really really really really really really really really really really really really really really really really really long thing</div>
          </div>
        </div>
        <div class="list-container" id="right-col">
          <div class="list-item">
            <div class="bullet"></div>
            <div class="text">Something</div>
          </div>
          <div class="list-item">
            <div class="bullet"></div>
            <div class="text">Something else</div>
          </div>
          <div class="list-item">
            <div class="bullet"></div>
            <div class="text">Another thing</div>
          </div>
        </div>
      </div>
    </div>
    <div id="footer">This is my footer</div>
  </body>
</html>

No comments:

Post a Comment