Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
f16af9a98d
|
|||
ec45ad29ed | |||
17afce6710 | |||
f83fe98b38 | |||
2f244761ed |
@@ -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="0.11.3"
|
||||||
LABEL description="Astro based personal website"
|
LABEL description="Astro based personal website"
|
||||||
|
|
||||||
ENV PNPM_HOME="/pnpm"
|
ENV PNPM_HOME="/pnpm"
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "site-profile",
|
"name": "site-profile",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.11.1",
|
"version": "0.11.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
|
58
pnpm-lock.yaml
generated
58
pnpm-lock.yaml
generated
@@ -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.1(@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.1(@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,10 +28,10 @@ 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.1(@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.6(react-dom@19.1.0(react@19.1.0))(react@19.1.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==}
|
||||||
@@ -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.1:
|
||||||
resolution: {integrity: sha512-Oov5JsMFHuUmuO+Nx6plfv3nQNK1Xl/8CgLvR8lBhZTjYnraxhuPX5COVAzbom+YLgwaDfK7KBd8zOEopRf9mg==}
|
resolution: {integrity: sha512-/gH9cLIp6UNdbJO1FPBVN/Ea+1I9hJdQoLJKYUsXIRIfHcyF/3NCg0QVDJGw1oWkyQT6x6poQsnbgY9UXitjiw==}
|
||||||
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
|
||||||
|
|
||||||
@@ -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.1(@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.1(@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.1(@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.1(@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
|
||||||
@@ -3542,12 +3542,12 @@ 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:
|
||||||
@@ -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
|
||||||
|
|
||||||
@@ -3727,7 +3727,7 @@ snapshots:
|
|||||||
|
|
||||||
'@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.1(@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
|
||||||
@@ -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: {}
|
||||||
|
|
||||||
|
@@ -19,7 +19,7 @@ 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 - 3}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
@@ -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-cyan-600 hover:text-zinc-100 hover:shadow-xl dark:bg-zinc-100 dark:text-zinc-900 dark:hover:bg-cyan-600"
|
||||||
>
|
>
|
||||||
<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"
|
||||||
|
@@ -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,7 +51,7 @@ 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
|
||||||
@@ -111,10 +101,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">
|
||||||
@@ -158,6 +145,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
|
||||||
@@ -222,45 +210,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 +264,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 +386,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>
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -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">
|
||||||
@@ -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>
|
||||||
|
@@ -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"
|
||||||
@@ -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">
|
||||||
@@ -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>
|
|
||||||
|
@@ -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
|
||||||
@@ -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>
|
||||||
|
@@ -132,7 +132,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;
|
||||||
}
|
}
|
||||||
|
@@ -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')) {
|
||||||
|
Reference in New Issue
Block a user