webdev: (Default)
One of the things that was very important to us as we were building Google Web Designer was to build a tool that output code that was as clean as possible. Standards compliance wasn't enough--you can write fairly obfuscated code that is completely compliant with all relevant standards. We wanted to do better than just comply with the standards, we wanted clean, human-readable code that is as close as possible to what we might have written by hand.

In particular, several of the members of the GWD team--including myself--have a lot of practical and theoretical experience with CSS, and we wanted our tool to output well-structured terse CSS. As it turns out, that was more difficult than it sounds.

When an element is created in GWD, our styles controller gives it a base CSS class with a generated class name that follows the pattern "gwd-tagname-randomstring". Any CSS property changes applied to the element would go into that class. This pattern provides a couple of useful features right off:
  1. It namespaces all of our generated classes with a gwd prefix, so you can easily tell generated code from handwritten code.

  2. Including the tag name in the class gives some indication of what tag the class has been applied to. If we just used a completely random string, the class names would be a lot more opaque.
Useful class names are only the beginning, though. Any decent authoring tool has to have standard editing features like cut/copy and paste, and GWD is no exception. In terms of CSS, we determined that we wanted copied elements to share a single base class. So if you create an element on stage the code snippet looks something like this:
/* css */
.gwd-div-mak0 {
 width: 10px;
 height: 10px;
 background-color: rgb(0, 0 0);
 top: 63px;
 left: 100px;
}

/* markup */
<div class="gwd-div-mak0" id="original"></div>

(Here I have added an ID attribute to the tag to distinguish it from the duplicate we're about to make. Our paste routine is smart enough not to introduce duplicate IDs into the DOM.) If you selected that element on stage and hit Copy and then Paste, the result would be:
/* css */
.gwd-div-mak0 {
 width: 10px;
 height: 10px;
 background-color: rgb(0, 0 0);
 top: 63px;
 left: 100px;
}

/* markup */
<div class="gwd-div-mak0" id="original"></div>
<div class="gwd-div-mak0" id="original_1"></div>

Sharing classes among visually identical elements is CSS 101, so that's what we did.

Now here's the first question: what happens when you move the new element? When you do that, you change its top and left properties...but you don't want to affect the original element. So at this point, our Styles Controller is smart enough to know it needs to add a new class to the element that has been changed:
/* css */
.gwd-div-mak0 {
 width: 10px;
 height: 10px;
 background-color: rgb(0, 0 0);
 top: 63px;
 left: 100px;
}

.gwd-div-ghk3 {
 top: 100px;
 left: 200px;
}

/* markup */
<div class="gwd-div-mak0" id="original"></div>
<div class="gwd-div-mak0 gwd-div-ghk3" id="original_1"></div>

If you move the original element, it gets a new class too:
/* css */
.gwd-div-mak0 {
 width: 10px;
 height: 10px;
 background-color: rgb(0, 0 0);
 top: 63px;
 left: 100px;
}

.gwd-div-ghk3 {
 top: 100px;
 left: 200px;
}

.gwd-div-lj0n {
 top: 262px;
 left: 555px;
}

/* markup */
<div class="gwd-div-mak0 gwd-div-lj0n" id="original"></div>
<div class="gwd-div-mak0 gwd-div-ghk3" id="original_1"></div>

To bring us to our first edge case, what if you make changes to both elements background-color, width and height properties, resulting in this situation:
/* css */
.gwd-div-mak0 {
 width: 10px;
 height: 10px;
 background-color: rgb(0, 0 0);
 top: 63px;
 left: 100px;
}

.gwd-div-ghk3 {
 width: 15px;
 height: 15px;
 background-color: rgb(10, 10, 10);
 top: 100px;
 left: 200px;
}

.gwd-div-lj0n {
 width: 25px;
 height: 25px;
 background-color: rgb(100, 100, 100);
 top: 262px;
 left: 555px;
}

/* markup */
<div class="gwd-div-mak0 gwd-div-lj0n" id="original"></div>
<div class="gwd-div-mak0 gwd-div-ghk3" id="original_1"></div>

At this point the original shared class is no longer affecting either element; all of its properties are overridden by later classes for both elements. What should the tool do at this point? Should it remove the assignment from both elements? Should it delete the class from the style sheet? And if we do delete the class, what should we do if someone then hits Paste again?

This is just one of the situations we encountered, and is actually among the simplest. Other, more complex situations arise when you combine the desire to have terse CSS with features like cut/copy/paste, undo/redo, animation, and element inspection. Toss in platform inconsistencies and the occasional browser bug and you end up with a lot of edge cases to resolve.

To a significant extent we're still working on this as a team. Moving forward, we are enhancing our data models and changing some feature implementations, which should make it easier to resolve some of the edge cases. And we've received a lot of feedback from users about their expectations as well, which helps us refine our goals. This is a tough problem, but we're going to keep at it. The quality of our code output is very important to us.

Profile

webdev: (Default)
Jon Reid

October 2013

S M T W T F S
  12 345
6789101112
13141516171819
20212223242526
272829 3031  

Links

Syndicate

RSS Atom