Bouton radio (et bouton radio riche) - DsfrRadioButton 
🌟 Introduction 
Important
Il est fortement déconseillé d’utiliser directement DsfrRadioButton, il est préférable d’utiliser DsfrRadioButtonSet.
Les boutons radio permettent à l’utilisateur de sélectionner une seule option dans une liste.
Le bouton radio ne peut pas être utilisé seul : il faut au minimum 2 options. Il est préférable de ne pas sélectionner d’option par défaut pour que le choix de l’utilisateur soit conscient (en particulier si le choix est obligatoire).
Les boutons radio riches permettent de sélectionner un choix parmi une liste d’options illustrées. À la différence du bouton radio simple, l’image permet d’illustrer et d’accompagner l’utilisateur dans son choix.
Concernant les images, elles peuvent être des artworks/pictogrammes au format SVG. Il faut alors se référer aux fondamentaux techniques du DSFR pour la définition des SVG.
🏅 La documentation sur le bouton radio et le bouton radio riche sur le DSFR
La story sur le bouton radio sur le storybook de VueDsfr📐 Structure 
Le composant DsfrRadioButton est composé des éléments suivants :
- Un élément <div>englobant l'ensemble des éléments du bouton radio
- Un input (input) définie par différentes propriétés.
- Un label avec un message descriptif optionnel.
- Une image optionnelle (imgousvg) en fonction des propriétés renseignées
🛠️ Props 
| Nom | Type | Description | Valeur par défaut | Obligatoire | 
|---|---|---|---|---|
| name | string | Nom du champ <input>associé à l'ensemble des boutons radio | ✅ | |
| modelValue | stringounumberoubooleanouundefined | Valeur courante du composant (sélection courante) | '' | ✅ | 
| label | string | Libellé du bouton radio | '' | ✅ | 
| hint | string | Descriptif du bouton radio | '' | ✅ | 
| inline | boolean | Affiche le bouton radio en ligne | false | |
| id | string | Id du champ <input> | undefined | |
| small | boolean | Affiche le bouton radio en taille réduite | false | |
| disabled | boolean | Indique si le bouton radio est désactivé. | false | |
| img | string | Source de l'image à afficher. | undefined | |
| imgTitle | string | Titre de l'image à ajouter en attribut titleaimgou à ajouter en élémenttitledans la balisesvg. | undefined | |
| svgPath | string | Chemin vers le SVG à afficher. | undefined | |
| svgAttrs | Record<string, unknown> | Chemin vers le SVG à afficher. | { viewBox: '0 0 80 80', width: '80px', height: '80px' } | Attributs pour le SVG. | 
📡 Événements 
DsfrRadioButton émet l'événement suivant :
| Nom | Description | 
|---|---|
| update:modelValue | Est émis lorsque la valeur du bouton radio est sélectionnée | 
🧩 Slots 
DsfrRadioButton fournit les slots suivants pour la personnalisation :
- label: Permet de personnaliser le contenu de la légende.
- required-tip: Permet d'ajouter un astérisque indiquant que le champ est obligatoire (fonctionne uniquement si l'attribut- requiredest défini sur le composant).
🪆 Relation avec DsfrRadioButtonSet 
Le composant DsfrRadioButtonSet utilise le composant DsfrRadioButton pour rendre visuellement chaque option du groupe. Chaque bouton radio individuel hérite des props du composant DsfrRadioButtonSet excepté modelValue.
📝 Exemples 
<script lang="ts" setup>
import { ref } from 'vue'
import DsfrRadioButton from '../DsfrRadioButton.vue'
import DsfrRadioButtonSet from '../DsfrRadioButtonSet.vue'
const modelValue1 = ref()
const options = [
  {
    label: 'Première valeur',
    id: 'name1-1',
    value: 'name1-1',
    hint: 'Description de la première valeur',
  },
  {
    label: 'Deuxième valeur',
    id: 'name1-2',
    value: 'name1-2',
    hint: 'Description de la deuxième valeur',
  },
  {
    label: 'Troisième valeur',
    id: 'name1-3',
    value: 'name1-3',
    hint: 'Description de la troisième valeur',
  },
]
</script>
<template>
  <div class="fr-container fr-my-2v">
    <DsfrRadioButtonSet>
      <DsfrRadioButton
        v-for="option of options"
        :key="option.id"
        v-model="modelValue1"
        name="name-1"
        v-bind="option"
      />
    </DsfrRadioButtonSet>
    <p>
      modelValue1: {{ modelValue1 }}
    </p>
  </div>
