Skip to content

Unidad 6: Contenido y Formularios en Bootstrap

6.1 Tipografía y Estilos de Texto

Bootstrap proporciona una serie de clases predefinidas para trabajar con tipografía de manera eficiente y responsiva. Estas clases permiten definir el tamaño del texto, su peso, alineación y otros aspectos visuales sin necesidad de escribir CSS personalizado.

Tamaños de Fuente

Bootstrap define tamaños de texto con clases como:

Clase Tamaño aproximado
.fs-1 2.5rem (40px)
.fs-2 2rem (32px)
.fs-3 1.75rem (28px)
.fs-4 1.5rem (24px)
.fs-5 1.25rem (20px)
.fs-6 1rem (16px)

Ejemplo de tamaños de fuente

<p class="fs-1">Texto grande (.fs-1)</p>
<p class="fs-4">Texto mediano (.fs-4)</p>
<p class="fs-6">Texto pequeño (.fs-6)</p>

Encabezados (headings)

Bootstrap mantiene los estilos predeterminados de los encabezados HTML (h1 a h6), pero también permite modificar su tamaño con .h1 a .h6.

Ejemplo de encabezados

<h1 class="h1">Encabezado h1</h1>
<h2 class="h2">Encabezado h2</h2>
<h3 class="h3">Encabezado h3</h3>
<h4 class="h4">Encabezado h4</h4>
<h5 class="h5">Encabezado h5</h5>
<h6 class="h6">Encabezado h6</h6>

En este ejemplo, los encabezados siguen su tamaño original, pero pueden usarse en otros elementos para mantener la jerarquía visual.

Display Headings

Los display headings (.display-1 a .display-6) son encabezados más grandes y llamativos, ideales para títulos destacados.

Clase Tamaño aproximado
.display-1 5rem (80px)
.display-2 4.5rem (72px)
.display-3 4rem (64px)
.display-4 3.5rem (56px)
.display-5 3rem (48px)
.display-6 2.5rem (40px)

Ejemplo de display headings

<h1 class="display-1">Display 1</h1>
<h2 class="display-2">Display 2</h2>
<h3 class="display-3">Display 3</h3>
<h4 class="display-4">Display 4</h4>
<h5 class="display-5">Display 5</h5>
<h6 class="display-6">Display 6</h6>
  • .display-1 es el más grande y .display-6 el más pequeño.
  • Se utilizan para resaltar títulos en una página.

Pesos y Estilos de Fuente

Podemos cambiar el peso del texto usando:

  • fw-bold → Negrita
  • fw-semibold → Seminegrita
  • fw-normal → Normal
  • fw-light → Ligero
  • fst-italic → Cursiva

Ejemplo de pesos y estilos de texto

<p class="fw-bold">Texto en negrita</p>
<p class="fw-light fst-italic">Texto ligero e italic</p>

Alineación de Texto

Podemos alinear el texto usando:

  • text-start → Alineado a la izquierda
  • text-center → Centrado
  • text-end → Alineado a la derecha

Ejemplo de alineación de texto

<p class="text-start">Texto alineado a la izquierda</p>
<p class="text-center">Texto centrado</p>
<p class="text-end">Texto alineado a la derecha</p>

Ejercicio títulos y tamaños de fuentes

  1. Crea un documento HTML dode podamos ver el contenido como en la imagen.
  2. Utiliza las clases de Bootstrap para darle estilo al contenido.
Solución
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Ejemplo de Fuentes con Bootstrap</title>
    <link
    href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
    rel="stylesheet"
    />
</head>
<body class="container mt-4">
    <div class="border border-primary p-4">
    <h1 class="display-1 text-center">Ejemplo de Fuentes en Bootstrap</h1>

    <!-- Tamaños de fuente -->
    <h2 class="fs-1">Texto grande (.fs-1)</h2>
    <h3 class="fs-3">Texto mediano (.fs-3)</h3>
    <h4 class="fs-5">Texto más pequeño (.fs-5)</h4>

    <!-- Encabezados con clases h1-h6 -->
    <h2 class="h1">Encabezado con clase h1</h2>
    <h3 class="h3">Encabezado con clase h3</h3>
    <h4 class="h5">Encabezado con clase h5</h4>

    <!-- Display headings -->
    <h2 class="display-2">Display 2</h2>
    <h3 class="display-4">Display 4</h3>
    <h4 class="display-6">Display 6</h4>

    <!-- Pesos y estilos de fuente -->
    <p class="fw-bold">Texto en negrita (.fw-bold)</p>
    <p class="fw-semibold">Texto semi-negrita (.fw-semibold)</p>
    <p class="fw-normal">Texto normal (.fw-normal)</p>
    <p class="fw-light">Texto ligero (.fw-light)</p>
    <p class="fst-italic">Texto en cursiva (.fst-italic)</p>

    <!-- Alineación de texto -->
    <p class="text-start">Texto alineado a la izquierda (.text-start)</p>
    <p class="text-center">Texto centrado (.text-center)</p>
    <p class="text-end">Texto alineado a la derecha (.text-end)</p>
    </div>

    <!-- Bootstrap JS -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

6.2 Imágenes Responsivas

Bootstrap permite hacer que las imágenes sean adaptables mediante la clase .img-fluid, lo que garantiza que se ajusten automáticamente al ancho del contenedor.

Ejemplo de imagen responsiva

<img src="imagen.jpg" class="img-fluid" alt="Imagen adaptable">

La clase .img-fluid añade max-width: 100% y height: auto para evitar que la imagen se desborde de su contenedor.

Imagen Responsiva

Además, podemos hacer que las imágenes sean circulares o redondeadas con:

  • rounded → Bordes ligeramente redondeados
  • rounded-circle → Imagen circular
  • rounded-pill → Bordes ovalados

Ejemplo de imágenes redondeadas

<img src="imagen.jpg" class="rounded" alt="Bordes redondeados">
<img src="imagen.jpg" class="rounded-circle" alt="Imagen circular">
<img src="imagen.jpg" class="rounded-pill" alt="Bordes ovalados">

Imágenes Redondeadas

Ejercicio imágenes responsivas

  1. Crea un documento HTML donde podamos ver el contenido como en la imagen.
  2. Los tamaños de las imagenes son: 800x400, 200x200, 200x100 y 100x75
  3. Utiliza las clases de Bootstrap para darle estilo al contenido.
Solución
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ejemplo de Imágenes Responsivas en Bootstrap</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="container mt-4 border border-primary p-4">

    <h1 class="text-center mb-4">Ejemplo de Imágenes Responsivas en Bootstrap</h1>

    <!-- Imagen Responsiva -->
    <h2>Imagen Responsiva</h2>
    <img src="./img/bsTutorial800x400.png" class="img-fluid" alt="Imagen adaptable">
    <p>La clase <code>.img-fluid</code> asegura que la imagen se ajuste al tamaño del contenedor.</p>

    <!-- Imágenes con bordes redondeados -->
    <h2>Imágenes Redondeadas</h2>
    <div class="row text-center">
        <div class="col-md-4">
            <img src="./img/bsTutorial200x200.png" class="rounded" alt="Bordes redondeados">
            <p>Imagen con <code>.rounded</code></p>
        </div>
        <div class="col-md-4">
            <img src="./img/bsTutorial200x200.png" class="rounded-circle" alt="Imagen circular">
            <p>Imagen con <code>.rounded-circle</code></p>
        </div>
        <div class="col-md-4">
            <img src="./img/bsTutorial200x100.png" class="rounded-pill" alt="Bordes ovalados">
            <p>Imagen con <code>.rounded-pill</code></p>
        </div>
    </div>

    <!-- Alineación de Imágenes -->
    <h2 class="mt-4">Alineación de Imágenes</h2>
    <p>Usamos las clases <code>.float-start</code>, <code>.float-end</code> y <code>.float-none</code> para cambiar la alineación.</p>

    <img src="./img/bsTutorial150x75.png" class="float-start me-3" alt="Alineación a la izquierda">
    <p>Imagen alineada a la izquierda con <code>.float-start</code>.</p>

    <img src="./img/bsTutorial150x75.png" class="float-end ms-3" alt="Alineación a la derecha">
    <p>Imagen alineada a la derecha con <code>.float-end</code>.</p>

    <div class="clearfix"></div>

    <img src="./img/bsTutorial150x75.png" class="float-none d-block mx-auto" alt="Sin alineación">
    <p class="text-center">Imagen sin alineación (centrada con <code>.float-none</code> y <code>.d-block mx-auto</code>).</p>

    <!-- Bootstrap JS -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

6.2.1 Alineación de Imágenes

Podemos alinear las imágenes con las clases:

  • float-start → Alinea a la izquierda
  • float-end → Alinea a la derecha
  • float-none → Elimina la alineación

Ejemplo de alineación de imágenes

<img src="imagen.jpg" class="float-start" alt="Alineación a la izquierda">
<img src="imagen.jpg" class="float-end" alt="Alineación a la derecha">
<img src="imagen.jpg" class="float-none" alt="Sin alineación">

Alineación de Imágenes


6.3 Tablas y Listas con Bootstrap

🔹 Tablas Estilizadas

Bootstrap permite mejorar la apariencia de las tablas con clases predefinidas como:

  • .table → Activa estilos de tabla básicos
  • .table-striped → Filas con colores alternos
  • .table-bordered → Bordes alrededor de todas las celdas
  • .table-hover → Cambio de color al pasar el mouse
  • .table-dark → Inversión de colores

Ejemplo de tabla estilizada

<table class="table table-striped table-hover table-bordered ">
    <thead>
        <tr>
            <th>#</th>
            <th>Nombre</th>
            <th>Edad</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>1</td>
            <td>Ana</td>
            <td>25</td>
        </tr>
        <tr>
            <td>2</td>
            <td>Juan</td>
            <td>30</td>
        </tr>
    </tbody>
</table>

Tabla Estilizada

Si queremos resaltar una fila o celda, podemos usar las clases:

Ejemplo de tabla con colores

    <!-- On tables -->
    <table class="table-primary">...</table>
    <table class="table-secondary">...</table>
    <table class="table-success">...</table>
    <table class="table-danger">...</table>
    <table class="table-warning">...</table>
    <table class="table-info">...</table>
    <table class="table-light">...</table>
    <table class="table-dark">...</table>

    <!-- On rows -->
    <tr class="table-primary">...</tr>
    <tr class="table-secondary">...</tr>
    <tr class="table-success">...</tr>
    <tr class="table-danger">...</tr>
    <tr class="table-warning">...</tr>
    <tr class="table-info">...</tr>
    <tr class="table-light">...</tr>
    <tr class="table-dark">...</tr>

    <!-- On cells (`td` or `th`) -->
    <tr>
    <td class="table-primary">...</td>
    <td class="table-secondary">...</td>
    <td class="table-success">...</td>
    <td class="table-danger">...</td>
    <td class="table-warning">...</td>
    <td class="table-info">...</td>
    <td class="table-light">...</td>
    <td class="table-dark">...</td>
    </tr>

