Skip to content

Monogram tile

A neutral letter tile for companies and providers, never a brand logo.

Status
Beta
Level
Atom
Category
Identity
Adoption
Not used yet
import { MonogramTile } from "@oration/canon/components/monogram-tile";
packages/canon/src/components/monogram-tile.tsx
Suppliers with open invoices
SupplierOpen invoicesBalance
Northwind Freight14$184,220.00
Halcyon Logistics6$91,377.12
Orchard Street Produce9$12,904.50
Baker & Lowe Supply2$4,120.00
import { MonogramTile } from "@oration/canon/components/monogram-tile";export function Hero() {    const suppliers = [        {            name: "Northwind Freight",            color: "blue",            open: "$184,220.00",            invoices: 14,        },        {            name: "Halcyon Logistics",            color: "teal",            open: "$91,377.12",            invoices: 6,        },        {            name: "Orchard Street Produce",            color: "green",            open: "$12,904.50",            invoices: 9,        },        {            name: "Baker & Lowe Supply",            color: "amber",            open: "$4,120.00",            invoices: 2,        },    ] as const;    return (        <div className="w-full max-w-lg overflow-hidden rounded-xl bg-card shadow-border">            <table className="w-full text-13">                <caption className="sr-only">                    Suppliers with open invoices                </caption>                <thead>                    <tr className="h-8 border-b border-border text-left text-muted-foreground">                        <th scope="col" className="px-3 font-medium">                            Supplier                        </th>                        <th scope="col" className="px-3 text-right font-medium">                            Open invoices                        </th>                        <th scope="col" className="px-3 text-right font-medium">                            Balance                        </th>                    </tr>                </thead>                <tbody>                    {suppliers.map((supplier) => (                        <tr                            key={supplier.name}                            className="h-9 border-b border-border last:border-b-0"                        >                            <td className="px-3">                                <span className="flex items-center gap-2">                                    <MonogramTile                                        name={supplier.name}                                        color={supplier.color}                                        size="sm"                                    />                                    <span className="truncate font-medium text-foreground">                                        {supplier.name}                                    </span>                                </span>                            </td>                            <td className="px-3 text-right tabular-nums">                                {supplier.invoices}                            </td>                            <td className="px-3 text-right font-medium tabular-nums">                                {supplier.open}                            </td>                        </tr>                    ))}                </tbody>            </table>        </div>    );}

Usage#

Monogram tile is a company's initials in a rounded square on a pale tag tint: suppliers, customers' companies, integrations, SSO and AI providers, teams and workspaces. It is Oration's stand-in for a logo, and it stays neutral on purpose: provider names such as Deepgram or Twilio appear as letter tiles, never as their brand marks. The square shape is the point. Circles are people, tiles are organizations, so keep the two apart.

When to use

  • Beside a company or supplier name in rows, cells, cards and record headers: Northwind Freight, Halcyon Logistics.
  • For integrations and AI or telephony providers in settings and pickers, in gray so no provider looks endorsed.
  • For teams and a workspace without an uploaded logo, in the record's own tag color.
  • At xs inline in running text, such as an approval that names the supplier.

When not to use

  • For a person, even one who represents a company. Use Avatar
  • For an AI agent. Use Agent avatar
  • For records with no meaningful letters, such as cohorts or anonymous callers. Use Identicon
  • For a category or value of a record, such as a supplier's tier. Use Tag

The Option Hue Rule

Tag hues belong to select-option values and to identity tints on avatars and monogram tiles. The tile's color identifies the company; it never encodes status, risk or a category.

No brand logos

Providers and partners are shown as neutral letter tiles, never with their logos or brand colors. It keeps screens calm and avoids implying endorsements Oration hasn't made.

Anatomy#

  1. Tile. A square from 16 to 48px with corners that step with size: 4, 5, 8, 10 and 12px.
  2. Tint. The tag fill for color (--tag-{color}), with an inset 1px outline of black at 5% (white at 10% in dark) so pale tiles keep their edge.
  3. Initials. Semibold, tight tracking, in the tag's deep ink. Two letters from md up, one at xs and sm.

Examples#

Sizes

16 to 48px. xs sits inline in text, sm and md in rows and cells, lg in cards and xl in record and settings headers. The two smallest show one letter.

xs, 16px
sm, 20px
md, 24px
lg, 32px
xl, 48px
import { MonogramTile } from "@oration/canon/components/monogram-tile";export function Sizes() {    const sizes = [        { size: "xs", label: "xs, 16px" },        { size: "sm", label: "sm, 20px" },        { size: "md", label: "md, 24px" },        { size: "lg", label: "lg, 32px" },        { size: "xl", label: "xl, 48px" },    ] as const;    return (        <div className="flex flex-wrap items-end justify-center gap-8">            {sizes.map((item) => (                <div                    key={item.size}                    className="flex flex-col items-center gap-2"                >                    <MonogramTile                        name="Halcyon Logistics"                        color="teal"                        size={item.size}                    />                    <span className="text-xs text-muted-foreground">                        {item.label}                    </span>                </div>            ))}        </div>    );}

