LBweb Łukasz Bącik

#frontendowe spojrzenie na tworzenie stron www

facebook chat Komentarze: 0

Piszemy chat Facebooka w CSS

Każdy użytkownik posiadający konto w popularnym serwisie społecznościowym Facebook zna to okienko. Mowa tutaj oczywiście o oknie wiadomości z innym użytkownikiem. Dzisiaj postaram się pokazać, jak zbudować taki czat w czystym CSS, bez zbędnych kombinacji. Zapraszam do lektury.

Wstęp

Chat Facebooka w CSS chciałem napisać już od dłuższego czasu. Nie stanowi to większego problemu jeśli chodzi o sam kod CSS, jednak zastanawiałem się, w jakiej formie przedstawić samo budowanie. Wybór padł na rozwiązanie „krok po kroku”. Zatem zaczynajmy.

messages facebook
wiadomości na Facebooku w CSS

Schemat chatu w CSS

Jak wiecie, okno wiadomości na Facebooku jest przypięte do prawej dolnej części okna przeglądarki. Nie inaczej będzie w naszym przypadku. Kod, od którego zacząłem jest prostym podziałem okna chatu.

<div class="chat">
  <div class="chat__wrapper">
    <div class="chat__header"></div>
    <div class="chat__content"></div>
    <div class="chat__form"></div>
  </div>
</div>

Podzieliłem okno w flexbox na 3 osobne kontenery, w których będą odpowiednio informacja o rozmówcy w nagłówku, przewijana lista wiadomości oraz pole do wpisania wiadomości. Oczywiście należy napisać odpowiedni kod, aby chat Facebooka w CSS miał szansę wyglądać jak oficjalny.

:root {
  --chatWidthContainer: 330px;
  --chatHeightContainer: 450px;
  --motiveColor: #0084FF;
  --messageBackground: #F0F2F5;
  --messageBackgroundText: #E4E6EB;
}

.chat {
  position: fixed;
  bottom: 0;
  right: 80px;
  color: #050505;

  &__wrapper {
    width: var(--chatWidthContainer);
    height:  var(--chatHeightContainer);
    display: flex;
    flex-flow: column nowrap;
    border: 0;
    border-top-left-radius: 8px;
    border-top-right-radius: 8px;
    background-color: #fff;
    filter: drop-shadow(0 0 0.5rem rgba(0, 0, 0, 0.15));
  }

  &__header {
    width: 100%;
    display: flex;
    flex-flow: row nowrap;
    align-items: center;
    box-shadow: 0 0.2rem 0.1rem rgba(0, 0, 0, 0.08);
  }

  &__content {
    display: flex;
    align-items: flex-end;
    overflow: hidden;
    margin-top: auto;
  }

  &__form {
    display: flex;
  }
}

Na początek należy przykleić chat do prawej dolnej części ekranu, co zrobiłem w liniach od 7 do 9. Następnie opakowanie chatu w chat__wrapper, aby nadać mu oczekiwane wymiary. Wrapper ten ma jeszcze zadanie podzielić okno chatu na 3 części w kolumnie (linie 15 i 16). Dodatkowo przyjemny cień dookoła oraz lekko zaokrąglone rogi.

Nagłówek chatu to także kontener typu flex z elementami w poziomie. Również content jest kontenerem flex, jednak zawartość będzie wyrównana do dolnej krawędzi.

Dół okna to pole edycji wiadomości.

Nagłówek chatu

Górna część okna to informacje o naszym rozmówcy (avatar wraz z imieniem i nazwiskiem) oraz przycisk zamknięcia okna.

<div class="chat__header">
  <div class="chat__avatar">
    <img src="#" alt="avatar" />
  </div>
  <div class="chat__user">
    Boe Jiden
  </div>
  <div class="chat__close">×</div>
</div>

Kod HTML jest banalny i chyba nie trzeba go tłumaczyć, prawda? Natomiast style CSS są już warte omówienia.

