Angular 按钮库 | ng-hub-ui-buttons

面向 Angular 22 的完整按钮系统,基于 signals API——标准按钮、FAB、speed dial 与 overlay dropdown。除 ng-hub-ui-utils 外零依赖。

最后更新 2026年9月21日

概览

团队为何搜索这个库

当你需要一个统一、可主题化的按钮生态,覆盖从行内 CTA 到浮动操作 overlay 的所有交互模式,并希望它基于 Angular Signals、拥有一致的单一 API 时,请使用 ng-hub-ui-buttons。

安装

npm install ng-hub-ui-buttons

跳转到

适用于

  • 主要 CTA 按钮
  • 浮动操作菜单
  • 上下文菜单 dropdown
  • 工具栏中的纯图标操作

关于 buttons

ng-hub-ui-buttons 是面向 Angular 22+ standalone 应用的零依赖按钮库。它提供 HubButtonComponent——可作为 <hub-button> 使用,也可作为原生宿主上的属性(<button hubButton>)(五种变体 × 六种颜色 × 四种尺寸)——HubFabComponent(九个固定位置)、HubSpeedDialComponent 以及 HubDropdownDirective(基于 overlay,由 ng-hub-ui-utils 的 OverlayService 驱动)。所有视觉属性都是 CSS 自定义属性,只需一份样式表覆盖即可为整个系统设置主题。

功能指南

Button Variants

Five visual styles (solid, outline, soft, ghost, link) × nine built-in semantic colours with size scale sm–xl.

示例:
Button Variants

Button Variants

Element formThe same component as a custom element (<hub-button>) instead of the [hubButton] attribute.

加载 / 忙碌状态

加载中的按钮会显示可替换的动画 SVG 加载图标,并完全不可交互 —— aria-busy、原生 disabled、移出 Tab 顺序,避免重复提交。

示例:
加载 / 忙碌状态

加载 / 忙碌状态

自定义颜色与变体

color 输入是开放集合:用 hub-btn-color-rules() mixin 注册任意强调色,或用 hub-btn-variant-rules() 打造全新外观。

示例:
自定义颜色与变体

自定义颜色与变体

Floating Action Button

Fixed-position circular button with nine placement presets and optional scroll-collapse behaviour.

示例:
Floating Action Button

Floating Action Button

FAB preview area — fixed within this container via CSS containment

Speed Dial

Expandable FAB that reveals a stack of action items. Closes on Escape or backdrop click.

示例:
Speed Dial

Speed Dial

Bottom right

Bottom left

Top right

Top left

Overlay Dropdown

Directive-driven dropdown that attaches a template-ref panel to any trigger element via the overlay service.

示例:
Overlay Dropdown

Overlay Dropdown

一次只开一个下拉菜单

打开任一下拉菜单都会关闭已经打开的那个,无论它是怎么被打开的。点击外部关闭只覆盖了存在点击的情形;由代码打开的菜单会让两个面板同时留在页面上。

示例:
一次只开一个下拉菜单

一次只开一个下拉菜单

Click this box, open a menu, then press 1, 2 or 3. A keypress opens the menu from code with no click anywhere — the case the click-outside rule never covered.

CSS Variable Theming

Every visual detail — size, radius, shadow, colour — is a --hub-btn-* or --hub-dropdown-* custom property.

示例:
CSS Variable Theming

CSS Variable Theming

核心特性

最近更改

Version 22.12.3 - 9/21/26, 12:00 AM

fixed: hub-fab no longer throws during server rendering. With collapseOnScroll set, ngOnInit subscribed to window without asking what platform it was on. The subscription is skipped outside the browser, as the dropdown directive and the speed dial already did.

Version 22.12.2 - 9/20/26, 12:00 AM

changed: The npm keywords name what the package ships — buttons, floating-action-button, dropdown-menu, menu — and the properties a reader filters on: standalone, zoneless, rtl. Metadata only: no code, types or styles change.

Version 22.12.1 - 9/16/26, 12:00 AM

changed: Repository, issue and README links follow the move to the hub-env organization. Issues for every Hub UI package are now gathered in hub-env/hub-ui, and the repository and bugs fields of the manifest point at the new addresses. No code, types or styles change.

Version 22.12.0 - 9/8/26, 12:00 AM

