{"id":47265261,"url":"https://github.com/yelbolt/unoff-ui","last_synced_at":"2026-04-01T17:49:23.056Z","repository":{"id":264323531,"uuid":"797226703","full_name":"yelbolt/unoff-ui","owner":"yelbolt","description":"Unoff UI is a comprehensive library of UI components designed specifically for building Figma, Penpot, Sketch and Framer plugins.","archived":false,"fork":false,"pushed_at":"2026-03-30T17:30:55.000Z","size":5996,"stargazers_count":1,"open_issues_count":5,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-30T17:39:48.597Z","etag":null,"topics":["figma-plugin","framer-plugin","penpot-plugin","sketch-plugin","ui-components"],"latest_commit_sha":null,"homepage":"https://ui.unoff.dev","language":"SCSS","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yelbolt.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-05-07T12:42:43.000Z","updated_at":"2026-03-30T17:30:43.000Z","dependencies_parsed_at":"2025-12-03T23:08:30.039Z","dependency_job_id":null,"html_url":"https://github.com/yelbolt/unoff-ui","commit_stats":null,"previous_names":["a-ng-d/figmug-ui","a-ng-d/unoff-ui","yelbolt/unoff-ui"],"tags_count":56,"template":false,"template_full_name":null,"purl":"pkg:github/yelbolt/unoff-ui","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yelbolt%2Funoff-ui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yelbolt%2Funoff-ui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yelbolt%2Funoff-ui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yelbolt%2Funoff-ui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yelbolt","download_url":"https://codeload.github.com/yelbolt/unoff-ui/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yelbolt%2Funoff-ui/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31290621,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["figma-plugin","framer-plugin","penpot-plugin","sketch-plugin","ui-components"],"created_at":"2026-03-15T04:16:23.418Z","updated_at":"2026-04-01T17:49:23.048Z","avatar_url":"https://github.com/yelbolt.png","language":"SCSS","funding_links":[],"categories":[],"sub_categories":[],"readme":"![GitHub package.json version](https://img.shields.io/github/package-json/v/yelbolt/unoff-ui?color=informational) ![GitHub last commit](https://img.shields.io/github/last-commit/yelbolt/unoff-ui?color=informational) ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/yelbolt/unoff-ui/npm.yml?label=npm) ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/yelbolt/unoff-ui/chromatic.yml?label=Chromatic) ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/yelbolt/unoff-ui/deploy.yml?label=Deployment)\n![GitHub](https://img.shields.io/github/license/yelbolt/unoff-ui?color=informational)\n\n# Unoff UI\n\nUnoff 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.\n\n\u003cimg width=\"1280\" height=\"640\" alt=\"image\" src=\"https://github.com/user-attachments/assets/a4c6c693-db16-43a8-a553-28362380a1b3\" /\u003e\n\n## Features\n\n- **Built with React**: A popular JavaScript library for building user interfaces\n- **Bundled with Vite**: Fast and optimized build tool for modern web projects\n- **Tested with Vitest**: Ensures reliability and robustness of components with interaction tests\n- **Exposed with Storybook**: Interactive UI component explorer for easy development and testing\n- **Design tokens with Terrazzo**: Theme management using design tokens for consistent styling across platforms. [View Terrazzo Guide](./docs/terrazzo-guide.md)\n- **Theme Generator**: Create custom themes easily with the [Theme Generator](./docs/theme-generator.md) tool based on Figma theme structure\n- **SCSS Builder**: Generate theme-specific SCSS files from tokens using the build-scss script, with support for building components across all themes\n- **Interaction Testing**: Automated interaction tests for all components using Storybook play functions.\n\n## Installation\n\nTo install Unoff UI, use npm or yarn:\n\n```bash\nnpm install @unoff/ui\n# or\nyarn add @unoff/ui\n```\n\n## Testing\n\nUnoff UI comes with comprehensive interaction tests for all components:\n\n```bash\n# Run only Storybook interaction tests\nnpm run test:storybook\n```\n\nTests can also be run directly in Storybook UI:\n\n1. Start Storybook: `npm run storybook`\n2. Open the Tests panel in the sidebar\n3. Click \"Run all\" to execute all interaction tests\n\n## Theme Development Tools\n\nUnoff UI provides powerful tools for creating and managing custom themes:\n\n### Theme Generator\n\nCreate new themes based on existing design systems (Sketch, Figma UI, etc.) with a single command:\n\n```bash\nnpm run create:theme\n```\n\nThe Theme Generator automates the creation of all necessary files and configurations:\n\n- Tokens JSON files\n- Terrazzo configuration\n- Storybook integration\n- SCSS imports\n\n[Learn more about the Theme Generator](./docs/theme-generator.md)\n\n### SCSS Builder\n\nGenerate theme-specific SCSS files from design tokens with these commands:\n\n```bash\n# List available themes and components\nnpm run scss:list\n\n# Build all SCSS files\nnpm run scss:build\n\n# Build SCSS for a specific theme\nnpm run scss:build theme=themeName\n\n# Build SCSS for a specific component across all themes\nnpm run scss:build component=componentName\n\n# Build SCSS for a specific component within a specific theme\nnpm run scss:build theme=themeName component=componentName\n\n# Build specific token types across all themes\nnpm run scss:build text\nnpm run scss:build color\nnpm run scss:build icon\nnpm run scss:build type\n\n# Build specific token types for a specific theme\nnpm run scss:build theme=themeName text\nnpm run scss:build theme=themeName color\n```\n\n## Usage\n\n### Slots\n\n#### Bar\n\n```tsx\nimport { Bar } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cBar\n      leftPartSlot={\u003cdiv\u003eLeft very long text that may be truncated\u003c/div\u003e}\n      rightPartSlot={\u003cdiv\u003eRight very long text that may be truncated\u003c/div\u003e}\n      truncate={['LEFT', 'RIGHT']}\n      padding=\"12px\"\n    /\u003e\n  )\n}\n```\n\n#### Form Item\n\n```tsx\nimport { FormItem } from '@unoff/ui'\nimport { Input } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cFormItem\n      id=\"text-input-item\"\n      label=\"Type your name\"\n      helper={{\n        type: 'INFO',\n        message: 'First name followed by your last name',\n      }}\n      shouldFill={false}\n      isBlocked={false}\n      isNew={false}\n    \u003e\n      \u003cInput\n        id=\"text-input-item\"\n        type=\"TEXT\"\n        value=\"Jean-Michel Avous\"\n      /\u003e\n    \u003c/FormItem\u003e\n  )\n}\n```\n\n#### Section\n\n```tsx\nimport { Section } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cSection\n      title=\"Section Title\"\n      description=\"Section description goes here\"\n      isNew={false}\n    \u003e\n      \u003cdiv\u003eSection content goes here\u003c/div\u003e\n    \u003c/Section\u003e\n  )\n}\n```\n\n#### Drawer\n\n```tsx\nimport { Drawer } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cDrawer\n      title=\"Drawer Title\"\n      isOpen={true}\n      onClose={() =\u003e console.log('Drawer closed')}\n    \u003e\n      \u003cdiv\u003eDrawer content goes here\u003c/div\u003e\n    \u003c/Drawer\u003e\n  )\n}\n```\n\n### Actions\n\n#### Primary Button\n\n```tsx\nimport { Button } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cButton\n      type=\"primary\"\n      size=\"default\"\n      label=\"Primary action button\"\n      preview={{\n        image: 'https://placehold.co/96x96',\n        text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',\n      }}\n      feature=\"PRIMARY_ACTION\"\n      action={() =\u003e console.log('Primary button clicked')}\n    /\u003e\n  )\n}\n```\n\n#### Secondary Button\n\n```tsx\nimport { Button } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cButton\n      type=\"secondary\"\n      size=\"default\"\n      label=\"Secondary action button\"\n      feature=\"SECONDARY_ACTION\"\n      action={() =\u003e console.log('Secondary button clicked')}\n    /\u003e\n  )\n}\n```\n\n#### Tertiary Button\n\n```tsx\nimport { Button } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cButton\n      type=\"tertiary\"\n      label=\"Tertiary action button\"\n      feature=\"TERTIARY_ACTION\"\n      isLink={true}\n      url=\"https://example.com\"\n      action={() =\u003e console.log('Tertiary button clicked')}\n    /\u003e\n  )\n}\n```\n\n#### Destructive Button\n\n```tsx\nimport { Button } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cButton\n      type=\"destructive\"\n      size=\"default\"\n      label=\"Destructive action button\"\n      feature=\"DESTRUCTIVE_ACTION\"\n      action={() =\u003e console.log('Destructive button clicked')}\n    /\u003e\n  )\n}\n```\n\n#### Icon Button\n\n```tsx\nimport { Button } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cButton\n      type=\"icon\"\n      size=\"default\"\n      state=\"default\"\n      icon=\"adjust\"\n      helper={{\n        label: 'Adjust',\n        type: 'SINGLE_LINE',\n      }}\n      action={() =\u003e console.log('Icon button clicked')}\n    /\u003e\n  )\n}\n```\n\n#### Segmented Control\n\n```tsx\nimport { SegmentedControl } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cSegmentedControl\n      items={[\n        {\n          id: 'LIST',\n          icon: { type: 'PICTO', name: 'list' },\n          helper: { label: 'List view' },\n        },\n        {\n          id: 'GRID',\n          icon: { type: 'PICTO', name: 'grid' },\n          helper: { label: 'Grid view' },\n        },\n        {\n          id: 'CANVAS',\n          icon: { type: 'PICTO', name: 'frame' },\n          helper: { label: 'Canvas view' },\n        },\n      ]}\n      active=\"LIST\"\n      action={(e) =\u003e console.log(e.currentTarget.dataset.feature)}\n    /\u003e\n  )\n}\n```\n\n### Inputs\n\n#### Short Text Input\n\n```tsx\nimport { Input } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cInput\n      id=\"short-text-typing\"\n      type=\"TEXT\"\n      placeholder=\"Type something (64 characters max.)…\"\n      value=\"\"\n      charactersLimit={64}\n      feature=\"TYPE_SHORT_TEXT\"\n      state=\"DEFAULT\"\n      isAutoFocus={false}\n      isClearable={false}\n      isFramed={true}\n      onChange={(e) =\u003e console.log(e.target.value)}\n    /\u003e\n  )\n}\n```\n\n#### Long Text Input\n\n```tsx\nimport { Input } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cInput\n      id=\"long-text-typing\"\n      type=\"LONG_TEXT\"\n      placeholder=\"Type something\"\n      value=\"\"\n      feature=\"TYPE_LONG_TEXT\"\n      state=\"DEFAULT\"\n      isGrowing={false}\n      onChange={(e) =\u003e console.log(e.target.value)}\n    /\u003e\n  )\n}\n```\n\n#### Color Picker\n\n```tsx\nimport { Input } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cInput\n      id=\"color-picker\"\n      type=\"COLOR\"\n      value=\"#87ebe7\"\n      feature=\"PICK_COLOR\"\n      onChange={(e) =\u003e console.log(e.target.value)}\n    /\u003e\n  )\n}\n```\n\n#### Numeric Stepper\n\n```tsx\nimport { Input } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cInput\n      id=\"numeric-stepper\"\n      type=\"NUMBER\"\n      icon={{\n        type: 'LETTER',\n        value: 'H',\n      }}\n      value=\"20\"\n      min=\"0\"\n      max=\"100\"\n      step=\"1\"\n      feature=\"ADJUST_NUMBER\"\n      onChange={(e) =\u003e console.log(e.target.value)}\n    /\u003e\n  )\n}\n```\n\n### Dropdown\n\n#### Single Selection\n\n```tsx\nimport { Dropdown } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cDropdown\n      id=\"dropdown-button\"\n      options={[\n        {\n          label: 'Option 1',\n          value: 'OPTION_1',\n          type: 'OPTION',\n        },\n        {\n          label: 'Option 2',\n          value: 'OPTION_2',\n          type: 'OPTION',\n          children: [\n            {\n              label: 'Option 2.1',\n              value: 'OPTION_2.1',\n              type: 'OPTION',\n            },\n            {\n              label: 'Option 2.2',\n              value: 'OPTION_2.2',\n              type: 'OPTION',\n            },\n          ],\n        },\n        {\n          type: 'SEPARATOR',\n        },\n        {\n          label: 'Title',\n          type: 'TITLE',\n        },\n        {\n          label: 'Option 3',\n          value: 'OPTION_3',\n          type: 'OPTION',\n        },\n      ]}\n      selected=\"OPTION_1\"\n      alignment=\"LEFT\"\n      onChange={(value) =\u003e console.log(value)}\n    /\u003e\n  )\n}\n```\n\n#### Multiple Selection\n\n```tsx\nimport { Dropdown } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cDropdown\n      id=\"dropdown-button\"\n      options={[\n        {\n          label: 'All',\n          value: 'ANY',\n          type: 'OPTION',\n        },\n        {\n          label: 'Option 1',\n          value: 'OPTION_1',\n          type: 'OPTION',\n        },\n        {\n          label: 'Option 2',\n          value: 'OPTION_2',\n          type: 'OPTION',\n        },\n      ]}\n      selected={['ANY']}\n      alignment=\"LEFT\"\n      onChange={(values) =\u003e console.log(values)}\n    /\u003e\n  )\n}\n```\n\n### Sliders\n\n#### Simple Slider\n\n```tsx\nimport { SimpleSlider } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cSimpleSlider\n      id=\"simple-slider\"\n      label=\"Simple Slider\"\n      value={50}\n      min={0}\n      max={100}\n      colors={{\n        min: 'white',\n        max: 'black',\n      }}\n      feature=\"ADJUST_VALUE\"\n      onChange={(feature, type, value) =\u003e console.log(value)}\n    /\u003e\n  )\n}\n```\n\n#### Multiple Slider\n\n```tsx\nimport { MultipleSlider } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cMultipleSlider\n      id=\"multiple-slider\"\n      label=\"Multiple Slider\"\n      stops={[\n        { id: 'stop-1', value: 20 },\n        { id: 'stop-2', value: 50 },\n        { id: 'stop-3', value: 80 },\n      ]}\n      min={0}\n      max={100}\n      colors={{\n        min: 'white',\n        max: 'black',\n      }}\n      feature=\"ADJUST_VALUES\"\n      onChange={(feature, type, value) =\u003e console.log(value)}\n    /\u003e\n  )\n}\n```\n\n### Dialogs\n\n#### Simple Dialog\n\n```tsx\nimport { Dialog } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cDialog\n      title=\"Are you sure to delete?\"\n      actions={{\n        destructive: {\n          label: 'Delete',\n          action: () =\u003e console.log('Delete action'),\n        },\n        secondary: {\n          label: 'Cancel',\n          action: () =\u003e console.log('Cancel action'),\n        },\n      }}\n      pin=\"CENTER\"\n      onClose={() =\u003e console.log('Dialog closed')}\n    \u003e\n      \u003cdiv className=\"dialog__text\"\u003e\n        \u003cp\u003eDeleting this item will remove it permanently.\u003c/p\u003e\n      \u003c/div\u003e\n    \u003c/Dialog\u003e\n  )\n}\n```\n\n#### Form Dialog\n\n```tsx\nimport { Dialog } from '@unoff/ui'\nimport { Input } from '@unoff/ui'\nimport { FormItem } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cDialog\n      title=\"What do you want to say?\"\n      actions={{\n        primary: {\n          label: 'Submit',\n          action: () =\u003e console.log('Submit action'),\n        },\n      }}\n      pin=\"CENTER\"\n      onClose={() =\u003e console.log('Dialog closed')}\n    \u003e\n      \u003cdiv className=\"dialog__form\"\u003e\n        \u003cdiv className=\"dialog__form__item\"\u003e\n          \u003cFormItem\n            label=\"Full Name\"\n            id=\"type-fullname\"\n            shouldFill\n          \u003e\n            \u003cInput type=\"TEXT\" /\u003e\n          \u003c/FormItem\u003e\n        \u003c/div\u003e\n        \u003cdiv className=\"dialog__form__item\"\u003e\n          \u003cFormItem\n            label=\"Email\"\n            id=\"type-email\"\n            shouldFill\n          \u003e\n            \u003cInput type=\"TEXT\" /\u003e\n          \u003c/FormItem\u003e\n        \u003c/div\u003e\n        \u003cdiv className=\"dialog__form__item\"\u003e\n          \u003cFormItem\n            label=\"Message\"\n            id=\"type-message\"\n            shouldFill\n          \u003e\n            \u003cInput\n              type=\"LONG_TEXT\"\n              placeholder=\"Type your message here\"\n            /\u003e\n          \u003c/FormItem\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/Dialog\u003e\n  )\n}\n```\n\n#### Loading Dialog\n\n```tsx\nimport { Dialog } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cDialog\n      title=\"Loading…\"\n      pin=\"CENTER\"\n      isLoading={true}\n      onClose={() =\u003e console.log('Dialog closed')}\n    /\u003e\n  )\n}\n```\n\n### Lists\n\n#### Simple List\n\n```tsx\nimport { ActionsList } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cActionsList\n      options={[\n        {\n          label: 'Option 1',\n          value: 'OPTION_1',\n          type: 'OPTION',\n          action: () =\u003e console.log('Option 1 clicked'),\n        },\n        {\n          label: 'Option 2',\n          value: 'OPTION_2',\n          type: 'OPTION',\n          action: () =\u003e console.log('Option 2 clicked'),\n        },\n        {\n          label: 'Option 3',\n          value: 'OPTION_3',\n          type: 'OPTION',\n          action: () =\u003e console.log('Option 3 clicked'),\n        },\n        {\n          label: 'Option 4',\n          value: 'OPTION_4',\n          type: 'OPTION',\n          action: () =\u003e console.log('Option 4 clicked'),\n        },\n      ]}\n      selected=\"OPTION_1\"\n    /\u003e\n  )\n}\n```\n\n#### Grouped List\n\n```tsx\nimport { ActionsList } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cActionsList\n      options={[\n        {\n          label: 'Group 1',\n          type: 'TITLE',\n        },\n        {\n          label: 'Option 1',\n          value: 'OPTION_1',\n          type: 'OPTION',\n          action: () =\u003e console.log('Option 1 clicked'),\n        },\n        {\n          label: 'Option 2',\n          value: 'OPTION_2',\n          type: 'OPTION',\n          action: () =\u003e console.log('Option 2 clicked'),\n        },\n        {\n          type: 'SEPARATOR',\n        },\n        {\n          label: 'Group 2',\n          type: 'TITLE',\n        },\n        {\n          label: 'Option 3',\n          value: 'OPTION_3',\n          type: 'OPTION',\n          action: () =\u003e console.log('Option 3 clicked'),\n        },\n        {\n          label: 'Option 4',\n          value: 'OPTION_4',\n          type: 'OPTION',\n          action: () =\u003e console.log('Option 4 clicked'),\n        },\n      ]}\n    /\u003e\n  )\n}\n```\n\n#### Nested List\n\n```tsx\nimport { ActionsList } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cActionsList\n      options={[\n        {\n          label: 'Group 1',\n          value: 'GROUP_1',\n          type: 'OPTION',\n          children: [\n            {\n              label: 'Option 1',\n              value: 'OPTION_A_1',\n              type: 'OPTION',\n              action: () =\u003e console.log('Option A.1 clicked'),\n            },\n            {\n              label: 'Option 2',\n              value: 'OPTION_A_2',\n              type: 'OPTION',\n              action: () =\u003e console.log('Option A.2 clicked'),\n            },\n          ],\n        },\n        {\n          label: 'Group 2',\n          value: 'GROUP_2',\n          type: 'OPTION',\n          children: [\n            {\n              label: 'Option 1',\n              value: 'OPTION_B_1',\n              type: 'OPTION',\n              action: () =\u003e console.log('Option B.1 clicked'),\n            },\n            {\n              label: 'Option 2',\n              value: 'OPTION_B_2',\n              type: 'OPTION',\n              action: () =\u003e console.log('Option B.2 clicked'),\n            },\n          ],\n        },\n      ]}\n    /\u003e\n  )\n}\n```\n\n### Tags\n\n#### Basic Chip\n\n```tsx\nimport { Chip } from '@unoff/ui'\n\nfunction App() {\n  return \u003cChip state=\"ACTIVE\"\u003eNew\u003c/Chip\u003e\n}\n```\n\n#### Chip with Color Indicator\n\n```tsx\nimport { Chip, ColorChip } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cChip\n      state=\"ON_BACKGROUND\"\n      leftSlot={\n        \u003cColorChip\n          color=\"blue\"\n          width=\"8px\"\n          height=\"8px\"\n          isRounded={true}\n        /\u003e\n      }\n      rightSlot={\u003cdiv style={{ fontSize: '11px' }}\u003e✔︎\u003c/div\u003e}\n    \u003e\n      AA\n    \u003c/Chip\u003e\n  )\n}\n```\n\n### Assets\n\n#### Icon\n\n```tsx\nimport { Icon } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003c\u003e\n      {/* Pictogram Icon */}\n      \u003cIcon\n        type=\"PICTO\"\n        iconName=\"adjust\"\n      /\u003e\n\n      {/* Letter Icon */}\n      \u003cIcon\n        type=\"LETTER\"\n        iconLetter=\"L\"\n      /\u003e\n    \u003c/\u003e\n  )\n}\n```\n\n#### Avatar\n\n```tsx\nimport { Avatar } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003c\u003e\n      {/* Avatar with Image */}\n      \u003cAvatar\n        avatar=\"https://example.com/avatar.jpg\"\n        fullName=\"John Doe\"\n        isInverted={false}\n      /\u003e\n\n      {/* Default Avatar */}\n      \u003cAvatar isInverted={false} /\u003e\n    \u003c/\u003e\n  )\n}\n```\n\n#### Thumbnail\n\n```tsx\nimport { Thumbnail } from '@unoff/ui'\n\nfunction App() {\n  return (\n    \u003cThumbnail\n      src=\"https://example.com/image.jpg\"\n      width=\"300px\"\n      height=\"200px\"\n    /\u003e\n  )\n}\n```\n\n## Testing\n\nTo run tests:\n\n```bash\nnpm test\n# or\nyarn test\n```\n\n## License\n\nThis project is licensed under the MIT License. See the [LICENSE](LICENSE) file for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyelbolt%2Funoff-ui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyelbolt%2Funoff-ui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyelbolt%2Funoff-ui/lists"}