Jhey ʕ·ᴥ· ʔ
@jhey.dev
⚡️ makin' ideas click
👨🍳 staff design engineer @shopify (ex @google, @vercel 🫡)
📚 making ui/ux course craftofui.dev
👰♀️ @seaotta.dev
👨🍳 staff design engineer @shopify (ex @google, @vercel 🫡)
📚 making ui/ux course craftofui.dev
👰♀️ @seaotta.dev
super easy CSS sticky scroll hide/reveal 🧑🍳
.sticky-atc {
position: fixed;
inset: auto 0 0 0;
transition: translate 0.26s cubic-bezier(.55,0,.67,1.25);
/* hide when scrolling down */
@container scroll-state(scrolled: bottom) {
translate: 0 100%;
transition-delay: 0.5s;
}
}
.sticky-atc {
position: fixed;
inset: auto 0 0 0;
transition: translate 0.26s cubic-bezier(.55,0,.67,1.25);
/* hide when scrolling down */
@container scroll-state(scrolled: bottom) {
translate: 0 100%;
transition-delay: 0.5s;
}
}
October 22, 2025 at 11:22 PM
super easy CSS sticky scroll hide/reveal 🧑🍳
.sticky-atc {
position: fixed;
inset: auto 0 0 0;
transition: translate 0.26s cubic-bezier(.55,0,.67,1.25);
/* hide when scrolling down */
@container scroll-state(scrolled: bottom) {
translate: 0 100%;
transition-delay: 0.5s;
}
}
.sticky-atc {
position: fixed;
inset: auto 0 0 0;
transition: translate 0.26s cubic-bezier(.55,0,.67,1.25);
/* hide when scrolling down */
@container scroll-state(scrolled: bottom) {
translate: 0 100%;
transition-delay: 0.5s;
}
}
small details that matter 🧑🍳
use CSS pseudoelements to bridge the :hover gap w/ :has() + inset
a:has(+ .dots) a::after,
.dots + a::after {
position: absolute;
content: '';
}
a:has(+ .dots) a::after { inset: 0 -50% 0 100%; }
.dots + a::after { inset: 0 100% 0 -50%; }
use CSS pseudoelements to bridge the :hover gap w/ :has() + inset
a:has(+ .dots) a::after,
.dots + a::after {
position: absolute;
content: '';
}
a:has(+ .dots) a::after { inset: 0 -50% 0 100%; }
.dots + a::after { inset: 0 100% 0 -50%; }
October 22, 2025 at 4:54 PM
small details that matter 🧑🍳
use CSS pseudoelements to bridge the :hover gap w/ :has() + inset
a:has(+ .dots) a::after,
.dots + a::after {
position: absolute;
content: '';
}
a:has(+ .dots) a::after { inset: 0 -50% 0 100%; }
.dots + a::after { inset: 0 100% 0 -50%; }
use CSS pseudoelements to bridge the :hover gap w/ :has() + inset
a:has(+ .dots) a::after,
.dots + a::after {
position: absolute;
content: '';
}
a:has(+ .dots) a::after { inset: 0 -50% 0 100%; }
.dots + a::after { inset: 0 100% 0 -50%; }
the only atlas you should be invested in. touch grass.
October 22, 2025 at 12:55 PM
the only atlas you should be invested in. touch grass.
apple-style disclosure w/ CSS details + summary 👨🍳
::details-content {
interpolate-size: allow-keywords;
transition: content-visibility, height;
transition-behavior: allow-discrete;
}
[open]::details-content { height: fit-content; }
CSS :has() handling the image swaps 🤙
::details-content {
interpolate-size: allow-keywords;
transition: content-visibility, height;
transition-behavior: allow-discrete;
}
[open]::details-content { height: fit-content; }
CSS :has() handling the image swaps 🤙
October 22, 2025 at 9:45 AM
apple-style disclosure w/ CSS details + summary 👨🍳
::details-content {
interpolate-size: allow-keywords;
transition: content-visibility, height;
transition-behavior: allow-discrete;
}
[open]::details-content { height: fit-content; }
CSS :has() handling the image swaps 🤙
::details-content {
interpolate-size: allow-keywords;
transition: content-visibility, height;
transition-behavior: allow-discrete;
}
[open]::details-content { height: fit-content; }
CSS :has() handling the image swaps 🤙
easing is everything
use different CSS timing functions per axis for motion paths 👨🍳
.p {
animation: x .8s var(--x-time), y .8s var(--y-time);
animation-composition: accumulate; 👈
}
@keyframes x { to { translate: var(--x) 0 }
@keyframes y { to { translate: 0 var(--y) }
use different CSS timing functions per axis for motion paths 👨🍳
.p {
animation: x .8s var(--x-time), y .8s var(--y-time);
animation-composition: accumulate; 👈
}
@keyframes x { to { translate: var(--x) 0 }
@keyframes y { to { translate: 0 var(--y) }
October 19, 2025 at 4:49 PM
easing is everything
use different CSS timing functions per axis for motion paths 👨🍳
.p {
animation: x .8s var(--x-time), y .8s var(--y-time);
animation-composition: accumulate; 👈
}
@keyframes x { to { translate: var(--x) 0 }
@keyframes y { to { translate: 0 var(--y) }
use different CSS timing functions per axis for motion paths 👨🍳
.p {
animation: x .8s var(--x-time), y .8s var(--y-time);
animation-composition: accumulate; 👈
}
@keyframes x { to { translate: var(--x) 0 }
@keyframes y { to { translate: 0 var(--y) }
progressive css-only pagination indicators w/ anchor positioning 👨🍳
nav::before { position-anchor: --h; }
nav::before { left: anchor(left); }
a:hover { anchor-name: --h; }
use the nav pseudoelements, ::before for intent, ::after for current 🤙
nav::before { position-anchor: --h; }
nav::before { left: anchor(left); }
a:hover { anchor-name: --h; }
use the nav pseudoelements, ::before for intent, ::after for current 🤙
October 16, 2025 at 11:05 AM
progressive css-only pagination indicators w/ anchor positioning 👨🍳
nav::before { position-anchor: --h; }
nav::before { left: anchor(left); }
a:hover { anchor-name: --h; }
use the nav pseudoelements, ::before for intent, ::after for current 🤙
nav::before { position-anchor: --h; }
nav::before { left: anchor(left); }
a:hover { anchor-name: --h; }
use the nav pseudoelements, ::before for intent, ::after for current 🤙
you ever take an enneagram test? we did them for an onsite last week. honestly answered a lot of things for me 💯 high recommend
type 3
type 3
October 16, 2025 at 10:47 AM
you ever take an enneagram test? we did them for an onsite last week. honestly answered a lot of things for me 💯 high recommend
type 3
type 3
Reposted by Jhey ʕ·ᴥ· ʔ
Putting CSS Anchor Positioning to the test by throwing dropdown menus around 💪
[popover].dropdown {
top: anchor(bottom);
right: anchor(right);
position-try-fallbacks: flip-block,flip-inline;
}
That's it 👆 Keeps the menu tethered to the button
(may as well bring over some hits from the other place)
[popover].dropdown {
top: anchor(bottom);
right: anchor(right);
position-try-fallbacks: flip-block,flip-inline;
}
That's it 👆 Keeps the menu tethered to the button
(may as well bring over some hits from the other place)
November 21, 2024 at 12:18 PM
Putting CSS Anchor Positioning to the test by throwing dropdown menus around 💪
[popover].dropdown {
top: anchor(bottom);
right: anchor(right);
position-try-fallbacks: flip-block,flip-inline;
}
That's it 👆 Keeps the menu tethered to the button
(may as well bring over some hits from the other place)
[popover].dropdown {
top: anchor(bottom);
right: anchor(right);
position-try-fallbacks: flip-block,flip-inline;
}
That's it 👆 Keeps the menu tethered to the button
(may as well bring over some hits from the other place)
Reposted by Jhey ʕ·ᴥ· ʔ
Opportunities will appear in the place you dedicate your energy to.
The results will not always be immediate but dedication and perfecting whatever your craft is and showing up publicly does lead to new doors.
You have to show up how you want to be perceived though.
The results will not always be immediate but dedication and perfecting whatever your craft is and showing up publicly does lead to new doors.
You have to show up how you want to be perceived though.
October 15, 2025 at 4:00 PM
Opportunities will appear in the place you dedicate your energy to.
The results will not always be immediate but dedication and perfecting whatever your craft is and showing up publicly does lead to new doors.
You have to show up how you want to be perceived though.
The results will not always be immediate but dedication and perfecting whatever your craft is and showing up publicly does lead to new doors.
You have to show up how you want to be perceived though.
cross-browser liquid toggle 👨🔬
CSS + SVG filter + drag mechanics 🧑🍳
(exploding view 👇)
CSS + SVG filter + drag mechanics 🧑🍳
(exploding view 👇)
October 15, 2025 at 3:05 PM
cross-browser liquid toggle 👨🔬
CSS + SVG filter + drag mechanics 🧑🍳
(exploding view 👇)
CSS + SVG filter + drag mechanics 🧑🍳
(exploding view 👇)
responsive CSS pinned sidebar transition 📌
.layout:has(:popover-open) {
grid-template-columns: var(--sidebar-width) 1fr;
}
aside:popover-open {
translate: 0 var(--ctrl);
height: var(--extend);
}
actual zero JS for the layout transition here
so many details to play with! 🧑🍳
.layout:has(:popover-open) {
grid-template-columns: var(--sidebar-width) 1fr;
}
aside:popover-open {
translate: 0 var(--ctrl);
height: var(--extend);
}
actual zero JS for the layout transition here
so many details to play with! 🧑🍳
October 14, 2025 at 2:01 PM
responsive CSS pinned sidebar transition 📌
.layout:has(:popover-open) {
grid-template-columns: var(--sidebar-width) 1fr;
}
aside:popover-open {
translate: 0 var(--ctrl);
height: var(--extend);
}
actual zero JS for the layout transition here
so many details to play with! 🧑🍳
.layout:has(:popover-open) {
grid-template-columns: var(--sidebar-width) 1fr;
}
aside:popover-open {
translate: 0 var(--ctrl);
height: var(--extend);
}
actual zero JS for the layout transition here
so many details to play with! 🧑🍳
add to cart 🛒
configurable css + svg 👇
configurable css + svg 👇
October 9, 2025 at 10:16 AM
add to cart 🛒
configurable css + svg 👇
configurable css + svg 👇
cursor tracking on 3D CSS surfaces 👨🍳
inverse projection mappin' to calculate proximity in 3D space and apply an eased 0-1 value 🤙
the key: measuring the 4 corners, check the reveal 👇
(ideal for 2D canvas w/ 3D transforms)
inverse projection mappin' to calculate proximity in 3D space and apply an eased 0-1 value 🤙
the key: measuring the 4 corners, check the reveal 👇
(ideal for 2D canvas w/ 3D transforms)
October 9, 2025 at 10:13 AM
cursor tracking on 3D CSS surfaces 👨🍳
inverse projection mappin' to calculate proximity in 3D space and apply an eased 0-1 value 🤙
the key: measuring the 4 corners, check the reveal 👇
(ideal for 2D canvas w/ 3D transforms)
inverse projection mappin' to calculate proximity in 3D space and apply an eased 0-1 value 🤙
the key: measuring the 4 corners, check the reveal 👇
(ideal for 2D canvas w/ 3D transforms)
🚨💚🤙 BREAKING: Jhey Tompkins to Shopify, here we go! Agreement in place between all parties involved
July 25, 2025 at 11:56 PM
🚨💚🤙 BREAKING: Jhey Tompkins to Shopify, here we go! Agreement in place between all parties involved
little details: hold to confirm signature ✍️
signature playback mirrors the timing of your strokes
not just what you drew, but how you drew it, retaining the feel and personality
signature playback mirrors the timing of your strokes
not just what you drew, but how you drew it, retaining the feel and personality
July 11, 2025 at 7:54 PM
little details: hold to confirm signature ✍️
signature playback mirrors the timing of your strokes
not just what you drew, but how you drew it, retaining the feel and personality
signature playback mirrors the timing of your strokes
not just what you drew, but how you drew it, retaining the feel and personality
SVG filters provide a basic lighting system you can sync with a little JavaScript and use in your web apps 💡
... not as powerful as MacOS implementation but pretty cool
... not as powerful as MacOS implementation but pretty cool
July 8, 2025 at 8:46 PM
SVG filters provide a basic lighting system you can sync with a little JavaScript and use in your web apps 💡
... not as powerful as MacOS implementation but pretty cool
... not as powerful as MacOS implementation but pretty cool
you can just <select> things 🧑🍳
custom select with CSS (progressively enhanced)
select {
&,
&::picker(select) { appearance: base-select; }
}
custom select with CSS (progressively enhanced)
select {
&,
&::picker(select) { appearance: base-select; }
}
July 8, 2025 at 6:12 PM
you can just <select> things 🧑🍳
custom select with CSS (progressively enhanced)
select {
&,
&::picker(select) { appearance: base-select; }
}
custom select with CSS (progressively enhanced)
select {
&,
&::picker(select) { appearance: base-select; }
}
obligatory "new role, new macbook" post
wHAt dO i inSTaLl fiRsT?
i jus' run a node script and it does it all for me 🧑🍳
wHAt dO i inSTaLl fiRsT?
i jus' run a node script and it does it all for me 🧑🍳
July 8, 2025 at 6:10 PM
obligatory "new role, new macbook" post
wHAt dO i inSTaLl fiRsT?
i jus' run a node script and it does it all for me 🧑🍳
wHAt dO i inSTaLl fiRsT?
i jus' run a node script and it does it all for me 🧑🍳
css cyber[popover] 2025 w/ sfx
jus' javascript for the audio and keyboard shortcuts — [popover], starting-style, linear(), and mask for the rest 🤙
jus' javascript for the audio and keyboard shortcuts — [popover], starting-style, linear(), and mask for the rest 🤙
July 3, 2025 at 7:42 PM
css cyber[popover] 2025 w/ sfx
jus' javascript for the audio and keyboard shortcuts — [popover], starting-style, linear(), and mask for the rest 🤙
jus' javascript for the audio and keyboard shortcuts — [popover], starting-style, linear(), and mask for the rest 🤙
happy birthday to my absolute world @seaotta.dev 🫶
~3 years ago, went to speak at a conference in leeds, the rest is history
~3 years ago, went to speak at a conference in leeds, the rest is history
June 27, 2025 at 1:49 PM
happy birthday to my absolute world @seaotta.dev 🫶
~3 years ago, went to speak at a conference in leeds, the rest is history
~3 years ago, went to speak at a conference in leeds, the rest is history
thank you all – building on CodePen has truly changed my life and shaped my career in ways never imagined
June 23, 2025 at 5:43 PM
thank you all – building on CodePen has truly changed my life and shaped my career in ways never imagined
had to try CSS filter + SVG glass displacement on webcam input – crank saturation for cheap gold ✨
June 23, 2025 at 11:59 AM
had to try CSS filter + SVG glass displacement on webcam input – crank saturation for cheap gold ✨
configurable chromatic aberration with SVG ✨
June 16, 2025 at 12:14 AM
configurable chromatic aberration with SVG ✨