.chat {
  &__header {
    width: 100%;
    display: flex;
    flex-flow: row nowrap;
    align-items: center;
    box-shadow: 0 0.2rem 0.1rem rgba(0, 0, 0, 0.08);
    * {
      box-sizing: content-box;
    }
  } 
  &__avatar {
    width: 100%;
    max-width: 32px;
    aspect-ratio: 1;
    padding: 8px;
    border-radius: 50%;
  }
  &__user {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 1rem;
    font-weight: 500;
    padding: 8px 8px 8px 2px;
  }
  &__close {
    display: flex;
    margin-left: auto;
    font-size: 1.4rem;
    font-weight: 500;
    line-height: 1;
    padding: 8px;
    cursor: pointer;
    color: var(--motiveColor);
  }
}

Header to nic innego jak kontener typu flex z elementami w poziomie (linie od 4 do 6). Dodatkiem jest tutaj box-sizing: content-box w celu dopasowania wysokości elementów, o czym pisałem nie tak dawno. Avatar użytkownika otrzymał właściwość aspect-ratio, by zawsze miał taką samą wysokość i szerokość. Nazwa użytkownika otrzymała właściwość text-overflow: ellipsis, aby dłuższy tekst został subtelnie przycięty, nie rozpychając przy tym kontenera. Znak zamknięcia okna został opatrzony stylem margin-left: auto, by odsunąć go do prawej strony.

Sekcja wiadomości

Tę część postaram się omówić w częściach.

<div class="chat__content">
  <div class="chat__scrollable">
    <div class="chat__row">
      
    </div>
  </div>
</div>

Już same nazwy klas wskazują co się będzie tu działo. Kontener chat__scrollable będzie elementem przewijanym zawierającym wiersze z wiadomościami. Style CSS tego układu to:

.chat {
  &__content {
    display: flex;
    align-items: flex-end;
    overflow: hidden;
    margin-top: auto;
  }
  &__scrollable {
    overflow-y: auto;
    width: 100%;
    height: 100%;
    display: flex;
    flex-flow: column-reverse;
  }
  
  &__row {
    width: 100%;
    padding: 10px 15px;
  }
}

Kontener content będzie wyrównany do dołu okna. W przypadku, gdy rozpoczniemy pustą konwersację, wiadomości będą pojawiać się na dole listy, tak jak ma to miejsce na Facebooku. Blok scrollable jest elementem przwijanej overflow-y: auto, odwróconej kolumny flex-flow: column-reverse, aby wiadomości najnowsze były na dole. Mając przedstawiony schemat działania i układu wiadomości, przejdę do kodu samej wiadomości. Będą dwa rodzaje: wiadomość nasza oraz wiadomość rozmówcy.

Kod naszej wiadomości:

<div class="chat__message">
  <div class="message message--my">
    <div class="message__items">
      <div class="message__wrapper">
        <div class="message__icons"></div>
        <div class="message__text"></div>
        <div class="message__status"></div>
      </div>
    </div>
  </div>
</div>

Kontener message oznaczony będzie dodatkowo modyfikatorem message--my. Wewnątrz będzie blok items, a w nim wrappery z wiadomościami.

Teraz kod CSS odpowiednio układający wiadomości, aby przypominały te z Facebooka:

.message {
  max-width: 80%;
  display: flex;
  flex-flow: row nowrap;
  align-items: center;
  
  &__items {
    display: flex;
    flex-flow: column wrap;
    align-items: center;
  }
  
  &__wrapper {
    position: relative;
    
    &:not(:last-child) {
      margin-bottom: 3px;
    }
  }
  
  &__author {
    width: 100%;
    max-width: 28px;
    aspect-ratio: 1;
    padding: 8px 8px 8px 0;
    border-radius: 50%;
    margin-top: auto;
  }
  
  &__text {
    border-radius: 20px;
    padding: 8px 12px;
  }
  
  &__icons {
    display: flex;
    flex-flow: row nowrap;
    align-items: center;
    opacity: 0;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
  }
  &__icon {
    width: 20px;
    height: 20px;
    cursor: pointer;
    background-repeat: no-repeat;
    background-size: 15px 15px;
    background-position: center;
    border-radius: 50%;
    padding: 3px;
    aspect-ratio: 1;
    &:hover {
      background-color: rgba(0, 0, 0, 0.1);
    }
  }
  
  &--my {
    margin-left: auto;
    
    .message {
      &__text {
        background-color: var(--motiveColor);
        color: #fff;
      }

      &__icons {
        left: -25%;
      }

      &__wrapper {
        padding-right: 15px;
      }

      &__status {
        position: absolute;
        bottom: 0;
        right: 0;
        
        .sent {
          width: 14px;
          height: 14px;
          background-color: #BCC0C4;
          border-radius: 50%;
          background-image: url('https://www.lukaszbacik.pl/images/icons/icon-sent-5evyh5ecgvr.svg');
          background-position: center;
          background-size: 10px 10px;
          display: block;
        }
        .viewed {
          width: 14px;
          height: 14px;
          background-size: 10px 10px;
          display: block;
        }
      }
    }
  }
  
  &--other {
    margin-right: auto;
    .message {
      &__text {
        background-color: var(--messageBackgroundText);
      }

      &__icons {
        left: 100%;
      }
    }
  }
}

