$30 off During Our Annual Pro Sale. View Details »

Components, patterns and sh*t it’s hard to deal with

Components, patterns and sh*t it’s hard to deal with

or… How I came up with a good use of quotes from Lost in Translation.

A brief run through a few ways of handling flexibility when it comes to patterns and components: modular architectures are hard and defined patterns need to express the right level of flexibility not to defy the point of a pattern library at all.

Marco Cedaro

January 23, 2017
Tweet

More Decks by Marco Cedaro

Other Decks in Technology

Transcript

  1. Modular
    architecture

    View Slide

  2. Modular architecture
    Classes and
    Components

    View Slide

  3. Modular architecture
    Classes and
    Components
    and Modifiers

    View Slide

  4. Modular architecture
    Classes and
    Components
    and modifiers
    and containers

    View Slide

  5. Modular architecture
    Classes and
    Components
    and modifiers
    and containers
    and overrides

    View Slide

  6. Modular architecture
    Classes and
    and modifiers
    and containers
    and overrides
    Components, 

    patterns and sh*t it’s
    hard to deal with

    View Slide

  7. Components, 

    patterns and sh*t 

    it’s hard to deal with

    View Slide

  8. Components, 

    patterns and sh*t 

    it’s hard to deal with
    or… 

    How I came up with a good use of quotes from Lost in Translation

    View Slide

  9. The issue

    View Slide

  10. THE ISSUE
    How do we manage our code, in order
    to re-use patterns without making them
    too rigid for the day to day activities?

    View Slide

  11. REPHRASED
    How do we re-use our patterns in
    slightly different use cases?

    View Slide

  12. View Slide

  13. 1
    2
    3
    4
    FRAMING THE ISSUE
    It’s NOT about any specific tech stack
    It’s about modularity at its core
    It’s about modules responsibilities
    it’s about maintainability (among other coding practices)

    View Slide

  14. Ideas and experiments

    View Slide

  15. 1
    Different name spaces

    View Slide

  16. View Slide

  17. className="content-actions__button"
    iconId="close"
    />

    View Slide

  18. className="content-actions__button"
    iconId="close"
    />

    View Slide

  19. //_content-actions.scss
    .content-actions {
    //[...]
    &__button {
    flex: 1 0 auto;
    padding: rem-px-offset(1rem, -2px) 1rem;
    line-height: 1.5;
    &:hover,
    &:focus {
    background: $grey-1;
    }
    &:active {
    background: $grey-2;
    }
    }
    }

    View Slide

  20. How would this rules
    interact with the base
    button styles? 

    (it’s actually useless, afaik)

    What if we decided to
    refactor the base
    button?
    //_content-actions.scss
    .content-actions {
    //[...]
    &__button {
    flex: 1 0 auto;
    padding: rem-px-offset(1rem, -2px) 1rem;
    line-height: 1.5;
    &:hover,
    &:focus {
    background: $grey-1;
    }
    &:active {
    background: $grey-2;
    }
    }
    }

    View Slide

  21. What role does this
    override play in the
    pattern library space?
    //_content-actions.scss
    .content-actions {
    //[...]
    &__button {
    flex: 1 0 auto;
    padding: rem-px-offset(1rem, -2px) 1rem;
    line-height: 1.5;
    &:hover,
    &:focus {
    background: $grey-1;
    }
    &:active {
    background: $grey-2;
    }
    }
    }

    View Slide

  22. This is the most flexible way to extend
    anything.
    WHY DOES IT WORK?

    View Slide

  23. WHY IT DOESN’T WORK
    The css for the module lives in different
    files and the default style could be
    overridden in unexpected ways.

    View Slide

  24. WHY IT DOESN’T WORK
    We are creating many variants of the
    original patterns.

    View Slide

  25. No technique offers the same flexibility.

    Do we need that flexibility? How does it
    play with having a pattern library?
    ALTERNATIVES?

    View Slide

  26. 2
    Ad hoc BEM-like modifiers

    View Slide

  27. View Slide

  28. View Slide

  29. className="dialog--game-intent">


    View Slide

  30. className="dialog--game-intent">


    View Slide

  31. //_dialog.scss
    .dialog {
    //[...]
    &--game-intent {
    @include mappy-bp(medium) {
    width: 43.75rem;
    height: auto;
    }
    }
    }

    View Slide

  32. //_dialog.scss
    .dialog {
    //[...]
    &--game-intent {
    @include mappy-bp(medium) {
    width: 43.75rem;
    height: auto;
    }
    }
    }
    The modifier lives in
    the same space of
    the dialog.

    …but how many of
    these modifiers
    would we need?

    View Slide

  33. How does this effect
    our performance?


    Does it effect our
    productivity?
    //_dialog.scss
    .dialog {
    //[...]
    &--wizard {
    @include mappy-bp(medium) {
    width: 43.75rem;
    height: 35rem;
    }
    }
    &--game-intent {
    @include mappy-bp(medium) {
    width: 43.75rem;
    height: auto;
    }
    }
    &--save-results {
    @include mappy-bp(medium) {
    width: 23.75rem;
    height: auto;
    }
    }
    }

    View Slide

  34. This practice allows for a lot of
    flexibility, giving a reasonable control
    and keeping all the variants in the same
    space.
    WHY DOES IT WORK?

    View Slide

  35. WHY IT DOESN’T WORK
    The generic component css files would
    start having knowledge of specific
    implementations.
    The file size might be effected by
    unused code.

    View Slide

  36. WHY IT DOESN’T WORK
    Again: do we need this level of
    flexibility?
    How many variants do we need to take
    care of if we decided to edit the base
    component.

    View Slide

  37. 3
    Pre-defined modifiers

    View Slide

  38. View Slide

  39. className="dialog--prompt">


    View Slide

  40. className="dialog--prompt">


    View Slide

  41. //_dialog.scss
    .dialog {
    //[...]
    &--prompt {
    display: block;
    overflow: hidden;
    max-width: map-get($dialog-prompt, max-width);
    height: auto;
    margin: map-get($dialog-prompt, margin);
    padding: 2rem 0 0;
    border-radius: 3px;
    }
    }

    View Slide

  42. Even though it looks
    a lot like the previous
    one, the semantic
    value of the modifiers
    is really different.
    //_dialog.scss
    .dialog {
    //[...]
    &--prompt {
    display: block;
    overflow: hidden;
    max-width: map-get($dialog-prompt, max-width);
    height: auto;
    margin: map-get($dialog-prompt, margin);
    padding: 2rem 0 0;
    border-radius: 3px;
    }
    }

    View Slide

  43. The patterns are at the center: no
    special cases, but pre-defined flavours
    of the basic components. 


    This make it easy to identify
    responsibilities and to refactor base
    components.
    WHY DOES IT WORK?

    View Slide

  44. WHY IT DOESN’T WORK
    Sometime it would drive to preemptive
    abstraction, which is, most of the time,
    a bad idea.

    View Slide

  45. It’s a great practice when we identify
    variants of base components.
    WHY DOES IT WORK?

    View Slide

  46. type="prompt">


    View Slide

  47. View Slide

  48. The issue

    View Slide

  49. WE NEVER FOUND AN ANSWER
    How do we re-use our patterns in
    slightly different use cases?

    View Slide

  50. And what can be tackled without
    defying the point of even having a
    pattern library?
    WHAT ARE WE REALLY TRING TO SOLVE?

    View Slide

  51. (Problem) => Solution*
    * see what I did there?

    View Slide

  52. View Slide

  53. View Slide

  54. 1
    2
    3
    4
    WHAT DO WE NEED TO CHANGE?
    Arrangement in parent components (margin, sizing, paddings…)
    Arrangement in relation to other components
    Theming (colours, borders…)
    What else?

    View Slide

  55. 1
    Arrangement in parent components

    View Slide

  56. className="game-intent__dialog">




    View Slide

  57. className="game-intent__dialog">




    View Slide

  58. //_dialog.scss
    .dialog {
    width: 100%;
    height: 100%;
    //[...]
    }
    //_game-intent.scss
    .game-intent {
    //[...]
    &__dialog {
    @include mappy-bp(medium) {
    width: 43.75rem;
    height: auto;
    }
    }
    }

    View Slide

  59. //_dialog.scss
    .dialog {
    width: 100%;
    height: 100%;
    //[...]
    }
    //_game-intent.scss
    .game-intent {
    //[...]
    &__dialog {
    @include mappy-bp(medium) {
    width: 43.75rem;
    height: auto;
    }
    }
    }
    Every module has the
    right responsibility:
    parents define
    positioning in the
    parent space;
    children adapts and
    define themselves.

    View Slide

  60. This practices defines responsibilities
    in a neat way and it enables for specific
    implementations without invalidating
    patterns.
    WHY DOES IT WORK?

    View Slide

  61. This is really different
    because the latter
    custom class won’t
    override the Dialog
    styles.
    className="custom-class">




    className="custom-class">


    View Slide

  62. WHY IT MIGHT NOT WORK
    Potentially you might need a wrapper
    HTML element that could have been
    avoided.

    View Slide

  63. 2
    Arrangement in relation to other components

    View Slide

  64. className="space-max inner-space-min">


    View Slide

  65. className="space-max inner-space-min">


    View Slide

  66. Helper classes, if the
    base components are
    expecting them,
    could help avoiding
    repeated code.
    className="space-max inner-space-min">


    View Slide

  67. Besides the benefit of not having to
    come up with new class names,
    creating a set of positional helpers
    moves the conversation regarding
    component relationships back to the
    pattern library.
    WHY DOES IT WORK?

    View Slide

  68. WHY IT MIGHT NOT WORK
    The positional classes might get stale if
    not codified properly in the pattern lib.


    We might end up with weird stuff. 

    (e.g: .space-maxYminX or .inner-space-noYmaxX)

    View Slide

  69. 3
    Theming

    View Slide

  70. //_question-content-block.scss
    .question-content-block {
    //[...]
    &__icon-button {
    //[...]
    .icon {
    width: $content-block-icon-large-size;
    height: $content-block-icon-large-size;
    }
    }

    View Slide

  71. //_question-content-block.scss
    .question-content-block {
    //[...]
    &__icon-button {
    //[...]
    .icon {
    width: $content-block-icon-large-size;
    height: $content-block-icon-large-size;
    }
    }

    View Slide

  72. //_question-content-block.scss
    .question-content-block {
    //[...]
    &__icon-button {
    //[...]
    @include icon-size($content-block-icon-medium-size);
    }
    }
    //_icon.scss
    @mixin icon-size($size) {
    .icon {
    width: $size;
    height: $size;
    }
    }

    View Slide

  73. //_question-content-block.scss
    .question-content-block {
    //[...]
    &__icon-button {
    //[...]
    @include icon-size($content-block-icon-medium-size);
    }
    }
    //_icon.scss
    @mixin icon-size($size) {
    .icon {
    width: $size;
    height: $size;
    }
    }

    View Slide

  74. The icon exposes an
    API to change some
    predefined properties
    The responsibilities
    order of things is
    maintained
    //_question-content-block.scss
    .question-content-block {
    //[...]
    &__icon-button {
    //[...]
    @include icon-size($content-bloc…
    }
    }
    //_icon.scss
    @mixin icon-size($size) {
    .icon {
    width: $size;
    height: $size;
    }
    }

    View Slide

  75. Every base component can be as
    flexible as it defines itself to be.
    Developers always have control on
    what they expose.
    WHY DOES IT WORK?

    View Slide

  76. WHY IT MIGHT NOT WORK
    The complexity of the sass logic might
    grow.

    View Slide

  77. 3
    What else?

    View Slide

  78. View Slide

  79. View Slide

  80. View Slide

  81. View Slide