Vine

Vine · dark-first primitives

Pills. Weight.
One curve.

A dark-first component set on shadcn/ui, Next.js and Tailwind — chunky 48px pills, weight-led hierarchy, and one asymmetric curve for every interaction.

pill radius 9999px press grow 1.04×

Live board

The controls, working

Palette

Primary pill

Surface pill

Search pill

Range

24h

Live updates

Refresh every 30 seconds.

cubic-bezier(.3,0,0,1)
primitives
64
screens
81
blocks
78
demo apps
5
try it
01Principles
01 / 09

The rules the tokens encode.

vine-ds is not a recolor of a stock preset — geometry, motion, and the type ramp all differ. These four rules are baked into the tokens, so every component inherits them without being told.

Pill radius 9999pxease-productive300–900
Controls are pills
01

Every button, chip, and search field is fully rounded, so icon buttons come out as perfect circles. Fields keep a deliberate 4px corner.

One curve
02

cubic-bezier(.3,0,0,1) drives everything interactive. 50ms to respond, 150ms to let go — asymmetric on purpose.

The 4% grow
03

Press targets scale to 1.04 under the cursor and snap flat on press. The one gesture the whole system is recognised by.

Weight, not size
04

900 display copy sits directly over a 400 supporting line. Exactly one light weight exists, and it is reserved for subtitles.

Tall controls

32 / 48 / 56px, with 48px as the default. Dense surfaces dip to 32.

Hard focus ring

2px solid, offset outward on pills and inward on fields. Never a glow, never the accent.

Light is a counterpart

Warm neutrals and near-black ink — deliberately not an inversion of the dark theme.

02Color
02 / 09

Quiet everywhere else. One earned accent.

A near-black ground carries the interface; the accent appears only where it means action, selection, or focus. Four palettes share one lightness ramp, so a card, a divider and a muted label land at the same contrast in all four. Status families stay fixed — a scheme may re-tune only the one status colour its brand would be confused with. The accent itself is one value in both modes, so its label answers to the fill rather than to the mode: black above L 0.58, white below. The label is the accent’s second, non-color signal, and it stays put when the mode flips.

Role before valueLabel follows the fillWhite label destructive
Backgroundvar(--background)

Canvas

--background

Foregroundvar(--foreground)

Ink

--foreground

The accentvar(--brand)

Brand

--brand

Action pillvar(--primary)

Primary

--primary

Dangervar(--destructive)

Destructive

--destructive

Confirmedvar(--success)

Success

--success

Attentionvar(--warning)

Warning

--warning

Guidancevar(--info)

Info

--info

Neutral pillvar(--surface-solid)

Surface solid

--surface-solid

Hover washvar(--tinted)

Tinted

--tinted

The four palettes

One lightness ramp, four hues. Pick one to preview it across the whole page.

tint 60tint 30--brandhoverpress

The whole identity lives in --brand. Nothing else references the hue directly, so a scheme is just a new set of values for that one family — which is exactly what data-palette on <html> swaps.

03Typography
03 / 09

A 300–900 ramp that reads across the room.

Figtree carries every interface role with its full weight range, so hierarchy comes from weight contrast rather than size contrast. JetBrains Mono handles values, numbers, and keys — always tabular.

Nine roles300–90012px minimum

Interface family

Figtree · Geometric grotesque

300—900

Aa Gg

Open geometric bowls and a tall x-height stay legible at 12px, while the full 900 weight keeps landmarks heavy from across the room.

300400500600700800900

Numeric family

JetBrains Mono · Tabular

tnum

67,421.30

Value

2.41T

Change

+2.41%

Updated

14:32

Fixed-width figures align values vertically. Signs, precision, and units carry meaning before colour ever enters.

Type scale

Nine roles, one measured rhythm

Size / line-height · weight

