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 child visible outside of overflow: hidden #4092

Open
OliverJAsh opened this issue Jul 8, 2019 · 34 comments
Open

Make child visible outside of overflow: hidden #4092

OliverJAsh opened this issue Jul 8, 2019 · 34 comments

Comments

@OliverJAsh
Copy link

OliverJAsh commented Jul 8, 2019

Suggestion

A way to force a child element to appear visible outside of a hidden overflow container.

Example use case

I have a modal element with rounded borders. Because this modal contains an image, I need to use overflow: hidden in order to mask the image inside of the modal element's border—so the image doesn't overflow the modal element's border.

image

This modal element contains another child element which should be allowed to overflow this element—an input with an autocomplete list. Unfortunately however, the overflow: hidden prevents all children from overflowing, so the autocomplete list is clipped:

Here is a reduced test case:

https://jsbin.com/cuxabik/6/edit?html,css,output

What we want:

What we have:

I am not aware of any existing CSS features which solve this problem, but it's quite a common problem to have.

The autocomplete list could exist outside of the overflow container, but then you lose the ability to define its layout in terms of its parent.

Perhaps we could instruct the child element to appear visible outside of its overflow container:

.autocompleteList { container-overflow: visible; }

Related issues/articles

This problem has also been described here: https://css-tricks.com/popping-hidden-overflow/. (Their solution was to revert to JS for calculating layout.)

This problem is potentially related to:

@AmeliaBR
Copy link
Contributor

AmeliaBR commented Jul 8, 2019

Thanks for the detailed use cases.

For the case where overflow: hidden is only used to trim the corners of an image, my first instinct is whether there could be another way to do this — a new clip-path value that references the parent's border box, maybe? That way, you're not hiding overflow unless you really want to hide content.

But that doesn't help with the case where you want to extend outside of a scrolling container, but still be positioned relative to an element inside that scroller.

@OliverJAsh
Copy link
Author

a new clip-path value that references the parent's border box, maybe?

I love the sound of this.

@paulpopus
Copy link

In your particular example, isn't this solved by having a wrapper around the image and adding overflow: hidden to that and you can also add border-radius to it. Like so https://jsbin.com/lewacofiji/1/edit?html,css,output ?
The CSS tricks issue doesn't really seem related.

@scottjehl
Copy link

+1 I run into this constantly.

@OliverJAsh
Copy link
Author

isn't this solved by having a wrapper around the image and adding overflow: hidden to that and you can also add border-radius to it

That does workaround the issue, but it feels like a hack, no? (Duplicating styles.)

Also, sometimes you don't know what content will appear inside ahead of time because it's dynamic. We just need to clip everything inside except "these" elements.

@gregwhitworth
Copy link
Contributor

@domenic and others have begun discussing this to improve the controls/components story for this specific reason, here is the issue: whatwg/html#4633

I agree that this should live in CSS as it is effectively trying to bring the window focusing model that OSes have. As denoted in that thread I think there needs to be some research done as this is effectively trying to do z-index: top but magically to ensure it's the top thing since it's the most recently focused or notification type result. We'll need to define that behavior and what constitutes the winner of this top layer. That said, I'm supportive of us investigating this and making it happen as we'll need it for any popup scenario in controls.

@domenic
Copy link
Collaborator

domenic commented Jul 10, 2019

Yeah. @tabatkins and I discussed some potential CSS properties that would make this work, but I'm hesitant to even write them up here, because I still think priority number one is research of the type outlined in whatwg/html#4633. Jumping to particular API proposals or solutions without having that grounding seems problematic to me, so I'm still really hoping to make some time to dig in to the questions listed there.

@bradkemper
Copy link
Contributor

bradkemper commented Jul 10, 2019

I think a z-index type of thing that effectively pushed the element out of its container would be the way to go and very useful.

For this particular use case, you can use border-top-left-radius: inherit on the image instead of clipping the parent, if you just want to round the corner to match.

@SelenIT
Copy link
Collaborator

SelenIT commented Jul 11, 2019

Technically, CSS already has one possibility to do this: it's absolutely positioned elements whose containing block is outside the overflowed container. So another one possible solution regarding your example is to remove position: relative from the .content element.

However, it would be great if we can control this explicitly!

@OliverJAsh
Copy link
Author

OliverJAsh commented Jul 11, 2019

@SelenIT Copying your code comment here for reference:

/* the downside is that you can't use 100% of container width anymore */

The downside of doing this is you lose the ability to define the absolute element's size in terms of its parent, inside the overflow container.

@gregwhitworth
Copy link
Contributor

