Loading video...

Video Failed to Load

Go Home

dynamic toggle with CSS :has() 🎁 .control:not(:has(:first-of-type:checked)) .indicator { translate: 100% 0; } leverage [type=radio]:checked with :has() to set the visuals 🤙

194,194 views • 1 year ago •via X (Twitter)

15 Comments

ℂ𝕠𝕕𝕖𝕩, ℍ𝕖𝕣𝕖's profile picture
ℂ𝕠𝕕𝕖𝕩, ℍ𝕖𝕣𝕖1 year ago

This looks nice, but from a UX perspective I don't want the interface changing. It should be visible for the user to easily discover there are more options with meaning.

jhey ▲🐻🎈's profile picture
jhey ▲🐻🎈1 year ago

for sure - don't doubt it 💯 was more so for the challenge of making it without scripts and using a radio button 🤙 saw this patterns as an action as well and don't think it's a good use case

Martin Bavio's profile picture
Martin Bavio1 year ago

Your stuff is starting to look out of a Minority Report scene. Mesmerizing.

Alfon's profile picture
Alfon1 year ago

Jenius! I wonder if this can be scaled for more than two items :P

3trax Gamer's profile picture
3trax Gamer1 year ago

Can we have code ?

Jorge Baumann.js's profile picture
Jorge Baumann.js1 year ago

neat

Arthur's profile picture
Arthur1 year ago

Gotta use it fr

Sean brydon's profile picture
Sean brydon1 year ago

Can we have a playground for this 👀

AkinAyo's profile picture
AkinAyo1 year ago

Love this

OC's profile picture
OC1 year ago

Loved it but do you think with framer motion it feels more natural as it has spring animations ?

Steve Lauda - Design Partner's profile picture
Steve Lauda - Design Partner1 year ago

pure css only Jhey? 😍

jhey ▲🐻🎈's profile picture
jhey ▲🐻🎈1 year ago

that's the one ✅🧑‍🍳 tricky part was working out a way to transition the premium indicator - so what I did was make it the little dot and then morph it up

Steve Lauda - Design Partner's profile picture
Steve Lauda - Design Partner1 year ago

I noticed that 😍 so sleek!

Dmazing Studio's profile picture
Dmazing Studio1 year ago

Your design choices feel intentional and smart! 💎✨️

Tyler Durden's profile picture
Tyler Durden1 year ago

Bonito demais sooo!!

Related Videos

CSS Tip! 💪 You can create these tab controls with CSS :has() + radio buttons ✨ .tabs:has(input:nth-of-type(3)) { --count: 3; } .tabs:has(:checked:nth-of-type(3)) { --active: 2; } .tabs::after { translate: calc(var(--active, 0) * 100%) 0; width: calc(100% / var(--count)); } Two CSS :has() tricks here combined with a rendering trick 🤙 The tab control is a container using display: grid. You can use :has() to count the number of tabs in the container: .tabs:has(input:nth-of-type(3)) { --count: 3; } .tabs:has(input:nth-of-type(4)) { --count: 4; } Using the cascade, the last valid :has() gives you the number of tabs 🫶 Once you know the number of tabs, you know how to size the indicator: .tabs::after { content: ""; position: absolute; height: 100%; width: calc(100% / var(--count)); } It's a pseudoelement that uses --count to determine its size 📏 The next :has() trick is determining which tab is active or :checked as it's an input [type=radio] .tabs:has(:checked:nth-of-type(2)) { --active: 1; } .tabs:has(:checked:nth-of-type(3)) { --active: 2; } You can use a zero-indexed translation here. If the second input is :checked, set --active: 1, then translate the pseudoelement on the tabs to that position 👉 .tabs::after { translate: calc(var(--active, 0) * 100%) 0; } The last rendering trick is using mix-blend-mode 👀 The tabs have a black background-color, the pseudoelement is white, and the label text is white. When you use mix-blend-mode: difference on the pseudoelement it will give this effect that the text transitions from white to black sliding across 😎 .tabs::after { color: hsl(0 0% 100%); mix-blend-mode: difference; } You can totally mix up the colors here though and go with a different effect. The mechanics of how you can use CSS :has() is the main point here 🙏 As always, any questions, suggestions, etc. let me know CodePen.IO link below! 👇 (There's even a Tailwind CSS play for this one too 👀)

jhey ʕ•ᴥ•ʔ

437,300 views • 2 years ago

CSS Trick 🤙 You can create these tab bar controls by using :has() to count the number of tabs ⭐️ .tabs:has(input:nth-of-type(3)){--count: 3;} .tabs:has(:checked:nth-of-type(3)){--active: 200%;} .tabs::after{ translate:var(--active) 0;} Let's break it down in this ! 📼 Couple of CSS :has() tricks here combined with custom properties 😎 First things first, lay out the tabs using display: grid. This gives you a way to create equal-width tabs 🙏 .tabs { display: grid; grid-auto-flow: column; } Then you use :has() to count the number of tabs and store that in a custom property 🤓 .tabs:has(input:nth-of-type(3)) { --count: 3; } .tabs:has(input:nth-of-type(4)) { --count: 4; } Using the cascade, the last valid :has() gives you the number of tabs 🫶 Using the tab count, you can size the tab indicator. For the tab indicator, use the tabs pseudoelement: .tabs::after { content: ""; position: absolute; height: 100%; width: calc(100% / var(--count)); } See how you can use --count to determine its size 📏 Next, use :has() to determine which tab is active or :checked with input [type=radio] .tabs:has(:checked:nth-of-type(2)) { --active: 1; } .tabs:has(:checked:nth-of-type(3)) { --active: 2; } You can use a zero-indexed translation here. If the second input is :checked, set --active: 1, then translate the pseudoelement on the tabs to that position 👉 .tabs::after { translate: calc(var(--active, 0) * 100%) 0; } Or you could set active to the translation: .tabs:has(:checked:nth-of-type(2)) { --active: 100%; } Setting the custom property allows you to use the index elsewhere if you need it 🤙 The final piece is using mix-blend-mode 👀 The tabs have a black background-color, the pseudoelement is white, and the label text is white. When you use mix-blend-mode: difference on the pseudoelement it will give this effect that the text transitions from white to black sliding across 😎 .tabs::after { color: hsl(0 0% 100%); mix-blend-mode: difference; } You can totally mix up the colors here though and go with a different effect. The mechanics of how you can use CSS :has() is the main point here 🙏 As always, any questions, suggestions, etc. let me know CodePen.IO link below! 👇 (There's even a Tailwind CSS play for this one too 👀)

