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

Drawer Component Research/Proposal #1135

Open
quick007 opened this issue Dec 31, 2024 · 7 comments
Open

Drawer Component Research/Proposal #1135

quick007 opened this issue Dec 31, 2024 · 7 comments

Comments

@quick007
Copy link

quick007 commented Dec 31, 2024

Drawer Component

On mobile, it can be hard to reach for certain elements and engage with content such as dropdowns. Thus, drawers were created as a way to more easily access content. They have been used in iOS for many years and are starting to make their way to the web as well. Note that some platforms also call these "sheets". I've attached some examples along with a video at the bottom.

Concept

The concept of a drawer or sheet is a panel that slides up from the bottom with a medium or large detent. A medium detent takes up about half the screen while a large detent takes up most of the screen. The content behind the drawer is de-emphasized and scaled down a slight bit with a black border surrounding it.

This component is normally intended mainly for mobile use as an alternative to a dropdown or a traditional popover on larger screens. Developers implementing drawers normally show this component for smaller screens and then use CSS to hide it on larger screens. It can also function like the drawer by ant design if the attribute direction is set to "right" which makes it open from the right.

The Problem

While there are packages that implement this already, creating one from scratch is a significant challenge for developers. Having one that handles all the edge cases, animations, and works smoothly would be magical.

Proposed Autonomy

This structure is similar to the way it is implemented in Vaul but uses more native controls, similar to the popover API. I'm styling with tailwind here for brevity. ::background works the same as with a popup. I've also "passed" a data attribute with the name drawer-detent-(size) that can be used for styling, although I'm sure there's a better way to do that.

<drawer 
  id="my-drawer" 
  class="[&::backdrop]:bg-black/25 bg-gray-100 rounded-t-xl overflow-hidden data-[drawer-detent-large]:overflow-auto"
  defaultopen="false" <!-- specify detent that it is open rather than just true -->
  model="true" <!-- The modality of the dialog. When set to true, interaction with outside elements will be disabled and only dialog content will be visible to screen readers. -->
  direction="bottom" <!-- see comments for discussion on this, will use a CSS first solution instead -->
  locked="false" <!-- if true, cannot be dragged up/down to close -->
  repositionInputs="true" <!-- intelligently moves the dialog upwards above the keyboard and to ensure an <input /> can be seen when clicked on mobile />
>
  <h1>Title</h1>
  <!-- other content, can be whatever a dev wants -->
</drawer>

<button command="show" commandfor="my-drawer">
  Open drawer
</button>

It can also be controlled programmatically like so:

const drawer = document // ...

const open = drawer.isOpen();

drawer.showDrawer();
drawer.hideDrawer();
drawer.setDetent("medium");

There are also events based on when it's opened/closed. I would also like a way to be able to specify where/how many detents there are but I don't know if a way to do that easily with attributes. Maybe smth like detents="sm:100px, md:200px ..." or detents="sm:0.3, md:0.6 ..." where the numbers are the amount of screen that is covered? Also, there should be a way to specify which detents add a bit of margin around the backdrop screen (see shadcn for an example).

Some examples of drawers

Apple sheet at medium detent

Apple sheet at large detent

github.mp4

P.S. I'm not sure how the typical research process operates for this type of work, so please advise me on the best way to move forward! There is probably a lot more customizability here that could be done so please let me know what you think in the comments. The Vaul docs is a good starting place for figuring out how to best implement this.

@Link2Twenty
Copy link

Here is the material take on a bottom sheets and side sheets

@scottaohara
Copy link
Collaborator

scottaohara commented Jan 9, 2025

Sorry to be that guy, but unless this is a proposal for "standardizing" a custom drawer component (for the global design system effort) - i'm not seeing how this is different enough from a dialog to warrant a new element or redundant features to dialog/popovers. However, while I'm not seeing a strong argument for why a new element is needed, I acknowledge that there might be / likely are some proposals for certain behaviors/features that could spin out of this to help make dialogs easier to present in this way.

