Gus UI

Components

Design system
for web apps and websites

Unified design tokens and React components. One import. Pre-compiled CSS. Built for consistency and speed.

@gusvega/ui

@gusvega/ui

Components, built-in theme values, and compiled CSS in one package.

Theme overrides

Customize colors, spacing, and type with CSS variables or createThemeVariables().

Get Started

Start building in minutes

Install the packages you need. Import CSS. Start building.

Everything in One Package

$npm install @gusvega/ui

Components, compiled CSS, and default theme values ship together.

Simple Theme Overrides

$import { createThemeVariables } from '@gusvega/ui'

Override theme values globally or for one subtree without another package.

// layout.tsx
import "@gusvega/ui/dist/style.css";

// any page/component
import { Button } from "@gusvega/ui";

Design System

Built-In Theme

Colors

Customize colors with CSS variables or the theme helper:

import { createThemeVariables } from '@gusvega/ui';

const themeVars = createThemeVariables({
  colors: {
    neutral: {
      900: '#0f172a',
    },
  },
});

neutral-100

neutral-500

neutral-900

Spacing

Override spacing in one place and every component picks it up:

:root {
  --gus-space-4: 1.125rem;
  --gus-space-6: 1.75rem;
}
xs: 4px
sm: 8px
md: 16px

Typography

Typography stays overridable without rebuilding the library:

:root {
  --gus-font-family-sans: "Satoshi", ui-sans-serif, system-ui, sans-serif;
  --gus-font-size-base: 1rem;
  --gus-line-height-base: 1.6rem;
}

Font Sizes

xs: 12px

sm: 14px

base: 16px

lg: 18px

2xl: 24px

Font Weights

normal: 400

medium: 500

semibold: 600

bold: 700

Extend & Customize

1. Import the library styles once:

// app/layout.tsx
import '@gusvega/ui/style.css';

2. Override values in CSS or generate them in React:

import { createThemeVariables } from '@gusvega/ui';

const themeVars = createThemeVariables({
  colors: {
    neutral: {
      900: '#0f172a',
      100: '#f1f5f9',
    },
  },
});

3. Apply the variables and the components update automatically:

<div style={themeVars}>
  <Card>
    <Button>Click me</Button>
  </Card>
</div>

Showcase

Components

Button

Actions

Triggers actions and events. 4 variants, 3 sizes, disabled state.

Variants

Sizes

import { Button } from '@gusvega/ui';

// Variants
<Button size="sm" variant="primary">Primary</Button>
<Button size="sm" variant="secondary">Secondary</Button>
<Button size="sm" variant="ghost">Ghost</Button>
<Button size="sm" disabled>Disabled</Button>

// Sizes
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>

Badge

Data Display

Small status label. 3 variants for different levels of emphasis.

DefaultSecondaryOutline
Newv0.0.1MITTypeScript
import { Badge } from '@gusvega/ui';

<Badge variant="default">Default</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="outline">Outline</Badge>

// In context
<Badge variant="secondary">New</Badge>
<Badge variant="outline">v0.0.1</Badge>
<Badge>MIT</Badge>
<Badge variant="secondary">TypeScript</Badge>

Avatar

Data Display

User representation with initials or image. 3 sizes, 3 colors, stackable.

import { Avatar } from '@gusvega/ui';

// Sizes
<Avatar initials="SM" size="sm" />
<Avatar initials="MD" size="md" />
<Avatar initials="LG" size="lg" />

// Colors
<Avatar initials="Dk" color="dark" />
<Avatar initials="Md" color="medium" />
<Avatar initials="Lt" color="light" />

// Stacked group
<div className="flex -space-x-2">
  <Avatar initials="A" size="sm" color="dark" />
  <Avatar initials="B" size="sm" color="medium" />
  <Avatar initials="+3" size="sm" color="light" />
</div>

Input

Form

Text input with forwardRef support, error state, and disabled variant.

import { Input } from '@gusvega/ui';

<Input placeholder="Default input" />

// Error state
<Input
  placeholder="With error state"
  error="This field is required"
/>

// Disabled
<Input placeholder="Disabled" disabled />

Label

Form

Form field label with optional required marker.

import { Label } from '@gusvega/ui';

<Label htmlFor="email">Standard label</Label>
<Label htmlFor="name" required>Required label</Label>

// With input
<Label htmlFor="input" className="mb-1.5">With input</Label>
<Input id="input" placeholder="Value..." />

Textarea

Form

Multiline text input with forwardRef support and resizable height.

import { Textarea } from '@gusvega/ui';

<Textarea placeholder="Write your message here..." rows={3} />

// Disabled
<Textarea placeholder="Disabled textarea" disabled rows={2} />

Checkbox

Form

Controlled boolean input with peer-based custom styling.

import { Checkbox } from '@gusvega/ui';

const [v, setV] = useState({
  email: true,
  marketing: false,
  auth: true,
});

<Checkbox
  checked={v.email}
  onChange={(x) => setV(p => ({ ...p, email: x }))}
  label="Email notifications"
/>
<Checkbox
  checked={v.marketing}
  onChange={(x) => setV(p => ({ ...p, marketing: x }))}
  label="Marketing emails"
/>
<Checkbox
  checked={v.auth}
  onChange={(x) => setV(p => ({ ...p, auth: x }))}
  label="Two-factor auth"
/>
<Checkbox
  checked={false}
  onChange={() => {}}
  label="Disabled option"
  disabled
/>

Radio

Form

Single-select controlled input. Group via a shared name prop.

import { Radio } from '@gusvega/ui';

const [plan, setPlan] = useState('pro');

<Radio name="plan" value="free"
  label="Free — $0/mo"
  checked={plan === 'free'} onChange={setPlan} />

<Radio name="plan" value="pro"
  label="Pro — $12/mo"
  checked={plan === 'pro'} onChange={setPlan} />

<Radio name="plan" value="team"
  label="Team — $49/mo"
  checked={plan === 'team'} onChange={setPlan} />

<Radio name="plan" value="enterprise"
  label="Enterprise (contact us)"
  checked={plan === 'enterprise'} onChange={setPlan}
  disabled />

Switch

Form

Toggle control for binary settings. Animates on state change.

import { Switch } from '@gusvega/ui';

const [v, setV] = useState({
  notifs: true,
  marketing: false,
  updates: true,
});

<Switch
  checked={v.notifs}
  onChange={(x) => setV(p => ({ ...p, notifs: x }))}
  label="Push notifications"
/>
<Switch
  checked={v.marketing}
  onChange={(x) => setV(p => ({ ...p, marketing: x }))}
  label="Marketing emails"
/>
<Switch
  checked={v.updates}
  onChange={(x) => setV(p => ({ ...p, updates: x }))}
  label="Product updates"
/>
<Switch checked={false} onChange={() => {}}
  label="Disabled" disabled />

Select

Form

Styled native select with custom chevron. Supports placeholder and disabled options.

import { Select } from '@gusvega/ui';

<Select
  placeholder="Choose a framework..."
  options={[
    { value: 'next',  label: 'Next.js'  },
    { value: 'remix', label: 'Remix'    },
    { value: 'vite',  label: 'Vite'     },
    { value: 'astro', label: 'Astro', disabled: true },
  ]}
  onChange={(value) => console.log(value)}
/>

FormField

Form

Compound wrapper that connects Label, input, error message, and hint text.

Only letters and numbers.

import { FormField, Input } from '@gusvega/ui';

// With hint
<FormField
  label="Username"
  htmlFor="ff-1"
  hint="Only letters and numbers."
>
  <Input id="ff-1" placeholder="gus_vega" />
</FormField>

// With error
<FormField
  label="Email"
  htmlFor="ff-2"
  required
  error="Invalid email address."
>
  <Input id="ff-2" type="email" placeholder="you@example.com" />
</FormField>

Tag

Data Display

Rectangular label with optional remove button. Use for categories, filters, or selections.

DefaultDarkOutline
TypeScriptReactTailwind
import { Tag } from '@gusvega/ui';

<Tag>Default</Tag>
<Tag variant="default">Dark</Tag>
<Tag variant="outline">Outline</Tag>

// Removable
<Tag onRemove={() => {}}>TypeScript</Tag>
<Tag onRemove={() => {}}>React</Tag>
<Tag onRemove={() => {}}>Tailwind</Tag>

Stat

Data Display

Metric display with optional trend indicator. Use in dashboards and overview cards.

Total Users

12,345

+8.2% this month

Revenue

$84.2k

+12.5% this month

Churn Rate

2.4%

-0.3% this month

Uptime

99.9%

import { Stat } from '@gusvega/ui';

<Stat label="Total Users" value="12,345"
  change="+8.2% this month" trend="up" />

<Stat label="Revenue" value="$84.2k"
  change="+12.5% this month" trend="up" />

<Stat label="Churn Rate" value="2.4%"
  change="-0.3% this month" trend="down" />

<Stat label="Uptime" value="99.9%" />

Table

Data Display

Structured data display with hover rows and responsive overflow scroll.

NameRoleStatusJoined
Gus VegaEngineerActiveJan 2024
Jane DoeDesignerActiveMar 2024
Alex KimPMAwayJun 2024
import {
  Table, TableHeader, TableBody,
  TableRow, TableHead, TableCell,
  Badge
} from '@gusvega/ui';

<Table>
  <TableHeader>
    <TableRow>
      <TableHead>Name</TableHead>
      <TableHead>Role</TableHead>
      <TableHead>Status</TableHead>
      <TableHead>Joined</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell className="font-medium">Gus Vega</TableCell>
      <TableCell>Engineer</TableCell>
      <TableCell><Badge>Active</Badge></TableCell>
      <TableCell>Jan 2024</TableCell>
    </TableRow>
    <TableRow>
      <TableCell className="font-medium">Jane Doe</TableCell>
      <TableCell>Designer</TableCell>
      <TableCell><Badge>Active</Badge></TableCell>
      <TableCell>Mar 2024</TableCell>
    </TableRow>
    <TableRow>
      <TableCell className="font-medium">Alex Kim</TableCell>
      <TableCell>PM</TableCell>
      <TableCell><Badge variant="secondary">Away</Badge></TableCell>
      <TableCell>Jun 2024</TableCell>
    </TableRow>
  </TableBody>
</Table>

Code & Kbd

Data Display

Inline code and block code for documentation. Kbd for keyboard shortcut hints.

Run npm install gus-ui to get started.

KCommand palette
SSave
import { Code, Kbd } from '@gusvega/ui';

// Inline
<p>Run <Code>npm install gus-ui</Code> to get started.</p>

// Block
<Code block>
  {"import { Button } from '@gusvega/ui';"}
</Code>

// Keyboard shortcuts
<div className="flex items-center gap-1.5">
  <Kbd>⌘</Kbd>
  <Kbd>K</Kbd>
  <span className="text-xs text-neutral-500">Command palette</span>
</div>
<div className="flex items-center gap-1.5">
  <Kbd>⌘</Kbd>
  <Kbd>S</Kbd>
  <span className="text-xs text-neutral-500">Save</span>
</div>

Progress

Feedback

Animated fill bar for indicating completion. Value range: 0–100.

Storage72%
Bandwidth45%
Memory91%
import { Progress } from '@gusvega/ui';

<Progress value={72} />

// With labels
<div>
  <div className="flex justify-between mb-2">
    <span>Storage</span>
    <span>72%</span>
  </div>
  <Progress value={72} />
</div>

<div>
  <div className="flex justify-between mb-2">
    <span>Bandwidth</span>
    <span>45%</span>
  </div>
  <Progress value={45} />
</div>

<div>
  <div className="flex justify-between mb-2">
    <span>Memory</span>
    <span>91%</span>
  </div>
  <Progress value={91} />
</div>

Spinner

Feedback

CSS animation loading indicator. 3 sizes for different contexts.

sm
md
lg
import { Spinner } from '@gusvega/ui';

<Spinner size="sm" />
<Spinner size="md" />
<Spinner size="lg" />

// With button
<Button disabled>
  <Spinner size="sm" className="mr-2" />
  Loading...
</Button>

Alert

Feedback

Contextual message with optional title. 3 variants for different emphasis levels.

import { Alert } from '@gusvega/ui';

<Alert variant="default" title="Heads up">
  Your free trial ends in 3 days. Upgrade to keep access.
</Alert>

<Alert variant="outline" title="Action required">
  Please verify your email address to continue.
</Alert>

<Alert variant="filled" title="Deployed successfully">
  Version 1.2.0 is now live in production.
</Alert>

Skeleton

Feedback

Pulse animation placeholder for loading states. Rectangular, circular, and text variants.

import { Skeleton } from '@gusvega/ui';

// Text lines
<Skeleton variant="text" />
<Skeleton variant="text" width="60%" />

// Circular (avatar placeholder)
<Skeleton variant="circular" width={40} height={40} />

// Rectangular (image / card placeholder)
<Skeleton variant="rectangular" width={200} height={120} />

Tabs

Navigation

Panel switcher with context-based state. Compound: Tabs, TabsList, TabsTrigger, TabsContent.

Tabs organize content into separate panels — only one panel is visible at a time.

import { Tabs, TabsList, TabsTrigger, TabsContent } from '@gusvega/ui';

<Tabs defaultValue="overview">
  <TabsList>
    <TabsTrigger value="overview">Overview</TabsTrigger>
    <TabsTrigger value="code">Code</TabsTrigger>
    <TabsTrigger value="api">API</TabsTrigger>
    <TabsTrigger value="disabled" disabled>Disabled</TabsTrigger>
  </TabsList>
  <TabsContent value="overview">Overview content here.</TabsContent>
  <TabsContent value="code">Code content here.</TabsContent>
  <TabsContent value="api">API content here.</TabsContent>
</Tabs>

Breadcrumb

Navigation

Hierarchical navigation trail. Last item renders as current page (no href).

import { Breadcrumb, BreadcrumbItem } from '@gusvega/ui';

<Breadcrumb>
  <BreadcrumbItem href="#">Home</BreadcrumbItem>
  <BreadcrumbItem href="#">Components</BreadcrumbItem>
  <BreadcrumbItem>Breadcrumb</BreadcrumbItem>
</Breadcrumb>

// Longer breadcrumb
<Breadcrumb>
  <BreadcrumbItem href="#">Docs</BreadcrumbItem>
  <BreadcrumbItem href="#">Navigation</BreadcrumbItem>
  <BreadcrumbItem href="#">Breadcrumb</BreadcrumbItem>
  <BreadcrumbItem>Examples</BreadcrumbItem>
</Breadcrumb>

Card

Layout

Content container with header, body, and footer slots. All sub-components are optional.

Gus Vega

Design Systems Engineer

Building the GUS UI design system — tokens, components, and documentation.

DesignTypeScriptReact
import {
  Card, CardHeader, CardContent, CardFooter,
  Avatar, Badge, Button
} from '@gusvega/ui';

<Card className="w-full">
  <CardHeader>
    <div className="flex items-center gap-3">
      <Avatar initials="GU" size="md" />
      <div>
        <p className="font-semibold">Gus Vega</p>
        <p className="text-xs text-neutral-500">Design Systems Engineer</p>
      </div>
    </div>
  </CardHeader>
  <CardContent>
    <p className="text-sm text-neutral-500 leading-relaxed">
      Building the GUS UI design system — tokens, components, and documentation.
    </p>
    <div className="flex gap-2 mt-3">
      <Badge variant="secondary">Design</Badge>
      <Badge variant="outline">TypeScript</Badge>
      <Badge variant="outline">React</Badge>
    </div>
  </CardContent>
  <CardFooter className="flex justify-end gap-2">
    <Button variant="ghost" size="sm">Message</Button>
    <Button size="sm">Follow</Button>
  </CardFooter>
</Card>

Separator

Layout

Visual divider for horizontal and vertical layouts.

Above the separator

Below the separator

TokensLibraryRefapp
import { Separator } from '@gusvega/ui';

// Horizontal (default)
<Separator />

// Vertical (set a fixed height on parent)
<div className="flex items-center gap-4 h-8">
  <span>Tokens</span>
  <Separator orientation="vertical" />
  <span>Library</span>
  <Separator orientation="vertical" />
  <span>Refapp</span>
</div>

Stack

Layout

Flex layout helper with direction, gap, alignment, and justify props.

RowItem 2Item 3
Dashboard
import { Stack, Badge, Button } from '@gusvega/ui';

// Row
<Stack direction="row" gap={2} align="center">
  <Badge>Row</Badge>
  <Badge variant="secondary">Item 2</Badge>
  <Badge variant="outline">Item 3</Badge>
</Stack>

// Column
<Stack direction="col" gap={2}>
  <Button size="sm">First</Button>
  <Button size="sm" variant="secondary">Second</Button>
  <Button size="sm" variant="ghost">Third</Button>
</Stack>

// Space between
<Stack direction="row" justify="between" align="center"
  className="border border-neutral-100 rounded-lg p-3">
  <span className="font-medium">Dashboard</span>
  <div className="flex gap-2">
    <Button size="sm" variant="ghost">Cancel</Button>
    <Button size="sm">Save</Button>
  </div>
</Stack>

Container

Layout

Max-width wrapper for consistent page and section layouts. Supports polymorphic 'as' prop.

Container example

Max-width wrapper with consistent padding and alignment