Listas Estilizadas

Lista sin estilos:

Elimina los estilos predeterminados de las listas con la clase .list-unstyled. Se aplican los márgenes y paddings necesarios. Solo se aplica a la lista principal, no a las listas anidadas.

Ejemplo de lista sin estilos

<ul class="list-unstyled">
    <li>Elemento 1</li>
    <li>Elemento 2</li>
    <li>Nested List
        <ul>
            <li>Elemento 31</li>
            <li>Elemento 32</li>
        </ul>
    </li>
    <li>Elemento 4</li>
</ul>

Listas inline: Elimina las viñetas, aplica algunos estilos y alinea los elementos en línea.

Ejemplo de lista en línea

1
2
3
4
5
<ul class="list-inline">
    <li class="list-inline-item">Elemento 1</li>
    <li class="list-inline-item">Elemento 2</li>
    <li class="list-inline-item">Elemento 3</li>
</ul>

Ahora vamos a ver algunas mejoras de las listas aunque estas se trate más de un componente que de una clase.

Podemos mejorar las listas con las siguientes clases:

  • .list-group → Convierte una lista en un grupo visualmente atractivo
  • .list-group-item → Define cada ítem
  • .list-group-item-action → Hace que los elementos sean clicables
  • .list-group-item-primary → Aplica colores predefinidos

Ejemplo de lista estilizada

1
2
3
4
5
<ul class="list-group">
    <li class="list-group-item active">Elemento activo</li>
    <li class="list-group-item">Elemento normal</li>
    <li class="list-group-item list-group-item-success">Elemento resaltado</li>
</ul>

Lista con enlaces utilizando .list-group-item-action:

Ejemplo de lista con link items

1
2
3
4
5
<div class="list-group  ">
    <a href="#" class="list-group-item list-group-item-action">Elemento 1</a>
    <a href="#" class="list-group-item list-group-item-action">Elemento 2</a>    
    <a href="#" class="list-group-item list-group-item-action">Elemento 3</a>
</div>

Quitar los bordes con .list-group-flush:

Ejemplo de lista sin bordes

1
2
3
4
5
6
7
8
<div class="container mt-3">
    <ul class="list-group list-group-flush">
        <li class="list-group-item">First item</li>
        <li class="list-group-item">Second item</li>
        <li class="list-group-item">Third item</li>
        <li class="list-group-item">Fourth item</li>
    </ul>
</div>

Numbered list utilizando .list-group-numbered:

Ejemplo de lista numerada

1
2
3
4
5
6
7
<div class="container mt-3">
    <ol class="list-group list-group-numbered">
        <li class="list-group-item">First item</li>
        <li class="list-group-item">Second item</li>
        <li class="list-group-item">Third item</li>
    </ol>
</div>

Colores de fondo en los items de la lista:

Lista con Colores de Fondo

Ejemplo de lista con colores de fondo

<ul class="list-group">
    <li class="list-group-item list-group-item-success">Success item</li>
    <li class="list-group-item list-group-item-secondary">Secondary item</li>
    <li class="list-group-item list-group-item-info">Info item</li>
    <li class="list-group-item list-group-item-warning">Warning item</li>
    <li class="list-group-item list-group-item-danger">Danger item</li>
    <li class="list-group-item list-group-item-primary">Primary item</li>
    <li class="list-group-item list-group-item-dark">Dark item</li>
    <li class="list-group-item list-group-item-light">Light item</li>
</ul>

Listas de grupos horizontales

Ejemplo de lista de grupos horizontales

1
2
3
4
5
6
<ul class="list-group list-group-horizontal">
    <li class="list-group-item">First item</li>
    <li class="list-group-item">Second item</li>
    <li class="list-group-item">Third item</li>
    <li class="list-group-item">Fourth item</li>
</ul>

Lista de grupos con insignias badge:

Lista con Insignias

Ejemplo de lista con insignias

<body>
    <div class="container mt-3">
        <h2>List Group With Badges</h2>
        <p>Combine .badge classes with utility/helper classes to add badges inside the list group:</p>
        <ul class="list-group">
            <li class="list-group-item d-flex justify-content-between align-items-center">Inbox
                <span class="badge bg-primary rounded-pill">12</span>
            </li>
            <li class="list-group-item d-flex justify-content-between align-items-center">Ads
                <span class="badge bg-primary rounded-pill">50</span>
            </li>
            <li class="list-group-item d-flex justify-content-between align-items-center">Junk
                <span class="badge bg-primary rounded-pill">99</span>
            </li>
        </ul>
    </div>
</body>

6.5 Formularios en Bootstrap

6.5.1 Introducción a los Formularios

Bootstrap proporciona una estructura flexible y predefinida para trabajar con formularios de manera rápida y responsiva. Los formularios en Bootstrap ofrecen un diseño limpio y coherente en diferentes dispositivos y resoluciones.

Características principales de los formularios en Bootstrap

  • Soporte para inputs, selects, checkboxes, radios y botones estilizados.
  • Diseño responsivo con clases para organizar los elementos en filas y columnas.
  • Posibilidad de usar etiquetas flotantes y validación de formularios.

Estructura básica de un formulario en Bootstrap

Ejemplo de formulario básico en Bootstrap

<form>
    <div class="mb-3">
        <label for="nombre" class="form-label">Nombre</label>
        <input type="text" class="form-control" id="nombre" placeholder="Ingresa tu nombre">
    </div>
    <div class="mb-3">
        <label for="email" class="form-label">Correo Electrónico</label>
        <input type="email" class="form-control" id="email" placeholder="correo@ejemplo.com">
    </div>
    <div class="mb-3 form-check">
        <input type="checkbox" class="form-check-input" id="exampleCheck1">
        <label class="form-check-label" for="exampleCheck1">Check me out</label>
    </div>
    <button type="submit" class="btn btn-primary">Enviar</button>
</form>

Formulario Básico

Nota

Tenga en cuenta también que agregamos una clase .form-label a cada elemento de etiqueta para garantizar un relleno correcto. Las casillas de verificación tienen un marcado diferente. Se envuelven alrededor de un elemento contenedor con .form-check, y las etiquetas tienen una clase .form-check-label, mientras que las casillas de verificación y los botones de opción usan .form-check-input

En este ejemplo, hemos creado un formulario simple con un campo de texto, un campo de correo electrónico y un botón de envío. La estructura básica de un formulario en Bootstrap incluye etiquetas, inputs y botones con clases predefinidas.

Clases importantes en formularios

Clase Descripción
.form-control Aplica estilos a los campos de entrada
.form-label Aplica estilos a las etiquetas de los inputs
.form-text Agrega un texto de ayuda debajo de un input
.form-check Permite diseñar checkboxes y radios
.form-select Estiliza los <select> de un formulario
.input-group Permite agrupar inputs con elementos adicionales
.form-floating Genera etiquetas flotantes en los inputs

6.5.2 Form-Control

La clase .form-control en Bootstrap permite dar estilo a los elementos de entrada de texto (input, textarea), asegurando que sean responsivos y accesibles.

Características de .form-control

  • Aplica un diseño uniforme a inputs y textareas.
  • Se expande al 100% del ancho disponible.
  • Compatible con estados de validación (.is-valid, .is-invalid).
  • Se puede personalizar con diferentes tamaños (.form-control-lg, .form-control-sm).

Ejemplo de .form-control en un formulario

<form>
    <div class="mb-3">
        <label for="nombre" class="form-label">Nombre</label>
        <input type="text" class="form-control" id="nombre" placeholder="Ingresa tu nombre">
    </div>
    <div class="mb-3">
        <label for="comentarios" class="form-label">Comentarios</label>
        <textarea class="form-control" id="comentarios" rows="3"></textarea>
    </div>
    <button type="submit" class="btn btn-primary">Enviar</button>
</form>

Inhabilitar un Campo de Entrada

Podemos deshabilitar un campo de entrada con el atributo disabled.

Ejemplo de campo de entrada deshabilitado

<input type="text" class="form-control" placeholder="Campo deshabilitado" disabled>

Esto impide que el usuario pueda modificar el valor del campo.

Accesibilidad

Asegúrese de que todos los controles de formulario tengan un nombre accesible y apropiado para que su propósito pueda transmitirse a los usuarios de tecnologías de asistencia. La forma más sencilla de lograrlo es utilizar un eleento <label> o, en el caso de los botones, incluir texto suficientemente descriptivo como parte del contenido de <button>...</button>.

Para situaciones en las que no es posible incluir un contenido de texto visible o apropiado en un label, existen formas alternativas de proporcionar un nombre accesible, como:

  • <label>: elementos ocultos usando la clase .visually-hidden
  • Apuntando a un elemento existente que puede actuar como etiqueta usando aria-labelledby
  • Proporcionar un atributo title
  • Establecer explícitamente el nombre accesible en un elemento usandoaria-label

Si no se encuentra presente ninguno de estos elementos, las tecnologías de asistencia pueden recurrir al uso del atributo placeholder como alternativa al nombre accesible en los elementos <input><textarea>. Los ejemplos de esta sección ofrecen algunas sugerencias específicas para cada caso.

Si bien el uso de contenido oculto visualmente ( .visually-hiddenaria-label, e incluso contenio placeholder que desaparece una vez que un campo de formulario tiene contenido) beneficiará a los usuarios de tecnología de asistencia, la falta de texto de etiqueta visible aún puede ser problemática para ciertos usuarios. Algún tipo de etiqueta visible es generalmente el mejor enfoque, tanto para la accesibilidad como para la usabilidad.

Tamaños de .form-control

Podemos cambiar el tamaño del campo de entrada con las clases:

  • .form-control-lg → Input más grande.
  • .form-control-sm → Input más pequeño.

Tamaños de Inputs

Ejemplo de tamaños de inputs

1
2
3
<input type="text" class="form-control form-control-lg" placeholder="Input grande">
<input type="text" class="form-control" placeholder="Input normal">
<input type="text" class="form-control form-control-sm" placeholder="Input pequeño">

Form-text

Se puede crear texto de formulario a nivel de bloque o de nivel de línea utilizando .form-text.

Nota

El texto del formulario debe estar asociado explícitamente con el control de formulario al que se relaciona mediante el atributo aria-describedby. Esto garantizará que las tecnologías de asistencia (como los lectores de pantalla) anuncien este texto del formulario cuando el usuario se centre en el control o ingrese en él.

El texto del formulario que se encuentra debajo de las entradas se puede estilizar con .form-text. Si se va a utilizar un elemento a nivel de bloque, se agrega un margen superior para facilitar el espaciado con respecto a las entradas que se encuentran arriba.

