{"id":3861,"date":"2024-09-16T14:56:52","date_gmt":"2024-09-16T19:56:52","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=3861"},"modified":"2024-09-17T08:54:43","modified_gmt":"2024-09-17T13:54:43","slug":"selecting-previous-siblings","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/selecting-previous-siblings\/","title":{"rendered":"Selecting Previous Siblings"},"content":{"rendered":"\n<p>I saw a video going around recently with a title that makes me feel very old: <a href=\"https:\/\/www.youtube.com\/watch?v=P80sM7ausCA&amp;t=9s\">&#8220;Crazy CSS Using By Master CSS&#8221;<\/a>. The voice in it is also weird\/fake\/AI or something? Part of me finds it annoying and part of me finds it cool as I have a feeling English probably isn&#8217;t the creators native language and by making it English through the power of technology the video probably gets a broader audience. I guess it&#8217;s proof that if you make something super cool, the title doesn&#8217;t matter. <\/p>\n\n\n\n<p>Look at me \u2014 I&#8217;ve digressed before I even started.<\/p>\n\n\n\n<p>The demo they build in the video is fantastically cool. It&#8217;s a row of cards with a hover effect. The card being hovered has an effect where is appears to move toward you in 3D. Not only that, the two cards <em>after<\/em> the hovered card have their own unique effects, enhancing the 3D. But the ultimate touch is that the two cards <em>before<\/em> the hovered card also do, which has long been in &#8220;impossible&#8221; territory in CSS. No more!<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"532\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/09\/Screenshot-2024-09-16-at-12.29.12%E2%80%AFPM.png?resize=1024%2C532&#038;ssl=1\" alt=\"\" class=\"wp-image-3866\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/09\/Screenshot-2024-09-16-at-12.29.12%E2%80%AFPM.png?resize=1024%2C532&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/09\/Screenshot-2024-09-16-at-12.29.12%E2%80%AFPM.png?resize=300%2C156&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/09\/Screenshot-2024-09-16-at-12.29.12%E2%80%AFPM.png?resize=768%2C399&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/09\/Screenshot-2024-09-16-at-12.29.12%E2%80%AFPM.png?resize=1536%2C799&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/09\/Screenshot-2024-09-16-at-12.29.12%E2%80%AFPM.png?resize=2048%2C1065&amp;ssl=1 2048w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/09\/Screenshot-2024-09-16-at-12.29.12%E2%80%AFPM.png?w=3000&amp;ssl=1 3000w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">Screengrab of the awesome hover effect from the video. <\/figcaption><\/figure>\n\n\n\n<p>Let&#8217;s cover that selector approach in CSS.<\/p>\n\n\n\n<p>The element being hovered. Simple.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.card<\/span><span class=\"hljs-selector-pseudo\">:hover<\/span> {\n\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now the next element after it:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.card<\/span><span class=\"hljs-selector-pseudo\">:hover<\/span> + <span class=\"hljs-selector-class\">.card<\/span> {\n\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>To be a bit more flexible and write a bit less code, we could write that like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.card<\/span> {\n  &amp;:hover {\n\n    <span class=\"hljs-comment\">\/* next card *\/<\/span>\n    &amp; + * {\n\n    }\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And we can keep going to the 2nd card <em>after<\/em> the hovered card:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.card<\/span> {\n  &amp;:hover {\n\n    <span class=\"hljs-comment\">\/* next card *\/<\/span>\n    &amp; + * {\n\n    }\n    <span class=\"hljs-comment\">\/* next card after that *\/<\/span>\n    &amp; + * + * {\n\n    }\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>So now we have selected the hovered card itself and essentially the next two to the right. What about those cards on the left? The &#8220;previous siblings&#8221;, as it were?<\/p>\n\n\n\n<p>Consider what we&#8217;ve already done. We&#8217;ve proven we can select the <em>next<\/em> card after a selector. So what if we could say &#8220;<em>if<\/em> the next card is hovered, select me?&#8221;. That&#8217;s what we can do with <code>:has()<\/code> now in CSS. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.card<\/span> {\n  &amp;:hover {\n\n    <span class=\"hljs-comment\">\/* Select previous sibling! *\/<\/span>\n    :has(+ &amp;) {\n\n    }\n  }\n}\n  <\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>If you find the nesting confusing, it&#8217;s just like this<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-pseudo\">:has(+<\/span> <span class=\"hljs-selector-class\">.card<\/span><span class=\"hljs-selector-pseudo\">:hover)<\/span> {\n\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Which says &#8220;any element where the <em>next<\/em> element is a hovered card.&#8221;<\/p>\n\n\n\n<p>I&#8217;ll go one level deeper there, and combine it all:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.card<\/span> {\n  &amp;:hover {\n\n    <span class=\"hljs-comment\">\/* next card *\/<\/span>\n    &amp; + * {\n\n    }\n    <span class=\"hljs-comment\">\/* next card after that *\/<\/span>\n    &amp; + * + * {\n\n    }\n\n    <span class=\"hljs-comment\">\/* previous card *\/<\/span>\n    <span class=\"hljs-selector-pseudo\">:has(+<\/span> &amp;) {\n\n    }\n    <span class=\"hljs-comment\">\/* previous card before that *\/<\/span>\n    <span class=\"hljs-selector-pseudo\">:has(+<\/span> * + &amp;) {\n\n    }\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The selector part is the hard part here. Now that we&#8217;ve got that going, the styling could be anything. Keeping it a smidge simple at first, I&#8217;ll apply <code>scale(1.3)<\/code> to the hovered element, <code>scale(1.2)<\/code> to the next &amp; previous sibling, then <code>scale(1.1)<\/code> to the next &amp; previous siblings after that. Plus a smidge of brightness filtering and we get:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_yLddLOj\" src=\"\/\/codepen.io\/anon\/embed\/yLddLOj?height=450&amp;theme-id=47434&amp;slug-hash=yLddLOj&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed yLddLOj\" title=\"CodePen Embed yLddLOj\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>(Just to prove how cool and smart I am, <a href=\"https:\/\/codepen.io\/chriscoyier\/pen\/qBoogaX\">here&#8217;s me playing with this very idea two years ago<\/a>.)<\/p>\n\n\n\n<p>The video used some even cooler CSS to apply to the sibling cards, beginning with some 3D transform setup. The big idea was to use <code>translateZ()<\/code> rather than <code>scale()<\/code> to make the cards bigger (e.g. bring them &#8220;closer to the viewer&#8221;) which has a cool side effect of having an origin point that the cards appear to move away from. Coupled with a little <code>rotateY()<\/code> on the siblings, the effect makes what looks like a mound of cards that follows the cursor. Bravo!<\/p>\n\n\n\n<p>I didn&#8217;t see a live demo attached to the video in any way, so here&#8217;s my own re-creation (<a href=\"https:\/\/codepen.io\/chriscoyier\/full\/zYVXQdL\">view big<\/a>):<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_zYVXQdL\" src=\"\/\/codepen.io\/anon\/embed\/zYVXQdL?height=450&amp;theme-id=47434&amp;slug-hash=zYVXQdL&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed zYVXQdL\" title=\"CodePen Embed zYVXQdL\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>And a video in case you&#8217;re not on a device with a fine pointer.<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='299' src='https:\/\/videopress.com\/embed\/4tyJz1ng?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=1&amp;muted=1&amp;persistVolume=0&amp;playsinline=1&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1725245713'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Selecting the *next* element in CSS can be done easily with the + combinator. Selecting the *previous* element used to be impossible, but now can be like :has(+ .el), and can be extended in either direction.<\/p>\n","protected":false},"author":1,"featured_media":3871,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[40,7,242],"class_list":["post-3861","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-has","tag-css","tag-css-combinators"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/09\/cards.jpg?fit=1200%2C762&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/3861","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=3861"}],"version-history":[{"count":7,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/3861\/revisions"}],"predecessor-version":[{"id":3872,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/3861\/revisions\/3872"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/3871"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=3861"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=3861"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=3861"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}