CSS Generation With JavaScript – An Underutilized Content Management Tool

There are some interesting new things going on the world of web site layouts with CSS and JavaScript all the time. Tricks and tools to add to a client side developer’s arsenal for making flexible, content accommodating navigation, layouts and presentations. Though I wouldn’t give away any of our progress, I can’t help but wonder if sometimes the amount of work we ask a visitor’s browser to do is overkill. One way to shift this workload off the browser — without placing undo burden onto the site management staff or its budget by requiring a high level of technical expertise with each site update — is to move the it to an offline or backend CMS tool creating static code for publication. This is particularly useful when doing multiple site deployments with a similar theme or building different localized site versions where the need for flexibility in type doesn’t change from user to user, but from content update to content update or deployment to deployment.

Through the use of fairly simple to create build tools we can create ‘static’ CSS for deployment and consumption and trim the amount and complexity of layout code sent with each page.

Example – Sizing Large Headings

On a site I worked on last summer I had the following scenario: Sectional headings on the web site should by default be a large fixed size, allowing for 8 or 10 characters to fill the given horizontal space. This worked 80% of the time when the site was in English, but as the site sections got translated it was clear that the default font size was too large.

What were our options at that point? We could guess at the smallest font size needed to fit the longest probable section name and then set all section headers to that resulting in a smallish feeling most of the time. We could write some client side JavaScript code to send to every visitor and resize the type on the fly [or use a tool like sIFR which does this type of thing as part of its text replacement]. Or we could create a tool that is used once as part of the deployment process or CMS to set the various heading sizes.

Defining the Rules and Constraints

Whether you’re putting the logic on the public web server for download, the CMS, or in some developer or designers brain you need to determine the contraints and parameters for setting the type size. For this section header example lets work as follows:

  • Headers fill a 600px wide column and in a 120px high block
  • 100px Georgia, Bold is the base font spec
  • When a header doesn’t fit it should be the largest font size possible but still fit inside the box

screenshot of sample header sizes

Applied to the phrases our site uses for each section we see that the base font size works in most cases, but for the one outlier ‘Privacy Policy’ we need to drop the size down to 82px or so.

Deciding on the Desired CSS

If we did the above exercise to a static site that would never get updated or translated we’d probably use some type of page or header ID to apply the one exception as well as other header style changes like color or background. Even though this isn’t the case we still need to plan what the static CSS we will generate looks like and what the HTML ‘hooks’ will be.

HTML
<div class="headerBox" id="hdr-blog">
    <h2>Blog</h2>
</div>
<div class="headerBox" id="hdr-archives">
    <h2>Archives</h2>
</div>
<div class="headerBox" id="hdr-calendar">
    <h2>Calendar</h2>
</div>
<div class="headerBox" id="hdr-privacy">
    <h2>Privacy Policy</h2>
</div>
CSS
.headerBox {
    width: 600px;
    height: 120px;
    border: 1px dashed #444;
}
.headerBox h2 {
    margin: 0;
    font-family: Georgia;
    font-weight: bold;
    font-size: 100px;
    white-space: nowrap;
}
/* section specific color themes */
#hdr-blog h2 {
	color: #f0f;
}
/* deployment specific font sizes */
#hdr-privacy h2 {
	font-size: 82px;
}

It is that last section — ‘deployment specific font sizes’ — that we need to generate once for each deployment based on the length of the varied text.

Developing the Custom Editor

I had arrived at the 82 pixel font size for ‘Privacy Policy’ by trial and error and editing the CSS manually. It took about 30 seconds to land on the right size, but that task doesn’t scale well. It gets boring and tedious over the span of a single site, and requires someone manually editing CSS code over the life of the various site deployments, translations and updates. By writing a highly site specific admin tool to do this task we can move this maintenance work out of the coder’s hands and into that of a designer, content manager or translator. With great scripting libraries out there this shouldn’t take very long to do either. For our example I’m going to grab jQuery and jQuery UI to build a static & offline tool to generate the desired CSS that can then be copied to a file in our project by anyone on the team with access.

screenshot of header tool

The resulting tool is made up of three parts:

  1. Display of the content you’re adjusting or adding
  2. Interface for adjusting content parameters
  3. Generation of CSS code based on customizations

jQuery UI sliders were initialized based on the outlined parameters above [default to 100px, move inside a reasonable range] and adjustments are seen on the fly.

CSS is then generated in kind of a brute force fashion going through the hidden fields set by the sliders:

JavaScript
$("fieldset.headerWidget").each(function(i) {
	var hdr = $(this).attr("id");
	var css = "";
	// First Line
	css += "#hdr-";
	css += hdr;
	css += " h2 {n";
	// font size
	if ($(this).find(".hdrsize").val()) {
		css += "font-size: ";
		css += $(this).find(".hdrsize").val();
		css += "px;n";
	}
	css += "}n";
	css += "n";
	output.css += css;
});
CSS Output:
#hdr-blog h2 {
}
#hdr-archives h2 {
}
#hdr-calendar h2 {
}
#hdr-privacy h2 {
font-size: 82px;
}

