Found a neat CSS trick to automatically swap between black or white text based on any arbitrary background color!
With relative color syntax, you can use calc to adjust the text color depending if the background is above or below 50% lightness in the LCH color space.
私の作ったロゴ達の透過画像が欲しい方はこのリンクのOneDriveに入れていますので都度確認してください🫠🫠🫠
If you want transparent images of my logos, I have them in OneDrive at this link.
Please check back each time.
↓↓↓↓↓↓↓↓
https://t.co/wKCz9OVdaQ
CSS Tip! 🤯
You can create a CSS-only version of this balance slider using a scroll animation on the underlying input[type=range] 🚀
::-webkit-slider-thumb {
view-timeline: --thumb inline;
}
Scroll animation driven by the slider thumb animates a number between the "min" and "max" of the range 😅
@property --value {
inherits: true;
initial-value: 0;
syntax: '<integer>';
}
@keyframes sync { to { --value: 100; }}
Tie that up to the contain animation-range ⚡️
.control {
animation: sync both linear reverse;
animation-timeline: --thumb;
animation-range: contain;
}
Hoist the view timeline so all the parts of the control can use it!
.control { timeline-scope: --thumb; }
Use that value in a counter which is used for the labels. Create a low and a high for each side 😇
.control__label {
counter-reset:
low var(--value)
high calc(100 - var(--value));
}
.control__label::before {
content: "COFFEE " counter(low) "%";
}
.control__label::after {
content: counter(high) "% MILK";
}
That's the magic of updating the label values ✨
For the big track, it's a fake track. You can make use of the same --value property and do some calc() to work out the width of each part.
<div class="control__track">
<div class="control__indicator"></div>
</div>
.control__track::before {
width: calc(var(--value) * 1% - 0.5rem);
background: var(--coffee);
border-radius: 4px;
transition: width 0.1s;
}
The width leaves a little gap for the indicator piece 🤙
The color calculation for --coffee isn't too wild but again you can use the same --value
.control__track {
--coffee: hsl(24 74% calc(
24% + (30% * ((100 - var(--value, 0)) / 100)) / 1
) / 0.4);
}
Now for the last piece. Making the track change height. You could set up another custom property and animate its value using the --thumb timeline too 🔥
@property --shift {
initial-value: 0;
inherits: true;
syntax: '<integer>';
}
@keyframes shift {
0%, 31%, 61%, 100% { --shift: 0; }
32%, 60% { --shift: 1; }
}
Then use that --shift to update the translation of the label and height of the track 🤓
.label {
transform: translateY(calc(var(--shift) * 50%));
transition: transform var(--speed) var(--timing);
}
Cool part here is that you can use the control to work out the @keyframes percentages 😅
Oh. And the timing for that little bounce? Use the linear() function 😎
:root {
--timing: linear(
0, 0.5007 7.21%, 0.7803 12.29%,
0.8883 14.93%, 0.9724 17.63%,
1.0343 20.44%, 1.0754 23.44%,
1.0898 25.22%, 1.0984 27.11%,
1.1014 29.15%, 1.0989 31.4%,
1.0854 35.23%, 1.0196 48.86%,
1.0043 54.06%, 0.9956 59.6%,
0.9925 68.11%, 1
);
}
Should probably do a video on this one. Lots of little custom property tricks for sure! 💯 It's not too far off the range slider with the tooltip that came up previously
As always, any questions, let me know! Also, this one only works in Chrome currently ✅🥲
This one's a bit rocket science ha 🚀
@CodePen link below! 👇
Intentional CSS :hover effects 🎈
Add a small transition-delay so you know the :hover is intentional. See the difference below 🎞️
a::after { transition: scale 0.2s ease-in-out; }
vs.
a::after { transition: scale 0.2s 0.125s ease-in-out; }
Can be useful for some designs 🤙
those little details™
Scale up and down on scroll with CSS 📜✨
a {
animation: scale, scale;
animation-direction: normal, reverse;
animation-timeline: view(inline);
animation-range: entry, exit;
}
@keyframes scale { 0% { scale: 0; } }
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 link below! 👇
Future CSS Tip! 🍏
You can create that Apple style blow out text effect with CSS scroll-driven animations ⚡️ No JavaScript!
The trick is to animate SVG text along the z-axis using 3D transforms 👀
.word {
animation: blow-out both ease-in;
animation-timeline: --section; 👈
animation-range: exit-crossing 10% exit 0%;
}
keyframes blow-out {
to { transform: translate3d(0.05ch, 0, 99vh); }
}
The animation makes the text cover the viewport and blends into the next section which has the same background-color 🤙
The section is set to 200vh. But the wrapper around the SVG text defines perspective and transform-style
.section__content {
transform-style: preserve-3d; 👈
perspective: 100vh;
}
We're using an SVG to maintain the crispness of the text as it scales up ✨ Use these attributes on the <text> within the SVG to align things 🤙
<text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle">
Your Name
</text>
It's the last day of #AppleEvent week but I do have some more of these I'll put together next week 😉
@CodePen link below! 👇
CSS Tip! ✨
You ever tried to use CSS transforms on part of an SVG icon and it didn't work? You likely needed this 👇
button rect { transform-box: fill-box; }
Combine with individual transform properties and transition delays to animate your SVG icons! 🎬
This SVG is made up of three rect(angles) that make up the menu icon 📝
button rect {
transform-box: fill-box;
transform-origin: 50% 50%;
}
The transform-box provides a way for us to define the box to which transforms are relative and gives us control over the transform-origin 💪
When you click your button, you can toggle the aria-pressed attribute. When true, update the line positions 🤙
[aria-pressed=true] rect:nth-of-type(1) {
translate: 0 333%;
rotate: -45deg;
}
[aria-pressed=true] rect:nth-of-type(2) {
rotate: 45deg;
}
[aria-pressed=true] rect:nth-of-type(3) {
translate: 0 -333%;
rotate: 45deg;
}
The magic part is defining transition-delay for both states ✨
When the button is pressed, translate the lines first and then rotate them
[aria-pressed=true] rect {
transition: translate 0.2s 0s, rotate 0.2s 0.3s;
}
When the button isn't pressed, rotate the lines first and then translate them to their original position
rect {
transition: rotate 0.2s 0s, translate 0.2s 0.3s;
}
The added "Trick" uses linear() if supported to get that springy easing effect 🐇
That's it! No need to create a hamburger menu with three <div>s. Use an SVG and CSS that will scale nicely at any size 🫶
@CodePen link below! 👇
Future CSS Tip! 🔮
You can create this scrolling image pop-out effect with CSS scroll-driven animations and zero JS! 🔥
.pop {
view-timeline-name: --pop;
}
img {
animation: slide both;
animation-timeline: --pop;
animation-range: entry 100% cover 50%;
}
.skateboarder {
--x: 0;
--y: -45%;
}
@ keyframes slide {
to { translate: var(--x) var(--y); }
}
The "trick" is to layer up two image elements and keep one clipped by the container 🤙
Slide them both together and it will look like one pops out of the container 🎈 You can leverage scoped custom properties to translate by different distances too! 😎
You can use macOS's built-in image background removal tool to get the pop-out image ✨ And for added effect, you can animate the brightness on that to make it stand out ⭐️
@CodePen link below! 👇
My side project with @ccescribano is a platform to build events (ticketing and event agenda). It's been useful to @commitconf (+100 speakers per event) and @CriteoEng, give it a try and let us know what you think!
Cómo detectas que alguien se ha saltado tu seguridad? Contenedores sirviendo conexiones SSH? Máquinas virtuales procesando crypto? Tu servidor web está intentando encriptar el bucket donde están tus copias de backup? Estamos buscando gente que sepa de #seguridad y quiera compartir su experiencia. El Call for Papers de Commit 2024 está abierto: https://t.co/fzL6MFHweD
Future CSS Tip! ⚡️
You can combine CSS :has() & :user-valid/:user-invalid to power <form> micro interactions 😍
No JavaScript. Watch for the little animations ✨
label {
--color: var(--default);
color: var(--color);
border-color: var(--color);
}
.group:has(:user-valid) {
--color: var(--valid);
}
.group:not(:focus-within):has(:user-invalid) {
--color: var(--invalid);
}
These new pseudo-elements only fire once an input has been interacted with. This means you don't need to do things like the "transparent :placeholder-shown" hack from before 🙌
Then you can lean into the power of scoped custom properties and change the theme based on the status of a form group 😎
It's a nice little touch. But remember, don't rely on color to communicate things!
@CodePen link below! 👇