Entries tagged “css”

Enabling a debug.css in django

I like for extra magical stuff to occur when I am in development/debug mode. One of those magical things is a a magic debug.css style sheet.

Read full post
Enabling a debug.css in django

I like for extra magical stuff to occur when I am in development/debug mode. One of those magical things is a a magic debug.css style sheet.

Read full post
Table based layouts? In this day in age? Really? How about grids?

[tags]css, yui, grids, table, layout[/tags]

Read full post
Table based layouts? In this day in age? Really? How about grids?

[tags]css, yui, grids, table, layout[/tags]

Read full post
jQuery shortcut functions and jQuery plugins

[tags]js, javascript, readability, yui, jquery, shortcuts[/tags]

Read full post
resetting CSS list items

[tags]css, symfony, blueprint, yui[/tags]

Read full post
resetting CSS list items

[tags]css, symfony, blueprint, yui[/tags]

Read full post
sfBlueprintPlugin: Train of thought development

I’ve been creating some dummy projects for my presentations at SymfonyCamp and decided now would be a good time to learn using the Blueprint CSS framework. It’s a bit different than YUI which I’ve been using heavily, but it has some potential.

Of course, instead of just downloading the framework, I made a plugin for symfony. It’s not much yet, but eventually I’ll throw in some helpers.

Enjoy!

[tags]css, symfony, blueprint, plugins, symfony camp, camp, yui[/tags]

Read full post
sfBlueprintPlugin: Train of thought development

I’ve been creating some dummy projects for my presentations at SymfonyCamp and decided now would be a good time to learn using the Blueprint CSS framework. It’s a bit different than YUI which I’ve been using heavily, but it has some potential.

Of course, instead of just downloading the framework, I made a plugin for symfony. It’s not much yet, but eventually I’ll throw in some helpers.

Enjoy!

[tags]css, symfony, blueprint, plugins, symfony camp, camp, yui[/tags]

Read full post
Symfony Camp: Ajax and Zend, what would you like to know?

[tags]symfonyCamp, symfony, netherlands, ajax, zend search lucene, zsl, jquery[/tags]

I’ve been asked to speak at SymfonyCamp (symfony['camp']) next month (you should all go if you can) and I thought I’d present as well as I could on Ajax and the Zend Framework Bridge (including Zend Search Lucene).

If you’re attending the camp and/or would like to hear about these topics please let me know any specific questions you might have about “symfony and Ajax” and “symfony and Zend” and I’ll try to address them in my presentations.

If you are unable to go fear not, I’ll try to post my notes on this site.

Read full post
FBML and embedded CMS

[tags]fbml, css, reviewsby.us, partials, symfony, sfFacebookPlatformPlugin[/tags]

Read full post
FBML and embedded CMS

[tags]fbml, css, reviewsby.us, partials, symfony, sfFacebookPlatformPlugin[/tags]

Read full post
Equal height columns with jQuery

[tags]css, jQuery, layout, javascript, equal, columns, equal columns[/tags]

I’ve seen a few examples of how to equalize column heights using javascript, and none of them seem appealing:

  • jquery.equalizecols.js
    • This required a few other libraries, and I wanted more flexibility (e.g. where the column should grow in order to equalize)
  • Project 7
    • The Project 7 approach was the most interesting, but the code seemed a bit messy and not so open source friendly (even thought it might have been). It would let you specify which element was to grow inside a column.
  • Nifty Corners
    • I had trouble with the syntax, but I liked how it just created a new element out of thin air…

So I wrote my own:

$("#col1, #col2").equalizeCols();

will equalize the columns as expected

$("#col1, #col2").equalizeCols("p,p");

will equalize the columns and add the extra space after the p tag in #col1 or #col2 (whichever is shorter).

Here’s our function:

This requires jQuery of course, and it hasn’t been tested much.

Read full post
Equal height columns with jQuery

[tags]css, jQuery, layout, javascript, equal, columns, equal columns[/tags]

