volca-drum-ui
v0.1.3
Published
Lightweight, accessible React component library designed for Figma Make integration
Downloads
407
Maintainers
Readme
@midi-volca-drum/ui-components
A lightweight, accessible React component library designed for Figma Make integration
Overview
A collection of production-ready React components extracted from the MIDI Volca Drum Editor project. Designed with Figma Make in mind, these components bridge the gap between design and code, making it easy to use production components in your Figma prototypes.
Key Features
- 🎨 Figma Make Ready - Designed to work seamlessly with Figma Make
- ♿ Accessible - WCAG 2.1 compliant with proper ARIA labels
- 🎹 Keyboard Navigation - Full keyboard support for all interactive components
- 🌗 Theme Support - CSS custom properties for easy theming
- 📦 Lightweight - ~8KB JS + ~13KB CSS (gzipped)
- ⚡ Tree-shakeable - Import only what you need
- 🔒 TypeScript - Full type definitions included
Installation
npm install @midi-volca-drum/ui-componentsPeer Dependencies
This library requires React 18 or higher:
npm install react@^18.0.0 react-dom@^18.0.0Quick Start
import { Button, Slider, Modal } from '@midi-volca-drum/ui-components';
import '@midi-volca-drum/ui-components/styles';
function App() {
const [value, setValue] = useState(64);
return (
<>
<Button variant="primary" onClick={() => console.log('Clicked!')}>
Click Me
</Button>
<Slider
id="volume"
label="Volume"
value={value}
onChange={setValue}
min={0}
max={127}
/>
</>
);
}Components
Accordion
Collapsible content sections with smooth animations.
import { Accordion } from '@midi-volca-drum/ui-components';
<Accordion
id="my-accordion"
title="Settings"
defaultExpanded={true}
direction="vertical"
>
<p>Your content here</p>
</Accordion>Props:
id(string, required) - Unique identifiertitle(string, required) - Header textdefaultExpanded(boolean) - Initial state (default: true)direction('vertical' | 'horizontal') - Collapse direction (default: 'vertical')className(string) - Additional CSS classeschildren(ReactNode) - Content to collapse/expand
Button
Versatile button component with multiple variants.
import { Button } from '@midi-volca-drum/ui-components';
<Button variant="primary" size="medium" onClick={handleClick}>
Click Me
</Button>Props:
variant('primary' | 'secondary' | 'text') - Visual style (default: 'primary')size('small' | 'medium' | 'large') - Button size (default: 'medium')disabled(boolean) - Disabled statetype('button' | 'submit' | 'reset') - HTML button typechildren(ReactNode, required) - Button content- All standard HTML button attributes
Modal
Accessible modal dialog with backdrop.
import { Modal } from '@midi-volca-drum/ui-components';
<Modal isOpen={isOpen} onClose={() => setIsOpen(false)} title="Settings">
<p>Modal content goes here</p>
</Modal>Props:
isOpen(boolean, required) - Visibility stateonClose(function, required) - Close handlertitle(string, required) - Modal titlechildren(ReactNode, required) - Modal content
RadioButton & RadioGroup
Radio button inputs with icon or text variants.
import { RadioGroup } from '@midi-volca-drum/ui-components';
const options = [
{ id: 'option1', value: '1', label: 'Option 1' },
{ id: 'option2', value: '2', label: 'Option 2' },
{ id: 'option3', value: '3', label: 'Option 3' },
];
<RadioGroup
name="my-radio-group"
options={options}
value={selectedValue}
onChange={setSelectedValue}
/>RadioGroup Props:
name(string, required) - Radio group nameoptions(RadioOption[], required) - Array of radio optionsvalue(string | number, required) - Selected valueonChange(function, required) - Change handlerclassName(string) - Additional CSS classesdisabled(boolean) - Disabled state
RadioOption:
id(string, required) - Unique identifiervalue(string | number, required) - Option valuelabel(string, required) - Display labelimageSrc(string) - Icon image URL (for icon variant)imageAlt(string) - Icon alt textvariant('icon' | 'text') - Display style (default: 'text')disabled(boolean) - Disabled state
Slider
Range input with visual feedback and custom formatting.
import { Slider } from '@midi-volca-drum/ui-components';
<Slider
id="volume"
label="Volume"
value={volume}
onChange={setVolume}
min={0}
max={127}
step={1}
formatValue={(v) => `${v}%`}
/>Props:
id(string, required) - Unique identifierlabel(string, required) - Slider labelvalue(number, required) - Current valueonChange(function, required) - Change handler:(value: number) => voidmin(number) - Minimum value (default: 0)max(number) - Maximum value (default: 127)step(number) - Step increment (default: 1)formatValue(function) - Custom value formatter:(value: number) => stringclassName(string) - Additional CSS classesdisabled(boolean) - Disabled state
Switch
Toggle switch for boolean settings.
import { Switch } from '@midi-volca-drum/ui-components';
<Switch
id="dark-mode"
label="Dark Mode"
checked={isDarkMode}
onChange={() => setIsDarkMode(!isDarkMode)}
/>Props:
id(string, required) - Unique identifierlabel(string, required) - Switch labelchecked(boolean, required) - Current stateonChange(function, required) - Toggle handler:() => voiddisabled(boolean) - Disabled state
Toast
Toast notifications for user feedback. This component wraps react-hot-toast with styled defaults.
import { toast } from '@midi-volca-drum/ui-components';
// Success toast
toast.success('Settings saved successfully!');
// Error toast
toast.error('Failed to connect to device');
// Info toast
toast('Processing your request...');
// Custom duration
toast.success('File uploaded', { duration: 5000 });
// With custom ID (prevents duplicates)
toast.success('Connection established', { id: 'midi-connect' });Toast API:
toast(message, options?)- Default toasttoast.success(message, options?)- Success toast with green indicatortoast.error(message, options?)- Error toast with red indicatortoast.loading(message, options?)- Loading toast with spinnertoast.custom(component, options?)- Custom toast componenttoast.dismiss(toastId?)- Dismiss specific or all toasts
Common Options:
duration(number) - Display duration in ms (default: 4000)position('top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right') - Toast positionid(string) - Unique ID to prevent duplicate toastsicon(ReactNode) - Custom iconstyle(CSSProperties) - Custom inline styles
Note: The Toast component uses react-hot-toast under the hood. You can import Toaster to customize the toast container:
import { Toaster } from 'react-hot-toast';
function App() {
return (
<>
<Toaster position="top-right" />
{/* Your app content */}
</>
);
}Theming & Customization
All components use CSS custom properties (CSS variables) for styling, making them fully themeable without modifying component code.
Theme Switching
The library supports multiple themes using the data-theme attribute approach. By default, components use dark theme colors. Override with light theme by setting data-theme="light" on the root element:
// Example theme switcher component
function ThemeToggle() {
const [isDark, setIsDark] = useState(true);
const toggleTheme = () => {
if (isDark) {
document.documentElement.dataset.theme = 'light';
} else {
delete document.documentElement.dataset.theme;
}
setIsDark(!isDark);
};
return (
<button onClick={toggleTheme}>
{isDark ? 'Switch to Light' : 'Switch to Dark'}
</button>
);
}Custom Theme Example
Create your own theme by defining CSS variables:
/* Dark theme (default) */
:root, :root:not([data-theme="light"]) {
--color-background-app: #141a24;
--color-background-elevated: #0f141f;
--color-text-title: #b2bfcc;
--color-text-subtitle: #99a6b2;
--color-stroke-cyan: #00b2f2;
}
/* Light theme */
:root[data-theme="light"] {
--color-background-app: #ffffff;
--color-background-elevated: #f8f9fa;
--color-text-title: #212529;
--color-text-subtitle: #495057;
--color-stroke-cyan: #00b2f2;
}
/* Custom brand theme */
:root[data-theme="brand"] {
--color-stroke-cyan: #ff6b35; /* Custom accent color */
--color-stroke-green: #4ecdc4;
--color-stroke-orange: #f7931e;
}Complete CSS Custom Properties Reference
Colors - Background
--color-background-app /* Main app background */
--color-background-elevated /* Elevated surfaces (cards, modals) */
--color-background-container /* Container backgrounds */
--color-background-card /* Card backgrounds */
--color-background-object /* UI object backgrounds */
--color-background-hover /* Hover state background */
--color-bg-surface /* Surface backgrounds */
--color-bg-base /* Base layer background */Colors - Text
--color-text-active /* Active/selected text */
--color-text-title /* Title text */
--color-text-body /* Body text */
--color-text-subtitle /* Subtitle text */
--color-text-muted /* Muted/secondary text */
--color-text-disabled /* Disabled text */Colors - Stroke/Accent
--color-stroke-green /* Success/green accent */
--color-stroke-green-overlay /* Green overlay/background */
--color-stroke-cyan /* Primary/cyan accent */
--color-stroke-cyan-overlay /* Cyan overlay/background */
--color-stroke-orange /* Warning/orange accent */
--color-stroke-orange-overlay /* Orange overlay/background */
--color-stroke-neutral /* Neutral borders/strokes */Colors - Buttons
--color-button-status-default /* Status button default */
--color-button-primary-default /* Primary button default */
--color-button-primary-hover /* Primary button hover */
--color-button-secondary-default /* Secondary button default */
--color-button-secondary-hover /* Secondary button hover */Colors - Other
--color-input-background /* Input field backgrounds */
--color-error-default /* Error state color */
--color-error-background /* Error background */Spacing
--spacing-xxs: 8px
--spacing-xs: 10px
--spacing-s: 12px
--spacing-m: 16px
--spacing-l: 20px
--spacing-xl: 24px
--spacing-xxl: 32px
--spacing-xxxl: 48pxBorder Radius
--border-radius-s: 6px
--border-radius-m: 8px
--border-radius-l: 12px
--border-radius-xl: 20pxTypography
/* Display */
--font-display-lg-size: 20px
--font-display-lg-weight: 700
--font-display-lg-line-height: 1.4
--font-display-lg-letter-spacing: -0.01em
/* Headings */
--font-heading-lg-size: 16px
--font-heading-lg-weight: 700
--font-heading-md-size: 14px
--font-heading-md-weight: 700
--font-heading-sm-size: 12px
--font-heading-sm-weight: 700
/* Body */
--font-body-lg-size: 16px
--font-body-lg-weight: 600
--font-body-md-size: 13px
--font-body-md-weight: 600
--font-body-sm-size: 12px
--font-body-sm-weight: 600
/* Labels */
--font-label-md-size: 12px
--font-label-md-weight: 700
--font-label-md-line-height: 1.4
--font-label-md-letter-spacing: 0.02em
/* Caption */
--font-caption-size: 10px
--font-caption-weight: 600
--font-caption-line-height: 1.4
--font-caption-letter-spacing: 0.02em
/* Font Family */
--font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serifPersistence
Theme preference can be persisted using localStorage:
function useTheme() {
const [theme, setTheme] = useState(() => {
return localStorage.getItem('theme') || 'dark';
});
useEffect(() => {
if (theme === 'light') {
document.documentElement.dataset.theme = 'light';
} else {
delete document.documentElement.dataset.theme;
}
localStorage.setItem('theme', theme);
}, [theme]);
return { theme, setTheme };
}Figma Make Integration
This library is specifically designed to work with Figma Make. Follow these steps to integrate:
- Install the library in your project
- Import components in your Figma Make setup
- Use production components in your Figma prototypes instead of mockups
See /docs/figma-make-setup.md for detailed integration guide.
Development
Setup
# Install dependencies
npm install
# Build library
npm run build
# Watch mode for development
npm run dev
# Type checking
npm run type-check
# Linting
npm run lint
npm run lint:fixProject Structure
src/
├── components/
│ ├── Accordion/
│ │ ├── Accordion.tsx
│ │ ├── Accordion.css
│ │ └── index.ts
│ ├── Button/
│ ├── Modal/
│ └── ...
├── styles/
│ └── index.css
└── index.tsBuilding
The library is built using Vite in library mode:
npm run buildOutput:
dist/index.js- ES module bundledist/index.d.ts- TypeScript declarationsdist/style.css- Component styles
Browser Support
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
Contributing
Contributions are welcome! This library was extracted from the MIDI Volca Drum Editor project.
Guidelines
- Follow existing code style
- Add TypeScript types for all props
- Include CSS with each component
- Test accessibility (keyboard navigation, ARIA labels)
- Keep components generic (no app-specific logic)
License
MIT © Hans Desmedt
Related Projects
- MIDI Volca Drum Editor - The parent project
- Figma Make - Use production code in Figma prototypes
Support
For issues and questions:
