Repository: Automattic/wp-calypso
Pull request #101376: A4A: Replace Pressable offering badge with CoreBadge
Author: login: mirka
Labels: name: Bug
description: When a feature is broken and / or not performing as intended
name: Accessibility
description: (a11y)
Created: 2025-03-14T18:00:15Z | Merged: 2025-03-17T16:36:45Z

Part of #101301
Stacked on #101377

## Proposed Changes

Replaces the Pressable offering badge in the A4A overview, from the `a8c-components` `Badge` with style overrides to the standard `CoreBadge`.

| Before | After |
|--------|-------|
|<img src="https://github.com/user-attachments/assets/85689f56-f639-4bac-b085-0706b16ddf3a" alt="Offering badge, before" width="663">|<img src="https://github.com/user-attachments/assets/2aeb8558-c691-42d0-be62-3a9b97cfd14b" alt="Offering badge, after" width="663">|

## Why are these changes being made?

To fix color contrast issues and consistency.

## Testing Instructions

Force enable this condition to show the badge:

```diff
diff --git a/client/a8c-for-agencies/sections/overview/body/hosting/pressable-offerring.tsx b/client/a8c-for-agencies/sections/overview/body/hosting/pressable-offerring.tsx
index 9c363dd01b..73e462526b 100644
--- a/client/a8c-for-agencies/sections/overview/body/hosting/pressable-offerring.tsx
+++ b/client/a8c-for-agencies/sections/overview/body/hosting/pressable-offerring.tsx
@@ -44,9 +44,7 @@ const PressableOffering = () => {
 			<h3 className="a4a-offering-item__title">
 				<HStack spacing={ 3 }>
 					<span>{ translate( 'Pressable' ) }</span>
-					{ isPressableRegular && (
-						<CoreBadge intent="success">{ translate( "You're signed up!" ) }</CoreBadge>
-					) }
+					{ true && <CoreBadge intent="success">{ translate( "You're signed up!" ) }</CoreBadge> }
 				</HStack>
 			</h3>
 		</div>
```

Run `yarn start-a8c-for-agencies` and go to `http://agencies.localhost:3000/overview`.

## Pre-merge Checklist

<!--
Complete applicable items on this checklist **before** merging into trunk. Inapplicable items can be left unchecked.

Both the PR author and reviewer are responsible for ensuring the checklist is completed.
-->

