comixa-ui
v0.1.5
Published
Comixa UI — comic-themed React components built with Tailwind CSS
Maintainers
Readme
Comic-themed React components built with Tailwind CSS. Hard shadows, ink borders, punchy motion.
import {
Button,
ComicPanel,
Features,
Gallery,
PageTransition,
toast,
ToastProvider,
} from "comixa-ui";Install
npm i comixa-uiPeer deps (required in your app):
npm i react react-dom tailwindcssSetup Tailwind
1. Use the Comixa UI preset
// tailwind.config.js
const comixaPreset = require("comixa-ui/preset");
/** @type {import('tailwindcss').Config} */
module.exports = {
presets: [comixaPreset],
content: [
"./src/**/*.{js,ts,jsx,tsx}",
// IMPORTANT: scan the package so Tailwind keeps comic utility classes
"./node_modules/comixa-ui/dist/**/*.{js,mjs,cjs}",
],
theme: {
extend: {},
},
plugins: [],
};ESM:
import comixaPreset from "comixa-ui/preset";
export default {
presets: [comixaPreset],
content: [
"./src/**/*.{js,ts,jsx,tsx}",
"./node_modules/comixa-ui/dist/**/*.{js,mjs,cjs}",
],
};2. Optional fonts
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
href="https://fonts.googleapis.com/css2?family=Bangers&family=Comic+Neue:wght@400;700&display=swap"
rel="stylesheet"
/>Usage
import { Button, Input, Badge } from "comixa-ui";
export function Example() {
return (
<div className="flex flex-col gap-4 bg-paper p-8">
<Badge variant="yellow">New</Badge>
<Button variant="pop" effect="pop">
Pow!
</Button>
<Button variant="primary" size="lg">
Continue
</Button>
<Button variant="danger" effect="shake">
Boom
</Button>
<Input placeholder="Hero name..." />
<Input state="error" placeholder="Try again..." />
<Input variant="filled" state="success" placeholder="Looks good" />
</div>
);
}Components
| Preview | Component | What it is | |:-------:|-----------|------------| | | Button | CTAs with variants, loading, icon buttons | | | Badge | Status tags and labels | | | Sticker | Tilted promo stickers | | | Input | Ink-bordered text fields | | | Select | Custom listbox dropdown | | | Checkbox | Checkable options | | | Radio | Single-choice radios | | | Toast | Notifications | | | Divider | Comic rules | | | Stats | Metric tiles | | - | Ribbon | Promo ribbons, tickets, burst labels | | - | ComicPage / ComicPanel | Comic grids and cover-grade hero panels | | - | Gallery | Grid, featured, and draggable horizontal image strips | | - | PageTransition | Route/content enter effects | | - | ComicLoader | Comic loading indicators | | - | ComicReveal | Replayable section reveal animations | | - | ComicCursor | Global cursor follower with trailing dots | | - | Features | Feature grids and comic highlight cards |
Button
| Prop | Values | Default |
|------|--------|---------|
| variant | pop primary danger success outline ghost | pop |
| size | sm md lg | md |
| loading | true / false | false |
| icon | true / false — square icon-only button | false |
| effect | none pop shake wiggle | none |
<Button loading>Saving…</Button>
<Button icon aria-label="Add">
<PlusIcon />
</Button>Input
| Prop | Values | Default |
|------|--------|---------|
| variant | default ghost filled | default |
| inputSize | sm md lg | md |
| state | default error success | default |
Badge
| Prop | Values | Default |
|------|--------|---------|
| variant | yellow red blue green pink orange outline ink soft | yellow |
| size | sm md lg | md |
Select
Custom listbox (not a native <select>). Arrow stays on the right.
<Select
value={hero}
onValueChange={setHero}
variant="pop"
placeholder="Pick a hero"
options={[
{ value: "zap", label: "Captain Zap" },
{ value: "boom", label: "Boom Knight" },
]}
/>| Prop | Values / notes | Default |
|------|--------|---------|
| options | { value, label, disabled? }[] | required |
| value / defaultValue | string | — |
| onValueChange | (value: string) => void | — |
| variant | default ghost filled pop | default |
| selectSize | sm md lg | md |
| state | default error success | default |
| classNames | root trigger value placeholder icon list option optionActive | — |
Checkbox / Radio / Switch
| | Checkbox / Radio | Switch |
|--|--|--|
| variant | default primary danger success pink | same |
| size prop | checkboxSize / radioSize: sm md lg | switchSize |
| extras | label | checked, onCheckedChange, label |
RadioGroup wraps radios (orientation: horizontal | vertical).
Avatar
| Prop | Values | Default |
|------|--------|---------|
| variant | default yellow blue red green pink ink | default |
| size | sm md lg xl | md |
| shape | rounded square circle | rounded |
Also: src, name (initials), fallback, plus AvatarGroup.
Tooltip
<Tooltip content="Hello!" side="top" variant="pop">
<Button>Hover me</Button>
</Tooltip>| Prop | Values | Default |
|------|--------|---------|
| side | top right bottom left | top |
| variant | default pop paper danger success blue | default |
| delay | ms | 120 |
SpeechBubble
Classic comic dialogue bubble (no boom/explosion).
<SpeechBubble shape="speech" tone="cream" tail="bottomLeft">
Hey! Did you see that?
</SpeechBubble>
<SpeechBubble shape="thought" tail="bottomRight">
Hmm…
</SpeechBubble>| Prop | Values | Default |
|------|--------|---------|
| shape | speech thought | speech |
| tone | default pop danger blue pink cream | default |
| size | sm md lg | md |
| tail | bottomLeft bottomRight bottom none | bottomLeft |
ComicPage
Comic-strip grid. layout="2-1" = two panels on top, one full-width below.
ComicPanel can also be used on its own as a cover-grade hero panel.
<ComicPage layout="2-1">
<ComicPanel caption="1">Top left</ComicPanel>
<ComicPanel caption="2">Top right</ComicPanel>
<ComicPanel variant="alert">Wide bottom</ComicPanel>
</ComicPage>
<ComicPanel variant="hero" shadow="xl" halftone tilt hover>
<ComicHero />
</ComicPanel>Layouts: 1 1-1 2 2-1 1-2 2-2 3 1-1-1
Panel variants: default cream sky alert pop night hero
Panel shadow: none sm md lg xl
Panel flags: halftone, tilt, hover
SoundBadge
<SoundBadge variant="pow" />
<SoundBadge variant="boom" burst />
<SoundBadge variant="bam" word="SNAP!" />Variants: pow bam wow boom zap crash wham bang kapow splash
Divider
variant: solid dashed zigzag dots burst — optional label
Sticker
variant colors + tilt (none left right wild) + shape (square circle ticket)
Ribbon
Comic promo labels for launch pages, tickets, corners, and bursts.
<Ribbon variant="banner">New issue</Ribbon>
<Ribbon variant="corner" tilt="right">Hot</Ribbon>
<Ribbon variant="ticket">Limited</Ribbon>
<Ribbon variant="burst">Pow</Ribbon>| Prop | Values | Default |
|------|--------|---------|
| variant | banner corner ticket burst | banner |
| size | sm md lg | md |
| tilt | none left right | none |
Card
| Prop | Values | Default |
|------|--------|---------|
| variant | default cream pop panel danger speech outline | default |
| padding | none sm md lg | md |
| effect | none pop wiggle | none |
Parts: CardHeader, CardTitle, CardDescription, CardContent, CardFooter.
<Card variant="speech">
<CardHeader>
<CardTitle>Caption</CardTitle>
<CardDescription>A comic speech bubble.</CardDescription>
</CardHeader>
<CardContent>Pow!</CardContent>
</Card>Dialog (Modal)
| Prop (root) | Values | Default |
|------|--------|---------|
| open | boolean | required |
| onOpenChange | (open: boolean) => void | required |
| Prop (DialogContent) | Values | Default |
|------|--------|---------|
| variant | default cream boom alert success panel | default |
| size | sm md lg | md |
| effect | none pop shake | pop |
const [open, setOpen] = useState(false);
<>
<Button onClick={() => setOpen(true)}>Open</Button>
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent variant="boom">
<DialogHeader>
<DialogTitle>Boom!</DialogTitle>
<DialogDescription>Something exploded (in a good way).</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button onClick={() => setOpen(false)}>Got it</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</>Navbar
Customizable compound navbar — every part accepts className.
| Prop (Navbar) | Values | Default |
|------|--------|---------|
| variant | default cream pop panel ink transparent | default |
| position | static sticky fixed | static |
Parts: NavbarBrand, NavbarContent, NavbarMenu, NavbarLink, NavbarActions, NavbarItem, NavbarToggle, NavbarMobileMenu.
<Navbar variant="pop" position="sticky" className="px-6">
<NavbarBrand href="/" className="text-ink">Comixa</NavbarBrand>
<NavbarContent>
<NavbarMenu className="gap-2">
<NavbarLink href="/" active className="shadow-comic">Home</NavbarLink>
<NavbarLink href="/docs">Docs</NavbarLink>
</NavbarMenu>
</NavbarContent>
<NavbarActions className="gap-3">
<Button size="sm">Join</Button>
<NavbarToggle className="bg-comic-yellow" />
</NavbarActions>
<NavbarMobileMenu className="bg-paper-cream">
<NavbarLink href="/">Home</NavbarLink>
<NavbarLink href="/docs">Docs</NavbarLink>
</NavbarMobileMenu>
</Navbar>Toast
Wrap your app with ToastProvider, then call toast() anywhere.
<ToastProvider
position="bottom-right"
duration={3500}
closable
viewportClassName="p-2"
>
<App />
</ToastProvider>
toast({ title: "Pow!", description: "Saved.", variant: "success" });
toast.success("Done");
toast.danger("Oops", "Try again");
// Duration (ms). 0 or Infinity = stay until dismissed
toast({ title: "Quick", duration: 1000 });
toast({ title: "Sticky", duration: 0 });
// Hide the × button
toast({ title: "No close", closable: false, duration: 3000 });
// Per-toast position (overrides provider default)
toast({ title: "Top!", position: "top-center" });
toast.info("Hi", "From the corner", { position: "top-left", duration: 5000 });
// Custom classes per slot
toast({
title: "Styled",
className: "max-w-sm",
classNames: {
root: "rounded-2xl",
title: "text-comic-red",
description: "opacity-80",
close: "bg-comic-yellow",
},
});| | |
|--|--|
| Variant | default pop success danger info |
| Position | top-left top-right top-center bottom-left bottom-right bottom-center |
| duration | ms number — 0 / Infinity keeps toast open |
| closable | true / false — show/hide × button |
Testimonials / Features / Pricing / FAQ / Stats
Compound marketing sections:
import {
Features,
Feature,
Testimonials,
Testimonial,
Pricing,
PricingTier,
FAQ,
FAQItem,
Stats,
Stat,
Button,
Avatar,
} from "comixa-ui";
<Testimonials columns={3}>
<Testimonial
quote="Pow!"
author="Captain Zap"
role="Hero"
rating={5}
avatar={<Avatar name="CZ" size="sm" shape="circle" />}
/>
</Testimonials>
<Features columns={3}>
<Feature
variant="yellow"
title="Hero panels"
description="Cover-grade panels with halftone and hard shadows."
/>
<Feature
variant="blue"
title="Composable"
description="Stack sections without leaving the comic visual language."
/>
</Features>
<Pricing>
<PricingTier
name="Hero"
price="$19"
period="mo"
featured
badge="Popular"
features={["All components"]}
cta={<Button className="w-full">Start</Button>}
/>
</Pricing>
<FAQ type="single" defaultValue="a">
<FAQItem value="a" title="What is Comixa?">
Comic React UI with Tailwind.
</FAQItem>
</FAQ>
<Stats columns={4}>
<Stat value="12k+" label="Heroes" animate />
</Stats>Feature variants: default yellow blue burst outline
Feature alignment: left center
Features columns: 1 2 3 4
Gallery
Image galleries for covers, portfolio work, products, and proof sections.
const items = [
{
src: "/covers/neon.jpg",
alt: "Neon skyline",
title: "Neon Alley",
description: "Campaign cover.",
badge: "New",
},
{
src: "/covers/road.jpg",
alt: "Desert road",
title: "Road Cut",
badge: "Hot",
},
];
<Gallery variant="featured" items={items} />
<Gallery variant="strip" items={items} />| Prop | Values / notes | Default |
|------|--------|---------|
| variant | grid strip featured | grid |
| items | { src, alt, title?, description?, badge? }[] | required |
| framed | true / false | true |
variant="featured" makes the first image the featured card.variant="strip" supports horizontal wheel scrolling and mouse drag scrolling.
PageTransition
Replayable content/page enter transitions.
<PageTransition variant="panel-swipe" transitionKey={routeId}>
<YourPage />
</PageTransition>Variants: panel-swipe burst flip speed-lines
Props: transitionKey, duration, easing, padding
ComicLoader
<ComicLoader variant="dots" />
<ComicLoader variant="burst" label="Zap" tone="red" />Variants: dots burst panel speech
Tones: yellow blue red green pink
ComicReveal
Replayable section reveals.
<ComicReveal variant="panel-wipe" revealKey={id}>
<Card>Fresh panel</Card>
</ComicReveal>Variants: pop slide-up panel-wipe spotlight
Props: revealKey, delay, duration
ComicCursor
Global cursor follower with trailing dots. Mount it once on a page.
<ComicCursor variant="dot" trailCount={6} />
<button data-comixa-cursor-zone>
Hovering this grows and fades the follower
</button>Variants: dot ring spark
Props: enabled, hideNativeCursor, size, trailCount
Animated text
repeat controls how many times the animation plays (Infinity = forever, default).
import { LetterReveal, Typewriter, ComicText, Highlight } from "comixa-ui";
<LetterReveal repeat={Infinity}>Boom Town</LetterReveal>
<LetterReveal repeat={3}>Three times</LetterReveal>
<Typewriter speed={40} repeat={Infinity}>Typing with ink…</Typewriter>
<Typewriter repeat={1}>Once only</Typewriter>
<ComicText effect="pop" tilt="left" repeat={Infinity}>Kapow!</ComicText>
<p>
Ship <Highlight tone="yellow" repeat={Infinity}>faster</Highlight> pages.
</p>Background
import {
DotsBackground,
GridBackground,
ExplosionBackground,
ComicPaperBackground,
} from "comixa-ui";
<DotsBackground className="rounded-xl p-8" intensity="md">
Hero copy
</DotsBackground>Variants: dots grid lines pattern explosion comic-paper
Tone: paper cream yellow ink · Intensity: sm md lg
Design tokens (via preset)
- Colors:
ink,paper,comic.yellow|red|blue|green|pink|orange - Shadows:
shadow-comic,shadow-comic-sm,shadow-comic-lg - Animations:
animate-comic-pop,animate-comic-shake,animate-comic-wiggle,animate-comic-dialog-in,animate-comic-overlay-in,animate-comic-letter-in,animate-comic-caret-blink,animate-comic-highlight-wipe - Fonts:
font-comic,font-body
