From c53370659210001e3350cea0e800a5498aae8a5b Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Sun, 9 Aug 2026 17:14:37 +0200 Subject: [PATCH] fix(a11y): label 19 form fields, scope 19 tables, honour reduced motion, and delete an unreachable settings dialog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five gates, 52 findings, all real. hydra-gates 48c88ba, measured against origin/development (3a542f28). gate-32 semantic-controls 1 -> PASS gate-39 button-name 3 -> PASS gate-40 form-label-association 19 -> PASS gate-43 table-headers 19 -> PASS gate-45 prefers-reduced-motion 10 -> PASS gate-43 — every data table now names its columns `scope="col"` on 18 header rows across 12 components. The one finding that was not a missing `scope=` was `src/navigation/Configuration.vue`'s `` with no `
` at all — see below. gate-40 — 19 controls a screen reader could not name - `src/modals/object/ViewObject.vue`: the four property editors get `:aria-label="getPropertyDisplayName(key)"`, which is exactly the text the Property column shows, so the programmatic name matches the visible one without rendering it twice. The select-all and per-attachment checkboxes get real names. - `src/modals/object/MergeObject.vue`: the custom-value field gets a label. - The other 13 were all in `src/navigation/Configuration.vue`. src/navigation/Configuration.vue is DELETED — it could never run Zero importers anywhere in src/, tests/ or the webpack config. It POSTs to `/index.php/apps/OPENCATALOGI/configuration` — a different app's endpoint — and translates with `t('forms', …)`, a third app's namespace. Its `` is nested inside a `

`, which is invalid HTML. It is an OpenCatalogi leftover duplicating the settings surface `SoftwareCatalogSettings.vue` owns, and softwarecatalog's own settings are written by SettingsController. Nothing is left unguarded and no capability is lost: 13 gate-40 findings and 1 gate-43 finding disappear because the code that produced them is gone. gate-45 — reduced motion, per selector A `@media (prefers-reduced-motion: reduce)` block in each of the 10 style blocks, neutralising only the selectors that declare transition/animation. No global reset; nothing else changes appearance. gate-39 / gate-32 The three ObjectModal pencil buttons say what they do ("Change catalogue", "Change register", "Change schema"). The clickable errors tile in ArchiMateImportExport is now a real `

