Skip to content

Moving Raíz, Agenda and Account

What changes for Arbol's three dashboards when they move from their own components/ui folders onto @arbol-ai/ui.

The library started as the components/ui folders of Arbol’s three dashboards. Most components keep the apps’ names and props, so moving a screen is usually changing an import from ~/components/ui/… to @arbol-ai/ui. This page lists what differs.

Setup, once per app

  1. bun add @arbol-ai/ui (React Aria, React and Tailwind are already there).
  2. In the entry stylesheet, after Tailwind: @import '@arbol-ai/ui/theme.css';. Keep the app’s own fonts.css (the theme names Arbol Sans; it never ships font files). The theme does not ship the apps’ global * { min-width: 0 } or the unlayered h1, h2 { font-family: Arbolito } rule: keep them in the app while its own screens depend on them.
  3. Render RouterUIProvider from @arbol-ai/ui/router at the root (it is UIProvider wired to TanStack Router: navigate({ href }), and base paths only for app paths, so tel: and wa.me links stay intact), with the locale:
    • Raíz and Account (Paraglide): locale={getLocale() === 'es' ? 'es-CO' : 'en-US'}.
    • Agenda (Spanish only): locale="es-CO". Agenda had no I18nProvider, so React Aria’s own strings (search clear button, drop zone…) followed the browser’s language; this fixes it.
  4. Mount <Toaster /> once, inside UIProvider, to replace Raíz’s sonner Toaster.

Renamed props and changed defaults

Component Before (app) Now
Badge variant="success", "destructive-soft"… tone (neutral, brand, success, warning, danger, info) and variant (soft, outline); variant="outline" still works. JSDoc maps every old name.
Tooltip (Raíz) Tooltip + TooltipContent TooltipTrigger + Tooltip (React Aria’s names).
SaveBar dirty, busy, label isDirty, isPending, saveLabel.
Notice default tone success default tone info: pass tone="success" where a bare <Notice> meant success.
ErrorState heading h1 heading h2 by default; pass level={1} when it is the page.
Select (Raíz) value: string, onValueChange React Aria’s value / onChange; label, description and error built in.
PhoneField (Raíz PhoneInput) onValueChange, disabled, invalid, emptyLabel onChange, isDisabled, isInvalid; the empty text is a library string.
CursorPagination (Raíz ListPagination) onPrev onPrevious.
FilterToggle (Agenda InactiveFilter) selected isSelected.
UnsavedChanges (Agenda) imported TanStack Router <UnsavedChangesDialog {...useUnsavedChangesBlocker(isDirty)} /> from @arbol-ai/ui/router.
RowMenu navigated with useNavigate uses href, routed by UIProvider navigate.
Overlay onClose after a 200 ms timer same API; onClose fires after the exit animation ends. Open it from a button with OverlayTrigger, not React Aria’s DialogTrigger.
ColorSwatchPicker (Agenda ColorPicker) oklch palette pass the palette as hex (#rrggbb), rgb() or hsl().
Sheet (Raíz) non-modal always modal; the assistant side panel stays app layout.
ConfirmationDialog (Raíz, two of them) labels object, fixed DOM ids one ConfirmDialog with plain props and an optional type-to-confirm value.
Toasts (Raíz, sonner) toast.error(message) the same call shape from @arbol-ai/ui.

Fixed on the way

  • White text on the old destructive red (3.8:1) and on the 90%-opacity primary hover failed WCAG AA; both now pass.
  • Account’s SearchInput ignored a controlled value on the first render; Agenda’s could run a stale search.
  • Account’s SettingToggleCard nested a <label> in a <label>; Raíz’s EditableTitle rendered <button><h1>.
  • Raíz’s breadcrumbs announced “breadcrumb” in English; DataTable rows had no keyboard path; nav landmarks had no names.
  • Agenda’s pick list built plurals by concatenation (“1 seleccionado/s”).

Type to search…

↑↓ navigate↵ selectEsc close