Le groupe de boutons permet à l’utilisateur de faire un choix entre plusieurs actions.

Les boutons dans le contexte d'un groupe suivent les même règles que le composant bouton (blue star) :

Groupe de boutons vertical

Cette disposition permet d’associer plusieurs boutons d’actions à la verticale :

Illustration des règles d’espacement :

{"htmlCode":"<link href=\"https://www.gouvernement.fr/sites/default/files/static_assets/dsfrv1.css\" rel=\"stylesheet\">\n<div class=\"fr-p-1w\" style=\"text-align:center;max-width:350px;margin:0 auto;\">\n<ul class=\"fr-btns-group\">\n    <li>\n        <button class=\"fr-btn\">\n            Label bouton\n        </button>\n    </li>\n    <li>\n        <button class=\"fr-btn fr-btn--secondary\">\n            Label bouton\n        </button>\n    </li>\n</ul>\n</div>"}

<ul class="fr-btns-group">
    <li>
        <button class="fr-btn">
            Label bouton
        </button>
    </li>
    <li>
        <button class="fr-btn fr-btn--secondary">
            Label bouton
        </button>
    </li>
</ul>

Groupe de boutons horizontal

Cette disposition permet d’associer plusieurs boutons d’actions à l’horizontale :

Illustration des règles d’espacement :

{"htmlCode":"<link href=\"https://www.gouvernement.fr/sites/default/files/static_assets/dsfrv1.css\" rel=\"stylesheet\">\n<style>\nli {margin-top:0!important;}\n</style>\n<ul class=\"fr-btns-group fr-btns-group--inline\">\n    <li>\n        <button class=\"fr-btn\">\n            Label bouton\n        </button>\n    </li>\n    <li>\n        <button class=\"fr-btn fr-btn--secondary\">\n            Label bouton\n        </button>\n    </li>\n</ul>\n\n\n<!-- Groupe de boutons icône seule\n    L'attribut title reprenant l'intitulé du bouton peut être ajouté pour\n    permettre l'affiche d'une infobulle.\n-->\n\n<ul class=\"fr-btns-group fr-btns-group--inline\">\n    <li>\n        <button class=\"fr-btn fr-fi-checkbox-line\" title=\"Label bouton\">\n            Label bouton\n        </button>\n    </li>\n    <li>\n        <button class=\"fr-btn fr-fi-checkbox-line fr-btn--secondary\" title=\"Label bouton 2\">\n            Label bouton 2\n        </button>\n    </li>\n    <li>\n        <button class=\"fr-btn fr-fi-checkbox-line fr-btn--secondary\" title=\"Label bouton 3\">\n            Label bouton 3\n        </button>\n    </li>\n</ul>"}

Pour les développeurs

Le mode horizontal est définie grâce au modificateur fr-btns-group--inline

<ul class="fr-btns-group fr-btns-group--inline">
    <li>
        <button class="fr-btn">
            Label bouton
        </button>
    </li>
    <li>
        <button class="fr-btn fr-btn--secondary">
            Label bouton
        </button>
    </li>
</ul>


<!-- Groupe de boutons icône seule
    L'attribut title reprenant l'intitulé du bouton peut être ajouté pour
    permettre l'affiche d'une infobulle.
-->

<ul class="fr-btns-group fr-btns-group--inline">
    <li>
        <button class="fr-btn fr-fi-checkbox-line" title="Label bouton">
            Label bouton
        </button>
    </li>
    <li>
        <button class="fr-btn fr-fi-checkbox-line fr-btn--secondary" title="Label bouton 2">
            Label bouton 2
        </button>
    </li>
    <li>
        <button class="fr-btn fr-fi-checkbox-line fr-btn--secondary" title="Label bouton 3">
            Label bouton 3
        </button>
    </li>
</ul>

Il est aussi possible d’appliquer le mode horizontal à partir d’un point de rupture défini en ajoutant l'extension -sm, -md, ou -lg au modificateur fr-btns-group--inline , ainsi :

<ul class="fr-btns-group fr-btns-group--inline-sm"> ...</ul>
<ul class="fr-btns-group fr-btns-group--inline-md"> ...</ul>
<ul class="fr-btns-group fr-btns-group--inline-lg"> ...</ul>

Placement

Par défaut le groupe de boutons horizontal est ferré à gauche du conteneur. Il est possible de le centrer et de le ferrer à droite avec les modificateurs suivants :

Tailles

Dans un groupe de boutons, la gestion de la taille des boutons SM, MD et LG se fait au niveau du groupe uniquement.
La taille MD est la taille par défaut.
Les modificateurs fr-btns-group--sm , ou fr-btns-group--lg sur le groupe permettent d'appliquer les tailles SM et LG aux boutons.

