European Accessibility Act 2025 : ce que ça change pour votre entreprise
Depuis quelques mois, l'European Accessibility Act revient régulièrement dans les conversations sur le numérique. Mais entre les termes techniques, les dates qui changent selon les sources, et le flou sur qui est réellement concerné, il est difficile de savoir quoi faire concrètement. Cet article va clarifier les points essentiels — et vous aider à comprendre ce que cela implique pour votre entreprise.
Qu'est-ce que l'European Accessibility Act ?
L'European Accessibility Act (EAA) est une directive européenne adoptée en 2019, dont la transposition en droit français est désormais en cours. Son objectif est d'harmoniser les règles d'accessibilité numérique à l'échelle de l'Union européenne, en les étendant progressivement au-delà des seules administrations publiques.
Concrètement, la directive exige que les produits et services numériques à destination des consommateurs respectent les standards d'accessibilité WCAG 2.1 niveau AA. Elle couvre les sites web, les applications mobiles, les bornes interactives, les terminaux bancaires, les services de billetterie en ligne, et plus largement tout service numérique proposé au grand public.
Le calendrier de mise en application se divise en deux phases : à partir de juin 2025, les nouveaux services numériques lancés par des entreprises dans le périmètre de la directive doivent être conformes dès leur mise en ligne. Pour les services déjà existants, une période de transition court jusqu'en juin 2030.
Qui est concerné ? Beaucoup plus de PME qu'on ne le pense
C'est là que beaucoup d'entreprises se trompent. L'EAA ne s'applique pas qu'aux grandes entreprises du CAC 40 ou aux multinationales. Le critère de déclenchement est relativement bas :
- Entreprises de plus de 10 salariés , ou
- Entreprises réalisant plus de 2 millions d'euros de chiffre d'affaires annuel
Et le service doit être proposé aux consommateurs — c'est-à-dire aux particuliers. Si vous avez un site e-commerce, un formulaire de prise de rendez-vous en ligne, un espace client, une boutique, un service de réservation — vous êtes probablement concerné.
Les micro-entreprises (moins de 10 salariés et moins de 2 millions d'euros de CA) bénéficient d'une exemption. Mais dès que vous dépassez ces seuils et que vous proposez des services numériques aux particuliers, la directive s'applique à vous.
Ce que la directive exige concrètement
Le niveau requis par l'EAA est le niveau AA des WCAG 2.1 — soit un cran au-dessus du niveau A. Cela signifie que, en plus des critères fondamentaux (images, liens, formulaires, structure de titres), votre site devra respecter des exigences complémentaires :
- Contrastes de couleurs : le texte doit présenter un ratio de contraste d'au moins 4,5:1 avec son fond (3:1 pour les grands textes). C'est l'un des critères les plus souvent non conformes sur les sites aux ambiances visuelles soignées.
- Navigation cohérente : les mécanismes de navigation doivent être identiques d'une page à l'autre, et les composants similaires doivent être présentés de manière constante.
- Aide à la saisie : les formulaires doivent fournir des suggestions en cas d'erreur, et permettre à l'utilisateur de vérifier et corriger ses données avant soumission.
- Titres et étiquettes : les intitulés des champs et des sections doivent décrire leur contenu ou leur objet de manière claire.
Ce qui se passe si vous n'anticipez pas
La directive prévoit des mécanismes de contrôle et de sanction. En France, la DINUM (Direction interministérielle du numérique) est l'organisme chargé du suivi de la conformité. Les entreprises non conformes s'exposent à des procédures de mise en demeure, potentiellement à des sanctions financières, et surtout à des plaintes de la part d'associations de défense des droits des personnes handicapées.
Mais au-delà des risques légaux, la vraie conséquence pour les entreprises qui n'anticipent pas est plus prosaïque : elles devront faire face à une mise en conformité urgente, sous pression, et à un coût souvent bien supérieur à ce qu'une approche progressive aurait représenté. Refondre un site existant pour le rendre conforme niveau AA est un chantier nettement plus lourd que de l'anticiper lors d'une création ou d'une refonte planifiée.
L'approche FolioDesign : anticiper sans pression
Chez FolioDesign, nous avons structuré notre offre pour permettre à nos clients d'anticiper l'EAA sans avoir à tout faire d'un coup. La logique est progressive :
Le niveau A est inclus dans tous nos forfaits. C'est la fondation — les 25 critères essentiels vérifiés, le rapport livré avec le site. Pour la grande majorité des PME qui ne sont pas encore dans le périmètre obligatoire de l'EAA, c'est déjà une démarche sérieuse et documentée.
Le niveau AA est disponible en option. Pour les clients qui entrent dans le périmètre de la directive, ou qui souhaitent anticiper leur mise en conformité avant 2030, nous proposons un accompagnement complémentaire vers le niveau AA. Ce n'est pas un basculement brutal — c'est une progression logique à partir du niveau A déjà établi.
Cette transparence est voulue. Promettre le niveau AA à tous les clients sans vérification rigoureuse serait malhonnête. Proposer le niveau A comme socle et le niveau AA comme progression, c'est une approche honnête qui correspond à la réalité des besoins de nos clients.
Le rôle de Folia et de la plateforme partenaire
L'un des avantages concrets de l'approche FolioDesign est que beaucoup d'éléments de conformité sont automatisés dès la création du site, sans effort supplémentaire de la part du client.
Folia, l'assistant IA intégré à notre plateforme partenaire, génère automatiquement les textes alternatifs des images, structure les titres de manière cohérente, et optimise les meta descriptions — trois des éléments les plus fréquemment défaillants dans les audits RGAA. Cette prévention en amont réduit significativement le nombre de corrections à apporter lors de l'audit.
La plateforme partenaire elle-même intègre nativement des mécanismes d'accessibilité dans son infrastructure : attributs ARIA, gestion du focus clavier, données structurées schema.org. Ces fondations techniques sont présentes d'office — elles ne dépendent pas des choix de contenu du client.
Ce que vous devriez faire maintenant
Si votre entreprise dépasse les seuils de l'EAA et propose des services numériques aux particuliers, voici les étapes concrètes à envisager :
- Évaluer votre niveau actuel : un audit niveau A vous donnera une vision claire de votre situation de départ et des corrections prioritaires à apporter.
- Corriger le niveau A en premier : c'est le socle. Les erreurs niveau A sont généralement plus simples à corriger et auront un impact immédiat sur l'expérience utilisateur et le référencement.
- Planifier la progression vers le niveau AA : avec une vision claire du niveau A, la progression vers le AA est beaucoup moins intimidante. C'est une évolution, pas une révolution.
- Intégrer l'accessibilité dans votre prochaine refonte : si vous prévoyez une refonte de site dans les prochaines années, c'est le moment idéal pour intégrer l'accessibilité dès la conception plutôt que de corriger après coup.
L'EAA est une réalité réglementaire dont l'échéance approche. FolioDesign accompagne ses clients dans cette démarche avec une approche progressive et transparente — niveau A inclus dans tous les forfaits, niveau AA disponible en option. La meilleure façon d'aborder ces changements, c'est de commencer maintenant.
🚀 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 )