Texto de Formulario

Ejemplo de texto de formulario

1
2
3
4
5
<label for="inputPassword5" class="form-label">Password</label>
<input type="password" id="inputPassword5" class="form-control" aria-describedby="passwordHelpBlock">
<div id="passwordHelpBlock" class="form-text">
    Your password must be 8-20 characters long, contain letters and numbers, and must not contain spaces, special characters, or emoji.
</div>

Texto al lado del control

<div class="row g-3 align-items-center">
    <div class="col-auto">
        <label for="inputPassword6" class="col-form-label">Password</label>
    </div>
    <div class="col-auto">
        <input type="password" id="inputPassword6" class="form-control" aria-describedby="passwordHelpInline">
    </div>
    <div class="col-auto">
        <span id="passwordHelpInline" class="form-text">
            Must be 8-20 characters long.
        </span>
    </div>
</div>

Controles desactivados y de solo lectura

Agregue el atributo disabled booleano a una entrada para darle una apariencia gris, eliminar eventos de puntero y evitar el foco. Agregue el atributo readonly booleano a una entrada para evitar la modificación del valor de la entrada. readonlyLas entradas aún pueden tener foco y seleccionarse, mientras que disabledlas entradas no.

Ejemplo de controles desactivados y de solo lectura

1
2
3
<input class="form-control" type="text" placeholder="Disabled input" aria-label="Disabled input example" disabled>
<input class="form-control" type="text" value="Disabled readonly input" aria-label="Disabled input example"     disabled readonly>
<input class="form-control" type="text" value="Readonly input here..." aria-label="readonly input example" readonly>

texto sin formato de solo lectura

Para permitir que el texto se seleccione y copie, pero no se modifique, use la clase .form-control-plaintext en lugar de .form-control.

Ejemplo de texto sin formato de solo lectura

<div class="mb-3 row">
    <label for="staticEmail" class="col-sm-2 col-form-label">Email</label>
    <div class="col-sm-10">
        <input type="text" readonly class="form-control-plaintext" id="staticEmail" value="email@example.com">
    </div>
</div>
<div class="mb-3 row">
    <label for="inputPassword" class="col-sm-2 col-form-label">Password</label>
    <div class="col-sm-10">
        <input type="password" class="form-control" id="inputPassword">
    </div>
</div>

En el ejemplo podemos ver el texto email@example.com que no se puede modificar y sin el formato de un campo de entrada, pero el campo de contraseña si se puede modificar.

Seleccionar archivos

Seleccionar Archivos

Ejemplo de selección de archivos

<div class="mb-3">
    <label for="formFile" class="form-label">Default file input example</label>
    <input class="form-control" type="file" id="formFile">
</div>
<div class="mb-3">
    <label for="formFileMultiple" class="form-label">Multiple files input example</label>
    <input class="form-control" type="file" id="formFileMultiple" multiple>
</div>
<div class="mb-3">
    <label for="formFileDisabled" class="form-label">Disabled file input example</label>
    <input class="form-control" type="file" id="formFileDisabled" disabled>
</div>
<div class="mb-3">
    <label for="formFileSm" class="form-label">Small file input example</label>
    <input class="form-control form-control-sm" id="formFileSm" type="file">
</div>
<div>
    <label for="formFileLg" class="form-label">Large file input example</label>
    <input class="form-control form-control-lg" id="formFileLg" type="file">
</div>

Seleccionar con un color color picker

Establezca type="color"y agregue '.form-control-color' a <input>. Usamos la clase modificadora para establecer heights fijos y anular algunas inconsistencias entre navegadores.

Selección de Color

Ejemplo de selección de color

<label for="exampleColorInput" class="form-label">Color picker</label>
<input type="color" class="form-control-color" id="exampleColorInput" value="#563d7c" title="Choose your color">

Datalist

Los elementos <datalist> contienen opciones para los controles de entrada con el atributo list. Las opciones se pueden mostrar al enfocar el control de entrada o haciendo clic en un botón.

Ejemplo de datalist

1
2
3
4
5
6
7
8
9
<label for="exampleDataList" class="form-label">Select a browser from this list:</label>
<input class="form-control" list="browsers" id="exampleDataList" placeholder="Type to search...">
<datalist id="browsers">
    <option value="Chrome">
    <option value="Firefox">
    <option value="Safari">
    <option value="Opera">
    <option value="Internet Explorer">
</datalist>

Con esta estructura, los formularios en Bootstrap son fáciles de diseñar y totalmente adaptables.

6.5.3 Form-Select

La clase .form-select permite personalizar los elementos <select> en Bootstrap, proporcionando un estilo más moderno y responsivo.

Características de .form-select

  • Aplica un diseño uniforme a los desplegables <select>.
  • Se expande automáticamente al 100% del ancho disponible.
  • Compatible con estados de validación (.is-valid, .is-invalid).
  • Puede configurarse con diferentes tamaños (.form-select-lg, .form-select-sm).

Ejemplo de .form-select en un formulario

<form>
    <div class="mb-3">
        <label for="opciones" class="form-label">Selecciona una opción</label>
        <select class="form-select" id="opciones">
            <option selected>Elige una opción...</option>
            <option value="1">Opción 1</option>
            <option value="2">Opción 2</option>
            <option value="3">Opción 3</option>
        </select>
    </div>
</form>

Tamaños de .form-select

Podemos modificar el tamaño del select con:

  • .form-select-lg → Select más grande.
  • .form-select-sm → Select más pequeño.

Ejemplo de tamaños de select

<select class="form-select form-select-lg">
    <option selected>Selecciona grande</option>
    <option value="1">Opción 1</option>
</select>

<select class="form-select">
    <option selected>Selecciona normal</option>
    <option value="1">Opción 1</option>
</select>

<select class="form-select form-select-sm">
    <option selected>Selecciona pequeño</option>
    <option value="1">Opción 1</option>
</select>

El atributo multiple está soportado y permite seleccionar múltiples opciones.

Ejemplo de select múltiple

1
2
3
4
5
6
<select class="form-select" multiple aria-label="multiple select example">
    <option selected>Open this select menu</option>
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
</select>

También el atributo disabled da al control una apariencia gris y deshabilita la interacción, eventos de puntero y el foco.

Con esta estructura, los select en Bootstrap son fáciles de estilizar y totalmente adaptables a diferentes necesidades.

6.5.4 Checks y Radios

Bootstrap permite personalizar los checkboxes y radio buttons con la clase .form-check, haciendo que sean más intuitivos y fáciles de estilizar.

Las casillas de verificación y los botones de opción predeterminados del navegador se reemplazan con la ayuda de .form-check, una serie de clases para ambos tipos de entrada que mejora el diseño y el comportamiento de sus elementos HTML, que brindan mayor personalización y consistencia entre navegadores. Las casillas de verificación sirven para seleccionar una o varias opciones en una lista, mientras que los botones de opción sirven para seleccionar una opción entre muchas.

Estructuralmente, nuestros <input>s y <label>s son elementos hermanos en contraposición a un <input> dentro de un <label>. Esto es un poco más detallado, ya que debe especificar id y for sin atributos para relacionar el <input>y <label>. Usamos el selector de hermanos ( ~) para todos nuestros estados <input>, como :checked o :disabled. Cuando se combina con la clase .form-check-label, podemos diseñar fácilmente el texto de cada elemento en función del estado del <input>.

Nuestras comprobaciones utilizan íconos Bootstrap personalizados para indicar estados marcados o indeterminados.

Características de .form-check

  • Se usa para checkboxes y radios.
  • Mejora la accesibilidad y el diseño de los inputs.
  • Puede usarse en disposición horizontal o vertical.
  • Compatible con estados de validación.

Ejemplo de checkbox y radio button

<form>
    <div class="mb-3">
        <label class="form-label">Selecciona tus preferencias</label>
        <div class="form-check">
            <input class="form-check-input" type="checkbox" id="opcion1">
            <label class="form-check-label" for="opcion1">Opción 1</label>
        </div>
        <div class="form-check">
            <input class="form-check-input" type="checkbox" id="opcion2">
            <label class="form-check-label" for="opcion2">Opción 2</label>
        </div>
    </div>
    <div class="mb-3">
        <label class="form-label">Selecciona una opción</label>
        <div class="form-check">
            <input class="form-check-input" type="radio" name="radioGrupo" id="radio1">
            <label class="form-check-label" for="radio1">Opción A</label>
        </div>
        <div class="form-check">
            <input class="form-check-input" type="radio" name="radioGrupo" id="radio2">
            <label class="form-check-label" for="radio2">Opción B</label>
        </div>
    </div>
</form>

Checks y Radios en Línea

Podemos alinear los checkboxes y radios en una sola línea con .form-check-inline.

Ejemplo de checkboxes y radios en línea

1
2
3
4
5
6
7
8
<div class="form-check form-check-inline">
    <input class="form-check-input" type="checkbox" id="check1">
    <label class="form-check-label" for="check1">Check 1</label>
</div>
<div class="form-check form-check-inline">
    <input class="form-check-input" type="checkbox" id="check2">
    <label class="form-check-label" for="check2">Check 2</label>
</div>

Checks Deshabilitados o indeterminados

Los checkboxes pueden estar deshabilitados o en un estado indeterminado. Si están deshabilitados, no se pueden seleccionar. Si están en un estado indeterminado, se muestran con un estilo diferente.

Nota

Revisar: No me ha funcionado en el navegador, pero en la documentación de Bootstrap si se muestra el ejemplo.

Ejemplo de checkbox deshabilitado e indeterminado

1
2
3
4
5
6
7
8
<div class="form-check">
    <input class="form-check-input" type="checkbox" id="checkDisabled" disabled>
    <label class="form-check-label" for="checkDisabled">Check deshabilitado</label>
</div>
<div class="form-check">
    <input class="form-check-input" type="checkbox" id="checkIndeterminate" value="">
    <label class="form-check-label" for="checkIndeterminate">Check indeterminado</label>
</div>

Switches

Un interruptor tiene el marcado de una casilla de verificación personalizada, pero utiliza la clase .form-switch para representar un interruptor de palanca. Considere utilizar el role="switch" para transmitir con mayor precisión la naturaleza del control a las tecnologías de asistencia que admiten esta función. En tecnologías de asistencia más antiguas, simplemente se anunciará como una casilla de verificación normal como alternativa. Los interruptores también admiten el atributo disabled.

Switches

Ejemplo de switch

<div class="form-check form-switch">
    <input class="form-check-input" type="checkbox" role="switch" id="flexSwitchCheckDefault">
    <label class="form-check-label" for="flexSwitchCheckDefault">Default switch checkbox input</label>
</div>
<div class="form-check form-switch">
    <input class="form-check-input" type="checkbox" role="switch" id="flexSwitchCheckChecked" checked>
    <label class="form-check-label" for="flexSwitchCheckChecked">Checked switch checkbox input</label>
</div>
<div class="form-check form-switch">
    <input class="form-check-input" type="checkbox" role="switch" id="flexSwitchCheckDisabled" disabled>
    <label class="form-check-label" for="flexSwitchCheckDisabled">Disabled switch checkbox input</label>
</div>
<div class="form-check form-switch">
    <input class="form-check-input" type="checkbox" role="switch" id="flexSwitchCheckCheckedDisabled" checked disabled>
    <label class="form-check-label" for="flexSwitchCheckCheckedDisabled">Disabled checked switch checkbox input</label>
</div>

Checks y Radios con etiquetas en línea

Agrupe casillas de verificación o radios en la misma fila horizontal agregando .form-check-inline a cualquier .form-check.

Ejemplo de checks y radios en línea

<div class="form-check form-check-inline">
    <input class="form-check-input" type="checkbox" id="inlineCheckbox1" value="option1">
    <label class="form-check-label" for="inlineCheckbox1">1</label>
</div>
<div class="form-check form-check-inline">
    <input class="form-check-input" type="checkbox" id="inlineCheckbox2" value="option2">
    <label class="form-check-label" for="inlineCheckbox2">2</label>
</div>
<div class="form-check form-check-inline">
    <input class="form-check-input" type="checkbox" id="inlineCheckbox3" value="option3" disabled>
    <label class="form-check-label" for="inlineCheckbox3">3 (disabled)</label>
</div>
<div class="form-check form-check-inline">
    <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1">
    <label class="form-check-label" for="inlineRadio1">1</label>
</div>
<div class="form-check form-check-inline">
    <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio2" value="option2">
    <label class="form-check-label" for="inlineRadio2">2</label>
</div>
<div class="form-check form-check-inline">
    <input class="form-check-input" type="radio" name="inlineRadioOptions" id="inlineRadio3" value="option3" disabled>
    <label class="form-check-label" for="inlineRadio3">3 (disabled)</label>
</div>

Toggle buttons

Cree casillas de verificación y botones de opción similares a botones utilizando estilos .btn en lugar de .form-check-label en elementos <label>. Estos botones de alternancia se pueden agrupar en un grupo de botones (buttons group) si es necesario.

Toggle Buttons

Ejemplo de toggle buttons

1
2
3
4
5
6
7
8
<input type="checkbox" class="btn-check" id="btn-check" autocomplete="off">
<label class="btn btn-primary" for="btn-check">Single toggle</label>

<input type="checkbox" class="btn-check" id="btn-check-2" checked autocomplete="off">
<label class="btn btn-primary" for="btn-check-2">Checked</label>

<input type="checkbox" class="btn-check" id="btn-check-3" autocomplete="off" disabled>
<label class="btn btn-primary" for="btn-check-3">Disabled</label>

Botones de alternancia de radio. Solo se puede seleccionar una opción a la vez.

Toggle Radio Buttons

Ejemplo de toggle radio buttons

<input type="radio" class="btn-check" name="options" id="option1" autocomplete="off" checked>
<label class="btn btn-secondary" for="option1">Checked</label>

<input type="radio" class="btn-check" name="options" id="option2" autocomplete="off">
<label class="btn btn-secondary" for="option2">Radio</label>

<input type="radio" class="btn-check" name="options" id="option3" autocomplete="off" disabled>
<label class="btn btn-secondary" for="option3">Disabled</label>

<input type="radio" class="btn-check" name="options" id="option4" autocomplete="off">
<label class="btn btn-secondary" for="option4">Radio</label>

Estilos desalinados (outlined styles)

Los estilos de casillas de verificación y botones de opción se pueden desalinear con .btn-outline-* y .btn-check-outline-*.

Estilos desalineados

Ejemplo de estilos desalineados

<input type="checkbox" class="btn-check" id="btn-check-outlined" autocomplete="off">
<label class="btn btn-outline-primary" for="btn-check-outlined">Single toggle</label><br>

<input type="checkbox" class="btn-check" id="btn-check-2-outlined" checked autocomplete="off">
<label class="btn btn-outline-secondary" for="btn-check-2-outlined">Checked</label><br>

<input type="radio" class="btn-check" name="options-outlined" id="success-outlined" autocomplete="off" checked>
<label class="btn btn-outline-success" for="success-outlined">Checked success radio</label>

<input type="radio" class="btn-check" name="options-outlined" id="danger-outlined" autocomplete="off">
<label class="btn btn-outline-danger" for="danger-outlined">Danger radio</label>

6.5.5 Form-Range

La clase .form-range permite estilizar los elementos <input type="range"> en Bootstrap, proporcionando un diseño moderno y responsivo.

Form-Range

Características de .form-range

  • Aplica un diseño uniforme a los controles de rango.
  • Compatible con diferentes valores y pasos personalizados.
  • Puede combinarse con etiquetas y valores dinámicos.

Ejemplo de un control deslizante

<label for="rango" class="form-label">Selecciona un valor</label>
<input type="range" class="form-range" id="rango" min="0" max="100">

Personalización del Rango

Podemos definir valores mínimos, máximos y pasos intermedios step.

Ejemplo de control de rango personalizado

<label for="rangoCustom" class="form-label">Rango con pasos</label>
<input type="range" class="form-range" id="rangoCustom" min="0" max="50" step="5">

En este caso, el control deslizante se moverá en intervalos de 5 unidades.

Desactivar un Control de Rango

Podemos deshabilitar un control de rango con disabled.

Ejemplo de control de rango deshabilitado

<input type="range" class="form-range" disabled>

Esto impide que el usuario modifique el valor.

Con esta estructura, los controles de rango en Bootstrap son fáciles de personalizar y se adaptan perfectamente a formularios interactivos.

6.5.6 Input-Group

La clase .input-group en Bootstrap permite combinar inputs con etiquetas o botones, facilitando la creación de formularios más funcionales y atractivos.

Características de .input-group

  • Permite agregar iconos, botones o textos dentro de un input.
  • Compatible con diferentes tamaños (.input-group-lg, .input-group-sm).
  • Puede incluir múltiples elementos dentro del grupo.
  • Soporta elementos adicionales antes o después del campo de entrada.
  • Puede usarse con form-control para inputs estilizados.

Veamos primero un ejemplo completo de un input-group con un campo de texto y un botón de búsqueda.

Input-Group

Ejemplo de input-group con varios elementos

<div class="input-group mb-3">
    <span class="input-group-text" id="basic-addon1">@</span>
    <input type="text" class="form-control" placeholder="Username" aria-label="Username" aria-describedby="basic-addon1">
</div>

<div class="input-group mb-3">
    <input type="text" class="form-control" placeholder="Recipient’s username" aria-label="Recipient’s username" aria-describedby="basic-addon2">
    <span class="input-group-text" id="basic-addon2">@example.com</span>
</div>

<div class="mb-3">
    <label for="basic-url" class="form-label">Your vanity URL</label>
    <div class="input-group">
        <span class="input-group-text" id="basic-addon3">https://example.com/users/</span>
        <input type="text" class="form-control" id="basic-url" aria-describedby="basic-addon3 basic-addon4">
    </div>
    <div class="form-text" id="basic-addon4">Example help text goes outside the input group.</div>
</div>

<div class="input-group mb-3">
    <span class="input-group-text">$</span>
    <input type="text" class="form-control" aria-label="Amount (to the nearest dollar)">
    <span class="input-group-text">.00</span>
</div>

<div class="input-group mb-3">
    <input type="text" class="form-control" placeholder="Username" aria-label="Username">
    <span class="input-group-text">@</span>
    <input type="text" class="form-control" placeholder="Server" aria-label="Server">
</div>

<div class="input-group">
    <span class="input-group-text">With textarea</span>
    <textarea class="form-control" aria-label="With textarea"></textarea>
</div>

Ejemplo de Uso Básico

Ejemplo de input con botón

1
2
3
4
<div class="input-group mb-3">
    <input type="text" class="form-control" placeholder="Usuario">
    <button class="btn btn-outline-secondary" type="button">Buscar</button>
</div>

Ejemplo de input con texto adicional

<div class="input-group mb-3">
    <span class="input-group-text">@</span>
    <input type="text" class="form-control" placeholder="Nombre de usuario">
</div>

Ejemplo de input con múltiples elementos

<div class="input-group mb-3">
    <span class="input-group-text">$</span>
    <input type="text" class="form-control" placeholder="Cantidad">
    <span class="input-group-text">.00</span>
</div>

Variaciones de Tamaño

Podemos cambiar el tamaño del input-group para adaptarlo a nuestras necesidades.

Ejemplo de tamaños de input-group

<div class="input-group input-group-lg mb-3">
    <span class="input-group-text">Grande</span>
    <input type="text" class="form-control">
</div>

<div class="input-group mb-3">
    <span class="input-group-text">Normal</span>
    <input type="text" class="form-control">
</div>

<div class="input-group input-group-sm mb-3">
    <span class="input-group-text">Pequeño</span>
    <input type="text" class="form-control">
</div>

Uso de Checkboxes y Radios dentro del Input-Group

Podemos incluir checkboxes o radios dentro del input group para opciones interactivas.

Ejemplo con checkbox y radio dentro del input-group

<div class="input-group mb-3">
    <div class="input-group-text">
        <input class="form-check-input mt-0" type="checkbox" value="">
    </div>
    <input type="text" class="form-control" placeholder="Texto con checkbox">
</div>

<div class="input-group mb-3">
    <div class="input-group-text">
        <input class="form-check-input mt-0" type="radio" value="">
    </div>
    <input type="text" class="form-control" placeholder="Texto con radio">
</div>

Botones dentro del Input-Group

Los botones pueden colocarse antes o después del campo de entrada.

Ejemplo con botón dentro del input-group

<div class="input-group mb-3">
    <button class="btn btn-outline-secondary" type="button">Buscar</button>
    <input type="text" class="form-control" placeholder="Buscar usuario">
</div>

<div class="input-group mb-3">
    <input type="text" class="form-control" placeholder="Enviar mensaje">
    <button class="btn btn-outline-primary" type="button">Enviar</button>
</div>

Inputs con Dropdowns

Podemos agregar un menú desplegable junto a un campo de entrada.

Ejemplo de input con dropdown

<div class="input-group mb-3">
    <button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown">Opciones</button>
    <ul class="dropdown-menu">
        <li><a class="dropdown-item" href="#">Acción 1</a></li>
        <li><a class="dropdown-item" href="#">Acción 2</a></li>
        <li><a class="dropdown-item" href="#">Acción 3</a></li>
    </ul>
    <input type="text" class="form-control" placeholder="Selecciona una opción">
