{"id":292177,"date":"2019-09-06T07:19:05","date_gmt":"2019-09-06T14:19:05","guid":{"rendered":"http:\/\/css-tricks.com\/?p=292177"},"modified":"2019-09-09T06:53:04","modified_gmt":"2019-09-09T13:53:04","slug":"various-methods-for-expanding-a-box-while-preserving-the-border-radius","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/various-methods-for-expanding-a-box-while-preserving-the-border-radius\/","title":{"rendered":"Various Methods for Expanding a Box While Preserving the Border Radius"},"content":{"rendered":"

\ufeffI’ve recently noticed an interesting change on CodePen: on hovering the pens on the homepage, there’s a rectangle with rounded corners expanding in the back.<\/p>\n

<\/p>\n

\"Animated
Expanding box effect on the CodePen homepage.<\/figcaption><\/figure>\n

Being the curious creature that I am, I had to check how this works! Turns out, the rectangle in the back is an absolutely positioned ::after<\/code> pseudo-element.<\/p>\n

\"Collage.
Initial ::after<\/code> styles. A positive offset goes inwards from the parent’s padding<\/code> limit, while a negative one goes outwards.<\/figcaption><\/figure>\n

On :hover<\/code>, its offsets are overridden and, combined with the transition<\/code>, we get the expanding box effect.<\/p>\n

\"Collage.
The ::after<\/code> styles on :hover<\/code>.<\/figcaption><\/figure>\n

The right<\/code> property has the same value (-1rem<\/code>) in both the initial and the :hover<\/code> rule sets, so it’s unnecessary to override it, but all the other offsets move by 2rem<\/code> outwards (from 1rem<\/code> to -1rem<\/code> for the top<\/code> and left<\/code> offsets and from -1rem<\/code> to -3rem<\/code> for the bottom<\/code> offset)<\/p>\n

One thing to notice here is that the ::after<\/code> pseudo-element has a border-radius<\/code> of 10px<\/code> which gets preserved as it expands. Which got me to think about what methods we have for expanding\/shrinking (pseudo-) elements while preserving their border-radius<\/code>. How many can you think of? Let me know if you have ideas that haven’t been included below, where we take a look at a bunch of options and see which is best suited for what situation.<\/p>\n

Changing offsets<\/h3>\n

This is the method used on CodePen and it works really well in this particular situation for a bunch of reasons. First off, it has great support. It also works when the expanding (pseudo-) element is responsive, with no fixed dimensions and, at the same time, the amount by which it expands is fixed (a rem<\/code> value). It also works for expanding in more than two directions (top<\/code>, bottom<\/code> and left<\/code> in this particular case).<\/p>\n

There are however a couple of caveats we need to be aware of.<\/p>\n

First, our expanding element cannot have position: static<\/code>. This is not a problem in the context of the CodePen use case since the ::after<\/code> pseudo-element needs to be absolutely positioned anyway in order to be placed underneath the rest of this parent’s content.<\/p>\n

Second, going overboard with offset animations (as well as, in general, animating any property that affects layout with box properties the way offsets, margins, border widths, paddings or dimensions do) can negatively impact performance. Again, this is not something of concern here, we only have a little transition<\/code> on :hover<\/code>, no big deal.<\/p>\n

Changing dimensions<\/h3>\n

Instead of changing offsets, we could change dimensions instead. However, this is a method that works if we want our (pseudo-) element to expand in, at most, two directions. Otherwise, we need to change offsets as well. In order to better understand this, let’s consider the CodePen situation where we want our ::after<\/code> pseudo-elements to expand in three directions (top<\/code>, bottom<\/code> and left<\/code>).<\/p>\n

The relevant initial sizing info is the following:<\/p>\n

.single-item::after {\r\n  top: 1rem;\r\n  right: -1rem;\r\n  bottom: -1rem;\r\n  left: 1rem;\r\n}<\/code><\/pre>\n

Since opposing offsets (the top<\/code>–bottom<\/code> and left<\/code>–right<\/code> pairs) cancel each other (1rem - 1rem = 0<\/code>), it results that the pseudo-element’s dimensions are equal to those of its parent (or 100%<\/code> of the parent’s dimensions).<\/p>\n

So we can re-write the above as:<\/p>\n

.single-item::after {\r\n  top: 1rem;\r\n  right: -1rem;\r\n  width: 100%;\r\n  height: 100%;\r\n}<\/code><\/pre>\n

On :hover<\/code>, we increase the width<\/code> by 2rem<\/code> to the left<\/code> and the height<\/code> by 4rem<\/code>, 2rem<\/code> to the top<\/code> and 2rem<\/code> to the bottom<\/code>. However, just writing:<\/p>\n

.single-item::after {\r\n  width: calc(100% + 2rem);\r\n  height: calc(100% + 4rem);\r\n}<\/code><\/pre>\n

…is not enough, as this makes the height<\/code> increase the downward direction by 4rem<\/code> instead of increasing it by 2rem<\/code> up and 2rem<\/code> down. The following demo illustrates this (put :focus<\/code> on or hover over the items to see how the ::after<\/code> pseudo-element expands):<\/p>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

We’d need to update the top<\/code> property as well in order to get the desired effect:<\/p>\n

.single-item::after {\r\n  top: -1rem;\r\n  width: calc(100% + 2rem);\r\n  height: calc(100% + 4rem);\r\n}<\/code><\/pre>\n

Which works, as it can be seen below:<\/p>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

But, to be honest, this feels less desirable than changing offsets alone.<\/p>\n

However, changing dimensions is a good solution in a different kind of situation, like when we want to have some bars with rounded corners that expand\/shrink in a single direction.<\/p>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

Note that, if we didn’t have rounded corners to preserve<\/em>, the better solution would be to use directional scaling via the transform<\/code> property.<\/p>\n

Changing padding\/border-width<\/h3>\n

Similar to changing the dimensions, we can change the padding<\/code> or border-width<\/code> (for a border<\/code> that’s transparent<\/code>). Note that, just like with changing the dimensions, we need to also update offsets if expanding the box in more than two dimensions:<\/p>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

In the demo above, the pinkish box represents the content-box<\/code> of the ::after<\/code> pseudo-element and you can see it stays the same size, which is important for this approach.<\/p>\n

In order to understand why it is important, consider this other limitation: we also need to have the box dimensions defined by two offsets plus the width<\/code> and the height<\/code> instead of using all four offsets. This is because the padding<\/code>\/ border-width<\/code> would only grow inwards if we were to use four offsets rather than two plus the width<\/code> and the height<\/code>.<\/p>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

For the same reason, we cannot have box-sizing: border-box<\/code> on our ::after<\/code> pseudo-element.<\/p>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

In spite of these limitations, this method can come in handy if our expanding (pseudo-) element has text content we don’t want to see moving around on :hover<\/code> as illustrated by the Pen below, where the first two examples change offsets\/ dimensions, while the last two change paddings\/ border widths:<\/p>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

Changing margin<\/h3>\n

Using this method, we first set the offsets to the :hover<\/code> state values and a margin<\/code> to compensate and give us the initial state sizing:<\/p>\n

.single-item::after {\r\n  top: -1rem;\r\n  right: -1rem;\r\n  bottom: -3rem;\r\n  left: -1rem;\r\n  margin: 2rem 0 2rem 2rem;\r\n}<\/code><\/pre>\n

Then we zero this margin<\/code> on :hover<\/code>:<\/p>\n

.single-item:hover::after { margin: 0 }<\/code><\/pre>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

This is another approach that works great for the CodePen situation, though I cannot really think of other use cases. Also note that, just like changing offsets or dimensions, this method affects the size of the content-box<\/code>, so any text content we may have gets moved and rearranged.<\/p>\n

Changing font size<\/h3>\n

This is probably the trickiest one of all and has lots of limitations, the most important of which being we cannot have text content on the actual (pseudo-) element that expands\/shrinks — but it’s another method that would work well in the CodePen case.<\/p>\n

Also, font-size<\/code> on its own doesn’t really do anything to make a box expand or shrink. We need to combine it with one of the previously discussed properties.<\/p>\n

For example, we can set the font-size<\/code> on ::after<\/code> to be equal to 1rem<\/code>, set the offsets to the expanded case and set em<\/code> margins that would correspond to the difference between the expanded and the initial state. <\/p>\n

.single-item::after {\r\n  top: -1rem;\r\n  right: -1rem;\r\n  bottom: -3rem;\r\n  left: -1rem;\r\n  margin: 2em 0 2em 2em;\r\n  font-size: 1rem;\r\n}<\/code><\/pre>\n

Then, on :hover<\/code>, we bring the font-size<\/code> to 0<\/code>:<\/p>\n

.single-item:hover::after { font-size: 0 }<\/code><\/pre>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

We can also use font-size<\/code> with offsets, though it gets a bit more complicated:<\/p>\n

.single-item::after {\r\n  top: calc(2em - 1rem);\r\n  right: -1rem;\r\n  bottom: calc(2em - 3rem);\r\n  left: calc(2em - 1rem);\r\n  font-size: 1rem;\r\n}\r\n\r\n.single-item:hover::after { font-size: 0 }<\/code><\/pre>\n

Still, what’s important is that it works, as it can be seen below:<\/p>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

Combining font-size<\/code> with dimensions is even hairier, as we also need to change the vertical offset value on :hover<\/code> on top of everything:<\/p>\n

.single-item::after {\r\n  top: 1rem;\r\n  right: -1rem;\r\n  width: calc(100% + 2em);\r\n  height: calc(100% + 4em);\r\n  font-size: 0;\r\n}\r\n\r\n.single-item:hover::after {\r\n  top: -1rem;\r\n  font-size: 1rem\r\n}<\/code><\/pre>\n

Oh, well, at least it works:<\/p>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

Same thing goes for using font-size<\/code> with padding<\/code>\/border-width<\/code>:<\/p>\n

.single-item::after {\r\n  top: 1rem;\r\n  right: -1rem;\r\n  width: 100%;\r\n  height: 100%;\r\n  font-size: 0;\r\n}\r\n\r\n.single-item:nth-child(1)::after {\r\n  padding: 2em 0 2em 2em;\r\n}\r\n\r\n.single-item:nth-child(2)::after {\r\n  border: solid 0 transparent;\r\n  border-width: 2em 0 2em 2em;\r\n}\r\n\r\n.single-item:hover::after {\r\n  top: -1rem;\r\n  font-size: 1rem;\r\n}<\/code><\/pre>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

Changing scale<\/h3>\n

If you’ve read pieces on animation<\/code> performance, then you’ve probably read it’s better to animate transforms instead of properties that impact layout, like offsets, margins, borders, paddings, dimensions — pretty much what we’ve used so far!<\/p>\n

The first issue that stands out here is that scaling an element also scales its corner rounding, as illustrated below:<\/p>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

We can get around this by also scaling the border-radius<\/code> the other way.<\/p>\n

Let’s say we scale an element by a factor $fx<\/code> along the x<\/var> axis and by a factor $fy<\/code> along the y<\/var> axis and we want to keep its border-radius<\/code> at a constant value $r<\/code>.<\/p>\n

This means we also need to divide $r<\/code> by the corresponding scaling factor along each axis.<\/p>\n

border-radius: #{$r\/$fx}\/ #{$r\/$fy};\r\ntransform: scale($fx, $fy)<\/code><\/pre>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

However, note that with this method, we need to use scaling factors, not amounts by which we expand our (pseudo-) element in this or that direction. Getting the scaling factors from the dimensions and expansion amounts is possible, but only if<\/em> they’re expressed in units that have a certain fixed relation between them. While preprocessors can mix units like in<\/code> or px<\/code> due to the fact that 1in<\/code> is always 96px<\/code>, they cannot resolve how much 1em<\/code> or 1%<\/code> or 1vmin<\/code> or 1ch<\/code> is in px<\/code> as they lack context. And calc()<\/code> is not a solution either, as it doesn’t allow us to divide a length value by another length value to get a unitless scale factor.<\/p>\n

This is why scaling is not a solution in the CodePen case, where the ::after<\/code> boxes have dimensions that depend on the viewport and, at the same time, expand by fixed rem<\/code> amounts.<\/p>\n

But if our scale amount is given or we can easily compute it, this is an option to consider, especially since making the scaling factors custom properties we then animate with a bit of Houdini magic can greatly simplify our code.<\/p>\n

border-radius: calc(#{$r}\/var(--fx))\/ calc(#{$r}\/var(--fy));\r\ntransform: scale(var(--fx), var(--fy))<\/code><\/pre>\n

Note that Houdini only works in Chromium browsers with the Experimental Web Platform features<\/strong> flag enabled.<\/p>\n

For example, we can create this tile grid animation:<\/p>\n

\n
Looping tile grid animation (Demo<\/a>, Chrome with flag only)<\/figcaption><\/figure>\n

The square tiles have an edge length $l<\/code> and with a corner rounding of $k*$l<\/code>:<\/p>\n

.tile {\r\n  width: $l;\r\n  height: $l;\r\n  border-radius: calc(#{$r}\/var(--fx))\/ calc(#{$r}\/var(--fy));\r\n  transform: scale(var(--fx), var(--fy))\r\n}<\/code><\/pre>\n

We register our two custom properties:<\/p>\n

CSS.registerProperty({\r\n  name: '--fx', \r\n  syntax: '<number>', \r\n  initialValue: 1, \r\n  inherits: false\r\n});\r\n\r\nCSS.registerProperty({\r\n  name: '--fy', \r\n  syntax: '<number>', \r\n  initialValue: 1, \r\n  inherits: false\r\n});<\/code><\/pre>\n

And we can then animate them:<\/p>\n

.tile {\r\n  \/* same as before *\/\r\n  animation: a $t infinite ease-in alternate;\r\n  animation-name: fx, fy;\r\n}\r\n\r\n@keyframes fx {\r\n  0%, 35% { --fx: 1 }\r\n  50%, 100% { --fx: #{2*$k} }\r\n}\r\n\r\n@keyframes fy {\r\n  0%, 35% { --fy: 1 }\r\n  50%, 100% { --fy: #{2*$k} }\r\n}<\/code><\/pre>\n

Finally, we add in a delay depending on the horizontal (--i<\/code>) and vertical (--j<\/code>) grid indices in order to create a staggered animation<\/code> effect:<\/p>\n

animation-delay: \r\n  calc((var(--i) + var(--m) - var(--j))*#{$t}\/(2*var(--m)) - #{$t}), \r\n  calc((var(--i) + var(--m) - var(--j))*#{$t}\/(2*var(--m)) - #{1.5*$t})<\/code><\/pre>\n

Another example is the following one, where the dots are created with the help of pseudo-elements:<\/p>\n

\n
Looping spikes animation (Demo<\/a>, Chrome with flag only)<\/figcaption><\/figure>\n

Since pseudo-elements get scaled together with their parents, we need to also reverse the scaling transform on them:<\/p>\n

.spike {\r\n  \/* other spike styles *\/\r\n  transform: var(--position) scalex(var(--fx));\r\n\r\n  &::before, &::after {\r\n    \/* other pseudo styles *\/\r\n    transform: scalex(calc(1\/var(--fx)));\r\n  }\r\n}<\/code><\/pre>\n

Changing… clip-path?!<\/h3>\n

This is a method I really like, even though it cuts out pre-Chromium Edge and Internet Explorer support.<\/p>\n

Pretty much every usage example of clip-path<\/code> out there has either a polygon()<\/code> value or an SVG reference value. However, if you’ve seen some of my previous articles, then you probably know there are other basic shapes we can use, like inset()<\/code>, which works as illustrated below:<\/p>\n

\n \"Illustration
How the inset()<\/code> function works. (Demo<\/a>)<\/figcaption><\/figure>\n

So, in order to reproduce the CodePen effect with this method, we set the ::after<\/code> offsets to the expanded state values and then cut out<\/a> what we don’t want to see with the help of clip-path<\/code>:<\/p>\n

.single-item::after {\r\n  top: -1rem;\r\n  right: -1rem;\r\n  bottom: -3em;\r\n  left: -1em;\r\n  clip-path: inset(2rem 0 2rem 2rem)\r\n}<\/code><\/pre>\n

And then, in the :hover<\/code> state, we zero all insets:<\/p>\n

.single-item:hover::after {\r\n  clip-path: inset(0)\r\n}<\/code><\/pre>\n

This can be seen in action below:<\/p>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

Alright, this works, but we also need a corner rounding. Fortunately, inset()<\/code> lets us specify that too as whatever border-radius<\/code> value we may wish.<\/p>\n

Here, a 10px<\/code> one for all corners along both directions does it:<\/p>\n

.single-item::after {\r\n  \/* same styles as before *\/\r\n  clip-path: inset(2rem 0 2rem 2rem round 10px)\r\n}\r\n\r\n.single-item:hover::after {\r\n  clip-path: inset(0 round 10px)\r\n}<\/code><\/pre>\n

And this gives us exactly what we were going for:<\/p>\n

See the Pen<\/a> by thebabydino (@thebabydino<\/a>) on CodePen<\/a>.<\/p>\n

Furthermore, it doesn’t really break anything in non-supporting browsers, it just always stays in the expanded state.<\/p>\n

However, while this is method that works great for a lot of situations — including the CodePen use case — it doesn’t work when our expanding\/shrinking elements have descendants that go outside their clipped parent’s border-box<\/code>, as it is the case for the last example given with the previously discussed scaling method.<\/p>\n","protected":false},"excerpt":{"rendered":"

\ufeffI’ve recently noticed an interesting change on CodePen: on hovering the pens on the homepage, there’s a rectangle with rounded corners expanding in the back.<\/p>\n","protected":false},"author":225572,"featured_media":295103,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"c2c_always_allow_admin_comments":false,"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[4],"tags":[612,518,478,1099,1147,1170],"jetpack_publicize_connections":[],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2019\/08\/background-boxes.png?fit=1200%2C600&ssl=1","jetpack-related-posts":[{"id":318332,"url":"https:\/\/css-tricks.com\/what-happens-when-border-radii-overlap\/","url_meta":{"origin":292177,"position":0},"title":"What Happens When Border Radii Overlap?","date":"August 17, 2020","format":false,"excerpt":"I\u2019d wager that most times we\u2019re rounding box corners in CSS, we\u2019re applying a uniform border-radius value across the border. It\u2019s a nice touch of polish in many designs. But there are times when we might want different radii for different corners. Easy, right? That way the property takes four\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/07\/border-radius-overlap.jpg?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":338290,"url":"https:\/\/css-tricks.com\/float-an-element-to-the-bottom-corner\/","url_meta":{"origin":292177,"position":1},"title":"Float an Element to the Bottom Corner","date":"April 19, 2021","format":false,"excerpt":"Need to lay out an element to the right or the left, such that text wraps around it? That\u2019s an easy task for the float property. But what about if you also want to push that element (let\u2019s call it an image) to one of the bottom corners while we\u2019re\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/04\/float-bottom-corner.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":287192,"url":"https:\/\/css-tricks.com\/restricting-a-pseudo-element-to-its-parents-border-box\/","url_meta":{"origin":292177,"position":2},"title":"Restricting a (pseudo) element to its parent’s border-box","date":"July 2, 2019","format":false,"excerpt":"Have you ever wanted to ensure that nothing of a (pseudo) element gets displayed outside its parent's border-box? In case you're having trouble picturing what that looks like, let's say we wanted to get the following result with minimal markup and avoiding brittle CSS. The desired result. This means we\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2019\/06\/pseudo-parent-boundary.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":261309,"url":"https:\/\/css-tricks.com\/breaking-css-box-shadow-vs-drop-shadow\/","url_meta":{"origin":292177,"position":3},"title":"Breaking down CSS Box Shadow vs. Drop Shadow","date":"October 20, 2017","format":false,"excerpt":"Drop shadows. Web designers have loved them for a long time to the extent that we used to fake them with PNG images before CSS Level 3 formally introduced them to the spec as the box-shadow property. I still reach for drop shadows often in my work because they add\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2017\/10\/box-drop-shadow-compare.png?fit=651%2C422&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":339203,"url":"https:\/\/css-tricks.com\/variable-aspect-ratio-card-with-conic-gradients-meeting-along-the-diagonal\/","url_meta":{"origin":292177,"position":4},"title":"Variable Aspect Ratio Card With Conic Gradients Meeting Along the Diagonal","date":"May 10, 2021","format":false,"excerpt":"I recently came across an interesting problem. I had to implement a grid of cards with a variable (user-set) aspect ratio that was stored in a --ratio custom property. Boxes with a certain aspect ratio are a classic problem in CSS and one that got easier to solve in recent\u2026","rel":"","context":"In "Article"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2021\/05\/hello-gorgeous.png?fit=1200%2C600&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]},{"id":317170,"url":"https:\/\/css-tricks.com\/irregular-shaped-links-with-subgrid\/","url_meta":{"origin":292177,"position":5},"title":"Irregular-shaped Links with Subgrid","date":"July 16, 2020","format":false,"excerpt":"Michelle Barker covers a situation where you need offset rectangles part of a clickable area. The tricky part is having just the rectangles be clickable. That rules out using some parent element and making the whole larger encompassing rectangle clickable, which is a common (but equally tricky) pattern. Kicking one\u2026","rel":"","context":"In "Link"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2020\/07\/irregular-shaped-links-with-subgrid-03a.jpg?fit=1200%2C602&ssl=1&resize=350%2C200","width":350,"height":200},"classes":[]}],"featured_media_src_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2019\/08\/background-boxes.png?fit=1024%2C512&ssl=1","_links":{"self":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/292177"}],"collection":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/users\/225572"}],"replies":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/comments?post=292177"}],"version-history":[{"count":9,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/292177\/revisions"}],"predecessor-version":[{"id":295565,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/292177\/revisions\/295565"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media\/295103"}],"wp:attachment":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media?parent=292177"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/categories?post=292177"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/tags?post=292177"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}