Rahhal's Marsam

Components

Browse the building blocks of every Rahhal mini-app.

58 components across the platform

Colors & radius

Every design token from `@rahhal/themes`. Stored as HSL channels (so Tailwind opacity modifiers work) AND surfaced as `#RRGGBB` hex for cross-platform consumers (iOS, Android, slides, Figma). Click any hex chip to copy it. Try toggling theme via the cog — every swatch flips live.

Foundations

Typography

Font families (sans · arabic · mono) with web stacks plus iOS/Android equivalents, type scale (rem + px), and weight ramp. Bilingual EN/AR samples live alongside each other so designers can review both at once. Click any chip to copy.

Foundations

Icons & brand marks

Download official Rahhal icons in PNG and SVG, with light and dark variants, React usage, and guidance for Lucide UI icons and social brand marks.

Foundations

RahhalIcon

Cross-platform icon renderer. Parses the same prefixed catalog strings the iOS shell understands (`url:…`, `emoji:…`, `system:…`, `resource:…`, plus bare URLs/emojis) so a CMS-authored value paints identically on every client. Falls back to a muted square when the value is empty; `system:` and `resource:` render as debug tokens on the web because there's no SF Symbols / iOS bundle equivalent.

Foundations

Button

Primary actionable element. Six variants (default, destructive, outline, secondary, ghost, link) and four sizes. Use `asChild` to keep button styling on a router-aware link.

Actions

BackButton

Small ghost capsule with a leading chevron — the standard 'go back' affordance for nested pages. Defaults to `window.history.back()`; pass `onClick` to plug in router-aware navigation. The chevron rotates automatically under RTL.

Actions

PasteButton

Icon button that reads the system clipboard and forwards the text to a callback. Defaults to a clipboard glyph; pair with a string-transform helper at the call site (URL extractor, code parser, normaliser).

Actions

Toggle · ToggleGroup

On/off button (`<Toggle>`) and grouped toggle sets (`<ToggleGroup>`). Use for formatting toolbars, view-mode switches, and any compact two-state control. `ToggleGroup` accepts `type="single"` for radio-style behavior or `type="multiple"` for checkbox-style.

Actions

Input & Textarea

Native `<input>` and `<textarea>` wrappers themed via design tokens. Pair with `<Label htmlFor>` for accessible click-the-text targets.

Forms

SearchBar

Capsule-shaped search input with leading magnifier, optional clear button, optional trailing slot (e.g. `⌘K` hint), and Enter-to-submit. Wrapped in a `<form role="search">` so iOS shows the on-screen "Search" key. Inputs in this design system are normally rectangular — search is the one intentional capsule exception, matching Apple's HIG and iOS Search.

Forms

FilterChip

Toggleable filter pill. Two interaction modes that compose: `active` + `onToggle` for multi-select category chips, `onRemove` for "applied filter" chips that show what's currently filtering the list. Optional `leading` icon and `count` badge. Use over `<Tabs>` when you have 3+ values, multi-select, or variable counts.

Forms

FileDropzone

Drop-or-browse file picker with native HTML5 drag-and-drop, click-to-select fallback, and built-in `accept` / `maxSize` / `maxFiles` validation. Always controlled — the parent owns the file list.

Forms

Switch · Checkbox · Radio

Boolean and small-set selectors. `<Switch>` for on/off settings, `<Checkbox>` for individual toggles, `<RadioGroup>` for mutually-exclusive choices.

Forms

Slider

Value picker with a draggable thumb. Built on Radix Slider. Pass a single-element array for a normal slider or a two-element array for a range slider.

Forms

Combobox

Searchable single-select picker. Composes `<Popover>` + `<Command>` + `<Button>` into the canonical shadcn pattern. Use for country / city / airport / category pickers where `<Select>` becomes unwieldy past ~10 options. Always controlled.

Forms

Calendar & DatePicker

Themed `<Calendar>` (react-day-picker) plus the canonical `<DatePicker>` recipe (Calendar inside a Popover). Single date and range modes share the same trigger. RTL-safe — the day grid mirrors automatically, and chevrons rotate via `rtl:rotate-180`.

Forms

Form (react-hook-form)

Validated form composition built on react-hook-form. `<FormField>` wires a controller into descendant `<FormLabel>` / `<FormControl>` / `<FormDescription>` / `<FormMessage>` parts via shared aria attributes — labels point to inputs, errors are announced, and focus rings show on the right element automatically.

Forms

Section

Page-section building block with consistent typography, spacing, and a header (title + description + optional action). Pair with `Card`, `List`, or `Table` for the surface; `Section` only handles rhythm and labeling so layouts stay uniform across mini-apps.

Surfaces

Card

Surface container for grouped content. Compose with `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`. Each piece is independent so partial layouts are fine.

Surfaces

List

Apple-style inset grouped list. Each `ListGroup` is a rounded card with hairline dividers between rows, optional uppercase header above and helper-text footer below — same model as iOS Settings. Compose `ListItem` rows with `leading` (icon/avatar), `title`, `description`, and `trailing` (value, chevron, switch). Use `variant="plain"` to embed inside another card.

Surfaces

PlaceCard

Compact, mobile-first card for a single geographic place. Name, optional address, and the coordinate readout stack vertically and truncate — never scroll horizontally. The title is adaptive: it shows `name`, else `address`, else the formatted coordinates, so a nameless waypoint still renders cleanly. Pass an `<Input/>` as `name` for inline editing; use `leading`/`trailing` for an icon and a checkbox/switch.

Surfaces

Table

Themed `<table>` family wrapped in a rounded card surface. Use for tabular data — invoices, bookings, comparisons. For settings-style or single-column lists, prefer `List` instead.

Surfaces

Kanban board

Column-based task board layout primitive. Provides the board frame, columns (with title / count / accent dot / action), and cards (with title / description / leading / trailing / meta slots). Layout only — apps wire drag-and-drop with their own DnD library (e.g. `@dnd-kit/core`). On small screens columns stack and snap-scroll horizontally so swipes feel native.

Surfaces

Map

Declarative wrapper around MapLibre GL JS. Compose `MapMarker`, `MapPolyline`, `MapPolygon`, `MapCircle`, and `MapCluster` as children — geometry inherits the platform's blue primary by default. Free OpenFreeMap tiles light/dark; switch via the `style` prop. RTL Arabic labels render correctly out of the box. The web component complements iOS MapKit and Android Maps SDK in the native Rahhal apps.

Surfaces

Carousel

Horizontal (or vertical) scroller built on Embla Carousel. Used for photo galleries, onboarding, and any "swipe through a row" UX. Detects `[dir="rtl"]` automatically and forwards the right axis direction to Embla.

Surfaces

Collapsible

Minimal disclosure primitive (one toggle, one panel). Re-exports of Radix Collapsible. For multiple sections, use `<Accordion>` instead.

Surfaces

Layout primitives

Three small but essential layout primitives shipped together: `<AspectRatio>` (lock a ratio for media), `<ScrollArea>` (themed custom scrollbar), and `<Separator>` (semantic divider). They show up everywhere — they get one shared catalog page so the sidebar stays focused on richer components.

Surfaces

Country facts

Drop-in country UI from `@rahhal/countries/react` — `CountryFlag`, `CountryCard`, `CountryFacts`, and `CountryDetail`. Each is data-in or self-fetching (by `name`/`code`) and accepts a `labels` prop so the host app localizes. Composed from `@rahhal/ui`, so it matches the app theme.

Data

Badge

Inline label for status, counts, tags. Four variants (default, secondary, destructive, outline).

Feedback

Avatar · Skeleton · Progress

Compact identity + loading-state primitives.

Feedback

Alert

Inline status callout. Pair with a leading icon for instant recognition.

Feedback

Callout

Inline note / hint / warning / important block. Lighter than an Alert — used inside page bodies for disclaimers, agent-targeted hints, deprecation notes, and similar asides.

Feedback

Toaster (sonner)

One `<Toaster />` mount + the imperative `toast(…)` helper. Themes follow `useTheme()` automatically.

Feedback

Spinner

Small indeterminate loading indicator. Use inline beside text, inside busy buttons, or centered in a wait block. Stroke is `currentColor` so it inherits text color, and the size scale matches Lucide icons.

Feedback

Rating

Star rating with read-only display + interactive input modes. Half-star increments by default, keyboard support (Arrow / Home / End), and an `aria-label` describing the current value. Always renders LTR so 1-star sits at the start edge in both reading directions.

Feedback

EmptyState

Recurring "nothing here yet" surface. Drop into any list, table, or section that can be empty — icon, title, description, and an optional action keep the spacing rhythm consistent across mini-apps.

Feedback

JsonViewer

Read-only, syntax-highlighted JSON block. Use for admin payload inspectors, debug panels, webhook viewers — anywhere an operator skims, copies, or downloads a structured payload. Theme tokens are wired through CSS variables so it follows light / dark automatically.

Feedback

Dialog · Sheet

Centered modal vs. side sheet. Use a dialog for short focused interactions; use a sheet for navigation drawers, filters, or contextual editors.

Overlays

AlertDialog

Interrupting confirmation dialog for destructive or unrecoverable actions. The user must explicitly confirm or cancel — clicking outside does not dismiss. Built on Radix Alert Dialog.

Overlays

Drawer

Bottom-sheet style modal that slides up from the edge of the viewport. Built on `vaul` for touch-friendly drag-to-dismiss. Use for mobile-first flows where a centered `<Dialog>` would feel heavy. For desktop side panels, prefer `<Sheet>`.

Overlays

Popover

Floating panel anchored to a trigger. Non-modal — clicking outside dismisses. Built on Radix Popover. Use for date pickers, filter panels, color pickers, settings popouts, or any disclosure that doesn't need to block the page.

Overlays

HoverCard

Small floating preview shown on hover or focus. Use for profile cards, link previews, or anywhere a tooltip would be too small for the content. Built on Radix Hover Card.

Overlays

Command palette

Searchable list / command palette built on `cmdk`. Use inline (`<Command>`) for typeahead pickers, or wrapped in `<CommandDialog>` for a global cmd-K launcher. Items are scored client-side as the user types.

Overlays

Dropdown · Select · Tooltip

Floating triggers and selectors. All three flip correctly under RTL.

Overlays

Tabs · Accordion

Disclose grouped content. Use tabs for parallel views, accordion for sequential reveal.

Navigation

NavBar

Opinionated top navigation built on `<AppHeader>`. Brand on the start, typed links centered on `md+`, actions on the end. Router-agnostic via a `renderLink` render prop so apps can supply their own `<NavLink>` / `<Link>`.

Navigation

Sidebar

Layout primitives for the catalog-style left rail every Rahhal mini-app uses: a glassy floating column with a sticky filter / brand area, a scrolling body, and collapsible sections. Pair `<AppSidebar>` with `<AppSidebarHeader>`, `<AppSidebarBody>`, `<AppSidebarSection>`, `<AppSidebarItem>` (and optional `<AppSidebarFooter>`). The chrome is layout-only — apps supply data, routing, and filtering. For mobile, render the same content inside a `<Sheet>` and add a hamburger button to `<AppHeader>`.

Navigation

Breadcrumb

Hierarchical navigation trail. Compose `<Breadcrumb>` → `<BreadcrumbList>` → `<BreadcrumbItem>` (containing `<BreadcrumbLink>` or `<BreadcrumbPage>`) with `<BreadcrumbSeparator>` between siblings. The default chevron flips under RTL.

Navigation

Pagination

Accessible page navigator. Compose `<Pagination>` → `<PaginationContent>` → `<PaginationItem>` (with `<PaginationLink>`, `<PaginationPrevious>`, `<PaginationNext>`, or `<PaginationEllipsis>` inside). Previous/Next chevrons rotate under RTL.

Navigation

Stepper

Multi-step progress indicator. Horizontal (default) or vertical orientation; status (`complete`, `current`, `upcoming`) is derived per-step from `activeStep`. Used for wizards and onboarding flows.

Navigation

WizardHeader

Sticky per-step header for multi-step flows inside a native host (`?host=…`), where the web hides its own AppHeader/AppFooter. Lays out a back control at the start, a centered title, and an optional action at the end — plus a slim segmented progress bar. Back navigation is delegated via `onBack` (omit it on the first step); top safe-area padding clears the iOS notch. RTL-aware.

Navigation

Embedded mode

Detection helper for when a mini-app is loaded inside an iOS / Android / desktop host container. The host signals via `?host=<value>`; the web persists it in sessionStorage and exposes it to layouts via `useIsEmbedded()` so chrome (`<AppHeader>`, `<AppFooter>`) can be hidden in favor of the host's native nav.

Navigation

Footer

Bottom chrome for a Rahhal mini-app. Mirrors the floating-pill aesthetic of `<AppHeader>`: a glassy capsule with `start` (links) and `end` (social) slots, plus a small brand line below. Helpers `<FooterLinks>`, `<FooterLink>`, and `<FooterSocial>` keep spacing and ARIA consistent across mini-apps.

Navigation

PhoneInput

International phone field with country picker. Default country is auto-detected from the user's IANA timezone (e.g. KSA for `Asia/Riyadh`). Numbers always render LTR even inside RTL regions.

Auth

InputOTP

Segmented one-time-code input. Used for the SMS code step of the Rahhal phone-OTP sign-in flow, but generic enough for any 4–8 digit verification code. Built on the `input-otp` library; logical Tailwind utilities mean the rounded ends and divider borders flip correctly under RTL.

Auth

LoginScreen (demo)

A UI-only walk-through of the canonical Rahhal phone-OTP login: phone step → OTP step → success. Two layouts share the same form body — `split` (default in this demo) adds a brand-led marketing pane on the start edge at `lg+` (hidden on mobile); `centered` is the focused single-card surface. The screens here are **not** wired to real auth — they navigate to a demo signed-in page. Production mini-apps consume the same UX via `<LoginScreen>` from `@rahhal/auth/react` and exchange the code with Supabase.

Auth

RequireRole

Render-gate that requires both an active session and a named role string in the user's `app_metadata.roles` claim. Compose at the top of a protected subtree (typically inside `<AuthProvider>` and above all admin routes) so the gate runs once. Roles are issued backend-side by writing `auth.users.app_metadata.roles`; this component is read-only.

Auth

AccessDeniedScreen

Full-screen "you don't have permission" surface. Pairs with `<RequireRole>` from `@rahhal/auth/react` as the default `deniedFallback` when a signed-in user is missing the role required to view a protected subtree. Same anatomy as `<EmptyState>` (icon · title · description · action) but with a destructive-tinted icon tile and a viewport-filling default layout.

Auth