pindoba
A multi-framework component system for Astro, React, Svelte, and Vue.
This is an alpha, and the current focus is deliberate: functionality and architecture — writing a component once and running it natively in four frameworks, without rewriting the logic for each one. The visual design pass is a planned, later milestone, so treat what you see here as working machinery rather than finished styling.
where we are
Still early, on purpose
Nine milestones, three of them shipped or in flight. The roadmap is the honest version of what this system is and isn't yet.
Function first, finish later
01 · milestone
Develop all components and test them
The first and most important focus: build the full component set across every framework, with parity and tests, so we can prove the ideas actually work before we refine them.
02 · milestone
Design pass
With the components working, raise the bar on how they look and feel — a deliberate visual and interaction polish across the system.
03 · milestone
Prove it in real-world cases
Make sure everything holds up in real applications — not just in isolation — so the ideas are validated against actual usage.
+6 more milestones
See the full roadmap
Every milestone, what it covers and where it stands — the long version of this strip.
architecture
One core, four frameworks
A component is written once as framework-agnostic logic and a style recipe. Each framework only contributes a thin template — and keeps its own idioms while doing it.
the shape of a component
Three packages, one component
01 · layer
styles
@pindoba/styles-*A Panda sva() recipe per component. One slot per rendered element, no framework anywhere near it.
02 · layer
core
@pindoba/core-*connect*() runs the logic, reads the recipe and returns one ready-to-spread prop bag per slot. This is the part that is written once.
03 · layer
ui
@pindoba/{astro,react,svelte,vue}-*Four thin templates that render the tree and spread the bags. A normalizer adapts naming at the boundary — class → className, and so on.
Behaviour, accessibility wiring and styling live in the two framework-agnostic layers. What each framework keeps is what it is actually good at: Svelte gets runes and bind:this, React gets hooks and refs, Astro gets slots, Vue gets its own reactivity — none of them re-implement the component.
one component · four frameworks
Button, everywhere
import Button from "@pindoba/astro-button";
<Button emphasis="primary">Create</Button>
Same props, same slots, same accessibility wiring in all four — only the import line and the template syntax change. The variants above (emphasis, size,feedback) come from one recipe, and the behaviour from one connectButton().
written once · spread everywhere
The core does the work
// packages/core/button/src/connect-button.ts
export function connectButton(options) {
const classes = buttonStyles.raw({ emphasis, size });
return defineConnect({
slots: { root: { ...rest, class: css(classes.root) } },
passThrough,
});
}
const api = $derived(connectButton(rest));
<button bind:this={element} {...api.rootProps}>
{@render children?.()}
</button>
const api = connectButton({ ...rest, class: className });
<button {...reactProps(api.rootProps)}>{children}</button>
const api = connectButton({ ...rest, class: className });
<button {...api.rootProps}><slot /></button>
Adding an attribute or fixing an ARIA string is a one-file change in core — every framework picks it up. Read how it is being rolled out on the roadmap.
composition
Compose real screens
Not a swatch board: these are the same primitives assembled into the layouts a product actually ships — a triage board, card surfaces, an app shell, an activity feed.
issue triage
Tab group · board, list, timeline
To do
#31
Add dark mode toggle
#28
Update onboarding flow
In progress
#42
Fix sidebar collapse on mobile
#39
Migrate to new API endpoints
Done
#36
Implement SSO login
#33
Resolve memory leak in worker
card surfaces
Banner-driven headers, panel-backed footers
Changelog · v0.2
Design tokens, refreshed
Jun 3 · 4 min read
Every color, space, and type value now flows from one source of truth — tweak a token and it ripples through every framework.
Workspace
Pro is live
Unlimited projects & priority builds
A filled header works as a hero strip — it carries its own palette while the body stays in the card's neutral surface.
Order #4,128
Payment received
Visa ·· 4242
A feedback-tinted footer doubles as a confirmation strip — success here, but warning and danger read the same way.
navigation shell
Sidebar, scope, active state
activity feed
Recent events
hana opened #47 — reduce input layout shift
ci build failed on main — lint step
ci deploy to staging succeeded
alex commented on #42
mel merged #44 — tab group focus order
forms & input
Data going in
Fields, validation states and selection controls, wired through the same headless core in every framework.
form · validation
Inputs, select, radio, checkbox, button
control states
Every control, every state
- Pick a milestone before saving.
feedback & overlays
Telling the user what happened
Alerts, toasts, popovers and tooltips — the parts of an interface that exist only to answer 'what just changed?'.
system feedback
Banner, alerts, accordion, popover, loading
alpha · pre-release
Feedback welcome — open an issue or ping us on GitHub.
Deploy complete
Quota 80%
Build failed
the catalogue
Every component, every framework
Each entry ships as an Astro, React, Svelte and Vue package over one shared core. The list is generated from the docs, so it can never fall behind.