Compare commits

..

18 Commits

Author SHA1 Message Date
505670dbf8 feat: remove unused packages
Some checks failed
test-build / guarddog (push) Successful in 34s
renovate / renovate (push) Successful in 47s
test-build / build (push) Successful in 1m6s
release-image-harbor / build (push) Successful in 1m1s
release-image-gitea / build (push) Successful in 1m3s
release-image-gitea / release (push) Failing after 28s
release-image-harbor / release (push) Successful in 2m27s
2026-02-16 00:28:58 -06:00
b3d7e7af2b chore(deps): update deps
All checks were successful
test-build / guarddog (push) Successful in 31s
renovate / renovate (push) Successful in 1m0s
test-build / build (push) Successful in 1m24s
2026-02-16 00:21:43 -06:00
440c95224d feat: release 2.11.0 2026-02-16 00:20:26 -06:00
b9ee82e9d8 Merge pull request 'chore(deps): update dependency eslint-plugin-astro to v1.6.0' (#340) from renovate/eslint-plugin-astro-1.x-lockfile into main
All checks were successful
test-build / guarddog (push) Successful in 33s
renovate / renovate (push) Successful in 49s
test-build / build (push) Successful in 1m26s
Reviewed-on: #340
2026-02-16 06:20:15 +00:00
3af9f08b7c chore(deps): update dependency eslint-plugin-astro to v1.6.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
test-build / guarddog (pull_request) Successful in 1m20s
test-build / build (pull_request) Successful in 2m18s
2026-02-16 06:09:50 +00:00
0bd56b172f Merge pull request 'chore(deps): update dependency @swup/astro to v1.8.0' (#339) from renovate/swup-astro-1.x-lockfile into main
All checks were successful
test-build / guarddog (push) Successful in 54s
renovate / renovate (push) Successful in 1m11s
test-build / build (push) Successful in 2m34s
Reviewed-on: #339
2026-02-16 06:08:50 +00:00
ebf70bd747 chore(deps): update dependency @swup/astro to v1.8.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
test-build / guarddog (pull_request) Successful in 38s
test-build / build (pull_request) Successful in 1m37s
2026-02-16 05:57:02 +00:00
9c5e9b6a5b Merge pull request 'chore(deps): update dependency @eslint-react/eslint-plugin to v2.13.0' (#338) from renovate/eslint-react-eslint-plugin-2.x-lockfile into main
All checks were successful
test-build / guarddog (push) Successful in 51s
test-build / build (push) Successful in 1m42s
renovate / renovate (push) Successful in 2m17s
Reviewed-on: #338
2026-02-16 05:55:20 +00:00
568f9e5164 chore(deps): update dependency @eslint-react/eslint-plugin to v2.13.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
test-build / guarddog (pull_request) Successful in 43s
test-build / build (pull_request) Successful in 1m50s
2026-02-16 05:40:13 +00:00
a74cc775d0 feat: final refactor of sections
All checks were successful
test-build / guarddog (push) Successful in 35s
test-build / build (push) Successful in 1m1s
renovate / renovate (push) Successful in 2m15s
2026-02-15 23:38:55 -06:00
5271be52a2 feat: rename button components to include button in name for consistency 2026-02-15 22:05:36 -06:00
8a649b7647 feat: imporvement pass over sections 2026-02-15 15:42:27 -06:00
c4be4653be fix: run theme on page swap 2026-02-15 00:05:16 -06:00
47a637353c feat: move improved components out of ui folder 2026-02-14 23:10:43 -06:00
a09a4ee240 feat: imporve theme toggle button 2026-02-14 23:08:12 -06:00
342ae8900a feat: refactor buttons, except for theme 2026-02-14 22:09:49 -06:00
2cdef1a553 feat: release 2.10.1
All checks were successful
test-build / guarddog (push) Successful in 45s
test-build / build (push) Successful in 1m17s
release-image-gitea / build (push) Successful in 1m19s
release-image-harbor / build (push) Successful in 3m18s
release-image-gitea / release (push) Successful in 4m4s
release-image-harbor / release (push) Successful in 2m30s
renovate / renovate (push) Successful in 4m22s
2026-02-14 17:19:58 -06:00
a8d6446674 feat: add docker login
All checks were successful
test-build / guarddog (push) Successful in 1m47s
renovate / renovate (push) Successful in 3m1s
test-build / build (push) Successful in 3m21s
2026-02-14 17:09:33 -06:00
49 changed files with 875 additions and 1056 deletions

View File

@@ -48,6 +48,13 @@ jobs:
username: ${{ gitea.actor }} username: ${{ gitea.actor }}
password: ${{ secrets.REPOSITORY_TOKEN }} password: ${{ secrets.REPOSITORY_TOKEN }}
- name: Login to Docker
uses: docker/login-action@v3
with:
registry: ${{ vars.DH_REGISTRY }}
username: ${{ secrets.DH_USERNAME }}
password: ${{ secrets.DH_TOKEN }}
- name: Create Kubeconfig - name: Create Kubeconfig
run: | run: |
mkdir $HOME/.kube mkdir $HOME/.kube

View File

@@ -48,6 +48,13 @@ jobs:
username: ${{ vars.REGISTRY_USER }} username: ${{ vars.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_SECRET }} password: ${{ secrets.REGISTRY_SECRET }}
- name: Login to Docker
uses: docker/login-action@v3
with:
registry: ${{ vars.DH_REGISTRY }}
username: ${{ secrets.DH_USERNAME }}
password: ${{ secrets.DH_TOKEN }}
- name: Create Kubeconfig - name: Create Kubeconfig
run: | run: |
mkdir $HOME/.kube mkdir $HOME/.kube

View File

@@ -27,7 +27,7 @@ ENV SITE_URL=https://www.alexlebens.dev
ENV DIRECTUS_URL=https://directus.alexlebens.net ENV DIRECTUS_URL=https://directus.alexlebens.net
ENV PORT=4321 ENV PORT=4321
LABEL version="2.10.0" LABEL version="2.11.0"
LABEL description="Astro based personal website" LABEL description="Astro based personal website"
EXPOSE $PORT EXPOSE $PORT

View File

@@ -1,7 +1,7 @@
{ {
"name": "site-profile", "name": "site-profile",
"type": "module", "type": "module",
"version": "2.10.0", "version": "2.11.0",
"homepage": "https://www.alexlebens.dev", "homepage": "https://www.alexlebens.dev",
"bugs": { "bugs": {
"url": "https://gitea.alexlebens.dev/alexlebens/site-profile/issues", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/issues",
@@ -39,18 +39,16 @@
"@iconify-json/pajamas": "^1.2.15", "@iconify-json/pajamas": "^1.2.15",
"@iconify-json/simple-icons": "^1.2.70", "@iconify-json/simple-icons": "^1.2.70",
"@playform/compress": "^0.2.1", "@playform/compress": "^0.2.1",
"@swup/astro": "^1.7.0", "@swup/astro": "^1.8.0",
"@tailwindcss/postcss": "^4.1.18", "@tailwindcss/postcss": "^4.1.18",
"@tailwindcss/vite": "^4.1.18", "@tailwindcss/vite": "^4.1.18",
"@types/react": "^19.2.14", "@types/react": "^19.2.14",
"@types/unist": "^3.0.3", "@types/unist": "^3.0.3",
"astro": "^5.17.2", "astro": "^5.17.2",
"astro-compressor": "^1.2.0",
"astro-icon": "^1.1.5", "astro-icon": "^1.1.5",
"marked": "^17.0.2", "marked": "^17.0.2",
"marked-shiki": "^1.2.1", "marked-shiki": "^1.2.1",
"mdast-util-to-string": "^4.0.0", "mdast-util-to-string": "^4.0.0",
"motion": "^12.34.0",
"preline": "^4.0.1", "preline": "^4.0.1",
"react": "^19.2.4", "react": "^19.2.4",
"react-dom": "^19.2.4", "react-dom": "^19.2.4",
@@ -62,13 +60,12 @@
"ultrahtml": "^1.6.0" "ultrahtml": "^1.6.0"
}, },
"devDependencies": { "devDependencies": {
"@eslint-react/eslint-plugin": "^2.12.4", "@eslint-react/eslint-plugin": "^2.13.0",
"@tailwindcss/forms": "^0.5.11", "@tailwindcss/forms": "^0.5.11",
"@tailwindcss/typography": "^0.5.19", "@tailwindcss/typography": "^0.5.19",
"astro-icon": "^1.1.5",
"eslint": "^10.0.0", "eslint": "^10.0.0",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-astro": "^1.5.0", "eslint-plugin-astro": "^1.6.0",
"eslint-plugin-format": "^1.4.0", "eslint-plugin-format": "^1.4.0",
"eslint-plugin-react": "^7.37.5", "eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",

383
pnpm-lock.yaml generated
View File

@@ -45,8 +45,8 @@ importers:
specifier: ^0.2.1 specifier: ^0.2.1
version: 0.2.1(@types/node@25.2.3)(jiti@2.6.1)(rollup@2.79.2)(typescript@5.9.3)(yaml@2.8.2) version: 0.2.1(@types/node@25.2.3)(jiti@2.6.1)(rollup@2.79.2)(typescript@5.9.3)(yaml@2.8.2)
'@swup/astro': '@swup/astro':
specifier: ^1.7.0 specifier: ^1.8.0
version: 1.7.0(@types/babel__core@7.20.5) version: 1.8.0(@types/babel__core@7.20.5)
'@tailwindcss/postcss': '@tailwindcss/postcss':
specifier: ^4.1.18 specifier: ^4.1.18
version: 4.1.18 version: 4.1.18
@@ -62,9 +62,6 @@ importers:
astro: astro:
specifier: ^5.17.2 specifier: ^5.17.2
version: 5.17.2(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@2.79.2)(terser@5.44.1)(typescript@5.9.3)(yaml@2.8.2) version: 5.17.2(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@2.79.2)(terser@5.44.1)(typescript@5.9.3)(yaml@2.8.2)
astro-compressor:
specifier: ^1.2.0
version: 1.2.0
astro-icon: astro-icon:
specifier: ^1.1.5 specifier: ^1.1.5
version: 1.1.5 version: 1.1.5
@@ -77,9 +74,6 @@ importers:
mdast-util-to-string: mdast-util-to-string:
specifier: ^4.0.0 specifier: ^4.0.0
version: 4.0.0 version: 4.0.0
motion:
specifier: ^12.34.0
version: 12.34.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
preline: preline:
specifier: ^4.0.1 specifier: ^4.0.1
version: 4.0.1 version: 4.0.1
@@ -109,8 +103,8 @@ importers:
version: 1.6.0 version: 1.6.0
devDependencies: devDependencies:
'@eslint-react/eslint-plugin': '@eslint-react/eslint-plugin':
specifier: ^2.12.4 specifier: ^2.13.0
version: 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) version: 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@tailwindcss/forms': '@tailwindcss/forms':
specifier: ^0.5.11 specifier: ^0.5.11
version: 0.5.11(tailwindcss@4.1.18) version: 0.5.11(tailwindcss@4.1.18)
@@ -124,8 +118,8 @@ importers:
specifier: ^10.1.8 specifier: ^10.1.8
version: 10.1.8(eslint@10.0.0(jiti@2.6.1)) version: 10.1.8(eslint@10.0.0(jiti@2.6.1))
eslint-plugin-astro: eslint-plugin-astro:
specifier: ^1.5.0 specifier: ^1.6.0
version: 1.5.0(eslint@10.0.0(jiti@2.6.1)) version: 1.6.0(eslint@10.0.0(jiti@2.6.1))
eslint-plugin-format: eslint-plugin-format:
specifier: ^1.4.0 specifier: ^1.4.0
version: 1.4.0(eslint@10.0.0(jiti@2.6.1)) version: 1.4.0(eslint@10.0.0(jiti@2.6.1))
@@ -1168,40 +1162,40 @@ packages:
resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
'@eslint-react/ast@2.12.4': '@eslint-react/ast@2.13.0':
resolution: {integrity: sha512-UNr5OsaNXfDBcIWZtahlNBjqEQeysAU4NzPqsxnogghUd7pX0aQxaQHXIOdSKTd5037PbyHIILbiimDmI/BYAQ==} resolution: {integrity: sha512-43+5gmqV3MpatTzKnu/V2i/jXjmepvwhrb9MaGQvnXHQgq9J7/C7VVCCcwp6Rvp2QHAFquAAdvQDSL8IueTpeA==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0' typescript: '>=4.8.4 <6.0.0'
'@eslint-react/core@2.12.4': '@eslint-react/core@2.13.0':
resolution: {integrity: sha512-YAZrvc62wcMFT06twU4orNeGI8HCv9/B3X19hzU9/vhN5hCdr4HM3tl9E7qd79anjFBO30zflhpghB1zeAfjyQ==} resolution: {integrity: sha512-m62XDzkf1hpzW4sBc7uh7CT+8rBG2xz/itSADuEntlsg4YA7Jhb8hjU6VHf3wRFDwyfx5VnbV209sbJ7Azey0Q==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0' typescript: '>=4.8.4 <6.0.0'
'@eslint-react/eff@2.12.4': '@eslint-react/eff@2.13.0':
resolution: {integrity: sha512-lpC7s9bvHvVMcYZ01Py/nUTWjhq2/VRI+ftfFfIeqPTYXuXt95V3MIS+jtTK74f3BVXqxpAFbSx81hpd7ISMeg==} resolution: {integrity: sha512-rEH2R8FQnUAblUW+v3ZHDU1wEhatbL1+U2B1WVuBXwSKqzF7BGaLqCPIU7o9vofumz5MerVfaCtJgI8jYe2Btg==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
'@eslint-react/eslint-plugin@2.12.4': '@eslint-react/eslint-plugin@2.13.0':
resolution: {integrity: sha512-ML7EyET19pPgGOQmm5uEN9yoVkHqTtPyLMPWL2wlap9LVuFxoMK2owESkrq7mhWrXPf/NLuO4Z7lTcu5Sqv1EQ==} resolution: {integrity: sha512-iaMXpqnJCTW7317hg8L4wx7u5aIiPzZ+d1p59X8wXFgMHzFX4hNu4IfV8oygyjmWKdLsjKE9sEpv/UYWczlb+A==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0' typescript: '>=4.8.4 <6.0.0'
'@eslint-react/shared@2.12.4': '@eslint-react/shared@2.13.0':
resolution: {integrity: sha512-a6qV2cQzxH3EFPlc2MU1/13D6J+LwlzUVTQ7vGAEKBHNEIxIwXBSdah8MGU8TQp/3+12oVMIm6FK71yZOjwqkA==} resolution: {integrity: sha512-IOloCqrZ7gGBT4lFf9+0/wn7TfzU7JBRjYwTSyb9SDngsbeRrtW95ZpgUpS8/jen1wUEm6F08duAooTZ2FtsWA==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0' typescript: '>=4.8.4 <6.0.0'
'@eslint-react/var@2.12.4': '@eslint-react/var@2.13.0':
resolution: {integrity: sha512-NITTpD0fNPVmFpiTwP4PuyLblhuKvMWDU36bIoQL+Xh7xxcCc+lbiUCCOQiOzFgz+l36JhnOAjoRJ4rnP3yDUA==} resolution: {integrity: sha512-dM+QaeiHR16qPQoJYg205MkdHYSWVa2B7ore5OFpOPlSwqDV3tLW7I+475WjbK7potq5QNPTxRa7VLp9FGeQqA==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
@@ -1429,13 +1423,9 @@ packages:
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@isaacs/balanced-match@4.0.1': '@isaacs/cliui@9.0.0':
resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==}
engines: {node: 20 || >=22} engines: {node: '>=18'}
'@isaacs/brace-expansion@5.0.1':
resolution: {integrity: sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==}
engines: {node: 20 || >=22}
'@isaacs/fs-minipass@4.0.1': '@isaacs/fs-minipass@4.0.1':
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
@@ -1747,8 +1737,8 @@ packages:
peerDependencies: peerDependencies:
swup: ^4.0.0 swup: ^4.0.0
'@swup/astro@1.7.0': '@swup/astro@1.8.0':
resolution: {integrity: sha512-OxZskzeYkEZ4+McbwJKyD018sHNlrKQLdtHzlmNjivrzQY/dB7ncnWqDs3d4IkA7Y+ydNjpwBqpR/asjDhTiag==} resolution: {integrity: sha512-FI8/jNXgyUoWpnOgdB3MOUobRtRzedKBE8+qbBH5c8CE4q9Ni/GHPGrXK/0xG+g1ZiIQuTF3OXRvTLNVouiBig==}
'@swup/body-class-plugin@3.3.0': '@swup/body-class-plugin@3.3.0':
resolution: {integrity: sha512-4h/6mAgDd0+ml8Gc2kX8tikIR0HZZLF+WTnfm0JTVrGDw74bgEBenbsaWFfIS+gn4RWKZdWBUOfaZ+Im5J7Gvw==} resolution: {integrity: sha512-4h/6mAgDd0+ml8Gc2kX8tikIR0HZZLF+WTnfm0JTVrGDw74bgEBenbsaWFfIS+gn4RWKZdWBUOfaZ+Im5J7Gvw==}
@@ -2153,8 +2143,8 @@ packages:
ajv@6.12.6: ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
ajv@8.17.1: ajv@8.18.0:
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==}
ansi-align@3.0.1: ansi-align@3.0.1:
resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
@@ -2231,12 +2221,8 @@ packages:
resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
astro-compressor@1.2.0: astro-eslint-parser@1.3.0:
resolution: {integrity: sha512-Y0UWW/AjyLcHlRhCK9yUqy0kg4qZHd54kDIMY4C/1huS6pSVZNsMY7a3Jk7o5RjytIkVaLDDkcUzR8SgipBH8g==} resolution: {integrity: sha512-aOLc/aDR7lTWAHlytEefwn4Y6qs6uMr69DZvUx2A1AOAZsWhGB/paiRWPtVchh9wzMvLeqr+DkbENhVreVr9AQ==}
engines: {node: '>=22'}
astro-eslint-parser@1.2.2:
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-icon@1.1.5: astro-icon@1.1.5:
@@ -2316,6 +2302,10 @@ packages:
balanced-match@1.0.2: balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
balanced-match@4.0.2:
resolution: {integrity: sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg==}
engines: {node: 20 || >=22}
base-64@1.0.0: base-64@1.0.0:
resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==}
@@ -2339,6 +2329,10 @@ packages:
brace-expansion@2.0.2: brace-expansion@2.0.2:
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
brace-expansion@5.0.2:
resolution: {integrity: sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==}
engines: {node: 20 || >=22}
braces@3.0.3: braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -2392,8 +2386,8 @@ packages:
caniuse-api@3.0.0: caniuse-api@3.0.0:
resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==}
caniuse-lite@1.0.30001769: caniuse-lite@1.0.30001770:
resolution: {integrity: sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==} resolution: {integrity: sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==}
ccount@2.0.1: ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
@@ -2895,8 +2889,8 @@ packages:
eslint-parser-plain@0.1.1: eslint-parser-plain@0.1.1:
resolution: {integrity: sha512-KRgd6wuxH4U8kczqPp+Oyk4irThIhHWxgFgLDtpgjUGVIS3wGrJntvZW/p6hHq1T4FOwnOtCNkvAI4Kr+mQ/Hw==} resolution: {integrity: sha512-KRgd6wuxH4U8kczqPp+Oyk4irThIhHWxgFgLDtpgjUGVIS3wGrJntvZW/p6hHq1T4FOwnOtCNkvAI4Kr+mQ/Hw==}
eslint-plugin-astro@1.5.0: eslint-plugin-astro@1.6.0:
resolution: {integrity: sha512-IWy4kY3DKTJxd7g652zIWpBGFuxw7NIIt16kyqc8BlhnIKvI8yGJj+Maua0DiNYED3F/D8AmzoTTTA6A95WX9g==} resolution: {integrity: sha512-yGIbLHuj5MOUXa0s4sZ6cVhv6ehb+WLF80tsrGaxMk6VTUExruMzubQDzhOYt8fbR1c9vILCCRSCsKI7M1whig==}
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' eslint: '>=8.57.0'
@@ -2906,15 +2900,15 @@ packages:
peerDependencies: peerDependencies:
eslint: ^8.40.0 || ^9.0.0 eslint: ^8.40.0 || ^9.0.0
eslint-plugin-react-dom@2.12.4: eslint-plugin-react-dom@2.13.0:
resolution: {integrity: sha512-KxjyVB+zb8DR+knd8pTCLRFZh3Tdvkv96LYzruTbM4rKAOpHwsmJ+5/tXo5P8kgl7bMBYsHR0akbhUm+tb/MBw==} resolution: {integrity: sha512-+2IZzQ1WEFYOWatW+xvNUqmZn55YBCufzKA7hX3XQ/8eu85Mp4vnlOyNvdVHEOGhUnGuC6+9+zLK+IlEHKdKLQ==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0' typescript: '>=4.8.4 <6.0.0'
eslint-plugin-react-hooks-extra@2.12.4: eslint-plugin-react-hooks-extra@2.13.0:
resolution: {integrity: sha512-7KiMJYYYG4K6KHd+6VtlmA/1BdUSzqcMIR3ebL47+6znYgwNatTouKOSX6V+HeQPW9IIsFfmmTvwlzWh+hsQJA==} resolution: {integrity: sha512-qIbha1nzuyhXM9SbEfrcGVqmyvQu7GAOB2sy9Y4Qo5S8nCqw4fSBxq+8lSce5Tk5Y7XzIkgHOhNyXEvUHRWFMQ==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
@@ -2926,8 +2920,8 @@ packages:
peerDependencies: peerDependencies:
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0
eslint-plugin-react-naming-convention@2.12.4: eslint-plugin-react-naming-convention@2.13.0:
resolution: {integrity: sha512-VCPmN15AFFZ2fuy8OA40zumN5p12BVlEJvA/YLb6b0uiLHdCQsV6kuZwCNRRUWE6DJmJBe9k5fZW/Ww71ceQyg==} resolution: {integrity: sha512-uSd25JzSg2R4p81s3Wqck0AdwRlO9Yc+cZqTEXv7vW8exGGAM3mWnF6hgrgdqVJqBEGJIbS/Vx1r5BdKcY/MHA==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
@@ -2938,22 +2932,22 @@ packages:
peerDependencies: peerDependencies:
eslint: '>=9' eslint: '>=9'
eslint-plugin-react-rsc@2.12.4: eslint-plugin-react-rsc@2.13.0:
resolution: {integrity: sha512-Bp2Yy3Hk3OUcUtGPRt5z/UtODFUP2ExM1j47J4KJJghrTay/UH01vFC+T0tXQ4GXGxpXt/aJJX42DJpbz5sY6Q==} resolution: {integrity: sha512-RaftgITDLQm1zIgYyvR51sBdy4FlVaXFts5VISBaKbSUB0oqXyzOPxMHasfr9BCSjPLKus9zYe+G/Hr6rjFLXQ==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0' typescript: '>=4.8.4 <6.0.0'
eslint-plugin-react-web-api@2.12.4: eslint-plugin-react-web-api@2.13.0:
resolution: {integrity: sha512-upmeq9d5D4wDgm+exkBAOAtWfdi5vRZGxYQhtmhXabmV2nEq/UFKT8vMkcnWNBJCpTM+bcb0YtJK6WVlM2WcdA==} resolution: {integrity: sha512-nmJbzIAte7PeAkp22CwcKEASkKi49MshSdiDGO1XuN3f4N4/8sBfDcWbQuLPde6JiuzDT/0+l7Gi8wwTHtR1kg==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: '>=4.8.4 <6.0.0' typescript: '>=4.8.4 <6.0.0'
eslint-plugin-react-x@2.12.4: eslint-plugin-react-x@2.13.0:
resolution: {integrity: sha512-DzVSPAOCHMawhkn30GUYCL2y89NgZk2umFTJ9RFpfoN1AejdT1T7wXysJiqzfezSpbxW0uzKSoWbjnbsTE0YPQ==} resolution: {integrity: sha512-cMNX0+ws/fWTgVxn52qAQbaFF2rqvaDAtjrPUzY6XOzPjY0rJQdR2tSlWJttz43r2yBfqu+LGvHlGpWL2wfpTQ==}
engines: {node: '>=20.19.0'} engines: {node: '>=20.19.0'}
peerDependencies: peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
@@ -3071,8 +3065,8 @@ packages:
fast-uri@3.1.0: fast-uri@3.1.0:
resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==}
fast-xml-parser@5.3.5: fast-xml-parser@5.3.6:
resolution: {integrity: sha512-JeaA2Vm9ffQKp9VjvfzObuMCjUYAp5WDYhRYL5LrBPY/jUDlUtOvDfot0vKSkB9tuX885BDHjtw4fZadD95wnA==} resolution: {integrity: sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==}
hasBin: true hasBin: true
fastq@1.20.1: fastq@1.20.1:
@@ -3149,20 +3143,6 @@ packages:
fraction.js@5.3.4: fraction.js@5.3.4:
resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==}
framer-motion@12.34.0:
resolution: {integrity: sha512-+/H49owhzkzQyxtn7nZeF4kdH++I2FWrESQ184Zbcw5cEqNHYkE5yxWxcTLSj5lNx3NWdbIRy5FHqUvetD8FWg==}
peerDependencies:
'@emotion/is-prop-valid': '*'
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
peerDependenciesMeta:
'@emotion/is-prop-valid':
optional: true
react:
optional: true
react-dom:
optional: true
fresh@2.0.0: fresh@2.0.0:
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
@@ -3569,8 +3549,8 @@ packages:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
engines: {node: '>=8'} engines: {node: '>=8'}
is-wsl@3.1.0: is-wsl@3.1.1:
resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==}
engines: {node: '>=16'} engines: {node: '>=16'}
isarray@2.0.5: isarray@2.0.5:
@@ -3583,6 +3563,10 @@ packages:
resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
jackspeak@4.2.3:
resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==}
engines: {node: 20 || >=22}
jake@10.9.4: jake@10.9.4:
resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -4003,8 +3987,8 @@ packages:
resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
hasBin: true hasBin: true
minimatch@10.1.2: minimatch@10.2.0:
resolution: {integrity: sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==} resolution: {integrity: sha512-ugkC31VaVg9cF0DFVoADH12k6061zNZkZON+aX8AWsR9GhPcErkcMBceb6znR8wLERM2AkkOxy2nWRLpT9Jq5w==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
minimatch@3.1.2: minimatch@3.1.2:
@@ -4032,26 +4016,6 @@ packages:
morphdom@2.7.8: morphdom@2.7.8:
resolution: {integrity: sha512-D/fR4xgGUyVRbdMGU6Nejea1RFzYxYtyurG4Fbv2Fi/daKlWKuXGLOdXtl+3eIwL110cI2hz1ZojGICjjFLgTg==} resolution: {integrity: sha512-D/fR4xgGUyVRbdMGU6Nejea1RFzYxYtyurG4Fbv2Fi/daKlWKuXGLOdXtl+3eIwL110cI2hz1ZojGICjjFLgTg==}
motion-dom@12.34.0:
resolution: {integrity: sha512-Lql3NuEcScRDxTAO6GgUsRHBZOWI/3fnMlkMcH5NftzcN37zJta+bpbMAV9px4Nj057TuvRooMK7QrzMCgtz6Q==}
motion-utils@12.29.2:
resolution: {integrity: sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==}
motion@12.34.0:
resolution: {integrity: sha512-01Sfa/zgsD/di8zA/uFW5Eb7/SPXoGyUfy+uMRMW5Spa8j0z/UbfQewAYvPMYFCXRlyD6e5aLHh76TxeeJD+RA==}
peerDependencies:
'@emotion/is-prop-valid': '*'
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
peerDependenciesMeta:
'@emotion/is-prop-valid':
optional: true
react:
optional: true
react-dom:
optional: true
mri@1.2.0: mri@1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
engines: {node: '>=4'} engines: {node: '>=4'}
@@ -5137,8 +5101,8 @@ packages:
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
engines: {node: '>=6'} engines: {node: '>=6'}
tar@7.5.7: tar@7.5.9:
resolution: {integrity: sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==} resolution: {integrity: sha512-BTLcK0xsDh2+PUe9F6c2TlRp4zOOBMTkoQHQIWSIzI0R7KG46uEwq4OPk2W7bZcprBMsuaeFsqwYr7pjh6CuHg==}
engines: {node: '>=18'} engines: {node: '>=18'}
terser@5.44.1: terser@5.44.1:
@@ -5278,8 +5242,8 @@ packages:
undici-types@7.16.0: undici-types@7.16.0:
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
undici@7.21.0: undici@7.22.0:
resolution: {integrity: sha512-Hn2tCQpoDt1wv23a68Ctc8Cr/BHpUSfaPYrkajTXOS9IKpxVRx/X5m1K2YkbK2ipgZgxXSgsUinl3x+2YdSSfg==} resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==}
engines: {node: '>=20.18.1'} engines: {node: '>=20.18.1'}
unicode-canonical-property-names-ecmascript@2.0.1: unicode-canonical-property-names-ecmascript@2.0.1:
@@ -5301,8 +5265,8 @@ packages:
unified@11.0.5: unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
unifont@0.7.3: unifont@0.7.4:
resolution: {integrity: sha512-b0GtQzKCyuSHGsfj5vyN8st7muZ6VCI4XD4vFlr7Uy1rlWVYxC3npnfk8MyreHxJYrz1ooLDqDzFe9XqQTlAhA==} resolution: {integrity: sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==}
unist-util-find-after@5.0.0: unist-util-find-after@5.0.0:
resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==}
@@ -5823,7 +5787,7 @@ snapshots:
'@astrojs/rss@4.0.15': '@astrojs/rss@4.0.15':
dependencies: dependencies:
fast-xml-parser: 5.3.5 fast-xml-parser: 5.3.6
piccolore: 0.1.3 piccolore: 0.1.3
'@astrojs/sitemap@3.7.0': '@astrojs/sitemap@3.7.0':
@@ -5839,7 +5803,7 @@ snapshots:
dlv: 1.1.3 dlv: 1.1.3
dset: 3.1.4 dset: 3.1.4
is-docker: 3.0.0 is-docker: 3.0.0
is-wsl: 3.1.0 is-wsl: 3.1.1
which-pm-runs: 1.1.0 which-pm-runs: 1.1.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -6794,9 +6758,9 @@ snapshots:
'@eslint-community/regexpp@4.12.2': {} '@eslint-community/regexpp@4.12.2': {}
'@eslint-react/ast@2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)': '@eslint-react/ast@2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)':
dependencies: dependencies:
'@eslint-react/eff': 2.12.4 '@eslint-react/eff': 2.13.0
'@typescript-eslint/types': 8.55.0 '@typescript-eslint/types': 8.55.0
'@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3) '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
@@ -6806,12 +6770,12 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@eslint-react/core@2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)': '@eslint-react/core@2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)':
dependencies: dependencies:
'@eslint-react/ast': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/ast': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/eff': 2.12.4 '@eslint-react/eff': 2.13.0
'@eslint-react/shared': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/shared': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/var': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/var': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.55.0 '@typescript-eslint/scope-manager': 8.55.0
'@typescript-eslint/types': 8.55.0 '@typescript-eslint/types': 8.55.0
'@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
@@ -6821,31 +6785,31 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@eslint-react/eff@2.12.4': {} '@eslint-react/eff@2.13.0': {}
'@eslint-react/eslint-plugin@2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)': '@eslint-react/eslint-plugin@2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)':
dependencies: dependencies:
'@eslint-react/eff': 2.12.4 '@eslint-react/eff': 2.13.0
'@eslint-react/shared': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/shared': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.55.0 '@typescript-eslint/scope-manager': 8.55.0
'@typescript-eslint/type-utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/type-utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/types': 8.55.0 '@typescript-eslint/types': 8.55.0
'@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
eslint: 10.0.0(jiti@2.6.1) eslint: 10.0.0(jiti@2.6.1)
eslint-plugin-react-dom: 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) eslint-plugin-react-dom: 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
eslint-plugin-react-hooks-extra: 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) eslint-plugin-react-hooks-extra: 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
eslint-plugin-react-naming-convention: 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) eslint-plugin-react-naming-convention: 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
eslint-plugin-react-rsc: 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) eslint-plugin-react-rsc: 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
eslint-plugin-react-web-api: 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) eslint-plugin-react-web-api: 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
eslint-plugin-react-x: 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) eslint-plugin-react-x: 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
ts-api-utils: 2.4.0(typescript@5.9.3) ts-api-utils: 2.4.0(typescript@5.9.3)
typescript: 5.9.3 typescript: 5.9.3
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@eslint-react/shared@2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)': '@eslint-react/shared@2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)':
dependencies: dependencies:
'@eslint-react/eff': 2.12.4 '@eslint-react/eff': 2.13.0
'@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
eslint: 10.0.0(jiti@2.6.1) eslint: 10.0.0(jiti@2.6.1)
ts-pattern: 5.9.0 ts-pattern: 5.9.0
@@ -6854,11 +6818,11 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@eslint-react/var@2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)': '@eslint-react/var@2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)':
dependencies: dependencies:
'@eslint-react/ast': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/ast': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/eff': 2.12.4 '@eslint-react/eff': 2.13.0
'@eslint-react/shared': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/shared': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.55.0 '@typescript-eslint/scope-manager': 8.55.0
'@typescript-eslint/types': 8.55.0 '@typescript-eslint/types': 8.55.0
'@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
@@ -6872,7 +6836,7 @@ snapshots:
dependencies: dependencies:
'@eslint/object-schema': 3.0.1 '@eslint/object-schema': 3.0.1
debug: 4.4.3 debug: 4.4.3
minimatch: 10.1.2 minimatch: 10.2.0
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -6941,7 +6905,7 @@ snapshots:
local-pkg: 1.1.2 local-pkg: 1.1.2
pathe: 2.0.3 pathe: 2.0.3
svgo: 3.3.2 svgo: 3.3.2
tar: 7.5.7 tar: 7.5.9
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -7056,11 +7020,7 @@ snapshots:
'@img/sharp-win32-x64@0.34.5': '@img/sharp-win32-x64@0.34.5':
optional: true optional: true
'@isaacs/balanced-match@4.0.1': {} '@isaacs/cliui@9.0.0': {}
'@isaacs/brace-expansion@5.0.1':
dependencies:
'@isaacs/balanced-match': 4.0.1
'@isaacs/fs-minipass@4.0.1': '@isaacs/fs-minipass@4.0.1':
dependencies: dependencies:
@@ -7377,7 +7337,7 @@ snapshots:
focus-options-polyfill: 1.6.0 focus-options-polyfill: 1.6.0
swup: 4.8.2 swup: 4.8.2
'@swup/astro@1.7.0(@types/babel__core@7.20.5)': '@swup/astro@1.8.0(@types/babel__core@7.20.5)':
dependencies: dependencies:
'@swup/a11y-plugin': 5.0.0(swup@4.8.2) '@swup/a11y-plugin': 5.0.0(swup@4.8.2)
'@swup/body-class-plugin': 3.3.0(swup@4.8.2) '@swup/body-class-plugin': 3.3.0(swup@4.8.2)
@@ -7857,9 +7817,9 @@ snapshots:
acorn@8.15.0: {} acorn@8.15.0: {}
ajv-draft-04@1.0.0(ajv@8.17.1): ajv-draft-04@1.0.0(ajv@8.18.0):
optionalDependencies: optionalDependencies:
ajv: 8.17.1 ajv: 8.18.0
ajv@6.12.6: ajv@6.12.6:
dependencies: dependencies:
@@ -7868,7 +7828,7 @@ snapshots:
json-schema-traverse: 0.4.1 json-schema-traverse: 0.4.1
uri-js: 4.4.1 uri-js: 4.4.1
ajv@8.17.1: ajv@8.18.0:
dependencies: dependencies:
fast-deep-equal: 3.1.3 fast-deep-equal: 3.1.3
fast-uri: 3.1.0 fast-uri: 3.1.0
@@ -7972,9 +7932,7 @@ snapshots:
get-intrinsic: 1.3.0 get-intrinsic: 1.3.0
is-array-buffer: 3.0.5 is-array-buffer: 3.0.5
astro-compressor@1.2.0: {} astro-eslint-parser@1.3.0:
astro-eslint-parser@1.2.2:
dependencies: dependencies:
'@astrojs/compiler': 2.13.1 '@astrojs/compiler': 2.13.1
'@typescript-eslint/scope-manager': 8.55.0 '@typescript-eslint/scope-manager': 8.55.0
@@ -8052,7 +8010,7 @@ snapshots:
tinyglobby: 0.2.15 tinyglobby: 0.2.15
tsconfck: 3.1.6(typescript@5.9.3) tsconfck: 3.1.6(typescript@5.9.3)
ultrahtml: 1.6.0 ultrahtml: 1.6.0
unifont: 0.7.3 unifont: 0.7.4
unist-util-visit: 5.1.0 unist-util-visit: 5.1.0
unstorage: 1.17.4 unstorage: 1.17.4
vfile: 6.0.3 vfile: 6.0.3
@@ -8154,7 +8112,7 @@ snapshots:
tinyglobby: 0.2.15 tinyglobby: 0.2.15
tsconfck: 3.1.6(typescript@5.9.3) tsconfck: 3.1.6(typescript@5.9.3)
ultrahtml: 1.6.0 ultrahtml: 1.6.0
unifont: 0.7.3 unifont: 0.7.4
unist-util-visit: 5.1.0 unist-util-visit: 5.1.0
unstorage: 1.17.4 unstorage: 1.17.4
vfile: 6.0.3 vfile: 6.0.3
@@ -8217,7 +8175,7 @@ snapshots:
autoprefixer@10.4.24(postcss@8.5.6): autoprefixer@10.4.24(postcss@8.5.6):
dependencies: dependencies:
browserslist: 4.28.1 browserslist: 4.28.1
caniuse-lite: 1.0.30001769 caniuse-lite: 1.0.30001770
fraction.js: 5.3.4 fraction.js: 5.3.4
picocolors: 1.1.1 picocolors: 1.1.1
postcss: 8.5.6 postcss: 8.5.6
@@ -8270,6 +8228,10 @@ snapshots:
balanced-match@1.0.2: {} balanced-match@1.0.2: {}
balanced-match@4.0.2:
dependencies:
jackspeak: 4.2.3
base-64@1.0.0: {} base-64@1.0.0: {}
baseline-browser-mapping@2.9.19: {} baseline-browser-mapping@2.9.19: {}
@@ -8298,6 +8260,10 @@ snapshots:
dependencies: dependencies:
balanced-match: 1.0.2 balanced-match: 1.0.2
brace-expansion@5.0.2:
dependencies:
balanced-match: 4.0.2
braces@3.0.3: braces@3.0.3:
dependencies: dependencies:
fill-range: 7.1.1 fill-range: 7.1.1
@@ -8309,7 +8275,7 @@ snapshots:
browserslist@4.28.1: browserslist@4.28.1:
dependencies: dependencies:
baseline-browser-mapping: 2.9.19 baseline-browser-mapping: 2.9.19
caniuse-lite: 1.0.30001769 caniuse-lite: 1.0.30001770
electron-to-chromium: 1.5.286 electron-to-chromium: 1.5.286
node-releases: 2.0.27 node-releases: 2.0.27
update-browserslist-db: 1.2.3(browserslist@4.28.1) update-browserslist-db: 1.2.3(browserslist@4.28.1)
@@ -8351,11 +8317,11 @@ snapshots:
caniuse-api@3.0.0: caniuse-api@3.0.0:
dependencies: dependencies:
browserslist: 4.28.1 browserslist: 4.28.1
caniuse-lite: 1.0.30001769 caniuse-lite: 1.0.30001770
lodash.memoize: 4.1.2 lodash.memoize: 4.1.2
lodash.uniq: 4.5.0 lodash.uniq: 4.5.0
caniuse-lite@1.0.30001769: {} caniuse-lite@1.0.30001770: {}
ccount@2.0.1: {} ccount@2.0.1: {}
@@ -8400,7 +8366,7 @@ snapshots:
parse5: 7.3.0 parse5: 7.3.0
parse5-htmlparser2-tree-adapter: 7.1.0 parse5-htmlparser2-tree-adapter: 7.1.0
parse5-parser-stream: 7.1.2 parse5-parser-stream: 7.1.2
undici: 7.21.0 undici: 7.22.0
whatwg-mimetype: 4.0.0 whatwg-mimetype: 4.0.0
chokidar@4.0.3: chokidar@4.0.3:
@@ -8983,12 +8949,12 @@ snapshots:
eslint-parser-plain@0.1.1: {} eslint-parser-plain@0.1.1: {}
eslint-plugin-astro@1.5.0(eslint@10.0.0(jiti@2.6.1)): eslint-plugin-astro@1.6.0(eslint@10.0.0(jiti@2.6.1)):
dependencies: dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.0.0(jiti@2.6.1)) '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.0(jiti@2.6.1))
'@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/sourcemap-codec': 1.5.5
'@typescript-eslint/types': 8.55.0 '@typescript-eslint/types': 8.55.0
astro-eslint-parser: 1.2.2 astro-eslint-parser: 1.3.0
eslint: 10.0.0(jiti@2.6.1) eslint: 10.0.0(jiti@2.6.1)
eslint-compat-utils: 0.6.5(eslint@10.0.0(jiti@2.6.1)) eslint-compat-utils: 0.6.5(eslint@10.0.0(jiti@2.6.1))
globals: 16.5.0 globals: 16.5.0
@@ -9009,13 +8975,13 @@ snapshots:
prettier: 3.8.1 prettier: 3.8.1
synckit: 0.11.12 synckit: 0.11.12
eslint-plugin-react-dom@2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3): eslint-plugin-react-dom@2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3):
dependencies: dependencies:
'@eslint-react/ast': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/ast': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/core': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/core': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/eff': 2.12.4 '@eslint-react/eff': 2.13.0
'@eslint-react/shared': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/shared': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/var': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/var': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.55.0 '@typescript-eslint/scope-manager': 8.55.0
'@typescript-eslint/types': 8.55.0 '@typescript-eslint/types': 8.55.0
'@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
@@ -9026,13 +8992,13 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-plugin-react-hooks-extra@2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3): eslint-plugin-react-hooks-extra@2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3):
dependencies: dependencies:
'@eslint-react/ast': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/ast': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/core': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/core': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/eff': 2.12.4 '@eslint-react/eff': 2.13.0
'@eslint-react/shared': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/shared': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/var': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/var': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.55.0 '@typescript-eslint/scope-manager': 8.55.0
'@typescript-eslint/type-utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/type-utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/types': 8.55.0 '@typescript-eslint/types': 8.55.0
@@ -9054,13 +9020,13 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-plugin-react-naming-convention@2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3): eslint-plugin-react-naming-convention@2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3):
dependencies: dependencies:
'@eslint-react/ast': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/ast': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/core': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/core': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/eff': 2.12.4 '@eslint-react/eff': 2.13.0
'@eslint-react/shared': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/shared': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/var': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/var': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.55.0 '@typescript-eslint/scope-manager': 8.55.0
'@typescript-eslint/type-utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/type-utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/types': 8.55.0 '@typescript-eslint/types': 8.55.0
@@ -9077,11 +9043,11 @@ snapshots:
dependencies: dependencies:
eslint: 10.0.0(jiti@2.6.1) eslint: 10.0.0(jiti@2.6.1)
eslint-plugin-react-rsc@2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3): eslint-plugin-react-rsc@2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3):
dependencies: dependencies:
'@eslint-react/ast': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/ast': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/shared': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/shared': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/var': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/var': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/types': 8.55.0 '@typescript-eslint/types': 8.55.0
'@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
eslint: 10.0.0(jiti@2.6.1) eslint: 10.0.0(jiti@2.6.1)
@@ -9090,13 +9056,13 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-plugin-react-web-api@2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3): eslint-plugin-react-web-api@2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3):
dependencies: dependencies:
'@eslint-react/ast': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/ast': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/core': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/core': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/eff': 2.12.4 '@eslint-react/eff': 2.13.0
'@eslint-react/shared': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/shared': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/var': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/var': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.55.0 '@typescript-eslint/scope-manager': 8.55.0
'@typescript-eslint/types': 8.55.0 '@typescript-eslint/types': 8.55.0
'@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
@@ -9107,13 +9073,13 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-plugin-react-x@2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3): eslint-plugin-react-x@2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3):
dependencies: dependencies:
'@eslint-react/ast': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/ast': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/core': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/core': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/eff': 2.12.4 '@eslint-react/eff': 2.13.0
'@eslint-react/shared': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/shared': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@eslint-react/var': 2.12.4(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@eslint-react/var': 2.13.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.55.0 '@typescript-eslint/scope-manager': 8.55.0
'@typescript-eslint/type-utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/type-utils': 8.55.0(eslint@10.0.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/types': 8.55.0 '@typescript-eslint/types': 8.55.0
@@ -9196,7 +9162,7 @@ snapshots:
imurmurhash: 0.1.4 imurmurhash: 0.1.4
is-glob: 4.0.3 is-glob: 4.0.3
json-stable-stringify-without-jsonify: 1.0.1 json-stable-stringify-without-jsonify: 1.0.1
minimatch: 10.1.2 minimatch: 10.2.0
natural-compare: 1.4.0 natural-compare: 1.4.0
optionator: 0.9.4 optionator: 0.9.4
optionalDependencies: optionalDependencies:
@@ -9276,7 +9242,7 @@ snapshots:
fast-uri@3.1.0: {} fast-uri@3.1.0: {}
fast-xml-parser@5.3.5: fast-xml-parser@5.3.6:
dependencies: dependencies:
strnum: 2.1.2 strnum: 2.1.2
@@ -9352,15 +9318,6 @@ snapshots:
fraction.js@5.3.4: {} fraction.js@5.3.4: {}
framer-motion@12.34.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
dependencies:
motion-dom: 12.34.0
motion-utils: 12.29.2
tslib: 2.8.1
optionalDependencies:
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
fresh@2.0.0: {} fresh@2.0.0: {}
fs-extra@10.1.0: fs-extra@10.1.0:
@@ -9829,7 +9786,7 @@ snapshots:
dependencies: dependencies:
is-docker: 2.2.1 is-docker: 2.2.1
is-wsl@3.1.0: is-wsl@3.1.1:
dependencies: dependencies:
is-inside-container: 1.0.0 is-inside-container: 1.0.0
@@ -9846,6 +9803,10 @@ snapshots:
has-symbols: 1.1.0 has-symbols: 1.1.0
set-function-name: 2.0.2 set-function-name: 2.0.2
jackspeak@4.2.3:
dependencies:
'@isaacs/cliui': 9.0.0
jake@10.9.4: jake@10.9.4:
dependencies: dependencies:
async: 3.2.6 async: 3.2.6
@@ -10451,9 +10412,9 @@ snapshots:
mini-svg-data-uri@1.4.4: {} mini-svg-data-uri@1.4.4: {}
minimatch@10.1.2: minimatch@10.2.0:
dependencies: dependencies:
'@isaacs/brace-expansion': 5.0.1 brace-expansion: 5.0.2
minimatch@3.1.2: minimatch@3.1.2:
dependencies: dependencies:
@@ -10482,20 +10443,6 @@ snapshots:
morphdom@2.7.8: {} morphdom@2.7.8: {}
motion-dom@12.34.0:
dependencies:
motion-utils: 12.29.2
motion-utils@12.29.2: {}
motion@12.34.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4):
dependencies:
framer-motion: 12.34.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
tslib: 2.8.1
optionalDependencies:
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
mri@1.2.0: {} mri@1.2.0: {}
mrmime@2.0.1: {} mrmime@2.0.1: {}
@@ -11694,7 +11641,7 @@ snapshots:
tapable@2.3.0: {} tapable@2.3.0: {}
tar@7.5.7: tar@7.5.9:
dependencies: dependencies:
'@isaacs/fs-minipass': 4.0.1 '@isaacs/fs-minipass': 4.0.1
chownr: 3.0.0 chownr: 3.0.0
@@ -11838,7 +11785,7 @@ snapshots:
undici-types@7.16.0: {} undici-types@7.16.0: {}
undici@7.21.0: {} undici@7.22.0: {}
unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-canonical-property-names-ecmascript@2.0.1: {}
@@ -11861,7 +11808,7 @@ snapshots:
trough: 2.2.0 trough: 2.2.0
vfile: 6.0.3 vfile: 6.0.3
unifont@0.7.3: unifont@0.7.4:
dependencies: dependencies:
css-tree: 3.1.0 css-tree: 3.1.0
ofetch: 1.5.1 ofetch: 1.5.1
@@ -12154,8 +12101,8 @@ snapshots:
yaml-language-server@1.19.2: yaml-language-server@1.19.2:
dependencies: dependencies:
'@vscode/l10n': 0.0.18 '@vscode/l10n': 0.0.18
ajv: 8.17.1 ajv: 8.18.0
ajv-draft-04: 1.0.0(ajv@8.17.1) ajv-draft-04: 1.0.0(ajv@8.18.0)
lodash: 4.17.21 lodash: 4.17.21
prettier: 3.8.1 prettier: 3.8.1
request-light: 0.5.8 request-light: 0.5.8

View File

@@ -1,6 +1,6 @@
--- ---
import BrandLogo from '@components/ui/logos/BrandLogo.astro'; import BrandLogo from '@components/ui/logos/BrandLogo.astro';
import ThemeToggle from '@components/ui/buttons/ThemeToggle.astro'; import ThemeToggleButton from '@components/buttons/ThemeToggleButton.astro';
import { NavigationLinks } from '@/config'; import { NavigationLinks } from '@/config';
const pathname = new URL(Astro.request.url).pathname; const pathname = new URL(Astro.request.url).pathname;
@@ -67,7 +67,7 @@ const currentPath = pathname.slice(1);
<div <div
id="navbar-collapse-with-animation" id="navbar-collapse-with-animation"
class="hs-collapse hidden grow basis-full overflow-hidden transition-all duration-300 md:block" class="hs-collapse hidden grow basis-full overflow-hidden transition-all duration-300 md:block md:overflow-visible"
> >
<div <div
class="mt-5 flex flex-col gap-x-0 gap-y-4 md:mt-0 md:flex-row md:items-center md:justify-end md:gap-x-4 md:gap-y-0 md:ps-7 lg:gap-x-7" class="mt-5 flex flex-col gap-x-0 gap-y-4 md:mt-0 md:flex-row md:items-center md:justify-end md:gap-x-4 md:gap-y-0 md:ps-7 lg:gap-x-7"
@@ -90,7 +90,7 @@ const currentPath = pathname.slice(1);
}) })
} }
<span class="md:inline-block"> <span class="md:inline-block">
<ThemeToggle /> <ThemeToggleButton />
</span> </span>
</div> </div>
</div> </div>

View File

@@ -1,5 +1,5 @@
--- ---
import PrimaryCTA from '@components/ui/buttons/PrimaryCTA.astro'; import GoLinkPrimaryButton from '@components/buttons/GoLinkPrimaryButton.astro';
import Image from '@components/ui/images/Image.astro'; import Image from '@components/ui/images/Image.astro';
interface Props { interface Props {
@@ -39,6 +39,6 @@ const { title, subTitle, btnExists, btnTitle, btnURL, img, imgAlt } = Astro.prop
> >
{subTitle} {subTitle}
</p> </p>
{btnExists ? <PrimaryCTA title={btnTitle} url={btnURL} /> : null} {btnExists ? <GoLinkPrimaryButton title={btnTitle} url={btnURL} /> : null}
</div> </div>
</section> </section>

View File

@@ -1,5 +1,5 @@
--- ---
import PrimaryCTA from '@components/ui/buttons/PrimaryCTA.astro'; import GoLinkPrimaryButton from '@components/buttons/GoLinkPrimaryButton.astro';
import Image from '@components/ui/images/Image.astro'; import Image from '@components/ui/images/Image.astro';
interface Props { interface Props {
@@ -43,7 +43,7 @@ const {
> >
{subTitle} {subTitle}
</p> </p>
{btnExists ? <PrimaryCTA title={btnTitle} url={btnURL} /> : null} {btnExists ? <GoLinkPrimaryButton title={btnTitle} url={btnURL} /> : null}
</div> </div>
{ {

View File

@@ -4,7 +4,7 @@ import Icon from '@components/ui/icons/icon.astro';
<button <button
type="button" type="button"
class="focus-visible:ring-secondary group inline-flex items-center rounded-lg p-2.5 text-neutral-600 ring-neutral-500 transition duration-300 outline-none hover:bg-neutral-100 focus:outline-none focus-visible:ring-1 focus-visible:outline-none dark:text-neutral-400 dark:ring-neutral-200 dark:hover:bg-neutral-700" class="button-base button-bg-blue group inline-flex items-center rounded-lg p-2.5"
data-bookmark-button="bookmark-button" data-bookmark-button="bookmark-button"
> >
<Icon name="bookmark" /> <Icon name="bookmark" />

View File

@@ -0,0 +1,31 @@
---
import { Icon } from 'astro-icon/components';
interface Props {
title?: string;
url?: string;
}
const { title, url } = Astro.props;
---
<a
class="button-base button-bg-gitea group inline-flex rounded-full gap-x-2"
href={url}
target="_blank"
rel="noopener noreferrer"
>
<div class="button-text-title flex relative items-center text-center">
<Icon
name="pajamas:gitea"
class="h-4 w-4 md:h-6 md:w-6"
/>
<span class="ml-2">
Continue to Gitea
</span>
<Icon
name="mdi:keyboard-arrow-right"
class="button-hover-arrow"
/>
</div>
</a>

View File

@@ -0,0 +1,28 @@
---
import Icon from '@components/ui/icons/icon.astro';
interface Props {
noArrow?: boolean;
}
const { noArrow } = Astro.props;
---
<button
class="button-base button-bg-blue group inline-flex rounded-lg gap-x-2"
id="back-button"
data-astro-prefetch
>
<div class="button-text-title flex relative items-center text-center">
{noArrow ? null : <Icon name="arrowLeft" />}
<span class="ml-2">
Go Back
</span>
</div>
</button>
<script>
document.getElementById('back-button')?.addEventListener('click', () => {
window.history.back();
});
</script>

View File

@@ -0,0 +1,25 @@
---
import { Icon } from 'astro-icon/components';
interface Props {
url?: string;
}
const { url } = Astro.props;
---
<a
class="button-base button-bg-teal group inline-flex rounded-lg gap-x-2"
href={url}
data-astro-prefetch
>
<div class="button-text-title flex relative items-center text-center">
<Icon
name="mdi:home-variant-outline"
class="card-hover-icon-scale h-3 w-3 md:h-5 md:w-5"
/>
<span class="ml-2">
Return Home
</span>
</div>
</a>

View File

@@ -0,0 +1,29 @@
---
import { Icon } from 'astro-icon/components';
interface Props {
title?: string;
url?: string;
noArrow?: boolean;
}
const { title, url, noArrow } = Astro.props;
---
<a
class="button-base button-bg-teal group inline-flex rounded-lg gap-x-2"
href={url}
data-astro-prefetch
>
<div class="button-text-title flex relative items-center text-center">
<span class="mr-2">
{title}
</span>
{noArrow ? null : (
<Icon
name="mdi:keyboard-arrow-right"
class="button-hover-arrow"
/>
)}
</div>
</a>

View File

@@ -0,0 +1,20 @@
---
interface Props {
title?: string;
url?: string;
}
const { title, url } = Astro.props;
---
<a
class="button-base button-bg-neutral group inline-flex rounded-lg gap-x-2"
href={url}
data-astro-prefetch
>
<div class="button-text-title flex relative items-center text-center">
<span>
{title}
</span>
</div>
</a>

View File

@@ -0,0 +1,51 @@
---
import Icon from '@components/ui/icons/icon.astro';
type SocialPlatform = {
name: string;
url: string;
svg: string;
};
interface Props {
pageTitle: string;
}
const { pageTitle } = Astro.props;
const socialPlatforms: SocialPlatform[] = [
{
name: 'Facebook',
url: `https://www.facebook.com/sharer/sharer.php?u=${Astro.url}`,
svg: 'facebook',
},
{
name: 'X',
url: `https://x.com/intent/tweet?url=${Astro.url}&text=${pageTitle}`,
svg: 'x',
},
{
name: 'LinkedIn',
url: `https://www.linkedin.com/sharing/share-offsite/?url=${Astro.url}`,
svg: 'linkedIn',
},
];
---
<div class="inline-flex items-center gap-x-2">
{
socialPlatforms.map((platform) => (
<a
class="button-base-hidden group inline-flex rounded-lg gap-x-2"
href={platform.url}
target="_blank"
rel="noopener noreferrer"
title={`Share on ${platform.name}`}
>
<div class="button-text-title-hidden flex relative items-center text-center">
<Icon name={platform.svg} class="h-5 w-5" />
</div>
</a>
))
}
</div>

View File

@@ -5,14 +5,14 @@
<button <button
id="theme-toggle" id="theme-toggle"
data-theme-toggle data-theme-toggle
class="group dark:hover:bg-steel/30 relative touch-manipulation overflow-hidden rounded-full p-1.5 transition-all duration-300 hover:bg-yellow-300/20 focus:outline-hidden sm:p-2" class="group dark:hover:bg-steel/30 hover:bg-yellow-300/20 transition-all duration-300 relative rounded-full p-1.5 sm:p-2 touch-manipulation"
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 flex h-5 w-5 items-center justify-center">
<!-- Sun icon --> <!-- Sun icon -->
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="icon-light absolute h-5 w-5 scale-100 rotate-0 text-neutral-600 transition-all duration-500 dark:scale-0 dark:-rotate-90 dark:text-neutral-400" class="icon-light absolute h-5 w-5 text-neutral-600 dark:text-neutral-400 scale-100 dark:scale-0 rotate-0 dark:-rotate-90 transition-all duration-500"
viewBox="0 0 24 24" viewBox="0 0 24 24"
fill="none" fill="none"
stroke="currentColor" stroke="currentColor"
@@ -29,7 +29,7 @@
<!-- Moon icon --> <!-- Moon icon -->
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="icon-dark absolute h-5 w-5 scale-0 rotate-90 text-neutral-600 transition-all duration-500 dark:scale-100 dark:rotate-0 dark:text-neutral-400" class="icon-dark absolute h-5 w-5 text-neutral-600 dark:text-neutral-400 scale-0 dark:scale-100 rotate-90 dark:rotate-0 transition-all duration-500"
viewBox="0 0 24 24" viewBox="0 0 24 24"
fill="none" fill="none"
stroke="currentColor" stroke="currentColor"
@@ -43,25 +43,23 @@
</button> </button>
<script is:inline> <script is:inline>
// Use a function to persist theme when using SPA transitions const applyTheme = () => {
// https://docs.astro.build/en/guides/view-transitions/#script-re-execution const isDark =
function applyTheme() { localStorage.theme === 'dark' ||
localStorage.theme === 'dark' (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches);
? document.documentElement.classList.add('dark') document.documentElement.classList.toggle('dark', isDark);
: document.documentElement.classList.remove('dark'); };
}
document.addEventListener('astro:after-swap', applyTheme);
applyTheme(); applyTheme();
document.addEventListener('astro:after-swap', applyTheme);
</script> </script>
<script> <script>
// Use a function to handle theme toggle to ensure it can be called from anywhere
function setupThemeToggle() { function setupThemeToggle() {
const themeToggles = document.querySelectorAll('[data-theme-toggle]'); const themeToggles = document.querySelectorAll('[data-theme-toggle]');
// Create theme switch overlay element if it doesn't exist // Create theme switch overlay element
if (!document.querySelector('.theme-switch-overlay')) { if (!document.querySelector('.theme-switch-overlay')) {
const overlay = document.createElement('div'); const overlay = document.createElement('div');
overlay.className = 'theme-switch-overlay fixed inset-0 pointer-events-none z-50'; overlay.className = 'theme-switch-overlay fixed inset-0 pointer-events-none z-50';
@@ -70,9 +68,7 @@
document.body.appendChild(overlay); document.body.appendChild(overlay);
} }
// Toggle theme when any theme toggle button is clicked
themeToggles.forEach((toggle) => { themeToggles.forEach((toggle) => {
// Add event listeners for both click and touch events
['click', 'touchend'].forEach((eventType) => { ['click', 'touchend'].forEach((eventType) => {
toggle.addEventListener( toggle.addEventListener(
eventType, eventType,
@@ -92,14 +88,10 @@
y = e.clientY - rect.top; y = e.clientY - rect.top;
} }
// Set the position variables for the radial gradient
document.documentElement.style.setProperty('--x', `${x}px`); document.documentElement.style.setProperty('--x', `${x}px`);
document.documentElement.style.setProperty('--y', `${y}px`); document.documentElement.style.setProperty('--y', `${y}px`);
// Get the overlay element
const overlay = document.querySelector('.theme-switch-overlay'); const overlay = document.querySelector('.theme-switch-overlay');
// Determine the new theme
const isDark = document.documentElement.classList.contains('dark'); const isDark = document.documentElement.classList.contains('dark');
const newTheme = isDark ? 'light' : 'dark'; const newTheme = isDark ? 'light' : 'dark';
@@ -110,7 +102,6 @@
overlay.style.opacity = '1'; overlay.style.opacity = '1';
} }
// Add transition class
document.documentElement.classList.add('theme-switching'); document.documentElement.classList.add('theme-switching');
// Force a reflow to ensure all elements update // Force a reflow to ensure all elements update
@@ -124,10 +115,7 @@
document.documentElement.classList.add('dark'); document.documentElement.classList.add('dark');
} }
// Store the preference
localStorage.setItem('theme', newTheme); localStorage.setItem('theme', newTheme);
// Dispatch a custom event for other components to react to
document.dispatchEvent( document.dispatchEvent(
new CustomEvent('themeChanged', { new CustomEvent('themeChanged', {
detail: { isDark: newTheme === 'dark' }, detail: { isDark: newTheme === 'dark' },
@@ -137,13 +125,10 @@
// Force another reflow to ensure all elements update // Force another reflow to ensure all elements update
document.body.offsetHeight; document.body.offsetHeight;
// Hide overlay after theme has changed
setTimeout(() => { setTimeout(() => {
if (overlay) { if (overlay) {
overlay.style.opacity = '0'; overlay.style.opacity = '0';
} }
// Remove transition class after animation completes
document.documentElement.classList.remove('theme-switching'); document.documentElement.classList.remove('theme-switching');
}, 300); }, 300);
}, 50); }, 50);
@@ -151,25 +136,6 @@
{ passive: false } { passive: false }
); );
}); });
// Add touch feedback
toggle.addEventListener(
'touchstart',
() => {
toggle.classList.add('active-touch');
},
{ passive: true }
);
toggle.addEventListener(
'touchend',
() => {
setTimeout(() => {
toggle.classList.remove('active-touch');
}, 150);
},
{ passive: true }
);
}); });
} }
@@ -201,61 +167,32 @@
</script> </script>
<style> <style>
/* Smooth transition for the entire page when theme changes */
:global(body) {
transition:
background-color 0.5s ease,
color 0.5s ease;
}
/* Theme transition overlay */
:global(.theme-switch-overlay) {
position: fixed;
inset: 0;
z-index: 9999;
pointer-events: none;
transition: opacity 0.3s ease-out;
}
/* Ensure theme transitions apply to all elements */
:global(.theme-switching *) {
transition-duration: 0.5s !important;
transition-property: background-color, border-color, color, fill, stroke !important;
}
/* Subtle hover animation */ /* Subtle hover animation */
#theme-toggle { #theme-toggle {
transform: translateY(0); transform: translateY(0);
box-shadow: 0 0 0 rgba(0, 0, 0, 0); box-shadow: 0 0 0 rgba(0, 0, 0, 0);
-webkit-tap-highlight-color: transparent; /* Remove default mobile tap highlight */ -webkit-tap-highlight-color: transparent;
min-height: 32px; /* Ensure minimum touch target size */ min-height: 32px;
min-width: 32px; /* Ensure minimum touch target size */ min-width: 32px;
} }
/* Only apply hover effects on non-touch devices */
@media (hover: hover) { @media (hover: hover) {
#theme-toggle:hover { #theme-toggle:hover {
transform: translateY(-2px); transform: translateY(-2px);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
} }
#theme-toggle:hover .icon-light:not(.dark .icon-light) { :global(:root:not(.dark)) #theme-toggle:hover .icon-light {
filter: drop-shadow-sm(0 0 2px rgba(251, 191, 36, 0.6)); filter: drop-shadow(0 0 2px rgba(251, 191, 36, 0.6));
transform: scale(1.1) rotate(15deg); transform: scale(1.1) rotate(15deg);
} }
#theme-toggle:hover .icon-dark:not(:not(.dark) .icon-dark) { :global(:root.dark) #theme-toggle:hover .icon-dark {
filter: drop-shadow-sm(0 0 2px rgba(129, 140, 248, 0.6)); filter: drop-shadow(0 0 2px rgba(129, 140, 248, 0.6));
transform: scale(1.1) rotate(-15deg); transform: scale(1.1) rotate(-15deg);
} }
} }
/* Touch feedback */
#theme-toggle.active-touch {
transform: scale(0.95);
transition: transform 0.15s ease-in-out;
}
/* Optimize animations for mobile */ /* Optimize animations for mobile */
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.icon-light, .icon-light,

View File

@@ -55,7 +55,7 @@ const visitClass = visitSource ? 'card-hover-text-gitea' : 'card-hover-text-titl
)} )}
<div class="ml-6 flex"> <div class="ml-6 flex">
<div class="relative inline-block"> <div class="relative inline-block">
<div class={`card-text-title ${visitClass} flex relative mx-auto min-h-11 items-center font-semibold text-md sm:mx-0 sm:mt-4`}> <div class={`card-text-title ${visitClass} flex relative items-center mx-auto min-h-11 font-semibold text-md sm:mx-0 sm:mt-4`}>
{visitSource && <Icon name="pajamas:gitea" />} {visitSource && <Icon name="pajamas:gitea" />}
<span class="relative inline-block overflow-hidden ml-2"> <span class="relative inline-block overflow-hidden ml-2">
{visitText} {visitText}

View File

@@ -4,7 +4,7 @@ import { readItems } from '@directus/sdk';
import type { Application } from '@lib/directusTypes'; import type { Application } from '@lib/directusTypes';
import directus from '@lib/directus'; import directus from '@lib/directus';
import HighlightsCard from '@components/ui/cards/HighlightsCard.astro'; import HighlightsCard from '@components/cards/HighlightsCard.astro';
const applications = ((await directus.request( const applications = ((await directus.request(
readItems('site_applications' as any, { readItems('site_applications' as any, {

View File

@@ -5,7 +5,7 @@ import type { Education, Certificate} from '@lib/directusTypes';
import directus from '@lib/directus'; import directus from '@lib/directus';
import { getDirectusImageURL } from '@lib/directusFunctions'; import { getDirectusImageURL } from '@lib/directusFunctions';
import EducationCard from '@components/ui/cards/EducationCard.astro'; import EducationCard from '@components/cards/EducationCard.astro';
const educations = ((await directus.request( const educations = ((await directus.request(
readItems('site_education' as any, { readItems('site_education' as any, {
@@ -27,7 +27,7 @@ const certificates = ((await directus.request(
Education Education
</h3> </h3>
<div class="mx-8"> <div class="mx-8">
<h4 class="smooth-reveal card-text-header-minor pt-5 "> <h4 class="smooth-reveal card-text-header-minor pt-5">
College College
</h4> </h4>
<div class="grid md:grid-cols-2 sm:grid-cols-1 gap-4 py-3"> <div class="grid md:grid-cols-2 sm:grid-cols-1 gap-4 py-3">

View File

@@ -0,0 +1,159 @@
---
import { Icon } from 'astro-icon/components';
import { readItems } from '@directus/sdk';
import type { Experience } from '@lib/directusTypes';
import directus from '@lib/directus';
const experiences = ((await directus.request(
readItems('site_experience'as any, {
fields: ['*'],
sort: ['-endDate'],
})
)) as unknown) as Experience[];
---
<section class:list={['flex flex-col gap-8', Astro.props.className]}>
<h3 class="smooth-reveal card-text-header flex relative items-center w-full gap-3 pb-10">
Experience
</h3>
<ul class="ml-8 w-full flex flex-col">
{experiences.map((experience: Experience) => {
const startYear = new Date(experience.startDate).getFullYear();
const endYear = experience.endDate != null ? new Date(experience.endDate).getFullYear() : 'Present';
return (
<li class="relative">
<div class="smooth-reveal group relative grid sm:grid-cols-18 sm:gap-8 md:gap-6 pb-16">
<header class="relative sm:col-span-3 text-header font-semibold text-lg mt-1">
<time datetime={experience.startDate} data-title={experience.startDate}>
{startYear}
</time>
{' '}-{' '}
<time datetime={experience.endDate} data-title={experience.endDate}>
{endYear}
</time>
</header>
<div class="relative flex flex-col sm:col-span-12 pb-6">
<div class="absolute bg-stone-400 -translate-x-[1.71rem] rounded-full h-2 w-2 mt-4"/>
<h3>
<div
class="inline-flex items-center text-2xl leading-tight font-semibold"
aria-label="{position} - {company}"
>
<span class="text-header">
{experience.position} <span>@</span>
{experience.url ? (
<a
class="hover:text-main"
href={experience.url}
title={`Ver ${experience.name}`}
target="_blank"
>
{experience.name}
</a>
) : (
<span>{experience.name}</span>
)}
</span>
</div>
</h3>
{(experience.location || experience.location_type) && (
<div class="text-secondary text-sm">
{experience.location} {experience.location && experience.location_type && '-'} {experience.location_type}
</div>
)}
<div class="text-md mt-4 flex flex-col gap-4" x-data="{ expanded: false }">
{experience.summary && (
<div class="flex flex-col gap-1">
<h4 class="text-header font-semibold">
Summary:
</h4>
<ul class="flex flex-col text-primary list-disc gap-2 [&>li]:ml-4">
<li class="marker:text-main">
{experience.summary}
</li>
</ul>
</div>
)}
{(experience.responsibilities || experience.achievements) && (
<div class="relative flex flex-col gap-4 max-sm:h-auto! md:after:absolute md:after:bottom-0 md:after:h-12 md:after:w-full md:after:bg-gradient-to-t md:after:from-neutral-200 dark:md:after:from-stone-700 md:after:content-[''] " :class="expanded ? 'after:hidden' : ''" x-show="expanded" x-collapse.min.50px>
{experience.responsibilities && (
<div class="flex flex-col gap-1">
<h4 class="text-header font-semibold">
Responsibilities:
</h4>
<ul class="flex flex-col text-primary list-disc gap-2 [&>li]:ml-4">
{experience.responsibilities.map(responsibility => (
<li class="marker:text-main">
{responsibility}
</li>
))}
</ul>
</div>
)}
{experience.achievements && (
<div class="flex flex-col gap-1">
<h4 class="text-header font-semibold">
Achievements:
</h4>
<ul class="flex flex-col text-primary list-disc gap-2 [&>li]:ml-4">
{experience.achievements.map(achievement => (
<li class="marker:text-main">
{achievement}
</li>
))}
</ul>
</div>
)}
</div>
<button @click="expanded = ! expanded" class="group/more flex items-center justify-center text-primary hover:text-primary-hover text-xs underline transition-all gap-1.5 w-fit cursor-pointer">
<span x-text="expanded ? 'Show less' : 'Show more'">
Show more
</span>
<svg
class="group-hover/more:translate-y-0.5 ease-out duration-300 h-4 w-4"
:class="{ 'rotate-180': expanded }"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="6 9 12 15 18 9" />
</svg>
</button>
<ul
class="flex print:hidden flex-wrap gap-2"
aria-label="Technologies used"
>
{experience.skills && experience.skills.map(skill => {
const iconName = skill.toLowerCase();
const skillName = skill.split(':')[1].replace(/^language-/, '').replace(/-/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase());
return (
<li class="flex items-center bg-steel/20 dark:bg-bermuda/20 text-neutral-800 dark:text-neutral-200 text-xs rounded-md border border-solid border-steel/20 dark:border-bermuda/20 gap-1 px-2 py-0.5">
<Icon name={`${iconName}`} class="h-4 w-4" /> <span>{skillName}</span>
</li>
)
})}
</ul>
)}
</div>
</div>
</div>
</li>
);
})}
</ul>
</section>
<!-- Alpine Plugins -->
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/collapse@3.x.x/dist/cdn.min.js"></script>
<!-- Alpine Core -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>

View File

@@ -2,15 +2,13 @@
import { readSingleton } from '@directus/sdk'; import { readSingleton } from '@directus/sdk';
import directus from '@lib/directus'; import directus from '@lib/directus';
import FeaturesCard from '@components/ui/cards/FeaturesCard.astro'; import FeaturesCard from '@components/cards/FeaturesCard.astro';
const global = await directus.request(readSingleton('site_global')); const global = await directus.request(readSingleton('site_global'));
--- ---
<section class="mx-auto mb-20 max-w-340 px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"> <section class="mx-auto mb-20 max-w-340 px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full">
<div <div class="flex flex-col items-center justify-center gap-y-2 sm:flex-row sm:gap-x-12 sm:gap-y-0 lg:gap-x-24">
class="flex flex-col items-center justify-center gap-y-2 sm:flex-row sm:gap-x-12 sm:gap-y-0 lg:gap-x-24"
>
<div class="max-w-5xl sm:px-6 lg:px-8"> <div class="max-w-5xl sm:px-6 lg:px-8">
<div class="flex flex-wrap gap-6 sm:grid-cols-2 sm:gap-6 lg:grid-cols-3 justify-center"> <div class="flex flex-wrap gap-6 sm:grid-cols-2 sm:gap-6 lg:grid-cols-3 justify-center">
<FeaturesCard <FeaturesCard

View File

@@ -1,22 +1,20 @@
--- ---
import GiteaBtn from '@components/ui/buttons/GiteaBtn.astro'; import GiteaButton from '@components/buttons/GiteaButton.astro';
const { title, subTitle, url } = Astro.props;
const btnTitle = 'Continue to Gitea';
interface Props { interface Props {
title: string; title: string;
subTitle?: string; subTitle?: string;
url?: string; url?: string;
} }
const { title, subTitle, url } = Astro.props;
--- ---
<section class="lg:px- relative mx-auto mb-20 max-w-340 px-4 pt-30 pb-30 sm:px-6"> <section class="lg:px- relative mx-auto mb-20 max-w-340 px-4 pt-30 pb-30 sm:px-6">
<div <!-- Animated shapes -->
class="smooth-reveal absolute top-[55%] left-0 scale-90 md:top-[20%] xl:top-[25%] xl:left-[10%]" <div class="smooth-reveal absolute top-[55%] left-0 scale-90 md:top-[20%] xl:top-[25%] xl:left-[10%]">
>
<svg <svg
class="animate-hover animate-hover-1" class="gitea-animate-hover gitea-animate-hover-1"
width="64" width="64"
height="64" height="64"
fill="none" fill="none"
@@ -46,7 +44,7 @@ interface Props {
</div> </div>
<div class="smooth-reveal absolute top-0 left-[85%] scale-75"> <div class="smooth-reveal absolute top-0 left-[85%] scale-75">
<svg <svg
class="animate-hover animate-hover-2" class="gitea-animate-hover gitea-animate-hover-2"
width="64" width="64"
height="64" height="64"
fill="none" fill="none"
@@ -80,11 +78,9 @@ interface Props {
d="M10.5 19H9M15 19h-1.5"></path> d="M10.5 19H9M15 19h-1.5"></path>
</svg> </svg>
</div> </div>
<div <div class="smooth-reveal absolute bottom-[5%] left-[60%] scale-[.6] xl:bottom-[15%] xl:left-[35%]">
class="smooth-reveal absolute bottom-[5%] left-[60%] scale-[.6] xl:bottom-[15%] xl:left-[35%]"
>
<svg <svg
class="animate-hover animate-hover-3" class="gitea-animate-hover gitea-animate-hover-3"
width="64" width="64"
height="64" height="64"
fill="none" fill="none"
@@ -106,59 +102,54 @@ interface Props {
></path> ></path>
</svg> </svg>
</div> </div>
<!-- Hero Section Heading --> <!-- Heading -->
<div class="smooth-reveal-2 mx-auto mt-5 max-w-xl text-center"> <div class="smooth-reveal-2 mx-auto mt-5 max-w-xl text-center">
<h2 <h1 class="card-text-header block">
class="block text-4xl leading-tight font-bold tracking-tight text-balance text-neutral-800 md:text-5xl lg:text-5xl dark:text-neutral-200"
>
{title} {title}
</h2> </h1>
</div> </div>
<!-- Hero Section Sub-heading --> <!-- Sub-heading -->
<div class="smooth-reveal-2 mx-auto mt-5 max-w-3xl text-center"> <div class="smooth-reveal-2 mx-auto mt-5 max-w-3xl text-center">
{ {subTitle && (
subTitle && ( <p class="card-text-header-description">
<p class="text-lg text-pretty text-neutral-600 dark:text-neutral-400">{subTitle}</p> {subTitle}
) </p>
} )}
</div> </div>
<!-- Github Button --> <!-- Gitea Button -->
{ {url && (
url && ( <div class="smooth-reveal-2 flex justify-center mt-8 gap-3">
<div class="smooth-reveal-2 mt-8 flex justify-center gap-3"> <GiteaButton url={url}/>
<GiteaBtn url={url} title={btnTitle} /> </div>
</div> )}
)
}
</section> </section>
<style> <style>
@keyframes animate-hover { @keyframes gitea-animate-hover {
from { from {
transform: translateY(15px); transform: translateY(15px);
} }
to { to {
transform: translateY(-15px); transform: translateY(-15px);
} }
} }
.animate-hover { .gitea-animate-hover {
animation: animate-hover ease-in-out; animation: gitea-animate-hover ease-in-out;
animation-iteration-count: infinite; animation-iteration-count: infinite;
animation-direction: alternate; animation-direction: alternate;
} }
.animate-hover-1 { .gitea-animate-hover-1 {
animation-duration: 5s; animation-duration: 5s;
} }
.animate-hover-2 { .gitea-animate-hover-2 {
animation-duration: 5.5s; animation-duration: 5.5s;
} }
.animate-hover-3 { .gitea-animate-hover-3 {
animation-duration: 6s; animation-duration: 6s;
} }
</style> </style>

View File

@@ -0,0 +1,31 @@
---
import GoLinkPrimaryButton from '@components/buttons/GoLinkPrimaryButton.astro';
interface Props {
title: string;
subTitle: string;
btnExists?: boolean;
btnTitle?: string;
btnURL?: string;
}
const { title, subTitle, btnExists, btnTitle, btnURL } = Astro.props;
---
<section class="mx-auto mt-10 px-4 sm:px-6 lg:px-8 lg:pt-10 2xl:max-w-full">
<div class="flex-wrap md:flex md:items-center md:justify-between">
<div class="w-full md:w-auto">
<h1 class="smooth-reveal card-text-header block lg:text-6xl">
{title}
</h1>
<p class="smooth-reveal card-text-header-description mt-4">
{subTitle}
</p>
{btnExists ? (
<div class="smooth-reveal mt-4 md:mt-8">
<GoLinkPrimaryButton title={btnTitle} url={btnURL}/>
</div>
) : null}
</div>
</div>
</section>

View File

@@ -0,0 +1,56 @@
---
import GoLinkPrimaryButton from '@components/buttons/GoLinkPrimaryButton.astro';
import GoLinkSecondaryButton from '@components/buttons/GoLinkSecondaryButton.astro';
import Image from '@components/ui/images/Image.astro';
interface Props {
title: string;
subTitle?: string;
primaryBtn?: string;
primaryBtnURL?: string;
secondaryBtn?: string;
secondaryBtnURL?: string;
src?: any;
alt?: string;
rounded?: boolean;
}
const { title, subTitle, primaryBtn, primaryBtnURL, secondaryBtn, secondaryBtnURL, src, alt } = Astro.props;
const roundedClasses = Astro.props.rounded ? "rounded-xl" : null;
---
<section class="mx-auto grid max-w-340 gap-4 px-4 py-14 sm:px-6 md:grid-cols-2 md:items-center md:gap-8 lg:px-8 2xl:max-w-full">
<div>
<h1 class="smooth-reveal card-text-header block lg:text-7xl">
<Fragment set:html={title} />
</h1>
{subTitle && (
<p class="smooth-reveal card-text-header-description lg:w-4/5 mt-6">
{subTitle}
</p>
)}
<div class="smooth-reveal grid sm:inline-flex mt-7 w-full gap-3">
{primaryBtn && <GoLinkPrimaryButton title={primaryBtn} url={primaryBtnURL} />}
{secondaryBtn && <GoLinkSecondaryButton title={secondaryBtn} url={secondaryBtnURL} />}
</div>
</div>
<div class="smooth-reveal-fade md:block w-full hidden">
<div class="flex justify-center w-full top-12 md:ml-4 overflow-hidden">
{src && alt && (
<Image
src={src}
alt={alt}
class={`h-full w-105 scale-100 object-cover object-center ${roundedClasses}`}
draggable="false"
loading="eager"
format="webp"
quality="low"
widths={[840]}
disableBlur={true}
/>
)}
</div>
</div>
</section>

View File

@@ -1,8 +1,9 @@
--- ---
import { readItems } from '@directus/sdk'; import { readItems } from '@directus/sdk';
import directus from '@lib/directus';
import type { Post } from '@lib/directusTypes'; import type { Post } from '@lib/directusTypes';
import directus from '@lib/directus';
import BlogCard from '@components/blog/BlogCard.astro'; import BlogCard from '@components/blog/BlogCard.astro';
const posts = await directus.request( const posts = await directus.request(
@@ -20,11 +21,14 @@ const recentPosts = posts
<section class="mx-auto mb-20 max-w-340 px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"> <section class="mx-auto mb-20 max-w-340 px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full">
<div class="mx-auto mb-10 max-w-2xl text-center lg:mb-14"> <div class="mx-auto mb-10 max-w-2xl text-center lg:mb-14">
<h1 <h1 class="smooth-reveal card-text-header block">
class="smooth-reveal block text-4xl font-bold text-neutral-800 md:text-5xl md:leading-tight lg:text-5xl dark:text-neutral-200"
>
Latest Posts Latest Posts
</h1> </h1>
<div class="smooth-reveal mx-auto mt-5 max-w-3xl text-center">
<span class="card-text-header-description">
Checkout my most recent thoughts here
</span>
</div>
</div> </div>
<div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-3"> <div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{recentPosts.map((b) => <BlogCard post={b} />)} {recentPosts.map((b) => <BlogCard post={b} />)}

View File

@@ -4,7 +4,7 @@ import { readItems } from '@directus/sdk';
import type { Project } from '@lib/directusTypes'; import type { Project } from '@lib/directusTypes';
import directus from '@lib/directus'; import directus from '@lib/directus';
import HighlightsCard from '@components/ui/cards/HighlightsCard.astro'; import HighlightsCard from '@components/cards/HighlightsCard.astro';
const projects = ((await directus.request( const projects = ((await directus.request(
readItems('site_projects' as any, { readItems('site_projects' as any, {

View File

@@ -6,86 +6,63 @@ import type { Skill } from '@lib/directusTypes';
import directus from '@lib/directus'; import directus from '@lib/directus';
const skills = await directus.request( const skills = ((await directus.request(
readItems('site_skills', { readItems('site_skills' as any, {
fields: ['*'], fields: ['*'],
sort: ['-date_created'], sort: ['-date_created'],
}) })
); )) as unknown) as Skill[];
const baseClasses = 'mx-2 min-w-[220px] sm:mx-4 sm:min-w-[280px]';
const borderClasses =
'border border-neutral-100 hover:border-neutral-200 dark:border-stone-500/20 dark:hover:border-neutral-800';
const bgColorClasses = 'bg-neutral-100/80 dark:bg-neutral-800/60 dark:hover:bg-neutral-800/90';
const hoverClasses = 'hover:-translate-y-2 hover:scale-105 ';
const shadowClasses = 'shadow-xs hover:shadow-lg';
--- ---
<section class:list={['flex flex-col gap-4', Astro.props.className]}> <section class:list={['flex flex-col gap-4', Astro.props.className]}>
<h3 <h3 class="smooth-reveal card-text-header flex relative items-center w-full gap-3 pb-5">
class="relative flex w-full items-center gap-3 pb-4 text-5xl text-neutral-800 dark:text-neutral-200"
>
Skills Skills
</h3> </h3>
<div class=""> <div class="">
<div class="tech-stack-slider relative overflow-hidden py-4 sm:py-8"> <div class="tech-stack-slider relative overflow-hidden py-4 sm:py-8">
<!-- Main slider container --> <!-- Main slider container -->
<div class="slider-track animate-slide flex"> <div class="slider-track animate-slide flex">
{ {[...skills, ...skills, ...skills].map((skill: Skill) => {
[...skills, ...skills, ...skills].map((skill: Skill) => { return (
return ( <div class="skill-card card-base transform hover:-translate-y-2 hover:scale-105 transition-all duration-300 mx-2 min-w-55 sm:mx-4 sm:min-w-70">
<div <div class="p-4 sm:p-6">
class={`skill-card transform rounded-xl transition-all duration-300 ${baseClasses} ${borderClasses} ${bgColorClasses} ${hoverClasses} ${shadowClasses}`} <div class="flex items-center justify-between mb-4 sm:mb-6">
> <div class="flex items-center gap-2 sm:gap-4">
<div class="p-4 sm:p-6"> <div class="flex items-center justify-center rounded-lg text-primary">
<div class="mb-4 flex items-center justify-between sm:mb-6"> <Icon name={skill.icon} class="h-8 w-8 sm:h-12 sm:w-12" />
<div class="flex items-center gap-2 sm:gap-4">
<div class="flex transform items-center justify-center rounded-lg text-neutral-800 transition-transform group-hover:rotate-12 dark:text-neutral-200">
<Icon name={skill.icon} class="h-8 w-8 sm:h-12 sm:w-12" />
</div>
<h3 class="text-base font-semibold text-neutral-900 sm:text-xl dark:text-neutral-100">
{skill.title}
</h3>
</div>
<span class="rounded-full bg-neutral-200 px-2 py-0.5 font-mono text-xs text-neutral-700 sm:px-2.5 sm:py-1 sm:text-sm dark:bg-neutral-800 dark:text-neutral-300">
{skill.level}%
</span>
</div>
<div class="relative h-1.5 w-full overflow-hidden rounded-full bg-stone-500/20 sm:h-2 dark:bg-stone-500/20">
<div
class="progress-bar-animate from-steel via-bermuda to-steel absolute top-0 left-0 h-full rounded-full bg-linear-to-r transition-all duration-1000"
style={`width: ${skill.level}%`}
/>
</div>
<div class="mt-1 flex justify-between font-mono text-[10px] text-neutral-600 sm:mt-2 sm:text-xs dark:text-neutral-400">
<span>Beginner</span>
<span>Advanced</span>
</div>
</div> </div>
<h3 class="text-neutral-900 dark:text-neutral-100 text-base font-semibold sm:text-xl">
{skill.title}
</h3>
</div> </div>
); <span class=" bg-neutral-200 dark:bg-neutral-800 text-neutral-700 dark:text-neutral-300 font-mono text-xs sm:text-sm rounded-full px-2 sm:px-2.5 py-0.5 sm:py-1">
}) {skill.level}%
} </span>
</div>
<div class="relative bg-stone-500/20 dark:bg-stone-500/20 rounded-full h-1.5 sm:h-2 w-full overflow-hidden">
<div
class="progress-bar-animate bg-linear-to-r from-steel via-bermuda to-steel absolute top-0 left-0 h-full rounded-full transition-all duration-1000"
style={`width: ${skill.level}%`}
/>
</div>
<div class="flex justify-between text-secondary font-mono text-[10px] mt-1 sm:mt-2 sm:text-xs">
<span>Beginner</span>
<span>Advanced</span>
</div>
</div>
</div>
);
})}
</div> </div>
<!-- Gradient overlays --> <!-- Gradient overlays -->
<div <div class="bg-linear-to-r from-neutral-200 to-transparent dark:from-stone-700 absolute top-0 bottom-0 left-0 z-10 w-12 sm:w-24"/>
class="absolute top-0 bottom-0 left-0 z-10 w-12 bg-linear-to-r from-neutral-200 to-transparent sm:w-24 dark:from-stone-700" <div class="bg-linear-to-l from-neutral-200 to-transparent dark:from-stone-700 absolute top-0 bottom-0 right-0 z-10 w-12 sm:w-24"/>
>
</div>
<div
class="absolute top-0 right-0 bottom-0 z-10 w-12 bg-linear-to-l from-neutral-200 to-transparent sm:w-24 dark:from-stone-700"
>
</div>
</div> </div>
</div> </div>
</section> </section>
<script> <script>
document.addEventListener('astro:page-load', () => { document.addEventListener('astro:page-load', () => {
// Create infinite scrolling effect
function setupInfiniteScroll() { function setupInfiniteScroll() {
const cards = document.querySelectorAll('.skill-card'); const cards = document.querySelectorAll('.skill-card');
if (!cards.length) return; if (!cards.length) return;
@@ -93,7 +70,6 @@ const shadowClasses = 'shadow-xs hover:shadow-lg';
setupInfiniteScroll(); setupInfiniteScroll();
// 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');
@@ -144,7 +120,7 @@ const shadowClasses = 'shadow-xs hover:shadow-lg';
</script> </script>
<style> <style>
/* Tech Stack Slider */ /* Specific css to enable sliding effect */
.slider-track { .slider-track {
width: fit-content; width: fit-content;
animation: scroll 40s linear infinite; animation: scroll 40s linear infinite;
@@ -155,7 +131,7 @@ const shadowClasses = 'shadow-xs hover:shadow-lg';
transform: translateX(0); transform: translateX(0);
} }
100% { 100% {
transform: translateX(calc(-220px * 6 - 16px * 6)); /* Card width + margin for mobile */ transform: translateX(calc(-220px * 6 - 16px * 6));
} }
} }
@@ -169,7 +145,7 @@ const shadowClasses = 'shadow-xs hover:shadow-lg';
transform: translateX(0); transform: translateX(0);
} }
100% { 100% {
transform: translateX(calc(-280px * 6 - 32px * 6)); /* Card width + margin for desktop */ transform: translateX(calc(-280px * 6 - 32px * 6));
} }
} }
} }

View File

@@ -1,5 +1,5 @@
--- ---
import WeatherCard from '@components/ui/cards/WeatherCard.astro'; import WeatherCard from '@components/cards/WeatherCard.astro';
import { getFiveDayForecast } from '@support/weather'; import { getFiveDayForecast } from '@support/weather';
const { latitude = "44.95", longitude = "-93.09", cityName = "St. Paul, Minnesota", timezone = "America/Chicago" } = Astro.props; const { latitude = "44.95", longitude = "-93.09", cityName = "St. Paul, Minnesota", timezone = "America/Chicago" } = Astro.props;
@@ -8,7 +8,7 @@ const { forecastDays, error } = await getFiveDayForecast(latitude, longitude, ti
<section class="mx-auto mb-20 max-w-340 px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"> <section class="mx-auto mb-20 max-w-340 px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full">
<div class="mx-auto mb-10 max-w-2xl text-center lg:mb-14"> <div class="mx-auto mb-10 max-w-2xl text-center lg:mb-14">
<h1 class="card-text-header smooth-reveal block font-bold md:leading-tight"> <h1 class="smooth-reveal card-text-header block">
Weather in my Area Weather in my Area
</h1> </h1>
<div class="smooth-reveal mx-auto mt-5 max-w-3xl text-center"> <div class="smooth-reveal mx-auto mt-5 max-w-3xl text-center">
@@ -17,7 +17,6 @@ const { forecastDays, error } = await getFiveDayForecast(latitude, longitude, ti
</span> </span>
</div> </div>
</div> </div>
{error ? ( {error ? (
<div class="card-base p-10 text-accent text-center"> <div class="card-base p-10 text-accent text-center">
{error} {error}

View File

@@ -1,32 +0,0 @@
---
import { Icon } from 'astro-icon/components';
const { title, url } = Astro.props;
interface Props {
title?: string;
url?: string;
}
const baseClasses =
'group group-hover inline-flex items-center justify-center gap-x-3 rounded-full px-4 py-3 text-center text-sm font-medium text-neutral-200';
const borderClasses = 'border border-transparent';
const bgColorClasses =
'bg-gitea-primary hover:bg-gitea-secondary dark:bg-gitea-secondary dark:hover:bg-gitea-primary';
const shadowClasses = 'shadow-sm';
const fontSizeClasses = '2xl:text-base';
---
<a
class={`${baseClasses} ${borderClasses} ${bgColorClasses} ${shadowClasses} ${fontSizeClasses} `}
href={url}
target="_blank"
rel="noopener noreferrer"
>
<Icon name="pajamas:gitea" class="h-4 w-4 md:h-6 md:w-6" />
{title}
<Icon
name="mdi:keyboard-arrow-right"
class="h-3 w-3 translate-y-0.25 transition duration-300 group-hover:translate-x-1 md:h-5 md:w-5"
/>
</a>

View File

@@ -1,35 +0,0 @@
---
import Icon from '@components/ui/icons/icon.astro';
const { title, noArrow } = Astro.props;
interface Props {
title?: string;
url?: string;
noArrow?: boolean;
addHome?: boolean;
}
const baseClasses =
'group inline-flex items-center justify-center gap-x-2 rounded-lg px-4 py-3 text-sm font-bold text-neutral-50 ring-neutral-500 transition duration-300 focus-visible:ring outline-none';
const borderClasses = 'border border-transparent';
const bgColorClasses = 'bg-steel hover:bg-sky-800 active:bg-orange-500 dark:focus:outline-none';
const disableClasses = 'disabled:pointer-events-none disabled:opacity-50';
const fontSizeClasses = '2xl:text-base';
const ringClasses = 'dark:ring-neutral-200';
---
<button
class={`${baseClasses} ${borderClasses} ${bgColorClasses} ${disableClasses} ${fontSizeClasses} ${ringClasses}`}
id="back-button"
data-astro-prefetch
>
{noArrow ? null : <Icon name="arrowLeft" />}
{title}
</button>
<script>
document.getElementById('back-button')?.addEventListener('click', () => {
window.history.back();
});
</script>

View File

@@ -1,45 +0,0 @@
---
import { Icon } from 'astro-icon/components';
const { title, url, noArrow, addHome, addClass } = Astro.props;
interface Props {
title?: string;
url?: string;
noArrow?: boolean;
addHome?: boolean;
addClass?: string;
}
const baseClasses =
'group inline-flex items-center justify-center gap-x-2 rounded-lg px-4 py-3 text-sm font-bold text-neutral-100 transition duration-300 ';
const borderClasses = 'border border-transparent';
const bgColorClasses = 'bg-bermuda hover:bg-turquoise dark:bg-turquoise dark:hover:bg-bermuda';
const disableClasses = 'disabled:pointer-events-none disabled:opacity-50';
const fontSizeClasses = '2xl:text-base';
const ringClasses = 'dark:ring-neutral-200';
---
<a
class={`${baseClasses} ${borderClasses} ${bgColorClasses} ${disableClasses} ${fontSizeClasses} ${ringClasses} ${addClass}`}
href={url}
data-astro-prefetch
>
{
addHome ? (
<Icon
name="mdi:home-variant-outline"
class="h-3 w-3 translate-y-0.25 transition duration-300 group-hover:translate-x-1 md:h-5 md:w-5"
/>
) : null
}
{title}
{
noArrow ? null : (
<Icon
name="mdi:keyboard-arrow-right"
class="h-3 w-3 translate-y-0.25 transition duration-300 group-hover:translate-x-1 md:h-5 md:w-5"
/>
)
}
</a>

View File

@@ -1,26 +0,0 @@
---
const { title, url } = Astro.props;
interface Props {
title?: string;
url?: string;
}
const baseClasses =
'inline-flex items-center justify-center gap-x-2 rounded-lg px-4 py-3 text-center text-sm font-medium text-neutral-600 shadow-sm outline-none ring-neutral-500 focus-visible:ring transition duration-300';
const borderClasses = 'border border-neutral-200';
const bgColorClasses = 'bg-neutral-300';
const hoverClasses = 'hover:bg-neutral-400/50 hover:text-neutral-600 active:text-neutral-700';
const disableClasses = 'disabled:pointer-events-none disabled:opacity-50';
const fontSizeClasses = '2xl:text-base';
const ringClasses = 'ring-neutral-500';
const darkClasses =
'dark:border-neutral-700 dark:bg-neutral-700 dark:text-neutral-300 dark:ring-neutral-200 dark:hover:bg-neutral-600 dark:focus:outline-none';
---
<a
class={`${baseClasses} ${borderClasses} ${bgColorClasses} ${hoverClasses} ${disableClasses} ${fontSizeClasses} ${ringClasses} ${darkClasses}`}
href={url}
>
{title}
</a>

View File

@@ -1,150 +0,0 @@
---
import Icon from '@components/ui/icons/icon.astro';
const { pageTitle, title = 'Share' } = Astro.props;
interface Props {
pageTitle: string;
title?: string;
}
type SocialPlatform = {
name: string;
url: string;
svg: string;
};
const socialPlatforms: SocialPlatform[] = [
{
name: 'Facebook',
url: `https://www.facebook.com/share.php?u=${Astro.url}&title=${pageTitle}`,
svg: 'facebook',
},
{
name: 'X',
url: `https://twitter.com/home/?status=${pageTitle}${Astro.url}`,
svg: 'x',
},
{
name: 'LinkedIn',
url: `https://www.linkedin.com/shareArticle?mini=true&url=${Astro.url}&title=${pageTitle}`,
svg: 'linkedIn',
},
];
---
<div class="hs-dropdown relative inline-flex [--auto-close:inside] [--placement:top-left]">
<button
id="hs-dropup"
type="button"
class="hs-dropdown-toggle inline-flex items-center gap-x-2 rounded-lg px-4 py-3 text-sm font-medium text-neutral-600 ring-neutral-500 transition duration-300 outline-none hover:bg-neutral-100 hover:text-neutral-700 focus-visible:ring dark:text-neutral-400 dark:ring-neutral-200 dark:hover:bg-neutral-700 dark:hover:text-neutral-300 dark:focus:outline-none"
>
<Icon name="share" />
{title}
</button>
<div
class="hs-dropdown-menu duration hs-dropdown-open:opacity-100 z-10 hidden w-72 divide-y divide-neutral-200 rounded-lg bg-neutral-50 p-2 opacity-0 shadow-md transition-[opacity,margin] dark:divide-neutral-700 dark:border dark:border-neutral-700 dark:bg-neutral-800"
aria-labelledby="hs-dropup"
>
<div class="py-2 first:pt-0 last:pb-0">
{
socialPlatforms.map((platform) => (
<a
class="flex items-center gap-x-3.5 rounded-lg px-3 py-2 text-sm text-neutral-700 hover:bg-neutral-200 focus:bg-neutral-100 focus:outline-none dark:text-neutral-300 dark:hover:bg-neutral-700 dark:hover:text-neutral-300 dark:focus:bg-neutral-700"
href={platform.url}
>
<Icon name={platform.svg} />
Share on {platform.name}
</a>
))
}
</div>
<div class="py-2 first:pt-0 last:pb-0">
<button
type="button"
class="js-clipboard hover:text-dark focus-visible:ring-secondary group inline-flex w-full items-center gap-x-3.5 rounded-lg px-3 py-2 text-sm text-neutral-700 hover:bg-neutral-200 focus:bg-neutral-100 focus:outline-none focus-visible:ring-1 focus-visible:outline-none dark:text-neutral-300 dark:hover:bg-neutral-700 dark:hover:text-neutral-300 dark:focus:bg-neutral-700"
data-clipboard-success-text="Copied"
>
<svg
class="js-clipboard-default h-4 w-4 transition group-hover:rotate-6"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect width="8" height="4" x="8" y="2" rx="1" ry="1"></rect>
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path>
</svg>
<svg
class="js-clipboard-success hidden h-4 w-4 text-neutral-700 dark:text-neutral-300"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
<span class="js-clipboard-success-text">Copy link</span>
</button>
</div>
</div>
</div>
<!--Import the necessary Dropdown and Clipboard plugins-->
<!--https://preline.co/plugins/html/dropdown.html-->
<!--<script is:inline src="/scripts/vendor/preline/dropdown/index.js"></script>-->
<!-- https://clipboardjs.com/ -->
<!--<script is:inline src="/scripts/vendor/clipboard.min.js"></script>-->
<script is:inline>
(function () {
window.addEventListener('load', () => {
const $clipboards = document.querySelectorAll('.js-clipboard');
$clipboards.forEach((el) => {
const clipboard = new ClipboardJS(el, {
text: () => {
return window.location.href;
},
});
clipboard.on('success', () => {
const $default = el.querySelector('.js-clipboard-default');
const $success = el.querySelector('.js-clipboard-success');
const $successText = el.querySelector('.js-clipboard-success-text');
const successText = el.dataset.clipboardSuccessText || '';
let oldSuccessText;
if ($successText) {
oldSuccessText = $successText.textContent;
$successText.textContent = successText;
}
if ($default && $success) {
$default.style.display = 'none';
$success.style.display = 'block';
}
setTimeout(() => {
if ($successText && oldSuccessText) {
$successText.textContent = oldSuccessText;
}
if ($default && $success) {
$success.style.display = '';
$default.style.display = '';
}
}, 800);
});
});
});
})();
</script>

View File

@@ -1,152 +0,0 @@
---
import { Icon } from 'astro-icon/components';
import { readItems } from '@directus/sdk';
import type { Experience } from '@lib/directusTypes';
import directus from '@lib/directus';
const experiences = await directus.request(
readItems('site_experience', {
fields: ['*'],
sort: ['-endDate'],
})
);
---
<section
class:list={['flex flex-col gap-8', Astro.props.className]}
>
<h3 class="relative flex w-full items-center gap-3 pb-10 text-5xl text-neutral-800 dark:text-neutral-200">Experience</h3>
<ul class="ml-8 w-full flex flex-col">
{
experiences.map(
(experience: Experience) => {
const startYear = new Date(experience.startDate).getFullYear();
const endYear = experience.endDate != null ? new Date(experience.endDate).getFullYear() : 'Present';
return (
<li class="relative">
<div class="group smooth-reveal relative grid pb-1 transition-all sm:grid-cols-18 sm:gap-8 md:gap-6 lg:hover:!opacity-100">
<header class="relative mt-1 text-lg font-semibold sm:col-span-3 text-neutral-800 dark:text-neutral-200">
<time datetime={experience.startDate} data-title={experience.startDate}>
{startYear}
</time>{' '}
-{' '}
<time datetime={experience.endDate} data-title={experience.endDate}>
{endYear}
</time>
</header>
<div class="relative flex flex-col pb-6 before:absolute before:mt-8 before:-ml-6 before:h-full before:w-px before:bg-stone-400 sm:col-span-12">
<div class="absolute mt-4 h-2 w-2 -translate-x-[1.71rem] rounded-full bg-stone-400" />
<h3>
<div
class="inline-flex items-center text-2xl leading-tight font-semibold"
aria-label="{position} - {company}"
>
<span class="text-neutral-800 dark:text-neutral-200">
{experience.position} <span>@</span>
{experience.url ? (
<a
class="hover:text-steel dark:hover:text-bermuda"
href={experience.url}
title={`Ver ${experience.name}`}
target="_blank"
>
{experience.name}
</a>
) : (
<span>{experience.name}</span>
)}
</span>
</div>
</h3>
{(experience.location || experience.location_type) && (
<div class="text-sm text-neutral-600 dark:text-neutral-400">
{experience.location} {experience.location && experience.location_type && '-'} {experience.location_type}
</div>
)}
<div class="text-md mt-4 flex flex-col gap-4" x-data="{ expanded: false }">
{experience.summary && (
<div class="flex flex-col gap-1">
<h4 class="font-semibold text-neutral-800 dark:text-neutral-200">Summary:</h4>
<ul class="flex list-disc flex-col gap-2 text-neutral-700 dark:text-neutral-400 [&>li]:ml-4">
{Array.isArray(experience.summary) ? (
experience.summary.map((item) => ({ item }))
) : (
<li class="marker:text-steel dark:marker:text-bermuda">{experience.summary}</li>
)}
</ul>
</div>
)}
{(experience.responsibilities || experience.achievements) && (
<div class="relative flex flex-col gap-4 max-sm:!h-auto md:after:absolute md:after:bottom-0 md:after:h-12 md:after:w-full md:after:bg-gradient-to-t md:after:from-neutral-200 dark:md:after:from-stone-700 md:after:content-[''] " :class="expanded ? 'after:hidden' : ''" x-show="expanded" x-collapse.min.50px>
{experience.responsibilities && (
<div class="flex flex-col gap-1">
<h4 class="font-semibold text-neutral-800 dark:text-neutral-200">Responsibilities:</h4>
<ul class="text-neutral-700 dark:text-neutral-400 [&>li]:ml-4 flex list-disc flex-col gap-2">
{experience.responsibilities.map(responsibility => (
<li class="marker:text-steel dark:marker:text-bermuda">{responsibility}</li>
))}
</ul>
</div>
)}
{experience.achievements && (
<div class="flex flex-col gap-1">
<h4 class="font-semibold text-neutral-800 dark:text-neutral-200">Achievements:</h4>
<ul class="text-neutral-700 dark:text-neutral-400 [&>li]:ml-4 flex list-disc flex-col gap-2">
{experience.achievements.map(achievement => (
<li class="marker:text-steel dark:marker:text-bermuda">{achievement}</li>
))}
</ul>
</div>
)}
</div>
<button @click="expanded = ! expanded" class="group/more w-fit cursor-pointer items-center justify-center gap-1.5 text-xs underline text-neutral-700 dark:text-neutral-300 hover:text-neutral-900 dark:hover:text-neutral-400 transition-all flex">
<span x-text="expanded ? 'Show less' : 'Show more'">Show more</span>
<svg
class="h-4 w-4 duration-200 ease-out group-hover/more:translate-y-0.5"
:class="{ 'rotate-180': expanded }"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<polyline points="6 9 12 15 18 9" />
</svg>
</button>
<ul class="flex print:hidden flex-wrap gap-2" aria-label="Technologies used">
{experience.skills && experience.skills.map(skill => {
const iconName = skill.toLowerCase();
return (
<li class="bg-steel/20 border-steel/20 text-neutral-800 dark:bg-bermuda/20 dark:border-bermuda/20 dark:text-neutral-200 flex gap-1 items-center border-solid border rounded-md px-2 py-0.5 text-xs">
<Icon name={`mdi:${iconName}`} /> <span>{skill}</span>
</li>
)
})}
</ul>
)}
</div>
</div>
</div>
</li>
);
}
)
}
</ul>
</section>
<!-- Alpine Plugins -->
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/collapse@3.x.x/dist/cdn.min.js"></script>
<!-- Alpine Core -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>

View File

@@ -1,35 +0,0 @@
---
import PrimaryCTA from '@components/ui/buttons/PrimaryCTA.astro';
interface Props {
title: string;
subTitle: string;
btnExists?: boolean;
btnTitle?: string;
btnURL?: string;
}
const { title, subTitle, btnExists, btnTitle, btnURL } = Astro.props;
---
<section class="mx-auto mt-10 px-4 sm:px-6 lg:px-8 lg:pt-10 2xl:max-w-full">
<div class="flex-wrap md:flex md:items-center md:justify-between">
<div class="w-full md:w-auto">
<h1
class="smooth-reveal block text-4xl font-bold tracking-tight text-balance text-neutral-800 md:text-5xl lg:text-6xl dark:text-neutral-200"
>
{title}
</h1>
<p class="smooth-reveal mt-4 text-lg text-pretty text-neutral-600 dark:text-neutral-400">
{subTitle}
</p>
{
btnExists ? (
<div class="smooth-reveal mt-4 md:mt-8">
<PrimaryCTA title={btnTitle} url={btnURL} />
</div>
) : null
}
</div>
</div>
</section>

View File

@@ -1,66 +0,0 @@
---
import PrimaryCTA from '@components/ui/buttons/PrimaryCTA.astro';
import SecondaryCTA from '@components/ui/buttons/SecondaryCTA.astro';
import Image from '@components/ui/images/Image.astro';
const { title, subTitle, primaryBtn, primaryBtnURL, secondaryBtn, secondaryBtnURL, src, alt } =
Astro.props;
interface Props {
title: string;
subTitle?: string;
primaryBtn?: string;
primaryBtnURL?: string;
secondaryBtn?: string;
secondaryBtnURL?: string;
src?: any;
alt?: string;
rounded?: boolean;
}
const roundedClasses = Astro.props.rounded ? "rounded-xl" : null;
---
<section
class="mx-auto grid max-w-340 gap-4 px-4 py-14 sm:px-6 md:grid-cols-2 md:items-center md:gap-8 lg:px-8 2xl:max-w-full"
>
<div>
<h1
class="smooth-reveal block text-3xl font-bold tracking-tight text-balance text-neutral-800 sm:text-4xl lg:text-7xl lg:leading-tight dark:text-neutral-200"
>
<Fragment set:html={title} />
</h1>
{
subTitle && (
<p class="smooth-reveal mt-6 text-lg leading-relaxed text-pretty text-neutral-700 lg:w-4/5 dark:text-neutral-300">
{subTitle}
</p>
)
}
<div class="smooth-reveal mt-7 grid w-full gap-3 sm:inline-flex">
{primaryBtn && <PrimaryCTA title={primaryBtn} url={primaryBtnURL} />}
{secondaryBtn && <SecondaryCTA title={secondaryBtn} url={secondaryBtnURL} />}
</div>
</div>
<div class="smooth-reveal-fade hidden w-full md:block">
<div class="top-12 flex w-full justify-center overflow-hidden md:ml-4">
{
src && alt && (
<Image
src={src}
alt={alt}
class={`h-full w-[420px] scale-100 object-cover object-center ${roundedClasses}`}
draggable="false"
loading="eager"
format="webp"
quality="low"
widths={[840]}
disableBlur={true}
/>
)
}
</div>
</div>
</section>

View File

@@ -3,8 +3,8 @@ import { readSingleton } from '@directus/sdk';
import directus from '@lib/directus'; import directus from '@lib/directus';
import BaseLayout from '@layouts/BaseLayout.astro'; import BaseLayout from '@layouts/BaseLayout.astro';
import PrimaryCTA from '@components/ui/buttons/PrimaryCTA.astro'; import GoBackButton from '@/components/buttons/GoBackButton.astro';
import GoBack from '@/components/ui/buttons/GoBack.astro'; import GoHomeButton from '@/components/buttons/GoHomeButton.astro';
const global = await directus.request(readSingleton('site_global')); const global = await directus.request(readSingleton('site_global'));
--- ---
@@ -61,8 +61,8 @@ const global = await directus.request(readSingleton('site_global'));
<div <div
class="smooth-reveal mt-10 flex flex-col items-center justify-center gap-4 sm:flex-row" class="smooth-reveal mt-10 flex flex-col items-center justify-center gap-4 sm:flex-row"
> >
<GoBack title="Go Back" /> <GoBackButton/>
<PrimaryCTA title="Return Home" url={global.site_url} noArrow addHome /> <GoHomeButton url={global.site_url} />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -3,11 +3,11 @@ import { readSingleton } from '@directus/sdk';
import directus from '@lib/directus'; import directus from '@lib/directus';
import BaseLayout from '@layouts/BaseLayout.astro'; import BaseLayout from '@layouts/BaseLayout.astro';
import HeroSection from '@components/ui/sections/HeroSection.astro'; import HeroSection from '@components/sections/HeroSection.astro';
import Experience from '@components/ui/sections/Experience.astro'; import ExperienceSection from '@components/sections/ExperienceSection.astro';
import EducationSection from '@components/ui/sections/EducationSection.astro'; import EducationSection from '@components/sections/EducationSection.astro';
import ProjectSection from '@components/ui/sections/ProjectSection.astro'; import ProjectSection from '@components/sections/ProjectSection.astro';
import SkillsSlider from '@components/ui/sections/SkillsSlider.astro'; import SkillsSliderSection from '@components/sections/SkillsSliderSection.astro';
import portraitImg from '@images/portrait.avif'; import portraitImg from '@images/portrait.avif';
@@ -44,10 +44,10 @@ const global = await directus.request(readSingleton('site_global'));
<section class="mx-auto max-w-7xl px-4 py-10 sm:px-6 lg:px-8 lg:py-14"> <section class="mx-auto max-w-7xl px-4 py-10 sm:px-6 lg:px-8 lg:py-14">
<div class="flex flex-col gap-y-24 md:gap-y-32"> <div class="flex flex-col gap-y-24 md:gap-y-32">
<Experience className="smooth-reveal" /> <ExperienceSection className="smooth-reveal" />
<EducationSection className="smooth-reveal" /> <EducationSection className="smooth-reveal" />
<ProjectSection className="smooth-reveal" /> <ProjectSection className="smooth-reveal" />
<SkillsSlider className="smooth-reveal" /> <SkillsSliderSection className="smooth-reveal" />
</div> </div>
</section> </section>

View File

@@ -3,8 +3,8 @@ import { readSingleton } from '@directus/sdk';
import directus from '@lib/directus'; import directus from '@lib/directus';
import BaseLayout from '@layouts/BaseLayout.astro'; import BaseLayout from '@layouts/BaseLayout.astro';
import HeroSection from '@components/ui/sections/HeroSection.astro'; import HeroSection from '@components/sections/HeroSection.astro';
import ApplicationSection from '@components/ui/sections/ApplicationSection.astro'; import ApplicationSection from '@components/sections/ApplicationSection.astro';
import applicationImg from '@images/cedar_tree.png'; import applicationImg from '@images/cedar_tree.png';

View File

@@ -10,6 +10,7 @@ import { createHighlighter } from 'shiki';
import { getDirectusImageURL } from '@lib/directusFunctions'; import { getDirectusImageURL } from '@lib/directusFunctions';
import BaseLayout from '@layouts/BaseLayout.astro'; import BaseLayout from '@layouts/BaseLayout.astro';
import Image from '@components/ui/images/Image.astro'; import Image from '@components/ui/images/Image.astro';
import SocialShareButton from '@components/buttons/SocialShareButton.astro';
import { formatDateTime } from '@support/time'; import { formatDateTime } from '@support/time';
export async function getStaticPaths() { export async function getStaticPaths() {
@@ -162,6 +163,9 @@ const content = marked.parse(post.content);
)) ))
} }
</div> </div>
<SocialShareButton
pageTitle={post.title}
/>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -7,7 +7,7 @@ import directus from '@lib/directus';
import BaseLayout from '@layouts/BaseLayout.astro'; import BaseLayout from '@layouts/BaseLayout.astro';
import BlogSelectedArticles from '@components/blog/BlogSelectedArticles.astro'; import BlogSelectedArticles from '@components/blog/BlogSelectedArticles.astro';
import BlogRecentArticles from '@components/blog/BlogRecentArticles.astro'; import BlogRecentArticles from '@components/blog/BlogRecentArticles.astro';
import HeroSection from '@components/ui/sections/HeroSection.astro'; import HeroSection from '@components/sections/HeroSection.astro';
import blogImg from '@images/autumn_tree.png'; import blogImg from '@images/autumn_tree.png';
const global = await directus.request(readSingleton('site_global')); const global = await directus.request(readSingleton('site_global'));

View File

@@ -6,7 +6,7 @@ import directus from '@lib/directus';
import type { Post } from '@lib/directusTypes'; import type { Post } from '@lib/directusTypes';
import BaseLayout from '@layouts/BaseLayout.astro'; import BaseLayout from '@layouts/BaseLayout.astro';
import BlogCard from '@components/blog/BlogCard.astro'; import BlogCard from '@components/blog/BlogCard.astro';
import HeaderSection from '@components/ui/sections/HeaderSection.astro'; import HeaderSection from '@components/sections/HeaderSection.astro';
export async function getStaticPaths() { export async function getStaticPaths() {
const categories = await getCollection('categories'); const categories = await getCollection('categories');

View File

@@ -7,7 +7,7 @@ import type { Post } from '@lib/directusTypes';
import directus from '@lib/directus'; import directus from '@lib/directus';
import BaseLayout from '@layouts/BaseLayout.astro'; import BaseLayout from '@layouts/BaseLayout.astro';
import BlogCategoryCard from '@components/blog/BlogCategoryCard.astro'; import BlogCategoryCard from '@components/blog/BlogCategoryCard.astro';
import HeroSection from '@components/ui/sections/HeroSection.astro'; import HeroSection from '@components/sections/HeroSection.astro';
import { timeago } from '@support/time'; import { timeago } from '@support/time';
import categoryImg from '@images/autumn_bench.png'; import categoryImg from '@images/autumn_bench.png';

View File

@@ -3,11 +3,12 @@ import { readSingleton } from '@directus/sdk';
import directus from '@lib/directus'; import directus from '@lib/directus';
import BaseLayout from '@layouts/BaseLayout.astro'; import BaseLayout from '@layouts/BaseLayout.astro';
import HeroSection from '@components/ui/sections/HeroSection.astro'; import HeroSection from '@components/sections/HeroSection.astro';
import FeaturesSection from '@components/ui/sections/FeaturesSection.astro'; import FeaturesSection from '@components/sections/FeaturesSection.astro';
import WeatherSection from '@components/ui/sections/WeatherSection.astro'; import WeatherSection from '@components/sections/WeatherSection.astro';
import LatestPosts from '@components/ui/sections/LatestPosts.astro'; import LatestPostsSection from '@components/sections/LatestPostsSection.astro';
import HeroSectionAlt from '@components/ui/sections/HeroSectionAlt.astro'; import GiteaSection from '@components/sections/GiteaSection.astro';
import homeImg from '@images/autumn_mountain.png'; import homeImg from '@images/autumn_mountain.png';
const global = await directus.request(readSingleton('site_global')); const global = await directus.request(readSingleton('site_global'));
@@ -53,9 +54,9 @@ const weather = await directus.request(readSingleton('site_weather'));
> >
</WeatherSection> </WeatherSection>
<LatestPosts /> <LatestPostsSection />
<HeroSectionAlt <GiteaSection
title="Follow me on Gitea" title="Follow me on Gitea"
subTitle="I love open source and have my code availabile on my Gitea server." subTitle="I love open source and have my code availabile on my Gitea server."
url="https://gitea.alexlebens.dev" url="https://gitea.alexlebens.dev"

View File

@@ -1,5 +1,7 @@
@import 'tailwindcss'; @import 'tailwindcss';
@import 'preline/variants.css'; @import 'preline/variants.css';
@import './utilities.css';
@plugin '@tailwindcss/typography'; @plugin '@tailwindcss/typography';
@plugin '@tailwindcss/forms'; @plugin '@tailwindcss/forms';
@@ -10,11 +12,17 @@
/* Custom colors */ /* Custom colors */
@theme { @theme {
--color-midnight: #0c354d; --color-midnight: #0c354d;
--color-turquoise: #0da797; --color-ocean: #134e70;
--color-cobalt: #6c9cb0;
--color-steel: #4682b4; --color-steel: #4682b4;
--color-turquoise: #0da797;
--color-bermuda: #7fbab4; --color-bermuda: #7fbab4;
--color-desert: #f9deb2; --color-desert: #f9deb2;
--color-bronze: #9e7f5e; --color-bronze: #9e7f5e;
--color-gitea-primary: #609926; --color-gitea-primary: #609926;
--color-gitea-secondary: #4c7a33; --color-gitea-secondary: #4c7a33;
@@ -23,6 +31,7 @@
--color-header: light-dark(var(--color-neutral-800), var(--color-neutral-200)); --color-header: light-dark(var(--color-neutral-800), var(--color-neutral-200));
--color-primary: light-dark(var(--color-neutral-600), var(--color-neutral-200)); --color-primary: light-dark(var(--color-neutral-600), var(--color-neutral-200));
--color-primary-hover: light-dark(var(--color-neutral-800), var(--color-neutral-400));
--color-secondary: light-dark(var(--color-neutral-500), var(--color-neutral-400)); --color-secondary: light-dark(var(--color-neutral-500), var(--color-neutral-400));
} }
@@ -98,63 +107,6 @@
} }
} }
@utility card-base {
@apply rounded-xl transition-all duration-300
border border-neutral-100 dark:border-stone-500/20
bg-neutral-100/80 hover:bg-neutral-100 dark:bg-neutral-800/60 dark:hover:bg-neutral-800/90
shadow-xs hover:shadow-md dark:shadow-md dark:hover:shadow-lg
}
@utility card-hover-icon-color {
@apply transition-all duration-300
text-primary
group-hover:text-main
}
@utility card-hover-icon-scale {
@apply transition-all duration-300
drop-shadow-sm
group-hover:scale-110
}
@utility card-text-header {
@apply text-header
md:text-5xl
text-4xl
}
@utility card-text-header-minor {
@apply text-header
md:text-3xl
text-2xl
font-semibold
}
@utility card-text-header-description {
@apply text-primary
text-lg
text-pretty
}
@utility card-text-title {
@apply text-primary
font-bold
}
@utility card-text-description {
@apply text-secondary
}
@utility card-hover-text-title {
@apply transition-all duration-300
group-hover:text-main
}
@utility card-hover-text-gitea {
@apply transition-all duration-300
group-hover:text-gitea-primary
}
/* Content reveal animations */ /* Content reveal animations */
.smooth-reveal, .smooth-reveal,
.smooth-reveal-2, .smooth-reveal-2,

110
src/styles/utilities.css Normal file
View File

@@ -0,0 +1,110 @@
/* Button classes */
@utility button-base {
@apply transition-all duration-300
border border-transparent
shadow-sm hover:shadow-md dark:shadow-md dark:hover:shadow-lg
px-4 py-3
}
@utility button-base-hidden {
@apply transition-all duration-300
border border-transparent
hover:bg-neutral-100 dark:hover:bg-neutral-700
p-2
}
@utility button-hover-arrow {
@apply translate-y-px transition duration-300
group-hover:translate-x-1
h-3 w-3 md:h-5 md:w-5
}
@utility button-text-title {
@apply text-neutral-200 2xl:text-base
text-sm font-bold
}
@utility button-text-title-hidden {
@apply transition-all duration-300
text-neutral-600 group-hover:text-neutral-700 dark:text-neutral-400 dark:group-hover:text-neutral-300 2xl:text-base
text-sm font-medium
}
@utility button-bg-blue {
@apply transition-all duration-300
bg-cobalt hover:bg-steel dark:bg-steel dark:hover:bg-cobalt
}
@utility button-bg-teal {
@apply transition-all duration-300
bg-bermuda hover:bg-turquoise dark:bg-turquoise dark:hover:bg-bermuda
}
@utility button-bg-neutral {
@apply transition-all duration-300
border border-neutral-100 dark:border-stone-500/20
bg-neutral-100/80 hover:bg-neutral-100 dark:bg-neutral-800/60 dark:hover:bg-neutral-800/90
}
@utility button-bg-gitea {
@apply transition-all duration-300
bg-gitea-primary hover:bg-gitea-secondary dark:bg-gitea-secondary dark:hover:bg-gitea-primary
}
/* Card classes */
@utility card-base {
@apply rounded-xl
border border-neutral-100 dark:border-stone-500/20
bg-neutral-100/80 hover:bg-neutral-100 dark:bg-neutral-800/60 dark:hover:bg-neutral-800/90
shadow-xs hover:shadow-md dark:shadow-md dark:hover:shadow-lg
}
@utility card-hover-icon-color {
@apply transition-all duration-300
text-primary
group-hover:text-main
}
@utility card-hover-icon-scale {
@apply transition-all duration-300
drop-shadow-sm
group-hover:scale-110
}
@utility card-text-header {
@apply text-header
text-4xl md:text-5xl
font-bold leading-tight tracking-tight text-balance
}
@utility card-text-header-minor {
@apply text-header
md:text-3xl
text-2xl
font-semibold
}
@utility card-text-header-description {
@apply text-primary
text-lg
text-pretty leading-relaxed
}
@utility card-text-title {
@apply text-primary
font-bold
}
@utility card-hover-text-title {
@apply transition-all duration-300
group-hover:text-main
}
@utility card-hover-text-gitea {
@apply transition-all duration-300
group-hover:text-gitea-primary
}
@utility card-text-description {
@apply text-secondary
}