Crea effetti immagine reattivi con gradienti CSS e proporzioni

Pubblicato: 2022-03-10
Riepilogo rapido ↬ Un classico problema nei CSS è mantenere le proporzioni delle immagini tra i componenti correlati, come le schede. La proprietà aspect-ratio recentemente supportata in combinazione con object-fit fornisce un rimedio a questo mal di testa del passato! Impariamo a usare queste proprietà, oltre a creare un effetto di immagine gradiente reattivo per un tocco in più.

Per prepararci ai nostri futuri effetti immagine, imposteremo un componente della scheda che ha un'immagine grande in alto seguita da un titolo e una descrizione. Il problema comune con questa configurazione è che potremmo non avere sempre il controllo perfetto su cosa sia l'immagine e, cosa più importante per il nostro layout, quali siano le sue dimensioni . E mentre questo può essere risolto ritagliando in anticipo, possiamo comunque riscontrare problemi dovuti a contenitori di dimensioni reattive. Una conseguenza sono le posizioni irregolari del contenuto delle carte che risaltano davvero quando si presenta una fila di carte.

Un'altra soluzione precedente oltre al ritaglio potrebbe essere stata quella di passare da un img inline a un div vuoto che esisteva solo per presentare l'immagine tramite background-image . Ho implementato questa soluzione molte volte in passato. Un vantaggio è l'utilizzo di un vecchio trucco per le proporzioni che utilizza un elemento di altezza zero e imposta un valore padding-bottom . L'impostazione di un valore di riempimento come percentuale produce un valore finale calcolato relativo alla larghezza dell'elemento. Potresti aver utilizzato questa idea anche per mantenere un rapporto 16:9 per gli incorporamenti di video, nel qual caso il valore di riempimento si trova con la formula: 9/16 = 0.5625 * 100% = 56.26% . Ma esploreremo due moderne proprietà CSS che non implicano calcoli extra, ci danno maggiore flessibilità e consentono anche di mantenere la semantica fornita usando un img reale invece di un div vuoto.

Per prima cosa, definiamo la semantica HTML, incluso l'uso di un elenco non ordinato come contenitore delle carte:

 <ul class="card-wrapper"> <li class="card"> <img src="" alt=""> <h3>A Super Wonderful Headline</h3> <p>Lorem ipsum sit dolor amit</p> </li> <!-- additional cards --> </ul>

Successivamente, creeremo un insieme minimo di stili di base per il componente .card . Imposteremo alcuni stili visivi di base per la scheda stessa, un rapido aggiornamento al titolo h3 previsto, quindi gli stili essenziali per iniziare a modellare l'immagine della scheda.

 .card { background-color: #fff; border-radius: 0.5rem; box-shadow: 0.05rem 0.1rem 0.3rem -0.03rem rgba(0, 0, 0, 0.45); padding-bottom: 1rem; } .card > :last-child { margin-bottom: 0; } .card h3 { margin-top: 1rem; font-size: 1.25rem; } img { border-radius: 0.5rem 0.5rem 0 0; width: 100%; } img ~ * { margin-left: 1rem; margin-right: 1rem; }

L'ultima regola utilizza il combinatore di pari livello generale per aggiungere un margine orizzontale a qualsiasi elemento che segue l' img poiché vogliamo che l'immagine stessa sia a filo con i lati della carta.

E i nostri progressi finora ci portano al seguente aspetto della carta:

Una carta con gli stili di base precedentemente descritti applicati e inclusa l'immagine di Unsplash di un dessert su un piattino accanto a una bevanda calda in una tazza
Una carta con gli stili di base precedentemente descritti applicati e che include l'immagine di Unsplash di un dessert su un piattino accanto a una bevanda calda in una tazza. (Grande anteprima)

Infine, creeremo gli stili .card-wrapper per un layout rapido e reattivo utilizzando la griglia CSS. Questo rimuoverà anche gli stili di elenco predefiniti.

 .card-wrapper { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(30ch, 1fr)); grid-gap: 1.5rem; }

