Emil Kowalski's banner
Emil Kowalski's profile picture

Emil Kowalski

@emilkowalski • 108,889 subscribers

Working at @Linear, teaching at https://t.co/J4qhNgnHnS

Shorts

Don’t forget to stagger your animations if you animate a lot of elements. Here’s the difference it makes:

Don’t forget to stagger your animations if you animate a lot of elements. Here’s the difference it makes:

125,138 views

One of my favorite details of Sonner: The toast can be swiped down to dismiss. Instead of prreventing the toast to be dragged upwards, you experience friction. You can still drag it, but it will slow down and eventually stop. It’s nicer than just stopping the toast immediately.

One of my favorite details of Sonner: The toast can be swiped down to dismiss. Instead of prreventing the toast to be dragged upwards, you experience friction. You can still drag it, but it will slow down and eventually stop. It’s nicer than just stopping the toast immediately.

26,453 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.

668,273 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.

472,019 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.

322,255 views

One nice subtle benefit of using Base UI over Radix is that the animations are interruptible since Base uses CSS transitions instead of keyframes. This is me spamming on both dropdowns, notice the glitchiness in the one on the left.

One nice subtle benefit of using Base UI over Radix is that the animations are interruptible since Base uses CSS transitions instead of keyframes. This is me spamming on both dropdowns, notice the glitchiness in the one on the left.

68,801 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:

122,455 views

Made a small dialog animation improvement at Linear: Matched the previously mismatched animation duration of overlay and the dialog + swapped spring animaton for an ease-out one so it feels less abrupt. Toast at the bottom right shows whether it’s before or after.

Made a small dialog animation improvement at Linear: Matched the previously mismatched animation duration of overlay and the dialog + swapped spring animaton for an ease-out one so it feels less abrupt. Toast at the bottom right shows whether it’s before or after.

37,539 views

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.

59,091 views

A small quality of life update: When you were using the Linear Agent and a toast was render, it covered the chat. Now the toast renders next to it and changes position when the chat is open/closed.

A small quality of life update: When you were using the Linear Agent and a toast was render, it covered the chat. Now the toast renders next to it and changes position when the chat is open/closed.

46,328 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 :)

66,877 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.

742,510 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!

89,488 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.

72,223 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,992 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!).

39,550 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.

52,268 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,589 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,387 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.

32,403 views

Videos

emilkowalski's profile picture

A year at Linear

Emil Kowalski

402,736 views • 1 year ago

No more content to load