Even including whitespace and comments the JavaScript written to make the tool work is under 50 lines. If the changes were more complex or more parameters were set the CSS could alternately be generated based on the current appearance of the item, however this may result in more verbose CSS code to publish.

Manual Sliders vs. Automatic Adjustments

In this example I’ve used manual sliders for resizing the text. The same could have been done automatically by resizing the text with JS until it measured to fit the constraints. Which way you build a depends on the project and who is doing the content management or setting up the deployment and a balance between automation and intervention. For the example I pulled from we wanted to give the manager a little room to play and use their own judgement.

Expanding on the Idea

For simplicity I’ve shown build a standalone offline editor here, this works if you have other manual tasks as part of a deployment or site build process, but sometimes you’ll need more. It would be quite easy to build a tool like this into a CMS project and have the CSS generated then saved to the proper location. Given the common markup patterns the list of headers could also be generated based on server side content. It all could flipped on its side and rather then edit all section headers at once this this could be part of a record editor where the font size [or other specs] are saved with the individual record. While that might take more infrastructure work it may be what best fits your project.

These custom tools are not limited to the font size example here either. Anything that needs some finessing when text changes, items get added, deactivated, etc can be a candidate for content management tools rather then complex client side code. For the same site referred to earlier each deployment or localized version also had a top navigation bar that varied — both in length of text of each section but also some site features [read; nav items] were optional. To take the tedious task of re-spacing the navigation for each version of the site out by hand &the need for the coder to do it we instead took 2 days to create a custom tool to space the navigation and position the submenus according to the length of the translated section names and which of the optional elements were activated.

At the end of the day where these build tools fit into your site’s deployment process will vary, but creating custom or one off tools can be an interesting and quick problem for a web developer to solve and the time spent doing so can pay great dividends over the life of a web site or product.

Minimized HTML5 Attributes, Selectors & jQuery

After working with some HTML5 web forms attributes on a small project I have come to the conclusion that for now it is best to…

Use <input required="required"> not <input required>

Some backstory — A few weeks back I was working on a small non-public web site heavy on forms and thought it would be a good fit as an HTML5 test case. For a variety of technical reasons [input formatting in particular] I didn’t go whole hog into it using all the various input type attributes, but did use the required attribute as a hook for JavaScript based form validation and styling. What I found was that generally there was adequate support for styling and selecting based on this new, and unknown to many browsers, required attribute — Yay! we can use this stuff today! However, there were a few browser CSS selector and jQuery 1.3.2 quirks that lead me to the conclusion that it is safest to use the expanded form of the required attribute and not the minimized or shorter form as HTML5 allows. This gives you the most solid and flexible options when choosing selectors in CSS or jQuery code.

Required Attribute Test Cases

I put together a simple test page and did some browser testing. The test cases and results chart are posted here.

Required attribute test cases

What you can see from these tests is that all modern browsers have strong support in CSS for the attribute selector and its use against a possibly unknown attribute. As one would hope hope they select all required fields, both shorthand and long form, based on [required] selector string [green text] and only the long form based on [required=required] [bolded]. Furthermore, Opera 10 also supports selection based on the :required pseudo selector.

The puzzling behavior comes in the few scenarios where the CSS selector behaves properly but the selection of elements via the jQuery library and JavaScript fails for those same selectors. This is seen in the Firefox 3.0/Gecko 1.9.0 based browsers where $("[required]") selects only the case where the attribute has been explicitly given a value.

One of the positive things you’ll see from these tests is that an unknown value for the input type attribute (in this case type="number") did not impact the selector behaviors in any browser.

Some Followup Investigation Needed

The linked test cases were quickly pulled together and I haven’t taken the time to look at what is going on from a more general angle. If any core jQuery or browser selector engine gurus want to chime in there are still a few things I want to chase down…

  • Are the test results different for a minimized attribute like disabled or checked that a non-HTML5 browser may understand?
  • I’d like to run the jQuery test cases against a trunk build or early 1.4 build, and figure out what is a jQuery bug, browser selection bug, and what bugs need to be filed to get the library working more consistently across the platforms it supports, I’d also like to pull jQuery out of the equation and just use the browser’s native selection engines.
  • Check up on the documentation and mailing list discussions to see if there some magical handling of the $(":required") selector coming in some future jQuery build because right now the results are all over the map.
  • I don’t even want to begin to think about how capitalization and attribute case may have an impact across browsers.

Ahhh, ‘web standards’… why do you bring so much pain with your promise?