Front-End Best Practices for Sitecore Development

Wednesday, October 22, 2014 @ 02:00

By: Bill Drol, Senior Developer – Given the dynamic nature of Sitecore development and the number of moving parts associated with content delivery in general, front-end best practices may sometimes take a back-seat to other development efforts. The following ideas are available to help promote solid front-end work in Sitecore, or any CMS development environment.

Please note, although these points mention .LESS CSS and Bootstrap specifically, they're relevant to any popular front-end technology stack.


1. Developers sometimes confuse the purpose of CSS classes and IDs. CSS IDs represent unique content that occur just once per page. That's a requirement - the HTML is actually invalid if an ID is used more than once per page. CSS classes, however, often identify repetitive page content. Use CSS classes when you want to describe the behavior, mode or state of the given element. Use CSS IDs when you want to hook into a unique specific element. For example:

	<ul id="client-list">
		<li class="contact"> ... </li>
		<li class="contact"> ... </li>
		<li class="contact"> ... </li>
	</ul>

	...

	<ul id="marketing-list">
		<li class="contact"> ... </li>
		<li class="contact"> ... </li>
		<li class="contact"> ... </li>
	</ul>

	...

	<ul id="business-lead-list">
		<li class="contact"> ... </li>
		<li class="contact"> ... </li>
		<li class="contact"> ... </li>
	</ul>

2. Whenever possible, in Sitecore layouts and sub-layouts, use generic CSS IDs and class names such as product-list and product for your styling and Javascript hooks. Avoid the temptation to hard-code technology-specific or implementation-specific class names into your template markup. Especially when using .LESS, instead of writing Bootstrap-specific row and column markup this way:

	<ul class="row visible-md">
		<li class="col-md-4 hidden-md"> ... </li>
		<li class="col-md-4 hidden-sm"> ... </li>
		<li class="col-md-4 hidden-sm"> ... </li>
	</ul>

Try to keep it technology-agnostic, like this:

	<ul id="product-list">
		<li class="product"> ... </li>
		<li class="product"> ... </li>
		<li class="product"> ... </li>
	</ul>