Nota : se questa tecnica della griglia non ti è familiare, rivedi la spiegazione nel mio tutorial sulle soluzioni moderne per la griglia a 12 colonne.

Con questo applicato e con tutte le carte contenenti un'immagine con un percorso sorgente valido, i nostri stili .card-wrapper ci danno il seguente layout:

Vengono visualizzate tre carte di seguito a causa degli stili di layout dell'involucro delle carte applicati. Ogni carta ha un'immagine unica che ha diverse proporzioni naturali, con l'ultima carta che ha un'immagine orientata verticalmente che è più del doppio dell'altezza delle altre immagini della carta
Vengono visualizzate tre carte di seguito a causa degli stili di layout dell'involucro delle carte applicati. Ogni scheda ha un'immagine unica che ha diverse proporzioni naturali, con l'ultima scheda che ha un'immagine orientata verticalmente che è più del doppio dell'altezza delle altre immagini della scheda. (Grande anteprima)

Come dimostrato nell'immagine di anteprima, questi stili di base non sono sufficienti per contenere correttamente le immagini date le loro dimensioni naturali variabili. Abbiamo bisogno di un metodo per vincolare queste immagini in modo uniforme e coerente.

Altro dopo il salto! Continua a leggere sotto ↓

Abilita Dimensioni immagine uniformi con object-fit

Come notato in precedenza, potresti aver precedentemente effettuato un aggiornamento in questo scenario per modificare le immagini da aggiungere tramite background-image invece e utilizzare background-size: cover per gestire correttamente il ridimensionamento dell'immagine. Oppure potresti aver provato a imporre il ritaglio in anticipo (ancora un obiettivo degno poiché qualsiasi riduzione delle dimensioni dell'immagine migliorerà le prestazioni!).

Ora abbiamo la proprietà object-fit disponibile che consente a un tag img di fungere da contenitore per l'immagine. Inoltre, viene fornito con un valore di cover che si traduce in un effetto simile alla soluzione dell'immagine di sfondo, ma con il vantaggio di mantenere la semantica di un'immagine inline. Applichiamolo e vediamo come funziona.

Abbiamo bisogno di accoppiarlo con una dimensione di height per una guida aggiuntiva su come vogliamo che si comporti il ​​contenitore di immagini (ricorda che avevamo già aggiunto width: 100% ). E useremo la funzione max() per selezionare 10rem o 30vh a seconda di quale sia più grande in un determinato contesto, il che impedisce all'altezza dell'immagine di ridursi troppo su finestre più piccole o quando l'utente ha impostato uno zoom grande.

 img { /* ...existing styles */ object-fit: cover; height: max(10rem, 30vh); }

Suggerimento per l'accessibilità bonus : dovresti sempre testare i tuoi layout con uno zoom del 200% e del 400% sul desktop. Sebbene al momento non sia presente una query multimediale di zoom , funzioni come max() possono aiutare a risolvere i problemi di layout. Un altro contesto utile per questa tecnica è la spaziatura tra gli elementi.

Con questo aggiornamento, abbiamo decisamente migliorato le cose e il risultato visivo è come se dovessimo utilizzare la vecchia tecnica dell'immagine di sfondo:

Le immagini a tre carte ora sembrano avere un'altezza uniforme e il contenuto dell'immagine è centrato all'interno dell'immagine come se fosse un contenitore
Le immagini a tre carte ora sembrano avere un'altezza uniforme e il contenuto dell'immagine è centrato all'interno dell'immagine come se fosse un contenitore. (Grande anteprima)

Ridimensionamento dell'immagine coerente in modo reattivo con aspect-ratio

Quando si utilizza object-fit da solo, uno svantaggio è che abbiamo ancora bisogno di impostare alcuni suggerimenti per le dimensioni.

Una prossima proprietà (attualmente disponibile nei browser Chromium) chiamata aspect-ratio migliorerà la nostra capacità di ridimensionare in modo coerente le immagini.