</template>⚙️ Code source du composant 
<script lang="ts" setup>
import type { DsfrRadioButtonProps } from './DsfrRadioButton.types'
import { computed } from 'vue'
import { useRandomId } from '../../utils/random-utils'
export type { DsfrRadioButtonProps }
const props = withDefaults(defineProps<DsfrRadioButtonProps>(), {
  id: () => useRandomId('basic', 'radio'),
  modelValue: '',
  label: '',
  hint: '',
  img: undefined,
  svgPath: undefined,
  rich: false,
  svgAttrs: () => ({ viewBox: '0 0 80 80', width: '80px', height: '80px' }),
})
defineEmits<{ (e: 'update:modelValue', payload: string | number | boolean): void }>()
const defaultSvgAttrs = { viewBox: '0 0 80 80', width: '80px', height: '80px' }
const richComputed = computed(() => props.rich || (!!props.img || !!props.svgPath))
</script>
<template>
  <div
    class="fr-fieldset__element"
    :class="{ 'fr-fieldset__element--inline': inline }"
  >
    <div
      class="fr-radio-group"
      :class="{
        'fr-radio-rich': richComputed,
        'fr-radio-group--sm': small,
      }"
    >
      <input
        :id="id"
        type="radio"
        :name="name"
        :value="value"
        :checked="modelValue === value"
        :disabled="disabled"
        v-bind="$attrs"
        @click="$emit('update:modelValue', value)"
      >
      <label
        :for="id"
        class="fr-label"
      >
        <!-- @slot Slot pour personnaliser tout le contenu de la balise <label> cf. [DsfrInput](/?path=/story/composants-champ-de-saisie-champ-simple-dsfrinput--champ-avec-label-personnalise). Une **props porte le même nom pour un label simple** (texte sans mise en forme) -->
        <slot name="label">
          {{ label }}
          <!-- @slot Slot pour indiquer que le champ est obligatoire. Par défaut, met une astérisque si `required` est à true (dans un `<span class="required">`) -->
          <slot name="required-tip">
            <span
              v-if="$attrs.required"
              class="required"
            > *</span>
          </slot>
        </slot>
        <span
          v-if="hint"
          class="fr-hint-text"
        >
          {{ hint }}
        </span>
      </label>
      <div
        v-if="img || svgPath"
        class="fr-radio-rich__pictogram"
      >
        <img
          v-if="img"
          :src="img"
          class="fr-artwork"
          alt=""
          :title="imgTitle"
        >
        <svg
          v-else
          aria-hidden="true"
          class="fr-artwork"
          v-bind="{ ...defaultSvgAttrs, ...svgAttrs }"
        >
          <title v-if="imgTitle">{{ imgTitle }}</title>
          <use
            class="fr-artwork-decorative"
            :href="`${svgPath}#artwork-decorative`"
          />
          <use
            class="fr-artwork-minor"
            :href="`${svgPath}#artwork-minor`"
          />
          <use
            class="fr-artwork-major"
            :href="`${svgPath}#artwork-major`"
          />
        </svg>
      </div>
    </div>
  </div>
</template>export type DsfrRadioButtonProps = {
  id?: string
  name?: string
  modelValue: string | number | boolean | undefined
  disabled?: boolean
  small?: boolean
  inline?: boolean
  value: string | number | boolean
  label?: string
  hint?: string
  rich?: boolean
  img?: string
  imgTitle?: string
  svgPath?: string
  svgAttrs?: Record<string, unknown>
}
export type DsfrRadioButtonOptions = (Omit<DsfrRadioButtonProps, 'modelValue'>)[]
export type DsfrRadioButtonSetProps = {
  titleId?: string
  disabled?: boolean
  required?: boolean
  small?: boolean
  inline?: boolean
  name?: string
  errorMessage?: string
  validMessage?: string
  legend?: string
  hint?: string
  modelValue?: string | number | boolean | undefined
  options?: Omit<DsfrRadioButtonProps, 'modelValue'>[]
  ariaInvalid?: boolean | 'grammar' | 'spelling'
}