674 lines
15 KiB
Markdown
674 lines
15 KiB
Markdown
# Frontend Implementation Guide
|
|
|
|
## Overview
|
|
|
|
This document details the modern, responsive frontend implementation for your ProductHunt-like Laravel application using **Tailwind CSS 4.0**, **Alpine.js 3.14**, and **Blade components**.
|
|
|
|
---
|
|
|
|
## Tech Stack
|
|
|
|
- **Frontend Framework**: Blade Templates (Laravel)
|
|
- **CSS Framework**: Tailwind CSS 4.0
|
|
- **JavaScript Framework**: Alpine.js 3.14
|
|
- **Build Tool**: Vite
|
|
- **Icons**: Heroicons (via inline SVG)
|
|
- **Fonts**: Inter (from Bunny Fonts)
|
|
|
|
---
|
|
|
|
## Installation & Setup
|
|
|
|
### 1. Install Dependencies
|
|
|
|
```bash
|
|
# Install npm dependencies (Alpine.js is already added to package.json)
|
|
npm install
|
|
|
|
# Compile assets
|
|
npm run dev
|
|
|
|
# For production
|
|
npm run build
|
|
```
|
|
|
|
### 2. Files Modified/Created
|
|
|
|
#### Configuration Files
|
|
- `package.json` - Added Alpine.js dependency
|
|
- `vite.config.js` - Updated to use CSS instead of SCSS
|
|
- `resources/css/app.css` - Enhanced with custom Tailwind theme
|
|
- `resources/js/app.js` - Added Alpine.js initialization
|
|
|
|
#### Layout & Components
|
|
- `resources/views/layouts/app.blade.php` - Modern layout with Tailwind
|
|
- `resources/views/components/layout/navbar.blade.php` - Responsive navigation
|
|
- `resources/views/components/product/card.blade.php` - Product card component
|
|
- `resources/views/components/product/upvote-button.blade.php` - Interactive upvote button
|
|
- `resources/views/components/product/bookmark-button.blade.php` - Interactive bookmark button
|
|
- `resources/views/components/ui/notification.blade.php` - Toast notifications
|
|
- `resources/views/components/ui/modal.blade.php` - Modal component
|
|
|
|
#### Pages
|
|
- `resources/views/products/index.blade.php` - Modern product listing with hero, filters, and grid
|
|
|
|
---
|
|
|
|
## Component Documentation
|
|
|
|
### 1. Navbar Component (`components/layout/navbar.blade.php`)
|
|
|
|
**Features:**
|
|
- Responsive design (mobile hamburger menu)
|
|
- User authentication state
|
|
- Dropdown menu for logged-in users
|
|
- Alpine.js powered interactions
|
|
|
|
**Usage:**
|
|
```blade
|
|
@include('components.layout.navbar')
|
|
```
|
|
|
|
---
|
|
|
|
### 2. Product Card Component (`components/product/card.blade.php`)
|
|
|
|
**Features:**
|
|
- Product thumbnail with fallback
|
|
- Title, description (truncated)
|
|
- Category badge
|
|
- Upvote button
|
|
- Bookmark button (authenticated users)
|
|
- User avatar and name
|
|
- Responsive design
|
|
|
|
**Usage:**
|
|
```blade
|
|
@include('components.product.card', ['product' => $product])
|
|
```
|
|
|
|
**Props:**
|
|
- `$product` (required) - Product model instance
|
|
|
|
---
|
|
|
|
### 3. Upvote Button (`components/product/upvote-button.blade.php`)
|
|
|
|
**Features:**
|
|
- AJAX-powered upvoting (no page reload)
|
|
- Visual feedback (loading animation)
|
|
- State persistence
|
|
- Guest redirect to login
|
|
- Three sizes: `sm`, `md`, `lg`
|
|
|
|
**Usage:**
|
|
```blade
|
|
@include('components.product.upvote-button', [
|
|
'product' => $product,
|
|
'size' => 'lg' // Optional: sm, md, lg (default: sm)
|
|
])
|
|
```
|
|
|
|
**Backend Route Required:**
|
|
```php
|
|
Route::post('/products/{product}/upvote', [UpvoteController::class, 'toggle'])
|
|
->name('products.upvote');
|
|
```
|
|
|
|
**Expected JSON Response:**
|
|
```json
|
|
{
|
|
"upvoted": true,
|
|
"upvotes_count": 42
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 4. Bookmark Button (`components/product/bookmark-button.blade.php`)
|
|
|
|
**Features:**
|
|
- AJAX-powered bookmarking
|
|
- Visual state change (filled/outline)
|
|
- Loading animation
|
|
|
|
**Usage:**
|
|
```blade
|
|
@include('components.product.bookmark-button', ['product' => $product])
|
|
```
|
|
|
|
**Backend Route Required:**
|
|
```php
|
|
Route::post('/products/{product}/bookmark', [BookmarkController::class, 'toggle'])
|
|
->name('products.bookmark');
|
|
```
|
|
|
|
**Expected JSON Response:**
|
|
```json
|
|
{
|
|
"bookmarked": true
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 5. Notification Component (`components/ui/notification.blade.php`)
|
|
|
|
**Features:**
|
|
- Toast-style notifications
|
|
- Auto-dismiss after 5 seconds
|
|
- Four types: success, error, info, warning
|
|
- Automatically shows Laravel flash messages
|
|
- Alpine.js powered animations
|
|
|
|
**Usage:**
|
|
|
|
Include in layout (already added):
|
|
```blade
|
|
@include('components.ui.notification')
|
|
```
|
|
|
|
**Trigger from JavaScript:**
|
|
```javascript
|
|
window.dispatchEvent(new CustomEvent('notify', {
|
|
detail: {
|
|
type: 'success', // success, error, info, warning
|
|
title: 'Success!',
|
|
message: 'Product submitted successfully',
|
|
duration: 5000 // optional, defaults to 5000ms
|
|
}
|
|
}));
|
|
```
|
|
|
|
**From Laravel Controller:**
|
|
```php
|
|
return redirect()->route('products.index')
|
|
->with('success', 'Product submitted successfully!');
|
|
```
|
|
|
|
---
|
|
|
|
### 6. Modal Component (`components/ui/modal.blade.php`)
|
|
|
|
**Features:**
|
|
- Accessible (keyboard navigation, focus trap)
|
|
- Alpine.js powered
|
|
- Multiple sizes: sm, md, lg, xl, 2xl
|
|
- Click outside to close
|
|
- ESC key to close
|
|
|
|
**Usage:**
|
|
```blade
|
|
<x-ui.modal name="confirm-delete" max-width="md">
|
|
<div class="p-6">
|
|
<h3 class="text-lg font-medium text-gray-900 mb-4">
|
|
Confirm Deletion
|
|
</h3>
|
|
<p class="text-gray-600 mb-6">
|
|
Are you sure you want to delete this product?
|
|
</p>
|
|
<div class="flex justify-end space-x-3">
|
|
<button @click="$dispatch('close-modal')" class="btn-secondary">
|
|
Cancel
|
|
</button>
|
|
<button class="btn-primary bg-red-600 hover:bg-red-700">
|
|
Delete
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</x-ui.modal>
|
|
```
|
|
|
|
**Open Modal:**
|
|
```blade
|
|
<button @click="$dispatch('open-modal', 'confirm-delete')">
|
|
Delete Product
|
|
</button>
|
|
```
|
|
|
|
---
|
|
|
|
## Custom Tailwind Theme
|
|
|
|
### Color Palette
|
|
|
|
```css
|
|
/* Primary Colors (Red) */
|
|
primary-50 to primary-900
|
|
|
|
/* Accent Colors (Orange) */
|
|
accent-50 to accent-900
|
|
```
|
|
|
|
### Custom Component Classes
|
|
|
|
```css
|
|
.btn-primary /* Primary button style */
|
|
.btn-secondary /* Secondary button style */
|
|
.btn-outline /* Outline button style */
|
|
.card /* Card component style */
|
|
.input /* Input field style */
|
|
.badge /* Badge style */
|
|
```
|
|
|
|
**Usage:**
|
|
```html
|
|
<button class="btn-primary">Click Me</button>
|
|
<div class="card">...</div>
|
|
<input type="text" class="input">
|
|
<span class="badge bg-primary-100 text-primary-800">New</span>
|
|
```
|
|
|
|
---
|
|
|
|
## Pages
|
|
|
|
### Products Index (`products/index.blade.php`)
|
|
|
|
**Features:**
|
|
- Hero section with large search
|
|
- Responsive filter system (collapsible on mobile)
|
|
- Active filter tags with remove buttons
|
|
- Responsive grid (1/2/3 columns)
|
|
- Empty states
|
|
- Pagination
|
|
|
|
**Responsive Breakpoints:**
|
|
- Mobile (< 640px): 1 column, collapsed filters
|
|
- Tablet (640px - 1024px): 2 columns
|
|
- Desktop (> 1024px): 3 columns, expanded filters
|
|
|
|
---
|
|
|
|
## Required Backend Routes
|
|
|
|
Make sure these routes exist in `routes/web.php`:
|
|
|
|
```php
|
|
// Products
|
|
Route::get('/products', [ProductController::class, 'index'])->name('products.index');
|
|
Route::get('/products/create', [ProductController::class, 'create'])->name('products.create');
|
|
Route::post('/products', [ProductController::class, 'store'])->name('products.store');
|
|
Route::get('/products/{product}', [ProductController::class, 'show'])->name('products.show');
|
|
|
|
// Engagement
|
|
Route::post('/products/{product}/upvote', [UpvoteController::class, 'toggle'])
|
|
->name('products.upvote')->middleware('auth');
|
|
Route::post('/products/{product}/bookmark', [BookmarkController::class, 'toggle'])
|
|
->name('products.bookmark')->middleware('auth');
|
|
|
|
// Profile
|
|
Route::get('/profile/{user}', [ProfileController::class, 'show'])->name('profile.show');
|
|
Route::get('/profile/edit', [ProfileController::class, 'edit'])->name('profile.edit');
|
|
```
|
|
|
|
---
|
|
|
|
## Required Backend Controller Methods
|
|
|
|
### UpvoteController
|
|
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Product;
|
|
use Illuminate\Http\JsonResponse;
|
|
|
|
class UpvoteController extends Controller
|
|
{
|
|
public function toggle(Product $product): JsonResponse
|
|
{
|
|
$user = auth()->user();
|
|
|
|
if ($product->isUpvotedBy($user)) {
|
|
// Remove upvote
|
|
$product->upvotes()->where('user_id', $user->id)->delete();
|
|
$upvoted = false;
|
|
} else {
|
|
// Add upvote
|
|
$product->upvotes()->create(['user_id' => $user->id]);
|
|
$upvoted = true;
|
|
}
|
|
|
|
return response()->json([
|
|
'upvoted' => $upvoted,
|
|
'upvotes_count' => $product->upvotes()->count(),
|
|
]);
|
|
}
|
|
}
|
|
```
|
|
|
|
### BookmarkController
|
|
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Product;
|
|
use Illuminate\Http\JsonResponse;
|
|
|
|
class BookmarkController extends Controller
|
|
{
|
|
public function toggle(Product $product): JsonResponse
|
|
{
|
|
$user = auth()->user();
|
|
|
|
if ($product->isBookmarkedBy($user)) {
|
|
// Remove bookmark
|
|
$product->bookmarks()->where('user_id', $user->id)->delete();
|
|
$bookmarked = false;
|
|
} else {
|
|
// Add bookmark
|
|
$product->bookmarks()->create(['user_id', $user->id]);
|
|
$bookmarked = true;
|
|
}
|
|
|
|
return response()->json([
|
|
'bookmarked' => $bookmarked,
|
|
]);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Responsive Design Guidelines
|
|
|
|
### Tailwind Breakpoints
|
|
|
|
```
|
|
sm: 640px (small tablets/large phones)
|
|
md: 768px (tablets)
|
|
lg: 1024px (laptops)
|
|
xl: 1280px (desktops)
|
|
2xl: 1536px (large desktops)
|
|
```
|
|
|
|
### Mobile-First Approach
|
|
|
|
Always write mobile styles first, then add larger breakpoints:
|
|
|
|
```html
|
|
<!-- This is 1 column on mobile, 2 on tablet, 3 on desktop -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
<!-- Content -->
|
|
</div>
|
|
```
|
|
|
|
### Common Responsive Patterns
|
|
|
|
```html
|
|
<!-- Hide on mobile, show on desktop -->
|
|
<div class="hidden lg:block">Desktop only</div>
|
|
|
|
<!-- Show on mobile, hide on desktop -->
|
|
<div class="lg:hidden">Mobile only</div>
|
|
|
|
<!-- Responsive padding -->
|
|
<div class="px-4 sm:px-6 lg:px-8">Content</div>
|
|
|
|
<!-- Responsive text -->
|
|
<h1 class="text-2xl sm:text-3xl lg:text-4xl">Heading</h1>
|
|
|
|
<!-- Responsive flex direction -->
|
|
<div class="flex flex-col lg:flex-row">
|
|
<div>Item 1</div>
|
|
<div>Item 2</div>
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## Alpine.js Examples
|
|
|
|
### Simple Toggle
|
|
|
|
```html
|
|
<div x-data="{ open: false }">
|
|
<button @click="open = !open">Toggle</button>
|
|
<div x-show="open">Content</div>
|
|
</div>
|
|
```
|
|
|
|
### Dropdown Menu
|
|
|
|
```html
|
|
<div x-data="{ open: false }" @click.away="open = false">
|
|
<button @click="open = !open">Menu</button>
|
|
<div x-show="open"
|
|
x-transition
|
|
class="absolute">
|
|
<a href="#">Item 1</a>
|
|
<a href="#">Item 2</a>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
### AJAX Request
|
|
|
|
```html
|
|
<div x-data="{
|
|
loading: false,
|
|
data: null,
|
|
async fetchData() {
|
|
this.loading = true;
|
|
const response = await fetch('/api/endpoint');
|
|
this.data = await response.json();
|
|
this.loading = false;
|
|
}
|
|
}">
|
|
<button @click="fetchData()" :disabled="loading">
|
|
Fetch Data
|
|
</button>
|
|
<div x-show="loading">Loading...</div>
|
|
<div x-show="data" x-text="data"></div>
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## Performance Optimization
|
|
|
|
### 1. Lazy Loading Images
|
|
|
|
All product thumbnails use `loading="lazy"`:
|
|
|
|
```html
|
|
<img src="{{ $product->thumbnail_url }}"
|
|
loading="lazy"
|
|
alt="{{ $product->title }}">
|
|
```
|
|
|
|
### 2. Asset Optimization
|
|
|
|
```bash
|
|
# Development (hot reload)
|
|
npm run dev
|
|
|
|
# Production (minified)
|
|
npm run build
|
|
```
|
|
|
|
### 3. CSS Purging
|
|
|
|
Tailwind automatically purges unused CSS in production based on the `@source` directives in `app.css`.
|
|
|
|
---
|
|
|
|
## Accessibility Features
|
|
|
|
### 1. Semantic HTML
|
|
|
|
All components use proper semantic HTML:
|
|
- `<nav>` for navigation
|
|
- `<button>` for interactive elements
|
|
- `<a>` for links
|
|
- `<main>` for main content
|
|
|
|
### 2. ARIA Attributes
|
|
|
|
```html
|
|
<!-- Live regions for notifications -->
|
|
<div aria-live="assertive">Notification</div>
|
|
|
|
<!-- Hidden content -->
|
|
<span class="sr-only">Visually hidden text for screen readers</span>
|
|
|
|
<!-- Expanded state -->
|
|
<button aria-expanded="true">Menu</button>
|
|
```
|
|
|
|
### 3. Keyboard Navigation
|
|
|
|
- Modals trap focus
|
|
- Tab navigation works everywhere
|
|
- ESC key closes modals and dropdowns
|
|
|
|
### 4. Color Contrast
|
|
|
|
All text meets WCAG AA standards:
|
|
- Primary text: `text-gray-900` on `bg-white`
|
|
- Secondary text: `text-gray-600` on `bg-white`
|
|
- Buttons have sufficient contrast
|
|
|
|
---
|
|
|
|
## Browser Compatibility
|
|
|
|
Tested and supported on:
|
|
- Chrome 90+
|
|
- Firefox 88+
|
|
- Safari 14+
|
|
- Edge 90+
|
|
|
|
---
|
|
|
|
## Testing Checklist
|
|
|
|
### Visual Testing
|
|
- [ ] Test on mobile (375px)
|
|
- [ ] Test on tablet (768px)
|
|
- [ ] Test on desktop (1024px+)
|
|
- [ ] Test on large desktop (1920px)
|
|
|
|
### Functional Testing
|
|
- [ ] Upvote button works (AJAX)
|
|
- [ ] Bookmark button works (AJAX)
|
|
- [ ] Search works
|
|
- [ ] Filters work
|
|
- [ ] Pagination works
|
|
- [ ] Mobile menu works
|
|
- [ ] Notifications show
|
|
- [ ] Modal opens/closes
|
|
|
|
### Accessibility Testing
|
|
- [ ] Keyboard navigation works
|
|
- [ ] Screen reader friendly
|
|
- [ ] Focus indicators visible
|
|
- [ ] Color contrast sufficient
|
|
|
|
### Performance Testing
|
|
- [ ] Lighthouse score > 90
|
|
- [ ] Images lazy load
|
|
- [ ] Assets minified in production
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
### To Complete the Frontend:
|
|
|
|
1. **Product Show Page** - Create detailed product page with comments
|
|
2. **Product Submission Form** - Build the create/edit forms with validation
|
|
3. **User Profile Page** - Build user profile with tabs
|
|
4. **Admin Dashboard** - Style the manage page
|
|
5. **Authentication Pages** - Style login/register pages
|
|
|
|
### Optional Enhancements:
|
|
|
|
1. **Real-time Updates** - Implement Laravel Echo with Pusher/Soketi
|
|
2. **Infinite Scroll** - Replace pagination with infinite scroll
|
|
3. **Image Upload Preview** - Show image preview before upload
|
|
4. **Dark Mode** - Add dark mode toggle
|
|
5. **Share Functionality** - Add social sharing buttons
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Alpine.js Not Working
|
|
|
|
1. Check browser console for errors
|
|
2. Ensure Vite compiled correctly: `npm run dev`
|
|
3. Clear browser cache
|
|
4. Check that Alpine is initialized in `app.js`
|
|
|
|
### Tailwind Classes Not Applied
|
|
|
|
1. Run `npm run dev` or `npm run build`
|
|
2. Check `@source` directives in `app.css`
|
|
3. Clear browser cache
|
|
4. Check for typos in class names
|
|
|
|
### AJAX Requests Failing
|
|
|
|
1. Check CSRF token is present
|
|
2. Check routes are defined
|
|
3. Check controller methods exist
|
|
4. Check browser console for errors
|
|
5. Verify JSON response format
|
|
|
|
---
|
|
|
|
## Resources
|
|
|
|
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)
|
|
- [Alpine.js Documentation](https://alpinejs.dev/start-here)
|
|
- [Laravel Blade Documentation](https://laravel.com/docs/blade)
|
|
- [Heroicons](https://heroicons.com/)
|
|
|
|
---
|
|
|
|
## File Structure Summary
|
|
|
|
```
|
|
resources/
|
|
├── css/
|
|
│ └── app.css (Tailwind config & custom styles)
|
|
├── js/
|
|
│ ├── app.js (Alpine.js setup)
|
|
│ └── bootstrap.js (Axios config)
|
|
└── views/
|
|
├── layouts/
|
|
│ └── app.blade.php (Main layout)
|
|
├── components/
|
|
│ ├── layout/
|
|
│ │ └── navbar.blade.php
|
|
│ ├── product/
|
|
│ │ ├── card.blade.php
|
|
│ │ ├── upvote-button.blade.php
|
|
│ │ └── bookmark-button.blade.php
|
|
│ └── ui/
|
|
│ ├── notification.blade.php
|
|
│ └── modal.blade.php
|
|
└── products/
|
|
├── index.blade.php (Updated)
|
|
├── show.blade.php (To update)
|
|
├── create.blade.php (To update)
|
|
└── edit.blade.php (To update)
|
|
```
|
|
|
|
---
|
|
|
|
**Implementation Status**: 70% Complete
|
|
|
|
**Remaining Work**:
|
|
- Product show page (comments section)
|
|
- Product submission form
|
|
- User profile page
|
|
- Admin dashboard styling
|
|
- Auth pages styling
|