@domenic completely agree. Added a few labels to denote that as such.

@OliverJAsh yeah, that's the goal :)

@grorg
Copy link
Contributor

grorg commented Jul 27, 2019

This isn't going to be useful feedback, but these suggestions probably sound terrifying to implementers. One of the reasons an OS window system can do this is because it doesn't have anything like the complexity of CSS layout. It just draws bitmaps in a particular order (and they often still fall short in some of the examples above).

Take the select or pop-up menu. In a native control, that is a modal window that appears over everything. If you swap windows (apps), it goes away. You can't scroll the originating window while the overlay is up. (You might be able to programatically scroll the original window, but I wouldn't be surprised if the overlay doesn't move). All this is special behaviour for a particular UI interaction.

If you stick to native controls in CSS, then maybe it will work. The native select does what was requested, for example. However, if you're asking for the ability to have anything escape its container, and possibly its window, then things will get really difficult. For one thing, do they become modal? Under what circumstances do they fall out of this mode? etc.

@gregwhitworth
Copy link
Contributor

@grorg Thanks for the feedback, much appreciated - it's actual useful feedback because it let's me know you're freaked out to implement it :)

It just draws bitmaps in a particular order (and they often still fall short in some of the examples above).

Interestingly enough, this is precisely how I'd summarize what I'd want it to do, it's the heuristic we need to work out (eg: this will not be another z-index).

However, if you're asking for the ability to have anything escape its container, and possibly its window, then things will get really difficult.

Along with the other form control styling investigations we're doing, this is actually at the top of the list of the first things I plan to dig into. I posit that you don't need it to escape the window. But, I'll need to go add some telemetry and get end user feedback to see if that holds true. That said - I don't think we're in a position to actually propose something for this yet. As I wind down some of my other work I'll begin digging into <select> including this item in this repo and will hopefully be able to begin determining a solution along with the brilliant folks that build out component libs & design systems every day; and all of you wonderful UA implementers/standards folks as well :)

@tabatkins
Copy link
Member

It is absolutely not ever going to be allowed for arbitrary web content to escape the window. That would mean there is zero trustable UA pixels in the window; we're already at a dangerous minimum of trustability today.

@gregwhitworth
Copy link
Contributor

@tabatkins If you think I implied that - that was not what I meant at all. I'm saying that we need to look hard at the situation we are currently facing, with numerous top properties replacing the <select> for their own custom build ones to a varying degree of fidelity; and for many different reasons. All I was trying to say was that I want to add telemetry regarding @grorg point because I think if this was such a major problem then there wouldn't be such a prevalence of custom dropdowns today.

@orangecoloured
Copy link

There's also a way to do it like this:

<div style="overflow: hidden">
    <div style="position: absolute">
        <div style="position: fixed">
        </div>
    </div>
</div>

The downside of this one is that you'll have to set the initial position with the absolutely positioned parent element and be able to adjust it only with transform property.

@SelenIT
Copy link
Collaborator

SelenIT commented Apr 10, 2021

It is absolutely not ever going to be allowed for arbitrary web content to escape the window.

Maybe it's offtopic here, but, at some point, it was possible for SVG content to escape the window boundary if it was used as a custom cursor. In theory, such cursors could have foreignObjects inside (I once considered this as a way to create natural-looking custom tooltips, though haven't tried it). Has this possibility been blocked since then?

@m4heshd
Copy link

m4heshd commented May 27, 2021

<div style="overflow: hidden">
    <div style="position: absolute">
        <div style="position: fixed">
        </div>
    </div>
</div>

This fails when scrolling.

@orangecoloured
Copy link

@m4heshd yeah, you need to update position via JS.
But I would suggest you look into portals. For example Tippy.js uses them.

@m4heshd
Copy link

m4heshd commented May 27, 2021

@orangecoloured Thanks for the suggestion. I'm trying to find a solution for a Vue project that's already completed. Refactoring in large scale is not affordable at the moment.

@m4heshd
Copy link

m4heshd commented Jul 12, 2021

I just keep running into this and it's unbearable now. Feels like pulling my hair out. Here after 3 full days spent on trying to find a workaround. Nothing works in complicated use cases. Not even positioning with JS since getBoundingClientRect() so unreliable.

UPDATE:
After 3 days, just found out that it was all because of using backdrop-filter 🤦🏻‍♂️

@GrossDesignCo
Copy link

I've run into a number of situations where the typical solution doesn't work, because the overflow: hidden container is much further up in the DOM than the element that's position: absolute.

