Saltar al contenido

Aunque trabajamos principalmente en el backend, todo desarrollador Laravel necesita entender HTML y CSS. Son la base sobre la que construimos nuestras vistas y componentes Blade.

Este artículo cubre los fundamentos que debes dominar para escribir componentes Blade sólidos.

Qué es HTML

HTML (HyperText Markup Language) es el lenguaje que define la estructura y el contenido de una página web. Funciona mediante etiquetas que envuelven el contenido.

<p>Esto es un párrafo</p>

Las etiquetas tienen un nombre entre signos <>. La mayoría necesitan una etiqueta de apertura y una de cierre con /:

<etiqueta>contenido</etiqueta>

Algunas etiquetas son auto-cerrantes, no necesitan cierre:

<img src="foto.jpg" alt="Descripción">
<input type="text" name="email">
<br>
<hr>

Estructura básica de un documento HTML

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Título de la página</title>
</head>
<body>
    <!-- Aquí va el contenido visible -->
</body>
</html>

El <head> contiene metadatos, el <body> contiene lo que ve el usuario.

Etiquetas esenciales

Texto:

<h1>Título principal</h1>
<h2>Subtítulo</h2>
<h3>Título de sección</h3>
<!-- h4, h5, h6 para niveles menores -->

<p>Párrafo de texto normal.</p>

<strong>Texto importante (negrita)</strong>
<em>Texto enfatizado (cursiva)</em>

<a href="https://ejemplo.com">Enlace</a>

Listas:

<!-- Lista sin orden -->
<ul>
    <li>Elemento 1</li>
    <li>Elemento 2</li>
</ul>

<!-- Lista ordenada -->
<ol>
    <li>Primero</li>
    <li>Segundo</li>
</ol>

Contenedores:

<div>Contenedor genérico (bloque)</div>
<span>Contenedor genérico (en línea)</span>

Semánticos:

<header>Cabecera de página o sección</header>
<nav>Navegación</nav>
<main>Contenido principal (uno por página)</main>
<article>Contenido independiente</article>
<section>Sección temática</section>
<aside>Contenido secundario (sidebar)</aside>
<footer>Pie de página o sección</footer>

Formularios:

<form action="/enviar" method="POST">
    <label for="email">Email</label>
    <input type="email" id="email" name="email">
    
    <label for="mensaje">Mensaje</label>
    <textarea id="mensaje" name="mensaje"></textarea>
    
    <select name="pais">
        <option value="es">España</option>
        <option value="mx">México</option>
    </select>
    
    <button type="submit">Enviar</button>
</form>

Imágenes:

<img src="ruta/imagen.jpg" alt="Descripción de la imagen">

Atributos

Las etiquetas pueden tener atributos que añaden información o comportamiento:

<a href="https://ejemplo.com" target="_blank" title="Ir a ejemplo">
    Enlace
</a>

<input 
    type="email" 
    name="email" 
    id="email" 
    placeholder="[email protected]"
    required
>

Los atributos más comunes son id (identificador único), class (clases CSS) y style (estilos en línea).

Qué es CSS

CSS (Cascading Style Sheets) define la apariencia visual del HTML: colores, tamaños, espaciados, posiciones, etc.

Sintaxis básica

selector {
    propiedad: valor;
    otra-propiedad: otro-valor;
}

Ejemplo:

p {
    color: blue;
    font-size: 16px;
}

Selectores principales

/* Por etiqueta */
p { color: black; }
h1 { font-size: 2rem; }

/* Por clase (con punto) */
.destacado { background: yellow; }
.boton { padding: 10px 20px; }

/* Por ID (con almohadilla) - evitar, poco reutilizable */
#header { height: 60px; }

/* Combinados */
p.intro { font-weight: bold; }      /* p con clase intro */
.card .title { font-size: 1.2rem; } /* .title dentro de .card */

/* Estados */
a:hover { color: red; }             /* Al pasar el ratón */
input:focus { border-color: blue; } /* Al enfocar */

Propiedades esenciales

Texto:

.texto {
    color: #333;                    /* Color del texto */
    font-size: 16px;                /* Tamaño */
    font-weight: bold;              /* Grosor: normal, bold, 100-900 */
    font-family: Arial, sans-serif; /* Tipografía */
    text-align: center;             /* Alineación: left, center, right */
    line-height: 1.5;               /* Altura de línea */
}