Display
48 / 48 · 800
Page title
Title
48 / 60 · 400
A lighter section headline
Heading
32 / 40 · 700
Subsection heading
Subtitle
24 / 33 · 300
The one light weight in the ramp
Body
16 / 26 · 400
Body copy at 16px with relaxed leading.
Label
14 / 14 · 700
Form labels & navigation
Caption
12 / 18 · 400
Legal lines and metadata
Overline
12 / 12 · 700
UPPERCASE, TRACKED
04Shape
04 / 09

Everything you press is round. Everything you type in is not.

One corner token for every job. Controls are fully rounded, so an icon button comes out as a perfect circle; fields keep a deliberate 4px corner so a row of inputs still reads as a form. Borders stay hairline until something is focused.

Pills pressFields stay square-ishHairline until focus
2px

2xs

--corner-2xs

Focus rings, tight chips

4px

xs

--corner-xs

Every text field

6px

sm

--corner-sm

Small surfaces

8px

md

--corner-md

Cards and popovers

16px

lg

--corner-lg

Large surfaces

9999px

pill

--corner-pill

Every button, chip and search field

Hairline

--border-hairline · 1px

Every resting edge

Thin

--border-thin · 2px

Selected and emphasised

Focus

--border-focus · 2px

The hard focus ring

Thick

--border-thick · 4px

Rules that carry meaning

05Elevation
05 / 09

Depth is a step in lightness. Shadow is for what floats.

Five surfaces run from the canvas to the popover. Each sits a fixed lightness step from its neighbour, in every palette, and a hairline finishes the edge, so a card never melts into the page. Only things that leave the page — menus, dialogs, drag previews — carry a shadow, and there are exactly two.

Five surfacesTwo shadowsOne scrim

Canvas

--canvas

Behind everything

Background

--background

The page

Elevated

--elevated

Bands and wells

Card

--card

Content on a surface

Popover

--popover

Floating panels

Overlay

--shadow-overlay

Menus, popovers, dialogs

Drop

--shadow-drop

Lifted cards and drag previews

50% veil

Scrim

--scrim

Behind a dialog, drawer or sheet. The same in every palette and both modes.

06Layout & spacing
06 / 09

4px rhythm. 48px controls.

A 4px base keeps every gap a multiple of the grid, and a finite spacing scale controls vertical rhythm. On top of it, controls hold their own line: 48px is the default and the pill keeps the whole rhythm chunky.

4px base12 → 4 columns32 / 48 / 56 controls

Column grid

Twelve columns · responsive

01
02
03
04
05
06
07
08
09
10
11
12

Twelve columns with 24px gutters on wide screens, eight at intermediate widths, and four on narrow screens. The 4px base means a gutter can never land off-grid.

Spacing scale

Finite steps, clear jobs

2XS
2px
XS
4px
SM
6px
MD
8px
LG
12px
BASE
16px
XL
24px
2XL
32px
3XL
48px

8px separates peers, 16px groups a module, 24px forms broad gutters, and 48px — the control height — marks a task boundary.

07Components
07 / 09

Primitives on a working canvas.

Live controls, not static inventory. Each card is a production-shaped specimen on vine-ds components — hover the pills and the 4% grow is right there.

Live statesToken drivenKeyboard ready

Actions · variants and sizes

Button
Action hierarchy
One raised commitment, quieter alternatives, and a separate destructive.
5 variants

Variants

Sizes

Icon · perfect circles

48px default · 56px touch1.04× / 50ms

Forms · the pill and the field

Input · Select
Filter controls
Search is a pill on the elevated surface; fields keep a quiet 4px corner.

Live updates

Refresh every 30 seconds.

Connected
3 filters applied

Choice · toggles and ranges

Switch · Slider
Delivery channel
Compact switches and a range slider share one 4% gesture.
2 on

Email summary

Daily at 09:00 UTC

SMS alerts

Beyond threshold

Weekly digest

Every Monday

42%
State is instant · 50msmotion-reduce

Measures · continuous values

Progress · Spinner
Import progress
Finite work uses Progress; unknown waits use the Spinner. Never both.
Import progress18 / 25
x
Verified92%
x
Rejected31%
x

Indexing records

12 sources queued

Active
Progress sits flat · 1px track1s fill