Colors

Any of the ten tag colors, taken from the record. The color identifies the company and carries no other meaning.

  • Twiliogray
  • Northwind Freightblue
  • Cedarlineindigo
  • Vantage Paperviolet
  • Peony Floralspink
  • Redwood Metalsred
  • Orchard Streetorange
  • Baker & Lowe Supplyamber
  • Greenline Packaginggreen
  • Halcyon Logisticsteal
import { MonogramTile } from "@oration/canon/components/monogram-tile";import { TAG_COLORS } from "@oration/canon/components/tag";export function Colors() {    const companies: Record<(typeof TAG_COLORS)[number], string> = {        gray: "Twilio",        blue: "Northwind Freight",        indigo: "Cedarline",        violet: "Vantage Paper",        pink: "Peony Florals",        red: "Redwood Metals",        orange: "Orchard Street",        amber: "Baker & Lowe Supply",        green: "Greenline Packaging",        teal: "Halcyon Logistics",    };    return (        <ul className="grid w-full max-w-xl grid-cols-2 gap-x-6 gap-y-3 sm:grid-cols-3">            {TAG_COLORS.map((color) => (                <li key={color} className="flex min-w-0 items-center gap-2.5">                    <MonogramTile                        name={companies[color]}                        color={color}                        size="lg"                    />                    <span className="flex min-w-0 flex-col">                        <span className="truncate text-13 text-foreground">                            {companies[color]}                        </span>                        <code className="font-mono text-xs text-muted-foreground">                            {color}                        </code>                    </span>                </li>            ))}        </ul>    );}

How initials are chosen

The first letters of the first two words, skipping a standalone & and anything that isn't a letter.

Namemdsm
Northwind Freight
Halcyon
Baker & Lowe Supply
3PL Direct
import { MonogramTile } from "@oration/canon/components/monogram-tile";export function Initials() {    const names = [        "Northwind Freight",        "Halcyon",        "Baker & Lowe Supply",        "3PL Direct",    ];    return (        <div className="grid w-full max-w-md grid-cols-[1fr_auto_auto] items-center gap-x-6 gap-y-3 text-13">            <span className="text-xs text-muted-foreground">Name</span>            <span className="text-xs text-muted-foreground">md</span>            <span className="text-xs text-muted-foreground">sm</span>            {names.map((name) => (                <div key={name} className="contents">                    <span className="text-foreground">{name}</span>                    <MonogramTile name={name} color="gray" size="md" />                    <MonogramTile name={name} color="gray" size="sm" />                </div>            ))}        </div>    );}

Providers

AI, speech and telephony providers are gray tiles with the name beside them, so none reads as featured.

  • Deepgram

    Speech to text with interim results.

  • ElevenLabs

    Voices for outbound W-9 and payment calls.

  • Twilio

    Phone numbers and SIP trunking.

import { Button } from "@oration/canon/components/button";import { MonogramTile } from "@oration/canon/components/monogram-tile";import { toast } from "@oration/canon/components/toast";export function Providers() {    const providers = [        {            name: "Deepgram",            description: "Speech to text with interim results.",            connected: true,        },        {            name: "ElevenLabs",            description: "Voices for outbound W-9 and payment calls.",            connected: true,        },        {            name: "Twilio",            description: "Phone numbers and SIP trunking.",            connected: false,        },    ];    return (        <ul className="grid w-full max-w-2xl gap-3 sm:grid-cols-3">            {providers.map((provider) => (                <li                    key={provider.name}                    className="flex flex-col gap-3 rounded-xl bg-card p-4 shadow-border"                >                    <div className="flex items-start gap-3">                        <MonogramTile name={provider.name} color="gray" />                        <div className="flex min-w-0 flex-col">                            <p className="text-sm font-medium text-foreground">                                {provider.name}                            </p>                            <p className="text-13 text-muted-foreground">                                {provider.description}                            </p>                        </div>                    </div>                    <Button                        type="button"                        variant="outline"                        size="sm"                        className="self-start"                        onClick={() =>                            toast.add({                                title: provider.connected                                    ? `${provider.name} settings opened`                                    : `Connect ${provider.name}`,                                description: provider.connected                                    ? undefined                                    : "Add an API key to start using it.",                            })                        }                    >                        {provider.connected ? "Manage" : "Connect"}                    </Button>                </li>            ))}        </ul>    );}

Inline in text

At xs, the tile sits on the text baseline beside the supplier's name inside a sentence, as approvals on Home do.

Approve early payment

Pay Halcyon Logistics for INV-20931 today to take the 2% discount, saving $1,827.54.

import { Button } from "@oration/canon/components/button";import { MonogramTile } from "@oration/canon/components/monogram-tile";import { toast } from "@oration/canon/components/toast";export function InlineMention() {    return (        <div className="flex w-full max-w-md flex-col gap-2 rounded-xl bg-card p-4 shadow-border">            <p className="text-sm font-semibold text-foreground">                Approve early payment            </p>            <p className="text-13 leading-5 text-pretty text-foreground/90">                Pay{" "}                <span className="inline-flex items-center gap-1 align-[-3px] font-medium text-foreground">                    <MonogramTile                        name="Halcyon Logistics"                        color="teal"                        size="xs"                    />                    Halcyon Logistics                </span>{" "}                for INV-20931 today to take the 2% discount, saving $1,827.54.            </p>            <div className="flex justify-end gap-2 pt-1">                <Button                    type="button"                    variant="ghost"                    size="sm"                    onClick={() =>                        toast.add({                            title: "Skipped",                            description: "INV-20931 stays on the Friday run.",                        })                    }                >                    Skip                </Button>                <Button                    type="button"                    size="sm"                    onClick={() =>                        toast.add({                            type: "success",                            title: "Early payment approved",                            description:                                "Halcyon Logistics will be paid today.",                        })                    }                >                    Approve                </Button>            </div>        </div>    );}

States#

States
StateTreatment
StaticA monogram has no interactive states. Hover and focus belong to the row or card that holds it.
One letterAt xs and sm, or when the name is one word: Halcyon is H.
Neutralcolor="gray" for providers and integrations, so no brand stands out.

Behavior#

  • Initials come from the first letter of the first two words. A standalone & is skipped, so Baker & Lowe Supply is BL.
  • Characters other than A to Z, spaces and & are stripped before the initials are taken, so digits and punctuation never show.
  • The tile is always aria-hidden; the name beside it is what people and screen readers read.
  • It renders a <span>, so it sits inline with text at xs and aligns with items-center in rows.

Do and don't#

OpenAI
Do. Show providers as gray letter tiles with the provider's name beside them.
OpenAI
Don't. Recreate a provider's logo or brand color. It looks like an endorsement and breaks the calm of the page.
Northwind Freight
Do. Use a square tile for a company and a circle for a person.
Northwind Freight
Don't. Put a company's initials in a circle. It reads as a teammate named NF.

Content#

  • Pass the name people know, not the legal entity: Northwind Freight, not Northwind Freight Holdings, LLC.
  • When a name starts with a digit or symbol, the tile skips it: 3PL Direct draws PD. Check that the letters still make sense.
  • Keep the name visible beside the tile. The letters alone don't identify a company.

Accessibility#

  • Always aria-hidden. There is no title prop, so never use a tile without the company's name next to it.
  • The tag fill and deep ink pairs are the ones tags use for 12px text. At xs, sm and md the letters are 8 to 10px, so treat them as a shape, not as text anyone must read.
  • No motion and no focus.

Design tokens#

Design tokens
TokenUsed for
--tag-{color}, --tag-{color}-fgFill and initials for each of the ten tag colors
outline-black/5, dark:outline-white/10Inset 1px edge
rounded-md, rounded-lg, rounded-xl8, 10 and 12px corners at md, lg and xl

API reference#

MonogramTile

A letter tile for an organization. Also exports the type MonogramSize.

Props of MonogramTile
PropTypeDefaultDescription
nameRequiredstringNo defaultThe organization's name. Initials are derived from it.
colorRequired"gray" | "blue" | "indigo" | "violet" | "pink" | "red" | "orange" | "amber" | "green" | "teal"No defaultA TagColor. gray for providers; the record's own color for companies, teams and workspaces.
size"xs" | "sm" | "md" | "lg" | "xl""md"16, 20, 24, 32 or 48px.
classNamestringNo defaultMerged last.

Known gaps#

Where the implementation and the system disagree today. Follow the system, not the gap.

Initials at xs, sm and md are 8, 9 and 10px, under the 11px floor in The Thirteen-Fourteen Rule.

The 4px and 5px corners at xs and sm are outside the 6, 8, 10 and 12px radius ramp.

Initials strip every character outside A to Z, so accented first letters vanish: Émile Roux becomes MR and Ørsted becomes R.

There is no title or label prop, so the tile can't be used on its own accessibly.

color is always passed by hand from the record. There is no seed option, while Avatar and the contact center derive tints from identityColors(seed), and indigo is allowed here but excluded from IDENTITY_HUES.

Most of the product imports it through apps/web/src/components/records/avatars.tsx, which re-exports it and wraps it as CompanyLogo, and duplicates its tint and size maps for its own avatars. The adoption count on this page undercounts real use.