Usando questa proprietà, possiamo definire un rapporto per ridimensionare l'immagine invece di impostare dimensioni esplicite. Continueremo a usarlo in combinazione con object-fit per garantire che queste dimensioni influiscano solo sull'immagine come contenitore, altrimenti l'immagine potrebbe apparire distorta.

Ecco la nostra regola dell'immagine completamente aggiornata:

 img { border-radius: 0.5rem 0.5rem 0 0; width: 100%; object-fit: cover; aspect-ratio: 4/3; }

Inizieremo con un rapporto immagine di 43 per il contesto della nostra carta, ma puoi scegliere qualsiasi rapporto. Ad esempio, 11 per un quadrato o 169 per incorporamenti video standard.

Ecco le carte aggiornate, anche se probabilmente sarà difficile notare la differenza visiva in questo caso particolare poiché le proporzioni sembrano corrispondere molto all'aspetto che abbiamo ottenuto impostando l' height per object-fit solo.

Le immagini a tre carte hanno dimensioni identiche di larghezza e altezza, leggermente diverse dalla precedente soluzione di adattamento agli oggetti
Le immagini a tre carte hanno dimensioni identiche di larghezza e altezza, leggermente diverse dalla precedente soluzione di adattamento agli oggetti. (Grande anteprima)

L'impostazione di un "rapporto di aspetto" fa sì che il rapporto venga mantenuto man mano che gli elementi crescono o si restringono, mentre quando si impostano solo "adatta all'oggetto" e "altezza" il rapporto dell'immagine sarà costantemente in mutamento al variare delle dimensioni del contenitore.

Aggiunta di effetti reattivi con gradienti e funzioni CSS

OK, ora che sappiamo come impostare immagini di dimensioni costanti, divertiamoci un po' aggiungendo un effetto sfumato!

Il nostro obiettivo con questo effetto è far sembrare che l'immagine stia svanendo nel contenuto della scheda. Potresti essere tentato di avvolgere l'immagine nel suo contenitore per aggiungere il gradiente, ma grazie al lavoro che abbiamo già fatto sul dimensionamento dell'immagine, possiamo capire come farlo in sicurezza sul main .card .

