apply prettier formatting
Some checks failed
renovate / renovate (push) Has been cancelled

This commit is contained in:
2025-06-08 16:45:36 -05:00
parent 67f12ecf72
commit 51041f6ae9
32 changed files with 3303 additions and 2509 deletions

View File

@@ -1,16 +1,15 @@
---
import Layout from './Layout.astro';
import directus from "../../lib/directus"
import { readSingleton } from "@directus/sdk";
import directus from '../../lib/directus';
import { readSingleton } from '@directus/sdk';
const global = await directus.request(readSingleton("global"));
const global = await directus.request(readSingleton('global'));
export interface Props {
title: string;
description?: string;
}
---
<Layout title={global.title} description={global.description}>

View File

@@ -1,16 +1,15 @@
---
import Layout from './Layout.astro';
import directus from "../../lib/directus"
import { readSingleton } from "@directus/sdk";
import directus from '../../lib/directus';
import { readSingleton } from '@directus/sdk';
const global = await directus.request(readSingleton("global"));
const global = await directus.request(readSingleton('global'));
export interface Props {
title: string;
description?: string;
}
---
<Layout title={global.title} description={global.description}>
@@ -26,7 +25,7 @@ export interface Props {
document.documentElement.classList.add('theme-switching');
const rippleElements = document.querySelectorAll('.theme-ripple');
rippleElements.forEach(el => {
rippleElements.forEach((el) => {
el.classList.add('ripple-active');
setTimeout(() => {
el.classList.remove('ripple-active');
@@ -35,8 +34,8 @@ export interface Props {
const event = new CustomEvent('themeChange', {
detail: {
theme: document.documentElement.classList.contains('dark') ? 'dark' : 'light'
}
theme: document.documentElement.classList.contains('dark') ? 'dark' : 'light',
},
});
document.dispatchEvent(event);
@@ -47,8 +46,7 @@ export interface Props {
}
const socialLinks = document.querySelectorAll('.social-link');
socialLinks.forEach(link => {
socialLinks.forEach((link) => {
link.addEventListener('mouseenter', () => {
link.classList.add('hover-active');
});

View File

@@ -5,13 +5,15 @@ import ShareButtons from '../components/ShareButtons.astro';
import TagList from '../components/TagList.astro';
import './styles/markdown.css';
import directus from "../../lib/directus"
import { readItems } from "@directus/sdk";
import directus from '../../lib/directus';
import { readItems } from '@directus/sdk';
export async function getStaticPaths() {
const posts = await directus.request(readItems("posts", {
fields: ['*'],
}));
const posts = await directus.request(
readItems('posts', {
fields: ['*'],
})
);
return posts.map((post) => ({ params: { slug: post.slug }, props: post }));
}
@@ -23,58 +25,64 @@ try {
canonicalURL = new URL(Astro.url.pathname, Astro.site || process.env.SITE_URL);
} catch (error) {
console.error('Error creating canonical URL:', error);
canonicalURL = new URL("https://www.example.com");
canonicalURL = new URL('https://www.example.com');
}
---
<Layout title={post.title} description={post.description}>
<article class="prose dark:prose-invert prose-zinc lg:prose-lg mx-auto max-w-4xl">
<article class="prose prose-zinc mx-auto max-w-4xl dark:prose-invert lg:prose-lg">
<div class="mb-12">
<h1 class="mb-4 text-4xl font-bold tracking-tight text-zinc-900 dark:text-zinc-100 sm:text-5xl">
<h1
class="mb-4 text-4xl font-bold tracking-tight text-zinc-900 dark:text-zinc-100 sm:text-5xl"
>
{post.title}
</h1>
<div class="flex items-center gap-x-4 text-sm text-zinc-500 dark:text-zinc-400 mb-6">
<div class="mb-6 flex items-center gap-x-4 text-sm text-zinc-500 dark:text-zinc-400">
<FormattedDate date={published_date} />
</div>
<TagList tags={post.tags} class="mt-2" />
</div>
<!-- Hero image -->
{post.image && (
<div class="relative mb-8 sm:mb-12 overflow-hidden rounded-xl shadow-lg">
<div class="aspect-[16/9] w-full">
<img
src={`${process.env.DIRECTUS_URL ?? "https://directus.alexlebens.dev"}/assets/${post.image}?width=500`}
alt={post.image_alt}
class="w-full h-full object-cover"
loading="eager"
/>
{
post.image && (
<div class="relative mb-8 overflow-hidden rounded-xl shadow-lg sm:mb-12">
<div class="aspect-[16/9] w-full">
<img
src={`${process.env.DIRECTUS_URL ?? 'https://directus.alexlebens.dev'}/assets/${post.image}?width=500`}
alt={post.image_alt}
class="h-full w-full object-cover"
loading="eager"
/>
</div>
<div class="absolute inset-0 bg-gradient-to-t from-black/30 to-transparent" />
</div>
<div class="absolute inset-0 bg-gradient-to-t from-black/30 to-transparent"></div>
</div>
)}
)
}
<div class="markdown-content">
<slot />
</div>
<!-- Add the like button after the content -->
<div class="mt-12 pt-8 border-t border-zinc-200 dark:border-zinc-800">
<div class="flex flex-col sm:flex-row items-center justify-between gap-6">
<ShareButtons url={canonicalURL.toString()} title={post.title} /> <!-- Convert URL to string -->
<div class="mt-12 border-t border-zinc-200 pt-8 dark:border-zinc-800">
<div class="flex flex-col items-center justify-between gap-6 sm:flex-row">
<ShareButtons url={canonicalURL.toString()} title={post.title} />
<!-- Convert URL to string -->
</div>
</div>
{post.updated_date && (
<div class="mt-8 text-sm text-zinc-500 dark:text-zinc-400 italic">
Last updated on <FormattedDate date={post.updated_date} />
</div>
)}
{
post.updated_date && (
<div class="mt-8 text-sm italic text-zinc-500 dark:text-zinc-400">
Last updated on <FormattedDate date={post.updated_date} />
</div>
)
}
</article>
<slot name="after-article" />
</Layout>
@@ -85,37 +93,40 @@ try {
const article = document.querySelector('article');
if (article) {
article.classList.add('article-entering');
// Remove class after animation completes
setTimeout(() => {
article.classList.remove('article-entering');
}, 1000);
}
// Ensure consistent code block styling
function updateCodeBlockStyles() {
document.querySelectorAll('pre').forEach(pre => {
document.querySelectorAll('pre').forEach((pre) => {
// Force the background color with !important for both light and dark mode
pre.setAttribute('style', 'background-color: #1e293b !important');
// Also apply to any nested code elements
const codeElements = pre.querySelectorAll('code');
codeElements.forEach(code => {
code.setAttribute('style', 'background-color: transparent !important; color: #e5e7eb !important;');
codeElements.forEach((code) => {
code.setAttribute(
'style',
'background-color: transparent !important; color: #e5e7eb !important;'
);
});
});
}
// Initial application
updateCodeBlockStyles();
// Watch for theme changes
const observer = new MutationObserver(() => {
updateCodeBlockStyles();
});
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
// Also run on any content changes that might add new code blocks
const contentObserver = new MutationObserver((mutations) => {
for (const mutation of mutations) {
@@ -125,71 +136,74 @@ try {
}
}
});
contentObserver.observe(document.body, { childList: true, subtree: true });
// Clean up observers when navigating away
document.addEventListener('spa-navigation-start', () => {
observer.disconnect();
contentObserver.disconnect();
});
// Remove the parallax effect for hero image
// Handle prev/next navigation links
const navLinks = document.querySelectorAll('.blog-nav-link');
navLinks.forEach(link => {
navLinks.forEach((link) => {
if (!link.hasAttribute('data-spa-handled')) {
link.setAttribute('data-spa-handled', 'true');
link.addEventListener('mouseenter', () => {
link.classList.add('nav-link-hover');
});
link.addEventListener('mouseleave', () => {
link.classList.remove('nav-link-hover');
});
}
});
// Animate headings when they enter the viewport
const animateHeadings = () => {
const headings = document.querySelectorAll('article h2, article h3');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('heading-visible');
observer.unobserve(entry.target);
}
});
}, {
threshold: 0.2,
rootMargin: '0px 0px -100px 0px'
});
headings.forEach(heading => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('heading-visible');
observer.unobserve(entry.target);
}
});
},
{
threshold: 0.2,
rootMargin: '0px 0px -100px 0px',
}
);
headings.forEach((heading) => {
heading.classList.add('heading-animated');
observer.observe(heading);
});
return observer;
};
// Initialize heading animations
const headingObserver = animateHeadings();
// Enhance code blocks with syntax highlighting and copy button
function enhanceCodeBlocks() {
// Enhance code blocks with syntax highlighting and copy button
function enhanceCodeBlocks() {
const codeBlocks = document.querySelectorAll('pre code');
codeBlocks.forEach(codeBlock => {
codeBlocks.forEach((codeBlock) => {
// Skip if already processed
if (codeBlock.parentElement.classList.contains('enhanced')) return;
// Mark as enhanced
codeBlock.parentElement.classList.add('enhanced');
// Create copy button
const copyButton = document.createElement('button');
copyButton.className = 'copy-code-button';
@@ -199,19 +213,19 @@ try {
<path d="M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z" />
</svg>
`;
// Add copy functionality
copyButton.addEventListener('click', () => {
const code = codeBlock.textContent;
navigator.clipboard.writeText(code);
// Show copied feedback
copyButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
</svg>
`;
setTimeout(() => {
copyButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
@@ -221,40 +235,40 @@ try {
`;
}, 2000);
});
// Add copy button to pre element
codeBlock.parentElement.appendChild(copyButton);
// Fix line numbers implementation
const codeText = codeBlock.textContent;
const lines = codeText.split('\n');
const lineNumbers = document.createElement('div');
lineNumbers.className = 'line-numbers';
// Always include all lines, including empty ones
for (let i = 0; i < lines.length; i++) {
const lineNumber = document.createElement('span');
lineNumber.textContent = i + 1;
lineNumbers.appendChild(lineNumber);
}
codeBlock.parentElement.classList.add('with-line-numbers');
codeBlock.parentElement.insertBefore(lineNumbers, codeBlock);
// Fix language label detection and display
const className = codeBlock.className;
const languageMatch = className.match(/language-(\w+)/);
if (languageMatch && languageMatch[1]) {
const language = languageMatch[1];
// Add language label at top right
const languageLabel = document.createElement('div');
languageLabel.className = 'language-label';
languageLabel.textContent = language;
codeBlock.parentElement.appendChild(languageLabel);
// Add language badge at bottom right with markdown syntax
const languageBadge = document.createElement('div');
languageBadge.className = 'language-badge';
@@ -267,28 +281,29 @@ try {
languageBadge.style.backgroundColor = 'rgba(75, 85, 99, 0.7)';
languageBadge.style.color = '#e5e7eb';
languageBadge.style.borderRadius = '0.25rem';
languageBadge.style.fontFamily = 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
languageBadge.style.fontFamily =
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
languageBadge.style.zIndex = '10';
codeBlock.parentElement.appendChild(languageBadge);
}
});
}
// Enhance tables with better styling
function enhanceTables() {
const tables = document.querySelectorAll('.markdown-content table');
tables.forEach(table => {
tables.forEach((table) => {
if (table.classList.contains('enhanced-table')) return;
table.classList.add('enhanced-table');
// Wrap table in responsive container
const wrapper = document.createElement('div');
wrapper.className = 'table-container';
table.parentNode.insertBefore(wrapper, table);
wrapper.appendChild(table);
// Add zebra striping to rows
const rows = table.querySelectorAll('tbody tr');
rows.forEach((row, index) => {
@@ -300,16 +315,16 @@ try {
});
});
}
// Enhance blockquotes with icons
function enhanceBlockquotes() {
const blockquotes = document.querySelectorAll('.markdown-content blockquote');
blockquotes.forEach(blockquote => {
blockquotes.forEach((blockquote) => {
if (blockquote.classList.contains('enhanced-quote')) return;
blockquote.classList.add('enhanced-quote');
// Add quote icon
const icon = document.createElement('div');
icon.className = 'quote-icon';
@@ -318,16 +333,16 @@ try {
<path stroke-linecap="round" stroke-linejoin="round" d="M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 01.865-.501 48.172 48.172 0 003.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z" />
</svg>
`;
blockquote.insertBefore(icon, blockquote.firstChild);
});
}
// Run all enhancements
enhanceCodeBlocks();
enhanceTables();
enhanceBlockquotes();
// Clean up observers when navigating away
document.addEventListener('spa-navigation-start', () => {
if (headingObserver) {
@@ -335,16 +350,16 @@ try {
}
});
}
// Initialize on first load
document.addEventListener('DOMContentLoaded', setupBlogPostTransitions);
// Re-initialize when content changes via Astro's view transitions
document.addEventListener('astro:page-load', setupBlogPostTransitions);
// For compatibility with custom transition system
document.addEventListener('page-transition-complete', setupBlogPostTransitions);
// Also initialize when SPA navigation completes
document.addEventListener('spa-navigation-complete', setupBlogPostTransitions);
</script>
@@ -353,19 +368,21 @@ try {
/* Enhanced hero image styling */
article img:first-of-type {
border-radius: 1rem;
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
box-shadow:
0 10px 25px -5px rgba(0, 0, 0, 0.1),
0 8px 10px -6px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}
article img:first-of-type:hover {
transform: scale(1.01);
}
/* Article entrance animation */
.article-entering {
animation: article-fade-in 0.8s ease-out forwards;
}
@keyframes article-fade-in {
from {
opacity: 0;
@@ -376,6 +393,6 @@ try {
transform: translateY(0);
}
}
/* Rest of the styles remain unchanged... */
</style>

View File

@@ -5,14 +5,14 @@ import Background from '../components/Background.astro';
import '../styles/global.css';
interface Props {
title?: string | undefined;
description?: string | undefined;
title?: string | undefined;
description?: string | undefined;
}
const { title, description } = Astro.props;
---
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
@@ -21,33 +21,41 @@ const { title, description } = Astro.props;
<meta name="generator" content={Astro.generator} />
<meta name="description" content={description} />
<title>{title}</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
/>
</head>
<body class="bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100 min-h-screen flex flex-col">
<body
class="flex min-h-screen flex-col bg-white text-zinc-900 dark:bg-zinc-900 dark:text-zinc-100"
>
<!-- Page transition overlay - for smooth transitions between pages -->
<div id="page-transition" class="fixed inset-0 z-40 bg-white dark:bg-zinc-900 opacity-0 pointer-events-none transition-opacity duration-300 flex items-center justify-center">
<div
id="page-transition"
class="pointer-events-none fixed inset-0 z-40 flex items-center justify-center bg-white opacity-0 transition-opacity duration-300 dark:bg-zinc-900"
>
<div class="transition-spinner"></div>
</div>
<!-- Background component with dot pattern and ambient glow -->
<Background />
<div class="max-w-3xl mx-auto px-4 sm:px-6 w-full flex-grow">
<div class="mx-auto w-full max-w-3xl flex-grow px-4 sm:px-6">
<Navigation />
<main class="py-12">
<slot />
</main>
</div>
<Footer />
<script>
// SPA transition system with history API
document.addEventListener('DOMContentLoaded', () => {
const pageTransition = document.getElementById('page-transition');
const mainContent = document.querySelector('main');
// Initialize content with entrance animation
if (mainContent) {
mainContent.classList.add('content-entering');
@@ -55,7 +63,7 @@ const { title, description } = Astro.props;
mainContent.classList.remove('content-entering');
}, 800);
}
// Function to load content via fetch
async function loadContent(url) {
try {
@@ -64,52 +72,55 @@ const { title, description } = Astro.props;
pageTransition.classList.remove('opacity-0', 'pointer-events-none');
pageTransition.classList.add('opacity-100');
}
// Fade out current content
if (mainContent) {
mainContent.style.opacity = '0';
mainContent.style.transform = 'translateY(10px)';
}
// Fetch the new page content
const response = await fetch(url);
if (!response.ok) throw new Error(`Failed to fetch ${url}`);
const html = await response.text();
// Create a temporary element to parse the HTML
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
// Extract the main content
const newContent = doc.querySelector('main');
if (!newContent) throw new Error('Could not find main content in the fetched page');
// Extract the title
const newTitle = doc.querySelector('title');
if (newTitle) {
document.title = newTitle.textContent;
}
// Extract meta description
const newDescription = doc.querySelector('meta[name="description"]');
if (newDescription) {
const currentDescription = document.querySelector('meta[name="description"]');
if (currentDescription) {
currentDescription.setAttribute('content', newDescription.getAttribute('content') || '');
currentDescription.setAttribute(
'content',
newDescription.getAttribute('content') || ''
);
}
}
// Wait a bit for transition effect
await new Promise(resolve => setTimeout(resolve, 300));
await new Promise((resolve) => setTimeout(resolve, 300));
// Replace the content
if (mainContent && newContent) {
mainContent.innerHTML = newContent.innerHTML;
// Run scripts in the new content
Array.from(newContent.querySelectorAll('script')).forEach(oldScript => {
Array.from(newContent.querySelectorAll('script')).forEach((oldScript) => {
const newScript = document.createElement('script');
Array.from(oldScript.attributes).forEach(attr => {
Array.from(oldScript.attributes).forEach((attr) => {
newScript.setAttribute(attr.name, attr.value);
});
newScript.textContent = oldScript.textContent;
@@ -118,17 +129,17 @@ const { title, description } = Astro.props;
}
});
}
// Fade in new content with animation
if (mainContent) {
mainContent.style.opacity = '0';
mainContent.style.transform = 'translateY(10px)';
setTimeout(() => {
mainContent.style.transition = 'opacity 0.5s ease, transform 0.5s ease';
mainContent.style.opacity = '1';
mainContent.style.transform = 'translateY(0)';
// Add entrance animation class
mainContent.classList.add('content-entering');
setTimeout(() => {
@@ -136,7 +147,7 @@ const { title, description } = Astro.props;
}, 800);
}, 50);
}
// Hide transition overlay
if (pageTransition) {
setTimeout(() => {
@@ -144,33 +155,34 @@ const { title, description } = Astro.props;
pageTransition.classList.remove('opacity-100');
}, 200);
}
// Dispatch custom event for content loaded
document.dispatchEvent(new CustomEvent('spa-content-loaded', {
detail: { url }
}));
document.dispatchEvent(
new CustomEvent('spa-content-loaded', {
detail: { url },
})
);
// Scroll to top or to saved position
window.scrollTo(0, 0);
// Re-attach event listeners to new content
attachLinkListeners();
} catch (error) {
console.error('Error loading content:', error);
// Fallback to traditional navigation on error
window.location.href = url;
}
}
// Function to attach event listeners to all links
function attachLinkListeners() {
document.querySelectorAll('a').forEach(link => {
document.querySelectorAll('a').forEach((link) => {
// Skip links that are already handled, anchor links, external links, or have special attributes
if (
link.hasAttribute('data-spa-handled') ||
!link.href.startsWith(window.location.origin) ||
!link.href.startsWith(window.location.origin) ||
link.href.includes('#') ||
link.hasAttribute('target') ||
link.hasAttribute('download') ||
@@ -179,36 +191,36 @@ const { title, description } = Astro.props;
) {
return;
}
// Mark as handled to avoid duplicate listeners
link.setAttribute('data-spa-handled', 'true');
link.addEventListener('click', (e) => {
// Don't handle if modifier keys are pressed (for opening in new tab, etc.)
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
return;
}
e.preventDefault();
const targetHref = link.href;
// Don't transition if clicking the current page
if (targetHref === window.location.href) {
return;
}
// Update browser history
window.history.pushState({ path: targetHref }, '', targetHref);
// Load the new content
loadContent(targetHref);
});
});
}
// Initial attachment of link listeners
attachLinkListeners();
// Handle browser back/forward navigation
window.addEventListener('popstate', (e) => {
if (e.state && e.state.path) {
@@ -217,7 +229,7 @@ const { title, description } = Astro.props;
loadContent(window.location.href);
}
});
// Check RSS feed availability
const checkAndGenerateRSS = async () => {
try {
@@ -229,33 +241,36 @@ const { title, description } = Astro.props;
console.warn('Could not check RSS feed status.');
}
};
// Check RSS feed availability
checkAndGenerateRSS();
});
// Theme handling with transition effects
function setupThemeHandling() {
// Apply theme from localStorage or system preference
const theme = localStorage.getItem('theme');
if (theme === 'dark' || (!theme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
if (
theme === 'dark' ||
(!theme && window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
// Listen for theme changes
document.addEventListener('themeChanged', () => {
// Add transition class to body
document.body.classList.add('theme-transitioning');
// Remove class after transition completes
setTimeout(() => {
document.body.classList.remove('theme-transitioning');
}, 500);
});
}
// Initialize theme handling
document.addEventListener('DOMContentLoaded', setupThemeHandling);
</script>
@@ -268,7 +283,7 @@ const { title, description } = Astro.props;
transition: opacity 0.3s ease;
backdrop-filter: blur(4px);
}
/* Transition spinner animation */
.transition-spinner {
width: 30px;
@@ -278,27 +293,31 @@ const { title, description } = Astro.props;
border-top-color: #3b82f6;
animation: spin 0.7s linear infinite;
}
:global(.dark) .transition-spinner {
border-color: rgba(255, 255, 255, 0.1);
border-top-color: #60a5fa;
}
@keyframes spin {
to { transform: rotate(360deg); }
to {
transform: rotate(360deg);
}
}
/* Content entrance animation */
main {
opacity: 1;
transform: translateY(0);
transition: opacity 0.5s ease, transform 0.5s ease;
transition:
opacity 0.5s ease,
transform 0.5s ease;
}
main.content-entering {
animation: content-fade-in 0.6s ease forwards;
}
@keyframes content-fade-in {
from {
opacity: 0;
@@ -309,7 +328,7 @@ const { title, description } = Astro.props;
transform: translateY(0);
}
}
/* Theme transition effect */
body.theme-transitioning * {
transition-duration: 0.3s !important;

View File

@@ -7,7 +7,7 @@ const { title, description } = Astro.props;
<BaseLayout title={title} description={description}>
<ViewTransitions fallback="swap" />
<div transition:animate="slide">
<slot />
</div>
@@ -18,7 +18,7 @@ const { title, description } = Astro.props;
::view-transition-old(root) {
animation: 0.5s cubic-bezier(0.76, 0, 0.24, 1) both slide-to-left;
}
::view-transition-new(root) {
animation: 0.5s cubic-bezier(0.76, 0, 0.24, 1) both slide-from-right;
}

File diff suppressed because it is too large Load Diff