Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f79906fa62 | ||
| a2c1d23af6 | |||
| e3179b0480 | |||
| c4104a52d1 | |||
| 954112e30e | |||
| f35c73b028 | |||
| 5cf1cd30d8 | |||
| 787479e077 | |||
| 91c9a4bb91 | |||
| 220c29f4f7 | |||
| dfeb181a1d | |||
| 641c7cb33f | |||
| 93a53cab3d | |||
| a6c889f76a | |||
| 9319228ef6 | |||
| 148fe8eeff | |||
|
|
3651999a98 | ||
| 27df97522c | |||
| 8d1d47dc4f | |||
|
|
6fd9084b77 | ||
| 7b791dbb8a | |||
| 7b8fb380d4 | |||
|
|
a08fff1d77 | ||
| 6feb1a4f71 | |||
| 500d9e2ea0 | |||
| 70a94990e2 | |||
| d0f10986cd | |||
| 629403fdde | |||
|
|
cd1d8b439d | ||
| fb2e87749c | |||
| 43f0f7b324 | |||
| eab6e0c31d | |||
| 805cb28185 | |||
| 54c82a7f79 | |||
| 9a62f867f1 |
17
.pre-commit-config.yaml
Normal file
17
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v6.0.0
|
||||||
|
hooks:
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: check-merge-conflict
|
||||||
|
- id: check-json
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||||
|
rev: v4.0.0-alpha.11
|
||||||
|
hooks:
|
||||||
|
- id: prettier
|
||||||
|
types_or: [javascript, typescript, css, scss, html, json, yaml, markdown]
|
||||||
|
additional_dependencies:
|
||||||
|
- prettier
|
||||||
|
- prettier-plugin-astro
|
||||||
|
- prettier-plugin-tailwindcss
|
||||||
61
CHANGELOG.md
61
CHANGELOG.md
@@ -1,3 +1,64 @@
|
|||||||
|
# [3.13.0](https://gitea.alexlebens.dev/alexlebens/site-profile/compare/3.12.1...3.13.0) (2026-03-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* change execution mode ([a6c889f](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/a6c889f76a2e55e2efe8a19ffdacdb7327dd0116))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add and update pre-commit ([148fe8e](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/148fe8eeffeb377ce8006c5126625e7d338a4db7))
|
||||||
|
* add fallback ([787479e](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/787479e077e3608112f6ddad41d6d5e686c0166f))
|
||||||
|
* add fallback ([220c29f](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/220c29f4f766ff78967c8198e0e1605067404551))
|
||||||
|
* add fallback to run animations on switch ([954112e](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/954112e30e1ce912ca7098455555f675a49f9841))
|
||||||
|
* add semantic-release/npm ([91c9a4b](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/91c9a4bb91eea9baea5fadcebbc280f416555e23))
|
||||||
|
* change paths ([9319228](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/9319228ef6df0039dc8ee6ab88882f2e5e044d14))
|
||||||
|
* consolidate css into tailwind ([dfeb181](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/dfeb181a1d781d5bcdcf7240a67e594f3a170e50))
|
||||||
|
* downgrade to astro 5 ([f35c73b](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/f35c73b02873405b8ab350b6b5a8385339ff2329))
|
||||||
|
* move scripts to script folder ([641c7cb](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/641c7cb33feec65b185d307a7cf1c9879b884a03))
|
||||||
|
* refactor static paths and photoswipe on blog page, move script to base layout ([93a53ca](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/93a53cab3d6e1cb6395a60537d07de22686f21f6))
|
||||||
|
* remove react ([e3179b0](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/e3179b0480a56d173439fafddca7d75f96fb1b42))
|
||||||
|
* revert shiki css changes ([c4104a5](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/c4104a52d170cef2da0a087f7252caa5bc3a19db))
|
||||||
|
|
||||||
|
## [3.12.1](https://gitea.alexlebens.dev/alexlebens/site-profile/compare/3.12.0...3.12.1) (2026-03-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* handle script with swup ([8d1d47d](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/8d1d47dc4f09ecdd56d898e7d70b0c27f1b02e04))
|
||||||
|
|
||||||
|
# [3.12.0](https://gitea.alexlebens.dev/alexlebens/site-profile/compare/3.11.0...3.12.0) (2026-03-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* enable prerender pending resoltuion of server island fix ([7b8fb38](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/7b8fb380d4a4770ac695c74a4cbf7019d5f069e8))
|
||||||
|
|
||||||
|
# [3.11.0](https://gitea.alexlebens.dev/alexlebens/site-profile/compare/3.10.0...3.11.0) (2026-03-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* adjustment to spacing ([629403f](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/629403fddea28e86a7e3fc7f9a7cec5bc43ef027))
|
||||||
|
* improve category cards spacing, overflow, and add fade effect to edges ([d0f1098](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/d0f10986cddafffa7893a8751bd1a893dd672983))
|
||||||
|
* move script handling to use swup instead of astro transitions, move animations to baselayout ([500d9e2](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/500d9e2ea03c07bc819132b91c99e12cd8d5fc56))
|
||||||
|
* remove astro features that are replaced by swup ([70a9499](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/70a94990e2067891d68cbfb3831b07e3df346c4e))
|
||||||
|
|
||||||
|
# [3.10.0](https://gitea.alexlebens.dev/alexlebens/site-profile/compare/3.9.0...3.10.0) (2026-03-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* function needing paranthensis ([54c82a7](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/54c82a7f79a335f8dfbe41cbaed7e784a0d80aa7))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add scripts to clear and run from scratch ([805cb28](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/805cb281853edff06adaf4c62b7ae256a00bea3f))
|
||||||
|
* disable prerender pending resoltuion of server island fix ([eab6e0c](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/eab6e0c31dbd24470fa55409326d830a374f86cf))
|
||||||
|
* test placing weather section below posts ([43f0f7b](https://gitea.alexlebens.dev/alexlebens/site-profile/commit/43f0f7b3248cead38e8f9a6ff0d9e411483e440c))
|
||||||
|
|
||||||
# [3.9.0](https://gitea.alexlebens.dev/alexlebens/site-profile/compare/3.8.0...3.9.0) (2026-03-13)
|
# [3.9.0](https://gitea.alexlebens.dev/alexlebens/site-profile/compare/3.8.0...3.9.0) (2026-03-13)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,11 +21,10 @@ FROM ${REGISTRY}/bun:1.3.10-alpine3.22 AS runtime
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=prod-deps /app/node_modules /app/node_modules
|
COPY --from=prod-deps /app/node_modules /app/node_modules
|
||||||
COPY --from=build /app/dist /app/dist
|
COPY --from=build /app/dist /app/dist
|
||||||
COPY --from=builder /app/package.json ./
|
|
||||||
|
|
||||||
ARG APP_VERSION=latest
|
ARG APP_VERSION=latest
|
||||||
ARG APP_VERSION
|
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
ENV HOST=0.0.0.0
|
ENV HOST=0.0.0.0
|
||||||
ENV PORT=4321
|
ENV PORT=4321
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
|
|
||||||
import node from '@astrojs/node';
|
import node from '@astrojs/node';
|
||||||
import react from '@astrojs/react';
|
|
||||||
import sitemap from '@astrojs/sitemap';
|
import sitemap from '@astrojs/sitemap';
|
||||||
|
|
||||||
import tailwindcss from '@tailwindcss/vite';
|
import tailwindcss from '@tailwindcss/vite';
|
||||||
import icon from 'astro-icon';
|
import icon from 'astro-icon';
|
||||||
import swup from '@swup/astro';
|
import swup from '@swup/astro';
|
||||||
|
|
||||||
import { getSiteURL } from './src/support/url';
|
import { getSiteURL } from './src/scripts/url';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: getSiteURL(),
|
site: getSiteURL(),
|
||||||
@@ -24,10 +23,7 @@ export default defineConfig({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
prefetch: true,
|
|
||||||
|
|
||||||
integrations: [
|
integrations: [
|
||||||
react(),
|
|
||||||
sitemap(),
|
sitemap(),
|
||||||
icon({
|
icon({
|
||||||
include: {
|
include: {
|
||||||
@@ -45,17 +41,16 @@ export default defineConfig({
|
|||||||
}),
|
}),
|
||||||
(await import('@playform/compress')).default({
|
(await import('@playform/compress')).default({
|
||||||
CSS: true,
|
CSS: true,
|
||||||
JavaScript: true,
|
JavaScript: false,
|
||||||
HTML: {
|
HTML: {
|
||||||
'html-minifier-terser': {
|
'html-minifier-terser': {
|
||||||
collapseWhitespace: true,
|
collapseWhitespace: true,
|
||||||
minifyCSS: false,
|
minifyCSS: false,
|
||||||
minifyJS: true,
|
minifyJS: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Image: false,
|
Image: false,
|
||||||
SVG: true,
|
SVG: true,
|
||||||
Logger: 2,
|
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -76,13 +71,4 @@ export default defineConfig({
|
|||||||
adapter: node({
|
adapter: node({
|
||||||
mode: 'standalone',
|
mode: 'standalone',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
build: {
|
|
||||||
// Specifies the directory in the build output where Astro-generated assets (bundled JS and CSS for example) should live.
|
|
||||||
// see https://docs.astro.build/en/reference/configuration-reference/#buildassets
|
|
||||||
assets: 'assets',
|
|
||||||
// see https://docs.astro.build/en/reference/configuration-reference/#buildassetsprefix
|
|
||||||
assetsPrefix:
|
|
||||||
!!import.meta.env.S3_ENABLE || !!process.env.S3_ENABLE ? 'https://digitalocean.com' : '',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
23
package.json
23
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "site-profile",
|
"name": "site-profile",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "3.9.0",
|
"version": "3.13.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",
|
||||||
@@ -19,8 +19,10 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
|
"full-dev": "rm -rf dist node_modules .astro && bun install && astro build && astro dev",
|
||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
"preview": "astro preview",
|
"preview": "astro preview",
|
||||||
|
"full-preview": "rm -rf dist node_modules .astro && bun install && astro build && astro preview",
|
||||||
"astro": "astro",
|
"astro": "astro",
|
||||||
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,md,mdx,astro}\"",
|
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,md,mdx,astro}\"",
|
||||||
"lint": "eslint \"src/**/*.{js,ts,jsx,tsx,astro}\"",
|
"lint": "eslint \"src/**/*.{js,ts,jsx,tsx,astro}\"",
|
||||||
@@ -28,22 +30,20 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/check": "^0.9.7",
|
"@astrojs/check": "^0.9.7",
|
||||||
"@astrojs/node": "^10.0.1",
|
"@astrojs/node": "^9.5.5",
|
||||||
"@astrojs/react": "^5.0.0",
|
|
||||||
"@astrojs/rss": "^4.0.17",
|
"@astrojs/rss": "^4.0.17",
|
||||||
"@astrojs/sitemap": "^3.7.1",
|
"@astrojs/sitemap": "^3.7.1",
|
||||||
"@directus/sdk": "^21.2.0",
|
"@directus/sdk": "^21.2.0",
|
||||||
"@giscus/react": "^3.1.0",
|
|
||||||
"@iconify-json/mdi": "^1.2.3",
|
"@iconify-json/mdi": "^1.2.3",
|
||||||
"@iconify-json/pajamas": "^1.2.15",
|
"@iconify-json/pajamas": "^1.2.15",
|
||||||
"@iconify-json/simple-icons": "^1.2.73",
|
"@iconify-json/simple-icons": "^1.2.74",
|
||||||
"@playform/compress": "^0.2.1",
|
"@playform/compress": "^0.2.2",
|
||||||
"@swup/astro": "^1.8.0",
|
"@swup/astro": "^1.8.0",
|
||||||
"@tailwindcss/postcss": "^4.2.1",
|
"@tailwindcss/postcss": "^4.2.1",
|
||||||
"@tailwindcss/vite": "^4.2.1",
|
"@tailwindcss/vite": "^4.2.1",
|
||||||
"@types/react": "^19.2.14",
|
|
||||||
"@types/unist": "^3.0.3",
|
"@types/unist": "^3.0.3",
|
||||||
"astro": "^6.0.4",
|
"astro": "^5.18.1",
|
||||||
|
"astro-compress": "^2.4.0",
|
||||||
"astro-icon": "^1.1.5",
|
"astro-icon": "^1.1.5",
|
||||||
"dayjs": "^1.11.20",
|
"dayjs": "^1.11.20",
|
||||||
"markdown-it": "^14.1.1",
|
"markdown-it": "^14.1.1",
|
||||||
@@ -52,8 +52,6 @@
|
|||||||
"mdast-util-to-string": "^4.0.0",
|
"mdast-util-to-string": "^4.0.0",
|
||||||
"photoswipe": "^5.4.4",
|
"photoswipe": "^5.4.4",
|
||||||
"preline": "^4.1.2",
|
"preline": "^4.1.2",
|
||||||
"react": "^19.2.4",
|
|
||||||
"react-dom": "^19.2.4",
|
|
||||||
"reading-time": "^1.5.0",
|
"reading-time": "^1.5.0",
|
||||||
"sharp": "^0.34.5",
|
"sharp": "^0.34.5",
|
||||||
"sharp-ico": "^0.1.5",
|
"sharp-ico": "^0.1.5",
|
||||||
@@ -62,11 +60,11 @@
|
|||||||
"ultrahtml": "^1.6.0"
|
"ultrahtml": "^1.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint-react/eslint-plugin": "^2.13.0",
|
|
||||||
"@saithodev/semantic-release-gitea": "^2.1.0",
|
"@saithodev/semantic-release-gitea": "^2.1.0",
|
||||||
"@semantic-release/changelog": "^6.0.3",
|
"@semantic-release/changelog": "^6.0.3",
|
||||||
"@semantic-release/commit-analyzer": "^13.0.1",
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
||||||
"@semantic-release/git": "^10.0.1",
|
"@semantic-release/git": "^10.0.1",
|
||||||
|
"@semantic-release/npm": "^13.1.5",
|
||||||
"@semantic-release/release-notes-generator": "^14.1.0",
|
"@semantic-release/release-notes-generator": "^14.1.0",
|
||||||
"@tailwindcss/forms": "^0.5.11",
|
"@tailwindcss/forms": "^0.5.11",
|
||||||
"@tailwindcss/typography": "^0.5.19",
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
@@ -75,9 +73,6 @@
|
|||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-astro": "^1.6.0",
|
"eslint-plugin-astro": "^1.6.0",
|
||||||
"eslint-plugin-format": "^2.0.1",
|
"eslint-plugin-format": "^2.0.1",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
|
||||||
"eslint-plugin-react-hooks": "^7.0.1",
|
|
||||||
"eslint-plugin-react-refresh": "^0.5.2",
|
|
||||||
"prettier": "^3.8.1",
|
"prettier": "^3.8.1",
|
||||||
"prettier-plugin-astro": "^0.14.1",
|
"prettier-plugin-astro": "^0.14.1",
|
||||||
"prettier-plugin-tailwindcss": "^0.7.2",
|
"prettier-plugin-tailwindcss": "^0.7.2",
|
||||||
|
|||||||
@@ -95,5 +95,3 @@ const currentPath = pathname.slice(1);
|
|||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<script is:inline src="/vendor/preline/collapse2.1.0.min.js"></script>
|
|
||||||
|
|||||||
@@ -43,106 +43,93 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
const applyTheme = () => {
|
(() => {
|
||||||
const isDark =
|
const isDark =
|
||||||
localStorage.theme === 'dark' ||
|
localStorage.theme === 'dark' ||
|
||||||
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||||
document.documentElement.classList.toggle('dark', isDark);
|
document.documentElement.classList.toggle('dark', isDark);
|
||||||
};
|
})();
|
||||||
|
|
||||||
applyTheme();
|
|
||||||
|
|
||||||
document.addEventListener('astro:after-swap', applyTheme);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function setupThemeToggle() {
|
if (!document.querySelector('.theme-switch-overlay')) {
|
||||||
const themeToggles = document.querySelectorAll('[data-theme-toggle]');
|
const overlay = document.createElement('div');
|
||||||
|
overlay.className = 'theme-switch-overlay fixed inset-0 pointer-events-none z-50';
|
||||||
// Create theme switch overlay element
|
overlay.style.opacity = '0';
|
||||||
if (!document.querySelector('.theme-switch-overlay')) {
|
overlay.style.transition = 'opacity 0.15s ease-out';
|
||||||
const overlay = document.createElement('div');
|
document.body.appendChild(overlay);
|
||||||
overlay.className = 'theme-switch-overlay fixed inset-0 pointer-events-none z-50';
|
|
||||||
overlay.style.opacity = '0';
|
|
||||||
overlay.style.transition = 'opacity 0.15s ease-out';
|
|
||||||
document.body.appendChild(overlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
themeToggles.forEach((toggle) => {
|
|
||||||
['click', 'touchend'].forEach((eventType) => {
|
|
||||||
toggle.addEventListener(
|
|
||||||
eventType,
|
|
||||||
(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
// Get click/touch position for radial animation
|
|
||||||
let x, y;
|
|
||||||
if (e.type === 'touchend' && e.changedTouches && e.changedTouches[0]) {
|
|
||||||
const rect = toggle.getBoundingClientRect();
|
|
||||||
x = e.changedTouches[0].clientX - rect.left;
|
|
||||||
y = e.changedTouches[0].clientY - rect.top;
|
|
||||||
} else {
|
|
||||||
const rect = toggle.getBoundingClientRect();
|
|
||||||
x = e.clientX - rect.left;
|
|
||||||
y = e.clientY - rect.top;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.documentElement.style.setProperty('--x', `${x}px`);
|
|
||||||
document.documentElement.style.setProperty('--y', `${y}px`);
|
|
||||||
|
|
||||||
const overlay = document.querySelector('.theme-switch-overlay');
|
|
||||||
const isDark = document.documentElement.classList.contains('dark');
|
|
||||||
const newTheme = isDark ? 'light' : 'dark';
|
|
||||||
|
|
||||||
// Show overlay during transition
|
|
||||||
if (overlay) {
|
|
||||||
overlay.style.backgroundColor =
|
|
||||||
newTheme === 'dark' ? 'rgba(24, 24, 27, 0.3)' : 'rgba(255, 255, 255, 0.3)';
|
|
||||||
overlay.style.opacity = '1';
|
|
||||||
}
|
|
||||||
|
|
||||||
document.documentElement.classList.add('theme-switching');
|
|
||||||
|
|
||||||
// Force a reflow to ensure all elements update
|
|
||||||
document.body.offsetHeight;
|
|
||||||
|
|
||||||
// Toggle dark mode with a slight delay to allow overlay to appear
|
|
||||||
setTimeout(() => {
|
|
||||||
if (isDark) {
|
|
||||||
document.documentElement.classList.remove('dark');
|
|
||||||
} else {
|
|
||||||
document.documentElement.classList.add('dark');
|
|
||||||
}
|
|
||||||
|
|
||||||
localStorage.setItem('theme', newTheme);
|
|
||||||
document.dispatchEvent(
|
|
||||||
new CustomEvent('themeChanged', {
|
|
||||||
detail: { isDark: newTheme === 'dark' },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Force another reflow to ensure all elements update
|
|
||||||
document.body.offsetHeight;
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
if (overlay) {
|
|
||||||
overlay.style.opacity = '0';
|
|
||||||
}
|
|
||||||
document.documentElement.classList.remove('theme-switching');
|
|
||||||
}, 150);
|
|
||||||
}, 50);
|
|
||||||
},
|
|
||||||
{ passive: false }
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
['click', 'touchend'].forEach((eventType) => {
|
||||||
|
document.addEventListener(
|
||||||
|
eventType,
|
||||||
|
(e) => {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
const toggle = target.closest('[data-theme-toggle]');
|
||||||
|
|
||||||
// Run setup on load
|
if (!toggle) return;
|
||||||
document.addEventListener('astro:page-load', setupThemeToggle);
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let x, y;
|
||||||
|
const rect = toggle.getBoundingClientRect();
|
||||||
|
|
||||||
|
if (eventType === 'touchend') {
|
||||||
|
const touchEvent = e as TouchEvent;
|
||||||
|
if (touchEvent.changedTouches && touchEvent.changedTouches[0]) {
|
||||||
|
x = touchEvent.changedTouches[0].clientX - rect.left;
|
||||||
|
y = touchEvent.changedTouches[0].clientY - rect.top;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const mouseEvent = e as MouseEvent;
|
||||||
|
x = mouseEvent.clientX - rect.left;
|
||||||
|
y = mouseEvent.clientY - rect.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.documentElement.style.setProperty('--x', `${x}px`);
|
||||||
|
document.documentElement.style.setProperty('--y', `${y}px`);
|
||||||
|
|
||||||
|
const overlay = document.querySelector('.theme-switch-overlay') as HTMLElement;
|
||||||
|
const isDark = document.documentElement.classList.contains('dark');
|
||||||
|
const newTheme = isDark ? 'light' : 'dark';
|
||||||
|
|
||||||
|
if (overlay) {
|
||||||
|
overlay.style.backgroundColor =
|
||||||
|
newTheme === 'dark' ? 'rgba(24, 24, 27, 0.3)' : 'rgba(255, 255, 255, 0.3)';
|
||||||
|
overlay.style.opacity = '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
document.documentElement.classList.add('theme-switching');
|
||||||
|
|
||||||
|
document.body.offsetHeight;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (isDark) {
|
||||||
|
document.documentElement.classList.remove('dark');
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem('theme', newTheme);
|
||||||
|
document.dispatchEvent(
|
||||||
|
new CustomEvent('themeChanged', {
|
||||||
|
detail: { isDark: newTheme === 'dark' },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
document.body.offsetHeight;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (overlay) {
|
||||||
|
overlay.style.opacity = '0';
|
||||||
|
}
|
||||||
|
document.documentElement.classList.remove('theme-switching');
|
||||||
|
}, 150);
|
||||||
|
}, 50);
|
||||||
|
},
|
||||||
|
{ passive: false }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// Also run on page visibility change to ensure theme is consistent
|
|
||||||
document.addEventListener('visibilitychange', () => {
|
document.addEventListener('visibilitychange', () => {
|
||||||
if (document.visibilityState === 'visible') {
|
if (document.visibilityState === 'visible') {
|
||||||
const currentTheme = localStorage.getItem('theme');
|
const currentTheme = localStorage.getItem('theme');
|
||||||
@@ -154,7 +141,6 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for system preference changes
|
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', ({ matches }) => {
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', ({ matches }) => {
|
||||||
if (!localStorage.getItem('theme')) {
|
if (!localStorage.getItem('theme')) {
|
||||||
if (matches) {
|
if (matches) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Image } from 'astro:assets';
|
|||||||
import type { Post } from '@lib/directusTypes';
|
import type { Post } from '@lib/directusTypes';
|
||||||
|
|
||||||
import PostMetadataSnippet from '@/components/snippets/PostMetadataSnippet.astro';
|
import PostMetadataSnippet from '@/components/snippets/PostMetadataSnippet.astro';
|
||||||
import { getDirectusImageURL } from '@/support/url';
|
import { getDirectusImageURL } from '@/scripts/url';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
post: Post;
|
post: Post;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
import Logo from '@components/images/Logo.astro';
|
import Logo from '@components/images/Logo.astro';
|
||||||
import { getDirectusImageURL } from '@/support/url';
|
import { getDirectusImageURL } from '@/scripts/url';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
slug: string;
|
slug: string;
|
||||||
@@ -22,8 +22,8 @@ const { slug, title, description, logoLight, logoDark, count, publishDate } = As
|
|||||||
data-astro-prefetch
|
data-astro-prefetch
|
||||||
>
|
>
|
||||||
<div class="relative grow overflow-hidden">
|
<div class="relative grow overflow-hidden">
|
||||||
<div class="absolute inset-1 flex flex-col p-3 md:p-4 lg:p-5">
|
<div class="mask-fade-edges absolute inset-1 flex flex-col p-3 md:p-4 lg:p-5">
|
||||||
<div class="flex flex-row items-center mb-4">
|
<div class="flex flex-row items-center mb-4 shrink-0">
|
||||||
<div class="card-hover-icon-scale shrink-0 mr-3">
|
<div class="card-hover-icon-scale shrink-0 mr-3">
|
||||||
<Logo
|
<Logo
|
||||||
srcLight={getDirectusImageURL(logoLight)}
|
srcLight={getDirectusImageURL(logoLight)}
|
||||||
@@ -35,12 +35,12 @@ const { slug, title, description, logoLight, logoDark, count, publishDate } = As
|
|||||||
{title}
|
{title}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="overflow-hidden">
|
||||||
<p class="card-text-description mb-4">
|
<p class="card-text-description line-clamp-3">
|
||||||
{description}
|
{description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-text-description flex items-center justify-between text-xs mt-auto pt-1 md:pt-2">
|
<div class="card-text-description flex shrink-0 items-center justify-between text-xs mt-auto pt-1 md:pt-2">
|
||||||
<span class="inline-flex items-center">
|
<span class="inline-flex items-center">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { Icon } from 'astro-icon/components';
|
import { Icon } from 'astro-icon/components';
|
||||||
|
|
||||||
import Logo from '@components/images/Logo.astro';
|
import Logo from '@components/images/Logo.astro';
|
||||||
import { getDirectusImageURL } from '@/support/url';
|
import { getDirectusImageURL } from '@/scripts/url';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
topic: string;
|
topic: string;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { Post } from '@lib/directusTypes';
|
|||||||
|
|
||||||
import ReadMoreButton from '@components/buttons/ReadMoreButton.astro';
|
import ReadMoreButton from '@components/buttons/ReadMoreButton.astro';
|
||||||
import PostMetadataSnippet from '@/components/snippets/PostMetadataSnippet.astro';
|
import PostMetadataSnippet from '@/components/snippets/PostMetadataSnippet.astro';
|
||||||
import { getDirectusImageURL } from '@/support/url';
|
import { getDirectusImageURL } from '@/scripts/url';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
post: Post;
|
post: Post;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { Post } from '@lib/directusTypes';
|
|||||||
|
|
||||||
import ReadMoreButton from '@components/buttons/ReadMoreButton.astro';
|
import ReadMoreButton from '@components/buttons/ReadMoreButton.astro';
|
||||||
import PostMetadataSnippet from '@/components/snippets/PostMetadataSnippet.astro';
|
import PostMetadataSnippet from '@/components/snippets/PostMetadataSnippet.astro';
|
||||||
import { getDirectusImageURL } from '@/support/url';
|
import { getDirectusImageURL } from '@/scripts/url';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
post: Post;
|
post: Post;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
import Logo from '@components/images/Logo.astro';
|
import Logo from '@components/images/Logo.astro';
|
||||||
import { getDirectusImageURL } from '@/support/url';
|
import { getDirectusImageURL } from '@/scripts/url';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
url: string;
|
url: string;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { Icon } from 'astro-icon/components';
|
import { Icon } from 'astro-icon/components';
|
||||||
import { Image } from 'astro:assets';
|
import { Image } from 'astro:assets';
|
||||||
|
|
||||||
import { getDirectusImageURL } from '@/support/url';
|
import { getDirectusImageURL } from '@/scripts/url';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
import ReadMoreButton from '@components/buttons/ReadMoreButton.astro';
|
import ReadMoreButton from '@components/buttons/ReadMoreButton.astro';
|
||||||
import Logo from '@components/images/Logo.astro';
|
import Logo from '@components/images/Logo.astro';
|
||||||
import { getDirectusImageURL } from '@/support/url';
|
import { getDirectusImageURL } from '@/scripts/url';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type { Post } from '@lib/directusTypes';
|
|||||||
import CategoryCard from '@components/cards/CategoryCard.astro';
|
import CategoryCard from '@components/cards/CategoryCard.astro';
|
||||||
import LargeCategoryCard from '@components/cards/LargeCategoryCard.astro';
|
import LargeCategoryCard from '@components/cards/LargeCategoryCard.astro';
|
||||||
import directus from '@lib/directus';
|
import directus from '@lib/directus';
|
||||||
import { formatFromNow } from '@support/time';
|
import { formatFromNow } from '@/scripts/time';
|
||||||
|
|
||||||
const global = await directus.request(readSingleton('site_global'));
|
const global = await directus.request(readSingleton('site_global'));
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
import GoLinkPrimaryButton from '@components/buttons/GoLinkPrimaryButton.astro';
|
import GoLinkPrimaryButton from '@components/buttons/GoLinkPrimaryButton.astro';
|
||||||
import Logo from '@components/images/Logo.astro';
|
import Logo from '@components/images/Logo.astro';
|
||||||
import { getDirectusImageURL } from '@/support/url';
|
import { getDirectusImageURL } from '@/scripts/url';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type { HeaderImage } from '@lib/directusTypes';
|
|||||||
import GoLinkPrimaryButton from '@components/buttons/GoLinkPrimaryButton.astro';
|
import GoLinkPrimaryButton from '@components/buttons/GoLinkPrimaryButton.astro';
|
||||||
import GoLinkSecondaryButton from '@components/buttons/GoLinkSecondaryButton.astro';
|
import GoLinkSecondaryButton from '@components/buttons/GoLinkSecondaryButton.astro';
|
||||||
import directus from '@lib/directus';
|
import directus from '@lib/directus';
|
||||||
import { getDirectusImageURL } from '@/support/url';
|
import { getDirectusImageURL } from '@/scripts/url';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -82,7 +82,7 @@ const images = await Promise.all(imagesData.map(async (img) => ({
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('astro:page-load', () => {
|
function initHeroImage() {
|
||||||
const container = document.getElementById('hero-image-container');
|
const container = document.getElementById('hero-image-container');
|
||||||
if (container) {
|
if (container) {
|
||||||
const images = container.querySelectorAll('.hero-image');
|
const images = container.querySelectorAll('.hero-image');
|
||||||
@@ -96,5 +96,13 @@ const images = await Promise.all(imagesData.map(async (img) => ({
|
|||||||
images[randomIndex].classList.add('flex');
|
images[randomIndex].classList.add('flex');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', initHeroImage);
|
||||||
|
} else {
|
||||||
|
initHeroImage();
|
||||||
|
}
|
||||||
|
if ((window as any).swup) {
|
||||||
|
(window as any).swup.hooks.on('page:view', initHeroImage);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const global = await directus.request(readSingleton('site_global'));
|
|||||||
const { posts, title, subTitle } = Astro.props;
|
const { posts, title, subTitle } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="max-w-340 2xl:max-w-full px-4 sm:px-6 lg:px-8 py-10 lg:py-14 mx-auto mb-2 md:mb-8">
|
<section class="max-w-340 2xl:max-w-full px-4 sm:px-6 lg:px-8 py-10 lg:py-14 mx-auto mb-2 md:mb-4">
|
||||||
<div class="text-center max-w-2xl mx-auto mb-10 lg:mb-14">
|
<div class="text-center max-w-2xl mx-auto mb-10 lg:mb-14">
|
||||||
<h1 class="smooth-reveal card-text-header block">
|
<h1 class="smooth-reveal card-text-header block">
|
||||||
{title}
|
{title}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ interface Props {
|
|||||||
const { posts } = Astro.props;
|
const { posts } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="smooth-reveal flex flex-col gap-4 md:mb-20">
|
<section class="smooth-reveal-cards flex flex-col gap-4 md:mb-20">
|
||||||
{posts.map((post, index) => index % 2 === 0 ? (
|
{posts.map((post, index) => index % 2 === 0 ? (
|
||||||
<LargeBlogLeftCard post={post}/>
|
<LargeBlogLeftCard post={post}/>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
---
|
---
|
||||||
import WeatherCard from '@components/cards/WeatherCard.astro';
|
import WeatherCard from '@components/cards/WeatherCard.astro';
|
||||||
import { getFiveDayForecast } from '@support/weather';
|
import { getFiveDayForecast } from '@/scripts/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;
|
||||||
|
|
||||||
const { forecastDays, error } = await getFiveDayForecast(latitude, longitude, timezone);
|
const { forecastDays, error } = await getFiveDayForecast(latitude, longitude, timezone);
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="max-w-340 2xl:max-w-fullpx-4 sm:px-6 lg:px-8 py-10 lg:py-14 mx-auto mb-2 md:mb-8">
|
<section class="max-w-340 2xl:max-w-full px-4 sm:px-6 lg:px-8 py-10 lg:py-14 mx-auto mb-2 md:mb-8">
|
||||||
<div class="text-center max-w-2xl mx-auto mb-10 lg:mb-14">
|
<div class="text-center max-w-2xl mx-auto mb-10 lg:mb-14">
|
||||||
<h1 class="smooth-reveal card-text-header block">
|
<h1 class="smooth-reveal card-text-header block">
|
||||||
Weather in my Area
|
Weather in my Area
|
||||||
@@ -19,8 +19,12 @@ const { forecastDays, error } = await getFiveDayForecast(latitude, longitude, ti
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{error ? (
|
{error ? (
|
||||||
<div class="card-base p-10 text-accent text-center">
|
<div class="smooth-reveal flex flex-wrap justify-center gap-4 lg:gap-6">
|
||||||
Sorry, {error.toLowerCase}
|
<div class="card-base flex flex-col justify-center items-center p-10 w-48 h-56">
|
||||||
|
<h1 class="card-text-header block text-accent text-center">
|
||||||
|
Sorry, {error.toLowerCase()}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div class="flex flex-wrap justify-center gap-4 lg:gap-6">
|
<div class="flex flex-wrap justify-center gap-4 lg:gap-6">
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import getReadingTime from 'reading-time';
|
|||||||
import type { Post } from '@lib/directusTypes';
|
import type { Post } from '@lib/directusTypes';
|
||||||
|
|
||||||
import Logo from '@components/images/Logo.astro';
|
import Logo from '@components/images/Logo.astro';
|
||||||
import { formatShortDate, formatDate } from '@support/time';
|
import { formatShortDate, formatDate } from '@/scripts/time';
|
||||||
import { getDirectusImageURL } from '@/support/url';
|
import { getDirectusImageURL } from '@/scripts/url';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
post: Post;
|
post: Post;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
---
|
---
|
||||||
import { ClientRouter } from 'astro:transitions';
|
|
||||||
import { readSingleton } from '@directus/sdk';
|
import { readSingleton } from '@directus/sdk';
|
||||||
|
|
||||||
import BaseHead from '@components/BaseHead.astro';
|
import BaseHead from '@components/BaseHead.astro';
|
||||||
@@ -39,8 +38,7 @@ const normalizeTitle = !title ? global.name : `${title} | ${global.name}`;
|
|||||||
structuredData={structuredData}
|
structuredData={structuredData}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ClientRouter fallback="swap" />
|
<!-- Set Theme -->
|
||||||
|
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
const theme = (() => {
|
const theme = (() => {
|
||||||
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
|
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
|
||||||
@@ -60,6 +58,12 @@ const normalizeTitle = !title ? global.name : `${title} | ${global.name}`;
|
|||||||
window.localStorage.setItem('theme', theme);
|
window.localStorage.setItem('theme', theme);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- Preline -->
|
||||||
|
<script
|
||||||
|
src="/vendor/preline/collapse2.1.0.min.js"
|
||||||
|
is:inline
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Rybbit Tracking Snippet -->
|
<!-- Rybbit Tracking Snippet -->
|
||||||
<script
|
<script
|
||||||
src="https://rybbit.alexlebens.dev/api/script.js"
|
src="https://rybbit.alexlebens.dev/api/script.js"
|
||||||
@@ -97,14 +101,47 @@ const normalizeTitle = !title ? global.name : `${title} | ${global.name}`;
|
|||||||
</html>
|
</html>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { initPhotoSwipe } from '@/scripts/photoswipe';
|
||||||
|
import { animateContent } from '@/scripts/animations';
|
||||||
|
|
||||||
const resetScroll = () => {
|
const resetScroll = () => {
|
||||||
const scrollContainer = document.getElementById('reset-scroll');
|
const scrollContainer = document.getElementById('reset-scroll');
|
||||||
if (scrollContainer) {
|
if (scrollContainer) scrollContainer.scrollTop = 0;
|
||||||
scrollContainer.scrollTop = 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
resetScroll();
|
resetScroll();
|
||||||
document.addEventListener('astro:after-swap', resetScroll);
|
initPhotoSwipe();
|
||||||
|
animateContent();
|
||||||
|
|
||||||
|
const observer = new MutationObserver((mutations) => {
|
||||||
|
const hasNewNodes = mutations.some(
|
||||||
|
(mutation) => mutation.type === 'childList' && mutation.addedNodes.length > 0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasNewNodes) {
|
||||||
|
animateContent();
|
||||||
|
if (typeof (window as any).HSStaticMethods !== 'undefined') {
|
||||||
|
(window as any).HSStaticMethods.autoInit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const targetNode = document.getElementById('reset-scroll');
|
||||||
|
if (targetNode) {
|
||||||
|
observer.observe(targetNode, { childList: true, subtree: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('swup:page:view', () => {
|
||||||
|
resetScroll();
|
||||||
|
initPhotoSwipe();
|
||||||
|
animateContent();
|
||||||
|
if (typeof (window as any).HSStaticMethods !== 'undefined') {
|
||||||
|
(window as any).HSStaticMethods.autoInit();
|
||||||
|
}
|
||||||
|
if (typeof (window as any).rybbit === 'function') {
|
||||||
|
(window as any).rybbit('trackPageview');
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -133,6 +170,7 @@ const normalizeTitle = !title ? global.name : `${title} | ${global.name}`;
|
|||||||
/* Background that creates the "glimmer" effect */
|
/* Background that creates the "glimmer" effect */
|
||||||
.bg {
|
.bg {
|
||||||
animation: slide 25s ease-in-out infinite alternate;
|
animation: slide 25s ease-in-out infinite alternate;
|
||||||
|
will-change: transform;
|
||||||
background-image: linear-gradient(-55deg, var(--bg-primary) 33.3%, var(--bg-secondary) 33.3%, var(--bg-secondary) 66.6%, var(--bg-tertiary) 66.6%);
|
background-image: linear-gradient(-55deg, var(--bg-primary) 33.3%, var(--bg-secondary) 33.3%, var(--bg-secondary) 66.6%, var(--bg-tertiary) 66.6%);
|
||||||
filter: blur(40px);
|
filter: blur(40px);
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import type {
|
|||||||
Skill,
|
Skill,
|
||||||
} from '@lib/directusTypes';
|
} from '@lib/directusTypes';
|
||||||
|
|
||||||
import { getDirectusURL } from '@/support/url';
|
import { getDirectusURL } from '@/scripts/url';
|
||||||
|
|
||||||
type Schema = {
|
type Schema = {
|
||||||
site_global: Global;
|
site_global: Global;
|
||||||
|
|||||||
@@ -86,24 +86,6 @@ const global = await directus.request(readSingleton('site_global'));
|
|||||||
const randomFact = funFacts[Math.floor(Math.random() * funFacts.length)];
|
const randomFact = funFacts[Math.floor(Math.random() * funFacts.length)];
|
||||||
funFactElement.textContent = randomFact;
|
funFactElement.textContent = randomFact;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add smooth reveal animations for content after loading
|
|
||||||
document.addEventListener('astro:page-load', () => {
|
|
||||||
const animateContent = () => {
|
|
||||||
// Animate group 1
|
|
||||||
const smoothReveal = document.querySelectorAll('.smooth-reveal');
|
|
||||||
smoothReveal.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
100 + index * 150
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
animateContent();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -47,33 +47,3 @@ const global = await directus.request(readSingleton('site_global'));
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
<script>
|
|
||||||
// Add smooth reveal animations for content after loading
|
|
||||||
document.addEventListener('astro:page-load', () => {
|
|
||||||
const animateContent = () => {
|
|
||||||
const smoothReveal = document.querySelectorAll('.smooth-reveal');
|
|
||||||
smoothReveal.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
50 + index * 100
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate with just fade in with staggered delay
|
|
||||||
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
|
|
||||||
smoothRevealFade.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal-fade');
|
|
||||||
},
|
|
||||||
100 + index * 250
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
animateContent();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -52,56 +52,3 @@ const posts = await directus.request(
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
<script>
|
|
||||||
// Add smooth reveal animations for content after loading
|
|
||||||
document.addEventListener('astro:page-load', () => {
|
|
||||||
const animateContent = () => {
|
|
||||||
// Animate group 1
|
|
||||||
const smoothReveal = document.querySelectorAll('.smooth-reveal');
|
|
||||||
smoothReveal.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
50 + index * 100
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate group 2
|
|
||||||
const smoothReveal2 = document.querySelectorAll('.smooth-reveal-2');
|
|
||||||
smoothReveal2.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
200 + index * 150
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate topic cards with staggered delay
|
|
||||||
const smoothRevealCards = document.querySelectorAll('.smooth-reveal-cards');
|
|
||||||
smoothRevealCards.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
500 + index * 100
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate with just fade in with staggered delay
|
|
||||||
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
|
|
||||||
smoothRevealFade.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal-fade');
|
|
||||||
},
|
|
||||||
100 + index * 250
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
animateContent();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -37,23 +37,3 @@ const global = await directus.request(readSingleton('site_global'));
|
|||||||
<ApplicationSection className="smooth-reveal-2" />
|
<ApplicationSection className="smooth-reveal-2" />
|
||||||
|
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
<script>
|
|
||||||
// Add smooth reveal animations for content after loading
|
|
||||||
document.addEventListener('astro:page-load', () => {
|
|
||||||
const animateContent = () => {
|
|
||||||
// Animate group 1
|
|
||||||
const smoothReveal = document.querySelectorAll('.smooth-reveal');
|
|
||||||
smoothReveal.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
50 + index * 100
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
animateContent();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -1,51 +1,64 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
import { Image } from 'astro:assets';
|
import { Image } from 'astro:assets';
|
||||||
import { marked } from 'marked';
|
import { marked } from 'marked';
|
||||||
import markedShiki from 'marked-shiki';
|
import markedShiki from 'marked-shiki';
|
||||||
import { createHighlighter } from 'shiki';
|
import { createHighlighter } from 'shiki';
|
||||||
import { readItems, readSingleton } from '@directus/sdk';
|
import { readItems, readSingleton } from '@directus/sdk';
|
||||||
import 'photoswipe/style.css';
|
import "photoswipe/style.css";
|
||||||
|
|
||||||
|
import type { Post } from '@/lib/directusTypes'
|
||||||
|
|
||||||
import SocialShareButton from '@components/buttons/SocialShareButton.astro';
|
import SocialShareButton from '@components/buttons/SocialShareButton.astro';
|
||||||
import PostMetadataSnippet from '@/components/snippets/PostMetadataSnippet.astro';
|
import PostMetadataSnippet from '@/components/snippets/PostMetadataSnippet.astro';
|
||||||
import BaseLayout from '@layouts/BaseLayout.astro';
|
import BaseLayout from '@layouts/BaseLayout.astro';
|
||||||
import directus from '@lib/directus';
|
import directus from '@lib/directus';
|
||||||
import { getDirectusImageURL } from '@/support/url';
|
import { getDirectusImageURL } from '@/scripts/url';
|
||||||
|
|
||||||
const post = Astro.props;
|
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const posts = await directus.request(readItems('posts', {
|
const posts = await directus.request(readItems('posts', {
|
||||||
fields: ['*', { category: ['*'] }],
|
fields: ['*', { category: ['*'] }],
|
||||||
}));
|
}));
|
||||||
return posts.map((post) => ({
|
const globalData = await directus.request(readSingleton('site_global'));
|
||||||
|
const highlighter = await createHighlighter({
|
||||||
|
themes: ['github-light', 'github-dark'],
|
||||||
|
langs: ['typescript', 'python', 'css', 'html', 'yaml', 'bash', 'json'],
|
||||||
|
});
|
||||||
|
const renderer = {
|
||||||
|
image({ href, title, text }: { href: string; title: string | null; text: string }) {
|
||||||
|
return `
|
||||||
|
<a
|
||||||
|
href="${href}"
|
||||||
|
class="pswp-link"
|
||||||
|
data-pswp-src="${href}"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<img src="${href}" alt="${text}" title="${title || ''}" loading="lazy" />
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
marked.use({ renderer });
|
||||||
|
marked.use(markedShiki({
|
||||||
|
highlight(code, lang) {
|
||||||
|
return highlighter.codeToHtml(code, {
|
||||||
|
lang: lang || 'plaintext',
|
||||||
|
themes: { light: 'github-light', dark: 'github-dark' },
|
||||||
|
defaultColor: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return posts.map((post: Post) => ({
|
||||||
params: { slug: post.slug },
|
params: { slug: post.slug },
|
||||||
props: post,
|
props: {
|
||||||
|
post,
|
||||||
|
global: globalData,
|
||||||
|
content: marked.parse(post.content || ''),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
const global = await directus.request(readSingleton('site_global'));
|
const { post, global, content } = Astro.props;
|
||||||
|
|
||||||
const highlighter = await createHighlighter({
|
|
||||||
themes: ['github-light', 'github-dark'],
|
|
||||||
langs: ['typescript', 'python', 'css', 'html', 'yaml', 'bash', 'json'],
|
|
||||||
});
|
|
||||||
|
|
||||||
marked.use(markedShiki({
|
|
||||||
highlight(code, lang) {
|
|
||||||
return highlighter.codeToHtml(code, {
|
|
||||||
lang: lang || 'plaintext',
|
|
||||||
themes: {
|
|
||||||
light: 'github-light',
|
|
||||||
dark: 'github-dark',
|
|
||||||
},
|
|
||||||
defaultColor: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
const content = marked.parse(post.content || '');
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout
|
<BaseLayout
|
||||||
@@ -65,7 +78,7 @@ const content = marked.parse(post.content || '');
|
|||||||
name: global.name,
|
name: global.name,
|
||||||
description: global.about,
|
description: global.about,
|
||||||
},
|
},
|
||||||
image: [],
|
image: [getDirectusImageURL(post.image)],
|
||||||
headline: post.title,
|
headline: post.title,
|
||||||
datePublished: post.published_date,
|
datePublished: post.published_date,
|
||||||
dateModified: post.updated_date,
|
dateModified: post.updated_date,
|
||||||
@@ -85,7 +98,7 @@ const content = marked.parse(post.content || '');
|
|||||||
<Image
|
<Image
|
||||||
class="rounded-2xl sm:rounded-b-none w-full max-h-150 object-cover"
|
class="rounded-2xl sm:rounded-b-none w-full max-h-150 object-cover"
|
||||||
src={getDirectusImageURL(post.image)}
|
src={getDirectusImageURL(post.image)}
|
||||||
alt={post.image_alt}
|
alt={post.image_alt || post.title}
|
||||||
draggable="false"
|
draggable="false"
|
||||||
format="webp"
|
format="webp"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
@@ -110,13 +123,15 @@ const content = marked.parse(post.content || '');
|
|||||||
</article>
|
</article>
|
||||||
|
|
||||||
<div class="grid sm:flex sm:items-center sm:justify-between gap-y-5 sm:gap-y-0 max-w-5xl mx-auto mt-10 md:mt-14">
|
<div class="grid sm:flex sm:items-center sm:justify-between gap-y-5 sm:gap-y-0 max-w-5xl mx-auto mt-10 md:mt-14">
|
||||||
<div class="flex flex-wrap sm:flex-nowrap sm:items-center gap-x-2 gap-y-1 sm:gap-y-0">
|
{post.tags && post.tags.length > 0 && (
|
||||||
{post.tags.map((tag: string) => (
|
<div class="flex flex-wrap sm:flex-nowrap sm:items-center gap-x-2 gap-y-1 sm:gap-y-0">
|
||||||
<span class="inline-flex items-center button-base bg-cobalt dark:bg-turquoise text-neutral-100 text-xs font-bold rounded-lg gap-x-1.5 px-3 py-1.5">
|
{post.tags.map((tag: string) => (
|
||||||
{tag}
|
<span class="inline-flex items-center button-base bg-cobalt dark:bg-turquoise text-neutral-100 text-xs font-bold rounded-lg gap-x-1.5 px-3 py-1.5">
|
||||||
</span>
|
{tag}
|
||||||
))}
|
</span>
|
||||||
</div>
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<SocialShareButton pageTitle={post.title}/>
|
<SocialShareButton pageTitle={post.title}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -139,56 +154,3 @@ const content = marked.parse(post.content || '');
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
<script>
|
|
||||||
import PhotoSwipeLightbox from 'photoswipe/lightbox';
|
|
||||||
const prose = document.querySelector('.prose');
|
|
||||||
if (prose) {
|
|
||||||
const images = prose.querySelectorAll('img');
|
|
||||||
images.forEach((img) => {
|
|
||||||
if (img.closest('a')) return;
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = img.src;
|
|
||||||
link.dataset.pswpSrc = img.src;
|
|
||||||
link.dataset.pswpWidth = img.naturalWidth.toString();
|
|
||||||
link.dataset.pswpHeight = img.naturalHeight.toString();
|
|
||||||
link.target = '_blank';
|
|
||||||
link.classList.add('pswp-link');
|
|
||||||
|
|
||||||
img.parentNode?.insertBefore(link, img);
|
|
||||||
link.appendChild(img);
|
|
||||||
|
|
||||||
if (!img.complete) {
|
|
||||||
img.onload = () => {
|
|
||||||
link.dataset.pswpWidth = img.naturalWidth.toString();
|
|
||||||
link.dataset.pswpHeight = img.naturalHeight.toString();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const lightbox = new PhotoSwipeLightbox({
|
|
||||||
gallery: prose,
|
|
||||||
children: 'a.pswp-link',
|
|
||||||
pswpModule: () => import('photoswipe'),
|
|
||||||
});
|
|
||||||
|
|
||||||
lightbox.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add smooth reveal animations for content after loading
|
|
||||||
document.addEventListener('astro:page-load', () => {
|
|
||||||
const animateContent = () => {
|
|
||||||
const smoothReveal = document.querySelectorAll('.smooth-reveal');
|
|
||||||
smoothReveal.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
100 + index * 100
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
animateContent();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -57,56 +57,3 @@ const recentPosts: Post[] = posts.filter(
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
<script>
|
|
||||||
// Add smooth reveal animations for content after loading
|
|
||||||
document.addEventListener('astro:page-load', () => {
|
|
||||||
const animateContent = () => {
|
|
||||||
// Animate group 1
|
|
||||||
const smoothReveal = document.querySelectorAll('.smooth-reveal');
|
|
||||||
smoothReveal.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
200 + index * 300
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate group 2
|
|
||||||
const smoothReveal2 = document.querySelectorAll('.smooth-reveal-2');
|
|
||||||
smoothReveal2.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
500 + index * 100
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate topic cards with staggered delay
|
|
||||||
const smoothRevealCards = document.querySelectorAll('.smooth-reveal-cards');
|
|
||||||
smoothRevealCards.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
1000 + index * 250
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate with just fade in with staggered delay
|
|
||||||
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
|
|
||||||
smoothRevealFade.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal-fade');
|
|
||||||
},
|
|
||||||
100 + index * 250
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
animateContent();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -75,56 +75,3 @@ const categoriesPosts = posts
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
<script>
|
|
||||||
// Add smooth reveal animations for content after loading
|
|
||||||
document.addEventListener('astro:page-load', () => {
|
|
||||||
const animateContent = () => {
|
|
||||||
// Animate group 1
|
|
||||||
const smoothReveal = document.querySelectorAll('.smooth-reveal');
|
|
||||||
smoothReveal.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
50 + index * 100
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate group 2
|
|
||||||
const smoothReveal2 = document.querySelectorAll('.smooth-reveal-2');
|
|
||||||
smoothReveal2.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
200 + index * 150
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate topic cards with staggered delay
|
|
||||||
const smoothRevealCards = document.querySelectorAll('.smooth-reveal-cards');
|
|
||||||
smoothRevealCards.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
500 + index * 100
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate with just fade in with staggered delay
|
|
||||||
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
|
|
||||||
smoothRevealFade.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal-fade');
|
|
||||||
},
|
|
||||||
100 + index * 250
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
animateContent();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -37,56 +37,3 @@ const global = await directus.request(readSingleton('site_global'));
|
|||||||
<CategorySection />
|
<CategorySection />
|
||||||
|
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
<script>
|
|
||||||
// Add smooth reveal animations for content after loading
|
|
||||||
document.addEventListener('astro:page-load', () => {
|
|
||||||
const animateContent = () => {
|
|
||||||
// Animate group 1
|
|
||||||
const smoothReveal = document.querySelectorAll('.smooth-reveal');
|
|
||||||
smoothReveal.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
50 + index * 100
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate group 2
|
|
||||||
const smoothReveal2 = document.querySelectorAll('.smooth-reveal-2');
|
|
||||||
smoothReveal2.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
200 + index * 150
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate topic cards with staggered delay
|
|
||||||
const smoothRevealCards = document.querySelectorAll('.smooth-reveal-cards');
|
|
||||||
smoothRevealCards.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
500 + index * 100
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate with just fade in with staggered delay
|
|
||||||
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
|
|
||||||
smoothRevealFade.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal-fade');
|
|
||||||
},
|
|
||||||
100 + index * 250
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
animateContent();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -55,19 +55,35 @@ const recentPosts = posts
|
|||||||
|
|
||||||
<FeatureSection />
|
<FeatureSection />
|
||||||
|
|
||||||
|
<RecentPostsSection
|
||||||
|
posts={recentPosts}
|
||||||
|
title="Latest Posts"
|
||||||
|
subTitle="Checkout my most recent thoughts here"
|
||||||
|
/>
|
||||||
|
|
||||||
<WeatherSection
|
<WeatherSection
|
||||||
server:defer
|
server:defer
|
||||||
latitude={weather.latitude}
|
latitude={weather.latitude}
|
||||||
longitude={weather.longitude}
|
longitude={weather.longitude}
|
||||||
cityName={weather.location}
|
cityName={weather.location}
|
||||||
timezone={weather.timezone}
|
timezone={weather.timezone}
|
||||||
/>
|
>
|
||||||
|
<div slot="fallback" class="max-w-340 2xl:max-w-full px-4 sm:px-6 lg:px-8 py-10 lg:py-14 mx-auto mb-2 md:mb-8">
|
||||||
<RecentPostsSection
|
<div class="text-center max-w-2xl mx-auto mb-10 lg:mb-14">
|
||||||
posts={recentPosts}
|
<h1 class="smooth-reveal card-text-header block">
|
||||||
title="Latest Posts"
|
Loading weather...
|
||||||
subTitle="Checkout my most recent thoughts here"
|
</h1>
|
||||||
/>
|
<div class="smooth-reveal mx-auto mt-5 max-w-3xl text-center flex justify-center">
|
||||||
|
<div class="h-6 w-64"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap justify-center gap-4 lg:gap-6">
|
||||||
|
{Array.from({ length: 5 }).map((_, index) => (
|
||||||
|
<div class={`card-base h-62.5 w-40 animate-pulse bg-neutral-200 dark:bg-neutral-800 ${index === 3 ? "hidden min-[800px]:block" : index >= 4 ? "hidden min-[1100px]:block" : ""}`}></div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</WeatherSection>
|
||||||
|
|
||||||
<GiteaSection
|
<GiteaSection
|
||||||
title="Follow me on Gitea"
|
title="Follow me on Gitea"
|
||||||
@@ -76,62 +92,3 @@ const recentPosts = posts
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
||||||
<script>
|
|
||||||
// Add smooth reveal animations for content after loading
|
|
||||||
document.addEventListener('astro:page-load', () => {
|
|
||||||
const animateContent = () => {
|
|
||||||
// Animate group 1
|
|
||||||
const smoothReveal = document.querySelectorAll('.smooth-reveal');
|
|
||||||
smoothReveal.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
50 + index * 100
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate group 2
|
|
||||||
const smoothReveal2 = document.querySelectorAll('.smooth-reveal-2');
|
|
||||||
smoothReveal2.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
200 + index * 250
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate topic cards with staggered delay
|
|
||||||
const smoothRevealCards = document.querySelectorAll('.smooth-reveal-cards');
|
|
||||||
smoothRevealCards.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
400 + index * 250
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate with just fade in with staggered delay
|
|
||||||
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
|
|
||||||
smoothRevealFade.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal-fade');
|
|
||||||
},
|
|
||||||
100 + index * 250
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
animateContent();
|
|
||||||
|
|
||||||
const observer = new MutationObserver(() => {
|
|
||||||
animateContent();
|
|
||||||
});
|
|
||||||
|
|
||||||
observer.observe(document.body, { childList: true, subtree: true });
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|||||||
31
src/scripts/animations.ts
Normal file
31
src/scripts/animations.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
const animateContent = () => {
|
||||||
|
const smoothReveal = document.querySelectorAll('.smooth-reveal');
|
||||||
|
smoothReveal.forEach((el, index) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
el.classList.add('animate-reveal');
|
||||||
|
}, 50 + index * 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
const smoothReveal2 = document.querySelectorAll('.smooth-reveal-2');
|
||||||
|
smoothReveal2.forEach((el, index) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
el.classList.add('animate-reveal');
|
||||||
|
}, 200 + index * 250);
|
||||||
|
});
|
||||||
|
|
||||||
|
const smoothRevealCards = document.querySelectorAll('.smooth-reveal-cards');
|
||||||
|
smoothRevealCards.forEach((el, index) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
el.classList.add('animate-reveal');
|
||||||
|
}, 400 + index * 250);
|
||||||
|
});
|
||||||
|
|
||||||
|
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
|
||||||
|
smoothRevealFade.forEach((el, index) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
el.classList.add('animate-reveal-fade');
|
||||||
|
}, 100 + index * 250);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export { animateContent };
|
||||||
38
src/scripts/photoswipe.ts
Normal file
38
src/scripts/photoswipe.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import PhotoSwipeLightbox from 'photoswipe/lightbox';
|
||||||
|
import PhotoSwipe from "photoswipe";
|
||||||
|
|
||||||
|
let lightbox: PhotoSwipeLightbox | null = null;
|
||||||
|
|
||||||
|
function initPhotoSwipe() {
|
||||||
|
const links = document.querySelectorAll<HTMLAnchorElement>('a.pswp-link');
|
||||||
|
|
||||||
|
links.forEach((link) => {
|
||||||
|
const img = link.querySelector('img');
|
||||||
|
if (img) {
|
||||||
|
const applyDimensions = () => {
|
||||||
|
link.dataset.pswpWidth = (img.naturalWidth || 1920).toString();
|
||||||
|
link.dataset.pswpHeight = (img.naturalHeight || 1080).toString();
|
||||||
|
};
|
||||||
|
if (img.complete) {
|
||||||
|
applyDimensions();
|
||||||
|
} else {
|
||||||
|
img.addEventListener('load', applyDimensions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (lightbox) {
|
||||||
|
lightbox.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
lightbox = new PhotoSwipeLightbox({
|
||||||
|
gallery: '.prose',
|
||||||
|
children: 'a.pswp-link',
|
||||||
|
pswpModule: PhotoSwipe,
|
||||||
|
allowPanToNext: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
lightbox.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
export { initPhotoSwipe };
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
@import 'tailwindcss';
|
@import 'tailwindcss';
|
||||||
@import './utilities.css';
|
@import './utilities-buttons.css';
|
||||||
|
@import './utilities-cards.css';
|
||||||
|
@import './utilities-misc.css';
|
||||||
|
|
||||||
@plugin '@tailwindcss/typography';
|
@plugin '@tailwindcss/typography';
|
||||||
@plugin '@tailwindcss/forms';
|
@plugin '@tailwindcss/forms';
|
||||||
@@ -43,6 +45,29 @@
|
|||||||
--color-background-card: light-dark(color-mix(in srgb, var(--color-neutral-100) 80%, transparent), color-mix(in srgb, var(--color-neutral-800) 60%, transparent));
|
--color-background-card: light-dark(color-mix(in srgb, var(--color-neutral-100) 80%, transparent), color-mix(in srgb, var(--color-neutral-800) 60%, transparent));
|
||||||
|
|
||||||
--color-divider: light-dark(color-mix(in srgb, var(--color-slate-400) 40%, transparent), color-mix(in srgb, var(--color-neutral-500) 50%, transparent));
|
--color-divider: light-dark(color-mix(in srgb, var(--color-slate-400) 40%, transparent), color-mix(in srgb, var(--color-neutral-500) 50%, transparent));
|
||||||
|
|
||||||
|
/* Typography */
|
||||||
|
--color-prose-blog-body: var(--color-neutral-700);
|
||||||
|
--color-prose-blog-headings: var(--color-neutral-900);
|
||||||
|
--color-prose-blog-links: var(--color-orange-300);
|
||||||
|
|
||||||
|
--color-prose-blog-invert-body: var(--color-neutral-400);
|
||||||
|
--color-prose-blog-invert-headings: var(--color-neutral-200);
|
||||||
|
|
||||||
|
--radius-markdown-img: 0.5rem;
|
||||||
|
|
||||||
|
/* Reveal Animations */
|
||||||
|
--animate-reveal: reveal 0.8s ease forwards;
|
||||||
|
--animate-reveal-fade: reveal-fade 1.8s ease forwards;
|
||||||
|
|
||||||
|
@keyframes reveal {
|
||||||
|
from { opacity: 0; transform: translateY(20px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
@keyframes reveal-fade {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
@@ -51,7 +76,6 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
|
||||||
--theme-transition: 0.3s ease;
|
--theme-transition: 0.3s ease;
|
||||||
--scroll-offset: 0px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:root:where(.dark, .dark *) {
|
:root:where(.dark, .dark *) {
|
||||||
@@ -90,7 +114,54 @@
|
|||||||
border-color var(--theme-transition);
|
border-color var(--theme-transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Shiki syntax highlighting */
|
/* Typography */
|
||||||
|
.prose blockquote {
|
||||||
|
font-style: normal;
|
||||||
|
quotes: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose img {
|
||||||
|
@apply rounded-lg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose-blog {
|
||||||
|
--tw-prose-body: var(--color-neutral-700);
|
||||||
|
--tw-prose-headings: var(--color-neutral-900);
|
||||||
|
--tw-prose-lead: var(--color-neutral-700);
|
||||||
|
--tw-prose-links: var(--color-orange-300);
|
||||||
|
--tw-prose-bold: var(--color-neutral-900);
|
||||||
|
--tw-prose-counters: var(--color-neutral-600);
|
||||||
|
--tw-prose-bullets: var(--color-neutral-400);
|
||||||
|
--tw-prose-hr: var(--color-neutral-300);
|
||||||
|
--tw-prose-quotes: var(--color-neutral-500);
|
||||||
|
--tw-prose-quote-borders: var(--color-neutral-300);
|
||||||
|
--tw-prose-captions: var(--color-neutral-700);
|
||||||
|
--tw-prose-code: var(--color-neutral-700);
|
||||||
|
--tw-prose-pre-code: var(--color-neutral-900);
|
||||||
|
--tw-prose-pre-bg: var(--color-white);
|
||||||
|
--tw-prose-th-borders: var(--color-neutral-300);
|
||||||
|
--tw-prose-td-borders: var(--color-neutral-200);
|
||||||
|
|
||||||
|
&:where(.dark, .dark *) {
|
||||||
|
--tw-prose-body: var(--color-neutral-400);
|
||||||
|
--tw-prose-headings: var(--color-neutral-200);
|
||||||
|
--tw-prose-lead: var(--color-neutral-300);
|
||||||
|
--tw-prose-links: var(--color-orange-300);
|
||||||
|
--tw-prose-bold: var(--color-neutral-300);
|
||||||
|
--tw-prose-counters: var(--color-neutral-400);
|
||||||
|
--tw-prose-bullets: var(--color-neutral-600);
|
||||||
|
--tw-prose-hr: var(--color-neutral-700);
|
||||||
|
--tw-prose-quotes: var(--color-neutral-500);
|
||||||
|
--tw-prose-quote-borders: var(--color-neutral-500);
|
||||||
|
--tw-prose-captions: var(--color-neutral-400);
|
||||||
|
--tw-prose-code: var(--color-neutral-350);
|
||||||
|
--tw-prose-pre-code: var(--color-neutral-300);
|
||||||
|
--tw-prose-th-borders: var(--color-neutral-600);
|
||||||
|
--tw-prose-td-borders: var(--color-neutral-700);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Shiki */
|
||||||
:root {
|
:root {
|
||||||
--shiki-fg: var(--shiki-light);
|
--shiki-fg: var(--shiki-light);
|
||||||
--shiki-bg: var(--color-neutral-200);
|
--shiki-bg: var(--color-neutral-200);
|
||||||
@@ -114,32 +185,3 @@
|
|||||||
color: var(--shiki-dark) !important;
|
color: var(--shiki-dark) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Content reveal animations */
|
|
||||||
.smooth-reveal,
|
|
||||||
.smooth-reveal-2,
|
|
||||||
.smooth-reveal-cards {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(20px);
|
|
||||||
transition:
|
|
||||||
opacity 0.8s ease,
|
|
||||||
transform 0.8s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-reveal {
|
|
||||||
opacity: 1 !important;
|
|
||||||
transform: translateY(0) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.smooth-reveal-fade {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(0px);
|
|
||||||
transition:
|
|
||||||
opacity 1.8s ease,
|
|
||||||
transform 0.8s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.animate-reveal-fade {
|
|
||||||
opacity: 1 !important;
|
|
||||||
transform: translateY(0) !important;
|
|
||||||
}
|
|
||||||
|
|||||||
51
src/styles/utilities-buttons.css
Normal file
51
src/styles/utilities-buttons.css
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
@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-200 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 group-hover:bg-turquoise dark:bg-turquoise dark:hover:bg-bermuda dark:group-hover:bg-bermuda
|
||||||
|
}
|
||||||
|
|
||||||
|
@utility button-bg-neutral {
|
||||||
|
@apply transition-all duration-300
|
||||||
|
border border-neutral-100 dark:border-stone-500/20
|
||||||
|
bg-background-card hover:bg-neutral-100 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
|
||||||
|
}
|
||||||
@@ -1,57 +1,3 @@
|
|||||||
/* 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-200 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 group-hover:bg-turquoise dark:bg-turquoise dark:hover:bg-bermuda dark:group-hover:bg-bermuda
|
|
||||||
}
|
|
||||||
|
|
||||||
@utility button-bg-neutral {
|
|
||||||
@apply transition-all duration-300
|
|
||||||
border border-neutral-100 dark:border-stone-500/20
|
|
||||||
bg-background-card hover:bg-neutral-100 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 {
|
@utility card-base {
|
||||||
@apply transition-all duration-300
|
@apply transition-all duration-300
|
||||||
rounded-xl
|
rounded-xl
|
||||||
@@ -131,10 +77,3 @@
|
|||||||
@apply transition-all duration-300
|
@apply transition-all duration-300
|
||||||
text-secondary-hover
|
text-secondary-hover
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Misc */
|
|
||||||
@utility nav-base {
|
|
||||||
@apply border border-neutral-100 dark:border-stone-500/20
|
|
||||||
bg-neutral-100 dark:bg-neutral-800
|
|
||||||
shadow-xs dark:shadow-md
|
|
||||||
}
|
|
||||||
44
src/styles/utilities-misc.css
Normal file
44
src/styles/utilities-misc.css
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/* Nav */
|
||||||
|
@utility nav-base {
|
||||||
|
@apply border border-neutral-100 dark:border-stone-500/20
|
||||||
|
bg-neutral-100 dark:bg-neutral-800
|
||||||
|
shadow-xs dark:shadow-md
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fade edges of a div */
|
||||||
|
@utility mask-fade-edges {
|
||||||
|
-webkit-mask-image:
|
||||||
|
linear-gradient(to right, transparent, black var(--fade-dist, 1rem), black calc(100% - var(--fade-dist, 1rem)), transparent),
|
||||||
|
linear-gradient(to bottom, transparent, black var(--fade-dist, 1rem), black calc(100% - var(--fade-dist, 1rem)), transparent);
|
||||||
|
mask-image:
|
||||||
|
linear-gradient(to right, transparent, black var(--fade-dist, 1rem), black calc(100% - var(--fade-dist, 1rem)), transparent),
|
||||||
|
linear-gradient(to bottom, transparent, black var(--fade-dist, 1rem), black calc(100% - var(--fade-dist, 1rem)), transparent);
|
||||||
|
|
||||||
|
-webkit-mask-composite: source-in;
|
||||||
|
mask-composite: intersect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
@utility smooth-reveal {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
transition: opacity 0.8s ease, transform 0.8s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@utility smooth-reveal-2 {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
transition: opacity 0.8s ease, transform 0.8s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@utility smooth-reveal-cards {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
transition: opacity 0.8s ease, transform 0.8s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@utility smooth-reveal-fade {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(0px);
|
||||||
|
transition: opacity 1.8s ease, transform 0.8s ease;
|
||||||
|
}
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
// Add smooth reveal animations for content after loading
|
|
||||||
document.addEventListener('astro:page-load', () => {
|
|
||||||
const animateContent = () => {
|
|
||||||
// Animate group 1
|
|
||||||
const smoothReveal = document.querySelectorAll('.smooth-reveal');
|
|
||||||
smoothReveal.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
50 + index * 100
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate group 2
|
|
||||||
const smoothReveal2 = document.querySelectorAll('.smooth-reveal-2');
|
|
||||||
smoothReveal2.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
200 + index * 250
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate topic cards with staggered delay
|
|
||||||
const smoothRevealCards = document.querySelectorAll('.smooth-reveal-cards');
|
|
||||||
smoothRevealCards.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal');
|
|
||||||
},
|
|
||||||
400 + index * 250
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animate with just fade in with staggered delay
|
|
||||||
const smoothRevealFade = document.querySelectorAll('.smooth-reveal-fade');
|
|
||||||
smoothRevealFade.forEach((el, index) => {
|
|
||||||
setTimeout(
|
|
||||||
() => {
|
|
||||||
el.classList.add('animate-reveal-fade');
|
|
||||||
},
|
|
||||||
100 + index * 250
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
animateContent();
|
|
||||||
});
|
|
||||||
@@ -1,64 +1,6 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
content: [
|
content: ['./node_modules/preline/preline.js'],
|
||||||
'./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}',
|
plugins: [require('preline/plugin')],
|
||||||
'./node_modules/preline/preline.js',
|
|
||||||
],
|
|
||||||
darkMode: 'class',
|
|
||||||
theme: {
|
|
||||||
extend: {
|
|
||||||
typography: ({ theme }) => ({
|
|
||||||
blog: {
|
|
||||||
css: {
|
|
||||||
'--tw-prose-body': theme('colors.neutral[700]'),
|
|
||||||
'--tw-prose-headings': theme('colors.neutral[900]'),
|
|
||||||
'--tw-prose-lead': theme('colors.neutral[700]'),
|
|
||||||
'--tw-prose-links': theme('colors.orange[300]'),
|
|
||||||
'--tw-prose-bold': theme('colors.neutral[900]'),
|
|
||||||
'--tw-prose-counters': theme('colors.neutral[600]'),
|
|
||||||
'--tw-prose-bullets': theme('colors.neutral[400]'),
|
|
||||||
'--tw-prose-hr': theme('colors.neutral[300]'),
|
|
||||||
'--tw-prose-quotes': theme('colors.neutral[500]'),
|
|
||||||
'--tw-prose-quote-borders': theme('colors.neutral[300]'),
|
|
||||||
'--tw-prose-captions': theme('colors.neutral[700]'),
|
|
||||||
'--tw-prose-code': theme('colors.neutral[700]'),
|
|
||||||
'--tw-prose-pre-code': theme('colors.neutral[900]'),
|
|
||||||
'--tw-prose-pre-bg': theme('colors.white'),
|
|
||||||
'--tw-prose-th-borders': theme('colors.neutral[300]'),
|
|
||||||
'--tw-prose-td-borders': theme('colors.neutral[200]'),
|
|
||||||
|
|
||||||
'--tw-prose-invert-body': theme('colors.neutral[400]'),
|
|
||||||
'--tw-prose-invert-headings': theme('colors.neutral[200]'),
|
|
||||||
'--tw-prose-invert-lead': theme('colors.neutral[300]'),
|
|
||||||
'--tw-prose-invert-links': theme('colors.orange[300]'),
|
|
||||||
'--tw-prose-invert-bold': theme('colors.neutral[300]'),
|
|
||||||
'--tw-prose-invert-counters': theme('colors.neutral[400]'),
|
|
||||||
'--tw-prose-invert-bullets': theme('colors.neutral[600]'),
|
|
||||||
'--tw-prose-invert-hr': theme('colors.neutral[700]'),
|
|
||||||
'--tw-prose-invert-quotes': theme('colors.neutral[500]'),
|
|
||||||
'--tw-prose-invert-quote-borders': theme('colors.neutral[500]'),
|
|
||||||
'--tw-prose-invert-captions': theme('colors.neutral[400]'),
|
|
||||||
'--tw-prose-invert-code': theme('colors.neutral[350]'),
|
|
||||||
'--tw-prose-invert-pre-code': theme('colors.neutral[300]'),
|
|
||||||
'--tw-prose-invert-th-borders': theme('colors.neutral[600]'),
|
|
||||||
'--tw-prose-invert-td-borders': theme('colors.neutral[700]'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
DEFAULT: {
|
|
||||||
css: {
|
|
||||||
blockquote: {
|
|
||||||
fontStyle: 'normal',
|
|
||||||
quotes: 'none',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
require('tailwindcss/nesting'),
|
|
||||||
require('preline/plugin'),
|
|
||||||
require('@tailwindcss/typography'),
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,20 +4,19 @@
|
|||||||
"exclude": ["dist"],
|
"exclude": ["dist"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"baseUrl": "src",
|
"baseUrl": ".",
|
||||||
"jsx": "react-jsx",
|
|
||||||
"types": ["astro/client"],
|
"types": ["astro/client"],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["*"],
|
"@/*": ["src/*"],
|
||||||
"@src/*": ["src/*"],
|
"@src/*": ["src/*"],
|
||||||
"@lib/*": ["lib/*"],
|
"@lib/*": ["src/lib/*"],
|
||||||
"@components/*": ["components/*"],
|
"@components/*": ["src/components/*"],
|
||||||
"@content/*": ["content/*"],
|
"@content/*": ["src/content/*"],
|
||||||
"@layouts/*": ["layouts/*"],
|
"@layouts/*": ["src/layouts/*"],
|
||||||
"@styles/*": ["styles/*"],
|
"@styles/*": ["src/styles/*"],
|
||||||
"@pages/*": ["pages/*"],
|
"@pages/*": ["src/pages/*"],
|
||||||
"@support/*": ["support/*"],
|
"@support/*": ["src/scripts/*"],
|
||||||
"@images/*": ["images/*"]
|
"@images/*": ["src/images/*"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user