6.7 KiB
Story 14.5: Latest Posts Section
Story
As a website visitor I want to see recent legal articles and updates on the home page So that I can access valuable legal content and see that the firm is active
Acceptance Criteria
AC1: Section Container
Given a visitor scrolls to the latest posts section When viewing the section Then display with:
- Warm Cream (
#F4F1EA) background - Section ID
id="posts"for anchor linking - Adequate padding (64px vertical on desktop)
AC2: Section Heading
Given the latest posts section When displayed Then show a section heading:
- English: "Latest Articles" or "Legal Insights"
- Arabic: "أحدث المقالات" or "رؤى قانونية"
- Typography: H2, SemiBold, Forest Green (
#2D322A) - Centered alignment
AC3: Display 3 Latest Posts
Given the posts section When posts exist in the database Then display the 3 most recent published posts showing:
- Post title
- Publication date
- Brief excerpt (first 100-150 characters of body)
- "Read More" link
AC4: Post Card Design
Given each post card When displayed Then include:
- Card background: White (
#FFFFFF) - Title: Bold, Forest Green (
#2D322A), clickable link - Date: Small text, muted color
- Excerpt: Body text, truncated with ellipsis
- "Read More" link: Warm Gold (
#A68966) - Subtle shadow and border-radius
- Hover effect on card or title
AC5: Empty State
Given the posts section When no published posts exist Then either:
- Hide the entire section, OR
- Show a message: "Articles coming soon" / "المقالات قريباً"
AC6: View All Link
Given the posts section When posts are displayed Then show a "View All Articles" / "عرض جميع المقالات" link:
- Links to
/posts - Styled as secondary button or text link with arrow
- Positioned below the post cards, centered
AC7: Grid Layout
Given different screen sizes When viewing the posts section:
- Desktop (≥992px): 3 columns (1 row of 3 cards)
- Tablet (576-991px): 2 columns + 1 below, or 3 columns
- Mobile (<576px): 1 column (3 rows, stacked)
AC8: RTL Support
Given Arabic language is selected When viewing the posts section Then cards and text align right-to-left correctly
AC9: Dynamic Data
Given the posts section When rendered Then query posts from database:
Post::where('status', 'published')
->latest()
->take(3)
->get()
Technical Notes
Files to Modify
resources/views/pages/home.blade.php- Add posts section with data querylang/en/home.php- Add translationslang/ar/home.php- Add translations
Controller/Page Component
The home page needs to pass posts data. Options:
- Use a Livewire/Volt component to fetch posts
- Pass posts via route closure or controller
Option 1: Volt Component (Recommended)
Convert home.blade.php to a Volt component:
<?php
use App\Models\Post;
use Livewire\Volt\Component;
new class extends Component {
public function with(): array
{
return [
'latestPosts' => Post::where('status', 'published')
->latest()
->take(3)
->get(),
];
}
}; ?>
Option 2: Route with data
// routes/web.php
Route::get('/', function () {
return view('pages.home', [
'latestPosts' => Post::where('status', 'published')
->latest()
->take(3)
->get(),
]);
})->name('home');
HTML Structure
@if($latestPosts->count() > 0)
<section id="posts" class="py-16 lg:py-20 bg-background">
<div class="container mx-auto px-4">
<div class="text-center mb-12">
<h2 class="text-2xl lg:text-3xl font-semibold text-text mb-4">
{{ __('home.posts_title') }}
</h2>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
@foreach($latestPosts as $post)
<article class="bg-card p-6 rounded-lg shadow-card">
<time class="text-xs text-body/70 mb-2 block">
{{ $post->created_at->translatedFormat('j F Y') }}
</time>
<h3 class="text-lg font-bold text-text mb-3">
<a href="{{ route('posts.show', $post) }}" class="hover:text-cta transition-colors">
{{ $post->title }}
</a>
</h3>
<p class="text-body text-sm mb-4 line-clamp-3">
{{ Str::limit(strip_tags($post->body), 150) }}
</p>
<a href="{{ route('posts.show', $post) }}" class="text-cta font-medium text-sm hover:text-cta-hover">
{{ __('home.read_more') }} →
</a>
</article>
@endforeach
</div>
<div class="text-center">
<flux:button variant="ghost" href="{{ route('posts.index') }}">
{{ __('home.view_all_posts') }}
</flux:button>
</div>
</div>
</section>
@endif
Translation Keys
// lang/en/home.php (additions)
'posts_title' => 'Latest Articles',
'read_more' => 'Read More',
'view_all_posts' => 'View All Articles',
'posts_empty' => 'Articles coming soon',
// lang/ar/home.php (additions)
'posts_title' => 'أحدث المقالات',
'read_more' => 'اقرأ المزيد',
'view_all_posts' => 'عرض جميع المقالات',
'posts_empty' => 'المقالات قريباً',
Post Model Reference
Existing Post model fields:
title- Post title (bilingual stored via JSON or separate fields)body- Post contentstatus- 'draft' or 'published'created_at- Timestamp
Date Formatting
Use Laravel's translatedFormat() for bilingual dates:
$post->created_at->translatedFormat('j F Y')
// English: "15 January 2026"
// Arabic: "15 يناير 2026"
Dev Checklist
- Determine data passing method (Volt component or route)
- Create posts section HTML structure
- Add section heading with translations
- Implement post card design
- Query and display 3 latest published posts
- Add date formatting with translation support
- Implement excerpt truncation
- Add "Read More" links to individual posts
- Add "View All" link to posts index
- Handle empty state (hide section or show message)
- Implement responsive grid layout
- Test RTL layout
- Verify links work correctly
Estimation
Complexity: Medium (requires data fetching) Risk: Low - Uses existing Post model and routes
Dependencies
- Previous stories for page structure
- Existing Post model
- Posts routes (
posts.index,posts.show)