Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Graduated Proposals section #1167

Merged
merged 1 commit into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion site/src/components/navigation/navigation.astro
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const sortedMenuNodes = menuNodes.sort((a, b) => {
})

const groupedNodes = groupBy(sortedMenuNodes, (node) => node.frontmatter.menu)
const menuHeadings = ["Overview", "Documentation", "Active Proposals", "Non-active Proposals", "Research"]
const menuHeadings = ["Overview", "Documentation", "Graduated Proposals", "Active Proposals", "Non-active Proposals", "Research"]

// This is due to the component matrix being 2.2mb page size
const routesToNotPrefetch = ['/research/component-matrix']
Expand Down
1 change: 1 addition & 0 deletions site/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />
5 changes: 4 additions & 1 deletion site/src/pages/components/accordion.explainer.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
menu: Active Proposals
menu: Graduated Proposals
name: Exclusive Accordion (Explainer)
path: /components/accordion.explainer
pathToResearch: /components/accordion.research
Expand All @@ -16,6 +16,9 @@ layout: ../../layouts/ComponentLayout.astro
- [openui/open-ui#925](https://github.com/openui/open-ui/issues/925) (UA ability for user to over-ride exclusive accordions functionality)
- [w3c/html-aam#509](https://github.com/w3c/html-aam/issues/509) (describe grouping (and naming of the group) for exclusive accordions `<details name>`)
- PR [whatwg/html#9400](https://github.com/whatwg/html/pull/9400) (Add name attribute for grouping details elements into an exclusive accordion)
- [Specification](https://html.spec.whatwg.org/multipage/interactive-elements.html#attr-details-name)
- [MDN blog on Exclusive Accordions](https://developer.mozilla.org/en-US/blog/html-details-exclusive-accordions/)
- [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details#multiple_named_disclosure_boxes)

{/* START doctoc generated TOC please keep comment here to allow auto update */}
{/* DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE */}
Expand Down
258 changes: 258 additions & 0 deletions site/src/pages/components/future-invokers.explainer.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
---
menu: Active Proposals
name: Invoker Commands Future (Explainer)
layout: ../../layouts/ComponentLayout.astro
---

- Authors: [Keith Cirkel](https://github.com/keithamus), [Luke Warlow](https://github.com/lukewarlow)

**NOTE:** See the original explainer for the core proposal [here](/components/invokers.explainer).

{/* START doctoc generated TOC please keep comment here to allow auto update */}
{/* DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE */}

# Invoker Buttons

## Introduction

This document outlines potential future enhancements to [Invoker Commands](/components/invokers.explainer).

All commands noted here are ideas only and are not included in the initial release of this feature.

### Defaults

Depending on the target set by `commandfor`, invoking the button will trigger
additional behaviours alongside the event dispatch, depending on the value of
`command`. The following table represents ideas on how built-in invocations on specific
element types are handled. These need further design on exactly how
they will behave based on implications such as accessibility, security,
interactivity, and how the button may need to respond to such actions.

| Invokee Element | `command` hint | Behaviour |
| :-------------------- | :--------------------- | :------------------------------------------------------------------------------------------------------------- |
| `<dialog>` | `'request-close'` | If the `<dialog>` is `open`, request to close and use the button `value` for returnValue. Similar to `.requestClose(value) |
| `<* openable>` | `'toggle-openable'` | Opens the `openable` if closed, otherwise closes. Similar to `.toggleOpenable()` |
| `<* openable>` | `'close-openable'` | Closes the `openable` if open, otherwise does nothing. Similar to `.closeOpenable()` |
| `<* openable>` | `'open-openable'` | Opens the `openable` if closed, otherwise does nothing. Similar to `.openOpenable()` |
| `<details>` | `'toggle'` | If the `<details>` is `open`, then close it, otherwise open it |
| `<details>` | `'open'` | If the `<details>` is not `open`, then open it |
| `<details>` | `'close'` | If the `<details>` is `open`, then close it |
| `<dialog>` | `'toggle'` | If the `<dialog>` is `open`, then close it and use the button `value` for returnValue, otherwise open as modal |
| `<select>` | `'show-picker'` | Shows the native picker. Similar to `.showPicker()` on the invokee |
| `<input>` | `'show-picker'` | Shows the native picker. Similar to `.showPicker()` on the invokee |
| `<video>` | `'play-pause'` | If the video is not playing, plays the video. Otherwise pauses it. Similar to `el.playing = !el.playing` |
| `<video>` | `'pause'` | If the video is playing, pause the video. Similar `.playing = false` |
| `<video>` | `'play'` | If the video is not playing, play the video. Similar to `.playing = true` |
| `<video>` | `'toggle-muted'` | If the video is muted, it unmutes the video, otherwise it mutes it. Similar to `el.muted = !el.muted` |
| `<audio>` | `'play-pause'` | If the audio is not playing, plays the audio. Otherwise pauses it. Similar to `el.playing = !el.playing` |
| `<audio>` | `'pause'` | If the audio is playing, pause the audio. Similar `.playing = false` |
| `<audio>` | `'play'` | If the audio is not playing, play the audio. Similar to `.playing = true` |
| `<audio>` | `'toggle-muted'` | If the audio is muted, it unmutes the audio, otherwise it mutes it. Similar to `el.muted = !el.muted` |
| `<*>` | `'toggle-fullscreen'` | If the element is fullscreen, then exit, otherwise request to enter |
| `<*>` | `'request-fullscreen'` | Request the element to enter into 'fullscreen' mode |
| `<*>` | `'exit-fullscreen'` | If the element is fullscreen, then exit |
| `<input type=number>` | `'step-up'` | Call `.stepUp()` on the invokee |
| `<input type=number>` | `'step-down'` | Call `.stepDown()` on the invokee |

> Note: Ideas are welcome. Please submit an issue if you have one!

Further to the initial ship we're also exploring implicit `command` or implicit
`commandfor` values where the value can easily be inferred.

### Accessibility

For built-in behaviours `command` attribute maps to specific accessibility
mappings which are placed on the button. The button may also use the
`commandfor` referenced element to gather other details (for example the state
of the element) to reflect that state on the button.

These mappings will happen implicitly on the browsers Accessible Nodes, and so
while (for simplicity) this section refers to various `aria-` attributes,
buttons will not sprout these attributes in the DOM, but the effective
equivalent will be exposed to Assistive Technologies.

#### Buttons with `command=request-close`

Buttons with this dialog command will implicitly receive `aria-details=IDREF`,
where `IDREF` matches that of the `commandfor` attribute, while the dialog is
in the showing state, and the button is not a descendant of the dialog.

<details>
<summary>Why?</summary>

A button that cancels a dialog is very typically found inside of the dialog, but
in some cases it may be found outside, perhaps as a "toggle" style button which
opens and closes a dialog as non-modal. This button may be used to close an open
dialog that is shown as non-modal. It may be useful for a user to traverse into
the dialog before closing it, for example to check if they have unsaved changes
within the dialog.

</details>

<br/><br/>

Buttons will also implicitly receive an `aria-expanded` value, if they are not a
descendant of the `commandfor=` referenced element. The state of the
`aria-expanded` value will map to the state of the dialog's openness. When the
dialog is open the button will have `aria-expanded="true"`, when closed,
`aria-expanded="false"`. This will be recomputed whenever the dialog changes
state, such that the button always reflects the state of openness.

<details>
<summary>Why?</summary>

A button that cancels a dialog is very typically found inside of the dialog, but
in some cases it may be found outside, perhaps as a "toggle" style button which
opens and closes a dialog as non-modal.

Buttons outside of the dialog may be used to close an open dialog that is shown
as non-modal. It may be useful for a user to traverse into the dialog before
closing it, for example to check if they have unsaved changes within the dialog.
It may also be useful to know if the dialog is already closed (as in its
`aria-expanded` state is false), as this may help the user make a decision to
whether or not they action the close button.

</details>

<br/><br/>

<details>
<summary>Other considerations not explicitly proposed.</summary>

##### aria-pressed

Given elements will have `aria-expanded`, adding `aria-pressed` would be
confusing or redundant, and as such won't be proposed for these buttons.

##### aria-controls

While `aria-controls` attempts to establish a similar style of relationship to
`aria-details`, `aria-details` sees broader support among various assistive
technologies, and it would be redundant to add both.

</details>

<br/><br/>

#### Other built-in `command=` types

Further built-in commands will be proposed on a case-per-case basis,
and additional aria or other logic will be considered with those at the time.

### Security

See the main [Invoker Commands explainer](/components/invokers.explainer#security) for an overview of the
security considerations.

**Note**: The security considerations for the proposed commands are not yet complete and will be specified in more detail
in the future.

Below is a brief summary of the security considerations for the commands proposed above:

#### Details

This proposal aims to allow opening and closing `<details>` elements with
`invoketarget`. This is not considered to be new capability, as it is already
possible to do this with the `<summary>` element.

#### Input

It should be noted that input pickers (like the file picker dialog) render native
controls outside of the bounds of the document frame (iframe or browser window).
Due to this, the security implications of invoking these elements should be
carefully considered.

Scripts can call `.showPicker()` on form elements, and the picker will open.
There are some cross-origin restrictions, for example calling `.showPicker()`
on elements in a cross-origin context only works for `<input type=file>` or
`<input type=color>`.

It is also possible to wrap an `<input>` in a `<label>`, where invoking the
label will open the picker. This is quite common practice when trying to style
`<input type=file>` for example; dressing the `<label>` to look like a
`<button>` and hiding the `<input>`.

This proposal allows showing the pickers of input elements, for example an
invoker can target an `<input type=file>` and clicking the invoker will open
the operating systems file picker dialog. As stated above, this is already
possible with `<label>` elements, but care should be taken to ensure that
the same cross-origin restrictions apply for Invokers as they do for scripting.

#### Fullscreen elements

Turning an element into a fullscreen needs to be carefully consdidered.
Scripting already allows this via the `.requestFullscreen()` API which, as the
name suggests, may not always be successful given the UA context. This API
requires the document context to be active, for example requiring a user
activation (this is true of invokers in the general case), it also requires
the Permissions Policy to allow for this behaviour, and requires the element to
not already be on the Top Layer.

All of these constraints should also be true for invokers opening fullscreen
elements. This means the only _significant_ new behavioural difference between
an invoker opening a fullscreen element vs the existing behaviour which
requires scripting, is that sanboxed iframes which disallow scripting but allow
fullscreen may now declare a button that can fullscreen. Consider the following:

```html
<iframe sandbox allow="fullscreen">
<html id="invokee">
<button type="button" commandfor="invokee" command="toggleFullscreen">Invoke</button>
<button onclick="invokee.requestFullscreen()">Go fullscreen</button>
</html>
</iframe>
````

In this example, the `<button onclick>` will do nothing as the iframe is
sandboxed and does not allow scripting. The `<button invoketarget>` will
fullscreen the element, however, as the `allow=fullscreen` Permission Policy is
enabled to allow fullscreen.

This is considered to be a low security threat, as it requires opt-in to the
fullscreen Permission Policy explitly via iframe attributes or scripting. We
will, however, continue to explore if this should be a possibility.

#### Media Elements

This proposal allows video and audio elements to be controlled with buttons
outside of the browsers native video controls. This includes playing, pausing,
and toggling mute/unmute. Today this is only possible using scripting via the
equivalent scripting APIs: `.play()` / `.pause()` / `.muted=`. These APIs are
guarded by Permissions Policy, and so should the invoker equivalent. Some User
Agents can be configured to reject calls to `.play()`, and this should also be
true of invokers. As such the key new capability here is the ability to call
these APIs without scripting enabled. This is effectively the same concern as
fullscreen; the security model is guarded around the Permissions Policy.

There is also additional concern around the media element invokers being able
to circumvent autoplay policies. Invokers should not be able to circumvent
these. By design, invokers require a user interaction (clicking the button)
and so this should not circumvent autoplay policies.

#### Further proposals...

There are continued suggestions for new capabilities of Invokers, such as
[invoking picture-in-picture](https://github.com/openui/open-ui/issues/916).

Each of these will need to be taken into consideration on a case-per-case
basis. The following questions will need to be answered for each of these:

- Is it possible for the user to do this today with scripting?
- Is it possible for the user to do this today without scripting?
- Does this behaviour bypass scripting sandboxing rules, such as
Permissions Policy or iframes?
- Does this enable buttons to invoke content that appears outside of the
browser frame?

### PAQ (Potentially Asked Questions)

#### Do we have to always supply both? Can't we make `command` or `commandfor` implicit?

The original proposal had the concept of an "auto" `command` value which would
determine an explicit command based on various heuristics, such as the target
element. This has been deferred for the initial ship, but may be explored
further. This is considered out of scope for the initial ship, however.

We may also explore the possibility of making `commandfor` implicit, for example
if a button is a descendant of a dialog, omitting `commandfor` may make sense.
This is also considered out of scope for the initial ship.
Loading