Fondo:

.caja {
    background-color: #f5f5f5;
    background-image: url('imagen.jpg');
    background-size: cover;
    background-position: center;
}

Espaciado:

.elemento {
    /* Margen: espacio exterior */
    margin: 20px;              /* Todos los lados */
    margin: 10px 20px;         /* Vertical | Horizontal */
    margin: 10px 20px 15px 5px;/* Top Right Bottom Left */
    margin-top: 10px;          /* Individual */
    
    /* Padding: espacio interior */
    padding: 20px;
    padding: 10px 20px;
}

Dimensiones:

.caja {
    width: 100%;        /* Ancho */
    max-width: 600px;   /* Ancho máximo */
    min-width: 300px;   /* Ancho mínimo */
    height: 200px;      /* Alto */
}

Bordes:

.caja {
    border: 1px solid #ccc;           /* Grosor estilo color */
    border-radius: 8px;               /* Esquinas redondeadas */
    border-bottom: 2px solid blue;    /* Solo un lado */
}

Display:

.bloque { display: block; }        /* Ocupa toda la línea */
.linea { display: inline; }        /* En línea con otros */
.flex { display: flex; }           /* Contenedor flexible */
.grid { display: grid; }           /* Contenedor en cuadrícula */
.oculto { display: none; }         /* Invisible */

Unidades

.elemento {
    /* Absolutas */
    width: 200px;      /* Píxeles */
    
    /* Relativas */
    width: 50%;        /* Porcentaje del padre */
    font-size: 1rem;   /* Relativo al root (16px por defecto) */
    font-size: 1.5em;  /* Relativo al padre */
    width: 100vw;      /* Viewport width */
    height: 100vh;     /* Viewport height */
}

Cómo incluir CSS

Archivo externo (recomendado):

<head>
    <link rel="stylesheet" href="estilos.css">
</head>

En el documento:

<head>
    <style>
        p { color: blue; }
    </style>
</head>

En línea (evitar):

<p style="color: blue;">Texto</p>

Componentes Blade con HTML y CSS

Ahora que conoces las bases, veamos cómo aplicarlas en componentes Blade.

Layout base

{{-- resources/views/components/layouts/app.blade.php --}}
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{ $title ?? config('app.name') }}</title>
    @vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
    <header class="header">
        <nav class="nav">
            {{ $navigation ?? '' }}
        </nav>
    </header>

    <main class="main">
        {{ $slot }}
    </main>

    <footer class="footer">
        {{ $footer ?? '' }}
    </footer>
</body>
</html>
/* resources/css/app.css */
.header {
    background-color: #1f2937;
    padding: 1rem;
}

.nav {
    max-width: 1200px;
    margin: 0 auto;
}

.main {
    max-width: 1200px;
    margin: 0 auto;
    padding: 2rem 1rem;
}

.footer {
    background-color: #f3f4f6;
    padding: 2rem 1rem;
    text-align: center;
}

Componente botón

{{-- resources/views/components/button.blade.php --}}
@props([
    'type' => 'button',
    'variant' => 'primary',
])

<button 
    type="{{ $type }}" 
    {{ $attributes->class(['btn', "btn-{$variant}"]) }}
>
    {{ $slot }}
</button>
.btn {
    display: inline-block;
    padding: 0.5rem 1rem;
    font-size: 1rem;
    font-weight: 500;
    border: none;
    border-radius: 0.375rem;
    cursor: pointer;
}

.btn-primary {
    background-color: #3b82f6;
    color: white;
}

.btn-primary:hover {
    background-color: #2563eb;
}

.btn-secondary {
    background-color: #6b7280;
    color: white;
}

.btn-secondary:hover {
    background-color: #4b5563;
}

.btn-danger {
    background-color: #ef4444;
    color: white;
}

.btn-danger:hover {
    background-color: #dc2626;
}

Uso:

<x-button>Guardar</x-button>
<x-button variant="secondary">Cancelar</x-button>
<x-button type="submit" variant="danger">Eliminar</x-button>

Componente input

{{-- resources/views/components/input.blade.php --}}
@props([
    'name',
    'label',
    'type' => 'text',
    'value' => null,
])

@php
    $id = $name;
    $hasError = $errors->has($name);
@endphp

<div class="form-group">
    <label class="form-label" for="{{ $id }}">
        {{ $label }}
    </label>
    
    <input 
        type="{{ $type }}"
        id="{{ $id }}"
        name="{{ $name }}"
        value="{{ old($name, $value) }}"
        {{ $attributes->class(['form-input', 'form-input-error' => $hasError]) }}
    >
    
    @error($name)
        <p class="form-error">{{ $message }}</p>
    @enderror
</div>
.form-group {
    margin-bottom: 1rem;
}

.form-label {
    display: block;
    margin-bottom: 0.5rem;
    font-weight: 500;
    color: #374151;
}

.form-input {
    width: 100%;
    padding: 0.5rem 0.75rem;
    font-size: 1rem;
    border: 1px solid #d1d5db;
    border-radius: 0.375rem;
}

.form-input:focus {
    outline: none;
    border-color: #3b82f6;
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

.form-input-error {
    border-color: #ef4444;
}

.form-error {
    margin-top: 0.25rem;
    font-size: 0.875rem;
    color: #ef4444;
}

Uso:

<x-input name="email" type="email" label="Correo electrónico" required />
<x-input name="password" type="password" label="Contraseña" />

Componente card

{{-- resources/views/components/card.blade.php --}}
@props([
    'title' => null,
])

<article {{ $attributes->class(['card']) }}>
    @if($title)
        <header class="card-header">
            <h3 class="card-title">{{ $title }}</h3>
        </header>
    @endif
    
    <div class="card-body">
        {{ $slot }}
    </div>
    
    @isset($footer)
        <footer class="card-footer">
            {{ $footer }}
        </footer>
    @endisset
</article>
.card {
    background-color: white;
    border: 1px solid #e5e7eb;
    border-radius: 0.5rem;
    overflow: hidden;
}

.card-header {
    padding: 1rem;
    border-bottom: 1px solid #e5e7eb;
}

.card-title {
    margin: 0;
    font-size: 1.125rem;
    font-weight: 600;
}

.card-body {
    padding: 1rem;
}

.card-footer {
    padding: 1rem;
    background-color: #f9fafb;
    border-top: 1px solid #e5e7eb;
}

Uso:

<x-card title="Usuario">
    <p>Información del usuario...</p>
    
    <x-slot:footer>
        <x-button>Editar</x-button>
    </x-slot:footer>
</x-card>

Componente alerta

{{-- resources/views/components/alert.blade.php --}}
@props([
    'type' => 'info',
])

<div {{ $attributes->class(['alert', "alert-{$type}"]) }} role="alert">
    {{ $slot }}
</div>
.alert {
    padding: 1rem;
    border-radius: 0.375rem;
    margin-bottom: 1rem;
}

.alert-info {
    background-color: #eff6ff;
    color: #1e40af;
    border: 1px solid #bfdbfe;
}

.alert-success {
    background-color: #f0fdf4;
    color: #166534;
    border: 1px solid #bbf7d0;
}

.alert-warning {
    background-color: #fffbeb;
    color: #92400e;
    border: 1px solid #fde68a;
}

.alert-error {
    background-color: #fef2f2;
    color: #dc2626;
    border: 1px solid #fecaca;
}

Uso:

<x-alert type="success">Guardado correctamente.</x-alert>
<x-alert type="error">Ha ocurrido un error.</x-alert>

Siguiente paso: domina Blade

Una vez tienes claros los fundamentos de HTML y CSS, el siguiente paso es dominar Blade, el motor de plantillas de Laravel. Directivas, componentes, slots, stacks... todo lo que necesitas para construir vistas potentes y reutilizables.

👉 Laravel Blade: guía completa del motor de plantillas de Laravel

Conclusión

HTML define la estructura, CSS define la apariencia. Con estos fundamentos y la potencia de los componentes Blade puedes construir interfaces consistentes y mantenibles sin necesidad de ser un experto en frontend.

school Curso completo

Curso Laravel 12
Completo 2026

El único curso 100% actualizado que incluye Laravel 12, Livewire 3, Vue 3, React 19 e Inertia 2. Aprende con proyectos reales y las últimas funcionalidades.

access_time 8 horas de contenido
layers 4 tecnologías en 1
update 100% actualizado
code Proyectos prácticos
Ver Curso Laravel 12 arrow_forward

star Incluido en cualquier suscripción

Rutas de aprendizaje