You have some things in the proposed anatomy for this that are interesting, that I have some comments on.

  1. the direction attribute - this seems like reintroducing a variant of the obsolete center element, or obsolete align / valign attributes. A custom drawer component could be positioned with CSS, and could arguably have a default/overwritable position based on viewport size/orientation, to help have a reasonable default.

  2. with the command / commandfor attributes, a dialog can be opened as modal without the need for an attribute, so that's probably not necessary.

  3. the drawertitle and drawerdescription attributes would be far more useful as more generic global attributes, as there are use cases for needing to do this beyond the narrow context of a "drawer" (which per your own comment in the anatomy - it's a modal dialog).

There is an effort in the ARIA working group to have article and dialog elements get an implicit accName from the first descendent heading element. That implicit behavior would negate the need for a 'drawertitle' attribute. Though, a more generic attribute to indicate specific content is a name or description for a specific element that can be named/described might be useful. Replicating aria-labelledby and aria-describedby but as native HTML attributes. I had proposed a description element or attribute while working on the customizable select, but that was deemed scope creep. I should write a proposal...


what might be good here is identifying - beyond visual / animation / positioning - what is unique about this proposal that distinguishes it from a dialog? Are these attributes / features that could be added to dialog to fill the gap, rather than recreate dialog but just with some modified styling / positioning and animation behaviors?

for instance, a dialog can contain anything - would a drawer be different in that it doesn't allow authors to add anything - but commonly is used more for a collection of controls (buttons, links, options?) which may be introduced by a heading / paragraph of description - but otherwise other content is not allowed/recommended?

just spit balling here to try and semantically differentiate a drawer from a dialog - though in the a11y api these are commonly just dialogs.

@quick007
Copy link
Author

Thanks for the feedback!

what might be good here is identifying - beyond visual / animation / positioning - what is unique about this proposal that distinguishes it from a dialog?

This is a very good point, and I thought about this when I was writing it as well. From my view, those are the differentiators- the idea is this is a bespoke component that requires rigid restrictions. The magic of the drawer is the animations, movement, etc. are consistent and clean. By muddling responsibility with the dialog component, styles and attributes that might traditionally work with a dialog might not work when it's in "drawer" mode and vice versa.

There are also some apis that will only be available with drawers, like specifying a detent for example.

with the command / commandfor attributes, a dialog can be opened as modal without the need for an attribute, so that's probably not necessary.

I'll go ahead and remove that from my proposal then. I was unaware of this (admittedly super cool) API when I wrote this.

the direction attribute - this seems like reintroducing a variant of the obsolete center element, or obsolete align / valign attributes.

Agree here. I don't want to reinvent the wheel, and that was an oversight on my part. Much easier to do in pure CSS with some media queries. That being said, what would be the correct way to implement that in CSS? Something like top: 0; right: 0; bottom: 0;, or would drawer-position: top/left/bottom/right be better?

would a drawer be different in that it doesn't allow authors to add anything

Drawers would be able to contain anything, I should have been more clear with that (updated the original issue to reflect this)

Though, a more generic attribute to indicate specific content is a name or description for a specific element that can be named/described might be useful. Replicating aria-labelledby and aria-describedby but as native HTML attributes.

I agree here. More semantic HTML elements that just work as opposed to aria labels make a lot of sense, and developing a specific one for this feature alone doesn't.

@scottaohara
Copy link
Collaborator

scottaohara commented Jan 10, 2025

The magic of the drawer is the animations, movement, etc. are consistent and clean.
There are also some apis that will only be available with drawers, like specifying a detent for example.

it sounds to me then that there may be a property or attribute to add to dialog to get what it is your looking for - though it sounds to me that they're mostly visual / animation based. again that doesn't seem like a strong argument for a new element to make its way into HTML.

For some context, one reason why popover is an attribute and not an element is because depending on the way it was pitched, it was either redundant to the dialog element, or it was redundant to a div. i'm simplifying that a bit for brevity - but the point being is that if this is essentially just a dialog with some presentational tweaks, then it doesn't seem like it'd make the cut for a new element, since there is already precedent for not wanting to add elements that would be redundant to existing elements. popover as an attribute worked though, because it introduced new behaviors for existing semantic elements that could then be presented as popups.

Drawers would be able to contain anything, I should have been more clear with that (updated the original issue to reflect this)

imo, this weakens the argument this is semantically any different than dialog.

i could see some of the attributes you're proposing being renamed and becoming available on the dialog element to accomplish the same end goal though.

Like take your proposal and swap out drawer for dialog. what's really different then? the linked custom implementation is already a custom dialog anyway.

Some other things to think through (not to imply you should have had this all figured out to start - but stuff that would need to be addressed in some way to move forward):

  • how is someone who cannot use a mouse, or who has motor disabilities and thus can't drag easily supposed to achieve different detents? i would have expected a focusable element, or keyboard command to achieve this.
  • have you considered how one would navigate to the drawer if it's not modal? i looked at one of the vaul examples that was non-modal, and after invoking it, i couldn't tell how to get to the drawer without mashing the tab key until i finally reached it. The example at https://vaul.emilkowal.ski/ looks like it should be modal since it takes up the whole screen, but keyboard focus doesn't move to it, and thus there are some wcag violations.
    • It'd be good think about what could be done to mitigate such issues - e.g., making sure focus is automatically moved to the drawer - or an option exists to allow a keyboard user to immediately jump to it, if not automatically (as there are use cases where someone might want to open a drawer/dialog, but just read it and not move keyboard focus to it)
    • it'd also be good to think about if there should be some sort of auto-dismissal or some other way to make sure that if a drawer is not modal, that someone's keyboard focus doesn't get obscured by it.

@quick007
Copy link
Author

quick007 commented Jan 13, 2025

it sounds to me then that there may be a property or attribute to add to dialog to get what it is your looking for

My main concern is that drawers would contain additional attributes and different behaviors than dialogs. Is it proper behavior for an attribute to also enable the existence of other attributes? Drawers can also be modal or non-modal depending on the detent (the apple maps app is a good example of how this functions), and this would function override the the current modal/non-modal aria labels.

I believe I understand much better now why this should just be a part of drawer (thanks for your example with the popover), but I want to confirm that it's the best way forward as opposed to a new element. If the two examples I mentioned above are ok, then I'll go ahead and rewrite them to be a part of the dialog API.

how is someone who cannot use a mouse, or who has motor disabilities and thus can't drag easily supposed to achieve different detents? i would have expected a focusable element, or keyboard command to achieve this.

Why not shift + alt + uparrow/downarrow to switch between detents, then Esc to close the modal.

have you considered how one would navigate to the drawer if it's not modal

It's not ideal for non-model content to capture focus (though model ones always should) automatically. Similar to the way a user navigates detents, they can also use shift + alt + rightarrow/leftarrow to cycle between their focus on the main content and the open drawer(s). Ideally, devs only have one of these open, but my understanding is that the dialog API can have multiple anyways. Also, another question is how does the dialog element handle this. I was looking at the accessibility notes on MDN but I didn't see anything about focusing non-modal dialogs.

I haven't had a chance to fully update the original issue yet, but I'll do it once I hear back from you about the dialog question.

btw, do you have any comment on the CSS positioning I discussed in my previous comment?

@scottaohara
Copy link
Collaborator

Is it proper behavior for an attribute to also enable the existence of other attributes?

i wouldn't think about it that way. but an attribute might necessitate other attributes be specified by the developer, unless there are defaults that can be inferred in the absense of an attribute.

Drawers can also be modal or non-modal depending on the detent

i don't think that should be an issue. i'd also submit it's more about whether there is a backdrop that obscures the visible content that isn't otherwise obscured by the drawer/dialog. that's a clear modal signal, regardless of the detent. arguably a 100% detent drawer could be made modal as well.

but I want to confirm that it's the best way forward as opposed to a new element.

imo it makes more sense to add features to what exists, than duplicate what exists with a few extra features that are heavily based on presentation.

Why not shift + alt + uparrow/downarrow to switch between detents,

that could work for desktop web with a standard keyboard - sure. but we also have to account for other scenarios. e.g., devices that can render web content but don't provide a full keyboard. e.g., none of those keys exist on an ios on-screen keyboard. i'm not saying an answer needs to be given now. but it should be an open question of what could be done (which could be identifying the different ways different platforms could provide this functionality - OR suggesting that user agents provide a customizable (styleable) control to allow for this behavior).

It's not ideal for non-model content to capture focus ... automatically.

in many situations, sure. but not always. that's why we didnt' have popover automatically take focus / require devs to either use script or the autofocus attribute.

I was looking at the accessibility notes on MDN but I didn't see anything about focusing non-modal dialogs

yup - there is a gap here. one that there was brief conversation about resolving. but my point here being is that if this was a new element, it'd be another instance of needing to account for it. but if reusing the dialog element - then at least it would maintain status quo.

btw, do you have any comment on the CSS positioning I discussed in my previous comment?

nothing at this time. beyond that i would think an author could setup their detents to be the values they specify, and then an author could use CSS to make sure the drawer behaves as necessary based on viewport size / device orientation. otherwise, if this was an attribute, then someone might need to mutate the attribute if they wanted the drawer to be positioned differently at a different viewport size. but doing it this way, they could just write the CSS for it and leave the attribute alone.

@quick007
Copy link
Author

Sorry for the long wait and thanks for all the feedback! When I get a chance, I'll rewrite this issue from the ground up and base it on the dialog element. As I have conversed with you and thought more about this element, there are a lot of design decisions that I need to tweak and update- especially with regards to detents. Those were more or less an afterthought in my original issue.

Maybe I should come to one of the meetings that openUI meetings and ask them about it as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants