Thursday, October 4, 2012

More CSS, less JS: Orion Page Layout

We're in the "end game" of the Orion 1.0 release, and I realize that one of my bigger efforts won't surface in any of our end-user oriented New and Noteworthy lists. This summer, I revamped our JS-based approach for page layout and instead use mostly CSS for our layout and animation. Why didn't we do this in the first place? Here's a story about that.

Viewports and layout

Our very first Orion page was our editor. Given that a user might be editing a huge file, and working with only a small part of it, the editor does not render the entire document. It adds elements to the DOM as needed. The browser page can't do the scrolling since it doesn't know about the editor viewport. That meant we needed to pin the page footer to the bottom of the editor page, giving the editor the chunk of real estate between the header and footer.

We found that dijit's BorderContainer managed exactly this kind of layout and happily delegated the positioning work to this widget. This approach was adopted for the rest of our Orion pages. It didn't immediately occur to me that the editor layout was really a special case, until I started watching sites like github move to skinnier headers (and the requisite fatter footers). As footers became fatter (taller?), the fact that our footers were unnecessarily pinned to the page bottom really started to bother me. Shouldn't we be able to have a bottom area under a split main panel, but not have to pin it to the bottom?

The pain of a hybrid approach

That wasn't the only thing bugging me. Most of our UI implementation does not use dijit widgets. We have many of our own UI components, such as the editor, navigator, and compare view. We rely on dijit for things like menus, dropdowns and dialogs. It turns out that using dijit layout mechanisms to manage both dijit and non-dijit components is a pain. The dijit layouts very nicely handle the ripple associated with widget size changes and layout. But if your component is not a dijit widget, you end up with code that has to walk up the DOM looking for dijit layout managers to inform about what you've just done in the DOM. This just felt...errr....wrong. Note I'm not saying dijit layout is bad, just that a hybrid approach is bad. We needed to either embrace the dijit widget and layout model with all our components, or stop using dijit layout. The first idea wasn't viable because folks who consume our editor don't want to bring in a library they don't already use. And doesn't the rest of the world just use CSS?

Leaner layout

After looking at a few different grid systems and site templates, the first thing to decide was whether we were trying to build a generic grid and layout system or just solve our own problems. Orion plugins are either contributing behavior to our pages or linking to their own. Since it doesn't matter to us how you accomplish your layout, our solution needn't be too general or generic (a departure from our Eclipse thinking!)

Page regions

A quick tour of our pages showed that we have (at most) four regions to contend with. The header, the footer, an optional side panel, and main panel. The CSS classes use this terminology throughout. The model is one of a "side panel" defaulting to 1/3 width on the left and a "main panel" taking the rest of the room. A page always has a fixed header but may have a fluid footer (at the bottom of content) or a fixed footer on the bottom of the page. Part of the exercise in establishing this model was separating out the layout classes from the other styling, moving all of our layout related css to its own layout file.

Splitters

A missing piece was the splitter. By keeping the splitter very particular to Orion's model of a vertical split between side and main panel, this was only a couple hundred lines of JS, including reasonable documentation. Splitter classes in the CSS control the default positioning and animation effects. The splitter is the only JS code managing any of our main page layout.

CSS Transitions

We previously relied on dojo's animation features to animate the effect of the side panel opening and closing. Now we've adopted simple CSS transitions for animating this effect. The downside is that you won't see the animation at play in older browsers like IE9, but the upside is that the animation is smoother, faster, and less buggy.

Some details

If you read the CSS, you'll see it's mostly about the concepts I've described here. A few more details are involved.

Toolbars

Classes dealing with the toolbar ensure that the toolbar maintains a fixed vertical position and that message bars and input bars appear in the right place underneath the toolbar, letting the rest of the main panel scroll.

dijitManagesLayout

We have some CSS classes for pages to use if they are still using dijit layout widgets inside their implementation. At that point, it's up the page to deal with any "hybrid" problems when dijit is managing non-dijit widgets. We just need to know when dijit layout is taking over.

Fluid footers

So where are those fluid footers? Since our footer is still pretty skinny, there wasn't a crying a need to revisit the footer content and pinning behavior for 1.0. Even so, I'm very happy with the result of this effort. We got rid of a lot of code, the animations are smoother, the DOM is simpler to debug, and our CSS is better organized.

1 comment: