Plantillas en laravel

Plantillas en Laravel

En Laravel, las plantillas constituyen la capa de presentación de la aplicación .

Son la parte encargada de generar el HTML que finalmente recibe el navegador del usuario . Sin embargo, no se trata de HTML estático aislado: las plantillas están directamente relacionadas con el código del backend, ya que:

  • reciben datos desde los controladores,
  • muestran información dinámica
  • permiten integrar lógica mínima de presentación.

Dentro de la arquitectura MVC, las plantillas forman parte de la Vista (View). El controlador prepara los datos y los envía a la vista, que se encarga de representarlos en formato HTML (o en otros formatos como JSON, si se trata de una API). De esta forma, se mantiene la separación entre lógica de negocio y presentación. Las plantillas las escribiremos en la carpeta ./resources .

Laravel ofrece distintas formas de gestionar esta capa de presentación, dependiendo del enfoque arquitectónico que se quiera adoptar.


Blade

Blade es el motor de plantillas nativo de Laravel. Permite escribir HTML enriquecido con directivas especiales (@if, @foreach, @extends, etc.) que facilitan la inserción de datos dinámicos y la reutilización de estructuras.

Blade no es un framework frontend, sino un sistema de renderizado del lado del servidor. El HTML se genera completamente en el backend antes de enviarse al navegador. Es ideal para aplicaciones clásicas basadas en renderizado server-side y para proyectos donde no se necesita una SPA (Single Page Application).

En este caso Las plantillas se escriben en la carpeta ./resources/views.

Laravel no las sirve directamente al navegador. Cuando una vista se renderiza, el motor Blade la compila internamente a código PHP y la almacena en storage/framework/views.

  • Ese código PHP compilado se ejecuta en el servidor y genera el HTML final que se envía al cliente.

Vue, React u otros frameworks frontend

Laravel puede integrarse con frameworks modernos de frontend como Vue o React. En este caso, la generación del HTML dinámico se traslada principalmente al cliente (navegador), mientras que Laravel actúa como backend o como proveedor de API.

Esta integración puede realizarse de distintas formas:

  • Como backend API puro (Laravel devuelve JSON).
  • Mediante Inertia.js, que permite usar Vue o React sin necesidad de construir una API REST tradicional.
  • En combinación con Vite, que gestiona el empaquetado de los recursos frontend.

Este enfoque es adecuado cuando se desea construir interfaces más dinámicas y reactivas.

En este caso, los ficheros (componentes), se escriben en la carpeta resources/js.

Mediante herramientas como Vite , el código es procesado, transpìlado (por ejemplo, JSX o sintaxis moderna de JavaScript a JavaScript compatible con el navegador), y empaquetado en archivos optimizados que se generan en el directorio public/build.

Estos archivos son los que finalmente se sirven al navegador.


Livewire

Livewire es una solución intermedia que permite crear interfaces dinámicas sin abandonar completamente el renderizado del lado del servidor. Funciona integrando componentes PHP que se comunican con el frontend mediante peticiones AJAX automáticas.

El desarrollador escribe principalmente código PHP y Blade, y Livewire se encarga de sincronizar los cambios con el navegador sin necesidad de escribir JavaScript complejo. Es una alternativa interesante cuando se quiere interactividad avanzada manteniendo una arquitectura centrada en Laravel.


En resumen, Laravel permite múltiples estrategias para la capa de presentación:

  • Blade → Renderizado clásico del lado del servidor.
  • Vue / React → Renderizado dinámico del lado del cliente.
  • Livewire → Interactividad avanzada con lógica principalmente en PHP.

La elección depende del nivel de dinamismo requerido, la complejidad de la interfaz y la arquitectura general del proyecto.

1 - Plantillas en laravel

Plantillas con Blade