- [ ] Has the general commit checklist been followed? (PCYsg-hS-p2)
- [ ] [Have you written new tests](https://wpcalypso.wordpress.com/devdocs/docs/testing/index.md) for your changes?
- [ ] Have you tested the feature in Simple (P9HQHe-k8-p2), Atomic (P9HQHe-jW-p2), and self-hosted Jetpack sites (PCYsg-g6b-p2)?
- [ ] Have you checked for TypeScript, React or other console errors?
- [ ] Have you used memoizing on expensive computations? More info in [Memoizing with create-selector](https://github.com/Automattic/wp-calypso/blob/trunk/packages/state-utils/src/create-selector/README.md) and [Using memoizing selectors](https://react-redux.js.org/api/hooks#using-memoizing-selectors) and [Our Approach to Data](https://github.com/Automattic/wp-calypso/blob/trunk/docs/our-approach-to-data.md)
- [ ] Have we added the "[Status] String Freeze" label as soon as any new strings were ready for translation (p4TIVU-5Jq-p2)?
    - [ ] For UI changes, have we tested the change in various languages (for example, ES, PT, FR, or DE)? The length of text and words vary significantly between languages. 
- [ ] For changes affecting Jetpack: Have we added the "[Status] Needs Privacy Updates" label if this pull request changes what data or activity we track or use (p4TIVU-aUh-p2)?



Diff:
diff --git a/client/a8c-for-agencies/sections/overview/body/hosting/index.tsx b/client/a8c-for-agencies/sections/overview/body/hosting/index.tsx
index 5a0fdbae3fc0..a5d7795cafeb 100644
--- a/client/a8c-for-agencies/sections/overview/body/hosting/index.tsx
+++ b/client/a8c-for-agencies/sections/overview/body/hosting/index.tsx
@@ -9,7 +9,7 @@ import { A4A_MARKETPLACE_HOSTING_WPCOM_LINK } from 'calypso/a8c-for-agencies/com
 import WordPressLogo from 'calypso/components/wordpress-logo';
 import { recordTracksEvent } from 'calypso/state/analytics/actions/record';
 import './styles.scss';
-import PressableOffering from './pressable-oferring';
+import PressableOffering from './pressable-offering';
 
 const OverviewBodyHosting = () => {
 	const translate = useTranslate();
diff --git a/client/a8c-for-agencies/sections/overview/body/hosting/pressable-oferring.tsx b/client/a8c-for-agencies/sections/overview/body/hosting/pressable-offering.tsx
similarity index 88%
rename from client/a8c-for-agencies/sections/overview/body/hosting/pressable-oferring.tsx
rename to client/a8c-for-agencies/sections/overview/body/hosting/pressable-offering.tsx
index 577da2753679..9c363dd01bee 100644
--- a/client/a8c-for-agencies/sections/overview/body/hosting/pressable-oferring.tsx
+++ b/client/a8c-for-agencies/sections/overview/body/hosting/pressable-offering.tsx
@@ -1,10 +1,12 @@
 import page from '@automattic/calypso-router';
-import { Badge, Button, FoldableCard, Gridicon } from '@automattic/components';
+import { Button, FoldableCard, Gridicon } from '@automattic/components';
+import { __experimentalHStack as HStack } from '@wordpress/components';
 import { Icon, external } from '@wordpress/icons';
 import { useTranslate } from 'i18n-calypso';
 import { useDispatch, useSelector } from 'react-redux';
 import { A4A_MARKETPLACE_HOSTING_PRESSABLE_LINK } from 'calypso/a8c-for-agencies/components/sidebar-menu/lib/constants';
 import pressableIcon from 'calypso/assets/images/pressable/pressable-icon.svg';
+import CoreBadge from 'calypso/components/core/badge';
 import { getActiveAgency, isAgencyOwner } from 'calypso/state/a8c-for-agencies/agency/selectors';
 import { recordTracksEvent } from 'calypso/state/analytics/actions/record';
 
@@ -39,14 +41,14 @@ const PressableOffering = () => {
 	const header = (
 		<div className="a4a-offering-item__title-container">
 			<img className="pressable-icon" src={ pressableIcon } alt="Pressable" />
-			<div className="a4a-overview-pressable-offering-header-title">
-				<h3 className="a4a-offering-item__title">
-					{ translate( 'Pressable' ) }
+			<h3 className="a4a-offering-item__title">
+				<HStack spacing={ 3 }>
+					<span>{ translate( 'Pressable' ) }</span>
 					{ isPressableRegular && (
-						<Badge type="success">{ translate( "You're signed up!" ) }</Badge>
+						<CoreBadge intent="success">{ translate( "You're signed up!" ) }</CoreBadge>
 					) }
-				</h3>
-			</div>
+				</HStack>
+			</h3>
 		</div>
 	);
 
diff --git a/client/a8c-for-agencies/sections/overview/body/hosting/styles.scss b/client/a8c-for-agencies/sections/overview/body/hosting/styles.scss
index fea09f561136..28cc5fc3ffae 100644
--- a/client/a8c-for-agencies/sections/overview/body/hosting/styles.scss
+++ b/client/a8c-for-agencies/sections/overview/body/hosting/styles.scss
@@ -3,25 +3,6 @@
 	margin: 0;
 }
 
-.a4a-overview-pressable-offering-header-title {
-	.badge {
-		margin-left: 12px;
-	}
-
-	.badge--success {
-		background: var(--studio-green-5);
-		color: var(--studio-green-50);
-	}
-	.badge--warning {
-		background: var(--studio-yellow-5);
-		color: var(--studio-yellow-70);
-	}
-	.badge--error {
-		background: var(--studio-red-5);
-		color: var(--studio-red-70);
-	}
-}
-
 .pressable-icon {
 	width: 24px;
 	height: 24px;
diff --git a/client/components/core/badge/README.md b/client/components/core/badge/README.md
index badfa9722cb8..bff3c027212e 100644
--- a/client/components/core/badge/README.md
+++ b/client/components/core/badge/README.md
@@ -1,6 +1,6 @@
 # CoreBadge Component
 
-A wrapper component around WordPress's private `Badge` component from `@wordpress/components`.
+A wrapper component around WordPress's private [`Badge` component](https://wordpress.github.io/gutenberg/?path=/docs/components-badge--docs) from `@wordpress/components`.
 
 ## Usage
 
diff --git a/client/components/core/badge/index.tsx b/client/components/core/badge/index.tsx
index 37568b88070d..b359b6b8e57e 100644
--- a/client/components/core/badge/index.tsx
+++ b/client/components/core/badge/index.tsx
@@ -1,16 +1,30 @@
 import { privateApis } from '@wordpress/components';
 import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/private-apis';
 import React from 'react';
+import type { BadgeProps } from '@wordpress/components/src/badge/types';
 
-const CoreBadge = ( { children }: { children: React.ReactNode } ) => {
-	// TODO: When the component is publicly available, we should remove the private API usage and
-	// import it directly from @wordpress/components as it will cause a build error.
-	const { unlock } = __dangerousOptInToUnstableAPIsOnlyForCoreModules(
-		'I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.',
-		'@wordpress/components'
-	);
-	const { Badge } = unlock( privateApis );
-	return <Badge>{ children }</Badge>;
+// TODO: When the component is publicly available, we should remove the private API usage and
+// import it directly from @wordpress/components as it will cause a build error.
+const { unlock } = __dangerousOptInToUnstableAPIsOnlyForCoreModules(
+	'I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.',
+	'@wordpress/components'
+);
+const { Badge } = unlock( privateApis );
+
+/**
+ * A wrapper component around WordPress's private [`Badge` component](https://wordpress.github.io/gutenberg/?path=/docs/components-badge--docs)
+ * from `@wordpress/components`.
+ *
+ * ```jsx
+ * import CoreBadge from 'calypso/components/core/badge';
+ *
+ * function MyComponent() {
+ * 	return <CoreBadge>Badge Content</CoreBadge>;
+ * }
+ * ```
+ */
+const CoreBadge = ( props: BadgeProps & React.HTMLAttributes< HTMLSpanElement > ) => {
+	return <Badge { ...props } />;
 };
 
 export default CoreBadge;


Reviews:
user: login: mirka
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-14T18:50:03Z
user: login: nuriapenya
body: LGTM! 💎 Great accessibility improvement
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T12:01:38Z
user: login: tyxla
body: Tests well and code LGTM, thanks!

Let's make sure tests pass before 🚢  (looks like a flaky test)
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-17T13:27:41Z
user: login: mirka
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T14:20:31Z

Review comments:
diff_hunk: @@ -9,7 +9,7 @@ import { A4A_MARKETPLACE_HOSTING_WPCOM_LINK } from 'calypso/a8c-for-agencies/com
 import WordPressLogo from 'calypso/components/wordpress-logo';
 import { recordTracksEvent } from 'calypso/state/analytics/actions/record';
 import './styles.scss';
-import PressableOffering from './pressable-oferring';
+import PressableOffering from './pressable-offering';
path: client/a8c-for-agencies/sections/overview/body/hosting/index.tsx
user: login: mirka
body: Corrected a filename misspelling.
created_at: 2025-03-14T18:49:05Z
author_association: MEMBER
diff_hunk: @@ -3,25 +3,6 @@
 	margin: 0;
 }
 
-.a4a-overview-pressable-offering-header-title {
path: client/a8c-for-agencies/sections/overview/body/hosting/styles.scss
user: login: mirka
body: I confirmed that none of these styles are being used elsewhere.
created_at: 2025-03-14T18:49:51Z
author_association: MEMBER
diff_hunk: @@ -39,14 +41,14 @@ const PressableOffering = () => {
 	const header = (
 		<div className="a4a-offering-item__title-container">
 			<img className="pressable-icon" src={ pressableIcon } alt="Pressable" />
-			<div className="a4a-overview-pressable-offering-header-title">
-				<h3 className="a4a-offering-item__title">
-					{ translate( 'Pressable' ) }
+			<h3 className="a4a-offering-item__title">
+				<HStack spacing={ 3 }>
+					<span>{ translate( 'Pressable' ) }</span>
path: client/a8c-for-agencies/sections/overview/body/hosting/pressable-offering.tsx
user: login: tyxla
body: Why is the `span` necessary?
created_at: 2025-03-17T13:25:44Z
author_association: MEMBER
diff_hunk: @@ -39,14 +41,14 @@ const PressableOffering = () => {
 	const header = (
 		<div className="a4a-offering-item__title-container">
 			<img className="pressable-icon" src={ pressableIcon } alt="Pressable" />
-			<div className="a4a-overview-pressable-offering-header-title">
-				<h3 className="a4a-offering-item__title">
-					{ translate( 'Pressable' ) }
+			<h3 className="a4a-offering-item__title">
+				<HStack spacing={ 3 }>
+					<span>{ translate( 'Pressable' ) }</span>
path: client/a8c-for-agencies/sections/overview/body/hosting/pressable-offering.tsx
user: login: mirka
body: The flexbox behavior we want here won't kick in if it's just a raw text node 👍 
created_at: 2025-03-17T14:20:31Z
author_association: MEMBER

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-14T18:00:27Z
body: <details>
<summary>Calypso Live <a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-136941">(direct link)</a></summary>
<table>
<tr>
<td>
<img src="https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl=https%3A%2F%2Fcalypso.live%3Fimage%3Dregistry.a8c.com%2Fcalypso%2Fapp%3Abuild-136941%26flags%3Doauth&choe=UTF-8" />
</td>
<td>
<a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-136941">https://calypso.live?image=registry.a8c.com/calypso/app:build-136941</a>
</td>
</tr>
</table>
</details>
<details>
<summary>Jetpack Cloud live <a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-136941&env=jetpack">(direct link)</a></summary>
<table>
<tr>
<td>
<img src="https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl=https%3A%2F%2Fcalypso.live%3Fimage%3Dregistry.a8c.com%2Fcalypso%2Fapp%3Abuild-136941%26env%3Djetpack%26flags%3Doauth&choe=UTF-8" />
</td>
<td>
<a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-136941&env=jetpack">https://calypso.live?image=registry.a8c.com/calypso/app:build-136941&env=jetpack</a>
</td>
</tr>
</table>
</details>
<details>
<summary>Automattic for Agencies live <a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-136941&env=a8c-for-agencies">(direct link)</a></summary>
<table>
<tr>
<td>
<img src="https://chart.googleapis.com/chart?chs=150x150&cht=qr&chl=https%3A%2F%2Fcalypso.live%3Fimage%3Dregistry.a8c.com%2Fcalypso%2Fapp%3Abuild-136941%26env%3Da8c-for-agencies%26flags%3Doauth&choe=UTF-8" />
</td>
<td>
<a href="https://calypso.live?image=registry.a8c.com/calypso/app:build-136941&env=a8c-for-agencies">https://calypso.live?image=registry.a8c.com/calypso/app:build-136941&env=a8c-for-agencies</a>
</td>
</tr>
</table>
</details>
<!--calypso-live-watermark:apr@v1-->
author_association: NONE
user: login: matticbot
created_at: 2025-03-14T18:07:44Z
body: <!-- icfy-watermark: c52822 -->
Here is how your PR affects size of JS and CSS bundles shipped to the user's browser:

**Sections** (~61 bytes added 📈 [gzipped])
<details>

```
name                          parsed_size           gzip_size
a8c-for-agencies-overview          +683 B  (+0.1%)     +195 B  (+0.1%)
a8c-for-agencies-marketplace       +208 B  (+0.0%)      +32 B  (+0.0%)
```

Sections contain code specific for a given set of routes. Is downloaded and parsed only when a particular route is navigated to.
</details>

**Legend**
<details>
<summary>What is parsed and gzip size?</summary>

**Parsed Size:** Uncompressed size of the JS and CSS files. This much code needs to be parsed and stored in memory.
**Gzip Size:** Compressed size of the JS and CSS files. This much data needs to be downloaded over network.
</details>

Generated by performance advisor bot at [iscalypsofastyet.com](http://iscalypsofastyet.com).
author_association: CONTRIBUTOR
user: login: matticbot
created_at: 2025-03-14T18:15:43Z
body: **This PR modifies the release build for the following Calypso Apps:**

_For info about this notification, see here: PCYsg-OT6-p2_

* blaze-dashboard
* command-palette-wp-admin
* help-center
* notifications
* odyssey-stats
* whats-new

To test WordPress.com changes, run `install-plugin.sh $pluginSlug a4a-offering-badge` on your sandbox.
<!--calypso-app-artifacts-watermark:apr@v1-->
author_association: CONTRIBUTOR