Blocks

Several components already wired together — a login popup, a mini dashboard, a swap card, a widget — copied as one working screen.

A block is the rung between one component and a whole page section. A component is a button. A section is a landing-page band. A block is what sits in between: a login popup, a mini dashboard, a swap card, a settings panel, a widget — several catalog components already assembled and wired to real state.

Browse them at Blocks. The chips filter by what the piece is: Auth, Dashboard, Cards, Widgets, Commerce, Crypto, Chat, Forms, Media, Settings, Onboarding.

What you actually get

The point of a block is the part a component library never hands you: the composition. Which pieces, in what order, at what spacing, sharing which bit of state. BlkLoginPopup invents nothing — the fields are VsInput, the actions are VsButton, the rule is VsSeparator, the toggle is VsCheckbox — and what it adds is the working screen those four make together.

So the copy you get is the assembly plus every component it uses. Copy a block and you get its file and the source of each Vs* inside it, in one paste-ready bundle. Nothing is left as an import you have to go and find.

Blocks are Vue SFCs

Components, sections and animations ship as native custom elements — one self-contained vs-*.js, zero dependencies. Blocks are Vue single-file components instead, and deliberately so: a block’s whole value is that it imports the catalog’s own pieces, and a “zero-dependency single file” would have to inline them and stop being an assembly at all.

<script setup lang="ts">
import VsInput from './components/VsInput.vue';
import VsButton from './components/VsButton.vue';
</script>

If you are not on Vue, the block is still the best reference you can get for the layout and the wiring — and every component it uses has a framework-neutral vs-*.js port of its own in Components.

Every prop is a control

Same rule as the rest of the catalog: nothing is hardcoded. Titles, labels, which optional rows appear — all props with defaults, all editable from the control bar before you copy, all visible in the preview as you change them.

Blocks emit events rather than doing the work: BlkLoginPopup emits submit with the form payload and social with the provider. Wiring it to your own auth endpoint is one listener.

Responsive by container query

A block is judged inside a box — a catalog card, a phone frame, a column of your app — not at the width of the window. Its breakpoints are therefore @container queries, so it lays itself out from the space it is actually given. Drop one into a 380px column and it behaves like it is on a phone, whatever the browser window is doing.

Adding one

pnpm new:block --name SwapCard --category Finance --group crypto

That scaffolds src/blocks/BlkSwapCard/ with its meta.ts and a Vue file already set up with the container query and the “import, do not invent” rule in a comment at the top. If a piece the block needs does not exist yet, build it as a real component first and import it — a one-off widget buried inside a block is a component nobody else can find.