I’ve seen a few examples of how to equalize column heights using javascript, and none of them seem appealing:

  • jquery.equalizecols.js
    • This required a few other libraries, and I wanted more flexibility (e.g. where the column should grow in order to equalize)
  • Project 7
    • The Project 7 approach was the most interesting, but the code seemed a bit messy and not so open source friendly (even thought it might have been). It would let you specify which element was to grow inside a column.
  • Nifty Corners
    • I had trouble with the syntax, but I liked how it just created a new element out of thin air…

So I wrote my own:

$("#col1, #col2").equalizeCols();

will equalize the columns as expected

$("#col1, #col2").equalizeCols("p,p");

will equalize the columns and add the extra space after the p tag in #col1 or #col2 (whichever is shorter).

Here’s our function:

This requires jQuery of course, and it hasn’t been tested much.

Read full post
Textmate Snippets for YUI em calculations

[tags]yui, yahoo, css, snippet, textmate, ems, px[/tags]

If you use YUI grid layouts you’ll notice that ems are the preferred units and for good reason. But ems don’t make sense to people like us who want to be super precise down to the pixel… pixels make sense.

So type in a number select it and run this ruby script as a TextMate command (that outputs as a snippet):

You’ll have the proper tab stops to change the newly calculated ems from width to margin-left or margin-right or whatever it is you desire.

Read full post
Textmate Snippets for YUI em calculations

[tags]yui, yahoo, css, snippet, textmate, ems, px[/tags]

If you use YUI grid layouts you’ll notice that ems are the preferred units and for good reason. But ems don’t make sense to people like us who want to be super precise down to the pixel… pixels make sense.

So type in a number select it and run this ruby script as a TextMate command (that outputs as a snippet):

You’ll have the proper tab stops to change the newly calculated ems from width to margin-left or margin-right or whatever it is you desire.

Read full post
TextMate + YUI = YUI snippets!

I do a lot of YUI grid layouts and I love the nestable grids:

<div class="yui-g$1"> 
	<div class="yui-u first">
		$2
	</div>
	<div class="yui-u">
		$3
	</div>
</div>

There’s a tab stop after yui-g in case you want to use one of the variants (yui-gb, yui-gc, etc).

I’m working on a site that uses two equal width columns… a lot… so this comes in quite handy. So long tables.

Read full post
TextMate + YUI = YUI snippets!

I do a lot of YUI grid layouts and I love the nestable grids:

<div class="yui-g$1"> 
	<div class="yui-u first">
		$2
	</div>
	<div class="yui-u">
		$3
	</div>
</div>

There’s a tab stop after yui-g in case you want to use one of the variants (yui-gb, yui-gc, etc).

I’m working on a site that uses two equal width columns… a lot… so this comes in quite handy. So long tables.

Read full post
How to make Prototype Window Class as easy as Lightbox Gone Wild

I like the way that Lightbox Gone Wild will automatically pickup any links with the class=lbOn, but I wanted to use (at some point) Prototype Window Class instead.

Luckily PWC is built on Prototype which means we’ve already loaded a helpful library.

In order to take all class=lbOn objects and run them through PWC we just write a simple loop and iterate.

So here’s the low-down:

  • Download PWC
  • Copy window.js
  • Use the included prototype & script.aculo.us if you don’t already include it in your page
  • copy any themes you wish to use.

In your page add this bit of JavaScript:

So, this code simply looks for all the anchor tags with class=lbOn and then creates a new mylb instance for each anchor. The end.

Read full post
Cropping Images using DHTML (Prototype) and symfony

Note: Like many of my tutorials, you don’t need symfony, just PHP. However, I develop in symfony and take advantage of the MVC-support that it offers.

Years ago when I was working on a photo gallery for davedash.com I got the art of making tumbnails down fairly well. It was automated and didn’t allow for specifying how the thumbnail should be made. With dozens of photos (which was a lot back then), when would I find that kind of time.

Flashback to today, for my company… we want users with avatars… but nothing too large. Maybe a nice 80x80 picture. Well the coolest UI I’ve seen was Apple’s Address Book which let you use this slider mechanism to crop a fixed sized image from a larger image.

Here’s a demo.

Overview

The front-end GUI is based on code from digg which is based on the look and feel (as near as I can tell) from Apple.

The GUI provides a clever visual way of telling the server how to chop the image. The gist is this, sliding the image around and zooming in and out change a few form values that get passed to another script which uses this data to produce the image.

