-
Notifications
You must be signed in to change notification settings - Fork 668
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
[css-borders-4] New border-radius
value for perfectly matching nested radii
#7707
Comments
|
|
Or |
What if you have extra elements in between |
I was assuming the nearest parent with a rounded corner, hence the long winded name to try and be explicit about what you'll get: "match the nearest parent with a border radius" [edit] |
I agree, the word Maybe something that avoids both of those terms, e.g. But we could bikeshed this all day 😄 |
Would the new property know which corners align with the parent? E.g. in your example is it necessary to specify which corners align, or could you just do this: .card {
border-radius: 24px;
}
.card picture {
border-radius: match-nearest-parent;
} I wonder if only referencing the immediate parent would help avoid corner cases (pun intended). E.g. it feels a bit weird that going from |
I also don't like the phrase "nearest parent", since a node has either one parent or none. I think the idea is to match the nearest ancestor that has a border-radius... but I also agree with @flackr's point that maybe just looking at the parent is the right thing. |
Love this idea! May I suggest one more use case to consider - rounded content aligned by position within a larger bordered parent, like inline buttons inside text fields or buttons aligned to corners of much larger areas: To be clear - if the algorithm was naively matching corner radii 1:1 in the case above, the three non-aligned corners of the child button would have near-0 radii because of how far they are from their counterparts. In this case you'd want all corners to match the radius of the nearest aligned corner. I think this behavior would probably be too magic to do in one keyword. Instead it might be nice to have the option to target a specific parent corner for each corner individually. As a sketch... .code-example > button {
border-top-left-radius: align ancestor-top-right;
border-top-right-radius: align ancestor-top-right;
border-bottom-left-radius: align ancestor-top-right;
border-bottom-right-radius: align ancestor-top-right;
} The use case may be too particular to support directly like this, just wanted to surface it. |
I think we all agree about having something easy to use but I can see a lot of cases where this can be tricky. We are talking about padding but what about margin applied to the child element or an inset applied to a positioned element or an element having a width/height equal to, for example, 90% and is centered (we will have 10% of margin that we need to consider) I can also see the case where the padding is not equal on all the sides so we should think about how the keyword should behave in these cases. What if instead of defining the value for radius we define a value to get the "distance" between an element and its containing block? For example: 1dt (distance top) will be equal to the top distance between the element its containing block. That distance can be a padding, margin or whatever. We all needed such value one day and we use JS to get it (https://api.jquery.com/position/) knowing such values (they will be 4) we can use them to define the radius like we want example .parent {
border-radius: 24px 24px 0 0;
}
.child {
border-radius: calc(24px - min(1dl,1dt)) calc(24px - min(1dr,1dt)) 0 0;
} I know it look a bit complex but the logic is to get the smallest distance between left and top when defined the top-left radius so that even if the padding,margin is not the same we still get a nice radius that match to the parent one I also think such values can be useful in other situations as well. |
Yes, there are potentially a large range of possible situations that fit this pattern (multiple wrappers between you and the border-radius container, you using margin vs the container using padding, etc), but I don't think it's reasonable to try and address all of them. So a simple "look at parent, subtract parent's padding, floor at 0" seems like it hits the 80% case in a super simple, easy to explain fashion. (And hopefully much more easily implementable than handling the wider range of situations.) |
Covering all cases correctly seems tricky, e.g. the But challenging the premise, I think the solution that you actually want is: .card {
border-radius: 24px;
overflow: clip;
overflow-clip-margin: content-box;
} with Demo: https://software.hixie.ch/utilities/js/live-dom-viewer/saved/10669 (works on Chromium) |
This is tricky but I really like the intrinsic idea here. Maybe utilizing containers might be an option? This way we can declare the path to .panel {
container: panel / size;
}
.content {
border-radius: follow follow 0 0 / panel; /* default is parent */
} |
.parent {
border-radius: 24px;
padding: 10px;
}
.child {
border-radius: auto;
} |
@argyleink Just a note about the calculation. I think we have to take in consideration the optical perception. The current math need to add a smaller % of the outer radius to fix the optical issue. I made a live example: |
I don't like Something like Though as I said, @equinusocio If you want to change how the radius of the padding edge is calculated, please file another issue. |
Nope, as shown in the pen, guess is related to this issue. "The math is handled by the browser when the keyword is used" |
For visual parity you could just use a thick border, which might even trump the overflow clip approach. The thick border is messy and overflow clip is a bit too restrictive. I think the case to have a simple unintrusive solution for this is clear. .panel {
border-radius: 24px;
container: panel / size;
}
.content {
border-radius: follow follow 0 0; /* default is parent */
border-radius-target: panel; /* could be child element */
} I've suggested |
I would suggest Ans since I'm already leaving a comment, I might as well go crazy and suggest some feature-creep: .outer {
border-radius: 1em;
padding: 10px;
}
.between {
padding: 10px;
}
.inner {
border-radius: concentric(.outer); /* Similar to JS "nearest" function */
} With the HTML nested like this And also, this assumes that the corners of the outer and inner radius are on a 45 degree line, which might not be the case when vertical and horizontal padding. And last but not least, instead of handling this as a combination of paddings and margins, which fails to consider many other attributes that can shift the position of an object, wouldn't it make more sense to simply look at the X and Y positions of both corners and calculate the absolute distance like that? |
I've been thinking about how to solve a separate issue of allowing some elements to ignore the padding of a container in order to make full-bleed elements (images in cards, highlight of list-items, etc.) and I wonder if there some shared primitives here in terms of getting a parent's properties without custom variables in order to make the I think there are two things possibly shared:
So maybe something similar in spirit to this could work: .card {
container: card / normal;
border-radius: 24px;
}
@container card style(border-radius: /* unsure what goes here */) {
.card picture {
/* this isn't actually right because inherit() will produce a serialized value that won't work in calc() */
border-radius: calc(inherit(border-radius) - inherit(padding));
}
} This might not be workable. The difficulty of doing math on multi-valued properties like |
This is similar to the discussion around <div class="card">
<div class="extra-layout-element-for-card-that-has-no-border-radius">
<picture>
<img ... >
</picture>
<footer>element that would get the radius, but immediate parent doesn't have border-radius</footer>
</div>
</div> +1 to @justinfagnani - I think container queries would be useful here, and specifically matching a container's style value. That way, you can specify what value you want to use from what specific parent. But to his last question, I think something more reusable like |
agree, could combine @justinfagnani and @DarkWiiPlayer) suggestions? .card {
container: card / normal;
border-radius: 24px;
}
.card picture {
border-radius: concentric(card) concentric(card) 0 0;
} if authors don't specify the container, it's the nearest container by default: .card picture:only-child {
border-radius: concentric();
} |
In spirit, I agree. I just wonder how you actually structure the calculation. Given that Can we possibly make is so that conceptually |
@justinfagnani, despite it being a shorthand, you could inherit the entire thing and apply the subtracted padding to each value. |
border-radius
value for perfectly matching nested radiiborder-radius
value for perfectly matching nested radii
Does it work if there are many wrapping elements between Is that bad if the keyword/function behaves like the position absolute (depending on the first position relative ancestor), in this case depending on to the first ancestor with |
only issue with the basic math approach of outer/inner radius is that it does not takes into consideration the |
|
Would Any chance we could open this up to full selector matching, like |
Yes, my idea when I proposed that syntax was to have full selector support, but apparently that idea got lost somewhere along the thread. I don't see any good reason not to make this as generic as possible by simply allowing a selector for the browser to use the first matching ancestor. |
I prefer |
Any strong opinions about why it can't behave like other props? |
I've thought a lot about how this might exist systematically, I put my thoughts in this post. But here's the finer points:
Ultimately, I think @tabatkins comment about not addressing all the scenarios is the best path forward. It just means we need to be clear about what this is expected to solve and what it will not. |
First thoughts:
|
I agree with @ddamato
Reading through all of this, I keep thinking the same thing, how would the people I work with use this? I work with a lot of people who would never know how to write the calc function to handle the basic functionality. Most of the time they wouldn't really look for a solution to copy and paste in, they would just try to guess numbers that look "correct" or use what the designer provided. That is a very limited and rigid solution. If there was a value they could add easily to handle most cases, I think they would reach for it a lot. There are properties/values in CSS that only work when the other elements have the correct values as well. I think it's worth addressing the simple use cases with a value that is easy to understand. Then, if the author needed to do something more complex, they would have to write all of the radii independently for the specific use case. |
Just a thought, as the complexity seems to be building as @LeaVerou pointed out— could this be a good use case for declarative functions, if all these non-% radii use cases are essentially variable interpolation? Here is a nested border-radius example using different values for both the (expand/collapse source)<parent>
<child></child>
</parent> parent {
--br-tl: 30px;
--br-tr: 48px;
--br-br: 82px;
--br-bl: 130px;
--p-t: 20px;
--p-b: 10px;
--p-r: 26px;
--p-l: 44px;
border-radius: var(--br-tl) var(--br-tr) var(--br-br) var(--br-bl);
padding: var(--p-t) var(--p-r) var(--p-b) var(--p-l);
}
child {
border-radius: calc(var(--br-tl) - var(--p-t)) calc(var(--br-tr) - var(--p-r))
calc(var(--br-br) - var(--p-b)) calc(var(--br-bl) - var(--p-l));
} Using declarative functions with a new (expand/collapse source)@custom-function --get-nested-radius {
result: calc(self(border-top-left-radius) - self(padding-top)) calc(self(border-top-right-radius) - self(padding-right)) calc(self(border-bottom-right-radius) - self(padding-bottom)) calc(self(border-bottom-left-radius) - self(padding-left));
}
parent {
border-radius: 30px 48px 82px 130px;
padding: 20px 10px 26px 44px;
--nested-radius: --get-nested-radius();
}
child {
border-radius: var(--nested-radius);
} I'm sure I'm missing some level of that calculation where the corner radius needs to account for the padding of both sides it touches and take a weighted average of the two, but this is essentially the idea. I agree that having something like this built into the browser could be handy, but if there's any part of this that could potentially change based on use case, would it be better to leave that calculation up to the end user/dev? Btw if there's a more accurate parent-directed formula to build the nested |
How do we change the internal radius if there is only one element? Maybe we need to introduce a new |
@yisibl Use margin-top: 50px;
box-shadow: 0 -40px 0 10px var(--bd-color), 0 0 0 10px var(--bd-color);
border-radius: 10px; |
@Loirooriol |
The Problem:
The majority of implementations where nested border radius are used; are asymmetrical and imperfect. The imperfect solution is easy to do, while the perfect solution is harder.
Proposed Solution:
A new
border-radius
value for nested elements, making symmetrical and perfectly matching nested radii easy. The math is handled by the browser when the keyword is used:parent-radius - parent-padding
.Corners compared
Below is a focused screenshot of the corners for comparison. The border radius on the left has a wobble to it, as the curves don't match. The border radius on the right does match and follows the same curve for a nice perfect finish. Which do you want in your design?
Conclusion
While it's relatively trivial to do the math inside
calc()
with custom properties passed down to children, it's not easy or intuitive. By adding an additional border-radius value that does the effect easily, we'll see better designs because the nice choice is easy and built-in.Demo source:
https://codepen.io/argyleink/pen/LYmpqMB
The text was updated successfully, but these errors were encountered: