environ 10 minutes de lecture

Front

Java is to JavaScript what Car is to Carpet. (Chris Heilmann)

Scroll

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/

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) %>
      

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…