Bordes y sombras
Lección 05 de 37 · 4 min
Un radio bien elegido y una sombra suave hacen que una tarjeta parezca un objeto. Un contorno de foco claro hace que se pueda usar con el teclado.
Borde y radio
El borde (border) es un atajo de tres valores: ancho, estilo y color, como border: 1px solid oklch(0.9 0 0). El estilo es obligatorio: si falta, vale none y el borde no se dibuja.
El radio (border-radius) redondea las esquinas. Acepta de uno a cuatro valores, que arrancan arriba a la izquierda y siguen como un reloj. Con porcentajes, el radio horizontal se mide sobre el ancho y el vertical sobre el alto: 50% da un círculo en una caja cuadrada y una elipse en una rectangular.
.tarjeta { border-radius: 16px; }.pestana { border-radius: 16px 16px 0 0; } /* solo arriba */.avatar { border-radius: 50%; } /* círculo si es cuadrado */.pildora { border-radius: 9999px; } /* el navegador lo achica */
Cuando una caja redondeada tiene otra adentro, los radios tienen que ser concéntricos: el radio interior es el exterior menos la distancia entre los dos bordes (el relleno, más el ancho del borde si lo hay). Con 16px de radio y 8px de relleno, la caja de adentro lleva 8px. Si el relleno supera al radio, el interior queda en 0.
Mové los controles y desmarcá el radio concéntrico: con el mismo radio en el bloque de color, la franja entre el bloque y el borde de la tarjeta se ensancha en las esquinas. Los círculos naranjas trazan cada radio, y la cota de arriba mide el relleno.
Muestra de pintura con radio de 28px y relleno de 12px; el bloque de color de adentro tiene radio de 16px. Sombras en capas, sin anillo de foco.
- radio de la tarjeta
- 28px
- relleno
- 12px
- radio interior
- 28 − 12 = 16px
mismo centro, curvas paralelas
.tarjeta {border-radius: 28px;padding: 12px;box-shadow:0 1px 2px oklch(0 0 0 / 6%),0 4px 8px oklch(0 0 0 / 6%),0 12px 24px oklch(0 0 0 / 8%);}@media (prefers-color-scheme: dark) {.tarjeta {box-shadow:0 0 0 1px oklch(1 0 0 / 10%),0 1px 2px oklch(0 0 0 / 6%),0 4px 8px oklch(0 0 0 / 6%),0 12px 24px oklch(0 0 0 / 8%);}}.muestra {border-radius: 16px; /* 28 − 12 */}
<button class="rounded-[28px] p-3 shadow-[0_1px_2px_oklch(0_0_0/6%),0_4px_8px_oklch(0_0_0/6%),0_12px_24px_oklch(0_0_0/8%)] dark:ring-1 dark:ring-white/10"><div class="rounded-2xl"></div>…</button>
Sombras
La sombra (box-shadow) toma desplazamiento horizontal, desplazamiento vertical, desenfoque, extensión y color. El desenfoque y la extensión son opcionales; la extensión agranda la sombra antes de desenfocarla, o la achica si es negativa. Con inset se dibuja adentro de la caja. La sombra sigue el radio y no ocupa lugar: no mueve nada en el layout.
Separadas por comas entran varias sombras, y la primera queda arriba. Una luz real deja una sombra corta y marcada pegada al objeto y otra larga y difusa. Apilar dos o tres sombras chicas con poca opacidad imita eso mejor que una sola grande.
.tarjeta {box-shadow:0 1px 2px oklch(0 0 0 / 6%),0 4px 8px oklch(0 0 0 / 6%),0 12px 24px oklch(0 0 0 / 8%);}@media (prefers-color-scheme: dark) {.tarjeta {box-shadow:0 0 0 1px oklch(1 0 0 / 10%),0 1px 2px oklch(0 0 0 / 6%),0 4px 8px oklch(0 0 0 / 6%),0 12px 24px oklch(0 0 0 / 8%);}}
En modo oscuro una sombra negra casi no contrasta con el fondo. Por eso se suma 0 0 0 1px: sin desplazamiento ni desenfoque y con 1px de extensión, dibuja una línea clara de 1px que marca el contorno como un borde, pero sin ocupar lugar.
Contorno y foco
El contorno (outline) se dibuja por fuera del borde y, como la sombra, no ocupa lugar: aparecer o desaparecer no mueve nada. Desde 2023 todos los navegadores principales lo dibujan siguiendo el radio. outline-offset lo separa del borde; con un valor negativo lo mete adentro.
- El borde ocupa lugar en el box model; el contorno no.
- El borde se puede definir lado por lado; el contorno rodea toda la caja.
- En el modo de alto contraste de Windows (
forced-colors) las sombras desaparecen, pero el contorno se mantiene.
Por eso el foco se marca con el contorno. No uses outline: none sin un reemplazo visible: quien navega con el teclado pierde de vista dónde está. Con :focus-visible el contorno aparece al navegar con Tab, pero no al hacer clic en un botón.
:focus-visible {outline: 2px solid currentColor;outline-offset: 2px;}
En Tailwind
En Tailwind el radio usa una escala con nombre: rounded-lg son 8px y rounded-2xl son 16px. La sombra, el anillo y el contorno tienen sus propias clases. Los valores fuera de la escala van entre corchetes, con guiones bajos en lugar de espacios.
| CSS | Tailwind |
|---|---|
| border-radius: 16px | rounded-2xl |
| border-radius: 16px 16px 0 0 | rounded-t-2xl |
| border-radius: 9999px | rounded-full |
| border: 1px solid | border |
| box-shadow: 0 1px 2px … | shadow-[0_1px_2px_oklch(0_0_0/6%)] |
| box-shadow: 0 0 0 1px | ring · ring-white/10 |
| outline: 2px · outline-offset: 2px | outline-2 outline-offset-2 |
<!-- 16px de radio y 8px de relleno: adentro, 16 − 8 = 8px --><article class="rounded-2xl p-2 shadow-md dark:ring-1 dark:ring-white/10"><img class="rounded-lg" src="foto.jpg" alt="…"></article><!-- foco visible solo con teclado --><button class="rounded-lg px-4 py-2 focus-visible:outline-2focus-visible:outline-offset-2">Guardar</button>
Borders and shadows
Lesson 05 of 37 · 4 min
A well-chosen radius and a soft shadow make a card look like an object. A clear focus outline makes it usable with the keyboard.
Border and radius
border is a shorthand for three values: width, style and color, as in border: 1px solid oklch(0.9 0 0). The style is required: if it's missing, it's none and the border isn't drawn.
border-radius rounds the corners. It takes one to four values, starting at the top left and going around like a clock. With percentages, the horizontal radius is measured against the width and the vertical one against the height: 50% gives a circle on a square box and an ellipse on a rectangle.
.card { border-radius: 16px; }.tab { border-radius: 16px 16px 0 0; } /* top only */.avatar { border-radius: 50%; } /* a circle if it's square */.pill { border-radius: 9999px; } /* the browser shrinks it to fit */
When a rounded box holds another one, the radii have to be concentric: the inner radius is the outer one minus the distance between the two edges (the padding, plus the border width if there is one). With a 16px radius and 8px of padding, the inner box takes 8px. If the padding is larger than the radius, the inner one stays at 0.
Move the controls and uncheck the concentric radius: with the same radius on the color block, the band between the block and the card's edge gets wider at the corners. The orange circles trace each radius, and the mark at the top measures the padding.
Paint chip with a 28px radius and 12px padding; the color block inside has a 16px radius. Layered shadows, without a focus ring.
- card radius
- 28px
- padding
- 12px
- inner radius
- 28 − 12 = 16px
same center, parallel curves
.card {border-radius: 28px;padding: 12px;box-shadow:0 1px 2px oklch(0 0 0 / 6%),0 4px 8px oklch(0 0 0 / 6%),0 12px 24px oklch(0 0 0 / 8%);}@media (prefers-color-scheme: dark) {.card {box-shadow:0 0 0 1px oklch(1 0 0 / 10%),0 1px 2px oklch(0 0 0 / 6%),0 4px 8px oklch(0 0 0 / 6%),0 12px 24px oklch(0 0 0 / 8%);}}.swatch {border-radius: 16px; /* 28 − 12 */}
<button class="rounded-[28px] p-3 shadow-[0_1px_2px_oklch(0_0_0/6%),0_4px_8px_oklch(0_0_0/6%),0_12px_24px_oklch(0_0_0/8%)] dark:ring-1 dark:ring-white/10"><div class="rounded-2xl"></div>…</button>
Shadows
box-shadow takes a horizontal offset, a vertical offset, blur, spread and color. Blur and spread are optional; spread grows the shadow before blurring it, or shrinks it when negative. With inset it's drawn inside the box. The shadow follows the radius and takes no room: it doesn't move anything in the layout.
Several shadows fit, separated by commas, and the first one sits on top. Real light leaves a short, sharp shadow right under the object and a long, diffuse one. Stacking two or three small, low-opacity shadows imitates that better than a single large one.
.card {box-shadow:0 1px 2px oklch(0 0 0 / 6%),0 4px 8px oklch(0 0 0 / 6%),0 12px 24px oklch(0 0 0 / 8%);}@media (prefers-color-scheme: dark) {.card {box-shadow:0 0 0 1px oklch(1 0 0 / 10%),0 1px 2px oklch(0 0 0 / 6%),0 4px 8px oklch(0 0 0 / 6%),0 12px 24px oklch(0 0 0 / 8%);}}
In dark mode a black shadow barely contrasts with the background. That's why 0 0 0 1px gets added: with no offset, no blur and 1px of spread, it draws a light 1px line that marks the edge like a border, but takes no room.
Outline and focus
outline is drawn outside the border and, like the shadow, takes no room: showing or hiding it moves nothing. Since 2023 every major browser draws it following the radius. outline-offset moves it away from the border; a negative value pulls it inside.
- The border takes room in the box model; the outline doesn't.
- The border can be set side by side; the outline wraps the whole box.
- In Windows high contrast mode (
forced-colors) shadows disappear, but the outline stays.
That's why focus is marked with the outline. Don't use outline: none without a visible replacement: keyboard users lose track of where they are. With :focus-visible the outline shows when navigating with Tab, but not when clicking a button.
:focus-visible {outline: 2px solid currentColor;outline-offset: 2px;}
In Tailwind
In Tailwind the radius uses a named scale: rounded-lg is 8px and rounded-2xl is 16px. Shadow, ring and outline have their own classes. Values off the scale go in brackets, with underscores instead of spaces.
| CSS | Tailwind |
|---|---|
| border-radius: 16px | rounded-2xl |
| border-radius: 16px 16px 0 0 | rounded-t-2xl |
| border-radius: 9999px | rounded-full |
| border: 1px solid | border |
| box-shadow: 0 1px 2px … | shadow-[0_1px_2px_oklch(0_0_0/6%)] |
| box-shadow: 0 0 0 1px | ring · ring-white/10 |
| outline: 2px · outline-offset: 2px | outline-2 outline-offset-2 |
<!-- 16px radius and 8px padding: inside, 16 − 8 = 8px --><article class="rounded-2xl p-2 shadow-md dark:ring-1 dark:ring-white/10"><img class="rounded-lg" src="photo.jpg" alt="…"></article><!-- focus visible with the keyboard only --><button class="rounded-lg px-4 py-2 focus-visible:outline-2focus-visible:outline-offset-2">Save</button>
