Dialog styling with nested vanilla CSS
Hi,
I've taken this code from Adam Argyle as a basis for styling my dialogs:
dialog {
/* Exit Stage To */
transform: translateY(-20px);
&, &::backdrop {
transition:
display 1s allow-discrete,
overlay 1s allow-discrete,
opacity 1s ease,
transform 1s ease;
/* Exit Stage To */
opacity: 0;
transform: translateY(20px);
}
/* On Stage */
&[open] {
opacity: 1;
transform: translateY(0px);
display: flex;
flex-direction: column;
gap: 1rem;
width: 750px;
border-top: 1rem solid var(--color-matisse);
max-width: calc(100% - 2rem);
box-shadow: 0 0 1rem oklch(from var(--color-black) l c h / 0.5);
padding: 1rem;
&::backdrop {
opacity: 0.8;
}
header {
display: flex;
align-items: start;
gap: 1rem;
button {
position: absolute;
top: 0;
right: 0;
}
}
html:has(&[open]) {
overflow: hidden;
scrollbar-gutter: stable;
}
/* Enter Stage From */
@starting-style {
&[open],
&[open]::backdrop {
opacity: 0;
}
&[open] {
transform: translateY(10px);
}
}
&::backdrop {
background-color: black;
}
}
}
I ahve another dialog that I am using for a mobile nav. This is styled totally independently so don't want these styles conflicting with it.
As those styles (for the mobile nav) are specified after these ones, I COULD just overwrite all the properties, but that seems silly.
Ideally i'd just like to stop these being applied to the nav so I tried adding :not(#mobile-nav)
to the dialog
selector, but it fails to work.
Any ideas here?81 Replies
I ahve another dialog that I am using for a mobile nav.you're trying to fix bad solutions by monkey-patching css don't use a dialog for that
What's wrong with using a dialog for that? It's a piece of UI that appears.
it's not the right element for it
Either way, this isn;t really about whether a dialog is the right element ot use or not, this is about trying to resolve the issue.
there are "better" options
Ok, I agree it's the wrong element. Please help me fix the issue whilst keeping the mobile nav as the dialog.
I agree, totally!
Let's put that to the side and focus on the issue
if you really really really must use a dialog, set a data attribute
how will that help?
well, you add something like
data-type="menu"
or something
then you only add the menu styles to data-type="menu"
the xyz styles only for xyz
this way, you don't have to override anything and just set the non-global styles where neededSo I should style my global dialog normally, and then on the mobile nav dialog reverse all those that do not apply?
no, have the styles for the mobile menu set under, for example,
[data-type="menu"]
and the styles for a form set under [data-type="form"]
or something like this
just the styles specific for that data-type
I don;t understand the need for the data attribute, the mobile nav had a unique id on it, surely that serves the same purpose?
or classes
classes work too
not exactly, but kinda
you will need to do a fair bit of monkey patching on that css for that to work
I don't see the difference between using a data attribute and an id
Please explain
the difference is that you don't have to override anything
How does that work? Can you provide an example?
no, not now, im playing overwatch
what are the styles you need to put on the menu?
#mobile-nav {
background: none;
box-shadow: none;
padding: 0;
border-radius: 0;
overflow: clip;
inset: 0 0 0 auto;
margin: 0 0 0 auto;
block-size: 100dvh;
max-block-size: 100%;
transition: display var(--_duration) allow-discrete, overlay var(--_duration) allow-discrete; &::backdrop { transition: opacity var(--_duration) var(--ease-4); opacity: 0; background-color: light-dark(#0003, #0008); cursor: zoom-out; } & > section { transition: translate var(--_duration) var(--ease-in-out-5); translate: 100% 0; } &[open] { &, &::backdrop { opacity: 1; }
& > section { opacity: 1; translate: 0; } } @starting-style { &[open], &[open]::backdrop { opacity: 0; } &[open] > section { opacity: 0; translate: 100% 0; } }
transition: display var(--_duration) allow-discrete, overlay var(--_duration) allow-discrete; &::backdrop { transition: opacity var(--_duration) var(--ease-4); opacity: 0; background-color: light-dark(#0003, #0008); cursor: zoom-out; } & > section { transition: translate var(--_duration) var(--ease-in-out-5); translate: 100% 0; } &[open] { &, &::backdrop { opacity: 1; }
& > section { opacity: 1; translate: 0; } } @starting-style { &[open], &[open]::backdrop { opacity: 0; } &[open] > section { opacity: 0; translate: 100% 0; } }
and what's not repeated for all?
I'm unsure right now
can you try to find out?
Surely the only way for me to acheive this is to style
dialog
one way, then to style #mobilenav
by cancelling out any properties from the global ones that I don't want?
Unless you know of a better way?the better way is what i suggested before
but i'm confused as to what that "better way" is.
I'm very intregued to know.
If you can provide a very quick simple demo then i'd be more than hapy to take a look.
basically, just
<dialog data-type="menu">
and in the css you add the menu-specific styles to &[data-type="menu"] { ... }
and for the other dialogs, you use a different data-type
this means that you won't need to override any other styles for anything
or you can use classes
or you can use css layers with the classes or attributesAh so not to style anything on just
dialog
? That means I need to add some sort of attribute, class etc onto all future dialogs.yes
I see what you mean, but all future dialogs will be the same so need to be styled globally, it's just the menu one that is different
how do you know you won't use a different dialog?
Well obviously i don't but right now, in the scope of my app, I have two, the main type and the mobile nav type
what's the tech stack?
It's a rails app
So, my original question still stands.
Does anyone know of a way to acheive this by not overwirting or cancelling out the styles from global dialog to a more specific dialog?
Most likely there isn;t but thought i'd ask?
yeah, rails doesn't have anything analogous to components, right?
Nope
yeah, my option is annoying af then
you can try to see if you can use layers for this
sounds like you should be able to
Not sure how layers can help. I'm already using layers
you can use a layer for the usual styles, and then another for the other styles
i dont know how to explain, will experiment in a bit
They're already on separate layers, the styles for
dialog
are affecting #mobile-nav
(as expected to be honest). Only way around this i can think of is to overwrite the ones in #mobile-nav` that don't need to be therei will try some things
Ok, Look forward to seeing what you find
i couldn't come up with anything
ok
thanks
:not(#mobile-nav) should work. Can you show an example of the selector you used that wasnt working?
Also agree that dialog isn't the right element for a nav though, it's hidden from the accessibility tree until it's triggered and into the top layer of the dom.
it also steals the focus onto the dialog itself, if there's nothing that sets the focus automatically, which is utterly useless
I thought the
:not
selector should work and that's what I wanted to discuss but @ἔρως kept pushing the conversation around to correct/incorrect usage of the <dialog>
element.
My selector is dialog:not(#mobile-nav) {
, further down, and nested within that, I have &[open]
.
These styles are in one layer, and my #mobile-nav
styles are in a higher layer.
On the subject of the use of <dialog>
here, can we park this in this topic and stay on track. Whether you think the use is right or wrong for this situation, right here, right now, I do not care. It is a valid discussion and I am more than happy to start another thread to discuss that but let's stay on topic and discuss why the selector I have written is not working. Fair?
I'd appreciate any discussion and comments from anyone about how we can solve this.it works, it just kills the styles for EVERYTHING and you have to re-style everything
again, if you used a different element for the menu, you wouldnt have to faf about like this
What do you mean by “it works, it just kills the styles for EVERYTHING?”
the selector does "the thing", but by doing "the thing" it is only styling the other dialogs and not the one in the headed
But it doesn’t! That’s the whole reason I am posting here.
🤔
i need to see that
is there a live example somewhere?
I'll work on one and post back
Pro-tip: use three backticks to create a code block (instead of doing single-backticks on each line). See #How To Ask Good Questions for the correct syntax
ive quickly tested on jsfiddle and the selector should work - https://jsfiddle.net/pf7eoxd8/
ive simulated everything i could in as little code because lazy
I'll build a demo this evening
alright
I might be misunderstanding the original question but couldn't you reset the dialog styles with
inital
?
i think there are animations and stuff that's shared between both types of dialogs
Actually that’s a good shout. I may try that. There is nothing shared between them. It’s just I want global dialog styles and then ones specific for this mobile nav so that may be a solution. As I said I’ll try to build a demo and have a play with all: initial. Appreciate the suggestion!
Quick demo using your CSS from this thread https://codepen.io/cbolson/pen/xbxXRez (will delete later)
Awesome. Thanks @Chris Bolson
The
dialog:not(#mobile-nav)
suggestion by Clevermissfox should also work.
Also, placing them in separate layers should have worked too (it did in my test)Maybe my code got a bit out of wack. I'll try to tidy it up and see how I get on
I know the feeling 😆
Slowly making progress with this, but a related question.
@Chris Bolson In this demo here: https://codepen.io/argyleink/pen/jOgxGmX Any idea how I can make the animation come in from the right hand side? I've swapped all the appropriate properties but the "translate from right" is messed up. Any suggestions?
This is my fork where I tried switching it to the right: https://codepen.io/rctneil/pen/EaxwbLG
Why don't you translate the whole dialog rather than just the contents?
@Chris Bolson That is exactly what I asked Adam Argyle on Blue Sky earlier today (https://bsky.app/profile/rctneil.co.uk/post/3lk45csp74k2w)
It confused me. I've managed to put this toegther which is super close to what I am after:
https://codepen.io/rctneil/pen/VYwMXmO?editors=1100
Just unsure why, when closing, it jumps downwards a little in Safari?
Your nested selector is computing to
dialog:not(#mobile-nav)[open]
which isn’t valid. You can rewrite it in a couple of ways but essentially you need dialog[open]:not(#mobile-nav)
actually, it is valid
here, you can see that it does work
i've used different tags and attributes and a different id, but the result is the same
at the end, the span is being matching by
p:not(#sport)[data-x="1"] span
the order kinda sorta doesn't matterAnyone have any ideas how to get rid of the closing jump in Safari?
i dont use anything apple - too rich for my blood
and for my wallet
Aha Ive used the wrong ID #mobile-nav not #mobileNav . You’re right it should work 🤔 maybe some specificity issue then . I would look in the dev tools to see if it identifies an error with the syntax or if there’s a style somewhere you’ve forgotten about that’s overwriting
Will do. My current task is a bit away from that, just trying to complete the dialog panel I liked to in the codepen. Then i'll go back and style the regular dialogs where I may his this issue again!
It could be the timing function that you are using. Try something simpler to see if that resolves the jumping issue.
Nope, changing the easing doesn't help. Very weird
Try adding
top: 0;
to the dialogThat does it. Not sure why though as it's not absolutely positioned.
Default styles of a dialog do include position absolute , there are a handful of default styles that exist unless you override
So all looks good apart from Firefox where it works fine but on close it just vanishes and doesn't animate out. I can live with that. I think it's a lack of support for
allow-discrete
.yes, that won't work in FF yet
Not a major issue