Skip to content

Commit

Permalink
feat: add mark timeline transition
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Nov 8, 2023
1 parent 211f951 commit 5831d55
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 68 deletions.
1 change: 1 addition & 0 deletions src/components/ui/input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const Input = forwardRef<
'bg-base-100 px-3 py-[calc(theme(spacing.2)-1px)] placeholder:text-zinc-400 focus:outline-none focus:ring-2',
'border-zinc-900/10 dark:border-zinc-700',
'focus:border-accent-focus dark:bg-zinc-700/[0.15] dark:text-zinc-200 dark:placeholder:text-zinc-500',
props.type === 'password' ? 'font-mono' : 'font-[system-ui]',
className,
)}
{...props}
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/markdown/index.demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const MarkdownCustomize: DocumentComponent = () => {
return (
<QueryClientProvider client={queryClient}>
<ThemeProvider>
<main className="relative m-auto mt-6 max-w-[800px] border border-accent/10">
<main className="relative m-auto mt-6 max-w-[800px]">
<Markdown
extendsRules={{
codeBlock: {
Expand Down
37 changes: 37 additions & 0 deletions src/components/ui/markdown/markdown.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,41 @@
p {
@apply break-words;
}

mark {
--lightness: 80%;
--highlighted: 1;
--highlight: hsl(var(--inc) / var(--lightness));
background: transparent;
}

@supports (animation-timeline: view()) {
mark {
--highlighted: 0;
animation: highlight steps(1) both;
animation-timeline: view();
animation-range: entry 100% cover 10%;
}
}

[data-theme='dark'] mark {
--lightness: 35%;
}

mark span {
background: linear-gradient(
120deg,
var(--highlight, lightblue) 50%,
transparent 50%
)
110% 0 / 200% 100% no-repeat;
background-position: calc((1 - var(--highlighted)) * 110%) 0;
transition: background-position 1s;
}
}

@keyframes highlight {
to {
--highlighted: 1;
}
}
2 changes: 1 addition & 1 deletion src/components/ui/markdown/parsers/mark.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const MarkRule: MarkdownToJSX.Rule = {
key={state?.key}
className="rounded-md bg-always-yellow-400 bg-opacity-80 px-1 text-black"
>
{output(node.content, state!)}
<span>{output(node.content, state!)}</span>
</mark>
)
},
Expand Down
59 changes: 5 additions & 54 deletions src/components/ui/theme-switcher/ThemeSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
'use client'

import { useCallback } from 'react'
import { flushSync } from 'react-dom'
import { atom } from 'jotai'
import { useTheme } from 'next-themes'
import { tv } from 'tailwind-variants'

import { useIsClient } from '~/hooks/common/use-is-client'
import { isUndefined } from '~/lib/_'
import { jotaiStore } from '~/lib/store'
import { transitionViewIfSupported } from '~/lib/dom'

const styles = tv({
base: 'rounded-inherit inline-flex h-[32px] w-[32px] items-center justify-center border-0 text-current',
Expand Down Expand Up @@ -88,16 +85,9 @@ const DarkIcon = () => {
)
}

const mousePositionAtom = atom({ x: 0, y: 0 })
export const ThemeSwitcher = () => {
const handleClient: React.MouseEventHandler = useCallback((e) => {
jotaiStore.set(mousePositionAtom, {
x: e.clientX,
y: e.clientY,
})
}, [])
return (
<div className="relative inline-block" onClick={handleClient}>
<div className="relative inline-block">
<ThemeIndicator />
<ButtonGroup />
</div>
Expand Down Expand Up @@ -125,48 +115,9 @@ const ButtonGroup = () => {
const { setTheme } = useTheme()

const buildThemeTransition = (theme: 'light' | 'dark' | 'system') => {
if (
!('startViewTransition' in document) ||
window.matchMedia(`(prefers-reduced-motion: reduce)`).matches
) {
setTheme(theme)
return
}

const $document = document.documentElement

const mousePosition = jotaiStore.get(mousePositionAtom)
const { x, y } = mousePosition

if (isUndefined(x) && isUndefined(y)) return

const endRadius = Math.hypot(
Math.max(x, window.innerWidth - x),
Math.max(y, window.innerHeight - y),
)

document
.startViewTransition(() => {
flushSync(() => setTheme(theme))
})
?.ready.then(() => {
if (mousePosition.x === 0) return
const clipPath = [
`circle(0px at ${x}px ${y}px)`,
`circle(${endRadius}px at ${x}px ${y}px)`,
]

$document.animate(
{
clipPath,
},
{
duration: 300,
easing: 'ease-in',
pseudoElement: '::view-transition-new(root)',
},
)
})
transitionViewIfSupported(() => {
flushSync(() => setTheme(theme))
})
}

return (
Expand Down
12 changes: 12 additions & 0 deletions src/lib/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,15 @@ export const stopPropagation: ReactEventHandler<any> = (e) =>
e.stopPropagation()

export const preventDefault: ReactEventHandler<any> = (e) => e.preventDefault()

export const transitionViewIfSupported = (updateCb: () => any) => {
if (window.matchMedia(`(prefers-reduced-motion: reduce)`).matches) {
updateCb()
return
}
if (document.startViewTransition) {
document.startViewTransition(updateCb)
} else {
updateCb()
}
}
34 changes: 22 additions & 12 deletions src/styles/variables.css
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,34 @@ html {
color: theme(colors.always.neutral.800);
}

::view-transition-old(root),
::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
animation: turnOff 800ms ease-in-out;
}
::view-transition-old(root) {
z-index: 9999;
}
::view-transition-new(root) {
z-index: 1;
animation: none;
}
[data-theme='dark']::view-transition-old(root) {
z-index: 1;

@keyframes turnOn {
0% {
clip-path: polygon(0% 0%, 100% 0, 100% 0, 0 0);
}
100% {
clip-path: polygon(0% 0%, 100% 0, 100% 100%, 0 100%);
}
}

[data-theme='dark']::view-transition-new(root) {
z-index: 9999;
animation: turnOn 800ms ease-in-out;
}
::view-transition-old(root) {
animation: none;
}

[data-theme='light']::view-transition-new(root) {
z-index: 9999;
@keyframes turnOff {
0% {
clip-path: polygon(0 100%, 100% 100%, 100% 100%, 0% 100%);
}
100% {
clip-path: polygon(0 100%, 100% 100%, 100% 0, 0 0);
}
}

1 comment on commit 5831d55

@vercel
Copy link

@vercel vercel bot commented on 5831d55 Nov 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

shiro – ./

shiro-innei.vercel.app
springtide.vercel.app
shiro-git-main-innei.vercel.app
innei.in

Please sign in to comment.