`, `` or ` ` elements), or from associated content (e.g. an ` ` and it's `` element), so there are many cases you won't need to reach for `aria-labelledby`
371 | * Ensure that the element referenced by `aria-labelledby` exists on the page
372 | * If the referenced element is missing, screen readers will still read out the the other text content contained in the element, but users may be missing out on the additional context that the referenced element might be adding
373 | * When using `aria-labelledby` in a component, consider whether the element you are referencing exists inside the component's template code
374 | * If the element you are targeting with `aria-labelledby` is stored in another component or snippet, you may not be able to guarantee that it will always be on the page
375 | * In this case, opt to use `` or `aria-label` instead
376 | * There are some caveats to be aware of when using `aria-labelledby` on `` elements - See [Ensure correct usage of `aria-labelledby` on `` elements](#ensure-correct-usage-of-aria-labelledby-on-section-elements)
377 | * Additional documentation available at [MDN - aria-labelledby](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-labelledby)
378 |
379 | #### `aria-label`
380 | * `aria-label` is another approach for labelling elements, and can be used when there is no visible element in the DOM that can be used directly as a label or referenced with `aria-labelledby`
381 | * **Use `aria-label` with caution** - if adding `visually-hidden` text is possible, this is preferred instead
382 | * `aria-label` has a rocky history with screen reader support with certain combinations of screen readers and browsers, whereas printed text is always read out (unless `display: none` or `visibility: hidden` styles or the `hidden` or `aria-hidden` attributes are used)
383 | * Using `visually-hidden` text that is rendered in the DOM can also benefit users users that are translating the language of your site as these services are often better at detecting `visually-hidden` text then they are `aria-label` attributes
384 | * See [this blog post from 2020](https://gomakethings.com/revisting-aria-label-versus-a-visually-hidden-class/)
385 | * Examples of using this are similar to adding a `` element
386 |
387 | ```vue
388 |
393 |
394 |
395 |
396 |
400 |
401 |
402 | ```
403 |
404 | ### Ensure correct usage of `aria-labelledby` on `` elements
405 |
406 | * When a `` element is given an accessible name, for example with the `aria-labelledby` or `aria-label` attribute, the element receives an implicit ARIA attribute [`role="region"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/region_role) which marks it as a [landmark](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles#3._landmark_roles) for screen readers and other assistive technologies
407 | * These landmarks can then be used as another method for screen reader users to navigate around the page and understand its contents
408 | * **However, creating additional landmarks with the `` element must only be done when necessary**
409 | * Adding too many landmark roles on the page can create "noise" for screen readers and actually make the site harder to understand and use, so must be used sparingly
410 | * For Canvas projects, `aria-labelledby` should only be used on `` elements that are used as the main section or main component for a page template, and not used on reusable 'sections everywhere' sections or components
411 | * See [Main components](https://we-make-websites.gitbook.io/canvas/components/other-types/main-components) for help identifying what the main component of a template should be
412 | * The standard for main components in Canvas is that they have a `` element containing relevant text, with the `` element given the `aria-labelledby="template-title"` attribute
413 | * When replacing or updating one of the default Canvas main components, ensure that you still have an element with `id="template-title"` for the `aria-labelledby` attribute to reference
414 |
415 |
416 | [ꜛ Back to TOC](#table-of-contents)
417 |
418 | ## Alt Text
419 | * Alt text is extremely useful for ensuring that images on the site can be used by people with various disabilities
420 | * Images can be categorised in different ways and there are some different recommendations for alt text depending on the type of image
421 | * The W3C Web Accessibility Initiative has some good resources around Images:
422 | * [Images Tutorial](https://www.w3.org/WAI/tutorials/images/) - Outlines the different purposes an image might have and recommendations for adding accessible text
423 | * [`alt` Decision Tree](https://www.w3.org/WAI/tutorials/images/decision-tree/) - Provides a helpful decision tree on how to handle different situations with images, including when an empty `alt` attribute is appropriate
424 |
425 | [ꜛ Back to TOC](#table-of-contents)
426 |
427 | ## Element IDs
428 |
429 | * A variety of accessibility code and functionality (such as the `label` element's `for` attribute, `aria-labelledby` and other `aria-` attributes) use the `id` attribute to reference other elements
430 | * For example, linking a checkbox input with a label using `id`/`for` allows the user to click on the label to toggle the checkbox
431 | * This helps make the input easier to trigger with a mouse, and also makes it possible to style custom inputs (for example custom checkboxes) by applying styles to the `` element
432 | * When writing components that use the `id` attribute - ensure that the `id` will always be unique
433 | * Avoid hard coding an `id` for an element unless you know it will only be used once
434 | * This is particularly important when the component is designed to be re-used, for example a swatch or a line item component
435 | * Unique IDs can be generated using passed in data, and additionally namespaces if needed
436 | * For section type Canvas components - you could also pass the Shopify `section.id` as a prop and use that in your element `id`
437 |
438 | ### Example of a dynamic `id` based on unique prop data
439 |
440 | ```vue
441 |
442 |
443 |
447 | Quantity
448 |
449 |
450 |
455 |
456 |
457 |
458 |
471 | ```
472 |
473 | ### Example of setting an explicit unique `id` with a `namespace` prop
474 |
475 | ```vue
476 |
477 |
478 |
482 | Quantity
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
508 | ```
509 |
510 | ### Usage
511 |
512 | ```vue
513 |
514 |
518 |
519 |
520 |
524 | ```
525 |
526 | * Use unique data from the component's props, or add a `namespace` prop, or use a combination of both to ensure that the `id` is always unique
527 | * When using a `namespace` prop, make the prop `required` to ensure that its not missed
528 | * Utilise the browser tools and Storybook Accessibility checks that detect if an `id` is not unique
529 | * Consider your loading states as well as your standard/active states
530 | * Often when a component is in a loading state it might not have access prop data that could be used for a unique `id` - For example using a `product.id` in an element's `id`, but the product is still being fetched and not yet set
531 | * Consider testing your loading states to check for non-unique IDs
532 |
533 | [ꜛ Back to TOC](#table-of-contents)
534 |
535 | ## Additional Resources
536 | * https://www.powermapper.com/tests/screen-readers/aria/index.html
537 | * Outlines how different ARIA attributes are handled by different screen-readers
538 | * https://webaim.org/techniques/keyboard/
539 | * An outline of keyboard accessibility best-practices
540 | * Also includes a table of some of the most common keyboard interactions to consider when testing a site's keyboard accessibility
541 |
542 | [ꜛ Back to TOC](#table-of-contents)
543 |
--------------------------------------------------------------------------------
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/we-make-websites/wmw-coding-guidelines/a0d95d7af2bd00d072c216126931b8641df97036/assets/logo.png
--------------------------------------------------------------------------------
/css/README.md:
--------------------------------------------------------------------------------
1 | # CSS/SCSS Guidelines
2 |
3 | ## Table of contents
4 |
5 | * [General](#general)
6 | * [Methods](#methods)
7 | * [Declarations](#declarations)
8 | * [Naming](#naming)
9 | * [Formatting](#formatting)
10 | * [Colours](#colours)
11 | * [Nesting](#nesting)
12 | * [Properties](#properties)
13 |
14 | [← Back to homepage](../README.md)
15 |
16 | ## General
17 |
18 | * [DRY (Don't Repeat Yourself)](#dry-dont-repeat-yourself)
19 | * [Overriding stylelint](#overriding-stylelint)
20 |
21 | ### DRY (Don't Repeat Yourself)
22 |
23 | #### Don't
24 |
25 | ```scss
26 | .foo {
27 | background-color: var(--color-neutral-3);
28 | border: 1px solid var(--color-neutral-1);
29 | color: var(--color-neutral-2);
30 | }
31 |
32 | .bar {
33 | background-color: var(--color-neutral-3);
34 | border: 1px solid var(--color-neutral-1);
35 | color: var(--color-neutral-2);
36 | }
37 | ```
38 |
39 | #### Do
40 |
41 | ```scss
42 | .btn {
43 | background-color: var(--color-neutral-3);
44 | border: 1px solid var(--color-neutral-1);
45 | color: var(--color-neutral-2);
46 | }
47 | ```
48 |
49 | * Keep things DRY, aim to use standard classes so you can re-use your code
50 | * SCSS `@include` and `@extend` can encourage poor coding practice, just because it's one line in your code doesn't mean it is when compiled
51 | * If you're applying the same include or styles on lots of selectors, create a class and apply it in HTML to keep things DRY
52 | * Make sure you inspect elements in Figma to find their corresponding classes and CSS variables to avoid repeating them
53 |
54 | #### Exceptions
55 |
56 | * Sometimes it's still better to KISS (Keep It Simple, Stupid) than DRY
57 |
58 | ### Overriding stylelint
59 |
60 | #### Don't
61 |
62 | ```scss
63 | .foo {
64 | // stylelint-disable-next-line
65 | background-color: var(--color-neutral-1) !important;
66 | margin: 0;
67 | // stylelint-disable-next-line
68 | position: absolute !important;
69 | }
70 |
71 | // stylelint-disable-next-line
72 | .barClass {
73 | color: var(--color-neutral-6)
74 | }
75 | ```
76 |
77 | #### Do
78 |
79 | ```scss
80 | // stylelint-disable declaration-no-important
81 |
82 | .foo {
83 | background-color: var(--color-neutral-1) !important;
84 | margin-block: 0;
85 | margin-inline: 0;
86 | position: absolute !important;
87 | }
88 |
89 | // Custom class from app
90 | // stylelint-disable-next-line selector-class-pattern
91 | .barClass {
92 | color: var(--color-neutral-6)
93 | }
94 | ```
95 |
96 | * Sometimes it's necessary to override stylelint, usually due to app code
97 | * When this happens do not use `stylelint-disable` to disable all linting in the whole file
98 | * Instead use `stylelint-disable-next-line [rule]` to disable just that rule on the next line, then add a preceding comment explaining why it's necessary
99 | * If you find yourself disabling the same rule frequently in a file then add a comment at the top of the file disabling _just_ that rule using `stylelint-disable [rule]
100 | * Add the rule comment at the top of the file just after the intro comment with a newline between the rule comment and the first declaration block
101 | * Add the rule comments in alphabetical order
102 | * Stylelint will tell you which rule you're breaking when hovering over the error
103 | * Note that the stylelint rules listed under [deprecated](https://stylelint.io/user-guide/rules/#deprecated) are supported by prefixing the rule with `stylistic/`, e.g. `stylistic/color-hex-case` instead of just `color-hex-case` as these rules are set to be removed in stylelint 16
104 |
105 | [ꜛ Back to TOC](#table-of-contents)
106 |
107 | ## Methods
108 |
109 | * [Fullscreen elements](#fullscreen-elements)
110 | * [Magic numbers](#magic-numbers)
111 | * [Mobile first](#mobile-first)
112 | * [Responsive banner padding](#responsive-banner-padding)
113 | * [RTL](#rtl)
114 |
115 | ### Fullscreen elements
116 |
117 | #### Don't
118 |
119 | ```scss
120 | .foo {
121 | bottom: 0;
122 | left: 0;
123 | position: absolute;
124 | right: 0;
125 | top: 0;
126 | }
127 | ```
128 |
129 | #### Do
130 |
131 | ```scss
132 | .foo {
133 | height: 100%;
134 | left: 0;
135 | position: absolute;
136 | top: 0;
137 | width: 100%;
138 | }
139 | ```
140 |
141 | * Don't define all four positioning properties to set an element to fullscreen as these do not work on all elements
142 | * Instead use `height: 100%` and `width: 100%`
143 |
144 | ### Magic numbers
145 |
146 | #### Don't
147 |
148 | ```scss
149 | .foo {
150 | left: 23px;
151 | }
152 | ```
153 |
154 | #### Do
155 |
156 | ```scss
157 | .foo {
158 | // 10px because of font height
159 | left: calc(var(--layout-gutter) - 10px - (var(--nav-height) / 2));
160 | }
161 | ```
162 |
163 | #### Or if you have to
164 | ```scss
165 | .foo {
166 | // This values matches the sum of widths above it
167 | left: 23px;
168 | }
169 | ```
170 |
171 | * Avoid magic numbers, these are numbers that 'just work' but are set arbitrarily
172 | * Try and use calculations to make the values relative to existing known numbers
173 | * When someone else comes to edit they won't understand why it's this number specifically
174 | * If you have to use magic numbers then leave a comment explaining why that number, it can be just because it works, but explain what will break if it's changed
175 |
176 | > Read more about [magic numbers](https://css-tricks.com/magic-numbers-in-css/).
177 |
178 | ### Mobile first
179 |
180 | #### Don't
181 |
182 | ```scss
183 | .foo {
184 | // Desktop code
185 |
186 | @include mq($until: l) {
187 | // Mobile code
188 | }
189 | }
190 | ```
191 |
192 | ### Do
193 | ```scss
194 | .foo {
195 | // Mobile code
196 |
197 | @include mq($from: l) {
198 | // Desktop code
199 | }
200 | }
201 | ```
202 |
203 | * We should always develop mobile first
204 | * This means we're expanding to fill desktop instead of cramming things in to fit mobile
205 | * If we fail to add desktop styles then the site will still be usable
206 | * It's still acceptable to use `$until` media queries for extensive mobile-only code which you'd only have to override in the desktop breakpoint
207 |
208 | ### Responsive banner padding
209 |
210 | #### Don't
211 |
212 | ```scss
213 | .foo {
214 | aspect-ratio: 16 / 9;
215 | padding-block-start: 60%;
216 | }
217 | ```
218 |
219 | #### Do
220 |
221 | ```scss
222 | .foo {
223 | // 16:9 ratio
224 | padding-block-end: 56.25%;
225 | }
226 | ```
227 |
228 | #### Or
229 |
230 | ```scss
231 | @use 'sass:math';
232 |
233 | .foo {
234 | padding-block-end: percentage(math.div(9, 16));
235 | }
236 | ```
237 |
238 | * When using padding to create a responsive banner always use `padding-block-end`
239 | * Also add a preceding comment explaining the ratio of the banner
240 | * We don't recommend using [`aspect-ratio`](#aspect-ratio) because in browsers that don't support it (generally relatively recent versions of Safari) it would break the layout
241 |
242 | ### RTL
243 |
244 | * Use properties that automatically updated based on writing direction
245 | * See [`margin` & `padding`](#margin--padding) for details on `margin` and `padding`
246 | * [`inset`](#inset) does not have the necessary support yet
247 | * Continue using `top`, `right`, `bottom`, and `left` properties for now
248 | * Use the RTL selector to switch positions in right-to-left writing directions
249 |
250 | ```scss
251 | .foo {
252 | left: var(--spacing-m);
253 |
254 | [dir=rtl] & {
255 | left: unset;
256 | right: var(--spacing-m);
257 | }
258 | }
259 | ```
260 |
261 | [ꜛ Back to TOC](#table-of-contents)
262 |
263 | ## Declarations
264 |
265 | * [#IDs](#ids)
266 | * [!important](#important)
267 | * [Longhand properties](#longhand-properties)
268 | * [Prefixes](#prefixes)
269 | * [Property order](#property-order)
270 | * [Pseudo-elements & -selectors](#pseudo-elements-&--selectors)
271 | * [Variables](#variables)
272 |
273 | ### #IDs
274 |
275 | #### Don't
276 |
277 | ```scss
278 | #foo {
279 | background-color: var(--color-neutral-1);
280 | }
281 | ```
282 |
283 | #### Do
284 |
285 | ```scss
286 | .foo {
287 | background-color: var(--color-neutral-1);
288 | }
289 | ```
290 |
291 | * Don't use IDs, ever, definitely never use them nested under each other
292 | * They're overly specific and trump classes when it comes to CSS specificity
293 |
294 | #### Exceptions
295 |
296 | * Sometimes you may have to because you're working with apps or code you can't otherwise edit
297 | * In this case, and only as a last resort, use IDs
298 | * Add a `// stylelint-disable selector-max-id` comment at the start of the SCSS file immediately beneath the intro comment to disable linting for it
299 |
300 | ### `!important`
301 |
302 | #### Don't
303 |
304 | ```scss
305 | .foo {
306 | color: var(--color-brand-1-default) !important;
307 | }
308 | ```
309 |
310 | * Never use `!important` if it can be avoided
311 | * Add inline comments above the property explaining why if you have to use `!important`
312 |
313 | #### Exceptions
314 |
315 | * Sometimes you have to because of apps though
316 | * However before using `!important` consider doubling the specificity if you're battling code that is loaded in the page after your stylesheet but not actually inline (see below)
317 | * Add a `// stylelint-disable declaration-no-important` comment at the start of the SCSS file immediately beneath the intro comment to disable linting for it
318 |
319 | #### Doubling Specificity
320 |
321 | ```scss
322 | .foo.foo {
323 | // This style will override .foo styles
324 | }
325 | ```
326 |
327 | * This works because it's more specific than just the class name once, but won't override inline styles
328 |
329 | > Read more about [CSS specificity](https://www.smashingmagazine.com/2007/07/css-specificity-things-you-should-know/).
330 |
331 | ### Longhand properties
332 |
333 | #### Don't
334 |
335 | ```scss
336 | .foo {
337 | background: url('/path/to/image.jpg') no-repeat 50% 50% var(--color-neutral-6);
338 | font: var(--font-size-m)/var(--line-height-0) var(--font-family-1);
339 | }
340 | ```
341 |
342 | #### Do
343 |
344 | ```scss
345 | .foo {
346 | background-color: var(--color-neutral-6);
347 | background-image: url('/path/to/image.jpg');
348 | background-position: 50% 50%;
349 | background-repeat: no-repeat;
350 | font-family: var(--font-family-1);
351 | font-size: var(--font-size-m);
352 | line-height: var(--line-height-0);
353 | }
354 | ```
355 |
356 | * Avoid the shorthand `background` and `font` properties
357 | * For these properties the non-described properties automatically are set to `none`/`default`/`0` which causes issues
358 | * It's also recommended that you don't use [`transform`](#transform)
359 |
360 | #### Exceptions
361 |
362 | * You can use `margin` or `padding` if all the values are the same, or if you're setting the same value for both vertical properties, and both horizontal properties
363 | * Feel free to use shorthand properties for `border` and `transform`
364 |
365 | ```scss
366 | .foo {
367 | // This is okay
368 | margin: var(--spacing-m) var(--spacing-l);
369 | // This isn't okay as left and right have different values
370 | margin: var(--spacing-m) var(--spacing-l) var(--spacing-m) var(--spacing-xl);
371 | // Instead of the above use the following, can't use margin-block as Safari doesn't support it fully
372 | margin-block-end: var(--spacing-m);
373 | margin-block-start: var(--spacing-m);
374 | margin-inline-end: var(--spacing-l);
375 | margin-inline-start: var(--spacing-xl);
376 | }
377 | ```
378 |
379 | ### Prefixes
380 |
381 | * Prefixes are automatically added to to CSS properties
382 | * This also means that unused prefixes are automatically removed, if you definitely need the prefix then use the following:
383 |
384 | ```scss
385 | /* autoprefixer: ignore next */
386 | -webkit-prefixed-property: value;
387 | ```
388 |
389 | ### Property order
390 |
391 | ```scss
392 | .foo {
393 | // Includes
394 | @include transition(opacity);
395 | // Extends
396 | @extend .grid;
397 | // CSS variables
398 | --variable: var(--spacing-m);
399 | // SASS variables
400 | $parent: &;
401 | // Properties in alphabetical order
402 | background-color: transparent;
403 | border: 0;
404 | display: flex;
405 | // Prefixed properties should be in alphabetical order based on the property
406 | // itself, ignoring the prefix
407 | -webkit-line-clamp: unset;
408 | z-index: var(--layer-raised);
409 |
410 | // Pseudo-elements
411 | &::before,
412 | &::after {
413 | content: '';
414 | }
415 |
416 | // Nested elements
417 | &__bar {
418 | color: var(--color-neutral-4);
419 | }
420 |
421 | // Direct descendents
422 | > .baz {
423 | color: var(--color-neutral-3);
424 | }
425 |
426 | // Sibling selectors
427 | + .bam,
428 | ~ .bam {
429 | padding-inline-start: 0;
430 | }
431 |
432 | // Pseudo-selectors
433 | &:hover,
434 | &:focus {
435 | border: 1px solid var(--color-brand-1-default)
436 | }
437 |
438 | // Modifiers
439 | {&}--big {
440 | width: 100%;
441 |
442 | // Use $parent SASS variable to avoid re-writing parent selector
443 | #{$parent}__bar {
444 | color: var(--color-neutral-1);
445 | }
446 | }
447 |
448 | // Parent selectors
449 | .parent & {
450 | display: none;
451 | }
452 |
453 | // @media queries
454 | @media print {}
455 |
456 | // @supports queries
457 | @supports (grid-template-columns: subgrid) {}
458 |
459 | // mq media queries
460 | @include mq($from: l) {}
461 | }
462 | ```
463 |
464 | The order should be as follows, all items within each group should be sorted alphabetically:
465 | 1. Includes (e.g. `@include transition(opacity)`)
466 | 1. Extends (e.g. `@extend .grid`)
467 | 1. CSS variables (e.g. `--variable`)
468 | 1. SASS variables (e.g. `$parent`)
469 | 1. Properties (e.g. `background-color`)
470 | 1. Pseudo-elements (e.g. `&::before`, `&::after`, `&::placeholder` etc.)
471 | 1. Nested elements (e.g. `&__bar`)
472 | 1. Direct descendants (e.g. `> .baz`)
473 | 1. Sibling selectors (e.g. `+ .bam`, `~ .bam`, etc.)
474 | 1. Pseudo-selectors (e.g. `&:hover`, `&:focus`, etc.), this way they can change nested elements
475 | 1. Modifiers (e.g. `{&}--big`), this gives them precedence over all nested elements
476 | 1. Parent selectors (e.g. `.parent &`), so that they have the greatest priority
477 | 1. Media queries (e.g. `@media print`)
478 | 1. Support queries (e.g. `@supports (grid-template-columns: subgrid)`)
479 | 1. MQ media queries (e.g. `@include mq($from: l)`)
480 |
481 | ### Pseudo-elements & -selectors
482 |
483 | #### Don't
484 |
485 | ```scss
486 | .foo {
487 | &:after {}
488 |
489 | &:placeholder {}
490 | }
491 | ```
492 |
493 | #### Do
494 |
495 | ```scss
496 | .foo {
497 | &::after {}
498 |
499 | &::placeholder {}
500 |
501 | &:hover {}
502 | }
503 | ```
504 |
505 | * Pseudo-elements should use `::` (after, before, first-letter, first-line, marker, placeholder, and selection)
506 | * Pseudo-selectors should use `:` (hover, focus, active, nth-child etc.)
507 | * This helps differentiate between an element and a selector
508 |
509 | ### Variables
510 |
511 | **If you're using Canvas 3.0.0 or newer then use the `design` command in conjunction with the _tokens.json_ file that the designer provided to automatically create your project's variable and classes. [See documentation](https://we-make-websites.gitbook.io/canvas/features/styles/design-tokens)**
512 |
513 | When first setting up your project you should go through and define a series of variables and mixins to help with the maintenance of the project, it's a lot easier to change the property of one variable rather than hunting for all appearances of, say, a `font-family`.
514 |
515 | It is the project lead developer's responsibility to set them up to maintain conformity.
516 |
517 | [ꜛ Back to TOC](#table-of-contents)
518 |
519 | ## Naming
520 |
521 | * [BEM & CSS](#bem-&-css)
522 | * [BEM modifiers](#bem-modifiers)
523 | * [BEM naming](#bem-naming)
524 | * [Descriptive naming](#descriptive-naming)
525 | * [Naming conventions](#naming-conventions)
526 | * [Variable naming](#variable-naming)
527 | * [Naming conventions](#naming-conventions)
528 |
529 | ### BEM & CSS
530 |
531 | #### About
532 |
533 | **BEM** stands for Block Element Modifier and is a element class naming convention intended to standardise the way elements are named so everyone is on the same page and can work out the relationship of elements without having to refer back to the HTML constantly.
534 |
535 | > For more information go to [Get BEM](http://getbem.com/).
536 |
537 | Below is an ideal example of how BEM would be used across HTML and CSS (technically SCSS).
538 |
539 | #### HTML
540 | This HTML example also includes a suggested way of targeting elements in JavaScript (`js-click`).
541 |
542 | ```html
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
558 |
559 |
560 |
561 |
562 |
563 | ```
564 |
565 | ```html
566 |
573 | ```
574 |
575 | #### CSS
576 |
577 | ```scss
578 | // product.scss
579 | .product {
580 | &__title {}
581 |
582 | &__subtitle {}
583 |
584 | &__image-container {}
585 |
586 | &__image {}
587 |
588 | &__description {}
589 |
590 | // Newlines and spacing removed for brevity
591 | // Even without comments we can see what we're editing
592 | // It doesn't matter if subtitle becomes another element for example
593 | }
594 | ```
595 | ```scss
596 | // product-icons.scss
597 | .product-icons {
598 | &__icon {
599 | {&}--large {
600 | // We're already at three levels deep so always keep an eye on your nesting
601 | }
602 | }
603 |
604 | .product-card & {
605 | // Keep all the code for .product-icons in one place by qualifying its context
606 | // Code here will only apply to .product-icons when inside .product-card
607 | }
608 | }
609 | ```
610 |
611 | ### BEM modifiers
612 |
613 | #### Don't
614 |
615 | ```html
616 |
617 |
618 | ```
619 | ```scss
620 | .bad {
621 | &--modifier {}
622 | }
623 | ```
624 |
625 | #### Do
626 |
627 | ```html
628 |
629 | ```
630 | ```scss
631 | .good {
632 | {&}--modifier {}
633 | }
634 | ```
635 |
636 | * Don't just apply the modifier class and extend the block/element class
637 | * Apply the block/element as a class as well as the modifier class
638 | * Using interpolated brackets means the CSS will compile as `.good.good--modifier {}`
639 |
640 | ### Concatenating
641 |
642 | #### Don't
643 |
644 | ```scss
645 | .foo {
646 | .foo__bar {
647 | &-bez {}
648 | .foo__bar--modifier {}
649 | }
650 | }
651 | ```
652 |
653 | #### Do
654 |
655 | ```scss
656 | .foo {
657 | &__bar {
658 | {&}--modifier {}
659 | }
660 |
661 | &__bar-bez {}
662 | }
663 | ```
664 |
665 | * Keeps the BEM naming and nesting of selectors more obvious
666 | * The modifier has interpolation brackets `#{}` because `&&` is invalid SCSS
667 | * The modifier will compile as `.foo__bar.foo__bar--modifier` in CSS
668 | * This means it has greater specificity, without this any later changes to `.foo__bar` would override the modifier (for example in media queries) as `.foo__bar` and `.foo__bar--modifier` would have the same specificity so the properties applied later in the file would trump the modifier's
669 |
670 | #### Don't
671 |
672 | ```scss
673 | .foo {
674 | &__bar {
675 | &-bez {}
676 | }
677 | }
678 | ```
679 |
680 | #### Do
681 |
682 | ```scss
683 | .foo {
684 | &__bar {}
685 | &__bar-bez {}
686 | }
687 | ```
688 |
689 | * Do not use concatenation to join hyphenated class names
690 |
691 | > Read more about [CSS specificity](https://www.smashingmagazine.com/2007/07/css-specificity-things-you-should-know/).
692 |
693 | ### BEM naming
694 |
695 | #### Don't
696 |
697 | ```scss
698 | .site-search {}
699 | .site-search label {}
700 | .site-search input {}
701 | ```
702 |
703 | #### Do
704 |
705 | ```scss
706 | .site-search {}
707 | .site-search__title {}
708 | .site-search__field {}
709 | ```
710 |
711 | * Always use classes to select elements
712 | * Elements may change and no longer be a `` or ` `, breaking the CSS, causing maintenance issues
713 | * With decent BEM naming you shouldn't need to comment your CSS to say what things are or where they sit
714 | * Class names should be kebab-case
715 |
716 | #### Exceptions
717 |
718 | * Feel free to write comments to explain why you've done things strangely if you have had to
719 |
720 | ### Descriptive naming
721 |
722 | #### Don't
723 |
724 | ```scss
725 | .curley-font {
726 | font-family: 'Lobster', serif;
727 | }
728 |
729 | .red {
730 | color: var(--color-red);
731 | }
732 | ```
733 |
734 | #### Do
735 |
736 | ```scss
737 | .blog__subtitle {
738 | font-family: 'Lobster', serif;
739 | }
740 |
741 | .text-highlight {
742 | color: var(--color-red);
743 | }
744 | ```
745 |
746 | * CSS selectors should describe hierarchy, not the look as the look can change
747 |
748 | ### Naming conventions
749 |
750 | #### Don't
751 |
752 | ```scss
753 | @function FunctionName() {}
754 |
755 | @mixin mixin_name() {}
756 |
757 | @keyframes KEYFRAME_NAME() {}
758 | ```
759 |
760 | #### Do
761 |
762 | ```scss
763 | @function function-name() {}
764 |
765 | @mixin mixin-name() {}
766 |
767 | @keyframes keyframe-name() {}
768 | ```
769 |
770 | * Functions, mixins, and keyframes should all be kebab-case
771 |
772 | ### Variable naming
773 |
774 | #### Canvas 3.0.0 and newer
775 | * Use the `design` command
776 | * Use kebab-case for CSS and SCSS variables
777 | * Global CSS variables should be set in the `:root {}` declaration
778 | * Global SCSS variables should be set in a SCSS file in the _styles/config/_ folder
779 | * Local CSS and SCSS variables are only available in the declaration they are defined in
780 | * Prefer CSS variables, only use SCSS variables where SCSS functions would break with CSS variables
781 |
782 | #### Before Canvas 3.0.0
783 |
784 | * For global variables use `$SCREAMING_SNAKE_CASE`
785 | * For local variables use `$snake_case`
786 | * Local variables are only available in the declaration they are defined in
787 |
788 | ### Naming conventions
789 |
790 | #### Don't
791 |
792 | ```scss
793 | @function FunctionName() {}
794 |
795 | @mixin mixin_name() {}
796 |
797 | @keyframes KEYFRAME_NAME() {}
798 | ```
799 |
800 | #### Do
801 |
802 | ```scss
803 | @function function-name() {}
804 |
805 | @mixin mixin-name() {}
806 |
807 | @keyframes keyframe-name() {}
808 | ```
809 |
810 | * Functions, mixins, and keyframes should all be kebab-case
811 |
812 | [ꜛ Back to TOC](#table-of-contents)
813 |
814 | ## Formatting
815 |
816 | * [Calculations](#calculation)
817 | * [Capitalisation](#capitalisation)
818 | * [Commenting (inline)](#commenting-inline)
819 | * [Commenting (loud)](#commenting-loud)
820 | * [Commenting (introductory)](#commenting-introductory)
821 | * [Indenting](#indenting)
822 | * [Negative CSS variables](#negative-css-variables)
823 | * [Parenthesise on @includes](#parenthesise-on-includes)
824 | * [Whitespace](#whitespace)
825 | * [Zero values & units](#zero-values--units)
826 |
827 | ### Calculations
828 |
829 | #### Don't
830 |
831 | ```scss
832 | .foo {
833 | left: calc(100% - 30px * 2);
834 | width: 100% / 3;
835 | }
836 | ```
837 |
838 | #### Do
839 |
840 | ```scss
841 | .foo {
842 | left: calc(100% - (var(--gutter) * 2));
843 | width: (100% / 3);
844 | }
845 | ```
846 |
847 | * Wrap calculations in brackets for readability
848 | * Put spaces between the values and the mathematical operators
849 | * Put multiplying and dividing values after the starting value
850 | * Use variables in calculations to keep them relative
851 |
852 | #### Also
853 | ```scss
854 | .foo {
855 | --local-margin: (2 * var(--global-margin));
856 | margin-block-end: var(--local-margin);
857 | width: calc(100% + var(--local-margin));
858 | }
859 | ```
860 |
861 | * Use local variables to define adjusted global variables
862 |
863 | ### Capitalisation
864 |
865 | #### Don't
866 |
867 | ```scss
868 | .Foo {
869 | Color: var(--Color-Neutral-2);
870 | }
871 | ```
872 |
873 | #### Do
874 |
875 | ```scss
876 | .foo {
877 | color: var(--color-neutral-2);
878 | }
879 | ```
880 |
881 | * Use kebab-case for selectors and properties
882 | * Refer to [Variable naming](#variable-naming) for global and local variables
883 |
884 | ### Commenting (inline)
885 |
886 | #### Don't
887 |
888 | ```scss
889 | .foo {
890 | background-color: red; // Don't
891 | /* padding-block-start: 30px;
892 | width: 100% */
893 | }
894 | ```
895 |
896 | #### Do
897 |
898 | ```scss
899 | .foo {
900 | // Comment above the line
901 | background-color: red;
902 | // padding-block-start: 30px;
903 | // width: 100%;
904 | }
905 | ```
906 |
907 | * Use `//` for inline comments not the block level `/* */`
908 | * It's easier to un-comment
909 | * Inline comments should start on a new line preceding the property they're describing
910 | * Use sentence case for your comments
911 | * Do not end with a full stop
912 |
913 | ### Commenting (loud)
914 |
915 | ```scss
916 | .foo {
917 | color: var(--color-neutral-2);
918 | width: 100%;
919 |
920 | /**
921 | * Media queries.
922 | */
923 | @include mq($from: l) {
924 | color: var(--color-neutral-1);
925 | }
926 | }
927 | ```
928 |
929 | * Loud comments can be used to break up long code and make it easier to navigate
930 | * Use the format above for loud comments
931 | * Use sentence case for your comments
932 | * End each line with a full stop
933 |
934 | ### Commenting (introductory)
935 |
936 | ```css
937 | /**
938 | * Component: Footer Social
939 | * -----------------------------------------------------------------------------
940 | * Social icons and links in footer.
941 | *
942 | */
943 | ```
944 |
945 | * Include an introductory comment at the start of each file
946 | * Describe what folder it's in, the file's name, and list any special features or conditions
947 | * All lines except the first one should have a full stop
948 |
949 | ### Indenting
950 |
951 | #### Don't
952 |
953 | ```scss
954 | .foo {
955 | color: red;
956 |
957 | &_bar {
958 | // No good
959 | }
960 | }
961 | ```
962 |
963 | #### Do
964 |
965 | ```scss
966 | .foo {
967 | color: red;
968 |
969 | &_bar {
970 | // Better
971 | }
972 | }
973 | ```
974 |
975 | * Indent with 2 spaces, not tabs or 4 spaces
976 | * Shopify uses 2 spaces
977 | * Using spaces is easier to copy and paste
978 |
979 | ### Negative CSS variables
980 |
981 | #### Don't
982 |
983 | ```scss
984 | $margin: var(--spacing-m);
985 |
986 | .foo {
987 | margin-block-start: -$margin;
988 | }
989 | ```
990 |
991 | #### Do
992 |
993 | ```scss
994 | .foo {
995 | margin-block-start: calc(var(--spacing-m) * -1);
996 | }
997 | ```
998 |
999 | * To use negative values for CSS variables you have to times them by `-1` in a `calc()` function
1000 |
1001 | ### Parenthesise on @includes
1002 |
1003 | #### Where
1004 |
1005 | ```scss
1006 | @mixin animation($property: color) {
1007 | // Code
1008 | }
1009 |
1010 | @mixin visually-hidden() {
1011 | // Code
1012 | }
1013 | ```
1014 |
1015 | #### Don't
1016 |
1017 | ```scss
1018 | .foo {
1019 | @include visually-hidden();
1020 | }
1021 | ```
1022 |
1023 | #### Do
1024 |
1025 | ```scss
1026 | .foo {
1027 | @include animation(background-color);
1028 | @include visually-hidden;
1029 | }
1030 | ```
1031 |
1032 | * Do not include parenthesise on argument-less mixins
1033 |
1034 | ### Whitespace
1035 |
1036 | #### Don't
1037 |
1038 | ```scss
1039 | .foo{box-shadow: 0 0 rgba(0,0,0,0.5);color:red;font:{size:1em;weight:700;}}
1040 |
1041 | .foo,.spanner,.foobar{
1042 | color:red;
1043 | .baz{color:blue}}
1044 |
1045 | .foo>.bar {color:red;}
1046 | ```
1047 |
1048 | #### Do
1049 |
1050 | ```scss
1051 | .foo {
1052 | box-shadow: 0 0 rgba(0, 0, 0, 0.5);
1053 | color: rgb(255, 0, 0);
1054 | font-size: 1rem;
1055 | font-weight: 700;
1056 | }
1057 |
1058 | .foo, .foobar,
1059 | .spanner {
1060 | color: rgb(0, 255, 0);
1061 |
1062 | .baz {
1063 | color: rgb(0, 0, 255);
1064 | }
1065 | }
1066 |
1067 | .foo > .bar {
1068 | color: rgb(255, 0, 0);
1069 | }
1070 | ```
1071 |
1072 | * Add whitespace after commas (including property values), after selector name, after property colon and before and after child selector
1073 | * Each element selector block should have an empty line above it
1074 | * Each property on a new line and a new line for the closing }
1075 | * Keep related selectors on the same line, separate selectors go on a new line
1076 | * Add spaces between child selectors
1077 | * Put a semi-colon after a property, even if it's the last one
1078 | * Put a newline after the last selector
1079 |
1080 | #### Why
1081 | * It's easier to read
1082 | * Whitespace is free
1083 | * Code is minified anyway
1084 |
1085 | ### Zero values & units
1086 |
1087 | #### Don't
1088 |
1089 | ```scss
1090 | .foo {
1091 | animation-delay: 0;
1092 | margin-block: 0px;
1093 | opacity: .4567;
1094 | }
1095 | ```
1096 |
1097 | #### Do
1098 | ```scss
1099 | .foo {
1100 | animation-delay: 0s;
1101 | margin-block: 0;
1102 | opacity: 0.4;
1103 | }
1104 | ```
1105 |
1106 | * Do specify units on zero duration times
1107 | * Don't specify units on zero length values
1108 | * Do add a leading zero for decimal places
1109 | * Don't go to more than three decimal places, the fewer the better
1110 |
1111 | [ꜛ Back to TOC](#table-of-contents)
1112 |
1113 | ## Colours
1114 |
1115 | * [Colour properties](#colour-properties)
1116 | * [Colour variables](#colour-variables)
1117 |
1118 | ### Colour properties
1119 |
1120 | #### Don't
1121 |
1122 | ```scss
1123 | .foo {
1124 | color: red;
1125 | // Or
1126 | color: #FF0000;
1127 | // Or
1128 | color: hsl(0, 100%, 50%);
1129 | }
1130 | ```
1131 |
1132 | #### Do
1133 |
1134 | ```scss
1135 | .foo {
1136 | color: rgb(255 0 0);
1137 | // Or
1138 | color: rgb(255 0 0 / 50%);
1139 | }
1140 | ```
1141 |
1142 | * Use `rgb()` colour functions using space separation as they are more human readable
1143 | * Do not use comma separated `rgb()` colour functions
1144 | * These values are often supplied in brand guidelines
1145 |
1146 | #### Exceptions
1147 |
1148 | * If required, use lowercase and shorthand (where possible) hex colour values
1149 |
1150 | ### Colour variables
1151 |
1152 | #### Don't
1153 |
1154 | ```scss
1155 | $colour-blue-other: #6195ed;
1156 | $colour-dark-grey: #E2E3E4;
1157 | $LIGHT_GREY: #d4d7d9;
1158 | ```
1159 |
1160 | #### Do
1161 |
1162 | ```scss
1163 | --color-brand-1-default: rgb(97 149 237);
1164 | --color-neutral-2: rgb(226 227 228);
1165 | --color-neutral-3: rgb(212 215 217);
1166 | ```
1167 |
1168 | * Use the `design` command to generate variables and classes
1169 | * All colours should have variables defined for them, this will come from the designs
1170 | * Designers will determine the name of colours
1171 |
1172 | [ꜛ Back to TOC](#table-of-contents)
1173 |
1174 | ## Nesting
1175 |
1176 | * [Nesting levels](#nesting-levels)
1177 | * [Nesting media queries](#nesting-media-queries)
1178 |
1179 | ### Nesting levels
1180 |
1181 | #### Don't
1182 |
1183 | ```scss
1184 | .foo {
1185 | .this {
1186 | .is {
1187 | .very {
1188 | .bad {
1189 |
1190 | }
1191 | }
1192 | }
1193 | }
1194 |
1195 | @include mq($from: l) {
1196 | .this .is .very .bad {
1197 |
1198 | }
1199 | }
1200 | }
1201 | ```
1202 |
1203 | #### Do
1204 |
1205 | ```scss
1206 | .foo {
1207 | // Base level (not included)
1208 |
1209 | &__bar {
1210 | // Level one
1211 |
1212 | &:hover {
1213 | // Level two
1214 |
1215 | &.bar {
1216 | // Level three
1217 | }
1218 | }
1219 | }
1220 |
1221 | @include mq($from: l) {
1222 | // Base level (not included)
1223 |
1224 | &__bar {
1225 | // Level one
1226 |
1227 | &:hover {
1228 | // Level two
1229 |
1230 | &.bar {
1231 | // Level three
1232 | }
1233 | }
1234 | }
1235 | }
1236 | }
1237 | ```
1238 |
1239 | * Don't nest more than 3 levels
1240 | * As styles would require an even more specific selector to override them
1241 | * This would quickly add up to a significant maintenance issue
1242 | * This does mean that you can't always nest properties the way you normally would
1243 | * Media queries are not included when counting levels of nesting
1244 | * VS Code can show you how many levels deep you are in its Breadcrumbs feature (View > Toggle Breadcrumbs)
1245 |
1246 | ### Nesting media queries
1247 |
1248 | #### Don't
1249 |
1250 | ```scss
1251 | .foo {
1252 |
1253 | &__bar {
1254 | // Code
1255 |
1256 | @include mq($from: m) {
1257 | // Code
1258 | }
1259 | }
1260 |
1261 | &__section {
1262 | // Code
1263 |
1264 | @include mq($from: m) {
1265 | // Code
1266 | }
1267 | }
1268 | }
1269 | ```
1270 |
1271 | #### Do
1272 |
1273 | ```scss
1274 | .foo {
1275 |
1276 | &__bar {
1277 | // Code
1278 | }
1279 |
1280 | &__section {
1281 | // Code
1282 | }
1283 |
1284 | /**
1285 | * Media queries.
1286 | */
1287 | @include mq($from: m) {
1288 | &__bar {
1289 | // Code
1290 | }
1291 |
1292 | &__section {
1293 | // Code
1294 | }
1295 | }
1296 | }
1297 | ```
1298 |
1299 | * Keep media queries at the root of the declaration, not nested inside each selector
1300 | * This way it's easier to find media queries
1301 |
1302 | [ꜛ Back to TOC](#table-of-contents)
1303 |
1304 | ## Properties
1305 |
1306 | * [`aspect-ratio`](#aspect-ratio)
1307 | * [`border`](#border)
1308 | * [`font-family`](#font-family)
1309 | * [`font-size`](#font-size)
1310 | * [`height` & `width`](#height--width)
1311 | * [`inset`](#inset)
1312 | * [`line-height`](#line-height)
1313 | * [`margin` & `padding`](#margin--padding)
1314 | * [`transform`](#transform)
1315 | * [`transition`](#transition)
1316 |
1317 | ### `aspect-ratio`
1318 |
1319 | * We don't recommend using `aspect-ratio` because in browsers that don't support it (generally relatively recent versions of Safari) it would break the layout
1320 | * See [responsive banner padding](#responsive-banner-padding) for an alternative
1321 |
1322 | ### `border`
1323 |
1324 | #### Don't
1325 |
1326 | ```scss
1327 | .foo {
1328 | border: none;
1329 | }
1330 |
1331 | .bar {
1332 | border: 2px solid var(--color-neutral-5);
1333 |
1334 | &:hover {
1335 | border: 2px solid var(--color-neutral-1);
1336 | }
1337 | }
1338 | ```
1339 |
1340 | #### Do
1341 |
1342 | ```scss
1343 | .foo {
1344 | border: 0;
1345 | }
1346 |
1347 | .bar {
1348 | border: 2px solid var(--color-neutral-5);
1349 |
1350 | &:hover {
1351 | border-color: var(--color-neutral-1);
1352 | }
1353 | }
1354 | ```
1355 |
1356 | * Use `0` instead of `none` for borders
1357 | * When a border is set to `0` it will never display, however if a border is set to `none` but later overridden by a `border-style` it will display
1358 | * If you're changing a single part of the property then target that specific property rather than setting all the properties again, this is much easier to maintain
1359 |
1360 | ### `font-family`
1361 |
1362 | #### Don't
1363 |
1364 | ```scss
1365 | .foo {
1366 | font-family: 'Avenir';
1367 | }
1368 | ```
1369 |
1370 | #### Do
1371 |
1372 | ```scss
1373 | :root {
1374 | --font-family-1: 'Avenir', Helvetica, Arial, sans-serif;
1375 | }
1376 |
1377 | .foo {
1378 | font-family: var(--font-family-1);
1379 | }
1380 | ```
1381 |
1382 | * Don't just declare the custom font
1383 | * Always provide a font stack which includes web-safe fonts
1384 | * Edit `fontStacks` in _design.config.js_ in Canvas to add font stacks
1385 | * This prevents loading blank pages until the font loads
1386 | * The `font-family` should be declared in a global variable
1387 |
1388 | ### `height` & `width`
1389 |
1390 | #### Don't
1391 |
1392 | ```scss
1393 | .foo {
1394 | height: calc(var(--spacing-m) + var(--spacing-2xs));
1395 | width: 32px;
1396 | }
1397 | ```
1398 |
1399 | #### Do
1400 |
1401 | ```scss
1402 | .foo {
1403 | height: 1.25rem;
1404 | width: 2rem;
1405 | }
1406 | ```
1407 |
1408 | * Don't use spacing variables to build `height` or `width` values
1409 | * Don't use `px` units for `height` or `width` values
1410 | * Instead use `rem` values
1411 | * The spacing variables are only intended to keep spacing relative and not for dimensions
1412 | * `rem` units scale relatively when the user zooms in, whereas `px` units may cause issues
1413 |
1414 | #### Exceptions
1415 |
1416 | ```scss
1417 | .icon {
1418 | height: var(--icon-m);
1419 | width: var(--icon-m);
1420 | }
1421 | ```
1422 |
1423 | * There are existing variables for icon sizes
1424 | * Use these for icon element styles
1425 |
1426 | ### `font-size`
1427 |
1428 | #### Don't
1429 |
1430 | ```scss
1431 | .foo {
1432 | font-size: 12px;
1433 | // Or
1434 | font-size: 1em;
1435 | }
1436 | ```
1437 |
1438 | #### Do
1439 |
1440 | ```scss
1441 | :root {
1442 | --font-size-m: 1rem;
1443 | }
1444 |
1445 | .foo {
1446 | // Canvas 3.0.0 or newer
1447 | font-size: var(--font-size-m);
1448 | // Before Canvas 3.0.0
1449 | @include ms(0);
1450 | }
1451 | ```
1452 |
1453 | * Use rem, relative ems
1454 | * They're easier to understand as they're always relative to the base font-size
1455 | * `em` units change based on the current elements `font-size`
1456 | * `px` units are not as responsive to device or zoom level
1457 | * Canvas 3.0.0 or newer defines font sizes as global variables
1458 | * Before Canvas 3.0.0 we used the font function `ms()`
1459 |
1460 | ### `inset`
1461 |
1462 | * The `inset` property (and its longhand versions) do not have full browser support yet, avoid using
1463 | * See [RTL](#rtl) for more details
1464 |
1465 | ### `line-height`
1466 |
1467 | #### Don't
1468 |
1469 | ```scss
1470 | .foo {
1471 | line-height: 18.5px;
1472 | }
1473 |
1474 | .bar {
1475 | line-height: 2.35rem;
1476 | }
1477 | ```
1478 |
1479 | #### Do
1480 |
1481 | ```scss
1482 | .foo {
1483 | line-height: 160%;
1484 | }
1485 | ```
1486 |
1487 | * Use `%` units for relative `line-height`
1488 | * This means `line-height` will scale with the font size rather than being fixed
1489 | * Another consideration at the design phase is to keep `line-height` relative to the `font-size` in rational numbers
1490 |
1491 | ### `margin` & `padding`
1492 |
1493 | #### Don't
1494 |
1495 | ```scss
1496 | .foo {
1497 | margin-top: var(--spacing-xs);
1498 | margin-right: calc(var(--spacing-s) - var(--spacing-3xs));
1499 | padding-bottom: var(--spacing-m);
1500 | padding-left: var(--spacing-l);
1501 | }
1502 | ```
1503 |
1504 | #### Do
1505 |
1506 | ```scss
1507 | .foo {
1508 | margin-block-start: var(--spacing-xs);
1509 | margin-inline-end: var(--spacing-s);
1510 | padding-block-end: var(--spacing-m);
1511 | padding-inline-start: var(--spacing-l);
1512 | }
1513 | ```
1514 |
1515 | #### Logical properties
1516 | * Use `margin-block-*`, `margin-inline-*`, `padding-block-*`, and `padding-inline-*` properties
1517 | * These reflect the current `writing-mode`, `direction`, and `text-orientation` of the page meaning they better support multi-language, especially right-to-left languages
1518 | * Be wary of using `margin-block-start` as vertical margins collapse
1519 | * Use `margin` and `padding` shorthand properties only when all values match, or if values of properties on the same axis match
1520 | * Safari does not support CSS variables when using `margin-block`, `margin-inline`, `padding-block`, and `padding-inline` so use individual styles when using CSS variables
1521 |
1522 | > Read more about [`margin-block`](https://developer.mozilla.org/en-US/docs/Web/CSS/margin-block), [`margin-inline`](https://developer.mozilla.org/en-US/docs/Web/CSS/margin-inline), [`padding-block`](https://developer.mozilla.org/en-US/docs/Web/CSS/padding-block), and [`padding-inline`](https://developer.mozilla.org/en-US/docs/Web/CSS/padding-inline).
1523 |
1524 | #### Variable calculations
1525 | * Don't use `calc()` to combine spacing variables to perfectly match the pixel value of designs
1526 | * Instead use the closest existing spacing variable if possible
1527 | * Where the difference between the desired value and the available variables is too great you can use `calc()`, just remember to add a [magic number comment](#magic-numbers) explaining why
1528 |
1529 | ### `transform`
1530 |
1531 | #### Don't
1532 |
1533 | ```scss
1534 | .foo {
1535 | transform: translateY(var(--spacing-m)) scale(2) rotate(45deg)
1536 | }
1537 | ```
1538 |
1539 | #### Do
1540 |
1541 | ```scss
1542 | .foo {
1543 | rotate: 45deg;
1544 | scale: 2;
1545 | translate: 0 var(--spacing-m);
1546 | }
1547 | ```
1548 |
1549 | * Do not use the shorthand `transform` property to `rotate`, `scale`, or `translate` as these keywords have their own properties
1550 | * This allows you to animate and transition individual properties
1551 | * Continue using `transform` to use the `matrix` and `skew` keywords
1552 |
1553 | ### `transition`
1554 |
1555 | #### Don't
1556 |
1557 | ```scss
1558 | .foo {
1559 | transition: ease opacity 0.4s 0.2s;
1560 | }
1561 | ```
1562 |
1563 | #### Do
1564 |
1565 | ```scss
1566 | .foo {
1567 | @include transition(opacity)
1568 | }
1569 | ```
1570 |
1571 | * Use the `transition` mixin
1572 | * See the `get-transition-properties` function for default values used for timing and ease
1573 | * You can customise on a per mixin basis
1574 |
1575 | ```scss
1576 | .foo {
1577 | @include transition(transform var(--timing-slow) var(--easing-normal))
1578 | }
1579 | ```
1580 |
1581 | * Do not transition `margin` or positional properties (`top`, `left`, `right`, `bottom`) as these don't perform well and lead to poor frame rates
1582 | * Instead use `padding` or `translate` as these can be hardware-accelerated
1583 |
1584 | [ꜛ Back to TOC](#table-of-contents)
1585 |
--------------------------------------------------------------------------------
/frame/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Coding Guidelines
4 | Deprecated. See [CANVAS guidelines](/README.md).
5 |
6 | Standards, guidelines, and best practice we follow whilst developing at [We Make Websites](https://wemakewebsites.com/) on our FRAME framework.
7 |
8 | ## Table of contents
9 |
10 | * [HTML](html/README.md)
11 | * [Liquid](liquid/README.md)
12 | * [CSS/SCSS](css/README.md)
13 | * [JavaScript](https://github.com/Shopify/javascript#import-javascript-from-shopify)
14 | * [VS Code](vs-code/README.md)
15 |
16 | ## Why do we need guidelines?
17 |
18 | When working on large, long-running projects, with dozens of developers of differing specialities and abilities, it is important that we all work in a unified way in order to:
19 |
20 | * Keep the code base maintainable
21 | * Keep code transparent, sane, and readable
22 | * Keep the code base scalable
23 | * Maintain the code base's performance
24 |
25 | ## Applying the guidelines
26 |
27 | When you are writing new code you are expected to maintain the guidelines, if you update an existing feature you are expected to update any non-adhering code that you interact with or change. This means you are not expected to fix the whole file when adding new functionality, just the code you write or change.
28 |
29 | ### FRAME 3
30 | When committing your changes the SCSS and JS files will be automated linted due to `husky` and `lint-staged`. You will have to manually apply the guidelines to HTML and Liquid as there is no linter available.
31 |
32 | > **🗒 Note:** The oldest FRAME 3 projects will not adhere to the new HTML and Liquid guidelines as the guidelines were written after FRAME 3 was launched.
33 |
34 | ### FRAME 1 & 2
35 | You must apply all guidelines manually; there is no automatic linting available.
36 |
--------------------------------------------------------------------------------
/frame/css/README.md:
--------------------------------------------------------------------------------
1 | # CSS Guidelines
2 |
3 | ## stylelint & NPM
4 |
5 | There is an NPM module available to import our CSS guidelines into stylelint. Simply use `yarn add @we-make-websites/stylelint-config` to install (Frame 3 already includes this package).
6 |
7 | Visit [@we-make-websites/stylelint-config](https://www.npmjs.com/package/@we-make-websites/stylelint-config) or the [repo](https://github.com/we-make-websites/stylelint-config) for more information.
8 |
9 | ## Table of contents
10 |
11 | 1. [General](#general)
12 | 1. [Methods](#methods)
13 | 1. [Declarations](#declarations)
14 | 1. [Naming](#naming)
15 | 1. [Spacing](#spacing)
16 | 1. [Formatting](#formatting)
17 | 1. [Colours](#colours)
18 | 1. [Nesting](#nesting)
19 | 1. [Properties](#properties)
20 |
21 | [← Back to homepage](../README.md)
22 |
23 | ## General
24 |
25 | * [DRY (Don't Repeat Yourself)](#dry-dont-repeat-yourself)
26 | * [Notes on Frame](#notes-on-frame)
27 |
28 | ### [DRY (Don't Repeat Yourself)](#dry-dont-repeat-yourself)
29 |
30 | #### Don't
31 | ```scss
32 | .foo {
33 | background-color: #fff;
34 | border: 1px solid #000;
35 | color: #000;
36 | }
37 |
38 | .bar {
39 | background-color: #fff;
40 | border: 1px solid #000;
41 | color: #000;
42 | }
43 | ```
44 |
45 | #### Do
46 | ```scss
47 | .btn {
48 | background-color: rgb(255, 255, 255);
49 | border: 1px solid rgb(0, 0, 0);
50 | color: rgb(0, 0, 0);
51 | }
52 | ```
53 |
54 | * Keep things DRY, aim to use standard classes to so you can re-use your code
55 | * SCSS includes and extends can encourage poor coding practice, just because it's one line in your code doesn't mean it is when exported
56 | * If you're applying the same include or styles on lots of selectors, create a class and apply it in HTML to keep things DRY
57 | * This is where defined h1, h2, p etc. in Sketch files would be really useful so we can re-use established styles
58 |
59 | > **❗Exception:** Sometimes it's still better to KISS (Keep It Simple, Stupid) than DRY.
60 |
61 | ### [Notes on Frame](#notes-on-frame)
62 |
63 | #### Frame 3
64 | You are expected to follow the guidelines to the letter without fail, Frame 3 represents the best that We Make Websites produces so your code should be too. Frame 3 comes with a style lint which will highlight errors, using yarn format will automatically fix most of these errors for you.
65 |
66 | #### Frame 2
67 | When building a new project using Frame 2.0 it is expected that if you build any new sections or re-build existing templates you will follow these guidelines.
68 |
69 | #### Frame 1 and Old Workflow
70 | Not all of these guidelines will be possible when working with an existing site (or any site built before these guidelines). Frame 2.0 local concatenation of code allows us to use SCSS features that are not available with Shopify's old version of SCSS, these should be highlighted in the Notes or Exceptions are the end of the page.
71 |
72 | [ꜛ Back to TOC](#table-of-contents)
73 |
74 | ## Methods
75 |
76 | * [Mobile first](#mobile-first)
77 | * [Responsive banner padding](#responsive-banner-padding)
78 | * [Fullscreen elements](#fullscreen-elements)
79 | * [Magic numbers](#magic-numbers)
80 |
81 | ### [Mobile first](#mobile-first)
82 |
83 | #### Don't
84 | ```scss
85 | .foo {
86 | // Desktop code
87 |
88 | @media (max-width: 768px) {
89 | // Mobile code
90 | }
91 | }
92 | ```
93 |
94 | ### Do
95 | ```scss
96 | .foo {
97 | // Mobile code
98 |
99 | @media (min-width: 768px) {
100 | // Desktop code
101 | }
102 | }
103 | ```
104 |
105 | * We should always develop mobile first
106 | * This means we're expanding to fill desktop instead of cramming things in to fit mobile
107 | * If we miss styling something the site will still be usable
108 |
109 | ### [Responsive banner padding](#responsive-banner-padding)
110 |
111 | #### Don't
112 | ```scss
113 | .foo {
114 | padding-top: 60%;
115 | }
116 | ```
117 |
118 | #### Do
119 | ```scss
120 | .foo {
121 | // 16:9 ratio
122 | padding-bottom: 56.25%
123 | }
124 | ```
125 |
126 | * When using padding to create a responsive banner always use padding-bottom
127 | * Also add a preceding comment explaining the ratio of the banner
128 |
129 | ### [Fullscreen elements](#fullscreen-elements)
130 |
131 | #### Don't
132 | ```scss
133 | .foo {
134 | bottom: 0;
135 | left: 0;
136 | position: absolute;
137 | right: 0;
138 | top: 0;
139 | }
140 | ```
141 |
142 | #### Do
143 | ```scss
144 | .foo {
145 | height: 100%;
146 | left: 0;
147 | position: absolute;
148 | top: 0;
149 | width: 100%;
150 | }
151 | ```
152 |
153 | * Don't define all four positioning properties to set an element to fullscreen as these do not work on button elements
154 | * Instead use `height: 100%` and `width: 100%`
155 |
156 | ### [Magic numbers](#magic-numbers)
157 |
158 | #### Don't
159 | ```scss
160 | .foo {
161 | left: 23px;
162 | }
163 | ```
164 |
165 | #### Do
166 | ```scss
167 | .foo {
168 | // 10px because of font height
169 | left: ($GUTTER - 10px - ($NAV_HEIGHT / 2));
170 | }
171 | ```
172 |
173 | #### If you have to
174 | ```scss
175 | .foo {
176 | // This values matches the sum of widths above it
177 | left: 23px;
178 | }
179 | ```
180 |
181 | * Avoid magic numbers, these are numbers that 'just work' but are set arbitrarily
182 | * Try and use calculations to make the values relative to existing known numbers
183 | * When someone else comes to edit they won't understand why it's this number specifically
184 | * If you have to; leave a comment explaining why that number, it can be just because it works, but explain what will break if it's changed
185 |
186 | > **🗒 Note:** Read more on [CSS tricks](https://css-tricks.com/magic-numbers-in-css/).
187 |
188 | [ꜛ Back to TOC](#table-of-contents)
189 |
190 | ## Declarations
191 |
192 | * [#IDs](#ids)
193 | * [!important](#important)
194 | * [Longhand properties](#longhand-properties)
195 | * [Prefixes](#prefixes)
196 | * [Property order](#property-order)
197 | * [Pseudo-elements & -selectors](#pseudo-elements-&--selectors)
198 | * [Variables](#variables)
199 |
200 | ### [#IDs](#ids)
201 |
202 | #### Don't
203 | ```scss
204 | #foo {
205 | #bar {
206 | background-color: #000;
207 | }
208 | }
209 | ```
210 |
211 | #### Do
212 | ```scss
213 | .foo {
214 | .bar {
215 | background-color: rgb(0, 0, 0);
216 | }
217 | }
218 | ```
219 |
220 | * Don't use IDs, ever. Definitely never use them nested under each other
221 | * They're overly specific and trump classes in the order of CSS rendering
222 |
223 | > **❗Exception:** Sometimes you may have to because you're working with apps or code you can't otherwise edit. In this case, and only as a last resort, use IDs.
224 |
225 | ### [!important](#important)
226 |
227 | #### Don't
228 | ```scss
229 | .foo {
230 | color: red !important;
231 | }
232 | ```
233 |
234 | * Never use `!important` if it can be avoided
235 | * Add inline comments above the property explaining why if you have to use `!important`
236 |
237 | > **❗Exception:** Sometimes you have to because of apps though. However before using !important consider doubling the specificity (see below) if you're battling code that is loaded in the page after your stylesheet but not actually inline.
238 |
239 | #### Doubling Specificity
240 |
241 | ```scss
242 | .foo.foo {
243 | // This style will override .foo styles
244 | }
245 | ```
246 |
247 | * This works because it's more specific than just the class name once, but won't override inline styles
248 | * [Read this article](https://www.smashingmagazine.com/2007/07/css-specificity-things-you-should-know/) to find out more information about CSS specificity
249 |
250 | ### [Longhand properties](#longhand-properties)
251 |
252 | #### Don't
253 | ```scss
254 | .foo {
255 | background: url('/path/to/image.jpg') no-repeat 50% 50% #fff;
256 | font: 2rem/1.6 'Helvetica', sans-serif;
257 | }
258 | ```
259 |
260 | #### Do
261 | ```scss
262 | .foo {
263 | background-color: #fff;
264 | background-image: url('/path/to/image.jpg');
265 | background-position: 50% 50%;
266 | background-repeat: no-repeat;
267 | font-family: 'Helvetica', sans-serif;
268 | font-size: 2rem;
269 | line-height: 1.6;
270 | }
271 | ```
272 |
273 | * Avoid shorthand for background and font properties
274 | * For these properties the non-described properties automatically are set to none/default/0 which causes issues
275 | * The code is more verbose when making responsive changes
276 | * Do not nest properties (used to be accepted, it no longer is)
277 |
278 | > **❗Exception:** Feel free to use shorthand properties for `margin`, `padding`, `border`, and `transform`.
279 |
280 | ### [Prefixes](#prefixes)
281 |
282 | #### Frame 2 and 3
283 | Thanks to the Frame 2 and 3 watch command you don't need to add prefixes to CSS properties as these are added automatically before upload.
284 |
285 | #### Frame 1 and Old Workflow
286 |
287 | If you're using the Old Workflow or Frame 1 you will need to add prefixes, you can keep track of browser support using [Can I Use](https://caniuse.com/). The most commonly needed prefixes is for Flex properties, use [Should I Prefix](http://shouldiprefix.com/) to find out what prefixes you need.
288 |
289 | You will also need to add prefixes even if you're working on a Frame 2 or 3 project and not working on one of the bundled CSS files (like `checkout.scss.liquid`)
290 |
291 | ### [Property order](#property-order)
292 |
293 | #### Do
294 | ```scss
295 | .foo {
296 | // Extends
297 | @extend .grid;
298 | // Includes
299 | @include transition(0.5s);
300 | // Properties in alphabetical order
301 | // Frame 2+ auto-prefixes so no need to include them
302 | // Otherwise prefixed versions are listed under the main property
303 | background-color: transparent;
304 | border: 0;
305 | display: flex;
306 | display: -ms-flexbox;
307 | display: -webkit-flex;
308 | padding: 0;
309 |
310 | // Pseudo-element
311 | &::before {
312 | content: '';
313 | }
314 |
315 | // Nested elements
316 | &__bar {
317 | margin: 0;
318 | }
319 |
320 | // Direct descendents
321 | > .baz {
322 | padding: 0;
323 | }
324 |
325 | // Sibling selectors
326 | + .bam,
327 | ~ .bam {
328 | padding-left: 0;
329 | }
330 |
331 | // Pseudo-selectors
332 | &:focus, &:hover {
333 | box-shadow: 0 0 5px color(blue, 0.3);
334 | }
335 |
336 | // Modifiers
337 | {&}--big {
338 | width: 100%;
339 | }
340 |
341 | // Parents
342 | .parent & {
343 | display: none;
344 | }
345 | }
346 | ```
347 |
348 | The order should be as follows, all items within each group should be sorted alphabetically:
349 | 1. Local variables (e.g. `$local_margin`)
350 | 1. Extends (e.g. `@extend %font`)
351 | 1. Includes (e.g. `@include ms-respond()`)
352 | 1. Properties (e.g. `background-color`)
353 | 1. Pseudo-elements (e.g. `&::before`, `&::placeholder` etc.)
354 | 1. Nested elements (e.g. `&__bar`)
355 | 1. Direct descendants (e.g. `> .baz`, `+ .baz`)
356 | 1. Pseudo-selectors (e.g. `&:hover`), this way they can change nested elements
357 | 1. Modifiers (e.g. `{&}--big`), this gives them precedence over all nested elements
358 | 1. Parents (e.g. `.parent &`), so that they have the greatest priority
359 | 1. Media queries (e.g. `@include mq($from: large)`)
360 |
361 | ### [Pseudo-elements & -selectors](#pseudo-elements-&--selectors)
362 |
363 | #### Don't
364 | ```scss
365 | .foo {
366 | &:placeholder {}
367 |
368 | &:after {}
369 | }
370 | ```
371 |
372 | #### Do
373 | ```scss
374 | .foo {
375 | &::after {}
376 |
377 | &::placeholder {}
378 |
379 | &:hover {}
380 | }
381 | ```
382 |
383 | * Pseudo-elements should use `::` (after, before, first-letter, first-line, marker, placeholder, and selection)
384 | * Pseudo-selectors should use `:` (hover, focus, active, nth-child etc.)
385 | * This helps differentiate between an element and a selector
386 |
387 | ### [Variables](#variables)
388 |
389 | When first setting up your project you should go through and define a series of variables and mixins to help with the maintenance of the project, it's a lot easier to change the property of one variable rather than hunting for all appearances of, say, a `font-family`.
390 |
391 | > **🗒 Note:** On Frame 3 projects you can use the styleguide page template.
392 |
393 | It is the project lead developer's responsibility to set them up to maintain conformity.
394 |
395 | Common properties that benefit from variables are:
396 | * `border-radius`
397 | * `color`
398 | * `font-family`
399 | * `font-weight`
400 | * `margin` (gutters, grid gutters)
401 | * `transition` (duration, easing) – consider a mixin
402 |
403 | [ꜛ Back to TOC](#table-of-contents)
404 |
405 | ## Naming
406 |
407 | * [BEM & CSS](#bem-&-css)
408 | * [BEM modifiers](#bem-modifiers)
409 | * [BEM naming](#bem-naming)
410 | * [HTML naming](#html-naming)
411 | * [Descriptive naming](#descriptive-naming)
412 | * [Variable naming](#variable-naming)
413 |
414 | ### [BEM & CSS](#bem-&-css)
415 |
416 | #### About
417 |
418 | **BEM** stands for Block Element Modifier and is a element class naming convention intended to standardise the way elements are named so everyone is on the same page and can work out the relationship of elements without having to refer back to the HTML constantly. For more information go to [Get BEM](http://getbem.com/).
419 |
420 | Below is an ideal example of how BEM would be used across HTML and CSS (technically SCSS).
421 |
422 | #### HTML
423 | This HTML example also includes a suggested way of targeting elements in JavaScript (`js-click`).
424 |
425 | ```html
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
455 | ```
456 |
457 | #### CSS
458 | The below example will only work when you are using Frame 2 or higher as previous versions do not support interpolation brackets used in the class name.
459 |
460 | ```scss
461 | // Newlines and spacing removed for brevity
462 |
463 | .product {
464 | &__title {}
465 | &__subtitle {}
466 | &__image-container {}
467 | &__image {}
468 | &__description {}
469 |
470 | // Even without comments we can see what we're editing
471 | // It doesn't matter if subtitle becomes another element for example
472 | }
473 |
474 | .product-icons {
475 | &__icon {
476 | {&}--large {
477 | // Also we're already at three levels deep so always keep an eye on your nesting
478 | }
479 | }
480 |
481 | .product-card & {
482 | // Keep all the code for .product-icons in one place by qualifying its context
483 | // Code here will only apply to .product-icons when inside .product-card
484 | }
485 | }
486 | ```
487 |
488 | ### [BEM modifiers](#bem-modifiers)
489 |
490 | #### Don't
491 | ```html
492 |
493 |
494 | ```
495 | ```scss
496 | .bad {
497 | &--modifier {}
498 | }
499 | ```
500 |
501 | #### Do
502 | ```html
503 |
504 | ```
505 | ```scss
506 | .good {
507 | {&}--modifier {}
508 | }
509 | ```
510 |
511 | * Don't just apply the modifier class and extend the block/element class
512 | * Apply the block/element as a class as well as the modifier class
513 |
514 | ### [Concatenating](#concatenating)
515 |
516 | #### Don't
517 | ```scss
518 | .foo {
519 | .foo__bar {
520 | .foo__bar--modifier {}
521 | }
522 | }
523 | ```
524 |
525 | #### Do (Frame 2+ only)
526 | ```scss
527 | .foo {
528 | &__bar {
529 | {&}--modifier {}
530 | }
531 | }
532 | ```
533 |
534 | * Keeps the BEM naming and nesting of selectors more obvious
535 | * The modifier has interpolation brackets `#{}` because && is invalid SCSS
536 | * The modifier would render as `.foo__bar.foo__bar--modifier` in CSS
537 | * This means it has greater specificity, without this any later changes to .foo__bar would override the modifier (for example in media queries) as `.foo__bar` and `.foo__bar--modifier` would have the same specificity so the properties appear later would trump the modifier's
538 |
539 | > **❗Exception:** If you're working on a site using the Old Workflow or Frame 1.0 this can't be done as Shopify's version of SCSS doesn't support concatenating or interpolation brackets.
540 |
541 | ### [BEM naming](#bem-naming)
542 |
543 | #### Don't
544 | ```scss
545 | .site-search {}
546 | .site-search label {}
547 | .site-search input {}
548 | ```
549 |
550 | #### Do
551 | ```scss
552 | .site-search {}
553 | .site-search__title {}
554 | .site-search__field {}
555 | ```
556 |
557 | * Elements may change and no longer be a label or input, breaking the CSS, causing maintenance issues so always select with a class
558 | * With decent BEM naming you shouldn't need to comment your CSS to say what things are or where they sit
559 |
560 | > **❗Exception:** Feel free to write comments to explain why you've done things strangely if you have had to.
561 |
562 | ### [HTML naming](#html-naming)
563 |
564 | #### Don't
565 | ```scss
566 | h3.foo {
567 | font-size: 2rem;
568 | }
569 | ```
570 |
571 | #### Do
572 | ```scss
573 | .header__subtitle {
574 | font-size: 2rem;
575 | }
576 | ```
577 |
578 | * Don't use the HTML element name to select an element
579 | * Makes maintenance easier, if you change the HTML element you have to change it in the CSS too
580 | * BEM naming should make it clear what you're selecting
581 |
582 | ### [Descriptive naming](#descriptive-naming)
583 |
584 | #### Don't
585 | ```scss
586 | .curley-font {
587 | font-family: 'Lobster', serif;
588 | }
589 |
590 | .red {
591 | color: $COLOR_RED;
592 | }
593 | ```
594 |
595 | #### Do
596 | ```scss
597 | .blog__subtitle {
598 | font-family: 'Lobster', serif;
599 | }
600 |
601 | .u-highlight {
602 | color: $COLOR_RED;
603 | }
604 | ```
605 |
606 | * CSS selectors should describe hierarchy, not the look as the look can change
607 |
608 | ### [Variable naming](#variable-naming)
609 |
610 | #### Don't
611 | ```scss
612 | $foo: 20px;
613 |
614 | .foo {
615 | $foo-bar: (2 * $GRID_MARGIN);
616 | margin-left: $local_margin
617 | }
618 | ```
619 |
620 | #### Do
621 | ```scss
622 | $GRID_MARGIN: 20px;
623 |
624 | .foo {
625 | $local_margin: (2 * $GRID_MARGIN);
626 | margin-left: $local_margin
627 | }
628 | ```
629 |
630 | * For global variables use SCREAMING_SNAKE_CASE
631 | * For local variables use snake_case
632 |
633 | > **🗒 Note:** Local variables are only available in the declaration they are defined in.
634 |
635 | [ꜛ Back to TOC](#table-of-contents)
636 |
637 | ## Spacing
638 |
639 | * [Indenting](#indenting)
640 | * [Whitespace](#whitespace)
641 |
642 | ### [Indenting](#indenting)
643 |
644 | #### Don't
645 | ```scss
646 | .foo {
647 | color: red;
648 |
649 | &_bar {
650 | // No good
651 | }
652 | }
653 | ```
654 |
655 | #### Do
656 | ```scss
657 | .foo {
658 | color: red;
659 |
660 | &_bar {
661 | // Better
662 | }
663 | }
664 | ```
665 |
666 | * Indent with 2 spaces, not tabs or 4 spaces
667 | * Shopify uses 2 spaces
668 | * Using spaces is easier to copy and paste
669 |
670 | ### [Whitespace](#whitespace)
671 |
672 | #### Don't
673 | ```scss
674 | .foo{box-shadow: 0 0 rgba(0,0,0,0.5);color:red;font:{size:1em;weight:700;}}
675 |
676 | .foo,.spanner,.foobar{
677 | color:red;
678 | .baz{color:blue}}
679 |
680 | .foo>.bar {color:red;}
681 | ```
682 |
683 | #### Do
684 | ```scss
685 | .foo {
686 | box-shadow: 0 0 rgba(0, 0, 0, 0.5);
687 | color: rgb(255, 0, 0);
688 | font-size: 1rem;
689 | font-weight: 700;
690 | }
691 |
692 | .foo, .foobar,
693 | .spanner {
694 | color: rgb(0, 255, 0);
695 |
696 | .baz {
697 | color: rgb(0, 0, 255);
698 | }
699 | }
700 |
701 | .foo > .bar {
702 | color: rgb(255, 0, 0);
703 | }
704 | ```
705 |
706 | * Add whitespace after commas (including property values), after selector name, after property colon and before and after child selector
707 | * Each element selector block should have an empty line above it
708 | * Each property on a new line and a new line for the closing }
709 | * Keep related selectors on the same line, separate selectors go on a new line
710 | * Add spaces between child selectors
711 | * Put a semi-colon after a property, even if it's the last one
712 | * Put a newline after the last selector
713 |
714 | #### Why
715 | * It's easier to read
716 | * Whitespace is free
717 | * Code is minified anyway
718 |
719 | [ꜛ Back to TOC](#table-of-contents)
720 |
721 | ## Formatting
722 |
723 | * [Calculations](#calculation)
724 | * [Capitalisation](#capitalisation)
725 | * [Commenting (inline)](#commenting-inline)
726 | * [Commenting (introductory)](#commenting-introductory)
727 | * [Zero values & units](#zero-values-&-units)
728 | * [Parenthesise on @includes](#parenthesise-on-includes)
729 |
730 | ### [Calculations](#calculation)
731 |
732 | #### Don't
733 | ```scss
734 | .foo {
735 | left: calc(100% - 30px * 2);
736 | width: 100% / 3;
737 | }
738 | ```
739 |
740 | #### Do
741 | ```scss
742 | .foo {
743 | left: calc(100% - (#{$GUTTER} * 2));
744 | width: (100% / 3);
745 | }
746 | ```
747 |
748 | * Wrap calculations in brackets for readability
749 | * Put spaces between the values and the mathematical operators
750 | * Put multiplying and dividing values after the starting value
751 |
752 | #### Also
753 | ```scss
754 | .foo {
755 | $local_margin: (2 * $GLOBAL_MARGIN);
756 |
757 | margin-bottom: $local_margin;
758 | width: calc(100% + #{$local_margin});
759 | }
760 | ```
761 |
762 | * Use local variables to define adjusted global variables
763 |
764 | ### [Capitalisation](#capitalisation)
765 |
766 | #### Don't
767 | ```scss
768 | .Foo {
769 | Background: $color_Blue;
770 | }
771 | ```
772 |
773 | #### Do
774 | ```scss
775 | .foo {
776 | background: $COLOR_CORNFLOWER_BLUE;
777 | }
778 | ```
779 |
780 | * Use lowercase for selectors and properties
781 | * Refer to [Variable naming](#variable-naming) for global and local variables
782 |
783 | ### [Commenting (inline)](#commenting-inline)
784 |
785 | #### Don't
786 | ```scss
787 | .foo {
788 | background-color: red; // Don't
789 | /* padding-top: 30px;
790 | width: 100% */
791 | }
792 | ```
793 |
794 | #### Do
795 | ```scss
796 | .foo {
797 | // Comment above the line
798 | background-color: red;
799 | // padding-top: 30px;
800 | // width: 100%;
801 | }
802 | ```
803 |
804 | * Use `//` for commenting not the block level `/* */`
805 | * It's easier to un-comment
806 | * Inline comments should start on a new line preceding the property they're describing
807 | * Use sentence case for your comments
808 | * Do not end with a full stop
809 |
810 | ### [Commenting (introductory)](#commenting-introductory)
811 |
812 | ```css
813 | /**
814 | * Component: Footer Social
815 | * -----------------------------------------------------------------------------
816 | * Social icons and links in footer.
817 | *
818 | */
819 | ```
820 |
821 | * Include an introductory comment at the start of each file
822 | * Describe what folder it's in, the file's name, and list any special features or conditions
823 | * All lines except the first one should have a full stop
824 |
825 | ### [Zero values & units](#zero-values-&-units)
826 |
827 | #### Don't
828 | ```scss
829 | .foo {
830 | animation-delay: 0;
831 | margin: 0px;
832 | opacity: .4567;
833 | }
834 | ```
835 |
836 | #### Do
837 | ```scss
838 | .foo {
839 | animation-delay: 0s;
840 | margin: 0;
841 | opacity: 0.4;
842 | }
843 | ```
844 |
845 | * Do specify units on zero duration times
846 | * Don't specify units on zero length values
847 | * Do add a leading zero for decimal places
848 | * Don't go to more than three decimal places, the fewer the better
849 |
850 | ### [Parenthesise on @includes](#parenthesise-on-includes)
851 |
852 | #### Where
853 | ```scss
854 | @mixin animation($property: color) {
855 | // Code
856 | }
857 |
858 | @mixin visually-hidden() {
859 | // Code
860 | }
861 | ```
862 |
863 | #### Don't
864 | ```scss
865 | .foo {
866 | @include animation(background-color);
867 | @include visually-hidden();
868 | }
869 | ```
870 |
871 | #### Do
872 | ```scss
873 | .foo {
874 | @include animation(background-color);
875 | @include visually-hidden;
876 | }
877 | ```
878 |
879 | * Do not include parenthesise on argument-less mixins
880 |
881 | [ꜛ Back to TOC](#table-of-contents)
882 |
883 | ## Colours
884 |
885 | * [Colour properties](#colour-properties)
886 | * [Colour variables](#colour-variables)
887 |
888 | ### [Colour properties](#colour-properties)
889 |
890 | #### Don't
891 | ```scss
892 | .foo {
893 | color: RED;
894 | // Or
895 | color: #FF0000;
896 | // Or
897 | color: hsl(0, 100%, 50%);
898 | }
899 | ```
900 |
901 | #### Do
902 | ```scss
903 | .foo {
904 | color: rgb(255, 0, 0);
905 | // Or
906 | color: rgba(255, 0, 0, 0.5);
907 | }
908 | ```
909 |
910 | * Use RGB (or RGBA) colour values as they are more human readable
911 | * These values are often supplied in brand guidelines
912 |
913 | > **❗Exception:** If required, use lowercase and shorthand (where possible) hex colour values and names.
914 |
915 | ### [Colour variables](#colour-variables)
916 |
917 | #### Don't
918 | ```scss
919 | $colour-blue-other: #6195ed;
920 | $colour-dark-grey: #E2E3E4;
921 | $lighter-grey: #d4d7d9;
922 | ```
923 |
924 | #### Do
925 | ```scss
926 | $COLOR_CORNFLOWER_BLUE: rgb(97,149,237);
927 | $COLOR_IRON_1: rgb(226,227,228);
928 | $COLOR_IRON_2: rgb(212,215,217);
929 | ```
930 |
931 | * All colours should have variables defined for them, this will come from the design
932 | * Use color as the prefix for your variables (not colour, this is consistent with the CSS spec)
933 | * Colours are global variables so they should be in all caps
934 | * Use [this website](http://chir.ag/projects/name-that-color/#6195ED) to automatically generate colour names (unless they're named in the client's brand guidelines)
935 | * If you have two or more colours similar enough to share the same name then suffix them with a number where the 1 is the darkest variant of the colour
936 |
937 | > **🗒 Note:** VS Code automatically previews the colour.
938 |
939 | [ꜛ Back to TOC](#table-of-contents)
940 |
941 | ## Nesting
942 |
943 | * [Nesting levels](#nesting-levels)
944 | * [Nesting media queries](#nesting-media-queries)
945 |
946 | ### [Nesting levels](#nesting-levels)
947 |
948 | #### Don't
949 | ```scss
950 | .foo {
951 | .this {
952 | .is {
953 | .very {
954 | .bad {
955 |
956 | }
957 | }
958 | }
959 | }
960 |
961 | @media (max-width: 700px) {
962 | .this .is .very .bad {
963 |
964 | }
965 | }
966 | }
967 | ```
968 |
969 | #### Do
970 | ```scss
971 | .foo {
972 | // Base level (not included)
973 |
974 | &__bar {
975 | // Level one
976 |
977 | &:hover {
978 | // Level two
979 |
980 | &.bar {
981 | // Level three
982 | }
983 | }
984 | }
985 |
986 | @media (min-width: 700px) {
987 | // Base level (not included)
988 |
989 | &__bar {
990 | // Level one
991 |
992 | &:hover {
993 | // Level two
994 |
995 | &.bar {
996 | // Level three
997 | }
998 | }
999 | }
1000 | }
1001 | }
1002 | ```
1003 |
1004 | * Don't nest more than 3 levels
1005 | * Requires an even more specific selector to override it
1006 | * Mounts up to a significant maintenance issue
1007 | * This does mean that you can't always nest properties the way you normally would
1008 | * Media queries are not included when counting levels of nesting
1009 |
1010 | > **🗒 Note:** VS Code shows you how many levels deep you are in its Breadcrumbs feature (View > Toggle Breadcrumbs). You should only ever see three stages after the base declaration.
1011 |
1012 | ### [Nesting media queries](#nesting-media-queries)
1013 |
1014 | #### Don't
1015 | ```scss
1016 | .foo {
1017 |
1018 | &__bar {
1019 | // Code
1020 |
1021 | @media (min-width: 568px) {
1022 | // Code
1023 | }
1024 | }
1025 |
1026 | &__section {
1027 | // Code
1028 |
1029 | @media (min-width: 568px) {
1030 | // Code
1031 | }
1032 | }
1033 | }
1034 | ```
1035 |
1036 | #### Do
1037 | ```scss
1038 | .foo {
1039 |
1040 | &__bar {
1041 | // Code
1042 | }
1043 |
1044 | &__section {
1045 | // Code
1046 | }
1047 |
1048 | @media (min-width: 568px) {
1049 | &__bar {
1050 | // Code
1051 | }
1052 |
1053 | &__section {
1054 | // Code
1055 | }
1056 | }
1057 | }
1058 | ```
1059 |
1060 | * Keep media queries at the root of the declaration, not nested inside each selector
1061 | * This way it's easier to find media queries
1062 | * Frame 2+ keeps CSS files small and modular so `@media`/`@include mq()` won't get lost
1063 |
1064 | [ꜛ Back to TOC](#table-of-contents)
1065 |
1066 | ## Properties
1067 |
1068 | * [border](#border)
1069 | * [font-family](#font-family)
1070 | * [font-size](#font-size)
1071 | * [letter-spacing](#letter-spacing)
1072 | * [line-height](#line-height)
1073 | * [margin-top](#margin-top)
1074 | * [transition](#transition)
1075 |
1076 | ### [border](#border)
1077 |
1078 | #### Don't
1079 | ```scss
1080 | .foo {
1081 | border: none;
1082 | }
1083 | ```
1084 |
1085 | #### Do
1086 | ```scss
1087 | .bar {
1088 | border: 2px solid rgb(0, 0, 0);
1089 |
1090 | &:hover {
1091 | border-color: rgb(255, 255, 255);
1092 | }
1093 | }
1094 | ```
1095 |
1096 | * Use 0 instead of none for borders
1097 | * When a border is set to 0 it will never display, however if a border is set to none but later overridden by a border-style it will display
1098 | * If you're changing a single part of the property then target that specific property rather than setting all the properties again, this is much easier to maintain
1099 |
1100 | ### [font-family](#font-family)
1101 |
1102 | #### Don't
1103 | ```scss
1104 | .foo {
1105 | font-family: 'Avenir';
1106 | }
1107 | ```
1108 |
1109 | #### Do
1110 | ```scss
1111 | .foo {
1112 | font-family: 'Avenir', Helvetica, Arial, sans-serif;
1113 | }
1114 | ```
1115 |
1116 | * Don't only declare the custom font
1117 | * Always provide a font stack which includes web-safe fonts
1118 | * This prevents loading blank pages until the font loads
1119 | * The `font-family` should be declared in a variable, do not individually declare the `font-family`
1120 |
1121 | ### [font-size](#font-size)
1122 |
1123 | #### Don't
1124 | ```scss
1125 | .foo {
1126 | font-size: 12px;
1127 | }
1128 |
1129 | .bar {
1130 | font-size: 1em;
1131 | }
1132 | ```
1133 |
1134 | #### Do
1135 | ```scss
1136 | .foo {
1137 | @include ms-respond(font-size, 1);
1138 | // Or
1139 | font-size: rem(18);
1140 | // Or
1141 | font-size: 1rem;
1142 | }
1143 | ```
1144 |
1145 | * Use rem, relative ems. They're easier to understand as they're always relative to the base font-size
1146 | * Frame 3 comes with a responsive font `@mixin`, use this
1147 | * Most Frame 2 themes come with a REM function which lets your use pixel sizes
1148 |
1149 | #### Don't
1150 | ```scss
1151 | .foo {
1152 | font-size: 1.263rem;
1153 | }
1154 | ```
1155 |
1156 | * This should be considered at the design phase but keep font sizes relative to each other in rational numbers
1157 |
1158 | ### [letter-spacing](#letter-spacing)
1159 |
1160 | #### Don't
1161 | ```scss
1162 | .foo {
1163 | letter-spacing: 0.001rem;
1164 | }
1165 |
1166 | .bar {
1167 | letter-spacing: 0.02em;
1168 | }
1169 | ```
1170 |
1171 | #### Do
1172 | ```scss
1173 | .foo {
1174 | letter-spacing: 1px;
1175 | }
1176 | ```
1177 |
1178 | * Use pixel values for `letter-spacing`
1179 |
1180 | ### [line-height](#line-height)
1181 |
1182 | #### Don't
1183 | ```scss
1184 | .foo {
1185 | line-height: 18.5px;
1186 | }
1187 |
1188 | .bar {
1189 | line-height: 2.35rem;
1190 | }
1191 | ```
1192 |
1193 | #### Do
1194 | ```scss
1195 | .foo {
1196 | line-height: 1.6;
1197 | }
1198 | ```
1199 |
1200 | * Use unit-less relative `line-height`
1201 | * This means `line-height` will scale with the font size rather than being fixed
1202 | * Another consideration at the design phase is to keep line height relative to the font size in rational numbers
1203 |
1204 | ### [margin-top](#margin-top)
1205 |
1206 | #### Don't
1207 | ```scss
1208 | .foo {
1209 | margin-top: 30px;
1210 | }
1211 | ```
1212 |
1213 | #### Do
1214 | ```scss
1215 | .bar {
1216 | &:not(:last-child) {
1217 | margin-bottom: 30px;
1218 | }
1219 | }
1220 | ```
1221 |
1222 | * Don't use margin-top to space elements out as vertical margins collapse
1223 | * Use padding-top or margin-bottom on preceding elements
1224 |
1225 | > **❗Exception:** When you need a negative `margin-top` or when the element is toggled below a preceding element so you don't have to add a `margin-bottom` to the preceding element and then toggle a class to remove it
1226 |
1227 | ### [transition](#transition)
1228 |
1229 | #### Don't
1230 | ```scss
1231 | .foo {
1232 | transition: ease transform 0.4s 0.2s;
1233 | }
1234 | ```
1235 |
1236 | #### Do
1237 | ```scss
1238 | .foo {
1239 | transition: transform 0.4s ease 0.2s;
1240 | will-change: transform
1241 | }
1242 | ```
1243 |
1244 | * Make sure you put your transition properties in the correct order:
1245 | ```scss
1246 | transition: [transition-property] [transition-duration] [transition-timing-function] [transition-delay];
1247 | ```
1248 | * Use `will-change` to highlight to the browser that the property will change, this helps performance as the browser will then use hardware acceleration (not widely supported yet)
1249 | * Use this only when the transition is used frequently ie: navigation drawer
1250 | * Avoid overusing it as it could have the opposite effect and hinder all animations
1251 |
1252 | > **⚠️ Important!** Do not transition `margin` or positional properties (`top`, `left`, `right`, `bottom`) as these don't perform well and lead to poor frame rates, instead use `padding` or `transform` as they can be hardware-accelerated.
1253 |
1254 | [ꜛ Back to TOC](#table-of-contents)
--------------------------------------------------------------------------------
/frame/html/README.md:
--------------------------------------------------------------------------------
1 | # HTML Guidelines
2 |
3 | The below guidelines cover only specific scenarios and should not be considered exhaustive. For more general HTML guidelines visit [Google's HTML Style guide](https://google.github.io/styleguide/htmlcssguide.html).
4 |
5 | **Where there are differences our guidelines take precedence.**
6 |
7 | ## Table of contents
8 |
9 | 1. [Attributes](#attributes)
10 | 1. [Characters](#characters)
11 | 1. [Commenting](#commenting)
12 | 1. [`` or `
`](#div-or-span)
13 | 1. [Indenting](#indenting)
14 | 1. [Spacing & line character limits](#spacing--line-character-limits)
15 | 1. [Self-closing element](#self-closing-elements)
16 |
17 | [← Back to homepage](../README.md)
18 |
19 | ## Attributes
20 |
21 | * [Attribute order](#attribute-order)
22 | * [Attribute values](#attribute-values)
23 |
24 | ### [Attribute order](#attribute-order)
25 |
26 | #### Don't
27 | ```html
28 |
29 | >
30 | ```
31 |
32 | #### Do
33 | ```html
34 |
48 | ```
49 |
50 | * When an element has more than two attributes they should be stacked on newlines
51 | * They should follow this order:
52 | * `id`
53 | * `class`
54 | * {native}
55 | * `aria-`
56 | * `data-`
57 | * `js-`
58 | * {native} covers everything else, items within {native} should be in alphabetical order
59 | * This makes it easier for managing merges in Git
60 | * Make sure you trim trailing spaces after each line
61 |
62 | > **🗒 Note:** Be sure to follow the formatting; the trailing `>` should be on a newline with the attributes indented in two spaces.
63 |
64 | ### Also
65 | ```html
66 |
67 |
68 |
72 |
73 | ```
74 |
75 | * Use multi-line attributes if as a single line it would exceed 80 characters
76 |
77 | ### [Attribute values](#attribute-values)
78 |
79 | * `id` values should be PascalCase
80 | * `class` values should follow BEM naming conventions
81 | * `js-` attributes should be camelCase
82 |
83 | [ꜛ Back to TOC](#table-of-contents)
84 |
85 | ## [Characters](#characters)
86 |
87 | ### Don't
88 | ```html
89 |
90 | ```
91 |
92 | ### Do
93 | ```html
94 |
95 | ```
96 |
97 | * Use quotations `"` HTML elements, not apostrophes `'`
98 |
99 | [ꜛ Back to TOC](#table-of-contents)
100 |
101 | ## [Commenting](#commenting)
102 |
103 | ### Don't
104 | ```html
105 |
106 | ```
107 |
108 | ### Do
109 | ```html
110 | {% comment %} Comment goes here {% endcomment %}
111 | ```
112 |
113 | * Do not use HTML comments, use the `{% comment %}` filter with whitespace operators
114 | * Liquid `{% comment %}` will not be rendered in the HTML
115 | * Use spaces inside the `{% comment %}` tag
116 |
117 | [ꜛ Back to TOC](#table-of-contents)
118 |
119 | ## [`` or `
`](#div-or-span)
120 |
121 | `` and `
` are often used interchangeably but there are specific use cases for each:
122 |
123 | * If the content is going to be displayed inline, use ``
124 | * If you want the element to display block, on its own line, use ``
125 | * If you're using CSS to change its `display` property, consider using the other
126 |
127 | [ꜛ Back to TOC](#table-of-contents)
128 |
129 | ## [Indenting](#indenting)
130 |
131 | ### Don't
132 | ```html
133 |
134 | {{ product.description }}
135 |
136 | ```
137 |
138 | ### Do
139 | ```html
140 |
141 | {{ product.description }}
142 |
143 | ```
144 |
145 | * Indent two spaces inside each opening HTML element
146 |
147 | [ꜛ Back to TOC](#table-of-contents)
148 |
149 | ## [Spacing & line character limits](#spacing--line-character-limits)
150 |
151 | ### Don't
152 | ```html
153 |
154 |
{{ product.title }}
155 |
{{ product.type }}
156 |
{% render 'icon-misc' with icon: 'plus' %} {{ 'products.product.add_to_cart' | t }}
157 |
158 |
{{ product.description }}
159 |
{{ pages['shipping'].content | strip_html }}
160 |
161 |
162 |
163 | ```
164 |
165 | ### Do
166 | ```html
167 |
173 |
{{ product.title }}
174 |
{{ product.type }}
175 |
176 |
177 |
178 | {% render 'icon-misc' with icon: 'plus' %}
179 |
180 |
181 |
182 | {{ 'products.product.add_to_cart' | t }}
183 |
184 |
185 |
186 |
187 |
188 | {{ product.description }}
189 |
190 |
191 |
192 | {{ pages['shipping'].content }}
193 |
194 |
195 |
196 |
207 |
208 | ```
209 |
210 | * Use common sense spacing to improve the readability of your code
211 | * [Block-level elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements) should open onto a new line and indent
212 | * [Inline-level elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements#Elements) should be written on a single line
213 | * Single line elements should be grouped together by type
214 | * Multi-line elements should have a newline between them
215 | * This guideline includes empty block-level elements, e.g. the following is correct:
216 |
217 | ```html
218 |
223 | ```
224 |
225 | * Once you are writing something over multiple lines, each line should only have one attribute or value on it
226 | * If an attribute's value exceeds 80 characters then it should be split in the following format:
227 |
228 | ```html
229 |
239 |
240 |
241 | ```
242 |
243 | ### Exceptions
244 |
245 | * Short block-level elements like `
`, ` ` or `` can be displayed on a single line unless they're greater than 80 characters long
246 | * Inline-level elements greater than 80 characters long should follow multi-line rules
247 |
248 | [ꜛ Back to TOC](#table-of-contents)
249 |
250 | ## [Self-closing elements](#self-closing-elements)
251 |
252 | ### Don't
253 | ```html
254 |
255 |
256 | ```
257 |
258 | ### Do
259 | ```html
260 |
261 |
262 | ```
263 |
264 | * The `/` in self-closing tags is obsolete, do not use it
265 |
266 | [ꜛ Back to TOC](#table-of-contents)
--------------------------------------------------------------------------------
/frame/liquid/README.md:
--------------------------------------------------------------------------------
1 | # Liquid Guidelines
2 |
3 | The below guidelines cover only specific scenarios and should not be considered exhaustive. For more general HTML guidelines visit [Shopify's Liquid file requirements for themes](https://help.shopify.com/en/themes/development/theme-store-requirements/theme-file-requirements).
4 |
5 | **Where there are differences our guidelines take precedence.**
6 |
7 | ## Shopify Cheatsheet
8 |
9 | The [Shopify Cheatsheet](https://www.shopify.co.uk/partners/shopify-cheat-sheet) is no longer up-to-date. For a full and complete list of available objects, tags, and filters see the [Liquid reference](https://help.shopify.com/en/themes/liquid/objects) documentation.
10 |
11 | ## Table of contents
12 |
13 | 1. [Commenting](#commenting)
14 | 1. [Conditional statements](#conditional-statements)
15 | 1. [DRY (Don't Repeat Yourself)](#dry-dont-repeat-yourself)
16 | 1. [Formatting](#formatting)
17 | 1. [Language strings](#language-strings)
18 | 1. [Naming](#naming)
19 | 1. [Schema settings](#schema-settings)
20 | 1. [Snippets](#snippets)
21 | 1. [Split characters](#split-characters)
22 | 1. [Variables](#variables)
23 | 1. [Whitespace controls](#whitespace-controls)
24 |
25 | [← Back to homepage](../README.md)
26 |
27 | ## Commenting
28 |
29 | * [Inline](#inline)
30 | * [Introductory](#introductory)
31 |
32 | ### [Inline](#inline)
33 |
34 | #### Don't
35 | ```html
36 |
37 | {% if has_tag %}
38 | {{ product.description }}
39 | {% endif %}
40 | ```
41 |
42 | #### Do
43 | ```html
44 | {% comment %} Only display description if tag is present {% endcomment %}
45 | {% if has_tag %}
46 |
47 | {{ product.description }}
48 |
49 | {% endif %}
50 | ```
51 |
52 | * Use the `{% comment %}` tag for inline comments, not HTML comments
53 | * For `.js.liquid` or `.scss.liquid` file use the language appropriate comment
54 |
55 | ### [Introductory](#introductory)
56 |
57 | #### Do
58 | ```html
59 | {% comment %}
60 | ------------------------------------------------------------------------------
61 | Section: Featured link
62 | - Used to display up to 2 images with text overlays with links.
63 | ------------------------------------------------------------------------------
64 | {% endcomment %}
65 | ```
66 |
67 | * Include an introductory comment at the start of each file
68 | * Describe what folder it's in, the file's name, and list any special features or conditions
69 | * If it's something included using a `{% render %}` then show an example with its variables, e.g.
70 | ```html
71 | {% comment %}
72 | ------------------------------------------------------------------------------
73 | Snippet: Responsive image
74 | It creates a style tag and it restricts an image from growing larger than its max resolution.
75 |
76 | Usage:
77 | In your liquid template file, copy the following line
78 | - {% render 'responsive-image' with image: featured_image, image_class: "css-class", wrapper_class: "wrapper-css-class", max_width: 700, max_height: 800 %}
79 | ------------------------------------------------------------------------------
80 | {% endcomment %}
81 | ```
82 |
83 | [ꜛ Back to TOC](#table-of-contents)
84 |
85 | ## Conditional statements
86 |
87 | * [Conditional settings](#conditional-settings)
88 | * [Conditional spacing](#conditional-spacing)
89 | * [`{% if %}` or `{% case %}`](#-if--or--case-)
90 | * [Inline `{% if %}` statements](#inline--if--statements)
91 |
92 | ### [Conditional settings](#conditional-settings)
93 |
94 | #### Don't
95 | ```html
96 | {% for block in section.blocks %}
97 |
107 | {% endfor %}
108 | ```
109 |
110 | #### Do
111 | ```html
112 | {% for block in section.blocks %}
113 |
114 | {% if block.settings.image != '' %}
115 |
119 | {% endif %}
120 |
121 | {% if block.settings.title != '' %}
122 |
{{ block.settings.title }}
123 | {% endif %}
124 |
125 | {% if block.settings.text != '' %}
126 |
127 | {{ block.settings.text }}
128 |
129 | {% endif %}
130 |
131 | {% if block.settings.button url != '' and block.settings.button_text != '' %}
132 |
133 | {{ block.settings.button_text }}
134 |
135 | {% endif %}
136 |
137 | {% endfor %}
138 | ```
139 |
140 | * In areas where the client can customise the content do not assume that they will want to display every part of a section's settings
141 | * Wrap each setting in a `{% if %}` to hide it if no content is entered
142 | * Use `!= ''` rather than `{% if condition %}` or `!= blank` as it is more reliable, it is possible for a setting to exist if it previously had a value
143 | * When testing make sure the section does not appear visually broken, the client will expect it to work with missing settings
144 |
145 | ### [Conditional spacing](#conditional-spacing)
146 |
147 | #### Don't
148 | ```html
149 | {% if condition %}
150 |
151 |
152 |
153 | {% else %}
154 |
155 |
156 |
157 | {% endif %}
158 | ```
159 |
160 | #### Do
161 | ```html
162 | {% if condition %}
163 |
164 |
165 |
166 |
167 | {% else %}
168 |
169 |
170 |
171 | {% endif %}
172 | ```
173 |
174 | * When an `{% if %}` tag spans multiple lines add a newline before the `{% else %}` tag
175 | * This helps visually separate the code and make it easier to scan
176 |
177 | ### [`{% if %}` or `{% case %}`](#-if--or--case-)
178 |
179 | #### Don't
180 | ```html
181 | {% if variable == 'Hello' %}
182 | Content
183 | {% elsif variable == 'Goodbye' %}
184 | Content
185 | {% elsif variable == 'Good night' %}
186 | Content
187 | {% else %}
188 | Content
189 | {% endif %}
190 | ```
191 |
192 | #### Do
193 | ```html
194 | {% case variable %}
195 | {% when 'Hello' %}
196 | Content
197 | {% when 'Goodbye' %}
198 | Content
199 | {% when 'Good night '%}
200 | Content
201 | {% else %}
202 | Content
203 | {% endcase %}
204 | ```
205 |
206 | * Do not use `{% if %}` for more than two conditions, use `{% case %}` instead
207 | * `{% case %}` can only be used when exactly matching a condition, you can't use it to test if something `contains`, use `{% if %}` for this purpose
208 |
209 | ### [Inline `{% if %}` statements](#inline--if--statements)
210 |
211 | #### Don't
212 | ```html
213 |
214 | ```
215 |
216 | #### Do
217 | ```html
218 | {% if section.settings.width == 'full' or section.settings.display == 'full' %}
219 | {% assign row_class = 'row--full-width' %}
220 | {% endif %}
221 |
222 |
229 |
230 |
231 | ```
232 |
233 | * Do not put long `{% if %}` tags inline, assign them separately
234 | * If the `{% if %}` tag is short you can put it inline
235 | * Only include the space inside the `{% if %}` tag if it's on a single line, otherwise there is no need to include a space
236 |
237 | [ꜛ Back to TOC](#table-of-contents)
238 |
239 | ## [DRY (Don't Repeat Yourself)](#dry-dont-repeat-yourself)
240 |
241 | ### Don't
242 | ```html
243 |
244 |
245 |
246 |
250 |
251 |
252 |
{{ block.settings.slide_1_text }}
253 |
{{ block.settings.slide_1_text }}
254 |
255 |
264 |
265 |
266 |
267 |
268 | ...
269 | ...
270 | ...
271 | ```
272 |
273 | ### Do
274 | ```html
275 | {% for i in (1..4) %}
276 | {% assign slide_image = 'slide_#_image' | replace: '#', i %}
277 | {% assign slide_heading = 'slide_#_heading' | replace: '#', i %}
278 | {% assign slide_text = 'slide_#_text' | replace: '#', i %}
279 |
280 |
281 |
285 |
286 |
287 |
{{ block.settings[slide_heading] }}
288 |
289 |
290 | {{ block.settings[slide_text] }}
291 |
292 |
293 |
310 |
311 |
312 | {% endfor %}
313 | ```
314 |
315 | * Use `{% for i in (1..#) %}` in combination with `{% assign %}` and replace to remove repetitive code
316 | * Use the iteration to set variables which are then used to call the block's settings
317 | * Use Liquid's built in tags to avoid repeating code
318 |
319 | [ꜛ Back to TOC](#table-of-contents)
320 |
321 | ## Formatting
322 |
323 | * [`{% render %}`](#-render-)
324 | * [Characters](#characters)
325 | * [Indenting](#indenting)
326 | * [Spacing & line character limits](#spacing--line-character-limits)
327 |
328 | ### [`{% render %}`](#-render-)
329 |
330 | > 🗒 **Note:** `{% include %}` tags have been deprecated by Shopify and replaced with the `{% render %}` tag. [For full details visit this page](https://help.shopify.com/en/themes/liquid/tags/theme-tags#render).
331 |
332 | #### Don't
333 | ```html
334 | {% include 'icon-misc', icon: 'search', colour: 'red' %}
335 | {% render 'social-sharing' with share_title: product.title, share_permalink: product.url, share_image: product.featured_image %}
336 | ```
337 |
338 | #### Do
339 | ```html
340 | {% render 'icon-misc' with icon: 'search', colour: 'red' %}
341 |
342 | {% render 'social-sharing' with
343 | share_image: product.featured_image,
344 | share_permalink: product.url,
345 | share_title: product.title,
346 | %}
347 | ```
348 |
349 | * When setting variables on your `{% render %}` always use `with` instead of starting with a comma
350 | * After the first variable declaration you must use a comma `,`
351 | * If there are more than two variables and it goes over 80 characters then break it into a multi-line tag
352 | * Sort the variables alphabetically and end each line with a comma `,`
353 |
354 | ### [Characters](#characters)
355 |
356 | #### Don't
357 | ```html
358 | {% assign variable = "It's time for fun" %}
359 | ```
360 |
361 | #### Do
362 | ```html
363 | {% assign variable = 'It\'s time for fun' %}
364 | ```
365 |
366 | * Use apostrophes `'` in Liquid objects, tags, and filters, not quotations `"`
367 | * Escape apostrophes if they appear inside the string
368 |
369 | ### [Indenting](#indenting)
370 |
371 | #### Don't
372 | ```html
373 | {% if variable %}
374 | {{ product.title }}
375 | {% endif %}
376 | ```
377 |
378 | #### Do
379 | ```html
380 | {% if variable %}
381 | {{ product.title }}
382 | {% endif %}
383 | ```
384 |
385 | * Treat opening Liquid tags the same as HTML elements; indent two spaces inside
386 |
387 | ### [Spacing & line character limits](#spacing--line-character-limits)
388 |
389 | #### Don't
390 | ```html
391 | {% if template contains 'search' or template contains 'account' or template contains 'customer' or template contains 'cart' %} {% endif %}
392 | {% assign sanitized_var = string|downcase|split: '/'|last|remove:''|remove:'
'|money_with_currency %}
393 | {%if variable%}{{ product.title }}
394 | {{ product.price | money }}
{% else %}{{ product.title }} {%endif%}
395 | ```
396 |
397 | #### Do
398 | ```html
399 | {% if
400 | template contains 'search' or
401 | template contains 'account' or
402 | template contains 'customer' or
403 | template contains 'cart'
404 | %}
405 |
406 | {% endif %}
407 |
408 | {% assign sanitized_variable = string |
409 | downcase |
410 | split: '/' |
411 | last |
412 | remove:'' |
413 | remove:'
' %} |
414 | money_with_currency
415 | %}
416 |
417 | {% if variable %}
418 | {{ product.title }}
419 |
420 |
428 | {{ product.price | money }}
429 |
430 |
431 | {% else %}
432 | {{ product.title }}
433 | {% endif %}
434 | ```
435 |
436 | * Add spaces inside each object and tag
437 | * Add spaces around filters
438 | * Separate blocks of Liquid code with a newline
439 | * Opening `{% if %}` tags should be on separate lines
440 | * `{% if %}` tags with more than two filters and exceeding 80 characters should be split onto multiple lines
441 | * If the contents of an `{% if %}` tag spans more than two lines add a newline before any following `{% elsif %}` or `{% else %}` tags
442 | * `{% assign %}` tags with more than two filters and exceeding 80 characters should be broken up over multiple lines and indented
443 | * Once you are writing something over multiple lines, each line should only have one attribute or value on it
444 | * For details on HTML spacing see the [HTML](../html/README.md) rule for [Spacing & line character limit](../html/README.md#spacing--line-character-limits)
445 |
446 | [ꜛ Back to TOC](#table-of-contents)
447 |
448 | ## [Language strings](#language-strings)
449 |
450 | ### Don't
451 | ```html
452 |
453 |
{{ product.title }}
454 |
455 |
456 | {% if product.available %}
457 | In stock
458 | {% else %}
459 | Out of stock
460 | {% endif %}
461 |
462 |
463 | ```
464 |
465 | ### Do
466 | ```html
467 |
468 |
{{ product.title }}
469 |
470 |
471 | {% if product.available %}
472 | {{ 'products.product.in_stock' | t }}
473 | {% else %}
474 | {{ 'products.product.out_of_stock' | t }}
475 | {% endif %}
476 |
477 |
478 | ```
479 |
480 | * Never hard-code text in your template files
481 | * Always use Shopify's [translation filter](https://help.shopify.com/en/themes/development/theme-store-requirements/internationalizing/translation-filter)
482 | * Most of our clients are multi-lingual so it is expected that they will be able to translate their store without requiring additional development
483 |
484 | [ꜛ Back to TOC](#table-of-contents)
485 |
486 | ## Naming
487 |
488 | 1. [Section template naming](#section-template-naming)
489 | 1. [Snippet naming](#Snippet-naming)
490 | 1. [Tag naming](#tag-naming)
491 |
492 | ### [Section template naming](#section-template-naming)
493 |
494 | ```html
495 | template-[section].liquid
496 | ```
497 |
498 | * If a section replaces the template's content it should be prefixed with `template-`
499 | * E.g. `template-product.liquid` or `template-collection.liquid`
500 |
501 | ### [Snippet naming](#snippet-naming)
502 |
503 | ```html
504 | icon-[snippet].liquid
505 | ```
506 |
507 | * Snippets used to contain inline SVGs should be prefixed with `icon-`
508 | * E.g. `icon-payment.liquid`
509 |
510 | ```html
511 | section-[snippet].liquid
512 | ```
513 |
514 | * Snippets that contain a section's content should be prefixed with `section-`
515 | * E.g. `section-hero.liquid` or `section-featured-collection.liquid`
516 |
517 | ### [Tag naming](#tag-naming)
518 |
519 | ```html
520 | tag_name
521 | tag_name: [value]
522 | tag_name: [value1]_[value2] (etc.)
523 | ```
524 |
525 | * Use the naming convention `tag_name: [value]` for admin tags (products, orders, customers) with a value and `tag_name` for tags without a value
526 | * In most cases `[value]` should be in lowercase to make comparisons easier. However in instances where the value is outputted on the front-end in a specific case (e.g. Title Case) make sure this is clear in the tech spec
527 | * If you need to store separate values in the same tag then separate them using `_` such as `type_modal: [Model]_[Year]` (this isn't snake_case)
528 | * Do not use boolean values (e.g. `has_addon: true`) as simply having the tag in the first place is enough to know that it is `true` (e.g. `has_addon`)
529 | * If the tag is to be used in a search string (and therefore needs to be handlelised) then use the naming convention `tag_name--[value]`, this allows the value to be kebab-cased (e.g. `build_date--2019-07-03`)
530 | * Never put formatted money into the tag (e.g. `monthly_cost: £10`) as tags do not support all the formatting standards required by international stores (e.g. `monthly_cost: €12,34` will not be allowed because the comma ends the tag), instead use `monthly_cost: 1234` and format the cost using Liquid or JavaScript
531 |
532 | [ꜛ Back to TOC](#table-of-contents)
533 |
534 | ## Schema settings
535 |
536 | * [`default` & `label`](#default-&-label)
537 | * [Formatting & order](#formatting-&-order)
538 | * [`type`](#type)
539 |
540 | ### [`default` & `label`](#default-label)
541 | ```json
542 | {
543 | "type": "text",
544 | "id": "storeId",
545 | "label": "Store ID (enter a unique store ID)",
546 | }
547 | ```
548 |
549 | #### Do
550 | ```json
551 | {
552 | "type": "text",
553 | "id": "store_id",
554 | "label": "Store ID",
555 | "default": "dev_store",
556 | "info": "Enter a unique store ID using a snake case naming convention. e.g: gb_store"
557 | }
558 | ```
559 |
560 | * Don't try to fit everything into `label`, use `info` for more details
561 | * Add a default value where possible, this makes deployment easier
562 |
563 | ### [Formatting & order](#formatting-&-order)
564 |
565 | #### Don't
566 | ```json
567 | {
568 | "id": "storeId",
569 | "label": "Store ID (enter a unique store ID)",
570 | "type": "text"
571 | }
572 | ```
573 |
574 | #### Do
575 | ```json
576 | {
577 | "type": "text",
578 | "id": "store_id",
579 | "label": "Store ID",
580 | "default": "dev_store",
581 | "info": "Enter a unique store ID using a snake case naming convention. e.g: gb_store"
582 | }
583 | ```
584 |
585 | * Use the prescribed order, any other key value pairs should be added below in alphabetical order
586 | * Use snake case for `id`
587 | * Use sentence case for `label` and `info`
588 |
589 | ### [`Type`](#type)
590 |
591 | Specific rules for certain settings of `type`:
592 | * `range` – Use for fixed number choices (such as font size, height, duration etc.)
593 | * `richtext` – If you are changing `text` or `textarea` to `richtext` (or vice versa) you will need to delete any existing settings data as it will throw an error
594 | * `select` – Use for fixed text choices (not for numbers)
595 | * `textarea` – Do not use for raw HTML, use `html` for this
596 | * `url` – Only use for all choices, use one of the limited selections if you are only expecting a certain output (`collection`, `product`, `blog`, `page`, and `article`), do not use for video, use `video_url` for this
597 |
598 | [ꜛ Back to TOC](#table-of-contents)
599 |
600 | ## Snippets
601 |
602 | * [Section snippets](#section-snippets)
603 | * [Snippets general](#snippets-general)
604 |
605 | ### [Section snippets](#section-snippets)
606 |
607 | * Place the content for a section inside a snippet (see [snippet naming](#snippet-naming) for advice on naming)
608 | * This allows us to use a section anywhere as we can place the snippet inside a `{% case %}` block setup
609 |
610 | #### Example
611 |
612 | ##### Section
613 | ```html
614 | {% comment %}
615 | ----------------------------------------------------------------------------
616 | Section: Featured text
617 | – Large text banner.
618 | ----------------------------------------------------------------------------
619 | {% endcomment %}
620 |
625 | {% render 'section-featured-text' with object: section %}
626 |
627 |
628 | {% schema %}
629 | {
630 | "name": "Featured Text",
631 |
632 | }
633 | {% endschema %}
634 | ```
635 |
636 | ##### Snippet
637 | ```html
638 |
639 |
640 | {% if object.settings.title != '' %}
641 |
642 | {{ object.settings.title }}
643 |
644 | {% endif %}
645 |
646 | ```
647 |
648 | * With this setup you would then be able to add `{% render 'section-featured-text' with object: block %}` on a page other than the homepage
649 | * With `{% render %}` you would need to include all the parameters that the snippet requires
650 |
651 | ### [Snippets general](#snippets-general)
652 |
653 | * Use Liquid snippets to keep files small and manageable
654 | * In the same way a block gets its own SCSS and JS file, consider splitting it into its own snippet
655 | * Snippets are especially useful for repeating content
656 |
657 | [ꜛ Back to TOC](#table-of-contents)
658 |
659 | ## [Split characters](#split-characters)
660 |
661 | Split characters are used to effectively provide multiple description fields on the product page. It is useful when you need to output long form content in multiple locations.
662 |
663 | ### Example
664 | ```html
665 | {% if product.description != '' %}
666 | {% assign full_description = product.description |
667 | remove: ' ' |
668 | remove: ' ' |
669 | remove: '' |
670 | remove: ' ' |
671 | remove: '
' |
672 | remove: '
' |
673 | remove: '
'
674 | %}
675 |
676 | {% if full_description contains '---DESCRIPTION---' %}
677 | {% assign description = full_description |
678 | split: '---DESCRIPTION---
' |
679 | last |
680 | split: '---' |
681 | first
682 | %}
683 | {% endif %}
684 |
685 | {% if full_description contains '---SIZE GUIDE---' %}
686 | {% assign size_guide = full_description |
687 | split: '
---SIZE GUIDE---
' |
688 | last |
689 | split: '---' |
690 | first
691 | %}
692 | {% endif %}
693 | {% endif %}
694 |
695 | {% if description %}
696 |
697 | {{ description }}
698 |
699 | {% endif %}
700 |
701 | {% if size_guide %}
702 |
703 | {{ size_guide }}
704 |
705 | {% endif %}
706 | ```
707 |
708 | * Use split characters in the format `---[NAME]---` where `[NAME]` is the identifying name of the split content
709 | * `[NAME]` can have spaces, e.g. `---COMPATIBLE INFO---`
710 | * Assign the split description to variables at the top of the file then output the variable as and when you need it
711 | * Use split characters over metafields as they don't require any special apps to access and content can be automatically imported using apps like Excelify
712 | * Shopify tends to add extra HTML so in the example this has been stripped off, including empty line returns
713 |
714 | [ꜛ Back to TOC](#table-of-contents)
715 |
716 | ## Variables
717 |
718 | 1. [Assigning](#variable-assigning)
719 | 1. [Grouping](#variable-grouping)
720 | 1. [Naming](#variable-naming)
721 |
722 | ### [Variable assigning](#variable-assigning)
723 |
724 | #### Don't
725 | ```html
726 | {% for product in collection.products %}
727 | {% if product.tags contains 'has_shipping' %}
728 | {% assign has_shipping_tag = true %}
729 | {% endif %}
730 | {% endif %}
731 | ```
732 |
733 | #### Do
734 | ```html
735 | {% for product in collection.products %}
736 | {% assign has_shipping_tag = false %}
737 |
738 | {% if product.tags contains 'has_shipping' %}
739 | {% assign has_shipping_tag = true %}
740 | {% endif %}
741 | {% endif %}
742 | ```
743 |
744 | * Assign the default state of a variable before you first use it
745 | * This avoids false positives in `{% for %}` loops as it resets the variable at the start of each item
746 | * In the Don't example above once one product had the tag the variable is set to `true`, but nothing resets it meaning all products after it will have the variable set to `true`
747 |
748 | ### [Variable grouping](#variable-grouping)
749 |
750 | #### Don't
751 | ```html
752 | {% assign variable_a = 'Hello world' %}
753 |
754 | {{ product.title }}
755 | {{ product.description }}
756 | {% assign variable_b = 'Hello world' %}
757 | {% assign variable_c = 'Hello world' %}
758 |
759 | {% for variant in product.variants %}
760 | {{ variant.title }}
761 | {% assign variable_d = variant.title %}
762 | {% endfor %}
763 |
764 | {% assign variable_e = 'Hello world' %}
765 | {% for product in recommendations.products %}
766 | {{ product.title | link_to: product.url }}
767 | {% endfor %}
768 | ```
769 |
770 | #### Do
771 | ```html
772 | {% assign variable_a = 'Hello world' %}
773 | {% assign variable_b = 'Hello world' %}
774 | {% assign variable_c = 'Hello world' %}
775 | {% assign variable_e = 'Hello world' %}
776 |
777 | {{ product.title }}
778 | {{ product.description }}
779 |
780 | {% for variant in product.variants %}
781 | {{ variant.title }}
782 | {% assign variable_d = variant.title %}
783 | {% endfor %}
784 |
785 | {% for product in recommendations.products %}
786 | {{ product.title | link_to: product.url }}
787 | {% endfor %}
788 | ```
789 |
790 | * Group all variables which are not set inside specific `{% forloop %}` at the top of the file
791 | * If there are lots of variables consider placing them in a variables file, e.g. in the product section you would place them in a _product-variables.liquid_ snippet
792 |
793 | ### [Variable naming](#variable-naming)
794 |
795 | #### Don't
796 | ```html
797 | {% assign variableName = 'Hello world' %}
798 | {% capture img-var %}
799 | This isn't right
800 | {% endcapture %}
801 | ```
802 |
803 | #### Do
804 | ```html
805 | {% assign variable_string = 'Hello world' %}
806 | {% capture another_string %}
807 | This is correct
808 | {% endcapture %}
809 | ```
810 |
811 | * Use snake case when naming variables
812 | * Do not use abbreviations or shortened words
813 | * Keep the name easy to understand
814 |
815 | #### More examples
816 | ```html
817 | {% assign has_shipping_tag = true %}
818 | {% assign is_catalogue_product = false %}
819 |
820 | {% assign additional_handle_array = 'Item 1,Item 2,Item 3' | split: ',' %}
821 |
822 | {% assign installation_search_string = '' %}
823 | ```
824 |
825 | * If a variable is a boolean then name it as a question, 'has the shipping tag' becomes `has_shipping_tag`
826 | * If the variable is an array, then append `_array` to the name
827 | * If it's a string than append `_string`
828 | * This makes it obvious what you're dealing with when you have a lot of variables
829 |
830 | [ꜛ Back to TOC](#table-of-contents)
831 |
832 | ## [Whitespace controls](#whitespace-controls)
833 |
834 | ### Don't
835 | ```html
836 | {%- if variable_name -%}
837 | {%- assign another_variable = false -%}
838 | {%- endif -%}
839 | ```
840 |
841 | ### Do
842 | ```html
843 | {% if variable_name %}
844 | {% assign another_variable = false %}
845 | {% endif %}
846 | ```
847 |
848 | * Do not use [whitespace control](https://help.shopify.com/en/themes/liquid/basics/whitespace_) on tags
849 | * If you need to trim whitespace from objects then you can use whitespace controls, e.g. `{{- section.settings.body_copy -}}`
850 |
851 | > **🗒 Note:** Not all apps support whitespace controls.
852 |
853 | [ꜛ Back to TOC](#table-of-contents)
--------------------------------------------------------------------------------
/frame/vs-code/README.md:
--------------------------------------------------------------------------------
1 | # VS Code Settings
2 |
3 | The below settings are recommended to help you follow our other guidelines.
4 |
5 | ## Table of contents
6 |
7 | 1. [Extensions](#extensions)
8 | 1. [Settings](#settings)
9 |
10 | ## Extensions
11 |
12 | These are the minimum extensions we recommend you have installed.
13 |
14 | These links will open the Visual Studio Marketplace.
15 |
16 | * [Bracket Pair Colorizer 2](https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer-2)
17 | * [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker)
18 | * [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
19 | * [Import Cost](https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost)
20 | * [Liquid Languages Support](https://marketplace.visualstudio.com/items?itemName=neilding.language-liquid)
21 | * [Shopify Liquid Template Snippets](https://marketplace.visualstudio.com/items?itemName=killalau.vscode-liquid-snippets)
22 | * [Stylelint](https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint)
23 |
24 |
25 | [ꜛ Back to TOC](#table-of-contents)
26 |
27 | ## Settings
28 |
29 | Useful settings to make your life easier.
30 |
31 | ### Changing your settings
32 |
33 | 1. Press `cmd` + `,` to open the settings dialog in VS Code
34 | 1. Click the arrow pointing to the page icon in top right
35 | 1. Paste settings below into the JSON format file this has opened
36 |
37 | ### Table of contents
38 |
39 | * [Character limit](#character-limit)
40 | * [Disable file preview](#disable-file-preview)
41 | * [File associations](#file-associations)
42 | * [Git autofetch](#git-autofetch)
43 | * [Trim trailing whitespace](#trim-trailing-whitespace)
44 |
45 | ### [Character limit](#character-limit)
46 |
47 | ```js
48 | "editor.rulers": [
49 | 80
50 | ],
51 | "workbench.colorCustomizations": {
52 | "editorRuler.foreground": "#ffffff33"
53 | }
54 | ```
55 |
56 | * Adds a vertical border in your code editor denoting where the 80 character limit is
57 | * The colour is in the hexadecimal colour with an additional alpha value
58 |
59 | ### [Disable file preview](#disable-file-preview)
60 |
61 | ```js
62 | "workbench.editor.enablePreviewFromQuickOpen": false
63 | ```
64 |
65 | * When opening files from `cmd` + `p` file search they won't open in preview mode
66 | * This means that opening another file won't close the previous one
67 |
68 | ### [File associations](#file-associations)
69 |
70 | ```js
71 | "files.associations": {
72 | "*.scss.liquid": "scss",
73 | "*.js.liquid": "javascript"
74 | }
75 | ```
76 |
77 | * This tells VS Code to ignore the `.liquid` extension and open the files with the write sort of highlighting enabled
78 |
79 | ### [Git autofetch](#git-autofetch)
80 |
81 | ```js
82 | "git.autofetch": true
83 | ```
84 |
85 | * Autofetches the Git repo every five minutes
86 |
87 | ### [Trim trailing whitespace](#trim-trailing-whitespace)
88 |
89 | ```js
90 | "files.trimTrailingWhitespace": true
91 | ```
92 |
93 | * Trims any trailing whitespace on file save (makes linting happy)
94 |
95 | [ꜛ Back to TOC](#table-of-contents)
96 |
97 | [← Back to homepage](../README.md)
--------------------------------------------------------------------------------
/html-vue-template/README.md:
--------------------------------------------------------------------------------
1 | # HTML & Vue `` Guidelines
2 |
3 | These rules apply to all Liquid, HTML, and Vue `` code.
4 |
5 | See also [Vue's style guide](https://vuejs.org/style-guide/).
6 |
7 | ## Table of contents
8 |
9 | * [Attributes](#attributes)
10 | * [Characters](#characters)
11 | * [Commenting](#commenting)
12 | * [`` or `
`](#div-or-span)
13 | * [Indenting](#indenting)
14 | * [Spacing & line character limits](#spacing--line-character-limits)
15 | * [Self-closing element](#self-closing-elements)
16 | * [Vue render tags](#vue-render-tags)
17 |
18 | [← Back to homepage](../README.md)
19 |
20 | ## Attributes
21 |
22 | * [Attribute casing](#attribute-casing)
23 | * [Attribute order](#attribute-order)
24 |
25 | ### Attribute casing
26 |
27 | * The general rule is if the attribute is to do with CSS then you use kebab-case for the value
28 | * If the attribute is for JavaScript then use camelCase in the value
29 | * kebab-case
30 | * `id`
31 | * `class`
32 | * `for`
33 | * camelCase
34 | * `v-` bind
35 | * Vue `@` events
36 | * `data-`
37 | * `js-` selector values
38 |
39 | ### Attribute order
40 |
41 | #### Don't
42 |
43 | ```html
44 | {% # Liquid file %}
45 |
46 | ```
47 |
48 | #### Do
49 |
50 | ```html
51 | {% # Liquid file %}
52 |
66 | ```
67 |
68 | * When an element has more than one attribute then each attribute should be on its own line
69 | * Attributes should follow this order:
70 | * `is`
71 | * `v-for`
72 | * `v-if` / `v-else-if` / `v-else`
73 | * `v-show`
74 | * `v-cloak`
75 | * `id`
76 | * `ref`
77 | * `key`
78 | * `v-model`
79 | * `class`
80 | * { native }
81 | * `aria-`
82 | * `data-`
83 | * `js-`
84 | * `@event`
85 | * `v-html` / `v-text`
86 | * { native } covers everything else, items within { native } should be in alphabetical order
87 | * This makes it easier for managing merges in Git
88 | * Make sure you trim trailing spaces after each line
89 | * See also [spacing & line character limits](#spacing--line-character-limits)
90 |
91 | > Be sure to follow the formatting; the trailing `>` should be on a newline with the attributes indented in two spaces.
92 |
93 | ## Characters
94 |
95 | ### Don't
96 |
97 | ```html
98 |
99 | ```
100 |
101 | ### Do
102 |
103 | ```html
104 |
105 | ```
106 |
107 | * Use double quotations `"` in HTML elements, not apostrophes/single quotations `'`
108 | * Note that you should use single quotations in Liquid tags and filters
109 |
110 | [ꜛ Back to TOC](#table-of-contents)
111 |
112 | ## `` or `
`
113 |
114 | `` and `
` are often used interchangeably but there are specific use cases for each:
115 |
116 | * If the content is going to be displayed inline, use ``
117 | * If you want the element to display block, on its own line, use ``
118 | * If you're using CSS to change its `display` property, consider using the other
119 |
120 | [ꜛ Back to TOC](#table-of-contents)
121 |
122 | ## Indenting
123 |
124 | ### Don't
125 |
126 | ```html
127 |
128 | {{ product.description }}
129 |
130 | ```
131 |
132 | ### Do
133 |
134 | ```html
135 |
136 | {{ product.description }}
137 |
138 | ```
139 |
140 | * Indent two spaces inside each opening HTML element
141 |
142 | [ꜛ Back to TOC](#table-of-contents)
143 |
144 | ## Spacing & line character limits
145 |
146 | ### Don't
147 |
148 | ```html
149 | {% # Liquid file %}
150 |
151 |
{{ product.title }}
152 |
{{ product.type }}
153 |
{% render 'icon-misc' with icon: 'plus' %} {{ 'products.product.add_to_cart' | t }}
154 |
155 |
{{ product.description }}
156 |
{{ pages['shipping'].content | strip_html }}
157 |
158 |
159 |
160 | ```
161 |
162 | ### Do
163 |
164 | ```html
165 | {% # Liquid file %}
166 |
172 |
{{ product.title }}
173 |
{{ product.type }}
174 |
175 |
176 |
177 | {% render 'icon-misc' with icon: 'plus' %}
178 |
179 |
180 |
181 | {{ 'products.product.add_to_cart' | t }}
182 |
183 |
184 |
185 |
186 |
187 | {{ product.description }}
188 |
189 |
190 |
191 | {{ pages['shipping'].content }}
192 |
193 |
194 |
195 |
206 |
207 | ```
208 |
209 | * Use common sense spacing to improve the readability of your code
210 | * [Block-level elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements) should open onto a new line and indent
211 | * [Inline-level elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements#Elements) should be written on a single line
212 | * Single line elements should be grouped together by type
213 | * Multi-line elements should have a newline between them
214 | * This guideline includes empty block-level elements, e.g. the following is correct:
215 |
216 | ```html
217 | {% # Liquid file %}
218 |
223 | ```
224 |
225 | * Once you are writing something over multiple lines, each line should only have one attribute or value on it
226 | * If an attribute's value exceeds 80 characters then it too should be split in the following format:
227 |
228 | ```html
229 | {% # Liquid file %}
230 |
240 | {% # Content %}
241 |
242 | ```
243 |
244 | ### Exceptions
245 |
246 | * Short block-level elements like `
`, ` ` or `` can be displayed on a single line unless they're greater than 80 characters long
247 | * Inline-level elements greater than 80 characters long should follow multi-line rules
248 |
249 | [ꜛ Back to TOC](#table-of-contents)
250 |
251 | ## Self-closing elements
252 |
253 | ### Don't
254 |
255 | ```html
256 | {% # Liquid file %}
257 |
258 |
259 |
263 |
264 |
269 | ```
270 |
271 | ### Do
272 |
273 | ```html
274 | {% # Liquid file %}
275 |
276 |
277 |
281 |
282 |
287 | ```
288 |
289 | * Vue custom elements in Liquid files should not use a self-closing tag as this is invalid and causes errors
290 | * The `/` in self-closing tags is obsolete in most HTML elements, do not use it
291 |
292 | ### Exceptions
293 |
294 | * Inside a Vue `` tag you should use self-closing tags for elements which don't normally self-close:
295 |
296 | ```html
297 |
298 |
302 | ```
303 |
304 | [ꜛ Back to TOC](#table-of-contents)
305 |
306 | ## Vue render tags
307 |
308 | ### Don't
309 |
310 | ```html
311 |
312 |
313 | {{ product.title }}
314 |
315 | ```
316 |
317 | ### Do
318 |
319 | ```html
320 |
321 |
325 | ```
326 |
327 | * Use `v-html` or `v-text` to render content in an element instead of using the template render tags
328 |
329 | [ꜛ Back to TOC](#table-of-contents)
330 |
--------------------------------------------------------------------------------
/javascript-vue-sfc/README.md:
--------------------------------------------------------------------------------
1 | # JavaScript & Vue SFC
2 |
3 | Our guidelines are predominately based on [@shopify/eslint-plugin](https://github.com/Shopify/web-configs/tree/main/packages/eslint-plugin) with exceptions.
4 |
5 | See also [Vue's style guide](https://vuejs.org/style-guide/).
6 |
7 | When saving `eslint --fix` should run and automatically fix most issues.
8 |
9 | ## Table of contents
10 |
11 | * [Arrays](#arrays)
12 | * [Comments](#comments)
13 | * [If conditions](#if-conditions)
14 | * [Indents](#indents)
15 | * [Newlines](#newlines)
16 | * [Objects](#objects)
17 | * [Order](#order)
18 | * [Passing parameters](#passing-parameters)
19 | * [Semi-colons](#semi-colons)
20 | * [Variables](#variables)
21 | * [Whitespace](#whitespace)
22 |
23 | [← Back to homepage](../README.md)
24 |
25 | ## Arrays
26 |
27 | ### Don't
28 |
29 | ```js
30 | const foo = [
31 | 'bar'
32 | ]
33 |
34 | const bar = ['foo',
35 | 'bar'
36 | ]
37 | ```
38 |
39 | ### Do
40 |
41 | ```js
42 | const foo = ['bar']
43 |
44 | const bar = [
45 | 'foo',
46 | 'bar',
47 | ]
48 | ```
49 |
50 | * Single item arrays should be on a single line with no spaces inside the opening and closing tags
51 | * Multiple item arrays should have each item on a newline with a trailing comma
52 |
53 | [ꜛ Back to TOC](#table-of-contents)
54 |
55 | ## Comments
56 |
57 | * [Comments general](#comments-general)
58 | * [Long form](#long-form-comments)
59 | * [JSDoc](#jsdoc-comments)
60 | * [Short form](short-form-comments)
61 |
62 | ### General comments
63 |
64 | * Add a newline after comments, except those starting with `eslint` or `webpack`
65 | * Comments should begin with a capital letter
66 |
67 | ### Long form comments
68 |
69 | ```js
70 | /**
71 | * Folder: Title
72 | * -----------------------------------------------------------------------------
73 | * Description.
74 | *
75 | */
76 | ```
77 |
78 | * Use the long form comment for intro comments inside the `
312 |
313 |
316 | ```
317 |
318 | #### Do
319 |
320 | ```html
321 |
322 |
323 |
324 |
325 |
326 |
327 |
332 |
333 |
336 | ```
337 |
338 | * Only indent the contents of the `` tag
339 | * `