Desenvolupant amb passiΓ³ i un toc d'humor
A modern, multi-language portfolio website built with React, TypeScript, and Framer Motion. Features a distinctive design system with centralized configuration for branding, content, and styling.
- Multi-language Support: Catalan (default), Spanish, English
- Centralized Configuration: All colors, text, and branding in config files
- Smooth Animations: Framer Motion for delightful interactions
- Responsive Design: Mobile-first approach
- Manual Carousel: Scroll or arrow-controlled hero section
- Fixed Preview Projects: Split-screen layout with sticky preview
- Scroll-based Animations: Team image scales on scroll
- Primary: Black (#1a1a1a), White (#f5f5f5)
- Accent: Red (#e63946)
- Neutrals: Gray scale from 50-900
- Display: Space Grotesk (headers, bold statements)
- Body: Inter (readable, modern)
- Mono: Monospace (technical elements)
- Fast: 0.3s (interactions)
- Normal: 0.5s (transitions)
- Slow: 0.8s (page loads)
duckhats/
βββ config/
β βββ brand.config.ts # Colors, typography, spacing, animations
β βββ content.config.ts # All text content (CA/ES/EN)
β βββ team.config.ts # Team member data
βββ hooks/
β βββ useLanguage.ts # Language state management (Zustand)
β βββ useContent.ts # Access translated content
βββ components/
β βββ Navbar.tsx # Pill-shaped navigation
β βββ Hero.tsx # Manual carousel hero section
β βββ Projects.tsx # Split-screen project showcase
β βββ About.tsx # Company story
β βββ Team.tsx # Team photo with scroll animation
β βββ Members.tsx # Team member grid
β βββ Footer.tsx # Site footer
β βββ FullScreenMenu.tsx # Mobile/hamburger menu
β βββ JoinUs.tsx # CTA section
βββ pages/
β βββ HomePage.tsx
β βββ AboutPage.tsx
β βββ TeamPage.tsx
β βββ ProjectsPage.tsx
β βββ JoinUsPage.tsx
βββ App.tsx # Main app with routing
- Node.js 16+
- npm or yarn
# Clone the repository
git clone https://github.com/yourusername/duckhats.git
# Navigate to project
cd duckhats
# Install dependencies
npm install
# Start development server
npm startnpm run buildLanguages are managed through Zustand with localStorage persistence:
import { useLanguage } from "./hooks/useLanguage";
import { useContent } from "./hooks/useContent";
function Component() {
const { language, setLanguage } = useLanguage();
const content = useContent();
return <h1>{content.nav.home}</h1>;
}- Add language code to
config/content.config.ts - Translate all content keys
- Update
Languagetype - Add language toggle in Navbar
All branding is centralized in config/brand.config.ts:
export const brandConfig = {
name: "DuckHats",
colors: {
primary: { black: "#1a1a1a", white: "#f5f5f5" },
accent: { red: "#e63946" },
// ...
},
typography: {
/* ... */
},
animation: {
/* ... */
},
};import { brandConfig } from "../config/brand.config";
// In JSX:
<div
style={{
backgroundColor: brandConfig.colors.primary.white,
color: brandConfig.colors.primary.black,
}}
>
{brandConfig.name}
</div>;All text content lives in config/content.config.ts:
export const contentConfig = {
ca: {
nav: { home: "Inici", about: "Qui Som" /* ... */ },
hero: { tagline: "Desenvolupant amb passiΓ³..." /* ... */ },
// ...
},
es: {
/* Spanish translations */
},
en: {
/* English translations */
},
};- Framework: React 18
- Language: TypeScript
- Styling: Tailwind CSS
- Animation: Framer Motion
- Routing: React Router v6
- State: Zustand (language management)
- Build: Create React App / Vite
{
"react": "^18.2.0",
"react-router-dom": "^6.x",
"framer-motion": "^10.x",
"zustand": "^4.x",
"lucide-react": "^0.x"
}Always use brandConfig for colors:
// β Don't
<div className="bg-black text-white">
// β
Do
<div style={{
backgroundColor: brandConfig.colors.primary.black,
color: brandConfig.colors.primary.white
}}>Use brandConfig durations and easing:
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{
duration: brandConfig.animation.duration.normal,
ease: brandConfig.animation.easing.default,
}}
/>Always use content hooks:
const content = useContent();
return <h1>{content.hero.tagline}</h1>;- Never hardcode colors - Use
brandConfig.colors - Never hardcode text - Use
contentConfigviauseContent() - Use named exports - No default exports
- Mobile-first - Design for mobile, enhance for desktop
- Semantic HTML - Use proper tags (header, nav, section, etc.)
- Accessibility - ARIA labels, keyboard navigation
- Consistent spacing - Use brandConfig spacing values
Β© 2025 DuckHats. All rights reserved.
We're always looking for new ducks to join our pond! Designers, developers, and dreamers welcome.
Contact: info@duckhats.cat
Built with β€οΈ and code from the pond π¦