From 9f772a84ed856fa4f4438319c4c9a4e069b0ba03 Mon Sep 17 00:00:00 2001 From: tristan Date: Tue, 9 Jun 2026 15:40:44 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20accessibilit=C3=A9=20des=20composants=20?= =?UTF-8?q?(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit | Numéro du ticket | Titre du ticket | |------------------|-----------------| | | | ## Description de la PR ## Modification du .env ## Check list - [ ] Pas de régression - [ ] TU/TI/TF rédigée - [ ] TU/TI/TF OK - [ ] CHANGELOG modifié --------- Co-authored-by: admin malio Co-authored-by: THOLOT DECHENE Matthieu Co-authored-by: matthieu Reviewed-on: https://gitea.malio.fr/MALIO-DEV/malio-layer-ui/pulls/70 Co-authored-by: tristan Co-committed-by: tristan --- .playground/pages/composant/date/date.vue | 10 + .../pages/composant/input/inputAmount.vue | 12 + .../pages/composant/input/inputEmail.vue | 15 + .../pages/composant/input/inputUpload.vue | 16 + .../pages/composant/select/selectCheckbox.vue | 4 +- CHANGELOG.md | 14 + COMPONENTS.md | 37 +- app/assets/css/malio.css | 35 + app/components/malio/button/Button.vue | 2 +- app/components/malio/button/ButtonIcon.vue | 2 +- app/components/malio/checkbox/Checkbox.vue | 5 + app/components/malio/datatable/DataTable.vue | 4 +- app/components/malio/date/Date.test.ts | 93 +++ app/components/malio/date/Date.vue | 47 +- .../malio/date/internal/CalendarField.vue | 94 ++- .../malio/input/InputAmount.test.ts | 56 +- app/components/malio/input/InputAmount.vue | 62 +- .../malio/input/InputAutocomplete.vue | 40 +- app/components/malio/input/InputEmail.test.ts | 77 +++ app/components/malio/input/InputEmail.vue | 63 +- app/components/malio/input/InputNumber.vue | 10 +- app/components/malio/input/InputPassword.vue | 8 +- app/components/malio/input/InputPhone.vue | 8 +- app/components/malio/input/InputText.vue | 8 +- app/components/malio/input/InputTextArea.vue | 8 +- app/components/malio/input/InputUpload.vue | 67 +- .../input/composables/amountFormat.test.ts | 74 ++ .../malio/input/composables/amountFormat.ts | 40 ++ app/components/malio/radio/RadioButton.vue | 5 + app/components/malio/select/Select.vue | 82 ++- .../malio/select/SelectCheckbox.vue | 107 ++- .../malio/shared/useKbdFocusRing.ts | 48 ++ app/story/date/datePicker.story.vue | 11 + app/story/input/inputAmount.story.vue | 12 + app/story/input/inputEmail.story.vue | 16 + ...-06-09-inputamount-separateurs-milliers.md | 532 +++++++++++++++ .../2026-06-09-inputemail-bouton-ajout.md | 458 +++++++++++++ .../2026-06-09-maliodate-saisie-manuelle.md | 635 ++++++++++++++++++ ...inputamount-separateurs-milliers-design.md | 117 ++++ ...26-06-09-inputemail-bouton-ajout-design.md | 157 +++++ ...-06-09-maliodate-saisie-manuelle-design.md | 118 ++++ 41 files changed, 3111 insertions(+), 98 deletions(-) create mode 100644 app/components/malio/input/composables/amountFormat.test.ts create mode 100644 app/components/malio/input/composables/amountFormat.ts create mode 100644 app/components/malio/shared/useKbdFocusRing.ts create mode 100644 docs/superpowers/plans/2026-06-09-inputamount-separateurs-milliers.md create mode 100644 docs/superpowers/plans/2026-06-09-inputemail-bouton-ajout.md create mode 100644 docs/superpowers/plans/2026-06-09-maliodate-saisie-manuelle.md create mode 100644 docs/superpowers/specs/2026-06-09-inputamount-separateurs-milliers-design.md create mode 100644 docs/superpowers/specs/2026-06-09-inputemail-bouton-ajout-design.md create mode 100644 docs/superpowers/specs/2026-06-09-maliodate-saisie-manuelle-design.md diff --git a/.playground/pages/composant/date/date.vue b/.playground/pages/composant/date/date.vue index ec73c15..7d50df5 100644 --- a/.playground/pages/composant/date/date.vue +++ b/.playground/pages/composant/date/date.vue @@ -13,6 +13,15 @@

Valeur (ISO) : {{ value ?? 'null' }}

+ +
+

Valeur éditable (ISO) : {{ editableValue ?? 'null' }}

+
+
+

Grand montant (séparateurs)

+ +
+

modelValue émis : {{ bigValue || 'vide' }}

+
+
+

Désactivé

diff --git a/.playground/pages/composant/input/inputEmail.vue b/.playground/pages/composant/input/inputEmail.vue index 8583754..664bb8d 100644 --- a/.playground/pages/composant/input/inputEmail.vue +++ b/.playground/pages/composant/input/inputEmail.vue @@ -14,6 +14,20 @@ />
+
+

Ajout dynamique (bouton +)

+
+ +
+
+

Icône à gauche

(['']) const dynamicEmail = ref('') const requiredEmail = ref('') const lowercaseEmail = ref('') diff --git a/.playground/pages/composant/input/inputUpload.vue b/.playground/pages/composant/input/inputUpload.vue index abb474a..5762dba 100644 --- a/.playground/pages/composant/input/inputUpload.vue +++ b/.playground/pages/composant/input/inputUpload.vue @@ -14,6 +14,17 @@

Valeur : {{ uploadValue || '(aucun)' }}

+
+

Clearable (croix pour vider)

+ +

Valeur : {{ clearableUpload || '(aucun)' }}

+
+

Avec accept (PDF)

{ + clearableUpload.value = '' +} const dynamicError = computed(() => { if (!dynamicUpload.value) return '' diff --git a/.playground/pages/composant/select/selectCheckbox.vue b/.playground/pages/composant/select/selectCheckbox.vue index f9e1a77..6ab5f00 100644 --- a/.playground/pages/composant/select/selectCheckbox.vue +++ b/.playground/pages/composant/select/selectCheckbox.vue @@ -13,7 +13,7 @@
@@ -22,7 +22,7 @@ diff --git a/CHANGELOG.md b/CHANGELOG.md index e3a0d0b..6bc13e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,12 +41,23 @@ Liste des évolutions de la librairie Malio layer UI * Token Tailwind partagé `w-m-btn-action` (150px) exposé via `tailwind.config.ts` + CSS var `--m-btn-action-width` dans `malio.css` — utilisable côté consommateur pour les boutons d'action (``), themable en redéfinissant la CSS var * [#MUI-41] Prop `required` cohérente + astérisque rouge dans le label sur la famille formulaire (Select, SelectCheckbox, InputUpload, InputRichText gagnent la prop) * [#MUI-41] InputEmail : sanitisation à la saisie (suppression des espaces, option `lowercase`) +* [#MUI-42] MalioDate : saisie clavier `JJ/MM/AAAA` optionnelle (prop `editable`, masque maska, validation au blur, message `invalidMessage`) +* InputEmail : bouton `+` d'ajout optionnel (prop `addable`, event `add`), calqué sur InputPhone ; l'icône email passe à gauche quand le bouton est actif +* InputAmount : affichage groupé des milliers à la française (`1 234 567,89`) en temps réel ; `modelValue` reste propre (`'1234567.89'`) ; `maxLength` borne la longueur du modèle +* [#MUI-42] Anneau de focus clavier standardisé (`outline` 2px `m-primary`, offset 2px) affiché **uniquement** à la navigation clavier (jamais au clic souris), sur l'ensemble des champs et contrôles : inputs (Text, Email, Password, Phone, Amount, Number + boutons ±, Upload, TextArea, Autocomplete), Select, SelectCheckbox, famille Date (Date, DateRange, DateTime, DateWeek), Button, ButtonIcon. Mécanique : composable `useKbdFocusRing` (détection de modalité clavier/souris) + utilitaires CSS `.m-focus-ring` (éléments à `:focus-visible` natif) et `.m-focus-ring-kbd` (champs texte, où `:focus-visible` se déclenche aussi à la souris) +* [#MUI-42] Anneau « combo » : quand un dropdown / calendrier est ouvert (Autocomplete, Select, SelectCheckbox, Date), l'anneau entoure le champ **et** la liste / le calendrier d'un seul tenant, adapté au sens d'ouverture (utilitaires `.m-combo-ring-top` / `.m-combo-ring-bottom`) +* [#MUI-42] Navigation clavier WAI-ARIA APG sur les listes déroulantes : Select et SelectCheckbox gagnent la navigation (flèches, Home/End, Entrée/Espace, Échap, Tab — absente jusque-là), avec scroll automatique de l'option active et `aria-activedescendant` ; InputAutocomplete complété (scroll auto, ArrowUp ouvre sur la dernière option, Home/End, Tab ferme) +* [#MUI-42] SelectCheckbox : la ligne « Tout sélectionner » est intégrée à la navigation clavier ; le clic sur toute la ligne d'option (et plus seulement le label) coche/décoche +* [#MUI-42] InputUpload : prop `clearable` (croix `mdi:close` focusable qui vide le champ + event `clear`) et ouverture du sélecteur de fichier au clavier (Entrée / Espace) +* [#MUI-42] Famille Date : ouverture du calendrier au clavier (Entrée / Espace), fermeture par Échap ### Changed +* DataTable : libellés de pagination en français — `Préc.` / `Suiv.` (étaient `Prev` / `Next`) ; aria-labels déjà en français inchangés. * MalioButton : dimensions par défaut `w-[180px]` / `h-[38px]` (étaient `w-[200px]` / `h-[40px]`). * DataTable : tailles par défaut revues — texte header `16px` (était `20px`), texte body `14px` (était `18px`), sélecteur de lignes et boutons de pagination (Prev / numéros / Next) alignés à `30px` de haut, padding de `12px` entre le bas du tableau et la barre de pagination, texte header et body passés en noir (`text-black`, étaient `text-m-primary`). * Select : nouvelle prop `fieldClass` pour surcharger les classes du field (notamment la hauteur `h-[40px]` jusqu'ici codée en dur) ; utilisée par le DataTable pour passer le sélecteur de perPage à `30px`. * [#MUI-35] Refonte du composant drawer : slots `#header`/`#footer`, prop `side` (droite/gauche), `dismissable`, `closeOnEscape`, classes d'override, focus-trap, scroll-lock et fermeture au clavier. **Breaking** : la prop `title` est remplacée par le slot `#header`. +* [#MUI-42] Button / ButtonIcon : l'anneau de focus passe du halo `ring-2 ring-m-primary/50` à l'anneau standard `.m-focus-ring` (outline plein, offset 2px), pour l'homogénéité avec les autres composants. ### Fixed * DataTable : pagination réalignée verticalement après l'introduction du `min-h-[1rem]` du Select — la barre pagination passe en `items-center`, et le MalioSelect du sélecteur de perPage est encapsulé dans un wrapper `h-12` qui borne sa taille flex à la hauteur du field (le slot vide déborde invisiblement en dessous). Span « Lignes : » et boutons Prev/Page/Next sont désormais centrés exactement sur le field (y=24) @@ -61,3 +72,6 @@ Liste des évolutions de la librairie Malio layer UI * InputAutocomplete : suppression de 4 sources de saut visuel au focus / ouverture (extra translate label, padding `grow-height:focus`, `focus:pl-[11px]`, `!border-b-0` remplacé par `!border-b-transparent`) * Select / SelectCheckbox : mêmes correctifs anti-saut (suppression du padding `grow-height:focus` et remplacement de `!border-b-0` / `!border-t-0` par leurs variantes `transparent`) * MalioButton : largeur par défaut alignée sur `w-[200px]` (au lieu de `w-[240px]`) pour correspondre au sizing des formulaires de l'app +* [#MUI-42] RadioButton : ajout d'un focus visible au clavier (`outline` 2px `m-primary`, offset 2px) — l'input en `appearance-none` n'avait aucun indicateur de focus, seul l'`outline: auto 1px` du navigateur restait, quasi invisible. La navigation native (Tab entre groupes, flèches dans le groupe) reste inchangée +* [#MUI-42] Checkbox : ajout d'un focus visible au clavier sur la case (`outline` 2px `m-primary`, offset 2px) — l'input réel est masqué (`clip-path`), aucun indicateur n'apparaissait à la tabulation +* [#MUI-42] Select : le focus reste sur le bouton après sélection (un `blur()` renvoyait le focus au `body`, cassant la tabulation clavier — un Tab repartait du haut de page) diff --git a/COMPONENTS.md b/COMPONENTS.md index fcc7332..805579d 100644 --- a/COMPONENTS.md +++ b/COMPONENTS.md @@ -4,6 +4,8 @@ Tous les composants sont auto-importés avec le préfixe `Malio`. Utiliser `v-mo > **Champ obligatoire :** sur les composants de formulaire, la prop `required` ajoute un astérisque rouge dans le label. C'est un repère visuel ; la sémantique « obligatoire » est portée par l'attribut natif `required` ou `aria-required`. +> **Focus clavier :** tous les champs et contrôles affichent un anneau de focus (`outline` 2px `m-primary`, offset 2px) **uniquement** à la navigation clavier (Tab), jamais au clic souris. Sur les composants à dropdown/calendrier ouverts, l'anneau entoure le champ et la liste d'un seul tenant. Voir la note « Clavier » de chaque composant pour la navigation détaillée. + --- ## MalioInputText @@ -94,19 +96,25 @@ Champ email (`type="email"` + `inputmode="email"`) avec icône `mdi:email-outlin | `iconPosition` | `'left' \| 'right'` | `'right'` | Position de l'icône | | `iconSize` | `string \| number` | `24` | Taille icône | | `iconColor` | `string` | `'text-m-muted'` | Classe couleur icône | +| `addable` | `boolean` | `false` | Affiche un bouton `+` à droite qui émet l'event `add` (l'icône email passe à gauche) | +| `addIconName` | `string` | `'mdi:plus'` | Icône Iconify du bouton d'ajout | +| `addButtonLabel` | `string` | `'Ajouter une adresse email'` | aria-label du bouton d'ajout | | `inputClass` | `string` | `''` | Classes CSS input | | `labelClass` | `string` | `''` | Classes CSS label | | `groupClass` | `string` | `''` | Classes CSS conteneur | > **Sanitisation à la saisie :** tous les espaces sont supprimés automatiquement au fil de la frappe (sans masque). Avec `lowercase=true`, la valeur est également convertie en minuscules à la frappe. La validation du format (ex. présence d'un `@`) reste à la charge du parent via la prop `error` ou la couche de validation. -**Events :** `update:modelValue(value: string)` +**Events :** +- `update:modelValue(value: string)` +- `add()` — émis au clic du bouton `+` (uniquement si `addable`, non `disabled`, non `readonly`) ```vue + ``` --- @@ -194,7 +202,7 @@ Champ de saisie assistée (typeahead / combobox) : l'utilisateur tape pour filtr - `select(option: Option \| null)` — émis avec l'objet `Option` complet (utile pour récupérer aussi le `label`) - `create(value: string)` — émis quand `allowCreate=true` et que l'utilisateur valide une valeur libre -**Clavier :** `↓` / `↑` navigation, `Entrée` sélection (ou création), `Échap` ferme le dropdown. +**Clavier (WAI-ARIA APG) :** `↓` ouvre / option suivante, `↑` précédente (ou ouvre sur la dernière option si fermé), `Début`/`Fin`, scroll automatique de l'option active, `Entrée` sélection (ou création), `Échap` annule, `Tab` ferme. Anneau de focus clavier (combo champ + liste à l'ouverture). ```vue @@ -236,6 +244,8 @@ async function onSearchClients(query: string) { Champ montant avec icône devise (euro par défaut). +L'affichage est groupé à la française (`1 234 567,89` : espace pour les milliers, virgule décimale), mis à jour en temps réel pendant la saisie. La valeur émise (`modelValue`) reste une **chaîne numérique propre** (point décimal, sans espaces, ex. `'1234567.89'`). `maxLength` borne la longueur de cette chaîne propre (pas de l'affichage). + | Prop | Type | Défaut | Description | |------|------|--------|-------------| | `modelValue` | `string \| null` | `undefined` | Valeur (v-model) | @@ -251,6 +261,8 @@ Champ montant avec icône devise (euro par défaut). ```vue + + ``` --- @@ -352,16 +364,20 @@ Champ d'upload de fichier. | `label` | `string` | `''` | Label | | `accept` | `string` | `''` | Types de fichiers acceptés | | `displayIcon` | `boolean` | `true` | Afficher l'icône | +| `clearable` | `boolean` | `false` | Affiche une croix (`mdi:close`) focusable qui vide le champ quand un fichier est sélectionné | | `disabled` | `boolean` | `false` | Désactivé | | `readonly` | `boolean` | `false` | Champ en lecture seule (bordure noire, pas de focus bleu/grossissement, label/icône gris→noir selon rempli). | | `required` | `boolean` | `false` | Champ requis (astérisque rouge dans le label) | | `error` | `string` | `''` | Message d'erreur | | `reserveMessageSpace` | `boolean` | `true` | Réserve l'espace de la ligne message sous le champ (min-h) même sans message. `false` = la ligne ne prend de place que s'il y a un hint/error/success. | -**Events :** `update:modelValue(value: string)`, `file-selected(file: File)` +**Events :** `update:modelValue(value: string)`, `file-selected(file: File)`, `clear()` + +**Clavier :** `Entrée` / `Espace` ouvrent le sélecteur de fichier. La croix `clearable` est focusable (anneau clavier, `Entrée`/`Espace`). ```vue + ``` --- @@ -394,6 +410,8 @@ Liste déroulante. **Events :** `update:modelValue(value: string | number | null)` **Slots :** `icon` (icône dropdown custom) +**Clavier (WAI-ARIA APG) :** `↓`/`↑`/`Entrée`/`Espace` ouvrent ; liste ouverte → `↑↓` naviguent (scroll auto de l'option active), `Début`/`Fin`, `Entrée`/`Espace` sélectionnent, `Échap`/`Tab` ferment. Le focus reste sur le bouton après sélection. Anneau de focus clavier (combo bouton + liste à l'ouverture, adapté au sens haut/bas). + ```vue @@ -422,6 +440,8 @@ Liste déroulante multi-sélection avec checkboxes. **Events :** `update:modelValue(value: (string | number)[])` +**Clavier (WAI-ARIA APG) :** `↓`/`↑`/`Entrée`/`Espace` ouvrent ; liste ouverte → `↑↓` naviguent (scroll auto), `Début`/`Fin`, `Entrée`/`Espace` cochent/décochent l'option active (la liste **reste ouverte**), `Échap`/`Tab` ferment. La ligne « Tout sélectionner » est navigable au clavier. Le clic sur toute la ligne (pas que le label) coche/décoche. Anneau de focus clavier (combo bouton + liste à l'ouverture). + ```vue @@ -445,6 +465,8 @@ Case à cocher. **Events :** `update:modelValue(value: boolean)` +**Clavier :** `Espace` coche/décoche. Focus clavier visible sur la case (`outline` 2px `m-primary`). + ```vue @@ -468,6 +490,8 @@ Bouton radio (à utiliser en groupe avec le même `name`). **Events :** `update:modelValue(value: string | number | boolean | null)` +**Clavier :** comportement natif d'un groupe radio (options partageant le même `name`) — `Tab` / `Maj+Tab` entre/sort du groupe (1 seul arrêt par groupe), `↑↓←→` déplacent la sélection entre les options d'un même groupe. Focus clavier visible (`outline` 2px `m-primary`). + ```vue @@ -481,6 +505,8 @@ Sélecteur de date unique avec popover (grille de calendrier + vue mois/année). La valeur est une chaîne ISO `"YYYY-MM-DD"`. Cliquer un jour émet la date et ferme le popover. +Avec `editable`, l'utilisateur peut aussi taper la date au clavier. La valeur n'est émise qu'au blur (ou sur Entrée) si elle est valide et dans les bornes ; sinon le texte est conservé et le champ passe en erreur (`invalidMessage`). + | Prop | Type | Défaut | Description | |------|------|--------|-------------| | `modelValue` | `string \| null` | `undefined` | Date ISO `"YYYY-MM-DD"` (v-model) | @@ -497,15 +523,20 @@ La valeur est une chaîne ISO `"YYYY-MM-DD"`. Cliquer un jour émet la date et f | `min` | `string` | `undefined` | Date min `"YYYY-MM-DD"` (jours antérieurs désactivés) | | `max` | `string` | `undefined` | Date max `"YYYY-MM-DD"` (jours postérieurs désactivés) | | `clearable` | `boolean` | `true` | Affiche la croix d'effacement | +| `editable` | `boolean` | `false` | Autorise la saisie clavier `JJ/MM/AAAA` (masque maska, validation au blur) en plus du calendrier | +| `invalidMessage` | `string` | `'Date invalide'` | Message affiché quand la saisie clavier est invalide ou hors `min`/`max` | | `reserveMessageSpace` | `boolean` | `true` | Réserve l'espace de la ligne message sous le champ (min-h) même sans message. `false` = la ligne ne prend de place que s'il y a un hint/error/success. | | `inputClass` / `labelClass` / `groupClass` | `string` | `''` | Override des classes | **Events :** `update:modelValue(value: string | null)` +**Clavier :** `Entrée` / `Espace` ouvrent le calendrier, `Échap` ferme. Anneau de focus clavier (combo champ + calendrier à l'ouverture). La croix d'effacement est focusable. _(Comportement partagé par DateRange, DateTime, DateWeek via le shell CalendarField.)_ + ```vue + ``` --- diff --git a/app/assets/css/malio.css b/app/assets/css/malio.css index 2d5f716..0ab6772 100644 --- a/app/assets/css/malio.css +++ b/app/assets/css/malio.css @@ -2,6 +2,41 @@ @tailwind components; @tailwind utilities; +@layer components { + /* Anneau de focus clavier standard (navigation au Tab), invisible à la souris. + Deux déclencheurs, même rendu : + - .m-focus-ring → s'appuie sur :focus-visible natif. Pour les éléments + où :focus-visible se limite déjà au clavier (boutons, + onglets, tuiles, checkbox/radio…). + - .m-focus-ring-kbd → classe ajoutée en JS (via useKbdFocusRing) uniquement + quand le focus vient du clavier. Pour les champs texte, + où :focus-visible natif se déclenche aussi à la souris. + Le `:focus` sur .m-focus-ring-kbd élève la spécificité pour passer devant le + `outline-none` des inputs. */ + .m-focus-ring:focus-visible, + .m-focus-ring-kbd:focus { + outline: 2px solid rgb(var(--m-primary) / 1); + outline-offset: 2px; + } + + /* Anneau de focus clavier pour un combobox ouvert (input + liste) : l'anneau + entoure le bloc entier d'un seul tenant. L'input porte le contour haut+côtés, + la liste le contour côtés+bas ; la jonction (bas de l'input / haut de la liste) + reste sans contour pour un raccord sans couture. */ + .m-combo-ring-top { + box-shadow: + -2px 0 0 0 rgb(var(--m-primary) / 1), + 2px 0 0 0 rgb(var(--m-primary) / 1), + 0 -2px 0 0 rgb(var(--m-primary) / 1); + } + .m-combo-ring-bottom { + box-shadow: + -2px 0 0 0 rgb(var(--m-primary) / 1), + 2px 0 0 0 rgb(var(--m-primary) / 1), + 0 2px 0 0 rgb(var(--m-primary) / 1); + } +} + @layer base { :root { /* ── Globales ── */ diff --git a/app/components/malio/button/Button.vue b/app/components/malio/button/Button.vue index 11037c9..81f5e9f 100644 --- a/app/components/malio/button/Button.vue +++ b/app/components/malio/button/Button.vue @@ -84,7 +84,7 @@ const variantClasses = computed(() => { const mergedButtonClass = computed(() => twMerge( - 'inline-flex w-[180px] h-[38px] items-center justify-center gap-1 p-[10px] rounded-md text-base font-bold leading-[150%] transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-m-primary/50', + 'inline-flex w-[180px] h-[38px] items-center justify-center gap-1 p-[10px] rounded-md text-base font-bold leading-[150%] transition-colors duration-150 m-focus-ring', variantClasses.value, props.buttonClass, ), diff --git a/app/components/malio/button/ButtonIcon.vue b/app/components/malio/button/ButtonIcon.vue index 9534324..645d93e 100644 --- a/app/components/malio/button/ButtonIcon.vue +++ b/app/components/malio/button/ButtonIcon.vue @@ -52,7 +52,7 @@ const isFilled = computed(() => props.variant === 'filled') const mergedButtonClass = computed(() => twMerge( - 'inline-flex items-center justify-center rounded-md p-1 transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-m-primary/50', + 'inline-flex items-center justify-center rounded-md p-1 transition-colors duration-150 m-focus-ring', isFilled.value ? props.disabled ? 'bg-m-disabled text-white cursor-not-allowed' diff --git a/app/components/malio/checkbox/Checkbox.vue b/app/components/malio/checkbox/Checkbox.vue index 73ba5eb..bd950a7 100644 --- a/app/components/malio/checkbox/Checkbox.vue +++ b/app/components/malio/checkbox/Checkbox.vue @@ -180,6 +180,11 @@ const onChange = (event: Event) => { border-color: rgb(0, 0, 0); } +.inp-cbx:focus-visible + .cbx span:first-child { + outline: 2px solid rgb(var(--m-primary) / 1); + outline-offset: 2px; +} + .cbx span:first-child svg { position: absolute; top: 2px; diff --git a/app/components/malio/datatable/DataTable.vue b/app/components/malio/datatable/DataTable.vue index 00817ee..f2575d1 100644 --- a/app/components/malio/datatable/DataTable.vue +++ b/app/components/malio/datatable/DataTable.vue @@ -81,7 +81,7 @@