Entity mark
canonThe mark of any record: tone says which world the type belongs to, glyph says the exact type, shape says person / company / thing. One vocabulary for over twenty types.
Replaces seven parallel vocabularies (chip tile, palette glyph, listing tone, nav icons, ad-hoc header glyph, and the two picker maps in MentionInput and EntityPicker, which contradicted the canon — deal as businessplan, product rose in one picker and grey in the next, a tone on people and companies at all). Seven tones cannot cover twenty types, so the tone groups and the glyph identifies. Glyph size keeps the parity of the box — an odd difference puts the drawing on a half pixel.
Used in 21 files · 17 areas · 31× — components/assistant, components/deal, components/dialog, components/entity-preview, components/projects, components/search …
Default
export const Default = () => (
<div style={{ display: 'flex', gap: 'var(--sp-4)', alignItems: 'center' }}>
<EntityMark kind="person" name="Josef Novák" />
<EntityMark kind="company" name="Brno Tech a.s." />
<EntityMark kind="deal" name="Fotovoltaika — Fialka Group" />
<EntityMark kind="meeting" name="Úvodní schůzka" />
<EntityMark kind="document" name="Smlouva o dílo" />
</div>
);Variants
export const Variants = () => (
<div style={{ display: 'grid', gap: 'var(--sp-5)' }}>
{(
[
'identity',
'money',
'work',
'time',
'content',
'catalogue',
'property',
'selection',
] as const
).map((g) => (
<Labeled
key={g}
text={
g === 'identity'
? `${GROUP_LABEL[g]} — no tone; the colour is the hue of the name, so two people are not one blob`
: `${GROUP_LABEL[g]} — --ic-${ENTITY_MARK[byGroup(g)[0]!].tone}`
}
>
{byGroup(g).map((k) => (
<Named key={k} kind={k} />
))}
</Labeled>
))}
</div>
);Sizes
export const Sizes = () => (
<div style={{ display: 'grid', gap: 'var(--sp-5)' }}>
<Labeled text="inline 16 · sm 24 (default) · lg 32 — the same ladder as the avatar, to the pixel">
{(['inline', 'sm', 'lg'] as const).map((s) => (
<EntityMark key={s} kind="deal" name="Fotovoltaika" size={s} />
))}
</Labeled>
<Labeled text="a mixed row never jumps: person, company and thing share the box at every rung">
{(['inline', 'sm', 'lg'] as const).map((s) => (
<span key={s} style={{ display: 'inline-flex', gap: 'var(--sp-2)' }}>
<EntityMark kind="person" name="Josef Novák" size={s} />
<EntityMark kind="company" name="Brno Tech a.s." size={s} />
<EntityMark kind="task" name="Zavolat Novákovi" size={s} />
</span>
))}
</Labeled>
<Labeled text="the glyph shrinks with the tile and its stroke thins with it — 12/16/20 px, stroke 1.9/1.7/1.5">
{(['inline', 'sm', 'lg'] as const).map((s) => (
<EntityMark key={s} kind="vehicle" name="5AB 1234" size={s} />
))}
</Labeled>
</div>
);States
export const States = () => (
<div style={{ display: 'grid', gap: 'var(--sp-5)' }}>
<Labeled text="an unknown type is a visible grey question mark, never a silent fallback to some other type">
<EntityMark kind="nonsense" name="Něco jiného" />
<EntityMark kind="deal" name="Obchod" />
</Labeled>
<Labeled text="historical type names of the same thing resolve to one mark: contact = person, organization = company, location = place">
<EntityMark kind="contact" name="Josef Novák" />
<EntityMark kind="organization" name="Brno Tech a.s." />
<EntityMark kind="location" name="Sklad Slatina" />
</Labeled>
<Labeled text="a person or a company with a picture keeps it; the type layer does not touch identity">
<EntityMark
kind="person"
name="Josef Novák"
photo="data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%230072f5'/%3E%3Ccircle cx='32' cy='24' r='12' fill='%23fff'/%3E%3Cpath d='M8 64c0-14 11-22 24-22s24 8 24 22z' fill='%23fff'/%3E%3C/svg%3E"
/>
<EntityMark kind="person" name="Josef Novák" />
</Labeled>
</div>
);Composition
export const Composition = () => (
<div style={{ display: 'grid', gap: 'var(--sp-5)' }}>
<Labeled text="a listing row: the mark leads, the name follows, the type stays on the right">
<div style={{ display: 'grid', gap: 'var(--sp-2)', minWidth: 320 }}>
{(['person', 'company', 'deal', 'task', 'document'] as const).map((k) => (
<span key={k} style={{ display: 'flex', gap: 'var(--sp-3)', alignItems: 'center' }}>
<EntityMark kind={k} name={ENTITY_MARK[k].label} />
<span style={{ font: 'var(--cell)' }}>{ENTITY_MARK[k].label}</span>
</span>
))}
</div>
</Labeled>
<Labeled text="inline in a sentence — the mark sits on the text baseline without moving the line">
<span style={{ font: 'var(--p)' }}>
Obchod <EntityMark kind="deal" name="Fotovoltaika" size="inline" /> Fotovoltaika stojí na
schůzce <EntityMark kind="meeting" name="Úvodní schůzka" size="inline" /> Úvodní schůzka.
</span>
</Labeled>
</div>
);