Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/oddcamp/frontend-manual
Odd Camp's handbook for frontend people
https://github.com/oddcamp/frontend-manual
Last synced: about 2 months ago
JSON representation
Odd Camp's handbook for frontend people
- Host: GitHub
- URL: https://github.com/oddcamp/frontend-manual
- Owner: oddcamp
- Created: 2017-06-22T15:12:24.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2022-10-21T07:50:00.000Z (over 2 years ago)
- Last Synced: 2023-03-12T07:42:08.501Z (almost 2 years ago)
- Homepage:
- Size: 101 KB
- Stars: 18
- Watchers: 11
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Odd Camp's Front End Manual
_v0.7_This document outlines the basic stuff for Front End development at [Odd Camp](https://www.odd.camp). We should try to keep this as tiny as possible and include only the most important stuff.
These things are the default for all our projects unless anything else is specifically said. The entire Front End team should know these things. If you have questions or suggestions regarding this manual, hit up [Osvaldas](https://github.com/osvaldasvalutis). If you feel that you need some time of powering up your skills just holla at [Per](https://github.com/persand) and you'll get it.
## Table of Contents
- [Code editor](#code-editor)
- [Project setup](#project-setup)
- [HTML](#html)
- [CSS](#css)
- [JavaScript](#javascript)
- [Media](#media)
- [Accessibility](#accessibility)
- [Performance](#performance)
- [Compatibility](#compatibility)
- [Design](#design)## Code editor
We usually use linters and [.editorconfig](http://editorconfig.org/#download) in our projects. In order to smoothen the development process install ESlint and Stylelint extensions in your code editor. For _Visual Studio Code_ use these:
* [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint)
* [Prettier - Code formatter](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
* [stylelint](https://marketplace.visualstudio.com/items?itemName=stylelint.vscode-stylelint)
* [EditorConfig for VS Code](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig)[π‘ back to top](#table-of-contents)
## Project Setup
### Starters and boilerplates
When starting a Rails or GatsbyJS project, we usually use these pre-configured starters/boilerplates:
* [gatsby-starter-oddcamp](https://github.com/oddcamp/gatsby-starter-oddcamp)
* [rails-boilerplate](https://github.com/oddcamp/rails-boilerplate)If for some reason you need to start a project from scratch, please make sure to adapt as much of the following as possible:
### Dependencies
Use Yarn for dependencies:
* `yarn init`
* `yarn add package-name`### .editorconfig
All projects must have an [.editorconfig file](examples/.editorconfig) by default.
### Linters
Set up [ESLint](http://eslint.org), [stylelint](https://stylelint.io) and [Prettier](https://prettier.io) using the following configurations:
* [ESLint config](https://github.com/oddcamp/frontend-manual/tree/master/examples/.eslintrc)
* [stylelint config](https://github.com/oddcamp/frontend-manual/blob/master/examples/.stylelintrc)
* [Prettier config](https://github.com/oddcamp/frontend-manual/tree/master/examples/.prettierrc)[π‘ back to top](#table-of-contents)
## HTML
### Semantics and Accessibility
We should make an effort to produce valid [semantic HTML](https://codepen.io/mi-lee/post/an-overview-of-html5-semantics), that takes advantage of the full potential of HTML5's tags to produce clean code, that is readable by humans and machines alike.
> Don't mark the content up by how it looks, mark it up by what it means.
* Use available tags (`header`, `section`, `article`, etc.) to section content by its meaning, e.g.:
β DO:
```html
Articles
Title
Excerpt
Title
Excerpt
```β DON'T:
```html
Articles
Title
Excerpt
Title
Excerpt
```* An element that looks like a heading isn't necessarily a heading semantically, e.g.:
β DO:
```html
Title
A paragraph that looks like `h2` in the design
```β DON'T:
```html
Title
A paragraph that looks like `h2` in the design
```* Use `
β DO:
```html
```β DON'T:
```html
May 15, 2001
```* Use list tags `ul`, `ol`, `dl` to structure lists, e.g.:
β DO:
```html
- Title
- Description
- Title
- Description
```
β DON'T:
```html
```
* When working with elements users are supposed to interact with, use HTML tags meant for that purpose, e.g.:
β
DO:
```html
Go baby!
```
β DON'T:
```html
Go baby!
```
* Exclude repeating links and controls from tab order, e.g.:
...
```
* Provide text for links, buttons and images, otherwise screen readers will read full URLs for the users. Also use `[title]` attributes as hints for mouse users:
...
```
...
```
* Forms should have fragment identifiers set properly, which is crucial if the form appears _below the fold_, e.g.:
β
DO:
```html
...
```
* Also make sure:
- it embraces [ARIA](https://developers.google.com/web/fundamentals/accessibility/semantics-aria) attributes
- UI's are usable with a keyboard
- visually hidden elements are excluded from the tab order
- the rest of the page is excluded from tab order when a modal is opened
- dropdown menus and tabs work at least with the Tab key (enabling arrow buttons would be a nice touch)
- click and touch targets are at least `44x44px` in size
### Smart Quotes
Use the [correct quotation marks and apostrophes](http://smartquotesforsmartpeople.com) for content:
* β "Don't be dumb"
* π βYouβre smart!β
### Minimum viable `` tag composition
We recommend using [these tags](https://github.com/oddcamp/frontend-manual/tree/master/examples/head-tags-recomended.html) (as well as [manifest.json](https://github.com/oddcamp/frontend-manual/tree/master/examples/manifest.json)) in the HEAD area are of the document as a starting point.
### Templating languages
When writing HTML code in an environment that includes a templating engine (be it ERB or JSX), it's very easy to mess up code readability. In order to avoid that, it's best to put as much logic into the controllers as possible. If you still have to write framework related code in a template, it's better to do as much of it at the top of the file as possible, e.g.:
β
DO:
```erb
<%
people = WPRecord.by_model("person").where(uid: owners)
%>
-
<%= person[:fullname] %>
<% people.each do |person| %>
<% end %>
```
β DON'T:
```html
-
<%= person[:fullname] %>
<% WPRecord.by_model("person").where(uid: owners).each do |person| %>
<% end %>
```
### Learn More
* https://codepen.io/mi-lee/post/an-overview-of-html5-semantics
* http://smartquotesforsmartpeople.com
[π‘ back to top](#table-of-contents)
## CSS
### Preprocessor
* We use [SASS](https://sass-lang.com) as the standard for our styling needs.
* We use [Styled Components](https://www.styled-components.com) and [Polished](https://polished.js.org) for JS frameworks based projects.
### File Structure and Boilerplate
We usually use [SASS-Boilerplate](https://github.com/oddcamp/sass-boilerplate) together with [SASS-Utils](https://github.com/oddcamp/sass-utils) for SASS projects (please follow the guidelines provided in the repository pages).
- Aim for **componentization**, i.e. creating multiple independent/encapsulated small structures rather than a few large ones.
- In SASS projects, we usually split components into two categories/folders:
- `components` β global components
- `pages` β page-specific components
- Be very strict at placing components in their own files, e.g.:
β
DO:
```scss
// _button.scss
.button {
// ...
}
.button-special {
// ...
}
```
β DON'T:
```scss
// _button.scss
.button {
// ...
}
.checkbox {
// ...
}
```
### Naming
* We use [BEM](http://getbem.com) for naming convention.
* Selectors shouldn't be chained above three levels:
* π `.settings-nav` is good
* π `.settings-nav__links` is good
* β
`.settings-nav__links__list` is ok
* β `.settings-nav__links__list__item` should be avoided
* In case you need to chain more than three selector levels, consider nesting. Nested single class names should start with a dash, which indicates that this selector is scoped/local (strictly belongs to the parent selector) and altogether it won't conflict with global components that might habe the same name:
* π `.settings-nav .-links` is good
* β `.settings-nav .links` should be avoided
**Important!** Do not confuse the last example with cases where you are extending a global component:
* π `.settings-nav .links` is good if `.links` was a global component and was meant to be extended under the `.settings-nav` component
* For performance reasons, selectors shouldn't nest more than three levels deep:
* π `.settings-nav .-links__list` is good
* π `.settings-nav .-links .-list` is good
* β `.settings-nav .-links .-list .-item` should be avoided
They also shouldn't nest when there's no reason to:
* π `.settings-nav ul a` is good
* β `.settings-nav ul li a` should be avoided
* For secure scoping reasons, modifier classes should be formated according to BEM, or concatenated with double-dashed class names:
* π `.settings-nav--hidden` is good
* β
`.settings-nav.--hidden` is ok
* β `.settings-nav.hidden` should be avoided
### Using ID's
Although the ID attribute was primarily designed as an accessibility feature for fragmenting document, the requirements for uniqueness, specificity, and componentization are incompatible with its use in CSS. We should avoid using ID's, except in cases where they are strictly required (e.g. to uniquely identify a specific instance of a component, to set page anchors, among others).
### Componentization
Treating the whole page as a single component can easily get you in selector-chain hell. It becomes difficult to use parent modifier classes that affect child elements, and your code quickly turns into spaghetti. Therefore, it's recommended to always treat the page as a combination of multiple and small components, which by their nature are easily reusable and extendable, making the code more intelligible.
β Avoid complex SASS structures like this:
```scss
.settings {
// ...
&__wrapper {
// ...
&__nav {
// ...
}
&__sidebar {
//...
&__avatar {
// ...
}
}
}
}
```
π Always strive for breaking things into smaller components like this:
```scss
.settings-wrapper {
// ...
}
.settings-nav {
// ...
}
.settings-sidebar {
// ...
}
.settings-avatar {
// ...
}
```
If you have a component that is reused multiple times on the same page, avoid making assumptions about its context (i.e. use positioning-related properties such as `margin`, `position/top/left/...`). Instead, put that responsibility on a parent component:
```scss
.settings-avatar {
// ...
}
// avatar in sidebar
.settings-sidebar {
position: relative;
.settings-avatar {
position: absolute;
top: 0;
left: 0;
}
}
// avatar in main container
.settings-main {
// ...
.settings-avatar {
margin-top: 1.5rem;
}
}
```
### Extensions and overrides
It's usually a better practice to create extensions for a nested component, rather than overriding it. That way, things are more predictable and controllable:
β
DO:
```scss
.button {
&--green {
color: $color-green;
}
}
```
β DON'T:
```scss
.settings-sidebar {
.button {
color: $color-green;
}
}
```
However, if you need to adjust how the nested component behaves in a specific context, _overriding_ makes sense:
β
DO:
```scss
.settings-sidebar {
.button {
margin-top: rem(20);
}
}
```
### Units
For better accessibility we should use EMs/REMs and set the font size of the root element (`html`) to a percentage value, preferably `100%`:
```scss
html {
font-size: 100%;
}
```
This enables the users (most likely visually impaired) to scale the site visually. For example: let's say the root element's font size on our site is as recommended β `100%`. By default the website font size in the browser's settings is set to 16px, which means `1rem = 16px`. But if the user has that option set to `32px` the REM value is going to be affected accordingly: `1rem = 32px`. Accessible!
REMs should be used by default, EMs when we need local dependencies, and PXs only for the rare cases when things aren't supposed to scale at all (e.g. 1px thick borders). EMs and REMs should be calculated through a [helper from SASS-Utils](https://github.com/oddcamp/sass-utils#units) or [Polished](https://polished.js.org/docs/#rem):
```scss
.component {
width: em(320); // 320px
padding: rem(30 20); // 30px 20px
margin: rem(40 auto 20); // 40px auto
}
```
Using these _proportional_ units enables UI to depend on the screen size, e.g.:
```scss
html {
font-size: 100%;
@include mq(medium down) {
font-size: 87.5%;
}
}
```
### Variables
_Variablize_ as many of the global configurations as possible. Our [SASS-Boilerplate](https://github.com/oddcamp/sass-boilerplate/tree/master/src/base) will get you on the way.
Don't forget to set global `z-index`'es as variables β this will save you some time dealing with scroll fixed headers, modals, and such.
**Important:** Media Queries have to be EMs based ([here's why](https://zellwk.com/blog/media-query-units)). This is already solved by a helper from [SASS-Utils](https://github.com/oddcamp/sass-utils#mq-mixin).
### "Styled" Strategy
_Styled_ is a strategy for styling HTML elements that are usually inserted via WYSIWYG editors when writing articles, blog posts, etc. (`h1-6, p, blockquote, a, ul, ol, dl, table, code, pre`, among other common tags). When starting a new project we prefer every HTML element to be unstyled (naked) by default, and unopinionated about the context it's in. Benefits are:
- No need to overwrite default styles (e.g. remove margins, change hover effects, etc.) when an element is in a different context or should be styled differently;
- No need to track the changes in the default styling of an element and update every single instance where the styling was meant to be completely different;
- Visual consistency among browsers;
- Smaller CSS file size;
- **Always be sure** you attach the major `styled` class name to the direct parent element of the content:
β
DO:
```html
...
...
```
β DON'T:
```html
...
...
```
- Never `@extend .styled` as it will unnecessarily increase the size of the CSS bundle.
For further details, usage and tips follow the ["Styled" guide on SASS-boilerplate](https://github.com/oddcamp/sass-boilerplate#styled-strategy) repository page.
### Responsive Breakpoints
You should structure breakpoints to avoid overrides: define shared styles first and put the rest inside appropriate media queries.
```scss
.element {
color: #fff;
background-color: #000;
border-radius: rem(5);
// ^ shared styles
@include mq(small down) {
margin-top: rem(20);
}
@include mq(between small large) {
margin-bottom: rem(20);
}
@include mq(large up) {
position: absolute;
top: rem(20);
right: rem(20);
}
}
```
Benefits are:
* No need to overwrite styles in a media query when changing or adding the default ones;
* Smaller CSS file size.
We use [Media Queries helper](https://github.com/oddcamp/sass-utils#media-queries) from our SASS-Utils library.
### Usability and Accessibility
#### Indicating Interaction
Visually indicating that an element is available to be interacted with (e.g. a button is clickable) or the interaction has been successful (e.g. the button has been clicked) is a sign of good UX. Therefore we should always look to embrace `:hover`, `:focus` and `:active` pseudo-classes.
#### Outline
Outline is a crucial element when it comes to website accessibility. Even though sometimes it's visually disturbing and unnecessary, we should never remove outline for keyboard users. There's a Smart Outline library at [JS Utils](https://github.com/oddcamp/js-utils) that helps to deal with the issue:
```js
initSmartOutline()
```
Smart Outline hides the outline when interacting with a mouse and brings it back when interacting with a keyboard.
In some cases, it's really meaningful to reveal the outline for mouse users as well. For example, let's say there is a confirm-type modal that pops up with two buttons ("Delete" and "Cancel"). Adding the focus on the primary action button and revealing the outline would tell the user they can also press "Enter" button to delete an item, e.g.:
```js
confirmModal.on('show', (confirmBtn, cancelBtn) => {
confirmBtn.focus()
showSmartOutline()
})
```
### Performance
* If possible, do not transition `top/bottom/left/right` properties, use `transform: translate()` instead;
* Accelerate "expensive" CSS solutions with [`will-change`](https://developer.mozilla.org/en-US/docs/Web/CSS/will-change), but do not overuse it;
* Avoid nesting more than three selectors. Proper componentisation will help you;
* Don't hide text with `text-indent: -9999px`, there is a [better technique for it](http://www.zeldman.com/2012/03/01/replacing-the-9999px-hack-new-image-replacement/);
* To make objects round, do `border-radius: 50%` instead of `999px`.
### Fonts
* When using webfonts, always be sure to set [web-safe fallback font](https://www.cssfontstack.com) names. Make sure the fallback fonts are as close to the original one as possible. Our [SASS-Boilerplate](https://github.com/oddcamp/sass-boilerplate) comes with pre-defined web-safe fonts.
```scss
$ff-primary: "proxima-nova", "Arial", "Helvetica Neue", "Helvetica", sans-serif;
```
* Oftentimes, when using multiple typefaces on a project, they have different font weights. Usually, the default font-weight for the document is `400`. However, it will sometimes happens that the secondary font doesn't have `400` available as font-weight by design. It means that each time you set the secondary font for a component, you also have to specify the font-weight in order to preserve the visual consistency. Our [SASS-Boilerplate](https://github.com/oddcamp/sass-boilerplate) comes with predefined mixins that automate the process, e.g.:
```scss
.btn {
@include ff-secondary;
}
// ...becomes:
.btn {
font-family: "SecondaryFontName", "Arial", sans-serif;
font-weight: 500;
}
```
### Vendor Prefixes
Vendor prefixed properties should automatically be inserted by an asset bundler, therefore it's best to keep your code free of prefixed properties. This doesn't apply to properties that don't have standardized equivalents (such as `-webkit-overflow-scrolling`) or work differently than their standardized equivalents.
### Design Systems
It's common for us to work on several different projects for the same client. When this happens, we've found it useful to develop a collection of global, reusable styles β which we call a Design System. When declared as a dependency on a project, a design system gives us a nice collection of sensible defaults we can use to get started faster. Should the need arise, it also lets us update one default style across all projects related to a specific client. If you believe a design system would be beneficial in the long run, there is [a boilerplate with some sensible defaults in place](https://github.com/oddcamp/design-system-boilerplate). The repo includes instructions on how to set it up, as well as recommendations on how to seamlessly include it in your project without disrupting your workflow.
### Also...
- **Always make sure that any images, videos, and iframes have dimensional properties defined in CSS.** Never depend on the size of the original source as this is prone to change and break the layout.
β
DO:
```scss
svg {
width: em(20);
height: em(20);
opacity: 0.8;
}
img {
max-width: em(240);
width: 100%;
}
```
β DON'T:
```scss
svg {
opacity: 0.8;
}
img {
max-width: em(240);
}
```
- Never use CSS's property `content` for text β it's not an accessible solution:
β DON'T:
```scss
button::after {
content: "Display options"
}
```
- Use relative values for `line-height`s rather than fixed:
β
DO:
```scss
p {
line-height: 1.2;
}
```
β DON'T:
```scss
p {
line-height: 22px;
}
```
### Resources
* https://github.com/oddcamp/sass-boilerplate
* https://github.com/oddcamp/sass-utils
* https://github.com/airbnb/css#oocss-and-bem
* http://getbem.com/introduction
* http://www.intelligiblebabble.com/a-pattern-for-writing-css-to-scale
* https://zellwk.com/blog/media-query-units
* https://developer.mozilla.org/en-US/docs/Web/CSS/will-change
* https://www.cssfontstack.com
[π‘ back to top](#table-of-contents)
## JavaScript
### Style
The way we format the code is dictated by [our linters setup](#linters), but [Airbnb's JS style guide](https://github.com/airbnb/javascript) should be used as a secondary reference source.
### ES6
We prefer using ES6 together with [Babel](https://babeljs.io), to ensure the code is compiled down into browser-compatible JavaScript.
### JS Utils
We maintain and use [JS Utils](https://github.com/oddcamp/js-utils) library on real-life projects for easier development. The code examples below also rely on the library.
### jQuery
We discourage using jQuery for new projects if possible. Instead, strive to rely on dependency-free lightweight libraries, such as JS Utils and [similar ones](https://github.com/oddcamp/js-utils#other-resources) or consider [conditional loading](#loading-large-libraries-conditionally) for jQuery and its plugins.
### Selecting DOM elements
* Always be specific about what you are selecting, in order to avoid unexpected outcomes:
β
DO:
```js
const settingsSidebar = document.querySelector(`.settings-sidebar`)
const toggles = settingsSidebar.querySelectorAll(`input[type="radio"]`) // π this is correct
```
β DON'T:
```js
const settingsSidebar = document.querySelector(`.settings-sidebar`)
const toggle = document.querySelectorAll(`input[type="radio"]`)
```
In this case, we specifically select inputs of the `settingsSidebar`, not the whole document.
### Progressive Enhancement, Graceful Degradation
Treating JavaScript as an ehancement promotes the development of **fail-safe**, semantic, accessible websites. Take a look at this "Recent news" list example:
```html
Recent news
- ...
- ...
- ...
- ...
Show more
```
```js
const btn = document.querySelector('.js--show-more')
btn.setAttribute('role', 'button')
addEventListener(btn, 'click', (e) => {
const hiddenItems = document.querySelectorAll('.recent-news li.--hidden')
if(hiddenItems.length) {
e.preventDefault()
btn.removeAttribute('role')
removeClass(hiddenItems, '--hidden')
}
})
```
Instead of assuming JavaScript is there and using `button`, we use `a[href]` which would redirect users to the corresponding page in cases where JavaScript is disabled, it failed, or it hasn't been loaded yet. In parallel, we also progressively enhance the experience with some JavasScript which turns the anchor into a semantic button `a[role=button]`. After the first click, it reveals the hidden items, and after the second it works like a typical anchor that redirects users to the corresponding page.
### Performance and Optimization
#### Embedding JavaScript
Avoid placing JS file insertions in that work in a synchronous manner and therefore block rendering of the page. The more this happens, the later users will start seeing the page. JavaScript file embeds should be placed right before tag, preferably with [`defer` or `async`](http://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html) attributes inserted along.
```html
...
...
```
In cases when there's a need to run some JavaScript code in `` before page renders (e.g. Modernizr), it's best to minify and inline it:
```html
...
(function(){ /*...*/ })();
```
Even though ideally all the JavaScript code should be placed in external files, it's sometimes okay to inline it at the end of the document, preferably after external JS file references, e.g.:
```html
// inlined JS