Una herramienta poderosa y flexible incluida en Laravel, que nos va a permitir escribir html e incluir php y visualizar datos del servidor balde.php de una forma elegante y descriptiva. (Esto facilita visualizar datos

Los ficheros blade, tienen extensión blade.php y estarán ubicados en la carpeta ./resources/view .
Cuando hagamos referencia a los ficheros blade, esta información no hay que especificar, ni su ubicación, ni su extensión, como podemos ver en el siguiente ejemplo


1
2
return view('welcome');
//Va a retornar el fichero ./resources/view/welcome.blade.php

Contenido de un fichero blade

Dentro de un fichero blade (.blade.php)

En él podemos encontrar el siguiente tipo de código o instrucciones:

  • Código html y js

(como cualquier página html)

  • {{}} Doble braquets
  • Para mostrar el contenido de variables PHP o lo que retornara una expresión o función

      {{date(d-m-Y}} => Mostrará el resultado de la función 
      {{ $variable }}  => Se mostrará el valor de la varialbe

  • No se puede incluir código php

      {{$a = 5}} => Esto no se puede hacer

  • Blade automáticamente escapa el HTML en las variables para evitar ataques XSS.

  • También para comentar, en lugar de <!- - Comentario html - ->, podemos usar {{- - Comentario html - -}}

  • @
  • para utilizar directivas/estructuras de control propias de laravel, como como condicionales y bucles.

  • Por ejemplo, @if, @foreach, @switch , @auth – @endauth, @guest - @endguest entre otras.

  • Otro ejemplo es incluir código php con la diretiva @php

      @php
        $a = 5 //Aquí creamos la variable $a y la podremos usar  
      @endphp 
    

Herencia: Creando un layout

Concepto

En el desarrollo web con Laravel, la herencia en las plantillas Blade proporciona una forma eficiente de garantizar una estructura consistente en todas las páginas , fomentando un diseño corporativo uniforme.

Idea de su uso/funcionamiento

Herencia mediante Componentes Blade

Concepto

En el desarrollo web con Laravel, la reutilización de estructuras comunes se realiza actualmente mediante componentes Blade, que permiten definir una estructura base reutilizable para todas las páginas.

En lugar de utilizar las directivas @extends y @yield, se crea un componente de layout (por ejemplo <x-layout>) que encapsula la estructura general de la interfaz: cabecera, navegación, pie de página, estilos y scripts.

Este componente actúa como contenedor común sobre el que se insertan los contenidos específicos de cada vista.


Idea de uso y funcionamiento

Con este enfoque se define un componente principal que representa el diseño corporativo general de la aplicación.

Cada página no hereda formalmente mediante directivas, sino que compone su contenido dentro del componente.

El funcionamiento es el siguiente:

  • El layout define la estructura global.
  • Las páginas insertan su contenido en el {{ $slot }}.
  • Opcionalmente pueden definirse slots nombrados como title, header, description, etc.

De este modo:

  • Se mantiene coherencia visual en toda la aplicación.
  • Se separa claramente la estructura global del contenido específico.
  • Se adopta un enfoque basado en composición, más cercano al modelo de trabajo de frameworks como React o Vue.

Ventajas frente al enfoque clásico con @extends

  • Arquitectura más moderna y alineada con componentes.
  • Mejor encapsulación del diseño.
  • Mayor reutilización.
  • Modelo mental más coherente para estudiantes que trabajan también con frontend basado en componentes.

Conclusión

El uso de componentes Blade para estructurar layouts permite mantener la coherencia visual de la aplicación y facilita el mantenimiento.

Cualquier modificación en el componente principal se reflejará automáticamente en todas las vistas que lo utilicen, garantizando consistencia, modularidad y escalabilidad en el diseño.

Comentarios blade


Insertando comentarios en fichero .blade.php


Dentro de un fichero Blade , podemos comentar de dos maneras

  1. Comentarios HTML (<!-- Comentarios -->)
  2. Comentarios Blade ({{-- Comentarios --}})

Hay diferencias significativas entre comentar con <!-- Comentarios --> y {{-- Comentarios --}} en un fichero Blade de Laravel.
Estas diferencias se relacionan principalmente con cómo se manejan y se muestran estos comentarios en el HTML generado y enviado al navegador.

Comentarios HTML (<!-- Comentarios -->):

  • Son comentarios estándar de HTML.
  • Serán visibles en el código fuente del HTML generado y enviado al navegador.
  • Cualquiera que inspeccione el código fuente de la página en el navegador podrá ver estos comentarios.
  • Son útiles para anotaciones que no afectan la presentación de la página pero podrían ser de ayuda durante el desarrollo o para otros desarrolladores.

Comentarios Blade ({{-- Comentarios --}}):

  • Son específicos del motor de plantillas Blade de Laravel.
  • No serán incluidos en el HTML final generado. Esto significa que no aparecerán en el código fuente de la página cuando se vea en un navegador.
  • Son útiles para dejar notas o comentarios en el código que solo deben ser visibles durante el desarrollo y no deben ser expuestos a los usuarios finales o en el ambiente de producción.
  • Proporcionan una manera de hacer anotaciones en las plantillas Blade sin afectar lo que ve el usuario.

2 - Diseñando el layout

Creando un diseño


Proceso de diseño


Diseñar es un aspecto fundamentan antes de crear las páginas
Para crear un proyecto debemos crear previaemnte los mockups o Prototipados de pantallas
Posteriormente hemos de crear las páginas usando los estilos adecuados para conseguir los diseños previamente prototipados, esto implicará usar html y css Para crear los diseños se usan herramientas gráficas de prototipado, y para el css normalmente usaremos algún fremework tipo Bootstrap o tailwindcss

Herramientas para el diseño

Vamos a usar Balsamiq porque disponemos de licencia estudiante en el centro
Existen varias herramientas alternativas:


  1. Pencil Project:
    • Pencil Project es una herramienta de prototipado de código abierto que te permite crear mockups de manera sencilla. Es fácil de usar y ofrece una variedad de elementos predefinidos.
  2. MockFlow:
    • MockFlow es una plataforma en línea que ofrece una herramienta de diseño de wireframes y mockups. La versión gratuita tiene algunas limitaciones, pero aún así proporciona funcionalidades útiles.
  3. Balsamiq Cloud (versión gratuita) ):
    • Balsamiq, conocido por su aplicación de escritorio, también ofrece una versión en línea gratuita llamada Balsamiq Cloud que permite crear mockups y wireframes.
  4. Figma:
    • Figma es una herramienta de diseño colaborativo en línea que permite crear mockups, wireframes y prototipos. Tiene una versión gratuita con muchas funcionalidades.
  5. Wireframe.cc:
    • Wireframe.cc es una herramienta simple y fácil de usar para crear wireframes rápidos y sencillos. Puedes comenzar a usarla directamente en el navegador.

Laravel como fullstack

Laravel es un poderoso framework de backend.

Con Laravel, es posible realizar desarrollos web full-stack . Esto significa que este framework nos habilita para gestionar tanto el backend como el frontend de una aplicación web.
Así, Laravel se convierte en una solución integral que permite implementar todas las facetas del desarrollo web, desde la lógica del servidor hasta la interfaz de usuario.

Laravel Permite integrar de forma agnóstica cualquier herramienta para el front.

En el proceso de instalación, se instala tailwindcss como framework del css, pero lo podemos cambiar

Instalación de Tailwind: framework de css

Introducción

Tailwind CSS es un poderoso framework de estilos de CSS que se puede utilizar de manera efectiva con Laravel.

La simplicidad y flexibilidad de Tailwind permiten una integración fluida con proyectos Laravel, y es común ver su uso en conjunción con este framework de PHP.

Muchos paquetes y recursos integrados en el ecosistema de Laravel incorporan Tailwind CSS como herramienta preferida para la estilización de componentes y diseños, por ese motivo lo vamos a utilizar en este curso.


Instalación

Para la instalación necesitamos herramientas para gestionar paquetes del cliente node y npm, y vamos a usar vite como herramienta de compilación (traspilación) de ficheros en el frontend


Node y npm

Node

  1. Descarga e instala Node.js desde el sitio web oficial.

Antes de integrar Tailwind CSS en tu proyecto Laravel, necesitarás tener Node.js instalado. node y npm son Gestores de paquetes para el front Van a crear un fichero llamado pakage.json (vs composer.json)

npm install

Al ejecutar npm install , se creará en el proyecto una carpeta llamada node_modules , donde se instalarán los paquetes especificados en el fichero json.
Este directorio node_modules tampoco hay que subir a git, ya que lo podremos generar a partir del fichero pakage.json .
Su instalación es sencilla. Una vez instalado cerramos el terminal y al volverlo a abrir, ya lo podemos utilizar.


Tailwindcss

Tailwindcss


Vite

Vite

Vite en un proyecto Laravel es un moderno entorno de construcción frontend que proporciona una compilación (transpilación)rápida y eficiente de los recursos del front para ponerlos disponibles en ejecución.


Vite , por ejemplo, transpila archivos SCSS o LESS a CSS , procesa las directivas de Tailwind CSS para generar clases CSS utilizables .


Además, compila componentes de Vue o React a JavaScript , facilitando su ejecución en el navegador.

Diseño del layout

Planteamos el siguiente layout general

Para conseguirlo escribimos el siguiente código

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!doctype html>
<html lang="en">
   <head>
       @vite(['resources/css/app.css', 'resources/js/app.js'])
       <title>Document</title>
   </head>
   <body>
      <header class="lg:hidden bg-header flex flex-col justify-center items-center  py-2 space-y-1">
         HEADER MÓVIL
      </header>
      <header class="hidden lg:flex h-15v bg-header flex flex-row justify-center items-center">
         HEADER DESKTOP
      </header>
      <nav>
          <nav class="hidden lg:flex h-10v bg-nav flex flex-row justify-start items-center space-x-2">
            NAV MÓVIL
          </nav>
          <nav class="lg:hidden bg-nav flex flex-col justify-start space-y-1 px-2">
            NAV DESKTOP
          </nav>
      </nav>
      <main class="h-65v bg-main">
          @yield("contenido")
      </main>
      <footer  class="h-10v bg-footer footer items-center p-4 bg-neutral text-neutral-content">
         FOOTER          
      </footer>
   </body>
</html>

Diseño de la cabecera header

Escribimos el siguiente código

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
 <header class="lg:hidden bg-header flex flex-col justify-center items-center  py-2 space-y-1">
        <img class="h-3/5" src="{{asset("images/logos/nett_logo.png")}}" alt="logo nett">
        <button class="h-1/5 w-full btn bg-main "> Acceder</button>
        <button class="h-1/5 w-full btn  bg-main">Registrar</button>
    </header>
    <header class="hidden lg:flex h-15v bg-header flex flex-row justify-center items-center">
        <img class="w-1/5 max-h-full w-auto  p-5 " src="{{asset("images/logos/nett_logo.png")}}" alt="logo nett">
        <div class="w-3/5 flex justify-center items-center">
            <h1 class="text-3xl text-white h-auto max-w-full truncate ">APLICACIÓN EJEMPLO LARAVEL</h1>
        </div>
        <div class=" w-1/5 h-auto max-w-full flex items-center space-x-4 ">
            <button class="btn glass  text-white"> Acceder</button>
            <button class="btn  glass text-white">Registrar</button>
        </div>
    </header>

Diseño de la sección de menú: nav


Escribimos el siguiente código

**

Diseño de la sección de principal : main

En este caso vamos a escribir el contenido de una página landing page

Para ello usaremos de la librería Daisy , el componente Hero

Escribimos el siguiente código

home.blade.php
Establece una página de hereda de otra (un layout)
Para establecer una sección dónde se aportará contenido en la página que herede de ella
<!-- raw HTML omitted -->



Diseño de la sección de pie de página :

footer

En este caso vamos a escribir el contenido de una página

landing page

En este caso vamos a seleccionar un

footer

3 - Componentes

Introducción a los Componentes de Blade en Laravel

¿Qué son los Componentes de Blade?

Los componentes de Blade en Laravel son una poderosa herramienta para reutilizar código HTML y PHP en tus vistas.

Estos componentes son archivos .blade.php que residen en la carpeta resources/views/components. Si la carpeta no existe, puedes crearla manualmente. El hecho de que cada componente sea un archivo .blade.php independiente, facilita su reutilización y mantenimiento.

Al crear un layout, este se convierte en una composición de estos ficheros individuales. Cada componente representa una parte del layout , como: una cabecera, pie de página, sección principal o barra de navegación como podemos ver en la siguiente imagen:

Creación y Uso de Componentes

Tenemos dos formas de crear un componente:

  • Componente anónimo . Crear el fichero directamente en la carpeta correspondiente
  • Componentes basados en clases . A través de una clase usando Artisan****

Creando el componentes directamente

Simplemente creamos el fichero desde nuestro EDI.
En nuestro caso creamos los ficheros header.blade,php, main.blade.php, nav.blade.php y footer.blade.php .

Como son parte del layout, podríamos crearlo dentro de una carpeta llamada layout, por cuestiones organizativas:

Creando el componente con Artisan

Podemos crearlo a partir de una clase. El funcionamiento es igual. La forma de crearlo sería con artisan: Vamos a hacerlo para nuestro ejemplo:

1
php artisan make:Component nombre_componente

Crear componente directo Vs con Artisan

Artisan

Usar Artisan para crear componentes tiene ventajas:

  • Estructura Consistente : Artisan genera automáticamente la estructura de archivos necesaria, asegurando consistencia en todo el proyecto.
  • Rapidez y Conveniencia : Es rápido y evita la posibilidad de errores al crear los archivos manualmente.
    • Comandos Claros : Los comandos de Artisan son claros y documentados, lo que facilita su uso y aprendizaje.
      1
      
        php artisan make:component NombreDelComponente
      Si queremos crear el componente en una carpeta concreta lo podemos hacer especificandolo
      1
      
          php artisan make:component directorio/.../NombreDelComponente
    • Esto crea un nuevo componente en resources/views/components y un archivo de clase correspondiente en app/View/Components .
Crear Componentes Manualmente

Crear componentes manualmente también buena que te da más control en la estructura y organización de carpetas:

  • Flexibilidad : Tienes control total sobre la estructura y organización de tus archivos.
  • Simplicidad : Para proyectos más pequeños o si prefieres un enfoque más “manual”, este método puede ser más directo.

  • Para crear un componente manualmente:
  1. Crea un archivo Blade en resources/views/components .
  2. Opcionalmente, crea una clase de componente en app/View/Components .

Referenciar un Componente

Para usar un componente en tus vistas Blade, lo referencias como un elemento HTML, pero con una sintaxis especial que incluye un prefijo x-::

<x-miComponente />

Nuestro ejemplo

Creamos los componentes usando artisan.

php artisan make:component layout/Header
php artisan make:component layout/Nav
php artisan make:component layout/Main
php artisan make:component layout/Footer
La imagen muestra el resultado generado

Creción de componentes con artisan

Variables en Componentes

Los componentes de Blade ofrecen acceso a varias variables y objetos especiales que facilitan la construcción de interfaces dinámicas y reutilizables. Los más usados

  • $slot
  • $attributes
  • $errors

$slot

Contiene el contenido que se pasa al componente desde donde se llama.

Fichero layout

<x-layout.main>
<h1> Este es un contendio para el componente, lo recibirá como $slot</h1>
</x-layout.main>

Fichero del componente main.blade.php

 <header class="hidden lg:flex h-15v bg-header flex flex-row justify-center items-center">
    <img class="w-1/5 max-h-full w-auto  p-5 " src="{{asset("images/logos/logo.png")}}" alt="logo nett">
    <div class="w-3/5 flex justify-center items-center">
        <h1 class="text-3xl text-white h-auto max-w-full truncate ">
          {{$slot}}
        </h1>
    </div>
<!-- ...  -->
</header>

$atributes

Permite acceder a atributos adicionales pasados al componente. La variable $attributes en los componentes de Blade de Laravel es una instancia de Illuminate\View\ComponentAttributeBag

Proporciona varios métodos útiles para manipular los atributos que se pasan a un componente
Uno de los métodos más útiles es merge() :

El método merge() se usa comúnmente para combinar clases CSS y otros atributos. Esto es especialmente útil cuando deseas que tu componente tenga algunas clases CSS por defecto, pero también quieres permitir que se añadan clases adicionales desde fuera del componente.

Supongamos el siguiente ejemplo donde tenemos un componente boton.blade.php:
Aquí, btn btn-default son las clases predeterminadas para todos los botones.

1
2
3
<button {{ $attributes->merge(['class' => 'btn btn-default']) }}>
{{ $slot }}
</button>
Cuando usas el componente y pasas clases adicionales:

Laravel fusionará las clases, resultando en un botón que tiene class=“btn btn-default btn-large”.

1
<x-boton class="btn-large">Click Aquí</x-boton>
Laravel fusionará las clases, resultando en un botón que tiene class=“btn btn-default btn-large”.


Otros Métodos de $attributes

  • only(): Devuelve solo un subconjunto de los atributos.

  • except(): Devuelve todos los atributos excepto los especificados.

  • class(): Devuelve una instancia de Illuminate\View\ComponentAttributeBag solo con las clases.

  • first(): Devuelve el primer valor de los atributos especificados.

Pasando atributos a un componentes

Para pasar atributos a un componente, usamos una sintaxis similar a la de las etiquetas HTML:

<x-miComponente atributo="valor" />

Posteriormente en el componete podemos utilzar dicho valor.

Si lo establecemos, Laravel proporciona una conversión automática entre la notación kebab-case en la invocación del componente y camelCase dentro del componente.

Notación Kebab-Case y CamelCase

  • Kebab-Case: Usada al pasar atributos al componente. Ejemplo: mi-atributo.
  • CamelCase: Usada dentro del componente para referenciar estos atributos. Ejemplo: miAtributo.
Cuando invocas un componente y pasas un atributo, usas kebab-case:
<x-miComponente mi-atributo="valor" />
Dentro del archivo del componente, Laravel convierte automáticamente mi-atributo a miAtributo:
<!-- miComponente.blade.php -->
<div>
    {{ $miAtributo }} <!-- Accede al valor de 'mi-atributo' -->
</div>
```

4 - Ejemplo de nuestro proyecto

Landing autenticada en Laravel con Tailwind (Sin JavaScript)

Objetivos

  • Crear un grid reutilizable (máximo 3 columnas).
  • Crear un componente card reutilizable.
  • Implementar un menú principal con botón hamburguesa.
  • Implementar un menú de usuario independiente.
  • Usar solo Tailwind (sin JS).

1. Componente Grid

Ubicación: resources/views/components/concept-grid.blade.php

<div class="grid 
            grid-cols-1 
            sm:grid-cols-2 
            lg:grid-cols-3 
            gap-6 
            auto-rows-fr">
    {{ $slot }}
</div>

Explicación

  • grid → activa CSS Grid.
  • grid-cols-1 → 1 columna en móvil.
  • sm:grid-cols-2 → 2 columnas en tablet.
  • lg:grid-cols-3 → máximo 3 columnas en desktop.
  • gap-6 → separación uniforme.
  • auto-rows-fr → tarjetas misma altura.

2. Componente Card

Ubicación: resources/views/components/concept-card.blade.php

@props([
    'title',
    'image',
    'url',
    'buttonText' => 'Ver más'
])

<div class="flex flex-col bg-white rounded-xl shadow-md 
            hover:shadow-lg transition 
            overflow-hidden h-full">

    <div class="flex justify-center items-center p-6 bg-gray-50">
        <img src="{{ $image }}" 
             alt="{{ $title }}" 
             class="h-16 object-contain">
    </div>

    <div class="flex flex-col flex-1 p-6 text-center">

        <h3 class="text-lg font-semibold mb-2">
            {{ $title }}
        </h3>

        <div class="text-sm text-gray-600 flex-1">
            {{ $slot }}
        </div>

        <div class="mt-4">
            <a href="{{ $url }}"
               class="inline-block px-4 py-2 rounded-lg 
                      border border-indigo-600 
                      text-indigo-600 
                      hover:bg-indigo-600 
                      hover:text-white transition">
                {{ $buttonText }}
            </a>
        </div>

    </div>
</div>

3. Navbar con dos toggles (sin JS)

Concepto importante

No usamos dos hamburguesas iguales.

Separación clara:

  • ☰ → Menú principal (navegación global)
  • Usuario ▼ → Menú contextual del usuario

Ambos usan peer, pero son independientes.


3.1 Toggle del menú principal (Hamburguesa)

<div class="relative">

    <input type="checkbox" id="menu-toggle" class="hidden peer">

    <label for="menu-toggle"
           class="lg:hidden cursor-pointer text-2xl">
        ☰
    </label>

    <ul class="hidden 
               peer-checked:flex 
               flex-col 
               absolute 
               top-full left-0 
               w-full 
               bg-slate-800 
               text-white 
               lg:static 
               lg:flex 
               lg:flex-row 
               lg:w-auto 
               gap-6 
               p-4 lg:p-0">

        <li><a href="#">Inicio</a></li>
        <li><a href="#">Proyectos</a></li>
        <li><a href="#">Documentación</a></li>

    </ul>

</div>

Funcionamiento

  • peer → permite reaccionar al estado checked.
  • peer-checked:flex → muestra el menú.
  • En desktop (lg:) siempre visible.

3.2 Toggle del usuario (Dropdown independiente)

<div class="relative">

    <input type="checkbox" id="user-toggle" class="hidden peer">

    <label for="user-toggle"
           class="cursor-pointer px-3 py-1 
                  bg-slate-700 text-white 
                  rounded flex items-center gap-1">
        {{ auth()->user()->name }}
        <span class="text-xs">▼</span>
    </label>

    <div class="hidden 
                peer-checked:block 
                absolute 
                right-0 
                mt-2 
                w-44 
                bg-white 
                text-gray-800 
                rounded-lg 
                shadow-lg 
                border">

        <a href="/profile" 
           class="block px-4 py-2 hover:bg-gray-100">
            Perfil
        </a>

        <form method="POST" action="/logout">
            @csrf
            <button type="submit"
                    class="w-full text-left px-4 py-2 hover:bg-gray-100">
                Logout
            </button>
        </form>

    </div>

</div>

4. Conceptos que el alumnado debe entender

1. Separación de responsabilidades

  • Grid → layout.
  • Card → contenido.
  • Navbar → navegación.
  • Usuario → contexto de sesión.

2. Responsive design

Uso de prefijos:

  • sm:
  • lg:

3. Interactividad sin JS

Uso de:

  • peer
  • peer-checked:*
  • hidden
  • absolute

4. Limitaciones del enfoque sin JS

  • No se cierra al hacer click fuera.
  • No hay control de estado global.
  • No hay animaciones complejas.

Es válido para aprendizaje y proyectos simples.


Conclusión

Es posible crear:

  • Landing autenticada.
  • Grid responsive.
  • Componentes reutilizables.
  • Menú principal responsive.
  • Dropdown de usuario.

Todo usando únicamente:

  • Blade Components
  • Tailwind CSS
  • Sin JavaScript adicional.

5 - Diseño responsivo

Diseño responsivo

El diseño responsivo permite que una aplicación web se adapte dinámicamente al tamaño del dispositivo desde el que se visualiza.

Hoy en día una misma aplicación debe funcionar correctamente en:

  • 📱 Móviles
  • 📲 Tablets
  • 💻 Portátiles
  • 🖥️ Escritorio

La clave no es hacer varias webs, sino una única interfaz flexible.


Diferencias entre responsivo y adaptativo

🔹 Diseño Responsivo (Responsive Web Design)

  • Usa CSS flexible.
  • Se basa en media queries.
  • El layout se adapta de forma fluida.
  • No depende de dispositivos concretos.
  • Es el enfoque moderno.

Cambia progresivamente según el ancho del viewport.


🔹 Diseño Adaptativo (Adaptive Design)

  • Se crean varios layouts fijos.
  • Cada layout está pensado para un rango de dispositivos.
  • El servidor o el navegador decide cuál cargar.
  • Es menos flexible.
  • Mayor mantenimiento.

No es fluido, salta entre versiones predefinidas.


¿Qué es y en qué consiste el diseño responsivo?

El diseño responsivo se basa en tres pilares:

1️⃣ Layout flexible

Uso de porcentajes, flexbox o grid en lugar de medidas fijas.

2️⃣ Imágenes flexibles

Las imágenes deben escalar con el contenedor.

1
2
3
4
img {
max-width: 100%;
height: auto;
}

3️⃣ Media Queries

Permiten aplicar estilos según el ancho del dispositivo.

1
2
3
4
5
@media (min-width: 768px) {
.contenedor {
display: flex;
}
}

Tailwind y sistema mobile-first

Tailwind trabaja con enfoque mobile-first.

  • Primero se diseñan estilos para móvil.
  • Después se añaden variantes para pantallas mayores.

Ejemplo:

1
2
3
<div class="text-base md:text-xl lg:text-3xl">
    Texto adaptable
</div>

Explicación:

  • text-base → móvil
  • md:text-xl → desde 768px
  • lg:text-3xl → desde 1024px

La clase sin prefijo siempre corresponde al tamaño más pequeño.


Puntos de ruptura (Breakpoints)

En Tailwind por defecto:

  • sm → 640px
  • md → 768px
  • lg → 1024px
  • xl → 1280px
  • 2xl → 1536px

Ejemplo práctico:

1
2
3
4
<div class="flex flex-col md:flex-row">
    <aside class="w-full md:w-1/3">Sidebar</aside>
    <main class="w-full md:w-2/3">Contenido</main>
</div>

En móvil:

  • Se muestran en columna.

En escritorio:

  • Se muestran en fila.

Ejemplo en nuestra aplicación

Caso típico en nuestro proyecto:

Header con logo + título + botones login/register.

En escritorio:

  • Todo en horizontal.

En móvil:

  • Se ocultan los botones.
  • Aparece botón hamburguesa.
  • Quitamos el título
  • Lo ponemos en una columa

  • Ejemplo simplificado:

Botón hamburguesa

El botón hamburguesa se utiliza para:

  • Ahorrar espacio en móvil.
  • Mostrar/ocultar navegación.
  • Mantener la interfaz limpia.

Estructura básica:

1
2
3
4
5
<button class="md:hidden p-2">
<span class="block w-6 h-0.5 bg-black mb-1"></span>
<span class="block w-6 h-0.5 bg-black mb-1"></span>
<span class="block w-6 h-0.5 bg-black"></span>
</button>

Puede combinarse con:

  • hidden / block
  • flex / absolute
  • peer / peer-checked
  • Alpine o Vue para control dinámico

Importante: el diseño responsivo no es opcional. Es un requisito estructural en cualquier aplicación web moderna.

1. Proceso de creación: clase peer peer-checked mantener hermanos con el id

Permite que un input controle el estado visual de sus hermanos posteriores.

Código mínimo

1
2
3
4
5
<input type="checkbox" class="peer sr-only" id="menu">

<div class="opacity-0 peer-checked:opacity-100">
    Contenido visible cuando está checked
</div>

Clases imprescindibles

  • peer → convierte al input en referencia.
  • peer-checked:* → aplica estilos cuando el checkbox está marcado.
  • sr-only → oculta visualmente el input pero mantiene accesibilidad.

Regla técnica: el elemento afectado debe ser hermano posterior del peer.

—.

2. menu oculto y controlado visible con peer-checked

El menú existe en el DOM pero se muestra dinámicamente.

1
2
3
4
5
<div class="opacity-0 
            peer-checked:opacity-100
            transition-opacity duration-300">
    Menú
</div>

Clases clave

  • opacity-0 → estado inicial oculto.
  • peer-checked:opacity-100 → visible cuando está activo.
  • transition-opacity duration-300 → animación suave.

3. contenedor relative y menu absolute para colocar el menu

Permite posicionar el menú respecto a su botón.

1
2
3
4
5
6
7
8
9
<div class="relative">

    <label for="menu">Abrir</label>

    <div class="absolute left-full top-0 ml-2">
        Menú
    </div>

</div>
Clases imprescindibles
  • relative → referencia de posicionamiento.
  • absolute → saca el menú del flujo normal.
  • left-full → lo coloca a la derecha.
  • top-0 → alineación vertical.

Sin relative, el menú se posiciona respecto al viewport.


4. Overlay para cerrar al hacer click fuera

Un segundo label con el mismo id permite cerrar el menú sin JavaScript.

1
2
3
4
5
6
7
<label for="menu"
class="fixed inset-0 bg-black/40
opacity-0 pointer-events-none
peer-checked:opacity-100
peer-checked:pointer-events-auto
transition-opacity duration-300">
</label>
Clases imprescindibles
  • fixed → posición respecto al viewport.
  • inset-0 → ocupa toda la pantalla.
  • bg-black/40 → oscurece el fondo.
  • pointer-events-none → no bloquea cuando está oculto.
  • peer-checked:pointer-events-auto → activa interacción.

Al hacer click en el overlay, el checkbox se desmarca.

Resumen: Estructura final

  1. peer controla estado.
  2. peer-checked modifica hermanos.
  3. relative + absolute posiciona el menú.
  4. Overlay con fixed inset-0 permite cerrar sin JS.
  5. Cada dropdown usa su propio checkbox.

5. Otro peer para logout sin javascript

Cada desplegable necesita su propio checkbox independiente para no interferir con el menú hamburguesa.

*Código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<div class="relative">
    <input type="checkbox" class="peer sr-only" id="user_menu">

    <label for="user_menu" class="cursor-pointer flex items-center gap-1">
        {{ auth()->user()->name }}

        <svg class="w-4 h-4 transition-transform duration-300
                    peer-checked:rotate-180"
             fill="none" stroke="currentColor"
             viewBox="0 0 24 24">
            <path stroke-width="2"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                  d="M19 9l-7 7-7-7" />
        </svg>
    </label>

    <div class="absolute top-full mt-2
                opacity-0 scale-95
                peer-checked:opacity-100
                peer-checked:scale-100
                transition-all duration-300">
        Logout
    </div>

</div>
Clases imprescindibles
  • peer → controla el estado del dropdown.
  • peer-checked:* → activa visibilidad y animación.
  • relative → referencia para posicionamiento.
  • absolute top-full → menú aparece debajo.
  • transition-all → anima opacity y transform.
  • peer-checked:rotate-180 → rota la flecha al abrir.

Importante:
Cada menú (burger y logout) debe tener un id distinto y su propio peer, ya que cada checkbox controla únicamente a sus hermanos posteriores.

Posible código final

   <!--Para el buttron burguer, usaremos este checkbox-->
        {{-- 3) Panel desplegable --}}
        @guest
            <div class="relative">
            <input type="checkbox" class="peer sr-only"  id="burguer_button">
            <label for="burguer_button">
                <img class="w-8 h-8 object-containt" src="{{asset("/images/burguer.png")}}" alt="">
            </label>
                <label for="burguer_button"
                       class="fixed inset-0 bg-black/40
                  opacity-0 pointer-events-none
                  peer-checked:opacity-100
                  peer-checked:pointer-events-auto
                  transition-opacity duration-300">
                </label>
            <div class="opacity-0
                        peer-checked:opacity-100
                        transition-all duration-300 ease-out


             peer-checked:flex absolute bg-white p-2 flex-col left-9 top-4  transition rounded rounded-sm space-y-2">
                <a class="flex-1" href="{{ route('login') }}">
                    <button class="btn btn-sm btn-primary w-full" type="button">
                        Login
                    </button>
                </a>
                <a class="flex-1" href="{{ route('register') }}">
                    <button class="btn btn-sm btn-primary w-full" type="button">
                        Register
                    </button>
                </a>
            </div>
            </div>
        @endguest

        @auth
            <div class="relative">

                <input type="checkbox" class="peer sr-only" id="user_menu">

                <label for="user_menu"
                       class="cursor-pointer text-green-800 text-xl font-semibold">
                    {{ auth()->user()->name }}
                    <!-- Flecha -->
                    <svg class="w-4 h-4 transition-transform duration-300
                    peer-checked:rotate-180"
                         fill="none" stroke="currentColor"
                         viewBox="0 0 24 24">
                        <path stroke-width="2"
                              stroke-linecap="round"
                              stroke-linejoin="round"
                              d="M19 9l-7 7-7-7" />
                    </svg>
                </label>

                <div class="absolute left-0 top-full mt-2
                bg-white p-3 rounded shadow flex flex-col

                opacity-0 scale-95
                peer-checked:opacity-100
                peer-checked:scale-100

                transition-all duration-300 ease-out">

                    <form action="{{ route('logout') }}" method="POST">
                        @csrf
                        <button class="btn btn-primary w-full" type="submit">
                            Logout
                        </button>
                    </form>

                </div>

            </div>
        @endauth