Design system for Vue 3

Small pieces. Stronger systems.

Build consistent interfaces from composable components, foundations and utilities — importing exactly what you use, and nothing else.

Vue 3.5 · TypeScript · 77 components · 0 runtime dependencies

Two axes, not one
ComponentCSSJS
GrCard+ 223 B1 870 B
GrDialog+ 318 B9 789 B
GrDataTable+ 705 B16 243 B
GrSelect+ 2 335 B19 819 B

GrDataTable costs three times less CSS than GrSelect and nearly as much JS. One number does not predict the other.

Gzipped, package only — your framework is yours and is not counted here.

Not a screenshot

A working screen, assembled from the same components.

Conversion
4.8%
Revenue is a link, active users is a button (opened 0 times), conversion counts for 900 ms. Turn on "reduce motion" in the OS and the numbers stop counting.

The same demo that stands on the GrStatistic page — nothing was written twice for the sake of a homepage.

Composable

136 export paths. Import a component, a composable or a single directive — the graph stops there.

Consistent

Every colour, space and radius is a role, not a value. Both themes are first-class.

Customizable

Rebrand by changing token values. Component source stays untouched.

Developer-first

Types, auto-import, IDE metadata and an accessibility contract that is tested, not claimed.

The same cost, four ways

One number would have been easier to print.

Every figure here comes from a build, gzipped, with the framework excluded — it is yours, not ours. Which of these four matters depends on what constrains you, so all four are on the page.

What adoption costs

8 250B, then almost nothing

base + GrButton8 250 B
+ GrCard+ 223 B
+ GrDialog+ 318 B
+ GrDataTable+ 705 B
+ GrSelect+ 2 335 B

The first component carries tokens, both themes and the base layer. The next four together add less than a third of that.

Whether every component is cheap

13.6×spread across the set

GrBadge1 455 B
GrCard1 870 B
GrAlert2 762 B
GrButton3 391 B
GrTabs4 819 B
GrDialog9 789 B
GrDataTable16 243 B
GrSelect19 819 B

No. GrSelect weighs almost fourteen GrBadges. A library that claims every component is light is not measuring.

What you take on

0runtime dependencies

136 export paths

Nothing sits under the node. Your lockfile, your audit surface and your upgrade path stay yours — and 136 export paths mean the graph stops where you stop.

What a second theme costs

746B, at any size

1 · 7 504 B
2 · 8 250 B

The figure barely moves between one component and all 77. Dropping dark mode is not where weight is saved.

Measured 2026-08-29 · core 0.38.0 · preset 0.15.0 · gzip

The model

Four layers, each built from the one below it. The name is the architecture.

01

Tokens

Colour, space, radius, motion and layering as named roles.

02

Components

77 in the core, and companions around it. Each one importable alone.

03

Patterns

Compositions that solve a task: forms, search, settings, overlays.

04

Interfaces

Whole screens. Components are useful; systems are valuable.

Live, not pictures

Eight components you will meet first.

Each one is mounted on this page and reacts to the keyboard the way it will in your application. Click through — every card leads to the component page.

GrButton

The same matrix that lived in `playground-5`: for each `tone` you can see live buttons and force-rendered `hover`, `focus`, and `active` states across all `variant`s.

tone: primary

Live + 4 states × 5 variants
state \ variant primarysecondaryoutlineghostghost-border
Live
Rest
Hover
Focus
Active

tone: neutral

Live + 4 states × 5 variants
state \ variant primarysecondaryoutlineghostghost-border
Live
Rest
Hover
Focus
Active

tone: success

Live + 4 states × 5 variants
state \ variant primarysecondaryoutlineghostghost-border
Live
Rest
Hover
Focus
Active

tone: warning

Live + 4 states × 5 variants
state \ variant primarysecondaryoutlineghostghost-border
Live
Rest
Hover
Focus
Active

tone: danger

Live + 4 states × 5 variants
state \ variant primarysecondaryoutlineghostghost-border
Live
Rest
Hover
Focus
Active

tone: info

Live + 4 states × 5 variants
state \ variant primarysecondaryoutlineghostghost-border
Live
Rest
Hover
Focus
Active

tone: slate

Live + 4 states × 5 variants
state \ variant primarysecondaryoutlineghostghost-border
Live
Rest
Hover
Focus
Active

tone: azure

Live + 4 states × 5 variants
state \ variant primarysecondaryoutlineghostghost-border
Live
Rest
Hover
Focus
Active

GrInput

GrSelect

designplatform

GrDataTable

Номер
Маршрут
Перевозчик
Вес
Прибытие
Статус
SH-1043Санкт-Петербург — КазаньСеверный экспресс12,4 т14 августаВ пути
SH-1044Новороссийск — ПермьЮгТранс8,1 т16 августаПогрузка
SH-1045Владивосток — ИркутскДальлогистика21,7 т19 августаЗадержка

Номер закреплён слева, статус справа — при горизонтальной прокрутке они остаются на месте. Ширина тянется за правый край заголовка; с клавиатуры — стрелками на ручке, Enter возвращает колонку к авторазметке.

GrTabs

Overview tab is the default landing surface for a compact summary.
Active tab: overview

GrCommandPalette

or press K

Last command: · theme: light — try J without opening the palette.

GrToaster

One active `GrToaster` host is enough because `useToast` shares a global reactive store.

All 108 components across 8 packages

Foundations

A token is a role, not a hex code.

Colour, space, radius, motion and layering are named roles read from the package. Change the values and the components follow — 197 of them, in both themes.

Explore foundations

From nothing to a button

Three commands, no scaffolding.

  1. 01

    Install

    yarn add @feugene/granularity vue @floating-ui/dom
  2. 02

    Import

    import { GrButton } from '@feugene/granularity/components/GrButton'
  3. 03

    Render

    <GrButton tone="primary">Save</GrButton>

The full path — with the UnoCSS config and the three CSS imports — is on the getting started page. It fits on one screen too. Get started

Open development

Everything here is checkable.

Core version
0.41.0
License
LicenseRef-Granularity-EUC-1.1Apache-2.0 with an ethical use clause — source-available, not OSI-approved.

Start building with Granularity.

The shortest useful page is the getting started one. The rest of the documentation waits until you need it.

Get started