L'accessibilité web, c'est quoi concrètement ?
On entend de plus en plus parler d'accessibilité web. Les termes RGAA, WCAG, niveau A, niveau AA circulent dans les conversations sur le numérique. Mais concrètement, qu'est-ce que cela signifie pour vous, dirigeant d'une PME ? Faut-il s'en préoccuper ? Et surtout, par où commencer ?
Chez FolioDesign, nous avons choisi d'intégrer l'accessibilité dès la conception des sites de nos clients — pas comme une contrainte administrative, mais comme un vrai levier de qualité et de visibilité. Voici ce que vous devez savoir, sans jargon inutile.
Le RGAA, c'est quoi ?
Le RGAA — Référentiel Général d'Amélioration de l'Accessibilité — est le cadre officiel français qui définit les règles à respecter pour qu'un site web soit accessible aux personnes en situation de handicap. Il s'appuie sur les standards internationaux WCAG (Web Content Accessibility Guidelines), publiés par le W3C, l'organisme mondial de standardisation du web.
Concrètement, le RGAA s'intéresse à la manière dont votre site est perçu et utilisé par des personnes qui ne voient pas bien, qui n'entendent pas, qui naviguent au clavier sans souris, ou qui utilisent des technologies d'assistance comme les lecteurs d'écran. L'objectif est simple : un site accessible est un site que tout le monde peut utiliser, quelles que soient ses capacités.
Le référentiel est organisé en critères regroupés par thématiques : images, couleurs, multimédia, tableaux, liens, formulaires, navigation, structure des pages... Chaque critère peut être conforme, non conforme, ou non applicable selon le contenu de votre site.
Niveau A et niveau AA : quelle différence ?
Le RGAA — comme les WCAG sur lesquels il repose — définit trois niveaux de conformité : A, AA et AAA. Pour une PME, deux niveaux sont réellement pertinents.
Le niveau A représente le socle fondamental. Il couvre les erreurs les plus courantes et les plus impactantes : une image sans description textuelle, un lien avec un intitulé générique comme "Cliquez ici", un formulaire dont les champs ne sont pas correctement étiquetés, ou encore une page sans titre identifiable. Ce sont des erreurs simples à corriger, mais qui rendent littéralement le site inutilisable pour certains utilisateurs.
Le niveau AA va plus loin : il exige notamment des contrastes suffisants entre le texte et le fond, une structure de navigation cohérente sur l'ensemble du site, et des mécanismes permettant aux utilisateurs de naviguer efficacement. C'est ce niveau que visent les obligations légales à venir avec l'European Accessibility Act.
Pour la grande majorité des PME, viser le niveau A en premier est déjà un engagement sérieux et différenciant. C'est précisément ce que FolioDesign inclut dans ses forfaits : un diagnostic de conformité niveau A livré avec le site, avec le niveau AA disponible en option pour ceux qui souhaitent aller plus loin.
Qui est concerné légalement ?
Aujourd'hui en France, les obligations strictes de conformité RGAA s'appliquent principalement aux organismes publics, aux entreprises délégataires de service public, et aux grandes entreprises dont le chiffre d'affaires dépasse 250 millions d'euros. Les PME ne sont donc pas encore dans le champ obligatoire — mais la situation évolue.
L'European Accessibility Act (EAA), directive européenne en cours de transposition, va progressivement élargir le périmètre d'obligation. À partir de juin 2025, les nouveaux services numériques proposés aux particuliers par des entreprises de plus de 10 salariés ou réalisant plus de 2 millions d'euros de chiffre d'affaires entreront dans le champ de la directive. Les services existants auront jusqu'en 2030 pour se mettre en conformité.
Autrement dit : si vous attendez que l'obligation s'applique à vous pour vous en préoccuper, vous risquez de devoir faire face à une mise en conformité coûteuse et urgente dans quelques années. Anticiper maintenant, c'est répartir l'effort sur la durée.
L'accessibilité et le SEO : une alliance inattendue
Voici quelque chose que peu d'agences web expliquent clairement : Google fonctionne comme un lecteur d'écran. Le moteur de recherche ne voit pas vos images, il lit leurs descriptions. Il ne perçoit pas la hiérarchie visuelle de votre page, il analyse la structure de vos titres. Il ne comprend pas un lien intitulé "Découvrir", mais il comprend "Découvrir nos forfaits web pour PME".
En d'autres termes, un site accessible est quasi mécaniquement mieux référencé. Les balises alt sur les images, la structure cohérente des titres H1/H2/H3, les meta descriptions pertinentes, les intitulés de liens explicites — tout cela contribue simultanément à l'accessibilité et au référencement naturel. Ce ne sont pas deux chantiers séparés, c'est le même travail bien fait.
C'est pourquoi Folia, l'assistant IA intégré à la plateforme partenaire FolioDesign, génère automatiquement les textes alternatifs des images lors de la création du site. Cette fonctionnalité seule résout l'une des non-conformités les plus fréquentes — et améliore simultanément votre référencement sur les recherches d'images Google.
Ce que FolioDesign fait concrètement
Chez FolioDesign, l'accessibilité n'est pas une option qu'on vend en supplément après coup. Elle fait partie de notre approche dès la création du site, grâce à plusieurs niveaux d'action complémentaires.
- La plateforme partenaire que nous utilisons intègre nativement des bases d'accessibilité solides : attributs ARIA sur les éléments interactifs, navigation clavier fonctionnelle, données structurées schema.org pour les moteurs de recherche.
- Folia , notre assistant IA, prend en charge automatiquement les textes alt des images, la structure des titres et l'optimisation des meta descriptions — trois des éléments les plus fréquemment défaillants lors d'un audit RGAA.
- L'audit niveau A est inclus dans tous nos forfaits. Nous vérifions 25 critères WCAG et vous livrons un rapport clair avec les points conformes, les points à corriger, et les recommandations priorisées.
Nous sommes transparents sur ce que nous livrons : un diagnostic niveau A complet, et le niveau AA disponible en option pour les clients qui en ont besoin ou qui souhaitent anticiper les évolutions réglementaires.
En résumé
L'accessibilité web n'est pas réservée aux grandes entreprises ni aux organismes publics. C'est un standard de qualité qui améliore l'expérience de tous vos visiteurs, renforce votre référencement naturel, anticipe les évolutions légales, et positionne votre entreprise comme un acteur responsable du numérique.
Chez FolioDesign, nous avons fait le choix d'en faire un inclus — pas une option. Parce qu'un site bien fait, c'est un site accessible.
Vous souhaitez en savoir plus sur l'audit de conformité inclus dans les forfaits FolioDesign ? Découvrez nos forfaits et ce que comprend concrètement le diagnostic niveau A livré avec votre site.
🚀 Guide de configuration (3 étapes simples)
📊 Étape 1 : Créer le Google Sheet
- Créez un nouveau Google Sheet
- Nommez-le :
Commentaires Blog - Créez ces colonnes dans la première ligne :
- ✓ A1: name
- ✓ B1: email
- ✓ C1: comment
- ✓ D1: page_url
- ✓ E1: page_title
- ✓ F1: timestamp
- ✓ G1: status
- ✓ H1: Logs
- ✓ I1: custom_fields
- ✓ J1: moderation_flags
- ✓ K1: reports_count
- ✓ L1: reports_details
- ✓ M1: reports_last_date
- ✓ N1: comment_id (unique)
⚙️ Étape 2 : Configurer Google Apps Script (Code v4 FINAL — signalement corrigé)
- Dans votre Google Sheet, allez dans Extensions → Apps Script
- Supprimez le code par défaut
- Copiez-collez le code ci-dessous 👇
// ============================================================
// WIDGET BLOG COMMENTS — Google Apps Script v4
// CORRECTIONS v4 :
// - handleReport ne crée JAMAIS de nouvelle ligne
// - Recherche du commentaire par comment_id (col N)
// ET par timestamp (col F) en fallback
// - Email envoyé même si comment_id introuvable
// (signalement orphelin stocké dans feuille "Signalements")
// - Logs ultra-détaillés pour diagnostic
// ============================================================
function doPost(e) {
try {
// Compatible avec Content-Type: text/plain ET application/json
// (le widget envoie text/plain pour contourner la restriction no-cors)
const rawBody = e.postData ? e.postData.contents : '';
Logger.log('📨 Body brut reçu: ' + rawBody.substring(0, 200));
if (!rawBody || rawBody.trim() === '') {
Logger.log('❌ Body vide reçu');
return jsonResponse({ status: 'error', message: 'Body vide' });
}
const data = JSON.parse(rawBody);
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
if (!data || typeof data !== 'object') {
return jsonResponse({ status: 'error', message: 'Données invalides' });
}
Logger.log('✅ Action reçue: ' + (data.action || 'new_comment'));
if (data.action === 'report') {
return handleReport(data, sheet);
}
return handleNewComment(data, sheet);
} catch (error) {
Logger.log('❌ Erreur doPost: ' + error.toString());
return jsonResponse({ status: 'error', message: error.toString() });
}
}
// ------------------------------------------------------------
// NOUVEAU COMMENTAIRE
// ------------------------------------------------------------
function handleNewComment(data, sheet) {
const customFieldsJson = data.custom_fields || '';
const moderationFlags = data.moderation_flags || '';
const commentId = Utilities.getUuid();
const row = [
data.name || '',
data.email || '',
data.comment || '',
data.page_url || '',
data.page_title || '',
data.timestamp || new Date().toISOString(),
data.status || 'pending',
'', // Logs
customFieldsJson,
moderationFlags,
0, // reports_count (col K)
'', // reports_details (col L)
'', // reports_last_date (col M)
commentId // comment_id (col N)
];
sheet.appendRow(row);
Logger.log('✅ Nouveau commentaire ajouté | id=' + commentId);
if (data.enable_notification && data.admin_email) {
sendAdminNotification(data, moderationFlags);
}
return jsonResponse({
status: 'success',
message: 'Commentaire enregistré',
comment_id: commentId
});
}
// ------------------------------------------------------------
// SIGNALEMENT — NE CRÉE JAMAIS DE NOUVELLE LIGNE
// ------------------------------------------------------------
function handleReport(data, sheet) {
Logger.log('════════════════════════════════════════════');
Logger.log('🚩 handleReport v4 — début');
Logger.log('📦 Données reçues: ' + JSON.stringify(data));
Logger.log('════════════════════════════════════════════');
const reporterName = data.reporter_name || 'Anonyme';
const reporterEmail = data.reporter_email || 'Non fourni';
const reportReason = data.report_reason || 'Non spécifié';
const reportDetails = data.report_details || 'Aucun détail';
const reportDate = new Date().toISOString();
const commentId = (data.comment_id || '').toString().trim();
const adminEmail = (data.admin_email || '').toString().trim();
Logger.log('🔑 comment_id reçu: "' + commentId + '"');
Logger.log('📧 admin_email reçu: "' + adminEmail + '"');
// ── Chercher la ligne du commentaire ──────────────────────
const allValues = sheet.getDataRange().getValues();
let commentRow = -1;
if (commentId !== '') {
// Priorité : recherche par comment_id (col N = index 13)
Logger.log('🔍 Recherche par comment_id dans colonne N (' + allValues.length + ' lignes)...');
for (let i = 1; i < allValues.length; i++) {
const cellId = (allValues[i][13] || '').toString().trim();
Logger.log(' Ligne ' + (i+1) + ': "' + cellId + '"');
if (cellId === commentId) {
commentRow = i + 1;
Logger.log('✅ Trouvé par comment_id à la ligne ' + commentRow);
break;
}
}
}
// Fallback : recherche par timestamp (col F = index 5)
if (commentRow === -1 && data.comment_timestamp) {
Logger.log('🔍 Fallback: recherche par timestamp...');
const ts = (data.comment_timestamp || '').toString().trim();
for (let i = 1; i < allValues.length; i++) {
if ((allValues[i][5] || '').toString().trim() === ts) {
commentRow = i + 1;
Logger.log('✅ Trouvé par timestamp à la ligne ' + commentRow);
break;
}
}
}
// ── Mettre à jour la ligne (si trouvée) ───────────────────
let newCount = 1;
if (commentRow !== -1) {
const currentCount = parseInt(sheet.getRange(commentRow, 11).getValue()) || 0;
const currentDetails = sheet.getRange(commentRow, 12).getValue() || '';
newCount = currentCount + 1;
sheet.getRange(commentRow, 11).setValue(newCount);
Logger.log('✅ reports_count mis à jour: ' + currentCount + ' → ' + newCount);
const detailLine = '[' + reportDate + '] Par: ' + reporterName +
' (' + reporterEmail + ') | Raison: ' + reportReason +
' | Détails: ' + reportDetails;
const newDetails = currentDetails ? currentDetails + '\n\n' + detailLine : detailLine;
sheet.getRange(commentRow, 12).setValue(newDetails);
sheet.getRange(commentRow, 13).setValue(reportDate);
Logger.log('✅ Détails et date mis à jour');
} else {
// Commentaire introuvable — on NE crée PAS de nouvelle ligne
Logger.log('⚠️ Commentaire introuvable. Enregistrement dans feuille "Signalements".');
enregistrerSignalementOrphelin(data, reporterName, reporterEmail, reportReason, reportDetails, reportDate);
}
// ── Envoyer l'email admin ─────────────────────────────────
if (adminEmail !== '') {
Logger.log('📧 Envoi email signalement à: ' + adminEmail);
try {
sendReportNotification(data, newCount, reporterName, reporterEmail, reportReason, reportDetails, commentRow);
Logger.log('✅ Email signalement envoyé');
} catch (emailError) {
Logger.log('❌ Erreur envoi email: ' + emailError.toString());
}
} else {
Logger.log('⚠️ Aucun email admin configuré.');
Logger.log(' → Vérifiez le champ "Email admin pour signalements" dans le content editor du widget.');
Logger.log(' → Ou remplissez le champ "Email de l\'administrateur" (notifications générales).');
}
Logger.log('🚩 handleReport v4 — fin');
Logger.log('════════════════════════════════════════════');
return jsonResponse({
status: 'success',
message: 'Signalement traité',
reports_count: newCount,
row_updated: commentRow
});
}
// Sauvegarde orpheline si le commentaire est introuvable dans le Sheet
function enregistrerSignalementOrphelin(data, reporterName, reporterEmail, reportReason, reportDetails, reportDate) {
try {
const ss = SpreadsheetApp.getActiveSpreadsheet();
let feuille = ss.getSheetByName('Signalements');
if (!feuille) {
feuille = ss.insertSheet('Signalements');
feuille.appendRow(['date', 'comment_id', 'comment_text', 'reporter_name', 'reporter_email', 'reason', 'details']);
}
feuille.appendRow([
reportDate,
data.comment_id || '',
data.comment_text || '',
reporterName,
reporterEmail,
reportReason,
reportDetails
]);
Logger.log('✅ Signalement orphelin enregistré dans "Signalements"');
} catch (err) {
Logger.log('❌ Erreur enregistrement orphelin: ' + err.toString());
}
}
// ------------------------------------------------------------
// EMAILS
// ------------------------------------------------------------
function sendAdminNotification(data, moderationFlags) {
try {
const subject = '📧 Nouveau commentaire sur "' + data.page_title + '"';
const body =
'Un nouveau commentaire a été posté sur votre site :\n\n' +
'👤 Nom : ' + (data.name || '') + '\n' +
'📧 Email : ' + (data.email || 'Non fourni') + '\n' +
'📄 Page : ' + (data.page_title || '') + '\n' +
'🔗 URL : ' + (data.page_url || '') + '\n' +
'📅 Date : ' + (data.timestamp || '') + '\n' +
'✅ Statut : ' + (data.status || '') + '\n\n' +
'💬 Commentaire :\n' + (data.comment || '') + '\n\n' +
(moderationFlags ? '🚨 Modération : ' + moderationFlags + '\n\n' : '') +
'---\nAccédez à votre Google Sheet pour gérer ce commentaire.';
MailApp.sendEmail(data.admin_email, subject, body);
Logger.log('✅ Email nouveau commentaire envoyé');
} catch (error) {
Logger.log('❌ Erreur envoi email commentaire: ' + error.toString());
}
}
function sendReportNotification(data, reportsCount, reporterName, reporterEmail, reportReason, reportDetails, rowUpdated) {
const subject = '🚩 SIGNALEMENT — "' + (data.page_title || 'Page inconnue') + '"';
const rowInfo = rowUpdated !== -1
? 'Ligne mise à jour dans le Sheet : ' + rowUpdated
: '⚠️ Commentaire introuvable dans le Sheet (voir feuille "Signalements")';
const body =
'🚨 ALERTE SIGNALEMENT 🚨\n\n' +
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n' +
'📊 Total signalements : ' + reportsCount + '\n' +
rowInfo + '\n' +
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n' +
'💬 COMMENTAIRE SIGNALÉ\n' +
'📄 Page : ' + (data.page_title || 'N/A') + '\n' +
'🔗 URL : ' + (data.comment_page_url || 'N/A') + '\n' +
'👤 Auteur : ' + (data.comment_name || 'N/A') + '\n' +
'📧 Email auteur : ' + (data.comment_email || 'Non fourni') + '\n' +
'🔑 comment_id : ' + (data.comment_id || 'Non fourni') + '\n\n' +
'📝 Texte :\n"' + (data.comment_text || '') + '"\n\n' +
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n' +
'🚩 SIGNALÉ PAR\n' +
'👤 Nom : ' + reporterName + '\n' +
'📧 Email : ' + reporterEmail + '\n' +
'🚫 Raison : ' + reportReason + '\n' +
'📋 Détails : ' + reportDetails + '\n\n' +
'━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n' +
'⚠️ Veuillez vérifier ce commentaire dans votre Google Sheet.';
MailApp.sendEmail(data.admin_email, subject, body);
Logger.log('✅ Email signalement envoyé à ' + data.admin_email);
}
// ------------------------------------------------------------
// HELPER
// ------------------------------------------------------------
function jsonResponse(obj) {
return ContentService
.createTextOutput(JSON.stringify(obj))
.setMimeType(ContentService.MimeType.JSON);
}
- Cliquez sur 💾 Enregistrer
- ⚠️ IMPORTANT : Cliquez sur "Déployer" → "Gérer les déploiements"
- Cliquez sur ✏️ (Modifier) à côté du déploiement existant
- Changez "Version" → "Nouvelle version"
- Cliquez sur "Déployer"
- ✅ Votre webhook est maintenant mis à jour !
🔍 Pour voir les logs de débogage :
- Dans Google Apps Script, cliquez sur "Exécutions"(icône horloge)
- Cliquez sur la dernière exécution
- Vous verrez tous les logs détaillés du traitement du signalement
🔗 Étape 3 : Connecter à vos Collections
- Dans votre site, allez dans Collections
- Cliquez sur "Connect to Google Sheets"
- Autorisez et sélectionnez votre Google Sheet
- Configurez le mapping des colonnes
- Activez la synchronisation bidirectionnelle automatique
- Notez le nom de la collection créée
- Mettez à jour le champ "Nom de la collection" ci-dessous ⬇️
Laisser un commentaire
emoji
💬 Ceci est un exemple de commentaire pour prévisualiser les styles.
0 sur 0 commentaires affichés
❌ Erreur de chargement
Signaler un commentaire
We got it.
Thank you for contacting us.
We’ll get back to you as soon as possible.












Partagez votre avis sur cet article ( 0 )