pindoba

A multi-framework component system for Astro, React, Svelte, and Vue.

alpha4 frameworks47 componentsMIT

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.

01

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

Everything on this page works, but almost nothing on it is visually final. The current milestone is building the full component set across Astro, React, Svelte and Vue with real tests and cross-framework parity. The deliberate design pass — spacing, motion, the last 20% of polish — is a separate, planned milestone that starts once the machinery is proven.

01 · milestone

in flight

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

planned

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

planned

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.

02

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

astro

        
        
        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

ts

        
        
        // 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,
      
        
        
          });
      
        
        
        }
      
svelte
svelte

        
        
        const api = $derived(connectButton(rest));
      
        
        
        
      
        
        
        <button bind:this={element} {...api.rootProps}>
      
        
        
          {@render children?.()}
      
        
        
        </button>
      
react
tsx

        
        
        const api = connectButton({ ...rest, class: className });
      
        
        
        
      
        
        
        <button {...reactProps(api.rootProps)}>{children}</button>
      
astro
astro

        
        
        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.

03

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

24 open5 in progress

To do

8

#31

Add dark mode toggle

Feature
ACAlice Chen

#28

Update onboarding flow

Enhancement
BMBob Martin

In progress

5

#42

Fix sidebar collapse on mobile

Bug
CWCarol White

#39

Migrate to new API endpoints

Task
ACAlice Chen

Done

11

#36

Implement SSO login

Feature
BMBob Martin

#33

Resolve memory leak in worker

Bug
CWCarol White

card surfaces

Banner-driven headers, panel-backed footers

one component, many shapes

Changelog · v0.2

New

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.

Helena Rocha
Helena RochaDesign Systems

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.

$12 / seat

Order #4,128

Payment received

$48.00

Visa ·· 4242

A feedback-tinted footer doubles as a confirmation strip — success here, but warning and danger read the same way.

Secured

navigation shell

Sidebar, scope, active state

PU
pindoba/ui
5 sections · 15 open

activity feed

Recent events

last 24h
HP

hana opened #47 — reduce input layout shift

2m
CB

ci build failed on main — lint step

14m
CB

ci deploy to staging succeeded

1h
AR

alex commented on #42

3h
MT

mel merged #44 — tab group focus order

yesterday
04

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

Draft
AC
  • Alice Chen

  • Bob Martin

  • Carol White

  • Low

  • Medium

  • High

  • Critical

control states

Every control, every state

05

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

v0.1.4 shipped to staging with zero downtime.

Quota 80%

You've used 80% of your monthly API allowance.

Build failed

Pipeline error on step "lint". See the run logs.

syncing 3 of 7 repos…
keyboard
new issue⌘ N
search⌘ K
toggle view⌘ ⇧ V
06

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.

pindoba · a multi-framework component systemastro · react · svelte · vue · MIT