Emil Kowalski's banner
Emil Kowalski's profile picture

Emil Kowalski

@emilkowalski90,677 subscribers

Currently @linear, https://t.co/uxXxCQx3aK

Shorts

One of my favorite details of Sonner: By default the toast disappears after 4 seconds unless you hover over it. But what if a toast is triggered and the user switches to a different tab? 4 seconds would pass and the toast would never be seen. That’s why there’s a `useIsDocumentHidden` hook that checks if the document (tab) is hidden, and if so, it will pause the timer. This can be done with the "visibilitychange" event listener on the document. A small detail that improves the experience.

One of my favorite details of Sonner: By default the toast disappears after 4 seconds unless you hover over it. But what if a toast is triggered and the user switches to a different tab? 4 seconds would pass and the toast would never be seen. That’s why there’s a `useIsDocumentHidden` hook that checks if the document (tab) is hidden, and if so, it will pause the timer. This can be done with the "visibilitychange" event listener on the document. A small detail that improves the experience.

52,215 views

After more than 4 months, is opening in exactly 7 days. Some cool new things will be waiting :)

After more than 4 months, is opening in exactly 7 days. Some cool new things will be waiting :)

64,029 views

We had this long-standing papercut at Linear where the menu items overflow their outer container. It was hard to fix, or so I thought, because the structure is pretty complicated given all the design details, so a simple overflow: hidden wouldn’t work as it would break the animation. But today I had an epiphany that we could just use a simple CSS mask and fade the items out. Here’s a before & after with a zoom on the problem, look at the left edge:

We had this long-standing papercut at Linear where the menu items overflow their outer container. It was hard to fix, or so I thought, because the structure is pretty complicated given all the design details, so a simple overflow: hidden wouldn’t work as it would break the animation. But today I had an epiphany that we could just use a simple CSS mask and fade the items out. Here’s a before & after with a zoom on the problem, look at the left edge:

119,390 views

You might’ve seen this bug before where the hover state keeps flickering. The fix is to separate the trigger from the effect. Listen for hovers on the parent, but animate a child element instead. This ensures that the hover area stays consistent.

You might’ve seen this bug before where the hover state keeps flickering. The fix is to separate the trigger from the effect. Listen for hovers on the parent, but animate a child element instead. This ensures that the hover area stays consistent.

667,021 views

Made this interaction of Linear’s dithered logo for our latest release. It’s using canvas with a lot of tiny dots. There’s an invisible circle around the cursor. Any dot inside it gets pushed outward with a cubic falloff so it’s gentle on the edges, and strong at the center.

Made this interaction of Linear’s dithered logo for our latest release. It’s using canvas with a lot of tiny dots. There’s an invisible circle around the cursor. Any dot inside it gets pushed outward with a cubic falloff so it’s gentle on the edges, and strong at the center.

469,962 views

Don’t animate keyboard-initiated actions. As an example, I use Raycast hundreds of times a day. If it animated every time I opened it, it would make it feel slow, delayed, and disconnected from my actions. But there’s no animation at all. That’s the optimal experience.

Don’t animate keyboard-initiated actions. As an example, I use Raycast hundreds of times a day. If it animated every time I opened it, it would make it feel slow, delayed, and disconnected from my actions. But there’s no animation at all. That’s the optimal experience.

321,515 views

A recent addition to our /now page at Linear: Hovering over a video card will play the video starting from the frame visible on the thumbnail so it matches nicely. The Coinbase one is especially satisfying (a very nice video too!).

A recent addition to our /now page at Linear: Hovering over a video card will play the video starting from the frame visible on the thumbnail so it matches nicely. The Coinbase one is especially satisfying (a very nice video too!).

38,003 views

Something that AI unlocks is being able to try a few different variations of a feature quickly. I often ask my coding agent to come up with a few different variants and add a switcher so I can easily swap between them. It also saves your selection as a query param so I can show a specific variation to a colleague. This + a library like Leva for controls that directly influence an interaction makes prototyping and coming up with different ideas extremely easy.

Something that AI unlocks is being able to try a few different variations of a feature quickly. I often ask my coding agent to come up with a few different variants and add a switcher so I can easily swap between them. It also saves your selection as a query param so I can show a specific variation to a colleague. This + a library like Leva for controls that directly influence an interaction makes prototyping and coming up with different ideas extremely easy.

70,532 views

Fixed this small bug in Linear’s app today which I often see in other apps as well. Here’s a small before/after. The issue: When you open a popover inside a modal (think a custom select), clicking outside the popover to close it also closes the modal. The desired behavior is to close the select on click outside while keeping the modal open. This often happens because custom dropdowns or select components are rendered in a portal. The modal then treats the interaction as an outside click because, technically, the dropdown isn’t inside the modal in the DOM, it’s rendered elsewhere via a portal. The fix depends on your setup, whether it’s Radix/Base UI, fully custom etc. But since you now know the cause of it, you can ask your agent to fix it for you!

Fixed this small bug in Linear’s app today which I often see in other apps as well. Here’s a small before/after. The issue: When you open a popover inside a modal (think a custom select), clicking outside the popover to close it also closes the modal. The desired behavior is to close the select on click outside while keeping the modal open. This often happens because custom dropdowns or select components are rendered in a portal. The modal then treats the interaction as an outside click because, technically, the dropdown isn’t inside the modal in the DOM, it’s rendered elsewhere via a portal. The fix depends on your setup, whether it’s Radix/Base UI, fully custom etc. But since you now know the cause of it, you can ask your agent to fix it for you!

88,922 views

Introducing Animations on the web A course about how to craft animations that make people feel something. It contains everything I know about motion on the web.

Introducing Animations on the web A course about how to craft animations that make people feel something. It contains everything I know about motion on the web.

741,422 views

There’s a fun little easter egg in my animation course: As enrollment gets closer to closing, the butter cube melts more with each hour by animating the svg paths.

There’s a fun little easter egg in my animation course: As enrollment gets closer to closing, the butter cube melts more with each hour by animating the svg paths.

141,618 views

One of my favorite things from the new Linear homepage are these side drawers which you can open to get some info about a specific feature. Made with Base UI’s dialog component and some ease-out.

One of my favorite things from the new Linear homepage are these side drawers which you can open to get some info about a specific feature. Made with Base UI’s dialog component and some ease-out.

51,610 views

Hover transitions seen tens, maybe even hundreds of times a day can make your interface feel slower. Make them snappy by removing the transition altogether.

Hover transitions seen tens, maybe even hundreds of times a day can make your interface feel slower. Make them snappy by removing the transition altogether.

115,307 views

Keep your animations fast. A faster-spinning spinner makes you think the app loads faster. It improves perceived performance. The same applies to user-initiated actions. Use `ease-out` to make them feel snappy and responsive.

Keep your animations fast. A faster-spinning spinner makes you think the app loads faster. It improves perceived performance. The same applies to user-initiated actions. Use `ease-out` to make them feel snappy and responsive.

102,168 views

A high-ROI tip from is to record your animations and play them frame by frame to spot imperfections you wouldn’t otherwise notice. These hidden imperfections are what makes an animation feel off, even though it looks alright at first glance.

A high-ROI tip from is to record your animations and play them frame by frame to spot imperfections you wouldn’t otherwise notice. These hidden imperfections are what makes an animation feel off, even though it looks alright at first glance.

31,113 views

Some animations for the hero section of animations[.]dev. Made by a course student (dimi). Coming soon.

Some animations for the hero section of animations[.]dev. Made by a course student (dimi). Coming soon.

47,559 views

Some exciting updates coming to my animation course soon. Here’s a sneak peek (this won’t be a lesson on 3D):

Some exciting updates coming to my animation course soon. Here’s a sneak peek (this won’t be a lesson on 3D):

25,634 views

Trying some stuff out and I kinda like that the button changes color depending on the state that it's in?

Trying some stuff out and I kinda like that the button changes color depending on the state that it's in?

48,051 views

Videos

emilkowalski's profile picture

A year at Linear

Emil Kowalski

402,736 views • 10 months ago

No more content to load