Style-guide the smaller things
I've been presenting SC5 Style Guide several times during this year sharing my experience of using it for one of the SC5's clients, Elisa. Assuming that Elisa is a huge mobile operator with a lot of websites and a need to keep those websites visually consistent, there is no wonder that the SC5 Style Guide as a tool provides benefits there. But what about smaller websites? Would it be useful there? I did not know the answer to this question and wanted to experiment. The experiment was conducted on my blog codebase.
This is how the Living Style Guide looks like: varya.me/styleguide. You can see the interface decomposed into block pieces, each of those representing a meaningful interface unit. I haven't seen the interface of my blog from this perspective before. It gave me a lot of insight into the ways the CSS architecture of my blog should be designed. But, let's consider things one by one.
Fine-tuning the SC5 Style Guide tool
Everything starts with
npm install --save-dev sc5-styleguide
Then, I was able to use the tool to build the interface representation. It required a couple of Gulp tasks.
I had to deviate from the example configuration in a couple of places to make my Style Guide work as it does now. Let me name and explain these things.
Providing the appRoot
parameter.
As I am hosting the Style Guide website not it the root folder but in a subfolder named styleguide
, I inform the
generator about that, so it can build the properly working app:
gulp.task("styleguide:generate", function() {
return gulp.src(["desktop.blocks/**/*.css"])
.pipe(styleguide.generate({
title: "Varya.me Styleguide",
appRoot: '/styleguide',
...
Providing scripts
Under the extraHead
parameter I list all the additions needed. Some of my components
need JavaScript to function. It is indeed possible to ignore that and present
their views only, but I wanted them to be exactly as they were used in the blog.
Thanks to extraHead
I could supply any code needed. I used it to
link the same scripts that I have on my blog pages:
gulp.task("styleguide:generate", function() {
return gulp.src(["desktop.blocks/**/*.css"])
.pipe(styleguide.generate({
title: "Varya.me Styleguide",
...
extraHead: [
'<script src="http://yandex.st/jquery/1.7.2/jquery.min.js"></script>',
'<script src="/desktop.bundles/index/index.min.js"></script>',
...
]
...
However, I needed yet another trick to make the scripts work properly. My components are written with i-bem.js
framework which automatically initialises them on the domReady
event. This is what I wanted for the static pages because the
components markup is in HTML document. But for the Style Guide, which is architecturally a SPA, this did not work. The components
are rendered on the fly, and this obviously happens after domReady
; that is why by default they stay
uninitialized. So they should be initialized after they had been rendered. Luckily, the Style Guide
triggers an event on the window
object named styleguide:onRendered
every time a new component is re-rendered.
By handling this event, I managed to get the components initialized right after they appear on the page. This code is needed
for the Style Guide only, so I do not include it into the project bundle but provide as an additional script in the
Style Guide configuration:
gulp.task("styleguide:generate", function() {
return gulp.src(["desktop.blocks/**/*.css"])
.pipe(styleguide.generate({
title: "Varya.me Styleguide",
...
extraHead: [
...
'<script src="/styleguide/i-bem__init_styleguide.js"></script>'
]
...
You can check how the JavaScript components are working with the block which is initially empty and renders the list of repositories after querying the GitHub API: varya.me/styleguide/#/section/4.3. Or try out the Candies randomly built with JavaScript varya.me/styleguide/#/section/5.1.
The last important thing to make JavaScript work is to use disableEncapsulation: true
parameter. Normally the
Style Guide wraps each component in the ShadowDOM because it lets us developers write component CSS without
worrying about affecting the Style Guide interface. But it also makes the components scoped from the JavaScript linked
to the Style Guide website — including that additional JavaScript I showed above. By disabling the encapsulation, I made
the components to be inserted into the HTML page as they are, without any scoping. As for affecting the Style Guide
interface, in my case it will never happen as I use BEM and all my components are already "encapsulated" thanks to that methodology.
Hosting a static page
The SC5 Style Guide provides a development server which routes all the paths to its root. When hosting it outside, you
should care about the routing yourself as you would do for any other SPA. But my blog is hosted on GitHub Pages
which is a simple static hosting and does not provide any routing capabilities. For managing this case, I use the
disableHtml5Mode: true
option. It makes the Style Guide application generate the old-school links with #
. And so it
works.
Documenting the components
As I used BEM for my website frontend from very beginning, the CSS was already written the component-way. I only needed to create a descriptive structure and document the blocks with KSS.
Structuring the code
Unfortunately the BEM way of storing components in folders turned out to be not good enough to see the code from the living-style-guide perspective. On a file system, all the components are represented at the same level which turns them into a long list without much structure:
desktop.blocks/
├── article
├── articles-list
├── box
├── ...
└── text
github/varya/varya.github.com/desktop-blocks
In this case the atomic components are not distinct from the structural interface pieces (such as Header or Footer), from the side blocks, or from CSS provided to style third-party things. Flat structure is indeed good for bundling tools but we humans need more logical nesting.
For that I provide the overview.css
file, which has nothing to do with the final product and only helps me arrange the
blocks. I have 5 sections, in which I place the blocks depending on what they are:
/*
Atoms
Styleguide 1
styleguide:ignore:start
@import url("logo/logo.css"); // 1.1
@import url("text/text.css"); // 1.2
...
styleguide:ignore:end
*/
/*
Main blocks
Styleguide 2
styleguide:ignore:start
@import url("header/header.css"); // 2.1
...
styleguide:ignore:end
*/
github/varya/varya.github.com/desktop-blocks/overview.css
There are only comments in this file in which I provide the documentation of every subset as a Style Guide section.
Listing the paths of the dependant CSS files makes it easier to navigate through the code. The @import
syntax is used just
because :-)
The only trick here is the magic styleguide:ignore:start
and styleguide:ignore:end
keywords. It is possible to
place them inside comments in any piece of your CSS (or SASS or LESS) and make the Style Guide generator ignore
what is in between.
In my case, I tell it skip my overview lists because they have nothing to do with the codebase. But the names of the sections and their possible descriptions (I just haven't provided any) are in work.
Describing the pieces
Everything else went smoothly. For every component I provided the KSS description. The logo
block can be the
simplest example:
/*
Logo
markup:
<logo class="logo">···<b class="var">var</b>·<b class="ya">ya</b>;<b class="cursor"></b></logo>
Styleguide 1.1
*/
.logo
{
...
github/varya/varya.github.com/desktop-blocks/logo/logo.css
See it rendered: varya.me/styleguide/#/section/1.1
In some other components I used extra features. For example, different social icons are the same ico
element with different
modifiers. In the Style Guide I could documents them all together:
/*
Social icon
.social-ico__ico_type_rss - RSS
.social-ico__ico_type_twitter - Twitter
.social-ico__ico_type_github - Github
.social-ico__ico_type_facebook - Facebook
.social-ico__ico_type_linkedin - LinkedIn
markup:
<a class="link social-ico__ico {$modifiers}" href="#" title="Icon title"></a>
Styleguide 1.5.1
*/
.social-ico__ico
{
...
github/varya/varya.github.com/desktop-blocks/social-ico/social-ico.css
In the website the component is rendered separately with every modifier possible: varya.me/styleguide/#/section/1.5.1
For the complex components which contain other ones I used <sg-insert>
keyword. It takes another component's markup
by its reference number.
/*
Sidebar
markup:
<nav class="sidebar">
<sg-insert>4.2</sg-insert>
<sg-insert>4.3</sg-insert>
</nav>
Styleguide 4.1
*/
.sidebar
{
...
github/varya/varya.github.com/desktop-blocks/sidebar/sidebar.css
This makes the markup examples pretty short, however it expands for rendering the component and showing the markup on the website: varya.me/styleguide/#/section/4.1
Style-Guide-Driven Development
If you type "logo" in the Style Guide search field, it will find and render all the components where logo
is found!
The search goes through all the CSS codebase. Similarly you can search for the components using <em>
in their markup.
Or font:
in their CSS.
I personally like that the search source is not only the CSS of components but also their markup examples. During the refactorings this, for example, makes possible to select all the components containing inputs and look how the changes affect them.
However this is just a small addition to one major benefit I see in using the Style Guide. I find it far more important that it reveals my mistakes.
I already used the component way when building my blog. And I was pretty sure that my BEM experience is a 100% guarantee that I get this right. But even developing with a highly modular approach in mind, I did this from the page perspective. Before the components were integrated into the blog engine, I had developed a static page and this was where the components fitted first.
I considered them independent and so tried to write my code. But sitting at the same page they never actually were independent.
After the SC5 Style Guide magically represented each of them separately, I can see that the logo is aligned to the right. And why should it be? Obviously, my mistake when I tried to made it fit into the Header.
The same goes for the language switcher positioned on the right.
In the set of social icons, the language switcher provided with the RSS icon was considered to stay with it. But this only happens because at the blog pages it is placed into a narrow container. The Style Guide represents it on its own and I can see that it is not that flawless as I thought.
Such discoveries obviously lead to the refactoring :-)
To top it off, I must say that the experiment does not end and there are already findings for further posts. Stay tuned!