Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CSS for Software Engineers for CSS Developers

Harry Roberts
September 15, 2015

CSS for Software Engineers for CSS Developers

Applying traditional software engineering principles directly (or indirectly) to CSS.

Harry Roberts

September 15, 2015
Tweet

More Decks by Harry Roberts

Other Decks in Design

Transcript

  1. CSS for Software Engineers
    for CSS Developers
    Harry Roberts – SmashingConf Freiburg – September 2015

    View Slide

  2. View Slide

  3. 1959

    View Slide

  4. The First Modern
    Programming Language

    View Slide


  5. FLOW-MATIC
    FLOW-MATIC, originally known as B-0, was
    the first English-like data processing
    language. It was developed for the UNIVAC I
    at Remington Rand under Grace Hopper
    during the period from 1955 until 1959.

    View Slide

  6. The First Programming Language
    FLOW-MATIC.
    The first modern, electronic programming language.
    1955–1959.
    Developed by Grace Hopper.
    wikipedia.org/wiki/FLOW-MATIC

    View Slide


  7. FLOW-MATIC
    … Rand management considered the idea
    unfeasible. In early 1955, she and her team
    wrote a specification for such a
    programming language and implemented a
    prototype.

    View Slide

  8. 1959 2015
    1990
    1996

    View Slide

  9. 1959 2015
    1990
    1996
    FLOW-MATIC
    Me
    CSS
    Mum and Dad
    Sister

    View Slide

  10. View Slide

  11. 1959 2015
    1990
    1996
    FLOW-MATIC
    Me
    CSS
    Mum and Dad
    Sister

    View Slide

  12. 1959 2015
    1996

    View Slide

  13. A 37-Year Head Start
    All that wisdom!
    We should get as much from their head start as we can.
    The time between the first modern programming language
    and CSS is greater than the time between CSS and now.
    Software engineers have been writing code for 37 years
    longer than front-end developers have.

    View Slide

  14. DRY/Single Source of Truth

    View Slide


  15. Don’t Repeat Yourself
    Every piece of knowledge must have a single,
    unambiguous, authoritative representation
    within a system.
    — wikipedia.org/wiki/Don't_repeat_yourself

    View Slide

  16. Don’t Repeat Yourself
    Every discrete piece of information should exist only once.
    You shouldn’t need to make the same change several times.
    Repetition is extra overhead: more to maintain, to go wrong.
    Increases cognitive overhead.
    Contributes to bloat.

    View Slide

  17. Don’t Repeat Yourself
    .u-margin-top { margin-top: 12px; }
    .u-margin-right { margin-right: 12px; }
    .u-margin-bottom { margin-bottom: 12px; }
    .u-margin-left { margin-left: 12px; }

    View Slide

  18. Don’t Repeat Yourself
    .u-margin-top { margin-top: 12px; }
    .u-margin-right { margin-right: 12px; }
    .u-margin-bottom { margin-bottom: 12px; }
    .u-margin-left { margin-left: 12px; }
    We’ve typed
    the same value
    four times.

    View Slide

  19. Don’t Repeat Yourself
    $unit: 12px;
    .u-margin-top { margin-top: $unit; }
    .u-margin-right { margin-right: $unit; }
    .u-margin-bottom { margin-bottom: $unit; }
    .u-margin-left { margin-left: $unit; }
    Much better!
    A single source
    of truth.

    View Slide

  20. Don’t Repeat Yourself
    .page-title {
    font-family: "Custom Font", sans-serif;
    font-weight: 700;

    }
    .btn {
    font-family: "Custom Font", sans-serif;
    font-weight: 700;

    }
    .pagination {
    font-family: "Custom Font", sans-serif;
    font-weight: 700;

    }

    View Slide

  21. Don’t Repeat Yourself
    .page-title {
    font-family: "Custom Font", sans-serif;
    font-weight: 700;

    }
    .btn {
    font-family: "Custom Font", sans-serif;
    font-weight: 700;

    }
    .pagination {
    font-family: "Custom Font", sans-serif;
    font-weight: 700;

    }
    Thematically
    related but
    repeated.

    View Slide

  22. Don’t Repeat Yourself
    @mixin custom-font() {
    font-family: "Custom Font", sans-serif;
    font-weight: 700;
    }

    .page-title {
    @include custom-font();

    }
    .btn {
    @include custom-font();

    }
    .pagination {
    @include custom-font();

    }
    Argumentless
    mixins are
    perfectly okay.

    View Slide

  23. Don’t Repeat Yourself
    @mixin custom-font() {
    font-family: "Custom Font", sans-serif;
    font-weight: 700;
    }

    .page-title {
    @include custom-font();

    }
    .btn {
    @include custom-font();

    }
    .pagination {
    @include custom-font();

    }
    Gives the exact same
    output, but at least we
    haven’t duplicated
    anything manually.

    View Slide


  24. Single Source of Truth
    […] the practice of structuring information
    models and associated schemata such that
    every data element is stored exactly once.
    — wikipedia.org/wiki/Single_Source_of_Truth

    View Slide

  25. Single Source of Truth
    The more philosophical principle behind DRY.
    Key data should only exist once in source.
    Increases confidence.
    Prevents anomalies and disparity.
    Makes changes simpler.
    Keeps your house in order.

    View Slide

  26. Confusion
    DRY in source, not in production.
    Not about avoiding repetition…
    It’s about avoiding repeating yourself.
    Automation of repetition is fine.

    View Slide


  27. Confusion
    If you manually type a declaration 50 times in
    a project, you are repeating yourself: this is not
    DRY. If you can generate that declaration 50
    times without having to manually repeat it, this
    is DRY: you are generating repetition without
    actually repeating yourself. This is quite a
    subtle but important distinction to be aware of.
    — csswz.it/1ytQkxp

    View Slide

  28. Confusion
    .btn {
    color: white;
    font-weight: bold;

    }
    .calendar__title {
    font-size: 14px;
    font-weight: bold;
    }
    .message {
    font-weight: bold;
    }

    View Slide

  29. Confusion
    .btn {
    color: white;
    font-weight: bold;

    }
    .calendar__title {
    font-size: 14px;
    font-weight: bold;
    }
    .message {
    font-weight: bold;
    }
    This is purely
    coincidental.
    Don’t try to
    DRY it out.

    View Slide

  30. Confusion
    Don’t DRY if it’s repeated coincidentally.
    Repetition in compiled code is fine.
    Just avoid duplicating data in source.
    Going too far creates awkward and confusing structures in
    your code.

    View Slide

  31. DRY/Single Source of Truth
    Use a preprocessor to store key data in variables.
    Make use of mixins to generate repetition for you.
    Abstract design patterns out into reusable objects.
    Do not DRY anything that is purely coincidental.
    Repetition is better than the wrong abstraction.

    View Slide

  32. The Single
    Responsibility Principle

    View Slide


  33. The Single Responsibility Principle
    […] the single responsibility principle states
    that every class should have responsibility
    over a single part of the functionality
    provided by the software, and that
    responsibility should be entirely
    encapsulated by the class.
    — wikipedia.org/wiki/Single_responsibility_principle

    View Slide

  34. The Single Responsibility Principle
    AKA: Do one thing, one thing only, and one thing well.
    Break bigger monoliths down into individual concerns.
    Easier to reason about.
    Provides higher composability.

    View Slide

  35. View Slide

  36. View Slide

  37. The Single Responsibility Principle
    Subway is the epitome of SRP.
    Break things down into their smallest possible parts.
    Ensure each part fulfils its responsibility very well.
    Combine responsibilities to create complex components.
    Swap out, remove, or add discrete parts.
    Helps you Separate your Concerns.
    Gives you incredible opportunity and flexibility.

    View Slide

  38. 6,442,450,944

    View Slide

  39. 6,442,450,944
    6.4bn possible sandwich combinations.
    All by offering individual ingredients.
    Smaller pieces allow for greater composition.
    csswz.it/1XKydl8

    View Slide

  40. The Single Responsibility Principle
    #sandwich {

    bread: white;
    meat: chicken;
    salad: lettuce, onion, tomato;
    sauce: mayonnaise;

    }
    ...

    View Slide

  41. The Single Responsibility Principle
    #sandwich {

    bread: white;
    meat: chicken;
    salad: lettuce, onion, tomato;
    sauce: mayonnaise;

    }
    ...
    This is a monolith.
    It’s difficult to change,
    swap, or remove things.

    View Slide

  42. The Single Responsibility Principle
    .bread, .bread--white {}
    .chicken {}

    .lettuce {}
    .onion {}
    .tomato {}
    .mayonnaise {}
    ...
    Perfect! Now we
    can make our own
    sandwich from the
    ingredients we want.

    View Slide

  43. The Single Responsibility Principle
    .bread, .bread--white {}
    .chicken {}

    .lettuce {}
    .onion {}
    .tomato {}
    .mayonnaise {}
    ...
    No tomato?
    No problem!

    View Slide

  44. The Single Responsibility Principle
    Provide developers with the ingredients.
    Let them make the meals.
    Let’s look at a more realistic example…

    View Slide

  45. The Single Responsibility Principle
    .btn-login {
    display: inline-block;
    padding: 2em;
    background-color: green;
    color: white;

    }

    View Slide

  46. The Single Responsibility Principle
    .btn-login {
    display: inline-block;
    padding: 2em;
    background-color: green;
    color: white;

    }
    Mixing responsibilities.
    Base.
    Structural.
    Cosmetic.

    View Slide

  47. The Single Responsibility Principle
    .btn {
    display: inline-block;

    }
    .btn--large {
    padding: 2em;
    }
    .btn--positive {
    background-color: green;
    color: white;

    }

    View Slide

  48. The Single Responsibility Principle
    .btn {
    display: inline-block;

    }
    .btn--large {
    padding: 2em;
    }
    .btn--positive {
    background-color: green;
    color: white;

    }
    That’s better.

    View Slide

  49. The Single Responsibility Principle
    .btn { ... }
    .btn--large { ... }
    .btn--small { ... }
    .btn--positive { ... }
    .btn--negative { ... }
    .btn--full { ... }
    Plus now we can
    combine these classes
    with others to make lots
    of varieties of button.

    View Slide

  50. The Separation of Concerns

    View Slide


  51. The Separation of Concerns
    […] It is, that one is willing to study in depth an aspect of
    one’s subject matter in isolation for the sake of its own
    consistency […] But nothing is gained—on the contrary!
    —by tackling these various aspects simultaneously. It is
    what I sometimes have called ‘the separation of
    concerns’ […] it does not mean ignoring the other
    aspects, it is just doing justice to the fact that from this
    aspect’s point of view, the other is irrelevant. It is being
    one- and multiple-track minded simultaneously.
    — wikipedia.org/wiki/Separation_of_concerns

    View Slide

  52. The Separation of Concerns
    Each thing responsible for itself and nothing more.
    Reason about and study features in isolation.
    In CSS:
    Only bind CSS onto CSS-based classes only.
    Don’t write DOM-like selectors.
    Don’t bind CSS onto data-* attributes.
    Don’t bind JS onto CSS classes.

    View Slide

  53. The Separation of Concerns
    // Binding CSS onto accessibility hints.
    [role="nagivation"] { ... }
    // Putting DOM information into our CSS.
    header nav ul li a { ... }
    // Using HTML to provide cosmetics.

    // Binding JS onto styling hooks.
    document.getElementsByClassName('nav');

    View Slide

  54. The Separation of Concerns



    ...



    ...




    View Slide

  55. The Separation of Concerns



    ...



    ...




    View Slide

  56. The Separation of Concerns
    [role="navigation"] { ... }
    [role="navigation"] > ul { ... }
    [role="navigation"] > ul > li { ... }

    View Slide

  57. The Separation of Concerns
    [role="navigation"] { ... }
    [role="navigation"] > ul { ... }
    [role="navigation"] > ul > li { ... }
    Ewww!
    Loading our
    CSS with
    DOM information.

    View Slide

  58. The Separation of Concerns
    role="navigation">



    ...



    ...




    View Slide

  59. The Separation of Concerns
    role="navigation">



    ...



    ...




    Semantic
    concerns.

    View Slide

  60. The Separation of Concerns
    role="navigation">



    ...



    ...



    Accessibility
    concerns.

    View Slide

  61. The Separation of Concerns
    role="navigation">



    ...



    ...



    Stylistic
    concerns.

    View Slide

  62. The Separation of Concerns
    role="navigation">



    ...



    ...




    Behavioural
    concerns.

    View Slide

  63. The Separation of Concerns
    Grid systems are a great example.
    Handle your layout completely separately to your
    components.
    Writing CSS in JS breaks the Separation of Concerns.
    Can’t reconsider your JS architecture without having to
    reconsider your CSS architecture.

    View Slide


  64. The Separation of Concerns
    If in 14 months you find a new view library or
    framework you want to try out, you’re out of
    luck. You will have to invest a lot of time into
    pulling styles back out of JavaScript modules
    and into stylesheets again.
    — keithjgrant.com/posts/against-css-in-js.html

    View Slide

  65. Immutability

    View Slide


  66. Immutability
    …an immutable object is an object whose
    state cannot be modified after it is created.
    — wikipedia.org/wiki/Immutable_object

    View Slide

  67. Immutability
    Provides confidence.
    Makes things predictable.
    Helps debugging.
    Reduces cognitive overhead.
    Removes caveats, states, and conditions.

    View Slide

  68. Immutability
    .col-6 {
    width: 50%;
    }

    View Slide

  69. Immutability
    .col-6 {
    width: 50%;
    }
    @media screen and (max-width: 480px) {
    .col-6 {
    float: none;
    width: 100%;
    }
    }

    View Slide

  70. Immutability
    .col-6 {
    width: 50%;
    }
    @media screen and (max-width: 480px) {
    .col-6 {
    float: none;
    width: 100%;
    }
    }
    This has
    mutated!

    View Slide

  71. Immutability
    .col-6 has one input, but two potential outputs.
    Outcome depends on how/when you observe it.
    It has been mutated.
    Mutable state leads to confusion and unexpected outcomes.
    Particularly common in CSS.

    View Slide

  72. Immutability
    .col-6 {
    width: 50%;
    }
    @media screen and (max-width: 480px) {
    .col-6@sm {
    float: none;
    width: 100%;
    }
    }
    Use a
    different class.

    View Slide

  73. Immutability
    .sub-content h2 {
    text-align: left;

    }
    .u-text-center {
    text-align: center;
    }

    ...

    View Slide

  74. Immutability
    .sub-content h2 {
    text-align: left;

    }
    .u-text-center {
    text-align: center;
    }

    ...

    Specificity
    mismatch.
    This will be
    aligned left!

    View Slide

  75. Immutability
    Parts of the codebase are able to mutate other parts.
    Unpredictable outcomes.
    Unexpected side effects.
    This is fixable.

    View Slide

  76. Immutability
    .sub-content h2 {
    text-align: left;

    }
    .u-text-center {
    text-align: center !important;
    }

    ...

    The only time
    to use it.

    View Slide

  77. Immutability
    .btn {
    font-size: 1em;
    }
    .promo .btn {
    font-size: 1.2em;
    }

    View Slide

  78. Immutability
    .btn {
    font-size: 1em;
    }
    .promo .btn {
    font-size: 1.2em;
    }
    One class,
    two outcomes.

    View Slide

  79. Immutability
    .btn {
    font-size: 1em;
    }
    .btn--large {
    font-size: 1.2em;
    }
    Two classes,
    two outcomes.

    View Slide

  80. Immutability
    Don’t have several states of the same thing.
    Use Modifiers or Responsive Suffixes.
    Use !important to force immutability.
    Brings us nicely onto…

    View Slide

  81. Cyclomatic Complexity

    View Slide


  82. Cyclomatic Complexity
    Cyclomatic complexity is a software metric
    used to indicate the complexity of a program.
    It is a quantitative measure of the number of
    linearly independent paths through a
    program’s source code.
    — wikipedia.org/wiki/Cyclomatic_complexity

    View Slide

  83. Cyclomatic Complexity
    M = E - N + 2P
    Basically just the number of IFs/ELSEs.
    A form of static analysis.
    Counting the number of paths through a program.
    The amount of potential outcomes given certain conditions.
    Higher complexity is bad: simpler is always better.

    View Slide

  84. Cyclomatic Complexity
    M = E - N + 2P
    Basically just the number of IFs/ELSEs.
    A form of static analysis.
    Counting the number of paths through a program.
    The amount of potential outcomes given certain conditions.
    Higher complexity is bad: simpler is always better.
    Whut?!

    View Slide

  85. Cyclomatic Complexity
    div.main section.content h1 a span {}

    View Slide

  86. Cyclomatic Complexity
    @if div {
    @if .main {
    @if section {
    @if .content {
    @if h1 {
    @if a {
    @if span {
    // Do stuff.
    }
    }
    }
    }

    }
    }
    }

    View Slide

  87. Cyclomatic Complexity
    div.main section.content h1 a span {}
    Subject.
    This is the
    bit we actually
    care about.

    View Slide

  88. Cyclomatic Complexity
    div.main section.content h1 a span {}
    Conditions.
    All of this is
    just complexity.

    View Slide

  89. Cyclomatic Complexity
    .text-highlight {}
    Start off
    explicitly.
    Don’t add
    needless
    complexity.

    View Slide

  90. Cyclomatic Complexity
    Deeply nested or qualified selectors are bad.
    They carry a higher Cyclomatic Complexity.
    Reduce by using much shorter selectors.
    Get straight to the point.
    Remove as many conditions and caveats as possible.
    Start with the correct subject.

    View Slide

  91. View Slide

  92. Cyclomatic Complexity
    div#body section.container section.main
    div.categories ul li a.product-title {}

    View Slide

  93. Cyclomatic Complexity
    div#body section.container section.main
    div.categories ul li a.product-title {}

    View Slide

  94. The Open/Closed Principle

    View Slide


  95. The Open/Closed Principle
    Software entities (classes, modules,
    functions, etc.) should be open for extension,
    but closed for modification.
    — wikipedia.org/wiki/Open/closed_principle

    View Slide

  96. The Open/Closed Principle
    Never change anything at its source.
    Avoid the Domino Effect.
    Doing so causes visual regressions.
    Hard to keep track of the knock-on effects.
    Always make changes via extension (i.e. addition).
    Possibly the most useful principle for dealing with other
    peoples’ code.

    View Slide


  97. The Open/Closed Principle
    […] once completed, the implementation of a
    class could only be modified to correct errors;
    new or changed features would require that
    a different class be created. That class could
    reuse coding from the original class through
    inheritance.
    — wikipedia.org/wiki/Open/closed_principle

    View Slide

  98. The Open/Closed Principle
    .btn {
    ...
    padding: 1em 2em;
    }

    View Slide

  99. The Open/Closed Principle
    .btn {
    ...
    padding: 1em 2em;
    }
    Once this is
    out there, we
    can’t risk
    changing it
    directly.

    View Slide

  100. The Open/Closed Principle
    .btn {
    ...
    padding: 1em 2em;
    }
    .promo .btn {
    padding: 1.5em 2.5em;
    }

    View Slide

  101. The Open/Closed Principle
    .btn {
    ...
    padding: 1em 2em;
    }
    .promo .btn {
    padding: 1.5em 2.5em;
    }
    Even this is
    risky as we’re
    still modifying the
    base button class.

    View Slide

  102. The Open/Closed Principle
    .btn {
    ...
    padding: 1em 2em;
    }
    .btn--large {
    padding: 1.5em 2.5em;
    }
    Perfect!
    A brand new class
    adds the changes
    that we want to
    safely opt in to.

    View Slide

  103. The Open/Closed Principle
    A safe way to make changes.
    Everything gets opted into explicitly.
    Prevents changes from happening one-sidedly; the
    developer has to add the class into the markup as well.
    A second layer of safety: changes can’t be actioned from
    one place alone.
    Build things forward.
    Analogous to rewriting Git history.
    Safe way of working with legacy.

    View Slide

  104. Orthogonality

    View Slide


  105. Orthogonality
    Orthogonality in programming language
    design is the ability to use various language
    features in arbitrary combinations with
    consistent results.
    — wikipedia.org/wiki/Orthogonality

    View Slide

  106. Orthogonality
    Reduces interdependence.
    Improves composability.
    Separates concerns.
    Reduces collisions.
    Removes side effects.
    Good litmus test: can we reorder imports?

    View Slide

  107. View Slide

  108. Orthogonality
    (How well) can we arbitrarily combine things?
    The implication is that they don’t rely on one another.
    The hallmarks of a flexible and modular system.

    View Slide

  109. Orthogonality
    Another good test: will it nest?
    Can things be combined in the DOM?
    Well-scoped selectors improve orthogonality.

    View Slide

  110. View Slide

  111. Collisions!

    View Slide

  112. View Slide

  113. Proper scoping
    provides orthogonality

    View Slide

  114. The Moustache Principle

    View Slide


  115. The Moustache Principle
    Just because you can, it doesn’t
    mean that you should.
    — Harry Roberts

    View Slide

  116. View Slide

  117. Thank You
    Harry Roberts
    csswizardry.com
    [email protected]
    @csswizardry
    speakerdeck.com/csswizardry

    View Slide