# Decorate nicknames and messages

> An example of changing nickname colors and images, message backgrounds, line spacing, and hiding nicknames in the default skin.

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](/manage).

## Code {#code}

This one block of code contains every decoration on this page. Replace the image URLs with your own images.

```html
<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 {#nickname}

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 |

- `nickcon` is an image shown instead of the nickname text (within 16px height). If the image fails to load, the nickname text is shown. `nickcon: null` also removes a nickcon received through member integration.
- `iconsBefore` and `iconsAfter` replace the whole array. To keep existing icons, append to `c.current.iconsBefore` as in the code above.
- Use image URLs that start with `https://`, `http://`, `//`, or `/`. URLs in other formats are ignored.

## Message background and line spacing {#message-style}

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 {#hide-nickname}

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 {#timestamps}

`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 {#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 {#notes}

- `resolve` and `match` must 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 `order` value (registration order when equal). Later rules overwrite the same keys.
- `add` returns a remove function. To turn a decoration off, call that function or call `remove(id)`. For the full list, see [Skin UI API](/en/doc/3.4).
- This API exists only in the default skin (basic-v2).
