Is your feature request related to a problem? Please describe.
Every icon slot in this library is typed as React.ReactNode, and the built-in defaults are drawn from three unrelated sources — react-icons/md, @mui/icons-material, and hand-written inline SVG:
| Component |
Built-in icon |
Source |
BBButton |
helperIcon default, feedbackContent default |
MdSettings, MdCheckCircle |
BBBNavigation |
fallback icon |
MdExpandCircleDown |
BBBHint |
icon default, close button |
MdInfo, MdClose |
BBBModal |
close button |
MdClose |
BBBAccordion |
expand chevron |
MdExpandMore |
BBBSelect |
dropdown arrow |
MdExpandMore |
BBBCheckbox |
round variant |
RadioButtonUnchecked / RadioButtonChecked |
BBBSearch |
search glyph, clear button |
hand-written inline <svg> |
The BigBlueButton client draws its own UI with a different icon set — the bbb-icons webfont. A plugin built with this library therefore renders Material Design glyphs next to BBB glyphs, in the same toolbar, side by side. There is currently no supported way to ask a component for a BBB glyph.
The library has in fact already grown an undocumented accommodation for it. src/components/Button/styles.ts:218 contains:
export const IconWrapper = styled.div`
...
> i {
font-size: 2rem;
}
`;
A bare > i element selector only makes sense for icon-font <i> elements — the icon-font path exists in this codebase today, it is just accidental rather than designed.
On top of that, several icons are hardcoded with no prop to override them at all (BBBModal close, BBBAccordion expand, BBBSelect dropdown arrow, BBBSearch search/clear), so even the workaround above cannot reach them.
How BigBlueButton does it today (analysis)
Checked against bigbluebutton/bigbluebutton, bigbluebutton-html5.
The font and its stylesheet are global, and live outside React.
public/fonts/BbbIcons/bbb-icons.woff2 and bbb-icons.woff
public/stylesheets/bbb-icons.css — 437 lines, 135 .icon-bbb-* rules
client/main.html wires both up before the app boots:
<!-- client/main.html:164 -->
<link rel="preload" href="fonts/BbbIcons/bbb-icons.woff2?v=VERSION" as="font" type="font/woff2" crossorigin="anonymous"/>
<!-- client/main.html:174 -->
<link rel="stylesheet" href="stylesheets/bbb-icons.css" crossorigin="anonymous"/>
<!-- client/main.html:182 -->
<style>
@font-face {
font-family: 'bbb-icons';
src: url('fonts/BbbIcons/bbb-icons.woff2?v=VERSION') format('woff2'),
url('fonts/BbbIcons/bbb-icons.woff?v=VERSION') format('woff');
font-weight: normal;
font-style: normal;
}
</style>
The stylesheet does the styling; the React component only picks a class.
/* public/stylesheets/bbb-icons.css */
[class^="icon-bbb-"], [class*=" icon-bbb-"] {
font-family: 'bbb-icons' !important;
display: inline-block;
font-style: normal;
font-weight: 400;
line-height: 1;
width: 1em;
text-align: center;
vertical-align: middle;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-bbb-thumbs_up:before { content: "\e92d"; }
Two consequences worth keeping: the glyph is sized by font-size (it is 1em wide) and colored by color, exactly like text — so it composes with the flex wrappers this library already uses; and [class^="icon-bbb-"] matches any class with that prefix, which is how plugins extend the set (see below).
The client's own Icon component is a thin class-name builder (imports/ui/components/common/icon/icon-ts/component.tsx):
const Icon: React.FC<IconProps> = ({
ariaHidden, ariaLabel, className = '',
prependIconName = 'icon-bbb-', iconName = '', rotate = false,
}) => {
const isAriaHidden = ariaHidden ?? !ariaLabel;
return (
<Styled.Icon
aria-hidden={isAriaHidden}
aria-label={isAriaHidden ? undefined : ariaLabel}
role={isAriaHidden ? undefined : 'img'}
className={cx(className, [prependIconName, iconName].join(''))}
$rotate={rotate}
/>
);
};
Styled.Icon is styled.i with only a $rotate transform. The older component.jsx variant adds a color prop. Note the accessibility default: hidden unless an ariaLabel is given, in which case it becomes role="img".
prependIconName is a real extension point, not decoration. bigbluebutton-plugin-audioplayer/src/main.css ships its own glyphs under the same family and prefix:
@font-face {
font-family: 'bbb-icons';
src: url('../assets/audio-player.woff') format('woff');
}
.icon-bbb-audio-player:before { content: "\e820"; }
Any design here must keep a name outside the known set renderable.
The plugin SDK already standardised an icon-name value type. bigbluebutton-html-plugin-sdk/src/extensible-areas/common/icon/types.ts:
export interface PluginIconName { iconName: string; }
export interface PluginIconSvgContent { svgContent: React.SVGProps<SVGSVGElement>; }
export type PluginIconType = string | PluginIconName | PluginIconSvgContent;
and the client normalises it in several places, e.g. imports/ui/components/common/menu/component.jsx:23 and imports/ui/components/common/separator/component.tsx:15-17:
if (typeof icon === 'string') return <Icon iconName={icon} ... />;
if (icon && typeof icon === 'object' && 'iconName' in icon) return <Icon iconName={icon.iconName} ... />;
if (icon && typeof icon === 'object' && 'svgContent' in icon) return <Wrapper>{icon.svgContent}</Wrapper>;
Plugin authors already hold PluginIconType values. If this library speaks the same shape, those values can be forwarded to a BBButton unchanged.
Duplication is a known cost in the monorepo. bbb-learning-dashboard keeps its own copy at src/fonts/BbbIcons/bbb-icons.woff. Adding a third copy that ships inside the client bundle would be a regression, which drives the font delivery decision below.
Describe the solution you'd like
Six parts. Parts 1–3 are the core and are non-breaking; parts 4–6 are the ergonomics and coverage work.
1. A new BBBIcon component
New folder src/components/Icon/, exported as BBBIcon, mirroring the client's contract so that a glyph rendered by this library is indistinguishable from one rendered by BBB itself.
It ships no CSS and loads no font. It only emits the class name and relies on the rules already present in the host document.
export interface IconProps {
/** Name of the glyph in the BBB icon font, without the `icon-bbb-` prefix. */
name: BBBIconName | (string & {});
/** Class prefix prepended to `name`; override it to reach glyphs a plugin added to the font. @default 'icon-bbb-' */
prefix?: string;
/** Glyph size, applied as `font-size`; the glyph is 1em wide and square. @default '1em' */
size?: string;
/** Glyph color, applied as `color`; inherits from the parent when omitted. */
color?: string;
/** Rotates the glyph 180°, mirrored under `dir="rtl"`. @default false */
rotate?: boolean;
/** Accessible name; when set, the icon becomes `role="img"` instead of being hidden. */
ariaLabel?: string;
/** Forces the icon to be hidden from assistive technology; defaults to `true` unless `ariaLabel` is set. */
ariaHidden?: boolean;
/** Extra class names merged with the generated icon class. */
className?: string;
/** Value for the `data-test` attribute. */
dataTest?: string;
}
name is typed as BBBIconName | (string & {}) rather than a closed union: autocomplete lists the 135 known names, while a plugin-provided glyph still type-checks.
2. Generated, typed icon names
scripts/generate-icon-names.ts parses bbb-icons.css from a pinned BBB version and emits src/components/Icon/icon-names.ts:
export const BBB_ICON_NAMES = ['about', 'add', 'alert', /* …135 total */] as const;
export type BBBIconName = typeof BBB_ICON_NAMES[number];
Regenerating is a deliberate, reviewable commit, so the pinned BBB version is visible in the diff and the union never drifts silently.
3. Font assets only outside the BBB client
The constraint: inside the BBB client the library must add nothing — no @font-face, no re-declaration of bbb-icons.css. Redeclaring @font-face for the family bbb-icons would fight the client's own declaration, and shipping the .woff2 in the bundle would be a third copy of a font already served.
Proposal:
-
The same generator vendors bbb-icons.css + bbb-icons.woff2 as a dev-only asset, imported by .storybook/preview.ts. It is not reachable from any component's module graph, so it cannot end up in a consumer's bundle.
-
For apps outside BBB that still want the glyphs, a separate opt-in entry point with the side effect:
// only for apps that are NOT the BBB client
import '@bigbluebutton/bbb-ui-components-react/icon-font';
Added to package.json exports and to webpack.config.babel.js like any other entry, and listed in sideEffects.
-
No component ever imports it. Inside BBB, nobody calls it and nothing is added.
The BBBIcon README documents this explicitly: in a BBB plugin, import nothing — the font is already there.
4. Accept icon names on the existing icon props
A shared internal helper normalises the three accepted shapes, matching what the client already does for PluginIconType:
// src/components/Icon/render-icon.tsx
export type BBBIconProp = React.ReactNode | BBBIconName | { iconName: string };
export const renderIcon = (icon: BBBIconProp): React.ReactNode => {
if (typeof icon === 'string') return <BBBIcon name={icon} />;
if (icon && typeof icon === 'object' && 'iconName' in icon) return <BBBIcon name={icon.iconName} />;
return icon;
};
Every existing icon prop widens from React.ReactNode to BBBIconProp and is passed through renderIcon at render time. <MdSend /> keeps working unchanged.
Breaking-change flag. React.ReactNode already includes string, so today icon="save" renders the literal text save. Treating a bare string as an icon name changes runtime behaviour for anyone who relied on that. It is an unlikely usage — none of these props are documented as accepting text — but it is a real change. Options, to be settled in review: (a) ship the bare-string form and land it on the next major; (b) accept only the { iconName } object form, fully backwards compatible but more verbose and only partially PluginIconType-compatible; (c) ship (b) now and (a) in the next major. Recommendation: (a), since the whole point is letting plugin authors forward PluginIconType values verbatim.
5. Expose the icons that are currently hardcoded
These have no override today. Each gets a BBBIconProp prop whose default is the current Material icon, so behaviour is unchanged when the prop is omitted.
| Component |
New prop |
Replaces |
Current default |
BBBModal |
closeIcon |
component.tsx:70 |
<MdClose size="1.5rem" /> |
BBBHint |
closeIcon |
component.tsx:58 |
<MdClose fontSize="1rem" /> |
BBBAccordion |
expandIcon |
component.tsx:39 |
<MdExpandMore /> |
BBBSelect |
dropdownIcon |
component.tsx:38 |
MdExpandMore (MUI IconComponent) |
BBBSearch |
searchIcon, clearIcon |
component.tsx:87-103, :129-145 |
hand-written inline <svg> |
BBBSelect.dropdownIcon needs care: MUI's IconComponent takes a component type, not an element, so the prop has to be adapted rather than forwarded.
BBBCheckbox's RadioButtonUnchecked / RadioButtonChecked are left alone — they are the round variant's control rendering, not a decorative icon slot.
6. Replace the ad-hoc > i rule with a documented sizing contract
src/components/Button/styles.ts:218 currently hardcodes > i { font-size: 2rem; }. With BBBIcon owning size, that rule should become an explicit default that a caller can override, and the same sizing question answered consistently for BBBNavigation, BBBHint, BBBSlider and BBBInput, whose icon wrappers are each sized differently today.
Worth documenting once, in the BBBIcon README: a font glyph inherits font-size and color, so size/color behave like the identically named props of react-icons — the two icon sources stay interchangeable inside the same wrapper.
Describe alternatives you've considered
Do nothing; consumers keep passing <i className="icon-bbb-…" />. Rejected: no type safety, no a11y defaults, no sizing contract, the > i hack stays undocumented, and it cannot reach the hardcoded icons at all.
Convert the font to individual React SVG components (à la react-icons), generated from the font. Best tree-shaking and no global CSS dependency at all. Rejected: the font is not the source of truth for the vectors, the generated output would drift from the client's rendering, and it duplicates in bytes exactly what the client already loads — for the primary consumer (a plugin inside BBB) this is strictly worse.
Always bundle the font and the @font-face in the library. Self-contained and the simplest story for Storybook. Rejected per the constraint above: it duplicates ~30KB already served by the client, declares a competing @font-face for the family bbb-icons, and risks rendering a different font version than the surrounding UI.
Re-export the client's Icon component from the SDK instead. The SDK does not export it, and the library must stay usable outside a plugin context. Keeping a local BBBIcon whose contract matches the client's is the cheaper coupling.
Affected component(s)
BBBIcon (new), BBButton, BBBNavigation, BBBHint, BBBSelect, BBBInput, BBBSlider, BBBModal, BBBAccordion, BBBSearch.
Prop changes, per component
| Component |
Prop |
Change |
BBBIcon |
— |
New component |
BBButton |
icon (circle/squared/stacked) |
React.ReactNode → BBBIconProp |
BBButton |
iconStart, iconEnd (default layout) |
React.ReactNode → BBBIconProp |
BBButton |
helperIcon (stacked) |
React.ReactNode → BBBIconProp |
BBBNavigation |
icon |
React.ReactNode → BBBIconProp |
BBBHint |
icon |
React.ReactNode → BBBIconProp |
BBBHint |
closeIcon |
New, defaults to <MdClose fontSize="1rem" /> |
BBBSelect |
icon |
React.ReactNode → BBBIconProp |
BBBSelect |
dropdownIcon |
New, defaults to MdExpandMore |
BBBInput |
buttonIcon |
React.ReactNode → BBBIconProp |
BBBSlider |
iconStart, iconEnd |
React.ReactNode → BBBIconProp |
BBBModal |
closeIcon |
New, defaults to <MdClose size="1.5rem" /> |
BBBAccordion |
expandIcon |
New, defaults to <MdExpandMore /> |
BBBSearch |
searchIcon, clearIcon |
New, default to the current inline SVGs |
Not changed: BBButton.feedbackContent, BBBInput.beforeButton / afterButton / sentFeedbackContent, BBBAccordion.buttonHeader, BBBModal.footerContent — these are general content slots, not icon slots, and already accept a <BBBIcon /> element as-is. BBBCheckbox and BBBToggle are unaffected.
Proposed API / Usage Example
import { BBBIcon, BBButton, BBBHint, BBBModal } from '@bigbluebutton/bbb-ui-components-react';
// 1. Standalone, inside any content slot
<BBBIcon name="thumbs_up" />
<BBBIcon name="presentation" size="1.5rem" ariaLabel="Presentation" />
<BBBIcon name="right_arrow" rotate />
// 2. By name, on an existing icon prop (replaces the raw <i> workaround)
<BBButton layout="circle" size="sm" icon="thumbs_up" onClick={upvote} ariaLabel="Upvote" />
// 3. Forwarding a PluginIconType value straight from the SDK, unchanged
<BBButton layout="squared" icon={pluginItem.icon} onClick={run} ariaLabel={pluginItem.label} />
// 4. Replacing an icon that is hardcoded today
<BBBModal isOpen onRequestClose={close} title="Settings" closeIcon={<BBBIcon name="close" />}>
...
</BBBModal>
// 5. Material icons keep working, unchanged
<BBBHint label="Recording started" icon={<MdInfo fontSize="1rem" />} />
// 6. Glyph absent from the core font, added by the plugin's own font file;
// type-checks thanks to the `string & {}` arm of `name`
<BBBIcon name="audio-player" />
Outside the BBB client only (Storybook, standalone app, tests):
import '@bigbluebutton/bbb-ui-components-react/icon-font';
Additional context
Suggested delivery order. Parts 1–3 are self-contained and unblock consumers immediately (icon={<BBBIcon name="…" />} works on every existing prop without any other change). Parts 4–6 can follow as separate PRs; part 4 carries the breaking-change decision and should be settled before it lands.
Open questions for review
- Bare string vs
{ iconName } only — see the flag under part 4.
- Where is the pinned
bbb-icons.css read from at generation time: a committed vendored copy, or fetched from a tag of bigbluebutton/bigbluebutton?
- Should the generator run in CI to detect drift against a BBB release, or stay fully manual?
BBBSelect.dropdownIcon has to bridge MUI's IconComponent (component type) and this library's element-based icon props — confirm the adapter shape.
- Does
BBBIcon need the svgContent arm of PluginIconType too, so a plugin can forward any PluginIconType value, not just the name-based ones?
Repository conventions. The new component follows CLAUDE.md: folder src/components/Icon/ with component.tsx, styles.ts, index.ts, types.ts, constants.ts, component.stories.tsx, README.md, assets/example.png; plus entries in src/components/index.ts, the root README.md, webpack.config.babel.js and package.json exports. The Storybook story is the natural place to render the full 135-glyph grid as living documentation of the available names.
Sources consulted
bigbluebutton/bigbluebutton-html5: public/stylesheets/bbb-icons.css, public/fonts/BbbIcons/, client/main.html:164-190, imports/ui/components/common/icon/, imports/ui/components/common/menu/component.jsx:23-44, imports/ui/components/common/separator/component.tsx:15-17
bigbluebutton-html-plugin-sdk: src/extensible-areas/common/icon/types.ts
bigbluebutton-plugin-audioplayer: src/main.css:1-20
- This repository:
src/components/Button/styles.ts:218, and the icon props of every component listed above
Is your feature request related to a problem? Please describe.
Every icon slot in this library is typed as
React.ReactNode, and the built-in defaults are drawn from three unrelated sources —react-icons/md,@mui/icons-material, and hand-written inline SVG:BBButtonhelperIcondefault,feedbackContentdefaultMdSettings,MdCheckCircleBBBNavigationiconMdExpandCircleDownBBBHinticondefault, close buttonMdInfo,MdCloseBBBModalMdCloseBBBAccordionMdExpandMoreBBBSelectMdExpandMoreBBBCheckboxRadioButtonUnchecked/RadioButtonCheckedBBBSearch<svg>The BigBlueButton client draws its own UI with a different icon set — the
bbb-iconswebfont. A plugin built with this library therefore renders Material Design glyphs next to BBB glyphs, in the same toolbar, side by side. There is currently no supported way to ask a component for a BBB glyph.The library has in fact already grown an undocumented accommodation for it.
src/components/Button/styles.ts:218contains:A bare
> ielement selector only makes sense for icon-font<i>elements — the icon-font path exists in this codebase today, it is just accidental rather than designed.On top of that, several icons are hardcoded with no prop to override them at all (
BBBModalclose,BBBAccordionexpand,BBBSelectdropdown arrow,BBBSearchsearch/clear), so even the workaround above cannot reach them.How BigBlueButton does it today (analysis)
Checked against
bigbluebutton/bigbluebutton,bigbluebutton-html5.The font and its stylesheet are global, and live outside React.
public/fonts/BbbIcons/bbb-icons.woff2andbbb-icons.woffpublic/stylesheets/bbb-icons.css— 437 lines, 135.icon-bbb-*rulesclient/main.htmlwires both up before the app boots:The stylesheet does the styling; the React component only picks a class.
Two consequences worth keeping: the glyph is sized by
font-size(it is1emwide) and colored bycolor, exactly like text — so it composes with the flex wrappers this library already uses; and[class^="icon-bbb-"]matches any class with that prefix, which is how plugins extend the set (see below).The client's own
Iconcomponent is a thin class-name builder (imports/ui/components/common/icon/icon-ts/component.tsx):Styled.Iconisstyled.iwith only a$rotatetransform. The oldercomponent.jsxvariant adds acolorprop. Note the accessibility default: hidden unless anariaLabelis given, in which case it becomesrole="img".prependIconNameis a real extension point, not decoration.bigbluebutton-plugin-audioplayer/src/main.cssships its own glyphs under the same family and prefix:Any design here must keep a name outside the known set renderable.
The plugin SDK already standardised an icon-name value type.
bigbluebutton-html-plugin-sdk/src/extensible-areas/common/icon/types.ts:and the client normalises it in several places, e.g.
imports/ui/components/common/menu/component.jsx:23andimports/ui/components/common/separator/component.tsx:15-17:Plugin authors already hold
PluginIconTypevalues. If this library speaks the same shape, those values can be forwarded to aBBButtonunchanged.Duplication is a known cost in the monorepo.
bbb-learning-dashboardkeeps its own copy atsrc/fonts/BbbIcons/bbb-icons.woff. Adding a third copy that ships inside the client bundle would be a regression, which drives the font delivery decision below.Describe the solution you'd like
Six parts. Parts 1–3 are the core and are non-breaking; parts 4–6 are the ergonomics and coverage work.
1. A new
BBBIconcomponentNew folder
src/components/Icon/, exported asBBBIcon, mirroring the client's contract so that a glyph rendered by this library is indistinguishable from one rendered by BBB itself.It ships no CSS and loads no font. It only emits the class name and relies on the rules already present in the host document.
nameis typed asBBBIconName | (string & {})rather than a closed union: autocomplete lists the 135 known names, while a plugin-provided glyph still type-checks.2. Generated, typed icon names
scripts/generate-icon-names.tsparsesbbb-icons.cssfrom a pinned BBB version and emitssrc/components/Icon/icon-names.ts:Regenerating is a deliberate, reviewable commit, so the pinned BBB version is visible in the diff and the union never drifts silently.
3. Font assets only outside the BBB client
The constraint: inside the BBB client the library must add nothing — no
@font-face, no re-declaration ofbbb-icons.css. Redeclaring@font-facefor the familybbb-iconswould fight the client's own declaration, and shipping the.woff2in the bundle would be a third copy of a font already served.Proposal:
The same generator vendors
bbb-icons.css+bbb-icons.woff2as a dev-only asset, imported by.storybook/preview.ts. It is not reachable from any component's module graph, so it cannot end up in a consumer's bundle.For apps outside BBB that still want the glyphs, a separate opt-in entry point with the side effect:
Added to
package.jsonexportsand towebpack.config.babel.jslike any other entry, and listed insideEffects.No component ever imports it. Inside BBB, nobody calls it and nothing is added.
The
BBBIconREADME documents this explicitly: in a BBB plugin, import nothing — the font is already there.4. Accept icon names on the existing icon props
A shared internal helper normalises the three accepted shapes, matching what the client already does for
PluginIconType:Every existing icon prop widens from
React.ReactNodetoBBBIconPropand is passed throughrenderIconat render time.<MdSend />keeps working unchanged.5. Expose the icons that are currently hardcoded
These have no override today. Each gets a
BBBIconPropprop whose default is the current Material icon, so behaviour is unchanged when the prop is omitted.BBBModalcloseIconcomponent.tsx:70<MdClose size="1.5rem" />BBBHintcloseIconcomponent.tsx:58<MdClose fontSize="1rem" />BBBAccordionexpandIconcomponent.tsx:39<MdExpandMore />BBBSelectdropdownIconcomponent.tsx:38MdExpandMore(MUIIconComponent)BBBSearchsearchIcon,clearIconcomponent.tsx:87-103,:129-145<svg>BBBSelect.dropdownIconneeds care: MUI'sIconComponenttakes a component type, not an element, so the prop has to be adapted rather than forwarded.BBBCheckbox'sRadioButtonUnchecked/RadioButtonCheckedare left alone — they are theroundvariant's control rendering, not a decorative icon slot.6. Replace the ad-hoc
> irule with a documented sizing contractsrc/components/Button/styles.ts:218currently hardcodes> i { font-size: 2rem; }. WithBBBIconowningsize, that rule should become an explicit default that a caller can override, and the same sizing question answered consistently forBBBNavigation,BBBHint,BBBSliderandBBBInput, whose icon wrappers are each sized differently today.Worth documenting once, in the
BBBIconREADME: a font glyph inheritsfont-sizeandcolor, sosize/colorbehave like the identically named props ofreact-icons— the two icon sources stay interchangeable inside the same wrapper.Describe alternatives you've considered
Do nothing; consumers keep passing
<i className="icon-bbb-…" />. Rejected: no type safety, no a11y defaults, no sizing contract, the> ihack stays undocumented, and it cannot reach the hardcoded icons at all.Convert the font to individual React SVG components (à la
react-icons), generated from the font. Best tree-shaking and no global CSS dependency at all. Rejected: the font is not the source of truth for the vectors, the generated output would drift from the client's rendering, and it duplicates in bytes exactly what the client already loads — for the primary consumer (a plugin inside BBB) this is strictly worse.Always bundle the font and the
@font-facein the library. Self-contained and the simplest story for Storybook. Rejected per the constraint above: it duplicates ~30KB already served by the client, declares a competing@font-facefor the familybbb-icons, and risks rendering a different font version than the surrounding UI.Re-export the client's
Iconcomponent from the SDK instead. The SDK does not export it, and the library must stay usable outside a plugin context. Keeping a localBBBIconwhose contract matches the client's is the cheaper coupling.Affected component(s)
BBBIcon(new),BBButton,BBBNavigation,BBBHint,BBBSelect,BBBInput,BBBSlider,BBBModal,BBBAccordion,BBBSearch.Prop changes, per component
BBBIconBBButtonicon(circle/squared/stacked)React.ReactNode→BBBIconPropBBButtoniconStart,iconEnd(default layout)React.ReactNode→BBBIconPropBBButtonhelperIcon(stacked)React.ReactNode→BBBIconPropBBBNavigationiconReact.ReactNode→BBBIconPropBBBHinticonReact.ReactNode→BBBIconPropBBBHintcloseIcon<MdClose fontSize="1rem" />BBBSelecticonReact.ReactNode→BBBIconPropBBBSelectdropdownIconMdExpandMoreBBBInputbuttonIconReact.ReactNode→BBBIconPropBBBSlidericonStart,iconEndReact.ReactNode→BBBIconPropBBBModalcloseIcon<MdClose size="1.5rem" />BBBAccordionexpandIcon<MdExpandMore />BBBSearchsearchIcon,clearIconNot changed:
BBButton.feedbackContent,BBBInput.beforeButton/afterButton/sentFeedbackContent,BBBAccordion.buttonHeader,BBBModal.footerContent— these are general content slots, not icon slots, and already accept a<BBBIcon />element as-is.BBBCheckboxandBBBToggleare unaffected.Proposed API / Usage Example
Outside the BBB client only (Storybook, standalone app, tests):
Additional context
Suggested delivery order. Parts 1–3 are self-contained and unblock consumers immediately (
icon={<BBBIcon name="…" />}works on every existing prop without any other change). Parts 4–6 can follow as separate PRs; part 4 carries the breaking-change decision and should be settled before it lands.Open questions for review
{ iconName }only — see the flag under part 4.bbb-icons.cssread from at generation time: a committed vendored copy, or fetched from a tag ofbigbluebutton/bigbluebutton?BBBSelect.dropdownIconhas to bridge MUI'sIconComponent(component type) and this library's element-based icon props — confirm the adapter shape.BBBIconneed thesvgContentarm ofPluginIconTypetoo, so a plugin can forward anyPluginIconTypevalue, not just the name-based ones?Repository conventions. The new component follows
CLAUDE.md: foldersrc/components/Icon/withcomponent.tsx,styles.ts,index.ts,types.ts,constants.ts,component.stories.tsx,README.md,assets/example.png; plus entries insrc/components/index.ts, the rootREADME.md,webpack.config.babel.jsandpackage.jsonexports. The Storybook story is the natural place to render the full 135-glyph grid as living documentation of the available names.Sources consulted
bigbluebutton/bigbluebutton-html5:public/stylesheets/bbb-icons.css,public/fonts/BbbIcons/,client/main.html:164-190,imports/ui/components/common/icon/,imports/ui/components/common/menu/component.jsx:23-44,imports/ui/components/common/separator/component.tsx:15-17bigbluebutton-html-plugin-sdk:src/extensible-areas/common/icon/types.tsbigbluebutton-plugin-audioplayer:src/main.css:1-20src/components/Button/styles.ts:218, and the icon props of every component listed above