import { Container } from '@gusvega/ui';

// Default (div)
<Container>
  <h1>Page content</h1>
</Container>

// As section
<Container as="section">
  <h2>Section title</h2>
</Container>

// With custom className
<Container className="py-12">
  Content with padding
</Container>

PageIntro

Layout

Hero section for page headers with optional eyebrow, title, and children content.

Welcome - Build Amazing Interfaces

With a composable component library.

import { PageIntro } from '@gusvega/ui';

<PageIntro
  eyebrow="Welcome"
  title="Build Amazing Interfaces"
>
  <p>With a composable component library.</p>
</PageIntro>

// Centered variant
<PageIntro
  eyebrow="Features"
  title="What's Included"
  centered
>
  <Stack direction="row" gap={4}>
    <div>Feature 1</div>
    <div>Feature 2</div>
  </Stack>
</PageIntro>

SectionIntro

Layout

Section header with eyebrow and title. Supports smaller and invert variants.

Our Team - Meet the experts

Process - How we work

import { SectionIntro } from '@gusvega/ui';

// Standard
<SectionIntro
  eyebrow="Our Team"
  title="Meet the experts"
/>

// Smaller variant
<SectionIntro
  eyebrow="Process"
  title="How we work"
  smaller
/>

List

Layout

Semantic list component with ListItem compound. Supports animations with FadeIn.

  • Beautiful design system. Consistent components and tokens across projects
  • Type-safe components. Full TypeScript support with proper type inference
  • CSS compiled. Pre-compiled CSS bundle with Tailwind CSS
import { List, ListItem } from '@gusvega/ui';

<List>
  <ListItem title="Beautiful design system">
    Consistent components and tokens across projects
  </ListItem>
  <ListItem title="Type-safe components">
    Full TypeScript support with proper type inference
  </ListItem>
  <ListItem title="CSS compiled">
    Pre-compiled CSS bundle with Tailwind CSS
  </ListItem>
</List>

Blockquote

Layout

Semantic blockquote component with optional author and cite attributes.

Great design systems save time and improve consistency.

Design Team, Gus UI

Components should be composable and flexible.

Contributors, Open Source
import { Blockquote } from '@gusvega/ui';

<Blockquote author={{ name: 'Design Team', role: 'Gus UI' }}>
  Great design systems save time and improve consistency.
</Blockquote>

// With different author
<Blockquote author={{ name: 'Contributors', role: 'Open Source' }}>
  Components should be composable and flexible.
</Blockquote>

Modal

Overlays

Blocking dialog with backdrop, title, content, and action footer.

import { Modal, Button } from '@gusvega/ui';
import { useState } from 'react';

const [open, setOpen] = useState(false);

<Button onClick={() => setOpen(true)}>Open Modal</Button>

<Modal
  open={open}
  onClose={() => setOpen(false)}
  title="Confirm Action"
  description="Are you sure?"
  footer={
    <>
      <Button variant="ghost" size="sm" onClick={() => setOpen(false)}>Cancel</Button>
      <Button size="sm">Confirm</Button>
    </>
  }
>
  <p>This action cannot be undone.</p>
</Modal>

Drawer

Overlays

Side panel overlay for navigation or content. Left or right positioning.

import { Drawer, Button } from '@gusvega/ui';
import { useState } from 'react';

const [open, setOpen] = useState(false);

<Button onClick={() => setOpen(true)}>Open Drawer</Button>

<Drawer open={open} onClose={() => setOpen(false)} title="Navigation">
  <nav className="space-y-2">
    <a href="#" className="block text-sm hover:text-neutral-900">Home</a>
    <a href="#" className="block text-sm hover:text-neutral-900">Components</a>
    <a href="#" className="block text-sm hover:text-neutral-900">Docs</a>
  </nav>
</Drawer>

Tooltip

Overlays

Lightweight hint displayed on hover. Positioned around trigger.

import { Tooltip, Button } from '@gusvega/ui';

<Tooltip text="Click to save" side="top">
  <Button>Save</Button>
</Tooltip>

<Tooltip text="Delete this item" side="bottom">
  <Button variant="ghost">Delete</Button>
</Tooltip>

Popover

Overlays

Floating panel triggered by click/hover. Positioned relative to trigger.

import { Popover, Button } from '@gusvega/ui';

<Popover trigger={<Button>Options</Button>} side="bottom">
  <div className="space-y-2">
    <button className="block text-sm hover:text-neutral-900">Edit</button>
    <button className="block text-sm hover:text-neutral-900">Share</button>
    <button className="block text-sm hover:text-neutral-900">Delete</button>
  </div>
</Popover>

Accordion

Data Display

Collapsible content sections. Click to expand/collapse.

import { Accordion, AccordionItem } from '@gusvega/ui';

<Accordion>
  <AccordionItem title="What is Gus UI?">
    A design system for React with tokens and pre-built components.
  </AccordionItem>
  <AccordionItem title="How do I use it?">
    Import components and styles, then build your UI.
  </AccordionItem>
  <AccordionItem title="Is it free?">
    Yes, Gus UI is open source and free to use.
  </AccordionItem>
</Accordion>

Stepper

Feedback

Multi-step form indicator. Shows progress through steps.

Personal

Billing

Review

import { Stepper } from '@gusvega/ui';

<Stepper
  steps={['Personal Info', 'Billing', 'Confirm']}
  currentStep={1}
/>

Pagination

Navigation

Navigate through paginated data. Smart page number display.

import { Pagination } from '@gusvega/ui';

const [page, setPage] = useState(1);

<Pagination
  currentPage={page}
  totalPages={10}
  onPageChange={setPage}
/>

Timeline

Data Display

Display chronological events. Vertical timeline with dates.

v1.0.0 Released

Jan 15, 2024

v1.1.0 Released

Added new components

Feb 10, 2024

v1.2.0 Released

Mar 20, 2024

import { Timeline } from '@gusvega/ui';

<Timeline events={[
  { title: 'v1.0.0 Released', date: 'Jan 15, 2024' },
  { title: 'v1.1.0 Released', date: 'Feb 10, 2024', description: 'Added new components' },
  { title: 'v1.2.0 Released', date: 'Mar 20, 2024' },
]} />

Rating

Form

Star rating input. 1-5 stars for feedback.

Interactive

Readonly

import { Rating } from '@gusvega/ui';

const [rate, setRate] = useState(4);

<Rating value={rate} onChange={setRate} />

// Readonly
<Rating value={5} readonly />

ToggleGroup

Form

Button group for single or multi-selection.

import { ToggleGroup } from '@gusvega/ui';

<ToggleGroup
  options={[
    { value: 'left', label: 'Left' },
    { value: 'center', label: 'Center' },
    { value: 'right', label: 'Right' },
  ]}
  onChange={(value) => console.log(value)}
/>

Chip

Data Display

Small tag-like element. Removable with close button.

Design
React
Remove me
Default
import { Chip } from '@gusvega/ui';

<Chip label="Design" variant="secondary" />
<Chip label="React" variant="outline" />
<Chip label="Remove me" onRemove={() => {}} />

CopyButton

Actions

Button that copies text to clipboard with confirmation feedback.

import { CopyButton } from '@gusvega/ui';

<CopyButton text="npm install @gusvega/ui" />

<CopyButton
  text="git clone https://github.com/..."
  label="Copy URL"
  variant="secondary"
/>

EmptyState

Feedback

Message display for empty lists or no results state.

No results found

Try adjusting your search or filters

import { EmptyState, Button } from '@gusvega/ui';

<EmptyState
  title="No results found"
  description="Try adjusting your search or filters"
  action={<Button size="sm">Clear filters</Button>}
/>

HoverCard

Overlays

Card revealed on hover. For rich preview content.

import { HoverCard, Avatar } from '@gusvega/ui';

<HoverCard trigger={<Avatar initials="GV" size="md" />}>
  <div>
    <p className="font-semibold">Gus Vega</p>
    <p className="text-xs text-neutral-500">Designer</p>
  </div>
</HoverCard>

Toast

Feedback

Non-blocking notification with optional type and duration.

Toasts appear at bottom-left of screen

import { Toast, Button } from '@gusvega/ui';
import { useState } from 'react';

const [showToast, setShowToast] = useState(false);

<Button onClick={() => setShowToast(true)}>Show Toast</Button>

{showToast && (
  <Toast
    message="Operation completed successfully!"
    type="success"
    onClose={() => setShowToast(false)}
  />
)}

Combobox

Form

Searchable dropdown with filtering and keyboard navigation.

import { Combobox } from '@gusvega/ui';

<Combobox
  options={[
    { value: 'react', label: 'React' },
    { value: 'vue', label: 'Vue' },
    { value: 'svelte', label: 'Svelte' },
  ]}
  placeholder="Search frameworks..."
  onChange={(value) => console.log(value)}
/>

MultiSelect

Form

Multi-option select with checkboxes and removable tags.

Select items...
import { MultiSelect } from '@gusvega/ui';

<MultiSelect
  options={[
    { value: 'ts', label: 'TypeScript' },
    { value: 'react', label: 'React' },
    { value: 'tailwind', label: 'Tailwind' },
  ]}
  onChange={(values) => console.log(values)}
/>

Menu

Overlays

Dropdown menu with options and dividers.

import { Menu, Button } from '@gusvega/ui';

<Menu
  trigger={<Button size="sm">Actions</Button>}
  options={[
    { label: 'Edit', value: 'edit' },
    { label: 'Share', value: 'share' },
    { label: 'Delete', value: 'delete', divider: true },
  ]}
/>

Slider

Form

Range input with min/max/step and value display.

import { Slider } from '@gusvega/ui';
import { useState } from 'react';

const [value, setValue] = useState(50);

<Slider
  min={0}
  max={100}
  step={1}
  value={value}
  onChange={setValue}
/>

DatePicker

Form

Date input with label, hint, validation, and min/max constraints.

Select your travel date

Between 2024 – 2026

import { DatePicker } from '@gusvega/ui';

// Basic
<DatePicker label="Start Date" hint="Select your travel date" />

// With constraints
<DatePicker label="Deadline" required min="2024-01-01" max="2026-12-31" />

// Error state
<DatePicker label="Expiry" error="This field is required" />

TimePicker

Form

Time input with label, hint, validation, min/max, and step interval.

Business hours only

15-minute intervals

import { TimePicker } from '@gusvega/ui';

// With range constraint
<TimePicker label="Meeting Time" min="09:00" max="18:00" hint="Business hours only" />

// 15-minute intervals
<TimePicker label="Reminder" step={900} hint="15-minute intervals" />

// Error state
<TimePicker label="Deadline" error="Please select a valid time" />

FileUpload

Form

Drag & drop file upload with per-file list, size display, progress slot, and validation.

Drag files here or click to select

.pdf,.doc,.docx,.png,.jpg up to 5.0 MB · max 5 files

import { FileUpload } from '@gusvega/ui';

<FileUpload
  multiple
  accept=".pdf,.doc,.docx,.png,.jpg"
  maxSize={5 * 1024 * 1024}
  maxFiles={5}
  onFileSelect={(files) => console.log(files)}
/>

InputGroup

Form

Input with optional prefix and suffix elements.

$
.00
import { InputGroup } from '@gusvega/ui';

<InputGroup prefix="$" suffix=".00" placeholder="0" />

FormGroup

Form

Fieldset wrapper with legend and description.

Preferences

Customize your settings

import { FormGroup, Input } from '@gusvega/ui';

<FormGroup legend="Preferences" description="Customize your settings">
  <Input placeholder="Enter value" />
