Pixelastic

You can cut our wings but we will always remember what it was like to fly.

Posts tagged with "CSS"

The :visited pseudo-class specificity gotcha

In a previous post, I bloggued about the way to emulate OOCSS behavior with multiple classes in IE6.

Today, I'll do a follow up and write about a possible gotcha involving the :visited pseudo class.

Following the previous example let's imagine you have a styling for your defauls links (a { color:blue; }), one for the default buttons (.button { ... } ) and one for a custom button that extend the .button (.customButton { ... })

Now, imagine that you'll want to style all :visited links the same way non-visited links are styled. You might write something like :


a, a:visited { color:blue; }

Unfortunatly, this will have some nasty side effects on your .button and .customButton rules because a:visited will have precedence over .button and .customButton

You can find more information about CSS specificity in this Star Wars post.

Your first solution could be to add even more specificity to your own rules, to override the a:visited one, like so :


.button, .button:visited { ... }
.customButton, .customButton:visited { ... }

This will work, of course, but you're only adding complexity to your specificities, and this get more and more tedious the more you add other customised buttons.

In fact, there is a much better way, one that you could throw in your reset.css if it isn't there already :


a:visited { color:inherit; }

That way, all your visited links will inherit their color from their non-visited version. This mean that visited .button will use the .button color, visited .customButton will use .customButton color and simple visited links will use the a color.

Of course, if you defined a background-color in your a, you should define a background-color:inherit in your a:visited too.

Alternative way for multiple classes in IE6

In a pure OOCSS style of writing CSS, let's imagine you created a css class of .button that visually turns a simple link into a button.


<a href="#" class="button">I'm a button</a>

Now, if you want to define a custom version of your button, let's say a button that will trigger a very dangerous action, you might want to style it differently, so our user will think twice before hitting it.

You got two ways of achieving this, depending if you still support IE6 or not.

Simple way for non-IE6

If you don't care about IE6 (and hell, it's 2012, you shouldn't), you just have to add a second class to your button/link :


<a href="#" class="button dangerous">I'm a dangerous button</a>

And in your CSS file, just define some special styles (like a red background) to your dangerous button.


.button.dangerous { ... }

Actually, that's the path followed by Bootstrap (among others). But it will not correctly work in IE6, because it does not understand multiple classes rules. Instead, IE6 will read .button.dangerous {} the same as .dangerous {}.

This will cause problems as soon as you'll use the .important class on something else than a .button : IE will apply the .button.dangerous rules to anything with the .dangerous class.

Other way, for IE6

The solution I personnaly use to fix IE6 is to use more explicit classes instead of using multiple ones. For example, instead of .button.dangerous {} I'll use .buttonDangerous {} and write my html like this :


<a href="#" class="button buttonDangerous">I'm a dangerous button, even on IE6</a>

That way, the link will have both the styles of .button and .buttonDangerous. This will assure cross compatibility with IE6, at the expense of a (arguably) less readable markup.

As of today, I hope that I'll never have to code for IE6 websites again, but if you ever need to, that's a little trick that can really help.

Show and hide Flash elements without redraw

Imagine you have a Flash element in your HTML markup. If you apply any show/hide logic to it, the flash will be reset and played back to its first frame.

Actually, as soon as you change the display property of an element, if forces all inner Flash element to be restarted.

There are a few ways you can work around that, and here are the two main I'm using

Your Flash file is in a tab

Fairly common scenario, you have tabs on your page (maybe using jQuery UI or a custom code). When you display your Flash tab, all others are hidden, and when you display another tab, the Flash one is hidden.

Instead of using display:none / display:block, we'll be a little more sneaky and not really "hide" the element, but simply put it out of the user view.

Just apply the following CSS rule to the element you want to hide. It will just display it offscreen.

.tabHidden {
position:absolute;
top:0px;
left:-9999em;
}

Don't forget to add a position:relative to the HTML parent of your tab for the absolute positionning to work.

You have multiple Flash files in a list

This one is trickier. Imagine you have a list of elements, and each row contains a Flash file. You want to filter the list based on various criterias and only show the rows matching.

You can't use the previous technique here because some elements of your list will be in relative positionning while others will be in absolute positionning. And everytime you change the position property of your element, the Flash will be reset.

The trick here is to play with your element dimensions and visibility. You can change the visibility without triggering a redraw. As this will only make the element invisible but still taking space, you just have to put its height and width to 0 to make it effectively disappear.

.listElementHidden {
visibility:hidden;
width:0px;
height:0px:
margin:0px;
}

The margin:0px is here to clear any margin you might have defined around your element.

IE8 ghost :after and :before pseudo elements

IE8 has a strange bug (what bugs aren't strange in IE ?) when dealing with :after and :before pseudo-elements.

I was adding a nice looking arrow after one of my elements using :after. I wanted this arrow to only display when my element was hovered, so I wrote the following code :

<a href="#">Example</a>
a {
position:relative;
display:block;
height:30px;
}
a:after {
position:absolute;
content:"";
top:0px;
right:-15px;
width:15px;
height:30px;
background:url(arrow.gif) top left no-repeat;
display:none;
}
a:hover:after {
display:block;
}

As you can see, nothing too fancy. I positionned my arrow using an empty :after element and a background image. I defaulted the arrow to hidden, and only show it when hovering the element.

IE in action

It does work pretty well in moder browsers. It also seems to work on IE8. When you hover the element in IE8, the arrow gets displayed. But it does not gets hidden when you stop hovering it.

There's a kind of ghost element that keeps getting displayed. It gets removed if you directly mouse it, or scroll your page, or alt-tab, etc. This clearly is a display artefact.

To counter this I had to write it in an other fashion (less readable in my opinion). Removing the default a:after rule and adding all properties to a:hover:after :

a {
position:relative;
display:block;
height:30px;
}
a:hover:after {
position:absolute;
content:"";
top:0px;
right:-15px;
width:15px;
height:30px;
background:url(arrow.gif) top left no-repeat;
display:block;
}

Update

It should be noted that more generally, I gets confused and create ghost elements and styling when we try to update the :after/:before properties based on a rule selecting its parent.

There seems to have a little lag/delay before the properties gets applied, and most of the time they do not.

The dreaded <noscript> on IE7

This <noscript> tag on IE7 is making me crazy. Here are some "interesting" facts about it :

No content through Javascript

You can't get its content though javascript. It can be targeted but neither .innerHTML nor .textContent is set. It does not have any .childNodes either.

Gets displayed even when scripts are enabled

If you set noscript { display:block; border:15px solid red;}, it will get displayed even if Javascript is enabled. But with no content inside, you'll only have borders and background...

Styling it anyway

If you want to style it, just add an inner element and style this one :

<noscript><p>Lorem ipsum</p></noscript>
noscript p { background:blue; }

Disabling Javascript on IE7

If you want to disable Javascript on IE7, you'll have to go to Tools > Internet Options > Security > Custom and setting the "Enable ASP scripts" to No.

Yes, this doesn't make any sense.


Using fonts hosted on a subdomain with @font-face and Firefox

As a security reason, Firefox do not allow an @font-face rule to load fonts hosted on a different domain (even a subdomain).

I don't exactly understand why, I guess it has something to do with preventing crosslinking and copyright violation. I think we should keep the website author handle all this stuff and not required the browser to make assumptions like that.

Anyway, I recently tried to move my CSS file to a subdomain, to reduce pages loading times. Doing so I saw that my fonts did not correctly load on Firefox.

After some digging, I found that I had to manually allow them to be linked from an other domain, server-side. Here is the little snippet I added to my .htaccess

<FilesMatch "\.(ttf|otf|woff)$">
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
</IfModule>
</FilesMatch>

 

Setting the z-index of a jQuery UI datepicker

I ran into a collision issue with two of my absolutly positionned elements on a form page. I had an invisible flash buttun (used to launch an upload process) as well as jQuery UI datepicker.

The form was built in a way that when the datepicker was displayed, it was supposed to be on top of the invisible Flash button. Unfortunatly the button was in fact "on top" of the datepicker, but being invisible you do not notice it until you click.

This resulted in an upload dialog popping up and many confused users.

The fix

The Flash z-index is fixed in my CSS (10), so I thought that adding a greater z-index to the jQuery UI datepicker in CSS would be enough.

It is not. The jQuery UI datepicker seems to automatically set the z-index to 1, whatever you specified.

I checked the datepicker options, looking for a zIndex key, but found nothing...

I tried the beforeShow event, to manually set the z-index, but it seems that jQuery still update my value to 1 AFTER the event.

So I finally resorted to adding a small timeout to re-add my value after jQuery. This is a bit of a hack but given the context this is the only way I found.

// Set the datepicker zIndex on load
element.datepicker({
[...]
'beforeShow': function(input, datepicker) {
setTimeout(function() {
$(datepicker.dpDiv).css('zIndex', 100);
}, 500);
},
[...]
});

 

Some CSS hacks to target IE6 and IE7

After stopping using ugly IE hacks and moving to conditionnal comments to load a special IE stylesheet, I now use conditional comments to mark my body element with classes reflecting the current IE version.

<!--[if IE 6]><body class="ie ie6 ie-lt8 ie-lt9"><![endif]-->
<!--[if IE 7]><body class="ie ie7 ie-lt8 ie-lt9"><![endif]-->
<!--[if IE 8]><body class="ie ie8 ie-lt9"><![endif]-->
<!--[if IE 9]><body class="ie ie9"><![endif]-->
<!--[if !IE]><!--><body class="nie"><!--<![endif]-->

This saves me a lot of trouble : less files to manage and easier fixes to write. I quite happy with this solution and have tested it accross several projects for the past 3 months. It works really well.

I had to work on a legacy project last week, where this technique wasn't implemented but all the css code was still compressed using CSSTidy. And I ran into a couple of issues.

CSSTidy messes the star and underscore hacks

Using the brilliant _property and *property hacks to target IE6 and IE7 does not work in conjunction with CSSTidy.

For the _property hack, the property is kept as-is, with the underscore, but as they are alphabetically arranged, the _background gets added before the background, rendering it absolutly useless.

On the other hand, on the *property, the * gets removed, and the value is merged with the original value of the correct property. Useless too.

Other solutions that worked

To avoid digging into CSSTidy one more time, I tried to find other ways to achieve the same effect.

To target IE6 I used the !ie6 hack by writing .mySelector { property:value !ie6; }. IE6 is dumb enough to understand any !blahblah as !important.

I could have also used the fact that IE6 understands .class1.class2 as .class2, and could have written .ie6.mySelector { property:value; } (of course, you have absolutly no class="ie6" in your code)

To target IE7 I made a custom selector that its parsing engine is the only one to understand : *:first-child + html .mySelector { property:value; }

 

CSS for Javascript enabled browsers

In a previous post I was wondering if my way of loading js-specific CSS rules was right. I was loading a specific css file for js overrides.

This allow for lighter stylesheets for users without Javascript (they won't load useless rules), but also resulted in slower rendering for other users because they needed to load two stylesheets instead of one.

After much pondering, I now think that loading all the rules in one stylesheet is the way to go.

In addition with the number of request mentionned, keeping all your rules in the same file is much more easier to maintain. Js-specific rules aren't that big either so the small additional bytes are usually better than a full http request.

I now prefix all my Javascript rules by .js. I add a js class to the html element directly from the head, this helps in preventing any FOUC that may occur because we add the class before the body rendering.

<head>
    <script>document.documentElement.className+=' js';</script>
</head>

 

How to prevent a FOUC but still gracefully degrade the jQuery UI tabs

I finally managed to fix something that was bugging me for a very long time. I'm talking about the jQuery UI tabs.

They are pretty useful actually, but I always hated that FOUC they produce. For one split second, you'll see all your tabs content, then they'll be neatly re-arranged in tabs.

What I want are my tabs displayed correctly right away, on first load.

First, the markup

Here's a typical tabs markup :

<div class="tabs">
    <ul>
        <li><a href="#firstTab">First tab</a></li>
        <li><a href="#secondTab">Second tab</a></li>
    </ul>

    <div class="tabPanel" id="firstTab">
        First tab content
    </div>
   
    <div class="tabPanel" id="secondTab">
        Second tab content
    </div>
</div>

CSS to hide all tabs if Javascript is disabled

If your Javascript is disabled, so will jQuery UI. We will then hide the <ul> because it serves no purpose here. We will only show it if js is enabled

.tabs ul { display:none; }
.js .tabs ul { display:block; }

Applying jQuery UI tabs

By doing a $('.tabs').tabs(); jQuery UI will treat your <ul> as your tab menu and all your .tabPanel as their corresponding contents. It will hide all your panels, except for the first one. It does so by adding a .ui-tabs-panel class to every .tabPanel as well as a .ui-tabs-hide to every panel it hides.

Right now, you should add another CSS rule to hide the unused panels :

.ui-tabs-hide { display: none; }

But if you look at your page now, you'll see all your tabs content before they get hidden. That is the FOUC I was talking about. The jQuery UI documentation indicate that to remove it, you should directly add the .ui-tabs-hide class to panels you'll want to hide.

As also pointed in the doc, it will not gracefully degrade because users without Javascript won't even be able to see your other tabs. Also, it asks you to add server-side logic (HTML markup with jQuery specific classes) for something that should be handled entirely client-side.

Removing the FOUC while gracefully degrade

Ok, so what I did was writing two simple rules that will directly hide all unused panels while still displaying the active one, even before jQuery UI takes action.

.js .tabPanel + .tabPanel { display:none;}

That way, no FOUC, and users without Javascript still see all the content. Unfortunatly, if you now try clicking on your tabs, you'll see that nothing happens and you get stuck with your default panel always visible.

Fixing the jQuery UI tabs

As jQuery will add new classes to my elements, I'll just have to write more specific rules that use those classes. Here's the little piece of logic I came up with :

.js .tabPanel.ui-tabs-panel { display:block;}
.js .tabPanel.ui-tabs-panel.ui-tabs-hide { display:none;}

All the jQuery panels are shown, except for the one hidden by jQuery. All those rules being more and more specific, they will get applied once the tabs are activated but still override the previous display: declarations.

Conclusion

This is how I fixed an issue that was bugging me for years. Unfortunatly the CSS rules being dependent on the markup used, I haven't yet been able to write them in a global form that could be added to the main jQuery UI css files.

Previous123