</div>

Con esta estructura, el input-group en Bootstrap proporciona una forma flexible y eficiente para mejorar la experiencia del usuario en formularios web.

6.5.7 Floating Labels

Cree etiquetas de formulario maravillosamente simples que floten sobre sus campos de entrada. La clase .form-floating en Bootstrap permite utilizar etiquetas flotantes en los campos de entrada, lo que mejora la legibilidad del formulario y ahorra espacio.

Características de .form-floating

  • Aplica etiquetas flotantes a los inputs sin necesidad de placeholder.
  • Compatible con diferentes tipos de inputs (text, email, password, textarea, etc.).
  • Funciona con validaciones y estilos personalizados.
  • Mejora la accesibilidad y experiencia de usuario en formularios.

Floating Labels, antes de tener el foco

Floating Labels, después de tener el foco

Uso Básico de Floating Labels

Ejemplo básico de floating label

<div class="form-floating mb-3">
    <input type="email" class="form-control" id="email" placeholder="nombre@ejemplo.com">
    <label for="email">Correo Electrónico</label>
</div>

En este ejemplo, la etiqueta flotará automáticamente cuando el usuario escriba en el campo.

Floating Labels con Textareas

Podemos usar etiquetas flotantes con áreas de texto extendidas.

Ejemplo de floating label con textarea

<div class="form-floating">
    <textarea class="form-control" placeholder="Escribe tu mensaje" id="mensaje" style="height: 100px"></textarea>
    <label for="mensaje">Mensaje</label>
</div>

Floating Labels en Formularios con Select

Los select también pueden usar etiquetas flotantes para mejorar su presentación.

Ejemplo de floating label con select

<div class="form-floating">
    <select class="form-select" id="opciones" aria-label="Floating label select example">
        <option selected>Selecciona una opción</option>
        <option value="1">Opción 1</option>
        <option value="2">Opción 2</option>
        <option value="3">Opción 3</option>
    </select>
    <label for="opciones">Elige una opción</label>
</div>

Otras consideraciones

Podemos utilizar el atributo disabled para deshabilitar el campo de entrada y la etiqueta flotante. También el atributo readonly que permite que el texto se seleccione y copie, pero no se modifique.

Floating Labels y Inputs Groups

Floating labels también está soportado en input-groups.

Ejemplo de floating label en input-group

<div class="input-group mb-3">
    <span class="input-group-text">@</span>
    <div class="form-floating">
        <input type="text" class="form-control" id="floatingInputGroup1" placeholder="Username">
        <label for="floatingInputGroup1">Username</label>
    </div>
</div>

Nota

Al utilizar .input-group y .form-floating junto con la validación de formularios, el -feedback debe colocarse fuera del .form-floating, pero dentro del .input-group. Esto significa que la retroalimentación deberá mostrarse mediante javascript.

Ejemplo de floating label en input-group con validación

<div class="input-group has-validation">
    <span class="input-group-text">@</span>
    <div class="form-floating is-invalid">
        <input type="text" class="form-control is-invalid" id="floatingInputGroup2" placeholder="Username" required>
        <label for="floatingInputGroup2">Username</label>
    </div>
    <div class="invalid-feedback">
        Please choose a username.
    </div>
</div>

Dispocisión

Al trabajar con el sistema de cuadrícula Bootstrap, asegúrese de colocar los elementos del formulario dentro de las clases de columna.

Floating Labels en columnas

Ejemplo de floating label en columnas

<div class="row g-2">
    <div class="col-md">
        <div class="form-floating">
        <input type="email" class="form-control" id="floatingInputGrid" placeholder="name@example.com" value="mdo@example.com">
        <label for="floatingInputGrid">Email address</label>
        </div>
    </div>
    <div class="col-md">
        <div class="form-floating">
        <select class="form-select" id="floatingSelectGrid">
            <option selected>Open this select menu</option>
            <option value="1">One</option>
            <option value="2">Two</option>
            <option value="3">Three</option>
        </select>
        <label for="floatingSelectGrid">Works with selects</label>
        </div>
    </div>
</div>

6.5.8 Layout en Formularios

Dale estructura a tus formularios (desde implementaciones en línea hasta horizontales o de cuadrícula personalizadas) con nuestras opciones de diseño de formularios. Bootstrap ofrece distintas formas de estructurar y organizar los formularios para hacerlos más eficientes y adaptables a diferentes dispositivos.

Nota

Cada grupo de campos de formulario debe residir en un <form>. Bootstrap no proporciona un estilo predeterminado para el <form>, pero hay algunas funciones de navegador potentes que se proporcionan de forma predeterminada. - Los <button>s dentro de un <form> tiene un valor predeterminado para type="submit", así que trate de ser específico y siempre incluya un type. Como Bootstrap se aplica display: block y width: 100% a casi todos nuestros controles de formulario, los formularios se apilarán verticalmente de forma predeterminada. Se pueden usar clases adicionales para variar este diseño en función de cada formulario.

Las utilidades de margen son la forma más sencilla de agregar estructura a los formularios. Proporcionan una agrupación básica de etiquetas, controles, texto de formulario opcional y mensajes de validación de formulario. Recomendamos utilizar únicamente utilidades margin-bottom y una única dirección en todo el formulario para mantener la coherencia.

Siéntete libre de construir tus formularios como quieras, con <fieldset>s, <div>s o casi cualquier otro elemento.

Formularios en Línea

Los formularios pueden colocarse en una sola línea utilizando las clases .row y .col-auto para ajustar los campos.

Ejemplo de formulario en línea

<form class="row g-3">
    <div class="col-auto">
        <input type="text" class="form-control" placeholder="Nombre">
    </div>
    <div class="col-auto">
        <input type="email" class="form-control" placeholder="Correo">
    </div>
    <div class="col-auto">
        <button type="submit" class="btn btn-primary">Enviar</button>
    </div>
</form>

Formularios en Columnas

Para formularios más estructurados, podemos colocar cada campo en columnas específicas dentro de .row.

Ejemplo de formulario en columnas

<form>
    <div class="row mb-3">
        <div class="col">
            <input type="text" class="form-control" placeholder="Nombre">
        </div>
        <div class="col">
            <input type="email" class="form-control" placeholder="Correo Electrónico">
        </div>
    </div>
    <button type="submit" class="btn btn-primary">Enviar</button>
</form>

Formularios Completos en Contenedores

Los formularios pueden ser contenidos dentro de .container para ajustar el ancho de la página automáticamente.

Ejemplo de formulario en un contenedor

<div class="container">
    <form>
        <div class="mb-3">
            <label for="nombre" class="form-label">Nombre</label>
            <input type="text" class="form-control" id="nombre">
        </div>
        <div class="mb-3">
            <label for="email" class="form-label">Correo Electrónico</label>
            <input type="email" class="form-control" id="email">
        </div>
        <button type="submit" class="btn btn-primary">Enviar</button>
    </form>
</div>

Con estas opciones, Bootstrap permite organizar y presentar los formularios de manera flexible y optimizada para diferentes pantallas.

Formulario complejo creado con grid system

Ejemplo de formulario con grid system:

Formulario con grid system

Ejemplo de formulario con grid system

<form class="row g-3">
    <div class="col-md-6">
        <label for="inputEmail4" class="form-label">Email</label>
        <input type="email" class="form-control" id="inputEmail4">
    </div>
    <div class="col-md-6">
        <label for="inputPassword4" class="form-label">Password</label>
        <input type="password" class="form-control" id="inputPassword4">
    </div>
    <div class="col-12">
        <label for="inputAddress" class="form-label">Address</label>
        <input type="text" class="form-control" id="inputAddress" placeholder="1234 Main St">
    </div>
    <div class="col-12">
        <label for="inputAddress2" class="form-label">Address 2</label>
        <input type="text" class="form-control" id="inputAddress2" placeholder="Apartment, studio, or floor">
    </div>
    <div class="col-md-6">
        <label for="inputCity" class="form-label">City</label>
        <input type="text" class="form-control" id="inputCity">
    </div>
    <div class="col-md-4">
        <label for="inputState" class="form-label">State</label>
        <select id="inputState" class="form-select">
        <option selected>Choose...</option>
        <option>...</option>
        </select>
    </div>
    <div class="col-md-2">
        <label for="inputZip" class="form-label">Zip</label>
        <input type="text" class="form-control" id="inputZip">
    </div>
    <div class="col-12">
        <div class="form-check">
        <input class="form-check-input" type="checkbox" id="gridCheck">
        <label class="form-check-label" for="gridCheck">
            Check me out
        </label>
        </div>
    </div>
    <div class="col-12">
        <button type="submit" class="btn btn-primary">Sign in</button>
    </div>
</form>

6.5.9 Validación de Formularios

Bootstrap ofrece un sistema de validación de formularios basado en clases CSS y atributos HTML5 para proporcionar una experiencia de usuario clara y accesible.

Proporcione comentarios valiosos y prácticos a sus usuarios con la validación de formularios HTML5, a través de comportamientos predeterminados del navegador o estilos personalizados y JavaScript.

Como funciona

Así es como funciona la validación de formularios con Bootstrap:

  • La validación de formularios HTML se aplica a través de las dos pseudoclases de CSS, :invalid:valid. Se aplica a los elementos <input><select><textarea>.
  • Bootstrap limita el alcance de los estilos :invalid:valida la clase principal .was-validated, que normalmente se aplica a <form>. De lo contrario, cualquier campo obligatorio sin un valor se muestra como no válido al cargar la página. De esta manera, puede elegir cuándo activarlos (normalmente después de intentar enviar el formulario).
  • Para restablecer la apariencia del formulario (por ejemplo, en el caso de envíos de formularios dinámicos usando Ajax), elimine la clase .was-validated de <form>nuevamente después del envío.
  • Como alternativa, se pueden utilizar clases .is-invalid.is-valid en lugar de pseudoclases para la validación del lado del servidor . No requieren una .was-validatedclase principal.
  • Debido a limitaciones en el funcionamiento de CSS, no podemos (actualmente) aplicar estilos a un elemento <label> que viene antes de un control de formulario en el DOM sin la ayuda de JavaScript personalizado.
  • Todos los navegadores modernos admiten la API de validación de restricciones, una serie de métodos de JavaScript para validar controles de formulario.
  • Los mensajes de comentarios pueden utilizar los valores predeterminados del navegador (diferentes para cada navegador y sin estilos a través de CSS) o nuestros estilos de comentarios personalizados con HTML y CSS adicionales.
  • Puede proporcionar mensajes de validez personalizados setCustomValidity en JavaScript.

Con esto en mente, considere las siguientes demostraciones de nuestros estilos de validación de formularios personalizados, clases opcionales del lado del servidor y valores predeterminados del navegador.

Estilos personalizados de validación

