Record title
The record's name at the top of its page and its peek: the mark, the name you can rename in place, its status, and one line of facts.
Playground
Change the controls to try each prop. The code under the preview updates as you go and lists only the props you changed; copy it into your page.
halcyonbank.com · Financial services · London
import { RecordTitle } from '@/ui/record-title';<RecordTitle name="Halcyon Bank" label="Company name" />Company
A 32px mark, the name at 24/28/600, one status pill, then a 12px line of the facts that tell two records apart.
halcyonbank.com · Financial services · London
import { CompanyLogo } from '@/ui/company-logo';
import { Lozenge } from '@/ui/lozenge';
import { RecordTitle } from '@/ui/record-title';
export default function RecordTitleCompany() {
return (
<RecordTitle
name="Halcyon Bank"
label="Company name"
mark={<CompanyLogo name="Halcyon Bank" domain="halcyonbank.com" size="large" />}
pills={<Lozenge appearance="success">Client</Lozenge>}
meta="halcyonbank.com · Financial services · London"
onRename={async () => ({ ok: true })}
/>
);
}Person
A person's mark is their avatar.
Senior Data Engineer at Northwind Labs · London
import { Avatar } from '@/ui/avatar';
import { Lozenge } from '@/ui/lozenge';
import { RecordTitle } from '@/ui/record-title';
export default function RecordTitlePerson() {
return (
<RecordTitle
name="Maya Chen"
label="Candidate name"
mark={<Avatar name="Maya Chen" size="large" />}
pills={<Lozenge appearance="information">Open to work</Lozenge>}
meta="Senior Data Engineer at Northwind Labs · London"
onRename={async () => ({ ok: true })}
/>
);
}In a peek
size="peek" sets the name at 16/20/600 for the side peek; the full page keeps 24/28/600.
Senior Data Engineer at Northwind Labs
import { Avatar } from '@/ui/avatar';
import { RecordTitle } from '@/ui/record-title';
// In a side peek the name is 16/20/600 (Decided 5); the full page keeps 24/28/600.
export default function RecordTitlePeek() {
return (
<RecordTitle
size="peek"
name="Maya Chen"
label="Candidate name"
mark={<Avatar name="Maya Chen" size="large" />}
meta="Senior Data Engineer at Northwind Labs"
onRename={async () => ({ ok: true })}
/>
);
}Rename in place
Click the name or press F2. Enter saves, Escape keeps the old name, and leaving the field saves. The page shows the new name at once. An empty name asks for one.
import { useState } from 'react';
import { CompanyLogo } from '@/ui/company-logo';
import { RecordTitle } from '@/ui/record-title';
// Click the name or press F2. Enter saves, Escape keeps the old name, leaving the field saves. An empty name asks for one.
export default function RecordTitleRename() {
const [name, setName] = useState('Halcyon Bank');
return (
<RecordTitle
name={name}
label="Company name"
mark={<CompanyLogo name={name} domain="halcyonbank.com" size="large" />}
onRename={async (next) => {
await new Promise((resolve) => setTimeout(resolve, 400));
setName(next);
return { ok: true, canonical: { name: next } };
}}
/>
);
}With a note
While renaming, a note says when the rename reaches another record.
import { Avatar } from '@/ui/avatar';
import { RecordTitle } from '@/ui/record-title';
// When a rename reaches another record, a note says so while editing.
export default function RecordTitleNote() {
return (
<RecordTitle
name="Maya Chen"
label="Candidate name"
note="Also renames Maya Chen as a Contact."
mark={<Avatar name="Maya Chen" size="large" />}
onRename={async () => ({ ok: true })}
/>
);
}Read only
An archived record, or a member without edit rights, shows the name as plain text. Focus or hover it to read why.
Halcyon Bank
Archivedimport { CompanyLogo } from '@/ui/company-logo';
import { Lozenge } from '@/ui/lozenge';
import { RecordTitle } from '@/ui/record-title';
// An archived record, or a member without edit rights: the name is plain text with the reason in a tooltip.
export default function RecordTitleReadOnly() {
return (
<RecordTitle
name="Halcyon Bank"
label="Company name"
readOnly
readOnlyReason="Archived records can't be renamed. Restore it first."
mark={<CompanyLogo name="Halcyon Bank" domain="halcyonbank.com" size="large" />}
pills={<Lozenge appearance="neutral">Archived</Lozenge>}
onRename={async () => ({ ok: true })}
/>
);
}When the rename fails
The first title meets a newer name someone else saved; the second can't reach the server. Both bring back the stored name, keep what you typed for the next try, and say what happened in a toast that stays until you close it.
import { CompanyLogo } from '@/ui/company-logo';
import { RecordTitle } from '@/ui/record-title';
// Rename either one. The first meets someone else's newer name; the second can't reach the server. Both bring back the
// stored name, keep what you typed for the next try, and say what happened in a toast that stays until closed.
export default function RecordTitleFailures() {
return (
<div className="flex flex-col gap-300">
<RecordTitle
name="Orbital Freight"
label="Company name"
mark={<CompanyLogo name="Orbital Freight" size="large" />}
onRename={async () => ({
ok: false,
conflict: true,
error: 'James Reed renamed this company to Orbital Freight Ltd a moment ago. Your name is kept; rename again to use it.',
canonical: { name: 'Orbital Freight Ltd' },
})}
/>
<RecordTitle
name="Northwind Logistics"
label="Company name"
mark={<CompanyLogo name="Northwind Logistics" size="large" />}
onRename={() => Promise.reject(new Error('offline'))}
/>
</div>
);
}Long names
A long name wraps to two lines, then ends in an ellipsis; the full name shows on hover and is read in full. Names in any script fit, and a record with no facts yet has no meta line.
Ho Chi Minh City
import { Avatar } from '@/ui/avatar';
import { Lozenge } from '@/ui/lozenge';
import { RecordTitle } from '@/ui/record-title';
// A long name wraps to two lines, then ends in an ellipsis; the full name is in the tooltip and the accessible name.
// A record with no facts yet has no meta line, and a name in any script fits.
export default function RecordTitleLongName() {
return (
<div className="flex w-full max-w-120 flex-col gap-300">
<RecordTitle
name="Nguyễn Thị Minh Khai, Head of Data Platform Engineering and Analytics Governance, Halcyon Bank Asia Pacific"
label="Candidate name"
mark={<Avatar name="Nguyễn Thị Minh Khai" size="large" />}
pills={<Lozenge appearance="danger">Do not contact</Lozenge>}
meta="Ho Chi Minh City"
onRename={async () => ({ ok: true })}
/>
<RecordTitle name="王小明" label="Candidate name" mark={<Avatar name="王小明" size="large" />} onRename={async () => ({ ok: true })} />
</div>
);
}Record chip
Every reference to a record, in a cell, the details rail, a card or Flo's text. The mark says what the record is; the name says which one.
Tag
Descriptive metadata someone chose: Inbox tags, skills, sectors, guests on a meeting. An outlined chip with a colour dot. It labels a record; it never says where the record stands.