diff --git a/packages/styles/scss/components/buttongroup/_buttongroup.scss b/packages/styles/scss/components/buttongroup/_buttongroup.scss
index fb8d576e9be..d6ffb8839ae 100644
--- a/packages/styles/scss/components/buttongroup/_buttongroup.scss
+++ b/packages/styles/scss/components/buttongroup/_buttongroup.scss
@@ -32,7 +32,8 @@
}
.#{$prefix}--buttongroup-item,
- :host(#{$dds-prefix}-button-group-item) {
+ :host(#{$dds-prefix}-button-group-item),
+ :host(#{$dds-prefix}-button-cta) {
margin-top: $carbon--layout-01;
max-width: carbon--mini-units(40);
min-width: 0;
diff --git a/packages/web-components/src/components/button-group/button-group.scss b/packages/web-components/src/components/button-group/button-group.scss
index 07a883c5ab6..2b4f2aafb31 100644
--- a/packages/web-components/src/components/button-group/button-group.scss
+++ b/packages/web-components/src/components/button-group/button-group.scss
@@ -7,6 +7,7 @@
@import '@carbon/ibmdotcom-styles/scss/components/buttongroup/buttongroup';
-:host(#{$dds-prefix}-button-group-item) {
+:host(#{$dds-prefix}-button-group-item),
+:host(#{$dds-prefix}-button-cta) {
outline: none;
}
diff --git a/packages/web-components/src/components/button/button.ts b/packages/web-components/src/components/button/button.ts
index 6a7d3128eea..348ef58dad3 100644
--- a/packages/web-components/src/components/button/button.ts
+++ b/packages/web-components/src/components/button/button.ts
@@ -7,22 +7,212 @@
* LICENSE file in the root directory of this source tree.
*/
-import { customElement } from 'lit-element';
-import BXBtn from 'carbon-web-components/es/components/button/button.js';
+import { classMap } from 'lit-html/directives/class-map';
+import { html, property, internalProperty, customElement, LitElement } from 'lit-element';
+import settings from 'carbon-components/es/globals/js/settings';
+import ifNonNull from 'carbon-web-components/es/globals/directives/if-non-null.js';
+import FocusMixin from 'carbon-web-components/es/globals/mixins/focus.js';
+import { BUTTON_ICON_LAYOUT, BUTTON_KIND, BUTTON_SIZE } from 'carbon-web-components/es/components/button/button.js';
import ddsSettings from '@carbon/ibmdotcom-utilities/es/utilities/settings/settings.js';
import styles from './button.scss';
-export { BUTTON_KIND, BUTTON_SIZE } from 'carbon-web-components/es/components/button/button.js';
+export { BUTTON_KIND, BUTTON_SIZE };
+const { prefix } = settings;
const { stablePrefix: ddsPrefix } = ddsSettings;
/**
* Expressive button.
*
* @element dds-btn
+ * @csspart button The button.
*/
@customElement(`${ddsPrefix}-btn`)
-class DDSBtn extends BXBtn {
+class DDSBtn extends FocusMixin(LitElement) {
+ /**
+ * `true` if there is an icon.
+ */
+ @internalProperty()
+ protected _hasIcon = false;
+
+ /**
+ * `true` if there is a non-icon content.
+ */
+ @internalProperty()
+ protected _hasMainContent = false;
+
+ /**
+ * The CSS class list for the button/link node.
+ */
+ protected get _classes() {
+ const { disabled, kind, size, _hasIcon: hasIcon, _hasMainContent: hasMainContent } = this;
+ return classMap({
+ [`${prefix}--btn`]: true,
+ [`${prefix}--btn--${kind}`]: kind,
+ [`${prefix}--btn--disabled`]: disabled,
+ [`${prefix}--btn--icon-only`]: hasIcon && !hasMainContent,
+ [`${prefix}--btn--${size}`]: size,
+ [`${prefix}-ce--btn--has-icon`]: hasIcon,
+ });
+ }
+
+ /**
+ * Handles `slotchange` event.
+ */
+ protected _handleSlotChange({ target }: Event) {
+ const { name } = target as HTMLSlotElement;
+ const hasContent = (target as HTMLSlotElement)
+ .assignedNodes()
+ .some(node => node.nodeType !== Node.TEXT_NODE || node!.textContent!.trim());
+ this[name === 'icon' ? '_hasIcon' : '_hasMainContent'] = hasContent;
+ this.requestUpdate();
+ }
+
+ /**
+ * @returns The disabled link content.
+ */
+ protected _renderDisabledLink() {
+ const { _classes: classes } = this;
+ return html`
+
+ ${this._renderInner()}
+
+ `;
+ }
+
+ /**
+ * @returns The inner content.
+ */
+ protected _renderInner() {
+ const { _handleSlotChange: handleSlotChange } = this;
+ return html`
+
+
+ `;
+ }
+
+ /**
+ * `true` if the button should have input focus when the page loads.
+ */
+ @property({ type: Boolean, reflect: true })
+ autofocus = false;
+
+ /**
+ * `true` if the button should be disabled.
+ */
+ @property({ type: Boolean, reflect: true })
+ disabled = false;
+
+ /**
+ * The default file name, used if this button is rendered as ``.
+ */
+ @property({ reflect: true })
+ download!: string;
+
+ /**
+ * Link `href`. If present, this button is rendered as ``.
+ */
+ @property({ reflect: true })
+ href!: string;
+
+ /**
+ * The language of what `href` points to, if this button is rendered as ``.
+ */
+ @property({ reflect: true })
+ hreflang!: string;
+
+ /**
+ * Button icon layout.
+ */
+ @property({ reflect: true, attribute: 'icon-layout' })
+ iconLayout = BUTTON_ICON_LAYOUT.REGULAR;
+
+ /**
+ * Button kind.
+ */
+ @property({ reflect: true })
+ kind = BUTTON_KIND.PRIMARY;
+
+ /**
+ * The a11y role for ``.
+ */
+ @property({ attribute: 'link-role' })
+ linkRole = 'button';
+
+ /**
+ * URLs to ping, if this button is rendered as ``.
+ */
+ @property({ reflect: true })
+ ping!: string;
+
+ /**
+ * The link type, if this button is rendered as ``.
+ */
+ @property({ reflect: true })
+ rel!: string;
+
+ /**
+ * Button size.
+ */
+ @property({ reflect: true })
+ size = BUTTON_SIZE.REGULAR;
+
+ /**
+ * The link target, if this button is rendered as ``.
+ */
+ @property({ reflect: true })
+ target!: string;
+
+ /**
+ * The default behavior if the button is rendered as `