design-system-eduno
Design System Eduno
A modern React component library built with TypeScript, Tailwind CSS, and Radix UI. Provides a comprehensive set of customizable and accessible UI components with dynamic theming support.
Installation
From npm (Production)
pnpm add design-system-eduno
# or
npm install design-system-eduno
# or
yarn add design-system-eduno
Local Development
For local development without publishing a new version, see Local Development Linking.
Usage
Basic Setup
import { Button, ThemeProvider } from 'design-system-eduno';
function App() {
return (
<ThemeProvider theme={{ primaryColor: "#023047" }}>
<Button variant="default">Click me</Button>
</ThemeProvider>
);
}
Theme Configuration
The library includes a dynamic theming system that accepts hex color values:
<ThemeProvider
theme={{
primaryColor: "#023047",
secondaryColor: "#fb8500"
}}
>
{/* Your app */}
</ThemeProvider>
Available Components
- Form Components: Button, Input, Label, Checkbox, Select, Switch, DatePicker
- Data Display: DataTable, Badge, Avatar, Typography, Card
- Layout: Sidebar, Stepper, FormContainer, ScrollArea
- Feedback: Dialog, Tooltip, PageLoader, Skeleton, ErrorField
- Navigation: Command, DropdownMenu, Collapsible, Popover
- Other: Calendar, ProfileImage, Searchbar
Development
Prerequisites
- Node.js 18+
- pnpm (recommended)
Setup
# Install dependencies
pnpm install
# Start development server
pnpm dev
# Start Storybook (component documentation)
pnpm storybook
Build
# Build the library
pnpm build
# Build in watch mode (for local development)
pnpm build:watch
# Build Storybook
pnpm build-storybook
Linting
pnpm lint
Local Development Linking
To use this design system in another project locally without publishing:
Option 1: pnpm link (Recommended)
In this design system repository:
pnpm build
pnpm link --global
In your consuming project:
pnpm link --global design-system-eduno
For active development (auto-rebuild on changes):
# In design system repo - run in a separate terminal
pnpm build:watch
To unlink:
# In consuming project
pnpm unlink design-system-eduno
# In design system repo (optional)
pnpm unlink --global
Option 2: Direct File Path
In your consuming project's package.json:
{
"dependencies": {
"design-system-eduno": "file:../path/to/eduno-design-system"
}
}
Then run pnpm install. Remember to run pnpm build in the design system whenever you make changes.
Option 3: pnpm workspace (for monorepos)
{
"dependencies": {
"design-system-eduno": "workspace:*"
}
}
Component Examples
Button
import { Button } from 'design-system-eduno';
<Button variant="default" size="lg">Primary Button</Button>
<Button variant="outline">Outline Button</Button>
<Button variant="destructive">Delete</Button>
<Button isLoading>Loading...</Button>
DataTable
import { DataTable } from 'design-system-eduno';
<DataTable
data={users}
columns={columns}
configuration={{
pagination: {
currentPage: 1,
rowsPerPage: 10,
total: 100,
onChangePage: (page) => console.log(page),
onChangeRows: (rows) => console.log(rows),
},
search: {
query: searchQuery,
onSearch: (value) => setSearchQuery(value),
onClearSearch: () => setSearchQuery(''),
},
}}
filterMode="server" // or "client"
density="default" // "compact" (36px rows) | "default" (44px) | "comfortable" (52px)
emptyState={{
title: 'Todavía no hay clientes',
description: 'Cuando agregues el primero va a aparecer acá.',
action: <Button size="sm">Agregar cliente</Button>,
}}
/>
Per-column layout lives in meta, so the header and its cells always agree:
const columns: TColumnDef<IUser>[] = [
// Keeps the identity column in view while the table scrolls sideways
{ id: 'name', header: 'Nombre', meta: { pinned: 'left' }, minSize: 220, cell },
// Right-aligned and tabular: digits line up by place value and never shift
{ id: 'total', header: 'Total', meta: { numeric: true }, cell },
{ id: 'status', header: 'Estado', meta: { align: 'center' }, cell },
// `headerLabel` is what the column menu and the sort announcement read
{ id: 'actions', header: 'Acciones', meta: { align: 'right', headerLabel: 'Acciones' }, cell },
];
tableHeight is a max-h-* class: the surface shrinks to its rows on a short page and
scrolls under a sticky header on a long one. The empty state tells "nothing here yet"
apart from "nothing matched", and only the first case needs emptyState — the second one
offers to clear the search on its own.
Design decisions baked in:
- The search and the columns menu sit above the surface with real air between them and the table. Tucked inside, the field looked welded to the column titles.
- Header and footer share a tinted rail (
.ed-dt-rail), so the data sits between two edges. A header floating on the same surface as the rows reads as unfinished. - Column titles are 12px / 500 / uppercase with 0.025em tracking — the label signal, calibrated: 11px reads cheap and double the tracking over-spaces.
- Row height lives on the cell, so a 32px action trigger doesn't quietly decide the density. The default is 52px because these tables are read a row at a time to decide something; 36-44px is for auditing hundreds of rows.
- The footer spends the minimum chrome: the range, a rows-per-page menu, and prev/next only when there is more than one page. First/last are gone — four arrows for a two-arrow job.
- Only the sorted column shows a solid arrow; idle columns keep a faint one so touch users still see the affordance.
Dialog
import { Dialog } from 'design-system-eduno';
<Dialog
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Dialog Title"
description="Dialog description"
size="md"
>
{/* Dialog content */}
</Dialog>
Component Structure
All components must follow this folder structure:
ComponentName/
├── index.ts # Exports component and types
├── ComponentName.tsx # Main component implementation
├── types/
│ └── index.ts # TypeScript interfaces and types
├── constants/
│ └── index.ts # Constants (if needed)
├── components/ # Subcomponents (if needed)
│ └── SubComponent/
│ ├── index.ts
│ ├── SubComponent.tsx
│ └── types/
│ └── index.ts
├── storybook/
│ └── ComponentName.stories.tsx
└── tests/ # Tests (if needed)
└── ComponentName.test.tsx
Structure Guidelines
- index.ts: Always export the component and its types
- ComponentName.tsx: Main component using
React.forwardRefwhen wrapping DOM elements - types/index.ts: All TypeScript interfaces and types for the component
- constants/index.ts: Only if the component requires constants
- components/: Subcomponents follow the same structure recursively
- storybook/: Storybook stories in their own folder
- tests/: Test files in their own folder
Technology Stack
- React 19 - UI library
- TypeScript - Type safety
- Tailwind CSS - Utility-first styling
- Radix UI - Accessible component primitives
- Vite - Build tool
- Storybook - Component documentation
- class-variance-authority - Variant management
- TanStack Table - Powerful tables
- React Aria Components - Accessible interactions
- Framer Motion - Animations
Publishing
To publish a new version:
# Build the library
pnpm build
# Update version in package.json
npm version patch # or minor, major
# Publish to npm (requires npm login)
npm publish
Requirements
- React: ^19.0.0 (peer dependency)
- React DOM: ^19.0.0 (peer dependency)
- Node.js: 18+
License
[Add your license here]
Contributing
[Add contributing guidelines here]