Megaskin by Jockles
← Previous Chapter · Chapter Index · Next Chapter →
Chapter 7: WhatsApp CSS
Thanks to orphan_account and worlds_end_valentine, whose WhatsApp group chat skin and Whatsapp Work Skin Template /Revamped this module is built on. Copied and adapted from them: the group header and member list, sender names, reactions, and per-chat backgrounds.
7.1 Chapter Contents
- 7.2 Getting the CSS
- 7.3 How the Module is Laid Out
- 7.4 The Window and Bubbles
- 7.5 Bubble Tails
- 7.6 Time, Ticks and Quotes
- 7.7 Date Pills and System Notices
- 7.8 The Group Header
- 7.9 Profile Pictures and the Ten Colours
- 7.10 Group Messages With Pictures
- 7.11 Reactions and Large Emoji
- 7.12 The Message Bar
- 7.13 Link Cards
- 7.14 Photo Galleries
- 7.15 Captions and Links
- 7.16 Dark and Auto Themes
- 7.17 Wallpapers
- 7.18 Taking Parts Out
This chapter goes through the CSS for the WhatsApp chats in the last chapter. WhatsApp is a much bigger module than iMessage - it has headers, galleries, link cards and wallpapers - but it's built the same way, so if you've read the iMessage CSS chapter, a lot of this will look familiar. Where something works exactly like it does in iMessage, I'll point you back there rather than explain it twice.
7.2 Getting the CSS
The WhatsApp CSS is here: jockles.com/fic/megaskin/css/whatsapp.css. It needs the core CSS too (jockles.com/fic/megaskin/css/core.css), so paste the core into your work skin first and this underneath it.
7.3 How the Module is Laid Out
It's in the same order as every module - the main rules, then Links, Dark theme and Auto theme - with one extra section on the end: Wallpapers. They come last on purpose. When two rules are equally specific, the one further down wins, so a wallpaper beats the background that the dark and auto themes set. That's how a chat can be theme-auto and still keep its blush pink wallpaper in dark mode.
7.4 The Window and Bubbles
#workskin .wa {
background: #efeae2;
color: #111b21;
border: 1px solid #d1d7db;
border-radius: 10px;
}
#workskin .wa .mob-head {
background: #008069;
color: #ffffff;
text-align: left;
padding: 0.7em 1em;
}
#workskin .wa .msg {
display: table;
max-width: 80%;
min-width: 90px;
margin: 0.2em 0;
padding: 0.35em 0.6em 0.3em 0.6em;
border-radius: 8px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.13);
}
#workskin .wa .out { margin-left: auto; background: #d9fdd3; }
#workskin .wa .in { margin-right: auto; background: #ffffff; }
The beige is WhatsApp's own chat background, and #008069 is its green. The bubbles work like iMessage's: display: table makes them fit their words, and auto margins push them to one side. The differences are:
min-width: 90px, so even a one-word bubble is wide enough for the time and ticks underneath.- Smaller rounded corners (
8px), because WhatsApp's bubbles are squarer. box-shadow, which is the faint shadow under each bubble:0across,1pxdown,1pxblurry, in black at 13% strength.
7.5 Bubble Tails
#workskin .wa .msg.tail { position: relative; }
#workskin .wa .in.tail { border-top-left-radius: 0; }
#workskin .wa .in.tail::before {
content: '';
position: absolute;
top: 0;
left: -8px;
width: 0;
height: 0;
border-style: solid;
border-width: 0 8px 10px 0;
border-color: transparent #ffffff transparent transparent;
}
The tail is a triangle, and CSS draws triangles in a strange way. A box with no width and no height, but with thick borders, is just its borders - and where two borders meet, they meet at a slant. Make three of the borders see-through, and the one that's left is a triangle. Here, only the right-hand border has a colour (the second of the four in border-color, which go top, right, bottom, left), so the triangle points left. It's pinned just outside the top corner, which has its rounding taken off so the two join up.
Sent bubbles have the same thing mirrored, in green. If you change a bubble colour, change its tail too, or you'll get a white point on a coloured bubble.
7.6 Time, Ticks and Quotes
#workskin .wa .wa-time {
display: block;
text-align: right;
font-size: 0.68em;
color: #667781;
margin-top: 0.1em;
}
#workskin .wa .wa-read { color: #53bdeb; font-weight: bold; }
#workskin .wa .wa-quote {
display: block;
background: rgba(0, 0, 0, 0.06);
border-left: 4px solid #06cf9c;
border-radius: 5px;
padding: 0.25em 0.5em;
margin-bottom: 0.3em;
font-size: 0.85em;
color: #54656f;
}
The time sits on its own line at the bottom right of the bubble, and wa-read turns the ticks blue. The quote box has a see-through grey background, rgba(0, 0, 0, 0.06) - black at 6% - so it shows up a shade darker on a white bubble and on a green one, without needing a colour for each.
7.7 Date Pills and System Notices
#workskin .wa .mob-sys {
display: table;
margin: 0.7em auto;
padding: 0.25em 0.7em;
background: #ffffff;
border-radius: 7px;
color: #54656f;
opacity: 1;
}
The core's mob-sys is a full-width centred line. WhatsApp shrinks it to fit its words with display: table, centres it with auto on both sides, and gives it a white background, which turns it into a pill.
7.8 The Group Header
The header with the back arrow, picture and icons is a table, because tables are one of the few layouts AO3 never rearranges. (It's the approach from the WhatsApp group chat skin in the credits.)
#workskin .wa table.wa-bar {
width: 100%;
table-layout: fixed;
border-collapse: collapse;
border-spacing: 0;
margin: 0;
background: #008069;
color: #ffffff;
text-align: left;
}
#workskin .wa table.wa-bar td {
margin: 0;
padding: 0;
border: 0;
background: transparent;
color: inherit;
font: inherit;
vertical-align: middle;
}
- AO3, and some site skins, give tables borders, stripes and padding. The second rule wipes all of that off every cell.
table-layout: fixedmakes the columns keep the widths they're given, instead of stretching to fit whatever's in them. Without it, a long group name would squash the icons.
#workskin .wa table.wa-bar .wa-title {
padding: 0.55em 0.4em 0 0.4em;
font-weight: bold;
line-height: 1.2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
These three lines are how a long name gets cut off with "..." like on a phone: nowrap keeps it on one line, overflow: hidden hides what doesn't fit, and text-overflow: ellipsis adds the dots. The member list (wa-sub) does the same. The back arrow, picture and icons cells have set widths in pixels, and the title takes whatever room is left.
7.9 Profile Pictures and the Ten Colours
#workskin .wa .wa-pfp {
display: block;
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
border-radius: 50%;
background-color: #8696a0;
background-size: cover;
background-position: center;
color: #ffffff;
font-size: 0.72em;
font-weight: bold;
overflow: hidden;
}
A circle with no picture shows initials, and line-height the same as the height is what centres them from top to bottom. A picture or a profile goes inside with a pfp span, which the core crops into the circle. The circle in the header is the same, just bigger (38px).
Add colour1 to colour10 to a circle for its colour, and the same number to a name (who) or a quoted reply (wa-quote) to colour the name and the line down the side. The ten colours are in the core, shared with every app - see The Ten Colours in the core CSS chapter for how to change them.
7.10 Group Messages With Pictures
#workskin .wa .wa-row { position: relative; padding-left: 38px; margin: 0.2em 0; }
#workskin .wa .wa-row .wa-pfp { position: absolute; left: 0; top: 0; }
#workskin .wa .wa-row .msg { margin: 0; }
#workskin .wa .wa-row.cont { padding-left: 38px; }
A wa-row leaves a 38px gap down its left side, and the picture is pinned into that gap at the top. cont keeps the gap but has no picture in it, so later messages from the same person still line up.
#workskin .wa .wa-num {
float: right;
margin-left: 1em;
font-weight: normal;
color: #667781;
}
float: right sends the phone number to the far right of the name line, and the name stays on the left.
7.11 Reactions and Large Emoji
#workskin .wa .wa-reacts {
display: table;
position: relative;
margin: -0.55em 0 0.3em 0.7em;
padding: 0.1em 0.5em;
border-radius: 1em;
background: #ffffff;
color: #111b21;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
font-size: 0.85em;
}
#workskin .wa .wa-reacts.out { margin-left: auto; margin-right: 0.7em; }
#workskin .wa .wa-reacts.pad { margin-left: 46px; }
#workskin .wa .wa-big { font-size: 2.2em; line-height: 1.2; }
The reactions are a separate paragraph after the bubble, but the negative top margin (-0.55em) pulls them up so they overlap its bottom edge. position: relative makes sure they sit on top of the bubble instead of behind it. out moves them to the right, and pad moves them past a profile picture.
7.12 The Message Bar
#workskin .wa .wa-input { position: relative; padding: 0.5em 3.4em 0.5em 0.6em; }
#workskin .wa .wa-input br { display: none; }
#workskin .wa .wa-field {
display: table;
width: 100%;
box-sizing: border-box;
padding: 0.45em 0.8em;
border-radius: 2em;
background: #ffffff;
color: #667781;
}
#workskin .wa .wa-field span {
display: table-cell;
vertical-align: middle;
white-space: nowrap;
width: 1%;
padding-left: 0.5em;
}
#workskin .wa .wa-field .wa-ph { width: auto; padding-left: 0.5em; }
#workskin .wa .wa-mic {
position: absolute;
right: 0.6em;
top: 50%;
margin-top: -1.2em;
width: 2.4em;
height: 2.4em;
line-height: 2.4em;
text-align: center;
border-radius: 50%;
background: #00a884;
color: #ffffff;
}
br { display: none; }is there because AO3 turns line breaks in your HTML into<br />tags, and inside the bar they'd split it onto two lines. This rule just ignores them.- The white box is another table, and each icon is a cell.
width: 1%on the icons is a trick: a table cell can't be narrower than what's inside it, so asking for 1% makes it exactly as wide as its icon. The placeholder (wa-ph) is set back toauto, so it takes all the room that's left. - The green microphone circle is pinned in the space on the right, which the bar's
3.4emof right padding keeps clear. It's centred top to bottom withtop: 50%and a negative margin of half its height.
For a half-typed message, typed undoes the nowrap so long messages wrap, and makes the words dark (light in dark mode):
#workskin .wa .wa-field .wa-ph.typed { white-space: normal; color: #111b21; }
7.13 Link Cards
#workskin .wa .msg.share,
#workskin .wa .msg.gallery {
width: 80%;
padding: 0.25em 0.25em 0.3em 0.25em;
}
#workskin .wa .wa-link {
display: block;
overflow: hidden;
border-radius: 6px;
background: rgba(0, 0, 0, 0.06);
}
#workskin .wa .wa-link-img {
display: block;
max-height: 190px;
overflow: hidden;
}
#workskin .wa .wa-link-url::before {
content: "\1F517";
margin-right: 0.4em;
}
- A bubble with a link card or photos in it is a fixed
80%wide, with very little padding, so the card nearly fills it. - The card's picture is cut off at
190pxtall, so a tall preview doesn't take over the chat. A video (video) is always190pxtall instead, and a description (text) gets padding to make it about that size. \1F517is the code for the 🔗 emoji, added before the address with::before, so you don't have to type it.
7.14 Photo Galleries
Galleries are the trickiest part of the module, because every photo has to be a perfect square, whatever shape the picture is.
#workskin .wa .wa-photo {
display: block;
position: relative;
float: left;
width: 49%;
height: 0;
padding: 49% 0 0 0;
margin: 0 1% 1% 0;
overflow: hidden;
background: rgba(0, 0, 0, 0.12);
}
- The square: CSS has no simple way to say "as tall as it is wide" that works everywhere, but padding given in
%is always a percentage of the width, even top padding. Soheight: 0withpadding-top: 49%makes a box exactly as tall as its49%width. A tall photo uses99%, which is two squares and the gap between them. - The grid:
float: leftlines the photos up two to a row. Two photos and the1%gap make99%, not 100%, so that rounding in the browser can never make a row a hair too wide and push a photo down to the next line. nth-of-typerules take the gap off the right of every second photo, and off the bottom of the last row. Thebrrule hides AO3's line breaks here too, because in a grid they'd push photos out of place.
#workskin .wa .wa-photo img {
position: absolute;
top: -9999px;
bottom: -9999px;
left: 0;
display: block;
width: 100%;
height: auto;
min-width: 0;
min-height: 0;
max-width: none;
max-height: none;
margin: auto 0;
}
#workskin .wa .wa-photo img.land,
#workskin .wa .wa-photos.two .wa-photo img,
#workskin .wa .wa-photos.three .wa-photo:first-of-type img {
top: 0;
bottom: auto;
right: -9999px;
left: -9999px;
width: auto;
height: 100%;
margin: 0 auto;
}
This is the same centring trick as round pictures in the core, but from top to bottom: the picture is made as wide as the square and given an enormous space above and below, and margin: auto puts it in the middle, so the extra top and bottom are cropped evenly. land turns it on its side. The tall boxes always use the land way, because almost every photo is wider than a box two squares tall.
Two, three and one photos
#workskin .wa .wa-photos.two .wa-photo {
padding-top: 99%;
margin-bottom: 0;
}
#workskin .wa .wa-photos.three .wa-photo:first-of-type {
padding-top: 99%;
margin-bottom: 0;
}
#workskin .wa .wa-photos.one .wa-photo {
float: none;
width: 100%;
height: auto;
max-height: 26em;
padding: 0;
margin: 0;
}
twomakes both photos tall, side by side.threemakes the first one tall, and the other two float into the space beside it, one on top of the other.oneundoes the square entirely - no float, no padding trick - so the picture keeps its own shape.max-height: 26emtrims a very tall one at the bottom.
7.15 Captions and Links
#workskin .wa .wa-caption {
display: block;
padding: 0.3em 0.35em 0 0.35em;
}
#workskin .wa .wa-ln {
color: #027eb5;
text-decoration: underline;
}
#workskin .wa .fake-link, #workskin .wa a:not(.tl) { color: #027eb5; }
A caption only needs a little padding, to line its text up with the edge of the photo above. Links in the text, wa-ln and fake-link are all WhatsApp's link blue.
7.16 Dark and Auto Themes
The dark theme swaps the beige for WhatsApp's near-black, the bubbles for dark grey and dark green, and every bit of grey text for a lighter grey. It also has the lighter set of name colours, and the tails again in the new bubble colours. As in iMessage, the auto theme is the same rules again inside @media (prefers-color-scheme: dark), so change a dark colour in both places.
#workskin .wa.theme-dark { background: #0b141a; color: #e9edef; border-color: #222d34; }
#workskin .wa.theme-dark .out { background: #005c4b; color: #e9edef; }
#workskin .wa.theme-dark .in { background: #202c33; color: #e9edef; }
#workskin .wa.theme-dark .in.tail::before { border-color: transparent #202c33 transparent transparent; }
#workskin .wa.theme-dark .out.tail::before { border-color: transparent transparent transparent #005c4b; }
7.17 Wallpapers
#workskin .wa.bg-sage { background: #d5e6d0; }
#workskin .wa.bg-sunset { background-color: #f6a56d; background-image: linear-gradient(#f6d365, #fda085); }
Each wallpaper is one line. A plain one is a colour. A gradient fades from the first colour at the top to the second at the bottom, and also has a plain background-color in between the two, which shows in the rare reader that can't do gradients. To make your own, copy a line, give it a new name after bg-, and change the colours.
Using your own picture takes one rule, which is already in the skin waiting for you:
#workskin .wa.bg-image1 {
background-color: #1c1c1c;
background-image: url(https://example.com/my-wallpaper.jpg);
background-size: cover;
background-position: center;
}
- Upload your picture somewhere that gives you a direct link to it - one that ends in
.jpgor.png. (See Pictures and Alt Text, in the Core, Themes, and Extras chapter, for where.) - Put your link in place of
https://example.com/my-wallpaper.jpg, keeping the brackets. - Change
#1c1c1cto a colour close to your picture. It shows while the picture loads, or if the link ever breaks. - Use it like any other wallpaper:
<div class="mob wa theme-auto bg-image1">
background-size: cover stretches the picture to fill the chat without squashing it, cropping whatever doesn't fit, and center keeps the middle of it in view. For a second picture, copy the whole rule and call it bg-image2.
Picking a theme to go with a wallpaper
A wallpaper changes the background, but not the bubbles. A dark wallpaper with light bubbles, or a pale one with dark bubbles, can look odd, so match them up: theme-light with the pale wallpapers, theme-dark with slate, navy, forest, plum, black, night and dusk. theme-auto works with anything in the middle.
7.18 Taking Parts Out
WhatsApp is a long module, so it's worth trimming if you only use a few parts. These come out cleanly - search for the class names and delete every rule with them in, including in the dark and auto blocks:
- Group header:
wa-bar,wa-back,wa-title,wa-sub,wa-actions. Keepwa-pfpif you use pictures beside messages. - Pictures beside messages:
wa-rowandpad, andwa-pfpwith its colours if you don't use the group header either. - Reactions:
wa-reactsandwa-count. - Message bar:
wa-input,wa-field,wa-ph,wa-mic. - Link cards:
share, and everything startingwa-link. - Photos:
gallery,wa-photos,wa-photo,wa-desc. - Wallpapers: any
bg-line you don't use.
Leave the window, bubbles, time and theme blocks, which everything else depends on.