jhey ʕ•ᴥ•ʔ

70,670 views • 2 years ago

CSS Tip! 🎠 You can create a responsive infinite marquee animation with container queries and no duplicate items 🤙 li{ animation: slide; } @​keyframes slide { to { translate: 0% calc(var(--i) * -100%);}} The trick is animating the items, not the list 😎 More tricks 👇 To get this one working, you need to animate the items and not the list (Watch the video first?). Each item needs to know its row index (--i) in the list and the parent needs to know how many rows are in the list: ul { --count: 12; } li:nth-of-type(1), li:nth-of-type(2) { --i: 0; } li:nth-of-type(3), li:nth-of-type(4) { --i: 1; } Once you have that, translate each item based on its row index in the list li { translate: 0% calc((var(--count) - var(--i)) * 100%); } Now for the animation. The key here is that each row has an animation-delay calculated from its index (--i). That number is offset to make it negative so the animation start is offset ✨ ul { --duration: 10s; } li { --delay: calc((var(--duration) / var(--count)) * (var(--i) - 8)); animation: slide var(--duration) var(--delay) infinite linear; } Make sure to wrap that animation in: @​media (prefers-reduced-motion: no-preference) { ... } Lastly, the fun parts! 🤓 To create the "vignette" mask. Use a layered mask on the container 😷 .scene { --buff: 3rem; height: 100%; width: 100%; mask: linear-gradient(transparent, white var(--buff) calc(100% - var(--buff)), transparent), linear-gradient(90deg, transparent, white var(--buff) calc(100% - var(--buff)), transparent); mask-composite: intersect; } To create the 3D skewed effect, use a chained transform (Try toggling it in the demo ⚡️): .grid { transform: rotateX(20deg) rotateZ(-20deg) skewX(20deg); } As for the responsive part, use container queries! 🔥 article { container-type: inline-size; } When the article (card) is narrower than 400px update the grid and animation settings 🤙 Double the rows means double the duration! @​container (width < 400px) { .grid { --count: 12; grid-template-columns: 1fr; } li:nth-of-type(1) { --i: 0; } li:nth-of-type(2) { --i: 1; } li:nth-of-type(3) { --i: 2; } li:nth-of-type(4) { --i: 3; } li { --duration: 20s; } } CSS has the magic to be able to update those animations at runtime based on your custom property values 😎 An added bonus in this demo is that it doesn't require any JavaScript at all, for any of it 🤯 We can use CSS :has() for those toggles that update the styles, even the theme toggle! 🫶 Any questions, let me know! Make sure to check out the video. Will do a walkthrough one to follow-up 🤙 CodePen.IO link below! 👇

jhey ʕ•ᴥ•ʔ

541,845 views • 2 years ago