A modern web development agency website built with Next.js 14 and MDX.
- Framework: Next.js 14 (App Router)
- Language: TypeScript
- Content: MDX (Markdown + React Components)
- Styling: Tailwind CSS
- UI Components: shadcn/ui
- Email: Resend
- State Management: Zustand
- ✅ MDX-based blog with full-text search
- ✅ Dark mode support
- ✅ Responsive design
- ✅ SEO optimized
- ✅ Contact form with email notifications
- ✅ Reading time estimation
- ✅ Tag-based content filtering
- ✅ Featured posts
- ✅ Type-safe development
- Node.js 18+
- npm or yarn
- Clone the repository:
git clone [repository-url]
cd digital-studio- Install dependencies:
npm install- Create a
.env.localfile with your environment variables:
RESEND_API_KEY=your-resend-api-key- Run the development server:
npm run devOpen http://localhost:3000 to view the application.
├── app/
│ ├── (site)/ # Main site routes
│ └── api/ # API routes
├── components/
│ ├── ui/ # shadcn/ui components
│ └── [feature]/ # Feature-specific components
├── content/
│ ├── posts/ # Blog posts in MDX format
│ ├── pages/ # Static pages in MDX
│ └── data/ # JSON/YAML data files
├── lib/
│ ├── mdx.ts # MDX utilities
│ └── utils.ts # Utility functions
├── public/
│ └── images/ # Static images
└── typing.d.ts # Global type definitions
Create a new .mdx file in content/posts/:
---
title: Your Post Title
description: A brief description of your post
date: 2024-01-18
tags: ['Next.js', 'React']
categories: ['Tutorial']
author: Your Name
featured: false
image: /images/post-image.jpg
imageAlt: Description of the image
---
# Your Post Title
Your content here...You can use React components directly in your MDX files:
import { Button } from '@/components/ui/button';
<Button>Click me!</Button>An interactive before/after image comparison slider with keyboard navigation, animations, and extensive customization options.
Demo (Internal/Development Only): http://localhost:3000/before-after-demo
Component Location: src/components/BeforeAfterSlider.tsx
| Prop | Type | Default | Description |
|---|---|---|---|
| beforeImage | string |
required | URL of the before image |
| afterImage | string |
required | URL of the after image |
| beforeLabel | string |
"Before" |
Label for the before image |
| afterLabel | string |
"After" |
Label for the after image |
| title | string |
- | Optional title above/below the slider |
| description | string |
- | Optional description text |
| titleClassName | string |
- | Custom CSS classes for title |
| descriptionClassName | string |
- | Custom CSS classes for description |
| headerPosition | "top" | "bottom" |
"top" |
Position of title/description |
| className | string |
- | Additional CSS classes for the container |
| aspectRatio | string |
"16/9" |
CSS aspect ratio (e.g., "4/3", "1/1") |
| initialPosition | number |
35 |
Initial slider position (0-100) |
| labelPosition | LabelPosition |
"bottom-left" |
Default position for both labels |
| beforeLabelPosition | LabelPosition |
- | Override position for before label |
| afterLabelPosition | LabelPosition |
- | Override position for after label |
| labelClassName | string |
- | CSS classes for both labels |
| beforeLabelClassName | string |
- | Override classes for before label |
| afterLabelClassName | string |
- | Override classes for after label |
| sliderLineWidth | number |
2 |
Width of the slider line in pixels |
| sliderLineColor | string |
"white" |
Color of the slider line |
| sliderHandleColor | string |
"white" |
Color of the slider handle |
| sliderHandleSize | "sm" | "md" | "lg" |
"md" |
Size of the slider handle |
| animateOnHover | boolean |
false |
Enable hover animations |
| transitionDuration | number |
0 |
Transition duration in milliseconds |
| overlayOpacity | number |
0 |
Dark overlay opacity (0-1) |
| borderRadius | string |
- | Custom border radius (e.g., "1rem") |
| disabled | boolean |
false |
Disable all interactions |
| onPositionChange | function |
- | Callback when position changes |
| ariaLabelBefore | string |
- | Accessibility label for before image |
| ariaLabelAfter | string |
- | Accessibility label for after image |
Label Positions: "bottom-left", "bottom-right", "top-left", "top-right", "bottom-center", "top-center"
Basic Usage:
import BeforeAfterSlider from '@/components/BeforeAfterSlider';
<BeforeAfterSlider
beforeImage="/images/before.jpg"
afterImage="/images/after.jpg"
/>;With Title and Description:
<BeforeAfterSlider
beforeImage="/images/before.jpg"
afterImage="/images/after.jpg"
title="Website Redesign"
description="See how our modern redesign transformed the user experience"
beforeLabel="Old Design"
afterLabel="New Design"
/>Advanced Customization:
<BeforeAfterSlider
beforeImage="/images/before.jpg"
afterImage="/images/after.jpg"
title="UI Transformation"
description="Modern design with improved accessibility"
beforeLabel="Original"
afterLabel="Enhanced"
initialPosition={50}
labelPosition="top-center"
sliderHandleSize="lg"
animateOnHover={true}
transitionDuration={200}
overlayOpacity={0.1}
borderRadius="1rem"
className="shadow-2xl"
onPositionChange={(pos) => console.log(`Position: ${pos}%`)}
/>Custom Styling:
<BeforeAfterSlider
beforeImage="/images/before.jpg"
afterImage="/images/after.jpg"
sliderLineWidth={4}
sliderLineColor="#3b82f6"
sliderHandleColor="#3b82f6"
aspectRatio="4/3"
borderRadius="1.5rem"
className="mx-auto max-w-2xl"
/>When the slider is focused, users can navigate with:
- Arrow Left/Right: Move slider by 5%
- Home: Jump to start (0%)
- End: Jump to end (100%)
The project includes a custom favicon based on the logo component with a gradient design.
public/favicon.svg- SVG favicon for modern browserspublic/favicon.ico- Legacy faviconpublic/favicon-16x16.png- 16x16 PNG faviconpublic/favicon-32x32.png- 32x32 PNG faviconpublic/apple-touch-icon.png- 180x180 Apple touch icon
To generate new favicon PNG files from the logo, use the FaviconGenerator component:
- Temporarily add the component to a page:
import FaviconGenerator from '@/components/FaviconGenerator';
// In your page component
<FaviconGenerator />;- Visit the page and use the download buttons to get PNG files in different sizes
- Place the downloaded files in the
public/directory - For
.icofile, use an online converter with the 16x16 and 32x32 PNG files
The favicon metadata is already configured in src/app/(site)/layout.tsx.
npm run dev- Start development servernpm run build- Build for productionnpm run start- Start production servernpm run lint- Run ESLintnpm run typecheck- Run TypeScript type checking
The application is optimized for deployment on Vercel:
- Push your code to GitHub
- Import the repository in Vercel
- Add environment variables
- Deploy
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License.