Behind the scenes, you can then implement Bootstrap specifics in your .LESS files using Mixins. Please note, it may not be possible to remove all Bootstrap references from your markup (such as Bootstrap's panel class), but most of the major features have corresponding Mixins you can use instead of hard-coding classes into template markup. This process keeps your separation of concerns intuitive, simple and clear.


3. Create a main .LESS file that imports all needed .LESS dependencies. Ensure that your import statements follow a general to specific order (just like any set of CSS rules). Doing so makes it easy to support any desired folder hierarchy with the added bonus that you only need to compile, minimize and deploy CSS from a single (or a small number of) .LESS source files.

For instance, suppose you have a file named main-responsive.less that looks something like this:

	//----- Responsive site styles
	@import "/Layouts/MainLayout.less";
	@import "/Pages/About/AboutUsSubLayout.less";
	@import "/Pages/About/ContactSubLayout.less";
	@import "/Pages/Legal/PrivacySubLayout.less";
	@import "/Pages/Legal/LegalNoticeSubLayout.less";
	@import "/Pages/Products/ProductLandingSubLayout.less";
	@import "/Pages/Products/ProductDetailsSubLayout.less";

The advantage of this approach is that you can easily use of the cascading nature of CSS to derive a non-responsive version of the site, maybe in a file named main-desktop.less:

	//----- All styles from responsive site
	@import "main-responsive.less";

	//----- Code to disable Bootstrap responsive behavior
	@screen-xs : 1px;
	@screen-sm : 2px;
	@screen-md : 3px;
	@screen-lg : 9999px;

Then, for a printable version of the site, maybe another file named main-print.less:

	//----- All styles from desktop version of site
	import "main-desktop.less";

	//----- Print-friendly styles
	header , footer , #left-nav , .banner
	{
		 display : none;
	}

4. Choose a .LESS folder structure that parallels your Sitecore layout and sub-layout folder structure. Name your .LESS files the same as your layout and sub-layout .ASCX files. Doing so makes it easy to see, at a glance, which .LESS file corresponds to any given layout or sub-layout. For example:

	WebControls
	|
	|__ Layouts
	|  |__ MainLayout.ascx
	|
	|__ Pages
	   |
	   |__ About
	   |  |__ AboutUsSubLayout.ascx
	   |  |__ ContactSubLayout.ascx
	   |
	   |__ Legal
	   |  |__ PrivacySubLayout.ascx
	   |  |__ LegalNoticeSubLayout.ascx
	   |
	   |__ Products
	      |__ ProductLandingSubLayout.ascx
	      |__ ProductLandingSubLayout.ascx
	      |__ ProductDetailsSubLayout.ascx

	Styles
	|
	|__ Layouts
	|  |__ MainLayout.less
	|
	|__ Pages
	   |
	   |__ About
	   |  |__ AboutUsSubLayout.less
	   |  |__ ContactSubLayout.less
	   |
	   |__ Legal
	   |  |__ PrivacySubLayout.less
	   |  |__ LegalNoticeSubLayout.less
	   |
	   |__ Products
	      |__ ProductLandingSubLayout.less
	      |__ ProductLandingSubLayout.less
	      |__ ProductDetailsSubLayout.less

5. The value of meaningful namespaces in front-end code (whether CSS or Javascript) is just as valid and beneficial as it is in server-side code. This concept is sometimes under-valued or unrecognized by developers working primarily in C# or Java. Imagine creating a static website that has a large number of simple, hard-coded HTML pages. A namespace best-practice on such a website yields a unique id for each page, such as:

	<body id="home-page">
	<body id="product-page">
	<body id="contact-us-page">

No matter how large the site is, this ensures that CSS and Javascript hooks are free of naming conflicts. The dynamic nature of Sitecore, or any CMS, however, does not afford that luxury on a page-by-page basis. We do have something reasonably close though: Sitecore's layouts and sub-layouts. That's where we can attach our unique ids - and it comes pretty close to duplicating the namespacing seen in static HTML sites. So in layouts and sub-layouts, consider using a unique ID in the first line of markup whenever possible. For example, here's the start of the markup in a file named ProductsSublayout.ascx:

	<div id="products-sublayout">
		...
		...
		...
	</div>

Notice that the code in the corresponding .LESS file named ProductsSublayout.less (by convention, see #4 above) also begins with a matching ID:

	#products-sublayout
	{
		...
		...
		...
	}

Following this pattern makes it easy to override or customize anything at the sub-layout level. An exception to this pattern happens when a sub-layout is running inside a container that uses a higher priority unique ID. Perfect examples are site-wide components such as header, footer, breadcrumb and navigation components. In such cases, you might choose to use the unique ID of the higher-level container instead. Another exception would be a sub-layout that's shared across a large number of pages or components - in this case, name your ids to match the sub-layout name.


6. Use .LESS Mixins whenever possible to keep your code DRY (don't repeat yourself). Full reference at the http://lesscss.org official site.

One Caution: depending on your implementation (Bootstrap, for instance) and project requirements, calling Mixins can increase the amount of generated CSS after compilation. In many cases, you may be able to use the .extend() feature to reduce Mixin overhead. Please see the following http://lesscss.org/features/#extend-feature-reducing-css-size for details.


7. If you're using the .LESS version of Bootstrap and the content on your site is nearly complete, you may be able to reduce the size of compiled CSS by disabling Bootstrap features that aren't needed (depending on content). This requires that you're using tip #3 above.

In your main .LESS file (the one with all the import statements), you can safely comment out lines if they're not used. For example, commenting out the following reduces the generated CSS by 15kb... not a huge difference, but when you're trying to reduce bytes, this can help:

	//----- Bootstrap Components
	@import "website/bootstrap/component-animations.less";
	@import "website/bootstrap/dropdowns.less";
	@import "website/bootstrap/button-groups.less";
	// @import "website/bootstrap/input-groups.less";
	// @import "website/bootstrap/navs.less";
	// @import "website/bootstrap/navbar.less";
	// @import "website/bootstrap/breadcrumbs.less";
	// @import "website/bootstrap/pagination.less";
	// @import "website/bootstrap/pager.less";
	// @import "website/bootstrap/labels.less";
	// @import "website/bootstrap/badges.less";
	// @import "website/bootstrap/jumbotron.less";
	// @import "website/bootstrap/thumbnails.less";
	// @import "website/bootstrap/alerts.less";
	// @import "website/bootstrap/progress-bars.less";
	@import "website/bootstrap/media.less";
	@import "website/bootstrap/list-group.less";
	@import "website/bootstrap/panels.less";
	// @import "website/bootstrap/responsive-embed.less";
	// @import "website/bootstrap/wells.less";
	@import "website/bootstrap/close.less";

The downside would be if someone wanted to add a progress bar or jumbotron in the future. If so, those lines would need to be uncommented and the CSS would need to be recompiled and deployed to support the new content.

One Caution: especially when developing a responsive site, please test changes like this against a wide variety of browsers and devices. For cross-browser and cross-device testing, consider a service such as browserstack.com and consult Bootstrap for the final word about component dependencies at getbootstrap.com/components.


8. When using server-side responsive controls to eliminate content for smaller devices, you may be tempted to surround many pieces of content in a very granular fashion with such controls. In general, from the desktop down, consider using these controls on larger blocks of content or sections that you're certain are not required on smaller devices.

Ultimately it's a judgment based on content and requirements, but small quantities of text (such as navigation, breadcrumbs, intro text, etc.) or small icons are better managed with CSS media queries; this type of content elimination is probably over-kill for server-side responsive controls.

There's one other important part to keep in mind. The more granular you get with these kind of controls on the server, the more potential for unexpected conflicts on front-end requirements - especially expectations that Javascript may have on DOM structure.

In other words, if a server-side control eliminates (doesn't deliver) content for a particular mobile device, there could be Javascript written by another team member that wasn't even aware of the potential for content elimination. As always, clear communication between team members and roles are critical here.


9. For form management in Bootstrap (such as a contact us page), it's best to use simple, clean form markup without worrying about form structure, layout, alignment, etc. As long as your markup is standard and semantic, Bootstrap handles everything responsively.

Also, there's no need for ASP.NET client-side form validation (asp:requiredfield, asp:regular expression controls, etc.) because bootstrap handles all that as well. You'll probably add C# validation into the code-behind during form submit (a server-side best-practice), but client-side, Bootstrap is already form-friendly out of the box.

Sitecore's Web Forms For Marketers (WFFM), unfortunately, completely kills this idea for Bootstrap and other front-end frameworks. WFFM uses a somewhat outdated approach to form validation and client-side behavior. Essentially, WFFM makes it difficult to produce a clean, well-behaved business form that many of us have come to expect in modern web applications. If you're in a situation where you must use WFFM, there's not much recourse other than to let WFFM handle the form generation, content delivery and validation as best as it can and better leverage Bootstrap on other presentation elements.



Bill Drol
Bill Drol is an award winning web developer and author with core skills in server-side programming, client-side programming, database programming and a little of everything in-between.