Para los mensajes de validación de formularios personalizados de Bootstrap, deberá agregar el atributo booleano novalidate a su <form>. Esto deshabilita las sugerencias de herramientas de comentarios predeterminadas del navegador, pero aún brinda acceso a las API de validación de formularios en JavaScript. Intente enviar el formulario a continuación; nuestro JavaScript interceptará el botón de envío y le transmitirá los comentarios. Cuando intente enviarlo, verá los estilos :invalidy :validaplicados a los controles de su formulario.

Los estilos de comentarios personalizados aplican colores, bordes, estilos de enfoque e íconos de fondo personalizados para comunicar mejor los comentarios. Los íconos de fondo para <select>s solo están disponibles con .form-select, y no con .form-control.

Aquí tenemos un ejemplo de formulario pre validación:

Formulario pre-validación

y aquí el formulario post-validación:

Formulario post-validación

Ejemplo de formulario antes de la validación
<form class="row g-3 needs-validation" novalidate>
<div class="col-md-4">
    <label for="validationCustom01" class="form-label">First name</label>
    <input type="text" class="form-control" id="validationCustom01" value="Mark" required>
    <div class="valid-feedback">
    Looks good!
    </div>
</div>
<div class="col-md-4">
    <label for="validationCustom02" class="form-label">Last name</label>
    <input type="text" class="form-control" id="validationCustom02" value="Otto" required>
    <div class="valid-feedback">
    Looks good!
    </div>
</div>
<div class="col-md-4">
    <label for="validationCustomUsername" class="form-label">Username</label>
    <div class="input-group has-validation">
    <span class="input-group-text" id="inputGroupPrepend">@</span>
    <input type="text" class="form-control" id="validationCustomUsername" aria-describedby="inputGroupPrepend" required>
    <div class="invalid-feedback">
        Please choose a username.
    </div>
    </div>
</div>
<div class="col-md-6">
    <label for="validationCustom03" class="form-label">City</label>
    <input type="text" class="form-control" id="validationCustom03" required>
    <div class="invalid-feedback">
    Please provide a valid city.
    </div>
</div>
<div class="col-md-3">
    <label for="validationCustom04" class="form-label">State</label>
    <select class="form-select" id="validationCustom04" required>
    <option selected disabled value="">Choose...</option>
    <option>...</option>
    </select>
    <div class="invalid-feedback">
    Please select a valid state.
    </div>
</div>
<div class="col-md-3">
    <label for="validationCustom05" class="form-label">Zip</label>
    <input type="text" class="form-control" id="validationCustom05" required>
    <div class="invalid-feedback">
    Please provide a valid zip.
    </div>
</div>
<div class="col-12">
    <div class="form-check">
    <input class="form-check-input" type="checkbox" value="" id="invalidCheck" required>
    <label class="form-check-label" for="invalidCheck">
        Agree to terms and conditions
    </label>
    <div class="invalid-feedback">
        You must agree before submitting.
    </div>
    </div>
</div>
<div class="col-12">
    <button class="btn btn-primary" type="submit">Submit form</button>
</div>
</form>

Observar que la única diferencia entre el formulario pre-validación y el post-validación es la adición de la clase was-validated al formulario. Esta clase se agrega automáticamente a través de JavaScript cuando se intenta enviar el formulario. Si desea agregarla manualmente, asegúrese de hacerlo después de la validación del formulario. Más abajo se muestra el código JavaScript necesario para la validación de formularios.

Ejemplo de formulario después de la validación
    <form class="row g-3 needs-validation was-validated" novalidate="">
    <div class="col-md-4">
        <label for="validationCustom01" class="form-label">First name</label>
        <input type="text" class="form-control" id="validationCustom01" value="Mark" required="">
        <div class="valid-feedback">
        Looks good!
        </div>
    </div>
    <div class="col-md-4">
        <label for="validationCustom02" class="form-label">Last name</label>
        <input type="text" class="form-control" id="validationCustom02" value="Otto" required="">
        <div class="valid-feedback">
        Looks good!
        </div>
    </div>
    <div class="col-md-4">
        <label for="validationCustomUsername" class="form-label">Username</label>
        <div class="input-group has-validation">
        <span class="input-group-text" id="inputGroupPrepend">@</span>
        <input type="text" class="form-control" id="validationCustomUsername" aria-describedby="inputGroupPrepend" required="">
        <div class="invalid-feedback">
            Please choose a username.
        </div>
        </div>
    </div>
    <div class="col-md-6">
        <label for="validationCustom03" class="form-label">City</label>
        <input type="text" class="form-control" id="validationCustom03" required="">
        <div class="invalid-feedback">
        Please provide a valid city.
        </div>
    </div>
    <div class="col-md-3">
        <label for="validationCustom04" class="form-label">State</label>
        <select class="form-select" id="validationCustom04" required="">
        <option selected="" disabled="" value="">Choose...</option>
        <option>...</option>
        </select>
        <div class="invalid-feedback">
        Please select a valid state.
        </div>
    </div>
    <div class="col-md-3">
        <label for="validationCustom05" class="form-label">Zip</label>
        <input type="text" class="form-control" id="validationCustom05" required="">
        <div class="invalid-feedback">
        Please provide a valid zip.
        </div>
    </div>
    <div class="col-12">
        <div class="form-check">
        <input class="form-check-input" type="checkbox" value="" id="invalidCheck" required="">
        <label class="form-check-label" for="invalidCheck">
            Agree to terms and conditions
        </label>
        <div class="invalid-feedback">
            You must agree before submitting.
        </div>
        </div>
    </div>
    <div class="col-12">
        <button class="btn btn-primary" type="submit">Submit form</button>
    </div>
    </form>
</body>
</html>

Código JS para la validación de formularios:

Pata que la validación del formulario anterior funcione, es necesario agregar el siguiente código JavaScript. Podemos hacerlo de dos formas, una es agregarlo directamente en el archivo HTML, y la otra es crear un archivo JavaScript y enlazarlo en el archivo HTML.

   document.addEventListener("DOMContentLoaded", function () {
        const form = document.querySelector(".needs-validation");

        form.addEventListener("submit", function (event) {
            if (!form.checkValidity()) {
                event.preventDefault(); // Evita el envío si el formulario no es válido
                event.stopPropagation();
            }

        form.classList.add("was-validated"); // Agrega la clase para activar la validación visual
        });
    });

Validación por defecto del navegador

¿No está interesado en los mensajes de retroalimentación de validación personalizados o en escribir JavaScript para cambiar los comportamientos del formulario? Todo bien, puedes usar los valores predeterminados del navegador. Intenta enviar el siguiente formulario. Dependiendo de su navegador y sistema operativo, verá un estilo de comentarios ligeramente diferente.

Si bien estos estilos de retroalimentación no se pueden diseñar con CSS, aún puede personalizar el texto de retroalimentación a través de JavaScript.

Prueba el siguiente formulario, donde no se han añadido textos de retroalimentación personalizados:

Ejemplo de formulario con validación por defecto del navegador
<form class="row g-3">
<div class="col-md-4">
    <label for="validationDefault01" class="form-label">First name</label>
    <input type="text" class="form-control" id="validationDefault01" value="Mark" required>
</div>
<div class="col-md-4">
    <label for="validationDefault02" class="form-label">Last name</label>
    <input type="text" class="form-control" id="validationDefault02" value="Otto" required>
</div>
<div class="col-md-4">
    <label for="validationDefaultUsername" class="form-label">Username</label>
    <div class="input-group">
    <span class="input-group-text" id="inputGroupPrepend2">@</span>
    <input type="text" class="form-control" id="validationDefaultUsername" aria-describedby="inputGroupPrepend2" required>
    </div>
</div>
<div class="col-md-6">
    <label for="validationDefault03" class="form-label">City</label>
    <input type="text" class="form-control" id="validationDefault03" required>
</div>
<div class="col-md-3">
    <label for="validationDefault04" class="form-label">State</label>
    <select class="form-select" id="validationDefault04" required>
    <option selected disabled value="">Choose...</option>
    <option>...</option>
    </select>
</div>
<div class="col-md-3">
    <label for="validationDefault05" class="form-label">Zip</label>
    <input type="text" class="form-control" id="validationDefault05" required>
</div>
<div class="col-12">
    <div class="form-check">
    <input class="form-check-input" type="checkbox" value="" id="invalidCheck2" required>
    <label class="form-check-label" for="invalidCheck2">
        Agree to terms and conditions
    </label>
    </div>
</div>
<div class="col-12">
    <button class="btn btn-primary" type="submit">Submit form</button>
</div>
</form>

Validación del lado del servidor

Si bien la validación del lado del servidor es importante para garantizar la seguridad y la integridad de los datos, Bootstrap no proporciona estilos predeterminados para la validación del lado del servidor. Sin embargo, puede utilizar las clases .is-invalid y .is-valid para aplicar estilos personalizados a los campos de formulario.

Para solucionar problemas con el border radious, los grupos de entrada requieren una clase .has-validation adicional.

Validación del lado del servidor

Ejemplo de validación del lado del servidor

 <form class="row g-3">
<div class="col-md-4">
    <label for="validationServer01" class="form-label">First name</label>
    <input type="text" class="form-control is-valid" id="validationServer01" value="Mark" required>
    <div class="valid-feedback">
    Looks good!
    </div>
</div>
<div class="col-md-4">
    <label for="validationServer02" class="form-label">Last name</label>
    <input type="text" class="form-control is-valid" id="validationServer02" value="Otto" required>
    <div class="valid-feedback">
    Looks good!
    </div>
</div>
<div class="col-md-4">
    <label for="validationServerUsername" class="form-label">Username</label>
    <div class="input-group has-validation">
    <span class="input-group-text" id="inputGroupPrepend3">@</span>
    <input type="text" class="form-control is-invalid" id="validationServerUsername" aria-describedby="inputGroupPrepend3 validationServerUsernameFeedback" required>
    <div id="validationServerUsernameFeedback" class="invalid-feedback">
        Please choose a username.
    </div>
    </div>
</div>
<div class="col-md-6">
    <label for="validationServer03" class="form-label">City</label>
    <input type="text" class="form-control is-invalid" id="validationServer03" aria-describedby="validationServer03Feedback" required>
    <div id="validationServer03Feedback" class="invalid-feedback">
    Please provide a valid city.
    </div>
</div>
<div class="col-md-3">
    <label for="validationServer04" class="form-label">State</label>
    <select class="form-select is-invalid" id="validationServer04" aria-describedby="validationServer04Feedback" required>
    <option selected disabled value="">Choose...</option>
    <option>...</option>
    </select>
    <div id="validationServer04Feedback" class="invalid-feedback">
    Please select a valid state.
    </div>
