Compare commits

...

24 Commits
0.11.1 ... main

Author SHA1 Message Date
7836f49828 1.0.1 release
All checks were successful
renovate / renovate (push) Successful in 15s
test-build / build (push) Successful in 32s
release-image / release (push) Successful in 1m27s
2025-07-25 00:05:00 -05:00
25280a239c fix math
Some checks failed
test-build / build (push) Has been cancelled
renovate / renovate (push) Has been cancelled
2025-07-25 00:04:28 -05:00
c56dc99e72 disable descriptions using comments 2025-07-25 00:04:20 -05:00
48b7a13729 update colo
All checks were successful
renovate / renovate (push) Successful in 19s
test-build / build (push) Successful in 34s
2025-07-24 23:48:32 -05:00
ac026b0264 update workflow
All checks were successful
test-build / build (push) Successful in 31s
renovate / renovate (push) Successful in 34s
2025-07-24 21:24:50 -05:00
5332854856 1.0.0 release
All checks were successful
renovate / renovate (push) Successful in 24s
test-build / build (push) Successful in 38s
2025-07-24 20:51:35 -05:00
2e0c2f3de5 rewrite a few sections 2025-07-24 20:51:35 -05:00
88d510b06f update favicon 2025-07-24 20:51:35 -05:00
7843378503 Merge pull request 'Update typescript-eslint monorepo to v8.38.0' (#45) from renovate/typescript-eslint-monorepo into main
All checks were successful
test-build / build (push) Successful in 35s
renovate / renovate (push) Successful in 28s
Reviewed-on: #45
2025-07-25 00:47:24 +00:00
75016fdb4f Update typescript-eslint monorepo to v8.38.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
test-build / build (pull_request) Successful in 45s
2025-07-25 00:02:19 +00:00
4d74f74ab2 Merge pull request 'Update dependency framer-motion to v12.23.7' (#48) from renovate/framer-motion-12.x-lockfile into main
All checks were successful
test-build / build (push) Successful in 42s
renovate / renovate (push) Successful in 40s
2025-07-25 00:01:38 +00:00
2c1b7f577d Update dependency framer-motion to v12.23.7
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
test-build / build (pull_request) Successful in 45s
2025-07-25 00:01:27 +00:00
0e79b32012 Merge pull request 'Update dependency astro to v5.12.3' (#47) from renovate/astro-monorepo into main
Some checks failed
renovate / renovate (push) Has been cancelled
test-build / build (push) Has been cancelled
2025-07-25 00:01:22 +00:00
c1ef2d2ba2 Update dependency astro to v5.12.3
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
test-build / build (pull_request) Successful in 43s
2025-07-25 00:01:07 +00:00
020c709b43 0.12.0 release
Some checks failed
test-build / build (push) Successful in 48s
release-image / release (push) Successful in 1m37s
process-repository / process-repository (push) Failing after 26s
renovate / renovate (push) Successful in 1m43s
2025-07-23 20:32:04 -05:00
9f346ee156 add colors and logo 2025-07-23 20:30:55 -05:00
e820e4f163 add theme 2025-07-23 20:30:55 -05:00
796926316e Merge pull request 'Update dependency astro to v5.12.2' (#46) from renovate/astro-monorepo into main
All checks were successful
test-build / build (push) Successful in 34s
renovate / renovate (push) Successful in 41s
2025-07-24 00:01:08 +00:00
bf8578045e Update dependency astro to v5.12.2
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
test-build / build (pull_request) Successful in 30s
2025-07-24 00:00:57 +00:00
f16af9a98d Update dependency astro to v5.12.1
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
test-build / build (pull_request) Successful in 29s
test-build / build (push) Successful in 31s
renovate / renovate (push) Successful in 1m0s
2025-07-23 00:01:09 +00:00
ec45ad29ed 0.11.3 release
Some checks failed
test-build / build (push) Successful in 29s
release-image / release (push) Successful in 3m27s
process-repository / process-repository (push) Failing after 13s
renovate / renovate (push) Successful in 1m9s
2025-07-21 21:00:55 -05:00
17afce6710 minor tweaks and polish
All checks were successful
renovate / renovate (push) Successful in 1m11s
test-build / build (push) Successful in 1m36s
2025-07-21 20:58:34 -05:00
f83fe98b38 0.11.2 release
Some checks failed
test-build / build (push) Successful in 44s
release-image / release (push) Successful in 3m31s
process-repository / process-repository (push) Failing after 16s
renovate / renovate (push) Successful in 38s
2025-07-20 22:22:20 -05:00
2f244761ed add transition 2025-07-20 22:21:54 -05:00
21 changed files with 217 additions and 1067 deletions

View File

@@ -3,7 +3,7 @@ name: release-image
on: on:
push: push:
tags: tags:
- 0.* - 1.*
workflow_dispatch: workflow_dispatch:

View File

@@ -1,7 +1,7 @@
ARG REGISTRY=docker.io ARG REGISTRY=docker.io
FROM ${REGISTRY}/node:22.17.1-alpine3.22 AS base FROM ${REGISTRY}/node:22.17.1-alpine3.22 AS base
LABEL version="0.11.1" LABEL version="1.0.1"
LABEL description="Astro based personal website" LABEL description="Astro based personal website"
ENV PNPM_HOME="/pnpm" ENV PNPM_HOME="/pnpm"

View File

@@ -9,6 +9,7 @@ type Global = {
email: string; email: string;
portrait: string; portrait: string;
portrait_alt: string; portrait_alt: string;
logo: string;
about: string; about: string;
}; };

View File

@@ -1,7 +1,7 @@
{ {
"name": "site-profile", "name": "site-profile",
"type": "module", "type": "module",
"version": "0.11.1", "version": "1.0.1",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "astro dev", "dev": "astro dev",
@@ -31,13 +31,13 @@
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/typography": "^0.5.16", "@tailwindcss/typography": "^0.5.16",
"@typescript-eslint/parser": "8.37.0", "@typescript-eslint/parser": "8.38.0",
"eslint": "9.31.0", "eslint": "9.31.0",
"eslint-config-prettier": "10.1.8", "eslint-config-prettier": "10.1.8",
"eslint-plugin-astro": "1.3.1", "eslint-plugin-astro": "1.3.1",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"prettier-plugin-astro": "^0.14.1", "prettier-plugin-astro": "^0.14.1",
"prettier-plugin-tailwindcss": "^0.6.12", "prettier-plugin-tailwindcss": "^0.6.12",
"typescript-eslint": "8.37.0" "typescript-eslint": "8.38.0"
} }
} }

236
pnpm-lock.yaml generated
View File

@@ -10,13 +10,13 @@ importers:
dependencies: dependencies:
'@astrojs/mdx': '@astrojs/mdx':
specifier: ^4.3.0 specifier: ^4.3.0
version: 4.3.1(astro@5.12.0(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0)) version: 4.3.1(astro@5.12.3(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0))
'@astrojs/node': '@astrojs/node':
specifier: ^9.2.2 specifier: ^9.2.2
version: 9.3.0(astro@5.12.0(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0)) version: 9.3.0(astro@5.12.3(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0))
'@astrojs/react': '@astrojs/react':
specifier: ^4.3.0 specifier: ^4.3.0
version: 4.3.0(@types/node@24.0.15)(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(jiti@2.4.2)(lightningcss@1.30.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(yaml@2.8.0) version: 4.3.0(@types/node@24.1.0)(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(jiti@2.4.2)(lightningcss@1.30.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(yaml@2.8.0)
'@astrojs/rss': '@astrojs/rss':
specifier: ^4.0.12 specifier: ^4.0.12
version: 4.0.12 version: 4.0.12
@@ -28,13 +28,13 @@ importers:
version: 4.1.11 version: 4.1.11
'@tailwindcss/vite': '@tailwindcss/vite':
specifier: ^4.1.8 specifier: ^4.1.8
version: 4.1.11(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)) version: 4.1.11(vite@6.3.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0))
astro: astro:
specifier: ^5.10.1 specifier: ^5.10.1
version: 5.12.0(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0) version: 5.12.3(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0)
framer-motion: framer-motion:
specifier: ^12.16.0 specifier: ^12.16.0
version: 12.23.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) version: 12.23.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react: react:
specifier: ^19.1.0 specifier: ^19.1.0
version: 19.1.0 version: 19.1.0
@@ -58,8 +58,8 @@ importers:
specifier: ^0.5.16 specifier: ^0.5.16
version: 0.5.16(tailwindcss@4.1.11) version: 0.5.16(tailwindcss@4.1.11)
'@typescript-eslint/parser': '@typescript-eslint/parser':
specifier: 8.37.0 specifier: 8.38.0
version: 8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3) version: 8.38.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)
eslint: eslint:
specifier: 9.31.0 specifier: 9.31.0
version: 9.31.0(jiti@2.4.2) version: 9.31.0(jiti@2.4.2)
@@ -79,8 +79,8 @@ importers:
specifier: ^0.6.12 specifier: ^0.6.12
version: 0.6.14(prettier-plugin-astro@0.14.1)(prettier@3.6.2) version: 0.6.14(prettier-plugin-astro@0.14.1)(prettier@3.6.2)
typescript-eslint: typescript-eslint:
specifier: 8.37.0 specifier: 8.38.0
version: 8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3) version: 8.38.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)
packages: packages:
@@ -207,8 +207,8 @@ packages:
resolution: {integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==} resolution: {integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@babel/types@7.28.1': '@babel/types@7.28.2':
resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==} resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@capsizecss/unpack@2.4.0': '@capsizecss/unpack@2.4.0':
@@ -867,8 +867,8 @@ packages:
'@types/nlcst@2.0.3': '@types/nlcst@2.0.3':
resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==}
'@types/node@24.0.15': '@types/node@24.1.0':
resolution: {integrity: sha512-oaeTSbCef7U/z7rDeJA138xpG3NuKc64/rZ2qmUFkFJmnMsAPaluIifqyWd8hSSMxyP9oie3dLAqYPblag9KgA==} resolution: {integrity: sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==}
'@types/react-dom@19.1.6': '@types/react-dom@19.1.6':
resolution: {integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==} resolution: {integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==}
@@ -884,23 +884,23 @@ packages:
'@types/unist@3.0.3': '@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
'@typescript-eslint/eslint-plugin@8.37.0': '@typescript-eslint/eslint-plugin@8.38.0':
resolution: {integrity: sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==} resolution: {integrity: sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
'@typescript-eslint/parser': ^8.37.0 '@typescript-eslint/parser': ^8.38.0
eslint: ^8.57.0 || ^9.0.0 eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0' typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/parser@8.37.0': '@typescript-eslint/parser@8.38.0':
resolution: {integrity: sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==} resolution: {integrity: sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0' typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/project-service@8.37.0': '@typescript-eslint/project-service@8.38.0':
resolution: {integrity: sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==} resolution: {integrity: sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
typescript: '>=4.8.4 <5.9.0' typescript: '>=4.8.4 <5.9.0'
@@ -909,18 +909,18 @@ packages:
resolution: {integrity: sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==} resolution: {integrity: sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/scope-manager@8.37.0': '@typescript-eslint/scope-manager@8.38.0':
resolution: {integrity: sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==} resolution: {integrity: sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/tsconfig-utils@8.37.0': '@typescript-eslint/tsconfig-utils@8.38.0':
resolution: {integrity: sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==} resolution: {integrity: sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
typescript: '>=4.8.4 <5.9.0' typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/type-utils@8.37.0': '@typescript-eslint/type-utils@8.38.0':
resolution: {integrity: sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==} resolution: {integrity: sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 eslint: ^8.57.0 || ^9.0.0
@@ -934,18 +934,18 @@ packages:
resolution: {integrity: sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==} resolution: {integrity: sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/types@8.37.0': '@typescript-eslint/types@8.38.0':
resolution: {integrity: sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==} resolution: {integrity: sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.37.0': '@typescript-eslint/typescript-estree@8.38.0':
resolution: {integrity: sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==} resolution: {integrity: sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
typescript: '>=4.8.4 <5.9.0' typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/utils@8.37.0': '@typescript-eslint/utils@8.38.0':
resolution: {integrity: sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==} resolution: {integrity: sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 eslint: ^8.57.0 || ^9.0.0
@@ -955,8 +955,8 @@ packages:
resolution: {integrity: sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==} resolution: {integrity: sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/visitor-keys@8.37.0': '@typescript-eslint/visitor-keys@8.38.0':
resolution: {integrity: sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==} resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@ungap/structured-clone@1.3.0': '@ungap/structured-clone@1.3.0':
@@ -1022,8 +1022,8 @@ packages:
resolution: {integrity: sha512-JepyLROIad6f44uyqMF6HKE2QbunNzp3mYKRcPoDGt0QkxXmH222FAFC64WTyQu2Kg8NNEXHTN/sWuUId9sSxw==} resolution: {integrity: sha512-JepyLROIad6f44uyqMF6HKE2QbunNzp3mYKRcPoDGt0QkxXmH222FAFC64WTyQu2Kg8NNEXHTN/sWuUId9sSxw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
astro@5.12.0: astro@5.12.3:
resolution: {integrity: sha512-Oov5JsMFHuUmuO+Nx6plfv3nQNK1Xl/8CgLvR8lBhZTjYnraxhuPX5COVAzbom+YLgwaDfK7KBd8zOEopRf9mg==} resolution: {integrity: sha512-fU1hNPMkccm+FuonGsY5DFkC2QyuLCju++8L2ubzBtYBDBf6bmfgmVM7A2dK+Hl+ZJCUNgepsClhBpczj+2LRw==}
engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'}
hasBin: true hasBin: true
@@ -1472,8 +1472,8 @@ packages:
fontkit@2.0.4: fontkit@2.0.4:
resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==} resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==}
framer-motion@12.23.6: framer-motion@12.23.7:
resolution: {integrity: sha512-dsJ389QImVE3lQvM8Mnk99/j8tiZDM/7706PCqvkQ8sSCnpmWxsgX+g0lj7r5OBVL0U36pIecCTBoIWcM2RuKw==} resolution: {integrity: sha512-Qs+zNG9D/3c9C0riom1iXVVOOOaY3T32LIofgbQJz9APY/CUE5v6G41WkcZl2lVhaAgQDQcNq94f8qzLf3rTZA==}
peerDependencies: peerDependencies:
'@emotion/is-prop-valid': '*' '@emotion/is-prop-valid': '*'
react: ^18.0.0 || ^19.0.0 react: ^18.0.0 || ^19.0.0
@@ -2014,8 +2014,8 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
motion-dom@12.23.6: motion-dom@12.23.9:
resolution: {integrity: sha512-G2w6Nw7ZOVSzcQmsdLc0doMe64O/Sbuc2bVAbgMz6oP/6/pRStKRiVRV4bQfHp5AHYAKEGhEdVHTM+R3FDgi5w==} resolution: {integrity: sha512-6Sv++iWS8XMFCgU1qwKj9l4xuC47Hp4+2jvPfyTXkqDg2tTzSgX6nWKD4kNFXk0k7llO59LZTPuJigza4A2K1A==}
motion-utils@12.23.6: motion-utils@12.23.6:
resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==} resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==}
@@ -2560,8 +2560,8 @@ packages:
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
engines: {node: '>=16'} engines: {node: '>=16'}
typescript-eslint@8.37.0: typescript-eslint@8.38.0:
resolution: {integrity: sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==} resolution: {integrity: sha512-FsZlrYK6bPDGoLeZRuvx2v6qrM03I0U0SnfCLPs/XCCPCFD80xU9Pg09H/K+XFa68uJuZo7l/Xhs+eDRg2l3hg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 eslint: ^8.57.0 || ^9.0.0
@@ -2875,12 +2875,12 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@astrojs/mdx@4.3.1(astro@5.12.0(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0))': '@astrojs/mdx@4.3.1(astro@5.12.3(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0))':
dependencies: dependencies:
'@astrojs/markdown-remark': 6.3.3 '@astrojs/markdown-remark': 6.3.3
'@mdx-js/mdx': 3.1.0(acorn@8.15.0) '@mdx-js/mdx': 3.1.0(acorn@8.15.0)
acorn: 8.15.0 acorn: 8.15.0
astro: 5.12.0(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0) astro: 5.12.3(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0)
es-module-lexer: 1.7.0 es-module-lexer: 1.7.0
estree-util-visit: 2.0.0 estree-util-visit: 2.0.0
hast-util-to-html: 9.0.5 hast-util-to-html: 9.0.5
@@ -2894,10 +2894,10 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@astrojs/node@9.3.0(astro@5.12.0(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0))': '@astrojs/node@9.3.0(astro@5.12.3(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0))':
dependencies: dependencies:
'@astrojs/internal-helpers': 0.6.1 '@astrojs/internal-helpers': 0.6.1
astro: 5.12.0(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0) astro: 5.12.3(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0)
send: 1.2.0 send: 1.2.0
server-destroy: 1.0.1 server-destroy: 1.0.1
transitivePeerDependencies: transitivePeerDependencies:
@@ -2907,15 +2907,15 @@ snapshots:
dependencies: dependencies:
prismjs: 1.30.0 prismjs: 1.30.0
'@astrojs/react@4.3.0(@types/node@24.0.15)(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(jiti@2.4.2)(lightningcss@1.30.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(yaml@2.8.0)': '@astrojs/react@4.3.0(@types/node@24.1.0)(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(jiti@2.4.2)(lightningcss@1.30.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(yaml@2.8.0)':
dependencies: dependencies:
'@types/react': 19.1.6 '@types/react': 19.1.6
'@types/react-dom': 19.1.6(@types/react@19.1.6) '@types/react-dom': 19.1.6(@types/react@19.1.6)
'@vitejs/plugin-react': 4.5.1(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)) '@vitejs/plugin-react': 4.5.1(vite@6.3.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0))
react: 19.1.0 react: 19.1.0
react-dom: 19.1.0(react@19.1.0) react-dom: 19.1.0(react@19.1.0)
ultrahtml: 1.6.0 ultrahtml: 1.6.0
vite: 6.3.5(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) vite: 6.3.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@types/node' - '@types/node'
- jiti - jiti
@@ -2966,7 +2966,7 @@ snapshots:
'@babel/parser': 7.28.0 '@babel/parser': 7.28.0
'@babel/template': 7.27.2 '@babel/template': 7.27.2
'@babel/traverse': 7.27.4 '@babel/traverse': 7.27.4
'@babel/types': 7.28.1 '@babel/types': 7.28.2
convert-source-map: 2.0.0 convert-source-map: 2.0.0
debug: 4.4.1 debug: 4.4.1
gensync: 1.0.0-beta.2 gensync: 1.0.0-beta.2
@@ -2978,7 +2978,7 @@ snapshots:
'@babel/generator@7.27.5': '@babel/generator@7.27.5':
dependencies: dependencies:
'@babel/parser': 7.28.0 '@babel/parser': 7.28.0
'@babel/types': 7.28.1 '@babel/types': 7.28.2
'@jridgewell/gen-mapping': 0.3.8 '@jridgewell/gen-mapping': 0.3.8
'@jridgewell/trace-mapping': 0.3.25 '@jridgewell/trace-mapping': 0.3.25
jsesc: 3.1.0 jsesc: 3.1.0
@@ -2994,7 +2994,7 @@ snapshots:
'@babel/helper-module-imports@7.27.1': '@babel/helper-module-imports@7.27.1':
dependencies: dependencies:
'@babel/traverse': 7.27.4 '@babel/traverse': 7.27.4
'@babel/types': 7.28.1 '@babel/types': 7.28.2
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -3018,11 +3018,11 @@ snapshots:
'@babel/helpers@7.27.6': '@babel/helpers@7.27.6':
dependencies: dependencies:
'@babel/template': 7.27.2 '@babel/template': 7.27.2
'@babel/types': 7.28.1 '@babel/types': 7.28.2
'@babel/parser@7.28.0': '@babel/parser@7.28.0':
dependencies: dependencies:
'@babel/types': 7.28.1 '@babel/types': 7.28.2
'@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.27.4)': '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.27.4)':
dependencies: dependencies:
@@ -3038,7 +3038,7 @@ snapshots:
dependencies: dependencies:
'@babel/code-frame': 7.27.1 '@babel/code-frame': 7.27.1
'@babel/parser': 7.28.0 '@babel/parser': 7.28.0
'@babel/types': 7.28.1 '@babel/types': 7.28.2
'@babel/traverse@7.27.4': '@babel/traverse@7.27.4':
dependencies: dependencies:
@@ -3046,13 +3046,13 @@ snapshots:
'@babel/generator': 7.27.5 '@babel/generator': 7.27.5
'@babel/parser': 7.28.0 '@babel/parser': 7.28.0
'@babel/template': 7.27.2 '@babel/template': 7.27.2
'@babel/types': 7.28.1 '@babel/types': 7.28.2
debug: 4.4.1 debug: 4.4.1
globals: 11.12.0 globals: 11.12.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@babel/types@7.28.1': '@babel/types@7.28.2':
dependencies: dependencies:
'@babel/helper-string-parser': 7.27.1 '@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.27.1 '@babel/helper-validator-identifier': 7.27.1
@@ -3542,33 +3542,33 @@ snapshots:
postcss-selector-parser: 6.0.10 postcss-selector-parser: 6.0.10
tailwindcss: 4.1.11 tailwindcss: 4.1.11
'@tailwindcss/vite@4.1.11(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0))': '@tailwindcss/vite@4.1.11(vite@6.3.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0))':
dependencies: dependencies:
'@tailwindcss/node': 4.1.11 '@tailwindcss/node': 4.1.11
'@tailwindcss/oxide': 4.1.11 '@tailwindcss/oxide': 4.1.11
tailwindcss: 4.1.11 tailwindcss: 4.1.11
vite: 6.3.5(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) vite: 6.3.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)
'@types/babel__core@7.20.5': '@types/babel__core@7.20.5':
dependencies: dependencies:
'@babel/parser': 7.28.0 '@babel/parser': 7.28.0
'@babel/types': 7.28.1 '@babel/types': 7.28.2
'@types/babel__generator': 7.27.0 '@types/babel__generator': 7.27.0
'@types/babel__template': 7.4.4 '@types/babel__template': 7.4.4
'@types/babel__traverse': 7.20.7 '@types/babel__traverse': 7.20.7
'@types/babel__generator@7.27.0': '@types/babel__generator@7.27.0':
dependencies: dependencies:
'@babel/types': 7.28.1 '@babel/types': 7.28.2
'@types/babel__template@7.4.4': '@types/babel__template@7.4.4':
dependencies: dependencies:
'@babel/parser': 7.28.0 '@babel/parser': 7.28.0
'@babel/types': 7.28.1 '@babel/types': 7.28.2
'@types/babel__traverse@7.20.7': '@types/babel__traverse@7.20.7':
dependencies: dependencies:
'@babel/types': 7.28.1 '@babel/types': 7.28.2
'@types/debug@4.1.12': '@types/debug@4.1.12':
dependencies: dependencies:
@@ -3582,7 +3582,7 @@ snapshots:
'@types/fontkit@2.0.8': '@types/fontkit@2.0.8':
dependencies: dependencies:
'@types/node': 24.0.15 '@types/node': 24.1.0
'@types/hast@3.0.4': '@types/hast@3.0.4':
dependencies: dependencies:
@@ -3602,7 +3602,7 @@ snapshots:
dependencies: dependencies:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
'@types/node@24.0.15': '@types/node@24.1.0':
dependencies: dependencies:
undici-types: 7.8.0 undici-types: 7.8.0
@@ -3618,14 +3618,14 @@ snapshots:
'@types/unist@3.0.3': {} '@types/unist@3.0.3': {}
'@typescript-eslint/eslint-plugin@8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)': '@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)':
dependencies: dependencies:
'@eslint-community/regexpp': 4.12.1 '@eslint-community/regexpp': 4.12.1
'@typescript-eslint/parser': 8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/parser': 8.38.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/scope-manager': 8.37.0 '@typescript-eslint/scope-manager': 8.38.0
'@typescript-eslint/type-utils': 8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/type-utils': 8.38.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/utils': 8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/visitor-keys': 8.37.0 '@typescript-eslint/visitor-keys': 8.38.0
eslint: 9.31.0(jiti@2.4.2) eslint: 9.31.0(jiti@2.4.2)
graphemer: 1.4.0 graphemer: 1.4.0
ignore: 7.0.5 ignore: 7.0.5
@@ -3635,22 +3635,22 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/parser@8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)': '@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)':
dependencies: dependencies:
'@typescript-eslint/scope-manager': 8.37.0 '@typescript-eslint/scope-manager': 8.38.0
'@typescript-eslint/types': 8.37.0 '@typescript-eslint/types': 8.38.0
'@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3)
'@typescript-eslint/visitor-keys': 8.37.0 '@typescript-eslint/visitor-keys': 8.38.0
debug: 4.4.1 debug: 4.4.1
eslint: 9.31.0(jiti@2.4.2) eslint: 9.31.0(jiti@2.4.2)
typescript: 5.8.3 typescript: 5.8.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/project-service@8.37.0(typescript@5.8.3)': '@typescript-eslint/project-service@8.38.0(typescript@5.8.3)':
dependencies: dependencies:
'@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.8.3) '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.8.3)
'@typescript-eslint/types': 8.37.0 '@typescript-eslint/types': 8.38.0
debug: 4.4.1 debug: 4.4.1
typescript: 5.8.3 typescript: 5.8.3
transitivePeerDependencies: transitivePeerDependencies:
@@ -3661,20 +3661,20 @@ snapshots:
'@typescript-eslint/types': 8.34.1 '@typescript-eslint/types': 8.34.1
'@typescript-eslint/visitor-keys': 8.34.1 '@typescript-eslint/visitor-keys': 8.34.1
'@typescript-eslint/scope-manager@8.37.0': '@typescript-eslint/scope-manager@8.38.0':
dependencies: dependencies:
'@typescript-eslint/types': 8.37.0 '@typescript-eslint/types': 8.38.0
'@typescript-eslint/visitor-keys': 8.37.0 '@typescript-eslint/visitor-keys': 8.38.0
'@typescript-eslint/tsconfig-utils@8.37.0(typescript@5.8.3)': '@typescript-eslint/tsconfig-utils@8.38.0(typescript@5.8.3)':
dependencies: dependencies:
typescript: 5.8.3 typescript: 5.8.3
'@typescript-eslint/type-utils@8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)': '@typescript-eslint/type-utils@8.38.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)':
dependencies: dependencies:
'@typescript-eslint/types': 8.37.0 '@typescript-eslint/types': 8.38.0
'@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3)
'@typescript-eslint/utils': 8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)
debug: 4.4.1 debug: 4.4.1
eslint: 9.31.0(jiti@2.4.2) eslint: 9.31.0(jiti@2.4.2)
ts-api-utils: 2.1.0(typescript@5.8.3) ts-api-utils: 2.1.0(typescript@5.8.3)
@@ -3686,14 +3686,14 @@ snapshots:
'@typescript-eslint/types@8.34.1': {} '@typescript-eslint/types@8.34.1': {}
'@typescript-eslint/types@8.37.0': {} '@typescript-eslint/types@8.38.0': {}
'@typescript-eslint/typescript-estree@8.37.0(typescript@5.8.3)': '@typescript-eslint/typescript-estree@8.38.0(typescript@5.8.3)':
dependencies: dependencies:
'@typescript-eslint/project-service': 8.37.0(typescript@5.8.3) '@typescript-eslint/project-service': 8.38.0(typescript@5.8.3)
'@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.8.3) '@typescript-eslint/tsconfig-utils': 8.38.0(typescript@5.8.3)
'@typescript-eslint/types': 8.37.0 '@typescript-eslint/types': 8.38.0
'@typescript-eslint/visitor-keys': 8.37.0 '@typescript-eslint/visitor-keys': 8.38.0
debug: 4.4.1 debug: 4.4.1
fast-glob: 3.3.3 fast-glob: 3.3.3
is-glob: 4.0.3 is-glob: 4.0.3
@@ -3704,12 +3704,12 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@typescript-eslint/utils@8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)': '@typescript-eslint/utils@8.38.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)':
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@2.4.2)) '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@2.4.2))
'@typescript-eslint/scope-manager': 8.37.0 '@typescript-eslint/scope-manager': 8.38.0
'@typescript-eslint/types': 8.37.0 '@typescript-eslint/types': 8.38.0
'@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3)
eslint: 9.31.0(jiti@2.4.2) eslint: 9.31.0(jiti@2.4.2)
typescript: 5.8.3 typescript: 5.8.3
transitivePeerDependencies: transitivePeerDependencies:
@@ -3720,14 +3720,14 @@ snapshots:
'@typescript-eslint/types': 8.34.1 '@typescript-eslint/types': 8.34.1
eslint-visitor-keys: 4.2.1 eslint-visitor-keys: 4.2.1
'@typescript-eslint/visitor-keys@8.37.0': '@typescript-eslint/visitor-keys@8.38.0':
dependencies: dependencies:
'@typescript-eslint/types': 8.37.0 '@typescript-eslint/types': 8.38.0
eslint-visitor-keys: 4.2.1 eslint-visitor-keys: 4.2.1
'@ungap/structured-clone@1.3.0': {} '@ungap/structured-clone@1.3.0': {}
'@vitejs/plugin-react@4.5.1(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0))': '@vitejs/plugin-react@4.5.1(vite@6.3.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0))':
dependencies: dependencies:
'@babel/core': 7.27.4 '@babel/core': 7.27.4
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.27.4) '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.27.4)
@@ -3735,7 +3735,7 @@ snapshots:
'@rolldown/pluginutils': 1.0.0-beta.9 '@rolldown/pluginutils': 1.0.0-beta.9
'@types/babel__core': 7.20.5 '@types/babel__core': 7.20.5
react-refresh: 0.17.0 react-refresh: 0.17.0
vite: 6.3.5(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) vite: 6.3.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -3796,7 +3796,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
astro@5.12.0(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0): astro@5.12.3(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(rollup@4.45.1)(typescript@5.8.3)(yaml@2.8.0):
dependencies: dependencies:
'@astrojs/compiler': 2.12.2 '@astrojs/compiler': 2.12.2
'@astrojs/internal-helpers': 0.6.1 '@astrojs/internal-helpers': 0.6.1
@@ -3852,8 +3852,8 @@ snapshots:
unist-util-visit: 5.0.0 unist-util-visit: 5.0.0
unstorage: 1.16.1 unstorage: 1.16.1
vfile: 6.0.3 vfile: 6.0.3
vite: 6.3.5(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) vite: 6.3.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)
vitefu: 1.1.1(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)) vitefu: 1.1.1(vite@6.3.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0))
xxhash-wasm: 1.1.0 xxhash-wasm: 1.1.0
yargs-parser: 21.1.1 yargs-parser: 21.1.1
yocto-spinner: 0.2.3 yocto-spinner: 0.2.3
@@ -4371,9 +4371,9 @@ snapshots:
unicode-properties: 1.4.1 unicode-properties: 1.4.1
unicode-trie: 2.0.0 unicode-trie: 2.0.0
framer-motion@12.23.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): framer-motion@12.23.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies: dependencies:
motion-dom: 12.23.6 motion-dom: 12.23.9
motion-utils: 12.23.6 motion-utils: 12.23.6
tslib: 2.8.1 tslib: 2.8.1
optionalDependencies: optionalDependencies:
@@ -4732,7 +4732,7 @@ snapshots:
magicast@0.3.5: magicast@0.3.5:
dependencies: dependencies:
'@babel/parser': 7.28.0 '@babel/parser': 7.28.0
'@babel/types': 7.28.1 '@babel/types': 7.28.2
source-map-js: 1.2.1 source-map-js: 1.2.1
markdown-extensions@2.0.0: {} markdown-extensions@2.0.0: {}
@@ -5203,7 +5203,7 @@ snapshots:
mkdirp@3.0.1: {} mkdirp@3.0.1: {}
motion-dom@12.23.6: motion-dom@12.23.9:
dependencies: dependencies:
motion-utils: 12.23.6 motion-utils: 12.23.6
@@ -5788,12 +5788,12 @@ snapshots:
type-fest@4.41.0: {} type-fest@4.41.0: {}
typescript-eslint@8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3): typescript-eslint@8.38.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3):
dependencies: dependencies:
'@typescript-eslint/eslint-plugin': 8.37.0(@typescript-eslint/parser@8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/eslint-plugin': 8.38.0(@typescript-eslint/parser@8.38.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/parser': 8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/parser': 8.38.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)
'@typescript-eslint/typescript-estree': 8.37.0(typescript@5.8.3) '@typescript-eslint/typescript-estree': 8.38.0(typescript@5.8.3)
'@typescript-eslint/utils': 8.37.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/utils': 8.38.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3)
eslint: 9.31.0(jiti@2.4.2) eslint: 9.31.0(jiti@2.4.2)
typescript: 5.8.3 typescript: 5.8.3
transitivePeerDependencies: transitivePeerDependencies:
@@ -5919,7 +5919,7 @@ snapshots:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
vfile-message: 4.0.2 vfile-message: 4.0.2
vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0): vite@6.3.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0):
dependencies: dependencies:
esbuild: 0.25.8 esbuild: 0.25.8
fdir: 6.4.6(picomatch@4.0.3) fdir: 6.4.6(picomatch@4.0.3)
@@ -5928,15 +5928,15 @@ snapshots:
rollup: 4.45.1 rollup: 4.45.1
tinyglobby: 0.2.14 tinyglobby: 0.2.14
optionalDependencies: optionalDependencies:
'@types/node': 24.0.15 '@types/node': 24.1.0
fsevents: 2.3.3 fsevents: 2.3.3
jiti: 2.4.2 jiti: 2.4.2
lightningcss: 1.30.1 lightningcss: 1.30.1
yaml: 2.8.0 yaml: 2.8.0
vitefu@1.1.1(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)): vitefu@1.1.1(vite@6.3.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)):
optionalDependencies: optionalDependencies:
vite: 6.3.5(@types/node@24.0.15)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) vite: 6.3.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)
web-namespaces@2.0.1: {} web-namespaces@2.0.1: {}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

