Avancerad positionering

Element som delar plats

Hittills har varje element väntat på sin tur och tagit sin egen yta. Dokumentflödet staplar dem uppifrån och ned, och med Flexbox lade du dem i sidled. Men titta på nästan vilken webbplats som helst så hittar du element som delar samma plats: en badge i hörnet av ett produktkort, text ovanpå en hero-bild, en notis-prick på en ikon. Det är ett medvetet designval, och tekniken bakom heter positionering. I den här delen lär du dig mekaniken, i nästa bygger du hela hero-sektionen på den.

position: relative

.card {
    position: relative;
}

Det här gör nästan ingenting visuellt. Kortet stannar kvar i flödet och tar upp sitt utrymme som vanligt.

Vad du faktiskt skapar är ett koordinatsystem, ett ankare som absolut-positionerade barnelement mäter sin position ifrån. Det är ofta det enda skälet till att sätta position: relative på ett element.

En sak till som är bra att veta: alla element har en position, och standardvärdet är static. Ett element med static är opositionerat, det bara följer flödet. Så fort du sätter något annat värde, som relative, räknas elementet som positionerat, och det är den egenskapen resten av delen bygger på.

position: absolute

.badge {
    position: absolute;
    top: 0.75rem;
    right: 0.75rem;
}

position: absolute gör två saker:

  1. Bryter elementet ur flödet. Det tar inget utrymme. Övriga element agerar som om det inte finns.
  2. Positionerar elementet relativt närmaste positionerade förälder.

Hur hittar webbläsaren ankaret? Den vandrar uppåt i HTML-trädet du lärde känna i HTML-trädet, förälder för förälder, tills den stöter på ett positionerat element. Där stannar den, och det elementets yta blir koordinatsystemet. Hittar den inget positionerat element på hela vägen upp blir sidan själv koordinatsystemet.

Sedan placerar top, right, bottom och left elementet i det systemet. top: 0.75rem; right: 0.75rem betyder: lägg elementets överkant 0.75rem från koordinatsystemets överkant, och högerkanten 0.75rem från dess högerkant.

z-index, att styra ordningen

När element överlappar behöver du ibland styra vem som syns överst. Det gör du med z-index.

.badge {
    position: absolute;
    z-index: 2;
}

z-index fungerar bara på positionerade element. Sätter du det på ett opositionerat element händer ingenting, för element i det normala flödet överlappar aldrig varandra och har därför ingen ordning att styra.

Utan explicit z-index vinner det element som kommer sist i HTML-koden.

Praktiska användningsfall

Badge på ett produktkort

En badge är en liten etikett fastnålad i kortets hörn. Badgen ligger som första barn i kortet:

<article class="card">
    <span class="card__badge">Nyhet</span>
    <img src="/img/produkt.webp" alt="Produktbeskrivning" />
    <h3>Produktnamn</h3>
</article>

Och hela positioneringen är paret du just lärt dig:

.card {
    position: relative;
}

.card__badge {
    position: absolute;
    top: 0.75rem;
    right: 0.75rem;
}

Kortet blir koordinatsystem, badgen nålas fast 0.75rem från dess övre högra hörn. Resten av badgens utseende, bakgrundsfärg, padding och rundade hörn, är vanlig CSS du redan kan. Det kompletta receptet finns som Badgen i Receptsamlingen.

Overlay: ett barn som täcker hela föräldern

En overlay är samma mönster som badgen, men i stället för att nåla fast barnet i ett hörn spänner du ut det över förälderns hela yta:

.image-wrapper {
    position: relative;
}

.image-overlay {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
}

inset är en genväg som sätter top, right, bottom och left i ett enda värde. inset: 0 fäster alla fyra kanter mot koordinatsystemets kanter, och barnet täcker då hela ytan. Bakgrunden är halvtransparent (rgba med alpha 0.5), så bilden under skymtar igenom.

Ska overlayen bara synas vid hover använder du tillstånden från Interaktion & feedback: dold som standard, synlig vid hover, med en mjuk övergång.

.image-overlay {
    opacity: 0;
    transition: opacity 0.2s ease;
}

.image-wrapper:hover .image-overlay {
    opacity: 1;
}

Selektorn på sista raden betyder: overlayen, när muspekaren är över wrappern.

Lägg mönstret på minnet. I nästa del är overlayen inte en hover-effekt utan bärande arkitektur: hela hero-sektionen bygger på barn som täcker sin förälder med position: absolute och inset: 0.

Pröva själv: lådan, med och utan ankare

Se mekaniken med egna ögon i din slask-fil innan du bygger något på riktigt.

  1. Skapa en yttre och en inre låda: <div class="ruta"><div class="prick"></div></div>. Ge .ruta en ram och lite höjd, och ge .prick en bakgrundsfärg och en fast storlek, säg 2rem × 2rem.
  2. Sätt position: absolute; top: 0; right: 0;.prick. Ladda om. Pricken struntar helt i sin förälder och nålas fast i sidans övre högra hörn.
  3. Sätt position: relative.ruta. Nu flyttar pricken in i rutans hörn. Den har fått ett koordinatsystem.
  4. Öppna DevTools, markera .ruta och växla bocken bredvid position: relative av och på i Styles. Se pricken hoppa mellan sidans hörn och rutans. Det är exakt det som händer när en badge “hamnar fel”.

Uppgift: Bygg ett kort med badge

Bygg ett produktkort med positionerad badge för ditt fiktiva företag.

  1. Om du inte redan har ett kort: skapa ett <article class="card"> med bild, rubrik och en kort beskrivning. Receptet Kortet i Receptsamlingen är en färdig grund.
  2. Lägg till ett <span> med en badge-klass inuti kortet. Välj text som passar ditt företag: “Populär”, “Ny”, “Kampanj” eller liknande.
  3. Sätt position: relative på kortets wrapper-element.
  4. Positionera badgen i ett hörn med position: absolute och top/right.
  5. Vad händer om… ta bort position: relative från föräldern och se vad som händer. Var hamnar badgen? Lägg tillbaka det och dokumentera vad du observerade.
  6. Bonus: lägg till ett hover-overlay på kortet med inset: 0 och en halvtransparent bakgrund.

Motivera & reflektera

Skriv en mening i din README om vad badgen kommunicerar till din persona. “Ny”, “Populär” och “Kampanj” säger olika saker, vilket valde du och varför passar det just den besökare du designar för?

Nästa nivå

Din hover-overlay har ett problem: den syns bara med mus. En tangentbordsanvändare kommer aldrig åt den, och på en touch-skärm finns ingen hover alls. Lös det. Byt ut :hover mot :focus-within (eller lägg till det) så att overlayen även visas när något inuti kortet får tangentbordsfokus, och se till att eventuellt innehåll i overlayen går att tabba till. Lägg dessutom en @media (prefers-reduced-motion: reduce) som stänger av transition för användare som blir illamående av rörelse. Det är den svåra delen: en effekt som funkar för alla, inte bara för den som råkar ha en mus.