Pure CSS To Break Child Out of Flex/Grid Without Changing Position style?

I want content in a grid child to glide from the page to a modal on click.

The example below almost does what i have in mind, but not getting the sliding transition i want. I believe it’s because the position property is changing from static to fixed, so left and top can’t animate. If there was a way to keep the same position property in both states then we could animate left & top transition.

Some other ideas:

  • grid-column: 1 / -1 - Supposed to break out of grid and spread from first to last col. Not working for me. https://www.joshwcomeau.com/css/full-bleed/
  • width: 100%; box-sizing: border-box - I don’t know how this is supposed to work, but not breaking out of the grid. https://stackoverflow.com/a/5219611/209942
  • Change the parent of the content. Might require JS, or maybe :before and after pseudoclasses to wrap it in a different parent?

As I said in the other thread you can’t move from one positioning scheme to another without having something to animate to and from. You can’t animate from an auto position so you can’t animate from where something is sitting in an auto position (or non position) and then say left:200px as there is no from value. All you get is an immediate step to the left:200px;

You can magic number it by giving the element its position in relation to the viewport and then animate from that position but you have to work out all the positions which in a fluid layout would require numerous media queries each time an element wrapped as all the positions would change.

Here’s your demo adapted to a magic number scenario using a fixed width to avoid all the media queries that would be needed.


(View on codepen due to the fixed width)

As you can see that achieves what you want but obviously is too rigid apart form the smallest demo. In a real world you would want to calculate those coordinates with js on the fly.

1 Like

Here’s a very rough js version so that the page can be fluid. The only caveat is that the modal needs to stay fixed to the top left corner.

(Code needs refactoring as I was in a rush)

Of course that’s only part of the problem as you have made no provision for your extra content to be shown. At the moment you are just moving an image.:slight_smile:

1 Like

@PaulOB Ideas:

  • What about using grid-column to keep it CSS?
  • What about going full screen width and height, or using vw? Then we avoid specific positioning math.

You can’t animate from grid properties. An animation has to go from one fixed value to another.

Going full screen makes no difference to the concept. Vw units are just another unit and you would still need values to animate to :slight_smile:

1 Like
  • Ok, nevermind animation for the moment. Can i position the modal using grid-column?
  • Can we exploit grid support for overlapping cells?
  • Can we show the modal over top of the original cell position. Yes, that would mean the modal isn’t displayed in the same fixed position for all cells. Let’s say that’s acceptable. One concern is that for cells on the far left, right, top, or bottom of the page, the modal will overflow the page boundaries.
  • Can we use css calc() with vw and known fixed cell-width to calculate the magic number?

Yes you can make cells overlap in grid. You can just used named areas and place two items in the same area or span several areas. You would be better off choosing a cell in the middle of the page and make all the modal images appear in that space.

However if you are not animating it now then there’s nothing wrong with the fixed positioned method that was already being used and will keep the modal fixed in the centre of the viewport. You can size it as you wish.

As I see it you haven’t defined why you want to move this image and what else is supposed to happen. We need to know the other details before you can come to a solution for the image. If it’s just centering an image it seems a bit pointless to me. (Unlike your other demos where we move and show extra content in the modal.)

You need to plan ahead so we need to know about what happens next and what the final is supposed to look like.

I believe you have most of the tools to accomplish this now anyway depending on which way you approach it.

2 Likes

i said “nevermind animation for the moment”, and only in reference to that particular question.

Woah. I can position the content that way without changing it’s position property? Can i use left for that, rather than a grid property?

True, that’s my fallback if can’t get desired animation.

Still planning to move/unhide other content. I restricted this question to image to simplify the question.

i did so in the other thread. The animation is visually appealing, and helps the user understand what’s happening.

Can we use css calc() with vw and known fixed cell-width to calculate to get a negative magic new left property for the modal?

Here’s a basic example of a grid overlap/

Just click on a box.

I’m not really sure of the use-case yet as this is merely an answer to your query :slight_smile:

…and just for fun…

(Unfortunately I am away abroad on holiday for a few weeks now and only have access to a mobile so can offer no more code until I return. Hopefully you have enough examples to make your choice now. My choice would be a basic non animated version and then enhanced with js for the animation.)

1 Like

Beauty! Great work.

It seems your css assumes a fixed number of columns and rows, and it achieves responsiveness by scaling rather than wrapping. Not sure it can work with grid-wrapping.

Also you’re not animating the movement of the content from the grid to the panel. I’m guessing grid-column can’t be animated, right?

I realized for my use-case, i need the panel positioned relative to the viewport, not relative to the grid.

We can assume the clicked row is visible, ie within the viewport. In my scenario, with grid-wrapping, we can assume all columns are within the viewport. Therefor, i believe i want the row-position of the panel to be the same as the clicked row. The column would be grid-center, as you’ve done.

I don’t think we yet have a way to detect clicked-row, or clicked-vh, with CSS only.

For now, i’m going with the first method i posted:

Issue:

  • When window size is narrow enough to accommodate only one column of cells, the click behavior gets wonky. The clicked item no longer reserves it’s vacated space in the grid, so the following cells move up to fill the gap. How to fix?

Beauty! Great work.

It seems your css assumes a fixed number of columns and rows, and it achieves responsiveness by scaling rather than wrapping. Not sure it can work with grid-wrapping.

Also you’re not animating the movement of the content from the grid to the panel. I’m guessing grid-column can’t be animated, right?

I realized for my use-case, i need the panel vertical positioned relative to the viewport, not relative to the grid. Therefor, i believe i want:

  • panel row: same as the clicked row. Meaning, don’t change row property between states.
  • panel column: grid-center, as you’ve done. Thx to wrap, grid-center is guaranteed to be vw center.

That demo was in response to your question about grid cells that can overlap and that is the format you would need to in order to keep the modal in the middle. Otherwise you have no middle cells and you are back to positioning it in the top left corners as in your example. The fixed positioned modal is a much better idea as I mentioned before and won’t scroll away with the page.

You said forget about that for now/

Yes, I have mentioned several times that this is not possible. Only certain properties (and values) can be animated

Yes that’s the way the modals in my original demos were placed.

You can do that in your exam[le by setting a top auto and the modal will appear at the same vertical position as the element that called it (unless you scroll after selecting).

.linker:focus {
  position: fixed;
  width: 600px;
  max-width: 100vw;
  z-index: 1;
  height: 300px;
  background-color: lightsteelblue;
  top:auto;
  margin-top:2rem;
  left: 100px;
  box-shadow: 10px 10px 10px 50px rgba(0, 0, 0, 0.2);
  animation: fadeIn 0.7s;
}

.closer{
  display: none;
}

.linker:focus + .closer {
  display: inline;
  position: fixed;
  top: auto;
  margin-top:2rem;
  left: 650px;
  z-index: 15;
}

You were mistaken from the start and the height of the row is controlled by the height of the tallest item in that row thus forcing all others to the same height as that row (this is a default behaviour for flex and grid).

When you have one column the height of a row is zero when you move the content out of it. You would need to apply a min-height to hold the space open but of course that is a magic number fix again and undesirable.

.box{min-height:200px;}

Lastly you also need to reduce the size of the modal when it is too wide for the viewport.

1 Like

I also meant to mention that if you want the modal to follow the row position then it will need to ba absolute rather than fixed otherwise you will never see it if its below the fold.

Here’s your version updated to absolute and centred.

If you want the fixed version centred then its the same code except you will need to place it from the top with a fixed measurement and not auto.

Sorry but that’s it for me for a few days so I suggest you digest all the threads and information and come up with a version that suits you and then post a new thread if you have problems refining it. I will be around on mobile only in a couple of days time. :slight_smile:

1 Like

i decided to go with fixed position. Seems less sloppy than a panel which can appear in various spots, or get scrolled out of view.

Some issues:

Close X

  • The close-X doesn’t always appear at upper right corner – sometimes it falls off to the right. I shrink panel width on narrow screens using the following:
  max-width: 600px;
  width: 70vw;

The X position should adapt to changing panel size, and any browser-zoom. I attempted to fix with the following, but not quite working at certain in-between widths.

.linker:focus + .closer {
  left: min( 90vw , 700px );
}

This method seems like a poor way to approximate the correct position. It seems the close-X should be somehow positioned relative to the upper-right of the panel, so it will stick there no matter where the panel appears or what panel-width. A challenge is that the closer <a> cannot go inside the linker <a>, because a tags aren’t permitted to contain other a tags. So the linker and closer are siblings. The structure is (these are class-names of the divs):

<box>
     <linker />
     <closer />
<box/>

linker switches to position: fixed when the panel is displayed. If the closer remains inline or relative, then it won’t move to the panel – it will stay in the grid.

Better might be something like left and max-left, but there’s no such thing as “max-left”.

Currently, the linker IS the panel, by changing it’s position to fixed. Maybe i need to contain the closer and linker together by positioning them inside an extra container. Something like this?

<box>  grid placeholder
    <panel>
        <linker>
        <closer>
    </panel>
</box>

The challenge with that is we need to style panel when linker gets focus (due to mouse-click). We could use this:
panel:hasfocus
The problem is that the closer is also a link. The above hasfocus would detect when the linker is clicked (which we want), but will also detect when the closer is clicked (which we don’t want).

I think the solution should look something like this, but this isn’t working.
.box:has(linker:focused) .linker {
https://ishadeed.com/article/css-has-parent-selector

It may be preferable if the entire panel can be clicked to close it. I don’t know if that simplifies.

Panel Fadeout

  • It would be great if panel could fade-out when closing, but can’t figure how, without affecting the element in the grid. I think it has to be something like:
    .box:has(closer:focused) .linker {animation-fadeOut}

Jump Up

When content is too tall to fit on one screen, clicking a grid-element near the bottom of the grid causes the page to scroll up. I guess that’s because the page is scrolling to the panel, but that doesn’t make sense since the panel is fixed-positioned, so it should be treated as if it’s on the visible area of the page.

Image Cross-fade

  • It would be great if the image could cross-fade going from grid to panel, but i guess that’s impossible since it’s the same image!

You really should place it with the container and not as a separate element, Of course that means changing your designs as you can’t nest anchors.

I actually gave you a partial solution anyway in my absolute example and you can see the x stays track with the panel. It is placed left 50% and then half the width of the panel which can be done with calc. (my example is centered unlike yours so you will need to work that out )

This is from my example.

.linker:focus + .closer {
  left: calc(50% + min(34vw, 290px));
}

You would have to adjust based on the size of your panel and your panel position. I’m not on a computer as mentioned so not able to offer code unless I can copy and paste from somewhere.

An anchor with an empty hash href takes the browser to the top of the window by default. This is again a reason not to use your current approach because links with empty hrefs are not links at all. However you can avoid the issue by setting an id that is not present on the page. e.g. < a href="#nogo">. It works but whichever way you look at it its an ugly hack as it leaves a trail in the address bar.

Correct you need two images in the same place to crossfade. You could probably fade one out and then the other one in using keyframes but it could be awkward because you are using:focus. If you look at my last demo you can see the image fades in on the modal and then also fades back in when returned to its original position.

1 Like

I remade the page using buttons, for improved accessibility.

The opener and closer are working correctly.

  • Visible card contents aren’t displaying inside cell in the grid.
  • Hidden card contents aren’t displaying on the panel.
  • A link in the hidden content wasn’t clickable on the panel, even tho’ it’s z-index was highest.

Any suggestions? Thx!

You have the hidden card called .card-hidden but you us a hidden-contents class n the css which doesn’t exist? :wink:

I assume you want the first block to be the size of the coral background but not sure why you color the button and not the content and why the button isn’t absolutely positioned so that the content comes into view?

I’m guessing you were trying for something like this.

You won’t be able to absolutely place fluid content above the image in the modal because that won’t work if there is more than one line of text. I placed the image above the text so it could flow properly. (It may be [possible to change order using the order property instead but I didn’t go there yet).

I still think my image and caption approach and demos were far superior and more semantic. :wink:

Note that the :has pseudo class isn’t supported by all modern browsers quite yet.but is not far off.

1 Like

Thanks for that!

I got the class name wrong? Oops. :rofl:

  • My colors aren’t significant. I think i colored the button because at first i used the opener-button itself as the container. Then later i realized i needed a parent container.
  • Well, your modal-only hyperlink isn’t clickable when the modal is opened.
  • Ideally, modal-only text should be selectable.
  • Ideally, i’d like to be able to arrange the modal contents however desired, without weird restrictions, like i can’t position something above something…
  • “Semantic” means, tags are used as the browser (or user) expects them to be used. Why do you feel fig is more semantic than buttons? Or do you mean to combine them?

That is the same problem I have mentioned before in that focus isn’t a stable constant. As soon as you click on another focusable element the focus is lost on the previous element and the block disappears before the link is activated.

You can get around it by creating another rule that holds the block open while the link is focused but you will need to do that on a case by case basis if for example you had a form with multiple focusable elements in that hidden block. Hopefully you are not doing that anyway so using the .link class should be sufficient.

I mentioned the answer to that in my post and that is not to absolutely place the content but to use a structure such as flex or grid and then you can order or place items within that context and still maintain the flow.

Here are the 2 above points incorporated into a new codepen.

This again brings me back to one of my first questions in that you must clearly define your use case before you start writing code :slight_smile: It is only with a full understanding of the requirements that a solution can be tailored to fit those requirements.

Once again the problem is that your method of using focus to display the hidden elements is fine for a quick css trick but for a fully functioning layout it is not ideal. Every time you want to do something else you have to code for it and each time break the normal actions of html in the process.

If you went right back to one of my other demos and simply added a class with js to open the modal none of these issues would be present and the the full normal range of html operations would still be in order.

To cater for the selection of the text you once again have the issue of maintaining focus on the element so that it doesn’t disappear. This means that we have to create a focusable element for the text now so that we can hold the div open. This can be done using tabindex=“0” which allows an element to receive focus. Then we change the css so that when this element is focused we keep the div open also.

It is actually better in this case if we use :active for the text as that means the mouse is pressed down and allows for the text to be selected.

I have rolled all the above into a third demo.

Although it now all works it is still a fragile structure and of course we don’t have touch support or support in browsers that don;t support the :has selector.

I do agree in that as much as possible should be done with CSS but not at the extent of complexity and poorer accessibility. If I was coding this I would make the content available by default and then hide it if js was available and use a dynamic class to toggle the modal. I would also prefer a specific open and close button as to a click anywhere approach.

Yes combine them. The buttons are the actions but the image and caption are still (as far as the demo shows) an image with its caption which suits figure and figcaption nicely. If the image was just eye candy and not real content then that would not be the case.