npm package: @rufous/ui
Description: Experimental: A lightweight React UI component library (Beta)
Latest version: 0.3.12

# Rufous UI

A polished React component library built with design tokens, TypeScript, and zero runtime dependencies beyond React. Every component supports the `sx` prop for scoped style overrides.

**Live Showcase → [ui.dev.rufous.com](https://ui.dev.rufous.com/)**

---

## Installation

```bash
npm install @rufous/ui
# or
yarn add @rufous/ui
```

---

## Setup

Import the global styles once in your app entry:

```tsx
import "@rufous/ui/main.css";
```

Wrap your app with the theme provider:

```tsx
import { RufousThemeProvider } from "@rufous/ui";
import "@rufous/ui/main.css";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <RufousThemeProvider>
    <App />
  </RufousThemeProvider>
);
```

---

## Components

### Buttons

```tsx
import { StandardButton, AddButton, SubmitButton, CancelButton } from "@rufous/ui";

<StandardButton onClick={fn}>Standard</StandardButton>
<AddButton onClick={fn}>Add Item</AddButton>
<SubmitButton onClick={asyncFn}>Save</SubmitButton>
<CancelButton onClick={fn}>Cancel</CancelButton>
```

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `onClick` | `() => void \| Promise<void>` | — | Supports async; shows loader until resolved |
| `isLoading` | `boolean` | `false` | External loading state control |
| `disabled` | `boolean` | `false` | Disables the button |
| `sx` | `SxProp` | — | Scoped style overrides |

---

### TextField

```tsx
import { TextField } from "@rufous/ui";

<TextField
  label="Email"
  value={email}
  onChange={(e) => setEmail(e.target.value)}
  variant="outlined"
  required
/>
```

| Prop | Type | Default |
|------|------|---------|
| `label` | `string` | — |
| `variant` | `'outlined' \| 'filled' \| 'standard'` | `'outlined'` |
| `size` | `'small' \| 'medium'` | `'medium'` |
| `color` | `'primary' \| 'secondary' \| 'error' \| 'success' \| 'warning' \| 'info'` | `'primary'` |
| `error` | `boolean` | `false` |
| `helperText` | `ReactNode` | — |
| `fullWidth` | `boolean` | `false` |
| `InputProps` | `{ startAdornment?, endAdornment? }` | — |
| `sx` | `SxProp` | — |

---

### Select

```tsx
import { Select } from "@rufous/ui";

<Select
  options={[{ value: "us", label: "United States" }]}
  value={country}
  onChange={setCountry}
  label="Country"
/>

// Multiple selection
<Select options={options} value={selected} onChange={setSelected} multiple label="Tags" />
```

---

### DateField / DateRangeField

```tsx
import { DateField, DateRangeField } from "@rufous/ui";

<DateField label="Start Date" value={date} onChange={setDate} />

<DateRangeField
  label="Date Range"
  value={range}
  onChange={setRange}
/>
```

---

### Autocomplete

```tsx
import { Autocomplete } from "@rufous/ui";

<Autocomplete
  options={["React", "Vue", "Angular"]}
  value={val}
  onChange={setVal}
  label="Framework"
/>
```

---

### Checkbox

```tsx
import { Checkbox } from "@rufous/ui";

<Checkbox
  label="I agree to the terms"
  checked={checked}
  onChange={setChecked}
/>
```

---

### Dialog

```tsx
import { BaseDialog } from "@rufous/ui";

<BaseDialog
  open={open}
  title="Confirm Delete"
  onClose={onClose}
  onConfirm={onConfirm}
  showCancelButton
  fullWidth
>
  <p>Are you sure you want to delete this item?</p>
</BaseDialog>
```

| Prop | Type | Description |
|------|------|-------------|
| `open` | `boolean` | Controls visibility |
| `title` | `string` | Dialog title |
| `onClose` | `() => void` | Close handler |
| `onConfirm` | `() => void \| Promise<void>` | Async confirm; shows loader |
| `showCancelButton` | `boolean` | Show cancel button |
| `fullWidth` | `boolean` | Full container width |
| `sx` | `SxProp` | Scoped style overrides |

---

### Slider

```tsx
import { Slider } from "@rufous/ui";

<Slider label="Volume" value={vol} onChange={setVol} valueLabelDisplay="auto" />

// Range
<Slider label="Price" value={[20, 80]} onChange={setRange} range min={0} max={200} />
```

---

### Switch

```tsx
import { Switch } from "@rufous/ui";

<Switch checked={on} onChange={setOn} label="Enable notifications" />
```

---

### RadioGroup

```tsx
import { RadioGroup, Radio } from "@rufous/ui";

<RadioGroup label="Payment" options={options} value={val} onChange={setVal} />

// Composed
<RadioGroup value={val} onChange={setVal}>
  <Radio value="card" label="Credit Card" />
  <Radio value="paypal" label="PayPal" />
</RadioGroup>
```

---

### Rating

```tsx
import { Rating } from "@rufous/ui";

<Rating value={stars} onChange={setStars} />
<Rating value={4.5} precision={0.5} readOnly />
```

---

### ToggleButton

```tsx
import { ToggleButtonGroup, ToggleButton } from "@rufous/ui";

// Exclusive (radio)
<ToggleButtonGroup value={alignment} onChange={setAlignment} exclusive>
  <ToggleButton value="left">Left</ToggleButton>
  <ToggleButton value="center">Center</ToggleButton>
  <ToggleButton value="right">Right</ToggleButton>
</ToggleButtonGroup>

// Multi-select
<ToggleButtonGroup value={formats} onChange={setFormats}>
  <ToggleButton value="bold"><strong>B</strong></ToggleButton>
  <ToggleButton value="italic"><em>I</em></ToggleButton>
</ToggleButtonGroup>
```

---

### Avatar

```tsx
import { Avatar, AvatarGroup } from "@rufous/ui";

<Avatar src="/photo.jpg" alt="Alice Johnson" size="medium" />
<Avatar alt="Alice Johnson" color="#a41b06" />  // initials fallback

<AvatarGroup max={4}>
  <Avatar alt="Alice" />
  <Avatar alt="Bob" color="#1565c0" />
</AvatarGroup>
```

---

### Chip

```tsx
import { Chip } from "@rufous/ui";

<Chip label="React" color="primary" />
<Chip label="Remove me" color="error" onDelete={() => {}} />
<Chip label="Click me" color="default" variant="outlined" onClick={fn} />
```

---

### Divider

```tsx
import { Divider } from "@rufous/ui";

<Divider />
<Divider variant="middle" />
<Divider textAlign="center">OR</Divider>
<Divider orientation="vertical" flexItem />
```

---

### List

```tsx
import { List, ListItem, ListItemText, ListItemButton, ListItemIcon, ListSubheader } from "@rufous/ui";

<List subheader={<ListSubheader>Inbox</ListSubheader>}>
  <ListItemButton selected onClick={fn}>
    <ListItemIcon>📩</ListItemIcon>
    <ListItemText primary="Messages" secondary="3 unread" />
  </ListItemButton>
</List>
```

---

### Typography

```tsx
import { Typography } from "@rufous/ui";

<Typography variant="h1">Heading</Typography>
<Typography variant="body1" color="textSecondary">Paragraph text</Typography>
<Typography variant="caption" gutterBottom>Caption</Typography>
```

---

### Skeleton

```tsx
import { Skeleton } from "@rufous/ui";

<Skeleton variant="text" width={200} />
<Skeleton variant="circular" width={40} height={40} />
<Skeleton variant="rounded" width={300} height={120} animation="wave" />
```

---

### Tooltip

```tsx
import { Tooltip } from "@rufous/ui";

<Tooltip title="Delete this item" placement="top" arrow>
  <button>Delete</button>
</Tooltip>
```

---

### Progress

```tsx
import { CircularProgress, RufousLogoLoader } from "@rufous/ui";

<CircularProgress size={36} />
<RufousLogoLoader />
```

---

### Box / Stack / Grid

```tsx
import { Box, Stack, Grid } from "@rufous/ui";

// Box — generic flex/layout container
<Box display="flex" gap={16} padding={24}>...</Box>

// Stack — 1D flex layout
<Stack direction="row" spacing={2}>
  <div>A</div>
  <div>B</div>
</Stack>

// Grid — 12-column responsive grid
<Grid container spacing={3}>
  <Grid item xs={12} md={6}>Main</Grid>
  <Grid item xs={12} md={6}>Sidebar</Grid>
</Grid>
```

---

### Paper / Card / Accordion

```tsx
import { Paper, Card, CardContent, CardHeader, CardActions, Accordion, AccordionSummary, AccordionDetails } from "@rufous/ui";

<Paper elevation={3}>Content</Paper>

<Card>
  <CardHeader title="Title" subheader="Subtitle" />
  <CardContent>Body</CardContent>
  <CardActions><button>Action</button></CardActions>
</Card>

<Accordion>
  <AccordionSummary>FAQ Question</AccordionSummary>
  <AccordionDetails>Answer text here.</AccordionDetails>
</Accordion>
```

---

### Tabs

```tsx
import { Tabs, Tab } from "@rufous/ui";

<Tabs value={tab} onChange={setTab}>
  <Tab value="home" label="Home" />
  <Tab value="settings" label="Settings" />
</Tabs>
```

---

### Breadcrumbs

```tsx
import { Breadcrumbs } from "@rufous/ui";

<Breadcrumbs separator="›">
  <a href="/">Home</a>
  <a href="/products">Products</a>
  <span>Current</span>
</Breadcrumbs>
```

---

### Stepper

```tsx
import { Stepper, Step, StepLabel, StepContent } from "@rufous/ui";

<Stepper activeStep={activeStep}>
  {steps.map((label) => (
    <Step key={label}>
      <StepLabel>{label}</StepLabel>
    </Step>
  ))}
</Stepper>
```

---

### Menu

```tsx
import { Menu, MenuItem, MenuList, MenuDivider } from "@rufous/ui";

<Menu open={open} anchorEl={anchorRef.current} onClose={() => setOpen(false)}>
  <MenuList>
    <MenuItem icon="👤">Profile</MenuItem>
    <MenuItem icon="⚙️">Settings</MenuItem>
    <MenuDivider />
    <MenuItem icon="🚪">Sign Out</MenuItem>
  </MenuList>
</Menu>
```

---

### Drawer

```tsx
import { Drawer } from "@rufous/ui";

<Drawer open={open} onClose={() => setOpen(false)} anchor="right" width={320}>
  <div style={{ padding: 24 }}>Drawer content</div>
</Drawer>
```

---

### Snackbar

```tsx
import { Snackbar } from "@rufous/ui";

<Snackbar
  open={open}
  onClose={() => setOpen(false)}
  message="Changes saved successfully"
  severity="success"
  autoHideDuration={4000}
  anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
/>
```

---

### Link

```tsx
import { Link } from "@rufous/ui";

<Link href="/docs" color="primary">Documentation</Link>
<Link component="button" color="primary" onClick={fn}>Trigger action</Link>
```

---

### Popper / Popover

```tsx
import { Popper, Popover } from "@rufous/ui";

// Popper — no backdrop, low-level
<Popper open={open} anchorEl={anchorRef.current} placement="bottom-start">
  <div>Custom dropdown content</div>
</Popper>

// Popover — backdrop, closes on outside click
<Popover open={open} anchorEl={anchorRef.current} onClose={() => setOpen(false)}>
  <div style={{ padding: 20 }}>Popover content</div>
</Popover>
```

---

### Transitions

```tsx
import { Fade, Slide, Grow, Collapse, Zoom } from "@rufous/ui";

<Fade in={show}><div>Fades in/out</div></Fade>
<Slide in={show} direction="up"><div>Slides in from bottom</div></Slide>
<Grow in={show}><div>Grows from center</div></Grow>
<Collapse in={show}><div>Collapses vertically</div></Collapse>
<Zoom in={show}><div>Zooms in/out</div></Zoom>
```

---

### Icons

```tsx
import { CopyIcon, EditIcon, TrashIcon, DownloadIcon } from "@rufous/ui";

<CopyIcon onClick={() => copy(text)} />
<EditIcon />
<TrashIcon />
<DownloadIcon />
```

---

## The `sx` Prop

Every component accepts an `sx` prop for scoped, per-instance style overrides using nested CSS selectors. Values are injected as a scoped `<style>` tag — no inline style conflicts.

```tsx
import { TextField } from "@rufous/ui";
import type { SxProp } from "@rufous/ui";

<TextField
  label="Search"
  sx={{
    borderRadius: "12px",
    "& .rf-text-field__input": { fontSize: "0.9rem" },
    "&:hover": { boxShadow: "0 0 0 3px rgba(164,27,6,0.15)" },
  }}
/>
```

---

## License

MIT © Rufous UI