An in-depth walkthrough of our framework architecture

Although more technical in nature, we hope that it can also provide insights into the concept of our framework. How it is built, used and maintained.


UI-framework and Component templates

Our component framework has, like most UI-frameworks, UI-components that consist of HTML, CSS and possibly Javascript. Possibly also with dependencies to other resources such as icons and/or fonts. (we will not go into detail regarding the architecture of our UI framework in this post but it might be worth mentioning that it is based on the ITCSS-architecture and uses a BEM naming convention).

To be able to change the markup and styling of these components without having to update application source code as often, we’ve added an abstraction layer in the form of component templates on top of that. This layer and these templates, how they are used, stored and maintained are the focus of this blog post.

Component Templates

The component templates are built up of 2 files, an XSL-file and an XSD-file.

The XSD-file (XML Schema file) serves as a formal definition of the XML used to represent each component instance and also provides a convenient way for documenting the component and list a number of examples of use. The XSD-files are used to generate an online style guide that by definition is coherent with the actual implementation.

The XSL-file (Extensible Stylesheet Language) is used to transform XML templates into HTML. This transform should take place server-side but it would theoretically be possible to let web clients perform it client-side.

The idea is that if we can remove details about appearance (CSS classes) and structure (HTML markup) and only keep what’s necessary to represent the components content model, content and configuration-settings the components will be more stable over time. This also enables us to manage refactoring of a component’s HTML/CSS in a single place instead of having to update and redeploy applications.

Here is a simple example that show how an XSD and XSL file can look together with the template XML and the resulting HTML after a transformation has taken place.

XSD

The example below illustrates the schema definitions for the section component. Annotations are used to add a brief description and the user defined appinfo is used for additional metadata and example snippets for use in automatic generation of documentation i.e. a style guide. This is followed by the formal content model for the component, including possible attributes.

# XSD example (shortened)
<xsd:complexType name="card">
<xsd:annotation>
<xsd:documentation>
Use to present an excerpt of information together with one or more possible actions. The main purpose of a card is to serve as an entry point to more detailed information, a card may for example contain an excerpt of that leads to a page.
</xsd:documentation>
<xsd:appinfo>
<type>Molecule</type>
<category>Card</category>
<!-- Dependencies -->
<resource type="CSS" name="components.card.css"
path="components.card.css"/>
<resource type="CSS" name="components.icon.css" path="components.icon.css"/>
<!-- Accepted child elements to card -->
<children>
<child name="url" type="URL" mandatory="true">
<description>Address when card title is linked</description>
</child>
   <child name="header" type="Text" mandatory="true">
<description>Card title</description>
</child>
   ...more child-nodes...
</children>
<!-- Usage examples -->
<gbg:wrapper name="With an image and copy">
<gbg:card>
<gbg:element name="url">#</gbg:element>
<gbg:element name="header">Cattle Fruit She'd</gbg:element>
<gbg:element name="image">/category/nature/600x400</gbg:element>
<gbg:element name="content">
Above. Waters She'd fruit above you'll of saw saw us
</gbg:element>
</gbg:card>
</gbg:wrapper>
...more usage examples here...
</xsd:appinfo>
</xsd:annotation>
<!-- values and restriction for accepted attributes -->
<xsd:attribute name="lazy" type="xsd:boolean" default="true"> 
<xsd:annotation>
<xsd:documentation>
Flag to de-activate lazy loading (default is true)
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="color">   
<xsd:annotation>
<xsd:documentation>
Color scheme
</xsd:documentation>
</xsd:annotation>
  <xsd:simpleType>    
<xsd:restriction base="xsd:string">
<xsd:enumeration value="pink"/>
<xsd:enumeration value="green"/>
<xsd:enumeration value="blue"/>
...
<xsd:enumeration value="orange"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
...more attributes...
</xsd:complexType>
</xsd:schema>

For the full example code, please see card.xsd

HTML

This is an example of the XML and the resulting rendered HTML after being transformed using the XSL file below.

# XML
<gbg:card>
<gbg:element name="url">#</gbg:element>
<gbg:element name="header">Cattle Fruit She'd</gbg:element>
<gbg:element name="image">/category/nature/600x400</gbg:element>
<gbg:element name="content">
Above. Waters She'd fruit above you'll of saw saw us
</gbg:element>
</gbg:card>
# Resulting HTML
<article data-name="card" class="c-card">
<figure class="c-card__image">
<img class="c-image" src="/category/nature/600x400">
</figure>
<div class="c-card__header">
<h2 class="c-card__title">
<a class="c-card__title-link" href="#">Cattle Fruit She'd</a>
</h2>
</div>
  <div class="c-card__content">
Above. Waters She'd fruit above you'll of saw saw us.
</div>
</article>

XSL

The XSLT template below shows the top level matching rule and the course structure of the HTML markup of the component. A card component consists of an article element with a number of parameterised attributes and child elements for image, header and content, followed by a div tag with card actions, each tag rendered using their own specific rules.

