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

Style Documentation for the Resource-Constrained

Style Documentation for the Resource-Constrained

Betsy Haibel

May 06, 2016
Tweet

More Decks by Betsy Haibel

Other Decks in Programming

Transcript

  1. @betsythemuffin getting things done under resource constraints 1. Have a

    vision of where you want to go 2.Figure out baby steps that will get you there 3....make those steps smaller 4....and adaptable to changing circumstances 5....and recognize that the finished product will resemble but not match your vision 6.Then go do the thing!
  2. @betsythemuffin domain-driven design for the frontend - All applications need

    a "shared ubiquitous language" for business logic - Most applications need a SEPARATE "shared ubiquitous language" for UI logic.
  3. @betsythemuffin Why document it? - Defuse fear of unfamiliar code

    - More useful conversations with non-engineers
  4. @betsythemuffin what does belong? Look and feel of individual widgets

    - This can be as simple as a screenshot, or an embedded version -- and should be! look and feel of individual widgets
  5. @betsythemuffin what does belong? How widgets are arranged and grouped

    - What elements have sub-elements? What sub- and parent elements are allowed? - Can an element appear alone, or is it always part of a group? - What conventions demarcate elements and groups of elements? how elements are arranged
  6. @betsythemuffin what does belong? What problems does a widget solve?

    - "Only display notification boxes that relate to the user's current goal" - "Use accordions to Look and feel of individual widgets - This can be as simple as a screenshot, or an embedded version -- and should be! What UI problems does it solve?
  7. @betsythemuffin the "just use Bootstrap" fallacy - Do you use

    all of Bootstrap, or a subset? - Do you have custom, non-Bootstrap UI elements? - Do you have implicit rules about how Bootstrap's used? - Do you copypasta markup a lot?
  8. @betsythemuffin barriers to styleguide adoption - Sheer amount of work

    - Justifying sheer amount of work - Inflexible styleguides are bad in real-world UIs - "Free spirit" designers - Worries about dead documentation
  9. @betsythemuffin <ul  class='tabs'>      <li  class='tab<%=  current_page?(cats_path)  ?  '

     active'  :  ''  %>'>          <%=  link_to_unless_current  'Cats',  cats_path  %>      </li>      <li  class='tab<%=  current_page?(dogs_path)  ?  '  active'  :  ''  %>'>          <%=  link_to_unless_current  'Dogs',  dogs_path  %>      </li>   </ul>
  10. @betsythemuffin <ul  class='tabs'>      <li  class='tab<%=  current_page?(cats_path)  ?  '

     active'  :  ''  %>'>          <%=  link_to_unless_current  'Cats',  cats_path  %>      </li>   </ul> <ul  class='tabs'>      <li  class='tab<%=  current_page?(dogs_path)  ?  '  active'  :  ''  %>'>          <%=  link_to_unless_current  'Dogs',  dogs_path  %>      </li>   </ul>
  11. @betsythemuffin module  ComponentHelper      #  <li  class='tab  [active]'>  

       #        <a  href='[href]'>[name]</a>      #  </li>      def  tab(name,  href)          classes  =  current_page?(href)  ?  'tab  active'  :  'tab'          content_tag('li',  class:  classes)  do              link_to_unless_current(name,  href)          end      end   end
  12. @betsythemuffin  <ul  class='tabs'>   -­‐    <li  class='tab<%=  current_page?(cats_path)  ?

     '  active'  :  ''  %>'>   -­‐        <%=  link_to_unless_current  'Cats',  cats_path  %>   -­‐    </li>   +    <%=  tab('Cats',  cats_path)  %>   -­‐    <li  class='tab<%=  current_page?(dogs_path)  ?  '  active'  :  ''  %>'>   -­‐        <%=  link_to_unless_current  'Dogs',  dogs_path  %>   -­‐    </li>   +    <%=  tab('Cats',  cats_path)  %>    </ul>
  13. @betsythemuffin  <ul  class='tabs'>        <%=  tab('Cats',  cats_path)  %>

           <%=  tab('Dogs',  dogs_path)  %>    </ul>
  14. @betsythemuffin  <ul  class='tabs  tabs-­‐left  span7  big-­‐text  red  maybe-­‐use-­‐cool-­‐font'>    

       <%=  tab('Cats',  cats_path)  %>        <%=  tab('Dogs',  dogs_path)  %>    </ul> what your codebase looks like, probably (no shame) (I've been there)
  15. @betsythemuffin "presentational markup" <ul  class='tabs  tabs-­‐left  span7  big-­‐text  red  maybe-­‐cool-­‐font'></ul>

      <ul  class='tabs  tabs-­‐-­‐primary'>                                                              </ul>
  16. @betsythemuffin quick (S)CSS refactors %span5  {      width:  48%;

      }   .nav-­‐-­‐primary  {      @extend  span5;   } http://krasimirtsonev.com/blog/article/SASS-­‐mixins-­‐extends-­‐and-­‐ placeholders-­‐differences-­‐use-­‐cases
  17. @betsythemuffin <ul  class='tabs  tabs-­‐left  span7  big-­‐text  red  maybe-­‐cool-­‐font'>    

     <%=  tab('Cats',  cats_path)  %>      <%=  tab('Dogs',  dogs_path)  %>   </ul> <%=  tab_set  do  %>      <%=  tab('Cats',  cats_path)  %>      <%=  tab('Dogs',  dogs_path)  %>   <%  end  %> plain HTML helper nesting
  18. @betsythemuffin <%=  tab_set  do  %>      <%=  tab('Cats',  cats_path)

     %>      <%=  tab('Dogs',  dogs_path)  %>   <%  end  %> class  LayoutHelper      #  <ul  class='tabs  tabs-­‐left  span7  big-­‐text  red'>      #      [block  contents]      #  </ul>      def  tab_set(&block)          css_classes  =  %w(tabs  tabs-­‐left  span7  big-­‐text  red)          content_tag('ul',  class:  css_classes.join('  '),  &block)      end   end what your view looks like build the tag yield control
  19. @betsythemuffin most things are too big for helpers def  card(options)

         #  set  options      #  main  code      card_classes  =  size  ?  "card  card-­‐-­‐#{size}"  :  'card'      content_tag('div',  class:  'card')  do          [              card_header(header_text),              (subheading.present?  ?  card_subheading(subheading)  :  nil),              image_tag(image_url),      #  ...   end such long many configuration much sub-helpers
  20. @betsythemuffin helper nesting again            <%=

     card(size:  'large')  do  %>                  <%=  card_header  'My  Awesome  Cat  Photo'  %>                  <%=  card_image    cat.showcase_image_url  %>                  <%=  card_badges  {                           favorite:  favorite_count                    }                  %>              <%  end  %> parent body
  21. @betsythemuffin ComponentBuilders in the view <%=  card(size:  'large')  do  |c|

     %>      <%=  c.header  'My  Awesome  Cat  Photo'  %>      <%=  c.image    cat.showcase_image_url  %>      <%=  c.badges  {               favorite:  favorite_count      }      %>   <%  end  %> like FormBuilders for … not forms
  22. @betsythemuffin Instead, use ComponentBuilders  def  card(options  =  {},  &block)  

    -­‐    content_tag('div',  class:  'card',  &block)       +    CardBuilder.new(self,  options      ).render(&block)    end build a thing yield control
  23. @betsythemuffin Instead, use ComponentBuilders def  card(options  =  {},  &block)  

       CardBuilder.new(self,  options).render(&block)   end View context
  24. @betsythemuffin class  CardBuilder      def  initialize(h,  options  =  {})

             @h  =  h          @size  =  options.fetch(:size,  'normal')      end      #  <div  class='card  card-­‐-­‐[size]'>      #      [block  contents]      #  </div>      def  render(&block)          h.content_tag('div',  class:  css_classes,  &block)      end      #  <h2  class='card__header'>[text]</h2>      def  header(text)          h.content_tag('h2',  text,  class:  'card__header')      end   end initialization render shell sub-renderers
  25. @betsythemuffin ComponentBuilder initialization class  CardBuilder      def  initialize(h,  options

     =  {})          @h  =  h          @size  =  options.fetch(:size,  'normal')      end   end grab the view context
  26. @betsythemuffin ComponentBuilder initialization class  CardBuilder      def  initialize(h,  options

     =  {})          @h  =  h          @size  =  options.fetch(:size,  'normal')      end   end configure top-level stuff
  27. @betsythemuffin class  CardBuilder      #  <div  class='card  card-­‐-­‐[size]'>  

       #      [block  contents]      #  </div>      def  render(&block)          h.content_tag('div',  class:  css_classes,  &block)      end   end build the outer framework yield to sub-renderers
  28. @betsythemuffin class  CardBuilder      #  <h2  class='card__header'>[text]</h2>    

     def  header(text)          h.content_tag('h2',  text,  class:  'card__header')      end   end
  29. @betsythemuffin ComponentBuilders in the view <%=  card(size:  'large')  do  |c|

     %>      <%=  c.header  'My  Awesome  Cat  Photo'  %>      <%=  c.image    cat.showcase_image_url  %>      <%=  c.badges  {               favorite:  favorite_count      }      %>   <%  end  %>
  30. @betsythemuffin comment yr code module  ComponentHelper      #  <li

     class='tab  [active]'>      #        <a  href='[href]'>[name]</a>      #  </li>      def  tab(name,  href)          classes  =  current_page?(href)  ?  'tab  active'  :  'tab'          content_tag('li',  class:  classes)  do              link_to_unless_current(name,  href)          end      end   end
  31. @betsythemuffin how hologram works /*doc   ```html_example   <div  class='card'>

         <h2  class='card__title'>          Lorem  Ipsum  Dolor  Sit  Amet      </h2>      <img  src="http://placekitten.com/210/100"  />      More  text  goes  here!   </div>   ```   */   .card  {      /*  css  */   }
  32. @betsythemuffin helper documentation /*doc   ```helper_example   <%=  card  do

     |c|  %>
    <%  c.title  "Lorem  Ipsum  Dolor  Sit  Amet"  %>
    <%  c.image  placekitten_url(width:  210,  height:   100)  %>
    <%  c.body  "More  text  goes  here!"  %>
 <%  end  %>   ```   */   .card  {      /*  css  */   } HTML output HTML.erb output
  33. @betsythemuffin or roll your own method - "identify" - understand

    existing UI concepts - "codify" - use abstraction to "bless" useful UI concepts - "document" - communicate with devs, designers, stakeholders
  34. ActBlue - builds fundraising tech for the left, amplifying the

    voices of small-dollar donors - 1.1 BILLION total raised since 2004 - committed to a modern, culturally and technically sustainable approach - now hiring UX, Rails, and DevOps
  35. @betsythemuffin class  CardBuilder      def  initialize(h,  options  =  {})

             @h  =  h          @size  =  options.fetch(:size,  'normal')      end      def  header(text:  nil,  &block)          @header  =  text  if  text          @header  =  h.capture(&block)  if  block_given?          @header      end      def  render(&block)     block.call(self)          h.render(              partial:  'components/card',              locals:  {                  header:  header              }          end      end   end initialization configuration methods rendering
  36. @betsythemuffin class  CardBuilder      def  header(text:  nil,  &block)  

           @header  =  text  if  text          @header  =  h.capture(&block)  if  block_given?          @header      end   end
  37. @betsythemuffin class  CardBuilder      def  render(&block)     block.call(self)

             h.render(              partial:  'components/card',              locals:  {                  header:  header              }          end      end   end invoke the configuration block render a partial
  38. @betsythemuffin <div  class='card<%=  size  ?  "  card-­‐-­‐#{size}"  :  ''  %>'>

         <h2  class='card__title'>          <%=  header  %>      </div>      <%=  image_tag  image  %>      <%  if  badges.any?  %>          <div  class='card__badges'>              <%  badges.each  do  |kind,  text|  %>                  <span  class='badge  badge-­‐-­‐<%=  kind  %>'><%=  text  %></span>              <%  end  %>          </div>      <%  end  %>   </div>
  39. @betsythemuffin <%=  card(size:  'large')  do  |c|  %>      <%=

     c.header  'My  Awesome  Knitting  Pattern'  %>      <%=  c.image    pattern.showcase_image_url  %>      <%=  c.badges  {               favorite:  favorite_count      }      %>   <%  end  %> NOT YOUR FRIENDS ANY MORE