views:

79

answers:

2

After working on several large web applications, and seeing gigantic style sheets with no clear structure, I'd really love to know if people have found ways to keep their css clean for large and complicated web apps.

How do you move from a legacy, mess of css to cleaned up, nicely cascading, DRY stylesheets?

The app I'm currently working on has 12000 lines of css. It's grown to this size organically as early on there were no standards or review of the css, the only rule was to make the app match the design. Some of the problems we constantly have:

  • Conflicting styles: one developer adds a .header { font-weight: bold;} but .header was already used in other modules and shouldn't be bold in those.

  • Cascading problems: Foo widget has a .header but it also contains a list of Bar widgets with .header classes.

    • If we define .foo .header { ... } and .bar .header { ... } anything not explicitly overwritten in foo will show up in bar.
    • If we define .foo > .header and .bar > .header but later need to modify foo to wrap header in a div, our styles break.
  • Inheritance problems, we constantly redefine widget fonts to 11px/normal because some top container uses a 12px / 18 px line height.

  • Fighting against widget libraries, using libraries such as dojo/dijit or jquery ui that add tons of styles to be functional means that our code is littered with places where we have to override the library styles to get things looking just right. There are ~2000 lines of css just for tweaking the builtin dijit styles


We're at a point where we're thinking of implementing the following rules:

Namespace all new widget classes - if you have a widget foo all sub-classnames will be .foo_ so we get: .foo, .foo_header, .foo_content, .foo_footer. This makes our css essentially FLAT, but we see no other way to organize our code going forward without running into the legacy styles or the cascading problems I mentioned above.

Police generic styles - have a small handful of generic classes that are only to be applied in very specific situations. e.g. .editable - apply to portions of a sentence that should invoke an editor - should only contain text nodes.

Leverage css compiler mixins To avoid repeatedly defining the same styles in different widgets, define and use mixins. Although we worry the mixins will get out of control too.

How else can we move from css mess that constantly introduces regressions to something maintainable going forward.

+3  A: 

We're using a style guide in the form of a simple HTML page with examples of every CSS rule in the stylesheet. It's very easy to tell if you add a new, incompatible rule since the examples are aligned on top of eachother.

The other pro you get from this method is reusability: you know what you got and you know that you want the style guide to be as small as possible - therefore: reuse.

When you make changes to styles already in use: check the style guide. If it doesn't change it's probably good (you might need to browse around a bit if you've just changed something including box model-issues, or width, height, padding, margin in general).

How do you move from a legacy, mess of css to cleaned up, nicely cascading, DRY stylesheets?

Use the style guide as a unit test. Once you got the essential parts in it: reduce, refactor and combine (you most probably will find some collissions between .campaign_1 span and your regular rules, inheritance can be your friend).

Conflicting styles: one developer adds a .header { font-weight: bold;} but .header was already used in other modules and shouldn't be bold in those.

In reply to Adriano Varoli Piazza's comment and the quote above: I don't recall this as a problem fully belonging to the CSS but more to the HTML markup. No matter what you do, it will be some heavy lifting. Decide which rule you'd want to keep and take actions towards cleaning out the lesser-used-ones; for example: via inheritance: #news a .header { ... } or renaming the HTML-class a .stand_out_header { ... }.

About the following idea

Namespace all new widget classes - if you have a widget foo all sub-classnames will be .foo_ so we get: .foo, .foo_header, .foo_content, .foo_footer. This makes our css essentially FLAT, but we see no other way to organize our code going forward without running into the legacy styles or the cascading problems I mentioned above.

Use a containing element instead, which will be much more easy to maintain:

<div id="widget_email">
  <h2>One type of h2</h2>
</div>
<div id="widget_twitter">
  <h2>Another h2</h2>
</div>
chelmertz
what about the case he mentions of rules conflicting in one page and another?
Adriano Varoli Piazza
The style guide idea is fantastic.
CNelson
A: 

I find that the method for "namespacing" and limiting conflict in CSS is separate into different includes what you want to apply, so each page calls only what it needs. Conflicting rules can then be made more specific simply by defining them in a more particular include:

  • general css for all pages
    • css for pages in section A
    • css for pages in section B

So if you find a .header modification you added in the general css works in A but doesn't in B, you simply move it to the lower CSS file.

Yes, this implies more files to load. There are ways around it with server-side languages, like reading all files with php and sending only one block of content.

Adriano Varoli Piazza
Unfortunately, for this current app, we only have a single page - all content is loaded with ajax and client side templating and our only choice here is to load ALL css.
CNelson
Well, then, unless there's a way to load css with ajax too, I think the other answer is applicable in this case.
Adriano Varoli Piazza