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

Make it so dialog cannot be closed #385

Closed
BarFoo opened this issue Oct 16, 2022 · 13 comments
Closed

Make it so dialog cannot be closed #385

BarFoo opened this issue Oct 16, 2022 · 13 comments

Comments

@BarFoo
Copy link

BarFoo commented Oct 16, 2022

Describe what feature you'd like. Pseudo-code, mockups, or screenshots of similar solutions are encouraged!

I have a use case whereby a user must fill out a prompt before they can continue or close the dialog box. At the moment the dialog box can be closed and there appears to be no option to only allow closing programmatically.

What type of pull request would this be?

No response

Any links to similar examples or other references we should review?

No response

@endigo9740
Copy link
Contributor

endigo9740 commented Oct 16, 2022

Duplicate ticket to: #197

Note I've linked to this ticket to take this request into consideration. I have a few concerns in regards to the UX of this pattern:

  • What if the user doesn't have the answer to your prompt?
  • What if the user doesn't want to answer your prompt at that time?
  • Could this be abused for non-critical prompts?
  • The native window.prompt() doesn't offer this sort of functionality.

You typically don't want to lock users into a particular state that they have no power to progress for just standard activities. If this is onboarding, a simple required field in a form might be a better solution. If it's series of onboarding steps then I might recommend you review the locking mechanism for our Stepper component, it was built for this scenario.

However, if you still feel strongly that this require for your application, then free free to explain your reasoning in the locked ticket and I'll address this during the upcoming refactor to the Dialog system. Thanks!

@BarFoo
Copy link
Author

BarFoo commented Oct 17, 2022

Hi @endigo9740 and thanks for responding.

I'm developing a desktop app with Tauri + SvelteKit + Skeleton that requires the user to enter a password, which is used as part of a key for decrypting sensitive data from a local file. They can't access the data unless they enter their portion of the key, so it needs to force them to enter it or otherwise they can't access the desktop application anyway.

This is perhaps a rare case and I understand your concerns regarding UX, however in my opinion such responsibility lays with the developer using your library and not with you.

I've got this working now by using my own dialog, if you'd like I can submit a pull request?

Thanks

@endigo9740
Copy link
Contributor

endigo9740 commented Oct 17, 2022

I'll reiterate what I stated above, I don't think the requirement to enter data is an issue, that's standard practice through techniques such as required form fields. I'm just hesitant to support this through a modal specifically.

This is perhaps a rare case and I understand your concerns regarding UX, however in my opinion such responsibility lays with the developer using your library and not with you.

Unfortunately I have to disagree strongly here. First off, while I am the core maintainer, this is a community driven project. But we all have an incredibly important responsibility to create accessible interfaces. We never want to promote dark patterns. I'm not saying what you're doing is a dark pattern, but I lack enough info to make an informed decision on whether this is a good idea or not. Honestly it strikes me as something I would want to test and think through, rather than just rush in.

How about this, I bring this up to the core contrition team on Discord, so we can get some opinions beyond my own in here. This will happen publicly in our #contributors channel if you want to follow along. I'd encourage you chime in as well. But I'd like to put some forethought into this feature before it's introduced.

https://discord.com/channels/1003691521280856084/1003699523522142399

I'd say hold off on a PR for now. Given this feature is about to undergo a notable refactor, it's probably best to introduce it during that update.

@ryceg
Copy link
Contributor

ryceg commented Oct 17, 2022

This sort of use case is pretty common, but a dialog has to be escapable. The way that people usually get around this is by initiating a new dialog that essentially says "Are you sure? You cannot proceed without passing this step", and then kicks the user back to the previous step if they confirm. People like to be able to pick up and put down their applications, and dislike it when they demand attention- what happens in your current implementation if a user decides that they want to sign up with a different email address, or access a different file? What if they went to go get a burger, and then couldn't remember what they were doing in your app? By pushing the user back to the previous screen after a confirmation dialog you maintain user agency, but keep the stuff gated without making it inaccessible to assistive technologies.

@endigo9740
Copy link
Contributor

endigo9740 commented Oct 17, 2022

@ryceg Bar mentioned this is a Tauri app, which means desktop app built with web tech. Does assistive technology work the same as within a browser? I can't say I've ever heard one way or the other on that.

I agree with everything else though.

FYI I'm also reopening this for additional comment. We'll house discussion here rather than Discord, since the discussion moves fast there.

@endigo9740 endigo9740 reopened this Oct 17, 2022
@niktek
Copy link
Contributor

niktek commented Oct 17, 2022

Feels to me like a standard login screen. Can't access app without filling in form. Not sure why it would need to go to an undismissable dialog. But it sounds like there is a workaround available and I don't feel there is a use case for it in the standard skeleton library.

@endigo9740
Copy link
Contributor

endigo9740 commented Oct 17, 2022

I wanted to include the ARIA APG section for dialog and modals here:
https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/

I was struggling to put my finger on why I have hesitation with this, but I believe this bit towards the bottom helps:

image

Specifically:

Application code prevents all users from interacting in any way with content outside of it.

So following this, if the modal cannot be dismissed, then the user is deadlocked into the this state with no means to escape or move on. While this might not be harmful in your particular use case BarFoo, this is something that could potentially be harmful or misused by others.

We have to take that into account when building a library like this for a variety of different developers and end uses. Hopefully this makes sense!

@risalfajar
Copy link
Contributor

I understand your point, but let me explain my case, I think this is new

So I'm building a business app for a company, this app has lots of pages to show a table of data and an add button above it. For illustration:
image

