Pangea: A Challenge

Pangea is the title of the upcoming design for Noscope. For this redesign, I will be needing a plain div box to place content in. In my mockups, the box looks like this:

box-problem

It’s a fairly straight-forward box with four borders and an angled top-right corner. The markup looks like this:

<div class="box">
content
</div>

Now for the challenge: not worrying about IE and with no extra HTML markup, is it possible to CSS style this box, yet keeping the width and height liquid?

I have a few solutions already, but most of them require either JavaScript and/or extra source markup.

Update: A solution has been found. See the comments details.

Responses to “Pangea: A Challenge”

  1. Can’t be done, as far as I can tell. I’d love to know if anyone comes up with a solution :)

  2. David Emery says:

    How about having a large background image (wider then the possible max width of the box and taller then the max height) which has the angled corner, the top border and the right border. Make that aligned to the top right, and then have a border for the left and the bottom and that should work!

    With the relative simplicity of the corner and side borders, even a large gif shouldn’t be too large file size wise.

  3. Joen says:

    Michael Heilemann said:

    Can?t be done, as far as I can tell. I?d love to know if anyone comes up with a solution :)

    I’ll let Arthur C. Clarke respond:

    Clarke’s Second Law

    “The only way of discovering the limits of the possible is to venture a little way past them into the impossible.”

    :)

    David Emery said:

    How about having a large background image (wider then the possible max width of the box and taller then the max height) which has the angled corner, the top border and the right border. Make that aligned to the top right, and then have a border for the left and the bottom and that should work!

    This is a good idea, but unfortunately not quite good enough. I can live with the “max-width” limitation of the width, but imagine if this box were to contain a single comment, and I was fortunate enough to get a visit from someone who decided to comment with an essay, the height of the image would quickly be reached. In other words, a max-width is okay, but the height really needs to be “infinite”.

  4. With CSS3 you could have two background images on the element :)

  5. Joen says:

    Michael Heilemann said:

    With CSS3 you could have two background images on the element :)

    Talk to me!

    If this is really doable, I can top align an image of the angled corner and the top border, and then as my second background, right-align a border image with repeat-y!

    Though I’m getting a feeling this is “too good to be true”, so: is this Firefox/Opera/Safari CSS3? Or “as-yet-unimplemented” CSS3?

    Edit: (Sorry for being vague, I should’ve probably mentioned that in the post) Wait a minute, that was a draft of this post, in which I said “CSS3″ is okay…

  6. Stewart says:

    Well you could go down the route Michael suggests, Safari already supports 2 backgrounds for elements.

    What I would do is throw in an extra span or div at the top and align it to where you want the angle to be and do it that way.

    Otherwise no I don’t think it’s doable.

    What are you having nearest the top? A heading?

    If you so you could just put the angle in that and make it a bit higher than it needs to be.

  7. Joen says:

    Stewart said:

    Well you could go down the route Michael suggests, Safari already supports 2 backgrounds for elements.

    How about Mozilla and Opera?

    What I would do is throw in an extra span or div at the top and align it to where you want the angle to be and do it that way.

    I’m thinking of doing that using Javascript, and only for IE… I’d prefer a more elegant solution for the browsers I care about.

    What are you having nearest the top? A heading?

    If you so you could just put the angle in that and make it a bit higher than it needs to be.

    That’s the problem, the topmost tag will change from box to box. Sometimes it’ll be a P, sometimes a H2, sometimes an IMG. I THINK there’s also going to be cases with no tag at all, (i.e. just unwrapped text content).

  8. brendan says:

    There is a way to do what you want.

    Personally, I’d simply align the ‘text’ box right, left align the actual text and use a triangle the same colour as the background (with the box border colour on it’s internal corner) stuffed into the top right corner.

    You could then use overflow to shrink content left if the display is resized, as the text is bound to the right hand side and text can be set to flow so that it shrinks along with the actual box.

    I’m not a CSS guru, but that basic idea should work. It also has the benefit of potentially working in a number of browsers, not just firefox.

  9. Levi says:

    Personally, I?d simply align the ?text? box right, left align the actual text and use a triangle the same colour as the background (with the box border colour on it?s internal corner) stuffed into the top right corner.

    I was going to suggest the same thing.

    .box { background: #FFF url('custom-corner.gif') top right no-repeat; }

    But then of course there’s the issue of background, because this method will only let you have a plain single background colour, because if any kind of background page ends up behind it, you’ll have this ugly looking square coloured corner. Not nice at all.

    I hope you find a solution Joen, I’d love to know how this goes.

  10. Joen says:

    brendan & Levi,

    I THINK I’m getting the gist of what you mean here, but I think I’ve also already tried it.

    I don’t worry so much about having the corner be transparent, I wouldn’t mind using the background corner on a triangle in the top right corner.

    The problem, in this case, is the border, which would then be applied to all four sides of the box. If I top/right-align, the angle, it’ll be placed INSIDE the border of the box. Even if I use the CSS2 property “background-orientation: border”, the borders will still be topmost. If only I could use z-index on a background.

    As for the CSS3 option with several backgrounds, my test showed it to not work in Firefox…

  11. Jonas Rabbe says:

    I refer you to Dunstan’s relic of a page: Solving CSS problems for Mozilla Europe and the attached demo page. Shouldn’t be a problem for someone of your talents to hack the CSS of the “Using a containing div” case to your liking.

    The example is fixed layout, but should easily be adapted.

  12. Dunstan’s just using css3′s ‘content’ thingy on certain children of a box (with adjacent selectors and :after).

    This of course won’t work in IE, but here it goes:

    div.theOneToBox > p:first-child:after{

    background:That-corner.gif;

    height/width:bla;

    margins:yada;}

    Maybe it needs a z-index, maybe it needs to be positioned absolutely, who knows. Thing about feeding IE via JavaScript, is that IE visitors are the ones most likely to have JS switched off…(I’d think so anyway)

  13. Joen says:

    Jonas & James,

    It seems to work!

    HOWEVER, with the ::before corner positioned correctly, a rounding bug is introduced in Firefox. This is obviously due to the combination of a liquid width layout, .EM padding/margin and so on.

    I’m going to try and see if the rounding bug disappears if I specify the box padding using pixels instead. Other than that, so close, yet so far.

    Does anyone have a fix to the firefox rounding bug ?

    On a sidenote: If / when I fix this, I will of course post the resulting code and CSS here.

  14. Joen says:

    Tiny correction: By “it seems to work” referring to two different techniques, I was in fact referring to the fact that .box::before seems to behave in the same way as .box > p:first-child::after…

  15. Use the same unit for both the padding of the box, the border and the negative-margin on the corner. That * should * work.

  16. Joen says:

    James AkaXakA said:

    Use the same unit for both the padding of the box, the border and the negative-margin on the corner. That * should * work.

    Hmm…. While fairly ugly, the code I have in place now works 90% of the time… I think if I tweak it, clean it up and simplify things, it might just work.

    Edit: To clarify – by “works 90% of the time”, I mean the box looks like it’s supposed to without the rounding error 90% of the time, but the last 10% the rounding error is still there.

    This is what I have now:

    .box {
    padding: .5em 1em;
    margin: 0 0 .75em 0;
    background-color: #FFFFFF;
    border: .05em solid #d1d1d1;
    border-bottom: .27em solid #d1d1d1;
    position: relative;
    }
    .box:before {
    content: "";
    background: transparent url('images/box_corner.gif') no-repeat top right;
    display: block;
    font-size: .1em;
    height: 10em;
    margin: -5.5em -11em 0 0;
    }

    I’ll let you know how it pans out.

  17. Ah, looks solid enough…but,

    content: url(‘images/box_corner.gif’);

    Might replace:

    content: “”;

    background: transparent url(‘images/box_corner.gif’) no-repeat top right;

    display: block;

    And what’s font-size: .1em; doing there in the before thing?

  18. Matt says:

    Here’s one thought: let the elements inside .box create the borders on the sides.

    Give .box a background with an image with the height of the corner and width equal to the maxwidth of .box. Make sure this image has a border on the top, too.

    Give .box a border-bottom. Remove padding from .box.

    Remove margin from block level elements inside .box and give them a border-right and border-left. Replace margin with padding.


    .box {
    border-bottom:1px solid #ddd;
    background:#fff url(corner.gif) no-repeat right top;
    padding:0;
    }

    .box h1, .box h2, .box h3, .box p, .box li {
    margin:0;
    border:1px solid #ddd;
    border-width:0px 1px 0px 1px;
    }

  19. Levi says:

    Joen,

    Ah I see what you mean, I’ll keep an eye on this to see if I can help out or atleast walk away with some rather cutting edge CSS. :)

  20. Joen says:

    Matt said:

    For some reason the behaviour of those two is decidedly different, and the “content” technique will work only if I have zero padding applied to the box.

    The problem is, it’s hard for me to predict exactly what the box will hold… i.e. sometimes it won’t even hold a P tag (in the archives).

    James AkaXakA said:

    Ah, looks solid enough…but,

    content: url(?images/box_corner.gif?);

    Might replace:

    content: ??;

    background: transparent url(?images/box_corner.gif?) no-repeat top right;

    display: block;

    And what?s font-size: .1em; doing there in the before thing?

    I removed the font-size thing. It scales the EMs to behave differently, but it didn’t quite help me.

    The content technique for some reason only works when zero padding is applied.

    That said, the code below works often enough that I think it’s the solution! The EMs aren’t nice, but they seem to stay exactly outside of the area of Firefox’s rounding bug often enough that you’ll never notice (atleast on my system).

    I honestly think, if this can’t work, then I’ll go with a JS solution for both IE and Firefox.

    .box {
    background-color: #FFFFFF;
    border: .05em solid #d1d1d1;
    border-bottom: .27em solid #d1d1d1;
    position: relative;
    padding: 0em 1em .5em 1em;
    }
    .box:before {
    content: "";
    background: transparent url('images/box_corner.gif') no-repeat top right;
    display: block;
    height: .75em;
    margin: -.01em -1.075em 0 0;
    }
    

    James: Winner!

    Thanks for all your help!

  21. Joen says:

    This one seems to be near bulletproof. In Firefox I can’t get it to bork at all. That’s great.

    .box {
    background-color: #FFFFFF;
    border: 1px solid #d1d1d1;
    border-bottom: 4px solid #d1d1d1;
    position: relative;
    padding: 0px 15px .5em 1em;
    }
    .box:before {
    content: "";
    background: transparent url('images/box_corner.gif') no-repeat top right;
    display: block;
    height: 11px;
    margin: -1px -16px 0 0;
    }

    Edit: Due to the extra content presented, 2px are added to the inner top padding. To work around this, the following worked for me:

    .box>* {
    position: relative;
    top: -2px;
    }
    
  22. Now that’s clever, I didn’t know you could do that :D

  23. Joen says:

    Yeah well… like they say “with a little help from your friends” :)

    By the way, the method mentioned in comment 21 works consistently without fail in both Firefox and Opera. IE, of course, is another matter. Now to see if there’s any way I can test it in Safari.

  24. Jonas Rabbe says:

    Now to see if there?s any way I can test it in Safari

    Email me and I’ll send you screenshots.

  25. Joen says:

    Jonas Rabbe said:

    Email me and I?ll send you screenshots.

    Deal. I’ll concoct a package later today and send it. Thanks. If you could be so kind as to also test it in Firefox mac, that would be great.

  26. Jonas Rabbe says:

    If you could be so kind as to also test it in Firefox mac, that would be great.

    No problem.

  27. bramick says:

    I’m running Mac OS X 10.4.6. with:

    Camino: Version 2006042704 (1.0.1)

    Firefox: 1.5.0.4

    Safari: Version 2.0.3 (417.9.3)

    If you need any other mac screen shots let me know.

  28. Joen says:

    bramick said:

    I?m running Mac OS X 10.4.6. with:
    Camino: Version 2006042704 (1.0.1)

    Firefox: 1.5.0.4

    Safari: Version 2.0.3 (417.9.3)

    If you need any other mac screen shots let me know.

    Hey, forgot to respond to this. Thanks for the offer, but Jonas help is really all I need right now :)

    Oh, and if things go the way they’ve gone the last few days, I’ll be announcing a “final date” really soon.