Character Budget

Counts text against X, SMS, LinkedIn and meta-description limits at once, so posts aren't cut off and SMS don't silently cost double.

StopsPost cut off or SMS costs double

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/char-budget.json

The files land in your project. They're yours to edit.

Usage

char-budget-usage.tsx
import { CHAR_TARGETS, CharBudget } from "@/components/ui/char-budget"

<CharBudget
  label="Launch announcement"
  defaultValue={"Bollard UI is live! Components that stop users from making mistakes — undo toasts, hold-to-confirm, and more. It’s free & open source… try it today 🚀 https://bollard-ui.dev"}
  targets={[
    CHAR_TARGETS.x,
    CHAR_TARGETS.sms,
    CHAR_TARGETS.meta,
    { kind: "graphemes", label: "Push notification", limit: 110 },
  ]}
/>

What it catches

  • Text over each platform's limit, counted as people see characters (emoji and flags count once)
  • X's weighting (URLs as 23, emoji and CJK as 2)
  • Characters that force UCS-2 and shrink an SMS segment from 160 to 70
  • Extra SMS segments

Props

CharBudget props
PropTypeDefault
label

Visible label.

string"Message"
value

Controlled text.

string—
defaultValue

Initial text when uncontrolled.

string""
onValueChange

Called on every edit, including one-tap fixes.

(value: string) => void—
targets

Limits to check at once; defaults to X, SMS, LinkedIn and a meta description.

CharTarget[]Object.values(CHAR_TARGETS)
ucs2Message

Text of the warning shown when a character forces the SMS into UCS-2.

string"These switch the SMS to UCS-2, 70 characters per part in…
plainLabel

Label of the fix that swaps curly quotes, dashes and ellipses for plain ones.

string"Use plain characters"
messages

Reword or translate the counter's labels and announcements.

Partial<typeof CHAR_BUDGET_MESSAGES>—

* required. Also accepts the native props of <textarea> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Visible <label>; the field is described by an aria-live="polite" summary
  • Buttons that find a problem character have aria-labels naming it
  • Finding a character moves focus to it in the field

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

  • Platform limits are built in and can change; pass your own targets to override
  • Falls back to code points where Intl.Segmenter is unavailable
  • Real SMS cost also depends on the carrier and gateway

What it stops

Post cut off or SMS costs double

Also known as

character count · char limit · tweet · x post · sms · linkedin · meta description · textarea

Field type

Dependencies

Pairs well with