When the user press the add button, the app will show a form modal with dozens of inputs. This modal can be closed by clicking on a close button or on its backdrop (so it doesn't harm UX, as you mentioned), except for two cases:

  1. There's unsaved data in it, in which case there'll be a confirmation dialog to prevent the user from accidentally clicking close button or backdrop.
  2. The app is currently saving the data, in which case a loading indicator appear, and the dialog shouldn't be able to be closed. The reason is so that if the save is failed, the data isn't loss.

Why not just create a separate page?
Going back and forth between pages isn't good for performance and UX, since the app will reload & re-render the data, and the user may add data several times.

You can see that by implementing this feature it can improve UX for some cases. I know there's a possibility of abuse, but any tools can be abused if not used right, and we can't blame the tools for that. So I don't think limiting the tool's functionality is the best decision.
I really hope you can reconsider this, thank you!

@endigo9740
Copy link
Contributor

I feel like between the original thread and this one, that this has been addressed:
#764

When the user press the add button, the app will show a form modal with dozens of inputs.

I'm sorry but this is an extremely poor use case for an overlay and generally why using modals for complex forms is considered bad UX. It leads to the situation you're describing, where data loss is possible. You're introducing this problem in the first place by using the wrong tool for the job.

I know there's a possibility of abuse, but any tools can be abused if not used right, and we can't blame the tools for that. So I don't think limiting the tool's functionality is the best decision.

My stance hasn't changed from the last time this point was raised. To speak plainly, we're not going harm accessibility and we're definitely not going to encourage dark patterns. Modals (read: dialogs) are not intended to be fixed overlays. Skeleton will not be supporting this in any capacity.

If someone absolutely requires this feature then you have a few options:

  • Build a version yourself to your own specs
  • Clone Skeleton's modal feature and modify as desired (the MIT license allows for this)
  • Find another/better way to present the form (recommended)

I've said my part, repeatedly now, so I will not respond to further messages on this topic or in this thread.

@Shackless
Copy link

Shackless commented Apr 8, 2024

I have another real-word use case:
A modal with settings. Inside of this modal, the user can click a "record key/macro" button. If triggered, an area inside of my modal goes into a "press your keyboard keys now..." state which should be cancellable by 'Esc'. Now (and only while in this state), I want the modal to not auto-close.

Yes, niché and I agress that should NOT be the default (maybe with a a11y warning) but it should be technically possible to unbind the explicit 'Esc' key handler that the Skeleton modal registers.

CleanShot 2024-04-08 at 22 21 13@2x

@ryceg
Copy link
Contributor

ryceg commented Apr 9, 2024

That does sound like a reasonable use case for overriding the default behaviour, but I'm not sure that it's a common enough one to warrant the a11y issues that come with misuse- for your use case, I would suggest copying the source code and implementing it as a custom component.

@polaroidkidd
Copy link

I'm incredibly grateful for all the hard work everyone has put, and continues to, into this project.

There's a bunch of tickets on this subject and the authors answers are always the same. It won't be implemented, we're welcome to fork it and do it our way.

As an alternative (and fairly ugly workaround) I've patched the Modal.svelte component. It removes the close calls from onBackdropInteractionEnd and onKeyDown and added the backdrop event dispatch for the onKeyDown function

diff --git a/dist/utilities/Modal/Modal.svelte b/dist/utilities/Modal/Modal.svelte
index 0f0c0d736fa51d0743f1b11d67d9e3536fc4e28c..e3bea8cd8654ee1ce2cf302923179d4578742ab0 100644
--- a/dist/utilities/Modal/Modal.svelte
+++ b/dist/utilities/Modal/Modal.svelte
@@ -87,7 +87,6 @@ function onBackdropInteractionEnd(event) {
   if ((classList.contains("modal-backdrop") || classList.contains("modal-transition")) && registeredInteractionWithBackdrop) {
     if ($modalStore[0].response)
       $modalStore[0].response(void 0);
-    modalStore.close();
     dispatch("backdrop", event);
   }
   registeredInteractionWithBackdrop = false;
@@ -115,8 +114,10 @@ function onPromptSubmit(event) {
 function onKeyDown(event) {
   if (!$modalStore.length)
     return;
-  if (event.code === "Escape")
-    onClose();
+  if (event.code === "Escape"){
+		dispatch('backdrop', event);
+	}
+
 }
 $:
   cPosition = $modalStore[0]?.position ?? position;

Next I created a backdropClickStore

import { getContext, setContext } from 'svelte';
import { type Writable, writable } from 'svelte/store';

import { STORE_CONTEXTS } from './constants';

export function initBackdropClickStore(): Writable<number> {
	const store = writable(0);
	setContext(STORE_CONTEXTS.BackdropClick, store);
	return store;
}

export function getBackdropClickStore(): Writable<number> {
	const store = getContext<Writable<number>>(STORE_CONTEXTS.BackdropClick);
	if (!store) {
		console.error('BackdropClick Store is not initialized');
	}

	return store;
}

When initializing the Modal component I implement the on:backdrop event and increase the count of the backdropClickStore

<script lang="ts">
    import { getBackdropClickStore } from "./backdrop-click-store"
    const backdropClickStore = getBackdropClickStore();
</script>

<Modal
	components={protectedModalComponentRegistry}
	regionBackdrop="backdrop-blur-lg"
	on:backdrop={(e) => {
		e.preventDefault();
		$backdropClickStore++;
	}}
/>

Then, in the various modals I get the backdropClickStore, check if the backdrop has been clicked and handle my logic there (either close the modal or not)

	const backdropClickStore = getBackdropClickStore();
	const initialBubbleId = $backdropClickStore;
	backdropClickStore.subscribe((value) => {
		if (value > initialBubbleId) {
			modalStore.close();
		}
	});

This is a quick-and-dirty V2 solution.

@endigo9740
Copy link
Contributor

endigo9740 commented Jan 3, 2025

There's a bunch of tickets on this subject and the authors answers are always the same. It won't be implemented, we're welcome to fork it and do it our way.

The issue is closed so I'd suggest we put this topic to bed, but just FYI this behavior is different in v3 - specifically for Svelte.

Initially we'll be offering the <Modal> component, which does allow you to control the backdrop behavior:
https://next.skeleton.dev/docs/integrations/popover/svelte#modal

See the API reference for Modal, specifically the closeOnInteractOutside property:
https://next.skeleton.dev/docs/integrations/popover/svelte#api-reference

As we state at the top of the Popover page, these components are a temporary solution until we can finish Floating UI Svelte. (which resumed updates this week). At which point you'll have full access to the primitive features to decide how any and all popovers operate in your app. It's still going to be our recommendation you follow a11y guidelines, but it's completely up to you.

As for Skeleton v2, we will end development on it when v3 launches later this month so there's really no further discussion to be had on this topic. v2 will remain as is, v3 will deliver multiple options to go outside what we feel is the recommended approach to modal backdrop behavior.

Folks will have the options requested here from v3 forward, so a solution for this issue has been fulfilled.

@skeletonlabs skeletonlabs locked and limited conversation to collaborators Jan 3, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants