Showing posts with label css. Show all posts
Showing posts with label css. Show all posts

22 August, 2005

CSS as JSP

The Problem

I've been working on refactoring a project from tables to a table-less design with CSS. The stated benefit (with which I agree wholeheartedly) is that using tables for layout of non-tabular data (that is, using tables to position blocks of text) is improper because it is using the wrong tool for the job. Any artisan (woodworker, metalsmith, sculptor) would tell you this is a huge no-no, but in web design, it's gone unchallenged for virtual centuries (i.e. a few years). But the "it's-just-bad-art-and-bad-zen" argument isn't the whole story: alternative browsers (cell phones, audiobrowsers for the deaf, etc) have no idea what these tables mean, and will therefore confuse their users. The problem is that though CSS is standardized, some broswers (most notoriously, IE) don't conform to the standard.

The Established Solution

So far, people have been adding hacks to their CSS to get around browser differences. Things like the star html hack and the mac backslash hack are ingenious, but they impede development. They're hard for future programmers to understand (even with liberal comments), and they're logically messy. (No self-respecting C++ programmer would write

  width = 100;
  callBackSetWidth(120);  //Linux platforms before
                          //1997 don't support this,
                          //so they get the value 100
It's just ugly.)

My Solution

I came upon my solution by accident. I have been using the .jsp extension for my CSS files for a while now for the sole purpose of being able to use the <c:url> tag. If I use an image for a background, I don't want the link to have to reference my project name - epecially since the context may change to root at deployment.

Because the CSS files were already dynamically generated, I thought of doing browser dependency with tags. I've got
<c:set var="user_agent" value="${headerValues['user-agent'][0]}" />

<c:choose>
  <c:when  test="${fn:startsWith(user_agent, 'Mozilla/5.0')}">
    <%-- Firefox 1.0, Gecko, etc --%>
    <c:set var="firefox" value="${true}" />
    <c:set var="ie" value="${false}" />
    <c:set var="app_width" value="760" />
  </c:when>
  <c:otherwise>
    <%-- IE 6 --%>
    <c:set var="firefox" value="${false}" />
    <c:set var="ie" value="${true}" />
    <c:set var="app_width" value="762" />
  </c:otherwise>
</c:choose>
at the top of the file. You could easily have more cases if you need to support more browsers. Notice first the "ie" and "firefox" variables. These can be used by tags later for browser-specific settings. Also notice the global variable app_width; this is a nice way of promoting reuse (constants are our friends - it'd sure be nice to be able to make 'em final in the page scope, though) while allowing differences between browsers.