# Overlay System: Implementation This document provides an outline of all possible occurrences of overlays found in applications in general and thus provided by Lion. For all concepts referred to in this document, please read [Overlay System Scope](./OverlaySystemScope.md). ## Local and global overlay controllers Currently, we have a global and a local overlay controller, as two separate entities. Based on provided config, they handle all positioning logic, accessibility and interaction patterns. All of their configuration options will be described below as part of the _Responsive overlay_ section. ### Connection points and placement contexts It's currently not clear where the border between global and local overlays lie. They seem to be separated based on their 'dom connection point' (body vs 'page cursor'(usually invoker sibling)). However, there is no required relationship here: we can create a modal dialog from local context('page cursor') as well. Only, we would have a few concerns when creating global overlays from a local connection point: - Accessibility will be harder to implement. When wai-aria 1.0 needs to be supported, all siblings need to have aria-hidden="true" and all parents role="presentation". Not always straightforward in shadow dom. If we only need to support wai-aria 1.1, we could use aria-modal="true" on the element with role="dialog". (we basically need to test our supported browsers and screen readers for compatibility with aria-modal). - Stacking context need to be managed: the whole 'z-index chain' should win (it's a battle between parents in the hierarchy). This would require some complex code to cover all edge cases. - Side effects of parents adding transforms or clipping become a risk. This is hard to detect and 'counter'. When the dom connection point is 'body', content projection will not work, but a template that can be rendered without being dependent on its context will be required. There usually also is a correllation with their relative positioning context: invoker/other relative element for local(tooltip/popover/dropdown) vs. 'window/viewport level' for global (dialog/toast/sheet). For responsive overlays (see below for an elaborate explanation), we need to switch from global to local. When we switch the dom connection point, (think of rotating a mobile or tablet), we will loose the current focus, which can be an a11y concern. This can eventually be 'catched' by syncing the activeElement (we would only loose the screenreader active element (for instance, focused cell in table mode)) For maximum flexibility, it should be up to the developer to decide how overlays should be rendered, per instance of an overlay. ### Responsive overlay Based on screen size, we might want to switch the appearance of an overlay. For instance: an application menu can be displayed as a dropdown on desktop, but as a bottom sheet on mobile. Similarly, a dialog can be displayed as a popover on desktop, but as a (global) dialog on mobile. To implement such a flexible overlay, we need an 'umbrella' layer that allows for switching between different configuration options, also between the connection point in dom (global and local). Luckily, interfaces of Global and OverlayControllers are very similar. Therefore we can make a wrapping ResponsiveOverlayController. ### Configuration options for local and global overlays In total, we should end up with configuration options as depicted below, for all possible overlays. All boolean flags default to 'false'. Some options are mutually exclusive, in which case their dependent options and requirement will be mentioned. Note: a more generic and precise term for all mentionings of `invoker` below would actually be `relative positioning element`. ```text - {Element} elementToFocusAfterHide - the element that should be called `.focus()` on after dialog closes - {Boolean} hasBackdrop - whether it should have a backdrop (currently exclusive to globalOverlayController) - {Boolean} isBlocking - hides other overlays when mutiple are opened (currently exclusive to globalOverlayController) - {Boolean} preventsScroll - prevents scrolling body content when overlay opened (currently exclusive to globalOverlayController) - {Boolean} trapsKeyboardFocus - rotates tab, implicitly set when 'isModal' - {Boolean} hidesOnEsc - hides the overlay when pressing [esc] - {Boolean} hidesOnOutsideClick - hides the overlay when clicking next to it, exluding invoker. (currently exclusive to localOverlayController) - {String} cssPosition - 'absolute' or 'fixed'. TODO: choose name that cannot be mistaken for placement like cssPosition or positioningTechnique: https://github.com/ing-bank/lion/pull/61 - {TemplateResult} contentTemplate - {TemplateResult} invokerTemplate (currently exclusive to LocalOverlayController) - {Element} invokerNode (currently exclusive to LocalOverlayController) - {Element} contentNode (currently exclusive to LocalOverlayController) ``` These options are suggested to be added to the current ones: ```text - {Boolean} isModal - sets aria-modal and/or aria-hidden="true" on siblings - {Boolean} isGlobal - determines the connection point in DOM (body vs handled by user) TODO: rename to renderToBody? - {Boolean} isTooltip - has a totally different interaction - and accessibility pattern from all other overlays, so needed for internals. - {Boolean} handlesUserInteraction - sets toggle on click, or hover when `isTooltip` - {Boolean} handlesAccessibility - - For non `isTooltip`: - sets aria-expanded="true/false" and aria-haspopup="true" on invokerNode - sets aria-controls on invokerNode - returns focus to invokerNode on hide - sets focus to overlay content(?) - For `isTooltip`: - sets role="tooltip" and aria-labelledby/aria-describedby on the content - {Object} popperConfig - {String} placement - vertical/horizontal position to be supplied to `managePosition`. See https://github.com/ing-bank/lion/pull/61 for current api. Consists of a primary part (where the overlay is located relative from invoker) and secondary alignnment part (how the overlay 'snaps' to the perpendicular boundary of the invoker), separated via '-'. - primary : 'bottom' | 'top' | 'left' | 'right' | 'over' (this means the overlay will be positioned on top of the invoker. Think for instance of a select dropdown that opens a selected option on top of the invoker (default behavior of `