1. Please Respect the Global CSS Namespace

    TL;DR

    CSS doesn’t allow us (yet) to scope our styles to a particular component in an HTML document (unless we use iframes…). We can try to circumvent this limitation by respecting the global CSS namespace.

    We usually can safely set typography styles globally, but when it comes to form elements, tables and lists, it’s a bit trickier: we have to remember that not all the input fields will always look the same, tables won’t necessarily need zebra striping in every case, and unordered lists might be used to build user interface elements such as navigation menus.

    Also, we want to avoid using some class names that could easily create collisions with other components of the front-end architecture.

    Thankfully, web standards address this problem with Web Components and scoped styles (more on that at the bottom of this post). We’ll just have to wait for a broader browser support before we start implementing them.

    Story of a Polluted Global CSS Namespace

    When working on an existing codebase, I always find many design patterns directly styled in the global scope with element selectors (i.e. table, input, button).

    In my experience, globally styled elements can be a bottleneck for future development as overriding these styles have a maintainability and performance cost.

    Also, I found components styled using loose scoped classes (e.g. .component .title) or elements (e.g. .component h3). This is clearly not re-usable and even creates new problems as encapsulating multiple components will lead to styling collisions.

    I asked myself how it happened in the first place.

    The story I found the most believable is that the first developer to code the main patterns in the style guide (or pattern library) may think “according to the PSDs, tables will always look like this” or “our form elements surely won’t ever differ from what the designer handed to me”. He then picks the best elements for the job (an input, a table or a ul) and styles them accordingly.

    Thing is, exceptions happen along the road when refining the user experience. Then, any divergence from the initial global styles will lead to overriding them with… more styles.

    Now let’s take a rather extreme example and pretend that, for some hypothetical reason, the initial styling of all divs is a drop shadow.

    div {
      box-shadow: 1px 1px 3px 0 #666;
    }
    

    Every time we need a different styling for such an element in our HTML, we need to remove that nasty shadow with an additional rule:

    .a-component {
      box-shadow: none;
      /* styles */
    }
    .another-component {
      box-shadow: none;
      /* styles */
    }
    

    On a large scale project with many iterations and exceptions, this leads to a lot of superfluous code and deeply affects the maintainability of our platform.

    Of course we’ll want to include some sort of reset and/or normalize styling of basic elements, but then we shouldn’t get too carried away when we are styling raw elements in the global scope or we might regret it in the long run.

    Global Typography Settings

    In a content-first approach (especially for publication websites and blogs), it makes sense to style typography elements in the global scope.

    So, unless our typography is very complex (i.e. headings with many effects, a background and borders…), we can safely define those styles globally:

    • Headings
    • Paragraphs
    • Unordered & ordered lists
    • Definition lists
    • Blockquotes
    • code & pre
    • Links (be careful with those: default styling is welcome with it is very simple, not so much if it’s complex)

    Also, it can be useful to provide an easy way to reset the appearance of those elements with utility classes.

    Anthony Short doesn’t agree and thinks typography styling should be scoped, but in my experience styling it globally has been the best way of doing it. Make your own choice, experiment, and join the discussion.

    Elements We Don’t Want to Style Globally

    We usually can safely design around defaults related to typography, but what about forms, tables and lists ? These particular elements are used in so many different ways that it might not be as safe to set defaults for them. Let’s dig deeper into the why and discover how to avoid conflicts and ease our front-end development.

    Forms

    Here are some common selectors for form controls and fields:

    • form
    • input
    • input[type]
    • select
    • option
    • button

    In a user interface, those elements often need to be customized to fit particular situations. We don’t want to end up resetting too many styles whenever a button or a text field has a slightly different style.

    In this example, we are assuming ALL buttons will always have the same appearance:

    input[type=reset],
    input[type=submit],
    button {
      border-radius: 5px;
      box-shadow: 1px 1px 3px 0 #666;
      background: green;
      color: #fff;
      font-weight: bold;
      font-size: 16px;
    }
    

    The amount of code we need to reset these styles is tremendous.

    Instead, you’ll probably want to create a .button class:

    .button {
      /* styles */
    }
    

    … and apply it to any element that needs a fancy paint job.

    As previously mentioned, a sensible approach to styling form elements will be a set of classes, like .field, .searchfield, .button… we can then set modifier classes or create new styles easily: .button--primary (modifier of .button), .calltoaction (which could be another type of button), without any superfluous CSS properties.

    Following this method with forms allows reusability and better maintainability.

    Tables

    Now, not every table needs to be styled with fancy alternate rows, rounded corners, shadowy edges and sorting options.

    Below is an example I encountered where we had a globally styled table element (fancy borders!) with zebra striping:

    /**
     * Global table styling
     */
    table {
      border: 1px solid #ddd;
      border-radius: 3px;
    }
    /* Zebra striping */
    tr:nth-child(2n+1) {
      background-color: #eee;
    }
    

    Every new design pattern needed to take these rules into account, leading to the creation of a .naked-table class:

    /**
     * .naked-table
     * Un-styles a table
     */
    .naked-table {
      border: 0;
      border-radius: 0;
    }
    .naked-table tr:nth-child(2n+1) {
      background-color: transparent;
    }
    

    This is obviously a major issue for maintainability and, by extension, our mental wellness.

    Aside from adjusting paddings and text alignment to normalize the appearance across different user agents, we usually won’t want those selectors to appear in the global namespace:

    • table
    • thead
    • tfoot
    • tbody
    • th
    • td
    • tr
    • tr:nth-child(…)

    The preferred solution is to create a nice set of self-explanatory classes that will ease specific styling of tables, like .table, .table--striped (adding zebra striping to .table), .table-data (a different type of table)…

    .table {
      border: 1px solid #ddd;
      border-radius: 3px;
    }
    /* Zebra striping */
    .table--striped tr:nth-child(2n+1) {
      background-color: #eee;
    }
    

    Problem solved!

    In his Inuit.css framework, Harry Roberts demonstrates this point elegantly within the _tables.scss partial.

    The Red Zone of Generic Classes

    Members of a development team code in a shared space, as CSS is not good at scoping or blocking inheritance (yet).

    When a new module or component is plugged into a project, it is not unlikely to notice different coding conventions and sometimes very generic classes, removing the ability to use those identifiers elsewhere in the project.

    When building a specific module, we know we shouldn’t use identifiers as #header, #content or #footer because it makes complete sense. But we should also try not to use generic classes such as:

    • .title
    • .body
    • .content
    • .block
    • .img
    • .label
    • .open
    • .icon
    • .button
    • .inner
    • .wrapper
    • .more
    • .panel
    • .tab

    We do not want to enter this red zone: these classes are very likely to be used by other developers (probably in the base architecture itself), and unless we have a very good test suite that covers all the aspects of our front-end, using these classes will increase the chance of having conflicts and regressions.

    When creating new components or refactoring our codebase, we can follow principles inspired by a BEM-variant, introduced by Nicolas Gallagher for creating template components.

    In a nutshell, we’ll want to write something like .component-title instead of .component .title. There are many ways of naming identifiers, and none of them is an absolute winner. The only matter is consistency and ideally our whole codebase should follow the same conventions.

    DOM Episode IV: A New Hope

    The techniques described above aim to facilitate code reuse, avoid collisions and ease refactoring. Thankfully, we won’t have to worry anymore about it so much in the future, as web standards are working on a solution.

    Web Components and the Shadow DOM (relying on the DOM4 W3C specification) bring a whole new dimension to templating in HTML and CSS. Creating encapsulated and isolated widgets within packages will become a reality in the not-too-far future, enabling better coordination when assembling features.

    Also, the scoped attribute is coming for the <style> tag, meaning we won’t have to worry about selector collisions:

    <template>
      <style scoped>
        h2 {
          /*
            These styles will only be applied to h2s
            within the scope of <template>
          */
        }
      </style>
    </template>
    

    I am really excited about HTML Components and what they mean for the front-end architecture (a template tag, how cool is that?) and think we should draw as much inspiration as possible from the specification so our code and mindset are ready when we can actually start implementing it.

    Fancy experimenting with Shadow DOM today? Looks like the stable version of Chrome already supports it.

    In the mean time, respect your peers, respect the global CSS namespace.

Notes

  1. jalbertbowdenii reblogged this from kaelig
  2. cahnory reblogged this from kaelig
  3. fabiencanu reblogged this from kaelig
  4. goetter reblogged this from kaelig
  5. kaelig posted this