css for chat room speech bubble position

Viewed 1163

Hello I am trying to design a chat room and am stuck on the css of speech bubbles.

What I want Blue speech bubbles on the right and grey ones on the left. enter image description here

At the moment I tried several answers I found on SO like position relative/absolute. But none of the answers worked for me. If someone can help me here itd be great. enter image description here

Html

<div class="card-body p-3 pt-0">
    <div class="my-1 rounded-lg ">
        <div class="position-relative w-100">

            @foreach ($chats as $chat)
            <p class="text-xs">
                {{$chat->created_at}} @ {{$chat->name}}
            </p>
            <div class="border-0 {{ $chat->user_identifier == 'SELF' ? 'chat-bubble-send' : 'chat-bubble-receive' }}">
                <p class="text-sm ">
                    {{ $chat->message }}
                </p> 
            </div>
            @endforeach

        </div>
    </div>
</div>

Css

.chat-bubble-receive p {
    background: #42a5f5;
    color: #fff !important;
    border-radius: 20px 20px 3px 20px;
    display: block;
    max-width: 75%;
    padding: 7px 13px 7px 13px;
}

.chat-bubble-send p {
    background: #f1f1f1;
    color: #000 !important;
    border-radius: 20px 20px 20px 3px;
    display: block;
    max-width: 75%;
    padding: 7px 13px 7px 13px;
    position: absolute;
    left: 0;
}

.card-body {
    flex: 1 1 auto;
    padding: 1rem 1rem;
}

When the message is short enter image description here enter image description here

2 Answers

Chat messages using CSS Flex

enter image description here

  • Inside a flex parent, to determine the left/right position of the message elements — use margin-(left/right) set to auto
  • When using position:absolute make sure to use position:relative; (or any other position other than static) on a parent element.
  • Use bottom: 100% to position the absolute time details on top of the first message.
  • "Hide" display:none; by default the "name+datetime" pseudo element, but show it display:block; only for the first message in group by using :first-child and the + next sibling combinator selector
  • You could use the data-* attribute to store the extra details (time etc) and use a pseudo element like ::before to than populate it with that value using content: attr(data-time)

/* QuickReset */ * {margin: 0; box-sizing: border-box;}

.chat {
  --rad: 20px;
  --rad-sm: 3px;
  font: 16px/1.5 sans-serif;
  display: flex;
  flex-direction: column;
  padding: 20px;
  max-width: 500px;
  margin: auto;
}

.msg {
  position: relative;
  max-width: 75%;
  padding: 7px 15px;
  margin-bottom: 2px;
}

.msg.sent {
  border-radius: var(--rad) var(--rad-sm) var(--rad-sm) var(--rad);
  background: #42a5f5;
  color: #fff;
  /* moves it to the right */
  margin-left: auto;
}

.msg.rcvd {
  border-radius: var(--rad-sm) var(--rad) var(--rad) var(--rad-sm);
  background: #f1f1f1;
  color: #555;
  /* moves it to the left */
  margin-right: auto;
}

/* Improve radius for messages group */

.msg.sent:first-child,
.msg.rcvd+.msg.sent {
  border-top-right-radius: var(--rad);
}

.msg.rcvd:first-child,
.msg.sent+.msg.rcvd {
  border-top-left-radius: var(--rad);
}


/* time */

.msg::before {
  content: attr(data-time);
  font-size: 0.8rem;
  position: absolute;
  bottom: 100%;
  color: #888;
  white-space: nowrap;
  /* Hidden by default */
  display: none;
}

.msg.sent::before {
  right: 15px;
}

.msg.rcvd::before {
  left: 15px;
}


/* Show time only for first message in group */

.msg:first-child::before,
.msg.sent+.msg.rcvd::before,
.msg.rcvd+.msg.sent::before {
  /* Show only for first message in group */
  display: block;
}
<div class="chat">
  <div data-time="16:35" class="msg sent">Hi!<br>What's up?</div>
  <div data-time="Anna 16:36" class="msg rcvd">Hi dear! <br>Doing some CSS research, you?</div>
  <div data-time="16:38" class="msg sent">Also learning some cool CSS stuff </div>
  <div data-time="16:38" class="msg sent">!!</div>
  <div data-time="16:38" class="msg sent">Up for a coffee today? ☕</div>
  <div data-time="Anna 16:40" class="msg rcvd">It would be a pleasure!</div>
  <div data-time="Anna 16:40" class="msg rcvd"></div>
</div>

I suggest using flex magic with a new row-div wrapping every bubble.

.row:nth-of-type(2n) {
  display: flex;
  justify-content: flex-end;
}

.bubble { 
  display: inline-block;
  padding: 5px;
}
<div class="page-wrapper">
  <div class="bubble-wrapper">
    <div class="row">
      <div class="bubble">
        <p>First bubble</p>
        <small>Details</small>
      </div>
    </div>
    <div class="row">
      <div class="bubble">
        <p>Second bubble</p>
        <small>Details</small>
      </div>
    </div>
    <div class="row">
      <div class="bubble">
        <p>Third bubble</p>
        <small>Details</small>
      </div>
    </div>
    <div class="row">
      <div class="bubble">
        <p>Fourth bubble</p>
        <small>Details</small>
      </div>
    </div>
  </div>

</div>

Related