Documentation Index
Fetch the complete documentation index at: https://docs.parvej.me/llms.txt
Use this file to discover all available pages before exploring further.
Customization Guide
Learn how to customize colors, fonts, components, and add new features.
Theme Customization
Colors
Edit tailwind.config.js to change the color scheme:
// tailwind.config.js
export default {
theme: {
extend: {
colors: {
primary: {
DEFAULT: '#1E8479',
light: '#07C983',
dark: '#105652',
},
secondary: '#F59E0B',
accent: '#8B5CF6',
}
}
}
};
Using Custom Colors
// In components
<button className="bg-primary hover:bg-primary-dark text-white">
Click Me
</button>
<div className="text-primary-light border-primary">
Content
</div>
CSS Variables
For dynamic theming, use CSS variables in src/index.css:
:root {
--color-primary: #1E8479;
--color-primary-light: #07C983;
--color-primary-dark: #105652;
--color-background: #ffffff;
--color-text: #1f2937;
}
/* Dark mode */
.dark {
--color-background: #1f2937;
--color-text: #f9fafb;
}
Adding New Pages
Public Page
Create Component
Create src/pages/NewPage.jsx:export default function NewPage() {
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-3xl font-bold">New Page</h1>
{/* Your content */}
</div>
);
}
Add Route
In src/App.jsx:import NewPage from './pages/NewPage';
// In Routes
<Route path="/new-page" element={<NewPage />} />
Add to Navigation
Update menu settings in admin or edit Header.jsx
Admin Page
Create Component
Create src/pages/Admin/NewFeature/NewFeature.jsx:export default function NewFeature() {
return (
<div className="p-6">
<h1 className="text-2xl font-bold mb-6">New Feature</h1>
{/* Admin content */}
</div>
);
}
Add Route
In src/App.jsx under admin routes:<Route path="new-feature" element={<NewFeature />} />
Add to Sidebar
Edit src/pages/Admin/AdminLayout.jsx to add menu item
Adding New Collections
1. Create in Appwrite
- Go to Appwrite Console → Databases → portfolio_db
- Create new collection with attributes
- Set permissions
2. Create Service
// src/lib/newFeatureService.js
import { databases, ID, Query } from './appwrite';
const DATABASE_ID = import.meta.env.VITE_APPWRITE_DATABASE_ID;
const COLLECTION_ID = 'new_collection';
export const newFeatureService = {
async list() {
return await databases.listDocuments(DATABASE_ID, COLLECTION_ID);
},
async get(id) {
return await databases.getDocument(DATABASE_ID, COLLECTION_ID, id);
},
async create(data) {
return await databases.createDocument(
DATABASE_ID,
COLLECTION_ID,
ID.unique(),
data
);
},
async update(id, data) {
return await databases.updateDocument(
DATABASE_ID,
COLLECTION_ID,
id,
data
);
},
async delete(id) {
return await databases.deleteDocument(DATABASE_ID, COLLECTION_ID, id);
}
};
3. Create Admin UI
Use existing admin pages as templates. Key patterns:
// List view with CRUD
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
loadItems();
}, []);
async function loadItems() {
setLoading(true);
const result = await newFeatureService.list();
setItems(result.documents);
setLoading(false);
}
async function handleDelete(id) {
if (confirm('Delete this item?')) {
await newFeatureService.delete(id);
loadItems();
}
}
Adding Font Selector to New Pages
import FontSelector from '../../../components/FontSelector/FontSelector';
const [titleFont, setTitleFont] = useState("'Open Sans', sans-serif");
// In your form
<FontSelector
value={titleFont}
onChange={setTitleFont}
label="Title Font"
previewText="Preview Text Here"
/>
Customizing Rich Text Editor
Edit src/components/RichTextEditor/RichTextEditor.jsx:
// Add to toolbar
<button
onClick={() => handleCustomAction()}
className="p-2 hover:bg-gray-100 rounded"
title="Custom Action"
>
<CustomIcon className="w-4 h-4" />
</button>
// Handler function
const handleCustomAction = () => {
document.execCommand('customCommand', false, 'value');
// Or manipulate selection directly
};
Add New Embed Type
// In embed handler
const supportedEmbeds = {
youtube: /youtube\.com|youtu\.be/,
vimeo: /vimeo\.com/,
// Add new type
spotify: /spotify\.com/,
};
Custom Components
Reusable Card Component
// src/components/Card/Card.jsx
export default function Card({
title,
description,
image,
onClick,
className = ''
}) {
return (
<div
className={`bg-white rounded-lg shadow-md overflow-hidden
hover:shadow-lg transition-shadow cursor-pointer ${className}`}
onClick={onClick}
>
{image && (
<img src={image} alt={title} className="w-full h-48 object-cover" />
)}
<div className="p-4">
<h3 className="font-bold text-lg mb-2">{title}</h3>
<p className="text-gray-600 text-sm">{description}</p>
</div>
</div>
);
}
Loading Spinner
// src/components/Spinner/Spinner.jsx
export default function Spinner({ size = 'md' }) {
const sizes = {
sm: 'w-4 h-4',
md: 'w-8 h-8',
lg: 'w-12 h-12'
};
return (
<div className={`${sizes[size]} animate-spin rounded-full
border-2 border-gray-300 border-t-primary`} />
);
}
Environment-Specific Config
// src/config.js
const config = {
development: {
apiUrl: 'http://localhost:5173',
debug: true,
},
production: {
apiUrl: 'https://yourdomain.com',
debug: false,
}
};
export default config[import.meta.env.MODE] || config.development;
Adding Analytics
Google Analytics
<!-- index.html -->
<script async src="https://www.googletagmanager.com/gtag/js?id=GA_ID"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'GA_ID');
</script>
Track Page Views
// src/App.jsx
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
function App() {
const location = useLocation();
useEffect(() => {
// Track page view
if (window.gtag) {
window.gtag('event', 'page_view', {
page_path: location.pathname,
});
}
}, [location]);
// ...
}
Best Practices
Keep components small and focused. If a component exceeds 200 lines, consider splitting it.
Use TypeScript for better type safety and IDE support in larger projects.
Create a constants.js file for magic strings and configuration values.
Never hardcode API keys or secrets. Always use environment variables.
Test thoroughly after customizations, especially database schema changes.