{"htmlCode":"<link href=\"https://www.gouvernement.fr/sites/default/files/static_assets/dsfrv1.css\" rel=\"stylesheet\">\n<div class=\"fr-p-1w\" style=\"text-align:center;max-width:350px;margin:0 auto;\">\n<!-- Groupes de boutons en taille small -->\n\n<ul class=\"fr-btns-group fr-btns-group--sm\">\n    <li>\n        <button class=\"fr-btn\">\n            Label bouton\n        </button>\n    </li>\n    <li>\n        <button class=\"fr-btn fr-btn--secondary\">\n            Label bouton\n        </button>\n    </li>\n</ul>\n\n<!-- Groupes de boutons en taille large -->\n\n<ul class=\"fr-btns-group fr-btns-group--lg\">\n    <li>\n        <button class=\"fr-btn\">\n            Label bouton\n        </button>\n    </li>\n    <li>\n        <button class=\"fr-btn fr-btn--secondary\">\n            Label bouton\n        </button>\n    </li>\n</ul>\n</div>\n\n<script>\n(function () {\n  'use strict';\n\n  var prefix = 'fr';\n  var namespace = 'dsfr';\n\n  var api = window[namespace] || { core: {} };\n  window[namespace] = api;\n\n  var ns = function (name) { return (prefix + \"-\" + name); };\n\n  ns.selector = function (name, notation) {\n    if (notation === undefined) { notation = '.'; }\n    return (\"\" + notation + (ns(name)));\n  };\n\n  ns.attr = function (name, quoted, value) { return (\"data-\" + (ns(name))); };\n\n  ns.attr.selector = function (name, value) {\n    var result = ns.attr(name);\n    if (value !== undefined) { result += \"=\\\"\" + value + \"\\\"\"; }\n    return (\"[\" + result + \"]\");\n  };\n\n  ns.event = function (type) { return (namespace + \".\" + type); };\n\n  var modifiyClass = function (element, className, remove) {\n    if (className.charAt(0) === '.') { className = className.substr(1); }\n    var classNames = element.className.split(' ');\n    var index = classNames.indexOf(className);\n    if (remove === true) {\n      if (index > -1) { classNames.splice(index, 1); }\n    } else if (index === -1) { classNames.push(className); }\n    element.className = classNames.join(' ');\n  };\n\n  var addClass = function (element, className) { return modifiyClass(element, className); };\n\n  var removeClass = function (element, className) { return modifiyClass(element, className, true); };\n\n  var Renderer = function Renderer () {\n    this.closures = [];\n    this.nexts = [];\n    this.rendering = this.render.bind(this);\n    this.render();\n  };\n\n  Renderer.prototype.add = function add (closure) {\n      var this$1 = this;\n\n    this.closures.push(closure);\n    var remove = function () {\n      var index = this$1.closures.indexOf(closure);\n      if (index !== -1) { this$1.closures.splice(index, 1); }\n    };\n    return remove;\n  };\n\n  Renderer.prototype.next = function next (closure, frame) {\n    frame = frame === undefined ? 0 : frame - 1;\n    if (this.nexts[frame] === undefined) { this.nexts[frame] = []; }\n    this.nexts[frame].push(closure);\n  };\n\n  Renderer.prototype.render = function render () {\n    window.requestAnimationFrame(this.rendering);\n    for (var i = 0, list = this.closures; i < list.length; i += 1) {\n        var closure = list[i];\n\n        closure.call();\n      }\n    var nexts = this.nexts.shift();\n    if (nexts) {\n      for (var i$1 = 0, list$1 = nexts; i$1 < list$1.length; i$1 += 1) {\n          var closure$1 = list$1[i$1];\n\n          closure$1.call();\n        }\n    }\n  };\n\n  // TODO: initializer et renderer en 1, avec muttation observer pour ajouter et retirer les instances des objets attendus en fonctions de selecteurs spécifiques\n  var Engine = function Engine () {\n    this.renderer = new Renderer();\n    // this.instantier = new Instancier();\n  };\n\n  Engine.prototype.register = function register (selector, factory) {\n\n  };\n\n  Engine.prototype.start = function start () {\n    // this.renderer.start();\n  };\n\n  Engine.prototype.stop = function stop () {\n    // this.renderer.stop();\n  };\n\n  var engine = new Engine();\n\n  var Initializer = function Initializer (selector, builders) {\n    this.selector = selector;\n    this.builders = builders;\n    this.instances = [];\n\n    if (document.readyState !== 'loading') { window.requestAnimationFrame(this.start.bind(this)); }\n    else { document.addEventListener('DOMContentLoaded', this.start.bind(this)); }\n  };\n\n  Initializer.prototype.start = function start () {\n    if (this.instances.length > 0) { return; }\n\n    if (document.querySelectorAll(this.selector).length > 0) {\n      for (var i = 0; i < this.builders.length; i++) {\n        this.instances.push(this.builders[i]());\n      }\n    }\n  };\n\n  var instances = { };\n  var elements = { };\n  var count = 0;\n\n  var getElementId = function (element) {\n    for (var id$1 in elements) { if (elements[id$1] === element) { return id$1; } }\n    count++;\n    var id = count;\n    elements[id] = element;\n    return id;\n  };\n\n  var Instance = function Instance (element, isResizing, isRendering) {\n    var id = getElementId(element);\n    if (!instances[id]) { instances[id] = []; }\n    instances[id].push(this);\n    this.element = element;\n    this.id = element.id;\n    this._isRendering = false;\n    this._isResizing = false;\n    this.listeners = {};\n\n    this.isResizing = isResizing;\n    this.isRendering = isRendering;\n  };\n\n  var prototypeAccessors$3 = { isRendering: { configurable: true },isResizing: { configurable: true } };\n\n  Instance.prototype.dispatch = function dispatch (type, data) {\n    var event = new CustomEvent(type, data);\n    this.element.dispatchEvent(event);\n  };\n\n  Instance.prototype.listen = function listen (type, closure) {\n    if (!this.listeners[type]) { this.listeners[type] = []; }\n    if (this.listeners[type].indexOf(closure) > -1) { return; }\n    this.listeners[type].push(closure);\n    this.element.addEventListener(type, closure);\n  };\n\n  Instance.prototype.unlisten = function unlisten (type, closure) {\n    if (!type) {\n      for (var type$1 in this.listeners) { this.unlisten(type$1); }\n    } else if (!closure) {\n      if (!this.listeners[type]) { return; }\n      for (var i = 0, list = this.listeners[type]; i < list.length; i += 1) {\n          var closure$1 = list[i];\n\n          this.element.removeEventListener(closure$1);\n        }\n      this.listeners[type].length = 0;\n    } else {\n      if (!this.listeners[type]) { return; }\n      var index = this.listeners[type].indexOf(closure);\n      if (index > -1) { this.listeners[type].splice(index, 1); }\n      this.element.removeEventListener(closure);\n    }\n  };\n\n  prototypeAccessors$3.isRendering.get = function () { return this._isRendering; };\n\n  prototypeAccessors$3.isRendering.set = function (value) {\n    if (this._isRendering === value) { return; }\n    this._isRendering = value;\n    // TODO add & remove rendering\n  };\n\n  Instance.prototype.render = function render () {};\n\n  prototypeAccessors$3.isResizing.get = function () { return this._isResizing; };\n\n  prototypeAccessors$3.isResizing.set = function (value) {\n    if (this._isResizing === value) { return; }\n    this._isResizing = value;\n    // TODO add & remove resizing\n  };\n\n  Instance.prototype.resize = function resize () {};\n\n  Instance.prototype.destroy = function destroy () {};\n\n  Instance.getInstances = function getInstances (element, instanceClass) {\n    var id = getElementId(element);\n    if (!instances[id]) { return null; }\n    else if (!instanceClass) { return instances[id]; }\n    else { return instances[id].filter(function (instance) { return instance instanceof instanceClass; }); }\n  };\n\n  Object.defineProperties( Instance.prototype, prototypeAccessors$3 );\n\n  var GROUP_ATTR = ns.attr('group');\n\n  var groups = [];\n\n  var DisclosuresGroup = function DisclosuresGroup (id, element) {\n    this.id = id;\n    this.element = element;\n    this.members = [];\n    this._index = -1;\n    this._current = null;\n    groups.push(this);\n  };\n\n  var prototypeAccessors$2 = { length: { configurable: true },index: { configurable: true },current: { configurable: true },hasFocus: { configurable: true } };\n  var staticAccessors = { selector: { configurable: true } };\n\n  DisclosuresGroup.getGroupById = function getGroupById (id) {\n    for (var i = 0, list = groups; i < list.length; i += 1) {\n        var group = list[i];\n\n        if (group.constructor === this && group.id === id) { return group;\n      } }\n    return new this(id);\n  };\n\n  DisclosuresGroup.getGroupByElement = function getGroupByElement (element) {\n    for (var i = 0, list = groups; i < list.length; i += 1) {\n        var group = list[i];\n\n        if (group.element === element) { return group;\n      } }\n    return new this(false, element);\n  };\n\n  DisclosuresGroup.groupById = function groupById (member, groupConstructor) {\n    var id = member.element.getAttribute(GROUP_ATTR);\n    if (id === null) { return; }\n\n    var group = groupConstructor.getGroupById(id);\n    group.add(member);\n  };\n\n  DisclosuresGroup.groupByParent = function groupByParent (member, GroupConstructor, groupSelector) {\n    if (groupSelector === undefined) { groupSelector = GroupConstructor.selector; }\n    if (groupSelector === '') { return; }\n    var element = member.element.parentElement;\n\n    while (element) {\n      if (element.classList.contains(member.constructor.selector)) { return; }\n\n      if (element.classList.contains(groupSelector)) {\n        var group = GroupConstructor.getGroupByElement(element);\n        group.add(member);\n        return;\n      }\n      element = element.parentElement;\n    }\n  };\n\n  staticAccessors.selector.get = function () { return ''; };\n\n  DisclosuresGroup.prototype.add = function add (member) {\n    if (this.members.indexOf(member) !== -1) { return; }\n    this.members.push(member);\n    member.setGroup(this);\n\n    switch (true) {\n      case this.current !== null:\n      case !member.disclosed && !member.primal:\n        member.disclosed = false;\n        break;\n\n      default:\n        this._current = member;\n        this._index = this.members.indexOf(member);\n        member.disclosed = true;\n    }\n  };\n\n  prototypeAccessors$2.length.get = function () { return this.members.length; };\n\n  prototypeAccessors$2.index.get = function () { return this._index; };\n\n  prototypeAccessors$2.index.set = function (value) {\n    if (value < -1 || value >= this.length || this._index === value) { return; }\n    if (this.current !== null) { this.current.conceal(true, true); }\n    this._index = value;\n    this._current = this._index > -1 ? this.members[this._index] : null;\n    if (this.current !== null) { this.current.disclose(true); }\n    this.apply();\n  };\n\n  prototypeAccessors$2.current.get = function () { return this._current; };\n\n  prototypeAccessors$2.current.set = function (member) {\n    this.index = this.members.indexOf(member);\n  };\n\n  prototypeAccessors$2.hasFocus.get = function () {\n    if (this.current === undefined) { return null; }\n    return this.current.hasFocus;\n  };\n\n  DisclosuresGroup.prototype.apply = function apply () {};\n\n  Object.defineProperties( DisclosuresGroup.prototype, prototypeAccessors$2 );\n  Object.defineProperties( DisclosuresGroup, staticAccessors );\n\n  var DisclosureButton = function DisclosureButton (element, disclosure) {\n    this.element = element;\n    this.disclosure = disclosure;\n    this.hasAttribute = this.element.hasAttribute(this.disclosure.attributeName);\n    this.element.addEventListener('click', this.click.bind(this));\n    this.observer = new MutationObserver(this.mutate.bind(this));\n    this.observe();\n  };\n\n  var prototypeAccessors$1 = { disclosed: { configurable: true },hasFocus: { configurable: true } };\n\n  DisclosureButton.prototype.observe = function observe () {\n    this.observer.observe(this.element, { attributes: true });\n  };\n\n  DisclosureButton.prototype.click = function click (e) {\n    this.disclosure.change(this.hasAttribute);\n  };\n\n  DisclosureButton.prototype.mutate = function mutate (mutations) {\n      var this$1 = this;\n\n    mutations.forEach(function (mutation) {\n      if (mutation.type === 'attributes' && mutation.attributeName === this$1.disclosure.attributeName) { this$1.disclosure.change(this$1.disclosed); }\n    });\n  };\n\n  DisclosureButton.prototype.apply = function apply (value) {\n    if (!this.hasAttribute) { return; }\n    if (this.observer) { this.observer.disconnect(); }\n    this.element.setAttribute(this.disclosure.attributeName, value);\n    if (this.observer) { this.observe(); }\n  };\n\n  prototypeAccessors$1.disclosed.get = function () {\n    return this.element.getAttribute(this.disclosure.attributeName) === 'true';\n  };\n\n  prototypeAccessors$1.hasFocus.get = function () {\n    return this.element === document.activeElement;\n  };\n\n  Object.defineProperties( DisclosureButton.prototype, prototypeAccessors$1 );\n\n  var DISCLOSE_EVENT = ns.event('DISCLOSE');\n  var CONCEAL_EVENT = ns.event('CONCEAL');\n\n  var disclosures = [];\n\n  var Disclosure = /*@__PURE__*/(function (Instance) {\n    function Disclosure (element) {\n      Instance.call(this, element);\n      this.buttons = [];\n      this._selector = this.constructor.selector;\n      this.modifier = this._selector + '--' + this.type.id;\n      this.attributeName = this.type.ariaState ? 'aria-' + this.type.id : ns.attr(this.type.id);\n      this.pristine = true;\n\n      var buttons = document.querySelectorAll(this.type.ariaControls ? (\"[aria-controls=\\\"\" + (this.id) + \"\\\"]\") : ns.attr.selector('controls', this.id));\n\n      if (buttons.length > 0) { for (var i = 0; i < buttons.length; i++) { this.addButton(buttons[i]); } }\n\n      this.disclosed = this.primal === true;\n\n      this.gather();\n    }\n\n    if ( Instance ) Disclosure.__proto__ = Instance;\n    Disclosure.prototype = Object.create( Instance && Instance.prototype );\n    Disclosure.prototype.constructor = Disclosure;\n\n    var prototypeAccessors = { type: { configurable: true },GroupConstructor: { configurable: true },disclosed: { configurable: true },buttonHasFocus: { configurable: true },hasFocus: { configurable: true } };\n    var staticAccessors = { type: { configurable: true },selector: { configurable: true } };\n\n    Disclosure.prototype.gather = function gather () {\n      if (this.group) { return; }\n\n      DisclosuresGroup.groupById(this, this.GroupConstructor);\n      DisclosuresGroup.groupByParent(this, this.GroupConstructor);\n    };\n\n    Disclosure.build = function build (from) {\n      var elements = Array.prototype.slice.call(from.querySelectorAll((\".\" + (this.selector))));\n\n      for (var i = 0, list = elements; i < list.length; i += 1) {\n        var element = list[i];\n\n        disclosures.push(new this(element));\n      }\n    };\n\n    prototypeAccessors.type.get = function () { return this.constructor.type; };\n\n    staticAccessors.type.get = function () { return null; };\n\n    staticAccessors.selector.get = function () { return ''; };\n\n    Disclosure.prototype.addButton = function addButton (element) {\n      var button = this.buttonFactory(element);\n      if (button.hasAttribute) {\n        if (this.primal === undefined) {\n          this.primal = button.disclosed;\n        } else { button.apply(this.primal); }\n      }\n      this.buttons.push(button);\n    };\n\n    prototypeAccessors.GroupConstructor.get = function () { return DisclosuresGroup; };\n\n    Disclosure.prototype.buttonFactory = function buttonFactory (button) {\n      return new DisclosureButton(button, this);\n    };\n\n    Disclosure.prototype.disclose = function disclose (withhold) {\n      if (this.disclosed) { return false; }\n      this.pristine = false;\n      this.disclosed = true;\n      if (!withhold && this.group !== undefined) { this.group.current = this; }\n      return true;\n    };\n\n    Disclosure.prototype.conceal = function conceal (withhold, preventFocus) {\n      if (!this.disclosed) { return false; }\n      this.pristine = false;\n      this.disclosed = false;\n      if (!preventFocus) { this.focus(); }\n      if (!withhold && this.group !== undefined) { this.group.current = null; }\n      for (var i = 0, list = disclosures; i < list.length; i += 1) {\n        var disclosure = list[i];\n\n        if (disclosure !== this && this.element.contains(disclosure.element)) { disclosure.reset();\n      } }\n      return true;\n    };\n\n    prototypeAccessors.disclosed.get = function () {\n      return this._disclosed;\n    };\n\n    prototypeAccessors.disclosed.set = function (value) {\n      if (this._disclosed === value) { return; }\n      this.dispatch(value ? DISCLOSE_EVENT : CONCEAL_EVENT, this.type);\n      this._disclosed = value;\n      if (value) { addClass(this.element, this.modifier); }\n      else { removeClass(this.element, this.modifier); }\n      for (var i = 0; i < this.buttons.length; i++) { this.buttons[i].apply(value); }\n    };\n\n    Disclosure.prototype.reset = function reset () {};\n\n    Disclosure.prototype.change = function change (hasAttribute) {\n      if (!this.constructor.type.canConceal) { this.disclose(); }\n      else {\n        switch (true) {\n          case !hasAttribute:\n          case this.disclosed:\n            this.conceal();\n            break;\n\n          default:\n            this.disclose();\n        }\n      }\n    };\n\n    Disclosure.prototype.setGroup = function setGroup (group) {\n      this.group = group;\n    };\n\n    prototypeAccessors.buttonHasFocus.get = function () {\n      if (this.buttons.some(function (button) { return button.hasFocus; })) { return true; }\n      return false;\n    };\n\n    prototypeAccessors.hasFocus.get = function () {\n      if (this.element === document.activeElement) { return true; }\n      if (this.element.querySelectorAll(':focus').length > 0) { return true; }\n      return this.buttonHasFocus;\n    };\n\n    Disclosure.prototype.focus = function focus () {\n      for (var i = 0; i < this.buttons.length; i++) {\n        var button = this.buttons[i];\n        if (button.hasAttribute) {\n          button.element.focus();\n          return;\n        }\n      }\n    };\n\n    Object.defineProperties( Disclosure.prototype, prototypeAccessors );\n    Object.defineProperties( Disclosure, staticAccessors );\n\n    return Disclosure;\n  }(Instance));\n\n  Disclosure.DISCLOSE_EVENT = DISCLOSE_EVENT;\n  Disclosure.CONCEAL_EVENT = CONCEAL_EVENT;\n\n  var DISCLOSURE_TYPES = {\n    expand: {\n      id: 'expanded',\n      ariaState: true,\n      ariaControls: true,\n      canConceal: true\n    },\n    select: {\n      id: 'selected',\n      ariaState: true,\n      ariaControls: true,\n      canConceal: false\n    },\n    opened: {\n      id: 'opened',\n      ariaState: false,\n      ariaControls: true,\n      canConceal: true\n    }\n  };\n\n  /**\n   * Utilitaire de gestion des évenements clavier\n   * Utiliser KeyListener.add() pour ajouter un event listener\n   */\n  var KeyListener = function KeyListener (element) {\n    this.element = element;\n    this.collections = {};\n  };\n\n  /**\n   * key: la touche de clavier\n   * closure: la function à appliquer\n   * type: event type, optionnel, si c'est en down, up ou press\n   * stopPropagation: Boolean, permet d'empêcher le comportement par default de l'evenement\n   */\n  KeyListener.prototype._add = function _add (type, action) {\n    if (this.collections[type] === undefined) { this.collections[type] = new KeyActionCollection(type, this.element); }\n    this.collections[type].add(action);\n  };\n\n  KeyListener.prototype.down = function down (key, closure, preventDefault, stopPropagation) {\n    this._add('down', new KeyAction(key, closure, preventDefault, stopPropagation));\n  };\n\n  KeyListener.prototype.up = function up (key, closure, preventDefault, stopPropagation) {\n    this._add('up', new KeyAction(key, closure, preventDefault, stopPropagation));\n  };\n\n  KeyListener.prototype.dispose = function dispose () {\n    for (var i = 0, list = this.collections; i < list.length; i += 1) {\n        var collection = list[i];\n\n        collection.dispose();\n      }\n    this.types = null;\n  };\n\n  var KeyActionCollection = function KeyActionCollection (type, element) {\n    this.type = type;\n    this.element = element;\n    this.actions = [];\n    this.binding = this.handle.bind(this);\n    this.element.addEventListener('key' + type, this.binding);\n  };\n\n  KeyActionCollection.prototype.add = function add (action) {\n    this.actions.push(action);\n  };\n\n  KeyActionCollection.prototype.handle = function handle (e) {\n    for (var i = 0, list = this.actions; i < list.length; i += 1) {\n        var action = list[i];\n\n        action.handle(e);\n      }\n  };\n\n  KeyActionCollection.prototype.dispose = function dispose () {\n    this.element.removeEventListener('key' + this.type, this.binding);\n    this.actions = null;\n  };\n\n  var KeyAction = function KeyAction (key, closure, preventDefault, stopPropagation) {\n    this.key = key;\n    this.closure = closure;\n    this.preventDefault = preventDefault === true;\n    this.stopPropagation = stopPropagation === true;\n  };\n\n  KeyAction.prototype.handle = function handle (e) {\n    if (e.keyCode === this.key) {\n      this.closure(e);\n      if (this.preventDefault) {\n        e.preventDefault();\n      }\n      if (this.stopPropagation) {\n        e.stopPropagation();\n      }\n    }\n  };\n\n  KeyListener.TAB = 9;\n  KeyListener.ESCAPE = 27;\n  KeyListener.END = 35;\n  KeyListener.HOME = 36;\n  KeyListener.LEFT = 37;\n  KeyListener.UP = 38;\n  KeyListener.RIGHT = 39;\n  KeyListener.DOWN = 40;\n\n  var COLLAPSE_CLASS = ns('collapse');\n\n  var collapses = [];\n  var ascendants = {};\n\n  /**\n   * Tab coorespond au panel d'un élement Tabs (tab panel)\n   * Tab étend disclosure qui ajoute/enleve le modifier --selected,\n   * et ajoute/eleve l'attribut hidden, sur le panel\n   */\n  var Collapse = /*@__PURE__*/(function (Disclosure) {\n    function Collapse (element) {\n      Disclosure.call(this, element);\n      collapses.push(this);\n      this.requesting = this.request.bind(this);\n      element.addEventListener('transitionend', this.transitionend.bind(this));\n      if (this.disclosed) { this.unbound(); }\n    }\n\n    if ( Disclosure ) Collapse.__proto__ = Disclosure;\n    Collapse.prototype = Object.create( Disclosure && Disclosure.prototype );\n    Collapse.prototype.constructor = Collapse;\n\n    var staticAccessors = { type: { configurable: true },selector: { configurable: true } };\n\n    Collapse.prototype.gatherByAscendants = function gatherByAscendants () {\n      if (this.group) { return; }\n\n      for (var ascendant in ascendants) {\n        var element = this.element.parentElement;\n\n        while (element) {\n          if (element.classList.contains(ascendant)) {\n            if (typeof ascendants[ascendant] === 'string') {\n              DisclosuresGroup.groupByParent(this, DisclosuresGroup, ascendants[ascendant]);\n            } else {\n              DisclosuresGroup.groupByParent(this, ascendants[ascendant]);\n            }\n            return;\n          }\n\n          element = element.parentElement;\n        }\n      }\n    };\n\n    Collapse.prototype.gather = function gather () {\n      this.gatherByAscendants();\n      Disclosure.prototype.gather.call(this);\n    };\n\n    staticAccessors.type.get = function () { return DISCLOSURE_TYPES.expand; };\n    staticAccessors.selector.get = function () { return COLLAPSE_CLASS; };\n\n    Collapse.register = function register (ascendant, groupSelector) {\n      ascendants[ascendant] = groupSelector;\n      for (var i = 0, list = collapses; i < list.length; i += 1) {\n        var collapse = list[i];\n\n        collapse.gatherByAscendants();\n      }\n    };\n\n    Collapse.prototype.transitionend = function transitionend (e) {\n      if (!this.disclosed) { this.element.style.maxHeight = ''; }\n    };\n\n    Collapse.prototype.unbound = function unbound () {\n      this.element.style.maxHeight = 'none';\n    };\n\n    Collapse.prototype.disclose = function disclose (withhold) {\n      var this$1 = this;\n\n      if (this.disclosed) { return; }\n      this.unbound();\n      this.adjust();\n      this.requested = function () { Disclosure.prototype.disclose.call(this$1, withhold); };\n      window.requestAnimationFrame(this.requesting);\n    };\n\n    Collapse.prototype.conceal = function conceal (withhold, preventFocus) {\n      var this$1 = this;\n\n      if (!this.disclosed) { return; }\n      this.adjust();\n      this.requested = function () { Disclosure.prototype.conceal.call(this$1, withhold, preventFocus); };\n      window.requestAnimationFrame(this.requesting);\n    };\n\n    Collapse.prototype.request = function request () {\n      if (this.requested) { this.requested(); }\n      this.requested = null;\n    };\n\n    Collapse.prototype.adjust = function adjust () {\n      this.element.style.setProperty('--collapser', 'none');\n      var height = this.element.offsetHeight;\n      this.element.style.setProperty('--collapse', -height + 'px');\n      this.element.style.setProperty('--collapser', '');\n    };\n\n    Collapse.prototype.reset = function reset () {\n      if (!this.pristine) { this.disclosed = false; }\n    };\n\n    Object.defineProperties( Collapse, staticAccessors );\n\n    return Collapse;\n  }(Disclosure));\n\n  var Equisized = function Equisized (selector, group) {\n    this.selector = selector;\n    this.group = group;\n    this.elements = this.group.querySelectorAll(this.selector);\n    this.maxWidth = 0;\n\n    this.changing = this.change.bind(this);\n    window.addEventListener('resize', this.changing);\n    window.addEventListener('load', this.changing); // fix change before css load\n    // this.change();\n  };\n\n  Equisized.prototype.change = function change () {\n    this.reset();\n    for (var i = 0; i < this.elements.length; i++) {\n      var tmpWWidth = this._getWidth(this.elements[i]);\n      if (tmpWWidth > this.maxWidth) {\n        this.maxWidth = tmpWWidth;\n      }\n    }\n    this.apply();\n  };\n\n  Equisized.prototype.apply = function apply () {\n    for (var i = 0; i < this.elements.length; i++) {\n      this.elements[i].style.width = this.maxWidth + 1 + 'px';\n    }\n  };\n\n  Equisized.prototype.reset = function reset () {\n    for (var i = 0; i < this.elements.length; i++) {\n      this.elements[i].style.width = 'auto';\n    }\n    this.maxWidth = 0;\n  };\n\n  Equisized.prototype._getWidth = function _getWidth (el) {\n    var width = el.offsetWidth;\n    var style = getComputedStyle(el);\n    width += parseInt(style.marginLeft) + parseInt(style.marginRight);\n    return width;\n  };\n\n  api.core.ns = ns;\n  api.core.addClass = addClass;\n  api.core.removeClass = removeClass;\n  api.core.engine = engine;\n  api.core.Instance = Instance;\n  api.core.Initializer = Initializer;\n  api.core.Disclosure = Disclosure;\n  api.core.DisclosureButton = DisclosureButton;\n  api.core.DisclosuresGroup = DisclosuresGroup;\n  api.core.DISCLOSURE_TYPES = DISCLOSURE_TYPES;\n\n  api.KeyListener = KeyListener;\n  api.Collapse = Collapse;\n  api.Equisized = Equisized;\n\n  var build$8 = function () {\n    Collapse.build(document);\n  };\n\n  /* eslint-disable no-new */\n\n  new Initializer((\".\" + COLLAPSE_CLASS), [build$8]);\n\n  var ACCORDIONS_GROUP = api.core.ns('accordions-group');\n  var ACCORDION_ASCENDANT = api.core.ns('accordion');\n\n  var AccordionsGroup = /*@__PURE__*/(function (superclass) {\n    function AccordionsGroup () {\n      superclass.apply(this, arguments);\n    }\n\n    if ( superclass ) AccordionsGroup.__proto__ = superclass;\n    AccordionsGroup.prototype = Object.create( superclass && superclass.prototype );\n    AccordionsGroup.prototype.constructor = AccordionsGroup;\n\n    var staticAccessors = { selector: { configurable: true } };\n\n    staticAccessors.selector.get = function () { return ACCORDIONS_GROUP; };\n\n    Object.defineProperties( AccordionsGroup, staticAccessors );\n\n    return AccordionsGroup;\n  }(api.core.DisclosuresGroup));\n\n  api.AccordionsGroup = AccordionsGroup;\n\n  api.Collapse.register(ACCORDION_ASCENDANT, AccordionsGroup);\n\n  var BREADCRUMB_COLLAPSE_SELECTOR = (api.core.ns.selector('breadcrumb')) + \" \" + (api.core.ns.selector('collapse'));\n\n  var Breadcrumb = /*@__PURE__*/(function (superclass) {\n    function Breadcrumb (element) {\n      superclass.call(this, element);\n      this.collapse = api.core.Instance.getInstances(element, api.Collapse)[0];\n      this.links = [].concat( this.element.querySelectorAll('a[href]') );\n      this.count = 0;\n      if (this.links.length) {\n        this.listen(api.core.Disclosure.DISCLOSE_EVENT, this.focus.bind(this));\n        // TODO: refactor avec instance\n        this.resizing = this.resize.bind(this);\n        window.addEventListener('resize', this.resizing);\n      }\n    }\n\n    if ( superclass ) Breadcrumb.__proto__ = superclass;\n    Breadcrumb.prototype = Object.create( superclass && superclass.prototype );\n    Breadcrumb.prototype.constructor = Breadcrumb;\n\n    Breadcrumb.prototype.focus = function focus () {\n      var this$1 = this;\n\n      this.links[0].focus();\n      api.core.engine.renderer.next(function () { this$1.verify(); });\n    };\n\n    Breadcrumb.prototype.verify = function verify () {\n      this.count++;\n      if (this.count > 100) { return; }\n      if (document.activeElement !== this.links[0]) { this.focus(); }\n    };\n\n    Breadcrumb.prototype.resize = function resize () {\n      if (window.matchMedia('(min-width: 48em)').matches) {\n        if (this.collapse.buttons[0] === document.activeElement) { this.links.focus(); }\n      } else {\n        if (this.links.indexOf(document.activeElement) > -1) { this.collapse.focus(); }\n      }\n    };\n\n    return Breadcrumb;\n  }(api.core.Instance));\n\n  var build$7 = function () {\n    var breadcrumbs = [];\n    var breadcrumbNodes = document.querySelectorAll(BREADCRUMB_COLLAPSE_SELECTOR);\n    for (var i = 0; i < breadcrumbNodes.length; i++) { breadcrumbs.push(new Breadcrumb(breadcrumbNodes[i])); }\n  };\n\n  /* eslint-disable no-new */\n\n  new api.core.Initializer(BREADCRUMB_COLLAPSE_SELECTOR, [build$7]);\n\n  var BUTTON_SELECTOR = api.core.ns.selector('btn');\n  var BUTTONS_GROUP_SELECTOR = api.core.ns.selector('btns-group');\n  var EQUISIZED_BUTTONS_GROUP_SELECTOR = api.core.ns.selector('btns-group--equisized');\n\n  var build$6 = function () {\n    var group = document.querySelectorAll(EQUISIZED_BUTTONS_GROUP_SELECTOR);\n    var arrayEquisized = [];\n    for (var i = 0; i < group.length; i++) {\n      arrayEquisized.push(new api.Equisized(BUTTON_SELECTOR, group[i]));\n    }\n  };\n\n  /* eslint-disable no-new */\n\n  new api.core.Initializer(BUTTONS_GROUP_SELECTOR, [build$6]);\n\n  var MODAL_SELECTOR = api.core.ns.selector('modal');\n  var MODAL_CLASS = api.core.ns('modal');\n  var NO_SCROLL_CLASS = api.core.ns('no-scroll');\n  var SCROLL_SHADOW_CLASS = api.core.ns('scroll-shadow');\n  var MODAL_BODY_SELECTOR = api.core.ns.selector('modal__body');\n  var OFFSET_MOBILE = 32; // 32px => 8v => 2rem\n\n  var unordereds = [\n    '[tabindex=\"0\"]',\n    'a[href]',\n    'button:not([disabled])',\n    'input:not([disabled])',\n    'select:not([disabled])',\n    'textarea:not([disabled])',\n    'audio[controls]',\n    'video[controls]',\n    '[contenteditable]:not([contenteditable=\"false\" i])',\n    'details>summary:first-of-type',\n    'details'\n  ];\n\n  var UNORDEREDS = unordereds.join();\n\n  var ordereds = [\n    '[tabindex]:not([tabindex=\"-1\"]):not([tabindex=\"0\"])'\n  ];\n\n  var ORDEREDS = ordereds.join();\n\n  var isFocusable = function (element, container) {\n    if (window.getComputedStyle(element).visibility === 'hidden') { return false; }\n    if (container === undefined) { container = element; }\n\n    while (container.contains(element)) {\n      if (window.getComputedStyle(element).display === 'none') { return false; }\n      element = element.parentElement;\n    }\n\n    return true;\n  };\n\n  var FocusTrap = function FocusTrap (onTrap, onUntrap) {\n    this.element = null;\n    this.activeElement = null;\n    this.onTrap = onTrap;\n    this.onUntrap = onUntrap;\n    this.waiting = this.wait.bind(this);\n    this.handling = this.handle.bind(this);\n    this.current = null;\n  };\n\n  var prototypeAccessors = { trapped: { configurable: true },focusables: { configurable: true } };\n\n  prototypeAccessors.trapped.get = function () { return this.element !== null; };\n\n  FocusTrap.prototype.trap = function trap (element) {\n    if (this.trapped) { this.untrap(); }\n\n    this.element = element;\n    this.isTrapping = true;\n    this.wait();\n\n    if (this.onTrap) { this.onTrap(); }\n  };\n\n  FocusTrap.prototype.wait = function wait () {\n    if (!isFocusable(this.element)) {\n      api.core.engine.renderer.next(this.waiting);\n      return;\n    }\n\n    this.trapping();\n  };\n\n  FocusTrap.prototype.trapping = function trapping () {\n    if (!this.isTrapping) { return; }\n    this.isTrapping = false;\n    var focusables = this.focusables;\n    if (focusables.length) { focusables[0].focus(); }\n    this.element.setAttribute('aria-modal', true);\n    this.element.addEventListener('keydown', this.handling);\n\n    this.stunneds = [];\n    // this.stun(document.body);\n  };\n\n  FocusTrap.prototype.stun = function stun (node) {\n    for (var i = 0, list = node.children; i < list.length; i += 1) {\n      var child = list[i];\n\n        if (child === this.element) { continue; }\n      if (child.contains(this.element)) {\n        this.stun(child);\n        continue;\n      }\n      this.stunneds.push(new Stunned(child));\n    }\n  };\n\n  FocusTrap.prototype.handle = function handle (e) {\n    if (e.keyCode !== 9) { return; }\n\n    var focusables = this.focusables;\n    if (focusables.length === 0) { return; }\n\n    var first = focusables[0];\n    var last = focusables[focusables.length - 1];\n\n    var index = focusables.indexOf(document.activeElement);\n\n    if (e.shiftKey) {\n      if (!this.element.contains(document.activeElement) || index < 1) {\n        e.preventDefault();\n        last.focus();\n      } else if (document.activeElement.tabIndex > 0 || focusables[index - 1].tabIndex > 0) {\n        e.preventDefault();\n        focusables[index - 1].focus();\n      }\n    } else {\n      if (!this.element.contains(document.activeElement) || index === focusables.length - 1 || index === -1) {\n        e.preventDefault();\n        first.focus();\n      } else if (document.activeElement.tabIndex > 0) {\n        e.preventDefault();\n        focusables[index + 1].focus();\n      }\n    }\n  };\n\n  prototypeAccessors.focusables.get = function () {\n      var this$1 = this;\n\n    var unordereds = [].concat( this.element.querySelectorAll(UNORDEREDS) );\n\n    /**\n     *filtrage des radiobutttons de même name (la navigations d'un groupe de radio se fait à la flèche et non pas au tab\n     **/\n    var radios = [].concat( document.documentElement.querySelectorAll('input[type=\"radio\"]') );\n\n    if (radios.length) {\n      var groups = {};\n\n      for (var i = 0, list = radios; i < list.length; i += 1) {\n        var radio = list[i];\n\n          var name = radio.getAttribute('name');\n        if (groups[name] === undefined) { groups[name] = new RadioButtonGroup(name); }\n        groups[name].push(radio);\n      }\n\n      unordereds = unordereds.filter(function (unordered) {\n        if (unordered.tagName.toLowerCase() !== 'input' || unordered.getAttribute('type').toLowerCase() !== 'radio') { return true; }\n        var name = unordered.getAttribute('name');\n        return groups[name].keep(unordered);\n      });\n    }\n\n    var ordereds = [].concat( this.element.querySelectorAll(ORDEREDS) );\n\n    ordereds.sort(function (a, b) { return a.tabIndex - b.tabIndex; });\n\n    var noDuplicates = unordereds.filter(function (element) { return ordereds.indexOf(element) === -1; });\n    var concateneds = ordereds.concat(noDuplicates);\n    return concateneds.filter(function (element) { return element.tabIndex !== '-1' && isFocusable(element, this$1.element); });\n  };\n\n  FocusTrap.prototype.untrap = function untrap () {\n    if (!this.trapped) { return; }\n    this.isTrapping = false;\n\n    this.element.removeAttribute('aria-modal');\n    this.element.removeEventListener('keydown', this.handling);\n    this.element = null;\n\n    // for (const stunned of this.stunneds) stunned.unstun();\n    // this.stunneds = [];\n\n    if (this.onUntrap) { this.onUntrap(); }\n  };\n\n  Object.defineProperties( FocusTrap.prototype, prototypeAccessors );\n\n  var Stunned = function Stunned (element) {\n    this.element = element;\n    this.hidden = element.getAttribute('aria-hidden');\n    this.inert = element.getAttribute('inert');\n\n    this.element.setAttribute('aria-hidden', true);\n    this.element.setAttribute('inert', '');\n  };\n\n  Stunned.prototype.unstun = function unstun () {\n    if (this.hidden === null) { this.element.removeAttribute('aria-hidden'); }\n    else { this.element.setAttribute('aria-hidden', this.hidden); }\n\n    if (this.inert === null) { this.element.removeAttribute('inert'); }\n    else { this.element.setAttribute('inert', this.inert); }\n  };\n\n  var RadioButtonGroup = function RadioButtonGroup (name) {\n    this.name = name;\n    this.buttons = [];\n  };\n\n  RadioButtonGroup.prototype.push = function push (button) {\n    this.buttons.push(button);\n    if (button === document.activeElement || button.checked || this.selected === undefined) { this.selected = button; }\n  };\n\n  RadioButtonGroup.prototype.keep = function keep (button) {\n    return this.selected === button;\n  };\n\n  var ModalsGroup = /*@__PURE__*/(function (superclass) {\n    function ModalsGroup () {\n      superclass.call(this);\n      this.trap = new FocusTrap();\n    }\n\n    if ( superclass ) ModalsGroup.__proto__ = superclass;\n    ModalsGroup.prototype = Object.create( superclass && superclass.prototype );\n    ModalsGroup.prototype.constructor = ModalsGroup;\n\n    ModalsGroup.prototype.apply = function apply (value, initial) {\n      superclass.prototype.apply.call(this, value, initial);\n      if (this.current === null) { this.trap.untrap(); }\n      else { this.trap.trap(this.current.element); }\n    };\n\n    return ModalsGroup;\n  }(api.core.DisclosuresGroup));\n\n  var group = new ModalsGroup();\n\n  var Modal = /*@__PURE__*/(function (superclass) {\n    function Modal (element) {\n      superclass.call(this, element);\n      this.body = this.element.querySelector(MODAL_BODY_SELECTOR);\n      this.scrollDistance = 0;\n      this.scrolling = this.resize.bind(this, false);\n      this.resizing = this.resize.bind(this, true);\n      this.init();\n      this.resize(true);\n    }\n\n    if ( superclass ) Modal.__proto__ = superclass;\n    Modal.prototype = Object.create( superclass && superclass.prototype );\n    Modal.prototype.constructor = Modal;\n\n    var prototypeAccessors = { GroupConstructor: { configurable: true } };\n    var staticAccessors = { type: { configurable: true },selector: { configurable: true } };\n\n    Modal.prototype.init = function init () {\n      this.element.addEventListener('click', this.click.bind(this));\n\n      this.keyListener = new api.KeyListener(this.element);\n      this.keyListener.down(api.KeyListener.ESCAPE, this.conceal.bind(this), true, true);\n\n      if (this.body) {\n        this.body.addEventListener('scroll', this.scrolling);\n        window.addEventListener('resize', this.resizing);\n        // window.addEventListener('orientationchange', this.resizing);\n      }\n    };\n\n    Modal.prototype.click = function click (e) {\n      if (this.body && this.body !== e.target && !this.body.contains(e.target)) { this.conceal(); }\n    };\n\n    Modal.prototype.gather = function gather () {\n      group.add(this);\n    };\n\n    Modal.prototype.disclose = function disclose (withhold) {\n      if (!superclass.prototype.disclose.call(this, withhold)) { return false; }\n      this.resize(true);\n      this.handleScroll(false);\n      return true;\n    };\n\n    Modal.prototype.conceal = function conceal (withhold, preventFocus) {\n      if (!superclass.prototype.conceal.call(this, withhold, preventFocus)) { return false; }\n      this.handleScroll(true);\n      return true;\n    };\n\n    /**\n     * Fixe l'arrière plan quand la modal est ouverte\n     */\n    // TODO: créer une fonction de fix de scroll dans core (api.noScroll = true)\n    Modal.prototype.handleScroll = function handleScroll (isScrollable) {\n      if (isScrollable) {\n        api.core.removeClass(document.documentElement, NO_SCROLL_CLASS);\n        document.body.style.top = '';\n        window.scroll(0, this.scrollDistance);\n      } else {\n        if (!document.documentElement.classList.contains(NO_SCROLL_CLASS)) {\n          this.scrollDistance = window.scrollY;\n        }\n        document.body.style.top = this.scrollDistance * -1 + 'px';\n        api.core.addClass(document.documentElement, NO_SCROLL_CLASS);\n      }\n    };\n\n    /**\n     * Ajoute une ombre autour du footer lorsque l'on peut scroller dans la modale\n     * corrige le 100vh, en mobile notamment, lorsque la barre navigateur est présente par exemple.\n     */\n    Modal.prototype.resize = function resize (isResizing, e) {\n      var this$1 = this;\n\n      if (!this.body) { return; }\n      if (this.body.scrollHeight > this.body.clientHeight) {\n        if (this.body.offsetHeight + this.body.scrollTop >= this.body.scrollHeight) {\n          api.core.removeClass(this.body, SCROLL_SHADOW_CLASS);\n        } else {\n          api.core.addClass(this.body, SCROLL_SHADOW_CLASS);\n        }\n      } else {\n        api.core.removeClass(this.body, SCROLL_SHADOW_CLASS);\n      }\n\n      if (isResizing) {\n        this.body.style.maxHeight = (window.innerHeight - OFFSET_MOBILE) + 'px';\n\n        // Une deuxième fois après positionnement des barres du navigateur (ios)\n        // TODO: à tester si fonctionnel sans setTimeout\n        api.core.engine.renderer.next(function () {\n          this$1.body.style.maxHeight = (window.innerHeight - OFFSET_MOBILE) + 'px';\n        });\n      }\n    };\n\n    staticAccessors.type.get = function () { return api.core.DISCLOSURE_TYPES.opened; };\n    staticAccessors.selector.get = function () { return MODAL_CLASS; };\n\n    prototypeAccessors.GroupConstructor.get = function () { return ModalsGroup; };\n\n    Object.defineProperties( Modal.prototype, prototypeAccessors );\n    Object.defineProperties( Modal, staticAccessors );\n\n    return Modal;\n  }(api.core.Disclosure));\n\n  api.Modal = Modal;\n  api.ModalsGroup = ModalsGroup;\n  api.FocusTrap = FocusTrap;\n\n  /**\n   * initialise tout les éléments Modal dans la page\n   */\n  var build$5 = function () {\n    Modal.build(document);\n  };\n\n  /* eslint-disable no-new */\n\n  new api.core.Initializer(MODAL_SELECTOR, [build$5]);\n\n  var NAVIGATION_CLASS = api.core.ns('nav');\n  var NAVIGATION_LIST_CLASS = api.core.ns('nav__list');\n  var NAVIGATION_ITEM_CLASS = api.core.ns('nav__item');\n  var NAVIGATION_ITEM_RIGHT_CLASS = api.core.ns('nav__item--align-right');\n  var NAVIGATION_MENU_CLASS = api.core.ns('menu');\n\n  var Navigation = /*@__PURE__*/(function (superclass) {\n    function Navigation (id, element) {\n      superclass.call(this, id, element);\n\n      this.menus = [];\n\n      this.navList = element.querySelector((\".\" + NAVIGATION_LIST_CLASS));\n\n      document.addEventListener('focusout', this.focusOut.bind(this));\n      window.addEventListener('resize', this.resize.bind(this));\n      window.addEventListener('orientationchange', this.resize.bind(this));\n      this.resize();\n    }\n\n    if ( superclass ) Navigation.__proto__ = superclass;\n    Navigation.prototype = Object.create( superclass && superclass.prototype );\n    Navigation.prototype.constructor = Navigation;\n\n    var prototypeAccessors = { index: { configurable: true } };\n    var staticAccessors = { selector: { configurable: true } };\n\n    staticAccessors.selector.get = function () { return NAVIGATION_CLASS; };\n\n    Navigation.prototype.add = function add (member) {\n      superclass.prototype.add.call(this, member);\n\n      if (member.element.classList.contains(NAVIGATION_MENU_CLASS)) {\n        this.menus.push(new NavigationMenu(member, this.navList.getBoundingClientRect().right));\n      }\n    };\n\n    Navigation.prototype.focusOut = function focusOut (e) {\n      var this$1 = this;\n\n      requestAnimationFrame(function () {\n        if (this$1.current !== null && !this$1.current.hasFocus) { this$1.index = -1; }\n      });\n    };\n\n    prototypeAccessors.index.get = function () { return superclass.prototype.index; };\n\n    prototypeAccessors.index.set = function (value) {\n      if (value === -1 && this.current !== null && this.current.hasFocus) { this.current.focus(); }\n      superclass.prototype.index = value;\n    };\n\n    Navigation.prototype.resize = function resize () {\n      var right = this.navList.getBoundingClientRect().right;\n\n      for (var i = 0, list = this.menus; i < list.length; i += 1) {\n        var menu = list[i];\n\n        menu.place(right);\n      }\n    };\n\n    Object.defineProperties( Navigation.prototype, prototypeAccessors );\n    Object.defineProperties( Navigation, staticAccessors );\n\n    return Navigation;\n  }(api.core.DisclosuresGroup));\n\n  var NavigationMenu = function NavigationMenu (collapse, right) {\n    this.initialize(collapse);\n    this.place(right);\n  };\n\n  NavigationMenu.prototype.initialize = function initialize (collapse) {\n    this.element = collapse.element;\n\n    for (var i = 0, list = collapse.buttons; i < list.length; i += 1) {\n      var button = list[i];\n\n        if (!button.hasAttribute) { continue; }\n      this.button = button.element;\n      break;\n    }\n\n    var item = this.element.parentElement;\n    while (item) {\n      if (item.classList.contains(NAVIGATION_ITEM_CLASS)) {\n        this.item = item;\n        break;\n      }\n      item = item.parentElement;\n    }\n  };\n\n  NavigationMenu.prototype.place = function place (right) {\n    var styles = getComputedStyle(this.element);\n    var width = parseFloat(styles.width);\n    var left = this.button.getBoundingClientRect().left;\n\n    if (left + width > right) { api.core.addClass(this.item, NAVIGATION_ITEM_RIGHT_CLASS); }\n    else { api.core.removeClass(this.item, NAVIGATION_ITEM_RIGHT_CLASS); }\n  };\n\n  api.Navigation = Navigation;\n\n  api.Collapse.register(NAVIGATION_CLASS, Navigation);\n\n  var SCHEME_ATTR = api.core.ns.attr('theme');\n  var TRANSITION_ATTR = api.core.ns.attr('transition');\n\n  /**\n   * TODO: implémenter la valeur system\n   * window.matchMedia(\"(prefers-color-scheme: dark)\").addListener(\n   e => e.matches && activateDarkMode()) // listener\n   );\n   */\n\n  var Scheme = function Scheme () {\n    this.init();\n  };\n\n  Scheme.prototype.init = function init () {\n      var this$1 = this;\n\n    this.root = document.documentElement;\n\n    this.scheme = localStorage.getItem('scheme')\n      ? localStorage.getItem('scheme')\n      : null;\n\n    if (this.scheme === null) {\n      var scheme = this.root.getAttribute(SCHEME_ATTR);\n      if (scheme === 'dark' || scheme === 'light') {\n        this.scheme = scheme;\n      } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {\n        this.scheme = 'dark';\n        localStorage.setItem('scheme', 'dark');\n      } else { this.scheme = 'light'; }\n    }\n\n    if (this.scheme === 'dark') {\n      if (!this.root.hasAttribute(TRANSITION_ATTR)) {\n        this.root.setAttribute(SCHEME_ATTR, 'dark');\n      } else {\n        this.root.removeAttribute(TRANSITION_ATTR);\n        this.root.setAttribute(SCHEME_ATTR, 'dark');\n\n        setTimeout(function () {\n          this$1.root.setAttribute(TRANSITION_ATTR, '');\n        }, 300);\n      }\n    } else { this.root.setAttribute(SCHEME_ATTR, 'light'); }\n\n    this.observer = new MutationObserver(this.mutate.bind(this));\n    this.observer.observe(this.root, { attributes: true });\n  };\n\n  Scheme.prototype.mutate = function mutate (mutations) {\n      var this$1 = this;\n\n    mutations.forEach(function (mutation) {\n      if (mutation.type === 'attributes' && mutation.attributeName === SCHEME_ATTR) {\n        var scheme = this$1.root.getAttribute(SCHEME_ATTR);\n        if (scheme === 'dark') {\n          localStorage.setItem('scheme', 'dark');\n        } else if (scheme === 'light') {\n          localStorage.setItem('scheme', 'light');\n        }\n      }\n    });\n  };\n\n  api.Scheme = Scheme;\n\n  var RADIOS_THEME_NAME = \"input[name=\\\"\" + (api.core.ns.selector('radios-theme', '')) + \"\\\"]\";\n  var SWITCH_THEME_ID = api.core.ns.selector('switch-theme', '#');\n  var THEME_ATTR = api.core.ns.attr('theme');\n\n  /* eslint-disable no-new */\n\n  var build$4 = function () {\n    new Scheme();\n  };\n\n  var SwitchTheme = function SwitchTheme () {\n    this.attributeName = THEME_ATTR;\n    this.theme = null;\n    this.radios = document.querySelectorAll(RADIOS_THEME_NAME);\n\n    for (var i = 0; i < this.radios.length; i++) {\n      this.radios[i].addEventListener('change', this.change.bind(this));\n    }\n\n    this.observer = new MutationObserver(this.mutate.bind(this));\n    this.observe();\n    this.apply();\n  };\n\n  SwitchTheme.prototype.observe = function observe () {\n    this.observer.observe(document.documentElement, { attributes: true });\n  };\n\n  SwitchTheme.prototype.mutate = function mutate (mutations) {\n      var this$1 = this;\n\n    mutations.forEach(function (mutation) {\n      if (mutation.type === 'attributes' && mutation.attributeName === this$1.attributeName) {\n        this$1.apply();\n      }\n    });\n  };\n\n  SwitchTheme.prototype.apply = function apply () {\n    var theme = document.documentElement.getAttribute(this.attributeName);\n    this.isApplying = true;\n    for (var i = 0; i < this.radios.length; i++) {\n      this.radios[i].checked = this.radios[i].value === theme;\n    }\n    this.isApplying = false;\n  };\n\n  SwitchTheme.prototype.change = function change () {\n    if (this.isApplying) { return; }\n    if (this.observer) { this.observer.disconnect(); }\n    this.theme = document.querySelector(RADIOS_THEME_NAME + ':checked');\n    if (this.theme) {\n      document.documentElement.setAttribute(this.attributeName, this.theme.value);\n    } else {\n      document.documentElement.removeAttribute(this.attributeName);\n    }\n    if (this.observer) { this.observe(); }\n  };\n\n  /* eslint-disable no-new */\n\n  var build$3 = function () {\n    new SwitchTheme();\n  };\n\n  /* eslint-disable no-new */\n\n  new api.core.Initializer((\":root[\" + SCHEME_ATTR + \"]\"), [build$4]);\n  new api.core.Initializer((\"\" + SWITCH_THEME_ID), [build$3]);\n\n  var SIDEMENU_CLASS = api.core.ns('sidemenu');\n  var SIDEMENU_LIST_CLASS = api.core.ns('sidemenu__list');\n\n  /* eslint-disable no-new */\n\n  api.Collapse.register(SIDEMENU_CLASS, SIDEMENU_LIST_CLASS);\n\n  var TABLE_SELECTOR = api.core.ns.selector('table');\n  // export const TABLE_CLASS = api.core.ns('table');\n  var TABLE_SCROLLING_SELECTOR = (api.core.ns.selector('table')) + \":not(\" + (api.core.ns.selector('table--no-scroll')) + \")\";\n  var LEFT = 'left';\n  var RIGHT = 'right';\n  var SHADOW_CLASS = api.core.ns('table--shadow');\n  var SHADOW_LEFT_CLASS = api.core.ns('table--shadow-left');\n  var SHADOW_RIGHT_CLASS = api.core.ns('table--shadow-right');\n  var WRAPPER_CLASS = api.core.ns('table__wrapper');\n  var CAPTION_BOTTOM_CLASS = api.core.ns('table--caption-bottom');\n  var SCROLL_OFFSET = 1; // valeur en px du scroll avant laquelle le shadow s'active ou se desactive\n\n  var Table = function Table (table) {\n    this.init(table);\n  };\n\n  Table.prototype.init = function init (table) {\n    this.table = table;\n    this.tableElem = this.table.querySelector('table');\n    this.tableContent = this.tableElem.querySelector('tbody');\n    this.isScrollable = this.tableContent.offsetWidth > this.tableElem.offsetWidth;\n    this.caption = this.tableElem.querySelector('caption');\n    this.captionHeight = 0;\n    this.wrap();\n\n    var scrolling = this.change.bind(this);\n    this.tableElem.addEventListener('scroll', scrolling);\n    this.change();\n  };\n\n  Table.prototype.change = function change () {\n    var newScroll = this.tableContent.offsetWidth > this.tableElem.offsetWidth;\n    var firstTimeScrollable = this.tableElem.offsetWidth > this.table.offsetWidth;\n    if (newScroll || firstTimeScrollable) {\n      this.scroll();\n      this.handleCaption();\n    } else {\n      if (newScroll !== this.isScrollable) { this.delete(); }\n    }\n    this.isScrollable = newScroll;\n    firstTimeScrollable = false;\n  };\n\n  Table.prototype.delete = function delete$1 () {\n    api.core.removeClass(this.table, SHADOW_RIGHT_CLASS);\n    api.core.removeClass(this.table, SHADOW_LEFT_CLASS);\n    api.core.removeClass(this.table, SHADOW_CLASS);\n    if (this.caption) {\n      this.tableElem.style.marginTop = '';\n      this.caption.style.top = '';\n      this.tableElem.style.marginBottom = '';\n      this.caption.style.bottom = '';\n    }\n  };\n\n  Table.prototype.scroll = function scroll () {\n    api.core.addClass(this.table, SHADOW_CLASS);\n    this.setShadowPosition();\n  };\n\n  /* ajoute un wrapper autour du tableau */\n  Table.prototype.wrap = function wrap () {\n    var wrapperHtml = document.createElement('div');\n    wrapperHtml.className = WRAPPER_CLASS;\n    this.table.insertBefore(wrapperHtml, this.tableElem);\n    wrapperHtml.appendChild(this.tableElem);\n    this.tableInnerWrapper = wrapperHtml;\n  };\n\n  /* affiche les blocs shadow en fonction de la position du scroll, en ajoutant la classe visible */\n  Table.prototype.setShadowPosition = function setShadowPosition () {\n    var tableScrollLeft = this.getScrollPosition(LEFT);\n    var tableScrollRight = this.getScrollPosition(RIGHT);\n\n    // on inverse en cas de lecture droite - gauche\n    if (document.documentElement.getAttribute('dir') === 'rtl') {\n      this.setShadowVisibility(RIGHT, tableScrollLeft);\n      this.setShadowVisibility(LEFT, tableScrollRight);\n    } else {\n      this.setShadowVisibility(LEFT, tableScrollLeft);\n      this.setShadowVisibility(RIGHT, tableScrollRight);\n    }\n  };\n\n  /* Donne le nombre de pixels scrollés honrizontalement dans un element scrollable */\n  Table.prototype.getScrollPosition = function getScrollPosition (side) {\n    var inverter = 1;\n    // on inverse en cas de lecture droite - gauche pour que la valeur de scroll soit toujours positive\n    if (document.documentElement.getAttribute('dir') === 'rtl') {\n      inverter = -1;\n    }\n    switch (side) {\n      case LEFT:\n        return this.tableElem.scrollLeft * inverter;\n      case RIGHT:\n        return this.tableContent.offsetWidth - this.tableElem.offsetWidth - this.tableElem.scrollLeft * inverter;\n      default:\n        return false;\n    }\n  };\n\n  /* positionne la caption en top négatif et ajoute un margin-top au wrapper */\n  Table.prototype.handleCaption = function handleCaption () {\n    if (this.caption) {\n      var style = getComputedStyle(this.caption);\n      var newHeight = this.caption.offsetHeight + parseInt(style.marginTop) + parseInt(style.marginBottom);\n      this.captionHeight = newHeight;\n      if (this.table.classList.contains(CAPTION_BOTTOM_CLASS)) {\n        this.tableElem.style.marginBottom = this.captionHeight + 'px';\n        this.caption.style.bottom = -this.captionHeight + 'px';\n      } else {\n        this.tableElem.style.marginTop = this.captionHeight + 'px';\n        this.caption.style.top = -this.captionHeight + 'px';\n      }\n    }\n  };\n\n  /* ajoute la classe fr-table--shadow-right ou fr-table--shadow-right sur fr-table\n   en fonction d'une valeur de scroll et du sens (right, left) */\n  Table.prototype.setShadowVisibility = function setShadowVisibility (side, scrollPosition) {\n    // si on a pas scroll, ou qu'on scroll jusqu'au bout\n    if (scrollPosition <= SCROLL_OFFSET) {\n      if (side === LEFT) { api.core.removeClass(this.table, SHADOW_LEFT_CLASS); }\n      else if (side === RIGHT) { api.core.removeClass(this.table, SHADOW_RIGHT_CLASS); }\n    } else {\n      if (side === LEFT) { api.core.addClass(this.table, SHADOW_LEFT_CLASS); }\n      else if (side === RIGHT) { api.core.addClass(this.table, SHADOW_RIGHT_CLASS); }\n    }\n  };\n\n  api.Table = Table;\n\n  var tables = [];\n\n  var change = function () {\n    for (var i = 0; i < tables.length; i++) { tables[i].change(); }\n  };\n\n  var build$2 = function () {\n    var tableNodes = document.querySelectorAll(TABLE_SCROLLING_SELECTOR);\n    for (var i = 0; i < tableNodes.length; i++) { tables.push(new Table(tableNodes[i])); }\n\n    window.addEventListener('resize', change);\n    window.addEventListener('orientationchange', change);\n    change();\n  };\n\n  /* eslint-disable no-new */\n\n  new api.core.Initializer(TABLE_SELECTOR, [build$2]);\n\n  /**\n    * TabButton correspond au bouton cliquable qui change le panel\n    * TabButton étend de DisclosureButton qui ajoute/enelve l'attribut aria-selected,\n    * Et change l'attributte tabindex a 0 si le boutton est actif (value=true), -1 s'il n'est pas actif (value=false)\n   */\n  var TabButton = /*@__PURE__*/(function (superclass) {\n    function TabButton () {\n      superclass.apply(this, arguments);\n    }\n\n    if ( superclass ) TabButton.__proto__ = superclass;\n    TabButton.prototype = Object.create( superclass && superclass.prototype );\n    TabButton.prototype.constructor = TabButton;\n\n    TabButton.prototype.apply = function apply (value) {\n      superclass.prototype.apply.call(this, value);\n      if (this.hasAttribute) {\n        this.element.setAttribute('tabindex', value ? '0' : '-1');\n      }\n    };\n\n    return TabButton;\n  }(api.core.DisclosureButton));\n\n  var TABS_SELECTOR = api.core.ns.selector('tabs');\n  var TABS_CLASS = api.core.ns('tabs');\n  var TAB_CLASS = api.core.ns('tabs__tab');\n  var PANEL_CLASS = api.core.ns('tabs__panel');\n  var LIST_CLASS = api.core.ns('tabs__list');\n\n  /**\n  * TabGroup est la classe étendue de DiscosuresGroup\n  * Correspond à un objet Tabs avec plusieurs tab-button & Tab (panel)\n  */\n  var TabsGroup = /*@__PURE__*/(function (superclass) {\n    function TabsGroup (id, element) {\n      superclass.call(this, id, element);\n      this.list = element.querySelector((\".\" + LIST_CLASS));\n\n      element.addEventListener('transitionend', this.transitionend.bind(this));\n\n      this.init();\n      api.core.engine.renderer.add(this.render.bind(this));\n    }\n\n    if ( superclass ) TabsGroup.__proto__ = superclass;\n    TabsGroup.prototype = Object.create( superclass && superclass.prototype );\n    TabsGroup.prototype.constructor = TabsGroup;\n\n    var staticAccessors = { selector: { configurable: true } };\n\n    staticAccessors.selector.get = function () { return TABS_CLASS; };\n\n    TabsGroup.prototype.transitionend = function transitionend (e) {\n      this.element.style.transition = 'none';\n    };\n\n    TabsGroup.prototype.init = function init () {\n      this.keyListener = new api.KeyListener(this.element);\n      this.keyListener.down(api.KeyListener.RIGHT, this.arrowRightPress.bind(this), true, true);\n      this.keyListener.down(api.KeyListener.LEFT, this.arrowLeftPress.bind(this), true, true);\n      this.keyListener.down(api.KeyListener.HOME, this.homePress.bind(this), true, true);\n      this.keyListener.down(api.KeyListener.END, this.endPress.bind(this), true, true);\n    };\n\n    /**\n     * Selectionne l'element suivant de la liste si on est sur un bouton\n     * Si on est à la fin on retourne au début\n     */\n    TabsGroup.prototype.arrowRightPress = function arrowRightPress () {\n      if (document.activeElement.classList.contains(TAB_CLASS)) {\n        if (this.index < this.length - 1) {\n          this.index++;\n        } else {\n          this.index = 0;\n        }\n\n        this.focus();\n      }\n    };\n    /**\n     * Selectionne l'element précédent de la liste si on est sur un bouton\n     * Si on est au debut retourne a la fin\n     */\n    TabsGroup.prototype.arrowLeftPress = function arrowLeftPress () {\n      if (document.activeElement.classList.contains(TAB_CLASS)) {\n        if (this.index > 0) {\n          this.index--;\n        } else {\n          this.index = this.length - 1;\n        }\n\n        this.focus();\n      }\n    };\n    /**\n     * Selectionne le permier element de la liste si on est sur un bouton\n     */\n    TabsGroup.prototype.homePress = function homePress () {\n      if (document.activeElement.classList.contains(TAB_CLASS)) {\n        this.index = 0;\n        this.focus();\n      }\n    };\n    /**\n     * Selectionne le dernier element de la liste si on est sur un bouton\n     */\n    TabsGroup.prototype.endPress = function endPress () {\n      if (document.activeElement.classList.contains(TAB_CLASS)) {\n        this.index = this.length - 1;\n        this.focus();\n      }\n    };\n    TabsGroup.prototype.focus = function focus () {\n      if (this.current) { this.current.focus(); }\n    };\n\n    TabsGroup.prototype.apply = function apply () {\n      for (var i = 0; i < this._index; i++) { this.members[i].translate(-1); }\n      this.current.element.style.transform = '';\n      for (var i$1 = this._index + 1; i$1 < this.length; i$1++) { this.members[i$1].translate(1); }\n      this.element.style.transition = '';\n    };\n\n    TabsGroup.prototype.add = function add (tab) {\n      superclass.prototype.add.call(this, tab);\n      if (this.length === 1 || tab.disclosed) { this.current = tab; }\n      else {\n        var index = this.members.indexOf(tab);\n        if (this._index > -1 && this._index !== index) { tab.translate(index < this._index ? -1 : 1, true); }\n      }\n    };\n\n    TabsGroup.prototype.render = function render () {\n      if (this.current === null) { return; }\n      var paneHeight = Math.round(this.current.element.offsetHeight);\n      if (this.panelHeight === paneHeight) { return; }\n      this.panelHeight = paneHeight;\n      this.element.style.height = (this.panelHeight + this.list.offsetHeight) + 'px';\n    };\n\n    Object.defineProperties( TabsGroup, staticAccessors );\n\n    return TabsGroup;\n  }(api.core.DisclosuresGroup));\n\n  /**\n    * Tab coorespond au panel d'un élement Tabs (tab panel)\n    * Tab étend disclosure qui ajoute/enleve le modifier --selected,\n    * et ajoute/eleve l'attribut hidden, sur le panel\n    */\n  var Tab = /*@__PURE__*/(function (superclass) {\n    function Tab () {\n      superclass.apply(this, arguments);\n    }\n\n    if ( superclass ) Tab.__proto__ = superclass;\n    Tab.prototype = Object.create( superclass && superclass.prototype );\n    Tab.prototype.constructor = Tab;\n\n    var prototypeAccessors = { GroupConstructor: { configurable: true } };\n    var staticAccessors = { type: { configurable: true },selector: { configurable: true } };\n\n    staticAccessors.type.get = function () { return api.core.DISCLOSURE_TYPES.select; };\n    staticAccessors.selector.get = function () { return PANEL_CLASS; };\n\n    prototypeAccessors.GroupConstructor.get = function () { return TabsGroup; };\n\n    Tab.prototype.buttonFactory = function buttonFactory (element) {\n      return new TabButton(element, this);\n    };\n\n    Tab.prototype.translate = function translate (direction, initial) {\n      if (initial) { this.element.style.transition = 'none'; }\n      this.element.style.transform = \"translate(\" + (direction * 100) + \"%)\";\n      if (initial) { this.element.style.transition = ''; }\n    };\n\n    Tab.prototype.reset = function reset () {\n      this.group.index = 0;\n    };\n\n    Object.defineProperties( Tab.prototype, prototypeAccessors );\n    Object.defineProperties( Tab, staticAccessors );\n\n    return Tab;\n  }(api.core.Disclosure));\n\n  api.Tab = Tab;\n  api.TabButton = TabButton;\n  api.TabsGroup = TabsGroup;\n\n  /**\n  * Initialise tout les éléments Tab dans la page\n  */\n  var build$1 = function () {\n    Tab.build(document);\n  };\n\n  /* eslint-disable no-new */\n\n  new api.core.Initializer(TABS_SELECTOR, [build$1]);\n\n  var HEADER_SELECTOR = api.core.ns.selector('header');\n  var HEADER_SEARCH_SELECTOR = api.core.ns.selector('header__search');\n  var HEADER_MENU_SELECTOR = api.core.ns.selector('header__menu');\n  var HEADER_TOOLS_LINKS_SELECTOR = api.core.ns.selector('header__tools-links');\n  var HEADER_MENU_LINKS_SELECTOR = api.core.ns.selector('header__menu-links');\n  var HEADER_LINKS_GROUP_SELECTOR = HEADER_TOOLS_LINKS_SELECTOR + \" \" + (api.core.ns.selector('links-group'));\n\n  var Header = function Header (header) {\n    this.header = header || document.querySelector(HEADER_SELECTOR);\n    this.modals = [];\n\n    this.init();\n  };\n\n  Header.prototype.getModal = function getModal (selector) {\n    var element = this.header.querySelector(selector);\n    if (!element) { return; }\n    var modals = api.core.Instance.getInstances(element, api.Modal);\n    if (!modals || !modals.length) { return; }\n    this.modals.push(modals[0]);\n  };\n\n  Header.prototype.init = function init () {\n    this.getModal(HEADER_SEARCH_SELECTOR);\n    this.getModal(HEADER_MENU_SELECTOR);\n\n    this.linksGroup = this.header.querySelector(HEADER_LINKS_GROUP_SELECTOR);\n\n    this.toolsLinks = this.header.querySelector(HEADER_TOOLS_LINKS_SELECTOR);\n    this.menuLinks = this.header.querySelector(HEADER_MENU_LINKS_SELECTOR);\n\n    this.changing = this.change.bind(this);\n\n    window.addEventListener('resize', this.changing);\n    this.change();\n  };\n\n  Header.prototype.change = function change () {\n    this.isLarge = window.matchMedia('(min-width: 62em)').matches;\n\n    if (this.isLarge) {\n      for (var i = 0; i < this.modals.length; i++) {\n        this.modals[i].conceal();\n        this.modals[i].element.removeAttribute('role');\n      }\n    } else {\n      for (var i$1 = 0; i$1 < this.modals.length; i$1++) {\n        this.modals[i$1].element.setAttribute('role', 'dialog');\n      }\n    }\n\n    if (this.linksGroup !== null) {\n      if (this.isLarge) { this.toolsLinks.appendChild(this.linksGroup); }\n      else { this.menuLinks.appendChild(this.linksGroup); }\n    }\n  };\n\n  api.Header = Header;\n\n  var build = function () {\n    var elements = Array.prototype.slice.call(document.querySelectorAll(HEADER_SELECTOR));\n\n    var headers = [];\n\n    for (var i = 0, list = elements; i < list.length; i += 1) {\n      var element = list[i];\n\n      headers.push(new Header(element));\n    }\n  };\n\n  /* eslint-disable no-new */\n\n  new api.core.Initializer(HEADER_SELECTOR, [build]);\n\n}());\n</script>"}
<!-- Groupes de boutons en taille small -->

<ul class="fr-btns-group fr-btns-group--sm">
    <li>
        <button class="fr-btn">
            Label bouton
        </button>
    </li>
    <li>
        <button class="fr-btn fr-btn--secondary">
            Label bouton
        </button>
    </li>
</ul>

<!-- Groupes de boutons en taille large -->

<ul class="fr-btns-group fr-btns-group--lg">
    <li>
        <button class="fr-btn">
            Label bouton
        </button>
    </li>
    <li>
        <button class="fr-btn fr-btn--secondary">
            Label bouton
        </button>
    </li>
</ul>

Pour les designers SKETCH

Les groupes de boutons verticaux sont constitués de boutons “fluides” disponibles par défaut à 588 px pour s’intégrer dans un prototype mobile. Ces symboles sont prévus pour s’adapter à la largeur des conteneurs de vos designs.

Les groupes de boutons horizontaux sont constitués des boutons “avec padding”, qui se redimensionnent automatiquement en fonction du contenu texte

La modularité proposée en code permet d’obtenir des boutons d’une même largeur dans un groupe horizontal, la largeur étant définie par le bouton le plus large. Pour les utiliser il faudra créer le groupe avec les symboles de boutons “fluides” présents dans “ 02 Composants / - Élements - ne pas utiliser / Boutons / Fluides “, et respecter les spécifications d’espacements du groupe de boutons horizontaux

Pour les développeurs

Ajouter des icônes au groupe de boutons

Il est possible, d’utiliser dans le groupe des boutons, des boutons avec icônes.
La gestion des icônes, est gérée au niveau de chaque bouton. De la même façon qu’un bouton seul.

Attention, pour que le texte s’affiche (si vous souhaitez des boutons avec texte et bouton), il faut ajouter les modifiers fr-btns-group--icon-left ou fr-btns-group--icon-right sur la liste fr-btns-group.
Voir : Documentation sur les boutons pour l’affichage des icônes.
A noter que les icônes ont la même place au sein d’un groupe (à droite ou à gauche).

Forcer une même largeur pour l’ensemble des boutons du groupe

Une option permet de mettre automatiquement tous les boutons d’un groupe à la même largeur, en se basant sur le bouton le plus large. Cela permet une uniformité des boutons en mode horizontal comme vertical.

En mode vertical et en mobile, cette option permet aussi d'éviter que les boutons prennent 100% de la largeur du conteneur. Le modificateur fr-btns-group--equisized permet d’exécuter le JavaScript fixant la taille des boutons.

Cette option peut être combiné avec les modificateurs d’alignements fr-btns-group--right et et fr-btns-group--center pour aligner les boutons sur la droite ou au centre du conteneur. (Par défaut : alignés à gauche)

