1

Currently I have this markup that represents an icon container and some elements styled with CSS inside it that in the end show as graphic icon.

    <div class="icon">
        <div class="icon-element1"></div>
        <div class="icon-element2"></div>
        <div class="icon-element3"></div>
    </div>

The number of the child elements can be different depending on the complexity of the icon.

What I want is to somehow move as much as possible to CSS stylesheet, so that ideally I would have only <div class="icon"></div> and the rest would just render from CSS styles, something close in concept to :before/:after, a kind of virtual divs. I don't want to use JavaScript to add the elements dynamically. It would be possible to do this if we had multiple :before/:after.

Here is an example of an icon I get by using the markup from above:

Icon

As you can see, there are 3 child elements representing gray case, white screen and turquoise button.

Please advise, how I can simplify this markup so that not to have to put all the divs each time I want this icon to be shown.

Sergei Basharov
  • 51,276
  • 73
  • 200
  • 335

2 Answers2

2

You could use nth-child().

.icon:nth-child(2) {
    color:red;
}

In this instance, the selector would effectively style the second child of .icon.

You wouldn't need the redundant .icon-element classes.

I know you said that the number of child elements will be different, however, there is no difference between this method and the method you are currently using anyways other than reduced markup - which is exactly what you want.

Additionally, there is also the adjacent sibling selector + which could also be effectively used.

Josh Crozier
  • 233,099
  • 56
  • 391
  • 304
  • I think the OP doesn't want to add HTML for each of the possible divs inside the icons.. – Ruan Mendes Oct 23 '13 at 22:03
  • 1
    @JuanMendes What do you mean by that? – Josh Crozier Oct 23 '13 at 22:03
  • 2
    Well, ideally I don't want to put all these divs inside, if possible. So I would like to be able to drop
    and render the rest inner elements with CSS, like with :before or :after.
    – Sergei Basharov Oct 23 '13 at 22:05
  • @JoshC This forces you to put a div for each of the elements you may need, the OP would like to control that through CSS, I'm not saying it's possible – Ruan Mendes Oct 23 '13 at 22:05
  • @SergeyBasharov Since you are talking about icons specifically, you might want to check out [Font Awesome](http://fortawesome.github.io/Font-Awesome/) or [Glyphicons](http://glyphicons.com/) That assumes they are icons as the class name implies.. This answer I wrote a while back is somewhat relevant. http://stackoverflow.com/questions/18971474/cannot-view-the-source-image-file-on-a-website/18971518#18971518 – Josh Crozier Oct 23 '13 at 22:09
  • @JoshC, yes, I am trying to perform a similar thing, but colored, that's why I need it a bit different. – Sergei Basharov Oct 23 '13 at 22:11
1

You can actually achieve the icon in your image using just ::before and ::after, if you set position:relative on <div class="icon">, and position the pseudo-elements absolutely:

http://jsfiddle.net/RMs2L/3/

.icon {
    position: relative;
    width: 160px;
    height: 160px;
    background: #666;
}

.icon::before,
.icon::after {
    content: '';
    position: absolute;
    width: 120px;
    left: 20px;
}

.icon::before {
    height: 80px;
    top: 20px;
    background: #fff;
}

.icon::after {
    height: 20px;
    bottom: 20px;
    background: #00b9ae;
}

(Not tested in IE.)

But you’re quite right that for icons with more than three parts, just ::beforeand ::after wouldn’t cut it.

If your icons are only made up of areas of flat colour (or gradients), then multiple gradient backgrounds might work for icons with more elements:

http://jsfiddle.net/RMs2L/5/

.icon {
    position: relative;
    width: 160px;
    height: 160px;
    background-color: #666;
    background-image: linear-gradient(to bottom, #fff 0%, #fff 100%)
                    , linear-gradient(to bottom, #00b9ae 0%, #00b9ae 100%)
                    , linear-gradient(to bottom, #000 0%, #000 100%);
    background-size: 120px 40px
                   , 120px 20px
                   , 120px 20px;
    background-position: 20px 20px
                       , 20px 80px
                       , 20px 120px;
    background-repeat: no-repeat;
}

You could even do the same thing with one gradient background, using sharp gradient boundaries:

http://jsfiddle.net/RMs2L/6/

background-image: linear-gradient(to bottom, 
                  #fff 0px, #fff 40px,
                  rgba(0,0,0,0) 40px, rgba(0,0,0,0) 60px,
                  #00b9ae 60px, #00b9ae 80px,
                  rgba(0,0,0,0) 80px, rgba(0,0,0,0) 100px,
                  #000 100px, #000 120px);
background-size: 120px 120px;
background-position: 20px 20px;

And, of course, you can use actual background image files as well as, or instead of, gradients.

As you can see, the CSS is much more fiddly if you try to make icons out of gradient backgrounds, as compared to styling actual elements or pseudo-elements. It might make for clearer code if you suck it up and put in the HTML elements, and style them using :nth-child, as suggested elsewhere.

Paul D. Waite
  • 96,640
  • 56
  • 199
  • 270
  • 1
    I think this is the closest you can get to what the OP is asking. The other approach would be to just use background images as sprites... I still the best solution is to leave your markup clear, instead of tricking it with `:before` and `:after`, leave all the divs you may need in there so others don't have to chase all over the code to figure out what is happening (since you can't see pseudo elements in the elements panel of dev tools) – Ruan Mendes Oct 23 '13 at 23:36
  • @JuanMendes: yeah, I agree - I think using an HTML element for each part of the logo is much more readable. – Paul D. Waite Oct 24 '13 at 09:12