resources/views/
βββ layouts/
β βββ app.blade.php # Main layout
β βββ guest.blade.php # Layout untuk guest (login, register)
β βββ components/
β βββ navigation.blade.php
β βββ footer.blade.php
β βββ meta.blade.php
βββ components/
β βββ button.blade.php
β βββ card.blade.php
β βββ alert.blade.php
βββ pages/
βββ home.blade.php
βββ about.blade.php
βββ contact.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="scroll-smooth">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>@yield('title', config('app.name', 'Laravel'))</title>
<meta name="description" content="@yield('description', 'Default description')">
<meta name="keywords" content="@yield('keywords', 'laravel, tailwind, php')">
<meta property="og:type" content="website">
<meta property="og:url" content="{{ url()->current() }}">
<meta property="og:title" content="@yield('title', config('app.name'))">
<meta property="og:description" content="@yield('description', 'Default description')">
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="{{ url()->current() }}">
<meta property="twitter:title" content="@yield('title', config('app.name'))">
<meta property="twitter:description" content="@yield('description', 'Default description')">
<link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
@vite(['resources/css/app.css', 'resources/js/app.js'])
@stack('styles')
</head>
<body class="bg-gray-50 dark:bg-gray-900 text-gray-800 dark:text-gray-200 font-sans antialiased">
<div id="loading" class="fixed inset-0 bg-white dark:bg-gray-900 z-[100] flex items-center justify-center">
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-indigo-600"></div>
</div>
<a href="#main-content" class="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 bg-indigo-600 text-white px-4 py-2 rounded-md z-50">
Skip to main content
</a>
<div class="flex flex-col min-h-screen">
@include('layouts.components.navigation')
<main id="main-content" class="flex-grow">
@hasSection('header')
<header class="bg-white dark:bg-gray-800/50 shadow-sm border-b border-gray-200 dark:border-gray-700">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
@yield('header')
</div>
</header>
@endif
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pt-6">
<x-alert />
</div>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{{ $slot ?? '' }}
@yield('content')
</div>
</main>
@include('layouts.components.footer')
</div>
<button id="back-to-top" class="fixed bottom-8 right-8 bg-indigo-600 hover:bg-indigo-700 text-white p-3 rounded-full shadow-lg transition-all duration-300 opacity-0 invisible z-50">
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M5 15l7-7 7 7"/>
</svg>
</button>
@stack('scripts')
</body>
</html>Layout ini khusus untuk halaman yang tidak memerlukan navigasi utama, seperti login, register, atau lupa password.
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" class="scroll-smooth">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>@yield('title', config('app.name', 'Laravel'))</title>
<link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200 font-sans antialiased">
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0">
<div>
<a href="/" class="flex items-center space-x-2">
<div class="w-10 h-10 bg-gradient-to-r from-indigo-500 to-purple-600 rounded-lg flex items-center justify-center">
<span class="text-white font-bold text-lg">L</span>
</div>
<span class="text-2xl font-bold text-gray-900 dark:text-white">
{{ config('app.name') }}
</span>
</a>
</div>
<div class="w-full sm:max-w-md mt-6 px-6 py-4 bg-white dark:bg-gray-800 shadow-md overflow-hidden sm:rounded-lg">
{{ $slot ?? '' }}
@yield('content')
</div>
</div>
</body>
</html>Komponen navigasi sekarang menjadi lebih bersih tanpa inline style dan script.
<nav id="main-nav" class="bg-white/80 dark:bg-gray-800/80 backdrop-blur-lg shadow-sm sticky top-0 z-40 border-b border-gray-200 dark:border-gray-700/50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
<div class="flex items-center">
<a href="{{ route('home') }}" class="flex items-center space-x-2">
<div class="w-8 h-8 bg-gradient-to-r from-indigo-500 to-purple-600 rounded-lg flex items-center justify-center">
<span class="text-white font-bold text-sm">L</span>
</div>
<span class="text-xl font-bold text-gray-900 dark:text-white">
{{ config('app.name') }}
</span>
</a>
</div>
<div class="hidden md:flex items-center space-x-1">
<a href="{{ route('home') }}" class="nav-link {{ Request::routeIs('home') ? 'active' : '' }}">Home</a>
<a href="{{ route('about') }}" class="nav-link {{ Request::routeIs('about') ? 'active' : '' }}">About</a>
<a href="{{ route('contact') }}" class="nav-link {{ Request::routeIs('contact') ? 'active' : '' }}">Contact</a>
</div>
<div class="flex items-center space-x-4">
<button id="theme-toggle" type="button" class="p-2 rounded-lg text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-300 dark:focus:ring-gray-600">
<svg id="theme-toggle-dark-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path></svg>
<svg id="theme-toggle-light-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 5.05A1 1 0 003.636 6.464l.707.707a1 1 0 001.414-1.414l-.707-.707zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
</button>
<button id="mobile-menu-button" class="md:hidden p-2 rounded-lg text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600">
<span class="sr-only">Open main menu</span>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7"/></svg>
</button>
</div>
</div>
<div id="mobile-menu" class="md:hidden hidden py-2">
<div class="space-y-1">
<a href="{{ route('home') }}" class="mobile-nav-link {{ Request::routeIs('home') ? 'active' : '' }}">Home</a>
<a href="{{ route('about') }}" class="mobile-nav-link {{ Request::routeIs('about') ? 'active' : '' }}">About</a>
<a href="{{ route('contact') }}" class="mobile-nav-link {{ Request::routeIs('contact') ? 'active' : '' }}">Contact</a>
</div>
</div>
</div>
</nav><footer class="bg-gray-900 dark:bg-gray-950 text-gray-300">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<div class="grid grid-cols-1 md:grid-cols-4 gap-8">
<!-- Brand -->
<div class="md:col-span-2">
<div class="flex items-center space-x-2 mb-4">
<div class="w-8 h-8 bg-gradient-to-r from-indigo-500 to-purple-600 rounded-lg flex items-center justify-center">
<span class="text-white font-bold text-sm">L</span>
</div>
<span class="text-xl font-bold text-white">{{ config('app.name') }}</span>
</div>
<p class="text-gray-400 mb-4 max-w-md">
Modern Laravel application built with Tailwind CSS v4, designed for performance and scalability.
</p>
<div class="flex space-x-4">
<a href="#" class="text-gray-400 hover:text-indigo-400 transition-colors">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"/>
</svg>
</a>
<a href="#" class="text-gray-400 hover:text-indigo-400 transition-colors">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M22.46 6c-.77.35-1.6.58-2.46.69.88-.53 1.56-1.37 1.88-2.38-.83.5-1.75.85-2.72 1.05C18.37 4.5 17.26 4 16 4c-2.35 0-4.27 1.92-4.27 4.29 0 .34.04.67.11.98C8.28 9.09 5.11 7.38 3 4.79c-.37.63-.58 1.37-.58 2.15 0 1.49.75 2.81 1.91 3.56-.71 0-1.37-.2-1.95-.5v.03c0 2.08 1.48 3.82 3.44 4.21a4.22 4.22 0 0 1-1.93.07 4.28 4.28 0 0 0 4 2.98 8.521 8.521 0 0 1-5.33 1.84c-.34 0-.68-.02-1.02-.06C3.44 20.29 5.7 21 8.12 21 16 21 20.33 14.46 20.33 8.79c0-.19 0-.37-.01-.56.84-.6 1.56-1.36 2.14-2.23z"/>
</svg>
</a>
<a href="#" class="text-gray-400 hover:text-indigo-400 transition-colors">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.174-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.663.967-2.911 2.168-2.911 1.024 0 1.518.769 1.518 1.688 0 1.029-.653 2.567-.992 3.992-.285 1.193.6 2.165 1.775 2.165 2.128 0 3.768-2.245 3.768-5.487 0-2.861-2.063-4.869-5.008-4.869-3.41 0-5.409 2.562-5.409 5.199 0 1.033.394 2.143.889 2.741.197.261.225.489.165.757-.053.224-.172.687-.402 1.33-.026.111-.096.135-.221.08-1.235-.576-2.01-2.38-2.01-3.821 0-3.785 2.75-7.262 7.929-7.262 4.163 0 7.398 2.967 7.398 6.931 0 4.136-2.607 7.464-6.227 7.464-1.216 0-2.357-.631-2.75-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146C9.57 23.812 10.763 24.009 12.017 24.009c6.624 0 11.99-5.367 11.99-11.988C24.007 5.367 18.641.001.012.001z"/>
</svg>
</a>
</div>
</div>
<!-- Quick Links -->
<div>
<h3 class="text-white font-semibold mb-4">Quick Links</h3>
<ul class="space-y-2">
<li><a href="{{ route('home') }}" class="text-gray-400 hover:text-indigo-400 transition-colors">Home</a></li>
<li><a href="{{ route('about') }}" class="text-gray-400 hover:text-indigo-400 transition-colors">About</a></li>
<li><a href="{{ route('contact') }}" class="text-gray-400 hover:text-indigo-400 transition-colors">Contact</a></li>
<li><a href="#" class="text-gray-400 hover:text-indigo-400 transition-colors">Privacy Policy</a></li>
</ul>
</div>
<!-- Contact Info -->
<div>
<h3 class="text-white font-semibold mb-4">Contact</h3>
<ul class="space-y-2 text-gray-400">
<li>π§ hello@example.com</li>
<li>π± +62 123 456 789</li>
<li>π Barito Kuala, Indonesia</li>
</ul>
</div>
</div>
<div class="mt-8 pt-8 border-t border-gray-800 text-center text-gray-400">
<p>© {{ date('Y') }} {{ config('app.name') }}. All rights reserved.</p>
</div>
</div>
</footer>@extends('layouts.app')
@section('description', 'Welcome to our modern Laravel application built with Tailwind CSS v4')
@section('keywords', 'laravel, tailwind, home, modern')
@section('header')
<div class="text-center">
<h1 class="text-4xl font-bold text-gray-900 dark:text-white mb-2">
Welcome to Laravel
</h1>
<p class="text-lg text-gray-600 dark:text-gray-300">
Modern web application built with Tailwind CSS v4
</p>
</div>
@endsection
@section('content')
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<!-- Feature Cards -->
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 border border-gray-200 dark:border-gray-700">
<div class="w-12 h-12 bg-indigo-100 dark:bg-indigo-900 rounded-lg flex items-center justify-center mb-4">
<svg class="w-6 h-6 text-indigo-600 dark:text-indigo-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
</div>
<h3 class="text-xl font-semibold mb-2 text-gray-900 dark:text-white">Fast Performance</h3>
<p class="text-gray-600 dark:text-gray-300">Built with modern tools for optimal performance and user experience.</p>
</div>
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 border border-gray-200 dark:border-gray-700">
<div class="w-12 h-12 bg-green-100 dark:bg-green-900 rounded-lg flex items-center justify-center mb-4">
<svg class="w-6 h-6 text-green-600 dark:text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</div>
<h3 class="text-xl font-semibold mb-2 text-gray-900 dark:text-white">Reliable</h3>
<p class="text-gray-600 dark:text-gray-300">Tested and proven architecture with robust error handling.</p>
</div>
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 border border-gray-200 dark:border-gray-700">
<div class="w-12 h-12 bg-purple-100 dark:bg-purple-900 rounded-lg flex items-center justify-center mb-4">
<svg class="w-6 h-6 text-purple-600 dark:text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/>
</svg>
</div>
<h3 class="text-xl font-semibold mb-2 text-gray-900 dark:text-white">User Friendly</h3>
<p class="text-gray-600 dark:text-gray-300">Intuitive design that focuses on user experience and accessibility.</p>
</div>
</div>
<div class="mt-12 text-center">
<a href="{{ route('about') }}" class="inline-flex items-center px-6 py-3 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg transition-colors">
Learn More
<svg class="ml-2 w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"/>
</svg>
</a>
</div>
@endsection
@push('styles')
<style>
.hero-gradient {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
</style>
@endpush
@push('scripts')
<script>
console.log('Home page loaded successfully!');
</script>
@endpush@extends('layouts.app')
@section('title', 'About Us')
@section('description', 'Learn more about our company and our mission.')
@section('keywords', 'about, company, mission')
@section('header')
<div class="text-center">
<h1 class="text-4xl font-extrabold text-gray-900 dark:text-white sm:text-5xl md:text-6xl">
About <span class="text-gradient">Us</span>
</h1>
<p class="mt-4 max-w-2xl mx-auto text-xl text-gray-500 dark:text-gray-300">
We are a passionate team dedicated to building high-quality web applications.
</p>
</div>
@endsection
@section('content')
<div class="space-y-12">
<div class="prose dark:prose-invert prose-lg max-w-none">
<p>
Our journey began with a simple idea: to create software that is not only functional but also beautiful and a joy to use. We leverage the power of the Laravel framework and the flexibility of Tailwind CSS to deliver exceptional digital experiences. Our focus on clean code, performance, and user-centric design sets us apart.
</p>
<h2 class="text-3xl font-bold mt-12 mb-4">Our Mission</h2>
<p>
Our mission is to empower businesses and individuals by providing them with robust, scalable, and intuitive web solutions. We believe in the power of technology to solve complex problems and drive progress.
</p>
<blockquote class="border-l-4 border-indigo-500 pl-4 italic">
"The best way to predict the future is to invent it."
</blockquote>
</div>
</div>
@endsection@extends('layouts.app')
@section('title', 'Contact Us')
@section('description', 'Get in touch with us for any inquiries.')
@section('keywords', 'contact, support, inquiry')
@section('header')
<div class="text-center">
<h1 class="text-4xl font-extrabold text-gray-900 dark:text-white sm:text-5xl md:text-6xl">
Get in <span class="text-gradient">Touch</span>
</h1>
<p class="mt-4 max-w-2xl mx-auto text-xl text-gray-500 dark:text-gray-300">
We'd love to hear from you! Please fill out the form below.
</p>
</div>
@endsection
@section('content')
<div class="max-w-xl mx-auto">
<div class="card p-6 sm:p-8">
<form action="#" method="POST" class="space-y-6">
@csrf
<div>
<label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Full Name</label>
<div class="mt-1">
<input type="text" name="name" id="name" class="input-field" placeholder="John Doe">
</div>
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Email Address</label>
<div class="mt-1">
<input type="email" name="email" id="email" class="input-field" placeholder="you@example.com">
</div>
</div>
<div>
<label for="message" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Message</label>
<div class="mt-1">
<textarea rows="4" name="message" id="message" class="input-field" placeholder="Your message here..."></textarea>
</div>
</div>
<div>
<x-button type="submit" class="w-full justify-center">
Send Message
</x-button>
</div>
</form>
</div>
</div>
@endsectionFile app.css sekarang berisi semua custom styling, termasuk yang sebelumnya ada di navigation.blade.php
@import 'tailwindcss';
/* Override dark mode behavior (Tailwind v4 syntax) */
@custom-variant dark (&:where(.dark, .dark *));
/* Base Styles */
body {
@apply bg-gray-50 dark:bg-gray-900 text-gray-800 dark:text-gray-200 font-sans antialiased;
}
/* Smooth transitions for all elements */
*, ::before, ::after {
@apply transition-colors duration-200;
}
/* Custom Components */
@layer components {
/* Buttons */
.btn-primary {
@apply inline-flex items-center px-4 py-2 bg-indigo-600 hover:bg-indigo-700 text-white font-semibold rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800;
}
.btn-secondary {
@apply inline-flex items-center px-4 py-2 bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-900 dark:text-gray-100 font-semibold rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800;
}
/* Cards */
.card {
@apply bg-white dark:bg-gray-800/50 rounded-xl shadow-lg border border-gray-200 dark:border-gray-700 overflow-hidden backdrop-blur-lg;
}
/* Form Inputs */
.input-field {
@apply block w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700/50 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent;
}
/* Navigation Links */
.nav-link {
@apply text-gray-600 dark:text-gray-300 hover:text-indigo-600 dark:hover:text-indigo-400 px-3 py-2 rounded-md text-sm font-medium relative;
}
.nav-link.active {
@apply text-indigo-600 dark:text-indigo-400;
}
.nav-link.active::after {
@apply content-[''] absolute bottom-0 left-3 right-3 h-0.5 bg-indigo-600 dark:bg-indigo-400 rounded-full;
}
/* Mobile Navigation Links */
.mobile-nav-link {
@apply block px-3 py-2 rounded-md text-base font-medium text-gray-600 dark:text-gray-300 hover:text-indigo-600 dark:hover:text-indigo-400 hover:bg-gray-100 dark:hover:bg-gray-700;
}
.mobile-nav-link.active {
@apply text-indigo-600 dark:text-indigo-400 bg-indigo-50 dark:bg-gray-700;
}
}
/* Custom Utilities */
@layer utilities {
.text-gradient {
@apply bg-gradient-to-r from-indigo-500 to-purple-600 bg-clip-text text-transparent;
}
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
@apply bg-gray-100 dark:bg-gray-800;
}
::-webkit-scrollbar-thumb {
@apply bg-gray-300 dark:bg-gray-600 rounded-full;
}
::-webkit-scrollbar-thumb:hover {
@apply bg-gray-400 dark:bg-gray-500;
}Semua fungsionalitas JavaScript (loading, back-to-top, dark mode, mobile menu) kini berada di satu tempat.
import './bootstrap';
document.addEventListener('DOMContentLoaded', function () {
// --- Loading Spinner ---
const loadingEl = document.getElementById('loading');
if (loadingEl) {
window.addEventListener('load', () => {
loadingEl.style.display = 'none';
});
}
// --- Back to Top Button ---
const backToTopButton = document.getElementById('back-to-top');
if (backToTopButton) {
window.addEventListener('scroll', () => {
if (window.scrollY > 200) {
backToTopButton.classList.remove('opacity-0', 'invisible');
} else {
backToTopButton.classList.add('opacity-0', 'invisible');
}
});
backToTopButton.addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
}
// --- Dark Mode Toggle ---
const themeToggleBtn = document.getElementById('theme-toggle');
const themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
const themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
const applyTheme = (theme) => {
if (theme === 'dark') {
document.documentElement.classList.add('dark');
themeToggleLightIcon?.classList.remove('hidden');
themeToggleDarkIcon?.classList.add('hidden');
} else {
document.documentElement.classList.remove('dark');
themeToggleDarkIcon?.classList.remove('hidden');
themeToggleLightIcon?.classList.add('hidden');
}
};
const currentTheme = localStorage.getItem('color-theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
applyTheme(currentTheme);
themeToggleBtn?.addEventListener('click', () => {
const newTheme = document.documentElement.classList.contains('dark') ? 'light' : 'dark';
localStorage.setItem('color-theme', newTheme);
applyTheme(newTheme);
});
// --- Mobile Menu Toggle ---
const mobileMenuButton = document.getElementById('mobile-menu-button');
const mobileMenu = document.getElementById('mobile-menu');
mobileMenuButton?.addEventListener('click', () => {
const isExpanded = mobileMenuButton.getAttribute('aria-expanded') === 'true';
mobileMenuButton.setAttribute('aria-expanded', !isExpanded);
mobileMenu?.classList.toggle('hidden');
});
}); Membuat komponen Blade untuk elemen UI yang sering digunakan seperti alert dan button.
@if (session('success'))
<div class="bg-green-100 dark:bg-green-900/50 border border-green-200 dark:border-green-800 text-green-800 dark:text-green-200 px-4 py-3 rounded-lg mb-4" role="alert">
<strong class="font-bold">Success!</strong>
<span class="block sm:inline">{{ session('success') }}</span>
</div>
@endif
@if (session('error'))
<div class="bg-red-100 dark:bg-red-900/50 border border-red-200 dark:border-red-800 text-red-800 dark:text-red-200 px-4 py-3 rounded-lg mb-4" role="alert">
<strong class="font-bold">Error!</strong>
<span class="block sm:inline">{{ session('error') }}</span>
</div>
@endif
@if ($errors->any())
<div class="bg-red-100 dark:bg-red-900/50 border border-red-200 dark:border-red-800 text-red-800 dark:text-red-200 px-4 py-3 rounded-lg mb-4" role="alert">
<strong class="font-bold">Validation Error!</strong>
<ul class="mt-2 list-disc list-inside">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif Membuat komponen Blade untuk elemen UI yang sering digunakan seperti alert dan button.
@props([
'variant' => 'primary', // 'primary' or 'secondary'
'type' => 'button'
])
@php
$classes = [
'primary' => 'btn-primary',
'secondary' => 'btn-secondary',
][$variant];
@endphp
<button type="{{ $type }}" {{ $attributes->merge(['class' => $classes]) }}>
{{ $slot }}
</button><?php
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('pages.home');
})->name('home');
Route::get('/about', function () {
return view('pages.about');
})->name('about');
Route::get('/contact', function () {
return view('pages.contact');
})->name('contact');Layout ini sudah include dark mode toggle yang akan:
- Menyimpan preferensi user di localStorage
- Mengikuti system preference secara default
- Smooth transition antar tema
- Skip navigation link untuk screen readers
- Proper semantic HTML structure
- ARIA labels dan roles
- Keyboard navigation support
- High contrast colors
- Preconnect untuk Google Fonts
- Lazy loading untuk gambar (bisa ditambahkan)
- Minified CSS dan JS via Vite
- Optimized asset loading
@extends('layouts.app')
@section('title', 'Page Title')
@section('description', 'Page description for SEO')
@section('header')
<!-- Optional header content -->
@endsection
@section('content')
<!-- Main page content -->
@endsection
@push('styles')
<!-- Custom CSS untuk page ini -->
@endpush
@push('scripts')
<!-- Custom JS untuk page ini -->
@endpush// Di Controller
return redirect()->route('home')->with('success', 'Data berhasil disimpan!');
return redirect()->back()->with('error', 'Terjadi kesalahan!');Buat komponen reusable di resources/views/components/:
<!-- resources/views/components/button.blade.php -->
@props(['variant' => 'primary', 'size' => 'md', 'type' => 'button'])
@php
$classes = [
'primary' => 'btn-primary',
'secondary' => 'btn-secondary',
];
$sizes = [
'sm' => 'px-3 py-1.5 text-sm',
'md' => 'px-4 py-2',
'lg' => 'px-6 py-3 text-lg',
];
@endphp
<button
type="{{ $type }}"
{{ $attributes->merge(['class' => $classes[$variant] . ' ' . $sizes[$size]]) }}
>
{{ $slot }}
</button>Penggunaan:
<x-button variant="primary" size="lg">Save Data</x-button>
<x-button variant="secondary" onclick="closeModal()">Cancel</x-button>Edit tailwind.config.js:
export default {
content: [
'./resources/views/**/*.blade.php',
'./resources/js/**/*.{js,vue,ts,jsx,tsx}',
],
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
},
// Custom colors
brand: {
50: '#f0f9ff',
500: '#0ea5e9',
600: '#0284c7',
}
},
},
},
plugins: [],
}@layer components {
.fade-in {
animation: fadeIn 0.5s ease-in-out;
}
.slide-up {
animation: slideUp 0.3s ease-out;
}
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}- Pastikan route menggunakan
return view('pages.home')bukanwelcome - Cek apakah file layout ada di
resources/views/layouts/app.blade.php - Pastikan
@extends('layouts.app')di awal file view
- Cek apakah JavaScript untuk dark mode toggle sudah dimuat
- Pastikan Tailwind config sudah include
darkMode: 'class' - Cek browser console untuk error JavaScript
- Jalankan
npm run devataunpm run build - Pastikan
@vitedirective ada di layout - Clear browser cache
- Cek apakah custom CSS sudah ditambahkan ke
app.css
Layout ini sudah responsive dengan breakpoints:
sm:- Small screens (640px+)md:- Medium screens (768px+)lg:- Large screens (1024px+)xl:- Extra large screens (1280px+)
# Jalankan dev server
npm run dev
# Test di berbagai device sizes:
# - Mobile: 375px
# - Tablet: 768px
# - Desktop: 1024px+.text-xs /* 12px */
.text-sm /* 14px */
.text-base /* 16px */
.text-lg /* 18px */
.text-xl /* 20px */
.text-2xl /* 24px */
.text-3xl /* 30px */
.text-4xl /* 36px */.p-1 /* 4px */
.p-2 /* 8px */
.p-4 /* 16px */
.p-6 /* 24px */
.p-8 /* 32px */
.p-12 /* 48px */- Primary: Indigo (600, 700)
- Secondary: Gray (200, 300, 700, 800)
- Success: Green (50, 600, 800)
- Error: Red (50, 600, 800)
- Buat Component Library: Kembangkan komponen reusable
- Add Authentication: Integrate dengan Laravel Breeze/Jetstream
- Form Handling: Tambahkan form validation dan handling
- Data Tables: Implement sortable, filterable tables
- API Integration: Connect dengan backend APIs
- Testing: Tambahkan unit dan feature tests
Untuk berkontribusi pada layout ini:
- Fork repository
- Buat feature branch (
git checkout -b feature/new-component) - Commit changes (
git commit -am 'Add new component') - Push to branch (
git push origin feature/new-component) - Create Pull Request
π‘ Pro Tip: Simpan struktur ini sebagai template untuk project Laravel baru. Anda bisa membuat Artisan command untuk generate layout otomatis!
Layout system yang robust dan modern sudah siap digunakan. Dengan struktur ini, Anda dapat:
- Membangun aplikasi yang scalable
- Maintain consistency dalam design
- Mengembangkan fitur dengan lebih cepat
- Memberikan UX yang excellent
Happy coding! π