</div>
<div class="col-md-3">
    <label for="validationServer05" class="form-label">Zip</label>
    <input type="text" class="form-control is-invalid" id="validationServer05" aria-describedby="validationServer05Feedback" required>
    <div id="validationServer05Feedback" class="invalid-feedback">
    Please provide a valid zip.
    </div>
</div>
<div class="col-12">
    <div class="form-check">
    <input class="form-check-input is-invalid" type="checkbox" value="" id="invalidCheck3" aria-describedby="invalidCheck3Feedback" required>
    <label class="form-check-label" for="invalidCheck3">
        Agree to terms and conditions
    </label>
    <div id="invalidCheck3Feedback" class="invalid-feedback">
        You must agree before submitting.
    </div>
    </div>
</div>
<div class="col-12">
    <button class="btn btn-primary" type="submit">Submit form</button>
</div>
</form>

Elementos soportados por la validación

La validación de formularios HTML5 se aplica a través de las dos pseudoclases de CSS, :invalid y :valid. Se aplica a los elementos <input>, <select> y <textarea>. Bootstrap limita el alcance de los estilos :invalid y :valid a la clase principal .was-validated, que normalmente se aplica a <form>. De lo contrario, cualquier campo obligatorio sin un valor se muestra como no válido al cargar la página. De esta manera, puede elegir cuándo activarlos (normalmente después de intentar enviar el formulario).

Los estilos de validación están disponibles para los siguientes controles y componentes de formulario:

  • <input>s and <textarea>s with .form-control (including up to one .form-control in input groups)
  • <select>S con.form-select
  • .form-checkS con.form-check-input y.form-check-label

Aquí tenemos un ejemplo de formulario con validación de formularios personalizados, clases opcionales del lado del servidor y valores predeterminados del navegador.

Formulario con validación de formularios personalizados

<form class="was-validated">
<div class="mb-3">
    <label for="validationTextarea" class="form-label">Textarea</label>
    <textarea class="form-control" id="validationTextarea" placeholder="Required example textarea" required></textarea>
    <div class="invalid-feedback">
    Please enter a message in the textarea.
    </div>
</div>

<div class="form-check mb-3">
    <input type="checkbox" class="form-check-input" id="validationFormCheck1" required>
    <label class="form-check-label" for="validationFormCheck1">Check this checkbox</label>
    <div class="invalid-feedback">Example invalid feedback text</div>
</div>

<div class="form-check">
    <input type="radio" class="form-check-input" id="validationFormCheck2" name="radio-stacked" required>
    <label class="form-check-label" for="validationFormCheck2">Toggle this radio</label>
</div>
<div class="form-check mb-3">
    <input type="radio" class="form-check-input" id="validationFormCheck3" name="radio-stacked" required>
    <label class="form-check-label" for="validationFormCheck3">Or toggle this other radio</label>
    <div class="invalid-feedback">More example invalid feedback text</div>
</div>

<div class="mb-3">
    <select class="form-select" required aria-label="select example">
    <option value="">Open this select menu</option>
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
    </select>
    <div class="invalid-feedback">Example invalid select feedback</div>
</div>

<div class="mb-3">
    <input type="file" class="form-control" aria-label="file example" required>
    <div class="invalid-feedback">Example invalid form file feedback</div>
</div>

<div class="mb-3">
    <button class="btn btn-primary" type="submit" disabled>Submit form</button>
</div>
</form>
Ejemplo de validación completa
<?php
$errores = [];
$datos = [
    'first_name'   => '',
    'last_name'    => '',
    'username'     => '',
    'city'         => '',
    'community'    => '',
    'postal_code'  => '',
    'terms'        => ''
];

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    // Sanitización
    $datos['first_name'] = filter_input(INPUT_POST, 'first_name', FILTER_SANITIZE_SPECIAL_CHARS);
    $datos['last_name'] = filter_input(INPUT_POST, 'last_name', FILTER_SANITIZE_SPECIAL_CHARS);
    $datos['username'] = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_EMAIL);
    $datos['city'] = filter_input(INPUT_POST, 'city', FILTER_SANITIZE_SPECIAL_CHARS);
    $datos['community'] = filter_input(INPUT_POST, 'community', FILTER_SANITIZE_SPECIAL_CHARS);
    $datos['postal_code'] = filter_input(INPUT_POST, 'postal_code', FILTER_SANITIZE_NUMBER_INT);
    $datos['terms'] = isset($_POST['terms']) ? '1' : '';

    // Validaciones
    if (strlen($datos['first_name']) < 2 || strlen($datos['first_name']) > 40) {
        $errores['first_name'] = "El nombre debe tener entre 2 y 40 caracteres.";
    }

    if (strlen($datos['last_name']) < 2 || strlen($datos['last_name']) > 40) {
        $errores['last_name'] = "El apellido debe tener entre 2 y 40 caracteres.";
    }

    if (!filter_var($datos['username'], FILTER_VALIDATE_EMAIL)) {
        $errores['username'] = "El email no es válido.";
    }

    if (strlen($datos['city']) < 2 || strlen($datos['city']) > 50) {
        $errores['city'] = "La ciudad debe tener entre 2 y 50 caracteres.";
    }

    if (!in_array($datos['community'], ['valencia', 'andalucia'])) {
        $errores['community'] = "Debes seleccionar una comunidad válida.";
    }

    if (!preg_match('/^\d{5}$/', $datos['postal_code'])) {
        $errores['postal_code'] = "El código postal debe tener exactamente 5 dígitos.";
    }

    if (empty($datos['terms'])) {
        $errores['terms'] = "Debes aceptar los términos y condiciones.";
    }

    $exito = empty($errores);
}
?>

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Formulario con Validación PHP</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<div class="container mt-5">
<h1>Formulario</h1>

<?php if (isset($exito) && $exito): ?>
    <div class="alert alert-success">Formulario enviado correctamente.</div>
<?php elseif (!empty($errores)): ?>
    <div class="alert alert-danger">Hay errores en el formulario. Por favor, revísalo.</div>
<?php endif; ?>

<form class="row g-3" method="POST" novalidate>
    <!-- First Name -->
    <div class="col-md-4">
    <label for="first_name" class="form-label">First name</label>
    <input type="text" class="form-control <?= isset($errores['first_name']) ? 'is-invalid' : ($datos['first_name'] ? 'is-valid' : '') ?>"
            id="first_name" name="first_name" value="<?= htmlspecialchars($datos['first_name']) ?>" required>
    <?php if (isset($errores['first_name'])): ?>
        <div class="invalid-feedback"><?= $errores['first_name'] ?></div>
    <?php elseif ($datos['first_name']): ?>
        <div class="valid-feedback">Looks good!</div>
    <?php endif; ?>
    </div>

    <!-- Last Name -->
    <div class="col-md-4">
    <label for="last_name" class="form-label">Last name</label>
    <input type="text" class="form-control <?= isset($errores['last_name']) ? 'is-invalid' : ($datos['last_name'] ? 'is-valid' : '') ?>"
            id="last_name" name="last_name" value="<?= htmlspecialchars($datos['last_name']) ?>" required>
    <?php if (isset($errores['last_name'])): ?>
        <div class="invalid-feedback"><?= $errores['last_name'] ?></div>
    <?php elseif ($datos['last_name']): ?>
        <div class="valid-feedback">Looks good!</div>
    <?php endif; ?>
    </div>

    <!-- Email -->
    <div class="col-md-4">
    <label for="username" class="form-label">Email</label>
    <input type="email" class="form-control <?= isset($errores['username']) ? 'is-invalid' : ($datos['username'] ? 'is-valid' : '') ?>"
            id="username" name="username" value="<?= htmlspecialchars($datos['username']) ?>" required>
    <?php if (isset($errores['username'])): ?>
        <div class="invalid-feedback"><?= $errores['username'] ?></div>
    <?php elseif ($datos['username']): ?>
        <div class="valid-feedback">Email válido</div>
    <?php endif; ?>
    </div>

    <!-- City -->
    <div class="col-md-6">
    <label for="city" class="form-label">City</label>
    <input type="text" class="form-control <?= isset($errores['city']) ? 'is-invalid' : ($datos['city'] ? 'is-valid' : '') ?>"
            id="city" name="city" value="<?= htmlspecialchars($datos['city']) ?>" required>
    <?php if (isset($errores['city'])): ?>
        <div class="invalid-feedback"><?= $errores['city'] ?></div>
    <?php elseif ($datos['city']): ?>
        <div class="valid-feedback">Looks good!</div>
    <?php endif; ?>
    </div>

    <!-- Community -->
    <div class="col-md-3">
    <label for="community" class="form-label">Community</label>
    <select class="form-select <?= isset($errores['community']) ? 'is-invalid' : ($datos['community'] ? 'is-valid' : '') ?>"
            id="community" name="community" required>
        <option value="" disabled <?= $datos['community'] == '' ? 'selected' : '' ?>>Choose...</option>
        <option value="valencia" <?= $datos['community'] == 'valencia' ? 'selected' : '' ?>>Valencia</option>
        <option value="andalucia" <?= $datos['community'] == 'andalucia' ? 'selected' : '' ?>>Andalucía</option>
    </select>
    <?php if (isset($errores['community'])): ?>
        <div class="invalid-feedback"><?= $errores['community'] ?></div>
    <?php elseif ($datos['community']): ?>
        <div class="valid-feedback">Perfecto</div>
    <?php endif; ?>
    </div>

    <!-- Postal Code -->
    <div class="col-md-3">
    <label for="postal_code" class="form-label">Postal Code</label>
    <input type="text" class="form-control <?= isset($errores['postal_code']) ? 'is-invalid' : ($datos['postal_code'] ? 'is-valid' : '') ?>"
            id="postal_code" name="postal_code" value="<?= htmlspecialchars($datos['postal_code']) ?>" required pattern="\d{5}">
    <?php if (isset($errores['postal_code'])): ?>
        <div class="invalid-feedback"><?= $errores['postal_code'] ?></div>
    <?php elseif ($datos['postal_code']): ?>
        <div class="valid-feedback">Looks good!</div>
    <?php endif; ?>
    </div>

    <!-- Terms -->
    <div class="col-12">
    <div class="form-check">
        <input class="form-check-input <?= isset($errores['terms']) ? 'is-invalid' : ($datos['terms'] ? 'is-valid' : '') ?>"
            type="checkbox" id="terms" name="terms" value="1" <?= $datos['terms'] ? 'checked' : '' ?> required>
        <label class="form-check-label" for="terms">Agree to terms and conditions</label>
        <?php if (isset($errores['terms'])): ?>
        <div class="invalid-feedback"><?= $errores['terms'] ?></div>
        <?php elseif ($datos['terms']): ?>
        <div class="valid-feedback">Gracias por aceptar los términos</div>
        <?php endif; ?>
    </div>
    </div>

    <!-- Submit -->
    <div class="col-12">
    <button class="btn btn-primary" type="submit">Submit form</button>
    </div>
