Skip to content

Repository files navigation

addon-ui

A React UI toolkit for Addon Bone browser-extension applications.

npm versionnpm downloadsCILicense: MIT

Build consistent browser-extension interfaces with React components, typed UI configuration, theme customization, and an Addon Bone plugin for shared and app-specific UI files.

Why addon-ui

  • 🎨 Customizable Theming: Easily customize the look and feel of components through theme configuration
  • 🧩 Rich Component Set: Includes buttons, forms, layouts, modals, and more
  • 🔌 Addon Bone Integration: Seamless integration with the Addon Bone framework
  • 📚 Storybook Documentation: Comprehensive component documentation with examples
  • 🛠️ TypeScript Support: Full TypeScript support with type definitions

Table of Contents

Installation

addon-ui is designed to be used with Addon Bone. Your host application must provide compatible adnbn, react, and react-dom peer dependencies.

npm i addon-ui

With pnpm or Yarn:

pnpm add addon-ui
yarn add addon-ui

Quick Start

importReactfrom"react";import{Button,ButtonColor,ButtonVariant,TextField,UIProvider}from"addon-ui";functionApp(){return(<UIProvider><div><TextFieldlabel="Username"placeholder="Enter your username"/><Buttoncolor={ButtonColor.Primary}variant={ButtonVariant.Contained}>
Submit
</Button></div></UIProvider>);}exportdefaultApp;

Package Entry Points

ImportUse it for
addon-uiReact components, UI providers, and theme types.
addon-ui/configThe typed defineConfig() helper for UI configuration files.
addon-ui/pluginAddon Bone plugin setup and UI-file discovery.
addon-ui/themeSass mixins for extending theme styles.

See Plugin Setup to connect the package to an Addon Bone application.

Components

This library now ships with dedicated documentation files for each component in the docs/ directory. Start here:

Notes:

  • Each CSS variables table lists only component-scoped variables with exact fallback chains from the corresponding * .module.scss file.
  • Where a component wraps a Radix UI primitive, the doc links to the official Radix docs and lists common props.

Integration

Addon UI is designed exclusively for the Addon Bone framework and does not have a standalone build as it's connected as a plugin. This library is an integral part of the Addon Bone ecosystem for developing browser extensions with a shared codebase.

Addon Bone is a framework for developing browser extensions with a common codebase. This means you can create multiple extensions with the same functionality but with different designs, localizations, and feature sets depending on the needs of each extension while maintaining access to a shared codebase.

Plugin Setup

// adnbn.config.tsimport{defineConfig}from"adnbn";importuifrom"addon-ui/plugin";exportdefaultdefineConfig({plugins: [ui({themeDir: "./theme",// Directory for theme filesconfigName: "ui.config",// Name of config filesstyleName: "ui.style",// Name of style filesmergeConfig: true,// Merge configs from different directoriesmergeStyles: true,// Merge styles from different directoriessplitChunks: true,// Enable automatic chunk splitting for components}),],});

Plugin Options

OptionTypeDefaultDescription
themeDirstring"."Directory path where plugin configuration and style files are located.
configNamestring"ui.config"Name of the configuration file.
styleNamestring"ui.style"Name of the SCSS style file.
mergeConfigbooleantrueWhether to merge configuration files from different directories.
mergeStylesbooleantrueWhether to merge style files from different directories.
splitChunksboolean | (name: string) => string | undefinedfalseEnables automatic chunk splitting. If a function is provided, it can be used to customize chunk names.

Customizing Chunk Names

You can pass a callback function to splitChunks to customize the generated chunk names:

ui({splitChunks: name=>{if(name==="button")return"ui-core-button";return`ui-${name}`;},});

Configuration Files

The addon-ui configuration is designed to retrieve configuration from each extension separately, allowing for different designs for different extensions without changing any code. You only need to modify the configuration, style variables, and icons.

The plugin looks for configuration files in specific directories within your project. By default, it searches in the following locations (in order of priority):

  1. App-specific directory: src/apps/[app-name]/[app-src-dir]/[theme-dir]
  2. Shared directory: src/shared/[theme-dir]

Where [theme-dir] is the directory specified in the themeDir option (defaults to the current directory).

The mergeConfig option (default: true) determines whether configurations from multiple directories should be merged. When enabled, configurations from both app-specific and shared directories will be combined, with app-specific values taking precedence in case of conflicts. If disabled, only the first configuration found will be used (with app-specific having priority).

You can create these files to customize the UI components:

ui.config.ts

You can use the defineConfig helper which provides type checking:

// src/shared/theme/ui.config.tsimport{defineConfig}from"addon-ui/config";import{ButtonColor,ButtonRadius,ButtonVariant,TextFieldRadius,TextFieldSize}from"addon-ui";importCloseIconfrom"./icons/close.svg?react";exportdefaultdefineConfig({components: {button: {variant: ButtonVariant.Contained,color: ButtonColor.Primary,radius: ButtonRadius.Medium,},textField: {size: TextFieldSize.Medium,radius: TextFieldRadius.Small,},// ... other component configurations},icons: {close: CloseIcon,// Other custom icons},});

The example above shows how to use the TypeScript configuration with the Addon Bone framework. The defineConfig helper provides type checking and autocompletion for your configuration. You can import enum values from "addon-ui/config" to ensure type safety when configuring components. The configuration can also include SVG icons imported directly from your project files.

ui.style.scss

Similar to configuration files, style files are also searched for in the same directories with the same priority order. The mergeStyles option (default: true) works the same way as mergeConfig, allowing styles from multiple directories to be combined when enabled.

// src/shared/theme/ui.style.scss// Custom CSS variables and mixins for theming@import"addon-ui/theme";
@includelight {
// Base colors--primary-color: #3f51b5;
--secondary-color: #f50057;
--accent-color: #4caf50;
// Text colors--text-primary-color: #212121;
--text-secondary-color: #757575;
// Background colors--bg-primary-color: #ffffff;
--bg-secondary-color: #f5f5f5;
// Font settings--font-family: "Roboto", sans-serif;
--font-size: 14px;
--line-height: 1.5;
// Button specific variables--button-font-family: var(--font-family);
--button-font-size: var(--font-size);
--button-height: 34px;
--button-border-radius: 10px;
// Button size variants--button-height-sm: 24px;
--button-height-md: 44px;
--button-height-lg: 54px;
// Button radius variants--button-border-radius-sm: 5px;
--button-border-radius-md: 12px;
--button-border-radius-lg: 15px;
}
@includedark {
// Base colors for dark theme--primary-color: #7986cb;
--secondary-color: #ff4081;
--accent-color: #66bb6a;
// Text colors for dark theme--text-primary-color: #ffffff;
--text-secondary-color: #b0bec5;
// Background colors for dark theme--bg-primary-color: #121212;
--bg-secondary-color: #1e1e1e;
}

Customization

The addon-ui library allows for extensive customization to create different designs for different extensions without changing code. This is particularly useful in the Addon Bone framework where you might need to maintain multiple browser extensions with the same functionality but different visual appearances.

Global Theme Customization

You can customize the theme globally by passing props to the UIProvider:

import{UIProvider}from"addon-ui";constcustomTheme={components: {button: {variant: "outlined",color: "primary",},textField: {radius: "medium",},},icons: {// Custom icons},// Specify the DOM element to set theme/view/browser attributes oncontainer: "#app-root",};functionApp(){return<UIProvider{...customTheme}>{/* Your application */}</UIProvider>;}

UIProvider Props

PropTypeDefaultDescription
componentsComponentsProps{}Component-specific configuration overrides.
iconsIcons{}Custom SVG icons registration.
extraExtraProps{}App-wide extra properties.
storageThemeStorageContract | trueundefinedPersistence storage for theme settings.
containerstring | Element | false"html"Target element for attributes. Set to false to disable.
viewstringundefinedCustom view identifier for specific styling.

Using Extra Props

Extra Props is a powerful feature that allows you to extend component props with custom properties. This is particularly useful when you need to add custom functionality or data to components across your application without modifying the original component code.

What are Extra Props?

Extra Props provide a way to pass additional properties to components throughout your application using React Context. This allows you to:

  • Add application-specific properties to UI components
  • Share common data across multiple components
  • Extend the library's components with your own custom properties

How to Use Extra Props

  1. Configure Extra Props in your theme configuration:
// src/shared/theme/ui.config.tsimport{defineConfig}from"addon-ui/config";exportdefaultdefineConfig({components: {// Component configurations},extra: {// Your custom propertiesappName: "My Awesome App",version: "1.0.0",features: {darkMode: true,analytics: false,},},icons: {// Icon configurations},});
  1. Access Extra Props in your components using the useExtra hook:
import{useExtra}from"addon-ui";functionAppHeader(){constextra=useExtra();return(<header><h1>{extra.appName}</h1><span>Version: {extra.version}</span></header>);}

Example Use Case

A common use case for Extra Props is to add application-specific configuration to UI components. For example, you might want to add custom analytics tracking to buttons:

import{Button,useExtra}from"addon-ui";functionTrackableButton(props){constextra=useExtra();consthandleClick=e=>{// Use extra props for analyticsif(extra.features.analytics){trackButtonClick(props.trackingId);}// Call the original onClick handlerprops.onClick?.(e);};return<Button{...props}onClick={handleClick}/>;}

Extending ExtraProps in TypeScript

To get proper type checking for your custom Extra Props, you can extend the ExtraProps interface:

// ui.d.ts or similar fileimport"addon-ui";declare module "addon-ui"{interfaceExtraProps{appName: string;version: string;features: {darkMode: boolean;analytics: boolean;};// Add any other custom properties}}

With this type definition, TypeScript will provide proper type checking and autocompletion when using the useExtra hook:

importReactfrom"react";import{useExtra,Button}from"addon-ui";constFeatureFlag: React.FC<{feature: keyofExtraProps["features"];children: React.ReactNode}>=({
feature,
children,})=>{constextra=useExtra();// TypeScript knows that extra.features exists and has the properties we definedif(extra.features[feature]){return<>{children}</>;}returnnull;};// UsagefunctionApp(){return(<div><FeatureFlagfeature="darkMode"><Button>Dark Mode Enabled</Button></FeatureFlag></div>);}

Theming and style reuse

  • Global theme tokens (colors, typography, spacing, transitions) live in your ui.style.scss.
  • Each component also exposes its own --component-* variables. See the CSS variables tables in the docs to know exactly what you can override.
  • Theme Mixins: Use @import "addon-ui/theme"; to access @include light { ... } and @include dark { ... } mixins.
  • Universal Targeting: These mixins are container-agnostic. They work correctly whether the theme attribute is on a parent element or directly on the component itself.
  • Context-Aware:
    • When used at the top level, they generate global selectors: [theme="dark"] { ... }.
    • When used inside a component, they generate scoped selectors: [theme="dark"] .my-comp, .my-comp[theme="dark"] { ... }.

Radix UI and third-party integrations

Several components are built on Radix primitives (Dialog, Checkbox, ScrollArea, Switch, Toast) or wrap third-party tools (react-highlight-words, odometer). Each doc links to the official API and explains which props you can pass through.

Icons and sprite

  • Register icons in ui.config.ts or via UIProvider’s icons prop. The Icon component pulls symbols from the automatically mounted SvgSprite.
  • Icons are lazily registered: a symbol is added only after an Icon with that name renders at least once.
  • See docs/Icon.md and docs/SvgSprite.md for details and examples.

Extra props (cross-cutting configuration)

Use the extra field in ui.config.ts to supply app-wide values (feature flags, labels, analytics switches) and access them at runtime with the useExtra() hook. You can augment the ExtraProps TypeScript interface by declaration merging for full type safety.

Contributing

  • See CONTRIBUTING.md for the branch, commit, and release policy.
  • Keep canonical end-user documentation in the docs/ directory. When adding or changing CSS variables in a component’s *.module.scss, update the corresponding doc table.
  • Where a component wraps a Radix primitive, keep the “Radix UI props” section in sync if the underlying package changes.
  • Consider adding a short README stub inside each component folder that links to the canonical doc (optional for discoverability during development).
  • Run and maintain Storybook stories (if present) to validate visual changes.

About

A comprehensive React UI component library designed exclusively for the Addon Bone browser extension framework with customizable theming and consistent design patterns

Topics

Resources

Contributing

Security policy

Stars

4 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages