Standard.site landing page built in Next.js
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add animation system

+414 -3
+76
app/components/Accordion.tsx
··· 1 + 'use client' 2 + 3 + import { useState } from 'react' 4 + import { motion, AnimatePresence } from 'motion/react' 5 + import { PlusIcon } from 'lucide-react' 6 + 7 + interface AccordionItemProps { 8 + question: string 9 + answer: string 10 + isOpen: boolean 11 + onToggle: () => void 12 + } 13 + 14 + function AccordionItem({ question, answer, isOpen, onToggle }: AccordionItemProps) { 15 + return ( 16 + <div className="rounded-xl border border-border bg-card overflow-hidden"> 17 + <button 18 + onClick={ onToggle } 19 + className="flex w-full cursor-pointer items-center justify-between p-4 text-left" 20 + > 21 + <span className="font-medium text-base leading-snug tracking-tight text-base-content"> 22 + { question } 23 + </span> 24 + <motion.div 25 + animate={{ rotate: isOpen ? 135 : 0 }} 26 + transition={{ duration: 0.2, ease: [0.25, 0.1, 0.25, 1] }} 27 + > 28 + <PlusIcon className="size-5 text-base-content" /> 29 + </motion.div> 30 + </button> 31 + <AnimatePresence initial={ false }> 32 + { isOpen && ( 33 + <motion.div 34 + initial={{ height: 0, opacity: 0 }} 35 + animate={{ height: 'auto', opacity: 1 }} 36 + exit={{ height: 0, opacity: 0 }} 37 + transition={{ duration: 0.3, ease: [0.25, 0.1, 0.25, 1] }} 38 + className="overflow-hidden" 39 + > 40 + <div className="border-t border-border p-4"> 41 + <p className="text-base leading-snug tracking-tight text-muted"> 42 + { answer } 43 + </p> 44 + </div> 45 + </motion.div> 46 + ) } 47 + </AnimatePresence> 48 + </div> 49 + ) 50 + } 51 + 52 + interface AccordionProps { 53 + items: { question: string; answer: string }[] 54 + } 55 + 56 + export function Accordion({ items }: AccordionProps) { 57 + const [openIndex, setOpenIndex] = useState<number | null>(null) 58 + 59 + const handleToggle = (index: number) => { 60 + setOpenIndex(openIndex === index ? null : index) 61 + } 62 + 63 + return ( 64 + <> 65 + { items.map((item, index) => ( 66 + <AccordionItem 67 + key={ item.question } 68 + question={ item.question } 69 + answer={ item.answer } 70 + isOpen={ openIndex === index } 71 + onToggle={ () => handleToggle(index) } 72 + /> 73 + )) } 74 + </> 75 + ) 76 + }
+79
app/components/AnimateIn.tsx
··· 1 + 'use client' 2 + 3 + import { motion, type HTMLMotionProps } from 'motion/react' 4 + import { type ElementType, type ReactNode, useCallback, useRef, useState } from 'react' 5 + 6 + type MotionComponent = 'div' | 'section' | 'p' | 'h1' | 'h2' | 'h3' | 'span' | 'a' | 'article' | 'nav' | 'header' | 'footer' | 'aside' 7 + type Direction = 'up' | 'down' | 'left' | 'right' 8 + 9 + const directionOffsets: Record<Direction, { x?: number; y?: number }> = { 10 + up: { y: 20 }, 11 + down: { y: -20 }, 12 + left: { x: 20 }, 13 + right: { x: -20 }, 14 + } 15 + 16 + interface AnimateInProps { 17 + children?: ReactNode 18 + delay?: number 19 + viewportDelay?: number 20 + duration?: number 21 + className?: string 22 + as?: MotionComponent 23 + direction?: Direction 24 + onScroll?: boolean 25 + } 26 + 27 + export function AnimateIn({ 28 + children, 29 + delay = 0, 30 + viewportDelay, 31 + duration = 0.5, 32 + className, 33 + as = 'div', 34 + direction = 'up', 35 + onScroll = true 36 + }: AnimateInProps) { 37 + const Component = motion[as] as ElementType<HTMLMotionProps<typeof as>> 38 + const offset = directionOffsets[direction] 39 + const [actualDelay, setActualDelay] = useState(delay) 40 + const initializedRef = useRef(false) 41 + 42 + const callbackRef = useCallback((node: HTMLElement | null) => { 43 + if (!node || initializedRef.current || viewportDelay === undefined) return 44 + 45 + initializedRef.current = true 46 + const rect = node.getBoundingClientRect() 47 + const isInViewport = rect.top < window.innerHeight && rect.bottom > 0 48 + 49 + if (isInViewport) { 50 + setActualDelay(viewportDelay) 51 + } 52 + }, [viewportDelay]) 53 + 54 + const animationProps = onScroll 55 + ? { 56 + initial: { opacity: 0, ...offset }, 57 + whileInView: { opacity: 1, x: 0, y: 0 }, 58 + viewport: { once: true, margin: '-50px' }, 59 + } 60 + : { 61 + initial: { opacity: 0, ...offset }, 62 + animate: { opacity: 1, x: 0, y: 0 }, 63 + } 64 + 65 + return ( 66 + <Component 67 + ref={ callbackRef } 68 + { ...animationProps } 69 + transition={{ 70 + duration, 71 + delay: actualDelay, 72 + ease: [0.25, 0.1, 0.25, 1] 73 + }} 74 + className={ className } 75 + > 76 + { children } 77 + </Component> 78 + ) 79 + }
+252 -1
package-lock.json
··· 8 8 "name": "standard-site", 9 9 "version": "0.1.0", 10 10 "dependencies": { 11 + "@atproto/api": "^0.18.8", 12 + "lucide-react": "^0.562.0", 13 + "motion": "^12.23.26", 11 14 "next": "16.1.0", 12 15 "react": "19.2.3", 13 - "react-dom": "19.2.3" 16 + "react-dom": "19.2.3", 17 + "react-progressive-blur": "^1.0.6" 14 18 }, 15 19 "devDependencies": { 16 20 "@tailwindcss/postcss": "^4", ··· 34 38 }, 35 39 "funding": { 36 40 "url": "https://github.com/sponsors/sindresorhus" 41 + } 42 + }, 43 + "node_modules/@atproto/api": { 44 + "version": "0.18.8", 45 + "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.18.8.tgz", 46 + "integrity": "sha512-Qo3sGd1N5hdHTaEWUBgptvPkULt2SXnMcWRhveSyctSd/IQwTMyaIH6E62A1SU+8xBSN5QLpoUJNE7iSrYM2Zg==", 47 + "license": "MIT", 48 + "dependencies": { 49 + "@atproto/common-web": "^0.4.7", 50 + "@atproto/lexicon": "^0.6.0", 51 + "@atproto/syntax": "^0.4.2", 52 + "@atproto/xrpc": "^0.7.7", 53 + "await-lock": "^2.2.2", 54 + "multiformats": "^9.9.0", 55 + "tlds": "^1.234.0", 56 + "zod": "^3.23.8" 57 + } 58 + }, 59 + "node_modules/@atproto/api/node_modules/zod": { 60 + "version": "3.25.76", 61 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 62 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 63 + "license": "MIT", 64 + "funding": { 65 + "url": "https://github.com/sponsors/colinhacks" 66 + } 67 + }, 68 + "node_modules/@atproto/common-web": { 69 + "version": "0.4.7", 70 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.7.tgz", 71 + "integrity": "sha512-vjw2+81KPo2/SAbbARGn64Ln+6JTI0FTI4xk8if0ebBfDxFRmHb2oSN1y77hzNq/ybGHqA2mecfhS03pxC5+lg==", 72 + "license": "MIT", 73 + "dependencies": { 74 + "@atproto/lex-data": "0.0.3", 75 + "@atproto/lex-json": "0.0.3", 76 + "zod": "^3.23.8" 77 + } 78 + }, 79 + "node_modules/@atproto/common-web/node_modules/zod": { 80 + "version": "3.25.76", 81 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 82 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 83 + "license": "MIT", 84 + "funding": { 85 + "url": "https://github.com/sponsors/colinhacks" 86 + } 87 + }, 88 + "node_modules/@atproto/lex-data": { 89 + "version": "0.0.3", 90 + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.3.tgz", 91 + "integrity": "sha512-ivo1IpY/EX+RIpxPgCf4cPhQo5bfu4nrpa1vJCt8hCm9SfoonJkDFGa0n4SMw4JnXZoUcGcrJ46L+D8bH6GI2g==", 92 + "license": "MIT", 93 + "dependencies": { 94 + "@atproto/syntax": "0.4.2", 95 + "multiformats": "^9.9.0", 96 + "tslib": "^2.8.1", 97 + "uint8arrays": "3.0.0", 98 + "unicode-segmenter": "^0.14.0" 99 + } 100 + }, 101 + "node_modules/@atproto/lex-json": { 102 + "version": "0.0.3", 103 + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.3.tgz", 104 + "integrity": "sha512-ZVcY7XlRfdPYvQQ2WroKUepee0+NCovrSXgXURM3Xv+n5jflJCoczguROeRr8sN0xvT0ZbzMrDNHCUYKNnxcjw==", 105 + "license": "MIT", 106 + "dependencies": { 107 + "@atproto/lex-data": "0.0.3", 108 + "tslib": "^2.8.1" 109 + } 110 + }, 111 + "node_modules/@atproto/lexicon": { 112 + "version": "0.6.0", 113 + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.6.0.tgz", 114 + "integrity": "sha512-5veb8aD+J5M0qszLJ+73KSFsFrJBgAY/nM1TSAJvGY7fNc9ZAT+PSUlmIyrdye9YznAZ07yktalls/TwNV7cHQ==", 115 + "license": "MIT", 116 + "dependencies": { 117 + "@atproto/common-web": "^0.4.7", 118 + "@atproto/syntax": "^0.4.2", 119 + "iso-datestring-validator": "^2.2.2", 120 + "multiformats": "^9.9.0", 121 + "zod": "^3.23.8" 122 + } 123 + }, 124 + "node_modules/@atproto/lexicon/node_modules/zod": { 125 + "version": "3.25.76", 126 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 127 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 128 + "license": "MIT", 129 + "funding": { 130 + "url": "https://github.com/sponsors/colinhacks" 131 + } 132 + }, 133 + "node_modules/@atproto/syntax": { 134 + "version": "0.4.2", 135 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.2.tgz", 136 + "integrity": "sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA==", 137 + "license": "MIT" 138 + }, 139 + "node_modules/@atproto/xrpc": { 140 + "version": "0.7.7", 141 + "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.7.tgz", 142 + "integrity": "sha512-K1ZyO/BU8JNtXX5dmPp7b5UrkLMMqpsIa/Lrj5D3Su+j1Xwq1m6QJ2XJ1AgjEjkI1v4Muzm7klianLE6XGxtmA==", 143 + "license": "MIT", 144 + "dependencies": { 145 + "@atproto/lexicon": "^0.6.0", 146 + "zod": "^3.23.8" 147 + } 148 + }, 149 + "node_modules/@atproto/xrpc/node_modules/zod": { 150 + "version": "3.25.76", 151 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 152 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 153 + "license": "MIT", 154 + "funding": { 155 + "url": "https://github.com/sponsors/colinhacks" 37 156 } 38 157 }, 39 158 "node_modules/@babel/code-frame": { ··· 2378 2497 "funding": { 2379 2498 "url": "https://github.com/sponsors/ljharb" 2380 2499 } 2500 + }, 2501 + "node_modules/await-lock": { 2502 + "version": "2.2.2", 2503 + "resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz", 2504 + "integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==", 2505 + "license": "MIT" 2381 2506 }, 2382 2507 "node_modules/axe-core": { 2383 2508 "version": "4.11.0", ··· 3585 3710 "url": "https://github.com/sponsors/ljharb" 3586 3711 } 3587 3712 }, 3713 + "node_modules/framer-motion": { 3714 + "version": "12.23.26", 3715 + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.26.tgz", 3716 + "integrity": "sha512-cPcIhgR42xBn1Uj+PzOyheMtZ73H927+uWPDVhUMqxy8UHt6Okavb6xIz9J/phFUHUj0OncR6UvMfJTXoc/LKA==", 3717 + "license": "MIT", 3718 + "dependencies": { 3719 + "motion-dom": "^12.23.23", 3720 + "motion-utils": "^12.23.6", 3721 + "tslib": "^2.4.0" 3722 + }, 3723 + "peerDependencies": { 3724 + "@emotion/is-prop-valid": "*", 3725 + "react": "^18.0.0 || ^19.0.0", 3726 + "react-dom": "^18.0.0 || ^19.0.0" 3727 + }, 3728 + "peerDependenciesMeta": { 3729 + "@emotion/is-prop-valid": { 3730 + "optional": true 3731 + }, 3732 + "react": { 3733 + "optional": true 3734 + }, 3735 + "react-dom": { 3736 + "optional": true 3737 + } 3738 + } 3739 + }, 3588 3740 "node_modules/function-bind": { 3589 3741 "version": "1.1.2", 3590 3742 "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", ··· 4371 4523 "dev": true, 4372 4524 "license": "ISC" 4373 4525 }, 4526 + "node_modules/iso-datestring-validator": { 4527 + "version": "2.2.2", 4528 + "resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz", 4529 + "integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==", 4530 + "license": "MIT" 4531 + }, 4374 4532 "node_modules/iterator.prototype": { 4375 4533 "version": "1.1.5", 4376 4534 "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", ··· 4833 4991 "yallist": "^3.0.2" 4834 4992 } 4835 4993 }, 4994 + "node_modules/lucide-react": { 4995 + "version": "0.562.0", 4996 + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.562.0.tgz", 4997 + "integrity": "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==", 4998 + "license": "ISC", 4999 + "peerDependencies": { 5000 + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" 5001 + } 5002 + }, 4836 5003 "node_modules/magic-string": { 4837 5004 "version": "0.30.21", 4838 5005 "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", ··· 4900 5067 "url": "https://github.com/sponsors/ljharb" 4901 5068 } 4902 5069 }, 5070 + "node_modules/motion": { 5071 + "version": "12.23.26", 5072 + "resolved": "https://registry.npmjs.org/motion/-/motion-12.23.26.tgz", 5073 + "integrity": "sha512-Ll8XhVxY8LXMVYTCfme27WH2GjBrCIzY4+ndr5QKxsK+YwCtOi2B/oBi5jcIbik5doXuWT/4KKDOVAZJkeY5VQ==", 5074 + "license": "MIT", 5075 + "dependencies": { 5076 + "framer-motion": "^12.23.26", 5077 + "tslib": "^2.4.0" 5078 + }, 5079 + "peerDependencies": { 5080 + "@emotion/is-prop-valid": "*", 5081 + "react": "^18.0.0 || ^19.0.0", 5082 + "react-dom": "^18.0.0 || ^19.0.0" 5083 + }, 5084 + "peerDependenciesMeta": { 5085 + "@emotion/is-prop-valid": { 5086 + "optional": true 5087 + }, 5088 + "react": { 5089 + "optional": true 5090 + }, 5091 + "react-dom": { 5092 + "optional": true 5093 + } 5094 + } 5095 + }, 5096 + "node_modules/motion-dom": { 5097 + "version": "12.23.23", 5098 + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz", 5099 + "integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==", 5100 + "license": "MIT", 5101 + "dependencies": { 5102 + "motion-utils": "^12.23.6" 5103 + } 5104 + }, 5105 + "node_modules/motion-utils": { 5106 + "version": "12.23.6", 5107 + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", 5108 + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", 5109 + "license": "MIT" 5110 + }, 4903 5111 "node_modules/ms": { 4904 5112 "version": "2.1.3", 4905 5113 "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 4906 5114 "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 4907 5115 "dev": true, 4908 5116 "license": "MIT" 5117 + }, 5118 + "node_modules/multiformats": { 5119 + "version": "9.9.0", 5120 + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 5121 + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 5122 + "license": "(Apache-2.0 AND MIT)" 4909 5123 }, 4910 5124 "node_modules/nanoid": { 4911 5125 "version": "3.3.11", ··· 5406 5620 "dev": true, 5407 5621 "license": "MIT" 5408 5622 }, 5623 + "node_modules/react-progressive-blur": { 5624 + "version": "1.0.6", 5625 + "resolved": "https://registry.npmjs.org/react-progressive-blur/-/react-progressive-blur-1.0.6.tgz", 5626 + "integrity": "sha512-lsRT4UPTXZUVb/eLcST+AtozqqpEqyyUo+UCgSUr8VK9AzlX7YTTAS+QEIMSllSjWzo40EDs3nGnqPOdzX6YKA==", 5627 + "license": "MIT", 5628 + "dependencies": { 5629 + "react-progressive-blur": "^1.0.0" 5630 + }, 5631 + "peerDependencies": { 5632 + "react": ">=17.0.0", 5633 + "react-dom": ">=17.0.0" 5634 + } 5635 + }, 5409 5636 "node_modules/reflect.getprototypeof": { 5410 5637 "version": "1.0.10", 5411 5638 "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", ··· 6087 6314 "url": "https://github.com/sponsors/jonschlinkert" 6088 6315 } 6089 6316 }, 6317 + "node_modules/tlds": { 6318 + "version": "1.261.0", 6319 + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.261.0.tgz", 6320 + "integrity": "sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==", 6321 + "license": "MIT", 6322 + "bin": { 6323 + "tlds": "bin.js" 6324 + } 6325 + }, 6090 6326 "node_modules/to-regex-range": { 6091 6327 "version": "5.0.1", 6092 6328 "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", ··· 6274 6510 "typescript": ">=4.8.4 <6.0.0" 6275 6511 } 6276 6512 }, 6513 + "node_modules/uint8arrays": { 6514 + "version": "3.0.0", 6515 + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz", 6516 + "integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==", 6517 + "license": "MIT", 6518 + "dependencies": { 6519 + "multiformats": "^9.4.2" 6520 + } 6521 + }, 6277 6522 "node_modules/unbox-primitive": { 6278 6523 "version": "1.1.0", 6279 6524 "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", ··· 6298 6543 "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", 6299 6544 "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", 6300 6545 "dev": true, 6546 + "license": "MIT" 6547 + }, 6548 + "node_modules/unicode-segmenter": { 6549 + "version": "0.14.4", 6550 + "resolved": "https://registry.npmjs.org/unicode-segmenter/-/unicode-segmenter-0.14.4.tgz", 6551 + "integrity": "sha512-pR5VCiCrLrKOL6FRW61jnk9+wyMtKKowq+jyFY9oc6uHbWKhDL4yVRiI4YZPksGMK72Pahh8m0cn/0JvbDDyJg==", 6301 6552 "license": "MIT" 6302 6553 }, 6303 6554 "node_modules/unrs-resolver": {
+7 -2
package.json
··· 6 6 "dev": "next dev", 7 7 "build": "next build", 8 8 "start": "next start", 9 - "lint": "eslint" 9 + "lint": "eslint", 10 + "lexicons:sync": "npx tsx scripts/sync-lexicons.ts" 10 11 }, 11 12 "dependencies": { 13 + "@atproto/api": "^0.18.8", 14 + "lucide-react": "^0.562.0", 15 + "motion": "^12.23.26", 12 16 "next": "16.1.0", 13 17 "react": "19.2.3", 14 - "react-dom": "19.2.3" 18 + "react-dom": "19.2.3", 19 + "react-progressive-blur": "^1.0.6" 15 20 }, 16 21 "devDependencies": { 17 22 "@tailwindcss/postcss": "^4",