×
Test popUp
This is a test popUp that can be closed with the Escape key.
Press Escape to close this popUp
Zendriver Test Implementation
### Basic Browser Automation in Python Source: https://github.com/stephanlensky/zendriver/blob/main/docs/quickstart.md This Python example shows how to start a browser, navigate to a webpage, retrieve HTML content, save a screenshot, and close the browser. It depends on the zendriver library and asyncio. Input is a URL string; outputs include page content and a screenshot file. It may not work in all environments due to browser dependencies. ```python import asyncio import zendriver as zd async def main(): browser = await zd.start() page = await browser.get('https://example.com') # get HTML content of the page as a string content = await page.get_content() # save a screenshot await page.save_screenshot() # close the browser window await browser.stop() if __name__ == '__main__': asyncio.run(main()) ``` -------------------------------- ### Start Browser with Custom Options in Python Source: https://github.com/stephanlensky/zendriver/blob/main/docs/quickstart.md This Python code starts a browser with custom settings like headless mode, user data directory, executable path, arguments, and language. It depends on zendriver. Inputs are configuration parameters; outputs a browser instance. User data dir prevents auto-cleanup, which may lead to disk space issues. ```python import zendriver as zd browser = await zd.start( headless=False, user_data_dir="/path/to/existing/profile", # by specifying it, it won't be automatically cleaned up when finished browser_executable_path="/path/to/some/other/browser", browser_args=['--some-browser-arg=true', '--some-other-option'], lang="en-US" # this could set iso-language-code in navigator, not recommended to change ) tab = await browser.get('https://somewebsite.com') ``` -------------------------------- ### Advanced Multi-Page Browser Control in Python Source: https://github.com/stephanlensky/zendriver/blob/main/docs/quickstart.md This Python snippet illustrates opening multiple pages and tabs, selecting elements, flashing them, scrolling, and page management. It requires zendriver and asyncio. Inputs are URLs; outputs include manipulated page states. Limitations include potential issues with JavaScript-heavy sites. ```python import asyncio import zendriver as zd async def main(): browser = await zd.start() page = await browser.get('https://zendriver.dev/') elems = await page.select_all('*[src]') for elem in elems: await elem.flash() page2 = await browser.get('https://twitter.com', new_tab=True) page3 = await browser.get('https://github.com/ultrafunkamsterdam/nodriver', new_window=True) for p in (page, page2, page3): await p.bring_to_front() await p.scroll_down(200) await p # wait for events to be processed await p.reload() if p != page3: await p.close() if __name__ == '__main__': asyncio.run(main()) ``` -------------------------------- ### Install and Run Basic Zendriver Script Source: https://context7.com/stephanlensky/zendriver/llms.txt Demonstrates how to install Zendriver via pip and execute a simple asynchronous Python script to start a browser, navigate to a URL, retrieve HTML content, take a screenshot, and clean up resources. ```python # Install via pip # pip install zendriver import asyncio import zendriver as zd async def main(): # Start browser with default settings browser = await zd.start() # Navigate to a page page = await browser.get('https://example.com') # Get HTML content content = await page.get_content() print(content) # Take a screenshot await page.save_screenshot('example.png') # Clean up await browser.stop() if __name__ == '__main__': asyncio.run(main()) ``` -------------------------------- ### Configure Browser Settings via Config Object in Python Source: https://github.com/stephanlensky/zendriver/blob/main/docs/quickstart.md This Python snippet uses the Config class to set browser options equivalently. It requires zendriver. Inputs are config properties; outputs a configured setup. Similar to direct options, specifying user_data_dir avoids cleanup, potentially causing persistent data. ```python import zendriver as zd config = zd.Config() config.headless = False config.user_data_dir="/path/to/existing/profile", # by specifying it, it won't be automatically cleaned up when finished config.browser_executable_path="/path/to/some/other/browser", config.browser_args=['--some-browser-arg=true', '--some-other-option'], config.lang="en-US" # this could set iso-language-code in navigator, not recommended to change ``` -------------------------------- ### Python: Automate Imgur Screenshot Upload with Zendriver Source: https://github.com/stephanlensky/zendriver/blob/main/docs/quickstart.md This Python script uses Zendriver to automate uploading a screenshot of a GitHub page to Imgur. It navigates to Imgur, takes a screenshot of a specified GitHub repository, uploads the screenshot, fills in a title, and extracts the resulting shareable link. Requires the 'zendriver' and 'pathlib' libraries. ```python import asyncio from pathlib import Path import zendriver as zd DELAY = 2 async def main(): browser = await zd.start() tab = await browser.get("https://imgur.com") # now we first need an image to upload, lets make a screenshot of the project page save_path = Path("screenshot.jpg").resolve() # create new tab with the project page temp_tab = await browser.get( "https://github.com/ultrafunkamsterdam/undetected-chromedriver", new_tab=True ) # wait page to load await temp_tab # save the screenshot to the previously declared path of screenshot.jpg (which is just current directory) await temp_tab.save_screenshot(save_path) # done, discard the temp_tab await temp_tab.close() # accept goddamn cookies # the best_match flag will filter the best match from # matching elements containing "consent" and takes the # one having most similar text length consent = await tab.find("Consent", best_match=True) await consent.click() # shortcut await (await tab.find("new post", best_match=True)).click() file_input = await tab.select("input[type=file]") await file_input.send_file(save_path) # since file upload takes a while , the next buttons are not available yet await tab.wait(DELAY) # wait until the grab link becomes clickable, by waiting for the toast message await tab.select(".Toast-message--check") # this one is tricky. we are trying to find a element by text content # usually. the text node itself is not needed, but it's enclosing element. # in this case however, the text is NOT a text node, but an "placeholder" attribute of a span element. # so for this one, we use the flag return_enclosing_element and set it to False title_field = await tab.find("give your post a unique title", best_match=True) print(title_field) await title_field.send_keys("undetected zendriver") grab_link = await tab.find("grab link", best_match=True) await grab_link.click() # there is a delay for the link sharing popup. # let's pause for a sec await tab.wait(DELAY) # get inputs of which the value starts with http input_thing = await tab.select("input[value^=https]") my_link = input_thing.attrs.value print(my_link) await browser.stop() if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Set Asyncio Event Loop Policy for Windows in Python Source: https://github.com/stephanlensky/zendriver/blob/main/docs/quickstart.md This Python code sets the WindowsSelectorEventLoopPolicy for better asyncio compatibility on Windows. It depends on asyncio and sys. No direct inputs/outputs; affects event loop behavior. Limited to Windows platforms only. ```python import asyncio import sys if sys.platform == "win32": asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) ``` -------------------------------- ### Configure Zendriver Browser Startup Options Source: https://context7.com/stephanlensky/zendriver/llms.txt Shows two methods for configuring browser startup parameters in Zendriver. Method 1 passes arguments directly to the `start()` function, while Method 2 utilizes a `zd.Config` object for more granular control over settings like headless mode, user data directory, and browser arguments. ```python import asyncio import zendriver as zd async def main(): # Method 1: Pass arguments directly to start() browser = await zd.start( headless=False, user_data_dir="/path/to/profile", browser_executable_path="/usr/bin/chromium", browser_args=['--window-size=1920,1080', '--disable-gpu'], sandbox=True, lang="en-US", user_agent="Mozilla/5.0 Custom Agent" ) # Method 2: Use Config object for more control config = zd.Config() config.headless = True config.user_data_dir = "/tmp/browser_profile" config.browser_args = ['--incognito', '--disable-blink-features=AutomationControlled'] browser2 = await zd.Browser.create(config) await browser.stop() await browser2.stop() if __name__ == '__main__': asyncio.run(main()) ``` -------------------------------- ### Install Zendriver Package using Shell Source: https://github.com/stephanlensky/zendriver/blob/main/README.md This shell script installs the Zendriver package via pip, with comments listing alternative package managers. It requires a Python environment with pip installed. Outputs: Installs the Zendriver module system-wide, enabling its import in Python scripts. ```sh pip install zendriver # or uv add zendriver, poetry add zendriver, etc. ``` -------------------------------- ### Automate Browser Navigation with Zendriver Python Source: https://github.com/stephanlensky/zendriver/blob/main/README.md This Python script demonstrates starting a browser with Zendriver, navigating to a URL, and saving a screenshot. It depends on the zendriver package, asyncio, and Chrome browser. Inputs: A URL string; Outputs: A PNG screenshot file named 'browserscan.png'. ```python import asyncio import zendriver as zd async def main(): browser = await zd.start() page = await browser.get("https://www.browserscan.net/bot-detection") await page.save_screenshot("browserscan.png") await browser.stop() if __name__ == "__main__": asyncio.run(main()) ``` -------------------------------- ### Handling File Upload and Download with Zendriver (Python) Source: https://context7.com/stephanlensky/zendriver/llms.txt This example demonstrates uploading files via input elements and downloading with expectation patterns using Zendriver. It requires Zendriver, asyncio, os, and base64; inputs are file paths, outputs include saved screenshots and downloaded files. Limitations may arise from site-specific upload/download behaviors and file size handling. ```python import asyncio import os import base64 import zendriver as zd async def main(): browser = await zd.start() page = browser.main_tab # File upload example await page.get("https://imgur.com/upload") # Find file input element file_input = await page.select("input[type=file]") # Send file to input await file_input.send_file("/path/to/image.png") # Wait for upload await page.wait(2) # File download example with expect pattern await page.get("https://translate.yandex.com/en/ocr") # Upload an image first await (await page.select('input[type="file"]')).send_file("/path/to/document.jpg") await asyncio.sleep(3) # Handle download with context manager async with page.expect_download() as download_ex: # Trigger download action await (await page.select("#downloadButton")).mouse_click() # Wait for download to complete download = await download_ex.value print(f"Downloaded: {download.suggested_filename}") # Save downloaded file output_dir = "./downloads" with open(os.path.join(output_dir, download.suggested_filename), "wb") as f: bytes_file = base64.b64decode(download.url.split(",", 1)[-1]) f.write(bytes_file) await browser.stop() if __name__ == '__main__': asyncio.run(main()) ``` -------------------------------- ### Listen to CDP Console Message Events Source: https://github.com/stephanlensky/zendriver/blob/main/docs/tutorials/cdp.md This Python example demonstrates how to listen to CDP events, specifically console messages. It prints the console messages received from the browser. Requires the runtime domain to be enabled. ```python # Listen to console messages async def on_console_message(event): print(f"Console message: {event['message']['text']}") await page.on("Runtime.consoleAPICalled", on_console_message) ``` -------------------------------- ### Monitoring Network Traffic and Intercepting Requests with Zendriver (Python) Source: https://context7.com/stephanlensky/zendriver/llms.txt This code sets up event handlers to monitor outgoing requests and incoming responses using Zendriver's CDP integration. It depends on Zendriver and asyncio; inputs are network events, outputs are printed request/response details. Limitations include no modification of requests in this basic handler setup. ```python import asyncio from zendriver import cdp, start async def request_handler(event: cdp.network.RequestWillBeSent): """Handle outgoing requests""" request = event.request print(f"REQUEST: {request.method} {request.url}") for key, value in request.headers.items(): print(f" {key}: {value}") async def response_handler(event: cdp.network.ResponseReceived): """Handle incoming responses""" response = event.response print(f"RESPONSE: {response.status} {response.url}") print(f" Content-Type: {response.mime_type}") async def main(): browser = await start() tab = browser.main_tab # Register event handlers for network events tab.add_handler(cdp.network.RequestWillBeSent, request_handler) tab.add_handler(cdp.network.ResponseReceived, response_handler) # Navigate to trigger network events await browser.get("https://www.google.com") # Wait for events to process await tab await tab.sleep(2) await browser.stop() if __name__ == '__main__': asyncio.run(main()) ``` -------------------------------- ### Manipulate Browser Elements with Zendriver Source: https://context7.com/stephanlensky/zendriver/llms.txt Demonstrates advanced element interactions like getting attributes, properties, HTML, positioning, mouse events, flashing, scrolling, and taking screenshots. It also covers direct value setting, clearing input fields, and querying child elements within a parent element. Requires the zendriver library. ```python import asyncio import zendriver as zd async def main(): browser = await zd.start() page = await browser.get("https://example.com") # Find element link = await page.select("a") # Get element attributes href = link.attrs.href class_name = link.attrs.get("class", "") print(f"Link href: {href}, class: {class_name}") # Get JavaScript properties js_attrs = await link.get_js_attributes() inner_text = js_attrs.innerText # Get element HTML html = await link.get_html() print(f"Element HTML: {html}") # Element positioning position = await link.get_position() if position: print(f"Position: x={position.x}, y={position.y}") # Mouse interactions await link.mouse_move() # Move mouse to element await link.mouse_click(button="left", click_count=1) # Flash element for debugging (visual highlight) await link.flash(duration=0.5) # Scroll element into view await link.scroll_into_view() # Take screenshot of element only await link.save_screenshot("element.png") # Set value directly via JavaScript input_field = await page.select("input") await input_field.set_value("new value") # Clear input field await input_field.clear_input() # Query elements within element child_elements = await link.query_selector_all("span") await browser.stop() if __name__ == '__main__': asyncio.run(main()) ``` -------------------------------- ### Download grocery list as text file Source: https://github.com/stephanlensky/zendriver/blob/main/tests/sample_data/groceries.html Creates and downloads a text file containing grocery items. The function uses the Blob API to generate a file and creates a temporary link to trigger the download. No external dependencies required. ```javascript function downloadGroceryList() { const groceries = [ "Apples (42)", "Bananas", "Carrots", "Donuts", "Eggs", "French Fries", "Grapes" ]; const blob = new Blob([groceries.join('\n')], { type: 'text/plain' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'grocery_list.txt'; document.body.appendChild(a); a.click(); document.body.removeChild(a); } ``` -------------------------------- ### Execute Custom CDP Commands with Zendriver Source: https://context7.com/stephanlensky/zendriver/llms.txt Shows how to directly use low-level Chrome DevTools Protocol (CDP) commands for advanced browser control. This includes enabling domains, manipulating network conditions, setting geolocation, custom page navigation with referrers, and retrieving performance metrics. Requires the zendriver library. ```python import asyncio from zendriver import cdp, start async def main(): browser = await start() tab = browser.main_tab # Enable DOM domain await tab.send(cdp.dom.enable()) # Get document root node doc = await tab.send(cdp.dom.get_document()) root_node = doc.root # Custom network configuration await tab.send(cdp.network.enable()) await tab.send(cdp.network.set_cache_disabled(cache_disabled=True)) # Emulate network conditions (throttling) await tab.send(cdp.network.emulate_network_conditions( offline=False, latency=100, # ms download_throughput=750 * 1024, # bytes/s upload_throughput=250 * 1024 )) # Set geolocation await tab.send(cdp.emulation.set_geolocation_override( latitude=37.7749, longitude=-122.4194, accuracy=100 )) # Custom page navigation with referrer await tab.send(cdp.page.navigate( url="https://example.com", referrer="https://google.com" )) # Get performance metrics await tab.send(cdp.performance.enable()) metrics = await tab.send(cdp.performance.get_metrics()) for metric in metrics.metrics: print(f"{metric.name}: {metric.value}") await browser.stop() if __name__ == '__main__': asyncio.run(main()) ``` -------------------------------- ### Manage Browser Tabs and Windows with Zendriver Source: https://context7.com/stephanlensky/zendriver/llms.txt Covers the management of multiple browser windows and tabs, including opening new tabs and windows, iterating through them, activating tabs, scrolling, retrieving window information, resizing, setting window states (maximize, minimize, fullscreen), tiling windows, closing tabs, and bringing the main tab to the front. Requires the zendriver library. ```python import asyncio import zendriver as zd async def main(): browser = await zd.start() # Open multiple tabs tab1 = await browser.get("https://example.com") tab2 = await browser.get("https://github.com", new_tab=True) tab3 = await browser.get("https://google.com", new_window=True) # Iterate through all tabs for tab in browser.tabs: await tab.activate() print(f"Tab URL: {tab.url}") await tab.scroll_down(100) # Get window information window_id, bounds = await tab3.get_window() print(f"Window position: {bounds.left}, {bounds.top}") print(f"Window size: {bounds.width}x{bounds.height}") # Resize and position window await tab3.set_window_size( left=0, top=0, width=1920, height=1080 ) # Window states await tab3.maximize() await asyncio.sleep(1) await tab3.minimize() await asyncio.sleep(1) await tab3.fullscreen() # Tile windows in grid await browser.tile_windows(max_columns=3) # Close specific tabs await tab2.close() # Main tab reference main = browser.main_tab await main.bring_to_front() await browser.stop() if __name__ == '__main__': asyncio.run(main()) ``` -------------------------------- ### Zendriver Page Navigation and JavaScript Execution Source: https://context7.com/stephanlensky/zendriver/llms.txt Demonstrates various page navigation techniques and JavaScript execution within a browser controlled by Zendriver. This includes opening pages in new tabs or windows, navigating back/forward, reloading, executing arbitrary JavaScript code, scrolling the page, and managing tabs. ```python import asyncio import zendriver as zd async def main(): browser = await zd.start() # Open page in current tab page = await browser.get('https://example.com') # Open in new tab page2 = await browser.get('https://github.com', new_tab=True) # Open in new window page3 = await browser.get('https://google.com', new_window=True) # Navigate back/forward await page.back() await page.forward() await page.reload() # Execute JavaScript title = await page.evaluate("document.title") user_agent = await page.evaluate("navigator.userAgent") print(f"Title: {title}, UA: {user_agent}") # Scroll page await page.scroll_down(200) await page.scroll_up(100) # Bring tab to front await page2.bring_to_front() # Close specific tab await page2.close() await browser.stop() if __name__ == '__main__': asyncio.run(main()) ``` -------------------------------- ### Capture Screenshots and Export PDFs with Zendriver (Python) Source: https://context7.com/stephanlensky/zendriver/llms.txt This Python code using Zendriver automates taking full-page screenshots, base64 screenshots, custom viewport screenshots, PDF exports with various options, MHTML snapshots, and element-specific screenshots. It requires Python 3.10+, asyncio, and the zendriver library for asynchronous browser control. Inputs are URLs and optional file paths; outputs include image files, base64 strings, PDFs, and MHTML files. Limitations include dependency on browser rendering and potential timeouts for dynamic content. ```python import asyncio import zendriver as zd async def main(): browser = await zd.start() page = await browser.get("https://example.com") # Full page screenshot (PNG) await page.save_screenshot("full_page.png") # Screenshot as base64 string screenshot_b64 = await page.screenshot_b64() # Screenshot with custom viewport await page.set_window_size(width=1920, height=1080) await page.save_screenshot("viewport_1920x1080.png") # Export page as PDF pdf_path = await page.print_to_pdf( "document.pdf", landscape=False, display_header_footer=True, print_background=True, scale=1.0, paper_width=8.5, # inches paper_height=11.0, margin_top=0.4, margin_bottom=0.4, margin_left=0.4, margin_right=0.4 ) print(f"PDF saved to: {pdf_path}") # Save page as MHTML snapshot await page.save_snapshot("page_snapshot.mhtml") # Element-specific screenshot button = await page.select("button") await button.save_screenshot("button.png") await browser.stop() if __name__ == '__main__': asyncio.run(main()) ``` -------------------------------- ### Implement Wait Conditions and Timeouts with Zendriver (Python) Source: https://context7.com/stephanlensky/zendriver/llms.txt This Python code demonstrates Zendriver's wait strategies for dynamic content, including time-based waits, element appearance waits with timeouts, page ready state checks, custom conditions, and XPath queries. It uses Python 3.10+, asyncio, and the zendriver library for stealthy browser automation. Inputs are URLs and optional timeout values; outputs are found elements or exceptions. Limitations include reliance on browser state and potential failures for heavily dynamic pages without proper awaits. ```python import asyncio import zendriver as zd async def main(): browser = await zd.start() page = await browser.get("https://example.com") # Wait for specific time await page.wait(2.5) # 2.5 seconds await page.sleep(1) # Alternative syntax # Wait for element to appear (with timeout) try: # find() and select() automatically retry until timeout submit_button = await page.find("Submit", timeout=10) print("Button found!") except Exception as e: print(f"Element not found within timeout: {e}") # Wait for page ready state await page.wait_for_ready_state("complete") # Wait for custom condition async def condition_check(): result = await page.evaluate("document.readyState === 'complete'") return result await page.wait_for(condition_check, timeout=10) # Await tab to ensure references are updated await page # Process pending events # Wait for network idle (custom implementation) await page.wait(3) await page.evaluate("performance.getEntries().length") # XPath with timeout elements = await page.xpath("//button[@type='submit']", timeout=5) await browser.stop() if __name__ == '__main__': asyncio.run(main()) ``` -------------------------------- ### Customizing User Agent and Browser Fingerprint with Zendriver (Python) Source: https://context7.com/stephanlensky/zendriver/llms.txt This snippet customizes the browser's user agent, language, and platform to mimic a specific device and avoid detection. It requires the Zendriver library and asyncio. Inputs include custom user agent strings; outputs are verified via JavaScript evaluation, with limitations on full fingerprint evasion depending on site checks. ```python import asyncio import zendriver as zd async def main(): browser = await zd.start() tab = browser.main_tab # Set custom user agent, language, and platform await tab.set_user_agent( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", accept_language="de-DE", platform="Win32" ) # Verify changes user_agent = await tab.evaluate("navigator.userAgent") language = await tab.evaluate("navigator.language") platform = await tab.evaluate("navigator.platform") print(f"User Agent: {user_agent}") print(f"Language: {language}") print(f"Platform: {platform}") # Navigate to target site await tab.get("https://www.browserscan.net/bot-detection") await tab.save_screenshot("bot_detection_test.png") await browser.stop() if __name__ == '__main__': asyncio.run(main()) ``` -------------------------------- ### Zendriver Element Selection and Interaction with Web Pages Source: https://context7.com/stephanlensky/zendriver/llms.txt Illustrates how to select HTML elements using text matching and CSS selectors, and then interact with them. This includes filling form fields, clicking buttons, and waiting for page loads using Zendriver's asynchronous capabilities. ```python import asyncio import zendriver as zd async def main(): browser = await zd.start() page = await browser.get('https://github.com/login') # Find element by text (best match finds most similar text length) login_btn = await page.find("Sign in", best_match=True) # Find by CSS selector (single element) username_field = await page.select("input[name='login']") password_field = await page.select("input[name='password']") # Find all elements matching selector all_links = await page.select_all("a") # Fill form fields await username_field.send_keys("myusername") await password_field.send_keys("mypassword") # Click button await login_btn.click() # Wait for page to load await page await browser.stop() if __name__ == '__main__': asyncio.run(main()) ``` -------------------------------- ### Send CDP Command to Enable Runtime Domain Source: https://github.com/stephanlensky/zendriver/blob/main/docs/tutorials/cdp.md This Python snippet sends a CDP command to enable the runtime domain in Zendriver. It shows how to use CDP when Zendriver does not provide a specific API. Requires an active browser instance. ```python # Send a CDP command to enable the runtime domain await page.send_cdp_command("Runtime.enable") ``` -------------------------------- ### Managing Cookies for Session Persistence with Zendriver (Python) Source: https://context7.com/stephanlensky/zendriver/llms.txt This snippet handles saving, loading, and clearing cookies using Zendriver to maintain browser sessions across runs. It relies on Zendriver and asyncio; inputs are login credentials, outputs are printed cookie details. Limitations include profile directory management and site-specific cookie policies. ```python import asyncio import zendriver as zd async def main(): # Start browser with persistent profile browser = await zd.start(user_data_dir="./my_profile") # Login to a site page = await browser.get("https://example.com/login") username = await page.select("#username") password = await page.select("#password") await username.send_keys("myuser") await password.send_keys("mypass") await (await page.select("#submit")).click() # Wait for login await page.wait(2) # Save cookies to file await browser.cookies.save(".cookies.dat") await browser.stop() # Later session: restore cookies browser2 = await zd.start(user_data_dir="./my_profile") # Load cookies await browser2.cookies.load(".cookies.dat") # Now visit site with restored session page2 = await browser2.get("https://example.com/dashboard") # Get all cookies all_cookies = await browser2.cookies.get_all() for cookie in all_cookies: print(f"{cookie.name}: {cookie.value}") # Clear cookies await browser2.cookies.clear() await browser2.stop() if __name__ == '__main__': asyncio.run(main()) ``` -------------------------------- ### Create Escape Key Modal Dialog Interface Source: https://github.com/stephanlensky/zendriver/blob/main/tests/sample_data/special_key_detector.html Implements a complete modal dialog system with HTML structure and CSS styling. Features include responsive design, backdrop blur effects, and hover states for interactive elements. The modal supports focus management and visual feedback for open/closed states. ```HTML
Click the button below to open a popUp. Press Escape to close it.