Nous utilisons Middleman pour la gestion des projets front (minification, concaténation, rangement…)
Autres possibilités:
- Codekit
- grunt
- gulp
- fire.app
IDE et linters
Nous utilisons l’IDE Atom, avec les extensions:
- linter-eslint
- linter-sass-lint
Composants
Carousel/Slider
Validé: Bootstrap carousel si fonctions très simples, sinon Owl
Possibilités:
- Bootstrap carousel (très limité)
- Owl (fonctionne correctement)
- Slick (testé, trop de dépendances. Pour faire tourner correctement il faut 3 fichiers JS et 3 fichiers css !)
- Siema (pas de dépendance jQuery)
File upload
Pour jQuery: Dropzone.js (http://www.dropzonejs.com/)
Autres possibilités: Dropfile (http://adodson.com/dropfile/)
Pour AngularJS: ng-file-upload (https://github.com/danialfarid/ng-file-upload/)
Autres possibilités: https://github.com/nervgh/angular-file-upload/
Nav de one-page (ancres)
Validé: Scrollspy
Autres possibilités:
- Gumshoe (avec SmoothScroll)
Animations au scroll
Validé: ScrollReveal
Animations en séquence
Validé: ?
Greensock Tween (très lourd mais compat ie9)
Notices en popin
Validé: toastr
Images en popin
Validé: Natif bootstrap
Autres possibilités: Fancybox
Interactions mobiles
Validé: ?
Possibilités:
- Hammer.js
- jQuery mobile (catastrophique sur My Redken)
Mises en page spécifiques (type pinterest)
Possibilités:
- Masonry
- CSS Flex
Assets
Dépendances
On inclut les dépendances avec Sprockets.
/Gemfile
gem 'middleman-sprockets'
FIXME je crois que l'intégration de la gem Sprockets n'est plus nécessaire, ni l'activation. Arnaud
/config.rb
activate :sprockets
activate :relative_assets
sprockets.append_path File.join "#{root}", 'node_modules'
En tête du fichier .js, appeler les dépendences avec un require, par exemple :
//= require modernizr.js
Javascript
Version de javascript: ES5
Alternatives refusées: ES6 avec Babel et le cortège d’outils pour transpiler.
- js pur es5 (pas encore es6)
- si besoin jquery 3
- si besoin Angular 1
- yarn
Librairie légère: jQuery 3
Framework léger
AngularJS (Angular 1)
Alternatives refusées: Angular 2 (trop typescript, trop verbeux, sans amélioration en contrepartie), Ember (trop compliqué), React (trop compliqué), Vue (trop dépendant d’ES6), Backbone (syntaxe toute pourrie, peu de features)
Framework lourd
?
Si on a besoin d’un framework lourd, ne devrions nous pas faire du js vanille? Sujet compliqué, à discuter au cas par cas. En fonction des specs, React ou Vue peuvent-être de bons choix.
Stylesheets
Reset
Eric Meyer
Alternatives refusées: Normalize Sauf si on utilise Bootsrap, auqeul cas Normalize est inclus
Préprocesseur
SASS
Alternatives refusées: Less (trop de syntaxe)
Framework
Bootstrap 4
Alternatives refusées: Bootstrap 3 (sauf legacy), Foundation, Zurb On utilise un framework pour écrire moins de code CSS et JS, donc pas la peine de le mettre pour l’ignorer et tout réécrire.
Méthodologie
BEM
Alternative refusées: SMACSS Attention, BEM est difficilement compatible avec Bootstrap, et orienté application client. Ce n’est pas un bon choix pour un pur markup sémantique: ça n’empêche pas un markup d’être sémantique, mais ça crée du bruit.
Images
Compression png / jpg
Pour les images utilisant srcset :
- Il faut ajouter un attribut ‘width’ à l’élément <img> valant la largeur réelle de l’image pour éviter qu’elle dépasse sa taille réelle.
- On attribue des sources à l’attribut ‘srcset’ seulement pour des tailles strictement inférieures à la taille réelle de l’image.
TODO trouver un outil standard et automatisé pour compresser les images (si pas sprockets), et pour simplifier les srcset
Svg
Les svg sont pratiques pour l’indépendance de résolution, pour l’animation, et pour l’interactivité.
[Depuis Rails 5.2.1.1] Si uploadés via ActiveStorage : On rajoute un initializer pour pouvoir afficher les svg en front.
/active_storage.rb
ActiveStorage::Engine.config.active_storage.content_types_to_serve_as_binary.delete('image/svg+xml')
2 cas :
- soit on l’utilise comme une icône/image, et on l’intègre avec une balise img
- soit on l’utilise de manière plus créative, et on l’intègre inline
- Pour un asset du projet
<%= render inline: Rails.root.join('app/assets/images/path/to/icon.svg').read %>
- Pour un Blob d’ActiveStorage
<%= render inline: @brand.icon.open(&:read) %>
- Pour un asset du projet
Objectif : pouvoir manipuler le svg comme on veut, et le mettre en cache (donc pas inline).
Videos
mp4 ou Vimeo/Youtube
Compatibilité des vidéos sur mobile
Tag video html natif sans ajout de JS
Ne supporte pas l’autoplay :
- iOS 9 Safari 601
- iOS 9 Chrome 54
- iOS 9 Firefox 5.3
- iOS 9 Opera Mini 14
- iOS 10 Firefox 5.3
- Android Chrome 43
- Android Opera Mini 20
- Android Samsung Internet 4.0
- Windows Phone 8 IE Mobile 10
- Windows 10 Mobile Edge 14.1
- Windows 10 Mobile Opera Mini 9.1
Supporte l’autoplay :
- iOS 10 Safari 602 (w/ muted + playsinline attributes)
- iOS 10 Chrome 54 (w/ muted + playsinline attributes)
- iOS 10 Opera Mini 14 (plays fullscreen)
- Android Chrome 54 (w/ muted attribute)
- Android Firefox 49
- Windows Phone 8.1 IE Mobile 11
Aller plus loin ?
Pour avoir du son en autoplay et/ou avoir de l’autoplay sur version iOS 8 et 9, il est necessaire de passer par du JS pour dupliquer la video dans un canvas. Ne fonctionne pas sur Android.
- https://github.com/84Paris/canvasvideo.js
- https://github.com/bfred-it/iphone-inline-video
Icons
font-awesome
Déploiement
Staging
Les projets doivent se livrer pour recette en une ligne de commande :
middleman build
Pour se faire, nous utilisons la gem middleman sync et un bucket s3 spécifique.
/Gemfile
gem 'middleman-s3_sync'
gem 'mime-types'
/config.rb (ask @pabois or @arnaudlevy for real data)
activate :s3_sync do |s3_sync|
s3_sync.bucket = 'type bucket name here'
s3_sync.region = 'type region here'
s3_sync.aws_access_key_id = 'type access here'
s3_sync.aws_secret_access_key = 'type secret here'
s3_sync.prefix = 'type path here'
s3_sync.after_build = true
end
Production
Plusieurs possibilités, mais la meilleure à l’heure actuelle est Netlify. Le déploiement se fait au commit, donc il faut une branche dev une fois le site en prod.
linters
Le linting (“linter” son code) est une pratique qui vise à améliorer la qualité de son code et de ce fait la reprise et la maintenabilité de celui-ci. Chaque (grand) language de programmation a son (ou ses) linters.
Javascript
On a testé et utilisé longtemps jslint, qui regroupe les règles de Douglas Crockford. Le problème de JSlint c’est que le plugin pour Atom se base sur la version de jslint installée sur le système, et donc chaque développeur a potentiellement une version différente de JSlint.
Finalement on utilise ESLint, avec un fichier de spécifications pour indiquer nos propres réglages des règles.
Le fichier s’appelle .eslintrc.yml
et doit être à la racine du projet.
Voici le contenu :
env:
browser: true
extends: "eslint:recommended"
rules:
# key: 0 = allow, 1 = warn, 2 = error
# Possible Errors
no-await-in-loop: 1
no-console: 1
no-extra-parens: [1, 'all']
no-template-curly-in-string: 0
# Best Practices
accessor-pairs: 0
array-callback-return: 0
block-scoped-var: 1
class-methods-use-this: 0
complexity: 0
consistent-return: 0
curly: [1, 'all']
default-case: 1
dot-location: [1, 'property']
dot-notation: 0
eqeqeq: 1
guard-for-in: 0
max-classes-per-file: 0
no-alert: 1
no-caller: 1
no-div-regex: 1
no-else-return: 0
no-empty-function: 1
no-eq-null: 1
no-eval: 0
no-extend-native: 0
no-extra-bind: 0
no-extra-label: 1
no-floating-decimal: 1
no-implicit-coercion: 1
no-implied-eval: 1
no-invalid-this: 1
no-iterator: 1
no-labels: 0
no-lone-blocks: 1
no-loop-func: 1
no-magic-numbers: 0
no-multi-spaces: 1
no-multi-str: 1
no-new: 0
no-new-func: 1
no-new-wrappers: 1
no-octal-escape: 1
no-param-reassign: 1
no-proto: 1
no-restricted-globals: 1
no-restricted-properties: 0
no-return-assign: 1
no-return-await: 1
no-script-url: 1
no-self-compare: 1
no-sequences: 1
no-throw-literal: 1
no-unmodified-loop-condition: 1
no-unused-expressions: 1
no-useless-call: 1
no-useless-concat: 1
no-useless-return: 1
no-void: 1
no-warning-comments: 0
prefer-named-capture-group: 0
prefer-promise-reject-errors: 1
radix: 1
require-await: 1
require-unicode-regexp: 0
vars-on-top: 1
wrap-iife: 1
yoda: 1
# Strict Mode
strict: [1, 'safe']
# Variables
init-declarations: 0
no-label-var: 1
no-implicit-globals: 0
no-shadow: 1
no-undef-init: 1
no-undefined: 1
no-use-before-define: 1
# Stylistic Issues
array-bracket-newline: 0
array-bracket-spacing: [1, 'never']
array-element-newline: 0
block-spacing: [1, 'always']
brace-style: [1, '1tbs']
camelcase: 1
capitalized-comments: 0
comma-dangle: [1, 'never']
comma-spacing: [1, { "before": false, "after": true }]
comma-style: 1
computed-property-spacing: [1, 'never']
consistent-this: [1, 'that']
eol-last: 1
func-call-spacing: [1, 'never']
func-name-matching: [1, 'always']
func-names: 0
func-style: [1, 'expression']
function-paren-newline: [1, 'never']
id-blacklist: 0
id-length: 0
id-match: 0
implicit-arrow-linebreak: 0
indent: [1, 4]
jsx-quotes: 0
key-spacing: 1
keyword-spacing: 1
line-comment-position: [1, 'above']
linebreak-style: [1, 'unix']
lines-around-comment: 0
lines-between-class-members: [1, 'always', { exceptAfterSingleLine: true }]
max-depth: [1, 4]
max-len: 0
max-lines: 0
max-lines-per-function: 0
max-nested-callbacks: 0
max-params: [1, 4]
max-statements: 0
max-statements-per-line: [1, { max: 1 }]
multiline-comment-style: 0
multiline-ternary: 0
new-cap: 1
new-parens: 1
newline-per-chained-call: 1
no-array-constructor: 0
no-bitwise: 0
no-continue: 0
no-inline-comments: 0
no-lonely-if: 1
no-mixed-operators: 0
no-multi-assign: 1
no-multiple-empty-lines: 1
no-negated-condition: 0
no-nested-ternary: 1
no-new-object: 0
no-plusplus: 1
no-restricted-syntax: 0
no-tabs: 1
no-ternary: 0
no-trailing-spaces: 1
no-underscore-dangle: 1
no-unneeded-ternary: 1
no-whitespace-before-property: 1
nonblock-statement-body-position: 1
object-curly-newline: 0
object-curly-spacing: [1, 'always']
object-property-newline: 0
one-var: [1, 'consecutive']
one-var-declaration-per-line: [1, 'always']
operator-assignment: [1, 'always']
operator-linebreak: 0
padded-blocks: [1, 'never']
padding-line-between-statements: 0
prefer-object-spread: 0
quote-props: 0
quotes: [1, 'single']
semi: [1, 'always']
semi-spacing: [1, { before: false, after: true }]
semi-style: [1, 'last']
sort-keys: 0
sort-vars: 0
space-before-blocks: [1, 'always']
space-before-function-paren: [1, 'always']
space-in-parens: [1, 'never']
space-infix-ops: 0
space-unary-ops: [1, { words: true, nonwords: false }]
spaced-comment: [1, 'always', { markers: ["global", "="] }]
switch-colon-spacing: [1, { after: true, before: false }]
template-tag-spacing: [1, 'always']
unicode-bom: [1, 'never']
wrap-regex: 1
Télécharger le fichier .eslintrc.yml (et renommez le .eslintrc.yml [avec le point initial])
Grâce au plugin ESLint installé sur nos éditeurs on a donc un affichage des erreurs à la sauvegarde d’un fichier .js. Il est aussi possible de préciser à CodeClimate qu’on souhaite utiliser ce même fichier de config (ça n’est pas le cas par défaut). Il faut alors éditer le fichier .codeclimate.yml à la racine du projet et ajouter
plugins:
eslint:
enabled: true
channel: "eslint-5"
config:
config: .eslintrc.yml
SASS
On utilise SASS-Lint, avec un fichier de spécifications pour indiquer nos propres réglages des règles.
Le fichier s’appelle .sass-lint.yml
et doit être à la racine du projet.
Voici le contenu :
# Linter Options
options:
# Don't merge default rules
merge-default-rules: false
# Set the formatter to 'html'
formatter: html
# Output file instead of logging results
output-file: 'linters/sass-lint.html'
# Raise an error if more than 50 warnings are generated
max-warnings: 50
# File Options
files:
include: 'app/assets/stylesheets/**/*.s+(a|c)ss'
ignore:
- 'vendor/**/*.*'
# Rule Configuration
rules:
extends-before-mixins: 2
extends-before-declarations: 2
mixins-before-declarations:
- 2
-
exclude:
- breakpoint
- breakpoint-next
- breakpoint-min
- breakpoint-max
- breakpoint-infix
- media-breakpoint-up
- media-breakpoint-down
- media-breakpoint-between
- media-breakpoint-only
- mq
no-warn: 1
no-debug: 1
hex-length:
- 2
-
style: long
hex-notation:
- 2
-
style: uppercase
indentation:
- 4
-
size: 4
property-sort-order:
- 1
-
order: alphabetical
ignore-custom-properties: false
Télécharger le fichier .sass-lint.yml (et renommez le .sass-lint.yml [avec le point initial])
Grâce au plugin SASS Lint installé sur nos éditeurs on a donc un affichage des erreurs à la sauvegarde d’un fichier .sass. Il est aussi possible de préciser à CodeClimate qu’on souhaite utiliser ce même fichier de config (ça n’est pas le cas par défaut). Il faut alors éditer le fichier .codeclimate.yml à la racine du projet et ajouter
plugins:
sass-lint:
enabled: true
rules:
class-name-format:
- 1
- convention: 'hyphenatedbem'
Sources et références
Notes pour plus tard
Intégration vs dev front, HTML, CSS (Sass, less, préprocesseurs, responsivité, media query), JS, jQuery, Bootstrap, BEM, Angular, React…