Identity · presence and shortcuts

Avatar · Badge · Kbd
Review team
Circles stack, badges classify, and key groups stay discoverable.
4 online
ACNPMSJK
+8
Active
PrimaryDraftSelectedBlockedWatching

Shortcuts

Open search
⌘K
Create record
C
Toggle filters
F
Hairline ring on every circleWCAG AA

Alerts · inline feedback

Alert
System status
Messages that stay on the page — never a layer, never a surprise.
2 states

Etiquette

  • Stays on the pageno toast, no layer
  • Icon carries statethe first reading
  • One action, top rightkept small and quiet
Elevated fill · 1px borderrole=alert

Toggle groups · one-of-many

ToggleGroup
Quick filters
One shared selection — the active pill stays quiet and filled.
single

Segmented · one choice

Independent · many channels

Etiquette

  • Shared stateone switch moves the row
  • Single vs multiplepick one, or pile on
  • Arrow keys moveroving focus built in
0px spacing · square jointaria-pressed

Layers · focused decisions

Dialog · AlertDialog
Record decisions
One decision per layer — a form creates, a confirm destroys. Never both.
Esc closes

Anatomy

  • Backdropdims, never colours
  • Focus locktabbing stays inside
  • Two layers maxstack never gets deep
48px triggers · 100ms inbase-ui

Menus · actions in one tap

Dropdown Menu
Bulk actions
Secondary work lives one tap away; destructive sits at the end, in red.
4 selected

Group Alpha

4 rows selected · 48 total

Etiquette

  • Roving focusarrows move the highlight
  • Checkbox itemstoggles stay inside the menu
  • Destructive lastseparated, then red
One tap · arrow keys · Esctype-ahead

Popovers · anchored context

Popover
Share this board
Transient context stays anchored to the surface it belongs to.
Open it, then click anywhere to close.

Anatomy

  • Anchorstays with its trigger
  • Outside clickdismisses without a cross
  • Inline datano full-screen layer needed
Anchored · 4px offsetrole=dialog

Sheets · full-height context

Sheet
Activity panel
A side surface for context that must not leave the page.
Right

Etiquette

  • One panelnever two sheets at once
  • Slide, not zoomdirection follows the edge
  • Backdrop optionallight context stays quiet
Full height · 200ms ease-outsm:max-w-sm

Tooltips · names on hover

Tooltip
Dense action bar
Icon-only buttons stay quiet until their name is requested.

Etiquette

  • Name, not novelone word, plain
  • Keyboard toofocus shows the same name
  • No delay0ms on this bar
Hover or focus the circlesmotion-reduce

Command · type to find

Command
Jump to anything
Type to find records, people, and actions — shortcuts stay at the edge.
⌘K

Type-ahead narrows as you type · arrows move · Enter runs · Esc steps out.

Inline here · dialog in appscmdk

Drawer · bottom sheet

Drawer
Connect a device
The touch counterpart to Sheet — swipe down to dismiss, no close button needed.
Bottom
Open it, then swipe it away.

Etiquette

  • Swipe to dismissdrag handle optional
  • Height follows contentnever a fixed sheet
  • Snap pointspaused at waypoints
Slide down · 200ms ease-outtouch-first

Hover card · preview in place

HoverCard
Team lookup
Preview a person before committing to the profile page.
Preview

Etiquette

  • Under the cursorstays with the hover
  • Keyboard reachablefocus shows the same card
  • Preview, not navigationclicking is the commit
Anchored · follows the rowbase-ui

Tables · dense rhythm

Table
Deployment queue
48px rows keep scanning fast; values stay tabular.
12 pages
  • Q3 campaign
    14:32
    Avery ChenPassed
  • Alpha billing
    14:12
    Noor PatelFailed
  • Group Alpha
    13:58
    Mateo SilvaReview
  • SMS alerts
    13:41
    Avery ChenQueued
  • Weekly digest
    12:20
    Noor PatelPassed
5 of 48 rows shownsorted by date ↓
Hairline rows · 48pxtabular-nums

