Skip to content

Pictogramme - DsfrPictogram

🌟 Introduction

Le pictogramme est une illustration décorative du DSFR. Il est livré sous forme de SVG en trois calques (artwork-decorative, artwork-minor et artwork-major) qui prennent les couleurs du thème courant, clair ou sombre.

Le composant DsfrPictogram affiche ces pictogrammes partout où vous en avez besoin, sans passer par une tuile ou un bouton radio riche. Il choisit seul la façon de rendre le SVG selon ce que vous lui donnez, ce qui évite un piège courant : un pictogramme importé par un bundler arrive souvent sous forme de data URI, et une balise use ne sait pas référencer un fragment dans une data URI.

🏅 La documentation sur les pictogrammes sur le DSFR

La story sur le pictogramme sur le storybook de VueDsfr

📐 Structure

Le composant rend, selon les props reçues :

  • une balise img portant la classe fr-artwork si imgSrc est renseignée ;
  • le SVG en ligne, nettoyé de son contenu actif, si svgPath est une data URI exploitable ; les identifiants des calques y sont suffixés pour que deux pictogrammes de la même page restent indépendants ;
  • une balise img si svgPath est une data URI qui n’a pas pu être analysée ;
  • une balise svg référençant les trois calques du pictogramme dans les autres cas.

Sans imgSrc ni svgPath, le composant ne rend rien.

🛠️ Props

NomTypeDescriptionValeur par défautObligatoire
imgSrcstringSource de l’image à afficher, prioritaire sur svgPath.undefined
svgPathstringChemin du pictogramme SVG, tel qu’importé depuis @gouvfr/dsfr/dist/artwork/pictograms.undefined
svgAttrsRecord<string, unknown>Attributs à poser sur la balise svg, fusionnés avec les valeurs par défaut.{ viewBox: '0 0 80 80', width: '80px', height: '80px' }
titlestringTitre du pictogramme, à ne renseigner que s’il porte une information absente du texte environnant.undefined

📡 Événements

Ce composant ne déclenche pas d’événements spécifiques.

🧩 Slots

Ce composant n’a pas de slot.

♿ Accessibilité

Un pictogramme est une illustration : il est masqué aux technologies d’assistance (aria-hidden sur les SVG, alternative vide sur les images). Ne renseignez title que si le pictogramme porte une information absente du texte qui l’entoure.

📝 Exemples

vue
<script lang="ts" setup>
import svgSchool from '@gouvfr/dsfr/dist/artwork/pictograms/buildings/school.svg'
</script>

<template>
  <DsfrPictogram :svg-path="svgSchool" />
</template>
vue
<template>
  <DsfrPictogram
    :svg-path="svgSchool"
    :svg-attrs="{ width: '40px', height: '40px' }"
  />
</template>
vue
<script lang="ts" setup>
import svgCityHall from '@gouvfr/dsfr/dist/artwork/pictograms/buildings/city-hall.svg'
import svgSchool from '@gouvfr/dsfr/dist/artwork/pictograms/buildings/school.svg'
import svgContract from '@gouvfr/dsfr/dist/artwork/pictograms/document/contract.svg'
import { ref } from 'vue'

import DsfrButton from '../../DsfrButton/DsfrButton.vue'
import DsfrPictogram from '../DsfrPictogram.vue'

const pictograms = [svgSchool, svgCityHall, svgContract]
const currentIndex = ref(0)
const svgPath = ref(pictograms[0])

function nextPictogram () {
  currentIndex.value = (currentIndex.value + 1) % pictograms.length
  svgPath.value = pictograms[currentIndex.value]
}
</script>

<template>
  <div class="demo-container-col">
    <div class="flex items-center gap-4">
      <DsfrPictogram :svg-path="svgPath" />
      <DsfrPictogram
        :svg-path="svgPath"
        :svg-attrs="{ width: '40px', height: '40px' }"
      />
      <DsfrPictogram
        :svg-path="svgPath"
        :svg-attrs="{ width: '24px', height: '24px' }"
      />
    </div>
    <DsfrButton
      label="Changer de pictogramme"
      @click="nextPictogram()"
    />
  </div>
</template>

⚙️ Code source du composant

vue
<script lang="ts" setup>
import type { DsfrPictogramProps } from './DsfrPictogram.types'

import { computed } from 'vue'

import { useRandomId } from '../../utils/random-utils'
import { sanitizeInlineSvgMarkupFromDataUri } from '../../utils/svg-data-uri-utils'

export type { DsfrPictogramProps }

const props = withDefaults(defineProps<DsfrPictogramProps>(), {
  imgSrc: undefined,
  svgPath: undefined,
  svgAttrs: () => ({ viewBox: '0 0 80 80', width: '80px', height: '80px' }),
  title: undefined,
})

const dataImageRegex = /^data:image\/svg\+xml(?:;[^,]*)?,/i
const defaultSvgAttrs = { viewBox: '0 0 80 80', width: '80px', height: '80px' }

const inlineSvgIdSuffix = useRandomId('pictogram', 'artwork').replace(/[^\w-]/g, '_')
const svgDataUriComputed = computed(() => !!props.svgPath?.match(dataImageRegex))
const svgDataUriMarkupComputed = computed(() => (
  sanitizeInlineSvgMarkupFromDataUri(props.svgPath, inlineSvgIdSuffix)
))
</script>

<template>
  <img
    v-if="imgSrc"
    :src="imgSrc"
    class="fr-artwork"
    alt=""
    :title="title"
  >
  <span
    v-else-if="svgDataUriMarkupComputed"
    aria-hidden="true"
    class="fr-artwork fr-artwork--inline"
    v-html="svgDataUriMarkupComputed"
  />
  <img
    v-else-if="svgDataUriComputed"
    :src="svgPath"
    class="fr-artwork"
    alt=""
    :title="title"
  >
  <svg
    v-else-if="svgPath"
    aria-hidden="true"
    class="fr-artwork"
    v-bind="{ ...defaultSvgAttrs, ...svgAttrs }"
  >
    <title v-if="title">{{ title }}</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>
</template>

<style scoped>
.fr-artwork--inline :deep(svg) {
  width: 100%;
  height: 100%;
}
</style>
ts
export type DsfrPictogramProps = {
  /**
   * Chemin d’une image matricielle (ou d’un SVG à afficher tel quel) à utiliser comme pictogramme
   */
  imgSrc?: string
  /**
   * Chemin du pictogramme SVG, tel qu’importé depuis `@gouvfr/dsfr/dist/artwork/pictograms`
   */
  svgPath?: string
  /**
   * Attributs à poser sur la balise `svg` (`viewBox`, `width` et `height` par défaut)
   */
  svgAttrs?: Record<string, unknown>
  /**
   * Titre du pictogramme, à ne renseigner que s’il porte une information absente du texte environnant
   */
  title?: string
}