Frontend: What would you like to crop?

In this tutorial, we’re going to be cropping an 80x80 avatar from an uploaded image. The front-end requires the correct mix of Javascript, CSS, HTML and images. The Javascript sets up the initial placements of the image and the controls. The CSS presents some necessary styling. The images makeup some of the controls. The HTML glues everything together.

HTML

Let’s work on our HTML first. Since I used symfony, I created a crop action for a userpics module. So in our cropSuccess.php template:

<div id="ava">
	<?php echo form_tag("userpics/crop") ?>
		<div id="ava_img">
			<div id="ava_overlay"></div>
			<div id="ava_drager"></div>
			<img src="<?php echo $image ?>" id="avatar" />
		</div>
		<div id="ava_slider"><div id="ava_handle"></div></div>
		<input type="hidden" id="ava_width" name="width" value="80" />
		<input type="hidden" id="ava_x" name="x" value="100" />
		<input type="hidden" id="ava_y" name="y" value="100" />
		<input type="hidden" id="ava_image" name="file" value="<?php echo $image ?>" />
		</div>
		<input type="submit" name="submit" id="ava_submit" value="Crop" style="width: auto; font-size: 105%; font-weight: bold; margin: 1em 0;" />
	</form>
</div>

Right now a lot of this doesn’t quite make sense. If you attempt to render it, you will just see only the image. As we add the corresponding CSS and images it will make some more sense.

CSS and corresponding images

We’ll go through each style individually and explain what purpose it serves in terms of the GUI.

#ava is our container.

#ava {
	border: 1px solid gray;
	 width: 200px;
}

#ava_img is the area that contains our image. Our window for editing this image 200x200 pixels. If we drag out image out of bounds we just want the overflowing image to be clipped. We also want our position to be relative so any child elements can be positioned absolutely with respect to #ava_img.

#ava_img {
	width: 200px;
	height: 200px;
	overflow: hidden;
	position: relative;
}
overlay

#ava_overlay is a window we use to see what exactly will be our avatar. If it’s in the small 80x80 window in the center of the image, then it’s part of the avatar. If it’s in the fuzzy region, then it’s getting cropped out. This overlay of course needs to be positioned absolutely.

#ava_overlay {
	width: 200px;
	height: 200px;
	position: absolute;
	top: 0px;
	left: 0px;
	background: url('/images/overlay.png');
	z-index: 50;
}

#ava_drager is probably the least intuitive element (Heck, I’m not even sure if I’ve even got it right). In our demo you’re not actually dragging the image, because you can drag anywhere within the #ava_img container and move the image around. You’re using dragging an invisible handle. It’s a 400x400 pixel square that can be dragged all over the container and thusly move the image as needed.

#ava_drager {
	width: 400px;
	height: 400px;
	position: absolute;
	z-index: 100;
	color: #fff;
	cursor: move;
}

#avatar is our image, and since it will be moving all around the window, it requires absolute positioning.

#avatar {
	position: absolute;
}
overlay
overlay

#ava_slider and #ava_handle are our slider components. They should be self-explanatory.

#ava_slider {
	width: 200px;
	height: 27px;
	background: #eee;
	position: relative;
	border-top: 1px solid gray;	
	background: url('/images/slider_back.png');
}
#ava_handle {
	width: 19px;
	height: 20px;
	background: blue;
	position: absolute;
	background: url('/images/handle.png');
}
Internet Explorer

PNG do not work so well in Internet Explorer, but there is a small trick, adding these components into a style sheet that only IE can read will make things work:

#ava_overlay {
  background: none;
  filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/images/ui/cropper/overlay.png', sizingMethod='crop');
}

#ava_handle {
  background: none;
  filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/images/ui/cropper/handle.png', sizingMethod='crop');
}

The Javascript

The Javascript is actually not as complicated as you’d expect thanks to the wonder of prototype. This framework provides so much so easily. You’ll need to include prototype.js and dom-drag.js.

So let’s take a look.