Każda z wiadomości będzie miała szerokość 80% kontenera. Pozostałe 20% będzie przeznaczone na avatar użytkownika oraz ikonki do zarządzania daną wiadomością.

Kontener items będzie w układzie kolumnowym (linie 8-9), a w środku niego będzie wrapper zawierający owe 3 elementy.

W zależności od nadawcy wiadomości, prezentowana treść będzie wyrównana do prawej (nasza wiadomość, linia 60) lub do lewej strony (wiadomość rozmówcy, linia 102). Dodatkowo rozmówca będzie opatrzony jego avatarem. Nasze wiadomości będą zawierały z prawej strony status (wysłana/odczytana).

Kolory tła wiadomości także będą różne, wzorem chatu Facebooka. Ikony do zarządzania domyślnie są ukryte, jednak najechanie kursorem na wiadomość aktywuje je.

Pole wysyłania wiadomości

Ostatnim elementem okna chatu jest pole do edycji wiadomości. Kod HTML:

<div class="chat__form">
  <form action="" class="form">
    <div class="form__group form__group--small">
      <a href="#" class="form__icon">
        <img src="https://www.lukaszbacik.pl/images/icons/icon-plus-wrbsye5vre.svg" alt="icon plus" />
      </a>
    </div>
    <div class="form__group form__group--full">
      <div class="form__message" contenteditable="true"></div>
      <p class="form__placeholder">Aa</p>
    </div>
    <div class="form__group form__group--small">
      <a href="#" class="form__icon">
        <img src="https://www.lukaszbacik.pl/images/icons/icon-submit-wbtvawfcrvf33.svg" alt="icon submit" />
      </a>
    </div>
  </form>
</div>

Tutaj także wszystkie elementy są ułożone w poziomie: ikona dodawania mediów, pole wiadomości oraz przycisk wysyłki. Ciekawostką tutaj jest zabieg pokazywania/ukrywania tzw. placeholdera. W sytuacji, gdy pole message (koniecznie z atrybutem contenteditable) jest puste, następca jest wyświetlany. Jakakolwiek zawartość w polu wiadomości ukrywa go.

.form {
  display: flex;
  flex-flow: row nowrap;
  align-items: center;
  width: 100%;
  
  &__group {
    padding: 8px;
    display: flex;
    flex-basis: auto;
    position: relative;

    &--full {
      flex-grow: 1;
      padding: 8px 0;
    }
  }
  &__icon {
    border: 0;
    border-radius: 50%;
    padding: 4px;
    cursor: pointer;
    display: block;
    
    &:hover {
      background-color: rgba(0, 0, 0, 0.1);
    }
  }

  &__message {
    outline: 0;
    overflow-y: auto;
    width: 100%;
    padding: 8px 8px 8px 12px;
    background-color: var(--messageBackground);
    border: 0;
    border-radius: 20px;
    max-height: 80px;
    
    &:not(:empty) + .form__placeholder {
      display: none;
    }
  }
  &__placeholder {
    color: #65676B;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    left: 16px;
  }
}

Cały zabieg pokazywania i ukrywania placeholdera jest w linii 40. Zarządza nim selektor css „następcy”.

Układ szerokiego pola tekstowego i 2 wąskich pól po bokach to linie od 9 do 16.

Efekt końcowy chatu Facebooka w CSS

chat facebooka w css
chat facebooka w css

Oczywiście przygotowałem także działające demo. Polecam otworzyć je w nowej karcie.

dark

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.