slogan3

Help mij bij het implementeren van React Portalen voor geavanceerd focusbeheer en toegankelijkheid

description

Met deze prompt krijg je gerichte begeleiding om React Portalen effectief te gebruiken voor complexe UI-elementen waarbij toegankelijkheid en gebruikerservaring centraal staan. Het helpt je om focusbeheer en keyboard navigatie correct te implementeren, wat essentieel is voor gebruikers met beperkingen. Dit voorkomt veelvoorkomende valkuilen en zorgt voor een robuuste, toegankelijke interface die beter presteert dan standaard oplossingen.

prompt

Help mij bij het implementeren van React Portalen om complexe UI-elementen zoals interactieve modals, dialoogvensters of contextmenu's te renderen met geavanceerd focusbeheer en optimale toegankelijkheid. ...

try_prompt

generate_helper
disclaimerOnPageApi

ai_answers

provider: openai
model: gpt-4.1-nano
Natuurlijk! Hier is een uitgebreide gids voor het implementeren van React Portalen voor complexe UI-elementen zoals modale vensters, inclusief focus trap, keyboard navigatie en ARIA-rollen: 1. Gebruik React Portalen voor rendering: - Maak een aparte DOM-node (bijvoorbeeld `<div id="modal-root"></div>`) in je HTML. - Gebruik ReactDOM.createPortal() om je modaal in die node te renderen. ```jsx import { createPortal } from 'react-dom'; function Modal({ isOpen, onClose, children }) { if (!isOpen) return null; return createPortal( <div role="dialog" aria-modal="true" aria-labelledby="modalTitle" aria-describedby="modalDesc" tabIndex="-1" ref={modalRef}> {children} </div>, document.getElementById('modal-root') ); } ``` 2. Focus trap implementeren: - Gebruik een ref op het modale element. - Bij openen, focus de eerste focusbare element binnen de modal. - Voeg een keydown event listener toe om Tab en Shift+Tab te beheren, zodat focus binnen de modal blijft. - Bijvoorbeeld: ```jsx import { useRef, useEffect } from 'react'; function useFocusTrap(isOpen, onClose) { const modalRef = useRef(null); useEffect(() => { if (isOpen) { const focusableElements = modalRef.current.querySelectorAll( 'a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])' ); const firstElement = focusableElements[0]; firstElement.focus(); const handleKeyDown = (e) => { if (e.key === 'Tab') { const focusables = Array.prototype.slice.call(focusableElements); const index = focusables.indexOf(document.activeElement); if (e.shiftKey) { if (index === 0) { e.preventDefault(); focusables[focusables.length - 1].focus(); } } else { if (index === focusables.length - 1) { e.preventDefault(); focusables[0].focus(); } } } if (e.key === 'Escape') { onClose(); } }; document.addEventListener('keydown', handleKeyDown); return () => document.removeEventListener('keydown', handleKeyDown); } }, [isOpen, onClose]); return modalRef; } ``` 3. Keyboard navigatie: - Zorg dat alle interactieve elementen binnen de modal toegankelijk zijn via Tab en Shift+Tab. - Voeg eventueel extra toetsenbordintegraties toe afhankelijk van je behoeften. 4. ARIA-rollen en labels: - Gebruik `role="dialog"` of `role="alertdialog"` afhankelijk van het scenario. - Voeg `aria-modal="true"` toe voor schermlezercommunicatie. - Gebruik `aria-labelledby` en `aria-describedby` om de titel en beschrijving te koppelen: ```jsx <div role="dialog" aria-modal="true" aria-labelledby="modalTitle" aria-describedby="modalDesc" tabIndex="-1" ref={modalRef} > <h2 id="modalTitle">Titel van de modal</h2> <p id="modalDesc">Beschrijving of instructies</p> {/* rest van inhoud */} </div> ``` 5. Samenvatting: - Render je modal via React Portalen. - Beheer focus zodat deze automatisch naar de modal gaat en binnen de modal blijft (focus trap). - Zorg dat toetsenbordintegraties soepel verlopen. - Pas ARIA-rollen en labels toe voor toegankelijkheid. Wil je dat ik een compleet voorbeeld voor je opstel inclusief alle onderdelen?
provider: deepseek
model: deepseek-chat
Zeker! Ik help je bij het implementeren van een toegankelijk modaal venster met React Portalen. Hier is een complete handleiding: ## 1. Portal Setup voor Modaal Venster Eerst maak je een portal component: ```jsx // Portal.jsx import { useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; const Portal = ({ children }) => { const [portalElement, setPortalElement] = useState(null); useEffect(() => { const element = document.createElement('div'); element.setAttribute('id', 'modal-portal'); document.body.appendChild(element); setPortalElement(element); return () => { document.body.removeChild(element); }; }, []); if (!portalElement) return null; return createPortal(children, portalElement); }; export default Portal; ``` ## 2. Toegankelijk Modaal Component ```jsx // AccessibleModal.jsx import { useEffect, useRef, useCallback } from 'react'; import Portal from './Portal'; const AccessibleModal = ({ isOpen, onClose, title, children }) => { const modalRef = useRef(null); const previousActiveElement = useRef(null); // Focus management useEffect(() => { if (isOpen) { // Sla huidige focus element op previousActiveElement.current = document.activeElement; // Focus op modal zetten if (modalRef.current) { modalRef.current.focus(); } // Voorkom scrollen op achtergrond document.body.style.overflow = 'hidden'; } return () => { // Herstel scroll gedrag document.body.style.overflow = 'unset'; // Focus terugzetten naar vorig element if (previousActiveElement.current) { previousActiveElement.current.focus(); } }; }, [isOpen]); // Focus trapping const handleKeyDown = useCallback((event) => { if (!isOpen) return; const focusableElements = modalRef.current?.querySelectorAll( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' ); if (!focusableElements || focusableElements.length === 0) return; const firstElement = focusableElements[0]; const lastElement = focusableElements[focusableElements.length - 1]; switch (event.key) { case 'Tab': if (event.shiftKey) { if (document.activeElement === firstElement) { event.preventDefault(); lastElement.focus(); } } else { if (document.activeElement === lastElement) { event.preventDefault(); firstElement.focus(); } } break; case 'Escape': onClose(); break; default: break; } }, [isOpen, onClose]); // Click outside to close const handleBackdropClick = (event) => { if (event.target === event.currentTarget) { onClose(); } }; if (!isOpen) return null; return ( <Portal> {/* Backdrop */} <div className="modal-backdrop" onClick={handleBackdropClick} style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0, 0, 0, 0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 1000, }} > {/* Modal container */} <div ref={modalRef} role="dialog" aria-modal="true" aria-labelledby="modal-title" aria-describedby="modal-description" tabIndex={-1} onKeyDown={handleKeyDown} style={{ background: 'white', padding: '2rem', borderRadius: '8px', minWidth: '300px', maxWidth: '90vw', maxHeight: '90vh', overflow: 'auto', }} > {/* Titel */} <h2 id="modal-title" className="modal-title"> {title} </h2> {/* Beschrijving */} <div id="modal-description" className="sr-only"> Dialoogvenster voor gebruikerstoegang </div> {/* Inhoud */} <div className="modal-content"> {children} </div> {/* Actie knoppen */} <div style={{ marginTop: '1rem', display: 'flex', gap: '1rem', justifyContent: 'flex-end' }}> <button type="button" onClick={onClose} style={{ padding: '0.5rem 1rem' }} > Annuleren </button> <button type="submit" style={{ padding: '0.5rem 1rem' }} > Bevestigen </button> </div> </div> </div> </Portal> ); }; export default AccessibleModal; ``` ## 3. Gebruikers Toegang Modal Implementatie ```jsx // UserAccessModal.jsx import { useState } from 'react'; import AccessibleModal from './AccessibleModal'; const UserAccessModal = () => { const [isModalOpen, setIsModalOpen] = useState(false); const [formData, setFormData] = useState({ username: '', password: '', rememberMe: false }); const handleSubmit = (event) => { event.preventDefault(); // Verwerk login logica hier console.log('Form data:', formData); setIsModalOpen(false); }; const handleInputChange = (event) => { const { name, value, type, checked } = event.target; setFormData(prev => ({ ...prev, [name]: type === 'checkbox' ? checked : value })); }; return ( <div> <button onClick={() => setIsModalOpen(true)} aria-haspopup="dialog" > Gebruikerstoegang Beheren </button> <AccessibleModal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} title="Gebruikerstoegang Beheren" > <form onSubmit={handleSubmit}> <div style={{ marginBottom: '1rem' }}> <label htmlFor="username" style={{ display: 'block', marginBottom: '0.5rem' }}> Gebruikersnaam: </label> <input type="text" id="username" name="username" value={formData.username} onChange={handleInputChange} required aria-required="true" style={{ width: '100%', padding: '0.5rem' }} /> </div> <div style={{ marginBottom: '1rem' }}> <label htmlFor="password" style={{ display: 'block', marginBottom: '0.5rem' }}> Wachtwoord: </label> <input type="password" id="password" name="password" value={formData.password} onChange={handleInputChange} required aria-required="true" style={{ width: '100%', padding: '0.5rem' }} /> </div> <div style={{ marginBottom: '1rem' }}> <label> <input type="checkbox" name="rememberMe" checked={formData.rememberMe} onChange={handleInputChange} /> Onthoud mij </label> </div> </form> </AccessibleModal> </div> ); }; export default UserAccessModal; ``` ## 4. CSS voor Screen Reader Ondersteuning ```css /* screen-reader.css */ .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } /* Focus styles voor toegankelijkheid */ .modal-backdrop:focus-within { outline: 2px solid #0066cc; outline-offset: 2px; } button:focus, input:focus, select:focus { outline: 2px solid #0066cc; outline-offset: 2px; } ``` ## Belangrijke ARIA-attributen en Toegankelijkheidsfuncties: 1. **`role="dialog"`**: Identificeert het element als een dialoogvenster 2. **`aria-modal="true"`**: Gaat aan dat de modal de onderliggende content blokkeert 3. **`aria-labelledby`**: Koppelt de titel aan de modal 4. **`aria-describedby`**: Voorziet aanvullende beschrijving 5. **Focus trapping**: Houdt focus binnen de modal 6. **Escape-toets**: Sluit modal met Escape key 7. **Keyboard navigatie**: Volledige tab-ondersteuning ## Gebruik in je App: ```jsx // App.jsx import UserAccessModal from './components/UserAccessModal'; function App() { return ( <div className="App"> <h1>Mijn Applicatie</h1> <UserAccessModal /> </div> ); } export default App; ``` Deze implementatie zorgt voor een volledig toegankelijke modal met focus management, keyboard navigatie en ARIA-ondersteuning volgens WCAG richtlijnen.
error: Generation failed [deepseek] prompt_id=4073 locale=en err=DeepSeek connection/timeout | Generation failed [deepseek] prompt_id=4073 locale=es err=DeepSeek connection/timeout