# XSL example (shortened)
<xsl:template match="gbg:card">
<article data-name="card">
<xsl:attribute name="class">
<xsl:text>c-card</xsl:text>
<xsl:apply-templates select="@color">
<xsl:with-param name="prefix">
c-card
</xsl:with-param>
</xsl:apply-templates>
</xsl:attribute>
<xsl:apply-templates select="@truncate"/>
<xsl:apply-templates select="@flag"/>
<xsl:apply-templates select="@space"/>
<xsl:apply-templates select="gbg:element[@name='image']"/>
<xsl:apply-templates select="gbg:element[@name='header']"/>
<xsl:apply-templates
select="gbg:element[@name='content' and string-length() > 0]"/>
<xsl:if test="gbg:action">
<div class="c-card__actions">
<xsl:apply-templates select="gbg:action"/>
</div>
</xsl:if>
</article>
</xsl:template>
<xsl:template match="@flag[parent::gbg:card]">
<div class="c-card__flags">
<span class="c-card__flag c-card__flag--pink">
<xsl:value-of select="."/>
</span>
</div>
</xsl:template>
...more template rules...
</xsl:stylesheet>

For the full example code, please see card.xsl.

Structure of XSD and XSL files

The template framework consists of two top-level files, components.xsd and components.xsl, each linking to the corresponding file for every component.

# Listing of components.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:gbg="http://teik.goteborg.se/components"
xmlns="http://www.w3.org/1999/xhtml">

<xsl:output
indent="yes"
method="html"
omit-xml-declaration="yes"
encoding="utf-8"/>
<xsl:strip-space elements="a"/>
<xsl:include href="core/core.xsl"/>
<xsl:include href="pill/pill.xsl"/>
<xsl:include href="cell/cell.xsl"/>
<xsl:include href="list/list.xsl"/>
<xsl:include href="utility-bar/utility-bar.xsl"/>
<xsl:include href="breadcrumb/breadcrumb.xsl"/>
<xsl:include href="pagination/pagination.xsl"/>
<xsl:include href="accordion/accordion.xsl"/>
<xsl:include href="article/article.xsl"/>
<xsl:include href="message/message.xsl"/>
<xsl:include href="snippet/snippet.xsl"/>
<xsl:include href="mosaic/mosaic.xsl"/>
<xsl:include href="image/image.xsl"/>
<xsl:include href="icon/icon.xsl"/>
<xsl:include href="tabs/tabs.xsl"/>
<xsl:include href="toc/toc.xsl"/>
<xsl:include href="region/region.xsl"/>
<xsl:include href="section/section.xsl"/>
<xsl:include href="data-block/data-block.xsl"/>
<xsl:include href="spotlight/spotlight.xsl"/>
<xsl:include href="slideshow/slideshow.xsl"/>
<xsl:include href="navigation-card/navigation-card.xsl"/>
<xsl:include href="card/card.xsl"/>
<xsl:include href="page-header/page-header.xsl"/>
</xsl:stylesheet>

Note that some of the included files do not contain any component definitions but provide general matching rules that apply to several components.

Transformation service

The transformation service provides a REST API that serves two purposes:

  1. Publish XSD and XSL files from the template framework in a directory like structure
  2. Perform server-side transformation of xml markup and return html.

Both functions are described in detail below.

Publish file resources

The publishing feature of the REST API provides a way to access the source files. It also makes it possible to deploy several versions of the template framework in the same environment. The following example urls point to the latest stable version of the template framework in the production environment:

The top-level schema definitions:
http://goteborg.se/wps/components-stable/library/components.xsd

The schema of the card component:
http://goteborg.se/wps/components-stable/library/card/card.xsd

The examples contained in the schema definition of the card component:
http://goteborg.se/wps/components-stable/library/card/card.xml (without styling)

The top-level xslt definitions (can be used to perform client-side transforms)
http://goteborg.se/wps/components-stable/library/components.xsl

Transform posted markup

The REST API also provides a function to post xml and have it transformed to HTML using server-side mechanisms. The latter is used for the try-it-yourself function of the style guide.

http://goteborg.se/wps/components-stable/transform/

Transformation filter

The purpose of the transformation filter is to separate the template rules and the actual transforming process from the editorial content and application. It relies on environment specific mechanisms such as portlet or servlet filters while the rest of the framework is built on well established and platform independent standards.

The general idea is that every response from the server, whether it be static web pages or dynamic content, is passed to the filter which, if certain criterias are met, will search the content for an enclosing wrapper tag. If found, only the markup inside it is used as input to the transformation. This part of the markup will have to be well-formed XML or the transform will fail. The markup outside of the wrapper can have much looser restrictions which is important for things like appended javascript code.

A schematic view of how the Transformation filter works

The transformation filter makes use of the transformation service to load the correct version of the template framework, compile it and store it in a template cache for reuse in subsequent responses. This cache has to be refreshed after a new version of the transformation service has been deployed in order for the changes to take effect.

Pros and cons

Standards based

Most of the template framework relies on open standards such as XML Schema and Extensible Stylesheet Language making it language and platform independent. Apart from our production environment, based on the Java Portlet specification we have successfully implemented our framework using PHP on Apache.

Centralized administration

An update of the template framework will affect all pages using it without any need for recompiling or additional deploys.

Performance

Using cached and precompiled xslt templates reduces the latency of the transformation filter so that there is no significant loss of performance. Even fairly complex content is transformed within 10 ms typically.

Restrictions to well-formed XML

It has been noted that the transform will fail when illegal characters, such as ampersand (&) are used. These problems can usually be handled using CDATA sections but we frequently encounter transformation errors on pages when this is forgotten.

To summarize

The template framework acts as an additional layer on top of the components in the UI-framework.

The idea is that the template abstraction layer removes details of appearance and mark-up from the components which makes them more stable. This in turn means we don’t have to update the source code of our applications as often since some of the refactoring can be done at template level, in one place.

Check out our public component library to see live examples of our components: http://goteborg.se/wps/portal/component-framework/


This was part 6 in the publication: Our Story: Building a component library