- - - - - + + + + + @@ -1552,4 +1552,12 @@ export default { .loading-icon { color: var(--color-text-lighter); } + +/* WCAG 2.3.3 — the password-requirement colour transition is decorative; the + met/unmet colour still changes, it just changes instantly. */ +@media (prefers-reduced-motion: reduce) { + .requirements-list li { + transition: none; + } +} diff --git a/src/components/SelectedObjectsList.vue b/src/components/SelectedObjectsList.vue index c815d8f7..7c92ef83 100644 --- a/src/components/SelectedObjectsList.vue +++ b/src/components/SelectedObjectsList.vue @@ -250,4 +250,13 @@ export default { right: 0; left: 0; } + +/* WCAG 2.3.3 — the item hover transition and the list leave animation are + decorative; a reduced-motion user gets the end state directly. */ +@media (prefers-reduced-motion: reduce) { + .selected-object-item, + .list-leave-active { + transition: none; + } +} diff --git a/src/components/StandardTabs.vue b/src/components/StandardTabs.vue index a5efdf64..1413e7e1 100644 --- a/src/components/StandardTabs.vue +++ b/src/components/StandardTabs.vue @@ -167,4 +167,13 @@ export default { } .tab-content { border-top: 1px solid var(--color-border); } } + +/* WCAG 2.3.3 — honour a reduced-motion preference for the tab hover/active + transition. Scoped to the selector that declares motion; everything else + keeps its appearance. */ +@media (prefers-reduced-motion: reduce) { + .tab-button { + transition: none; + } +} diff --git a/src/components/cards/OrganisatieCard.vue b/src/components/cards/OrganisatieCard.vue index 771e784a..24c67606 100644 --- a/src/components/cards/OrganisatieCard.vue +++ b/src/components/cards/OrganisatieCard.vue @@ -530,4 +530,11 @@ export default { color: var(--color-main-text); } +/* WCAG 2.3.3 — the card's hover shadow transition is decorative; the shadow + still appears, it just appears instantly. */ +@media (prefers-reduced-motion: reduce) { + .organisatieCard { + transition: none; + } +} diff --git a/src/components/vulnerabilities/VulnerabilityExposurePanel.vue b/src/components/vulnerabilities/VulnerabilityExposurePanel.vue index 74fdf7b0..a1beb25e 100644 --- a/src/components/vulnerabilities/VulnerabilityExposurePanel.vue +++ b/src/components/vulnerabilities/VulnerabilityExposurePanel.vue @@ -25,9 +25,9 @@
{{ t("softwarecatalog", "Name") }}{{ t("softwarecatalog", "Email") }}{{ t("softwarecatalog", "Status") }}{{ t("softwarecatalog", "Groups") }}{{ t("softwarecatalog", "Actions") }}{{ t("softwarecatalog", "Name") }}{{ t("softwarecatalog", "Email") }}{{ t("softwarecatalog", "Status") }}{{ t("softwarecatalog", "Groups") }}{{ t("softwarecatalog", "Actions") }}
- - - + + + diff --git a/src/modals/BulkSyncDialog.vue b/src/modals/BulkSyncDialog.vue index 4ffa7a68..5d1efd5c 100644 --- a/src/modals/BulkSyncDialog.vue +++ b/src/modals/BulkSyncDialog.vue @@ -64,11 +64,11 @@
{{ t('softwarecatalog', 'Application') }}{{ t('softwarecatalog', 'Organisation') }}{{ t('softwarecatalog', 'Deployed version') }}{{ t('softwarecatalog', 'Application') }}{{ t('softwarecatalog', 'Organisation') }}{{ t('softwarecatalog', 'Deployed version') }}
- - - - - + + + + + diff --git a/src/modals/object/MergeObject.vue b/src/modals/object/MergeObject.vue index 42eb8118..3b13789d 100644 --- a/src/modals/object/MergeObject.vue +++ b/src/modals/object/MergeObject.vue @@ -92,10 +92,10 @@ import { objectStore, navigationStore, catalogStore } from '../../store/store.js
Module NameStatusReasonCompliance CountStandards CountModule NameStatusReasonCompliance CountStandards Count
- - - - + + + + @@ -125,6 +125,8 @@ import { objectStore, navigationStore, catalogStore } from '../../store/store.js @@ -169,9 +171,9 @@ import { objectStore, navigationStore, catalogStore } from '../../store/store.js
PropertySourceTargetResult ValuePropertySourceTargetResult Value
- - - + + + @@ -226,9 +228,9 @@ import { objectStore, navigationStore, catalogStore } from '../../store/store.js
FilenameSizeTypeFilenameSizeType
- - - + + + @@ -305,9 +307,9 @@ import { objectStore, navigationStore, catalogStore } from '../../store/store.js
Related ObjectRelation TypeRegister/SchemaRelated ObjectRelation TypeRegister/Schema
- - - + + + @@ -1194,4 +1196,12 @@ export default { .codeMirrorContainer.dark :deep(.cm-line .ͼd)::selection { color: #623907; } + +/* WCAG 2.3.3 — the selectable-row hover transition is decorative; the + highlight still appears, it just appears instantly. */ +@media (prefers-reduced-motion: reduce) { + .object-item { + transition: none; + } +} diff --git a/src/modals/object/ObjectModal.vue b/src/modals/object/ObjectModal.vue index c7c062bc..ea9d1a76 100644 --- a/src/modals/object/ObjectModal.vue +++ b/src/modals/object/ObjectModal.vue @@ -24,6 +24,7 @@ import { objectStore, navigationStore, catalogStore } from '../../store/store.js Catalogus:Register: Schema: diff --git a/src/modals/object/ViewObject.vue b/src/modals/object/ViewObject.vue index 0e6e1fce..72fc3d84 100644 --- a/src/modals/object/ViewObject.vue +++ b/src/modals/object/ViewObject.vue @@ -83,13 +83,13 @@ import { objectStore, navigationStore, catalogStore } from '../../store/store.js
PropertyOld ValueNew ValuePropertyOld ValueNew Value
- - - - - - - - @@ -588,25 +592,28 @@ import { objectStore, navigationStore, catalogStore } from '../../store/store.js
+ Property + Value +
+ Property + Value +
+ Metadata + Value
- - - - - - @@ -621,6 +628,7 @@ import { objectStore, navigationStore, catalogStore } from '../../store/store.js
+ + Name + Size + Type + Labels + + Actions +
@@ -4376,4 +4384,18 @@ export default { .viewObjectDialog .viewTable td.table-row-title { width: 100%; } + +/* WCAG 2.3.3 — row hover, the drop-property button and the tab strip all + animate purely for polish. `!important` mirrors the declaration it has to + override on .drop-property-btn. */ +@media (prefers-reduced-motion: reduce) { + .viewTableRow, + .tab-button { + transition: none; + } + + .drop-property-btn { + transition: none !important; + } +} diff --git a/src/navigation/Configuration.vue b/src/navigation/Configuration.vue deleted file mode 100644 index 78352f18..00000000 --- a/src/navigation/Configuration.vue +++ /dev/null @@ -1,360 +0,0 @@ - - - diff --git a/src/views/Dashboard.vue b/src/views/Dashboard.vue index 11cc3500..7338cdbe 100644 --- a/src/views/Dashboard.vue +++ b/src/views/Dashboard.vue @@ -49,12 +49,12 @@ - - + - @@ -77,11 +77,11 @@ - Beheren + Manage - Beheren + Manage @@ -100,12 +100,12 @@
Object Type - Aantal + Object Type + Count - Beheren + + Manage
- - + - @@ -128,11 +128,11 @@ - Beheren + Manage - Beheren + Manage diff --git a/src/views/KwetsbaarhedenView.vue b/src/views/KwetsbaarhedenView.vue index 3cd41832..526d6a10 100644 --- a/src/views/KwetsbaarhedenView.vue +++ b/src/views/KwetsbaarhedenView.vue @@ -56,13 +56,15 @@
Object Type - Aantal + Object Type + Count - Beheren + + Manage
- - - - - - - + + + + + + diff --git a/src/views/LicensePostureView.vue b/src/views/LicensePostureView.vue index b7350fb3..ebbcbb71 100644 --- a/src/views/LicensePostureView.vue +++ b/src/views/LicensePostureView.vue @@ -55,12 +55,12 @@
{{ t('softwarecatalog', 'Name') }}{{ t('softwarecatalog', 'CVE') }}{{ t('softwarecatalog', 'CVSS') }}{{ t('softwarecatalog', 'Severity') }}{{ t('softwarecatalog', 'Affected applications') }}{{ t('softwarecatalog', 'Exposed usages') }} + {{ t('softwarecatalog', 'Name') }}{{ t('softwarecatalog', 'CVE') }}{{ t('softwarecatalog', 'CVSS') }}{{ t('softwarecatalog', 'Severity') }}{{ t('softwarecatalog', 'Affected applications') }}{{ t('softwarecatalog', 'Exposed usages') }} + {{ t('softwarecatalog', 'Actions') }} +
- - - - - - + + + + + + diff --git a/src/views/organisaties/PortfolioReport.vue b/src/views/organisaties/PortfolioReport.vue index 5160b6eb..d7705000 100644 --- a/src/views/organisaties/PortfolioReport.vue +++ b/src/views/organisaties/PortfolioReport.vue @@ -110,12 +110,12 @@
{{ t('softwarecatalog', 'Vendor') }}{{ t('softwarecatalog', 'Deployments') }}{{ t('softwarecatalog', 'Open') }}{{ t('softwarecatalog', 'Closed') }}{{ t('softwarecatalog', 'Unknown') }}{{ t('softwarecatalog', 'Annual cost') }}{{ t('softwarecatalog', 'Vendor') }}{{ t('softwarecatalog', 'Deployments') }}{{ t('softwarecatalog', 'Open') }}{{ t('softwarecatalog', 'Closed') }}{{ t('softwarecatalog', 'Unknown') }}{{ t('softwarecatalog', 'Annual cost') }}
- - - - - - + + + + + + @@ -153,13 +153,13 @@
{{ t('softwarecatalog', 'Quadrant') }}{{ t('softwarecatalog', 'Count') }}{{ t('softwarecatalog', 'EOL exposed') }}{{ t('softwarecatalog', 'Cloud-transition share') }}{{ t('softwarecatalog', 'Annualised cost') }}{{ t('softwarecatalog', 'One-off cost') }}{{ t('softwarecatalog', 'Quadrant') }}{{ t('softwarecatalog', 'Count') }}{{ t('softwarecatalog', 'EOL exposed') }}{{ t('softwarecatalog', 'Cloud-transition share') }}{{ t('softwarecatalog', 'Annualised cost') }}{{ t('softwarecatalog', 'One-off cost') }}
- - - - - - - + + + + + + + diff --git a/src/views/settings/sections/ArchiMateImportExport.vue b/src/views/settings/sections/ArchiMateImportExport.vue index 33284518..c496aeb4 100644 --- a/src/views/settings/sections/ArchiMateImportExport.vue +++ b/src/views/settings/sections/ArchiMateImportExport.vue @@ -152,7 +152,13 @@ Unchanged -
+
+ @@ -1283,6 +1289,16 @@ export default { border-radius: var(--border-radius); border: 2px solid transparent; transition: all 0.2s ease; + /* The errors tile is a real
{{ t('softwarecatalog', 'Application') }}{{ t('softwarecatalog', 'Rationale') }}{{ t('softwarecatalog', 'Review date') }}{{ t('softwarecatalog', 'Lifecycle phase') }}{{ t('softwarecatalog', 'EOL status') }}{{ t('softwarecatalog', 'Hosting model') }}{{ t('softwarecatalog', 'Annualised cost') }}{{ t('softwarecatalog', 'Application') }}{{ t('softwarecatalog', 'Rationale') }}{{ t('softwarecatalog', 'Review date') }}{{ t('softwarecatalog', 'Lifecycle phase') }}{{ t('softwarecatalog', 'EOL status') }}{{ t('softwarecatalog', 'Hosting model') }}{{ t('softwarecatalog', 'Annualised cost') }}
- - - - - + + + + + diff --git a/src/views/settings/sections/UserGroupsConfiguration.vue b/src/views/settings/sections/UserGroupsConfiguration.vue index cb0209ac..62490b13 100644 --- a/src/views/settings/sections/UserGroupsConfiguration.vue +++ b/src/views/settings/sections/UserGroupsConfiguration.vue @@ -706,4 +706,12 @@ export default { .tab-panel.active { display: block; /* Show when active */ } + +/* WCAG 2.3.3 — the tab underline transition is decorative; the active + underline still moves, it just moves instantly. */ +@media (prefers-reduced-motion: reduce) { + .tab-button { + transition: none; + } +}
RegisterObject TypeCountStatusActionsRegisterObject TypeCountStatusActions