CSS Architecture Writing maintainable CSS Alexei Skachykhin – Software Engineer iTechArt, 2015
“Writing CSS is easy. Scaling CSS and keeping it maintainable is not.” Ben Frain
Guess what makes it hard to maintain
“I don’t understand the cultural fascination with failure being the source of great lessons to be learned. You might know what won’t work, but you still don’t know what will work.” Basecamp Excerpt from “Rework”
What’s missing? CSS that works to spec isn’t enough, it should be considered as a baseline
We need a plan to keep CSS styles organized. We call such plan an architecture
Evolving Design
Growing Complexity 7,046 Rules 10K Selectors 16,8K Declarations 90 Unique Colors
Growing Complexity 3,938 Rules 5,164 Selectors 9,826 Declarations 46 Unique Colors
Repetitive Design
Repetitive Design “We’re not designing pages, we’re designing systems of components.” Stephen Hay
Technical Difficulties  Source order affects application order  Specificity is hard to deal with  CSS operate in global scope  Permissive by its nature
1. Evolving Design 2. Growing Complexity 3. Repetitive Design 4. Technical Difficulties
It is time to follow architecture and cleanup our CSS
Building Blocks UI consists of components, where component is any distinguishable element of the design
Building Blocks Writing CSS is not about making HTML elements look like provided PSD, but translating system of components into Web compatible format
.contact-item { float: left; font-size: 28px; .contact-item .contact-item__info { margin-top: 4px; margin-left: 20px; padding-left: 31px; } .contact-item .contact-item__title { margin: 0; font-family: 'Lato', sans-serif; font-size: 16px; font-weight: bold; } .contact-item .contact-item__text { margin: 1px 0 14px; font-size: 14px; } } .subscribe { .clearfix(); .field-group { .clearfix(); } .sign-up-button { .secondary-button; .variant-action; display: block; border: none; } ul { margin: 5px; li { display: inline-block; } } } COMPONENT STYLED HTML
Design and markup are inseparable. Designers and Front-End Engineers must speak ubiquitous language to succeed.
CSS Architecture Components & Other Styles Dependency Management Physical Structure & Deployment
No constraints?
Goals of Architecture Predictable Reusable Maintainable Scalable
Goals of Architecture Predictable Reusable Maintainable Scalable Minimal Complexity
“Everyone else in the W3C CSS Working Group, the group which defines the future of CSS, fits the profile of a developer much more than that of a designer.” Lea Verou
Who’s in camp?
Existing Solutions  BEM  OOCSS  SMACSS  Atomic CSS  MCSS  ITCSS
Components
#1 DOM Independency CSS component styles must not rely on particular DOM structure
HERE
Example div.social-links { > div a { color: blue; } }  Visual styles are tightly coupled with markup which makes them hard to evolve  Reusability of component is limited  Ambiguity that leads to growth of specificity
Rule Avoid any signs of DOM elements in CSS code including references to HTML tags, ID (#) and (>) child selectors
Rule CSS selectors must convey semantics of the component and it’s integral parts. Class selector conveys a lot, element selector almost none
Example .social-links { .social-links__item { color: blue; } }
#2 Location Independency CSS component styles must not rely on component’s location on a page
HERE HERE
Example .button { color: white; } .sidebar { .button { color: orange; } }  Component is no longer self contained entity and its visual appearance is not predictable at dev time  Leads to growth of specificity
Rule Component styles must not be written in reliance on their location on a page, instead provide location independent variations that can be used anywhere
“Instead of saying - The buttons need to be smaller when they’re in the header, we need to be saying - We have a smaller variant of our buttons, and it’s these that we use in the header.” Harry Roberts
.button { color: white; /* Other declarations */ &.button--variant-action { color: orange; } } Example
#3 Limited Complexity CSS components can be complex and have integral parts, but no more than having 1 (2) layers of descendant styles
HERE
Example .filter { .filter__header { .filter__header__icon { /* Declarations */ } } .filter__list { .filter__list__link { /* Declarations */ } } }  Component is too complex which makes it hard to maintain  Following that style may lead to unnecessary dependencies between integral parts
Rule If CSS styles have no dependency they must not form parent-child relationship
Rule Complex components must be split into simpler components
.filter { .filter__header {} .filter__icon { /* Declarations */ } .filter__list {} .filter__link { /* Declarations */ } } Example
#4 Enforced Naming All aspects of component definition must be named in a way to communicate their relationship clearly and protect from incorrect use
.infobox /* Block */ { .infobox__body /* Element */ {} .infobox__footer {} .infobox__title {} &.infobox--size-full /* Modifier */ {} } Example
#5 Explicit States Dynamic behavior must be implemented with state classes that JavaScript toggles. No direct style manipulation is allowed with JavaScript.
HERE
open: function () { elements.root.show(); elements.trigger.css('color', 'blue'); }, close: function () { elements.root.hide(); elements.trigger.css('color', 'white'); } Example  Visual appearance of component is now split across JavaScript and CSS which makes is hard to maintain  There is no simple undo operation for setting style properties in JavaScript so previous state has to be maintained somewhere or inline styles manipulated directly
open: function () { elements.root.addClass('state-open'); elements.trigger.addClass('state-active'); }, close: function () { elements.root.removeClass('state-open'); elements.trigger.removeClass('state-active'); } Example
Rule Global states must not collide with local states to avoid overrides in CSS
.state-hidden { display: none; } .infobox { display: inline-block; &.state-hidden { display: inline-block; visibility: hidden; } } Example  Component is no longer self contained which causes inclusion of override declarations  In some cases may lead to a need to have !importants
.global-state-hidden { display: none; } .infobox { display: inline-block; &.state-hidden { visibility: hidden; } } Example
#6 JavaScript Hooks Never use component classes to select elements with JavaScript. Introduce specialized classes just for the sake of selecting elements.
HERE
Example var elements = { mailing_list: internal.$e, form: internal.$e.find('.social-widget__query'), submit_button: internal.$e.find('.button'), submit_button_text: internal.$e.find('span'), validation_message: internal.$e.find('.social-widget__validation-message') };  Hard to modify component styles due to dependencies in JavaScript. Makes whole component fragile
Example <div class="social-widget social-widget--inversed js-mailing-list"> <div class="social-widget__title">Stay in the loop</div> <form class="social-widget__query js-mailing-list-form"> <input class="js-mailing-list-email" type="text" name="email" placeholder="Email address"> <label class="button"> <span class="js-mailing-list-submit-button-text">Submit</span> <input class="js-mailing-list-submit-button" type="submit"> </label> </form> <div class="js-mailing-list-validation-message social-widget__validation-message state-hidden"> Email is not in correct format! </div> </div>
#7 Local Media Queries Responsiveness of a page must be based on responsiveness of each component thus Media Query must be local to a component
.blog-entry { .blog-entry-body { /* Declarations */ } } .infobox {} @media @phone { .blog-entry { .blog-entry-body { /* Declarations */ } } .infobox {} } Example  Code duplication makes components hard to maintain  Components are not self-contained which makes the outcome less predictable at dev time
.blog-entry { .blog-entry-body { margin-right: @gutter / 2; margin-bottom: @gutter / 2; margin-left: @gutter / 2; @media @phone { margin-left: @gutter_phone / 2; margin-right: @gutter_phone / 2; } } } Example
.infobox /* Component */ { .infobox__body /* Integral Part */ {} &.infobox--size-full /* Variant */ {} &.state-active /* State */ {} @media @phone /* Responsive */ {} } .js-infobox /* JavaScript Hook */ Anatomy of Component
#8 Layout vs Visual Layout aspects should be moved to dedicated components or integral parts
HERE
.infobox { /* Layout */ float: left; padding-left: @half-col-gutter; padding-right: @half-col-gutter; width: 33.33%; /* Visual */ background-color: green; padding: 10px; /* Collision */ } Example  Limits reusability  Usually require additional html elements just as styling hooks
Grid based layout is an emerging trend in Web and Graphics design
.grid-row { .clearfix(); .grid-row__column { float: left; padding-left: @half-col-gutter; padding-right: @half-col-gutter; width: 33.33%; } } .infobox { background-color: green; padding: 10px; } Example
#9 Container vs Content Container components must define layout and size constraints, content components – visual look (occasionally size constraints too)
Rule Don’t make any assumptions about content on container level
Rule Develop CSS components to be container agnostic
Other Styles
Types of Styles Base (Normalize, Reset) Utilities (clearfix, valign) Typography (Fonts, Headings, …) Constants (Colors, Sizes, …)
Physical Structure & Deployment
Physical Structure Single file for all of the styles Single file with comments One file per type of styles One file per component
Fallacies  Use id selectors for performance reasons  Too many classes is a bad thing, style based on tags  Nest CSS selectors to follow DOM hierarchy  Put all styles into single file all the time because it is easier to maintain  Introduce lots of utility classes to have greater flexibility when styling things
Thank you! Forward your questions to alexei.skachykhin@live.com

CSS Architecture: Writing Maintainable CSS

  • 1.
    CSS Architecture Writing maintainableCSS Alexei Skachykhin – Software Engineer iTechArt, 2015
  • 2.
    “Writing CSS iseasy. Scaling CSS and keeping it maintainable is not.” Ben Frain
  • 4.
    Guess what makes ithard to maintain
  • 8.
    “I don’t understandthe cultural fascination with failure being the source of great lessons to be learned. You might know what won’t work, but you still don’t know what will work.” Basecamp Excerpt from “Rework”
  • 9.
    What’s missing? CSS thatworks to spec isn’t enough, it should be considered as a baseline
  • 10.
    We need aplan to keep CSS styles organized. We call such plan an architecture
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
    Repetitive Design “We’re notdesigning pages, we’re designing systems of components.” Stephen Hay
  • 16.
    Technical Difficulties  Sourceorder affects application order  Specificity is hard to deal with  CSS operate in global scope  Permissive by its nature
  • 17.
    1. Evolving Design 2.Growing Complexity 3. Repetitive Design 4. Technical Difficulties
  • 18.
    It is timeto follow architecture and cleanup our CSS
  • 19.
    Building Blocks UI consistsof components, where component is any distinguishable element of the design
  • 21.
    Building Blocks Writing CSSis not about making HTML elements look like provided PSD, but translating system of components into Web compatible format
  • 22.
    .contact-item { float: left; font-size:28px; .contact-item .contact-item__info { margin-top: 4px; margin-left: 20px; padding-left: 31px; } .contact-item .contact-item__title { margin: 0; font-family: 'Lato', sans-serif; font-size: 16px; font-weight: bold; } .contact-item .contact-item__text { margin: 1px 0 14px; font-size: 14px; } } .subscribe { .clearfix(); .field-group { .clearfix(); } .sign-up-button { .secondary-button; .variant-action; display: block; border: none; } ul { margin: 5px; li { display: inline-block; } } } COMPONENT STYLED HTML
  • 23.
    Design and markupare inseparable. Designers and Front-End Engineers must speak ubiquitous language to succeed.
  • 24.
    CSS Architecture Components & OtherStyles Dependency Management Physical Structure & Deployment
  • 25.
  • 26.
    Goals of Architecture PredictableReusable Maintainable Scalable
  • 27.
    Goals of Architecture PredictableReusable Maintainable Scalable Minimal Complexity
  • 28.
    “Everyone else inthe W3C CSS Working Group, the group which defines the future of CSS, fits the profile of a developer much more than that of a designer.” Lea Verou
  • 29.
  • 30.
    Existing Solutions  BEM OOCSS  SMACSS  Atomic CSS  MCSS  ITCSS
  • 31.
  • 32.
    #1 DOM Independency CSScomponent styles must not rely on particular DOM structure
  • 33.
  • 34.
    Example div.social-links { > diva { color: blue; } }  Visual styles are tightly coupled with markup which makes them hard to evolve  Reusability of component is limited  Ambiguity that leads to growth of specificity
  • 35.
    Rule Avoid any signsof DOM elements in CSS code including references to HTML tags, ID (#) and (>) child selectors
  • 36.
    Rule CSS selectors mustconvey semantics of the component and it’s integral parts. Class selector conveys a lot, element selector almost none
  • 37.
  • 38.
    #2 Location Independency CSScomponent styles must not rely on component’s location on a page
  • 39.
  • 40.
    Example .button { color: white; } .sidebar{ .button { color: orange; } }  Component is no longer self contained entity and its visual appearance is not predictable at dev time  Leads to growth of specificity
  • 41.
    Rule Component styles mustnot be written in reliance on their location on a page, instead provide location independent variations that can be used anywhere
  • 42.
    “Instead of saying- The buttons need to be smaller when they’re in the header, we need to be saying - We have a smaller variant of our buttons, and it’s these that we use in the header.” Harry Roberts
  • 43.
    .button { color: white; /*Other declarations */ &.button--variant-action { color: orange; } } Example
  • 44.
    #3 Limited Complexity CSScomponents can be complex and have integral parts, but no more than having 1 (2) layers of descendant styles
  • 45.
  • 46.
    Example .filter { .filter__header { .filter__header__icon{ /* Declarations */ } } .filter__list { .filter__list__link { /* Declarations */ } } }  Component is too complex which makes it hard to maintain  Following that style may lead to unnecessary dependencies between integral parts
  • 47.
    Rule If CSS styleshave no dependency they must not form parent-child relationship
  • 48.
    Rule Complex components mustbe split into simpler components
  • 49.
    .filter { .filter__header {} .filter__icon{ /* Declarations */ } .filter__list {} .filter__link { /* Declarations */ } } Example
  • 50.
    #4 Enforced Naming Allaspects of component definition must be named in a way to communicate their relationship clearly and protect from incorrect use
  • 51.
    .infobox /* Block*/ { .infobox__body /* Element */ {} .infobox__footer {} .infobox__title {} &.infobox--size-full /* Modifier */ {} } Example
  • 52.
    #5 Explicit States Dynamicbehavior must be implemented with state classes that JavaScript toggles. No direct style manipulation is allowed with JavaScript.
  • 53.
  • 54.
    open: function (){ elements.root.show(); elements.trigger.css('color', 'blue'); }, close: function () { elements.root.hide(); elements.trigger.css('color', 'white'); } Example  Visual appearance of component is now split across JavaScript and CSS which makes is hard to maintain  There is no simple undo operation for setting style properties in JavaScript so previous state has to be maintained somewhere or inline styles manipulated directly
  • 55.
    open: function (){ elements.root.addClass('state-open'); elements.trigger.addClass('state-active'); }, close: function () { elements.root.removeClass('state-open'); elements.trigger.removeClass('state-active'); } Example
  • 56.
    Rule Global states mustnot collide with local states to avoid overrides in CSS
  • 57.
    .state-hidden { display: none; } .infobox{ display: inline-block; &.state-hidden { display: inline-block; visibility: hidden; } } Example  Component is no longer self contained which causes inclusion of override declarations  In some cases may lead to a need to have !importants
  • 58.
    .global-state-hidden { display: none; } .infobox{ display: inline-block; &.state-hidden { visibility: hidden; } } Example
  • 59.
    #6 JavaScript Hooks Neveruse component classes to select elements with JavaScript. Introduce specialized classes just for the sake of selecting elements.
  • 60.
  • 61.
    Example var elements ={ mailing_list: internal.$e, form: internal.$e.find('.social-widget__query'), submit_button: internal.$e.find('.button'), submit_button_text: internal.$e.find('span'), validation_message: internal.$e.find('.social-widget__validation-message') };  Hard to modify component styles due to dependencies in JavaScript. Makes whole component fragile
  • 62.
    Example <div class="social-widget social-widget--inversedjs-mailing-list"> <div class="social-widget__title">Stay in the loop</div> <form class="social-widget__query js-mailing-list-form"> <input class="js-mailing-list-email" type="text" name="email" placeholder="Email address"> <label class="button"> <span class="js-mailing-list-submit-button-text">Submit</span> <input class="js-mailing-list-submit-button" type="submit"> </label> </form> <div class="js-mailing-list-validation-message social-widget__validation-message state-hidden"> Email is not in correct format! </div> </div>
  • 63.
    #7 Local MediaQueries Responsiveness of a page must be based on responsiveness of each component thus Media Query must be local to a component
  • 64.
    .blog-entry { .blog-entry-body { /*Declarations */ } } .infobox {} @media @phone { .blog-entry { .blog-entry-body { /* Declarations */ } } .infobox {} } Example  Code duplication makes components hard to maintain  Components are not self-contained which makes the outcome less predictable at dev time
  • 65.
    .blog-entry { .blog-entry-body { margin-right:@gutter / 2; margin-bottom: @gutter / 2; margin-left: @gutter / 2; @media @phone { margin-left: @gutter_phone / 2; margin-right: @gutter_phone / 2; } } } Example
  • 66.
    .infobox /* Component*/ { .infobox__body /* Integral Part */ {} &.infobox--size-full /* Variant */ {} &.state-active /* State */ {} @media @phone /* Responsive */ {} } .js-infobox /* JavaScript Hook */ Anatomy of Component
  • 67.
    #8 Layout vsVisual Layout aspects should be moved to dedicated components or integral parts
  • 68.
  • 69.
    .infobox { /* Layout*/ float: left; padding-left: @half-col-gutter; padding-right: @half-col-gutter; width: 33.33%; /* Visual */ background-color: green; padding: 10px; /* Collision */ } Example  Limits reusability  Usually require additional html elements just as styling hooks
  • 70.
    Grid based layoutis an emerging trend in Web and Graphics design
  • 71.
    .grid-row { .clearfix(); .grid-row__column { float:left; padding-left: @half-col-gutter; padding-right: @half-col-gutter; width: 33.33%; } } .infobox { background-color: green; padding: 10px; } Example
  • 72.
    #9 Container vsContent Container components must define layout and size constraints, content components – visual look (occasionally size constraints too)
  • 73.
    Rule Don’t make anyassumptions about content on container level
  • 74.
    Rule Develop CSS componentsto be container agnostic
  • 75.
  • 76.
    Types of Styles Base (Normalize,Reset) Utilities (clearfix, valign) Typography (Fonts, Headings, …) Constants (Colors, Sizes, …)
  • 77.
  • 78.
    Physical Structure Single filefor all of the styles Single file with comments One file per type of styles One file per component
  • 79.
    Fallacies  Use idselectors for performance reasons  Too many classes is a bad thing, style based on tags  Nest CSS selectors to follow DOM hierarchy  Put all styles into single file all the time because it is easier to maintain  Introduce lots of utility classes to have greater flexibility when styling things
  • 80.
    Thank you! Forward yourquestions to alexei.skachykhin@live.com

Editor's Notes

  • #4 Всё начинается хорошо, но потом становится сложно сопровождать.
  • #7 Второй раз ты пытаешься хорошо писать свойства CSS, но всё равно что-то идёт не так.
  • #10 Interestingly, we don’t usually make this oversight with other languages. A Rails developer isn’t considered good just because his code works to spec. This is considered baseline. Of course it must work to spec; its merit is based on other things: Is the code readable? Is it easy to change or extend? Is it decoupled from other parts of the application? Will it scale?
  • #11 Тут важно сказать что подсознательно первое о чём мы думаем – это то что мы можем работать без плана. Но как было показано на примере это не работает! И дальше пойдёт описание driving forces.
  • #17 Interestingly, we don’t usually make this oversight with other languages. A Rails developer isn’t considered good just because his code works to spec. This is considered baseline. Of course it must work to spec; its merit is based on other things: Is the code readable? Is it easy to change or extend? Is it decoupled from other parts of the application? Will it scale?
  • #27 Требования к CSS архитектуре такие же как и к архитектуре программных компонентов -> Переход на следующий слайд
  • #28 Требования к CSS архитектуре такие же как и к архитектуре программных компонентов -> Переход на следующий слайд