Cal.comdesign-system

Input Addons and URL Fields

Decision guide for units, fixed suffixes, usernames, slugs, and responsive URL prefixes.

Choose the composition from the behavior of the static text, not from how one example domain happens to look locally. Production domains, organization paths, and enterprise usernames are not bounded.

This guide is the default for humans and agents adding or migrating an input with adjacent text.

Choose the pattern first

Do not decide based on today's cal.com value. A field is variable when deployment, organization, team, or user data can change the static text.

Bounded addons

Units, currencies, and fixed suffixes stay visible on mobile. Use align to place the addon and include fixed punctuation in text.

Cal.com · bounded addon patterns
minutes
.cal.com

Examples shipped in product:

  • minutes, credits, and events use align="after";
  • currency symbols use the default align="before";
  • organization subdomains use text=".cal.com" with align="after".

Do not use URL splitting or a mobile URL description for these bounded values.

Variable URL fields

URL fields have one invariant across all policies: mobile shows the complete current URL below the input. The desktop addon is hidden below sm; UrlFieldDescription restores the static prefix, separator, and live editable value.

Normalize the static input before calling the helper:

  • omit https:// / http:// from display text;
  • remove a trailing /;
  • pass path segments without leading or trailing /;
  • pass the editable slug separately as value.

Profile and username policy

Use when the field represents a profile identity and keeping the complete prefix in the desktop addon is more useful than reclaiming input width.

apps/web · team profile URL
acme-enterprise.cal.com/team/

acme-enterprise.cal.com/team/customer-success

Do not add UrlFieldLabelRow for this policy: domainDisplay is intentionally null.

Conventional URL policy

Use for create, duplicate, setup, and admin flows where the editable slug needs room. The default helper policy splits domains at URL_PREFIX_SPLIT_MIN_LENGTH while keeping the path next to the input.

apps/web · event URL with long domain
/team/customer-success/

acme-enterprise.cal.com/team/customer-success/15min

Always render UrlFieldLabelRow from the helper result. It renders nothing extra for short domains and provides the correct decorative desktop domain for long ones.

Do not:

  • count characters in the call site;
  • slice a domain locally;
  • render a second hand-written mobile label;
  • manually concatenate a separate mobile description;
  • put the protocol in visible addon or description text.

Separators are data

/ is the default, not an assumption. Pass separator and placement explicitly:

fullStaticText never includes that join separator. UrlInputWithAddon applies it consistently to the desktop addon and the complete mobile preview.

If .cal.com is a short fixed suffix that should remain visible on mobile, it is a bounded addon and belongs in InputWithAddonText instead.

Labels and accessibility

Every control stays inside Field.

  • The real FieldLabel names the editable control.
  • UrlFieldLabelRow marks the split domain aria-hidden because it is visual context, not a second label.
  • Use an explicit aria-label only when the visible label is intentionally decorative or the control is inside a separately labelled group.
  • Keep status and copy controls in endAddon; do not make the static text interactive.
  • UrlFieldDescription is supporting context, not the accessible name.

Exceptional horizontal layouts

Some existing surfaces place the URL input beside actions. In those cases:

  1. Set showDescription={false} on UrlInputWithAddon.
  2. Keep the URL input inside the horizontal group.
  3. Render UrlFieldDescription immediately after the group.
  4. Pass the same complete current URL: static text + separator + editable value.

This is a layout escape hatch, not the default composition.

Implementation checklist

Before opening a PR:

  • Search for a real field with the same product behavior.
  • Classify the addon as bounded, profile URL, conventional URL, or copy-first.
  • Use getUrlFieldAddonParts; do not add local split/truncation helpers.
  • Keep protocol and trailing separators out of normalized display text.
  • Use one separator source for desktop and mobile.
  • Verify a short domain, a domain at/above the split threshold, an empty addon, and the mobile layout.
  • Keep RHF Controller / Field / FieldError wiring unchanged when migrating.
  • Cite the product call site in docs when introducing a new variation.

Reference call sites

Example URLs

Not exhaustive — open these locally from http://localhost:3000.