1
public/favicon.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@@ -59,16 +59,15 @@ const socialLinks = [
<div class="col-span-1 md:col-span-3"> <div class="col-span-1 md:col-span-3">
<a href="/" class="group inline-block"> <a href="/" class="group inline-block">
<div class="flex items-center"> <div class="flex items-center">
<div <div class="mx-auto aspect-square overflow-hidden rounded-lg">
class="relative flex h-10 w-10 transform items-center justify-center overflow-hidden rounded-lg bg-gradient-to-br from-zinc-800 to-zinc-600 shadow-lg transition-transform dark:from-zinc-200 dark:to-zinc-400" <img
> src=`${process.env.DIRECTUS_URL ?? "https://directus.alexlebens.dev"}/assets/${global.logo}`
<span alt="logo"
class="theme-transition-all text-xl font-bold text-zinc-100 duration-300 dark:text-zinc-900" class="max-h-[40px] max-w-[40px] object-cover"
> loading="eager"
{global.initals} />
</span>
<div class="absolute inset-0"></div>
</div> </div>
<span <span
class="theme-transition-color ml-3 text-xl font-bold text-zinc-900 dark:text-zinc-100" class="theme-transition-color ml-3 text-xl font-bold text-zinc-900 dark:text-zinc-100"
> >
@@ -112,7 +111,7 @@ const socialLinks = [
<!-- Quick links --> <!-- Quick links -->
<div class="col-span-1 md:col-span-3"> <div class="col-span-1 md:col-span-3">
<h3 <h3
class="theme-transition-color relative inline-block pb-2 text-sm font-semibold tracking-wider text-zinc-900 uppercase after:absolute after:bottom-0 after:left-0 after:h-0.5 after:w-8 after:bg-zinc-300 after:content-[''] dark:text-zinc-100 dark:after:bg-zinc-700" class="theme-transition-color after:bg-turquoise dark:after:bg-turquoise relative inline-block pb-2 text-sm font-semibold tracking-wider text-zinc-900 uppercase after:absolute after:bottom-0 after:left-0 after:h-0.5 after:w-8 after:content-[''] dark:text-zinc-100"
> >
Navigation Navigation
</h3> </h3>

View File

@@ -25,7 +25,19 @@ const currentPath = pathname.slice(1);
> >
<div class="mx-auto flex max-w-3xl items-center justify-between px-4"> <div class="mx-auto flex max-w-3xl items-center justify-between px-4">
<!-- Logo --> <!-- Logo -->
<a href="/" class="text-xl font-bold text-zinc-900 dark:text-white">{global.initals}</a> <a
href="/"
class="from-midnight to-turquoise relative flex h-10 w-10 items-center justify-center overflow-hidden rounded-lg bg-gradient-to-br text-xl shadow-lg transition-transform"
>
<div class="mx-auto aspect-square overflow-hidden rounded-lg">
<img
src=`${process.env.DIRECTUS_URL ?? "https://directus.alexlebens.dev"}/assets/${global.logo}`
alt="logo"
class="max-h-[40px] max-w-[40px] object-cover"
loading="eager"
/>
</div>
</a>
<!-- Desktop navigation --> <!-- Desktop navigation -->
<nav class="hidden items-center space-x-6 sm:flex"> <nav class="hidden items-center space-x-6 sm:flex">

View File

@@ -19,8 +19,8 @@ const { tags = [], class: className = '' } = Astro.props;
</a> </a>
))} ))}
{tags.length > 2 && ( {tags.length > 2 && (
<span class="inline-flex items-center rounded-full bg-zinc-50 px-2 py-0.5 text-xs text-zinc-500 dark:bg-zinc-800/50 dark:text-zinc-400"> <span class="inline-flex items-center rounded-full bg-zinc-50 px-2 py-0.5 text-xs text-zinc-500 dark:bg-zinc-900 dark:text-zinc-400">
+{tags.length - 3} +{tags.length - 2}
</span> </span>
)} )}
</div> </div>

View File

@@ -5,7 +5,7 @@
<button <button
id="theme-toggle" id="theme-toggle"
data-theme-toggle data-theme-toggle
class="group relative touch-manipulation overflow-hidden rounded-full p-1.5 transition-all duration-300 hover:bg-zinc-100 focus:ring-2 focus:ring-zinc-300 focus:outline-hidden sm:p-2 dark:hover:bg-zinc-800 dark:focus:ring-zinc-700" class="group hover:bg-desert/50 dark:hover:bg-midnight/50 relative touch-manipulation overflow-hidden rounded-full p-1.5 transition-all duration-300 focus:ring-2 focus:ring-zinc-300 focus:outline-hidden sm:p-2 dark:focus:ring-zinc-700"
aria-label="Toggle dark mode" aria-label="Toggle dark mode"
> >
<div class="relative z-10 flex h-5 w-5 items-center justify-center"> <div class="relative z-10 flex h-5 w-5 items-center justify-center">

View File

@@ -40,7 +40,7 @@ try {
<p <p
class="mb-2 line-clamp-2 text-center text-sm text-zinc-600 sm:mb-3 sm:line-clamp-3 sm:text-left sm:text-base dark:text-zinc-400" class="mb-2 line-clamp-2 text-center text-sm text-zinc-600 sm:mb-3 sm:line-clamp-3 sm:text-left sm:text-base dark:text-zinc-400"
> >
{post.description} <!-- {post.description} -->
</p> </p>
<div <div

View File

@@ -20,7 +20,7 @@ const { title, description } = Astro.props;
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.png" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} /> <meta name="generator" content={Astro.generator} />
<meta name="description" content={description} /> <meta name="description" content={description} />
<title>{title}</title> <title>{title}</title>

View File

@@ -29,12 +29,8 @@ import Layout from '../layouts/Layout.astro';
<div class="mt-10 flex flex-col items-center justify-center gap-4 sm:flex-row"> <div class="mt-10 flex flex-col items-center justify-center gap-4 sm:flex-row">
<a <a
href="/" href="/"
class="group relative inline-flex items-center gap-2 overflow-hidden rounded-lg bg-zinc-900 px-6 py-3 text-zinc-100 shadow-lg transition-all duration-300 hover:bg-zinc-800 hover:shadow-xl dark:bg-zinc-100 dark:text-zinc-900 dark:hover:bg-zinc-200" class="relative inline-flex items-center gap-2 overflow-hidden rounded-lg bg-zinc-900 px-6 py-3 text-zinc-100 shadow-lg transition-all duration-300 hover:bg-bg-turquoise hover:text-zinc-100 hover:shadow-xl dark:bg-zinc-100 dark:text-zinc-900 dark:hover:bg-turquoise"
> >
<span
class="absolute inset-0 z-0 bg-gradient-to-r from-zinc-700 to-zinc-900 opacity-0 transition-opacity duration-300 group-hover:opacity-100 dark:from-zinc-300 dark:to-zinc-100"
>
</span>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
@@ -55,7 +51,7 @@ import Layout from '../layouts/Layout.astro';
<button <button
id="back-button" id="back-button"
class="group inline-flex items-center gap-2 rounded-lg border border-zinc-300 px-6 py-3 text-zinc-700 shadow-xs transition-all duration-300 hover:bg-zinc-100 hover:shadow-md dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800" class="group inline-flex translate-y-0 items-center gap-2 rounded-lg border border-zinc-300 px-6 py-3 text-zinc-700 shadow-xs transition-all duration-300 hover:bg-zinc-100 hover:shadow-md dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@@ -7,7 +7,6 @@ import { readSingleton, readItems } from '@directus/sdk';
const global = await directus.request(readSingleton('global')); const global = await directus.request(readSingleton('global'));
const about = await directus.request(readSingleton('about')); const about = await directus.request(readSingleton('about'));
const skills = await directus.request( const skills = await directus.request(
readItems('skills', { readItems('skills', {
fields: ['*'], fields: ['*'],
@@ -20,17 +19,14 @@ const skills = await directus.request(
class="theme-transition-all mx-auto max-w-6xl px-4 py-8 sm:px-6 sm:py-12 md:py-16" class="theme-transition-all mx-auto max-w-6xl px-4 py-8 sm:px-6 sm:py-12 md:py-16"
transition:animate="slide" transition:animate="slide"
> >
<!-- Hero Section --> <!-- Introduction Section -->
<div class="relative mb-12 sm:mb-16 md:mb-20"> <div class="relative mb-12 sm:mb-16 md:mb-20">
<div class="relative grid grid-cols-1 items-center gap-8 md:grid-cols-2 md:gap-12"> <div class="relative grid grid-cols-1 items-center gap-8 md:grid-cols-2 md:gap-12">
<div class="hero-text order-2 text-center md:order-1 md:text-left"> <div class="hero-text order-2 text-center md:order-1 md:text-left">
<h1 <h1
class="theme-transition-color hero-text mb-4 text-3xl font-bold tracking-tight text-zinc-900 sm:mb-6 sm:text-4xl md:text-5xl dark:text-zinc-100" class="theme-transition-color hero-text mb-4 text-3xl font-bold tracking-tight text-zinc-900 sm:mb-6 sm:text-4xl md:text-5xl dark:text-zinc-100"
> >
Hello, I'm <span Hello, I'm <span class="theme-transition-all bg-clip-text">{global.name}</span>
class="theme-transition-all bg-gradient-to-r from-zinc-500 to-zinc-900 bg-clip-text text-transparent dark:from-zinc-300 dark:to-zinc-100"
>{global.name}</span
>
</h1> </h1>
<p <p
@@ -38,12 +34,6 @@ const skills = await directus.request(
> >
{about.background} {about.background}
</p> </p>
<div
class="social-links-container theme-transition-children flex flex-wrap justify-center gap-4 md:justify-start"
>
<!-- Social links remain the same -->
</div>
</div> </div>
<div class="relative order-1 md:order-2"> <div class="relative order-1 md:order-2">
@@ -61,18 +51,16 @@ const skills = await directus.request(
</div> </div>
</div> </div>
<!-- About Section --> <!-- About Me section -->
<div class="theme-transition-all mb-16 sm:mb-20 md:mb-24"> <div class="theme-transition-all mb-16 sm:mb-20 md:mb-24">
<div class="mx-auto max-w-3xl"> <div class="mx-auto max-w-3xl">
<h2 <h2
class="theme-transition-color mb-6 flex items-center justify-center text-2xl font-bold text-zinc-900 sm:mb-8 sm:text-3xl md:justify-start dark:text-zinc-100" class="theme-transition-color mb-6 flex items-center justify-center text-2xl font-bold text-zinc-900 sm:mb-8 sm:text-3xl md:justify-start dark:text-zinc-100"
> >
<span <span class="theme-transition-bg bg-turquoise mr-4 hidden h-1 w-8 sm:inline-block sm:w-12"
class="theme-transition-bg mr-4 hidden h-1 w-8 bg-zinc-300 sm:inline-block sm:w-12 dark:bg-zinc-700"
></span> ></span>
About Me About Me
<span <span class="theme-transition-bg bg-turquoise ml-4 hidden h-1 w-8 sm:inline-block sm:w-12"
class="theme-transition-bg ml-4 hidden h-1 w-8 bg-zinc-300 sm:inline-block sm:w-12 dark:bg-zinc-700"
></span> ></span>
</h2> </h2>
@@ -111,10 +99,7 @@ const skills = await directus.request(
<div class="slider-track animate-slide flex"> <div class="slider-track animate-slide flex">
{ {
[...skills, ...skills, ...skills].map((skill, index) => ( [...skills, ...skills, ...skills].map((skill, index) => (
<div <div class="skill-card theme-transition-element Ztransition-all mx-2 min-w-[220px] transform rounded-xl border border-zinc-300 bg-white duration-300 hover:-translate-y-2 hover:scale-105 hover:border-zinc-200 hover:shadow-xl sm:mx-4 sm:min-w-[280px] dark:border-zinc-700 dark:bg-zinc-900 dark:hover:border-zinc-800 dark:hover:bg-zinc-900">
key={`${skill.title}-${index}`}
class="skill-card theme-transition-element mx-2 min-w-[220px] transform rounded-xl border border-zinc-200 bg-white transition-all duration-300 hover:-translate-y-2 hover:scale-105 hover:border-zinc-300 hover:shadow-xl sm:mx-4 sm:min-w-[280px] dark:border-zinc-700 dark:bg-zinc-800/50 dark:hover:border-zinc-600"
>
<div class="p-4 sm:p-6"> <div class="p-4 sm:p-6">
<div class="mb-4 flex items-center justify-between sm:mb-6"> <div class="mb-4 flex items-center justify-between sm:mb-6">
<div class="flex items-center gap-2 sm:gap-4"> <div class="flex items-center gap-2 sm:gap-4">
@@ -132,7 +117,7 @@ const skills = await directus.request(
<div class="theme-transition-bg relative h-1.5 w-full overflow-hidden rounded-full bg-zinc-100 sm:h-2 dark:bg-zinc-700"> <div class="theme-transition-bg relative h-1.5 w-full overflow-hidden rounded-full bg-zinc-100 sm:h-2 dark:bg-zinc-700">
<div <div
class="progress-bar-animate theme-transition-bg absolute top-0 left-0 h-full rounded-full bg-gradient-to-r from-zinc-700 via-zinc-600 to-zinc-800 transition-all duration-1000 dark:from-zinc-300 dark:via-zinc-400 dark:to-zinc-200" class="progress-bar-animate theme-transition-bg from-turquoise via-bermuda to-turquoise absolute top-0 left-0 h-full rounded-full bg-gradient-to-r transition-all duration-1000"
style={`width: ${skill.level}%`} style={`width: ${skill.level}%`}
/> />
</div> </div>
@@ -158,6 +143,7 @@ const skills = await directus.request(
</div> </div>
</div> </div>
</div> </div>
<!-- Contact Section --> <!-- Contact Section -->
<div class="theme-transition-all mx-auto max-w-3xl text-center"> <div class="theme-transition-all mx-auto max-w-3xl text-center">
<h2 <h2
@@ -174,7 +160,7 @@ const skills = await directus.request(
<div class="group"> <div class="group">
<a <a
href=`mailto:${global.email}` href=`mailto:${global.email}`
class="theme-transition-all inline-flex items-center justify-center rounded-lg bg-zinc-900 px-6 py-3 text-base font-medium text-zinc-100 transition-colors group-hover:bg-blue-600 group-hover:text-zinc-100 sm:px-8 sm:py-4 sm:text-lg dark:bg-zinc-100 dark:text-zinc-900 dark:group-hover:bg-blue-600 dark:group-hover:text-zinc-100" class="theme-transition-all group-hover:bg-turquoise inline-flex items-center justify-center rounded-lg bg-zinc-900 px-6 py-3 text-base font-medium text-zinc-100 transition-colors duration-300 group-hover:text-zinc-100 sm:px-8 sm:py-4 sm:text-lg dark:bg-zinc-100 dark:text-zinc-900 dark:group-hover:text-zinc-100"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -191,10 +177,7 @@ const skills = await directus.request(
></path> ></path>
</svg> </svg>
<span class="relative inline-block overflow-hidden"> <span class="relative inline-block overflow-hidden">
<span class="relative z-10">Say Hello</span> <span class="relative z-10">Send Email</span>
<span
class="absolute bottom-0 left-0 h-0.5 w-0 bg-zinc-100 transition-all duration-300 group-hover:w-full"
></span>
</span> </span>
</a> </a>
</div> </div>
@@ -222,45 +205,13 @@ const skills = await directus.request(
animateContent(); animateContent();
// Create seamless infinite scrolling effect // Create seamless infinite scrolling effect
const sliderTrack = document.querySelector('.slider-track');
function setupInfiniteScroll() { function setupInfiniteScroll() {
const cards = document.querySelectorAll('.skill-card'); const cards = document.querySelectorAll('.skill-card');
if (!cards.length) return; if (!cards.length) return;
// Set proper animation based on screen size
function updateScrollAnimation() {
if (window.innerWidth >= 640) {
sliderTrack.style.animation = 'scroll 60s linear infinite';
} else {
sliderTrack.style.animation = 'scroll 40s linear infinite';
}
}
updateScrollAnimation();
window.addEventListener('resize', updateScrollAnimation);
} }
setupInfiniteScroll(); setupInfiniteScroll();
// Pause animation on hover/touch
sliderTrack?.addEventListener('mouseenter', () => {
sliderTrack.style.animationPlayState = 'paused';
});
sliderTrack?.addEventListener('touchstart', () => {
sliderTrack.style.animationPlayState = 'paused';
});
sliderTrack?.addEventListener('mouseleave', () => {
sliderTrack.style.animationPlayState = 'running';
});
sliderTrack?.addEventListener('touchend', () => {
setTimeout(() => {
sliderTrack.style.animationPlayState = 'running';
}, 1000); // Delay resuming animation after touch
});
// Add hover effects to cards - only on non-touch devices // Add hover effects to cards - only on non-touch devices
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0; const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
const cards = document.querySelectorAll('.skill-card'); const cards = document.querySelectorAll('.skill-card');
@@ -308,18 +259,6 @@ const skills = await directus.request(
}); });
}); });
} }
// Handle theme transition
document.addEventListener('themeChange', () => {
cards.forEach((card, index) => {
setTimeout(() => {
card.classList.add('theme-changing');
setTimeout(() => {
card.classList.remove('theme-changing');
}, 600);
}, index * 50);
});
});
}); });
</script> </script>
@@ -442,36 +381,4 @@ const skills = await directus.request(
min-height: 44px; min-height: 44px;
} }
} }
/* Content reveal animations */
.hero-text h1,
.hero-text span,
.hero-text p,
.hero-text ~ div {
opacity: 0;
transform: translateY(20px);
transition:
opacity 0.8s ease,
transform 0.8s ease;
}
.animate-reveal {
opacity: 1 !important;
transform: translateY(0) !important;
}
/* Theme transition effect */
:global(.theme-switching) .theme-transition-element {
animation: fadeIn 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
/* Smooth card transition during theme switch */
.skill-card.theme-transition-element {
transition:
background-color var(--theme-transition),
border-color var(--theme-transition),
color var(--theme-transition),
box-shadow var(--theme-transition),
transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
</style> </style>

View File

@@ -117,32 +117,6 @@ const { post, nextPost, prevPost } = Astro.props;
<script> <script>
document.addEventListener('astro:page-load', () => { document.addEventListener('astro:page-load', () => {
// Show button when scrolled down
const backToTopButton = document.getElementById('back-to-top');
if (backToTopButton) {
const toggleBackToTopButton = () => {
if (window.scrollY > 300) {
backToTopButton.classList.remove('opacity-0', 'invisible');
backToTopButton.classList.add('opacity-100', 'visible');
} else {
backToTopButton.classList.remove('opacity-100', 'visible');
backToTopButton.classList.add('opacity-0', 'invisible');
}
};
// Scroll to top when clicked
backToTopButton.addEventListener('click', () => {
window.scrollTo({
top: 0,
behavior: 'smooth',
});
});
// Check scroll position
window.addEventListener('scroll', toggleBackToTopButton);
toggleBackToTopButton();
}
// Add smooth reveal animations for content after loading // Add smooth reveal animations for content after loading
const animateContent = () => { const animateContent = () => {
// Animate hero section // Animate hero section
@@ -258,24 +232,6 @@ const { post, nextPost, prevPost } = Astro.props;
</script> </script>
<style> <style>
/* Content reveal animations */
.hero-text h1,
.hero-text span,
.hero-text p,
.hero-text ~ div,
article.group {
opacity: 0;
transform: translateY(20px);
transition:
opacity 0.8s ease,
transform 0.8s ease;
}
.animate-reveal {
opacity: 1 !important;
transform: translateY(0) !important;
}
/* Language badge styling */ /* Language badge styling */
.language-badge { .language-badge {
font-family: font-family:
@@ -296,7 +252,7 @@ const { post, nextPost, prevPost } = Astro.props;
} }
} }
/* Enhanced typography for blog content - Responsive adjustments */ /* Typography for blog content */
.prose { .prose {
@reference text-zinc-800 dark:text-zinc-200; @reference text-zinc-800 dark:text-zinc-200;
} }

View File

@@ -37,7 +37,7 @@ const years = Object.keys(postsByYear).sort((a, b) => b - a);
<p <p
class="hero-text mx-auto mb-6 max-w-2xl text-sm text-zinc-600 sm:mb-10 sm:text-base dark:text-zinc-400" class="hero-text mx-auto mb-6 max-w-2xl text-sm text-zinc-600 sm:mb-10 sm:text-base dark:text-zinc-400"
> >
Thoughts, ideas, and explorations on technology and selfhosting. A couple thoughts, a few ideas, and some guides on technology, development, and selfhosting.
</p> </p>
</div> </div>
</div> </div>
@@ -73,7 +73,7 @@ const years = Object.keys(postsByYear).sort((a, b) => b - a);
</h2> </h2>
<p class="mb-2 line-clamp-2 text-center text-sm text-zinc-600 sm:mb-3 sm:line-clamp-3 sm:text-left sm:text-base dark:text-zinc-400"> <p class="mb-2 line-clamp-2 text-center text-sm text-zinc-600 sm:mb-3 sm:line-clamp-3 sm:text-left sm:text-base dark:text-zinc-400">
{sortedPosts[0].description} {/* {sortedPosts[0].description} */}
</p> </p>
<div class="mb-2 flex flex-wrap items-center justify-center gap-3 text-xs text-zinc-500 sm:mb-3 sm:justify-start sm:gap-4 sm:text-sm dark:text-zinc-400"> <div class="mb-2 flex flex-wrap items-center justify-center gap-3 text-xs text-zinc-500 sm:mb-3 sm:justify-start sm:gap-4 sm:text-sm dark:text-zinc-400">
@@ -92,7 +92,7 @@ const years = Object.keys(postsByYear).sort((a, b) => b - a);
> >
<span class="relative inline-block overflow-hidden"> <span class="relative inline-block overflow-hidden">
<span class="relative z-10">Read article</span> <span class="relative z-10">Read article</span>
<span class="absolute bottom-0 left-0 h-0.5 w-0 bg-zinc-800 transition-all duration-300 group-hover:w-full dark:bg-zinc-200" /> <span class="bg-turquoise absolute bottom-0 left-0 h-0.5 w-0 transition-all duration-300 group-hover:w-full" />
</span> </span>
<svg <svg
viewBox="0 0 16 16" viewBox="0 0 16 16"
@@ -133,7 +133,7 @@ const years = Object.keys(postsByYear).sort((a, b) => b - a);
href={`#year-${year}`} href={`#year-${year}`}
class={`mr-3 flex items-center rounded-xl border border-zinc-300 bg-white/50 px-4 py-2 whitespace-nowrap transition-all duration-300 hover:bg-zinc-50 sm:rounded-2xl md:mr-0 md:w-full md:px-0 md:py-3 md:whitespace-normal dark:border-zinc-800 dark:hover:bg-zinc-800/70 ${index === 0 ? 'bg-white/50 dark:bg-zinc-900/50' : ''}`} class={`mr-3 flex items-center rounded-xl border border-zinc-300 bg-white/50 px-4 py-2 whitespace-nowrap transition-all duration-300 hover:bg-zinc-50 sm:rounded-2xl md:mr-0 md:w-full md:px-0 md:py-3 md:whitespace-normal dark:border-zinc-800 dark:hover:bg-zinc-800/70 ${index === 0 ? 'bg-white/50 dark:bg-zinc-900/50' : ''}`}
> >
<span class="ml-3 text-base font-medium text-zinc-900 md:text-lg dark:text-zinc-100"> <span class="mr-3 ml-3 text-base font-medium text-zinc-900 md:text-lg dark:text-zinc-100">
{year} {year}
</span> </span>
<span class="mr-3 text-xs text-zinc-500 md:ml-auto md:text-sm dark:text-zinc-400"> <span class="mr-3 text-xs text-zinc-500 md:ml-auto md:text-sm dark:text-zinc-400">
@@ -195,7 +195,7 @@ const years = Object.keys(postsByYear).sort((a, b) => b - a);
</h3> </h3>
<p class="z-10 mb-4 line-clamp-2 grow text-center text-sm text-zinc-600 md:text-left dark:text-zinc-400"> <p class="z-10 mb-4 line-clamp-2 grow text-center text-sm text-zinc-600 md:text-left dark:text-zinc-400">
{post.description} {/* {post.description} */}
</p> </p>
<div class="mb-2 flex flex-wrap items-center justify-center gap-3 text-xs text-zinc-500 sm:mb-3 sm:justify-start sm:gap-4 sm:text-sm dark:text-zinc-400"> <div class="mb-2 flex flex-wrap items-center justify-center gap-3 text-xs text-zinc-500 sm:mb-3 sm:justify-start sm:gap-4 sm:text-sm dark:text-zinc-400">
@@ -212,7 +212,7 @@ const years = Object.keys(postsByYear).sort((a, b) => b - a);
> >
<span class="relative inline-block overflow-hidden"> <span class="relative inline-block overflow-hidden">
<span class="relative z-10">Read article</span> <span class="relative z-10">Read article</span>
<span class="absolute bottom-0 left-0 h-0.5 w-0 bg-zinc-800 transition-all duration-300 group-hover:w-full dark:bg-zinc-200" /> <span class="bg-turquoise absolute bottom-0 left-0 h-0.5 w-0 transition-all duration-300 group-hover:w-full" />
</span> </span>
<svg <svg
viewBox="0 0 16 16" viewBox="0 0 16 16"
@@ -244,56 +244,6 @@ const years = Object.keys(postsByYear).sort((a, b) => b - a);
<script> <script>
document.addEventListener('astro:page-load', () => { document.addEventListener('astro:page-load', () => {
// Force the viewport to be exactly the width of the device
const fixViewportWidth = () => {
const viewport = document.querySelector('meta[name="viewport"]');
if (!viewport) {
const meta = document.createElement('meta');
meta.name = 'viewport';
meta.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no';
document.getElementsByTagName('head')[0].appendChild(meta);
} else {
viewport.setAttribute(
'content',
'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'
);
}
// Fix for horizontal overflow
document.body.style.overflowX = 'hidden';
document.documentElement.style.overflowX = 'hidden';
document.documentElement.style.width = '100%';
document.body.style.width = '100%';
};
fixViewportWidth();
// Show button when scrolled down
const backToTopButton = document.getElementById('back-to-top');
if (backToTopButton) {
const toggleBackToTopButton = () => {
if (window.scrollY > 300) {
backToTopButton.classList.remove('opacity-0', 'invisible');
backToTopButton.classList.add('opacity-100', 'visible');
} else {
backToTopButton.classList.remove('opacity-100', 'visible');
backToTopButton.classList.add('opacity-0', 'invisible');
}
};
// Scroll to top when clicked
backToTopButton.addEventListener('click', () => {
window.scrollTo({
top: 0,
behavior: 'smooth',
});
});
// Check scroll position
window.addEventListener('scroll', toggleBackToTopButton);
toggleBackToTopButton();
}
// Add smooth reveal animations for content after loading // Add smooth reveal animations for content after loading
const animateContent = () => { const animateContent = () => {
// Animate hero section // Animate hero section
@@ -322,453 +272,10 @@ const years = Object.keys(postsByYear).sort((a, b) => b - a);
}; };
animateContent(); animateContent();
// Add smooth scrolling to year links
document.querySelectorAll('a[href^="#year-"]').forEach((anchor) => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const targetId = this.getAttribute('href');
const targetElement = document.querySelector(targetId);
if (targetElement) {
window.scrollTo({
top: targetElement.offsetTop - 100,
behavior: 'smooth',
});
// Update URL hash without jumping
history.pushState(null, null, targetId);
}
});
});
// Adjust tag items based on screen size with extreme precision
const adjustTagItems = () => {
const tagItems = document.querySelectorAll('.theme-ripple');
const width =
window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
const isVerySmall = width < 360;
const isExtremelySmall = width < 280;
const isMicroScreen = width < 240;
// Fix container width to match viewport exactly
const container = document.querySelector('.tag-cloud');
if (container) {
container.style.maxWidth = '100vw';
container.style.width = '100%';
container.style.boxSizing = 'border-box';
// Remove any margins that might cause overflow
container.style.marginLeft = '0';
container.style.marginRight = '0';
}
// Fix grid width
const grid = document.querySelector('.grid');
if (grid) {
grid.style.width = '100%';
grid.style.maxWidth = '100%';
}
tagItems.forEach((item) => {
// Set appropriate classes based on screen size
if (isMicroScreen) {
item.classList.add('micro-screen');
item.classList.remove('extremely-small-screen', 'very-small-screen');
} else if (isExtremelySmall) {
item.classList.add('extremely-small-screen');
item.classList.remove('very-small-screen', 'micro-screen');
} else if (isVerySmall) {
item.classList.add('very-small-screen');
item.classList.remove('extremely-small-screen', 'micro-screen');
} else {
item.classList.remove('very-small-screen', 'extremely-small-screen', 'micro-screen');
}
// Ensure text doesn't overflow on small screens
const tagName = item.querySelector('h3');
const tagCount = item.querySelector('p');
if (tagName) {
// Set title for accessibility
tagName.title = tagName.textContent.trim();
// Adjust text length based on screen size
if (isMicroScreen && tagName.textContent.length > 6) {
tagName.dataset.fullText = tagName.textContent;
tagName.textContent = tagName.textContent.substring(0, 6) + '...';
} else if (isExtremelySmall && tagName.textContent.length > 8) {
tagName.dataset.fullText = tagName.textContent;
tagName.textContent = tagName.textContent.substring(0, 8) + '...';
} else if (isVerySmall && tagName.textContent.length > 12) {
tagName.dataset.fullText = tagName.textContent;
tagName.textContent = tagName.textContent.substring(0, 12) + '...';
} else if (tagName.dataset.fullText) {
tagName.textContent = tagName.dataset.fullText;
delete tagName.dataset.fullText;
}
}
// Set the tag hue for hover effects
const hue = item.style.getPropertyValue('--tag-hue');
item.style.setProperty('--hover-hue', hue);
});
};
adjustTagItems();
// Run on resize with optimized debounce
let resizeTimer;
const handleResize = () => {
if (resizeTimer) {
window.cancelAnimationFrame(resizeTimer);
}
resizeTimer = window.requestAnimationFrame(() => {
fixViewportWidth();
adjustTagItems();
});
};
window.addEventListener('resize', handleResize);
window.addEventListener('orientationchange', handleResize);
// Ensure layout is recalculated after page is fully loaded
window.addEventListener('load', () => {
fixViewportWidth();
adjustTagItems();
// Force recalculation after images and fonts are loaded
setTimeout(() => {
fixViewportWidth();
adjustTagItems();
}, 500);
});
// Add touch support for hover effects
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
if (isTouchDevice) {
const articles = document.querySelectorAll('article');
articles.forEach((article) => {
article.addEventListener('touchstart', () => {
article.classList.add('is-touched');
});
article.addEventListener('touchend', () => {
setTimeout(() => {
article.classList.remove('is-touched');
}, 300);
});
});
}
// Add touch support for mobile devices
const addTouchSupport = () => {
const tagItems = document.querySelectorAll('.theme-ripple');
tagItems.forEach((item) => {
item.addEventListener(
'touchstart',
() => {
item.classList.add('touch-active');
},
{ passive: true }
);
item.addEventListener(
'touchend',
() => {
setTimeout(() => {
item.classList.remove('touch-active');
}, 150);
},
{ passive: true }
);
// Cancel active state if touch moves away
item.addEventListener(
'touchmove',
(e) => {
const touch = e.touches[0];
const rect = item.getBoundingClientRect();
if (
touch.clientX < rect.left ||
touch.clientX > rect.right ||
touch.clientY < rect.top ||
touch.clientY > rect.bottom
) {
item.classList.remove('touch-active');
}
},
{ passive: true }
);
});
};
addTouchSupport();
// Fix for iOS Safari and other mobile browsers
if (/iPhone|iPad|iPod|Android/.test(navigator.userAgent)) {
document.documentElement.style.setProperty(
'--safe-area-inset-bottom',
'env(safe-area-inset-bottom)'
);
// Fix for mobile viewport height issues
const setVh = () => {
const vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
};
setVh();
window.addEventListener('resize', setVh);
window.addEventListener('orientationchange', () => {
// Wait for orientation change to complete
setTimeout(() => {
setVh();
fixViewportWidth();
}, 100);
});
}
}); });
</script> </script>
<style> <style>
/* Content reveal animations */
.hero-text h1,
.hero-text span,
.hero-text p,
.hero-text ~ div,
article.group {
opacity: 0;
transform: translateY(20px);
transition:
opacity 0.8s ease,
transform 0.8s ease;
}
.animate-reveal {
opacity: 1 !important;
transform: translateY(0) !important;
}
/* Search container hover effect */
.search-container:hover .search-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes pulse {
0%,
100% {
opacity: 0;
}
50% {
opacity: 1;
}
}
/* Shadow for dark mode */
:global(.dark) .tag-cloud {
box-shadow:
0 0 0 1px rgba(255, 255, 255, 0.05),
0 2px 4px rgba(0, 0, 0, 0.1),
0 4px 8px rgba(0, 0, 0, 0.15);
}
/* Input focus animation */
input:focus + div .search-pulse {
animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
/* Base styles */
.tag-cloud {
box-shadow:
0 0 0 1px rgba(0, 0, 0, 0.03),
0 2px 4px rgba(0, 0, 0, 0.03),
0 4px 8px rgba(0, 0, 0, 0.05);
transform-style: preserve-3d;
perspective: 1000px;
transition: all var(--theme-transition);
width: 100% !important;
max-width: 100% !important;
box-sizing: border-box;
margin-left: 0 !important;
margin-right: 0 !important;
}
/* Fix for horizontal overflow */
:global(html),
:global(body) {
overflow-x: hidden;
width: 100%;
max-width: 100%;
}
:global(.max-w-6xl) {
max-width: 100% !important;
width: 100% !important;
}
/* Micro screens (below 240px) */
@media (max-width: 239px) {
.tag-cloud {
padding: 0.5rem !important;
margin: 0 !important;
border-radius: 0.25rem !important;
width: 100% !important;
}
.tag-cloud h2 {
font-size: 0.875rem !important;
margin-bottom: 0.375rem !important;
}
.grid {
grid-template-columns: repeat(1, minmax(0, 1fr)) !important;
gap: 0.375rem !important;
width: 100% !important;
}
.micro-screen .flex {
padding: 0.25rem !important;
}
.micro-screen h3 {
font-size: 0.625rem !important;
}
.micro-screen p {
font-size: 0.5rem !important;
}
}
/* Extra extra extra small screens (240px-279px) */
@media (min-width: 240px) and (max-width: 279px) {
.xxxs\:grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.xxxs\:px-2 {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.xxxs\:py-2 {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
.xxxs\:w-6 {
width: 1.5rem;
}
.xxxs\:h-6 {
height: 1.5rem;
}
.xxxs\:text-xs {
font-size: 0.75rem;
}
.xxxs\:gap-2 {
gap: 0.5rem;
}
.xxxs\:text-\[9px\] {
font-size: 9px;
}
}
/* Extra extra small screens (280px-359px) */
@media (min-width: 280px) {
.xxs\:grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.xxs\:px-2 {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.xxs\:py-2 {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
.xxs\:w-6 {
width: 1.5rem;
}
.xxs\:h-6 {
height: 1.5rem;
}
.xxs\:text-xs {
font-size: 0.75rem;
}
.xxs\:gap-2 {
gap: 0.5rem;
}
.xxs\:text-\[9px\] {
font-size: 9px;
}
}
/* Extra small screens (360px-639px) */
@media (min-width: 360px) {
.xs\:grid-cols-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.xs\:px-2 {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.xs\:py-2 {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
.xs\:w-7 {
width: 1.75rem;
}
.xs\:h-7 {
height: 1.75rem;
}
.xs\:text-xs {
font-size: 0.75rem;
}
.xs\:text-sm {
font-size: 0.875rem;
}
.xs\:gap-2 {
gap: 0.5rem;
}
.xs\:text-\[10px\] {
font-size: 10px;
}
}
/* Hide scrollbar but keep functionality */
.hide-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
/* Line clamp for descriptions */ /* Line clamp for descriptions */
.line-clamp-2 { .line-clamp-2 {
display: -webkit-box; display: -webkit-box;
@@ -833,13 +340,4 @@ const years = Object.keys(postsByYear).sort((a, b) => b - a);
transform 0.15s ease-in-out, transform 0.15s ease-in-out,
opacity 0.15s ease-in-out !important; opacity 0.15s ease-in-out !important;
} }
/* Fix for iOS Safari notch */
@supports (padding: max(0px)) {
.tag-cloud {
padding-left: max(0.75rem, env(safe-area-inset-left));
padding-right: max(0.75rem, env(safe-area-inset-right));
padding-bottom: max(0.75rem, env(safe-area-inset-bottom));
}
}
</style> </style>

View File

@@ -7,7 +7,6 @@ import directus from '../../lib/directus';
import { readItems, readSingleton } from '@directus/sdk'; import { readItems, readSingleton } from '@directus/sdk';
const global = await directus.request(readSingleton('global')); const global = await directus.request(readSingleton('global'));
const posts = await directus.request( const posts = await directus.request(
readItems('posts', { readItems('posts', {
fields: ['*'], fields: ['*'],
@@ -18,11 +17,11 @@ const posts = await directus.request(
const recentPosts = posts const recentPosts = posts
.sort((a, b) => b.published_date.getTime() - a.published_date.getTime()) .sort((a, b) => b.published_date.getTime() - a.published_date.getTime())
.slice(0, 3); .slice(0, 3);
const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0, 5); const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0, 5);
--- ---
<Layout title=`Home | ${global.name}`> <Layout title=`Home | ${global.name}`>
<!-- Header section -->
<section <section
class="theme-transition-all px-4 py-10 sm:px-6 sm:py-16 md:py-20" class="theme-transition-all px-4 py-10 sm:px-6 sm:py-16 md:py-20"
transition:animate="slide" transition:animate="slide"
@@ -38,7 +37,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
<span class="relative inline-block"> <span class="relative inline-block">
selfhosting. selfhosting.
<span <span
class="theme-transition-bg absolute -bottom-1 left-0 h-1 w-full origin-left transform bg-zinc-800 dark:bg-zinc-200" class="theme-transition-bg bg-turquoise absolute -bottom-1 left-0 h-1 w-full origin-left transform"
></span> ></span>
</span> </span>
</span> </span>
@@ -53,7 +52,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
> >
<a <a
href="/about" href="/about"
class="theme-transition-color group relative inline-flex min-h-[44px] items-center gap-2 text-sm font-medium text-zinc-900 transition-all duration-300 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-100" class="theme-transition-color group relative inline-flex min-h-[44px] items-center gap-2 text-sm font-medium text-zinc-600 transition-all duration-300 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-100"
> >
<span>More about me</span> <span>More about me</span>
<svg <svg
@@ -77,7 +76,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
<!-- Featured post section --> <!-- Featured post section -->
<section <section
class="theme-transition-all border-t border-zinc-100 px-4 py-10 sm:px-6 sm:py-12 md:py-16 dark:border-zinc-800" class="theme-transition-all border-t border-zinc-200 px-4 py-10 sm:px-6 sm:py-12 md:py-16 dark:border-zinc-800"
> >
<div class="mx-auto max-w-3xl"> <div class="mx-auto max-w-3xl">
<div <div
@@ -90,7 +89,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
</h2> </h2>
<a <a
href="/blog" href="/blog"
class="theme-transition-color group relative flex min-h-[44px] items-center justify-center self-center text-sm font-medium text-zinc-900 hover:text-zinc-700 sm:self-auto dark:text-zinc-400 dark:hover:text-zinc-100" class="theme-transition-color group relative flex min-h-[44px] items-center justify-center self-center text-sm font-medium text-zinc-600 transition-all duration-300 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-100"
> >
<span class="flex items-center gap-1"> <span class="flex items-center gap-1">
View all posts View all posts
@@ -111,12 +110,12 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
</a> </a>
</div> </div>
<!-- Grid for mobile layout --> <!-- Post grid -->
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 sm:gap-8 md:gap-12 lg:grid-cols-3"> <div class="grid grid-cols-1 gap-6 sm:grid-cols-2 sm:gap-8 md:gap-12 lg:grid-cols-3">
{ {
recentPosts.map((post, index) => ( recentPosts.map((post, index) => (
<article class="hover-3d theme-transition-element group relative mx-auto flex w-full max-w-sm flex-col items-start sm:mx-0"> <article class="theme-transition-element group relative mx-auto flex w-full max-w-sm flex-col items-start sm:mx-0">
<div class="theme-transition-all absolute -inset-x-4 -inset-y-6 z-0 border border-zinc-200 bg-white/50 transition-all duration-300 group-hover:bg-zinc-50 sm:-inset-x-6 sm:rounded-2xl dark:border-zinc-800 dark:bg-zinc-900/50 dark:group-hover:bg-zinc-800/70" /> <div class="theme-transition-all absolute -inset-x-4 -inset-y-6 z-0 border border-zinc-300 bg-white/50 transition-all duration-300 group-hover:bg-zinc-50 sm:-inset-x-6 sm:rounded-2xl dark:border-zinc-800 dark:bg-zinc-900/50 dark:group-hover:bg-zinc-800/70" />
{post.image && ( {post.image && (
<div class="relative z-10 mb-4 aspect-video w-full overflow-hidden rounded-lg"> <div class="relative z-10 mb-4 aspect-video w-full overflow-hidden rounded-lg">
@@ -142,7 +141,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
</h3> </h3>
<p class="z-10 mb-2 line-clamp-2 text-center text-sm text-zinc-600 sm:mb-3 sm:line-clamp-3 sm:text-left sm:text-base dark:text-zinc-400"> <p class="z-10 mb-2 line-clamp-2 text-center text-sm text-zinc-600 sm:mb-3 sm:line-clamp-3 sm:text-left sm:text-base dark:text-zinc-400">
{post.description} {/* {post.description} */}
</p> </p>
<div class="mb-2 flex flex-wrap items-center justify-center gap-3 text-xs text-zinc-500 sm:mb-3 sm:justify-start sm:gap-4 sm:text-sm dark:text-zinc-400"> <div class="mb-2 flex flex-wrap items-center justify-center gap-3 text-xs text-zinc-500 sm:mb-3 sm:justify-start sm:gap-4 sm:text-sm dark:text-zinc-400">
@@ -157,7 +156,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
> >
<span class="relative inline-block overflow-hidden"> <span class="relative inline-block overflow-hidden">
<span class="relative z-10">Read article</span> <span class="relative z-10">Read article</span>
<span class="absolute bottom-0 left-0 h-0.5 w-0 bg-zinc-800 transition-all duration-300 group-hover:w-full dark:bg-zinc-200" /> <span class="bg-turquoise absolute bottom-0 left-0 h-0.5 w-0 transition-all duration-300 group-hover:w-full" />
</span> </span>
<svg <svg
viewBox="0 0 16 16" viewBox="0 0 16 16"
@@ -180,16 +179,16 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
</div> </div>
</section> </section>
<!-- Topics section --> <!-- Tags section -->
{ {
allTags.length > 0 && ( allTags.length > 0 && (
<section class="theme-transition-all border-t border-zinc-100 px-4 py-10 sm:px-6 sm:py-12 md:py-16 dark:border-zinc-800"> <section class="theme-transition-all border-t border-zinc-200 px-4 py-10 sm:px-6 sm:py-12 md:py-16 dark:border-zinc-800">
<div class="mx-auto max-w-3xl"> <div class="mx-auto max-w-3xl">
<h2 class="theme-transition-color mb-6 text-center text-xl font-bold tracking-tight text-zinc-900 sm:mb-8 sm:text-left sm:text-2xl md:text-3xl dark:text-zinc-100"> <h2 class="theme-transition-color mb-6 text-center text-xl font-bold tracking-tight text-zinc-900 sm:mb-8 sm:text-left sm:text-2xl md:text-3xl dark:text-zinc-100">
Popular Tags Popular Tags
</h2> </h2>
<div class="hover-3d mx-auto grid max-w-xs grid-cols-1 gap-3 sm:max-w-none sm:grid-cols-2 sm:gap-4 md:grid-cols-3"> <div class="mx-auto grid max-w-xs grid-cols-1 gap-3 sm:max-w-none sm:grid-cols-2 sm:gap-4 md:grid-cols-3">
{allTags.map((tag) => { {allTags.map((tag) => {
const tagCount = posts.filter((post) => post.tags && post.tags.includes(tag)).length; const tagCount = posts.filter((post) => post.tags && post.tags.includes(tag)).length;
return ( return (
@@ -201,7 +200,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
<span class="theme-transition-color mr-2 text-sm font-medium text-zinc-900 dark:text-zinc-100"> <span class="theme-transition-color mr-2 text-sm font-medium text-zinc-900 dark:text-zinc-100">
#{tag} #{tag}
</span> </span>
<span class="theme-transition-all shrink-0 rounded-full bg-zinc-100 px-2 py-0.5 text-xs text-zinc-500 dark:bg-zinc-800 dark:text-zinc-400"> <span class="theme-transition-all shrink-0 rounded-full bg-zinc-100 px-2.5 py-0.5 text-xs font-medium text-zinc-600 transition-colors hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-400 dark:hover:bg-zinc-700">
{tagCount} {tagCount === 1 ? 'post' : 'posts'} {tagCount} {tagCount === 1 ? 'post' : 'posts'}
</span> </span>
</div> </div>
@@ -219,135 +218,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
</Layout> </Layout>
<script> <script>
// Add hover effect for cards on touch devices
document.addEventListener('astro:page-load', () => { document.addEventListener('astro:page-load', () => {
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
if (isTouchDevice) {
const cards = document.querySelectorAll('.hover-3d');
cards.forEach((card) => {
card.addEventListener('touchstart', () => {
card.classList.add('is-touched');
});
card.addEventListener('touchend', () => {
setTimeout(() => {
card.classList.remove('is-touched');
}, 300);
});
});
// Disable hover animations on touch devices
document.documentElement.classList.add('touch-device');
}
// Viewport height fix for mobile browsers
const setVh = () => {
const vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', `${vh}px`);
};
// Set initial value
setVh();
// Update on resize and scroll to prevent content shifting
window.addEventListener('resize', setVh);
// Use a debounced scroll handler to prevent performance issues
let scrollTimeout;
window.addEventListener('scroll', () => {
if (scrollTimeout) {
window.cancelAnimationFrame(scrollTimeout);
}
scrollTimeout = window.requestAnimationFrame(() => {
// Lock width during scroll
document.body.style.width = '100%';
document.body.style.overflowX = 'hidden';
});
});
// Fix for iOS Safari address bar height changes
if (/iPhone|iPad|iPod/.test(navigator.userAgent)) {
// Force the layout to use the initial viewport size
document.documentElement.style.setProperty('--initial-vh', `${window.innerHeight * 0.01}px`);
// Apply fixed height to sections to prevent resizing
const sections = document.querySelectorAll('section');
sections.forEach((section) => {
section.style.width = '100%';
});
}
// Theme change handler that preserves scroll position and provides smoother transitions
document.addEventListener('themeChanged', () => {
// Store current scroll position
const scrollPosition = window.scrollY;
// Create a temporary overlay for smoother transition
const overlay = document.createElement('div');
overlay.style.cssText = `
position: fixed;
inset: 0;
background-color: ${document.documentElement.classList.contains('dark') ? 'rgba(255,255,255,0.05)' : 'rgba(0,0,0,0.05)'};
z-index: 9999;
pointer-events: none;
opacity: 0;
transition: opacity 0.3s ease;
`;
document.body.appendChild(overlay);
// Fade in overlay
requestAnimationFrame(() => {
overlay.style.opacity = '0.5';
// Update theme-transition elements without forcing reflow of entire page
requestAnimationFrame(() => {
document
.querySelectorAll(
'.theme-transition-all, .theme-transition-bg, .theme-transition-color'
)
.forEach((el) => {
// Apply a subtle animation instead of a hard reset
el.style.transition = 'all 0.5s ease';
});
// Fade out overlay after transition completes
setTimeout(() => {
overlay.style.opacity = '0';
setTimeout(() => {
overlay.remove();
}, 300);
}, 300);
});
});
// Restore scroll position (prevents jumping to top)
if (scrollPosition > 0) {
setTimeout(() => {
window.scrollTo({
top: scrollPosition,
behavior: 'auto', // Use 'auto' to prevent animation
});
}, 10);
}
});
// Fix theme inconsistency issues by checking theme on visibility change
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
const storedTheme = localStorage.getItem('theme');
const currentThemeIsDark = document.documentElement.classList.contains('dark');
if (storedTheme === 'dark' && !currentThemeIsDark) {
document.documentElement.classList.add('dark');
} else if (storedTheme === 'light' && currentThemeIsDark) {
document.documentElement.classList.remove('dark');
}
}
});
// Add smooth reveal animations for content after loading // Add smooth reveal animations for content after loading
const animateContent = () => { const animateContent = () => {
// Animate hero section // Animate hero section
@@ -389,53 +260,3 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
animateContent(); animateContent();
}); });
</script> </script>
<style>
/* Fix for theme transition issues */
:global(:root) {
--theme-transition-duration: 0.5s;
--theme-transition-timing: ease;
}
:global(html),
:global(body) {
transition: background-color var(--theme-transition-duration) var(--theme-transition-timing);
}
:global(.theme-transition-all) {
transition: all var(--theme-transition-duration) var(--theme-transition-timing);
}
:global(.theme-transition-bg) {
transition: background-color var(--theme-transition-duration) var(--theme-transition-timing);
}
:global(.theme-transition-color) {
transition: color var(--theme-transition-duration) var(--theme-transition-timing);
}
/* Remove the forced transition disabling which causes flickering */
:global(.theme-switching),
:global(.theme-switching *) {
/* Use a subtle transition instead of none */
transition-duration: 0.3s !important;
}
/* Content reveal animations */
.hero-text span,
.hero-text + p,
.hero-text ~ div,
article.group,
a.group.flex.flex-col {
opacity: 0;
transform: translateY(20px);
transition:
opacity 0.8s ease,
transform 0.8s ease;
}
.animate-reveal {
opacity: 1 !important;
transform: translateY(0) !important;
}
</style>

View File

@@ -32,13 +32,10 @@ export async function getStaticPaths() {
const { tag } = Astro.params as { tag: string }; const { tag } = Astro.params as { tag: string };
const { posts = [] } = Astro.props; const { posts = [] } = Astro.props;
console.log(`Tag: ${tag}, Number of posts: ${posts.length}`);
const sortedPosts = const sortedPosts =
posts && posts.length > 0 posts && posts.length > 0
? [...posts].sort((a, b) => b.published_date.valueOf() - a.published_date.valueOf()) ? [...posts].sort((a, b) => b.published_date.valueOf() - a.published_date.valueOf())
: []; : [];
console.log(`Sorted posts length: ${sortedPosts.length}`);
const relatedTags = [ const relatedTags = [
...new Set(sortedPosts.flatMap((post) => post.tags || []).filter((t) => t !== tag)), ...new Set(sortedPosts.flatMap((post) => post.tags || []).filter((t) => t !== tag)),
@@ -46,7 +43,7 @@ const relatedTags = [
--- ---
<BaseLayout title={`Posts tagged with "${tag}"`}> <BaseLayout title={`Posts tagged with "${tag}"`}>
<div class="mx-auto max-w-5xl px-4 py-10 sm:py-16" transition:animate="slide" > <div class="mx-auto max-w-5xl px-4 py-10 sm:py-16" transition:animate="slide">
<div class="relative mb-10 sm:mb-16"> <div class="relative mb-10 sm:mb-16">
<div class="relative text-center sm:text-left"> <div class="relative text-center sm:text-left">
<a <a
@@ -106,7 +103,7 @@ const relatedTags = [
<span class="absolute -bottom-1 left-0 h-1 w-full bg-zinc-200 dark:bg-zinc-700" <span class="absolute -bottom-1 left-0 h-1 w-full bg-zinc-200 dark:bg-zinc-700"
></span> ></span>
<span <span
class="animate-expand absolute -bottom-1 left-0 h-1 w-full bg-zinc-900 opacity-70 dark:bg-zinc-100" class="animate-expand bg-turquoise absolute -bottom-1 left-0 h-1 w-full opacity-70"
></span> ></span>
</span> </span>
</h1> </h1>
@@ -217,7 +214,7 @@ const relatedTags = [
> >
<span class="relative inline-block overflow-hidden"> <span class="relative inline-block overflow-hidden">
<span class="relative z-10">Read article</span> <span class="relative z-10">Read article</span>
<span class="absolute bottom-0 left-0 h-0.5 w-0 bg-zinc-800 transition-all duration-300 group-hover:w-full dark:bg-zinc-200" /> <span class="bg-turquoise absolute bottom-0 left-0 h-0.5 w-0 transition-all duration-300 group-hover:w-full" />
</span> </span>
<svg <svg
viewBox="0 0 16 16" viewBox="0 0 16 16"
@@ -308,7 +305,7 @@ const relatedTags = [
); );
}); });
// Animate posts with staggered delay // Animate posts
const articles = document.querySelectorAll('article.group'); const articles = document.querySelectorAll('article.group');
articles.forEach((article, index) => { articles.forEach((article, index) => {
setTimeout( setTimeout(
@@ -321,52 +318,10 @@ const relatedTags = [
}; };
animateContent(); animateContent();
// Add hover effect for cards on touch devices
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
if (isTouchDevice) {
const cards = document.querySelectorAll('.hover-3d');
cards.forEach((card) => {
card.addEventListener('touchstart', () => {
card.classList.add('is-touched');
});
card.addEventListener('touchend', () => {
setTimeout(() => {
card.classList.remove('is-touched');
}, 300);
});
});
// Disable hover animations on touch devices
document.documentElement.classList.add('touch-device');
}
}); });
</script> </script>
<style> <style>
/* Grid pattern background */
.bg-grid-pattern {
background-size: 30px 30px;
background-image: radial-gradient(circle, rgba(0, 0, 0, 0.05) 1px, transparent 1px);
}
:global(.dark) .bg-grid-pattern {
background-image: radial-gradient(circle, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
}
/* Hide scrollbar but keep functionality */
.hide-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
/* Animated underline */ /* Animated underline */
@keyframes expand { @keyframes expand {
from { from {
@@ -381,19 +336,6 @@ const relatedTags = [
animation: expand 1s ease-out forwards; animation: expand 1s ease-out forwards;
} }
/* Content reveal animations */
.hero-text h1,
.hero-text span,
.hero-text p,
.hero-text ~ div,
article.group {
opacity: 0;
transform: translateY(20px);
transition:
opacity 0.8s ease,
transform 0.8s ease;
}
.animate-reveal { .animate-reveal {
opacity: 1 !important; opacity: 1 !important;
transform: translateY(0) !important; transform: translateY(0) !important;
@@ -413,11 +355,4 @@ const relatedTags = [
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
overflow: hidden; overflow: hidden;
} }
/* Responsive adjustments */
@media (max-width: 640px) {
.animate-blob {
animation-duration: 10s;
}
}
</style> </style>

View File

@@ -4,6 +4,15 @@
/* https://tailwindcss.com/docs/dark-mode */ /* https://tailwindcss.com/docs/dark-mode */
@custom-variant dark (&:where(.dark, .dark *)); @custom-variant dark (&:where(.dark, .dark *));
/* Add custom color palette */
@theme {
--color-midnight: #0c354d;
--color-turquoise: #0da797;
--color-bermuda: #7fbab4;
--color-desert: #f9deb2;
--color-bronze: #9e7f5e;
}
@layer base { @layer base {
:root { :root {
font-family: 'Inter', sans-serif; font-family: 'Inter', sans-serif;
@@ -132,7 +141,22 @@ button {
transition: all 0.5s ease; transition: all 0.5s ease;
} }
a.hover:hover, /* Content reveal animations */
button:hover { .hero-text h1,
transform: translateY(-2px); .hero-text span,
.hero-text p,
.hero-text + p,
.hero-text ~ div,
article.group,
a.group.flex.flex-col {
opacity: 0;
transform: translateY(20px);
transition:
opacity 0.8s ease,
transform 0.8s ease;
}
.animate-reveal {
opacity: 1 !important;
transform: translateY(0) !important;
} }

View File

@@ -23,7 +23,7 @@ const iconSets = {
si: SiIcons, si: SiIcons,
}; };
const DynamicIcon = ({ name, set = 'fa' }: { name: string; set: string }) => { const DynamicIcon = ({ name, set = 'fa' }: { name: string; set?: string }) => {
let IconComponent = FaIcons.FaAlignCenter; let IconComponent = FaIcons.FaAlignCenter;
if (name.startsWith('Fa')) { if (name.startsWith('Fa')) {