@caelum-house/design
v0.4.0
Published
Caelum design system — shared tokens, brand assets, and React components for the website and the React Native app.
Readme
@caelum-house/design
Caelum design system. Tokens, brand assets, and components, shared between the marketing site and the React Native app.
Status: v0.1 scaffold — Mark, Spinner, and the full token set. Roadmap below for what comes next.
Install
The package is published to GitHub Packages under the @caelum-house scope.
Authenticate once, then install like any npm package.
# .npmrc (or ~/.npmrc) — once
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
@caelum-house:registry=https://npm.pkg.github.com
# Then in either app
pnpm add @caelum-house/design
# or
npm install @caelum-house/designPeer dependencies:
react≥ 18 (always)react-dom≥ 18 (web only)react-native≥ 0.73 (RN only)react-native-svg≥ 15 (RN only)react-native-reanimated≥ 3 (RN only — needed for theSpinner)
Use
import {
ThemeProvider,
Mark, Spinner, Card, CTAPrimary,
Heading, Body, Eyebrow,
} from '@caelum-house/design';
// Wrap your app once
<ThemeProvider initial="dark">
<App />
</ThemeProvider>
// Then use components anywhere — they read the active theme from context
<Heading size="hero">Built to glow</Heading>
<Body>Caelum runs everything.</Body>
<Card variant="elevated">
<Mark size={48} />
<CTAPrimary href="/app">Open app</CTAPrimary>
</Card>Web and RN resolve to different files automatically:
- Vite / Webpack / esbuild →
*.tsx - Metro (RN) →
*.native.tsx
via React Native's platform-extension resolver and the react-native
export condition in package.json.
Components included
| Component | Web | RN |
|---|---|---|
| Mark — brand C-arc | ✓ | ✓ |
| Spinner — orbit-ring loader | ✓ | ✓ (via Reanimated) |
| Eyebrow / Label / Meta — tracked uppercase | ✓ | ✓ |
| Heading — display/hero/section sizes | ✓ | ✓ (fixed pixels on native) |
| Body — sm/md/lg prose | ✓ | ✓ |
| CTAPrimary / CTASecondary — buttons | ✓ | ✓ |
| Card — default/elevated/soft surface | ✓ | ✓ |
| ThemeProvider + useTheme() | ✓ | ✓ (same file) |
Structure
src/
tokens/ colors, typography, space, motion (platform-agnostic)
brand/ SVG path constants, mark geometry (platform-agnostic)
components/
Mark.tsx ⇢ web build
Mark.native.tsx ⇢ RN build
Spinner.tsx ⇢ web build
Spinner.native.tsx ⇢ RN build
*.stories.tsx ⇢ shared stories (one file, both Storybooks)Develop
pnpm install
pnpm storybook # http://localhost:6006
pnpm build # dist/ via tsup
pnpm typecheck
pnpm lintAdd a story for any new component (Foo.stories.tsx) — it'll auto-pick-up.
Release
Uses Changesets.
pnpm changeset # describe the change, pick a bump
git commit && git push
# CI opens a "Version Packages" PR — merge it to publishReleases go to GitHub Packages on push to main once the version PR is
merged. Consumers update at their own pace via pnpm update.
Roadmap
- [ ] React Native Storybook in the mobile app, consuming the same stories
- [ ] Refraction sweep on the mark (web + RN via Reanimated path interpolation)
- [ ] Button, Pill, Card components
- [ ] Typography primitives (
<Title>,<Body>,<Eyebrow>) - [ ] Theme provider — light/dark + system follow
- [ ] Icon set (lucide-react / lucide-react-native wrappers)
- [ ] Chromatic preview deploy
Why a separate repo
The website and the mobile app are deployed independently and move at different speeds, but they need a single source of truth for what the brand looks like. Pulling the design system out as its own package means both apps consume the same versioned artefact, design changes get a proper review, and Storybook serves as the canonical visual reference.
Components that cannot be expressed identically on both platforms (CSS effects with no RN equivalent) live in platform-specific files and stay visually in sync via the shared story specs.
