Addon UI - A comprehensive UI component library designed for the Addon Bone framework. This library provides a set of customizable React components with theming capabilities to build modern, responsive user interfaces.
- 🎨 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
- Installation
- Components
- Basic Usage
- Integration
- Customization
- Using Extra Props
- Theming and style reuse
- Radix UI and third-party integrations
- Icons and sprite
- Extra props
- Contributing
npm install addon-ui
pnpm add addon-ui
yarn add addon-ui
This library now ships with dedicated documentation files for each component in the docs/ directory. Start here:
- Avatar
- Button
- Checkbox
- Dialog
- Drawer
- Footer
- Header
- Highlight
- Icon
- IconButton
- List (covers List and ListItem)
- Modal
- Odometer (component + useOdometer hook)
- ScrollArea
- SvgSprite
- Switch
- Tag
- TextArea
- TextField
- Toast
- View
- ViewDrawer
- ViewModal
- Viewport (ViewportProvider + useViewport)
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.
import React from "react";
import {Button, ButtonColor, ButtonVariant, TextField, UIProvider} from "addon-ui";
function App() {
return (
<UIProvider>
<div>
<TextField label="Username" placeholder="Enter your username" />
<Button color={ButtonColor.Primary} variant={ButtonVariant.Contained}>
Submit
</Button>
</div>
</UIProvider>
);
}
export default App;
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.
// adnbn.config.ts
import {defineConfig} from "adnbn";
import ui from "addon-ui/plugin";
export default defineConfig({
plugins: [
ui({
themeDir: "./theme", // Directory for theme files
configFileName: "ui.config", // Name of config files
styleFileName: "ui.style", // Name of style files
mergeConfig: true, // Merge configs from different directories
mergeStyles: true, // Merge styles from different directories
}),
],
});
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):
- App-specific directory:
src/apps/[app-name]/[app-src-dir]/[theme-dir]
- 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:
You can use the defineConfig
helper which provides type checking:
// src/shared/theme/ui.config.ts
import {defineConfig} from "addon-ui/config";
import {ButtonColor, ButtonRadius, ButtonVariant, TextFieldRadius, TextFieldSize} from "addon-ui";
import CloseIcon from "./icons/close.svg?react";
export default defineConfig({
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.
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";
@include light {
// 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;
}
@include dark {
// 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;
}
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.
You can customize the theme globally by passing props to the UIProvider:
import {UIProvider} from "addon-ui";
const customTheme = {
components: {
button: {
variant: "outlined",
color: "primary",
},
textField: {
radius: "medium",
},
},
icons: {
// Custom icons
},
};
function App() {
return <UIProvider {...customTheme}>{/* Your application */}</UIProvider>;
}
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.
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
- Configure Extra Props in your theme configuration:
// src/shared/theme/ui.config.ts
import {defineConfig} from "addon-ui/config";
export default defineConfig({
components: {
// Component configurations
},
extra: {
// Your custom properties
appName: "My Awesome App",
version: "1.0.0",
features: {
darkMode: true,
analytics: false,
},
},
icons: {
// Icon configurations
},
});
- Access Extra Props in your components using the
useExtra
hook:
import {useExtra} from "addon-ui";
function AppHeader() {
const extra = useExtra();
return (
<header>
<h1>{extra.appName}</h1>
<span>Version: {extra.version}</span>
</header>
);
}
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";
function TrackableButton(props) {
const extra = useExtra();
const handleClick = e => {
// Use extra props for analytics
if (extra.features.analytics) {
trackButtonClick(props.trackingId);
}
// Call the original onClick handler
props.onClick?.(e);
};
return <Button {...props} onClick={handleClick} />;
}
To get proper type checking for your custom Extra Props, you can extend the ExtraProps
interface:
// ui.d.ts or similar file
import "addon-ui";
declare module "addon-ui" {
interface ExtraProps {
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:
import React from "react";
import {useExtra, Button} from "addon-ui";
const FeatureFlag: React.FC<{feature: keyof ExtraProps["features"]; children: React.ReactNode}> = ({
feature,
children,
}) => {
const extra = useExtra();
// TypeScript knows that extra.features exists and has the properties we defined
if (extra.features[feature]) {
return <>{children}</>;
}
return null;
};
// Usage
function App() {
return (
<div>
<FeatureFlag feature="darkMode">
<Button>Dark Mode Enabled</Button>
</FeatureFlag>
</div>
);
}
- Global theme tokens (colors, typography, spacing, transitions) live in your ui.style.scss. Components consume them through fallbacks.
- Each component also exposes its own
--component-*
variables. See the CSS variables tables in the docs to know exactly what you can override. - Light/dark modes: use
@import "addon-ui/theme";
and the provided@include light { ... }
/@include dark { ... }
mixins in your theme SCSS to scope tokens per color scheme.
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.
- Register icons in
ui.config.ts
or viaUIProvider
’sicons
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.
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.
- 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.