paperplane-react
v0.1.1
Published
A React UI component library with a paper/tactile feel — no Tailwind required
Maintainers
Readme
paperplane-react
A React UI component library with a paper and ink aesthetic — tactile, warm, and classic. Built with zero Tailwind dependency, works in any React environment.
✦ Why paperplane-react?
- No Tailwind required — styles are self-contained via CSS custom properties injected at runtime. No PostCSS config, no
contentglob, no purge issues. - Works everywhere — React 17+, Next.js (Pages & App Router), Remix, Vite, Create React App, etc.
- SSR-safe — includes
<PaperStyleTag />for Server Components andPaperProviderfor client-side injection. - Fully typed — TypeScript-first with complete prop types.
- Tiny bundle — zero runtime dependencies besides React itself.
Installation
npm install paperplane-react
# or
yarn add paperplane-react
# or
pnpm add paperplane-reactThat's it. No Tailwind config, no CSS imports, no PostCSS plugins.
Setup
React / Vite / CRA
Wrap your app with PaperProvider once:
// main.tsx or App.tsx
import { PaperProvider } from 'paperplane-react';
export default function App() {
return (
<PaperProvider>
<YourApp />
</PaperProvider>
);
}Next.js App Router (v13+)
Use PaperStyleTag in your root layout (Server Component) and PaperProvider for client-side interactivity:
// app/layout.tsx
import { PaperStyleTag, PaperProvider } from 'paperplane-react';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<PaperStyleTag />
</head>
<body>
<PaperProvider>
{children}
</PaperProvider>
</body>
</html>
);
}Next.js Pages Router
// pages/_app.tsx
import { PaperProvider } from 'paperplane-react';
import type { AppProps } from 'next/app';
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<PaperProvider>
<Component {...pageProps} />
</PaperProvider>
);
}Using Custom Fonts
PaperUI includes the Dareo custom font (available in both OTF and TTF formats). Use it to add a distinctive, handcrafted feel to your app.
Option 1: Direct CSS Import (Recommended)
// In your main app file or root layout
import 'paperplane-react/dist/fonts/fonts.css';
// Now use the font in any component
<Heading style={{ fontFamily: 'Dareo, serif' }}>Custom Heading</Heading>
// Or use the CSS variable
<Text style={{ fontFamily: 'var(--paper-font-dareo)' }}>Beautiful text</Text>Option 2: Via PaperProvider
Pass the loadFonts prop to automatically inject font declarations:
<PaperProvider loadFonts={true}>
<YourApp />
</PaperProvider>
// Then use in any component
<Heading>This uses Dareo if you set fontFamily</Heading>Or set a custom font globally:
<PaperProvider fontFamily="Dareo, serif" loadFonts={true}>
<YourApp />
</PaperProvider>Option 3: Manual Function Call
import { injectPaperFonts } from 'paperplane-react';
// In useEffect or component
useEffect(() => {
injectPaperFonts();
}, []);Components
<Paper>
The base surface. Like a sheet of paper on a desk.
import { Paper } from 'paperplane-react';
<Paper elevation="md" padding="lg">
Content on paper
</Paper>
<Paper elevation="lg" aged>
An aged, yellowed sheet
</Paper>
<Paper elevation="sm" torn>
With a torn bottom edge
</Paper>Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| elevation | "flat" \| "sm" \| "md" \| "lg" | "sm" | Shadow depth |
| padding | "none" \| "sm" \| "md" \| "lg" | "md" | Inner padding |
| aged | boolean | false | Cream/yellowed background |
| torn | boolean | false | Torn bottom edge effect |
| as | React.ElementType | "div" | Render as any HTML element |
<Button>
Tactile buttons that feel like they're pressed into paper.
import { Button } from 'paperplane-react';
<Button>Default</Button>
<Button variant="solid" color="primary">Save Document</Button>
<Button variant="outline" color="danger">Delete</Button>
<Button variant="ghost">Cancel</Button>
<Button variant="ink" color="primary">Ink Stamp Style</Button>
<Button loading>Saving...</Button>
<Button size="lg" leftIcon={<PencilIcon />}>Write</Button>Props:
| Prop | Type | Default |
|------|------|---------|
| variant | "solid" \| "outline" \| "ghost" \| "ink" | "solid" |
| size | "sm" \| "md" \| "lg" | "md" |
| color | "default" \| "primary" \| "danger" \| "warning" | "default" |
| loading | boolean | false |
| fullWidth | boolean | false |
| leftIcon | ReactNode | — |
| rightIcon | ReactNode | — |
<Card> / <CardHeader> / <CardBody> / <CardFooter>
Structured paper cards with optional accent lines.
import { Card, CardHeader, CardBody, CardFooter, Button } from 'paperplane-react';
<Card elevation="md" accent="primary" hoverable>
<CardHeader>
<h3>Letter of Intent</h3>
</CardHeader>
<CardBody>
Content of the card...
</CardBody>
<CardFooter>
<Button size="sm">Sign</Button>
<Button size="sm" variant="ghost">Decline</Button>
</CardFooter>
</Card>Card Props:
| Prop | Type | Default |
|------|------|---------|
| elevation | "flat" \| "sm" \| "md" \| "lg" | "sm" |
| accent | "none" \| "primary" \| "danger" \| "warning" \| "info" \| "brown" | "none" |
| aged | boolean | false |
| hoverable | boolean | false |
<Input>
import { Input } from 'paperplane-react';
<Input label="Full Name" placeholder="John Doe" fullWidth />
<Input label="Email" type="email" error="Invalid email address" />
<Input label="Amount" leftAddon="$" rightAddon="USD" />
<Input underline placeholder="Underline style..." /><Textarea>
import { Textarea } from 'paperplane-react';
<Textarea label="Your Letter" rows={6} fullWidth showCount maxLength={500} />
<Textarea lined placeholder="Lined paper style..." rows={5} /><Select>
import { Select } from 'paperplane-react';
<Select label="Category" fullWidth>
<option value="">Choose one…</option>
<option value="a">Option A</option>
<option value="b">Option B</option>
</Select><Badge>
import { Badge } from 'paperplane-react';
<Badge>Draft</Badge>
<Badge color="primary">Published</Badge>
<Badge variant="stamp" color="danger">Rejected</Badge>
<Badge variant="outline" color="warning">Pending</Badge><Alert>
import { Alert } from 'paperplane-react';
<Alert title="Note" color="info" dismissible>
Your document has been saved automatically.
</Alert>
<Alert color="danger" title="Error" icon={<WarningIcon />}>
Something went wrong. Please try again.
</Alert><Checkbox>
import { Checkbox } from 'paperplane-react';
<Checkbox label="I agree to the terms" />
<Checkbox label="Remember me" defaultChecked />
<Checkbox indeterminate label="Select all" /><Spinner>
import { Spinner } from 'paperplane-react';
<Spinner />
<Spinner size="lg" color="primary" />Typography: <Heading>, <Text>, <Divider>
import { Heading, Text, Divider } from 'paperplane-react';
<Heading as="h1" size="3xl">The Grand Chronicle</Heading>
<Heading as="h2" size="xl">Chapter One</Heading>
<Text>Body text in a warm serif typeface.</Text>
<Text size="sm" muted>A quiet footnote.</Text>
<Text italic>An italicized phrase.</Text>
<Text mono>monospace_text()</Text>
<Divider />
<Divider label="or" />
<Divider ornament /> {/* renders ✦ */}Theming & Customization
PaperUI exposes all design decisions as CSS custom properties. Override any of them in your own CSS:
:root {
/* Change primary color from forest green to navy */
--paper-primary: #1a3a5c;
--paper-primary-light: #2a5a8c;
--paper-primary-dark: #0f2240;
--paper-primary-bg: #f0f4f8;
/* Use a different serif font */
--paper-font-serif: 'Lora', Georgia, serif;
/* Adjust paper base color */
--paper-white: #fffdf8;
--paper-cream: #f8f3e8;
}Or via PaperProvider:
<PaperProvider fontFamily="'Lora', Georgia, serif">
<App />
</PaperProvider>Full list of CSS variables
See PAPER_CSS_VARS export for the complete token list, or inspect data-paperui="tokens" style tag in DevTools.
No-Tailwind Guarantee
PaperUI uses only:
- CSS custom properties (
:root { --paper-... }) injected once into<head> - Inline styles on components via React's
styleprop - A minimal
<style>tag per component for keyframe animations
There is no dependency on Tailwind CSS, PostCSS, or any CSS framework. It will never conflict with or require Tailwind configuration. If you're using Tailwind in your project, PaperUI sits alongside it without any issues.
Browser Support
All modern browsers. CSS custom properties are supported in Chrome 49+, Firefox 31+, Safari 9.1+, Edge 15+.
License
MIT