</FormGroup>

NavigationMenu

Navigation

Horizontal navigation with dropdown submenu support.

import { NavigationMenu } from '@gusvega/ui';

<NavigationMenu items={[
  { label: 'Home', href: '#' },
  { label: 'Products', href: '#', submenu: [
    { label: 'Product 1', href: '#' },
  ]},
]} />

Collapsible

Navigation

Expandable/collapsible container with animated arrow.

import { Collapsible } from '@gusvega/ui';

<Collapsible title="Advanced Options">
  <div>Hidden content here</div>
</Collapsible>

ButtonGroup

Actions

Group of buttons with active state highlighting.

import { ButtonGroup } from '@gusvega/ui';

<ButtonGroup
  buttons={[
    { label: 'List', onClick: () => {} },
    { label: 'Grid', onClick: () => {}, active: true },
  ]}
/>

SplitButton

Actions

Primary button with dropdown for secondary actions.

import { SplitButton } from '@gusvega/ui';

<SplitButton
  label="Save"
  onClick={() => {}}
  options={[
    { label: 'Save as Draft', onClick: () => {} },
    { label: 'Schedule', onClick: () => {} },
  ]}
/>

IconButton

Actions

Icon-only button with size and variant options.

import { IconButton } from '@gusvega/ui';

<div className="flex gap-2">
  <IconButton icon="🔍" size="sm" />
  <IconButton icon="🗑️" size="md" variant="secondary" />
  <IconButton icon="⚙️" size="lg" variant="ghost" />
</div>

SegmentedControl

Actions

Grouped button selection with active styling.

import { SegmentedControl } from '@gusvega/ui';
import { useState } from 'react';

const [selected, setSelected] = useState('day');

<SegmentedControl
  options={[
    { value: 'day', label: 'Day' },
    { value: 'week', label: 'Week' },
    { value: 'month', label: 'Month' },
  ]}
  value={selected}
  onChange={setSelected}
/>

Tree

Data Display

Hierarchical data display with expand/collapse.

import { Tree } from '@gusvega/ui';

const data = {
  label: 'Root',
  children: [
    { label: 'Child 1' },
    { label: 'Child 2', children: [{ label: 'Grandchild' }] },
  ],
};

<Tree node={data} />

DataGrid

Data Display

Table with sortable columns and hover effects.

John Doejohn@example.com
Jane Smithjane@example.com
import { DataGrid } from '@gusvega/ui';

<DataGrid
  columns={[
    { key: 'name', label: 'Name' },
    { key: 'email', label: 'Email' },
  ]}
  rows={[
    { name: 'John', email: 'john@example.com' },
  ]}
/>

BadgeCounter

Data Display

Number badge with 99+ display format.

59999+
import { BadgeCounter } from '@gusvega/ui';

<div className="flex gap-4">
  <BadgeCounter count={5} />
  <BadgeCounter count={99} />
  <BadgeCounter count={150} />
</div>

AlertDialog

Overlays

Modal confirmation dialog with title and description.

import { AlertDialog, Button } from '@gusvega/ui';
import { useState } from 'react';

const [open, setOpen] = useState(false);

<Button size="sm" onClick={() => setOpen(true)}>Open Dialog</Button>

{open && (
  <AlertDialog
    title="Confirm Action"
    description="Are you sure? This cannot be undone."
    onConfirm={() => setOpen(false)}
    onCancel={() => setOpen(false)}
  />
)}

DropZone

Overlays

Keyboard-accessible drop zone with file list, size display, per-file removal, and size validation.

Drop images here

