@decidrcn/ui
Version:
Decidr UI - A comprehensive React component library built with shadcn/ui and Radix UI. Supports React, Next.js, Rails, and works with npm, yarn, and pnpm.
437 lines (341 loc) • 10.6 kB
Markdown
# Decidr UI
A comprehensive React component library built with shadcn/ui and Radix UI, designed for modern web applications including Rails apps using Slim templates.
## Monorepo Structure
This project uses a **pnpm monorepo** structure:
- **Root**: Manages workspace dependencies and scripts
- **packages/ui**: The Decidr UI component library (this package)
- **apps/demo**: Demo app to showcase components
All packages now use **React 19** for consistency and future-proofing.
## Features
- 🎨 **Beautiful Design**: Built on shadcn/ui with Tailwind CSS
- ♿ **Accessible**: Powered by Radix UI primitives
- 🎯 **Type Safe**: Full TypeScript support
- 🚀 **Fast**: Optimized for performance
- 🎪 **Customizable**: Extensive theming and customization options
- 📱 **Responsive**: Mobile-first design approach
- ⚛️ **React19 Ready**: Fully compatible with React19🛤️ **Rails Compatible**: Works seamlessly with Rails applications using Slim templates
## Installation
### For React/Next.js Applications
```bash
npm install @decidrcn/ui
# or
yarn add @decidrcn/ui
# or
pnpm add @decidrcn/ui
```
### For Rails Applications1 **Add to your Gemfile:**
```ruby
gemjsbundling-rails
gem cssbundling-rails'
```2nstall the package:**
```bash
yarn add @decidrcn/ui
# or
npm install @decidrcn/ui
```
3. **Configure your build tool (esbuild, webpack, or Vite):**
**With esbuild:**
```javascript
// config/esbuild.config.js
const esbuild = require('esbuild')
esbuild.build({
entryPoints: ['app/javascript/application.js],
bundle: true,
outdir: app/assets/builds',
format:esm,
plugins: ],
external: ['@decidrcn/ui']
})
```
**With Vite:**
```javascript
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: react()],
build: {
rollupOptions:[object Object] external: ['@decidrcn/ui']
}
}
})
```
4**Import styles in your application:**
```javascript
// app/javascript/application.js
import '@decidrcn/ui/styles'
```
## Usage
### React/Next.js Quick Start
```tsx
import { Button } from@decidrcn/ui;
function App() {
return (
<Button variant="default>
Hello Decidr UI!
</Button>
);
}
```
### Rails with Slim Templates
1p React components in your Rails app:**
```javascript
// app/javascript/components/DecidrButton.jsx
import React from 'react'
import { Button } from '@decidrcn/ui'
export default function DecidrButton({ children, variant = 'default, ...props }) {
return (
<Button variant={variant}[object Object]...props}>
{children}
</Button>
)
}
```2 in Slim templates:**
```slim
/ app/views/layouts/application.html.slim
doctype html
html
head
title My Rails App
= csrf_meta_tags
= csp_meta_tag
= stylesheet_link_tag "application",data-turbo-track": "reload
= javascript_importmap_tags
body
/ Your content here
= yield
/ app/views/pages/home.html.slim
.container
h1 Welcome to My App
/ Using React components in Slim
#react-button-container data-props={variant: 'gradient', children:Click me!'}
/ Or with more complex props
#react-carousel-container data-props={products: @products.to_json}
```3t React components:**
```javascript
// app/javascript/application.js
import React from 'react'
import { createRoot } fromreact-dom/client'
import DecidrButton from./components/DecidrButton'
import ProductCarousel from './components/ProductCarousel'
// Mount components when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
// Mount button component
const buttonContainer = document.getElementById('react-button-container')
if (buttonContainer)[object Object]
const props = JSON.parse(buttonContainer.dataset.props || '{}')
const root = createRoot(buttonContainer)
root.render(React.createElement(DecidrButton, props))
}
// Mount carousel component
const carouselContainer = document.getElementById('react-carousel-container')
if (carouselContainer)[object Object]
const props = JSON.parse(carouselContainer.dataset.props || '{}')
const root = createRoot(carouselContainer)
root.render(React.createElement(ProductCarousel, props))
}
})
```
4d Slim integration with helpers:**
```ruby
# app/helpers/decidr_helper.rb
module DecidrHelper
def decidr_button(text, variant: 'default', **options)
content_tag :div,
,
id:decidr-button-#[object Object]SecureRandom.hex(4)}, data: {
component: 'DecidrButton',
props: {
children: text,
variant: variant,
**options
}.to_json
}
end
def decidr_carousel(items, **options)
content_tag :div,
,
id: decidr-carousel-#[object Object]SecureRandom.hex(4)}, data: {
component: 'ProductCarousel',
props: {
products: items,
**options
}.to_json
}
end
end
```
```slim
/ Using helpers in Slim
.container
= decidr_button "Submit Form", variant:gradient, size: lg"
= decidr_carousel @products, opts: { loop: true }
```
## Components
### Button
Enhanced button component with support for:
- Multiple variants (default, secondary, destructive, outline, ghost, link, success, warning, info, gradient)
- Custom colors and gradients
- Icon support (left, right, or icon-only)
- Radius customization
- Multiple sizes
- Default variant: White background with black text
- Gradient variant: Beautiful gradient with full radius support
```tsx
import { Button } from '@decidrcn/ui';
import[object Object]PlusIcon } from '@radix-ui/react-icons;
// Basic usage (white background, black text)
<Button>Click me</Button>
// Gradient button with full radius
<Button variant="gradient radius=full
Gradient Button
</Button>
// With custom colors
<Button bgColor="#8B5CF6 textColor="white hoverBgColor="#7C3AED>
Purple Button
</Button>
// With icons
<Button leftIcon={<PlusIcon className="h-44/>}>
Add Item
</Button>
// Icon only
<Button size=icon" leftIcon={<PlusIcon className="h-4 w-4>} />
```
### Carousel
Flexible carousel component perfect for products and events:
```tsx
import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious } from @decidrcn/ui';
<Carousel className="w-full max-w-6 <CarouselContent>
{products.map((product) => (
<CarouselItem key={product.id} className=md:basis-1/2 lg:basis-1/3">
<ProductCard product={product} />
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
```
### Avatar
Avatar component with shape variants:
- Circle (default)
- Square
- Rounded variants
```tsx
import { Avatar, AvatarFallback, AvatarImage } from @decidrcn/ui';
<Avatar shape="circle">
<AvatarImage src="user.jpg" alt="User />
<AvatarFallback>JD</AvatarFallback>
</Avatar>
```
### DecidrHeader
Semantic header component with proper text sizes for different heading levels:
- H1: Large title (text-4xl, font-bold)
- H2: Section header (text-3xl, font-semibold)
- H3: Subsection header (text-2l, font-semibold)
- H4-H6aller headers with appropriate sizing
```tsx
import { DecidrHeader } from@decidrcn/ui';
// Different heading levels with automatic text sizing
<DecidrHeader as="h1>Main Page Title</DecidrHeader>
<DecidrHeader as=h2Section Title</DecidrHeader>
<DecidrHeader as="h3>Subsection Title</DecidrHeader>
// Custom styling
<DecidrHeader as="h2assName=text-blue-600>
Custom Styled Header
</DecidrHeader>
```
### DecidrBody
Body text component with different size variants for optimal readability:
- xs: Extra small (text-xs) - Fine print, metadata
- sm: Small (text-sm) - Captions, secondary info
- base: Default (text-base) - Standard paragraphs
- lg: Large (text-lg) - Important content, emphasis
- xl: Extra large (text-xl) - Prominent content sections
```tsx
import { DecidrBody } from@decidrcn/ui';
// Different text sizes
<DecidrBody size="xl">Prominent content section</DecidrBody>
<DecidrBody size="lg">Important information</DecidrBody>
<DecidrBody size="base">Standard paragraph text</DecidrBody>
<DecidrBody size="sm">Secondary information</DecidrBody>
<DecidrBody size="xs">Fine print and metadata</DecidrBody>
// Different HTML elements
<DecidrBody as=span>Inline text</DecidrBody>
<DecidrBody as=div">Block text</DecidrBody>
// Custom styling
<DecidrBody size="lg" className="text-blue-600 italic>Custom styled body text
</DecidrBody>
```
## Rails Integration Best Practices
### 1. Component Organization
```
app/javascript/
├── components/
│ ├── decidr/
│ │ ├── DecidrButton.jsx
│ │ ├── DecidrCarousel.jsx
│ │ └── DecidrCard.jsx
│ └── shared/
└── application.js
```
### 2. Data Flow
- Pass data from Rails controllers to React components via `data-props`
- Use JSON serialization for complex objects
- Keep React components stateless when possible
### 3. Styling
- Import Decidr UI styles in your main application file
- Use Tailwind CSS classes in Slim templates
- Customize theme variables in your CSS
### 4Performance
- Lazy load React components when needed
- Use Turbo for fast page transitions
- Bundle components efficiently
## Decidr Logo
The Decidr logo is available as a React component:
```tsx
import { Logo } from @decidrcn/ui';
<Logo className="w-20 h-5 text-primary" />
```
- Used in Storybook and throughout the design system
- SVG-based, theme-aware (uses `currentColor`)
- Easily resizable and styleable
## Development
### Running Storybook
```bash
pnpm storybook
```
This will start Storybook on `http://localhost:66re you can explore all components, including the Decidr logo.
### Building
```bash
pnpm build
```
### Testing
```bash
pnpm test
```
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests if applicable
5. Submit a pull request
## Third-Party Licenses & Attribution
This package is built with and depends on:
- [shadcn/ui](https://github.com/shadcn-ui/ui) — MIT License © shadcn
- [Radix UI](https://www.radix-ui.com/) — MIT License © WorkOS, Inc.
Both projects are licensed under the [MIT License](https://opensource.org/licenses/MIT).
Copies of their licenses are available in their respective repositories.
We thank the authors and contributors of these projects for their amazing work!
## License
MIT © Decidr Team