formatting and layout
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
---
|
||||
// Background.astro - Dot pattern and ambient glow background with smooth theme transitions
|
||||
|
||||
---
|
||||
|
||||
<div class="theme-transition-all fixed inset-0 -z-10 overflow-hidden">
|
||||
|
@@ -8,10 +8,10 @@ const links = await directus.request(readSingleton('links'));
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
const navLinks = [
|
||||
{ text: 'About', href: '/about' },
|
||||
{ text: 'Home', href: '/' },
|
||||
{ text: 'Blog', href: '/blog' },
|
||||
{ text: 'Topics', href: '/topics' },
|
||||
{ text: 'RSS', href: '/rss.xml' },
|
||||
{ text: 'About', href: '/about' },
|
||||
];
|
||||
|
||||
const socialLinks = [
|
||||
@@ -53,7 +53,6 @@ const socialLinks = [
|
||||
|
||||
<div class="relative px-4 pt-16 pb-12 sm:px-6">
|
||||
<div class="mx-auto max-w-4xl">
|
||||
<!-- Main footer content -->
|
||||
<div class="grid grid-cols-1 gap-10 md:grid-cols-12">
|
||||
<!-- Brand section -->
|
||||
<div class="col-span-1 md:col-span-3">
|
||||
|
@@ -86,7 +86,7 @@ try {
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
/* Enhanced hero image styling */
|
||||
/* Hero image styling */
|
||||
article img:first-of-type {
|
||||
border-radius: 1rem;
|
||||
box-shadow:
|
||||
|
@@ -2,6 +2,7 @@
|
||||
import Navigation from '../components/Navigation.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import Background from '../components/Background.astro';
|
||||
|
||||
import '../styles/global.css';
|
||||
|
||||
interface Props {
|
||||
@@ -51,7 +52,6 @@ const { title, description } = Astro.props;
|
||||
class="flex min-h-screen flex-col bg-white text-zinc-900 dark:bg-zinc-900 dark:text-zinc-100"
|
||||
>
|
||||
|
||||
<!-- Background component with dot pattern and ambient glow -->
|
||||
<Background />
|
||||
|
||||
<div class="mx-auto w-full max-w-3xl grow px-4 sm:px-6">
|
||||
|
@@ -1,25 +0,0 @@
|
||||
---
|
||||
import { ClientRouter } from 'astro:transitions';
|
||||
import BaseLayout from './BaseLayout.astro';
|
||||
|
||||
const { title, description } = Astro.props;
|
||||
---
|
||||
|
||||
<BaseLayout title={title} description={description}>
|
||||
<ClientRouter fallback="swap" />
|
||||
|
||||
<div transition:animate="slide">
|
||||
<slot />
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
/* Custom transition styles */
|
||||
::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;
|
||||
}
|
||||
</style>
|
@@ -1,6 +1,6 @@
|
||||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import DynamicIcon from '../components/DynamicIcon.tsx';
|
||||
import DynamicIcon from '../utils/DynamicIcon.tsx';
|
||||
|
||||
import directus from '../../lib/directus';
|
||||
import { readSingleton, readItems } from '@directus/sdk';
|
||||
@@ -106,7 +106,7 @@ const skills = await directus.request(
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Skills Section - Improved for mobile -->
|
||||
<!-- Skills Section -->
|
||||
<div class="theme-transition-all mb-16 sm:mb-20 md:mb-24">
|
||||
<h2
|
||||
class="theme-transition-color mb-8 text-center text-2xl font-bold text-zinc-900 sm:mb-12 sm:text-3xl dark:text-zinc-100"
|
||||
@@ -274,7 +274,7 @@ const skills = await directus.request(
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* Reduce animation complexity on mobile for better performance */
|
||||
/* Reduce animation complexity on mobile */
|
||||
@media (max-width: 640px) {
|
||||
.skill-card {
|
||||
transition:
|
||||
@@ -334,7 +334,7 @@ const skills = await directus.request(
|
||||
}
|
||||
}
|
||||
|
||||
/* Improved touch targets for mobile */
|
||||
/* Touch targets for mobile */
|
||||
@media (max-width: 640px) {
|
||||
a,
|
||||
button {
|
||||
@@ -366,7 +366,6 @@ const skills = await directus.request(
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Wait for the DOM to be fully loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const sliderTrack = document.querySelector('.slider-track');
|
||||
|
||||
@@ -375,9 +374,6 @@ const skills = await directus.request(
|
||||
const cards = document.querySelectorAll('.skill-card');
|
||||
if (!cards.length) return;
|
||||
|
||||
// Clone the first set of cards and append to create seamless loop
|
||||
const firstSetCount = cards.length / 3; // We have 3 sets in the markup
|
||||
|
||||
// Set proper animation based on screen size
|
||||
function updateScrollAnimation() {
|
||||
if (window.innerWidth >= 640) {
|
||||
@@ -462,9 +458,7 @@ const skills = await directus.request(
|
||||
|
||||
// Handle theme transition
|
||||
document.addEventListener('themeChange', () => {
|
||||
// Add special effects during theme transition
|
||||
cards.forEach((card, index) => {
|
||||
// Add staggered animation delay
|
||||
setTimeout(() => {
|
||||
card.classList.add('theme-changing');
|
||||
setTimeout(() => {
|
||||
|
@@ -116,8 +116,6 @@ const { post, nextPost, prevPost } = Astro.props;
|
||||
</BlogPost>
|
||||
|
||||
<script>
|
||||
// Removing TOC-related functions
|
||||
|
||||
// Add copy buttons to code blocks
|
||||
function initializeCodeCopyButtons() {
|
||||
const codeBlocks = document.querySelectorAll('pre');
|
||||
@@ -185,7 +183,6 @@ const { post, nextPost, prevPost } = Astro.props;
|
||||
|
||||
// Main initialization function
|
||||
function initializeBlogPost() {
|
||||
// Initialize remaining components
|
||||
initializeCodeCopyButtons();
|
||||
|
||||
// Scroll to hash if present in URL
|
||||
@@ -207,8 +204,6 @@ const { post, nextPost, prevPost } = Astro.props;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Removing TOC-related styles */
|
||||
|
||||
/* Language badge styling */
|
||||
.language-badge {
|
||||
font-family:
|
||||
|
@@ -22,19 +22,11 @@ const postsByYear = sortedPosts.reduce((acc, post) => {
|
||||
}, {});
|
||||
|
||||
const years = Object.keys(postsByYear).sort((a, b) => b - a);
|
||||
|
||||
// Get total post count
|
||||
const totalPosts = sortedPosts.length;
|
||||
|
||||
// Get unique tags for search suggestions
|
||||
const allTags = [...new Set(sortedPosts.flatMap((post) => post.tags || []))];
|
||||
---
|
||||
|
||||
<BaseLayout title="Blog">
|
||||
<div class="mx-auto w-full max-w-6xl px-4 py-10 sm:px-6 sm:py-16">
|
||||
<!-- Header with search -->
|
||||
<div class="relative mb-12 sm:mb-20">
|
||||
<!-- Decorative elements -->
|
||||
<div
|
||||
class="animate-blob absolute -top-10 -left-10 h-48 w-48 rounded-full bg-zinc-100 opacity-30 blur-3xl sm:-top-20 sm:-left-20 sm:h-72 sm:w-72 dark:bg-zinc-800/30"
|
||||
>
|
||||
@@ -124,7 +116,7 @@ const allTags = [...new Set(sortedPosts.flatMap((post) => post.tags || []))];
|
||||
)
|
||||
}
|
||||
|
||||
<!-- Improved sidebar for mobile -->
|
||||
<!-- Sidebar for mobile -->
|
||||
<div class="relative md:col-span-3">
|
||||
<div class="mb-8 space-y-4 md:sticky md:top-24 md:mb-0">
|
||||
<h3
|
||||
@@ -156,7 +148,7 @@ const allTags = [...new Set(sortedPosts.flatMap((post) => post.tags || []))];
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Improved post grid for mobile -->
|
||||
<!-- Post grid for mobile -->
|
||||
<div class="md:col-span-9">
|
||||
{
|
||||
years.map((year) => (
|
||||
@@ -168,7 +160,7 @@ const allTags = [...new Set(sortedPosts.flatMap((post) => post.tags || []))];
|
||||
<div
|
||||
class={`grid grid-cols-1 ${postsByYear[year].length >= 2 ? 'md:grid-cols-2' : 'md:grid-cols-1'} gap-8 sm:gap-12`}
|
||||
>
|
||||
{postsByYear[year].map((post, index) => (
|
||||
{postsByYear[year].map((post) => (
|
||||
<article class="group relative mx-auto flex h-full w-full max-w-sm flex-col sm:max-w-md md:mx-0">
|
||||
{post.image && (
|
||||
<div class="mb-4 h-48 overflow-hidden rounded-lg sm:h-56">
|
||||
@@ -303,7 +295,7 @@ const allTags = [...new Set(sortedPosts.flatMap((post) => post.tags || []))];
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Improved touch targets for mobile */
|
||||
/* Touch targets for mobile */
|
||||
@media (max-width: 640px) {
|
||||
a,
|
||||
button {
|
||||
@@ -315,7 +307,6 @@ const allTags = [...new Set(sortedPosts.flatMap((post) => post.tags || []))];
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Script không thay đổi - giữ nguyên chức năng
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const backToTopButton = document.getElementById('back-to-top');
|
||||
|
||||
@@ -341,7 +332,7 @@ const allTags = [...new Set(sortedPosts.flatMap((post) => post.tags || []))];
|
||||
|
||||
// Check scroll position
|
||||
window.addEventListener('scroll', toggleBackToTopButton);
|
||||
toggleBackToTopButton(); // Initial check
|
||||
toggleBackToTopButton();
|
||||
}
|
||||
|
||||
// Add smooth scrolling to year links
|
||||
|
@@ -22,10 +22,10 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
|
||||
---
|
||||
|
||||
<Layout title=`Home | ${global.name}`>
|
||||
<!-- Hero Section with improved mobile responsiveness -->
|
||||
<!-- Hero Section with mobile responsiveness -->
|
||||
<section class="theme-transition-all px-4 py-10 sm:px-6 sm:py-16 md:py-20">
|
||||
<div class="relative mx-auto max-w-2xl">
|
||||
<!-- Adjusted blob positions and sizes for better mobile appearance -->
|
||||
<!-- Adjusted blob positions and sizes for mobile appearance -->
|
||||
<div
|
||||
class="animate-blob theme-transition-bg absolute -top-10 -left-10 h-40 w-40 rounded-full bg-zinc-100 opacity-50 blur-3xl sm:-top-20 sm:-left-20 sm:h-64 sm:w-64 dark:bg-zinc-800/50"
|
||||
>
|
||||
@@ -85,7 +85,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Featured Post Section - Improved for mobile -->
|
||||
<!-- Featured post section -->
|
||||
<section
|
||||
class="theme-transition-all border-t border-zinc-100 px-4 py-10 sm:px-6 sm:py-12 md:py-16 dark:border-zinc-800"
|
||||
>
|
||||
@@ -124,7 +124,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Improved grid for better mobile layout -->
|
||||
<!-- Grid for mobile layout -->
|
||||
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 sm:gap-8 md:gap-12 lg:grid-cols-3">
|
||||
{
|
||||
recentPosts.map((post, index) => (
|
||||
@@ -215,7 +215,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Topics/Tags Section - Improved for mobile -->
|
||||
<!-- Topics section -->
|
||||
{
|
||||
allTags.length > 0 && (
|
||||
<section class="theme-transition-all border-t border-zinc-100 px-4 py-10 sm:px-6 sm:py-12 md:py-16 dark:border-zinc-800">
|
||||
@@ -279,7 +279,6 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
|
||||
<script>
|
||||
// Add hover effect for cards on touch devices
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Check if it's a touch device
|
||||
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
||||
|
||||
if (isTouchDevice) {
|
||||
@@ -297,11 +296,11 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
|
||||
});
|
||||
});
|
||||
|
||||
// Disable hover animations on touch devices for better performance
|
||||
// Disable hover animations on touch devices
|
||||
document.documentElement.classList.add('touch-device');
|
||||
}
|
||||
|
||||
// Improved viewport height fix for mobile browsers
|
||||
// Viewport height fix for mobile browsers
|
||||
const setVh = () => {
|
||||
const vh = window.innerHeight * 0.01;
|
||||
document.documentElement.style.setProperty('--vh', `${vh}px`);
|
||||
@@ -339,7 +338,7 @@ const allTags = [...new Set(posts.flatMap((post) => post.tags || []))].slice(0,
|
||||
});
|
||||
}
|
||||
|
||||
// Improved theme change handler that preserves scroll position and provides smoother transitions
|
||||
// Theme change handler that preserves scroll position and provides smoother transitions
|
||||
document.addEventListener('themeChanged', () => {
|
||||
// Store current scroll position
|
||||
const scrollPosition = window.scrollY;
|
||||
|
@@ -14,7 +14,6 @@ export async function getStaticPaths() {
|
||||
})
|
||||
);
|
||||
|
||||
// Get all unique tags
|
||||
const uniqueTags = [...new Set(posts.flatMap((post) => post.tags || []))];
|
||||
|
||||
// Create a path for each tag
|
||||
@@ -41,7 +40,6 @@ const sortedPosts =
|
||||
: [];
|
||||
console.log(`Sorted posts length: ${sortedPosts.length}`);
|
||||
|
||||
const tagHue = Math.abs(tag.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0) % 360);
|
||||
const relatedTags = [
|
||||
...new Set(sortedPosts.flatMap((post) => post.tags || []).filter((t) => t !== tag)),
|
||||
].slice(0, 5);
|
||||
@@ -49,7 +47,6 @@ const relatedTags = [
|
||||
|
||||
<BaseLayout title={`Posts tagged with "${tag}"`}>
|
||||
<div class="mx-auto max-w-5xl px-4 py-10 sm:py-16">
|
||||
<!-- Header section -->
|
||||
<div class="relative mb-10 sm:mb-16">
|
||||
<div
|
||||
class="animate-blob absolute -top-20 -left-20 h-48 w-48 rounded-full bg-zinc-100 opacity-30 blur-3xl sm:h-64 sm:w-64 dark:bg-zinc-900/30"
|
||||
@@ -277,7 +274,7 @@ const relatedTags = [
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty state với màu zinc -->
|
||||
<!-- Empty state -->
|
||||
{
|
||||
sortedPosts.length === 0 && (
|
||||
<div class="py-12 text-center sm:py-20">
|
||||
|
@@ -31,7 +31,6 @@ const sortedTags = [...tagObjects].sort((a, b) => b.count - a.count);
|
||||
|
||||
<BaseLayout title="Explore Tags">
|
||||
<div class="theme-transition-all mx-auto w-full px-3 py-6 sm:px-6 sm:py-12 md:py-16">
|
||||
<!-- Enhanced header section with animated elements - improved for mobile -->
|
||||
<div class="theme-transition-element relative mb-8 text-center sm:mb-12 md:mb-16">
|
||||
<div
|
||||
class="animate-blob theme-transition-bg absolute -top-16 -left-16 h-36 w-36 rounded-full bg-zinc-100 opacity-50 blur-3xl sm:h-48 sm:w-48 md:h-72 md:w-72 dark:bg-zinc-800/50"
|
||||
@@ -146,9 +145,7 @@ const sortedTags = [...tagObjects].sort((a, b) => b.count - a.count);
|
||||
</BaseLayout>
|
||||
|
||||
<script>
|
||||
// Ultra-reliable responsiveness handling
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Fix viewport width issues on mobile
|
||||
const fixViewportWidth = () => {
|
||||
// Force the viewport to be exactly the width of the device
|
||||
const viewport = document.querySelector('meta[name="viewport"]');
|
||||
@@ -378,7 +375,6 @@ const sortedTags = [...tagObjects].sort((a, b) => b.count - a.count);
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* Ultra-responsive breakpoints for extreme reliability */
|
||||
/* Micro screens (below 240px) */
|
||||
@media (max-width: 239px) {
|
||||
.tag-cloud {
|
||||
@@ -545,7 +541,7 @@ const sortedTags = [...tagObjects].sort((a, b) => b.count - a.count);
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
/* Improved shadow for dark mode */
|
||||
/* Shadow for dark mode */
|
||||
:global(.dark) .tag-cloud {
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(255, 255, 255, 0.05),
|
||||
|
@@ -1,4 +1,3 @@
|
||||
/* Remove all the complex mobile menu styles and keep only what's necessary */
|
||||
@import 'tailwindcss';
|
||||
|
||||
/* Dark mode support for Tailwind CSS v4 */
|
||||
@@ -43,7 +42,7 @@
|
||||
scroll-padding-top: 4rem;
|
||||
}
|
||||
|
||||
/* Better touch targets on mobile */
|
||||
/* Touch targets on mobile */
|
||||
button,
|
||||
a {
|
||||
@apply min-h-[44px];
|
||||
|
@@ -9,7 +9,7 @@ import * as FiIcons from 'react-icons/fi';
|
||||
import * as LuIcons from 'react-icons/lu';
|
||||
import * as SiIcons from 'react-icons/si';
|
||||
|
||||
// DynamicIcon.tsx - Load React Icon library dynamically from attributes from Directus
|
||||
// Load React Icon library dynamically from attributes in Directus
|
||||
|
||||
const iconSets = {
|
||||
fa: FaIcons,
|
Reference in New Issue
Block a user