Nessa parte vamos estabelecer a identidade visual e os fundamentos de experiência do usuário antes de avançar para funcionalidades mais complexas, criando uma base sólida, consistente e escalável para toda a aplicação.
Etapa 1 — Definição da paleta visual
Arquivo:/tailwind.config.js
import defaultTheme from 'tailwindcss/defaultTheme';
import forms from '@tailwindcss/forms';
/** @type {import('tailwindcss').Config} */
export default {
darkMode: 'class',
content: [
'./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php',
'./storage/framework/views/*.php',
'./resources/views/**/*.blade.php',
'./resources/**/*.js',
],
theme: {
extend: {
colors: {
primary: '#2563eb',
surface: '#f1f5f9',
darksurface: '#0f172a',
},
},
},
plugins: [forms],
};
Foram definidas cores base para garantir consistência visual:
- azul como identidade e ações principais,
- tons de cinza para estrutura,
- contraste claro/escuro para legibilidade,
- ativado o darkMode: ‘class’.
Essa etapa garante que todo o sistema fale a mesma “linguagem visual”.
Etapa 2 — Tema claro/escuro
Arquivo:/resources/views/components/system-layout.blade.php
<html
lang="{{ app()->getLocale() }}"
x-data="{
dark: localStorage.getItem('theme') === 'dark',
lang: localStorage.getItem('lang') ?? '{{ app()->getLocale() }}',
sidebarOpen: window.innerWidth >= 1024,
toggleTheme() {
this.dark = !this.dark
localStorage.setItem('theme', this.dark ? 'dark' : 'light')
},
toggleLang() {
this.lang = this.lang === 'pt_BR' ? 'en' : 'pt_BR'
localStorage.setItem('lang', this.lang)
fetch(`/set-lang/${this.lang}`).then(() => location.reload())
}
}"
:class="{ 'dark': dark }"
>
Implementado o controle de tema com Alpine.js, permitindo ao usuário alternar entre modo claro e escuro.
A preferência é persistida no navegador, garantindo experiência consistente entre sessões.
Configurado o multi-idioma.
Etapa 3 — Novo Header
Arquivo:/resources/views/components/header.blade.php
<header class="bg-white dark:bg-darksurface border-b border-gray-200 dark:border-gray-700">
<div class="h-14 flex items-center justify-between px-6">
<div class="flex items-center gap-3">
<div class="w-8 h-8 bg-primary rounded"></div>
<span class="text-lg font-semibold">MeuSistema</span>
</div>
<div class="flex items-center gap-4">
<button @click="toggleTheme()" class="w-9 h-9 flex items-center justify-center rounded hover:bg-gray-100 dark:hover:bg-gray-700">
<span x-show="!dark">🌙</span>
<span x-show="dark">☀️</span>
</button>
<button @click="toggleLang()" class="w-9 h-9 flex items-center justify-center rounded hover:bg-gray-100 dark:hover:bg-gray-700"
x-text="lang === 'pt_BR' ? '🇧🇷' : '🇺🇸'">
</button>
<!-- Menu do usuário -->
<div x-data="{ open: false }" class="relative">
<button @click="open = !open" class="flex items-center gap-2 text-sm">
{{ Auth::user()->name }}
</button>
<div x-show="open" @click.outside="open = false"
class="absolute right-0 mt-2 w-40 bg-white dark:bg-gray-800 border rounded shadow">
<a href="{{ route('profile.edit') }}" class="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-700">
{{ __('Profile') }}
</a>
<form method="POST" action="{{ route('logout') }}">
@csrf
<button class="w-full text-left px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-700">
{{ __('Logout') }}
</button>
</form>
</div>
</div>
</div>
</div>
<div class="h-10 flex items-center px-6 text-sm text-gray-500 dark:text-gray-400">
{{ __('messages.dashboard') }}
</div>
</header>
O header passou a ser o centro de controle da aplicação, incorporando:
- identidade visual (logo + nome do sistema),
- alternância de tema,
- seletor de idioma,
- menu do usuário (perfil e logout),
- breadcrumbs para navegação contextual.
A estrutura em duas linhas separa claramente identidade e navegação, melhorando a leitura e organização da interface.
Etapa 4 — Internacionalização
Estrutura de idiomas
/lang/pt_BR/messages.php /lang/en/messages.php
/lang/pt_BR/messages.php
return [
'dashboard' => 'Painel',
'admin_panel' => 'Painel Administrativo',
'user_panel' => 'Painel Operacional',
];
/lang/en/messages.php
return [
'dashboard' => 'Dashboard',
'admin_panel' => 'Admin Panel',
'user_panel' => 'Operations Panel',
];
Middleware de idioma
Arquivo:/app/Http/Middleware/LocaleMiddleware.php
namespace App\Http\Middleware;
use Closure;
class LocaleMiddleware
{
public function handle($request, Closure $next)
{
app()->setLocale(session('locale', 'pt_BR'));
return $next($request);
}
}
Registro no Laravel 12
Arquivo:/bootstrap/app.php
$middleware->web(append: [
EnsureCompanyIsActive::class,
\App\Http\Middleware\LocaleMiddleware::class,
]);
Rota de idioma
Arquivo:/routes/web.php
Route::get('/set-lang/{lang}', function ($lang) {
if (in_array($lang, ['pt_BR', 'en'])) {
session(['locale' => $lang]);
}
});
Nesta etapa foi integrado o sistema de idiomas nativo do Laravel.
A escolha do idioma é controlada pelo header, persistida em sessão e aplicada a cada requisição por meio de middleware, garantindo que toda a interface reflita corretamente o idioma selecionado.
Todos os textos da aplicação passam a ser controlados por chaves de tradução, preparando o sistema para expansão internacional sem refatorações futuras.
Etapa 5 — Padronização de textos da interface
Arquivos envolvidos:
resources/views/components/header.blade.phpresources/views/dashboard/admin.blade.phpresources/views/dashboard/user.blade.php
Textos fixos foram substituídos por chamadas de tradução (__()), conectando a interface diretamente ao sistema de internacionalização.
Conclusão
✔ Interface com identidade visual consistente
✔ Dark mode funcional
✔ Sistema de idiomas completo
✔ Header profissional e funcional
✔ Base sólida para UX avançada
Leia Desenvolvendo um Sistema em Laravel — Parte 4A: Arquitetura de Interface e Base do Sistema.