diff --git a/api/package.json b/api/package.json
index 0abb38ad..e3dde550 100644
--- a/api/package.json
+++ b/api/package.json
@@ -16,7 +16,7 @@
"@data-fair/lib-common-types": "^1.21.0",
"@data-fair/lib-express": "^1.23.0",
"@data-fair/lib-node": "^2.12.1",
- "@data-fair/lib-utils": "^1.12.1",
+ "@data-fair/lib-utils": "^1.12.2",
"@data-fair/portals-shared-markdown": "*",
"cheerio": "^1.0.0",
"config": "^4.4.1",
diff --git a/package-lock.json b/package-lock.json
index d81d0d12..afc6b577 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -52,7 +52,7 @@
"@data-fair/lib-common-types": "^1.21.0",
"@data-fair/lib-express": "^1.23.0",
"@data-fair/lib-node": "^2.12.1",
- "@data-fair/lib-utils": "^1.12.1",
+ "@data-fair/lib-utils": "^1.12.2",
"@data-fair/portals-shared-markdown": "*",
"cheerio": "^1.0.0",
"config": "^4.4.1",
@@ -1096,9 +1096,9 @@
}
},
"node_modules/@data-fair/lib-utils": {
- "version": "1.12.1",
- "resolved": "https://registry.npmjs.org/@data-fair/lib-utils/-/lib-utils-1.12.1.tgz",
- "integrity": "sha512-SHSDYik7cd2Rkk2zG9EcM9OlpLH8NcKKgXMY1v/0GtjZOdYsl8GMqDUUNTff4F75Gdhc0IP6eaSKkl+vgjZxaw==",
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/@data-fair/lib-utils/-/lib-utils-1.12.2.tgz",
+ "integrity": "sha512-DO5x9f91kT6FabUYwIb9J/ls7fVd5R91NbRM15fftNvm4b6A1feGBodbydvHdd5WYNzpXyHyUAat62D8ZeHViw==",
"license": "MIT",
"peerDependencies": {
"dayjs": "1"
@@ -1169,9 +1169,9 @@
}
},
"node_modules/@data-fair/lib-vuetify": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/@data-fair/lib-vuetify/-/lib-vuetify-2.4.1.tgz",
- "integrity": "sha512-g16v2tRt+v52CTJRn2efND4szst1evOqZxeyGpHWb59IyIV7GoPSpzR3zqJHpdaRIcqpSmTxbW/yXJcv061kDQ==",
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/@data-fair/lib-vuetify/-/lib-vuetify-2.4.2.tgz",
+ "integrity": "sha512-Sm31iRBtsgNVbpGurtgrApl9g75NWyy/cijMNyL+DjGYLaFZEliy5kfAGVGSVAw9+Krhf56fry5bxJwu9Am8Kw==",
"license": "MIT",
"dependencies": {
"@data-fair/lib-common-types": "^1.10.4",
@@ -25198,10 +25198,10 @@
"@data-fair/frame": "^0.18.1",
"@data-fair/lib-common-types": "^1.21.0",
"@data-fair/lib-node": "^2.12.1",
- "@data-fair/lib-utils": "^1.12.1",
+ "@data-fair/lib-utils": "^1.12.2",
"@data-fair/lib-vue": "^1.29.1",
"@data-fair/lib-vue-agents": "^0.4.0",
- "@data-fair/lib-vuetify": "^2.4.0",
+ "@data-fair/lib-vuetify": "^2.4.2",
"@data-fair/lib-vuetify-agents": "^0.6.0",
"@data-fair/lib-vuetify-events": "^0.3.0",
"@data-fair/portals-shared-markdown": "*",
@@ -25226,7 +25226,7 @@
"shared/markdown": {
"name": "@data-fair/portals-shared-markdown",
"dependencies": {
- "@data-fair/lib-utils": "^1.12.1",
+ "@data-fair/lib-utils": "^1.12.2",
"marked": "^15.0.12",
"sanitize-html": "^2.17.0"
}
@@ -25237,7 +25237,7 @@
"@data-fair/lib-common-types": "^1.21.0",
"@data-fair/lib-vue": "^1.29.1",
"@data-fair/lib-vue-agents": "^0.4.0",
- "@data-fair/lib-vuetify": "^2.4.0",
+ "@data-fair/lib-vuetify": "^2.4.2",
"@data-fair/lib-vuetify-agents": "^0.6.0",
"@data-fair/portals-shared-markdown": "*",
"@intlify/unplugin-vue-i18n": "^11.0.7",
diff --git a/patches/README.md b/patches/README.md
index 6a254a4d..696792c8 100644
--- a/patches/README.md
+++ b/patches/README.md
@@ -114,6 +114,36 @@ stays valid.
**Removal criterion**: when Vuetify stops emitting `aria-hidden="false"` on labels.
+### `lib/components/VImg/VImg.js` — W3C HTML validity for decorative images
+
+`VImg` mirrors its `alt` prop onto its root element, next to a `role` that is guarded on
+the same value:
+
+```js
+'aria-label': props.alt,
+role: props.alt ? 'img' : undefined
+```
+
+A decorative image is declared with `alt=""` — the empty string is falsy, so the root
+gets no role, but the attribute is still emitted and the element renders as
+`
`. W3C rejects that: `aria-label` must not be used on
+a `div` whose role is `generic`. RGAA 8.2. Four occurrences on the portal visualisation
+catalogue (one per card thumbnail), twenty on a full catalogue page.
+
+This patch emits the attribute only when `alt` is non-empty (`props.alt || undefined`),
+the guard the neighbouring `role` binding already applies. Nothing changes for
+informative images: they keep `role="img"` and their label. Decorative ones lose an
+attribute that carried no name anyway — the inner `img` keeps its `alt=""`, which is
+what makes it decorative, so restitution is unchanged.
+
+There is no userland fix: the object holding `aria-label` is the last argument of the
+root's `mergeProps`, so it overrides any `aria-label` passed by the caller, and dropping
+the `alt` prop instead would leave the inner `img` without an `alt` attribute — one W3C
+error traded for another.
+
+**Removal criterion**: when Vuetify guards `aria-label` like it guards `role` — reported
+as vuetifyjs/vuetify#23111, fix proposed upstream in vuetifyjs/vuetify#23112.
+
### `lib/components/VSelect/VSelect.js` — W3C HTML validity for the hidden native select
`VSelect` mirrors its items into a `