Skip to main content

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

1

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>
  );
}
2

Add Route

In src/App.jsx:
import NewPage from './pages/NewPage';

// In Routes
<Route path="/new-page" element={<NewPage />} />
3

Add to Navigation

Update menu settings in admin or edit Header.jsx

Admin Page

1

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>
  );
}
2

Add Route

In src/App.jsx under admin routes:
<Route path="new-feature" element={<NewFeature />} />
3

Add to Sidebar

Edit src/pages/Admin/AdminLayout.jsx to add menu item

Adding New Collections

1. Create in Appwrite

  1. Go to Appwrite Console → Databases → portfolio_db
  2. Create new collection with attributes
  3. 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

Add New Toolbar Button

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.