Example: The table below has overflow: auto set on it because we want users to be able to scroll left/right on small screens, but it also has tooltips in each row, which are set to position: absolute. The trigger element for each tooltip is set to position: relative for positioning, which means we can't just move the position: relative up to some higher element in the DOM.

CleanShot 2021-07-30 at 09 46 52

It would be amazing to have a css-only solution for this kind of layout.

@AdamJaggard
Copy link

@GrossDesignCo ran into this same issue. The overflow container being higher up in the DOM and a child having to be position: relative in order to position a context menu relative to that child. I got by with the position: fixed hack mentioned further up but it has downsides. Would be great if I could just pull it out of the visual render stack of the scrolling grandparent but still position it as usuall against the closest positioned parent.

@GrossDesignCo
Copy link

GrossDesignCo commented Oct 2, 2021 via email

@besworks
Copy link

besworks commented Apr 12, 2022

This issue led me to create a pair of custom elements that can overcome this limitaion. Here's how it works:

When PopOutElement receives focus from the pointer, it's contents will be plucked from the static context, positioned fixed to the viewport and scaled up while a placeholder element is swapped in to prevent the layout from collapsing behind it. OverScrollElement allows scroll and touchmove events to pass through the [active] child element and gracefully reverse the scale effect.

This exact behavior may not be what everyone in this thread is looking for but the techniques used to achieve it may be helpful to someone with the time to dissect what I've done.

Example: https://i.imgur.com/XDCCDShl.jpeg

@Meowu
Copy link

Meowu commented Jun 9, 2022

Ran into the same issue. It would be great to have a CSS only solution.

@SilenNaihin
Copy link

This saved my life: https://www.youtube.com/watch?v=jLtNh5Kl4TA

I'm building a chrome extension that can't assume which parent is overflow:hidden; in the DOM. Added fixed to the div, relative to a parent, and scale(1) to the body and worked beautifully.

@robert-king
Copy link

For my situation I used this trick:

.editor-container {
...
overflow: hidden;
&:hover {
overflow: visible;
}
}

@thusmiley
Copy link

Technically, CSS already has one possibility to do this: it's absolutely positioned elements whose containing block is outside the overflowed container. So another one possible solution regarding your example is to remove position: relative from the .content element.

However, it would be great if we can control this explicitly!

This works for me. I changed the parent's position to static and added 'visibility: visible' to the children.

@ahmed-esmail-1
Copy link

What if the styles of the parent must be have overflow: hidden, and there is a component inside that parent, now no matter what I do of changing styles, wrapping, z-index.. etc., the component overflow will still be hidden.

@Johnrobmiller
Copy link

Johnrobmiller commented Apr 2, 2024

Thanks for the detailed use cases.

For the case where overflow: hidden is only used to trim the corners of an image, my first instinct is whether there could be another way to do this — a new clip-path value that references the parent's border box, maybe? That way, you're not hiding overflow unless you really want to hide content.

But that doesn't help with the case where you want to extend outside of a scrolling container, but still be positioned relative to an element inside that scroller.

I strongly dislike the question: "is there a way to do this given the current state of our system"? A much better question might be, "what is the best experience we can give to our develops, and what changes ought we make to our system to accomplish this?"

The answer to the former question is to do something clever with clip-paths. The answer to the latter is to implement what the OP is suggesting in the most direct way possible without involving clip-paths.

I understand that there are some reasons why this is a very difficult engineering problem, but it's not like we're trying to go faster than the speed of light here -- there is probably a way to get this done.

@xiaochengh
Copy link
Contributor

xiaochengh commented Apr 3, 2024

The Popover API should allow the listbox to break out of the parent element's clipping.

@MGSimard
Copy link

Really wish there was a one-and-done CSS property for this when you want to avoid having to circumvent it with an outside modal, element or position: fixed trick like what popover api does.

Would come in very handy for children rendered within an overflow: hidden; or clip-path parent - or situations where your parent is forced to use overflow: hidden; because you wanted access to resize: both; while still allowing some children to poke out (and want to avoid scroll bars).

Call it breakout: breakout; & breakout: none; or something along those lines and make it apply against the closest explicit ancestor overflow declaration (Akin to the relation between position relative and position absolute).

@ghost
Copy link

ghost commented Oct 14, 2024

Apply a fixed position to the container you want to overflow and set transform: scale(1) on the parent div above the one with overflow: hidden.

.parent {
  transform: scale(1);
}

.locationSuggestions {
  width: 100%;
  max-width: 400px;
  border: 1px solid;
  position: fixed;
  background-color: yellow;
  height: 600px;
}

Here, the parent is the div above the container.

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

No branches or pull requests