Question

I am working with an existing piece of CSS code that looks like this (excerpted from a much larger body of code):

.apl_widget_fourLevel {
margin:0 auto 1em;
overflow:hidden;
/* zoom:1 */ /* IE Sheet */  
}

/* a panel container */
.apl_widget_fourLevel .apl_widget_level {
    float:left;
    position:relative;
    overflow:hidden;
    text-align:center;
    width:102px;
    height:150px;
    margin:0 1px 0 0;   
}

/* extra height for widgets with the supplementary data beneath the panels */
/* reset width for selected mini panels */
.apl_widget_fourLevel.apl_widget_client1 .apl_widget_level {
    height:auto;
}

/* extra height for widgets with the supplementary data beneath the panels */
/* reset width for selected mini panels */
.apl_widget_fourLevel.apl_widget_fourLevelMini .apl_widget_level {
    height:auto;
    width:90px;
}

/* reset width for selected mini panels */
.apl_widget_fourLevel.apl_widget_fourLevelMini .apl_widget_level.apl_widget_levelSelected {
    width:102px;
}

    /* the gray label in the panel 
       enforce for mini display */
    .apl_widget_fourLevel .apl_widget_level .apl_widget_label,
    .apl_widget_fourLevel.apl_widget_fourLevelMini .apl_widget_level.apl_widget_levelSelected .apl_widget_label {
        position:absolute;
        top:20px;
        left:0;
        width:100%;
        margin:0;
        color:#555;
        font-weight:normal;
        text-transform:uppercase;
        font-size:100%;
        line-height:1.0em;
        z-index:1;
    }

    /* offset for mini labels */
    .apl_widget_fourLevel.apl_widget_fourLevelMini .apl_widget_level .apl_widget_label {
        margin-top:20px;
        font-size:85%;
    }

    /* label cascade reset for IE6, sigh */
    .apl_widget_fourLevel.apl_widget_fourLevelMini .apl_widget_level.apl_widget_levelSelected .apl_widget_label {
        margin-top:0;
        font-size:100%;
    }

    /* the value displayed in the panel */
    .apl_widget_fourLevel .apl_widget_level .apl_widget_value,
    .apl_widget_fourLevel.apl_widget_fourLevelMini .apl_widget_level.apl_widget_levelSelected .apl_widget_value {
        position:absolute;
        top:45px;
        left:0;
        width:100%;
        margin:0;
        color:#fff;
        font-weight:bold;
        font-size:28px;
        line-height:1.0em;
        z-index:1;
    }

    /* offset for mini value */
    .apl_widget_fourLevel.apl_widget_fourLevelMini .apl_widget_level .apl_widget_value {
        margin-top:15px;
        font-size:24px;
    }

    .apl_widget_fourLevel.apl_widget_client1 .apl_widget_level .apl_widget_value {
        margin-top:3px;
        font-size:20px;
        font-weight:normal;
        opacity:0;
        -moz-opacity:0;
        -webkit-opacity:0;
        filter: alpha(opacity=0);
    }

    /* value cascade reset for IE6, sigh  */
    .apl_widget_fourLevel.apl_widget_fourLevelMini .apl_widget_level.apl_widget_levelSelected .apl_widget_value {
        margin-top:0;
        font-size:28px;
    }

    /* range values smaller */
    .apl_widget_fourLevel.apl_widget_fourLevelRange .apl_widget_level .apl_widget_value {
        margin-top:7px;
        font-size:15px;
    }

    .apl_widget_fourLevel .apl_widget_value a {
        color:#fff;
    }

    /* supplemental value beneath the panel */
    .apl_widget_fourLevel .apl_widget_level .apl_widget_valueScore {
        position:absolute;
        bottom:0px;
        left:0;
        width:100%;
        color:#0072ad;
        font-weight:bold;
        font-size:28px;
        z-index:1;
    }

    .apl_widget_fourLevel .apl_widget_level .apl_widget_valueScore a {
        color:#0072ad;
    }

    /* low values will be lighter color */
    .apl_widget_fourLevel .apl_widget_level.apl_widget_levelLow .apl_widget_valueScore,
    .apl_widget_fourLevel .apl_widget_level.apl_widget_levelLow .apl_widget_valueScore a {
        color:#30a2dd;
    }

    /* the image container element */
    .apl_widget_fourLevel .apl_widget_level .apl_widget_panel {
        overflow:hidden;
        width:100%;
        height:135px;
        position:relative;
    }

        /* the panel image itself */
        .apl_widget_fourLevel .apl_widget_level .apl_widget_panel img {
            position:absolute;
            top:0;
            left:-5px;
            margin-top:-150px;
        }

        /* Individual Level image offsets */
        .apl_widget_fourLevel .apl_widget_level.apl_widget_level1 .apl_widget_panel img {
            left:-5px;
        }

        .apl_widget_fourLevel .apl_widget_level.apl_widget_level2 .apl_widget_panel img {
            left:-105px;
        }

        .apl_widget_fourLevel .apl_widget_level.apl_widget_level3 .apl_widget_panel img {
            left:-205px;
        }

        .apl_widget_fourLevel .apl_widget_level.apl_widget_level4 .apl_widget_panel img {
            left:-305px;
        }

        /* mini panel offsets */
        .apl_widget_fourLevel.apl_widget_fourLevelMini .apl_widget_level .apl_widget_panel img {
            margin-top:-300px;
            margin-left:-6px;
        }

        /* reset image offset via margin for highlighted/selected style */
        .apl_widget_fourLevel .apl_widget_level.apl_widget_levelSelected .apl_widget_panel img {
            margin:0;
        }

My major problem with this is the complexity: every rule has 3-5 selectors on it, making it really hard to figure out which rule applies. There are 25 rules here for styling four buttons with text. It can't be that hard!

Some background: this CSS styles a widget that shows four bitmapped images, one of which is selected, from a single bitmap using CSS sprites. Unselected images come from one row of the large bitmap and the image in the selected state comes from another row. The selected image is put into a box that is wider and taller than the boxes of unselected images.

So is there a program that will simplify this to something cognitively manageable? Is there a tool that can identify which values are unnecessary because they are replaced by more specific selectors? Are there best practices for dealing with CSS so that you don't get unnecessarily selective paths?


Update: 2010-08-31 12:25

So I looked into less as a way of conceptualizing the CSS code. And because my code isn't in less, I looked up css2less. Here is an excerpt of what css2less produces on the code in question:

.apl_widget_fourLevel {
    margin:0 auto 1em;
    overflow:hidden;

    .apl_widget_level.apl_widget_level1 {
        .apl_widget_panel {
            img {
                left:-5px;
            }
        }
    }
    .apl_widget_level.apl_widget_level2 {
        .apl_widget_panel {
            img {
                left:-105px;
            }
        }
    }
    .apl_widget_level.apl_widget_level3 {
        .apl_widget_panel {
            img {
                left:-205px;
            }
        }
    }
    .apl_widget_level.apl_widget_level4 {
        .apl_widget_panel {
            img {
                left:-305px;
            }
        }
    }
    ....
}

So here's the thing: apl_widget_levelX are actually unique. I think a good tool could generate this:

img#apl_widget_level1 { left:-5px; }
img#apl_widget_level2 { left:-105px; }
img#apl_widget_level3 { left:-205px; }
img#apl_widget_level4 { left:-305px; }

.apl_widget_fourLevel {
    margin:0 auto 1em;
    overflow:hidden;
    ....
}

Similar comments for the selected/unselected elements.

I like where the answers are going but I am looking for tools to make my life easier. The full CSS code in this file is 2500 lines and the entire suite is 11000 lines.


Update: 2010-09-01 09:50

This is what I turned it into by hand:

ul.apl_widget_content {
    width: 492px;
    height: 140px;
    position: relative;
}
ul.apl_widget_content li {
    background: url(/home/hbrown/tmp/widget_fourlevel_1.png) no-repeat;
    list-style: none;
    display: inline;
    position: absolute;
    text-align:center;
    text-transform:uppercase;
}

#apl_widget_level1 {
    left:5px; top: 0px;
    background-position: -13px -300px;
    width: 86px; height: 133px;
}
#apl_widget_level2 {
    left:105px; top: 0px;
    background-position: -113px -300px;
    width: 86px; height: 133px;
}
#apl_widget_level3 {
    left:205px; top: 0px;
    background-position: -213px -300px;
    width: 86px; height: 133px;
}
#apl_widget_level4 {
    left:305px; top: 0px;
    background-position: -313px -300px;
    width: 86px; height: 133px;
}
#apl_widget_level1s {
    left:5px; top: 0px;
    background-position: -5px 0px;
    width:102px; height: 133px;
}
#apl_widget_level2s {
    left:105px; top: 0px;
    background-position: -105px 0px;
    width:102px; height: 133px;
}
#apl_widget_level3s {
    left:205px; top: 0px;
    background-position: -205px 0px;
    width:102px; height: 133px;
}
#apl_widget_level4s {
    left:305px; top: 0px;
    background-position: -305px 0px;
    width:102px; height: 133px;
}
div.apl_widget_label {
    padding-top: 35px;
    font-size: 85%;
    font-weight:normal;
    top: 20px;
    line-height:1em;
}
.selected .apl_widget_label {
    padding-top: 15px;
}
div.apl_widget_value {
    font-size:24px;
    margin-top:10px;
}
.selected div.apl_widget_value {
    margin-top:15px;
}
.apl_widget_value a {
    text-decoration:none;
    color:#FFF;
}

Previously 175 lines. Now 75 lines. Most of the code (40 lines) does the CSS spriting.


Update 2010-09-01 11:30

HTML now looks like:

<ul class="apl_widget_content">
    <li id="apl_widget_level1">
        <div class="apl_widget_label">Level </div>
        <div class="apl_widget_value"><a href="#">1</a></div>
    </li>
    <li id="apl_widget_level2">
        <div class="apl_widget_label">Level</div>
        <div class="apl_widget_value"><a href="#">2</a></div>
    </li>
    <li id="apl_widget_level3s" class="selected">
        <div class="apl_widget_label">Level</div>
        <div class="apl_widget_value"><a href="#">3</a></div>
    </li>
    <li id="apl_widget_level4">
        <div class="apl_widget_label">Level</div>
        <div class="apl_widget_value"><a href="#">4</a></div>
    </li>
</ul>

HTML previously looked like:

<div class="apl_widget_strand_fourLevel apl_widget_fourLevelMini">
    <div class="apl_widget_content">
        <div class="apl_widget_level apl_widget_level1 ">
            <div class="apl_widget_label">Level</div>
            <div class="apl_widget_value"><a href="#">1</a></div>
            <div class="apl_widget_panel">
                <img alt="" src="widget_fourlevel_1.png">
            </div>
        </div>
        <div class="apl_widget_level apl_widget_level2 ">
            <div class="apl_widget_label">Level</div>
            <div class="apl_widget_value"><a href="#">2</a></div>
            <div class="apl_widget_panel">
                <img alt="" src="widget_fourlevel_1.png">
            </div>
        </div>
        <div class="apl_widget_level apl_widget_level3 apl_widget_levelSelected">
            <div class="apl_widget_label">Level</div>
            <div class="apl_widget_value"><a href="#">3</a></div>
            <div class="apl_widget_panel">
                <img alt="" src="widget_fourlevel_1.png">
            </div>
        </div>
        <div class="apl_widget_level apl_widget_level4 ">
            <div class="apl_widget_label">Level</div>
            <div class="apl_widget_value"><a href="#">4</a></div>
            <div class="apl_widget_panel">
                <img alt="" src="widget_fourlevel_1.png">
            </div>
        </div>
    </div>                    
</div>
Was it helpful?

Solution

Based on the posted HTML I'd suggest following changes:

The inner classes apl_widget_label and apl_widget_value are unnecessary and can simply replaced with unique elements (that is unique within the li). This may make the selectors slightly longer, but much better structured and more readable. Also the div around the link is unnecessary as the link can be styled directly.

<ul class="apl_widget_content">
    <li id="apl_widget_level1">
        <div>Level </div><a href="#">1</a>
    </li>
   ...

with

.apl_widget_content li div {
    padding-top: 35px;
    font-size: 85%;
    font-weight:normal;
    top: 20px;
    line-height:1em;
}
.apl_widget_content li.selected div {
    padding-top: 15px;
}
.apl_widget_content li a {
    font-size:24px;
    margin-top:10px;
    text-decoration:none;
    color:#FFF;
    display: block;
}
.apl_widget_content li.selected a {
    margin-top:15px;
}

You also can move the top, width and height properties in the level rules to the ul.apl_widget_content li(.selected) rules:

ul.apl_widget_content li {
   ...
   top: 0px;
   width: 86px; 
   height: 133px;
}

ul.apl_widget_content li.selected {
    width:102px; 
}

#apl_widget_level1 {
    background-position: -13px -300px;
}

It would be great if you could get rid of the "selected level" IDs (the ones ending with s), but I can't think of a reasonable way which still supports IE6.

I just see that you have set the li to display: inline while keeping block elements insde them. You'll need to be careful with that, because despite being technical correct CSS its exact rendering is not really defined. You may consider display: inline-block or float: left instead.

OTHER TIPS

The following are just comments; It's hard to give definitive answers to questions like this, esp. when the HTML structure is not available.


The first thing that really got me was the long class names. When you've got so much repetition within the name of the classes, I think you're doing something wrong. Names like

.apl_widget_fourLevel .apl_widget_level.apl_widget_level1 .apl_widget_panel img

Really should be shortened to (something like):

.apl_widget .fourlevel .panel img

Especially when your selectors are basically 90% repetitive, like

.apl_widget_level.apl_widget_level1

There's just no point for this! In CSS, the cascade guarantees inheritance, so adding a prefix too all your class names is just necessary. Surely if you've gotten to the point of indexing your class names, its time to swap them out for ids, like

#level1

It may seems small, but it really does make CSS more maintainable if you've got selectors that are compact and less repetitious - at least you won't be spending so much time scanning through selectors.


If the HTML structure of the widget is the same throughout the code, then you can actually swap out some of the classes for elements. It will of course reduce style reusability, but given that widgets are often do not share the same structure and design as the rest of the page, it should be possible for simpler widget styles to skimp on classes. Selectors like (say)

.profile img

would certainly be better than to just give that img a class - its both immediately obvious what you're doing, and easy to maintain at the same time.


Something else you might want to do is to only code for special cases, like this very simplified case

a {
  color: white;
  background: #666;
  text-decoration: none;
}

a.special {
  color: #B8E9FF;
}

a.brilliant {
  color: #FFAFAF;
}

Also, remove the repeated classes that stays the same within each case. Again, use the Cascade to its full potential.


3-5 selectors max is not unusual. 3-5 for every one of them is, though. The CSS selectors should logically go from simple to complex, as more cases are added. Thus, try finding the base of the widget, define a default, and code your way up.

The code snippet itself is not too bad except for the inclusion of too many and overly long class names. Rewriting from the bottom up though can often lead to optimizations, esp. if the requirements of the project has changed since the last developer (We don't need to support IE6 anymore, hurray!) But again, without seeing the structure it's hard to make definitive solutions.

If it's just for four buttons, I'd pull the page up in a modern browser and use a development toolkit (Firebug in Firefox, Dragonfly in Opera, the WebKit developer tools in Safari/Chrome) to inspect the buttons in question and see what the effective styles are on each.

You might want to go over this article I read not too long back, it has a really good overview of the pro's and con's of organizing your css. I'd also take a look at the bottom of the article it has some links to some more information.

From the looks of things your widget styles did seem to go a little overboard with the classitis, but at least its documented, I can't count the number of times I've seen undocumented css classes.

i think that you need to change the name of your clases, i see that you are using ".apl_widget_label" for almost everything and styling the element depending on the selectors.

for example:

/* the gray label in the panel 
   enforce for mini display */
    .apl_widget_fourLevel .apl_widget_level .apl_widget_label,
    .apl_widget_fourLevel.apl_widget_fourLevelMini .apl_widget_level.apl_widget_levelSelected .apl_widget_label {

why not create another class called "mini-display" and then your element would be like:

<div class=".apl_widget_label mini-display">..</div>

and your css would be:

.mini-display{..}

if you don't like it... i've seen some people that creates the classes like this

<div class="left margin-auto big red ...">..</div>

where each class changes something specific on the element (i.e left => float:left;). And they have like a library of classes that they always use.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top