Mention suggestions
Suggest people while someone types @ in a rich text editor.
Mention someone
Type @ and a few letters. Use the arrow keys and Enter or Tab, or click or tap a name. Escape closes the list and lets you keep typing. The editor keeps focus the whole time. Enter sends the message only when the list is closed.
Try @sam for two people with the same name, @ma for a long name, @zz for no results and @ada for one result. This page's script is a demonstration only. It simulates search and keeps everything on this page.
<link rel="stylesheet" href="/dist/combobox.css">
<!-- The editor keeps textbox semantics. Add these while suggestions are attached. -->
<div contenteditable="true" role="textbox" aria-multiline="true" aria-label="Message"
aria-autocomplete="list" aria-haspopup="listbox" aria-controls="mentions-list"
aria-activedescendant="mentions-list-ada">…</div>
<!-- Mount once, outside the editor. Keep it mounted while the editor exists. -->
<div class="rounded scheme-default" data-mention-suggestions data-state="ready"
style="--mention-suggestions-top: 212px; --mention-suggestions-left: 48px">
<ul role="listbox" id="mentions-list" aria-label="People">
<li class="rounded-sm scheme-subtle" role="option" id="mentions-list-ada" aria-selected="true" data-active>
<span class="avatar" aria-hidden="true"><span class="initials">AO</span></span>
<span>
<span data-mention-name>Ada Okafor</span>
<span data-mention-detail>Finance circle</span>
</span>
</li>
<li role="option" id="mentions-list-di" aria-selected="false">
<span><span data-mention-name>Di Ng</span></span>
</li>
</ul>
<p role="status" aria-atomic="true">2 people found</p>
</div>
Right to left
The list lines up with the start of the @ in the editor's direction. Pass the editor's dir to the root.
States
Set data-state on the root: closed, loading, ready, empty or error. The one status region shows loading, empty and error messages, and holds the result count out of sight when results are showing. While a new search loads, earlier rows can stay visible with aria-busy="true" on the listbox. They are dimmed and none is active. See the reference page for static markup of each state.
Keyboard, pointer and focus
| While open | Result |
|---|---|
| ArrowDown, ArrowUp | Move the active person. Wraps at the ends and skips unavailable people. With no people listed, the caret moves as usual. |
| Enter | Inserts the active person. With no active person, or with Shift, Ctrl, Cmd or Alt held, it closes the list. It never sends or submits. |
| Tab | Inserts the active person. With no active person it closes the list and moves on as usual. Shift+Tab closes the list and moves back. |
| Escape | Closes the list. Typing continues as plain text until a new @. |
| Any other key | Goes to the editor. |
| Click or tap | Inserts that person. Focus stays in the editor. Hovering only highlights. |
The editor handles every key. The list never takes focus. The active row uses rounded-sm scheme-subtle with no border or outline. Hover only shades the background.
Positioning
Your script places the list near the caret through custom properties on the root. Live Wires sets the width, the maximum height and the internal scrolling.
--mention-suggestions-topplaces the list below the caret.--mention-suggestions-bottomplaces it above the caret when there is more room there. Set one or the other.--mention-suggestions-leftis the physical left edge, kept inside the viewport and the editor's scroll container.--mention-suggestions-max-blockoptionally limits the height to the space available.
Mount the root outside the editor and outside any element with a transform, filter or layout containment. Inside a modal dialog, mount it in that dialog.
Content
- Show the person's name. Avatars are optional and decorative.
- Show a second line only when your application supplies a real identifier, such as a circle or role. Never make up a handle from a name.
- Long names wrap. To truncate instead, add
truncateto the name. The full name stays in the page and in the option's accessible name.