Exemple d’un groupe de boutons de même taille :

{"htmlCode":"<link href=\"https://www.gouvernement.fr/sites/default/files/static_assets/dsfrv1.css\" rel=\"stylesheet\">\n<div class=\"fr-p-1w\">\n<ul class=\"fr-btns-group fr-btns-group--equisized\">\n    <li>\n        <button class=\"fr-btn\">\n            Label bouton\n        </button>\n    </li>\n    <li>\n        <button class=\"fr-btn fr-btn--secondary\">\n            lorem ipsum label très long\n        </button>\n    </li>\n</ul>\n</div>\n\n<script>\n(function () {\n  'use strict';\n\n  var prefix = 'fr';\n  var namespace = 'dsfr';\n\n  var api = window[namespace] || { core: {} };\n  window[namespace] = api;\n\n  var ns = function (name) { return (prefix + \"-\" + name); };\n\n  ns.selector = function (name, notation) {\n    if (notation === undefined) { notation = '.'; }\n    return (\"\" + notation + (ns(name)));\n  };\n\n  ns.attr = function (name, quoted, value) { return (\"data-\" + (ns(name))); };\n\n  ns.attr.selector = function (name, value) {\n    var result = ns.attr(name);\n    if (value !== undefined) { result += \"=\\\"\" + value + \"\\\"\"; }\n    return (\"[\" + result + \"]\");\n  };\n\n  ns.event = function (type) { return (namespace + \".\" + type); };\n\n  var modifiyClass = function (element, className, remove) {\n    if (className.charAt(0) === '.') { className = className.substr(1); }\n    var classNames = element.className.split(' ');\n    var index = classNames.indexOf(className);\n    if (remove === true) {\n      if (index > -1) { classNames.splice(index, 1); }\n    } else if (index === -1) { classNames.push(className); }\n    element.className = classNames.join(' ');\n  };\n\n  var addClass = function (element, className) { return modifiyClass(element, className); };\n\n  var removeClass = function (element, className) { return modifiyClass(element, className, true); };\n\n  var Renderer = function Renderer () {\n    this.closures = [];\n    this.nexts = [];\n    this.rendering = this.render.bind(this);\n    this.render();\n  };\n\n  Renderer.prototype.add = function add (closure) {\n      var this$1 = this;\n\n    this.closures.push(closure);\n    var remove = function () {\n      var index = this$1.closures.indexOf(closure);\n      if (index !== -1) { this$1.closures.splice(index, 1); }\n    };\n    return remove;\n  };\n\n  Renderer.prototype.next = function next (closure, frame) {\n    frame = frame === undefined ? 0 : frame - 1;\n    if (this.nexts[frame] === undefined) { this.nexts[frame] = []; }\n    this.nexts[frame].push(closure);\n  };\n\n  Renderer.prototype.render = function render () {\n    window.requestAnimationFrame(this.rendering);\n    for (var i = 0, list = this.closures; i < list.length; i += 1) {\n        var closure = list[i];\n\n        closure.call();\n      }\n    var nexts = this.nexts.shift();\n    if (nexts) {\n      for (var i$1 = 0, list$1 = nexts; i$1 < list$1.length; i$1 += 1) {\n          var closure$1 = list$1[i$1];\n\n          closure$1.call();\n        }\n    }\n  };\n\n  // TODO: initializer et renderer en 1, avec muttation observer pour ajouter et retirer les instances des objets attendus en fonctions de selecteurs spécifiques\n  var Engine = function Engine () {\n    this.renderer = new Renderer();\n    // this.instantier = new Instancier();\n  };\n\n  Engine.prototype.register = function register (selector, factory) {\n\n  };\n\n  Engine.prototype.start = function start () {\n    // this.renderer.start();\n  };\n\n  Engine.prototype.stop = function stop () {\n    // this.renderer.stop();\n  };\n\n  var engine = new Engine();\n\n  var Initializer = function Initializer (selector, builders) {\n    this.selector = selector;\n    this.builders = builders;\n    this.instances = [];\n\n    if (document.readyState !== 'loading') { window.requestAnimationFrame(this.start.bind(this)); }\n    else { document.addEventListener('DOMContentLoaded', this.start.bind(this)); }\n  };\n\n  Initializer.prototype.start = function start () {\n    if (this.instances.length > 0) { return; }\n\n    if (document.querySelectorAll(this.selector).length > 0) {\n      for (var i = 0; i < this.builders.length; i++) {\n        this.instances.push(this.builders[i]());\n      }\n    }\n  };\n\n  var instances = { };\n  var elements = { };\n  var count = 0;\n\n  var getElementId = function (element) {\n    for (var id$1 in elements) { if (elements[id$1] === element) { return id$1; } }\n    count++;\n    var id = count;\n    elements[id] = element;\n    return id;\n  };\n\n  var Instance = function Instance (element, isResizing, isRendering) {\n    var id = getElementId(element);\n    if (!instances[id]) { instances[id] = []; }\n    instances[id].push(this);\n    this.element = element;\n    this.id = element.id;\n    this._isRendering = false;\n    this._isResizing = false;\n    this.listeners = {};\n\n    this.isResizing = isResizing;\n    this.isRendering = isRendering;\n  };\n\n  var prototypeAccessors$3 = { isRendering: { configurable: true },isResizing: { configurable: true } };\n\n  Instance.prototype.dispatch = function dispatch (type, data) {\n    var event = new CustomEvent(type, data);\n    this.element.dispatchEvent(event);\n  };\n\n  Instance.prototype.listen = function listen (type, closure) {\n    if (!this.listeners[type]) { this.listeners[type] = []; }\n    if (this.listeners[type].indexOf(closure) > -1) { return; }\n    this.listeners[type].push(closure);\n    this.element.addEventListener(type, closure);\n  };\n\n  Instance.prototype.unlisten = function unlisten (type, closure) {\n    if (!type) {\n      for (var type$1 in this.listeners) { this.unlisten(type$1); }\n    } else if (!closure) {\n      if (!this.listeners[type]) { return; }\n      for (var i = 0, list = this.listeners[type]; i < list.length; i += 1) {\n          var closure$1 = list[i];\n\n          this.element.removeEventListener(closure$1);\n        }\n      this.listeners[type].length = 0;\n    } else {\n      if (!this.listeners[type]) { return; }\n      var index = this.listeners[type].indexOf(closure);\n      if (index > -1) { this.listeners[type].splice(index, 1); }\n      this.element.removeEventListener(closure);\n    }\n  };\n\n  prototypeAccessors$3.isRendering.get = function () { return this._isRendering; };\n\n  prototypeAccessors$3.isRendering.set = function (value) {\n    if (this._isRendering === value) { return; }\n    this._isRendering = value;\n    // TODO add & remove rendering\n  };\n\n  Instance.prototype.render = function render () {};\n\n  prototypeAccessors$3.isResizing.get = function () { return this._isResizing; };\n\n  prototypeAccessors$3.isResizing.set = function (value) {\n    if (this._isResizing === value) { return; }\n    this._isResizing = value;\n    // TODO add & remove resizing\n  };\n\n  Instance.prototype.resize = function resize () {};\n\n  Instance.prototype.destroy = function destroy () {};\n\n  Instance.getInstances = function getInstances (element, instanceClass) {\n    var id = getElementId(element);\n    if (!instances[id]) { return null; }\n    else if (!instanceClass) { return instances[id]; }\n    else { return instances[id].filter(function (instance) { return instance instanceof instanceClass; }); }\n  };\n\n  Object.defineProperties( Instance.prototype, prototypeAccessors$3 );\n\n  var GROUP_ATTR = ns.attr('group');\n\n  var groups = [];\n\n  var DisclosuresGroup = function DisclosuresGroup (id, element) {\n    this.id = id;\n    this.element = element;\n    this.members = [];\n    this._index = -1;\n    this._current = null;\n    groups.push(this);\n  };\n\n  var prototypeAccessors$2 = { length: { configurable: true },index: { configurable: true },current: { configurable: true },hasFocus: { configurable: true } };\n  var staticAccessors = { selector: { configurable: true } };\n\n  DisclosuresGroup.getGroupById = function getGroupById (id) {\n    for (var i = 0, list = groups; i < list.length; i += 1) {\n        var group = list[i];\n\n        if (group.constructor === this && group.id === id) { return group;\n      } }\n    return new this(id);\n  };\n\n  DisclosuresGroup.getGroupByElement = function getGroupByElement (element) {\n    for (var i = 0, list = groups; i < list.length; i += 1) {\n        var group = list[i];\n\n        if (group.element === element) { return group;\n      } }\n    return new this(false, element);\n  };\n\n  DisclosuresGroup.groupById = function groupById (member, groupConstructor) {\n    var id = member.element.getAttribute(GROUP_ATTR);\n    if (id === null) { return; }\n\n    var group = groupConstructor.getGroupById(id);\n    group.add(member);\n  };\n\n  DisclosuresGroup.groupByParent = function groupByParent (member, GroupConstructor, groupSelector) {\n    if (groupSelector === undefined) { groupSelector = GroupConstructor.selector; }\n    if (groupSelector === '') { return; }\n    var element = member.element.parentElement;\n\n    while (element) {\n      if (element.classList.contains(member.constructor.selector)) { return; }\n\n      if (element.classList.contains(groupSelector)) {\n        var group = GroupConstructor.getGroupByElement(element);\n        group.add(member);\n        return;\n      }\n      element = element.parentElement;\n    }\n  };\n\n  staticAccessors.selector.get = function () { return ''; };\n\n  DisclosuresGroup.prototype.add = function add (member) {\n    if (this.members.indexOf(member) !== -1) { return; }\n    this.members.push(member);\n    member.setGroup(this);\n\n    switch (true) {\n      case this.current !== null:\n      case !member.disclosed && !member.primal:\n        member.disclosed = false;\n        break;\n\n      default:\n        this._current = member;\n        this._index = this.members.indexOf(member);\n        member.disclosed = true;\n    }\n  };\n\n  prototypeAccessors$2.length.get = function () { return this.members.length; };\n\n  prototypeAccessors$2.index.get = function () { return this._index; };\n\n  prototypeAccessors$2.index.set = function (value) {\n    if (value < -1 || value >= this.length || this._index === value) { return; }\n    if (this.current !== null) { this.current.conceal(true, true); }\n    this._index = value;\n    this._current = this._index > -1 ? this.members[this._index] : null;\n    if (this.current !== null) { this.current.disclose(true); }\n    this.apply();\n  };\n\n  prototypeAccessors$2.current.get = function () { return this._current; };\n\n  prototypeAccessors$2.current.set = function (member) {\n    this.index = this.members.indexOf(member);\n  };\n\n  prototypeAccessors$2.hasFocus.get = function () {\n    if (this.current === undefined) { return null; }\n    return this.current.hasFocus;\n  };\n\n  DisclosuresGroup.prototype.apply = function apply () {};\n\n  Object.defineProperties( DisclosuresGroup.prototype, prototypeAccessors$2 );\n  Object.defineProperties( DisclosuresGroup, staticAccessors );\n\n  var DisclosureButton = function DisclosureButton (element, disclosure) {\n    this.element = element;\n    this.disclosure = disclosure;\n    this.hasAttribute = this.element.hasAttribute(this.disclosure.attributeName);\n    this.element.addEventListener('click', this.click.bind(this));\n    this.observer = new MutationObserver(this.mutate.bind(this));\n    this.observe();\n  };\n\n  var prototypeAccessors$1 = { disclosed: { configurable: true },hasFocus: { configurable: true } };\n\n  DisclosureButton.prototype.observe = function observe () {\n    this.observer.observe(this.element, { attributes: true });\n  };\n\n  DisclosureButton.prototype.click = function click (e) {\n    this.disclosure.change(this.hasAttribute);\n  };\n\n  DisclosureButton.prototype.mutate = function mutate (mutations) {\n      var this$1 = this;\n\n    mutations.forEach(function (mutation) {\n      if (mutation.type === 'attributes' && mutation.attributeName === this$1.disclosure.attributeName) { this$1.disclosure.change(this$1.disclosed); }\n    });\n  };\n\n  DisclosureButton.prototype.apply = function apply (value) {\n    if (!this.hasAttribute) { return; }\n    if (this.observer) { this.observer.disconnect(); }\n    this.element.setAttribute(this.disclosure.attributeName, value);\n    if (this.observer) { this.observe(); }\n  };\n\n  prototypeAccessors$1.disclosed.get = function () {\n    return this.element.getAttribute(this.disclosure.attributeName) === 'true';\n  };\n\n  prototypeAccessors$1.hasFocus.get = function () {\n    return this.element === document.activeElement;\n  };\n\n  Object.defineProperties( DisclosureButton.prototype, prototypeAccessors$1 );\n\n  var DISCLOSE_EVENT = ns.event('DISCLOSE');\n  var CONCEAL_EVENT = ns.event('CONCEAL');\n\n  var disclosures = [];\n\n  var Disclosure = /*@__PURE__*/(function (Instance) {\n    function Disclosure (element) {\n      Instance.call(this, element);\n      this.buttons = [];\n      this._selector = this.constructor.selector;\n      this.modifier = this._selector + '--' + this.type.id;\n      this.attributeName = this.type.ariaState ? 'aria-' + this.type.id : ns.attr(this.type.id);\n      this.pristine = true;\n\n      var buttons = document.querySelectorAll(this.type.ariaControls ? (\"[aria-controls=\\\"\" + (this.id) + \"\\\"]\") : ns.attr.selector('controls', this.id));\n\n      if (buttons.length > 0) { for (var i = 0; i < buttons.length; i++) { this.addButton(buttons[i]); } }\n\n      this.disclosed = this.primal === true;\n\n      this.gather();\n    }\n\n    if ( Instance ) Disclosure.__proto__ = Instance;\n    Disclosure.prototype = Object.create( Instance && Instance.prototype );\n    Disclosure.prototype.constructor = Disclosure;\n\n    var prototypeAccessors = { type: { configurable: true },GroupConstructor: { configurable: true },disclosed: { configurable: true },buttonHasFocus: { configurable: true },hasFocus: { configurable: true } };\n    var staticAccessors = { type: { configurable: true },selector: { configurable: true } };\n\n    Disclosure.prototype.gather = function gather () {\n      if (this.group) { return; }\n\n      DisclosuresGroup.groupById(this, this.GroupConstructor);\n      DisclosuresGroup.groupByParent(this, this.GroupConstructor);\n    };\n\n    Disclosure.build = function build (from) {\n      var elements = Array.prototype.slice.call(from.querySelectorAll((\".\" + (this.selector))));\n\n      for (var i = 0, list = elements; i < list.length; i += 1) {\n        var element = list[i];\n\n        disclosures.push(new this(element));\n      }\n    };\n\n    prototypeAccessors.type.get = function () { return this.constructor.type; };\n\n    staticAccessors.type.get = function () { return null; };\n\n    staticAccessors.selector.get = function () { return ''; };\n\n    Disclosure.prototype.addButton = function addButton (element) {\n      var button = this.buttonFactory(element);\n      if (button.hasAttribute) {\n        if (this.primal === undefined) {\n          this.primal = button.disclosed;\n        } else { button.apply(this.primal); }\n      }\n      this.buttons.push(button);\n    };\n\n    prototypeAccessors.GroupConstructor.get = function () { return DisclosuresGroup; };\n\n    Disclosure.prototype.buttonFactory = function buttonFactory (button) {\n      return new DisclosureButton(button, this);\n    };\n\n    Disclosure.prototype.disclose = function disclose (withhold) {\n      if (this.disclosed) { return false; }\n      this.pristine = false;\n      this.disclosed = true;\n      if (!withhold && this.group !== undefined) { this.group.current = this; }\n      return true;\n    };\n\n    Disclosure.prototype.conceal = function conceal (withhold, preventFocus) {\n      if (!this.disclosed) { return false; }\n      this.pristine = false;\n      this.disclosed = false;\n      if (!preventFocus) { this.focus(); }\n      if (!withhold && this.group !== undefined) { this.group.current = null; }\n      for (var i = 0, list = disclosures; i < list.length; i += 1) {\n        var disclosure = list[i];\n\n        if (disclosure !== this && this.element.contains(disclosure.element)) { disclosure.reset();\n      } }\n      return true;\n    };\n\n    prototypeAccessors.disclosed.get = function () {\n      return this._disclosed;\n    };\n\n    prototypeAccessors.disclosed.set = function (value) {\n      if (this._disclosed === value) { return; }\n      this.dispatch(value ? DISCLOSE_EVENT : CONCEAL_EVENT, this.type);\n      this._disclosed = value;\n      if (value) { addClass(this.element, this.modifier); }\n      else { removeClass(this.element, this.modifier); }\n      for (var i = 0; i < this.buttons.length; i++) { this.buttons[i].apply(value); }\n    };\n\n    Disclosure.prototype.reset = function reset () {};\n\n    Disclosure.prototype.change = function change (hasAttribute) {\n      if (!this.constructor.type.canConceal) { this.disclose(); }\n      else {\n        switch (true) {\n          case !hasAttribute:\n          case this.disclosed:\n            this.conceal();\n            break;\n\n          default:\n            this.disclose();\n        }\n      }\n    };\n\n    Disclosure.prototype.setGroup = function setGroup (group) {\n      this.group = group;\n    };\n\n    prototypeAccessors.buttonHasFocus.get = function () {\n      if (this.buttons.some(function (button) { return button.hasFocus; })) { return true; }\n      return false;\n    };\n\n    prototypeAccessors.hasFocus.get = function () {\n      if (this.element === document.activeElement) { return true; }\n      if (this.element.querySelectorAll(':focus').length > 0) { return true; }\n      return this.buttonHasFocus;\n    };\n\n    Disclosure.prototype.focus = function focus () {\n      for (var i = 0; i < this.buttons.length; i++) {\n        var button = this.buttons[i];\n        if (button.hasAttribute) {\n          button.element.focus();\n          return;\n        }\n      }\n    };\n\n    Object.defineProperties( Disclosure.prototype, prototypeAccessors );\n    Object.defineProperties( Disclosure, staticAccessors );\n\n    return Disclosure;\n  }(Instance));\n\n  Disclosure.DISCLOSE_EVENT = DISCLOSE_EVENT;\n  Disclosure.CONCEAL_EVENT = CONCEAL_EVENT;\n\n  var DISCLOSURE_TYPES = {\n    expand: {\n      id: 'expanded',\n      ariaState: true,\n      ariaControls: true,\n      canConceal: true\n    },\n    select: {\n      id: 'selected',\n      ariaState: true,\n      ariaControls: true,\n      canConceal: false\n    },\n    opened: {\n      id: 'opened',\n      ariaState: false,\n      ariaControls: true,\n      canConceal: true\n    }\n  };\n\n  /**\n   * Utilitaire de gestion des évenements clavier\n   * Utiliser KeyListener.add() pour ajouter un event listener\n   */\n  var KeyListener = function KeyListener (element) {\n    this.element = element;\n    this.collections = {};\n  };\n\n  /**\n   * key: la touche de clavier\n   * closure: la function à appliquer\n   * type: event type, optionnel, si c'est en down, up ou press\n   * stopPropagation: Boolean, permet d'empêcher le comportement par default de l'evenement\n   */\n  KeyListener.prototype._add = function _add (type, action) {\n    if (this.collections[type] === undefined) { this.collections[type] = new KeyActionCollection(type, this.element); }\n    this.collections[type].add(action);\n  };\n\n  KeyListener.prototype.down = function down (key, closure, preventDefault, stopPropagation) {\n    this._add('down', new KeyAction(key, closure, preventDefault, stopPropagation));\n  };\n\n  KeyListener.prototype.up = function up (key, closure, preventDefault, stopPropagation) {\n    this._add('up', new KeyAction(key, closure, preventDefault, stopPropagation));\n  };\n\n  KeyListener.prototype.dispose = function dispose () {\n    for (var i = 0, list = this.collections; i < list.length; i += 1) {\n        var collection = list[i];\n\n        collection.dispose();\n      }\n    this.types = null;\n  };\n\n  var KeyActionCollection = function KeyActionCollection (type, element) {\n    this.type = type;\n    this.element = element;\n    this.actions = [];\n    this.binding = this.handle.bind(this);\n    this.element.addEventListener('key' + type, this.binding);\n  };\n\n  KeyActionCollection.prototype.add = function add (action) {\n    this.actions.push(action);\n  };\n\n  KeyActionCollection.prototype.handle = function handle (e) {\n    for (var i = 0, list = this.actions; i < list.length; i += 1) {\n        var action = list[i];\n\n        action.handle(e);\n      }\n  };\n\n  KeyActionCollection.prototype.dispose = function dispose () {\n    this.element.removeEventListener('key' + this.type, this.binding);\n    this.actions = null;\n  };\n\n  var KeyAction = function KeyAction (key, closure, preventDefault, stopPropagation) {\n    this.key = key;\n    this.closure = closure;\n    this.preventDefault = preventDefault === true;\n    this.stopPropagation = stopPropagation === true;\n  };\n\n  KeyAction.prototype.handle = function handle (e) {\n    if (e.keyCode === this.key) {\n      this.closure(e);\n      if (this.preventDefault) {\n        e.preventDefault();\n      }\n      if (this.stopPropagation) {\n        e.stopPropagation();\n      }\n    }\n  };\n\n  KeyListener.TAB = 9;\n  KeyListener.ESCAPE = 27;\n  KeyListener.END = 35;\n  KeyListener.HOME = 36;\n  KeyListener.LEFT = 37;\n  KeyListener.UP = 38;\n  KeyListener.RIGHT = 39;\n  KeyListener.DOWN = 40;\n\n  var COLLAPSE_CLASS = ns('collapse');\n\n  var collapses = [];\n  var ascendants = {};\n\n  /**\n   * Tab coorespond au panel d'un élement Tabs (tab panel)\n   * Tab étend disclosure qui ajoute/enleve le modifier --selected,\n   * et ajoute/eleve l'attribut hidden, sur le panel\n   */\n  var Collapse = /*@__PURE__*/(function (Disclosure) {\n    function Collapse (element) {\n      Disclosure.call(this, element);\n      collapses.push(this);\n      this.requesting = this.request.bind(this);\n      element.addEventListener('transitionend', this.transitionend.bind(this));\n      if (this.disclosed) { this.unbound(); }\n    }\n\n    if ( Disclosure ) Collapse.__proto__ = Disclosure;\n    Collapse.prototype = Object.create( Disclosure && Disclosure.prototype );\n    Collapse.prototype.constructor = Collapse;\n\n    var staticAccessors = { type: { configurable: true },selector: { configurable: true } };\n\n    Collapse.prototype.gatherByAscendants = function gatherByAscendants () {\n      if (this.group) { return; }\n\n      for (var ascendant in ascendants) {\n        var element = this.element.parentElement;\n\n        while (element) {\n          if (element.classList.contains(ascendant)) {\n            if (typeof ascendants[ascendant] === 'string') {\n              DisclosuresGroup.groupByParent(this, DisclosuresGroup, ascendants[ascendant]);\n            } else {\n              DisclosuresGroup.groupByParent(this, ascendants[ascendant]);\n            }\n            return;\n          }\n\n          element = element.parentElement;\n        }\n      }\n    };\n\n    Collapse.prototype.gather = function gather () {\n      this.gatherByAscendants();\n      Disclosure.prototype.gather.call(this);\n    };\n\n    staticAccessors.type.get = function () { return DISCLOSURE_TYPES.expand; };\n    staticAccessors.selector.get = function () { return COLLAPSE_CLASS; };\n\n    Collapse.register = function register (ascendant, groupSelector) {\n      ascendants[ascendant] = groupSelector;\n      for (var i = 0, list = collapses; i < list.length; i += 1) {\n        var collapse = list[i];\n\n        collapse.gatherByAscendants();\n      }\n    };\n\n    Collapse.prototype.transitionend = function transitionend (e) {\n      if (!this.disclosed) { this.element.style.maxHeight = ''; }\n    };\n\n    Collapse.prototype.unbound = function unbound () {\n      this.element.style.maxHeight = 'none';\n    };\n\n    Collapse.prototype.disclose = function disclose (withhold) {\n      var this$1 = this;\n\n      if (this.disclosed) { return; }\n      this.unbound();\n      this.adjust();\n      this.requested = function () { Disclosure.prototype.disclose.call(this$1, withhold); };\n      window.requestAnimationFrame(this.requesting);\n    };\n\n    Collapse.prototype.conceal = function conceal (withhold, preventFocus) {\n      var this$1 = this;\n\n      if (!this.disclosed) { return; }\n      this.adjust();\n      this.requested = function () { Disclosure.prototype.conceal.call(this$1, withhold, preventFocus); };\n      window.requestAnimationFrame(this.requesting);\n    };\n\n    Collapse.prototype.request = function request () {\n      if (this.requested) { this.requested(); }\n      this.requested = null;\n    };\n\n    Collapse.prototype.adjust = function adjust () {\n      this.element.style.setProperty('--collapser', 'none');\n      var height = this.element.offsetHeight;\n      this.element.style.setProperty('--collapse', -height + 'px');\n      this.element.style.setProperty('--collapser', '');\n    };\n\n    Collapse.prototype.reset = function reset () {\n      if (!this.pristine) { this.disclosed = false; }\n    };\n\n    Object.defineProperties( Collapse, staticAccessors );\n\n    return Collapse;\n  }(Disclosure));\n\n  var Equisized = function Equisized (selector, group) {\n    this.selector = selector;\n    this.group = group;\n    this.elements = this.group.querySelectorAll(this.selector);\n    this.maxWidth = 0;\n\n    this.changing = this.change.bind(this);\n    window.addEventListener('resize', this.changing);\n    window.addEventListener('load', this.changing); // fix change before css load\n    // this.change();\n  };\n\n  Equisized.prototype.change = function change () {\n    this.reset();\n    for (var i = 0; i < this.elements.length; i++) {\n      var tmpWWidth = this._getWidth(this.elements[i]);\n      if (tmpWWidth > this.maxWidth) {\n        this.maxWidth = tmpWWidth;\n      }\n    }\n    this.apply();\n  };\n\n  Equisized.prototype.apply = function apply () {\n    for (var i = 0; i < this.elements.length; i++) {\n      this.elements[i].style.width = this.maxWidth + 1 + 'px';\n    }\n  };\n\n  Equisized.prototype.reset = function reset () {\n    for (var i = 0; i < this.elements.length; i++) {\n      this.elements[i].style.width = 'auto';\n    }\n    this.maxWidth = 0;\n  };\n\n  Equisized.prototype._getWidth = function _getWidth (el) {\n    var width = el.offsetWidth;\n    var style = getComputedStyle(el);\n    width += parseInt(style.marginLeft) + parseInt(style.marginRight);\n    return width;\n  };\n\n  api.core.ns = ns;\n  api.core.addClass = addClass;\n  api.core.removeClass = removeClass;\n  api.core.engine = engine;\n  api.core.Instance = Instance;\n  api.core.Initializer = Initializer;\n  api.core.Disclosure = Disclosure;\n  api.core.DisclosureButton = DisclosureButton;\n  api.core.DisclosuresGroup = DisclosuresGroup;\n  api.core.DISCLOSURE_TYPES = DISCLOSURE_TYPES;\n\n  api.KeyListener = KeyListener;\n  api.Collapse = Collapse;\n  api.Equisized = Equisized;\n\n  var build$8 = function () {\n    Collapse.build(document);\n  };\n\n  /* eslint-disable no-new */\n\n  new Initializer((\".\" + COLLAPSE_CLASS), [build$8]);\n\n  var ACCORDIONS_GROUP = api.core.ns('accordions-group');\n  var ACCORDION_ASCENDANT = api.core.ns('accordion');\n\n  var AccordionsGroup = /*@__PURE__*/(function (superclass) {\n    function AccordionsGroup () {\n      superclass.apply(this, arguments);\n    }\n\n    if ( superclass ) AccordionsGroup.__proto__ = superclass;\n    AccordionsGroup.prototype = Object.create( superclass && superclass.prototype );\n    AccordionsGroup.prototype.constructor = AccordionsGroup;\n\n    var staticAccessors = { selector: { configurable: true } };\n\n    staticAccessors.selector.get = function () { return ACCORDIONS_GROUP; };\n\n    Object.defineProperties( AccordionsGroup, staticAccessors );\n\n    return AccordionsGroup;\n  }(api.core.DisclosuresGroup));\n\n  api.AccordionsGroup = AccordionsGroup;\n\n  api.Collapse.register(ACCORDION_ASCENDANT, AccordionsGroup);\n\n  var BREADCRUMB_COLLAPSE_SELECTOR = (api.core.ns.selector('breadcrumb')) + \" \" + (api.core.ns.selector('collapse'));\n\n  var Breadcrumb = /*@__PURE__*/(function (superclass) {\n    function Breadcrumb (element) {\n      superclass.call(this, element);\n      this.collapse = api.core.Instance.getInstances(element, api.Collapse)[0];\n      this.links = [].concat( this.element.querySelectorAll('a[href]') );\n      this.count = 0;\n      if (this.links.length) {\n        this.listen(api.core.Disclosure.DISCLOSE_EVENT, this.focus.bind(this));\n        // TODO: refactor avec instance\n        this.resizing = this.resize.bind(this);\n        window.addEventListener('resize', this.resizing);\n      }\n    }\n\n    if ( superclass ) Breadcrumb.__proto__ = superclass;\n    Breadcrumb.prototype = Object.create( superclass && superclass.prototype );\n    Breadcrumb.prototype.constructor = Breadcrumb;\n\n    Breadcrumb.prototype.focus = function focus () {\n      var this$1 = this;\n\n      this.links[0].focus();\n      api.core.engine.renderer.next(function () { this$1.verify(); });\n    };\n\n    Breadcrumb.prototype.verify = function verify () {\n      this.count++;\n      if (this.count > 100) { return; }\n      if (document.activeElement !== this.links[0]) { this.focus(); }\n    };\n\n    Breadcrumb.prototype.resize = function resize () {\n      if (window.matchMedia('(min-width: 48em)').matches) {\n        if (this.collapse.buttons[0] === document.activeElement) { this.links.focus(); }\n      } else {\n        if (this.links.indexOf(document.activeElement) > -1) { this.collapse.focus(); }\n      }\n    };\n\n    return Breadcrumb;\n  }(api.core.Instance));\n\n  var build$7 = function () {\n    var breadcrumbs = [];\n    var breadcrumbNodes = document.querySelectorAll(BREADCRUMB_COLLAPSE_SELECTOR);\n    for (var i = 0; i < breadcrumbNodes.length; i++) { breadcrumbs.push(new Breadcrumb(breadcrumbNodes[i])); }\n  };\n\n  /* eslint-disable no-new */\n\n  new api.core.Initializer(BREADCRUMB_COLLAPSE_SELECTOR, [build$7]);\n\n  var BUTTON_SELECTOR = api.core.ns.selector('btn');\n  var BUTTONS_GROUP_SELECTOR = api.core.ns.selector('btns-group');\n  var EQUISIZED_BUTTONS_GROUP_SELECTOR = api.core.ns.selector('btns-group--equisized');\n\n  var build$6 = function () {\n    var group = document.querySelectorAll(EQUISIZED_BUTTONS_GROUP_SELECTOR);\n    var arrayEquisized = [];\n    for (var i = 0; i < group.length; i++) {\n      arrayEquisized.push(new api.Equisized(BUTTON_SELECTOR, group[i]));\n    }\n  };\n\n  /* eslint-disable no-new */\n\n  new api.core.Initializer(BUTTONS_GROUP_SELECTOR, [build$6]);\n\n  var MODAL_SELECTOR = api.core.ns.selector('modal');\n  var MODAL_CLASS = api.core.ns('modal');\n  var NO_SCROLL_CLASS = api.core.ns('no-scroll');\n  var SCROLL_SHADOW_CLASS = api.core.ns('scroll-shadow');\n  var MODAL_BODY_SELECTOR = api.core.ns.selector('modal__body');\n  var OFFSET_MOBILE = 32; // 32px => 8v => 2rem\n\n  var unordereds = [\n    '[tabindex=\"0\"]',\n    'a[href]',\n    'button:not([disabled])',\n    'input:not([disabled])',\n    'select:not([disabled])',\n    'textarea:not([disabled])',\n    'audio[controls]',\n    'video[controls]',\n    '[contenteditable]:not([contenteditable=\"false\" i])',\n    'details>summary:first-of-type',\n    'details'\n  ];\n\n  var UNORDEREDS = unordereds.join();\n\n  var ordereds = [\n    '[tabindex]:not([tabindex=\"-1\"]):not([tabindex=\"0\"])'\n  ];\n\n  var ORDEREDS = ordereds.join();\n\n  var isFocusable = function (element, container) {\n    if (window.getComputedStyle(element).visibility === 'hidden') { return false; }\n    if (container === undefined) { container = element; }\n\n    while (container.contains(element)) {\n      if (window.getComputedStyle(element).display === 'none') { return false; }\n      element = element.parentElement;\n    }\n\n    return true;\n  };\n\n  var FocusTrap = function FocusTrap (onTrap, onUntrap) {\n    this.element = null;\n    this.activeElement = null;\n    this.onTrap = onTrap;\n    this.onUntrap = onUntrap;\n    this.waiting = this.wait.bind(this);\n    this.handling = this.handle.bind(this);\n    this.current = null;\n  };\n\n  var prototypeAccessors = { trapped: { configurable: true },focusables: { configurable: true } };\n\n  prototypeAccessors.trapped.get = function () { return this.element !== null; };\n\n  FocusTrap.prototype.trap = function trap (element) {\n    if (this.trapped) { this.untrap(); }\n\n    this.element = element;\n    this.isTrapping = true;\n    this.wait();\n\n    if (this.onTrap) { this.onTrap(); }\n  };\n\n  FocusTrap.prototype.wait = function wait () {\n    if (!isFocusable(this.element)) {\n      api.core.engine.renderer.next(this.waiting);\n      return;\n    }\n\n    this.trapping();\n  };\n\n  FocusTrap.prototype.trapping = function trapping () {\n    if (!this.isTrapping) { return; }\n    this.isTrapping = false;\n    var focusables = this.focusables;\n    if (focusables.length) { focusables[0].focus(); }\n    this.element.setAttribute('aria-modal', true);\n    this.element.addEventListener('keydown', this.handling);\n\n    this.stunneds = [];\n    // this.stun(document.body);\n  };\n\n  FocusTrap.prototype.stun = function stun (node) {\n    for (var i = 0, list = node.children; i < list.length; i += 1) {\n      var child = list[i];\n\n        if (child === this.element) { continue; }\n      if (child.contains(this.element)) {\n        this.stun(child);\n        continue;\n      }\n      this.stunneds.push(new Stunned(child));\n    }\n  };\n\n  FocusTrap.prototype.handle = function handle (e) {\n    if (e.keyCode !== 9) { return; }\n\n    var focusables = this.focusables;\n    if (focusables.length === 0) { return; }\n\n    var first = focusables[0];\n    var last = focusables[focusables.length - 1];\n\n    var index = focusables.indexOf(document.activeElement);\n\n    if (e.shiftKey) {\n      if (!this.element.contains(document.activeElement) || index < 1) {\n        e.preventDefault();\n        last.focus();\n      } else if (document.activeElement.tabIndex > 0 || focusables[index - 1].tabIndex > 0) {\n        e.preventDefault();\n        focusables[index - 1].focus();\n      }\n    } else {\n      if (!this.element.contains(document.activeElement) || index === focusables.length - 1 || index === -1) {\n        e.preventDefault();\n        first.focus();\n      } else if (document.activeElement.tabIndex > 0) {\n        e.preventDefault();\n        focusables[index + 1].focus();\n      }\n    }\n  };\n\n  prototypeAccessors.focusables.get = function () {\n      var this$1 = this;\n\n    var unordereds = [].concat( this.element.querySelectorAll(UNORDEREDS) );\n\n    /**\n     *filtrage des radiobutttons de même name (la navigations d'un groupe de radio se fait à la flèche et non pas au tab\n     **/\n    var radios = [].concat( document.documentElement.querySelectorAll('input[type=\"radio\"]') );\n\n    if (radios.length) {\n      var groups = {};\n\n      for (var i = 0, list = radios; i < list.length; i += 1) {\n        var radio = list[i];\n\n          var name = radio.getAttribute('name');\n        if (groups[name] === undefined) { groups[name] = new RadioButtonGroup(name); }\n        groups[name].push(radio);\n      }\n\n      unordereds = unordereds.filter(function (unordered) {\n        if (unordered.tagName.toLowerCase() !== 'input' || unordered.getAttribute('type').toLowerCase() !== 'radio') { return true; }\n        var name = unordered.getAttribute('name');\n        return groups[name].keep(unordered);\n      });\n    }\n\n    var ordereds = [].concat( this.element.querySelectorAll(ORDEREDS) );\n\n    ordereds.sort(function (a, b) { return a.tabIndex - b.tabIndex; });\n\n    var noDuplicates = unordereds.filter(function (element) { return ordereds.indexOf(element) === -1; });\n    var concateneds = ordereds.concat(noDuplicates);\n    return concateneds.filter(function (element) { return element.tabIndex !== '-1' && isFocusable(element, this$1.element); });\n  };\n\n  FocusTrap.prototype.untrap = function untrap () {\n    if (!this.trapped) { return; }\n    this.isTrapping = false;\n\n    this.element.removeAttribute('aria-modal');\n    this.element.removeEventListener('keydown', this.handling);\n    this.element = null;\n\n    // for (const stunned of this.stunneds) stunned.unstun();\n    // this.stunneds = [];\n\n    if (this.onUntrap) { this.onUntrap(); }\n  };\n\n  Object.defineProperties( FocusTrap.prototype, prototypeAccessors );\n\n  var Stunned = function Stunned (element) {\n    this.element = element;\n    this.hidden = element.getAttribute('aria-hidden');\n    this.inert = element.getAttribute('inert');\n\n    this.element.setAttribute('aria-hidden', true);\n    this.element.setAttribute('inert', '');\n  };\n\n  Stunned.prototype.unstun = function unstun () {\n    if (this.hidden === null) { this.element.removeAttribute('aria-hidden'); }\n    else { this.element.setAttribute('aria-hidden', this.hidden); }\n\n    if (this.inert === null) { this.element.removeAttribute('inert'); }\n    else { this.element.setAttribute('inert', this.inert); }\n  };\n\n  var RadioButtonGroup = function RadioButtonGroup (name) {\n    this.name = name;\n    this.buttons = [];\n  };\n\n  RadioButtonGroup.prototype.push = function push (button) {\n    this.buttons.push(button);\n    if (button === document.activeElement || button.checked || this.selected === undefined) { this.selected = button; }\n  };\n\n  RadioButtonGroup.prototype.keep = function keep (button) {\n    return this.selected === button;\n  };\n\n  var ModalsGroup = /*@__PURE__*/(function (superclass) {\n    function ModalsGroup () {\n      superclass.call(this);\n      this.trap = new FocusTrap();\n    }\n\n    if ( superclass ) ModalsGroup.__proto__ = superclass;\n    ModalsGroup.prototype = Object.create( superclass && superclass.prototype );\n    ModalsGroup.prototype.constructor = ModalsGroup;\n\n    ModalsGroup.prototype.apply = function apply (value, initial) {\n      superclass.prototype.apply.call(this, value, initial);\n      if (this.current === null) { this.trap.untrap(); }\n      else { this.trap.trap(this.current.element); }\n    };\n\n    return ModalsGroup;\n  }(api.core.DisclosuresGroup));\n\n  var group = new ModalsGroup();\n\n  var Modal = /*@__PURE__*/(function (superclass) {\n    function Modal (element) {\n      superclass.call(this, element);\n      this.body = this.element.querySelector(MODAL_BODY_SELECTOR);\n      this.scrollDistance = 0;\n      this.scrolling = this.resize.bind(this, false);\n      this.resizing = this.resize.bind(this, true);\n      this.init();\n      this.resize(true);\n    }\n\n    if ( superclass ) Modal.__proto__ = superclass;\n    Modal.prototype = Object.create( superclass && superclass.prototype );\n    Modal.prototype.constructor = Modal;\n\n    var prototypeAccessors = { GroupConstructor: { configurable: true } };\n    var staticAccessors = { type: { configurable: true },selector: { configurable: true } };\n\n    Modal.prototype.init = function init () {\n      this.element.addEventListener('click', this.click.bind(this));\n\n      this.keyListener = new api.KeyListener(this.element);\n      this.keyListener.down(api.KeyListener.ESCAPE, this.conceal.bind(this), true, true);\n\n      if (this.body) {\n        this.body.addEventListener('scroll', this.scrolling);\n        window.addEventListener('resize', this.resizing);\n        // window.addEventListener('orientationchange', this.resizing);\n      }\n    };\n\n    Modal.prototype.click = function click (e) {\n      if (this.body && this.body !== e.target && !this.body.contains(e.target)) { this.conceal(); }\n    };\n\n    Modal.prototype.gather = function gather () {\n      group.add(this);\n    };\n\n    Modal.prototype.disclose = function disclose (withhold) {\n      if (!superclass.prototype.disclose.call(this, withhold)) { return false; }\n      this.resize(true);\n      this.handleScroll(false);\n      return true;\n    };\n\n    Modal.prototype.conceal = function conceal (withhold, preventFocus) {\n      if (!superclass.prototype.conceal.call(this, withhold, preventFocus)) { return false; }\n      this.handleScroll(true);\n      return true;\n    };\n\n    /**\n     * Fixe l'arrière plan quand la modal est ouverte\n     */\n    // TODO: créer une fonction de fix de scroll dans core (api.noScroll = true)\n    Modal.prototype.handleScroll = function handleScroll (isScrollable) {\n      if (isScrollable) {\n        api.core.removeClass(document.documentElement, NO_SCROLL_CLASS);\n        document.body.style.top = '';\n        window.scroll(0, this.scrollDistance);\n      } else {\n        if (!document.documentElement.classList.contains(NO_SCROLL_CLASS)) {\n          this.scrollDistance = window.scrollY;\n        }\n        document.body.style.top = this.scrollDistance * -1 + 'px';\n        api.core.addClass(document.documentElement, NO_SCROLL_CLASS);\n      }\n    };\n\n    /**\n     * Ajoute une ombre autour du footer lorsque l'on peut scroller dans la modale\n     * corrige le 100vh, en mobile notamment, lorsque la barre navigateur est présente par exemple.\n     */\n    Modal.prototype.resize = function resize (isResizing, e) {\n      var this$1 = this;\n\n      if (!this.body) { return; }\n      if (this.body.scrollHeight > this.body.clientHeight) {\n        if (this.body.offsetHeight + this.body.scrollTop >= this.body.scrollHeight) {\n          api.core.removeClass(this.body, SCROLL_SHADOW_CLASS);\n        } else {\n          api.core.addClass(this.body, SCROLL_SHADOW_CLASS);\n        }\n      } else {\n        api.core.removeClass(this.body, SCROLL_SHADOW_CLASS);\n      }\n\n      if (isResizing) {\n        this.body.style.maxHeight = (window.innerHeight - OFFSET_MOBILE) + 'px';\n\n        // Une deuxième fois après positionnement des barres du navigateur (ios)\n        // TODO: à tester si fonctionnel sans setTimeout\n        api.core.engine.renderer.next(function () {\n          this$1.body.style.maxHeight = (window.innerHeight - OFFSET_MOBILE) + 'px';\n        });\n      }\n    };\n\n    staticAccessors.type.get = function () { return api.core.DISCLOSURE_TYPES.opened; };\n    staticAccessors.selector.get = function () { return MODAL_CLASS; };\n\n    prototypeAccessors.GroupConstructor.get = function () { return ModalsGroup; };\n\n    Object.defineProperties( Modal.prototype, prototypeAccessors );\n    Object.defineProperties( Modal, staticAccessors );\n\n    return Modal;\n  }(api.core.Disclosure));\n\n  api.Modal = Modal;\n  api.ModalsGroup = ModalsGroup;\n  api.FocusTrap = FocusTrap;\n\n  /**\n   * initialise tout les éléments Modal dans la page\n   */\n  var build$5 = function () {\n    Modal.build(document);\n  };\n\n  /* eslint-disable no-new */\n\n  new api.core.Initializer(MODAL_SELECTOR, [build$5]);\n\n  var NAVIGATION_CLASS = api.core.ns('nav');\n  var NAVIGATION_LIST_CLASS = api.core.ns('nav__list');\n  var NAVIGATION_ITEM_CLASS = api.core.ns('nav__item');\n  var NAVIGATION_ITEM_RIGHT_CLASS = api.core.ns('nav__item--align-right');\n  var NAVIGATION_MENU_CLASS = api.core.ns('menu');\n\n  var Navigation = /*@__PURE__*/(function (superclass) {\n    function Navigation (id, element) {\n      superclass.call(this, id, element);\n\n      this.menus = [];\n\n      this.navList = element.querySelector((\".\" + NAVIGATION_LIST_CLASS));\n\n      document.addEventListener('focusout', this.focusOut.bind(this));\n      window.addEventListener('resize', this.resize.bind(this));\n      window.addEventListener('orientationchange', this.resize.bind(this));\n      this.resize();\n    }\n\n    if ( superclass ) Navigation.__proto__ = superclass;\n    Navigation.prototype = Object.create( superclass && superclass.prototype );\n    Navigation.prototype.constructor = Navigation;\n\n    var prototypeAccessors = { index: { configurable: true } };\n    var staticAccessors = { selector: { configurable: true } };\n\n    staticAccessors.selector.get = function () { return NAVIGATION_CLASS; };\n\n    Navigation.prototype.add = function add (member) {\n      superclass.prototype.add.call(this, member);\n\n      if (member.element.classList.contains(NAVIGATION_MENU_CLASS)) {\n        this.menus.push(new NavigationMenu(member, this.navList.getBoundingClientRect().right));\n      }\n    };\n\n    Navigation.prototype.focusOut = function focusOut (e) {\n      var this$1 = this;\n\n      requestAnimationFrame(function () {\n        if (this$1.current !== null && !this$1.current.hasFocus) { this$1.index = -1; }\n      });\n    };\n\n    prototypeAccessors.index.get = function () { return superclass.prototype.index; };\n\n    prototypeAccessors.index.set = function (value) {\n      if (value === -1 && this.current !== null && this.current.hasFocus) { this.current.focus(); }\n      superclass.prototype.index = value;\n    };\n\n    Navigation.prototype.resize = function resize () {\n      var right = this.navList.getBoundingClientRect().right;\n\n      for (var i = 0, list = this.menus; i < list.length; i += 1) {\n        var menu = list[i];\n\n        menu.place(right);\n      }\n    };\n\n    Object.defineProperties( Navigation.prototype, prototypeAccessors );\n    Object.defineProperties( Navigation, staticAccessors );\n\n    return Navigation;\n  }(api.core.DisclosuresGroup));\n\n  var NavigationMenu = function NavigationMenu (collapse, right) {\n    this.initialize(collapse);\n    this.place(right);\n  };\n\n  NavigationMenu.prototype.initialize = function initialize (collapse) {\n    this.element = collapse.element;\n\n    for (var i = 0, list = collapse.buttons; i < list.length; i += 1) {\n      var button = list[i];\n\n        if (!button.hasAttribute) { continue; }\n      this.button = button.element;\n      break;\n    }\n\n    var item = this.element.parentElement;\n    while (item) {\n      if (item.classList.contains(NAVIGATION_ITEM_CLASS)) {\n        this.item = item;\n        break;\n      }\n      item = item.parentElement;\n    }\n  };\n\n  NavigationMenu.prototype.place = function place (right) {\n    var styles = getComputedStyle(this.element);\n    var width = parseFloat(styles.width);\n    var left = this.button.getBoundingClientRect().left;\n\n    if (left + width > right) { api.core.addClass(this.item, NAVIGATION_ITEM_RIGHT_CLASS); }\n    else { api.core.removeClass(this.item, NAVIGATION_ITEM_RIGHT_CLASS); }\n  };\n\n  api.Navigation = Navigation;\n\n  api.Collapse.register(NAVIGATION_CLASS, Navigation);\n\n  var SCHEME_ATTR = api.core.ns.attr('theme');\n  var TRANSITION_ATTR = api.core.ns.attr('transition');\n\n  /**\n   * TODO: implémenter la valeur system\n   * window.matchMedia(\"(prefers-color-scheme: dark)\").addListener(\n   e => e.matches && activateDarkMode()) // listener\n   );\n   */\n\n  var Scheme = function Scheme () {\n    this.init();\n  };\n\n  Scheme.prototype.init = function init () {\n      var this$1 = this;\n\n    this.root = document.documentElement;\n\n    this.scheme = localStorage.getItem('scheme')\n      ? localStorage.getItem('scheme')\n      : null;\n\n    if (this.scheme === null) {\n      var scheme = this.root.getAttribute(SCHEME_ATTR);\n      if (scheme === 'dark' || scheme === 'light') {\n        this.scheme = scheme;\n      } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {\n        this.scheme = 'dark';\n        localStorage.setItem('scheme', 'dark');\n      } else { this.scheme = 'light'; }\n    }\n\n    if (this.scheme === 'dark') {\n      if (!this.root.hasAttribute(TRANSITION_ATTR)) {\n        this.root.setAttribute(SCHEME_ATTR, 'dark');\n      } else {\n        this.root.removeAttribute(TRANSITION_ATTR);\n        this.root.setAttribute(SCHEME_ATTR, 'dark');\n\n        setTimeout(function () {\n          this$1.root.setAttribute(TRANSITION_ATTR, '');\n        }, 300);\n      }\n    } else { this.root.setAttribute(SCHEME_ATTR, 'light'); }\n\n    this.observer = new MutationObserver(this.mutate.bind(this));\n    this.observer.observe(this.root, { attributes: true });\n  };\n\n  Scheme.prototype.mutate = function mutate (mutations) {\n      var this$1 = this;\n\n    mutations.forEach(function (mutation) {\n      if (mutation.type === 'attributes' && mutation.attributeName === SCHEME_ATTR) {\n        var scheme = this$1.root.getAttribute(SCHEME_ATTR);\n        if (scheme === 'dark') {\n          localStorage.setItem('scheme', 'dark');\n        } else if (scheme === 'light') {\n          localStorage.setItem('scheme', 'light');\n        }\n      }\n    });\n  };\n\n  api.Scheme = Scheme;\n\n  var RADIOS_THEME_NAME = \"input[name=\\\"\" + (api.core.ns.selector('radios-theme', '')) + \"\\\"]\";\n  var SWITCH_THEME_ID = api.core.ns.selector('switch-theme', '#');\n  var THEME_ATTR = api.core.ns.attr('theme');\n\n  /* eslint-disable no-new */\n\n  var build$4 = function () {\n    new Scheme();\n  };\n\n  var SwitchTheme = function SwitchTheme () {\n    this.attributeName = THEME_ATTR;\n    this.theme = null;\n    this.radios = document.querySelectorAll(RADIOS_THEME_NAME);\n\n    for (var i = 0; i < this.radios.length; i++) {\n      this.radios[i].addEventListener('change', this.change.bind(this));\n    }\n\n    this.observer = new MutationObserver(this.mutate.bind(this));\n    this.observe();\n    this.apply();\n  };\n\n  SwitchTheme.prototype.observe = function observe () {\n    this.observer.observe(document.documentElement, { attributes: true });\n  };\n\n  SwitchTheme.prototype.mutate = function mutate (mutations) {\n      var this$1 = this;\n\n    mutations.forEach(function (mutation) {\n      if (mutation.type === 'attributes' && mutation.attributeName === this$1.attributeName) {\n        this$1.apply();\n      }\n    });\n  };\n\n  SwitchTheme.prototype.apply = function apply () {\n    var theme = document.documentElement.getAttribute(this.attributeName);\n    this.isApplying = true;\n    for (var i = 0; i < this.radios.length; i++) {\n      this.radios[i].checked = this.radios[i].value === theme;\n    }\n    this.isApplying = false;\n  };\n\n  SwitchTheme.prototype.change = function change () {\n    if (this.isApplying) { return; }\n    if (this.observer) { this.observer.disconnect(); }\n    this.theme = document.querySelector(RADIOS_THEME_NAME + ':checked');\n    if (this.theme) {\n      document.documentElement.setAttribute(this.attributeName, this.theme.value);\n    } else {\n      document.documentElement.removeAttribute(this.attributeName);\n    }\n    if (this.observer) { this.observe(); }\n  };\n\n  /* eslint-disable no-new */\n\n  var build$3 = function () {\n    new SwitchTheme();\n  };\n\n  /* eslint-disable no-new */\n\n  new api.core.Initializer((\":root[\" + SCHEME_ATTR + \"]\"), [build$4]);\n  new api.core.Initializer((\"\" + SWITCH_THEME_ID), [build$3]);\n\n  var SIDEMENU_CLASS = api.core.ns('sidemenu');\n  var SIDEMENU_LIST_CLASS = api.core.ns('sidemenu__list');\n\n  /* eslint-disable no-new */\n\n  api.Collapse.register(SIDEMENU_CLASS, SIDEMENU_LIST_CLASS);\n\n  var TABLE_SELECTOR = api.core.ns.selector('table');\n  // export const TABLE_CLASS = api.core.ns('table');\n  var TABLE_SCROLLING_SELECTOR = (api.core.ns.selector('table')) + \":not(\" + (api.core.ns.selector('table--no-scroll')) + \")\";\n  var LEFT = 'left';\n  var RIGHT = 'right';\n  var SHADOW_CLASS = api.core.ns('table--shadow');\n  var SHADOW_LEFT_CLASS = api.core.ns('table--shadow-left');\n  var SHADOW_RIGHT_CLASS = api.core.ns('table--shadow-right');\n  var WRAPPER_CLASS = api.core.ns('table__wrapper');\n  var CAPTION_BOTTOM_CLASS = api.core.ns('table--caption-bottom');\n  var SCROLL_OFFSET = 1; // valeur en px du scroll avant laquelle le shadow s'active ou se desactive\n\n  var Table = function Table (table) {\n    this.init(table);\n  };\n\n  Table.prototype.init = function init (table) {\n    this.table = table;\n    this.tableElem = this.table.querySelector('table');\n    this.tableContent = this.tableElem.querySelector('tbody');\n    this.isScrollable = this.tableContent.offsetWidth > this.tableElem.offsetWidth;\n    this.caption = this.tableElem.querySelector('caption');\n    this.captionHeight = 0;\n    this.wrap();\n\n    var scrolling = this.change.bind(this);\n    this.tableElem.addEventListener('scroll', scrolling);\n    this.change();\n  };\n\n  Table.prototype.change = function change () {\n    var newScroll = this.tableContent.offsetWidth > this.tableElem.offsetWidth;\n    var firstTimeScrollable = this.tableElem.offsetWidth > this.table.offsetWidth;\n    if (newScroll || firstTimeScrollable) {\n      this.scroll();\n      this.handleCaption();\n    } else {\n      if (newScroll !== this.isScrollable) { this.delete(); }\n    }\n    this.isScrollable = newScroll;\n    firstTimeScrollable = false;\n  };\n\n  Table.prototype.delete = function delete$1 () {\n    api.core.removeClass(this.table, SHADOW_RIGHT_CLASS);\n    api.core.removeClass(this.table, SHADOW_LEFT_CLASS);\n    api.core.removeClass(this.table, SHADOW_CLASS);\n    if (this.caption) {\n      this.tableElem.style.marginTop = '';\n      this.caption.style.top = '';\n      this.tableElem.style.marginBottom = '';\n      this.caption.style.bottom = '';\n    }\n  };\n\n  Table.prototype.scroll = function scroll () {\n    api.core.addClass(this.table, SHADOW_CLASS);\n    this.setShadowPosition();\n  };\n\n  /* ajoute un wrapper autour du tableau */\n  Table.prototype.wrap = function wrap () {\n    var wrapperHtml = document.createElement('div');\n    wrapperHtml.className = WRAPPER_CLASS;\n    this.table.insertBefore(wrapperHtml, this.tableElem);\n    wrapperHtml.appendChild(this.tableElem);\n    this.tableInnerWrapper = wrapperHtml;\n  };\n\n  /* affiche les blocs shadow en fonction de la position du scroll, en ajoutant la classe visible */\n  Table.prototype.setShadowPosition = function setShadowPosition () {\n    var tableScrollLeft = this.getScrollPosition(LEFT);\n    var tableScrollRight = this.getScrollPosition(RIGHT);\n\n    // on inverse en cas de lecture droite - gauche\n    if (document.documentElement.getAttribute('dir') === 'rtl') {\n      this.setShadowVisibility(RIGHT, tableScrollLeft);\n      this.setShadowVisibility(LEFT, tableScrollRight);\n    } else {\n      this.setShadowVisibility(LEFT, tableScrollLeft);\n      this.setShadowVisibility(RIGHT, tableScrollRight);\n    }\n  };\n\n  /* Donne le nombre de pixels scrollés honrizontalement dans un element scrollable */\n  Table.prototype.getScrollPosition = function getScrollPosition (side) {\n    var inverter = 1;\n    // on inverse en cas de lecture droite - gauche pour que la valeur de scroll soit toujours positive\n    if (document.documentElement.getAttribute('dir') === 'rtl') {\n      inverter = -1;\n    }\n    switch (side) {\n      case LEFT:\n        return this.tableElem.scrollLeft * inverter;\n      case RIGHT:\n        return this.tableContent.offsetWidth - this.tableElem.offsetWidth - this.tableElem.scrollLeft * inverter;\n      default:\n        return false;\n    }\n  };\n\n  /* positionne la caption en top négatif et ajoute un margin-top au wrapper */\n  Table.prototype.handleCaption = function handleCaption () {\n    if (this.caption) {\n      var style = getComputedStyle(this.caption);\n      var newHeight = this.caption.offsetHeight + parseInt(style.marginTop) + parseInt(style.marginBottom);\n      this.captionHeight = newHeight;\n      if (this.table.classList.contains(CAPTION_BOTTOM_CLASS)) {\n        this.tableElem.style.marginBottom = this.captionHeight + 'px';\n        this.caption.style.bottom = -this.captionHeight + 'px';\n      } else {\n        this.tableElem.style.marginTop = this.captionHeight + 'px';\n        this.caption.style.top = -this.captionHeight + 'px';\n      }\n    }\n  };\n\n  /* ajoute la classe fr-table--shadow-right ou fr-table--shadow-right sur fr-table\n   en fonction d'une valeur de scroll et du sens (right, left) */\n  Table.prototype.setShadowVisibility = function setShadowVisibility (side, scrollPosition) {\n    // si on a pas scroll, ou qu'on scroll jusqu'au bout\n    if (scrollPosition <= SCROLL_OFFSET) {\n      if (side === LEFT) { api.core.removeClass(this.table, SHADOW_LEFT_CLASS); }\n      else if (side === RIGHT) { api.core.removeClass(this.table, SHADOW_RIGHT_CLASS); }\n    } else {\n      if (side === LEFT) { api.core.addClass(this.table, SHADOW_LEFT_CLASS); }\n      else if (side === RIGHT) { api.core.addClass(this.table, SHADOW_RIGHT_CLASS); }\n    }\n  };\n\n  api.Table = Table;\n\n  var tables = [];\n\n  var change = function () {\n    for (var i = 0; i < tables.length; i++) { tables[i].change(); }\n  };\n\n  var build$2 = function () {\n    var tableNodes = document.querySelectorAll(TABLE_SCROLLING_SELECTOR);\n    for (var i = 0; i < tableNodes.length; i++) { tables.push(new Table(tableNodes[i])); }\n\n    window.addEventListener('resize', change);\n    window.addEventListener('orientationchange', change);\n    change();\n  };\n\n  /* eslint-disable no-new */\n\n  new api.core.Initializer(TABLE_SELECTOR, [build$2]);\n\n  /**\n    * TabButton correspond au bouton cliquable qui change le panel\n    * TabButton étend de DisclosureButton qui ajoute/enelve l'attribut aria-selected,\n    * Et change l'attributte tabindex a 0 si le boutton est actif (value=true), -1 s'il n'est pas actif (value=false)\n   */\n  var TabButton = /*@__PURE__*/(function (superclass) {\n    function TabButton () {\n      superclass.apply(this, arguments);\n    }\n\n    if ( superclass ) TabButton.__proto__ = superclass;\n    TabButton.prototype = Object.create( superclass && superclass.prototype );\n    TabButton.prototype.constructor = TabButton;\n\n    TabButton.prototype.apply = function apply (value) {\n      superclass.prototype.apply.call(this, value);\n      if (this.hasAttribute) {\n        this.element.setAttribute('tabindex', value ? '0' : '-1');\n      }\n    };\n\n    return TabButton;\n  }(api.core.DisclosureButton));\n\n  var TABS_SELECTOR = api.core.ns.selector('tabs');\n  var TABS_CLASS = api.core.ns('tabs');\n  var TAB_CLASS = api.core.ns('tabs__tab');\n  var PANEL_CLASS = api.core.ns('tabs__panel');\n  var LIST_CLASS = api.core.ns('tabs__list');\n\n  /**\n  * TabGroup est la classe étendue de DiscosuresGroup\n  * Correspond à un objet Tabs avec plusieurs tab-button & Tab (panel)\n  */\n  var TabsGroup = /*@__PURE__*/(function (superclass) {\n    function TabsGroup (id, element) {\n      superclass.call(this, id, element);\n      this.list = element.querySelector((\".\" + LIST_CLASS));\n\n      element.addEventListener('transitionend', this.transitionend.bind(this));\n\n      this.init();\n      api.core.engine.renderer.add(this.render.bind(this));\n    }\n\n    if ( superclass ) TabsGroup.__proto__ = superclass;\n    TabsGroup.prototype = Object.create( superclass && superclass.prototype );\n    TabsGroup.prototype.constructor = TabsGroup;\n\n    var staticAccessors = { selector: { configurable: true } };\n\n    staticAccessors.selector.get = function () { return TABS_CLASS; };\n\n    TabsGroup.prototype.transitionend = function transitionend (e) {\n      this.element.style.transition = 'none';\n    };\n\n    TabsGroup.prototype.init = function init () {\n      this.keyListener = new api.KeyListener(this.element);\n      this.keyListener.down(api.KeyListener.RIGHT, this.arrowRightPress.bind(this), true, true);\n      this.keyListener.down(api.KeyListener.LEFT, this.arrowLeftPress.bind(this), true, true);\n      this.keyListener.down(api.KeyListener.HOME, this.homePress.bind(this), true, true);\n      this.keyListener.down(api.KeyListener.END, this.endPress.bind(this), true, true);\n    };\n\n    /**\n     * Selectionne l'element suivant de la liste si on est sur un bouton\n     * Si on est à la fin on retourne au début\n     */\n    TabsGroup.prototype.arrowRightPress = function arrowRightPress () {\n      if (document.activeElement.classList.contains(TAB_CLASS)) {\n        if (this.index < this.length - 1) {\n          this.index++;\n        } else {\n          this.index = 0;\n        }\n\n        this.focus();\n      }\n    };\n    /**\n     * Selectionne l'element précédent de la liste si on est sur un bouton\n     * Si on est au debut retourne a la fin\n     */\n    TabsGroup.prototype.arrowLeftPress = function arrowLeftPress () {\n      if (document.activeElement.classList.contains(TAB_CLASS)) {\n        if (this.index > 0) {\n          this.index--;\n        } else {\n          this.index = this.length - 1;\n        }\n\n        this.focus();\n      }\n    };\n    /**\n     * Selectionne le permier element de la liste si on est sur un bouton\n     */\n    TabsGroup.prototype.homePress = function homePress () {\n      if (document.activeElement.classList.contains(TAB_CLASS)) {\n        this.index = 0;\n        this.focus();\n      }\n    };\n    /**\n     * Selectionne le dernier element de la liste si on est sur un bouton\n     */\n    TabsGroup.prototype.endPress = function endPress () {\n      if (document.activeElement.classList.contains(TAB_CLASS)) {\n        this.index = this.length - 1;\n        this.focus();\n      }\n    };\n    TabsGroup.prototype.focus = function focus () {\n      if (this.current) { this.current.focus(); }\n    };\n\n    TabsGroup.prototype.apply = function apply () {\n      for (var i = 0; i < this._index; i++) { this.members[i].translate(-1); }\n      this.current.element.style.transform = '';\n      for (var i$1 = this._index + 1; i$1 < this.length; i$1++) { this.members[i$1].translate(1); }\n      this.element.style.transition = '';\n    };\n\n    TabsGroup.prototype.add = function add (tab) {\n      superclass.prototype.add.call(this, tab);\n      if (this.length === 1 || tab.disclosed) { this.current = tab; }\n      else {\n        var index = this.members.indexOf(tab);\n        if (this._index > -1 && this._index !== index) { tab.translate(index < this._index ? -1 : 1, true); }\n      }\n    };\n\n    TabsGroup.prototype.render = function render () {\n      if (this.current === null) { return; }\n      var paneHeight = Math.round(this.current.element.offsetHeight);\n      if (this.panelHeight === paneHeight) { return; }\n      this.panelHeight = paneHeight;\n      this.element.style.height = (this.panelHeight + this.list.offsetHeight) + 'px';\n    };\n\n    Object.defineProperties( TabsGroup, staticAccessors );\n\n    return TabsGroup;\n  }(api.core.DisclosuresGroup));\n\n  /**\n    * Tab coorespond au panel d'un élement Tabs (tab panel)\n    * Tab étend disclosure qui ajoute/enleve le modifier --selected,\n    * et ajoute/eleve l'attribut hidden, sur le panel\n    */\n  var Tab = /*@__PURE__*/(function (superclass) {\n    function Tab () {\n      superclass.apply(this, arguments);\n    }\n\n    if ( superclass ) Tab.__proto__ = superclass;\n    Tab.prototype = Object.create( superclass && superclass.prototype );\n    Tab.prototype.constructor = Tab;\n\n    var prototypeAccessors = { GroupConstructor: { configurable: true } };\n    var staticAccessors = { type: { configurable: true },selector: { configurable: true } };\n\n    staticAccessors.type.get = function () { return api.core.DISCLOSURE_TYPES.select; };\n    staticAccessors.selector.get = function () { return PANEL_CLASS; };\n\n    prototypeAccessors.GroupConstructor.get = function () { return TabsGroup; };\n\n    Tab.prototype.buttonFactory = function buttonFactory (element) {\n      return new TabButton(element, this);\n    };\n\n    Tab.prototype.translate = function translate (direction, initial) {\n      if (initial) { this.element.style.transition = 'none'; }\n      this.element.style.transform = \"translate(\" + (direction * 100) + \"%)\";\n      if (initial) { this.element.style.transition = ''; }\n    };\n\n    Tab.prototype.reset = function reset () {\n      this.group.index = 0;\n    };\n\n    Object.defineProperties( Tab.prototype, prototypeAccessors );\n    Object.defineProperties( Tab, staticAccessors );\n\n    return Tab;\n  }(api.core.Disclosure));\n\n  api.Tab = Tab;\n  api.TabButton = TabButton;\n  api.TabsGroup = TabsGroup;\n\n  /**\n  * Initialise tout les éléments Tab dans la page\n  */\n  var build$1 = function () {\n    Tab.build(document);\n  };\n\n  /* eslint-disable no-new */\n\n  new api.core.Initializer(TABS_SELECTOR, [build$1]);\n\n  var HEADER_SELECTOR = api.core.ns.selector('header');\n  var HEADER_SEARCH_SELECTOR = api.core.ns.selector('header__search');\n  var HEADER_MENU_SELECTOR = api.core.ns.selector('header__menu');\n  var HEADER_TOOLS_LINKS_SELECTOR = api.core.ns.selector('header__tools-links');\n  var HEADER_MENU_LINKS_SELECTOR = api.core.ns.selector('header__menu-links');\n  var HEADER_LINKS_GROUP_SELECTOR = HEADER_TOOLS_LINKS_SELECTOR + \" \" + (api.core.ns.selector('links-group'));\n\n  var Header = function Header (header) {\n    this.header = header || document.querySelector(HEADER_SELECTOR);\n    this.modals = [];\n\n    this.init();\n  };\n\n  Header.prototype.getModal = function getModal (selector) {\n    var element = this.header.querySelector(selector);\n    if (!element) { return; }\n    var modals = api.core.Instance.getInstances(element, api.Modal);\n    if (!modals || !modals.length) { return; }\n    this.modals.push(modals[0]);\n  };\n\n  Header.prototype.init = function init () {\n    this.getModal(HEADER_SEARCH_SELECTOR);\n    this.getModal(HEADER_MENU_SELECTOR);\n\n    this.linksGroup = this.header.querySelector(HEADER_LINKS_GROUP_SELECTOR);\n\n    this.toolsLinks = this.header.querySelector(HEADER_TOOLS_LINKS_SELECTOR);\n    this.menuLinks = this.header.querySelector(HEADER_MENU_LINKS_SELECTOR);\n\n    this.changing = this.change.bind(this);\n\n    window.addEventListener('resize', this.changing);\n    this.change();\n  };\n\n  Header.prototype.change = function change () {\n    this.isLarge = window.matchMedia('(min-width: 62em)').matches;\n\n    if (this.isLarge) {\n      for (var i = 0; i < this.modals.length; i++) {\n        this.modals[i].conceal();\n        this.modals[i].element.removeAttribute('role');\n      }\n    } else {\n      for (var i$1 = 0; i$1 < this.modals.length; i$1++) {\n        this.modals[i$1].element.setAttribute('role', 'dialog');\n      }\n    }\n\n    if (this.linksGroup !== null) {\n      if (this.isLarge) { this.toolsLinks.appendChild(this.linksGroup); }\n      else { this.menuLinks.appendChild(this.linksGroup); }\n    }\n  };\n\n  api.Header = Header;\n\n  var build = function () {\n    var elements = Array.prototype.slice.call(document.querySelectorAll(HEADER_SELECTOR));\n\n    var headers = [];\n\n    for (var i = 0, list = elements; i < list.length; i += 1) {\n      var element = list[i];\n\n      headers.push(new Header(element));\n    }\n  };\n\n  /* eslint-disable no-new */\n\n  new api.core.Initializer(HEADER_SELECTOR, [build]);\n\n}());\n</script>"}

<ul class="fr-btns-group fr-btns-group--equisized">
    <li>
        <button class="fr-btn">
            Label bouton
        </button>
    </li>
    <li>
        <button class="fr-btn fr-btn--secondary">
            lorem ipsum label très long
        </button>
    </li>
</ul>

Règles d’utilisation

Usages

Accessibilité

Contenus

Il faut utiliser le même format d'écriture sur tout vos libellés de boutons (Exemple : 1ère lettre en majuscule, le reste en minuscule, infinitif, etc.).

Personnalisation

Le style de ce composant n’est pas personnalisable.
Toutefois, certains éléments des boutons sont optionnels et les icônes peuvent être changées - voir la structure du composant bouton.

note

Besoin d’aide ?

L'équipe du DSFR est là pour vous aider.
Retrouvez-la sur :

Besoin d’aide ?

L'équipe du DSFR est là pour vous aider.
Retrouvez-la sur :