</form>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Tooltip de validación

Si el diseño de su formulario lo permite, puede cambiar las clases .{valid|invalid}-feedback por las clases .{valid|invalid}-tooltip para mostrar la retroalimentación de validación en una información sobre herramientas con estilo. Asegúrese de tener un elemento principal con position: relative para posicionar la información sobre herramientas. En el ejemplo siguiente, nuestras clases de columna ya tienen esto, pero su proyecto puede requerir una configuración alternativa.

Tooltip de validación

Validaciones con tooltip

<<form class="row g-3 needs-validation" novalidate>
<div class="col-md-4 position-relative">
    <label for="validationTooltip01" class="form-label">First name</label>
    <input type="text" class="form-control" id="validationTooltip01" value="Mark" required>
    <div class="valid-tooltip">
    Looks good!
    </div>
</div>
<div class="col-md-4 position-relative">
    <label for="validationTooltip02" class="form-label">Last name</label>
    <input type="text" class="form-control" id="validationTooltip02" value="Otto" required>
    <div class="valid-tooltip">
    Looks good!
    </div>
</div>
<div class="col-md-4 position-relative">
    <label for="validationTooltipUsername" class="form-label">Username</label>
    <div class="input-group has-validation">
    <span class="input-group-text" id="validationTooltipUsernamePrepend">@</span>
    <input type="text" class="form-control" id="validationTooltipUsername" aria-describedby="validationTooltipUsernamePrepend" required>
    <div class="invalid-tooltip">
        Please choose a unique and valid username.
    </div>
    </div>
</div>
<div class="col-md-6 position-relative">
    <label for="validationTooltip03" class="form-label">City</label>
    <input type="text" class="form-control" id="validationTooltip03" required>
    <div class="invalid-tooltip">
    Please provide a valid city.
    </div>
</div>
<div class="col-md-3 position-relative">
    <label for="validationTooltip04" class="form-label">State</label>
    <select class="form-select" id="validationTooltip04" required>
    <option selected disabled value="">Choose...</option>
    <option>...</option>
    </select>
    <div class="invalid-tooltip">
    Please select a valid state.
    </div>
</div>
<div class="col-md-3 position-relative">
    <label for="validationTooltip05" class="form-label">Zip</label>
    <input type="text" class="form-control" id="validationTooltip05" required>
    <div class="invalid-tooltip">
    Please provide a valid zip.
    </div>
</div>
<div class="col-12">
    <button class="btn btn-primary" type="submit">Submit form</button>
</div>
</form>
Validación con tooltip
<?php
$errores = [];
$datos = [
    'first_name'   => '',
    'last_name'    => '',
    'username'     => '',
    'city'         => '',
    'community'    => '',
    'postal_code'  => '',
    'terms'        => ''
];

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $datos['first_name'] = filter_input(INPUT_POST, 'first_name', FILTER_SANITIZE_SPECIAL_CHARS);
    $datos['last_name'] = filter_input(INPUT_POST, 'last_name', FILTER_SANITIZE_SPECIAL_CHARS);
    $datos['username'] = filter_input(INPUT_POST, 'username', FILTER_SANITIZE_EMAIL);
    $datos['city'] = filter_input(INPUT_POST, 'city', FILTER_SANITIZE_SPECIAL_CHARS);
    $datos['community'] = filter_input(INPUT_POST, 'community', FILTER_SANITIZE_SPECIAL_CHARS);
    $datos['postal_code'] = filter_input(INPUT_POST, 'postal_code', FILTER_SANITIZE_NUMBER_INT);
    $datos['terms'] = isset($_POST['terms']) ? '1' : '';

    if (strlen($datos['first_name']) < 2 || strlen($datos['first_name']) > 40) {
        $errores['first_name'] = "El nombre debe tener entre 2 y 40 caracteres.";
    }

    if (strlen($datos['last_name']) < 2 || strlen($datos['last_name']) > 40) {
        $errores['last_name'] = "El apellido debe tener entre 2 y 40 caracteres.";
    }

    if (!filter_var($datos['username'], FILTER_VALIDATE_EMAIL)) {
        $errores['username'] = "El email no es válido.";
    }

    if (strlen($datos['city']) < 2 || strlen($datos['city']) > 50) {
        $errores['city'] = "La ciudad debe tener entre 2 y 50 caracteres.";
    }

    if (!in_array($datos['community'], ['valencia', 'andalucia'])) {
        $errores['community'] = "Debes seleccionar una comunidad válida.";
    }

    if (!preg_match('/^\d{5}$/', $datos['postal_code'])) {
        $errores['postal_code'] = "El código postal debe tener exactamente 5 dígitos.";
    }

    if (empty($datos['terms'])) {
        $errores['terms'] = "Debes aceptar los términos y condiciones.";
    }

    $exito = empty($errores);
}
?>

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Formulario con Tooltips</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
    .form-group-tooltip {
    margin-bottom: 3.5rem;
    }
    .invalid-tooltip,
    .valid-tooltip {
    font-size: 0.75rem;
    padding: 0.25rem 0.5rem;
    }
</style>
</head>
<body>

<div class="container mt-5">
<h1>Formulario con Tooltips</h1>

<?php if (isset($exito) && $exito): ?>
    <div class="alert alert-success">Formulario enviado correctamente.</div>
<?php elseif (!empty($errores)): ?>
    <div class="alert alert-danger">Hay errores en el formulario.</div>
<?php endif; ?>

<form class="row g-3 needs-validation" method="POST" novalidate>
    <!-- First Name -->
    <div class="col-md-4 position-relative form-group-tooltip">
    <label for="first_name" class="form-label">First name</label>
    <input type="text" class="form-control <?= isset($errores['first_name']) ? 'is-invalid' : ($datos['first_name'] ? 'is-valid' : '') ?>"
            id="first_name" name="first_name" value="<?= htmlspecialchars($datos['first_name']) ?>" required>
    <?php if (isset($errores['first_name'])): ?>
        <div class="invalid-tooltip"><?= $errores['first_name'] ?></div>
    <?php elseif ($datos['first_name']): ?>
        <div class="valid-tooltip">Looks good!</div>
    <?php endif; ?>
    </div>

    <!-- Last Name -->
    <div class="col-md-4 position-relative form-group-tooltip">
    <label for="last_name" class="form-label">Last name</label>
    <input type="text" class="form-control <?= isset($errores['last_name']) ? 'is-invalid' : ($datos['last_name'] ? 'is-valid' : '') ?>"
            id="last_name" name="last_name" value="<?= htmlspecialchars($datos['last_name']) ?>" required>
    <?php if (isset($errores['last_name'])): ?>
        <div class="invalid-tooltip"><?= $errores['last_name'] ?></div>
    <?php elseif ($datos['last_name']): ?>
        <div class="valid-tooltip">Looks good!</div>
    <?php endif; ?>
    </div>

    <!-- Email -->
    <div class="col-md-4 position-relative form-group-tooltip">
    <label for="username" class="form-label">Email</label>
    <input type="email" class="form-control <?= isset($errores['username']) ? 'is-invalid' : ($datos['username'] ? 'is-valid' : '') ?>"
            id="username" name="username" value="<?= htmlspecialchars($datos['username']) ?>" required>
    <?php if (isset($errores['username'])): ?>
        <div class="invalid-tooltip"><?= $errores['username'] ?></div>
    <?php elseif ($datos['username']): ?>
        <div class="valid-tooltip">Email válido</div>
    <?php endif; ?>
    </div>

    <!-- City -->
    <div class="col-md-6 position-relative form-group-tooltip">
    <label for="city" class="form-label">City</label>
    <input type="text" class="form-control <?= isset($errores['city']) ? 'is-invalid' : ($datos['city'] ? 'is-valid' : '') ?>"
            id="city" name="city" value="<?= htmlspecialchars($datos['city']) ?>" required>
    <?php if (isset($errores['city'])): ?>
        <div class="invalid-tooltip"><?= $errores['city'] ?></div>
    <?php elseif ($datos['city']): ?>
        <div class="valid-tooltip">Looks good!</div>
    <?php endif; ?>
    </div>

    <!-- Community -->
    <div class="col-md-3 position-relative form-group-tooltip">
    <label for="community" class="form-label">Community</label>
    <select class="form-select <?= isset($errores['community']) ? 'is-invalid' : ($datos['community'] ? 'is-valid' : '') ?>"
            id="community" name="community" required>
        <option value="" disabled <?= $datos['community'] == '' ? 'selected' : '' ?>>Choose...</option>
        <option value="valencia" <?= $datos['community'] == 'valencia' ? 'selected' : '' ?>>Valencia</option>
        <option value="andalucia" <?= $datos['community'] == 'andalucia' ? 'selected' : '' ?>>Andalucía</option>
    </select>
    <?php if (isset($errores['community'])): ?>
        <div class="invalid-tooltip"><?= $errores['community'] ?></div>
    <?php elseif ($datos['community']): ?>
        <div class="valid-tooltip">Perfecto</div>
    <?php endif; ?>
    </div>

    <!-- Postal Code -->
    <div class="col-md-3 position-relative form-group-tooltip">
    <label for="postal_code" class="form-label">Postal Code</label>
    <input type="text" class="form-control <?= isset($errores['postal_code']) ? 'is-invalid' : ($datos['postal_code'] ? 'is-valid' : '') ?>"
            id="postal_code" name="postal_code" value="<?= htmlspecialchars($datos['postal_code']) ?>" required pattern="\d{5}">
    <?php if (isset($errores['postal_code'])): ?>
        <div class="invalid-tooltip"><?= $errores['postal_code'] ?></div>
    <?php elseif ($datos['postal_code']): ?>
        <div class="valid-tooltip">Looks good!</div>
    <?php endif; ?>
    </div>

    <!-- Terms -->
    <div class="col-12 position-relative form-group-tooltip">
    <div class="form-check">
        <input class="form-check-input <?= isset($errores['terms']) ? 'is-invalid' : ($datos['terms'] ? 'is-valid' : '') ?>"
            type="checkbox" id="terms" name="terms" value="1" <?= $datos['terms'] ? 'checked' : '' ?> required>
        <label class="form-check-label" for="terms">Agree to terms and conditions</label>
        <?php if (isset($errores['terms'])): ?>
        <div class="invalid-tooltip"><?= $errores['terms'] ?></div>
        <?php elseif ($datos['terms']): ?>
        <div class="valid-tooltip">Gracias por aceptar los términos</div>
        <?php endif; ?>
    </div>
    </div>

    <!-- Submit -->
    <div class="col-12">
    <button class="btn btn-primary" type="submit">Submit form</button>
    </div>
</form>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>