UI Component Library
Themeable React component library built on shadcn/ui patterns. Provides buttons, cards, charts, page-layout blocks, and design tokens for building cartographic-themed interfaces.
npm: @timetiles/ui
Install
npm install @timetiles/uiUsage
Wrap your application with UIProvider and import components:
import { UIProvider } from "@timetiles/ui/provider";
import { Button } from "@timetiles/ui/components/button";
export default function App() {
return (
<UIProvider>
<Button variant="default">Get Started</Button>
</UIProvider>
);
}Themes
Two built-in themes are available as CSS files:
| Theme | Class | Description |
|---|---|---|
| Cartographic (default) | .theme-cartographic | Warm earth-tone palette inspired by vintage maps |
| Modern | .theme-modern | Clean, contemporary look |
Switching themes
Apply a theme CSS class to the <html> element and import the corresponding CSS file:
import "@timetiles/ui/themes/modern.css";
export default function Layout({ children }) {
return (
<html className="theme-modern">
<body>{children}</body>
</html>
);
}Creating a custom theme
Themes work by redefining semantic CSS variables. Components never reference palette values directly — they use tokens like bg-primary and text-foreground. Swap the tokens and every component adapts automatically.
/* themes/ocean.css */
.theme-ocean {
--background: oklch(0.97 0.01 220);
--foreground: oklch(0.2 0.02 230);
--primary: oklch(0.45 0.15 240);
--primary-foreground: oklch(0.97 0.01 220);
/* ... see THEMING.md for the full token list */
}Apply it in your layout:
import "./themes/ocean.css";
<html className="theme-ocean">Then configure the UIProvider so charts and maps also match:
<UIProvider
resolveTheme={() => "light"}
lightChartTheme={{ itemColor: "#2563eb", emphasisColor: "#1e40af" }}
mapColors={{
mapPoint: "#2563eb",
mapClusterGradient: ["#dbeafe", "#93c5fd", "#3b82f6", "#1d4ed8", "#1e3a5f"],
mapStroke: "#ffffff",
}}
>
{children}
</UIProvider>For the complete list of semantic tokens (core, chart, palette, sidebar, texture, fonts), see the THEMING.md reference.
Exported Components
UI Components
Button, Card, Checkbox, Collapsible, ConfirmDialog, ContentState, DataTable, DetailsGrid, Dialog, DropdownMenu, Input, Label, Select, Table, Tabs, Textarea
Page Layout
CallToAction, Features, Footer, Header, HeaderActions, HeaderBrand, HeaderNav, Hero, MobileNavDrawer, NewsletterCta, NewsletterForm, Stats, Testimonials, Timeline
Charts
BaseChart, BarChart, TimeHistogram, ChartEmptyState, ChartSkeleton
Icons
BlueskyIcon, BusinessIcon, EmailIcon, FacebookIcon, GitHubIcon, InsightsIcon, InstagramIcon, LinkedInIcon, LocationIcon, MapIcon, MastodonIcon, SupportIcon, TimelineIcon, XIcon, YouTubeIcon
UIProvider API
import { UIProvider } from "@timetiles/ui/provider";
<UIProvider
resolveTheme={() => "light" | "dark"}
lightChartTheme={ChartTheme}
darkChartTheme={ChartTheme}
mapColors={MapColors}
onNewsletterSubmit={(email, data) => Promise<void>}
>| Prop | Purpose |
|---|---|
resolveTheme | Returns "light" or "dark". Used by chart components. Defaults to "light". |
lightChartTheme / darkChartTheme | Override ECharts colors for each mode. Falls back to built-in defaults. |
mapColors | Override MapLibre point and cluster visualization colors. |
onNewsletterSubmit | Custom newsletter handler. Falls back to POST /api/newsletter/subscribe. |
ChartTheme
interface ChartTheme {
backgroundColor?: string;
textColor?: string;
axisLineColor?: string;
splitLineColor?: string;
itemColor?: string | string[];
tooltipBackground?: string;
tooltipForeground?: string;
emphasisColor?: string;
}MapColors
interface MapColors {
mapPoint: string;
mapClusterGradient: readonly [string, string, string, string, string];
mapStroke: string;
}