fixed: The emphasis slot that paints the label of an outline, soft, ghost or link button was mixed toward the ink by a percentage, which cannot darken a pale accent: the shipped warning measured 2.43:1 against the page and the shipped info 2.86:1, both under the 4.5:1 floor. Its luminosity is capped instead, matching the -emphasis derivation in ng-hub-ui-ds.

Version 22.11.2 - 9/8/26, 12:00 AM

changed: <hub-fab> says why it stays outside style encapsulation, which CODING_RULES.md now requires of every component that does. Nothing about the component changes; the two reasons were already load-bearing and were nowhere a reviewer could read them. First, the stylesheet is a public contract: hub-fab-color($type) is exported from ng-hub-ui-buttons/styles so a consumer can register their own accent, it emits a plain .hub-fab-<type> rule from their sheet, and the six built-in colours are generated by that very mixin from this one, so scoping this side would stop a consumer's variants being interchangeable with the built-ins. Second, the same rules dress an element in another component's view: <hub-speed-dial> renders its trigger as a button carrying the hub-fab classes inside its own template. Both are now pinned by tests, so the exception fails the day it stops being true.

added: ng-hub-ui-ds is declared as an optional peer dependency (>=22.0.0). Every colour, radius and spacing default in this library resolves through the --hub-sys-* / --hub-ref-* ladder and the manifest said nothing about it, so a consumer reading the package on npm could not tell that installing the token package is what gives the buttons the family palette and its dark mode. It stays optional: each token carries a literal fallback.

Version 22.11.1 - 9/6/26, 12:00 AM

fixed: <hub-fab> actually emits fabClick. The output was declared and documented but nothing ever emitted it, so the one event the component offers never fired and the only way to react to a FAB was to bind (click) on it, the very thing the output exists to spare you. Pressing the FAB now emits it once, and never while disabled, which is re-checked in code because pointer-events: none does not stop a programmatic click.

fixed: The @use of ng-hub-ui-buttons/styles resolves. The stylesheets have always shipped in the package, but the manifest declared no subpath for them, so tooling that honours the exports map could not reach the mixin API the README teaches, and the workaround was to reach into node_modules by physical path.

fixed: <hub-fab> is reachable without a pointer. The FAB is a custom tag with projected content, so nothing about it was a control: no role, no place in the tab order, no keyboard activation, and disabled said so only in CSS, which left screen-reader users with an unannounced blob and keyboard users with no way to reach it at all. It now advertises role="button", carries tabindex="0" (-1 while disabled, alongside aria-disabled="true") and activates on Enter and Space, the same treatment the element form of <hub-button> got in 22.8.0. Because it now reports as a button, do not nest a <hub-fab> inside another interactive element, and give it an aria-label when all it contains is an icon.

fixed: trigger="hover" on <hub-speed-dial> lets the pointer reach the items. The enter/leave pair sat on the trigger button and the items render in a sibling column one --hub-speed-dial-gap away, so starting the trip fired mouseleave and closed the dial before the pointer arrived: in hover mode no item could ever be pressed. Both handlers now sit on the host, whose box holds the button, the items and the gap between them.

fixed: trigger="hover" on [hubDropdown] lets the pointer reach the panel. The panel is attached at body level and offset from its trigger, so leaving the trigger closed the dropdown while the pointer was still crossing ground that belongs to neither. Leaving now only starts a short grace period, which arriving on the panel cancels, and the countdown starts again when the pointer leaves the panel too.

fixed: closeOnSelect="false" actually keeps the panel open. The click-outside listener excluded the trigger and nothing else, and the panel is not a descendant of its trigger since it hangs off the body, so every click on a menu item read as a click outside and closed the dropdown whatever the input said. The option now does what it documents, which is what a multi-select menu, a filter panel or a form inside a dropdown is built on.

fixed: The bare boolean attribute compiles beyond <hub-button>. 22.11.0 fixed disabled and loading on the button but left the same trap everywhere else: <hub-fab disabled>, <hub-speed-dial-item disabled>, <hub-dropdown-item selected> and closeOnSelect on [hubDropdown] all failed with TS2322, Type string is not assignable to type boolean, because an attribute written without a value passes the empty string. extended and collapseOnScroll on the FAB were in the same state. All of them now use booleanAttribute, so the bare form, ="" and [bound]="true" are interchangeable. Additive: every binding that compiled before still compiles.

changed: Comments that had fallen behind the code now match it: icon on hub-dropdown-item and hub-speed-dial-item is a CSS class, not a character or a ligature, and the SCSS headers write the current hub-button, [hubButton] selector instead of the pre-22.2.0 spelling.

changed: [hubDropdown] no longer promises a backdrop it never had. The README and the documentation site listed backdrop close among the ways the panel closes, which sent anyone counting on a dimmed, click-blocking layer behind an open menu looking for a variable to tint it. There is none: the overlay is created without hasBackdrop, so no such element is ever built and the onBackdropClick callback the directive registered could never fire. What closes the panel on an outside click is a document-level listener, and that is what the documentation now says. The dead registration is gone.

Version 22.11.0 - 9/3/26, 12:00 AM

fixed: <button hubButton disabled> compiles. `disabled` and `loading` were plain input(false) with no transform, so the bare HTML spelling of a boolean attribute — which passes the empty string — failed with TS2322, on exactly the usage `disabled`'s own documentation promises to mirror. Both use booleanAttribute now.

Version 22.10.1 - 9/1/26, 12:00 AM

fixed: A dropdown destroyed while open no longer leaves anything behind. Its overlay stayed attached to the body and the directive stayed recorded as the one open dropdown, so the next dropdown to open anywhere called close() on a destroyed instance, which emits closed on an OutputRef nobody owns any more: the browser reports that as NG0953, and it showed up in an ordinary table when you opened a row menu, navigated away and opened another. Both are torn down on destroy now, and silently, because closing there would emit the very event that has nobody left to receive it.

Version 22.10.0 - 9/1/26, 12:00 AM

added: hubActionsAdapter, so a host library can have its row actions drawn with this library button and dropdown without either package depending on the other. The same arrangement hubFormControlAdapter already uses for a table inputs: the host describes what a row offers in neutral terms and this maps the description onto the real components. Register it where the host expects it, for ng-hub-ui-paginable: provideHubPaginableActions(hubActionsAdapter).

added: hub-actions-cell, the component that adapter creates. Public because creating a component is the honest way to assemble hubDropdown: it needs a host element and an ng-template, which is natural in a template and awkward imperatively.

fixed: Only one dropdown is open at a time, however it was opened. Closing on click-outside already made a second one usually replace the first, since opening it is itself a click outside the first. Usually is not a guarantee: a dropdown opened from code produces no such click, and both panels stayed up.

fixed: A click on a row action no longer reaches whatever surrounds it. Drawn inside a clickable row, such as a table row that opens a detail page, pressing an action navigated away and the action own effect was lost with the screen it happened on.

Version 22.9.4 - 9/1/26, 12:00 AM

changed: The homepage in the manifest points at this library own documentation page rather than at the site root. Metadata only.

Version 22.9.3 - 8/17/26, 12:00 AM

fixed: The published package declared no licence. An absent license field is not neutral: a registry reports it as unlicensed, which legally reads as all rights reserved. The intent was always MIT; it is now stated in package.json and carried in a LICENSE file that ships with the package.

Version 22.9.2 - 8/8/26, 12:00 AM

fixed: Documentation links now point at the canonical localized URLs. The README linked to https://hubui.dev/<path> with no locale prefix and no trailing slash, and both forms are 301-redirected, so every reader arriving from npm or GitHub landed on a redirect instead of the canonical page.

Version 22.9.1 - 7/26/26, 12:00 AM

fixed: Declared the real ng-hub-ui-utils peer range (>=22.7.0); the previous >=1.0.0 floor resolved to a utils major without resolveHubAccent.

Version 22.9.0 - 7/7/26, 12:00 AM

added: The hub-btn-theme mixin: one-call token theming for <hub-button> and [hubButton] — accent, which drives the whole role family, plus border-radius, padding-x, padding-y and font-size. Every parameter defaults to null, so only what you pass is emitted and the rest keep the component defaults.

changed: The color input of button, FAB, speed dial and dropdown accepts ANY colour. On top of the built-in accents it now also takes a custom accent registered by the product and a literal colour (#hex, rgb(), oklch(), a CSS named colour), resolved through the shared resolveHubAccent helper from ng-hub-ui-utils.

changed: Internal: host bindings moved from the @HostBinding and @HostListener decorators to the host object in the component metadata, as the Angular style guide asks. No public API or behaviour change.

Version 22.8.0 - 7/5/26, 12:00 AM

added: The typed color API now also accepts neutral, light and dark (the CSS already shipped), so <hub-button color="neutral"> — and fab/speed-dial/dropdown — type-check.

added: Overridable hover + a new pressed (:active) state via --hub-btn-hover-bg/-border/-color and --hub-btn-active-bg/-border/-color — retune one interaction colour with a single line; existing visuals preserved.

added: The element form <hub-button> is now keyboard-accessible: it advertises role="button", a focusable tabindex and activates on Enter/Space; the [hubButton] attribute form on a native button/anchor stays free of redundant role/tabindex.

Version 22.7.0 - 7/2/26, 12:00 AM

added: Speed-dial label tokens --hub-speed-dial-label-bg and --hub-speed-dial-label-color: a theme-aware inverted pair for the tooltip-style label chip beside each action.

fixed: Speed dial and dropdown no longer crash SSR and prerender with "document is not defined". Both subscribed to the global document keydown in their constructors for Escape-to-close, which threw on the server and aborted rendering of any page containing them. The listeners use the injected DOCUMENT now and are only wired in the browser.

fixed: The speed-dial item label rendered inverted when the ds tokens were loaded, and the token fallbacks are realigned to the ng-hub-ui-ds light defaults so the with-ds and without-ds renders match.

Version 22.6.0 - 6/30/26, 12:00 AM

changed: Icons in buttons are projected, not configured: project a <hub-icon> or any other element as the content, and its side follows the markup order, with --hub-button-gap between icon and label. There is intentionally no icon input.

removed: The iconOnly input and the forced square layout it switched on (breaking). A button sizes to its content now; for an icon-only button, project just the icon and set an aria-label. Symmetric padding keeps it visually balanced without a dedicated mode.

Version 22.5.0 - 6/30/26, 12:00 AM

changed: `HubBtnComponent` renamed to `HubButtonComponent` (the old name stays as a deprecated alias).

changed: `HubButtonComponent` now has a dual selector `hub-button, [hubButton]` — usable as `<hub-button>` or as an attribute on a native host (`<button hubButton>` / `<a hubButton>`), with the loading spinner rendered in both forms.

deprecated: `HubBtnDirective` is replaced by `HubButtonComponent` (which now matches `[hubButton]`); it remains exported as a deprecated alias.

Version 22.4.0 - 6/26/26, 12:00 AM

added: The local accent slot --hub-btn-accent, and the whole role family derived from it at runtime with color-mix() and relative colour: emphasis, subtle and on recompute live, so registering a new accent is one CSS rule and needs no recompilation.

changed: Canonical zindex token names (breaking): --hub-fab-z-index becomes --hub-fab-zindex, --hub-dropdown-panel-z-index becomes --hub-dropdown-panel-zindex and --hub-speed-dial-z-index becomes --hub-speed-dial-zindex, matching the --hub-sys-zindex-* convention. A stylesheet left on the old spelling sets a variable nothing reads.

changed: Appearance decoupled from colour: solid, outline, soft, ghost and link consume only the local accent family, so they apply to any accent, known or registered, and the known-variant loop grew from five colours to the nine canonical ones.

Version 22.3.1 - 6/25/26, 12:00 AM

fixed: Design-token consistency pass: the inline fallback defaults are aligned with the canonical ng-hub-ui-ds values, and the hardcoded literals (z-index, font-weight, line-height, radii and theme-aware colours) are routed through their --hub-sys-* and --hub-ref-* tokens, so they follow the active theme. No visual change when the ds tokens are loaded.

Version 22.3.0 - 6/25/26, 12:00 AM

changed: Every dimension token consolidated onto the shared --hub-ref-* reference scale, so button, FAB and dropdown sizing resolves through the design-system tokens instead of hand-tuned rem literals. A few defaults shift to land on the scale: button md padding 14 to 16px and 7 to 8px, gap 6 to 8px, dropdown item padding 14 to 16px, and disabled opacity 0.55 to 0.65.

Version 22.2.1 - 6/23/26, 12:00 AM

changed: btn.component.ts renamed to button.component.ts, for naming consistency with the element selector.

Version 22.2.0 - 6/23/26, 12:00 AM

changed: `HubBtnComponent` selector renamed from `hub-btn` to `hub-button`.

changed: `HubBtnDirective` selector renamed from `[hubBtn]` to `[hubButton]`.

changed: The button CSS custom properties renamed from `--hub-btn-*` to `--hub-button-*`, for naming consistency with the element selector. The `--hub-btn-*` prefix returns in 22.4.0 and 22.8.0 for a different family: the accent slot and the interaction slots.

fixed: `peerDependencies`: `ng-hub-ui-utils` corrected to `>=1.0.0` to match its actual published versioning scheme.

Version 22.1.0 - 6/23/26, 12:00 AM

added: Public SCSS mixin API at `ng-hub-ui-buttons/styles`: `hub-btn-variant-rules()` generic primitive and `hub-btn-color-rules()` convenience mixin for registering custom semantic colors without modifying the library.

added: `hub-fab-color()`, `hub-dropdown-panel-color()`, `hub-dropdown-item-color()` and their `*-rules` counterparts for extending FAB and dropdown colors.

added: `HubDropdownDirective`: panel closes automatically on scroll to keep alignment with the trigger.

changed: `HubSpeedDialComponent`: trigger slot renamed from `slot="trigger"` to `hubTrigger` attribute.

changed: `HubSpeedDialItemComponent` and `HubDropdownItemComponent`: `icon` input now expects a CSS class string rendered via `<i [class]="icon()">` instead of raw text.

fixed: `HubFabComponent`: uses `position: static` when nested inside `HubSpeedDialComponent` instead of `position: fixed`.

Version 22.0.0 - 6/17/26, 12:00 AM

added: `HubBtnComponent` and `HubBtnDirective` with five variants, six colours, four sizes, loading and icon-only modes.

added: `HubFabComponent` with nine-position grid, scroll-collapse and CSS logical properties for RTL.

added: `HubSpeedDialComponent` and `HubSpeedDialItemComponent` with two-way `isOpen` model and Escape close.

added: `HubDropdownDirective` with eight placements, click/hover triggers, `closeOnSelect` and click-outside close.

added: `HubDropdownPanelComponent`, `HubDropdownItemComponent`, `HubDropdownDividerComponent`, `HubDropdownHeaderComponent` helper components.

added: SCSS token system: `:where()` zero-specificity defaults + `@each` semantic colour loops for all variants.

常见问题

Angular 的按钮组件怎么用?

把 hubButton 放在原生 button 元素上,然后挑 variant——solid、outline、soft、ghost 或 link——以及 color 和 size,size 取 sm、md、lg、xl。loading 会把文字换成一个加载指示器并给宿主标上 aria-busy,disabled 则对应真实的 disabled 属性。另外还有一个 hub-button 元素,想要这个外观又不需要表单控件时很有用,但它不是原生按钮:它不能提交表单,也不能设 type=submit,所以没有特别的理由就还是用那个属性指令。

怎么给按钮挂一个下拉菜单?

hubDropdown 是一个指令,它接收承载面板的那个 ng-template。placement 覆盖常见的八个位置,trigger 可选 click 或 hover,offsetY 把面板从按钮上移开,closeOnSelect 在选中后关闭,isOpen 是 model(),所以你可以自己驱动它。面板内容用 hub-dropdown-panel、hub-dropdown-item、hub-dropdown-header 和 hub-dropdown-divider 来填。打开一个下拉会关掉原本开着的那个,Escape 和点击外部也都会把它关上。

hub-fab 和 hub-speed-dial 有什么区别?

hub-fab 是单个浮动操作按钮:有 color,size 取 mini、standard 或 large,extended 对应带文字的版本,position 决定它待在哪个角落,collapseOnScroll 让它随着页面滚动而收缩。hub-speed-dial 是打开时展开成一组 hub-speed-dial-item 子项的那一个,direction 可取 up、down、left 或 right,trigger 可取 click 或 hover,还有双向的 isOpen 以及 opened 和 closed。记得给每个子项都加 label:没有它,无障碍名称会退回到图标的 class。

这个下拉菜单是无障碍的 menu 吗?

单靠它自己不是,上线之前值得知道这一点。那个面板只是一个定位好的浮层,里面是按钮形态的条目:没有 role=menu,没有 role=menuitem,触发元素上没有 aria-haspopup 或 aria-expanded,条目之间不能用方向键移动,打开时焦点不会移入面板,关闭时也不会还原。Escape 和点击外部是处理了的,每个条目也都是真正的 button,所以用 Tab 键能到达。如果你需要完整的 ARIA menu 模式,那些角色和按键处理要你自己补。