diff --git a/research/src/pages/popup/popup.proposal.mdx b/research/src/pages/popup/popup.proposal.mdx new file mode 100644 index 000000000..7e10a57e1 --- /dev/null +++ b/research/src/pages/popup/popup.proposal.mdx @@ -0,0 +1,420 @@ +--- +menu: Proposals +name: Popup (Editor's Draft) +path: /components/popup +pathToResearch: /components/popup.research +--- + +import '../../styles/spec.css' + +**Editors:** + +- Mason Freed (Google) +- Melanie Richards (Microsoft) + +## HTML Incubation Text: Popup + +_Target venue: [HTML Living Standard](https://html.spec.whatwg.org/multipage/)_ + +
+
+ Categories: +
+
+ Flow content. +
+
+ Sectioning root. +
+
+ Contexts in which this element can be used: +
+
+ Where flow content is expected. +
+
+ Content model: +
+
+ Flow content. +
+
+ Content attributes: +
+
+ Global attributes +
+
+ anchor — Creates an id-reference relationship with another element, which is used + for both anchor positioning and popup stack management +
+
+ delegatesfocus — Whether to move focus to the popup's first focusable element when + the popup is shown +
+
+ initiallyopen — Whether the popup is shown upon document load +
+
+ Accessibility considerations: +
+
+ Pending specification in ARIA in HTML and{' '} + HTML-AAM. +
+
+ +

+ Add IDL to frontmatter (#356) +

+ +A `popup` element represents transient application UI that “pops up” in the +[top layer](https://fullscreen.spec.whatwg.org/#top-layer) of a web document, enabling the user to +perform some task in a timely and immediate fashion. `popup` is similar to the `dialog` element, but +is distinguished by its transient nature (“light dismiss” behaviors) and mutual exclusivity: only +one `popup` element may be rendered at a time, with the exception of nested `popup`s. + +

+ Examples of popups include: button menus, listboxes used for item selection e.g. comboboxes, + "teaching" UI, and other progressive-disclosure widgets or content pickers. +

+ +The `open` IDL attribute, on getting, must return the last value to which it was set. On setting, +it must be set to the new value. When the element is created, it must be set to `false`. + +## Showing a `popup` element + +A `popup` element and its contents must not be rendered unless: + +- The `initiallyopen` attribute is specified +- The `show()` method is invoked +- Or the `popup` element has been invoked through user interaction with an element whose `popup` + attribute references the `popup` element's ID + +This requirement may be implemented indirectly through the style layer. For example, +user agents that +[support the suggested default rendering](https://html.spec.whatwg.org/multipage/infrastructure.html#renderingUA) +implement this requirement using the CSS rules described in the +[Rendering section](https://html.spec.whatwg.org/multipage/rendering.html#rendering). + + + +This specification introduces a new stacking layer in the document +[top layer](https://www.w3.org/TR/CSS2/zindex.html), called the **popup stack**. Each document has +one popup stack. + +The popup stack is an [ordered set](https://infra.spec.whatwg.org/#ordered-set) of elements, +rendered in the order they appear in the set. The last element in the set is rendered last, +and thus appears on top. + +Showing or hiding a `popup` element adds or removes the element from the popup stack, respectively. + +### The `initiallyopen` attribute + +The `initiallyopen` attribute is a +[boolean attribute](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attribute). +When specified, it indicates that the popup element will be shown upon document load, such that the +user can interact with it. + +**Example:** + +```html + +``` + +When a `popup` element becomes +[browsing-context connected](https://html.spec.whatwg.org/#becomes-browsing-context-connected), the +user agent must run the following steps, given a popup element _candidate subject_: + +1. If the `initiallyopen` content attribute is specified on the _candidate subject_, run the + [showing a `popup` element steps](#showing-popup-steps). +2. Otherwise, set the _candidate subject_'s _open state_ to `false`. + +

+ As a result of following this run step: in the event that initiallyopen is specified on multiple + popups, the last of these popups (with the initiallyopen attribute) in the DOM order will be the + popup that is rendered upon document load. +

+ +### The `show()` method + +The `show()` method renders the `popup` element, such that the user can interact with it. + +**Example:** + +```html + +

+ New! + I’m some sort of educational UI… +

+
+ + +``` + +The show() method steps are to run the [showing a `popup` element steps](#showing-popup-steps) with +[this](https://heycam.github.io/webidl/#this). + +### The `popup` attribute + +The `popup` attribute indicates that a related `popup` element should be shown or hidden as the +user interacts with the element where the `popup` attribute is specified. If the attribute is +specified, the attribute's value must be the ID of a `popup` element in the same +[node document](https://dom.spec.whatwg.org/#concept-node-document). + +**Example:** Invoking the `button` element in this example should show/hide the `popup` element, +depending on whether or not the `popup` element is currently shown: + +```html + + + + +``` + +The `popup` attribute is supported on a subset of +[interactive elements](https://html.spec.whatwg.org/multipage/interactive-elements.html): + +- `button` +- `input` in the `button` state +- `input` in the `email`, `number`, `search`, `tel`, `text`, or `url` states + +#### Showing/hiding a `popup` element via the `popup` attribute + +When an element with the `popup` attribute specified receives a user interaction that should show +the related `popup` element, this element shall be known as the `popup` element's _invoker_. + +

+ Multiple elements may have a popup attribute that refers to the same popup element. A given popup + element only has 0 or 1 invokers at a given time. For example, if the popup element was shown + programmatically, the invoker is null. Otherwise, the element that the user actually interacted + with to show the popup is its sole invoker. +

+ +If the _invoker_ is a `button` element or an `input` element in the `button` state, and the +_invoker_ is activated; OR the _invoker_ is an input in the `email`, `number`, `search`, `tel`, +`text`, or `url` states and focus is set to the _invoker_, the user agent must run the following +steps: + +1. Let _subject_ be the first element in tree order with the ID referenced by the `popup` attribute + specified on the _invoker_. If there is no such element, or the _subject_ is not a `popup` + element, then return. +2. Get the _subject_'s _open state_. +3. If _subject_'s _open state_ is `true`, run + [hiding a `popup` element steps](#hiding-current-popups), with _subject_ and _invoker_. +4. Otherwise, run [showing a `popup` element steps](#showing-popup-steps) with _subject_ and _invoker_. + +

+ Should we have a way to suppress these steps upon focus, so that the author can enable the popup + to be shown upon text entry (or some other logic) instead? +

+ +

+ Showing a popup element steps +

+ +The **showing a `popup` element steps**, given a popup element _candidate subject_ and an optional +element _invoker_, and an optional element _anchor element_, are: + +1. Run the [hiding currently-shown `popup` elements steps](#hiding-current-popups), with the + _candidate subject_, _invoker_ if it was given, and _anchor element_ if it was given. +2. Add the _candidate subject_ to _candidate subject_'s + [node document](https://dom.spec.whatwg.org/#concept-node-document)'s [popup stack](#popup-stack). +3. Set the _candidate subject_'s _open state_ to `true`. +4. If the `popup` element was shown as a result of a user interaction with an `invoker`, + set the _candidate subject_'s _invoker_ to _invoker_. +5. Let _focusableArea_ be the result of [getting the focusable area + steps](https://html.spec.whatwg.org/multipage/interaction.html#get-the-focusable-area) given _candidate subject_. +6. Run the [focusing steps](https://html.spec.whatwg.org/multipage/interaction.html#focusing-steps) given _focusableArea_. + +

+ Without the presence of the autofocus or delegatefocus attribute, focus remains on the active + element. This behavior is to enable scenarios where the popup is used in a composite control. For + example, a combobox where the user expects their focus to stay in the text input instead of moving + automatically to the listbox popup as and when it appears. +

+ +## Setting initial focus with the `autofocus` attribute + +The [autofocus](https://html.spec.whatwg.org/multipage/interaction.html#the-autofocus-attribute) +content attribute allows the author to indicate that an element is to be focused as soon as the +page is loaded or as soon as the `popup` within which it finds itself is shown. + +When specified on the `popup` element, it indicates that when the `popup` element is shown, focus +moves to the `popup` element. + +**Example:** + +```html + +

The popup itself will receive focus.

+ + +
+``` + +## Anchoring a `popup` to another element + +Many popups have a visual-logical relationship with another element that may not be a part of the +`popup` element's ancestry chain. For example, a `button` may control a `popup` element, and the +`popup` needs to be positioned with respect to this `button`. Or the `popup` element may simply be +used to draw attention to or describe another element. The `anchor` attribute indicates this +relationship. When specified, the attribute's value must be the ID of an element in the same +[node document](https://dom.spec.whatwg.org/#concept-node-document) as the `popup` element. + +**Example:** + +```html + + +

+ New! + I’m some sort of educational UI… +

+
+``` + +The target of the `anchor` attribute participates in determining whether a candidate `popup` element +is "nested" in a currently-shown `popup`; refer to hiding +currently-shown popup elements steps. + +

+ Whenever there is a spec to point to for CSS anchored positioning, call out here that the anchor + attribute is used to help resolve CSS anchor() functions. +

+ +## Hiding a `popup` element + +### The `hide()` method + +The `hide()` method hides a `popup` element. The `hide()` method steps are to run the +[hiding a `popup` element steps](#hiding-popup-steps) with +[this](https://heycam.github.io/webidl/#this). + +### Light dismissal + +"Light dismiss" describes the behavior of a component which dismisses itself automatically when a +user performs an action that implies that they are finished interacting with the component. The +actions that trigger light dismiss are: + +- The user presses the escape key. +- A focus change occurs (because of either user interaction or script), where the focus target is + outside of the contents of the component. This includes the case where the user invokes a + non-focusable element, which causes focus to jump to the nearest focusable ancestor of that element. +- An OS-level focus change occurs such that the window containing the component no longer has focus. + For example, the user switches to a different browser tab or switches to a different application. + +When a `popup` element is shown and a light dismiss interaction occurs: + +1. If a keypress event for the `ESCAPE` key was fired, run [hiding a `popup` element + steps](#hiding-popup-steps), with the _candidate subject_ set to the top-most `popup` element in + the [popup stack](#pop-stack). +2. If a focus change occurs, the user agent must set the _start node_ to the element which received + the focus event and run [Hiding currently-shown `popup` elements steps](#hiding-current-popups). +3. Otherwise, the user agent must set the _start node_ to null and run [Hiding currently-shown + `popup` elements steps](#hiding-current-popups). + +

+ Hiding currently-shown popup elements steps +

+ +The **hiding currently-shown `popup` elements steps**, given an optional popup element +_candidate subject_, an optional element _invoker_, and an optional element _anchor element_, and an +optional element _start node_, are as follows. + +For each popup element _subject_ in the +[node document](https://dom.spec.whatwg.org/#concept-node-document)'s [popup stack](#popup-stack): + +1. If _candidate subject_ is null, run the + [hiding a `popup` element steps](#hiding-popup-steps), with _subject_ as _candidate subject_. +2. Otherwise, starting with the _candidate subject_, walk the + [flat tree](https://drafts.csswg.org/css-scoping/#flattening) to determine whether the _subject_ + is an ancestor to the _candidate subject_. If this condition is met, then return. +3. Otherwise, if the _invoker_ is not null, starting with the _invoker_, walk the + [flat tree](https://drafts.csswg.org/css-scoping/#flattening) to determine whether the _subject_ + is an ancestor to the _invoker_. If this condition is met, then return. +4. Otherwise, if the _anchor element_ is not null, starting with the _anchor element_, walk the + [flat tree](https://drafts.csswg.org/css-scoping/#flattening) to determine whether the _subject_ + is an ancestor to the _anchor element_. If this condition is met, then return. +5. If _start node_ is not null: +6. If the _start node_ is the _subject_, then return. +7. Otherwise, starting with the _start node_, walk the + [flat tree](https://drafts.csswg.org/css-scoping/#flattening) to determine whether the _subject_ + is an ancestor to the _start node_. If this condition is met, then return. +8. Otherwise, for this _subject_, run the + [hiding a `popup` element steps](#hiding-popup-steps), with _subject_ as _candidate subject_, and + optional element _invoker_. + +

+ Hiding a popup element steps +

+ +The **hiding a `popup` element steps**, given a `popup` element _candidate subject_ and an optional +element _invoker_, are as follows: + +1. Remove the _candidate subject_ from the + [node document](https://dom.spec.whatwg.org/#concept-node-document)'s [popup stack](#popup-stack). +2. Set the _candidate subject_'s _open state_ to `false`. +3. Set the _candidate subject_'s _invoker_ to _null_. +4. [Queue an element task](https://html.spec.whatwg.org/multipage/webappapis.html#queue-an-element-task) + on the [user interaction task source](https://html.spec.whatwg.org/multipage/webappapis.html#user-interaction-task-source) + given the _candidate subject_ element to + [fire an event](https://dom.spec.whatwg.org/#concept-event-fire) + named `hide` at _candidate subject_. + +--- + +## HTML Incubation Text: User Interaction > Focus > Processing Model + +

+ The following text to be inserted into the + + get the focusable area steps + . +

+ +**If _focus target_ is a `popup` element** + +1. If the `delegatesfocus` attribute is specified on _focus target_, return the first focusable + descendent element of _focus target_, in + [tree order](https://dom.spec.whatwg.org/#concept-tree-order), that is not inert. +2. If there is no such descendent, but `delegatesfocus` is specified, return the _focus target_. +3. Otherwise, if the `autofocus` attribute is specified on _focus target_, return the _focus target_. +4. Otherwise, if the `autofocus` attribute is specified on a descendent element of _focus target_, + return the first such descendent element of _focus target_, in + [tree order](https://dom.spec.whatwg.org/#concept-tree-order), that is not inert. +5. Otherwise, return. diff --git a/research/src/styles/spec.css b/research/src/styles/spec.css index dc942d04a..bbc20b7c6 100644 --- a/research/src/styles/spec.css +++ b/research/src/styles/spec.css @@ -1,3 +1,16 @@ +dl { + border: 1px solid rgba(0, 0, 0, 0.2); + padding: 1em; +} + +dd { + margin: 0 0 0 1em; +} + +dd + dt { + margin-top: .5em; +} + .question { background: #F7F781; position: relative; @@ -43,14 +56,4 @@ padding: 0 .5em; margin-right: .5em; font-weight: bold; -} - -.todo::before { - background: #c78f2c; - content: "Question"; - width: max-content; - color: white; - padding: 0 .5em; - margin-right: .5em; - font-weight: bold; -} +} \ No newline at end of file