Skip to content

Info tip

The i-button popover that explains jargon in place, with an optional help video.

Status
Stable
Category
Content
Adoption
Not used yet
import { InfoTip } from "@oration/canon/components/info-tip";
packages/canon/src/components/info-tip.tsx

Payment approvals

Who signs off on a payment run before money leaves Cedarline's accounts.

Runs above this amount need Maya Okafor or another approver.
$
Send the bank a list of every check before it's mailed.
import { InfoTip } from "@oration/canon/components/info-tip";import { Input } from "@oration/canon/components/input";import { SettingsGroup, SettingsRow, SettingsSection } from "@oration/canon/components/settings-section";import { Switch } from "@oration/canon/components/switch";import { toast } from "@oration/canon/components/toast";import * as React from "react";export function Hero() {    const [positivePay, setPositivePay] = React.useState(true);    const [threshold, setThreshold] = React.useState("1,000,000");    return (        <div className="w-full max-w-2xl text-left">            <SettingsSection                title="Payment approvals"                description="Who signs off on a payment run before money leaves Cedarline's accounts."                info={                    <InfoTip                        title="Payment approvals"                        description="Runs above the threshold wait for an approver before they're sent to the bank. Smaller runs go out on schedule."                        video={{                            youtubeId: "aircAruvnKk",                            title: "Set up payment approvals",                            duration: "3:12",                        }}                        learnMoreHref="/design/patterns/education"                    />                }            >                <SettingsGroup>                    <SettingsRow                        label="Approval threshold"                        description="Runs above this amount need Maya Okafor or another approver."                        htmlFor="approval-threshold"                    >                        <div className="relative w-40">                            <span className="pointer-events-none absolute top-1/2 left-2.5 -translate-y-1/2 text-13 text-muted-foreground">                                $                            </span>                            <Input                                id="approval-threshold"                                inputMode="numeric"                                value={threshold}                                onChange={(event) =>                                    setThreshold(event.target.value)                                }                                aria-describedby="approval-threshold-description"                                className="pl-6 text-right tabular-nums"                            />                        </div>                    </SettingsRow>                    <SettingsRow                        label="Positive pay"                        description="Send the bank a list of every check before it's mailed."                        htmlFor="positive-pay"                        inline                        info={                            <InfoTip                                title="Positive pay"                                description="Your bank only clears checks that match the file Cedarline sends it, by number and amount. Altered or forged checks are rejected."                            />                        }                    >                        <Switch                            id="positive-pay"                            checked={positivePay}                            onCheckedChange={(checked) => {                                setPositivePay(checked);                                toast.add({                                    title: checked                                        ? "Positive pay is on"                                        : "Positive pay is off",                                    description: checked                                        ? "The next check file goes to the bank on Friday."                                        : "Checks clear without a match file.",                                });                            }}                        />                    </SettingsRow>                </SettingsGroup>            </SettingsSection>        </div>    );}

Usage#

Info tip is the small (i) beside a section title, metric or piece of jargon. It opens a 20rem popover with a title, one or two sentences, and optionally a help video and a link to the full article, so a page can explain Days payable outstanding or Remittance advice without a tour or a wall of help text. It is how Oration teaches in place, and it is used on nearly every settings section and metric. The common mistake is hiding required information in it: an info tip is for the curious, so the page must still work for someone who never opens one.

When to use

  • Beside a settings section title or row label whose name is jargon: Approval threshold, Positive pay, Warm transfer.
  • Beside a metric in a stat strip or chart header: Days payable outstanding, Match rate.
  • When a short explainer, a two-minute video or a help article would stop someone guessing.
  • On dense tool screens where a page intro would push the tool down.

When not to use

  • To name an icon button or show a shortcut. Use Tooltip
  • To explain what a whole overview page is for, once, at the top. Use Page intro
  • For instructions someone needs to fill a field in correctly. Put them under the field. Use Field
  • For a warning or a state that needs attention. Use Alert
  • For a small form or actions anchored to a button. Use Popover

Pages explain themselves

Every page answers what it is and why you'd use it in its first viewport: through its title and description, a page intro on overview pages, or info tips beside config sections, metrics and jargon. No tours, no coach marks.

The Quiet Indigo Rule

The (i) is Slate Meta and turns ink on hover. Its only indigo is the Learn more link, which is a link, and its focus ring.

Anatomy#

Days payable outstanding

Days payable outstanding

The average number of days between receiving an invoice and paying it.

Learn more
  1. Trigger. A 16px round button with a 14px info icon in Slate Meta, named About {title}. Its hit area extends to 24px.
  2. Title. 14px semibold, balanced, naming the term.
  3. Description. 13px on a 20px line, one or two sentences in plain words. Accepts rich content.
  4. Video. Optional Video facade: a thumbnail with a duration that loads the player only on play.
  5. Learn more. Optional 13px indigo link. External links open in a new tab with an arrow icon.

Examples#

Basic

A title and one or two sentences, 6px after the term it explains. Hover for 350ms or click the (i).

Remittance advice
import { InfoTip } from "@oration/canon/components/info-tip";export function Basic() {    return (        <span className="flex items-center gap-1.5 text-sm font-medium">            Remittance advice            <InfoTip                title="Remittance advice"                description="The note sent with a payment that lists which invoices it covers, so Northwind Freight can match the money to its records."            />        </span>    );}

Learn more link

learnMoreHref adds a link to the full article. Internal paths open in place; http(s):// links open in a new tab with an arrow and say so to screen readers.

Early payment discountForm W-9
import { InfoTip } from "@oration/canon/components/info-tip";export function LearnMore() {    return (        <div className="flex flex-col items-start gap-4">            <span className="flex items-center gap-1.5 text-sm font-medium">                Early payment discount                <InfoTip                    title="Early payment discount"                    description="Terms such as 2/10 net 30 take 2% off when you pay within 10 days. Cedarline schedules eligible invoices early when cash allows."                    learnMoreHref="/design/patterns/education"                />            </span>            <span className="flex items-center gap-1.5 text-sm font-medium">                Form W-9                <InfoTip                    title="Form W-9"                    description="The IRS form a US supplier fills in so you can report what you paid them. Cedarline holds payments to suppliers without one on file."                    learnMoreHref="https://www.irs.gov/forms-pubs/about-form-w-9"                />            </span>        </div>    );}

With a help video

video adds a Video facade: only the thumbnail loads until someone presses play, then the privacy-enhanced player takes its place and takes focus.

Remittance matching
import { InfoTip } from "@oration/canon/components/info-tip";export function WithVideo() {    return (        <span className="flex items-center gap-1.5 text-sm font-medium">            Remittance matching            <InfoTip                title="Remittance matching"                description="Cedarline reads each remittance and pairs its lines with open invoices, so short pays and duplicates show up before month end."                video={{                    youtubeId: "zjkBMFhNj_g",                    title: "Match remittances to invoices",                    duration: "4:05",                }}                learnMoreHref="/design/patterns/education"            />        </span>    );}

Beside a metric

Stat, Settings section and Settings row take an info slot for exactly this. Open metric tips below the strip with side="bottom" so they don't cover the header.

Days payable outstanding
38Down 4 days vs August
Match rate
96.4%
Paid this month
$4.2M
import { InfoTip } from "@oration/canon/components/info-tip";import { Stat, StatStrip } from "@oration/canon/components/stat";export function BesideMetric() {    return (        <StatStrip className="w-full max-w-2xl">            <Stat                label="Days payable outstanding"                value="38"                delta={{                    value: "4 days",                    direction: "down",                    label: "vs August",                }}                info={                    <InfoTip                        title="Days payable outstanding"                        description="The average number of days between receiving an invoice and paying it. Longer holds cash; too long strains suppliers."                        side="bottom"                    />                }            />            <Stat                label="Match rate"                value="96.4%"                info={                    <InfoTip                        title="Match rate"                        description="The share of remittance lines Cedarline matched to an invoice without a person stepping in."                        side="bottom"                    />                }            />            <Stat label="Paid this month" value="$4.2M" />        </StatStrip>    );}

States#

States
StateTreatment
RestThe (i) in Slate Meta with a help cursor.
HoverThe icon turns ink over 150ms. After 350ms the popover opens.
Focus visibleThe global 2px Focus Indigo outline at a 2px offset.
OpenThe icon stays ink (data-popup-open). The popover grows from a 0.97 scale over 160ms.
Closing150ms after the pointer leaves the trigger and the popover, it fades out over 110ms.
Video playingThe thumbnail is replaced by the privacy-enhanced YouTube player, which takes focus.

Behavior#

  • Click opens it, and so does hovering the (i) for 350ms with a pointer. Touch and keyboard users open it by tapping or pressing Enter.
  • It stays open while the pointer is on the trigger or the popover, and closes 150ms after it leaves both, so there's time to cross to the play button or the link.
  • Opened by click or keyboard, focus moves to the first control inside: the video's play button or the link. Escape or a click outside closes it and returns focus to the (i).
  • The popover is 20rem wide, capped at the viewport minus 2rem, 4px from the trigger, and flips when it would leave the screen. side defaults to top and align to center.
  • The video costs nothing until played: only the thumbnail loads, and the youtube-nocookie player loads on click with autoplay.
  • learnMoreHref starting with http:// or https:// opens in a new tab with rel="noopener noreferrer", an arrow icon and (opens in a new tab) for screen readers. Anything else navigates in place.
  • It uses Popover's motion: 160ms in from a 0.97 scale and 110ms out, reduced to a fade under reduced motion.

Do and don't#

Do. Explain the term in one or two plain sentences, with an example from the user's world.
Don't. Paste the help article into the description. If it needs headings or a list, link to it with learnMoreHref.
Approval threshold
Do. Put the (i) right after the label it explains, 6px away, on the same baseline.
Approval threshold
Don't. Park info tips at the far edge of a row or card, where it isn't clear which label they explain.
Do. Keep required instructions visible on the page and use the tip for background.
Don't. Hide how to fill a field in inside a tip. Most people never open one.

Content#

  • The title is the term itself, in sentence case: Days payable outstanding, not What is DPO?
  • The description starts with what it is, then why it matters: The average number of days between receiving an invoice and paying it. Longer holds cash; too long strains suppliers.
  • Use numbers and Cedarline's own objects in examples: suppliers, invoices, payment runs.
  • Video titles read like Oration help videos, with a real duration: Match remittances to invoices, 3:12.

Accessibility#

  • The trigger is a button named About {title}, so a list of buttons reads About Approval threshold, not Info.
  • The popover is labelled by its title and described by its description.
  • The link reads Learn more about {title}, plus (opens in a new tab) when external, so several on one page stay distinct.
  • The play button is named Play video: {title}, 3 minutes 12 seconds.
  • The visible icon is 16px; a pseudo-element extends the hit area to 24px. On touch layouts consider spacing it further from other targets.
  • Hover opening is a convenience. Everything is reachable by click, tap and keyboard.
Keyboard interactions
KeysAction
TabFocuses the (i), then the controls inside when open.
EnterOpens or closes the tip.
SpaceOpens or closes the tip.
EscCloses the tip and returns focus to the (i).

Design tokens#

Design tokens
TokenUsed for
--muted-foregroundThe (i) at rest
--foregroundThe (i) on hover and open; title
--popoverThe popover surface
shadow-mdThe overlay shadow, with a 1px ink ring at 10%
--primaryThe Learn more link
--mutedThe video facade before its thumbnail loads
--radius-lg10px popover corners

API reference#

InfoTip

The trigger and its popover. Built on Popover and Video facade.

Other props spread onto Nothing. Only the props below are accepted..

Props of InfoTip
PropTypeDefaultDescription
titleRequiredstringNo defaultThe term. Also names the trigger (About {title}) and the link.
descriptionRequiredReactNodeNo defaultOne or two sentences. Rendered in a div, so it can hold rich content.
video{ youtubeId: string; title: string; duration: string }No defaultA help video shown as a click-to-load facade.
learnMoreHrefstringNo defaultAdds a Learn more link. http(s):// opens in a new tab.
side"top" | "bottom" | "left" | "right""top"Where the popover opens.
align"start" | "center" | "end""center"Alignment along the trigger.
classNamestringNo defaultMerged onto the trigger button.

Known gaps#

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

InfoTip takes no open or onOpenChange, so it can't be opened from elsewhere, such as from a What's this? link in an empty state.

The hover delay (350ms) and close delay (150ms) are fixed and differ from the tooltip's 400ms, so a row with both opens them at different speeds.

The trigger's hit area is 24px everywhere. DESIGN.md asks for 32px targets on touch surfaces.