CSS challenge
You, dear reader, are almost certainly one of the most talented CSS experts on the planet. No, don’t blush; everybody says it.
So here’s a challenge for you. I have a defintion list. It’s a glossary—a collection of terms and definitions. Semantically, therefore, it’s indisputably correctly marked up as dt and one or more dds, all wrapped in a single dl. (It’s not a naughty dl in a form or any misuse like that).
I want to style it in a way that’s very common for glossaries—term on the left, and all the different definitions on the right. Each new term starts a new “row” for want of a better word. It needs to be robust; some terms are longer than their definitions; some terms have multiple definitions.
I can’t do it.
If I add a non-semantic (and invalid) div around each term and its definition(s), then float:left; clear:left;, it works perfectly. But without that meaningless extra element, it looks minging. (Colours and the classes used to add those are just for illustration.)
Whichever way I try (absolute positioning, using display:table-*, inserting generated content and floating and clearing that), I can’t get it to look as I want it.
It’s obviously a common problem. The HTML 5 group declined to add a di (definition item) grouping element to solve this, rightly saying
This is a styling problem and should be fixed in CSS. There’s no reason to add a grouping element to HTML, as the semantics are already unambiguous.
So, dear reader: can you do it? There’s free entry to standards.next and a snog with tongues for the lucky winner.
Rules: you can’t change the markup. This would be easy to do with multiple dls, but it isn’t a series of lists: it’s one list of multiple terms. And, yes, it could be marked up as a table, but it isn’t a table, it’s a list of definitions and we all know that tables for layout is evil.
(This isn’t me just being lazy; Molly is attending the CSS Working Group’s face-to-face PARTAAAY!! meeting, and we were discussing things to bring up there.)
26 Responses to “ CSS challenge ”
I don’t think it’s QUITE the effect you want, but you might be ok with it.
dd:after {
content: “.”;
display: block;
height: 0;
clear: both;
visibility: hidden;
}
Do you envisage a scenario with multiple terms per definition?
If not, then try floating your terms left and your definitions right with a clear:both on your terms (would need some custom – CC declared – for IE).
Otherwise, reckon some javascript jiggerpokery would be required.
Checking it out remove the:
dd.last { clear: both; }
it’s somewhat unnecessary.
OK I’m going to bite the bullet and provide the following test case: http://oksushi.com/test/dl.html
We just need to float the dt left and give it a width and clear: left, apply a margin-left of equal or greater width to the dd and then apply the clearfix trick to fix the issue with long dt and short dl.
Try this:
dl
{
list-style-type: none;
}
dt
{
width: 17%;
padding: 0.5em;
float: left;
clear: left;
margin: 0;
margin-right: -1000000px;
margin-bottom: 1em;
}
dd
{
float: left;
margin-left: 20%;
width: 70%;
margin-bottom: 1em;
padding: 0.5em;
}
(sadly it’s proportional width – not sure I can do it with a fixed column)
Hi Bruce, the following works in everything but IE6 (I think).
dl {list-style-type:none; margin-left: 16em; }
dt {width:13em;padding:0.5em;float:left;clear:left;margin:0 0 1em -16em;
font-weight:bold;color:#686663; color:red; border-bottom:1px solid green;}
dd {float:left;width:100%;margin: 0 0 1em;padding:.5em 0; color:blue;
border-bottom:1px dashed blue; }
.first {background-color:yellow;}
.second {background-color:pink;}
.third {background-color:orange;}
Haven’t had the chance yet to try out others’ solutions, but came up with this (seems to work in Safari and FF3, don’t know about IE). I’m setting a fixed pixel width for the dt “column” – in this case 290px with a 10px gutter. Em widths may work also.
dl {
margin-left: 300px;
}
dt {
font-weight: bold;
float: left;
width: 290px;
margin-left: -300px;
clear: left;
}
dd {
float: left;
width: 100%;
display: block;
}
Humph. Spent the last 30 minutes or so getting something to work, only to find Scott, Robert and James got there with a very similar idea to mine …
Anyway, here’s what I came up with:
* {
margin: 0;
padding: 0;
}
dl {
list-style-type:none;
}
dt {
width: 28%;
padding: 5px;
margin: 3% -30% 0 0;
background-color: #ddd;
color: #333;
border-bottom: 1px solid #333;
text-align: left;
float: left;
clear: left;
}
dd {
width: 65%;
margin: 3% 0 0 30%;
float: left;
border-bottom: 1px solid #eee;
}
Here is one solution, works in FF3+, Saf3+, IE6+: http://www.johanolsson.net/projects/test_dl.html
dl {
background: red;
overflow: hidden;
zoom: 1;
position: relative;
line-height: 1.4em;
}
dt {
background: orange;
float: left;
width: 50%;
position: relative;
top: .4em; /* adjust to get spacing between term/definition “items” */
z-index: 100;
margin-bottom: -1em; /* adjust to get spacing between term/definition “items” */
}
dd {
background: yellow;
display: block;
width: 50%;
position: relative;
padding-left: 50%;
margin: 0;
clear: left;
}
This allows for more than one definition per term.
(if someone else has posted something similar, sorry =) )
Why not this way?
Robert’s solution doesn’t work in IE, but adding a hack (is that allowed) seems to allow it to function in IE:
dl {
list-style-type:none;
margin-left: 16em;
}
dt {
border-bottom:1px solid green;
clear:left;
color:#686663;
float:left;
font-weight:bold;
margin:0 0 1em -16em;
padding:0.5em;
width:13em;
}
dd {
border-bottom:1px dashed blue;
color:blue;
float:left;
*float: none;
margin: 0 0 1em;
padding:.5em 0;
width:100%;
}
Hi Bruce,
Using your markup I applied these styles to get the desired effect. Main difference is that for standards compliant browsers I had to add a set width to the dl and then float the dd’s.
Hopefully this meets your requirements
—————
dl {width:900px;}
dt {clear:both; float:left; width:260px; padding:10px; margin:0 0 2em; font-weight:bold; color:#686663;}
dd {clear:right; float:right; width:570px; padding:10px; margin:0 0 2em;}
.first {background-color:yellow;}
.second {background-color:pink;}
.third {background-color:orange;}
** And these styles go in some < IE8 only conditional.
dd {float:none; clear:none; margin:0 0 2em 300px;}
—————
@Cal Wilson – your test page omits the scenario with a long term with multiple shorter definitions (Bruce’s second – pink – term in his test page). Your second and subsequent definitions will be pushed down the page to below the end of the term. The negative margin float solutions keep the definitions stacked up alongside the term.
Hi Bruce,
I haven’t had proper time to read your exact requirements, but I wrote this a couple of years back and think it could be of assistance. The end effect is illustrated if you want to check before you implement:
http://justbeyondthebridge.co.uk/blog/definition-lists-as-tables/
It doesn’t require any odd selectors or hacks.
Andy.
@James Hart
You are absolutely right! Here is a fixed-width solution that allows for the ‘long dt, many short dd’ problem. http://oksushi.com/test/dl-fixed.html
Hopefully this does the trick without setting a width on the dl and works in IE6+
dl {padding-left:300px;}
dt {clear:left; float:left; width:260px; padding:10px; margin:0 0 2em -300px; font-weight:bold; color:#686663;}
dd {float:left; width:100%; padding:10px 0; margin:0 0 2em;}
.first {background-color:yellow;}
.second {background-color:pink;}
.third {background-color:orange;}
/* show the styles below to lte IE7 */
dl {padding-left:0;}
dt {display:inline; clear:both; float:left; margin-left:0;}
dd {clear:none; float:none; width:auto; margin-left:300px;}
Just for fun, I thought I’d try it using the CSS3 Template Layout Module:
http://a.deveria.com/csstpl/definition-lists-tlm.html
Yes, I know this is probably cheating since it uses JavaScript to parse the CSS, but the markup is preserved and the output appears as desired. Hey, it worked for Kirk.
Also there’s the issue that this solution works for this example, but you’d have to adjust it for additional definitions.
Hello,
We’ve been using DL’s to layout forms (user-definitions lists) on Blinkbox for some time. You can see this in use on our registration page:
http://www.blinkbox.com/register
What follows is a cleaned up version for your general consumption.
Cheers
Chris
dl
{
margin: 0;
padding: 0;
}
dt
{
clear: left;
float: left;
text-align: right;
margin: 3px 0;
width: 12em;
}
dd
{
margin: 0 5px 5px;
padding-left: 13em;
}
dd:after /* Standard clearfix hack */
{
content: “.”;
display: block;
height: 0;
clear: both;
visibility: hidden;
overflow: hidden;
}
dd.wide
{
clear: left;
padding: 0 1em !important;
}
Could be fun
CSS puzzles like this one are what got me hooked in the first place. Nice to take a break from the humdrum and have a go at something like this. Always good to see how others approach it too.
More!
Late to the party, but this is something we did a few years ago: http://www.broads-authority.gov.uk/a-to-z.html
Something’s up with the fonts, but isn’t that the effect you’re after?
This looks fun; if I’d noticed the post in time I’d have joined in. Definitely up for more!
Oh, I used a negative margin on my site to shove even-width form controls to the right of my odd-width labels, which let me eschew any sort of tabular layout.
It really does feel naughty, even though it’s compliant XHTML 1.0 Strict and CSS2.1.
[...] original post – css challenge – describes what he is after: a “glossary style” appearance with the term on the left [...]
Hopefully this will do you the trick (if I understand what you’re after):
* { margin: 0;}
body { width: 800; margin-left: auto; margin-right: auto; }
dt { clear: both; float: left; display: inline; width: 49%; border: 1px solid #C00; }
dd { float: right; display: inline; width: 50%; border: 1px solid #00C; }
dd.last { clear: both; }
place the last class on the last definition:
term
def
def
def
def
Has a few probs in IE which will need ironing out, but FF is fine. Now back to C# and away from the CSS diversion.