Latest build: • View Source on GitHub • View Demo Store
Develop on top of a responsive, mobile-first and flexible foundation.
All the basic liquid and templating tags, well documented for you.
Add and remove components easily and build the store you want.
Timber is a front-end framework which makes building Shopify themes quick and easy. It can be used by theme creators of any skill level for themes of any scope. Seasoned pros and newbies alike can benefit from the starter templates, liquid markup, modules, and CSS frameworks provided in Timber.
Every template your store needs is included in one handy place, so no more sticky notes with checklists on your desk.
Jump past the learning curve of liquid template tags because we've provided you with the essentials.
Grounded in Sass, Timber provides you with a barebone set of styles that you can quickly put your designer touch on.
Timber leverages Shopify's translation engine to let any developer or merchant easily create their own theme language. Learn more about theme internationalization.
Collection sorting, Twitter Cards, Pinterest Rich Pins, and SEO-friendly Schema.org tags.
Download Timber.zip or clone the latest master branch from the GitHub repo:
git clone https://github.com/Shopify/Timber.git
Shopify Timber has been tested to work in IE 8+, Safari, Chrome, Firefox, Opera, Safari for iOS, Chrome for Android, and the stock Android browser.
The demo has fake products inserted so you can see what a populated site would look like. To see what your brand new site will look like when you install the theme, check out our empty store demo.
Once you've downloaded your theme it's time to get it onto your store. Either upload the zip to your-url.com/admin/themes
or use one of the programs below. Either way, you can use your text editor of choice.
If you're comfortable with the command line, use the Shopify Theme Gem on OS X or Windows to upload changes to your theme with ease.
If you prefer a beautiful interface, use the Desktop Theme Editor and start managing all of your theme assets.
You can sell your theme on the Shopify Theme Store by following our Guideline Checklist. You can also earn 20% of each customer’s bill that you refer to Shopify by enrolling in the Shopify Partner Program.
Timber is built with Sass, but you don't have to know it to use it. The compiling is all done on the Shopify server, so edit the .scss.liquid file just as you would CSS and watch the magic happen on your site.
You cannot use @imports
in a Sass liquid file. That means no dependence on Compass, Bourbon or similar Sass frameworks. A section labeled Sass Mixins has been included in timber.scss.liquid with a few helpers to start you off.
Define your breakpoints once, and Sass handles the rest. All breakpoints are defined in the Breakpoint and Grid Variables section of timber.scss.liquid. Easily change, add, or remove the breakpoints and names to suit your needs. These are the default breakpoints:
$breakpoints
is broken down into four lines, each a breakpoint: 'small', 'medium', 'medium-down', and 'large'. These are the names used in your classes to define each column width. A .grid__item
div with a class of large--one-half
will span 50% of its parent above 769px. Any screen size below that breakpoint will default to (mobile-first) 100%.
$viewportIncrement: 1px; $small: 480px; $medium: 768px; $large: 769px; $postSmall: $small + $viewportIncrement; $preMedium: $medium - $viewportIncrement; $preLarge: $large - $viewportIncrement; /* The following are dependencies of csswizardry grid */ $breakpoints: ( 'small' '(max-width: #{$small})', 'medium' '(min-width: #{$postSmall}) and (max-width: #{$medium})', 'medium-down' '(max-width: #{$medium})', 'large' '(min-width: #{$large})' );
This grid is based on csswizardry grids, though with some slight modifications. Built with Sass, the grid is fully integrated into your timber.scss.liquid stylesheet and has no external dependencies. Helper classes are generated by custom functions added to default grid Sass. Class names are created based on the $breakpoints
variable in timber.scss.liquid.
1
2.1
2.2
2.3
2.4
2.5
2.6
3.1
3.2
3.3
3.4
3.5
3.6
3.7
<div class="grid"> <div class="grid__item"> <p class="grid-demo">1</p> </div> <div class="grid__item"> <div class="grid"> <div class="grid__item large--one-half medium--one-third"> <p class="grid-demo">2.1</p> </div> <div class="grid__item large--one-quarter medium--one-third"> <p class="grid-demo">2.2</p> </div> <div class="grid__item large--one-quarter medium--one-third"> <p class="grid-demo">2.3</p> </div> <div class="grid__item large--one-third medium--one-whole"> <p class="grid-demo">2.4</p> </div> <div class="grid__item large--one-third medium--one-half small--one-half"> <p class="grid-demo">2.5</p> </div> <div class="grid__item large--one-third medium--one-half small--one-half"> <p class="grid-demo">2.6</p> </div> </div> // End sub grid </div> // End grid__item <div class="grid__item"> <div class="grid"> <div class="grid__item large--one-twelfth one-sixth small--one-third"> <p class="grid-demo">3.1</p> </div> <div class="grid__item large--one-twelfth one-sixth small--one-third"> <p class="grid-demo">3.2</p> </div> <div class="grid__item large--one-twelfth one-sixth small--one-third"> <p class="grid-demo">3.3</p> </div> <div class="grid__item large--one-twelfth one-sixth small--one-third"> <p class="grid-demo">3.4</p> </div> <div class="grid__item large--one-twelfth one-sixth small--one-third"> <p class="grid-demo">3.5</p> </div> <div class="grid__item large--one-twelfth one-sixth small--one-third"> <p class="grid-demo">3.6</p> </div> <div class="grid__item large--one-half one-whole"> <p class="grid-demo">3.7</p> </div> </div> // End sub grid </div> // End grid__item </div> // End grid
Each column — or .grid__item
— should be a direct child of a .grid
container. This grid is easily nestable. A .grid__item
column can contain another .grid
, which would consist of its own children. View the sub grid in Example 2 below.
1.1
1.2
1.1
1.2
1.3
1.4
// Two column grid <div class="grid"> <div class="grid__item one-half"> <p class="grid-demo">1.1</p> </div> <div class="grid__item one-half"> <p class="grid-demo">1.2</p> </div> </div> // Two column with sub grid <div class="grid"> <div class="grid__item large--one-half"> <p class="grid-demo">1.1</p> </div> <div class="grid__item large--one-half"> <div class="grid"> <div class="grid__item large--one-third"> <p class="grid-demo">1.2</p> </div> <div class="grid__item large--one-third"> <p class="grid-demo">1.3</p> </div> <div class="grid__item large--one-third"> <p class="grid-demo">1.4</p> </div> </div> </div> </div>
grid__item
will default to a 100% width, a moblie-first approachbreakpoint--column-width
- e.g. large--one-half
You can make use of no-margin grids with grid--full
.
Create custom alignments with push classes, push--large--one-third
.
1.1
1.1
1.1
2.1
2.2
// No margin grid <div class="grid--full"> <div class="grid__item large--one-third"> <p class="grid-demo">1.1</p> </div> <div class="grid__item large--one-third"> <p class="grid-demo">1.2</p> </div> <div class="grid__item large--one-third"> <p class="grid-demo">1.3</p> </div> </div> // Push classes <div class="grid"> <div class="grid__item large--one-half push--large--one-quarter"> <p class="grid-demo">2.1</p> </div> <div class="grid__item large--one-quarter push--large--one-quarter"> <p class="grid-demo">2.2</p> </div> </div>
If you don't know what silent classes are, you can ignore this section. The biggest point of contention for many grid systems is how many classes are required in your markup. With Sass, you can use silent classes to handle all .grid__item
widths in your stylesheet.
Set $use-silent-classes
to true in timber.scss.liquid, and use the following code to set the widths:
.foo { @extend %grid__item; @extend %small--one-half; @extend %large--one-quarter; }
Note: These helper classes use !important
to override any previous styling.
Each breakpoint you define as part of $breakpoints
in timber.scss will have helper classes with the same names generated. Let's work with our default breakpoints:
Small devices <480px |
Medium devices only >481px & <768px |
Medium and small devices ≤768px |
Large devices ≥769px |
|
---|---|---|---|---|
.small--show | Visible | |||
.medium--show | Visible | |||
.medium-down--show | Visible | Visible | Visible | |
.large--show | Visible | |||
.small--hide | Hidden | |||
.medium--hide | Hidden | |||
.medium-down--hide | Hidden | Hidden | Hidden | |
.large--hide | Hidden |
Basic helper classes to align an elements text.
.text-left
.text-right
.text-center
Change text alignment at any breakpoint with the following class format:
.breakpoint
+ --text-left
.breakpoint
+ --text-right
.breakpoint
+ --text-center
<div class="large--text-center medium--text-right small--text-center"> This will be center aligned on large devices, right aligned on medium-sized devices, and center aligned on small ones. </div>
.left
.right
.breakpoint
+ --left
.breakpoint
+ --right
<div class="large--right"> This will float right on large screens, and not at all at other breakpoints. </div>
Shopify liquid files don't support @import
. This means you can't rely on helpers like Compass or Bourbon. Instead, there is a section called Sass Mixins in timber.scss.liquid that provides a few of these helpers that you may be looking for.
Mixin based on: http://blog.grayghostvisuals.com/sass/sass-media-query-mixin/
Note: Standard media queries will obviously work as well if you'd rather not use the at-query
mixin.
Use this simple mixin to create media queries. Breakpoints are defined under Breakpoint and Grid Variables in timber.scss.liquid. This mixin can wrap class declarations (Example 1 below), or be placed within them (Example 2 below).
Make use of $min
or $max
variables for the constraint and the breakpoint variables defined under #Breakpoint and Grid Variables for the viewports.
$min: min-width; $max: max-width; @mixin at-query($constraint_, $viewport1_, $viewport2_:null) { ... }
Name | Type | Description |
---|---|---|
$constraint | String |
Options: min-width, max-width, or null |
$viewport1 | String |
If $constraint is set to max-width:
|
$viewport2 | String |
If set, this becomes the media query's max-width Default: null |
@include at-query($max, $small) { .foo { font-size: 0.8em; } } Output: @media screen and (max-width: 480px) { .foo { font-size: 0.8em; } }
The first parameter is optional when creating in-between queries. The two following snippets generate the same output.
.foo { @include at-query(null, $postSmall, $medium) { font-size: 0.8em; } } .foo { @include at-query($postSmall, $medium) { font-size: 0.8em; } } Output: @media screen and (min-width: 481px) and (max-width: 768px) { .foo { font-size: 0.8em; } }
Since we can't rely on Compass or Bourbon includes, there are a few mixins to get you started. Add any others you want to use under Sass Mixins in timber.scss.liquid.
clearfix
prefixer($property, $value, $prefixes)
- no defaultstransform($transform)
- no defaultuser-select($value)
- default nonebackface($visibility)
- default hiddenClearfix Input: .foo { @include clearfix(); } Output: .foo { *zoom: 1; // oldIE } .foo:after { content: ""; display: table; clear: both; } Prefix Input: .foo { @include prefixer(transform, scale(1), ms webkit spec); } Output: .foo { -ms-transform: scale(1); -webkit-transform: scale(1); transform: scale(1); } Transform Input: .foo { @include transform(translateX(50px)); } Output: .foo { -webkit-transform: translate(50px); -ms-transform: translate(50px); transform: translate(50px); } User-select Input: .foo { @include user-select(text); } Output: .foo { -webkit-user-select: text; -mox-user-select: text; -ms-user-select: text; user-select: text; } Backface Input: .foo { @include backface(); } Output: .foo { -webkit-backface-visibility: hidden; backface-visibility: hidden; }
Documentation - http://docs.shopify.com/themes/liquid-variables/paginate
Note: The paginate
liquid tag can limit how many articles are shown per page by wrapping the article loop with it.
Pagination will only work when you're trying to access an available object. The snippets below paginates through the blogs.news.articles object, so would only function properly on blog.liquid. View the documentation for other areas that can be paginated.
{% paginate blogs.news.articles by 5 %} <div class="pagination [pagination-custom]"> {{ paginate | default_pagination }} </div> {% endpaginate %}
Output:
{% paginate blogs.news.articles by 5 %} <div class="pagination"> {{ paginate | default_pagination | replace: '« Previous', '← Newer articles' | replace: 'Next »', 'Older articles →' }} </div> {% endpaginate %}
Output:
Documentation - https://docs.shopify.com/themes/liquid-documentation/objects/article
Get an article's tags with a simple for
loop. The example below also adds a comma after each tag, unless it's the last one. The code lives in snippet/tags-article.
Documentation - http://docs.shopify.com/support/your-website/navigation/creating-a-breadcrumb-navigation
The code lives in snippet/breadcrumb.
Documentation - http://docs.shopify.com/support/configuration/store-customization/where-do-i-get-my-mailchimp-form-action
MailChimp is the go-to newsletter service for Shopify. Use the link below to find your MailChimp form action and insert it in your site settings.
Using the Ajax Cart plugin allows a user to add a product to their cart without leaving the current page. The cart will appear as a drawer on the right side of the page.
The cart plugin is enabled by calling ajaxCart.init({})
after jQuery is loaded. Pass in the parameters below to customize it.
ajaxCart.init({ [options] })
Name | Type | Default | Description |
---|---|---|---|
formSelector |
Selector | form[action^="/cart/add"] |
Your add to cart form selector from the product page. |
addToCartSelector |
Selector |
input[type="submit'] (within formSelector) |
Your add to cart button selector from the product page. A class of |
cartContainer |
Selector | #CartContainer |
The template for your cart will be appended to an existing element on the page. By default, this element is in an off-screen drawer. |
cartCountSelector |
Selector | null |
Update the number of items in the cart. E.g. |
cartCostSelector |
Selector | null |
Update the total cart cost when products are added or removed. E.g. |
moneyFormat |
String | ${{amount}} |
Pass in your shop's currency with the liquid tag |
disableAjaxCart |
Boolean | false |
Set to true if you want to disable the cart but still use the JS quantity selectors (see below). |
enableQtySelectors |
Boolean | true |
Replace |
A custom event is fired when the cart has loaded in the DOM. To access it, set up a listener on the body element for afterCartLoad.ajaxCart
. Use this callback in external files or below the initialize JS. Multiple callback listeners is also valid.
jQuery('body').on('afterCartLoad.ajaxCart', function(evt, cart) { // Bind to 'afterCartLoad.ajaxCart' to run any javascript after the cart has loaded in the DOM // cart is the result of /GET cart.js (https://docs.shopify.com/support/your-website/themes/can-i-use-ajax-api#get-cart) // Open Timber's drawer when cart is ready timber.RightDrawer.open(); });
Events were added in v2.2.0. Usage is the same as the example above.
Disable all events with $(document.body).off('.ajaxCart')
.
Name | Parameters received | Description |
---|---|---|
beforeUpdateCartNote.ajaxCart |
evt , note |
Fired before the ajax post to update the cart's note |
afterUpdateCartNote.ajaxCart |
evt , note , cart |
Fired after the ajax post to update the cart's note |
errorUpdateCartNote.ajaxCart |
evt , XMLHttpRequest , textStatus |
Fired anytime there is an ajax error posting the cart note |
completeUpdateCartNote.ajaxCart |
evt , obj , jqxhr , text |
Fired after the request finishes |
beforeAddItem.ajaxCart |
evt |
Fired before an item is added to the cart from a form |
afterAddItem.ajaxCart |
evt , product , form |
Fired after an item is added to the cart from a form |
errorAddItem.ajaxCart |
evt , XMLHttpRequest , textStatus |
Fired anytime there is an ajax error adding a product to the cart |
completeAddItem.ajaxCart |
evt , obj , jqxhr , text |
Fired after the request finishes |
beforeGetCart.ajaxCart |
evt |
Fired before the cart is requested |
afterGetCart.ajaxCart |
evt , cart |
Fired after the cart is received. This runs regardless of a custom callback being sent to ShopifyAPI.getCart . |
beforeChangeItem.ajaxCart |
evt , line , quantity |
Fired before an item in the cart is changed |
afterChangeItem.ajaxCart |
evt , line , quantity , cart |
Fired after an item in the cart is changed |
errorChangeItem.ajaxCart |
evt , XMLHttpRequest , textStatus |
Fired anytime there is an ajax error changing a product item |
completeChangeItem.ajaxCart |
evt , obj , jqxhr , text |
Fired after the request finishes |
afterCartLoad.ajaxCart |
cart |
Fired after the cart is loaded. Similar to afterGetCart.ajaxCart , but in the Timber-specific callback. |
Events were added in v2.2.0. Usage is the same as the example above.
Disable all events with $(document.body).off('.timber')
.
Name | Parameters received | Description |
---|---|---|
beforeDrawerOpen.timber |
evt , drawer |
Fired before the drawer is opened |
afterDrawerOpen.timber |
evt , drawer
|
Fired after the drawer is opened |
beforeDrawerClose.timber |
evt , drawer
|
Fired before the drawer is closed |
afterDrawerClose.timber |
evt , drawer
|
Fired after the drawer is closed |
Required CSS is conditionally loaded in timber.scss.liquid, so you just have to include the JS. Place the following JS lines near the bottom of your theme.liquid layout file, after jQuery.
{{ 'handlebars.min.js' | asset_url | script_tag }} {% include 'ajax-cart-template' %} {{ 'ajax-cart.js' | asset_url | script_tag }}
<script> jQuery(function($) { ajaxCart.init({ formSelector: '#AddToCartForm', cartContainer: '#CartContainer', addToCartSelector: '#AddToCart', cartCountSelector: '#CartCount', cartCostSelector: '#CartCost', moneyFormat: "$ {{amount}}" }); }); jQuery('body').on('afterCartLoad.ajaxCart', function(evt, cart) { // Bind to 'afterCartLoad.ajaxCart' to run any javascript after the cart has loaded in the DOM timber.RightDrawer.open(); }); </script>
Mobile-friendly navigation and the ajax cart are displayed in drawers. All drawer JS is included in timber.js.liquid.
Drawers can be opened by clicking on elements with class js-drawer-open-left
or js-drawer-open-right
.
Drawers are initialized in a global JS variable timber.LeftDrawer
and timber.RightDrawer
. You can use timber.RightDrawer.open()
or timber.RightDrawer.close()
from external JS to show and hide the drawers.
Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper.
Suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat.
Eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum.
The author of the quote
No bullet
|
|
Round bullet
|
|
Square bullet
|
|
Inline List
|
|
Numeric bullet
|
|
Alpha bullet
|
|
To make long forms with block elements, add a class of .form-vertical
to the parent.
You cannot add a class directly to most liquid form elements (e.g. {% form 'contact' %}
). Instead, wrap the form in a div with the class .form-vertical
to achieve the same effect.
Use class input-full
Note: The placeholder attribute only works in IE10+, so invisible labels are disabled in IE9 and below.
Add a class of hidden-label
to a label to hide it, while keeping it accessible to screen readers. Use the placeholder
attribute as your visible label.
<form> <label for="testInvisibleLabel1" class="hidden-label">First Name</label> <input type="text" id="testInvisibleLabel1" name="testInvisibleLabel1" placeholder="First Name"> </form>
// Button on left <div class="input-group"> <span class="input-group-btn"> <input type="submit" class="btn" value="Go"> </span> <input type="text" name="q" value="" placeholder="Placeholder" class="input-group-field"> </div> // Button on right <form class="input-group"> <input type="email" value="" placeholder="Email address" class="input-group-field"> <span class="input-group-btn"> <input type="submit" class="btn--secondary" value="Subscribe"> </span> </form>
Timber offers a primary and secondary button to be used or customized as you need.
Use notes as helpers on forms, or brief messages to your customers.
This is an error message.
<div class="note"> This is a standard note </div> <div class="note form-success"> This is a success message </div> <div class="note form-error"> <p>This is an error message.</p> <ul class="disc"> <li>Bullets can offer more insight to the error</li> </ul> </div>
hr
hr.hr--clear
hr.hr--small
Timber offers a basic icon font for social and payment icons. Import assets/icons.json into IcoMoon to make any changes to the set. These icons are bulletproof, meaning they can have text fallbacks when @font-face
isn't supported.
<div class="icon-fallback-text"> <span class="icon icon-amazon_payments"></span> <span class="fallback-text">Amazon Payments</span> </div>