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

A Spree Themer's Toolkit

A Spree Themer's Toolkit

My slides from Spreeconf 2012

7e19cd5486b5d6dc1ef90e671ba52ae0?s=128

Wynn Netherland

February 16, 2012
Tweet

Transcript

  1. None
  2. Developer? Store owner? Both?

  3. ~ ⚡ whoami Wynn Netherland

  4. None
  5. None
  6. I’m

  7. 175 slides, 45 minutes

  8. LET'S DO THIS!

  9. Titles are the new bullets.

  10. 3.5 years ago...

  11. None
  12. None
  13. None
  14. Polyphonic

  15. having two or more independent but harmonically related melodic parts

    sounding together pol·y·phon·ic adjective:
  16. Are you listening to your store?

  17. Cha-ching!

  18. None
  19. Click-a-click-click-whiff!

  20. None
  21. Click-a-click-click-boom!

  22. None
  23. None
  24. Click-a-chug-a-chug-a

  25. Wasn't Jeff Casimir's talk great?

  26. Polymorphic

  27. occurring in or having many forms or shapes or appearances

    pol·y·morph·ic adjective:
  28. None
  29. None
  30. Why?

  31. your store != my store

  32. your business != my business

  33. Branding, trust.

  34. None
  35. None
  36. Geographic sensibilities

  37. None
  38. Methods of Spree customization

  39. Extensions

  40. Extensions refer to discrete packages of code that provide new

    functionality to a Spree application, while extensions may affect the appearance of a Spree application they generally only provide additional interfaces to enable users to interact with the new functionality provided.
  41. Extensions refer to discrete packages of code that provide new

    functionality to a Spree application, while extensions may affect the appearance of a Spree application they generally only provide additional interfaces to enable users to interact with the new functionality provided.
  42. Themes

  43. Themes are discrete packages of code that only affect the

    appearance of a Spree application (and generally only the front-end store). Themes do not generally include any logic customizations.
  44. Themes are discrete packages of code that only affect the

    appearance of a Spree application (and generally only the front-end store). Themes do not generally include any logic customizations.
  45. Powered by Rails Engines Wasn't Ryan Bigg's talk great?

  46. (Good conferences aren't DRY)

  47. The Asset Pipeline

  48. We rename Und now vee dance.

  49. None
  50. precompile is your friend

  51. bundle exec rake assets:precompile RAILS_ENV=development

  52. drwxr-xr-x 17 wynn staff 578 Nov 2 08:36 . drwxr-xr-x

    8 wynn staff 272 Nov 2 08:35 .. drwxr-xr-x 16 wynn staff 544 Nov 2 08:36 admin -rw-r--r-- 1 wynn staff 155 Nov 2 08:30 application.css -rw-r--r-- 1 wynn staff 143 Nov 2 08:30 application.css.gz drwxr-xr-x 7 wynn staff 238 Nov 2 08:36 creditcards drwxr-xr-x 3 wynn staff 102 Nov 2 08:36 datepicker -rw-r--r-- 1 wynn staff 1150 Nov 2 08:19 favicon.ico drwxr-xr-x 6 wynn staff 204 Nov 2 08:36 icons drwxr-xr-x 4 wynn staff 136 Nov 2 08:36 jqPlot drwxr-xr-x 15 wynn staff 510 Nov 2 08:36 jquery-ui drwxr-xr-x 3 wynn staff 102 Nov 2 08:35 jquery.alerts drwxr-xr-x 3 wynn staff 102 Nov 2 08:35 jquery.jstree -rw-r--r-- 1 wynn staff 6694 Nov 2 08:36 manifest.yml drwxr-xr-x 5 wynn staff 170 Nov 2 08:36 noimage -rw-r--r-- 1 wynn staff 1608 Nov 2 08:19 spinner.gif drwxr-xr-x 6 wynn staff 204 Nov 2 08:36 store
  53. active_reload is a must Included in Rails 3.2

  54. None
  55. None
  56. Anatomy of a theme

  57. spree_rdr_theme [ master: ✔ ] 6d ⚡ tree -d

  58. spree_rdr_theme [ master: ✔ ] 6d ⚡ tree -d .

    ├── app │ ├── assets │ │ ├── images │ │ │ └── store │ │ │ └── icons │ │ ├── javascripts │ │ │ └── store │ │ └── stylesheets │ │ └── store │ ├── overrides │ └── views │ └── spree │ ├── shared │ └── wishlists ├── lib │ └── generators │ └── spree_rdr_theme │ └── install └── vendor └── assets ├── images │ └── fancybox ├── javascripts └── stylesheets
  59. spree_rdr_theme [ master: ✔ ] 6d ⚡ tree -d .

    ├── app │ ├── assets │ │ ├── images │ │ │ └── store │ │ │ └── icons │ │ ├── javascripts │ │ │ └── store │ │ └── stylesheets │ │ └── store │ ├── overrides │ └── views │ └── spree │ ├── shared │ └── wishlists ├── lib │ └── generators │ └── spree_rdr_theme │ └── install └── vendor └── assets ├── images │ └── fancybox ├── javascripts └── stylesheets Your stuff
  60. spree_rdr_theme [ master: ✔ ] 6d ⚡ tree -d .

    ├── app │ ├── assets │ │ ├── images │ │ │ └── store │ │ │ └── icons │ │ ├── javascripts │ │ │ └── store │ │ └── stylesheets │ │ └── store │ ├── overrides │ └── views │ └── spree │ ├── shared │ └── wishlists ├── lib │ └── generators │ └── spree_rdr_theme │ └── install └── vendor └── assets ├── images │ └── fancybox ├── javascripts └── stylesheets Your Stylesheets, images, JavaScripts
  61. spree_rdr_theme [ master: ✔ ] 6d ⚡ tree -d .

    ├── app │ ├── assets │ │ ├── images │ │ │ └── store │ │ │ └── icons │ │ ├── javascripts │ │ │ └── store │ │ └── stylesheets │ │ └── store │ ├── overrides │ └── views │ └── spree │ ├── shared │ └── wishlists ├── lib │ └── generators │ └── spree_rdr_theme │ └── install └── vendor └── assets ├── images │ └── fancybox ├── javascripts └── stylesheets Your Deface overrides
  62. Deface Wasn't Brian Quinn's talk great?

  63. Deface::Override.new(:virtual_path => "posts/_form", :name => "example-1", :replace => "h1", :text

    => "<h1>New Post</h1>") Deface::Override.new(:virtual_path => "posts/_form", :name => "example-1", :replace => "h1") do "<h1>New Post</h1>" end In a block
  64. spree_rdr_theme [ master: ✔ ] 6d ⚡ tree -d .

    ├── app │ ├── assets │ │ ├── images │ │ │ └── store │ │ │ └── icons │ │ ├── javascripts │ │ │ └── store │ │ └── stylesheets │ │ └── store │ ├── overrides │ └── views │ └── spree │ ├── shared │ └── wishlists ├── lib │ └── generators │ └── spree_rdr_theme │ └── install └── vendor └── assets ├── images │ └── fancybox ├── javascripts └── stylesheets Your custom views
  65. spree_rdr_theme [ master: ✔ ] 6d ⚡ tree -d .

    ├── app │ ├── assets │ │ ├── images │ │ │ └── store │ │ │ └── icons │ │ ├── javascripts │ │ │ └── store │ │ └── stylesheets │ │ └── store │ ├── overrides │ └── views │ └── spree │ ├── shared │ └── wishlists ├── lib │ └── generators │ └── spree_rdr_theme │ └── install └── vendor └── assets ├── images │ └── fancybox ├── javascripts └── stylesheets Third party libraries and frameworks
  66. Polyglotnic Ok, so I made that one up

  67. A person having a speaking, reading, or writing knowledge of

    several languages pol·y·glot adjective:
  68. CSS3

  69. border-radius

  70. border-image

  71. www.zurb.com/playground/awesome-overlays

  72. background-size

  73. gradients

  74. RGBA, HSL, HSLA colors rgba (0,0,0,1) is the new black!

  75. text-shadow

  76. box-shadow

  77. word wrap

  78. outline

  79. columns

  80. @font-face Typographic freedom! means

  81. New selectors Cool

  82. * E .class #id E F E > F E

    + F E[attribute] E[attribute=value] E[attribute~=value] E[attribute|=value] :first-child :link :visited :lang() :before ::before :after ::after :first-letter ::first-letter :first-line ::first-line E[attribute^=value] E[attribute$=value] E[attribute*=value] E ~ F :root :last-child :only-child :nth-child() :nth-last-child() :first-of-type :last-of-type :only-of-type :nth-of-type() :nth-last-of-type() :empty :not() :target :enabled :disabled :checked CSS3 selectors (and some golden oldies) :header Steal this from jQuery, please
  83. CSS Frameworks

  84. None
  85. None
  86. Percentage-based grid, screenshot in Firefox 4.0 http://host.sonspring.com/yui3_grid

  87. None
  88. None
  89. None
  90. Sass

  91. Nested rules

  92. table.users tr td {background: #111} table.users tr td a {color:

    #333} Nested rules - selectors
  93. table.users { tr { td { background: #d1d1d; a {

    color: #111; } } } } Nested rules - selectors
  94. table.users { tr { td { color: #111; &.alt {

    color: #333; } &:hover { color: #666; } } } } Nested rules - selectors
  95. .users { font: { size: 1.2em; style: italics; weight: bold;

    } } Nested rules - properties
  96. Syntax options

  97. table.users { tr { td { background: #d1d1d; a {

    color: #111; } } } } Syntax options - SCSS Sassy CSS!
  98. table.users tr td background: #d1d1d a color: #111 Syntax options

    - Indented I — whitespace
  99. Variables

  100. .user { background: #333; border-size: 2px; } .owner { background:

    #333; border-size: 2px; } .admin { background: #666; border-size: 4px; } Variables
  101. $gray: #333; $default_border: 2px; .user { background: $gray; border-size: $default_border;}

    .owner { background: $gray; border-size: $default_border;} .admin { background: $gray + #333; border-size: $default_border + 2px; } Variables Even math! and color mixing!
  102. Mixins

  103. .avatar { padding: 2px; border: solid 1px #ddd; position: absolute;

    top: 5px; left: 5px; } p img { padding: 2px; border: solid 1px #ddd; } Mixins
  104. @mixin frame($padding_width: 2px, $border_color: #ddd) { padding: $padding_width; border: {

    width: 1px; style: solid; color $border_color; } } .avatar { @include frame; position: absolute; top: 5px; left: 5px; } p img { @include frame(1px, #ccc);} Mixins defines the mixin mixes in the rules
  105. Extend

  106. .flash { padding: 5px; border-width: 1px; font-weight: bold; } .error

    { color: #8a1f11; background: #fbe3e4; } .notice { color: #514721; background: #fff6bf; } Mixins
  107. .flash { padding: 5px; border-width: 1px; font-weight: bold; } .error

    { @extend .flash; color: #8a1f11; background: #fbe3e4; } .notice { @extend .flash; color: #514721; background: #fff6bf; } Mixins
  108. .flash, .error, .notice { padding: 5px; border-width: 1px; font-weight: bold;

    } .error { color: #8a1f11; background: #fbe3e4; } .notice { color: #514721; background: #fff6bf; } Mixins now we can use a single class in our markup
  109. Imports

  110. @import url(/css/reset.css) @import url(/css/typography.css) @import url(/css/layout.css) Imports parent + 3

    @imports = 4 HTTP requests
  111. @import "reset.scss" # _reset.scss @import "typography" # _typography.scss @import "layout.css"

    # url(layout.css) Imports Included at compile time - just one http request
  112. Imports + Mixins Now it gets fun!

  113. A brief detour

  114. Vendor prefixes and the growing -webkit monoculture

  115. Every Time You Call a Proprietary Feature “CSS3,” a Kitten

    Dies by L E A V E R O U
  116. Prefix or Posthack by E R I C M E

    Y E R
  117. “It’s time to abolish all vendor prefixes. They’ve become solutions

    for which there is no problem, and they are actively harming web standards.” — Peter-Paul Koch aka @ppk http://www.quirksmode.org/blog/archives/2010/03/css_vendor_pref.html
  118. Ummm. Not so fast.

  119. @import "compass/css3.scss"; .callout { @include border-radius(5px); @include linear-gradient("left top", "left

    bottom", #fff, #ddd); } .callout { -moz-border-radius: 5px; -webkit-border-radius: 5px; -border-radius: 5px; background-image: -moz-linear-gradient(top, bottom, from(#fff), to(#ddd)); background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0.00, #fff), color-stop(1.00, #ddd)); } Compass CSS3 mixins very different syntax
  120. “Well, reactions to my proposal to abolish vendor prefixes are

    mixed, and I might have overshot my target here.” -Peter-Paul Koch aka @ppk http://www.quirksmode.org/blog/archives/2010/03/css_vendor_pref_1.html
  121. Solutions?

  122. beta-new-css8-property-dilly isn't that just like not having it? ...and we're

    back.
  123. Vendor specific stylesheets Maybe. But I'd like to retain Internet

    Explorer's special status unto itself
  124. CSS preprocesors Hey, funny you should ask!

  125. Bring your favorite CSS Framework

  126. <div id= "wrapper" class="container"> <div id="content" class="span-7 prepend-1"> <div id="main">

    ... </div> <div id="featured" class="span-5 last"> ... </div> </div> <div id="sidebar" class="span-3 append-1 last"> ... </div> </div> </div> A Blueprint example boo? / Yay?
  127. #wrapper { @include container; #content { @include column(7); @include append(1);

    #featured { @include column(5, true); } } #sidebar { @include column(3, true); @include prepend(1); } } A Blueprint example
  128. Look, Ma! No math!

  129. Functions

  130. Very. Powerful. Functions.

  131. hue(#cc3) => 60deg saturation(#cc3) => 60% lightness(#cc3) => 50% adjust-hue(#cc3,

    20deg) => #9c3 saturate(#cc3, 10%) => #d9d926 desaturate(#cc3, 10%) => #bfbf40 lighten(#cc3, 10%) => #d6d65c darken(#cc3, 10%) => #a3a329 grayscale(#cc3) => desaturate(#cc3, 100%) = #808080 complement(#cc3) => adjust-hue(#cc3, 180deg) = #33c mix(#cc3, #00f) => #e56619 mix(#cc3, #00f, 10%) => #f91405 mix(#cc3, #00f, 90%) => #d1b72d Sass 2.4 color functions http://nex-3.com/posts/89-powerful-color-manipulation-with-sass
  132. mix(rgba(51, 255, 51, 0.75), #f00) => rgba(178, 95, 19, 0.875)

    mix(rgba(51, 255, 51, 0.90), #f00) => rgba(163, 114, 22, 0.95) alpha(rgba(51, 255, 51, 0.75)) => 0.75 opacity(rgba(51, 255, 51, 0.75)) => 0.75 opacify(rgba(51, 255, 51, 0.75), 0.1) => rgba(51, 255, 51, 0.85) fade-in(rgba(51, 255, 51, 0.75), 0.1) => rgba(51, 255, 51, 0.85) transparentize(rgba(51, 255, 51, 0.75), 0.1) => rgba(51, 255, 51, 0.65) fade-out(rgba(51, 255, 51, 0.75), 0.1) => rgba(51, 255, 51, 0.65) Sass color functions with alpha support! http://nex-3.com/posts/89-powerful-color-manipulation-with-sass
  133. None
  134. Sass and Compass CSS extensions & compiler Patterns & plugins

  135. ...like peas and carrots.

  136. A quick survey of Compass

  137. CSS3

  138. CSS3 ★ Appearance ★ Background Clip ★ Background Origin ★

    Background Size ★ Border Radius ★ Box ★ Box Shadow ★ Box Sizing ★ CSS3 Pie ★ Columns ★ Font Face ★ Gradient ★ Images ★ Inline Block ★ Opacity ★ Shared Utilities ★ Text Shadow ★ Transform ★ Transform (legacy) ★ Transition
  139. Image sprites

  140. @import "icon/*.png" .actions .new +icon-sprite(new) .edit +icon-sprite(edit) .save +icon-sprite(save) .delete

    +icon-sprite(delete) Image sprites .icon-sprite, .actions .new, .actions .edit, .actions .save, .actions .delete { background: url('/images/icon-34fe0604ab.png') no-repeat; } .actions .new { background-position: 0 -64px; } .actions .edit { background-position: 0 -32px; } .actions .save { background-position: 0 -96px; } .actions .delete { background-position: 0 0; } @import "icon/*.png" public/images/icon/new.png public/images/icon/edit.png public/images/icon/save.png public/images/icon/delete.png 1. 2. 3. I like the Sprite in you®
  141. URL helpers

  142. #nav background: image-url("nav_bg.png") repeat-x top center DEVELOPMENT #nav { background:

    url("/images/nav_bg.png") repeat-x top center; } PRODUCTION #nav { background: url("http://assets.example.com/images/nav_bg.png") repeat-x top center; } URL helpers relative paths for development, absolute for production
  143. stylesheet-url($path) font-url($path) image-url($path) URL helpers

  144. Share your patterns

  145. http://brandonmathis.com/projects/fancy-buttons/

  146. @import "fancy-buttons" button, a.button +fancy-button(#2966a8)

  147. compass-960

  148. $ninesixty-columns: 16 #wrap +grid-container #left-nav +alpha +grid(5) #main-content +grid-prefix(1) +grid(10)

    +omega Compass 960 https://github.com/chriseppstein/compass-960-plugin
  149. compass-wordpress

  150. $ gem install compass-wordpress CRANK OUT A NEW SASS-Y WORDPRESS

    THEME $ compass -r compass-wordpress \ -f wordpress -p thematic \ --sass-dir=sass --css-dir=css \ -s compressed my_awesome_theme AUTOCOMPILE YOUR CHANGES $ compass --watch Compass and WordPress shameless plug
  151. compass-formalize

  152. None
  153. Isn’t she Sassy, folks? GET THE BOOK.

  154. sass40 Save 40% and get early access! Sadly, sass100 is

    not a valid code.
  155. Haml

  156. <%= "boo" %>

  157. #profile .left.column #date= print_date #address= current_user.address .right.column #email= current_user.email #bio=

    current_user.bio HAML <div id="profile"> <div class="left column"> <div id="date"><%= print_date %></div> <div id="address"><%= current_user.address %></ div> </div> <div class="right column"> <div id="email"><%= current_user.email %></div> <div id="bio"><%= current_user.bio %></div> </div> </div> ERB
  158. Think and work in CSS selectors

  159. Never chase that </div> again

  160. COFFEESCRIPT

  161. It's still just JavaScript.

  162. Compiles right in the AP

  163. charity [ master: ✔ ] 1d ⚡ tree app/assets/ app/assets/javascripts/

    ├── application.js.coffee ├── ie8.js.coffee ├── media.js.coffee ├── mobile.js.coffee ├── pc │ ├── _boot.js.coffee │ ├── models │ │ ├── campaign.js.coffee │ │ ├── campaigns.js.coffee │ │ ├── project.js.coffee │ │ ├── projects.js.coffee │ │ └── scoreboard.js.coffee │ ├── routers │ │ └── discover.js.coffee │ └── views │ ├── calc.js.coffee │ ├── cropper.js.coffee │ ├── data_table.js.coffee │ ├── discover │ │ ├── app.js.coffee │ │ ├── campaigns_panel.js.coffee │ │ ├── map.js.coffee
  164. var foo = function () { } foo = ()

    -> I’d rather write this. JAVASCRIPT COFFEESCRIPT
  165. var button = Titanium.UI.createButton({ title: 'I am a Button', height:

    40, width: 200, top: 10 }); button.addEventListener('click', function(e) { alert("Oooh, that tickles!"); }); JAVASCRIPT
  166. button = Titanium.UI.createButton title: 'I am a Button' height: 40

    width: 200 top: 10 button.addEventListener 'click', (e) -> alert "Oooh, that tickles!" COFFEESCRIPT
  167. It's about more than aesthetics ;{ } For me,

  168. Some of my favorite features...

  169. for own key, value of query uri += "#{ key

    }=#{ escape(value) }&" COFFEESCRIPT var key, value; var __hasProp = Object.prototype.hasOwnProperty; for (key in query) { if (!__hasProp.call(query, key)) continue; value = query[key]; uri += "" + key + "=" + (escape(value)) + "&"; } JAVASCRIPT
  170. for own key, value of query uri += "#{ key

    }=#{ escape(value) }&" COFFEESCRIPT Comprehensions
  171. for own key, value of query uri += "#{ key

    }=#{ escape(value) }&" COFFEESCRIPT Interpolation
  172. courseButtonSubhead = Ti.UI.createLabel className: 'optRowSubhead' text: "#{GolfStatus.App.currentGame?.green?.name}" COFFEESCRIPT The Existential

    Operator
  173. class GolfStatus.Models.Game constructor: (@owner, @course, @playingFor='brag', @scoringFormat='low_net') -> @players =

    {} @addPlayer @owner if @owner @green = @course.greens[0] if @course @currentHole = 1 @maxHolePlayed = 1 # elsewhere game = new GolfStatus.Models.Game(...) COFFEESCRIPT Simple inheritance pattern
  174. class GolfStatus.Models.Game constructor: (@owner, @course, @playingFor='brag', @scoringFormat='low_net') -> @players =

    {} @addPlayer @owner if @owner @green = @course.greens[0] if @course @currentHole = 1 @maxHolePlayed = 1 COFFEESCRIPT @
  175. class GolfStatus.Models.Game constructor: (@owner, @course, @playingFor='brag', @scoringFormat='low_net') -> @players =

    {} @addPlayer @owner if @owner @green = @course.greens[0] if @course @currentHole = 1 @maxHolePlayed = 1 COFFEESCRIPT Default values
  176. class GolfStatus.Models.Game constructor: (@owner, @course, @playingFor='brag', @scoringFormat='low_net') -> @players =

    {} @addPlayer @owner if @owner @green = @course.greens[0] if @course @currentHole = 1 @maxHolePlayed = 1 COFFEESCRIPT More human conditionals
  177. GolfStatus.Models.Game::PlayingForTypes = brag: 'Bragging Rights' cash: 'Status Cash' GolfStatus.Models.Game::ScoringFormats =

    low_net: 'Low Net' low_grows: 'Low Gross' COFFEESCRIPT Easy object.prototype
  178. noticeHTML =''' <html> <head></head> <body> ''' noticeHTML += "<div>#{noticeText}</div>" noticeHTML

    += ''' </body> </html> ''' COFFEESCRIPT Heredocs
  179. Because string building sucks.

  180. And so much more.

  181. http://wynn.fm/2Y

  182. GET THE BOOK. http://wynn.fm/g8

  183. JavaScript Frameworks

  184. https://github.com/documentcloud/underscore From Jeremy Ashkenas, the creator of CoffeeScript. https://github.com/documentcloud/backbone

  185. QUESTIONS? http://spkr8.com/t/9283