Skip to content

Instantly share code, notes, and snippets.

@aldeka
Last active August 29, 2015 14:20
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aldeka/025de7c1ea76088de360 to your computer and use it in GitHub Desktop.
Save aldeka/025de7c1ea76088de360 to your computer and use it in GitHub Desktop.
Things I hate: UI patterns the web presentation layer can't pull off

It's a fairly common UI pattern in chat programs. You have a set of people talking in a chatroom, and one person sends multiple messages in a row. Instead of styling each of these messages like one-off messages, you style the additional messages so they appear as a unit with the first message by that author, until interrupted by someone else. You remove horizontal border lines, or you make the message author's name only appear once, or you do some nice thing with padding. Whatever.

Screenshot of Slack exhibiting this message-merging behavior

This is a pretty common pattern. It's also something that obviously ought to be handled by the presentation layer--your application code shouldn't care what order the chat messages happen to come in, and you shouldn't have to add wrapper divs and other jiggery-pokery when subsequent messages come in or get deleted and worry about determining whether this "chunk" of messages is interrupted by other chats or not.

Unless the number of participants in your chatroom is set in advance (aka, it's exclusively a one-on-one chat program), you cannot accomplish this with CSS.

:nth-of-type(1) or :first-of-type doesn't work on classes or other attributes. It only works on tag types. If you only need to work in modern browsers, however, you can define your own tag types.

  <author-blue class="message">What up</author-blue>
  <author-red class="message">How're you?</author-red>
  <author-blue class="message">Not bad</author-blue>
  <author-blue class="message">Almost done w. work</author-blue>
  <author-blue class="message">You?</author-blue>
  <author-red class="message">Chillin' at the train station</author-red>
  <author-red class="message">Train's late but the wifi's okay.</author-red>

The problem with this approach is it will only select the first time an author speaks in the conversation. It won't properly style the next time they speak.

Another approach is use the sibling selector, +. You have a default styling for all messages, then undo that styling for messages that are subsequent siblings. .message + .message will apply to all but the first message, regardless of author. What you want is to be able to detect siblings that have the same author.

CSS has some barebones regex-ish features that get painfully close to making this work. For example, .messages [class*="author-"] will select any message with a class that contains "author-", allowing you to generate class names or other attributes based on dynamic data and still style based on those attributes.

  <li class="message author-123">Hello world.</li>
  <li class="message author-4242">How many paths must a person walk?</li>
  <li class="message author-4242">Sorry for the inconvenience.</li>

But CSS doesn't have regex's capturing groups. [class^="author-"] + [class^="author-"] will select any siblings with "author-" starting the class--it will not check the rest of the class name to see if the classnames fully match.

So if you only have n chat participants, and will always have n chat participants, your job is easy. Just do something like:

  .message {
    border-top: 1px solid black;
  }
  .author-me {
    color: blue;
  }
  .author-you {
    color: red;
  }
  .author-me + .author-me, .author-you + .author-you {
    border-top: none;
  }

and

  <li class="message author-me">What up</li>
  <li class="message author-you">How're you?</li>
  <li class="message author-me">Not bad</li>
  <li class="message author-me">Almost done w. work</li>
  <li class="message author-me">You?</li>
  <li class="message author-you">Chillin' at the train station</li>
  <li class="message author-you">Train's late but the wifi's okay.</li>

But if the chat participant number is variable and generated, you cannot write a CSS rule that will present your message data the way you want. [class^="author-(\d+)"] + [class^="author-(\1)"], or something similar, is not possible in CSS.

Even more infuriatingly--at least, if you're using ReactJS to build your application--the obvious hack to work around this problem (generate a stylesheet per participant, as they enter the conversation, that gives their messages this CSS rule) is deliberately sabotaged by ReactJS.

I don't have a solution to this problem yet--not even a moderately hackish one. I'd rather have an uglier chat interface than manually check on every change to see if neighboring messages share an author; that just stinks of bugs that are hell to untangle.

Suggestions welcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment