Il problema negli angoli
In una card, un menu o un campo di ricerca, lo spazio fra due bordi arrotondati deve restare regolare anche negli angoli. Quando si lavora in fretta capita di dare lo stesso border-radius al contenitore e all'elemento interno. Il componente funziona e a prima vista il difetto può sfuggire. Negli angoli, però, le due curve non hanno lo stesso centro1.
Sbagliato
Giusto
Nell'esempio con raggi uguali lo spazio fra le curve si allarga negli angoli. Paul Hebert, di Cloud Four2, se n'è accorto così: aveva usato lo stesso valore per entrambi gli elementi e il risultato sembrava comunque storto.
Calcolare il raggio interno
Per mantenere uniforme lo spazio, il raggio interno deve diminuire della distanza fra i due bordi. In Figma o in Paper puoi ottenere questa relazione con un offset; nel codice puoi calcolarla:
raggio interno = raggio esterno − spazio
La formula
Hebert propone di pensare a ogni angolo come a un piccolo cerchio: border-radius ne imposta il raggio. Ora immagina due quarti di cerchio con lo stesso centro, uno dentro l'altro. La differenza fra i raggi è la distanza fra le curve, ed è per questo che lo spazio resta uguale lungo tutto l'angolo.
r = max(R − g, 0)
Qui R è il raggio esterno, r quello interno e g la distanza fra i due bordi. Finché la sottrazione dà un valore maggiore o uguale a zero, puoi leggere la relazione anche come R = r + g. Se il contenitore non ha un bordo, g coincide con il padding; se ce l'ha, g comprende anche il suo spessore.
- R
- raggio del bordo esterno
- r
- raggio del bordo interno
- g
- distanza tra i due bordi
R32px
g12px
r20px
La distanza fra le due curve resta 12pxanche nell'angolo.
Il codice
Nel codice conta tutta la distanza fra le due curve. Il border-radius del contenitore definisce il raggio esterno4. Per trovare quello dell'elemento interno togli lo spessore del bordo, se c'è, e poi il padding. È lo stesso ordine della specifica W3C3: prima il bordo, poi il padding.
Qui il raggio esterno è 32px. Il bordo è spesso 4px, quindi sulla sua curva interna il raggio scende a 28px. Togli i 12px di padding e il figlio usa 16px.
Esempi pratici
Card, menu e barra di ricerca hanno tutti un raggio esterno di 24px e 8pxdi padding, quindi l'interno corretto è 16px5. Attiva Corretto per confrontarlo con lo stesso componente a 8px.
Quando il raggio arriva a zero
Il calcolo ha un limite: se la distanza fra i bordi raggiunge o supera il raggio esterno, non resta spazio per una curva interna concentrica. Il max(…, 0) nella formula tiene conto di questo caso. La specifica CSS3porta a zero i raggi interni che risulterebbero negativi, e l'angolo diventa vivo.
La concentricità, quindi, non richiede che ogni elemento abbia angoli visibilmente arrotondati. Il raggio del figlio dipende dallo spazio che lo separa dal contenitore: assegnargli comunque un valore positivo cambierebbe quella relazione.
Controllare il risultato
Per scegliere il raggio interno, parti da quello esterno e misura tutta la distanza fra le curve, compreso lo spessore del bordo. Sottrai quella distanza e fermati a zero se il risultato è negativo. Poi confronta gli angoli nel componente: negli esempi puoi cambiare il raggio interno e vedere come varia lo spazio rispetto ai lati dritti.
Per approfondire
- CSS-Tricks, Careful With Your Nested Border-Radii: le due curve devono avere lo stesso centro. Con un bordo di
5pxe un raggio esterno di25px, l'interno scende a20px. - Cloud Four, The Math Behind Nesting Rounded Corners: Paul Hebert ricava la formula pensando agli angoli come a cerchi e la traduce in
calc(). - W3C, CSS Backgrounds and Borders, Rounded Corners: il raggio interno del bordo è quello esterno meno lo spessore, quello del contenuto toglie anche il padding, e un risultato negativo diventa zero.
- MDN, border-radius: la sintassi, i valori per i singoli angoli e i raggi ellittici.
- 30 seconds of code, Perfect nested border radius in CSS: lo stesso caso degli esempi pratici,
24pxfuori e8pxdi padding, scritto con variabili CSS.