https://github.com/yelbolt/unoff-ui
Unoff UI is a comprehensive library of UI components designed specifically for building Figma, Penpot, Sketch and Framer plugins.
https://github.com/yelbolt/unoff-ui
figma-plugin framer-plugin penpot-plugin sketch-plugin ui-components
Last synced: 3 months ago
JSON representation
Unoff UI is a comprehensive library of UI components designed specifically for building Figma, Penpot, Sketch and Framer plugins.
- Host: GitHub
- URL: https://github.com/yelbolt/unoff-ui
- Owner: yelbolt
- License: mit
- Created: 2024-05-07T12:42:43.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2026-03-30T17:30:55.000Z (3 months ago)
- Last Synced: 2026-03-30T17:39:48.597Z (3 months ago)
- Topics: figma-plugin, framer-plugin, penpot-plugin, sketch-plugin, ui-components
- Language: SCSS
- Homepage: https://ui.unoff.dev
- Size: 5.72 MB
- Stars: 1
- Watchers: 1
- Forks: 1
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README
    

# Unoff UI
Unoff UI is a comprehensive library of UI components designed specifically for building Figma, Penpot, and Sketch plugins. It leverages modern tools and frameworks to ensure a seamless development experience.

## Features
- **Built with React**: A popular JavaScript library for building user interfaces
- **Bundled with Vite**: Fast and optimized build tool for modern web projects
- **Tested with Vitest**: Ensures reliability and robustness of components with interaction tests
- **Exposed with Storybook**: Interactive UI component explorer for easy development and testing
- **Design tokens with Terrazzo**: Theme management using design tokens for consistent styling across platforms. [View Terrazzo Guide](./docs/terrazzo-guide.md)
- **Theme Generator**: Create custom themes easily with the [Theme Generator](./docs/theme-generator.md) tool based on Figma theme structure
- **SCSS Builder**: Generate theme-specific SCSS files from tokens using the build-scss script, with support for building components across all themes
- **Interaction Testing**: Automated interaction tests for all components using Storybook play functions.
## Installation
To install Unoff UI, use npm or yarn:
```bash
npm install @unoff/ui
# or
yarn add @unoff/ui
```
## Testing
Unoff UI comes with comprehensive interaction tests for all components:
```bash
# Run only Storybook interaction tests
npm run test:storybook
```
Tests can also be run directly in Storybook UI:
1. Start Storybook: `npm run storybook`
2. Open the Tests panel in the sidebar
3. Click "Run all" to execute all interaction tests
## Theme Development Tools
Unoff UI provides powerful tools for creating and managing custom themes:
### Theme Generator
Create new themes based on existing design systems (Sketch, Figma UI, etc.) with a single command:
```bash
npm run create:theme
```
The Theme Generator automates the creation of all necessary files and configurations:
- Tokens JSON files
- Terrazzo configuration
- Storybook integration
- SCSS imports
[Learn more about the Theme Generator](./docs/theme-generator.md)
### SCSS Builder
Generate theme-specific SCSS files from design tokens with these commands:
```bash
# List available themes and components
npm run scss:list
# Build all SCSS files
npm run scss:build
# Build SCSS for a specific theme
npm run scss:build theme=themeName
# Build SCSS for a specific component across all themes
npm run scss:build component=componentName
# Build SCSS for a specific component within a specific theme
npm run scss:build theme=themeName component=componentName
# Build specific token types across all themes
npm run scss:build text
npm run scss:build color
npm run scss:build icon
npm run scss:build type
# Build specific token types for a specific theme
npm run scss:build theme=themeName text
npm run scss:build theme=themeName color
```
## Usage
### Slots
#### Bar
```tsx
import { Bar } from '@unoff/ui'
function App() {
return (
Left very long text that may be truncated}
rightPartSlot={
Right very long text that may be truncated}
truncate={['LEFT', 'RIGHT']}
padding="12px"
/>
)
}
```
#### Form Item
```tsx
import { FormItem } from '@unoff/ui'
import { Input } from '@unoff/ui'
function App() {
return (
)
}
```
#### Section
```tsx
import { Section } from '@unoff/ui'
function App() {
return (
Section content goes here
)
}
```
#### Drawer
```tsx
import { Drawer } from '@unoff/ui'
function App() {
return (
console.log('Drawer closed')}
>
Drawer content goes here
)
}
```
### Actions
#### Primary Button
```tsx
import { Button } from '@unoff/ui'
function App() {
return (
console.log('Primary button clicked')}
/>
)
}
```
#### Secondary Button
```tsx
import { Button } from '@unoff/ui'
function App() {
return (
console.log('Secondary button clicked')}
/>
)
}
```
#### Tertiary Button
```tsx
import { Button } from '@unoff/ui'
function App() {
return (
console.log('Tertiary button clicked')}
/>
)
}
```
#### Destructive Button
```tsx
import { Button } from '@unoff/ui'
function App() {
return (
console.log('Destructive button clicked')}
/>
)
}
```
#### Icon Button
```tsx
import { Button } from '@unoff/ui'
function App() {
return (
console.log('Icon button clicked')}
/>
)
}
```
#### Segmented Control
```tsx
import { SegmentedControl } from '@unoff/ui'
function App() {
return (
console.log(e.currentTarget.dataset.feature)}
/>
)
}
```
### Inputs
#### Short Text Input
```tsx
import { Input } from '@unoff/ui'
function App() {
return (
console.log(e.target.value)}
/>
)
}
```
#### Long Text Input
```tsx
import { Input } from '@unoff/ui'
function App() {
return (
console.log(e.target.value)}
/>
)
}
```
#### Color Picker
```tsx
import { Input } from '@unoff/ui'
function App() {
return (
console.log(e.target.value)}
/>
)
}
```
#### Numeric Stepper
```tsx
import { Input } from '@unoff/ui'
function App() {
return (
console.log(e.target.value)}
/>
)
}
```
### Dropdown
#### Single Selection
```tsx
import { Dropdown } from '@unoff/ui'
function App() {
return (
console.log(value)}
/>
)
}
```
#### Multiple Selection
```tsx
import { Dropdown } from '@unoff/ui'
function App() {
return (
console.log(values)}
/>
)
}
```
### Sliders
#### Simple Slider
```tsx
import { SimpleSlider } from '@unoff/ui'
function App() {
return (
console.log(value)}
/>
)
}
```
#### Multiple Slider
```tsx
import { MultipleSlider } from '@unoff/ui'
function App() {
return (
console.log(value)}
/>
)
}
```
### Dialogs
#### Simple Dialog
```tsx
import { Dialog } from '@unoff/ui'
function App() {
return (
console.log('Delete action'),
},
secondary: {
label: 'Cancel',
action: () => console.log('Cancel action'),
},
}}
pin="CENTER"
onClose={() => console.log('Dialog closed')}
>
Deleting this item will remove it permanently.
)
}
```
#### Form Dialog
```tsx
import { Dialog } from '@unoff/ui'
import { Input } from '@unoff/ui'
import { FormItem } from '@unoff/ui'
function App() {
return (
console.log('Submit action'),
},
}}
pin="CENTER"
onClose={() => console.log('Dialog closed')}
>
)
}
```
#### Loading Dialog
```tsx
import { Dialog } from '@unoff/ui'
function App() {
return (
console.log('Dialog closed')}
/>
)
}
```
### Lists
#### Simple List
```tsx
import { ActionsList } from '@unoff/ui'
function App() {
return (
console.log('Option 1 clicked'),
},
{
label: 'Option 2',
value: 'OPTION_2',
type: 'OPTION',
action: () => console.log('Option 2 clicked'),
},
{
label: 'Option 3',
value: 'OPTION_3',
type: 'OPTION',
action: () => console.log('Option 3 clicked'),
},
{
label: 'Option 4',
value: 'OPTION_4',
type: 'OPTION',
action: () => console.log('Option 4 clicked'),
},
]}
selected="OPTION_1"
/>
)
}
```
#### Grouped List
```tsx
import { ActionsList } from '@unoff/ui'
function App() {
return (
console.log('Option 1 clicked'),
},
{
label: 'Option 2',
value: 'OPTION_2',
type: 'OPTION',
action: () => console.log('Option 2 clicked'),
},
{
type: 'SEPARATOR',
},
{
label: 'Group 2',
type: 'TITLE',
},
{
label: 'Option 3',
value: 'OPTION_3',
type: 'OPTION',
action: () => console.log('Option 3 clicked'),
},
{
label: 'Option 4',
value: 'OPTION_4',
type: 'OPTION',
action: () => console.log('Option 4 clicked'),
},
]}
/>
)
}
```
#### Nested List
```tsx
import { ActionsList } from '@unoff/ui'
function App() {
return (
console.log('Option A.1 clicked'),
},
{
label: 'Option 2',
value: 'OPTION_A_2',
type: 'OPTION',
action: () => console.log('Option A.2 clicked'),
},
],
},
{
label: 'Group 2',
value: 'GROUP_2',
type: 'OPTION',
children: [
{
label: 'Option 1',
value: 'OPTION_B_1',
type: 'OPTION',
action: () => console.log('Option B.1 clicked'),
},
{
label: 'Option 2',
value: 'OPTION_B_2',
type: 'OPTION',
action: () => console.log('Option B.2 clicked'),
},
],
},
]}
/>
)
}
```
### Tags
#### Basic Chip
```tsx
import { Chip } from '@unoff/ui'
function App() {
return New
}
```
#### Chip with Color Indicator
```tsx
import { Chip, ColorChip } from '@unoff/ui'
function App() {
return (
}
rightSlot={
✔︎}
>
AA
)
}
```
### Assets
#### Icon
```tsx
import { Icon } from '@unoff/ui'
function App() {
return (
<>
{/* Pictogram Icon */}
{/* Letter Icon */}
>
)
}
```
#### Avatar
```tsx
import { Avatar } from '@unoff/ui'
function App() {
return (
<>
{/* Avatar with Image */}
{/* Default Avatar */}
>
)
}
```
#### Thumbnail
```tsx
import { Thumbnail } from '@unoff/ui'
function App() {
return (
)
}
```
## Testing
To run tests:
```bash
npm test
# or
yarn test
```
## License
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for more information.