or click to browse — image/* — max 2.0 MB

import { DropZone } from '@gusvega/ui';

<DropZone
  multiple
  accept="image/*"
  maxSize={2 * 1024 * 1024}
  label="Drop images here"
  onDrop={(files) => console.log(files)}
/>

LoadingOverlay

Overlays

Full-screen loading state with spinner and message.

Loading...
import { LoadingOverlay } from '@gusvega/ui';

<LoadingOverlay isVisible={true} message="Loading..." />

CommandPalette

Overlays

Cmd+K style command palette with grouped commands, keyboard navigation (↑↓ Enter Esc), shortcut badges, and fuzzy search.

import { CommandPalette } from '@gusvega/ui';
import { useState } from 'react';

const COMMANDS = [
  { id: 'new-file',  label: 'New File',    shortcut: '⌘+N', group: 'File',    action: () => {} },
  { id: 'save',      label: 'Save',         shortcut: '⌘+S', group: 'File',    action: () => {} },
  { id: 'find',      label: 'Find in Files', shortcut: '⌘+⇧+F', group: 'Edit', action: () => {} },
  { id: 'dark-mode', label: 'Toggle Dark Mode',               group: 'View',   action: () => {} },
];

const [open, setOpen] = useState(false);

<Button onClick={() => setOpen(true)}>Open Palette</Button>
<CommandPalette
  commands={COMMANDS}
  open={open}
  onClose={() => setOpen(false)}
/>

InfoBox

Data Display

Highlighted info/tip box with type variants.

💡

Pro Tip

You can use keyboard shortcuts for faster workflow.

import { InfoBox } from '@gusvega/ui';

<InfoBox
  title="Pro Tip"
  message="You can use keyboard shortcuts for faster workflow."
  type="tip"
/>

IconTabs

Data Display

Tabs component with icons and text labels.

Home content
import { IconTabs } from '@gusvega/ui';

<IconTabs tabs={[
  { id: 'home', label: 'Home', icon: '🏠', content: <div>Home content</div> },
  { id: 'settings', label: 'Settings', icon: '⚙️', content: <div>Settings content</div> },
]} />

CodeBlockAdvanced

Data Display

Code display with syntax highlighting and copy button.

javascript
const hello = () => console.log('world');
import { CodeBlockAdvanced } from '@gusvega/ui';

<CodeBlockAdvanced
  language="javascript"
  code="const hello = () => console.log('world');"
/>

AdvancedBadge

Data Display

Badge variant with removable close button.

React
TypeScript
Tailwind
import { AdvancedBadge } from '@gusvega/ui';

<div className="flex gap-2 flex-wrap">
  <AdvancedBadge label="React" onRemove={() => {}} />
  <AdvancedBadge label="TypeScript" variant="success" onRemove={() => {}} />
  <AdvancedBadge label="Tailwind" variant="warning" />
</div>

Menu

Overlays

Dropdown menu with options and dividers.

import { Menu, Button } from '@gusvega/ui';

<Menu
  trigger={<Button size="sm">Actions</Button>}
  options={[
    { label: 'Edit', value: 'edit' },
    { label: 'Share', value: 'share' },
    { label: 'Delete', value: 'delete', divider: true },
  ]}
/>

ColorModeToggle

Theming

Toggle between light and dark mode. Requires ThemeProvider at the app root.

Click to toggle light / dark
import { ThemeProvider, ColorModeToggle, useTheme } from '@gusvega/ui';

// 1. Wrap your app
<ThemeProvider defaultColorMode="system">
  <App />
</ThemeProvider>

// 2. Place the toggle anywhere inside
<ColorModeToggle />

// 3. Or use the hook directly
const { resolvedColorMode, toggleColorMode } = useTheme();

Heading

Typography

Semantic heading component. Level maps to h1–h6 with sensible default sizes and weights.

Page Title (h1)

Section Heading (h2)

Subsection (h3)

Card Title (h4)

Label (h5)
Caption (h6)
import { Heading } from '@gusvega/ui';

<Heading level={1}>Page Title</Heading>
<Heading level={2}>Section Heading</Heading>
<Heading level={3}>Subsection</Heading>
<Heading level={4} weight="medium">Card Title</Heading>

// Override size independently of level
<Heading level={1} size="2xl">Smaller h1</Heading>

Text

Typography

Body text with variant, size, weight, and polymorphic as prop.

Default body text

Muted helper text

Error message

Success message

Warning message

Informational note

Small medium weight

import { Text } from '@gusvega/ui';

<Text>Default body text</Text>
<Text variant="muted">Muted helper text</Text>
<Text variant="destructive">Error message</Text>
<Text variant="success">Success message</Text>
<Text variant="warning">Warning message</Text>
<Text variant="info">Info message</Text>
<Text size="sm" weight="medium">Small medium text</Text>
<Text as="span" truncate>Long text that gets truncated...</Text>

Prose

Typography

Styled wrapper for markdown or MDX content. Handles all HTML elements with consistent typography.

Getting Started

Install the library with npm install @gusvega/ui and import the styles.

  • Import @gusvega/ui/style.css in your app entry
  • Wrap with ThemeProvider for dark mode
  • Import and use any component
Built on Tailwind CSS with zero runtime overhead.
import { Prose } from '@gusvega/ui';

<Prose>
  <h2>Getting Started</h2>
  <p>Install the library with <code>npm install @gusvega/ui</code>.</p>
  <ul>
    <li>Import the CSS</li>
    <li>Add ThemeProvider</li>
    <li>Use components</li>
  </ul>
  <blockquote>Built on Tailwind CSS with zero runtime overhead.</blockquote>
</Prose>

FadeIn

Animations

Fade + slide up when scrolled into view. FadeInStagger sequences children.

First item fades in
Second item follows
Third item last
import { FadeIn, FadeInStagger } from '@gusvega/ui';

<FadeIn>
  <Card>Fades in on scroll</Card>
</FadeIn>

<FadeInStagger stagger={100}>
  <div>Item 1</div>
  <div>Item 2</div>
  <div>Item 3</div>
</FadeInStagger>

SlideIn

Animations

Slide from any direction with a fade. Uses intersection observer.

from left
from right
from top
from bottom
import { SlideIn } from '@gusvega/ui';

<SlideIn from="left">
  <div>Slides from left</div>
</SlideIn>

<SlideIn from="right" delay={100}>
  <div>Slides from right</div>
</SlideIn>

<SlideIn from="bottom" delay={200}>
  <div>Slides from bottom</div>
</SlideIn>

ScaleIn

Animations

Scale up from a smaller size with spring easing. Great for cards and images.

Scales in with spring easing
import { ScaleIn } from '@gusvega/ui';

<ScaleIn from={0.85}>
  <Card>Scales in with spring</Card>
</ScaleIn>

BlurIn

Animations

Blur-to-sharp reveal with a subtle y-offset. Ideal for hero text.

Blur to sharp reveal

Animates opacity, blur, and y-offset simultaneously

import { BlurIn } from '@gusvega/ui';

<BlurIn blur={12} offset={10}>
  <Heading level={1}>Hero Title</Heading>
</BlurIn>

CountUp

Animations

Animated number counter that triggers when scrolled into view.

0+
Users
0.0%
Uptime
$0
Revenue
import { CountUp } from '@gusvega/ui';

<CountUp from={0} to={10000} suffix="+" separator="," />
<CountUp from={0} to={99.9} decimals={1} suffix="%" duration={2000} />
<CountUp from={0} to={4200} prefix="$" separator="," />

TypeWriter

Animations

Character-by-character text animation. Cycles through an array of words.

I am a
import { TypeWriter } from '@gusvega/ui';

// Single word
<TypeWriter words="Hello, World!" speed={60} />

// Cycling words
<TypeWriter
  words={['developer', 'designer', 'builder']}
  speed={80}
  loop
/>

LineChart

Charts

Multi-series line chart. Requires recharts peer dependency.

import { LineChart } from '@gusvega/ui';

<LineChart
  data={data}
  xKey="month"
  series={[
    { key: 'revenue', label: 'Revenue' },
    { key: 'users', label: 'Users' },
  ]}
/>

BarChart

Charts

Grouped or stacked bar chart with rounded corners.

import { BarChart } from '@gusvega/ui';

<BarChart
  data={data}
  xKey="month"
  series={[{ key: 'revenue', label: 'Revenue' }]}
  rounded
/>

// Stacked
<BarChart data={data} xKey="month" series={[...]} stacked />

AreaChart

Charts

Area chart with gradient fill. Supports stacked mode.

import { AreaChart } from '@gusvega/ui';

<AreaChart
  data={data}
  xKey="month"
  series={[
    { key: 'revenue', label: 'Revenue' },
    { key: 'users', label: 'Users' },
  ]}
  fillOpacity={0.15}
/>

PieChart

Charts

Pie and donut charts for proportional data.

Pie

Donut

import { PieChart } from '@gusvega/ui';

<PieChart
  data={[
    { name: 'Mobile', value: 55 },
    { name: 'Desktop', value: 32 },
    { name: 'Tablet', value: 13 },
  ]}
  donut
/>

SparkLine

Charts

Compact inline chart for trends in tables or stat cards.

line
area
bar
import { SparkLine } from '@gusvega/ui';

// In a stat card
<SparkLine data={[4, 7, 2, 9, 5, 11, 8]} type="area" height={40} />
<SparkLine data={[4, 7, 2, 9, 5, 11, 8]} type="bar" height={40} />
<SparkLine data={[4, 7, 2, 9, 5, 11, 8]} type="line" height={40} />

Grid

Layout

CSS grid with responsive column counts. GridItem supports colSpan and rowSpan.

One
Two
Three
Four
Five
Six
Span 2
Span 1
import { Grid, GridItem } from '@gusvega/ui';

// Fixed columns
<Grid cols={3} gap={4}>
  <div>Col 1</div>
  <div>Col 2</div>
  <div>Col 3</div>
</Grid>

// Responsive
<Grid cols={{ base: 1, md: 2, lg: 3 }} gap={4}>
  <GridItem colSpan={2}>Wide item</GridItem>
  <div>Normal</div>
</Grid>

AspectRatio

Layout

Preserves aspect ratio for media containers. Supports presets and numeric ratios.

16/9

16/9

4/3

4/3

1/1

1/1
import { AspectRatio } from '@gusvega/ui';

<AspectRatio ratio="16/9">
  <img src="..." className="w-full h-full object-cover rounded-lg" />
</AspectRatio>

// Custom ratio
<AspectRatio ratio={4 / 1}>
  <div className="w-full h-full bg-neutral-100 rounded-lg" />
</AspectRatio>

ScrollArea

Layout

Overflow container with a styled thin scrollbar using semantic tokens.

Vertical

Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Item 7
Item 8
Item 9
Item 10
Item 11
Item 12

Horizontal

Chip 1
Chip 2
Chip 3
Chip 4
Chip 5
Chip 6
Chip 7
Chip 8
Chip 9
Chip 10
import { ScrollArea } from '@gusvega/ui';

<ScrollArea height={200}>
  {items.map(item => <div key={item}>{item}</div>)}
</ScrollArea>

// Horizontal scroll
<ScrollArea direction="horizontal" height={80}>
  <div className="flex gap-2 w-max">...</div>
</ScrollArea>

Sticky

Layout

Sticky-positioned wrapper. top, bottom, and zIndex props.

Sticky Header (top: 0)
Scrollable content row 1
Scrollable content row 2
Scrollable content row 3
Scrollable content row 4
Scrollable content row 5
Scrollable content row 6
Scrollable content row 7
Scrollable content row 8
import { Sticky } from '@gusvega/ui';

// Sticky sidebar header
<Sticky top={64} zIndex={10}>
  <nav>Always visible while scrolling</nav>
</Sticky>

NumberInput

Form

Numeric input with increment and decrement buttons. Clamps to min/max.

import { NumberInput } from '@gusvega/ui';

const [val, setVal] = useState(5);

<NumberInput value={val} onChange={setVal} min={0} max={100} step={1} />
<NumberInput value={val} onChange={setVal} step={0.5} />
<NumberInput value={val} onChange={setVal} disabled />

PinInput

Form

Individual character boxes for OTP and PIN codes. Supports paste, arrow keys, masking.

6-digit OTP

Value:

import { PinInput } from '@gusvega/ui';

const [pin, setPin] = useState('');

// 6-digit OTP
<PinInput
  length={6}
  value={pin}
  onChange={setPin}
  onComplete={(v) => console.log('Done:', v)}
/>

// Masked 4-digit PIN
<PinInput length={4} value={pin} onChange={setPin} mask />

TagInput

Form

Type to add tags. Press Enter or comma to confirm. Backspace removes last tag.

reacttypescript

2 / 8

import { TagInput } from '@gusvega/ui';

const [tags, setTags] = useState(['react', 'typescript']);

<TagInput
  value={tags}
  onChange={setTags}
  placeholder="Add tag..."
  maxTags={8}
/>

ColorPicker

Form

Color swatch with hex text input. Click the swatch to open the native color picker.

import { ColorPicker } from '@gusvega/ui';

const [color, setColor] = useState('#6366f1');

<ColorPicker value={color} onChange={setColor} />

// Swatch only
<ColorPicker value={color} onChange={setColor} showInput={false} />

FileInput

Form

Simple styled file button that triggers a hidden file input. For full drag-and-drop see FileUpload.

import { FileInput } from '@gusvega/ui';

<FileInput
  label="Choose file"
  accept="image/*"
  onChange={(files) => console.log(files)}
/>

<FileInput label="Upload CSV" accept=".csv" disabled />

ProgressRing

Feedback

Circular progress indicator. Shows percentage completion in a ring.

25%50%72%100%
import { ProgressRing } from '@gusvega/ui';

<ProgressRing value={72} />
<ProgressRing value={45} size="sm" />
<ProgressRing value={100} />