Data Display
Card
A surface container with optional header, body, and footer slots.
| Prop | Type | Default | Description |
|---|---|---|---|
variant? | 'elevated' | 'outlined' | 'filled' | 'elevated' | Visual style |
header? | React.ReactNode | — | Content rendered in the header slot |
footer? | React.ReactNode | — | Content rendered in the footer slot |
children? | React.ReactNode | — | Card body content |
size? | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Controls padding scale |
accessibilityLabel? | string | — | Accessibility label |
import { Card, Text, Stack } from '@objectifthunes/limestone-sdk';
<Card variant="elevated" size="md">
<Text variant="heading">Profile</Text>
<Text variant="body" color="mutedForeground">Manage your account settings.</Text>
</Card>
Avatar
A user avatar that renders a remote image or falls back to initials derived from the name prop.
| Prop | Type | Default | Description |
|---|---|---|---|
src? | string | — | Remote image URL |
name? | string | — | Full name; initials are extracted when image is absent or fails to load |
size? | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Avatar diameter |
variant? | 'circle' | 'rounded' | 'circle' | Shape of the avatar |
fallbackColor? | ColorProp | — | Background color for the initials fallback |
accessibilityLabel? | string | — | Accessibility label |
import { Avatar, Stack } from '@objectifthunes/limestone-sdk';
// Image with fallback to initials
<Avatar src="https://example.com/user.jpg" name="Alice Martin" size="lg" />
// Initials only
<Avatar name="Bob Chen" size="md" variant="rounded" fallbackColor="primary" />
Tag
A compact label used to categorise or annotate content. Supports an optional dismiss button.
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Tag text |
variant? | 'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'default' | Color scheme |
size? | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'sm' | Height and font size |
icon? | string | — | Icon name rendered to the left of the label |
onDismiss? | () => void | — | When provided, a dismiss button is rendered |
accessibilityLabel? | string | — | Accessibility label |
import { Tag, Stack } from '@objectifthunes/limestone-sdk';
<Stack direction="horizontal" gap="sm">
<Tag label="React Native" variant="primary" />
<Tag label="TypeScript" variant="secondary" />
<Tag label="Bug" variant="error" onDismiss={() => removeTag('bug')} />
</Stack>
List
A data list backed by React Native FlatList or SectionList. Supports pull-to-refresh, infinite scroll, and sticky section headers.
| Prop | Type | Default | Description |
|---|---|---|---|
data? | T[] | — | Flat array of items (use with renderItem + keyExtractor) |
sections? | ListSection<T>[] | — | Sectioned data; each section has title and data |
renderItem | (item: T, index: number) => React.ReactNode | — | Row renderer |
keyExtractor | (item: T, index: number) => string | — | Unique key per row |
emptyComponent? | React.ReactNode | — | Rendered when the list is empty |
refreshing? | boolean | false | Controls pull-to-refresh spinner |
onRefresh? | () => void | — | Called when the user pulls to refresh |
onEndReached? | () => void | — | Called when the list scrolls near the end |
onEndReachedThreshold? | number | — | Distance from end that triggers onEndReached |
stickyHeaders? | boolean | false | Pin section headers while scrolling |
separatorColor? | ColorProp | — | Row separator line color |
import { List, Text, Box } from '@objectifthunes/limestone-sdk';
interface User { id: string; name: string; }
<List
data={users}
keyExtractor={(u) => u.id}
renderItem={(u) => (
<Box py="sm" px="md">
<Text variant="body">{u.name}</Text>
</Box>
)}
refreshing={isRefreshing}
onRefresh={reload}
onEndReached={loadMore}
/>
Accordion
A vertically stacked list of collapsible sections. Supports single or multiple open panels simultaneously.
| Prop | Type | Default | Description |
|---|---|---|---|
items | AccordionItem[] | — | Each item: { id, title, content, disabled? } |
defaultOpenIds? | string[] | [] | IDs of panels open on first render |
allowMultiple? | boolean | false | When true, multiple panels can be open at once |
variant? | 'default' | 'separated' | 'default' | separated adds gaps between panels |
accessibilityLabel? | string | — | Accessibility label for the accordion container |
import { Accordion } from '@objectifthunes/limestone-sdk';
import { Text } from '@objectifthunes/limestone-sdk';
const items = [
{ id: 'faq-1', title: 'What is limestone-sdk?', content: <Text variant="body">A mobile toolkit for React Native + Expo.</Text> },
{ id: 'faq-2', title: 'Does it support dark mode?', content: <Text variant="body">Yes — define a darkTheme in defineConfig.</Text> },
];
<Accordion items={items} allowMultiple defaultOpenIds={['faq-1']} />
EmptyState
A centred placeholder shown when a list or page has no content. Supports a title, message, icon, and a single action button.
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | — | Primary heading |
message? | string | — | Explanatory text below the heading |
icon? | string | — | Icon name rendered above the heading |
action? | { label: string; onPress: () => void } | — | Optional call-to-action button |
size? | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Controls icon size and padding |
accessibilityLabel? | string | — | Accessibility label |
import { EmptyState } from '@objectifthunes/limestone-sdk';
<EmptyState
icon="inbox"
title="No messages"
message="When you receive a message it will appear here."
action={{ label: 'Compose', onPress: openComposer }}
/>
StepIndicator
A horizontal progress indicator that visualises the steps of a multi-step flow.
| Prop | Type | Default | Description |
|---|---|---|---|
steps | Step[] | — | Each step: { label? } |
currentStep | number | — | Zero-based index of the active step |
variant? | 'default' | 'numbered' | 'default' | numbered shows the step number inside the circle |
size? | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Circle diameter |
accessibilityLabel? | string | — | Accessibility label |
Each step resolves to a StepStatus of completed, active, or upcoming.
import { StepIndicator } from '@objectifthunes/limestone-sdk';
const steps = [
{ label: 'Account' },
{ label: 'Profile' },
{ label: 'Payment' },
{ label: 'Review' },
];
<StepIndicator steps={steps} currentStep={1} variant="numbered" size="md" />
Timeline
A vertical list of timestamped events, each with an optional icon and severity variant.
| Prop | Type | Default | Description |
|---|---|---|---|
events | TimelineEvent[] | — | Ordered array of events |
size? | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Node size |
accessibilityLabel? | string | — | Accessibility label |
Each TimelineEvent has: id, title, description?, timestamp?, icon?, and variant? (default | success | warning | error).
import { Timeline } from '@objectifthunes/limestone-sdk';
const events = [
{
id: 'ev-1',
title: 'Order placed',
description: 'Your order #1042 has been received.',
timestamp: '10:00 AM',
variant: 'success',
},
{
id: 'ev-2',
title: 'Payment failed',
description: 'Card ending in 4242 was declined.',
timestamp: '10:05 AM',
variant: 'error',
},
{
id: 'ev-3',
title: 'Retrying payment',
timestamp: '10:06 AM',
variant: 'warning',
},
];
<Timeline events={events} size="md" />
---
## ListItem
A single row for use inside a `List` or standalone. Renders a leading element (icon or avatar), a title+subtitle text block, and a trailing element (icon, badge, or custom content).
| Prop | Type | Default | Description |
|---|---|---|---|
| `title` | `string` | — | Primary row text |
| `subtitle?` | `string` | — | Secondary text below the title |
| `leading?` | `React.ReactNode` | — | Content on the left (avatar, icon, image) |
| `trailing?` | `React.ReactNode` | — | Content on the right (icon, badge, text) |
| `onPress?` | `() => void` | — | Makes the row pressable |
| `disabled?` | `boolean` | `false` | Disables press interaction |
| `showChevron?` | `boolean` | `false` | Render a right-pointing chevron in the trailing slot |
| `separator?` | `boolean` | `true` | Render a bottom border |
| `size?` | `'sm' \| 'md' \| 'lg'` | `'md'` | Row height and font size |
| `accessibilityLabel?` | `string` | — | Accessibility label |
| `testID?` | `string` | — | Test identifier |
```typescript
import { ListItem, Avatar, Icon, Badge } from '@objectifthunes/limestone-sdk';
// Navigation row
<ListItem
title="Edit profile"
subtitle="Update your name, bio, and photo"
leading={<Icon name="user" size="md" color="mutedForeground" />}
showChevron
onPress={() => router.push('/profile/edit')}
/>
// Unread message row
<ListItem
title="Alice Martin"
subtitle="Hey, are you free tomorrow?"
leading={<Avatar name="Alice Martin" size="sm" />}
trailing={<Badge count={2} />}
onPress={() => openConversation('alice')}
/>
RatingStars
A star rating display and optional interactive input. In read-only mode it renders filled/half/empty stars; in interactive mode the user can tap to set a rating.
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | — | Current rating (0–max) |
max? | number | 5 | Total number of stars |
onValueChange? | (v: number) => void | — | When provided, the component is interactive |
allowHalf? | boolean | false | Allow 0.5-increment ratings |
size? | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Star size |
color? | ColorProp | 'warning' | Filled star color |
emptyColor? | ColorProp | 'muted' | Empty star color |
showValue? | boolean | false | Render the numeric value next to the stars |
accessibilityLabel? | string | — | Accessibility label |
testID? | string | — | Test identifier |
import { RatingStars } from '@objectifthunes/limestone-sdk';
import { useState } from 'react';
// Read-only display
<RatingStars value={4.5} allowHalf showValue />
// Interactive rating input
function ReviewForm() {
const [rating, setRating] = useState(0);
return (
<RatingStars
value={rating}
onValueChange={setRating}
size="lg"
/>
);
}
PriceDisplay
A formatted price block with optional original price (strike-through), discount badge, and per-period label. Resolves currency formatting from the locale.
| Prop | Type | Default | Description |
|---|---|---|---|
amount | number | — | Current price |
currency? | string | 'USD' | ISO 4217 currency code |
locale? | string | device locale | BCP 47 locale string for formatting |
originalAmount? | number | — | Original price rendered with a strike-through when provided |
period? | string | — | Billing period label, e.g. '/mo' or 'per year' |
size? | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Font size scale |
showDiscount? | boolean | false | Compute and display the discount percentage badge |
accessibilityLabel? | string | — | Accessibility label |
testID? | string | — | Test identifier |
import { PriceDisplay } from '@objectifthunes/limestone-sdk';
// Sale price with discount badge
<PriceDisplay
amount={7.99}
originalAmount={14.99}
currency="USD"
period="/mo"
showDiscount
/>
// Annual plan
<PriceDisplay
amount={79.99}
currency="EUR"
period="per year"
size="lg"
/>
KeyValueRow
A single-line row displaying a label on the left and a value on the right. Commonly used in detail screens, receipts, and settings summaries.
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Left-side descriptor |
value | string | React.ReactNode | — | Right-side content |
labelVariant? | 'body' | 'caption' | 'label' | 'body' | Typography variant for the label |
valueVariant? | 'body' | 'caption' | 'label' | 'body' | Typography variant for the value |
labelColor? | ColorProp | 'mutedForeground' | Label text color |
valueColor? | ColorProp | 'foreground' | Value text color |
separator? | boolean | true | Render a bottom border |
copyable? | boolean | false | Tap-to-copy the value string |
accessibilityLabel? | string | — | Accessibility label |
testID? | string | — | Test identifier |
import { KeyValueRow, Stack } from '@objectifthunes/limestone-sdk';
function OrderSummary({ order }: { order: Order }) {
return (
<Stack>
<KeyValueRow label="Order ID" value={order.id} copyable />
<KeyValueRow label="Status" value={order.status} />
<KeyValueRow label="Total" value={`$${order.total.toFixed(2)}`} />
<KeyValueRow label="Placed on" value={order.date} separator={false} />
</Stack>
);
}
Chart
A data visualisation component supporting line, bar, and pie chart types. Backed by react-native-svg — no WebView required.
| Prop | Type | Default | Description |
|---|---|---|---|
type | 'line' | 'bar' | 'pie' | — | Chart variant |
data | ChartDataset[] | — | One or more datasets; each has label, values (number[]), and optional color |
labels? | string[] | — | X-axis labels for line/bar charts |
height? | number | 220 | Chart height in pixels |
showGrid? | boolean | true | Render background grid lines (line/bar only) |
showLegend? | boolean | true | Render a colour legend below the chart |
showValues? | boolean | false | Render the numeric value on each bar or data point |
animated? | boolean | true | Animate on mount |
onDataPointPress? | (dataset: number, index: number, value: number) => void | — | Called when a bar or data point is tapped |
accessibilityLabel? | string | — | Accessibility label |
testID? | string | — | Test identifier |
import { Chart } from '@objectifthunes/limestone-sdk';
const revenueData = [
{ label: 'Revenue', values: [4200, 5800, 3900, 7100, 6400, 8200], color: '#7c3aed' },
];
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];
// Line chart
<Chart type="line" data={revenueData} labels={months} showGrid showLegend />
// Bar chart with tap handler
<Chart
type="bar"
data={revenueData}
labels={months}
showValues
onDataPointPress={(_, index, value) => showDetails(months[index], value)}
/>
// Pie chart
const spendData = [
{ label: 'Housing', values: [1800], color: '#7c3aed' },
{ label: 'Food', values: [620], color: '#f97316' },
{ label: 'Travel', values: [430], color: '#06b6d4' },
{ label: 'Other', values: [290], color: '#6366f1' },
];
<Chart type="pie" data={spendData} showLegend />
ActivityRing
A circular progress ring modelled after Apple Watch Activity rings. Supports one to three concentric rings, each representing a separate metric.
| Prop | Type | Default | Description |
|---|---|---|---|
rings | ActivityRingData[] | — | 1–3 rings; each has value (0–100), color, and optional label |
size? | number | 120 | Outer diameter in pixels |
strokeWidth? | number | 12 | Ring stroke width in pixels |
trackColor? | ColorProp | 'muted' | Unfilled track color |
animated? | boolean | true | Animate rings on mount |
showLabels? | boolean | false | Render value labels alongside each ring |
centerContent? | React.ReactNode | — | Content rendered in the centre of the ring stack |
accessibilityLabel? | string | — | Accessibility label |
testID? | string | — | Test identifier |
import { ActivityRing } from '@objectifthunes/limestone-sdk';
<ActivityRing
rings={[
{ value: 78, color: '#ef4444', label: 'Move' },
{ value: 55, color: '#22c55e', label: 'Exercise' },
{ value: 91, color: '#3b82f6', label: 'Stand' },
]}
size={140}
strokeWidth={14}
showLabels
/>
CountdownTimer
A live countdown that ticks down to a target date/time. Renders days, hours, minutes, and seconds with configurable granularity and an animated flip or fade transition between digit changes.
| Prop | Type | Default | Description |
|---|---|---|---|
target | Date | number | — | Target timestamp (Date or Unix ms) |
onComplete? | () => void | — | Called once when the countdown reaches zero |
format? | Array<'days' | 'hours' | 'minutes' | 'seconds'> | all four | Which units to display |
variant? | 'flip' | 'fade' | 'plain' | 'plain' | Digit transition animation |
size? | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Digit block size |
labelPosition? | 'above' | 'below' | 'none' | 'below' | Where to render unit labels |
separatorChar? | string | ':' | Character rendered between units |
accessibilityLabel? | string | — | Accessibility label |
testID? | string | — | Test identifier |
import { CountdownTimer } from '@objectifthunes/limestone-sdk';
// Flash sale ending in 2 hours
const saleEnd = new Date(Date.now() + 2 * 60 * 60 * 1000);
<CountdownTimer
target={saleEnd}
format={['hours', 'minutes', 'seconds']}
variant="flip"
size="lg"
onComplete={() => toast.info('Sale has ended')}
/>
CreditCardDisplay
A styled credit card visualisation showing the masked card number, cardholder name, expiry, and network logo. Supports a flip animation to reveal the CVV on the back face.
| Prop | Type | Default | Description |
|---|---|---|---|
cardNumber | string | — | Full or masked card number; last 4 digits are always shown |
cardholderName | string | — | Name printed on the card |
expiry | string | — | Expiry in MM/YY format |
network? | 'visa' | 'mastercard' | 'amex' | 'discover' | 'unknown' | 'unknown' | Card network for logo rendering |
cvv? | string | — | CVV shown on back face when flipped is true |
flipped? | boolean | false | Show the card back face |
onFlip? | () => void | — | Called when the card is tapped (use to toggle flipped) |
gradient? | string[] | theme primary gradient | Background gradient colours |
width? | number | 320 | Card width in pixels (height auto-calculated at 63.5% ratio) |
accessibilityLabel? | string | — | Accessibility label |
testID? | string | — | Test identifier |
import { CreditCardDisplay } from '@objectifthunes/limestone-sdk';
import { useState } from 'react';
function PaymentMethod({ card }: { card: Card }) {
const [flipped, setFlipped] = useState(false);
return (
<CreditCardDisplay
cardNumber={card.number}
cardholderName={card.holderName}
expiry={card.expiry}
network={card.network}
cvv={card.cvv}
flipped={flipped}
onFlip={() => setFlipped((f) => !f)}
/>
);
}
DataTable
A scrollable table with sortable columns, optional row selection, and pagination controls. Designed for dense data views such as transaction histories and admin dashboards.
| Prop | Type | Default | Description |
|---|---|---|---|
columns | DataColumn<T>[] | — | Column definitions; each has key, header, width?, sortable?, renderCell? |
data | T[] | — | Array of row data objects |
keyExtractor | (row: T) => string | — | Unique key per row |
selectable? | boolean | false | Show row checkboxes and a select-all header checkbox |
selectedKeys? | string[] | — | Controlled set of selected row keys |
onSelectionChange? | (keys: string[]) => void | — | Called when selection changes |
sortKey? | string | — | Currently sorted column key |
sortDirection? | 'asc' | 'desc' | 'asc' | Sort direction |
onSort? | (key: string, direction: 'asc' | 'desc') => void | — | Called when a sortable column header is tapped |
pageSize? | number | — | When set, renders pagination controls |
page? | number | 0 | Current page index (zero-based) |
totalRows? | number | — | Total row count for pagination display |
onPageChange? | (page: number) => void | — | Called when the user navigates to a new page |
emptyComponent? | React.ReactNode | — | Rendered when data is empty |
accessibilityLabel? | string | — | Accessibility label |
testID? | string | — | Test identifier |
import { DataTable } from '@objectifthunes/limestone-sdk';
import type { DataColumn } from '@objectifthunes/limestone-sdk';
import { useState } from 'react';
interface Transaction {
id: string;
date: string;
description: string;
amount: number;
status: 'completed' | 'pending' | 'failed';
}
const columns: DataColumn<Transaction>[] = [
{ key: 'date', header: 'Date', width: 100, sortable: true },
{ key: 'description', header: 'Description', sortable: false },
{ key: 'amount', header: 'Amount', width: 90, sortable: true,
renderCell: (row) => <Text color={row.amount < 0 ? 'destructive' : 'success'}>{`$${Math.abs(row.amount).toFixed(2)}`}</Text> },
{ key: 'status', header: 'Status', width: 100 },
];
function TransactionsTable({ transactions }: { transactions: Transaction[] }) {
const [sort, setSort] = useState<{ key: string; dir: 'asc' | 'desc' }>({ key: 'date', dir: 'desc' });
return (
<DataTable
columns={columns}
data={transactions}
keyExtractor={(t) => t.id}
sortKey={sort.key}
sortDirection={sort.dir}
onSort={(key, dir) => setSort({ key, dir })}
pageSize={20}
selectable
/>
);
}