Pagination · numbered walk

Pagination
Browse the index
One numbered pill marks the place; neighbours stay quiet.

Etiquette

  • Five pillsthen an ellipsis, then the end
  • Active is outlinenever filled, stays quiet
  • Prev / Nextlabels hide on mobile
48 rows · 12 per page2 / 12

Selection · one or many

Checkbox · Radio
Bulk export
Checkboxes count many; radios commit to one format.
3 selected

Export format

one choice
Indeterminate parent · one radiomixed state

Verification · fixed length

Input OTP
Two-factor code
Six slots at the control height; pastes split themselves.
6 digits

Code sent

a•••@vine.dev · valid 10 min

48px slots · auto-advancerole=group

Accordions · one panel open

Accordion
Workspace settings
One question per panel; the answer expands in place.
1 open

Digest emails leave at 09:00 UTC. Threshold alerts go out the moment a record fails.

Heights animate · 200msmotion-safe

Skeleton · honest loading

Skeleton
Queue, loading
Shapes match the final layout, so arrival feels like focus.
Pulse

Etiquette

  • Match the layoutsame boxes, same rhythm
  • One loading voicenever pulse and spin together
  • Prefer contentcache and streams first
Shapes mirror final UI1s ease

Breadcrumb · the trail back

Breadcrumb
Record location
The path home stays one quiet line above the record.
3 levels

Etiquette

  • Current is textnever a link to itself
  • Collapse the middleellipsis, then the last
  • Five stops maxbeyond that, restructure
Quiet · 1.5px separatorsaria-label

Collapsible · one reveal

Collapsible
Plan details
A single show/hide region — not a set, not an accordion.
1 open

Billing

Monthly · $24 per seat

Retention

24 months · then archive

Records are kept for 24 months, then archived for another 6 before deletion.

Etiquette

  • One region togglesaccordions for sets
  • Height animates200ms, one curve
  • Trigger is a buttonEnter and Space both work
Region keeps its framemotion-safe
08Motion
08 / 09

50ms to respond. 150ms to let go.

One easing curve drives everything interactive, applied asymmetrically. Rest-to-hover runs at 50ms so controls jump to meet the cursor; hover-to-rest takes 150ms so they are slow to let go. Focus is a hard ring — never a glow, never the accent.

One curveAsymmetricReduced-motion safe

The signature gesture

Hover the pill.

scale(1) → scale(1.04) under the cursoractive 1.0

The one gesture the whole system is recognised by. It snaps flat on click, and disappears entirely under prefers-reduced-motion.

One curve

Fast start. Long tail.

cubic-bezier(.3,0,0,1)

The curve bursts out of the gate and settles gradually — eager to respond, slow to let go. Linear feel is reserved for nothing interactive.

50ms
hover in
100ms
fields
150ms
rest ⇄ state
200ms
menus
250ms
sheets
300ms
transitions

Focus

A hard ring, never a glow.

2px · offset 2px

Button · ring offset outward

Field · ring offset inward

The ring uses --ring — white in dark mode, never the accent.Focus travels in DOM order.
09Apply
09 / 09

From token to a verified interface.

Start with the shared rules, then spend your judgment where the product is distinct. The system removes repeated decisions — not design thinking.

Intent firstCompose, don’t prescribeVerify before release
  1. 01

    Pick the pills

    Choose variant and size; the pill geometry is already settled. One clear commitment carries the decision.

    One clear action

  2. 02

    Map the roles

    Every colour, corner, and state points at a semantic token — never a local value. The theme stays one edit away.

    Theme-ready surface

  3. 03

    Compose live

    Put primitives on the page. The pill radius, the 4% grow, and the asymmetric curve arrive for free.

    Working interface

  4. 04

    Verify the edges

    Keyboard flow, contrast, overflow, both themes, and reduced motion — checked together, before shipping.

    Production confidence

The end of the guide · the start of the work

The best result reads as
the system’s default.

Use vine-ds to establish rhythm, weight, and the 4% grow. Then shape the final experience around the people and decisions it serves.