<script type="text/javascript" language="javascript" charset="utf-8">
// <![CDATA[
function setupAva() {
	if ($("avatar")) {
		var handle = $("ava_handle");
		var avatar = $("avatar");
		var drager = $("ava_drager");
		var slider = $("ava_slider");
		var ava_width = $("ava_width");
		var ava_x = $("ava_x");
		var ava_y = $("ava_y");
		// four numbers are minx, maxx, miny, maxy
		Drag.init(handle, null, 0, 134, 0, 0);
		Drag.init(drager, avatar, -100, 350, -100, 350);
		var start_w = avatar.width;
		var start_h = avatar.height;
		var ratio = (start_h / start_w);
		var new_h;
		var new_w;
		if (ratio > 1) {
			new_w = 80;
			new_h = (80*start_h)/start_w;
		} else {
			new_h = 80;
			new_w = (80*start_w)/start_h;
		}
		// these need to be set after we init
		avatar.style.top = '100px';
		avatar.style.left = '100px';
		avatar.style.width = new_w + 'px';
		avatar.style.height = new_h + 'px';
		avatar.style.margin = '-' + (new_h / 2) + 'px 0 0 -' + (new_w / 2) + 'px';
		handle.style.margin = '3px 0 0 20px';
		avatar.onDrag = function(x, y) {
			ava_x.value = x;
			ava_y.value = y;
		}
		handle.onDrag = function(x, y) {
			var n_width = (new_w + (x * 2));
			var n_height = (new_h + ((x * 2) * ratio));			
			avatar.style.width = n_width + 'px';
			avatar.style.height = n_height+ 'px';
			ava_width.value = n_width;	
			avatar.style.margin = '-' + (n_height / 2) + 'px 0 0 -' + (n_width / 2) + 'px';
		}
	}
}
Event.observe(window,'load',setupAva, false);
// ]]>
</script>

If this isn’t exactly crystal clear, I can explain. If you’re new to prototype, $() is the same as doucment.getElementByID() (at least for our purposes).

We need to initialize two draggable elements, one is our slider for zooming and the other is our avatar itself. We initialize the draggers using Drag.init(). We specify what to drag, if another element should be used as a handle and then the range of motion in xy coordinates. In the second call we use that #dragger to move around the image in this manner.

Drag.init(handle, null, 0, 134, 0, 0);
Drag.init(drager, avatar, -100, 350, -100, 350);

We want to initialize the the size and placement of the avatar. We do that using maths. First we want it in our 80x80 pixel box. So it should be roughly 80x80. I’ve set the math up so that the smallest side is 80 pixels (there’s reasons for doing this the other way around).

	if (ratio > 1) {
		new_w = 80;
		new_h = (80*start_h)/start_w;
	} else {
		new_h = 80;
		new_w = (80*start_w)/start_h;
	}

We then place the avatar element. We initialize it to be in the center of the screen (top: 100px;left:100px) and then nudge the image using margins.

	avatar.style.top = '100px';
	avatar.style.left = '100px';
	avatar.style.width = new_w + 'px';
	avatar.style.height = new_h + 'px';
	avatar.style.margin = '-' + (new_h / 2) + 'px 0 0 -' + (new_w / 2) + 'px';

We also use margins to place the handle.

	handle.style.margin = '3px 0 0 20px';

#ava_x and #ava_y tell us where the center of the avatar is. So when the avatar is moved we need to set these again:

	avatar.onDrag = function(x, y) {
		ava_x.value = x;
		ava_y.value = y;
	}

That was easy. Slighly more complicated is the zoomer function. We are basically adjusting the width and the height proportionately based on roughly where the slider is. Note that we’re still using that ratio variable that we calculated earlier. We basically take the new x-coordinate of the handle and allow our image to get just slightly larger than the #ava_image container.

	handle.onDrag = function(x, y) {
		var n_width = (new_w + (x * 2));
		var n_height = (new_h + ((x * 2) * ratio));			
		avatar.style.width = n_width + 'px';
		avatar.style.height = n_height+ 'px';
		ava_width.value = n_width;	
		avatar.style.margin = '-' + (n_height / 2) + 'px 0 0 -' + (n_width / 2) + 'px';
	}

We want to load initialize the slider right away when the page loads: Event.observe(window,'load',setupAva, false);

