Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel is required to process package imports. If you need a different preprocessor remove all packages first.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                .container
  .row
    .col.msg-window-container
      #msgWindow.card
        .card-header
          span.card-title Chat with Customer Support Agent
        .card-body#msgs
          .msg.to
            | Hello! How can I assist you today?
        .card-footer
          .input-group#msgForm(data-sender="me")
            input.form-control(type="text",placeholder="Type message and hit [Enter] to send.")
            .input-group-append
              button.btn.btn-outline-secondary(type="button") Send
        
              
            
!

CSS

              
                #msgWindow {
  margin-top: 20px;
}
#msgs {
  margin: 0px 25px;
  min-height: 200px;
  display: flex;
  flex-flow: column nowrap;
  justify-content: flex-end;
  align-items: flex-start;
}
$msg-border: 1px solid silver;
.msg {
  margin: 5px 0;
  border: $msg-border;
  padding: 3px 7px;
  display: inline-block;
  position: relative;
  border-radius: 10px;
  &::before, &::after {
    content: ''; display: inline-block; bottom: 0;
    position: absolute; border: $msg-border;
  }
  &::before {
    right: -20px;
    width: 15px; height: 15px;
    border-radius: 10px;
  }
  &::after {
    right: -35px;
    width: 10px; height: 10px;
    border-radius: 5px;
  }
  &.from {
    align-self: flex-end;
  }
  &.to {
    align-self: flex-start;
    &::before {
      right: inherit;
      left: -20px;
    }
    &::after {
      right: inherit;
      left: -35px;
    }
  }
  &.typing {
    color: silver;
  }
}
#msgForm {
  input,button {
    &:focus {
      box-shadow: none;
    }
  }
}
              
            
!

JS

              
                // Normal Brain: Designing a cool lil chat widget in CSS with some time between projects.
// Big Brain: Make a functional jQuery form submit and simulate a response.
// Galactic Brain: Do it in React.
// Multiversal Brain: Make a React Native chat app.

// Version: Big Brain, but with a headache.
// Headache: Needed to include babel polyfill to work with Promises.
$(function(){
  
  // Define some elements from the DOM and utility methods.
  let $form = $("#msgForm"),
      $newMsg = $form.find("input"),
      $sendBtn = $form.find("button"),
      $feed = $("#msgs"),
      _wait = ms => new Promise((r, j)=>setTimeout(r, ms)), // See [0]
      _secs = (a,b) => Math.floor(Math.random() * (b - a + 1)) + a;
  
  // Define our send method.
  var _send = data => {
    // Send data to a new .msg
    let $msg = $('<div class="msg"></div>'),
        {sender, typing} = data;
    if (sender !== "me") {
      $msg.addClass("to");
    } else {
      $msg.addClass("from");
    }
    $msg.text( data.msg );
    if (typing) $msg.addClass("typing");
    $msg.appendTo($feed);
    // If sending was successful, clear the text field.
    $newMsg.val("");
    // And simulate a reply from our agent.
    if (sender === "me") setTimeout(_agentReply,1000);
    if (typing) return $msg; // ref to new DOM .msg
  };

  
  
  var _agentReply = () => {
    // After a few seconds, the agent starts to type a message.
    let waitAfew = _wait( _secs(3000,5000) ),
        showAgentTyping = async () => {
          console.log("agent is typing...");
          // Let the user know the agent is typing
          let $agentMsg = _send({msg:"Agent is typing...",typing:true,sender:false});
          // and in a few seconds show the typed message.
          waitAfew.then(() => {
            // @TODO: Simulate actual typing by removing the typing message when the agent isn't typing, and before the agent sends the typed message. Also allow typing to continue a number of times with breaks in between.
            $agentMsg.text("Lorem ipsum dolor sit amet.");
            $agentMsg.removeClass("typing");
          });
          
        };
    waitAfew.then(showAgentTyping());
  };
  
  // Define event handlers: Hitting Enter or Send should send the form.
  $newMsg.on("keypress",function(e){
    // @TODO: Allow [mod] + [enter] to expand field & insert a <BR>
    if(e.which === 13) {
      // Stop the prop
      e.stopPropagation(); e.preventDefault();
      // Wrap the msg and send!
      let theEnvelope = {
        msg: $newMsg.val(),
        sender: "me"
      }
      return _send(theEnvelope);
    } else {
      // goggles
    }
  });
  $sendBtn.on("click", function(e){
    // Stop the prop
    e.stopPropagation(); e.preventDefault();
    // Wrap the msg and send!
    let theEnvelope = {
      msg: $newMsg.val(),
      sender: "me"
    }
    return _send(theEnvelope);
  });
});

/**
 * Roadmap / TODO / Bugs to be fixed
 *
 * 1) add max-height and overflow scrolling.
 * 2) add debounce/throttle to agent reply, so that when the user sends a new message before the agent has replied it wont create a new agent reply process. [big brain version only]
 *
 */

/**
 * Credits
 *
 * [0] wait() method from: https://hackernoon.com/lets-make-a-javascript-wait-function-fa3a2eb88f11
 *
 */
              
            
!
999px

Console