Emoji picker
An emoji picker is a panel for finding and inserting an emoji. People search or browse by category, choose a skin tone, and pick; you insert the emoji.
<tk-emoji-picker></tk-emoji-picker>
Anatomy
| Part | What it is |
|---|---|
tk-emoji-picker |
the panel; raised, with rounded corners |
search part |
the search box, with the skin-tone button at its end |
nav part |
a row of category tabs |
grid part |
the scrolling emoji grid, or the results while searching |
preview part |
optional footer with the focused emoji and its name |
Recently used emoji come first, then the categories: Smileys & emotion, People & body, Animals & nature, Food & drink, Travel & places, Activities, Objects, Symbols, Flags. The grid fades at an edge while there is more to scroll.
Options
<tk-emoji-picker preview columns="6" label="Choose a reaction"></tk-emoji-picker>
preview: shows the focused or hovered emoji large, with its name.columns: the number of emoji per row, 8 by default.label: names the grid, "Choose an emoji" by default.flags: the Flags category, on by default. Flags don't show on Windows; to drop them, setpicker.flags = falsein script. The attribute can't turn it off.
Search and browse
- Typing filters to matching emoji. With no match, the picker says "No emoji found. Try
another word."
Escor an empty box returns to browsing. - A category tab scrolls to that category; the tabs follow as people scroll.
- The search box doesn't change on hover. On focus, its border takes the focus color and grows thicker inward.
Skin tone
The button at the end of the search box opens six tones, Default to Dark. The tone
applies to every emoji that has variants. It is the skin-tone attribute, from 0 to
5; a change by the user fires tk-skin-tone-change.
Inserting and recents
Listen for tk-emoji-select and insert detail.emoji, which already has the skin tone
applied. The picker doesn't insert anything itself.
<tk-emoji-picker id="picker"></tk-emoji-picker>
<script type="module">
const picker = document.getElementById('picker');
picker.addEventListener('tk-emoji-select', (event) => {
document.getElementById('message').value += event.detail.emoji;
});
</script>For the recently used row, either set persist-recents and the picker keeps the list
and the tone in local storage, or keep the list yourself and set recent:
<tk-emoji-picker id="reactions"></tk-emoji-picker>
<script type="module">
const picker = document.getElementById('reactions');
let recent = JSON.parse(localStorage.getItem('recent') ?? '[]');
picker.recent = recent;
picker.addEventListener('tk-emoji-select', (event) => {
recent = [event.detail.emoji, ...recent.filter((e) => e !== event.detail.emoji)].slice(0, 24);
localStorage.setItem('recent', JSON.stringify(recent));
picker.recent = recent;
});
</script>With neither, the row says "No recent emoji yet".
In a popover
To open the picker from a button, put it in a
<tk-popover label="Emoji" style="--tk-popover-padding: 0;"> <tk-icon-button slot="trigger" label="Add emoji"><span aria-hidden="true">😀</span></tk-icon-button> <tk-emoji-picker></tk-emoji-picker> </tk-popover>
tk-emoji-select reaches your listener outside the popover. Open it on click, never on
hover.
Native emoji
Emoji are drawn by the system's emoji font, so they look like the rest of the person's device and need no images.
- Flags show as two letters on Windows.
- Combined emoji, such as 👨👩👧, may show as separate emoji on older systems, and very new emoji as an empty box.
- No color token tints emoji.
- The emoji data loads the first time a picker appears, about 85 kB gzipped, and is shared by every picker on the page. The grid says "Loading emoji…" until it arrives.
When to use
- Use an emoji picker wherever people insert an emoji: a message composer, a reaction bar's "+", a page or status icon.
- Use a
Select orCombobox for a short list of options your app defines. - Use a
Menu for actions. - It isn't a form field, a GIF or sticker picker, or a reaction counter. Build the reaction chips under a message yourself.
Do / Don't
| ✅ Do | ❌ Don't |
|---|---|
Insert detail.emoji on tk-emoji-select |
Treat the picker as a form field |
| Open a floating picker on click, in a popover | Open it on hover |
Set persist-recents, or keep recent yourself |
Expect recents with neither |
| Hide flags for an audience mostly on Windows | Count on flags showing everywhere |
| Let emoji use the system font | Swap in image sprites |
Related
Popover : opens the picker from a button.Icon button : the button that opens it.Combobox : search with a filtered list.Menu : for actions instead of content.
API
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
skin-tone |
0–5 |
0 |
the applied tone, Default to Dark |
persist-recents |
boolean | false |
the picker keeps recents and the tone in local storage |
flags |
boolean | true |
shows the Flags category; set the property to false to hide it |
preview |
boolean | false |
shows the preview footer |
columns |
number | 8 |
emoji per row |
label |
string | Choose an emoji |
the grid's accessible name |
Properties
| Property | Type | What it does |
|---|---|---|
recent |
string[] |
your recently used emoji, shown first; ignored with persist-recents |
Events
| Event | Detail | When |
|---|---|---|
tk-emoji-select |
{ emoji, label, hexcode, group, skinTone } |
someone picks an emoji; insert emoji |
tk-skin-tone-change |
{ skinTone } |
someone changes the tone |
The picker has no value and fires no change.
CSS
::part(search),::part(nav),::part(grid),::part(preview): the parts inAnatomy .--tk-emoji-cell-size: the size of each emoji cell,2.25remby default.--tk-emoji-size: the emoji size,1.375remby default.--tk-emoji-picker-height: the height of the scrolling grid,18remby default.
Tokens
| Part | Tokens |
|---|---|
| panel | surface/raised + shadow-2, radius-16 |
| search box | surface/default + border/subtle, radius-8; focused border/accent/focus |
| search icon | icon/secondary |
| category tab | state/hover on hover; the open category border/selected underline |
| emoji cell | radius-4; highlighted state/hover, pressed state/press |
| focus ring | border/accent/focus |
| section header, empty and loading text | text/secondary |
| dividers | border/subtle |
In high contrast, the panel gets a 1px border/default instead of the shadow. Values
per theme:
Accessibility
Built in:
- Browsing is a grid: one Tab stop, arrows move between emoji, and each emoji is named, such as "grinning face".
- While searching, the results become a list that
UpandDownmove through, and the number of results is announced. - The category tabs, the skin tones, and the search box each have a name.
- Nothing takes focus when the picker appears.
- Nothing animates.
Your part:
- Open a floating picker on click, with the popover recipe above.
- If the picker filters a larger view, announce that view's results yourself.
| Key | In the grid | In the search box |
|---|---|---|
| arrows | move between emoji, across categories; Up from the top row goes to search |
Down goes to the grid; while searching, Down / Up move through results |
Home / End |
first / last emoji in the row | — |
Page Down / Page Up |
next / previous category | — |
Enter / Space |
picks the emoji | Enter picks the highlighted result |
Esc |
— | clears the search; in an empty box, closes a surrounding popover |
Tab |
leaves the grid | moves to the next control |