Not terribly hard or complicated. Once these elements are all in place you have a working functioning slider. It returns the x and y coordinates of the center of the image with respect to our 200x200 pixel #ava_image. It also tells us the new width of our image. We feed this information into a new script and out should pop a new image which matches exactly what we see in our GUI.

Processing the crop

Initially I was frustrated with the data that was being sent. I knew the center of the image in relation to this 200x200 pixel canvas and its width… but what could I do with that. Well I could just recreate what I saw in the GUI. I needed to create a 200x200 pixel image first, place my original avatar resized (and resampled) at the precise coordinates and then cut out the center most 80x80 pixels to become the final avatar image.

If you note in our template above for cropSuccess.php we submit our form back to the crop action. Let’s look at the action:

public function executeCrop()
{
	if ($this->getRequestParameter('file')&&$this->getRequestParameter('width')) {			// we are saving our cropped image
		// Load the original avatar into a GD image so we can manipulate it with GD
		$o_filename = $this->getRequestParameter('file');  // we'll use this to find the file on our system
		$o_filename = sfConfig::get('sf_root_dir').'/web' . $o_filename;
		$o_im = @imagecreatetruecolor(80, 80) or die("Cannot Initialize new GD image stream");
		$o_imagetype = exif_imagetype($o_filename); // is this gif/jpeg/png
	
		// appropriately create the GD image
		switch ($o_imagetype) {
			case 1: // gif
				$o_im = imagecreatefromgif($o_filename);
				break;
			case 2: // jpeg
				$o_im = imagecreatefromjpeg($o_filename);	
				break;
			case 3: // png
				$o_im = imagecreatefrompng($o_filename);
				break;
		}
		
		// Let's create our canvas
		$im = @imagecreatetruecolor(200, 200) or die("Cannot Initialize new GD image stream");
		imagecolortransparent ( $im, 127 ); // set the transparency color to 127
		imagefilledrectangle( $im, 0, 0, 200, 200, 127 ); // fill the canvas with a transparent rectangle
	
		// let's get the new dimension for our image
	
		$new_width = $this->getRequestParameter('width');
		$o_width = imageSX($o_im);
		$o_height = imageSY($o_im);
		
		$new_height = $o_height/$o_width * $new_width;
	
		// we place the image at the xy coordinate and then shift it so that the image is now centered at the xy coordinate
		$x = $this->getRequestParameter('x') - $new_width/2;
		$y = $this->getRequestParameter('y') - $new_height/2;
		
		// copy the original image resized and resampled onto the canvas
		imagecopyresampled($im,$o_im,$x,$y,0,0,$new_width,$new_height,$o_width,$o_height); 
		imagedestroy($o_im);
		
		// $final will be our final image, we will chop $im and take out the 80x80 center
		$final = @imagecreatetruecolor(80, 80) or die("Cannot Initialize new GD image stream");
		imagecolortransparent ( $final, 127 ); // maintain transparency
	
		//copy the center of our original image and store it here
		imagecopyresampled ( $final, $im, 0, 0, 60, 60, 80, 80, 80, 80 );
		imagedestroy($im);
	
		//save our new user pic
		$p = new Userpic();
		$p->setUser($this->getUser()->getUser());
		$p->setGD2($final);
		$p->save();
		imagedestroy($final);
		$this->userpic = $p;
		return "Finished";
	}


	$this->getResponse()->addJavascript("dom-drag");
	$this->getResponse()->addJavascript('/sf/js/prototype/prototype');
	$this->getResponse()->addJavascript('/sf/js/prototype/effects');
	$this->image = '/images/userpics/originals/' . $this->getRequestParameter('file');
}

It’s doing exactly what the paragraph above explains when the image dimensions are given. The code is well commented so it should be easy enough to follow.

GD image functions in PHP are fairly robust and can help you do a lot of tricks with image data. Note the code to save the image, we’ll cover it in detail soon.

The Model

$p = new Userpic();
$p->setUser($this->getUser()->getUser());
$p->setGD2($final);
$p->save();

First some clarification the second line. myUser::getUser() gets the User object associated with the currently logged in user. The third line, however, is where the magic happens. Before we look at it, let’s have a quick look at our model:

userpic:
 _attributes: { phpName: Userpic }
 id:
 user_id:
 image: blob
 thumb: blob
 created_at:
 updated_at:

We have an image attribute and a thumb property to our Userpic object. This is where we store PNG versions of each icon and their 16x16 thumbnails respectively. We do this in Userpic::setGD2():

public function setGD2($gd2_image)
{
	//convert to PNG
	ob_start();
	imagepng($gd2_image);
	$png = ob_get_clean();
	//save 16x16
	$gd2_tn = @imagecreatetruecolor(16, 16) or die("Cannot Initialize new GD image stream");
	imagealphablending( $gd2_tn, true );
	imagecolortransparent ( $gd2_tn, 127 );
	
	imagecopyresampled ( $gd2_tn, $gd2_image, 0, 0, 0, 0, 16, 16, 80, 80 );
	ob_start();
	imagepng($gd2_tn);
	$tn = ob_get_clean();
	
	$this->setImage($png);
	$this->setThumb($tn);
}

We capture the output of the full size PNG, then we scale it again and capture the output of the thumbnail and set them.

Conclusion

When it comes to web apps, having a relatively simple GUI for people to resize images can go a long way in terms of adoption rate of avatars and custom user pictures by non technical users.

Enjoy, and if you found this useful (or better implemented it) let me know.

Read full post
Inverting color codes in Textmate

I deal a lot with hex color codes in CSS. One thing I occasionally need to do is invert color codes. Normally this is something I could Google for, but I wanted a solution that didn’t requiring constant reference.

My favorite text editor, Textmate, has a powerful automation system. I can write mini scripts in whatever language suitable and take advantage of the power of Unix shell scripting to execute them. From Googling, I learned enough ruby to learn that this:

printf("#%06X", 0xFFFFFF - STDIN.gets.gsub(/^#/,"").hex )

Will invert a hex color from standard input. What it’s doing is fairly simple. It’s using printf to print a formatted string. %06X means it should zero-fill the resulting string with up to six zeros, the same way a hex color string is (e.g. we write 0000FF and not FF to mean ‘blue’). The rest is simple subtraction. We take FFFFFF, the hex code for white, and subtract the input from STDIN and arrive at the inverse of what we started. Now to add this to TextMate we open Automation|Run Command|Edit Commands... and create a new command:

echo $TM_SELECTED_TEXT |ruby -e 'printf("#%06X", 0xFFFFFF - STDIN.gets.gsub(/^#/,"").hex )' 

This echo’s whatever is selected and pipes it to the ruby script. We set the command to input selected text and replace the selected text on output. Furthermore we can bind it to a keystroke. I chose Control-Alt-I, as it is unused on my system.

Voila, I can highlight any hex code and instantly invert it.

To keep this on one line, I neglected a few friendly features. One is interpreting 3-digit hex colors (e.g. #ccc), and the other is knowing whether or not to place the # in the result. If you can come up with an elegant solution, please post it below. Otherwise I hope this helps.

Read full post
Safari Fixes

Safari interprets /* */s differently than FireFox or IE. FF and IE will ignore a unmatched /* or */, whereas Safari will ignore parts of code if there’s a lone */. Once I found that out, I was able to get the list items that are used throughout the site to render properly.

Read full post
Breaking IE 6 with links on PNG backgrounds

In IE there’s a whole slew of troubles with PNG. One such trouble is links or anchors will not work in IE if you have a PNG image that has gone through the Microsoft [AlphaImageLoader], which is the only known way to render PNGs in IE6.

The solution, involves running the image filter on a separate element, and then positioning all the links within that element in a higher z-order. This is explained in better detail in Filter Flaws.

I’ve been running into too many designers who have been sending me too many designs that require translucent layers, and alpha-transparencies (and they should be able to, even though the trend is more simple these days). This of course subjects me to pretty much any strange quirk that IE6 can dish out with PNGs.

What’s frustrating is that 5 years ago I had PNGs problems. This is the Internet… very few problems on the Internet last that long. There’s certainly been enough people complaining about this malfunction in IE for years.

The problem with these problems is there is no “right” solution. Using [AlphaImageLoader] is a hack. As we can see with laying links upon backgrounds that use the loader, it’s prone to behavior we don’t normally expect. We shouldn’t have to raise the links above an invisible layer, in order to click on them.

Unfortunately as long as the majority of our users continue to use IE 6 (or older) we’ll be stuck with this problem. If we look at adoption rates it won’t be until a year after Internet Explorer 7 is out until it’s the top dog. Even then, if Firefox is still at 10% marketshare, that leaves another 40% with IE6 (or less) and the less popular browsers. We’re still stuck with this problem for some time.

Read full post
Editing CSS live in Firefox

Summary

Firefox + Web Developer Extension = Live CSS Editing if that makes sense to you, you probably don’t need to read on any further, except perhaps the “caveats” section.

Trusting a WYSIWYG editor for CSS can be quite inaccurate and after viewing a site within Firefox and IE it can be quite different than intended. This leads CSS developers into the Edit→Save→Reload→repeat↩ cycle.

What if you could edit the CSS that Firefox is using without having to go through this cycle?

Firefox and the Web Developer Extension

Firefox’s saving grace is the support for extensions.1 There’s a few extensions that appear on just about everyone’s top ten list of extensions for Firefox (here’s one list). Chris Pederick’s Web Developer Extension is one of those. Use it to manipulate cookies, style sheets, forms, images as well as get helpful information about the web page.

Editing CSS

The way I use CSS is by writing semantic HTML and then individually styling elements of my site. Sure a lot can be done without having to look at a page. If I want to mimic this site, I can try for:

body {
	     color: white;
	background: #333;
}

h2 { 
	color: #f6861a; /* orange */
}

Depending on how imaginative you are, you can get quite far without viewing a page. Now, however, you can just open up your unstyled page, select Edit CSS under the CSS menu of the Web Developer Extension and see the changes as you make them. You can throw Dreamweaver out. This is what you really need.

The greatest advantage of this is if you need to do pixel moving. Let’s say you have a complicated layout with absolutely positioned divs. Now you can move them a pixel at a time until they look just right.

Caveat

One major hang-up that I have with the Edit CSS feature is that it breaks relative references if you use url(). For example. Let’s say you have a /theme folder for your web site’s theme. Under the /theme you have theme.css and background.png. In theme.css you have:

body {
	background: url(background.png);
}

This will work fine, url() is relative to the file containing the CSS. When you go to Edit CSS, however, the relativity is broken, because Edit CSS adds the CSS to the currently viewed document. Therefore unless your CSS is in the same directory as your web page, anything relatively linked with url() is broken.

If this is a show stopper for you, use absolute references whenever possible. Of course with themed sites, this is often not possible. I’m sure someone clever can make some changes to this extension to fix it.

What about Internet Explorer

This method does leave out IE. You will still need to do some back and forth when looking at IE. There are a few things that can alleviate this process.

  • Use standards compliance mode. Having a similar enough box-model to work with will eliminate most of the differences noted in IE and Firefox.
  • Know the problem areas. There’s a few spots in IE that are problem areas. PNG is one, negative margins are another. If you know what they are, then when you use them you’ll be aware that you’ll need to adjust them for IE.
  • If you use hacks use the same ones. If you use a “hack” to make IE cooperate, try sticking to the same hack. It makes your code easier to read, and consistancy makes life a bit easier.

If you do all that, you’ll probably still save quite a bit of time in your CSS development.

CSS Vista

CSS Vista is a promising product. I tried it out recently (May 2006) and decided it isn’t stable enough to be useful. I would like it to be more integrated with IE as well as be a lot faster. I’m sure when they release 0.2, the stability will improve. It may have been a fluke with my laptop as well. Try it out, it might be able to be a good solution for Internet Explorer (and Firefox). Unfortunately it’s Windows only.


  1. In a heartbeat I would switch to Camino or Safari if they supported such a wide array of extensions.

Read full post
AJAX star rater for symfony

Francois from the symfony project beat me to the punch. I was going to post a detailed how-to on adding a star-rater to your web site (similar to the one’s I created for reviewsby.us), but for most of you this should do the trick. Unless people request it sooner, I’ll hold off on publishing the details on my star-rater for a while. It only offers a few minor differences (IMO advantages) to this snippet.

Read full post