Building UI Components with xstyle

By on September 17, 2013 8:36 am

xstyle_transparentIn this post, we will walk through creating widgets, or UI components in xstyle. CSS itself has a basic component-like unit, a CSS rule. However, xstyle enables a level of advanced UI for constructing rich components, beyond what can be specified with basic CSS. The simple extensions in xstyle allow us to generate DOM elements, respond to events, and encapsulate functionality for easy reuse and composition.

Traditionally, widgets in web applications have been created through JavaScript constructors. Typically JavaScript instances are created as an abstraction from the underlying DOM elements. This pattern is used in many frameworks, and certainly has been a useful concept for web application development. However, this approach suffers from several fundamental limitations. First, toolkits generally advise against the use of JavaScript constructors for minor elements, like headers and anchors. In dgrid, we usually advise against using widgets in every row, suggesting plain inputs instead. This means the JavaScript interface applies for some components, but other elements necessitate direct API interaction. The JavaScript interface is discontinuous because it simply won’t scale to the number of elements on a typical page.

This issue is exacerbated in mobile application where performance and memory is more constrained, but the modern capabilities mean more native elements can be used without full-blown widgets. Mobile web applications often prefer cleaner, simpler elements that can be described with native elements and CSS. Attempting to abstract all of these through a JavaScript interface often means either a gratuitously heavy interface or sharp discontinuities in the UI interface.

Even in full JavaScript enhanced widgets, significant portions of the UI are most efficiently and appropriately described through CSS. Some frameworks try to keep a pure JavaScript interface for all styling, leading to heavy reliance on inline styles instead of best practice CSS usage. Other toolkits do encourage CSS usage, but again, there means that presentation definitions are split between the JavaScript and CSS interface. Again, looking at the trends in browser development, CSS capabilities are growing at an amazing pace, and we can and should increasingly lean on these capabilities.

Creating a Component

In this tutorial, we will create a common UI element, a simple popup menu. For background information, read our post on getting started with xstyle, or the documentation.

Again, the basic unit of a UI component in CSS is simply a rule. Before looking at how we can improve upon this, it’s worth noting the advantage CSS intrinsically provides in selecting elements for applications; we don’t need a separate step to query the DOM for nodes and iterate through these to apply a widget constructor. The selector handles the application of presentation definition for us.

Now of course, a simple CSS rule is very limited. Traditional CSS allows us to use some predefined properties to style an element, but it lacks composition capabilities. One of the first important concepts that xstyle provides is the ability to create new named definitions that we can reference and reuse. Here we create a definition beginning with a regular CSS rule, but by applying the = operator, our new definition can be referenced like a new element or used as a property in another CSS rule. Here we define our new component, calling it a “popup-message”:

	popup-message = {
		...
	}

The next key construct that xstyle introduces for defining components is the ability to compose a component from child elements. This is achieved through xstyle’s element generation syntax. To begin, we create the sub-elements using standard CSS selector syntax. Additionally we can define text nodes with string literals. We will define a button element and div element (for the popup) as the children of our component. Element generation can use whitespace to denote hierarchy, here creating the elements as siblings that each have a text node child:

	popup-message = {
		=>
			button 'More information'
			div 'Here is more information';
	}

It is worth noting that element generation is also a very convenient way to use our new component definitions. For example, we could add a popup-message to our page’s footer element:

#footer {
	=>
		popup-message;
}

Next, xstyle allows us to style these elements directly inline as a nested rule. This is a natural, intuitive way to add another rule, within the stylesheet without having to create a new selector. Here we define our popup layer, setting as an absolutely positioned element and initially hiding it:

	popup-message = {
		=>
			button 'More information',
			div 'Here is more information' {
				position: absolute;
				display: none;
			};
	}

Building reusable components is more than just encapsulation. Here we have hard-coded in our text values, but to make this usable, we want to parameterize these so that this new component can be used with different values and settings. We can do this by creating new property variables. We will use a “label” property variable and a “content” property variable for defining the text of the button and the popup. We then use these variables within our element generation and xstyle will bind the values to the parent elements. We will also define a popup-background-color property to allow for easy adjustment of the popup’s background color, and use the standard CSS variable syntax to reference it. The “content” variable is implicitly defined, but we will explicitly create label and popup-background-color properties with default values:

	popup-message = {
		label=var: More information;
		content: Here is more information;
		popup-background-color=var: #aaa;
		=>
			button (label),
			div (content) {
				position: absolute;
				display: none;
				background-color: var(popup-background-color);
			};

These property variables can be used just like standard CSS properties, when we use (or extend) the popup-message component, and standard CSS properties can be used along with them. So now we can go back to our usage of popup-message, and provide specific values for these properties (note that we aren’t specifying a label here, we will just use the default):

#footer {
	=>
		popup-message {
			/* use our new properties we defined */
			popup-background-color: blue;
			content: Contents of another popup;
			color: #123; /* we can still use standard CSS properties as well*/
		};
}

The “content” property that was used has a special meaning, denoting the main contents of the component. In the example above, we used the “content” property as a CSS property. However, with our component the content can come from the target node (the inner nodes of the target node), or if can be defined in our element generation. For example we could create a popup:

=>
	popup-message
		h1 'Header inside popup'
		p 'A paragraph inside a popup';

The next step in creating this component is actually showing the popup in response to the button being clicked. At this point, we should point out that the goal of xstyle is not to eliminate the use of JavaScript, but rather provide declarative, reactive definitions that scale across many elements, for aspects of a component that can be best described with these tools, complementary to JavaScript. However, when it comes to the imperatively updating the state of a component, this is clearly JavaScript’s domain and xstyle is designed to interface with JavaScript to handle this type of activity. We will create a JavaScript module to work with this definition. We can think of this as the “model” for our CSS “view”. This model may in fact be functioning as a “view” for other models; often it can be beneficial to have several view-model layers, as exemplified in an MVVM approach. To use this model module, we will introduce it into our component view definition we have created:

	popup-message = {
		popup-model = module('my-package/popup-model');
		...

And now of course we need to create the referenced module. In this model, we will create a toggle function, that will be triggered by the button, to show the popup message.

define([], function{
	return {
		toggle: function(event){
			var popupDiv = event.parentNode.lastChild;
			popupDiv.style.display = (popupDiv.showing = !popupDiv.showing) ? 'block' : 'none';
		}
	};
});

Next, we connect the toggle function as an event listener to the button. We could do this in the JavaScript module, but xstyle allows us to conveniently declare event listeners as well, again keeping the definition intuitively next to the element declaration. xstyle uses event delegation to listen for events, which consistently aligns with the CSS/xstyle model, with the event selector implicitly derived from the rule in which it is declared. Here we add a ‘click’ listener:

		popup-model = module('my-package/popup-model');
		=>
			button (label) {
				on-click: popup-model/toggle;
			},
			div (content) ...

We now have a functioning popup message when clicking the button. Of course there are countless ways that we could improve upon this component, with better styling and more features, but this gives you an introduction on how to construct reusable components with xstyle’s extensible CSS.

Next steps

  • Feedback. We’re looking for your feedback on xstyle. If xstyle interests you, please try it out and tell us what you think.
  • Getting involved. We’re also looking for help in adding features and refining xstyle. Let us know if you would like to get involved, either in the comments or via GitHub.
  • Support and development. Need professional help for your next project or your team? Tell us about your project and let’s discuss how we can help you get the most from the web platform.

Other posts in the series