Introduction

346 components built on their own primitives. A CLI copies them into your repo and they become yours — there is no package to upgrade and nothing to fight when a design changes.

Not a dependency

astralyx-ui is a CLI and a registry, not a component library you import from. It writes source files into your project and then gets out of the way — the components in your tree have no link back to it, and upgrading the CLI cannot change a line you are shipping.

Every primitive lives in this repository: the slot that powers asChild, the popper that places every floating layer, the field that delegates focus, the listbox, the tabs, the router. Nothing is imported from a headless-UI library, so nothing can change under you.

That is a trade. You own the accessibility work — and it is done: every component here is checked for ARIA wiring, keyboard paths and focus management on every build.

One style contract

No component spells out its own focus ring, corner radius or control height. They compose fragments from src/lib/styles.ts, which is the single place any of it changes.

src/lib/styles.ts
tsx
export const controlSize = {
  xs: 'h-7 gap-1.5 px-3.5 text-xs rounded-full [corner-shape:round]',
  sm: 'h-8 gap-1.5 px-3.5 text-sm rounded-[var(--radius-control-sm)]',
  md: 'h-9 gap-2 px-4.5 text-sm rounded-[var(--radius-control-md)]',
  lg: 'h-10 gap-2 px-6 text-sm rounded-[var(--radius-control-lg)]',
  xl: 'h-12 gap-2.5 px-8 text-base rounded-[var(--radius-control-xl)]',
} as const

// Change a rung here and every control on it follows.

The rules it holds to

RuleWhy
Colour-only interactionNothing moves, resizes or gains elevation on hover or press. Layout never shifts under the pointer.
No shadowsDepth comes from a surface ladder — page, card, header, group — not from stacked elevation.
Secondary over outlineA screen of outlined controls is a grid of boxes with no hierarchy. A tint separates without another line.
iOS-style cornerscorner-shape: squircle everywhere, with a circular fallback where it is unsupported.
One gridHeights, radii, icon sizes and type all come from named scales. Off-grid values are caught on every build.