Decorate nicknames and messages
An example of changing nickname colors and images, message backgrounds, line spacing, and hiding nicknames in the default skin.
Last updated October 17, 2026
On this page
Instead of overriding CSS, the default skin (basic-v2) changes how nicknames and messages look through a defined mechanism (resolvers). It doesn't break when the skin changes, and scripts used together don't overwrite each other.
You can change the whole chat room's colors, font size, and look without code in the Skin menu of the admin page.
Code
This one block of code contains every decoration on this page. Replace the image URLs with your own images.
<script>U=window.U=window.U||{},U.events=U.events||[],U.chat=function(n){return{on:function(e,t){U.events.push([n,e,t])},off:function(e){for(var t=U.events.length;t>0;)U.events[--t][0]==n&&U.events[t][1]==e&&U.events.splice(t,1)}}};</script>
<script async src="https://client.uchat.io/uchat.js"></script>
<u-chat room="my-room" style="display:inline-block;width:100%;max-width:400px;height:500px"></u-chat>
<script>
U.chat('my-room').on('after.create#decorate', function (room) {
var ui = room.skin;
// 1. Moderator and admin nicknames: color and weight; admins get an image instead of the nickname
ui.nickname.resolver.add({
id: 'decorate.admin',
resolve: function (c) {
var auth = c.user ? Number(c.user.auth) : 0;
if (auth < 2) return null;
var patch = { style: { color: auth >= 3 ? '#e11d48' : '#2563eb', fontWeight: 'bold' } };
if (auth >= 3) patch.nickcon = { src: 'https://example.com/img/owner-name.png', alt: c.user.nick };
return patch;
},
});
// 2. Level 10 and up: add a badge icon before the nickname (keep existing icons)
ui.nickname.resolver.add({
id: 'decorate.level',
resolve: function (c) {
if (!c.user || Number(c.user.level) < 10) return null;
return {
iconsBefore: c.current.iconsBefore.concat([
{ src: 'https://example.com/img/badge.png', alt: 'Level 10' },
]),
};
},
});
// 3. Messages that contain my name: background color and wider line spacing
ui.message.resolver.add({
id: 'decorate.mention',
resolve: function (c) {
var me = room.my.nick;
if (!me || c.author.nick === me || c.message.content.indexOf(me) < 0) return null;
return { style: { backgroundColor: '#fff4cc' }, row: { spacing: 'relaxed' } };
},
});
// 4. Regular messages I sent: hide the nickname and change only the text color
ui.message.resolver.add({
id: 'decorate.mine',
resolve: function (c) {
if (!room.my.nick || c.author.nick !== room.my.nick) return null;
return { style: { color: '#1d4ed8' }, row: { hideNickname: true } };
},
});
// 5. Time sent (hh:mm) after the message
ui.message.extend({
id: 'decorate.time',
position: 'after',
mount: function (host, c) {
host.textContent = ' ' + String(c.message.created || '').slice(11, 16);
host.style.color = '#9ca3af';
host.style.fontSize = '11px';
},
});
});
// Rules that use my nickname (3, 4) are recalculated after joining or a nickname change
function refreshDecorate(room) {
room.skin.message.resolver.refresh();
}
U.chat('my-room').on('after.join#decorate', refreshDecorate);
U.chat('my-room').on('after.user.modi#decorate', refreshDecorate);
</script>
Nickname color and image
The resolve(c) of nickname.resolver.add is called every time a nickname is drawn. Return an object with only what you want to change, or null if there is nothing to change.
Value of c |
Description |
|---|---|
c.user |
The nickname's owner (nick, auth, level, icons, nickcon, and so on) |
c.location |
Where it is drawn. message for messages, user-list for the user list |
c.current |
The result with earlier resolvers applied (style, nickcon, iconsBefore, iconsAfter) |
c.base |
The default values before any resolver is applied |
nickconis an image shown instead of the nickname text (within 16px height). If the image fails to load, the nickname text is shown.nickcon: nullalso removes a nickcon received through member integration.iconsBeforeandiconsAfterreplace the whole array. To keep existing icons, append toc.current.iconsBeforeas in the code above.- Use image URLs that start with
https://,http://,//, or/. URLs in other formats are ignored.
Message background and line spacing
The resolve(c) of message.resolver.add is called when drawing regular messages and whispers. You can read c.message (content, created, kind, and so on) and the sender c.author.
Return row: { spacing: 'relaxed' } to widen that line's padding so it stands out. The default is normal.
Hide the nickname on my messages
Return row: { hideNickname: true } and the nickname is not drawn on that line. This applies only to regular messages; whispers always show the nickname, because you need to know who sent them.
room.my.nick is empty before joining, so rules that use your nickname are recalculated in after.join with message.resolver.refresh(). If a nickname rule needs the same, call nickname.resolver.refresh().
Time next to messages
message.extend attaches a small area before (before), inside (inline), or after (after) each message, and lets you draw it yourself with mount(host, c). c.message.created is in YYYY-MM-DD HH:MM:SS format. If you also pass match(c), it is attached only to messages for which it returns true.
Available values
| Target | Key | Value |
|---|---|---|
Nickname and message style |
color, backgroundColor |
#rgb, #rgba, #rrggbb, #rrggbbaa, rgb(), rgba(), hsl(), hsla() |
fontWeight |
normal, bold, 100–900 |
|
fontStyle |
normal, italic, oblique |
|
textDecoration |
none, underline, overline, line-through |
|
| Nickname | nickcon |
{ src, alt } or null |
iconsBefore, iconsAfter |
[{ src, alt }] |
|
| Message | row.spacing |
normal, relaxed |
row.hideNickname |
true, false |
Any other CSS properties or values, and HTML, are silently ignored. If you need a freer look, draw it yourself inside the area you get from message.extend or mount.*.
Things to note
resolveandmatchmust be synchronous. Returning a Promise is treated as an error and that rule is ignored.- With several resolvers, they are applied on top of each other in order of their
ordervalue (registration order when equal). Later rules overwrite the same keys. addreturns a remove function. To turn a decoration off, call that function or callremove(id). For the full list, see Skin UI API.- This API exists only in the default skin (basic-v2).
Still not sure how it works? Ask your question on the contact board.