feat(ui): add Container component and replace manual container divs

Implement a reusable Container component with size variants (sm, md, lg, xl, full) to standardize content width across the application. Replace all manual container divs with the new component for consistent styling and maintainability.
This commit is contained in:
joyzhao
2025-06-19 18:10:49 +08:00
parent b4a8d13cdd
commit 5fcf7a9d33
12 changed files with 97 additions and 46 deletions

View File

@@ -3,6 +3,7 @@ import { personalInfo } from "@/lib/data";
import { motion } from "framer-motion";
import { useState, useEffect } from "react";
import { defaultLang } from "@/i18n/ui";
import Container from "./ui/Container";
import { type FooterProps } from "@/types";
export default function Footer({ lang: propLang }: FooterProps) {
@@ -20,7 +21,7 @@ export default function Footer({ lang: propLang }: FooterProps) {
const t = useTranslations(lang);
return (
<footer className="border-t border-purple-500/10 py-6 bg-gradient-to-b from-background to-muted/20 backdrop-blur-sm">
<div className="container max-w-4xl mx-auto px-6 md:px-4">
<Container size="sm">
<motion.div
className="flex flex-col md:flex-row justify-between items-center"
initial={{ opacity: 0, y: 20 }}
@@ -67,7 +68,7 @@ export default function Footer({ lang: propLang }: FooterProps) {
</motion.span>
</motion.p>
</motion.div>
</div>
</Container>
</footer>
);
}

View File

@@ -1,6 +1,7 @@
import { personalInfo } from "@/lib/data";
import LanguageSwitcher from "./LanguageSwitcher";
import ThemeToggle from "./ui/theme-toggle";
import Container from "./ui/Container";
import { useTranslations, getLocalizedPath, type Lang } from "@/i18n/utils";
import { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
@@ -43,7 +44,7 @@ export default function GlassHeader({ lang: propLang }: GlassHeaderProps) {
? 'backdrop-blur-md backdrop-filter bg-white/80 dark:bg-black/80 border-b border-border/30 shadow-lg shadow-black/5 dark:shadow-black/20'
: 'bg-transparent'
}`}>
<div className="container max-w-4xl mx-auto p-4 flex justify-between items-center">
<Container size="sm" className="p-4 flex justify-between items-center">
<a
className="flex items-center text-lg font-medium transition-opacity duration-150 hover:opacity-80"
href={getLocalizedPath('/', lang)}
@@ -85,7 +86,7 @@ export default function GlassHeader({ lang: propLang }: GlassHeaderProps) {
{isMenuOpen ? <X size={24} /> : <Menu size={24} />}
</button>
</div>
</div>
</Container>
{/* Mobile Navigation */}
<div className={`md:hidden overflow-hidden transition-all duration-200 ease-out ${

View File

@@ -0,0 +1,39 @@
import { cn } from "@/lib/utils";
import { type ReactNode } from "react";
interface ContainerProps {
children: ReactNode;
className?: string;
size?: "sm" | "md" | "lg" | "xl" | "full";
}
/**
* Container component for consistent content width across the site
* @param children - The content to be wrapped
* @param className - Additional classes to apply
* @param size - Container size variant (sm: max-w-4xl, md: max-w-6xl, lg: max-w-7xl, xl: max-w-screen-2xl, full: w-full)
*/
export default function Container({
children,
className,
size = "md"
}: ContainerProps) {
// Map size variants to max-width classes
const sizeClasses = {
sm: "max-w-4xl", // Same as header/footer (narrower content)
md: "max-w-6xl", // Default for most content areas
lg: "max-w-7xl", // For wider content like blog posts with sidebars
xl: "max-w-screen-2xl", // For very wide content
full: "w-full" // No max width constraint
};
return (
<div className={cn(
"container mx-auto px-4 sm:px-6 lg:px-8",
sizeClasses[size],
className
)}>
{children}
</div>
);
}