Il primo passo è definire un gradiente . Utilizzeremo una proprietà personalizzata CSS per aggiungere i colori del gradiente per consentire di scambiare facilmente l'effetto del gradiente, a partire da un blu con un rosa. L'ultimo colore nel gradiente sarà sempre bianco per mantenere la transizione nello sfondo del contenuto della carta e creare il bordo "sfumato".

 .card { --card-gradient: #5E9AD9, #E271AD; background-image: linear-gradient( var(--card-gradient), white max(9.5rem, 27vh) ); /* ...existing styles */ }

Ma aspetta: è una funzione CSS max() ? In pendenza? Sì, è possibile, ed è la magia che rende questo gradiente efficace in modo reattivo!

Tuttavia, se dovessi aggiungere uno screenshot, non vedremmo ancora il gradiente che ha alcun effetto sull'immagine. Per questo, dobbiamo inserire la proprietà mix-blend-mode e in questo scenario utilizzeremo il valore di overlay :

 img { /* ...existing styles */ mix-blend-mode: overlay; }

La proprietà mix-blend-mode è simile all'applicazione degli stili di fusione dei livelli disponibili in software di manipolazione delle foto come Photoshop. E il valore di overlay avrà l'effetto di consentire ai toni medi dell'immagine di fondersi con il gradiente dietro di esso, portando al seguente risultato:

Ogni immagine della carta ha un effetto sfumato che inizia con un azzurro in alto, che si fonde con un rosa rossastro, e poi termina sfumando in un bianco prima del resto del contenuto del testo della carta
Ogni immagine della carta ha un effetto sfumato che inizia con un azzurro in alto, che si fonde con un rosa rossastro, e poi termina sfumando in un bianco prima del resto del contenuto del testo della carta. (Grande anteprima)

Ora, a questo punto, ci affidiamo solo al valore aspect-ratio per ridimensionare l'immagine. E se ridimensioniamo il contenitore e facciamo ridisporre il layout della scheda, la modifica dell'altezza dell'immagine provoca incoerenze in cui il gradiente diventa bianco.

Quindi aggiungeremo anche una proprietà max-height che utilizza anche la funzione max() e contiene valori leggermente maggiori di quelli nel gradiente. Il comportamento risultante è che il gradiente (quasi sempre) si allineerà correttamente con la parte inferiore dell'immagine.

 img { /* ...existing styles */ max-height: max(10rem, 30vh); }

È importante notare che l'aggiunta di una 'max-height' altera il comportamento di 'aspect-ratio'. Invece di usare sempre il rapporto esatto, sarà usato solo quando c'è abbastanza spazio allocato dato il nuovo vincolo extra di `max-height`.

Tuttavia, aspect-ratio continueranno comunque a garantire che le immagini vengano ridimensionate in modo coerente, come era il vantaggio rispetto al solo object-fit . Prova a commentare le aspect-ratio nella demo finale di CodePen per vedere la differenza che sta facendo tra le dimensioni dei contenitori.

Poiché il nostro obiettivo originale era quello di consentire dimensioni dell'immagine sempre reattive , abbiamo comunque colto nel segno. Per il tuo caso d'uso, potrebbe essere necessario giocherellare con i valori di rapporto e altezza per ottenere l'effetto desiderato.

Alternativa: mix-blend-mode e aggiunta di un filtro

L'uso della overlay come valore della mix-blend-mode era la scelta migliore per l'effetto dissolvenza in bianco che stavamo cercando, ma proviamo un'opzione alternativa per un effetto più drammatico.

Aggiorneremo la nostra soluzione per aggiungere una proprietà personalizzata CSS per il valore mix-blend-mode e anche per aggiornare i valori di colore per il gradiente:

 .card { --card-gradient: tomato, orange; --card-blend-mode: multiply; } img { /* ...existing styles */ mix-blend-mode: var(--card-blend-mode); }

Il valore di multiply ha un effetto di scurimento sui toni medi, ma mantiene il bianco e il nero così come sono, ottenendo il seguente aspetto:

Ogni immagine della carta ha una forte sfumatura arancione dal nuovo gradiente che parte da un rosso-arancione all'arancione puro. Le aree bianche sono ancora bianche e le aree nere sono ancora nere
Ogni immagine della carta ha una forte sfumatura arancione dal nuovo gradiente che parte da un rosso-arancione all'arancione puro. Le aree bianche sono ancora bianche e le aree nere sono ancora nere. (Grande anteprima)

Anche se abbiamo perso la dissolvenza e ora abbiamo un bordo duro nella parte inferiore dell'immagine, la parte bianca del nostro gradiente è ancora importante per garantire che il gradiente termini prima del contenuto della carta.

Un'ulteriore modifica che possiamo aggiungere è l'uso del filter e, in particolare, utilizzare la funzione grayscale() per rimuovere i colori dell'immagine e quindi fare in modo che il gradiente sia l'unica fonte di colorazione dell'immagine.

 img { /* ...existing styles */ filter: grayscale(100); }

L'utilizzo del valore di scala di grayscale(100) comporta la rimozione completa dei colori naturali dell'immagine e la sua trasformazione in bianco e nero. Ecco l'aggiornamento per il confronto con lo screenshot precedente del suo effetto quando si utilizza il nostro gradiente arancione con multiply :

Ora ogni immagine della carta ha ancora la sfumatura arancione ma tutti gli altri colori vengono rimossi e sostituiti da sfumature di grigio
Ora ogni immagine della carta ha ancora la sfumatura arancione ma tutti gli altri colori vengono rimossi e sostituiti da sfumature di grigio. (Grande anteprima)

Usa aspect-ratio come miglioramento progressivo

Come accennato in precedenza, attualmente aspect-ratio sono supportate solo nell'ultima versione dei browser Chromium (Chrome ed Edge). Tuttavia, tutti i browser supportano object-fit e questo, insieme ai nostri vincoli di height , si traduce in un risultato meno ideale ma comunque accettabile, visto qui per Safari:

L'altezza dell'immagine della carta è limitata, ma ogni carta ha un'altezza realizzata leggermente diversa
L'altezza dell'immagine della carta è limitata, ma ogni carta ha un'altezza realizzata leggermente diversa. (Grande anteprima)

Senza il funzionamento delle aspect-ratio , il risultato qui è che alla fine l'altezza dell'immagine è limitata, ma le dimensioni naturali di ciascuna immagine portano comunque a una certa variazione tra le altezze dell'immagine della scheda. Potresti invece voler cambiare per aggiungere max-height o utilizzare di nuovo la funzione max() per rendere max-height più reattiva su carte di dimensioni diverse.

Estendere gli effetti gradiente

Poiché abbiamo definito le interruzioni di colore del gradiente come proprietà personalizzata CSS, abbiamo accesso pronto per modificarle in contesti diversi. Ad esempio, potremmo modificare il gradiente per evidenziare più fortemente uno dei colori se la scheda è posizionata al passaggio del mouse o ha uno dei suoi figli a fuoco.

Innanzitutto, aggiorneremo ogni scheda h3 in modo che contenga un collegamento, ad esempio:

 <h3><a href="">A Super Wonderful Headline</a></h3>

Quindi, possiamo utilizzare uno dei nostri più recenti selettori disponibili — :focus-within — per modificare il gradiente della scheda quando il collegamento è a fuoco. Per una copertura aggiuntiva delle possibili interazioni, lo accoppieremo con :hover . E riutilizzeremo la nostra idea max() per assegnare un singolo colore che occupi la copertura della porzione dell'immagine della scheda. Lo svantaggio di questo particolare effetto è che le interruzioni del gradiente e i cambi di colore non sono animabili in modo affidabile, ma lo saranno presto grazie a CSS Houdini.

Per aggiornare il colore e aggiungere il nuovo stop colore, dobbiamo solo riassegnare il valore di --card-gradient all'interno di questa nuova regola:

 .card:focus-within, .card:hover { --card-gradient: #24a9d5 max(8.5rem, 20vh); }

I nostri valori max() sono inferiori all'originale in uso per white per mantenere il bordo sfumato. Se usassimo gli stessi valori, incontrerebbe il white e creerebbe una netta separazione del righello.

Durante la creazione di questa demo, inizialmente ho provato un effetto che utilizzava la transform con scale per un effetto di ingrandimento. Ma ho scoperto che a causa dell'applicazione della mix-blend-mode , il browser non ridipingeva costantemente l'immagine causando uno sgradevole sfarfallio. Ci saranno sempre dei compromessi nel richiedere al browser di eseguire effetti e animazioni solo CSS e, sebbene sia molto interessante quello che possiamo fare, è sempre meglio controllare l'impatto sulle prestazioni dei tuoi effetti.

Divertiti a sperimentare!

I moderni CSS ci hanno fornito alcuni fantastici strumenti per aggiornare i nostri toolkit di progettazione web, con aspect-ratio che è l'ultima aggiunta. Quindi vai avanti e sperimenta con object-fit , aspect-ratio e aggiungendo funzioni come max() nei tuoi gradienti per alcuni divertenti effetti reattivi! Assicurati solo di ricontrollare le cose su più browser (per ora!) e su diverse finestre e dimensioni dei contenitori.

Ecco la CodePen che include le caratteristiche e gli effetti che abbiamo recensito oggi:

Guarda la penna [Effetti immagine reattivi con gradienti CSS e proporzioni](https://codepen.io/smashingmag/pen/WNoERXo) di Stephanie Eckles.

Guarda gli effetti immagine reattivi della penna con gradienti CSS e proporzioni di Stephanie Eckles.

Cerchi di più? Assicurati di controllare la nostra Guida CSS qui su Smashing →