{"_id":"linkfolio","_rev":"41-4c62c508a9a42bbad4e0d6405baab5d5","name":"linkfolio","dist-tags":{"latest":"3.1.1"},"versions":{"1.1.1":{"name":"linkfolio","version":"1.1.1","keywords":["linkfolio","linktree","next.js","portfolio","ssr","tailwind","template"],"author":{"url":"heristop","name":"Alexandre Mogère"},"license":"MIT","_id":"linkfolio@1.1.1","maintainers":[{"name":"heristop","email":"heristop@yahoo.fr"}],"homepage":"https://github.com/heristop/linkfolio#readme","bugs":{"url":"https://github.com/heristop/linkfolio/issues"},"dist":{"shasum":"5be8754353e04c050164f9222302a1b731c9327c","tarball":"https://registry.npmjs.org/linkfolio/-/linkfolio-1.1.1.tgz","fileCount":23,"integrity":"sha512-CKqnFj5z1JOnMAGwlR17VCA3JCB1MyoYgtEDb4Ujy7tNfUHafYd2No+v7AYHveIR5cLQkNnKyheauSBXnA6MPQ==","signatures":[{"sig":"MEUCIQCGdEbZKXJ8nXVbScyNnvqQek1QqsBs2rHC5GU1z3WbOgIgODLsE29GcSheYyyW3JoE+Ge7WT1Sb6slBVjGu4099MA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":317100},"main":"./dist/index.cjs","types":"./dist/index.d.cts","module":"./dist/index.mjs","gitHead":"3f8de60a50a642013056a888753c4a522ed83180","scripts":{"dev":"next dev --turbopack","fmt":"oxfmt --write .","lint":"oxlint","build":"next build","start":"next start","fmt:check":"oxfmt --check .","build:package":"sh ./scripts/build.sh"},"_npmUser":{"name":"heristop","email":"heristop@yahoo.fr"},"repository":{"url":"git+https://github.com/heristop/linkfolio.git","type":"git"},"_npmVersion":"11.11.1","description":"Minimalist web page that acts as a hub for all your online presence","directories":{},"sideEffects":false,"_nodeVersion":"25.8.2","dependencies":{"react":"^19.2.4","react-dom":"^19.2.4"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.18.2","devDependencies":{"next":"16.2.1","husky":"^9.1.7","oxfmt":"^0.43.0","oxlint":"^1.58.0","tsdown":"^0.21.7","postcss":"^8.5.8","@swc/core":"^1.15.21","typescript":"^6.0.2","@types/node":"^25.5.0","lint-staged":"^16.4.0","@types/react":"^19.2.14","@playwright/test":"^1.58.2","@types/react-dom":"^19.2.3","@tailwindcss/postcss":"^4.2.2","@babel/plugin-transform-nullish-coalescing-operator":"^7.28.6"},"peerDependencies":{"next":"^15.0.0 || ^16.0.0","react":"^19.0.0","sharp":"^0.33.3","react-dom":"^19.0.0","tailwindcss":"^4.1.0"},"peerDependenciesMeta":{"sharp":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/linkfolio_1.1.1_1775150602239_0.9350584516251039","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"linkfolio","version":"2.0.0","keywords":["linkfolio","linktree","next.js","portfolio","ssr","tailwind","template"],"author":{"url":"heristop","name":"Alexandre Mogère"},"license":"MIT","_id":"linkfolio@2.0.0","maintainers":[{"name":"heristop","email":"heristop@yahoo.fr"}],"homepage":"https://github.com/heristop/linkfolio#readme","bugs":{"url":"https://github.com/heristop/linkfolio/issues"},"dist":{"shasum":"1851958b7e7de00504dcda354562b4ed51fabca4","tarball":"https://registry.npmjs.org/linkfolio/-/linkfolio-2.0.0.tgz","fileCount":23,"integrity":"sha512-LBaq4+tWgwMhSGDRMdB/OsjV+Ci93tnM7aP4zZb5mChBMYRgXO1sgptSL4mzrDtNsYty1zNZf1HyCuJ7jkUbDw==","signatures":[{"sig":"MEYCIQCPIZEx3M1Qm9AbBnMTzpI+aMNBXgRwSPpdUPBFEH1pnAIhAJCwZ/Lzp99Be4Ar8F+rq0Rfo75Z8Agw0B3+/WiwgKe7","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":326741},"main":"./dist/index.cjs","types":"./dist/index.d.cts","module":"./dist/index.mjs","gitHead":"6863453ef77e60c35a19f70cbe58463ddb64627e","scripts":{"dev":"next dev --turbopack","fmt":"oxfmt --write .","lint":"oxlint","build":"next build","start":"next start","fmt:check":"oxfmt --check .","build:package":"sh ./scripts/build.sh"},"_npmUser":{"name":"heristop","email":"heristop@yahoo.fr"},"repository":{"url":"git+https://github.com/heristop/linkfolio.git","type":"git"},"_npmVersion":"11.11.1","description":"Minimalist web page that acts as a hub for all your online presence","directories":{},"sideEffects":false,"_nodeVersion":"25.8.2","dependencies":{"react":"^19.2.4","react-dom":"^19.2.4"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.18.2","devDependencies":{"next":"16.2.2","husky":"^9.1.7","oxfmt":"^0.43.0","oxlint":"^1.58.0","tsdown":"^0.21.7","postcss":"^8.5.8","@swc/core":"^1.15.21","typescript":"^6.0.2","@types/node":"^25.5.0","lint-staged":"^16.4.0","@types/react":"^19.2.14","@playwright/test":"^1.59.1","@types/react-dom":"^19.2.3","@tailwindcss/postcss":"^4.2.2","@babel/plugin-transform-nullish-coalescing-operator":"^7.28.6"},"peerDependencies":{"next":"^15.0.0 || ^16.0.0","react":"^19.0.0","sharp":"^0.33.3","react-dom":"^19.0.0","tailwindcss":"^4.1.0"},"peerDependenciesMeta":{"sharp":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/linkfolio_2.0.0_1775168482352_0.6109207924888038","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"linkfolio","version":"2.1.0","keywords":["linkfolio","linktree","next.js","portfolio","ssr","tailwind","template"],"author":{"url":"heristop","name":"Alexandre Mogère"},"license":"MIT","_id":"linkfolio@2.1.0","maintainers":[{"name":"heristop","email":"heristop@yahoo.fr"}],"homepage":"https://github.com/heristop/linkfolio#readme","bugs":{"url":"https://github.com/heristop/linkfolio/issues"},"dist":{"shasum":"851de06ca5d794fe53f46c5874716049861c95e9","tarball":"https://registry.npmjs.org/linkfolio/-/linkfolio-2.1.0.tgz","fileCount":23,"integrity":"sha512-gD2Ht9Fczz08QsNjht/moB3DcJ652o1wXtWvyusOmpLfxTsL5SA45eLHHhSRzgsKk1LoXzWDKbUSnwlciT2wTw==","signatures":[{"sig":"MEQCIAzUEoeCi0tNpSY78iRCX0pFE7ArlJD0xdL5NjmlKopHAiBfhpC7+/KSGhv+vaSuPfXrIWtMVdISg5mKwWf+YoMY6A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":326741},"main":"./dist/index.cjs","types":"./dist/index.d.cts","module":"./dist/index.mjs","gitHead":"99499400549a1589f85d33d710036b34aae29457","scripts":{"dev":"next dev --turbopack","fmt":"oxfmt --write .","lint":"oxlint","build":"next build","start":"next start","fmt:check":"oxfmt --check .","build:package":"sh ./scripts/build.sh"},"_npmUser":{"name":"heristop","email":"heristop@yahoo.fr"},"repository":{"url":"git+https://github.com/heristop/linkfolio.git","type":"git"},"_npmVersion":"11.11.1","description":"Minimalist web page that acts as a hub for all your online presence","directories":{},"sideEffects":false,"_nodeVersion":"25.8.2","dependencies":{"react":"^19.2.4","react-dom":"^19.2.4"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.18.2","devDependencies":{"next":"16.2.2","husky":"^9.1.7","oxfmt":"^0.43.0","oxlint":"^1.58.0","tsdown":"^0.21.7","postcss":"^8.5.8","@swc/core":"^1.15.21","typescript":"^6.0.2","@types/node":"^25.5.0","lint-staged":"^16.4.0","@types/react":"^19.2.14","@playwright/test":"^1.59.1","@types/react-dom":"^19.2.3","@tailwindcss/postcss":"^4.2.2","@babel/plugin-transform-nullish-coalescing-operator":"^7.28.6"},"peerDependencies":{"next":"^15.0.0 || ^16.0.0","react":"^19.0.0","sharp":"^0.33.3","react-dom":"^19.0.0","tailwindcss":"^4.1.0"},"peerDependenciesMeta":{"sharp":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/linkfolio_2.1.0_1775214582744_0.8984598188973763","host":"s3://npm-registry-packages-npm-production"}},"2.2.0":{"name":"linkfolio","version":"2.2.0","keywords":["linkfolio","linktree","next.js","portfolio","ssr","tailwind","template"],"author":{"url":"heristop","name":"Alexandre Mogère"},"license":"MIT","_id":"linkfolio@2.2.0","maintainers":[{"name":"heristop","email":"heristop@yahoo.fr"}],"homepage":"https://github.com/heristop/linkfolio#readme","bugs":{"url":"https://github.com/heristop/linkfolio/issues"},"dist":{"shasum":"86daf2f8c35fb5581c1aecf5cd8b6e7a17ea8787","tarball":"https://registry.npmjs.org/linkfolio/-/linkfolio-2.2.0.tgz","fileCount":23,"integrity":"sha512-2aejvyaivCstwyq3QKAvg+cNgJO0Si+mtU/VUhvBp+aGfyQG8jLKHuLhqUvmQmL5W7jeIYzygvwCM53DlelWHg==","signatures":[{"sig":"MEUCIQDbuFkUoUHwWlNnuFUjnOrmGqEujEfoQt20XSXDxPwnMgIgQfAU9CpAD8f66E+A8Hins6QFUCZxsmnzy/KmLusk57I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":326825},"main":"./dist/index.cjs","types":"./dist/index.d.cts","module":"./dist/index.mjs","gitHead":"3451294cf806130b7c8c6bed14b7df62a11dc180","scripts":{"dev":"next dev --turbopack","fmt":"oxfmt --write .","lint":"oxlint","build":"next build","start":"next start","fmt:check":"oxfmt --check .","build:package":"sh ./scripts/build.sh"},"_npmUser":{"name":"heristop","email":"heristop@yahoo.fr"},"repository":{"url":"git+https://github.com/heristop/linkfolio.git","type":"git"},"_npmVersion":"11.11.1","description":"Minimalist web page that acts as a hub for all your online presence","directories":{},"sideEffects":false,"_nodeVersion":"25.8.2","dependencies":{"react":"^19.2.4","qrcode":"^1.5.4","react-dom":"^19.2.4","next-themes":"^0.4.6"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.18.2","devDependencies":{"next":"16.2.2","husky":"^9.1.7","oxfmt":"^0.43.0","oxlint":"^1.58.0","tsdown":"^0.21.7","postcss":"^8.5.8","@swc/core":"^1.15.21","typescript":"^6.0.2","@types/node":"^25.5.0","lint-staged":"^16.4.0","@types/react":"^19.2.14","@types/qrcode":"^1.5.6","@playwright/test":"^1.59.1","@types/react-dom":"^19.2.3","@tailwindcss/postcss":"^4.2.2","@babel/plugin-transform-nullish-coalescing-operator":"^7.28.6"},"peerDependencies":{"next":"^15.0.0 || ^16.0.0","react":"^19.0.0","sharp":"^0.33.3","react-dom":"^19.0.0","tailwindcss":"^4.1.0"},"peerDependenciesMeta":{"sharp":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/linkfolio_2.2.0_1775218247586_0.7499358396332763","host":"s3://npm-registry-packages-npm-production"}},"2.2.1":{"name":"linkfolio","version":"2.2.1","keywords":["linkfolio","linktree","next.js","portfolio","ssr","tailwind","template"],"author":{"url":"heristop","name":"Alexandre Mogère"},"license":"MIT","_id":"linkfolio@2.2.1","maintainers":[{"name":"heristop","email":"heristop@yahoo.fr"}],"homepage":"https://github.com/heristop/linkfolio#readme","bugs":{"url":"https://github.com/heristop/linkfolio/issues"},"dist":{"shasum":"6162eac189fe79e620b96a3f8272ec8c06dab993","tarball":"https://registry.npmjs.org/linkfolio/-/linkfolio-2.2.1.tgz","fileCount":23,"integrity":"sha512-/J6PZA3iNIx58GjkU9q07tQXBYog+hiUo73jresEZ2X30lW2g0xsZMBHdRrg9+vnkrr6SJxZBuGMKrZlOyzAVQ==","signatures":[{"sig":"MEYCIQD8YB0/54qhynX4NWbGfxFb8Eg5CrooX2mzDcYEeOHxegIhAIYVCmRbSaj76WWFDPo2M2GfMfwDvDe2zJ58KsBlxb1a","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":350751},"main":"./dist/index.cjs","types":"./dist/index.d.cts","module":"./dist/index.mjs","gitHead":"3451294cf806130b7c8c6bed14b7df62a11dc180","scripts":{"dev":"next dev --turbopack","fmt":"oxfmt --write .","lint":"oxlint","build":"next build","start":"next start","fmt:check":"oxfmt --check .","build:package":"sh ./scripts/build.sh"},"_npmUser":{"name":"heristop","email":"heristop@yahoo.fr"},"repository":{"url":"git+https://github.com/heristop/linkfolio.git","type":"git"},"_npmVersion":"11.11.1","description":"Minimalist web page that acts as a hub for all your online presence","directories":{},"sideEffects":false,"_nodeVersion":"25.8.2","dependencies":{"react":"^19.2.4","qrcode":"^1.5.4","react-dom":"^19.2.4","next-themes":"^0.4.6"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.18.2","devDependencies":{"next":"16.2.2","husky":"^9.1.7","oxfmt":"^0.43.0","oxlint":"^1.58.0","tsdown":"^0.21.7","postcss":"^8.5.8","@swc/core":"^1.15.21","typescript":"^6.0.2","@types/node":"^25.5.0","lint-staged":"^16.4.0","@types/react":"^19.2.14","@types/qrcode":"^1.5.6","@playwright/test":"^1.59.1","@types/react-dom":"^19.2.3","@tailwindcss/postcss":"^4.2.2","@babel/plugin-transform-nullish-coalescing-operator":"^7.28.6"},"peerDependencies":{"next":"^15.0.0 || ^16.0.0","react":"^19.0.0","sharp":"^0.33.3","react-dom":"^19.0.0","tailwindcss":"^4.1.0"},"peerDependenciesMeta":{"sharp":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/linkfolio_2.2.1_1775218492374_0.07002499021088204","host":"s3://npm-registry-packages-npm-production"}},"2.2.2":{"name":"linkfolio","version":"2.2.2","keywords":["linkfolio","linktree","next.js","portfolio","ssr","tailwind","template"],"author":{"url":"heristop","name":"Alexandre Mogère"},"license":"MIT","_id":"linkfolio@2.2.2","maintainers":[{"name":"heristop","email":"heristop@yahoo.fr"}],"homepage":"https://github.com/heristop/linkfolio#readme","bugs":{"url":"https://github.com/heristop/linkfolio/issues"},"dist":{"shasum":"8d7453f6b5cbca0702d092e17722f677f319335b","tarball":"https://registry.npmjs.org/linkfolio/-/linkfolio-2.2.2.tgz","fileCount":23,"integrity":"sha512-TW+kT+QPVDTCvtqRcIRkiR75Q6t2HDFtBsLzXeHu4emqfH4ZTX+wazkfIaapdc3B+KXiE4neEeNK2MCod0GvJA==","signatures":[{"sig":"MEUCIQCz/WTQkuXKZ5a5u3hjuLjVDRcxb2rNbcUTOGq/d8oGlgIgbdpx3KL70/c5V/j/4+4UQbU1ftBywVMI3okY+Ha/a9A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":350664},"main":"./dist/index.cjs","types":"./dist/index.d.cts","module":"./dist/index.mjs","gitHead":"3e5b6af7ad13adfd4f36e103a9a3bc45e81a92b3","scripts":{"dev":"next dev --turbopack","fmt":"oxfmt --write .","lint":"oxlint","build":"next build","start":"next start","fmt:check":"oxfmt --check .","build:package":"sh ./scripts/build.sh"},"_npmUser":{"name":"heristop","email":"heristop@yahoo.fr"},"repository":{"url":"git+https://github.com/heristop/linkfolio.git","type":"git"},"_npmVersion":"11.11.1","description":"Minimalist web page that acts as a hub for all your online presence","directories":{},"sideEffects":false,"_nodeVersion":"25.8.2","dependencies":{"react":"^19.2.4","qrcode":"^1.5.4","react-dom":"^19.2.4","next-themes":"^0.4.6"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.18.2","devDependencies":{"next":"16.2.2","husky":"^9.1.7","oxfmt":"^0.43.0","oxlint":"^1.58.0","tsdown":"^0.21.7","postcss":"^8.5.8","@swc/core":"^1.15.21","typescript":"^6.0.2","@types/node":"^25.5.0","lint-staged":"^16.4.0","@types/react":"^19.2.14","@types/qrcode":"^1.5.6","@playwright/test":"^1.59.1","@types/react-dom":"^19.2.3","@tailwindcss/postcss":"^4.2.2","@babel/plugin-transform-nullish-coalescing-operator":"^7.28.6"},"peerDependencies":{"next":"^15.0.0 || ^16.0.0","react":"^19.0.0","sharp":"^0.33.3","react-dom":"^19.0.0","tailwindcss":"^4.1.0"},"peerDependenciesMeta":{"sharp":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/linkfolio_2.2.2_1775219436599_0.6520776444626064","host":"s3://npm-registry-packages-npm-production"}},"2.3.0":{"name":"linkfolio","version":"2.3.0","keywords":["linkfolio","linktree","next.js","portfolio","ssr","tailwind","template"],"author":{"url":"heristop","name":"Alexandre Mogère"},"license":"MIT","_id":"linkfolio@2.3.0","maintainers":[{"name":"heristop","email":"heristop@yahoo.fr"}],"homepage":"https://github.com/heristop/linkfolio#readme","bugs":{"url":"https://github.com/heristop/linkfolio/issues"},"dist":{"shasum":"77204b75712aa25cf96587e8cf137fc0e8722ef8","tarball":"https://registry.npmjs.org/linkfolio/-/linkfolio-2.3.0.tgz","fileCount":23,"integrity":"sha512-OqmeRsQh2NlRg8TnFmlQZ9BIlr3j7cpgX9vctbE8IfM2tD42a51YMWCd5GNUclLkg97Wj8QNkkmUksStqrE90Q==","signatures":[{"sig":"MEUCID1Fzy80hMhmETKkIjN3F6K0qbDV90g6gPEjjBBHRaAaAiEAquiUH8xijjn+JfBeJb9H7wR/DliVKnEhHTj2r8Wuamo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":352946},"main":"./dist/index.cjs","types":"./dist/index.d.cts","module":"./dist/index.mjs","gitHead":"d78040c7f25a5c01d087a04fbc7d7c5bc84c415e","scripts":{"dev":"next dev --turbopack","fmt":"oxfmt --write .","lint":"oxlint","build":"next build","start":"next start","fmt:check":"oxfmt --check .","build:package":"sh ./scripts/build.sh"},"_npmUser":{"name":"heristop","email":"heristop@yahoo.fr"},"repository":{"url":"git+https://github.com/heristop/linkfolio.git","type":"git"},"_npmVersion":"11.11.1","description":"Minimalist web page that acts as a hub for all your online presence","directories":{},"sideEffects":false,"_nodeVersion":"25.8.2","dependencies":{"react":"^19.2.4","qrcode":"^1.5.4","react-dom":"^19.2.4","next-themes":"^0.4.6"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.18.2","devDependencies":{"next":"16.2.2","husky":"^9.1.7","oxfmt":"^0.43.0","oxlint":"^1.58.0","tsdown":"^0.21.7","postcss":"^8.5.8","@swc/core":"^1.15.21","typescript":"^6.0.2","@types/node":"^25.5.0","lint-staged":"^16.4.0","@types/react":"^19.2.14","@types/qrcode":"^1.5.6","@playwright/test":"^1.59.1","@types/react-dom":"^19.2.3","@tailwindcss/postcss":"^4.2.2","@babel/plugin-transform-nullish-coalescing-operator":"^7.28.6"},"peerDependencies":{"next":"^15.0.0 || ^16.0.0","react":"^19.0.0","sharp":"^0.33.3","react-dom":"^19.0.0","tailwindcss":"^4.1.0"},"peerDependenciesMeta":{"sharp":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/linkfolio_2.3.0_1775221831196_0.7798043128101289","host":"s3://npm-registry-packages-npm-production"}},"2.3.1":{"name":"linkfolio","version":"2.3.1","keywords":["linkfolio","linktree","next.js","portfolio","ssr","tailwind","template"],"author":{"url":"heristop","name":"Alexandre Mogère"},"license":"MIT","_id":"linkfolio@2.3.1","maintainers":[{"name":"heristop","email":"heristop@yahoo.fr"}],"homepage":"https://github.com/heristop/linkfolio#readme","bugs":{"url":"https://github.com/heristop/linkfolio/issues"},"dist":{"shasum":"95cdde4ae427836a82300d89b596e70bc5595163","tarball":"https://registry.npmjs.org/linkfolio/-/linkfolio-2.3.1.tgz","fileCount":23,"integrity":"sha512-TA8lf0Q7Mv7mjGYpQ6Ah2oCWrMvkpAo2M9Wq4uK+gccQzNn36sHGlNuRvf4KXJBTznLB/4G7d3M3PzPU/D7v8g==","signatures":[{"sig":"MEUCICD/H1E/Kx1sFgwixh063NlRUjuN7ObLE/FPhRYxv30RAiEAn3Dtt0oiWEA4j3pTV/Je1s1C+byBsuyVJ9HMIZs7aoA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":353002},"main":"./dist/index.cjs","types":"./dist/index.d.cts","module":"./dist/index.mjs","gitHead":"d78040c7f25a5c01d087a04fbc7d7c5bc84c415e","scripts":{"dev":"next dev --turbopack","fmt":"oxfmt --write .","lint":"oxlint","build":"next build","start":"next start","fmt:check":"oxfmt --check .","build:package":"sh ./scripts/build.sh"},"_npmUser":{"name":"heristop","email":"heristop@yahoo.fr"},"repository":{"url":"git+https://github.com/heristop/linkfolio.git","type":"git"},"_npmVersion":"11.11.1","description":"Minimalist web page that acts as a hub for all your online presence","directories":{},"sideEffects":false,"_nodeVersion":"25.8.2","dependencies":{"react":"^19.2.4","qrcode":"^1.5.4","react-dom":"^19.2.4","next-themes":"^0.4.6"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.18.2","devDependencies":{"next":"16.2.2","husky":"^9.1.7","oxfmt":"^0.43.0","oxlint":"^1.58.0","tsdown":"^0.21.7","postcss":"^8.5.8","@swc/core":"^1.15.21","typescript":"^6.0.2","@types/node":"^25.5.0","lint-staged":"^16.4.0","@types/react":"^19.2.14","@types/qrcode":"^1.5.6","@playwright/test":"^1.59.1","@types/react-dom":"^19.2.3","@tailwindcss/postcss":"^4.2.2","@babel/plugin-transform-nullish-coalescing-operator":"^7.28.6"},"peerDependencies":{"next":"^15.0.0 || ^16.0.0","react":"^19.0.0","sharp":"^0.33.3","react-dom":"^19.0.0","tailwindcss":"^4.1.0"},"peerDependenciesMeta":{"sharp":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/linkfolio_2.3.1_1775222072979_0.48820720012771934","host":"s3://npm-registry-packages-npm-production"}},"2.3.2":{"name":"linkfolio","version":"2.3.2","keywords":["linkfolio","linktree","next.js","portfolio","ssr","tailwind","template"],"author":{"url":"heristop","name":"Alexandre Mogère"},"license":"MIT","_id":"linkfolio@2.3.2","maintainers":[{"name":"heristop","email":"heristop@yahoo.fr"}],"homepage":"https://github.com/heristop/linkfolio#readme","bugs":{"url":"https://github.com/heristop/linkfolio/issues"},"dist":{"shasum":"d7e47a016e809b58a6750e1bb26095869566ba3a","tarball":"https://registry.npmjs.org/linkfolio/-/linkfolio-2.3.2.tgz","fileCount":23,"integrity":"sha512-fW0wp/lOjVFmy1W49UD1G37E/diX7OsC5IyhXOH9T9GzoYEWxV2ik7RQMHo8m6V1Ndz/RmgLDBACu28UvmKNiw==","signatures":[{"sig":"MEQCIGZSQBNZzTLW/9UirQvqKsOmz7nn99dZn4JRNlI6KeeOAiBZiG7fQ8stbZiL2MbcPiyU1AVgojZ4aXi781qw3bsXqw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":353447},"main":"./dist/index.cjs","types":"./dist/index.d.cts","module":"./dist/index.mjs","gitHead":"d78040c7f25a5c01d087a04fbc7d7c5bc84c415e","scripts":{"dev":"next dev --turbopack","fmt":"oxfmt --write .","lint":"oxlint","build":"next build","start":"next start","fmt:check":"oxfmt --check .","build:package":"sh ./scripts/build.sh"},"_npmUser":{"name":"heristop","email":"heristop@yahoo.fr"},"repository":{"url":"git+https://github.com/heristop/linkfolio.git","type":"git"},"_npmVersion":"11.11.1","description":"Minimalist web page that acts as a hub for all your online presence","directories":{},"sideEffects":false,"_nodeVersion":"25.8.2","dependencies":{"react":"^19.2.4","qrcode":"^1.5.4","react-dom":"^19.2.4","next-themes":"^0.4.6"},"_hasShrinkwrap":false,"packageManager":"pnpm@10.18.2","devDependencies":{"next":"16.2.2","husky":"^9.1.7","oxfmt":"^0.43.0","oxlint":"^1.58.0","tsdown":"^0.21.7","postcss":"^8.5.8","@swc/core":"^1.15.21","typescript":"^6.0.2","@types/node":"^25.5.0","lint-staged":"^16.4.0","@types/react":"^19.2.14","@types/qrcode":"^1.5.6","@playwright/test":"^1.59.1","@types/react-dom":"^19.2.3","@tailwindcss/postcss":"^4.2.2","@babel/plugin-transform-nullish-coalescing-operator":"^7.28.6"},"peerDependencies":{"next":"^15.0.0 || ^16.0.0","react":"^19.0.0","sharp":"^0.33.3","react-dom":"^19.0.0","tailwindcss":"^4.1.0"},"peerDependenciesMeta":{"sharp":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/linkfolio_2.3.2_1775222601022_0.8394585551099432","host":"s3://npm-registry-packages-npm-production"}},"3.0.0":{"name":"linkfolio","version":"3.0.0","keywords":["bento","bio-link","dark-mode","landing-page","link-hub","link-in-bio","linkfolio","linktree","linktree-alternative","next.js","nextjs","open-source","portfolio","react","self-hosted","ssr","tailwind","template","typescript"],"author":{"url":"heristop","name":"Alexandre Mogère"},"license":"MIT","_id":"linkfolio@3.0.0","maintainers":[{"name":"heristop","email":"heristop@yahoo.fr"}],"homepage":"https://github.com/heristop/linkfolio#readme","bugs":{"url":"https://github.com/heristop/linkfolio/issues"},"dist":{"shasum":"37aead2c4f59eff5477ba96b6cfc5414a9305df8","tarball":"https://registry.npmjs.org/linkfolio/-/linkfolio-3.0.0.tgz","fileCount":36,"integrity":"sha512-r8mvWKBDopj8QiZ9EXcGb+33viGxI2GhV2RdB2qwIOUegcGiW+2rAqeEIQgzumXrNxwf4Gg7nXzpoYArmcDG8A==","signatures":[{"sig":"MEQCIE907THlNdeZJLn4TsG4HqsdnTt/XeDo+qd+7hjNjmQwAiA1ZwXdJYpDgvKEtZNMsj24ul0BkbumxyHZv7f/MaTW1A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":499629},"main":"./dist/index.cjs","types":"./dist/index.d.cts","module":"./dist/index.mjs","engines":{"node":">=20.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./seo":{"import":{"types":"./dist/seo.d.mts","default":"./dist/seo.mjs"},"require":{"types":"./dist/seo.d.cts","default":"./dist/seo.cjs"}},"./dist/*":"./dist/*","./themes":{"import":{"types":"./dist/themes.d.mts","default":"./dist/themes.mjs"},"require":{"types":"./dist/themes.d.cts","default":"./dist/themes.cjs"}},"./package.json":"./package.json"},"gitHead":"d8c533148d152256cfecf646f38fab502fde4d00","scripts":{"dev":"next dev","fmt":"oxfmt --write .","lint":"oxlint","test":"playwright test","build":"next build","start":"next start","fmt:check":"oxfmt --check .","test:unit":"vitest run","typecheck":"tsc -p tsconfig.test.json --noEmit","test:update":"playwright test --update-snapshots","test:package":"pnpm run build:package && node --test tests/package-assets.test.mjs","build:package":"sh ./scripts/build.sh","prepublishOnly":"pnpm run build:package"},"_npmUser":{"name":"heristop","email":"heristop@yahoo.fr"},"repository":{"url":"git+https://github.com/heristop/linkfolio.git","type":"git"},"_npmVersion":"11.16.0","description":"Minimalist web page that acts as a hub for all your online presence","directories":{},"sideEffects":false,"_nodeVersion":"26.3.1","dependencies":{"react":"^19.2.8","qrcode":"^1.5.4","react-dom":"^19.2.8","next-themes":"^0.4.6"},"_hasShrinkwrap":false,"packageManager":"pnpm@11.21.0","devDependencies":{"next":"16.3.1","husky":"^9.1.7","oxfmt":"^0.63.0","oxlint":"^1.78.0","tsdown":"^0.22.14","vitest":"^4.1.10","postcss":"^8.5.26","axe-core":"^4.13.0","@swc/core":"^1.15.47","typescript":"^7.0.2","@types/node":"^26.2.0","lint-staged":"^17.3.0","tailwindcss":"^4.3.3","@types/react":"^19.2.18","@types/qrcode":"^1.5.6","@playwright/test":"^1.62.1","@types/react-dom":"^19.2.4","@tailwindcss/postcss":"^4.3.3","babel-plugin-react-compiler":"^1.0.0","@babel/plugin-transform-nullish-coalescing-operator":"^7.28.6"},"peerDependencies":{"next":"^16.0.0","react":"^19.0.0","sharp":"^0.33.3","react-dom":"^19.0.0","tailwindcss":"^4.3.0"},"peerDependenciesMeta":{"sharp":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/linkfolio_3.0.0_1786791495479_0.10402008217069225","host":"s3://npm-registry-packages-npm-production"}},"3.1.0":{"name":"linkfolio","version":"3.1.0","keywords":["bento","bio-link","dark-mode","landing-page","link-hub","link-in-bio","linkfolio","linktree","linktree-alternative","next.js","nextjs","open-source","portfolio","react","self-hosted","ssr","tailwind","template","typescript"],"author":{"url":"heristop","name":"Alexandre Mogère"},"license":"MIT","_id":"linkfolio@3.1.0","maintainers":[{"name":"heristop","email":"heristop@yahoo.fr"}],"homepage":"https://github.com/heristop/linkfolio#readme","bugs":{"url":"https://github.com/heristop/linkfolio/issues"},"dist":{"shasum":"46625c62dcc3e786739be0c8c48058cd675d48f7","tarball":"https://registry.npmjs.org/linkfolio/-/linkfolio-3.1.0.tgz","fileCount":36,"integrity":"sha512-a3mEXMO+p0lX3/Ye27K33iERXiQE6hw+6tUpCDYMdylbhYt1shLYnqNJi4g1/GIMJN/IpQkStMjq7+q1kFbiPw==","signatures":[{"sig":"MEUCIQCEwHYHywDgYF7Zr24wZr5OIcikbrGOOeLvcnWe+TqcOAIgK3kPffX31B9e0LLabXWpdZ7JCknr2+gZLTiE6U+Yq4E=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":511088},"main":"./dist/index.cjs","types":"./dist/index.d.cts","module":"./dist/index.mjs","engines":{"node":">=20.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./seo":{"import":{"types":"./dist/seo.d.mts","default":"./dist/seo.mjs"},"require":{"types":"./dist/seo.d.cts","default":"./dist/seo.cjs"}},"./assets":"./dist/assets/index.ts","./dist/*":"./dist/*","./themes":{"import":{"types":"./dist/themes.d.mts","default":"./dist/themes.mjs"},"require":{"types":"./dist/themes.d.cts","default":"./dist/themes.cjs"}},"./dist/assets":"./dist/assets/index.ts","./package.json":"./package.json","./assets/globals.css":"./dist/assets/globals.css"},"gitHead":"41e1937d39c28a947967781b9a76d695bc367a99","scripts":{"dev":"next dev","fmt":"oxfmt --write .","lint":"oxlint","test":"playwright test","build":"next build","start":"next start","fmt:check":"oxfmt --check .","test:unit":"vitest run","typecheck":"tsc -p tsconfig.test.json --noEmit","test:update":"playwright test --update-snapshots","test:package":"pnpm run build:package && node --test tests/package-assets.test.mjs","build:package":"sh ./scripts/build.sh","prepublishOnly":"pnpm run build:package"},"_npmUser":{"name":"heristop","email":"heristop@yahoo.fr"},"repository":{"url":"git+https://github.com/heristop/linkfolio.git","type":"git"},"_npmVersion":"11.16.0","description":"Minimalist web page that acts as a hub for all your online presence","directories":{},"sideEffects":false,"_nodeVersion":"26.3.1","dependencies":{"react":"^19.2.8","qrcode":"^1.5.4","react-dom":"^19.2.8","next-themes":"^0.4.6"},"_hasShrinkwrap":false,"packageManager":"pnpm@11.21.0","devDependencies":{"next":"16.3.1","husky":"^9.1.7","oxfmt":"^0.63.0","oxlint":"^1.78.0","tsdown":"^0.22.14","vitest":"^4.1.10","postcss":"^8.5.26","axe-core":"^4.13.0","@swc/core":"^1.15.47","typescript":"^7.0.2","@types/node":"^26.2.0","lint-staged":"^17.3.0","tailwindcss":"^4.3.3","@types/react":"^19.2.18","@types/qrcode":"^1.5.6","@playwright/test":"^1.62.1","@types/react-dom":"^19.2.4","@tailwindcss/postcss":"^4.3.3","babel-plugin-react-compiler":"^1.0.0","@babel/plugin-transform-nullish-coalescing-operator":"^7.28.6"},"peerDependencies":{"next":"^16.0.0","react":"^19.0.0","sharp":"^0.33.3","react-dom":"^19.0.0","tailwindcss":"^4.3.0"},"peerDependenciesMeta":{"sharp":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/linkfolio_3.1.0_1786800523657_0.6115426613328021","host":"s3://npm-registry-packages-npm-production"}},"3.1.1":{"name":"linkfolio","version":"3.1.1","description":"Minimalist web page that acts as a hub for all your online presence","keywords":["bento","bio-link","dark-mode","landing-page","link-hub","link-in-bio","linkfolio","linktree","linktree-alternative","next.js","nextjs","open-source","portfolio","react","self-hosted","ssr","tailwind","template","typescript"],"license":"MIT","author":{"name":"Alexandre Mogère","url":"heristop"},"repository":{"type":"git","url":"git+https://github.com/heristop/linkfolio.git"},"sideEffects":false,"main":"./dist/index.cjs","module":"./dist/index.mjs","types":"./dist/index.d.cts","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./seo":{"import":{"types":"./dist/seo.d.mts","default":"./dist/seo.mjs"},"require":{"types":"./dist/seo.d.cts","default":"./dist/seo.cjs"}},"./themes":{"import":{"types":"./dist/themes.d.mts","default":"./dist/themes.mjs"},"require":{"types":"./dist/themes.d.cts","default":"./dist/themes.cjs"}},"./assets":"./dist/assets/index.ts","./assets/globals.css":"./dist/assets/globals.css","./dist/assets":"./dist/assets/index.ts","./dist/*":"./dist/*","./package.json":"./package.json"},"scripts":{"dev":"next dev","build":"next build","build:package":"sh ./scripts/build.sh","prepublishOnly":"pnpm run build:package","start":"next start","lint":"oxlint","fmt":"oxfmt --write .","fmt:check":"oxfmt --check .","test":"playwright test","test:unit":"vitest run","test:package":"pnpm run build:package && node --test tests/package-assets.test.mjs","test:update":"playwright test --update-snapshots","typecheck":"tsc -p tsconfig.test.json --noEmit"},"dependencies":{"next-themes":"^0.4.6","qrcode":"^1.5.4","react":"^19.2.8","react-dom":"^19.2.8"},"devDependencies":{"@babel/plugin-transform-nullish-coalescing-operator":"^7.28.6","@playwright/test":"^1.62.1","@swc/core":"^1.15.47","@tailwindcss/postcss":"^4.3.3","@types/node":"^26.2.0","@types/qrcode":"^1.5.6","@types/react":"^19.2.18","@types/react-dom":"^19.2.4","axe-core":"^4.13.0","babel-plugin-react-compiler":"^1.0.0","husky":"^9.1.7","lint-staged":"^17.3.0","next":"16.3.1","oxfmt":"^0.63.0","oxlint":"^1.78.0","postcss":"^8.5.26","tailwindcss":"^4.3.3","tsdown":"^0.22.14","typescript":"^7.0.2","vitest":"^4.1.10"},"peerDependencies":{"next":"^16.0.0","react":"^19.0.0","react-dom":"^19.0.0","sharp":"^0.33.3","tailwindcss":"^4.3.0"},"peerDependenciesMeta":{"sharp":{"optional":true}},"engines":{"node":">=20.0.0"},"packageManager":"pnpm@11.21.0","gitHead":"72ebbcac489e39f833754f434c23a131afc081cf","_id":"linkfolio@3.1.1","bugs":{"url":"https://github.com/heristop/linkfolio/issues"},"homepage":"https://github.com/heristop/linkfolio#readme","_nodeVersion":"26.3.1","_npmVersion":"11.16.0","dist":{"integrity":"sha512-0ICxxWpvHBloqgI3rI3QARKY056xhTYPH7tYzUoprqC3AkuB9D/+HwYU09uc0X0kj7H71vwOUfdp5Ix5uJSRhA==","shasum":"51d329de8f5a068656cc5538adaee9d8f2ee438a","tarball":"https://registry.npmjs.org/linkfolio/-/linkfolio-3.1.1.tgz","fileCount":36,"unpackedSize":516739,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFZ90LNnqzMlQGhzg5AYi/LgxikhHwCl8+wcISD7gWmMAiEAlhgcMGYTe0mldu4yWg4hSqe+JJopnkRETVqc67+NNxE="}]},"_npmUser":{"name":"heristop","email":"heristop@yahoo.fr"},"directories":{},"maintainers":[{"name":"heristop","email":"heristop@yahoo.fr"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/linkfolio_3.1.1_1786904840278_0.5690697785311092"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-02T17:23:22.238Z","modified":"2026-08-16T18:27:20.564Z","0.1.0":"2023-10-22T00:07:30.515Z","0.2.0":"2023-10-23T11:27:43.116Z","0.2.1":"2023-10-23T13:55:25.529Z","0.2.2":"2023-10-23T14:02:06.309Z","0.2.3":"2023-10-23T21:09:35.746Z","0.2.4":"2023-10-25T21:27:57.771Z","0.2.5":"2023-10-25T23:19:11.490Z","0.2.6":"2023-10-28T19:18:08.887Z","0.2.7":"2023-10-29T20:39:12.907Z","0.2.8":"2023-10-30T09:25:02.213Z","0.2.9":"2023-10-30T16:42:07.007Z","0.2.10":"2023-11-01T13:08:32.789Z","0.2.11":"2023-11-01T13:11:29.414Z","0.3.0":"2024-04-13T01:26:42.226Z","0.4.0":"2024-05-03T21:06:02.995Z","0.5.0":"2024-05-14T22:08:04.028Z","0.6.0":"2024-05-15T11:16:10.181Z","0.7.0":"2024-08-30T18:12:48.824Z","0.8.0":"2024-09-06T21:02:47.782Z","0.9.0":"2024-09-24T15:37:01.908Z","0.10.0":"2024-09-24T17:54:46.320Z","0.11.0":"2024-09-25T07:42:21.471Z","1.0.0":"2025-10-11T16:22:57.341Z","1.0.1":"2025-10-12T18:10:22.786Z","1.0.2":"2025-10-12T23:19:19.869Z","1.0.3":"2025-10-12T23:41:33.492Z","1.1.0":"2026-04-01T00:11:36.339Z","1.1.1":"2026-04-02T17:23:22.390Z","2.0.0":"2026-04-02T22:21:22.547Z","2.1.0":"2026-04-03T11:09:42.920Z","2.2.0":"2026-04-03T12:10:47.810Z","2.2.1":"2026-04-03T12:14:52.564Z","2.2.2":"2026-04-03T12:30:36.840Z","2.3.0":"2026-04-03T13:10:31.459Z","2.3.1":"2026-04-03T13:14:33.144Z","2.3.2":"2026-04-03T13:23:21.239Z","3.0.0":"2026-08-15T10:58:15.636Z","3.1.0":"2026-08-15T13:28:43.790Z","3.1.1":"2026-08-16T18:27:20.427Z"},"bugs":{"url":"https://github.com/heristop/linkfolio/issues"},"author":{"name":"Alexandre Mogère","url":"heristop"},"license":"MIT","homepage":"https://github.com/heristop/linkfolio#readme","keywords":["bento","bio-link","dark-mode","landing-page","link-hub","link-in-bio","linkfolio","linktree","linktree-alternative","next.js","nextjs","open-source","portfolio","react","self-hosted","ssr","tailwind","template","typescript"],"repository":{"type":"git","url":"git+https://github.com/heristop/linkfolio.git"},"description":"Minimalist web page that acts as a hub for all your online presence","maintainers":[{"name":"heristop","email":"heristop@yahoo.fr"}],"readme":"# Linkfolio — Open-Source Link-in-Bio Page for Next.js\n\n[![Next.js 16](https://img.shields.io/badge/Next.js-16-000000?logo=nextdotjs&logoColor=white)](https://nextjs.org)\n[![React 19](https://img.shields.io/badge/React-19-087ea4?logo=react&logoColor=white)](https://react.dev)\n[![TypeScript 7](https://img.shields.io/badge/TypeScript-7-3178c6?logo=typescript&logoColor=white)](https://www.typescriptlang.org)\n[![Tailwind CSS 4](https://img.shields.io/badge/Tailwind_CSS-4-06b6d4?logo=tailwindcss&logoColor=white)](https://tailwindcss.com)\n[![CI](https://github.com/heristop/linkfolio/actions/workflows/playwright.yml/badge.svg)](https://github.com/heristop/linkfolio/actions/workflows/playwright.yml)\n[![License: MIT](https://img.shields.io/npm/l/linkfolio?color=0b7285)](./LICENSE)\n\nA self-hosted, open-source **Linktree alternative** built with Next.js and Tailwind CSS. One fast, accessible link-in-bio page that connects your audience to everything you publish — on your own domain, with no account, no subscription and no third party in between.\n\n[**Live demo**](https://linkfolio-demo.vercel.app/demo) · [**Documentation**](https://linkfolio-demo.vercel.app/docs) · [**Deploy your own**](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fheristop%2Flinkfolio&env=NEXT_APP_URL&envDescription=Your%20site%27s%20public%20URL&install-command=npm%20install%20%20--legacy-peer-deps)\n\n![Linkfolio — a self-hosted link-in-bio page built with Next.js and Tailwind CSS](https://github.com/heristop/linkfolio/blob/main/docs/preview-light.png?raw=true)\n\n## Quick start\n\nDeploy the template and edit one file:\n\n[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fheristop%2Flinkfolio&env=NEXT_APP_URL&envDescription=Your%20site%27s%20public%20URL&install-command=npm%20install%20%20--legacy-peer-deps)\n\nEverything about your page — name, avatar, links, layout, palette — lives in `config/user.config.ts`. Your deployment is your profile page and nothing else: `/` renders your links.\n\nAlready have a Next.js app? Install the package instead:\n\n```bash\nnpm install linkfolio    # or: pnpm add linkfolio · yarn add linkfolio\n```\n\n```tsx\nimport { LinkFolio } from \"linkfolio\";\nimport userConfig from \"./user.config\";\n\nexport default function Page() {\n  return <LinkFolio userConfig={userConfig} />;\n}\n```\n\n## Preview\n\n|                                                       Light                                                       |                                                   Dark                                                    |\n| :---------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------: |\n|    ![Linkfolio in light mode](https://github.com/heristop/linkfolio/blob/main/docs/preview-light.png?raw=true)    | ![Linkfolio in dark mode](https://github.com/heristop/linkfolio/blob/main/docs/preview-dark.png?raw=true) |\n|                                                 **Bento layout**                                                  |                                                **Mobile**                                                 |\n| ![Linkfolio bento mosaic layout](https://github.com/heristop/linkfolio/blob/main/docs/preview-bento.png?raw=true) | ![Linkfolio on mobile](https://github.com/heristop/linkfolio/blob/main/docs/preview-mobile.png?raw=true)  |\n\n## Features\n\n- **Two layouts** — a classic wrapping list, or a `bento` mosaic that sizes each tile from the group a link already belongs to.\n- **Five theme presets** — teal, ocean, forest, sunset and mauve, each generated from one lightness recipe so they share contrast rather than being eyeballed.\n- **Dark mode** — a genuine dark palette, not an inversion, transitioned with the View Transition API.\n- **Analytics, optional and pluggable** — a DOM event that works with any tracker, plus adapters you can register for your own.\n- **SEO built in** — `linkfolio/seo` exports `buildMetadata` and `buildJsonLd` so server components emit correct metadata and `ProfilePage` structured data.\n- **Accessible by default** — keyboard reachable, screen-reader labelled, and honouring `prefers-reduced-motion`, `prefers-reduced-transparency` and `prefers-contrast`.\n- **Fast** — a static Next.js page with the React Compiler enabled and images served through `next/image`.\n- **Yours to change** — full source access, injectable React components, and every colour and motion value exposed as a CSS custom property.\n\n## Why self-host instead of using Linktree?\n\n|               | Linkfolio                                            | Hosted link-in-bio services               |\n| ------------- | ---------------------------------------------------- | ----------------------------------------- |\n| Cost          | Free, MIT licensed                                   | Free tier with paid upgrades              |\n| Hosting       | Your own domain and infrastructure                   | Their domain, their infrastructure        |\n| Customisation | Full source access; inject your own React components | Limited to the options exposed            |\n| Data          | No third-party analytics unless you add them         | Visitor data flows through their platform |\n| Performance   | Static Next.js page you control                      | Depends on their platform                 |\n\n## Who it's for\n\nDevelopers, designers, writers and makers who want a personal link hub they fully control — a single page linking a portfolio, a blog, social profiles and side projects, deployable in minutes to Vercel.\n\n<details>\n<summary><b>Installation — template or existing project</b></summary>\n\nThere are two methods to get started with Linkfolio:\n\n### 1. Starting with the Linkfolio template\n\n[![Deploy](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fheristop%2Flinkfolio&env=NEXT_APP_URL&envDescription=Your%20site%27s%20public%20URL&install-command=npm%20install%20%20--legacy-peer-deps)\n\nEdit the `user.config.ts` file in the `config` directory to personalize and tailor your profile to your preferences.\n\nUsing this method, you can quickly deploy a Linkfolio page with Vercel using the provided template. Your deployment is your profile page and nothing else: `/` renders your links, and the project's own marketing pages (landing, `/docs`, `/demo`) are not built.\n\nSet `NEXT_APP_URL` to your site's public URL (e.g. `https://links.example.com`) so the canonical URL, sitemap and Open Graph tags point at your domain. On Vercel it falls back to the project's production URL; on other hosts it is required.\n\n> **Showcase mode** — the project's own demo deployment sets `LINKFOLIO_SHOWCASE=1`, which turns on the marketing landing page, `/docs` and `/demo`. Leave it unset for a personal deployment. The flag is read at build time, so changing it requires a redeploy.\n\n### 2. Integrating into an existing Next.js project\n\nInstall the `linkfolio` package in your Next.js / Tailwind project:\n\n```bash\nnpm install linkfolio\n```\n\nOr using Yarn / Pnpm:\n\n```bash\nyarn add linkfolio\n```\n\n```bash\npnpm add linkfolio\n```\n\n</details>\n\n<details>\n<summary><b>Usage and customization</b></summary>\n\n### Basic Usage\n\nHere's a simple example of how to use the `<LinkFolio />` component with just the `userConfig`:\n\n```javascript\nimport { LinkFolio } from \"linkfolio\";\n\nconst userConfig = {\n  avatarSrc: \"/assets/avatar.webp\",\n  avatarAlt: \"Avatar\",\n  fullName: \"Your Name\",\n  alias: \"@your_alias\",\n  metaTitle: \"LinkFolio\",\n  metaDescription: \"A Hub for all your online links 🔗\",\n  socialNetworks: [\n    {\n      url: \"https://github.com/{your_alias}\",\n      iconSrc: githubIcon,\n      title: \"GitHub\",\n      description: \"Open-source contributions\",\n    },\n    // Add more social networks here\n  ],\n};\n\nfunction MyPage() {\n  return <LinkFolio userConfig={userConfig} />;\n}\n```\n\nThis basic setup will create a Linkfolio page using the default components and styles.\n\n### Customization Options\n\nFor more advanced customization, you can use the optional component props. Here's an example showing how to use custom components and add additional content:\n\n```jsx\nimport { LinkFolio } from \"linkfolio\";\nimport MyCustomFooter from \"./MyCustomFooter\";\n\nfunction MyPage() {\n  return (\n    <LinkFolio\n      userConfig={userConfig}\n      BeforeSocialLinksComponent={() => (\n        <div className=\"mb-8 text-center\">\n          <h2>Welcome to my page!</h2>\n          <p>Check out my social links below:</p>\n        </div>\n      )}\n      AfterSocialLinksComponent={() => (\n        <div className=\"mt-8 text-center\">\n          <h2>Thanks for visiting!</h2>\n          <p>Reach me on any of these platforms.</p>\n        </div>\n      )}\n      FooterComponent={MyCustomFooter}\n    />\n  );\n}\n```\n\nIn addition to the content-slot props above, `LinkFolio` accepts the\nbehavioural props below for embedding the component inside a page that already has\nits own document structure — its own `<h1>`, its own JSON-LD, or its own\ntheme toggle:\n\n| Prop           | Type             | Default | Purpose                                                                                                                                                                                                                    |\n| -------------- | ---------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `renderJsonLd` | `boolean`        | `true`  | Renders the component's own `ProfilePage` + `Person` `<script type=\"application/ld+json\">`. Set to `false` when the host page already publishes its own structured data, to avoid a duplicate `Person` entity on that URL. |\n| `renderChrome` | `boolean`        | `true`  | Renders the `QrCodeButton` / `ShareButton` / `ThemeToggle` row. Set to `false` when the host page has its own theme toggle, to avoid two controls fighting over global theme state.                                        |\n| `headingLevel` | `\"h1\"` \\| `\"h2\"` | `\"h1\"`  | Heading level rendered for the full name in `UserProfile`. Set to `\"h2\"` when embedding `LinkFolio` on a page that already has its own `<h1>`.                                                                             |\n| `onLinkClick`  | `(link) => void` | —       | Called when a visitor clicks a link card. Client components only — see [Analytics](#analytics) for the server-safe equivalent.                                                                                             |\n\n</details>\n\n<details>\n<summary><b>Configuration reference</b></summary>\n\nThe `config/user.config.ts` file (or the `userConfig` object you pass to\n`<LinkFolio />` directly) accepts the following options:\n\n| Option              | Type                        | Default       | Purpose                                                                         |\n| ------------------- | --------------------------- | ------------- | ------------------------------------------------------------------------------- |\n| `avatarSrc`         | `string \\| StaticImageData` | —             | Path or imported image for the profile avatar                                   |\n| `avatarAlt`         | `string`                    | —             | Alt text for the avatar image                                                   |\n| `fullName`          | `string`                    | —             | Display name; used as the `Person` name and Open Graph site name                |\n| `alias`             | `string`                    | —             | Username or tagline shown under the name                                        |\n| `metaTitle`         | `string`                    | `\"Linkfolio\"` | Page `<title>`, `og:title`, `twitter:title`                                     |\n| `metaDescription`   | `string`                    | —             | Page description, `og:description`, `twitter:description`, `Person.description` |\n| `siteUrl`           | `string`                    | —             | Canonical origin, e.g. `\"https://example.com\"`. See note below.                 |\n| `jobTitle`          | `string`                    | —             | `Person.jobTitle` in structured data                                            |\n| `worksFor`          | `string`                    | —             | `Person.worksFor` organization name in structured data                          |\n| `locale`            | `string`                    | `\"en_US\"`     | Open Graph locale                                                               |\n| `lang`              | `string`                    | `\"en\"`        | `<html lang>` value                                                             |\n| `keywords`          | `string[]`                  | —             | Meta keywords                                                                   |\n| `themeColor`        | `string`                    | —             | Browser chrome theme colour; also written to `msapplication-TileColor`          |\n| `theme`             | `ThemeColors`               | —             | Light-mode CSS custom property overrides (colours + motion tokens)              |\n| `darkTheme`         | `ThemeColors`               | —             | Dark-mode CSS custom property overrides                                         |\n| `enableTypingAlias` | `boolean`                   | `false`       | Enable the typewriter effect on the alias                                       |\n| `layout`            | `\"classic\" \\| \"bento\"`      | `\"classic\"`   | Arrangement of the links section. See below.                                    |\n| `analytics`         | `AnalyticsConfig`           | —             | Opt-in analytics provider. Nothing loads unless set. See below.                 |\n| `socialNetworks`    | `SocialNetworkType[]`       | `[]`          | Your links: `url`, `iconSrc`, `title`, `description`, `hidden?`, `group?`       |\n\n> [!IMPORTANT]\n>\n> `siteUrl` isn't cosmetic. It anchors `metadataBase`, the canonical URL,\n> `og:url`, resolution of relative image paths (like `avatarSrc`) to absolute\n> URLs, and every `@id` in the JSON-LD entity graph (`Person`, `ProfilePage`,\n> `ItemList`, `WebSite`). Omit it and those fields are silently dropped, and\n> `@id`s fall back to non-resolvable fragment identifiers — the page still\n> renders, but the entity graph is measurably weaker.\n\n### Layouts\n\n`classic` (the default) renders one centred, wrapping row per link group.\n`bento` merges every group into a single mosaic grid and takes each tile's\nsize from the `group` the link already declares:\n\n| `group`       | Default tile  |\n| ------------- | ------------- |\n| `project`     | `2x2` — large |\n| `website`     | `2x1` — wide  |\n| anything else | `1x1` — small |\n\nAny link can override that with its own `span`, read as **columns x rows**:\n`\"1x1\"`, `\"2x1\"`, `\"1x2\"`, `\"2x2\"` or `\"2x3\"`. A second field, `direction`,\nsets how the card arranges its own contents — `\"vertical\"` stacks the image\nabove the title, `\"horizontal\"` sets them side by side (the default for a\none-row-tall wide tile, which has no vertical room for both):\n\n```javascript\nsocialNetworks: [\n  { url: \"...\", title: \"Portfolio\", group: \"website\", span: \"2x2\" },\n  { url: \"...\", title: \"GitHub\", span: \"1x2\" },\n  { url: \"...\", title: \"YouTube\", span: \"2x1\", direction: \"horizontal\" },\n  { url: \"...\", title: \"X\" }, // no span: 1x1, from its group\n];\n```\n\nBoth fields are ignored by the classic layout, so adding them costs nothing if\nyou switch back.\n\n```javascript\nconst userConfig = {\n  // ... other configurations\n  layout: \"bento\",\n};\n```\n\nNothing needs annotating to start — an existing `socialNetworks` array becomes\na bento as it stands, because every link already has a group.\n\nTiles are ordered so the sizes alternate: config files list links group by\ngroup, and honouring that order would produce bands of identical tiles rather\nthan a mosaic. Relative order within one size is preserved. The grid drops to\ntwo columns under 768px, where every wide and large tile takes a full row.\n\nA `horizontal` card carries a resting surface, since side by side a short\ncaption can leave enough space between the two halves to read as two separate\nthings. Four CSS custom properties tune all of this: `--lf-bento-columns`\n(default `4`), `--lf-bento-row` (default `8rem`, the height of one small\ntile), and `--lf-bento-tile-bg` / `--lf-bento-tile-hover-bg`, both mixed from\n`--color-primary` so they follow the palette in either mode.\n\nExample of adding the typewriter effect on the alias:\n\n```javascript\nconst userConfig = {\n  // ... other configurations\n  enableTypingAlias: true,\n};\n```\n\n### SEO helpers for server components (`linkfolio/seo`)\n\n`buildMetadata` and `buildJsonLd` produce the same `Metadata` object and\nJSON-LD graph that `<LinkFolio />` builds internally, so you can generate\nthem yourself — inside `generateMetadata`, a custom `<head>`, or a page that\nassembles its own JSON-LD `@graph`.\n\nThey are exported from the **`linkfolio/seo`** subpath, not from `linkfolio`:\n\n```javascript\n// Server component / route handler\nimport { buildMetadata, buildJsonLd } from \"linkfolio/seo\";\n\nexport async function generateMetadata() {\n  return buildMetadata(userConfig, { siteUrl: \"https://example.com\" });\n}\n\nconst jsonLd = buildJsonLd(userConfig, \"https://example.com\");\n```\n\nThe root `linkfolio` entry point carries a `\"use client\"` banner, because it\nexports the interactive `<LinkFolio />` component tree. Importing\n`buildMetadata` or `buildJsonLd` from `linkfolio` instead of `linkfolio/seo`\nand calling them in a server component — `generateMetadata` included — throws.\n`linkfolio/seo` has no client-side dependencies, which is why it exists as a\nseparate export.\n\n### Public configuration API\n\nLinkfolio also exposes a public API to access the user configuration. You can access it via the `/api/config` route. This API returns the configuration data in JSON format, which can be useful for:\n\n- Dynamically integrating your Linkfolio information into other parts of your application\n- Allowing third-party applications to fetch your Linkfolio data\n- Debugging purposes\n- Creating custom widgets or extensions that use your Linkfolio data\n\nExample usage:\n\n```javascript\nfetch(\"https://your-linkfolio-site.com/api/config\")\n  .then((response) => response.json())\n  .then((data) => console.log(data))\n  .catch((error) => console.error(\"Error:\", error));\n```\n\n> [!TIP]\n>\n> Since this API is public, ensure that you don't include any sensitive information in your Linkfolio configuration that you wouldn't want to be publicly accessible.\n\n</details>\n\n<details>\n<summary><b>Analytics</b></summary>\n\nLinkfolio ships no analytics and phones nobody home by default. When you do\nwant measurement, there are two layers and you can use either or both.\n\n### The DOM event — works with anything, needs no configuration\n\nEvery link card dispatches a `linkfolio:analytics` `CustomEvent` on `document`\nwhen it is clicked, whether or not a provider is configured:\n\n```javascript\ndocument.addEventListener(\"linkfolio:analytics\", (event) => {\n  const { name, params } = event.detail;\n  // name   → \"link_click\"\n  // params → { link_title, link_url, link_group }\n  myTracker.record(name, params);\n});\n```\n\nMiddle-clicks are reported too: opening a link in a background tab is a real\nnavigation that a `click` handler never sees. `link_url` is the sanitised URL\nthe anchor actually points at, so a `url` your config got wrong is reported as\n`\"#\"` rather than as a destination the visitor never reached.\n\nEach anchor also carries `data-lf-link`, `data-lf-url` and `data-lf-group`, so\na tag manager can build triggers off the DOM without any code from you.\n\nThe event and payload names are exported as `LINKFOLIO_ANALYTICS_EVENT` and\n`LINK_CLICK_EVENT` if you would rather not hard-code the strings.\n\n### The provider system — opt into a tracker\n\nName a provider in your config and Linkfolio loads its tag and forwards the\nevents to it:\n\n```javascript\nconst userConfig = {\n  // ... other configurations\n  analytics: {\n    provider: \"plausible\",\n    id: \"example.com\",\n  },\n};\n```\n\nThen render `<Analytics config={userConfig.analytics} />` once, at the end of\nyour `<body>` — the template already does this in `app/layout.tsx`.\n\nThe shipped `config/user.config.ts` reads its GA id from `NEXT_PUBLIC_GA_ID`\nrather than hardcoding one, so cloning the template does not point your traffic\nat somebody else's property. Set it in your host's environment (Vercel →\nSettings → Environment Variables) to switch analytics on; leave it unset and no\nthird-party script is loaded at all. The `NEXT_PUBLIC_` prefix is required — see\nthe note at the end of this section.\n\n| Provider      | `id` is your…              | Custom events        |\n| ------------- | -------------------------- | -------------------- |\n| `\"ga\"`        | GA4 measurement ID (`G-…`) | Yes, via `gtag`      |\n| `\"gtm\"`       | GTM container ID (`GTM-…`) | Yes, via `dataLayer` |\n| `\"plausible\"` | Site domain                | Yes, as props        |\n| `\"umami\"`     | Website ID                 | Yes                  |\n\nTwo provider quirks worth knowing before you read your dashboard. Plausible's\ndefault script is `script.outbound-links.js`, which already records outbound\nclicks on its own, so a card click shows up twice under two names — Plausible's\n`Outbound Link: Click` and the forwarded `link_click`. Point `src` at a plain\n`script.js` if you only want one. And GTM only pushes to `dataLayer`: nothing\nis recorded until you add a trigger for the `link_click` event in your\ncontainer.\n\n| Option            | Type                     | Default        | Purpose                                              |\n| ----------------- | ------------------------ | -------------- | ---------------------------------------------------- |\n| `provider`        | `string`                 | —              | Which adapter to use. Omit to load nothing.          |\n| `id`              | `string`                 | —              | Measurement ID, container ID, site domain or token.  |\n| `src`             | `string`                 | vendor default | Override the script origin, for self-hosted proxies. |\n| `attrs`           | `Record<string, string>` | —              | Extra attributes merged onto every injected script.  |\n| `trackLinkClicks` | `boolean`                | `true`         | Forward link clicks to the provider.                 |\n| `linkClickEvent`  | `string`                 | `\"link_click\"` | Rename the forwarded event, e.g. `\"select_content\"`. |\n\n`src` must be an `http(s)` URL. That string becomes a `<script src>`, so\nanything else — a `data:` or `javascript:` value — is ignored and the vendor\ndefault is used instead. `attrs` is string-valued and is deliberately not the\nplace for `async` or `defer`: loading is already controlled by `next/script`'s\n`strategy=\"afterInteractive\"`.\n\n`trackLinkClicks` and `linkClickEvent` only affect what reaches the provider.\nThe DOM event is unconditional and always carries `link_click`, so turning\nforwarding off never blinds your own listener.\n\nAn unknown provider, a missing `id`, or an `id` containing anything but\nletters, digits, dots, dashes and underscores is ignored — your page renders\nnormally and no script is injected.\n\n### Bring your own provider\n\nThe five above are registered through the same public function you have, so\nadding a sixth is not a fork:\n\n```javascript\nimport { registerAnalyticsAdapter } from \"linkfolio\";\n\nregisterAnalyticsAdapter(\"posthog\", {\n  scripts: (config) => [\n    {\n      id: \"lf-posthog\",\n      src: `https://eu.posthog.com/static/array.js?token=${config.id}`,\n    },\n  ],\n  send: (event) => window.posthog?.capture(event.name, event.params),\n});\n\n// then: analytics: { provider: \"posthog\", id: \"phc_…\" }\n```\n\nRun the registration at module scope in a **client** module — one carrying\n`\"use client\"` — that your page imports, so it has happened by the time\n`<Analytics>` renders. The client part matters: the `linkfolio` entry point is\nbannered `\"use client\"`, so `registerAnalyticsAdapter` and the other runtime\nexports are client references, and merely reading them from a server component\nthrows. Return `inline` instead of `src` for a bootstrap snippet — but\nremember you own the escaping of anything you interpolate into it.\n\nRegistering an existing name replaces it, which is how you point a built-in at\nyour own proxy. `resolveAnalyticsAdapter(name)` hands you the currently\nregistered adapter if you would rather wrap one than replace it; calling its\n`scripts()` yourself means validating `config.id` yourself too.\n\n### The callback prop\n\nIf you render `<LinkFolio />` from a client component, `onLinkClick` is the\nshorter path:\n\n```jsx\n<LinkFolio\n  userConfig={userConfig}\n  onLinkClick={(link) => console.log(link.title, link.url)}\n/>\n```\n\nFunctions cannot cross a server-component boundary, so from a server component\nlisten for the `linkfolio:analytics` DOM event instead — it carries the same\ndata.\n\n</details>\n\n<details>\n<summary><b>Styling, fonts and theming</b></summary>\n\n### Theme presets\n\n`themePreset` picks one of five bundled palettes — `teal`, `ocean`, `forest`, `sunset` and `mauve`. Each is generated from a single lightness recipe with a different hue, so they land at the same contrast instead of being tuned by eye, and each ships a light and a dark palette.\n\n```ts\nconst userConfig: UserConfig = {\n  themePreset: \"sunset\",\n  // `theme` and `darkTheme` still win per key, so a preset can be\n  // adopted and then adjusted rather than replaced wholesale.\n  darkTheme: { \"color-secondary\": \"oklch(0.78 0.05 30)\" },\n};\n```\n\nThe presets are exported too, if you want to read their values or build a picker:\n\n```ts\nimport { THEME_PRESETS, THEME_PRESET_KEYS } from \"linkfolio\";\n```\n\n### Theme tokens\n\nEvery colour, size and typeface the component uses is a CSS custom property.\nSet them from the config, where they apply to the light and dark palettes\nindependently:\n\n```ts\nconst userConfig: UserConfig = {\n  theme: {\n    \"color-primary\": \"oklch(0.4 0.06 280)\",\n    \"lf-card-bg\": \"oklch(0.99 0.005 280)\",\n    \"lf-name-font-family\": \"Georgia, serif\",\n  },\n  darkTheme: { \"lf-card-bg\": \"oklch(0.22 0.01 280)\" },\n};\n```\n\n…or as plain CSS, which is the easier route when the value refers to something\nthe config cannot see, such as a `next/font` variable:\n\n```css\n@theme {\n  --lf-name-font-family: var(--font-display), Georgia, serif;\n  --lf-title-font-family: var(--font-display), Georgia, serif;\n}\n```\n\n| Token group | Tokens                                                                                                                                                                                                     |\n| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Palette     | `color-primary`, `color-secondary`, `color-background-start`, `color-background-end`                                                                                                                       |\n| Card        | `lf-card-bg`, `lf-card-radius`, `lf-card-shadow`, `lf-card-border`, `lf-card-padding-x`, `lf-card-padding-y`, `lf-card-min-height`, `lf-card-backdrop`                                                     |\n| Profile     | `lf-name-color`, `lf-name-font-size`, `lf-name-font-weight`, `lf-name-font-family`, `lf-alias-color`, `lf-profile-margin-bottom`, `lf-accent-line-color`, `lf-accent-line-width`, `lf-accent-line-opacity` |\n| Cards       | `lf-title-font-family`, `lf-description-color`, `lf-network-hover-bg`                                                                                                                                      |\n| Layout      | `lf-links-gap-x`, `lf-links-gap-y`, `lf-links-padding-x`, `lf-bento-columns`, `lf-bento-row`, `lf-bento-tile-bg`, `lf-bento-tile-hover-bg`                                                                 |\n| Motion      | `lf-motion-fast`, `lf-motion-base`, `lf-motion-slow`, `lf-motion-reveal`, `lf-ease-out`, `lf-ease-spring`, `lf-press-scale`, `lf-press-scale-sm`, `lf-hover-lift`, `lf-hover-scale`                        |\n| Chrome      | `lf-button-opacity`, `lf-footer-opacity`                                                                                                                                                                   |\n\n`lf-name-font-family` and `lf-title-font-family` default to `inherit`, so the\nbody face applies until you name another. They exist because a typeface was\nthe one thing the theme could not express — a display font previously required\nwriting CSS against `.lf-name` and `.lf-title` directly.\n\n### Importing the bundled assets\n\nThe package ships the default icon set and the stylesheet:\n\n```ts\nimport { githubIcon, defaultAvatarIcon } from \"linkfolio/assets\";\n```\n\n```css\n@import \"linkfolio/assets/globals.css\";\n```\n\nLinkfolio uses TailwindCSS for styling. If you wish to customize styles, you can use the default Tailwind configuration `tailwind.config.ts` provided with the package.\n\nLinkfolio uses the `Raleway` font by default. If you wish to change the font, you can update the `font-family` in `layout.tsx`:\n\n```javascript\nimport { Roboto } from \"next/font/google\";\n\nconst font = Roboto({\n  weight: \"400\",\n  subsets: [\"latin\"],\n  display: \"swap\",\n});\n```\n\nTo customize the theme, you can override the default CSS variables in your own CSS file. If you have installed `linkfolio` as a package, you can import the default styles and override the variables like this:\n\n```css\n@import \"tailwindcss\";\n@import \"linkfolio/dist/assets/globals.css\";\n\n@source \"../node_modules/linkfolio/dist\";\n\n@theme {\n  --color-primary: #937fa3;\n  --color-secondary: #a56b8c;\n  --color-background-start: #e8eff7;\n  --color-background-end: #ede8f7;\n\n  --background-image-gradient-background: linear-gradient(\n    to bottom,\n    var(--color-background-start),\n    var(--color-background-end)\n  );\n}\n```\n\nIf you are using the template, you can directly modify the `src/assets/globals.css` file.\n\n### Motion tokens\n\nMotion is themeable the same way colour is. Every value below is a CSS custom\nproperty the interaction styles consume, so you can retune the feel — or remove\nit entirely — without forking the stylesheet. All of them are also typed on\n`ThemeColors`, so they work through the `theme` and `darkTheme` config keys.\n\n| Token                 | Default                          | Controls                    |\n| --------------------- | -------------------------------- | --------------------------- |\n| `--lf-motion-fast`    | `140ms`                          | Press-down feedback         |\n| `--lf-motion-base`    | `220ms`                          | Hover, colour, opacity      |\n| `--lf-motion-slow`    | `380ms`                          | Press release, settle       |\n| `--lf-motion-reveal`  | `500ms`                          | Card entrance               |\n| `--lf-ease-out`       | `cubic-bezier(0.32, 0.72, 0, 1)` | Standard deceleration       |\n| `--lf-ease-spring`    | a sampled `linear()` spring      | Settle after a release      |\n| `--lf-press-scale`    | `0.972`                          | Press depth on cards        |\n| `--lf-press-scale-sm` | `0.94`                           | Press depth on icon buttons |\n| `--lf-hover-lift`     | `-2px`                           | Card lift on hover          |\n| `--lf-hover-scale`    | `1.08`                           | Icon button growth on hover |\n\nPress-down is deliberately faster than release: the asymmetry is what makes the\ninteraction feel physical rather than scripted.\n\nFor a completely static build, flatten the amplitudes:\n\n```css\n@theme {\n  --lf-hover-lift: 0px;\n  --lf-hover-scale: 1;\n  --lf-press-scale: 1;\n  --lf-press-scale-sm: 1;\n}\n```\n\nVisitors who set \"reduce motion\" in their OS already get a gentler treatment\nautomatically: transforms and entrance animations are dropped, while opacity\nand colour transitions are kept short so a tap still visibly registers.\n\n</details>\n\n<details>\n<summary><b>Testing with Playwright</b></summary>\n\nPlaywright is used for end-to-end testing to ensure the integrity and functionality of the project.\n\n### Running Tests\n\nTo execute the Playwright tests, run the following command:\n\n```bash\nnpx playwright test\n```\n\n### Updating Reference Snapshots\n\nAs the project evolves, you might update the UI or functionality, causing the existing reference snapshots to be outdated. In such cases, you'll need to update the snapshots to match the latest changes.\n\nTo update the reference snapshots, run:\n\n```bash\nnpx playwright test --update-snapshots\n```\n\nThis will run the tests and update any snapshots that don't match the current render of your page.\n\n</details>\n\n## Example Usage\n\nFor a practical implementation of Linkfolio, check out my example repository: [My Linkfolio Page](https://github.com/heristop/my-linkfolio).\n\nThis repository demonstrates how to integrate and customize Linkfolio in a Next.js project.\n\n## Contributing\n\nIf you have ideas to improve Linkfolio or found a bug, do not hesitate to open an issue or a pull request. [`CONTRIBUTING.md`](./CONTRIBUTING.md) covers local setup, the checks CI runs, and how screenshot baselines are regenerated.\n\n## Brand\n\nLinkfolio's code is MIT licensed — fork it, modify it, and redistribute it freely.\n\nThe name \"Linkfolio\" and its logo are **not** covered by the MIT licence. The MIT grant covers copyright in the software, not the project's identity.\n\nYou're welcome to reference Linkfolio factually, e.g. \"built with Linkfolio\", \"a fork of Linkfolio\", or \"compatible with Linkfolio\". Please don't use the Linkfolio name or logo as the identity of a derivative project, in a product, company or domain name, or in any way that implies endorsement by or affiliation with this project.\n\nFor any other use, please [open an issue](https://github.com/heristop/linkfolio/issues) to ask first.\n\nSee [TRADEMARK.md](https://github.com/heristop/linkfolio/blob/main/TRADEMARK.md) for the full trademark policy.\n\n## License\n\nLinkfolio is open-sourced under the [MIT License](LICENSE).\n","readmeFilename":"README.md"}