Skip to content

feat(badge): add hue property for the ionic theme #30307

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 40 commits into from
Apr 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
9b25ed0
feat(css): add support for the subtle color map
brandyscarney Mar 26, 2025
2a0fdf3
Merge branch 'next' into FW-6252
brandyscarney Mar 26, 2025
e8605e7
test(theme): skip test because all tests fail on one or more colors
brandyscarney Mar 26, 2025
5391165
chore(): add updated snapshots
Ionitron Mar 26, 2025
19c944a
style: comments
brandyscarney Mar 26, 2025
3692846
feat(badge): add subtle hue for the ionic theme
brandyscarney Mar 26, 2025
4331eb7
chore: build
brandyscarney Mar 26, 2025
147550a
Merge branch 'next' into FW-6252
brandyscarney Mar 28, 2025
0d485b7
Merge branch 'FW-6252' into FW-6252-badge
brandyscarney Mar 28, 2025
e13d85d
Merge branch 'next' into FW-6252
brandyscarney Apr 2, 2025
e4f344e
Merge branch 'FW-6252' into FW-6252-badge
brandyscarney Apr 2, 2025
8fc775f
refactor(themes): add foreground color for colors when used as text
brandyscarney Apr 2, 2025
f64e50c
Merge branch 'FW-6252' into FW-6252-badge
brandyscarney Apr 2, 2025
9bfe374
test(theme): use proper color variants
brandyscarney Apr 2, 2025
caf3c2d
style: lint
brandyscarney Apr 3, 2025
aed941f
test(themes): re-enable tests with correct checks
brandyscarney Apr 3, 2025
e86e9d5
chore(): add updated snapshots
Ionitron Apr 3, 2025
59d713f
revert back to tokens
brandyscarney Apr 3, 2025
6306fe6
style: lint
brandyscarney Apr 3, 2025
203c306
Merge branch 'FW-6252' into FW-6252-badge
brandyscarney Apr 3, 2025
a84f282
Merge branch 'next' into FW-6252
brandyscarney Apr 4, 2025
60a241f
fix(themes): add tertiary, remove TODOs
brandyscarney Apr 4, 2025
465a5ef
lint again and again and again
brandyscarney Apr 4, 2025
252de35
Merge branch 'FW-6252' into FW-6252-badge
brandyscarney Apr 4, 2025
3187866
chore(): add updated snapshots
Ionitron Apr 4, 2025
75421d3
fix(themes): update other palettes to include foreground
brandyscarney Apr 4, 2025
eca3566
fix(themes): do not require the foreground variant for ios and md
brandyscarney Apr 7, 2025
67f8334
Merge branch 'next' into FW-6252
brandyscarney Apr 7, 2025
a4b5358
Merge branch 'FW-6252' into FW-6252-badge
brandyscarney Apr 7, 2025
333f5ba
test(badge): add hue test
brandyscarney Apr 7, 2025
344e8e6
test(badge): add default badge color to hue test
brandyscarney Apr 7, 2025
502fab6
fix(badge): should default hue to subtle
brandyscarney Apr 7, 2025
71f108c
fix(badge): only apply subtle when badge is used by itself with text …
brandyscarney Apr 8, 2025
36ba15c
chore(): add updated snapshots
brandyscarney Apr 8, 2025
3a32a7b
fix(badge): use bold hue for avatar
brandyscarney Apr 8, 2025
0ce063b
chore(): add updated snapshots
brandyscarney Apr 8, 2025
02ece96
chore: build
brandyscarney Apr 8, 2025
7d9352e
refactor(badge): only apply the color / background to hue classes
brandyscarney Apr 8, 2025
fdfeaad
docs
brandyscarney Apr 11, 2025
a2fedd0
Merge branch 'next' into FW-6252-badge
brandyscarney Apr 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ ion-backdrop,event,ionBackdropTap,void,true

ion-badge,shadow
ion-badge,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
ion-badge,prop,hue,"bold" | "subtle" | undefined,undefined,false,false
ion-badge,prop,mode,"ios" | "md",undefined,false,false
ion-badge,prop,shape,"round | rectangular" | "soft" | undefined,undefined,false,false
ion-badge,prop,size,"large" | "medium" | "small" | "xlarge" | "xsmall" | "xxsmall" | undefined,undefined,false,false
Expand Down
8 changes: 8 additions & 0 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,10 @@ export namespace Components {
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
/**
* Set to `"bold"` for a badge with vibrant, bold colors or to `"subtle"` for a badge with muted, subtle colors. Only applies to the `ionic` theme.
*/
"hue"?: 'bold' | 'subtle';
/**
* The mode determines the platform behaviors of the component.
*/
Expand Down Expand Up @@ -5870,6 +5874,10 @@ declare namespace LocalJSX {
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
/**
* Set to `"bold"` for a badge with vibrant, bold colors or to `"subtle"` for a badge with muted, subtle colors. Only applies to the `ionic` theme.
*/
"hue"?: 'bold' | 'subtle';
/**
* The mode determines the platform behaviors of the component.
*/
Expand Down
5 changes: 0 additions & 5 deletions core/src/components/badge/badge.common.scss
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,6 @@
vertical-align: baseline;
}

:host(.ion-color) {
background: #{color.current-color(base)};
color: #{color.current-color(contrast)};
}

// Badge (hint)
// --------------------------------------------------

Expand Down
28 changes: 26 additions & 2 deletions core/src/components/badge/badge.ionic.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
// --------------------------------------------------

:host {
--background: #{globals.ion-color(primary, base)};
--color: #{globals.ion-color(primary, contrast)};
--padding-start: #{globals.$ion-space-200};
--padding-end: #{globals.$ion-space-200};
--padding-top: #{globals.$ion-space-0};
Expand All @@ -20,6 +18,32 @@
font-weight: globals.$ion-font-weight-medium;
}

// Bold Badge
// --------------------------------------------------

:host(.badge-bold) {
--background: #{globals.ion-color(primary, base)};
--color: #{globals.ion-color(primary, contrast)};
}

:host(.badge-bold.ion-color) {
background: globals.current-color(base);
color: globals.current-color(contrast);
}

// Subtle Badge
// --------------------------------------------------

:host(.badge-subtle) {
--background: #{globals.ion-color(primary, base, $subtle: true)};
--color: #{globals.ion-color(primary, contrast, $subtle: true)};
}

:host(.badge-subtle.ion-color) {
background: globals.current-color(base, $subtle: true);
color: globals.current-color(contrast, $subtle: true);
}

// Badge Shapes
// --------------------------------------------------

Expand Down
5 changes: 5 additions & 0 deletions core/src/components/badge/badge.native.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
font-family: $font-family-base;
}

:host(.ion-color) {
background: current-color(base);
color: current-color(contrast);
}

// TODO(ROU-10747): Review size styles when sizes are defined for native themes.
:host([vertical]:not(.in-button):not(.in-tab-button)),
:host(:empty) {
Expand Down
35 changes: 35 additions & 0 deletions core/src/components/badge/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ export class Badge implements ComponentInterface {
*/
@Prop({ reflect: true }) color?: Color;

/**
* Set to `"bold"` for a badge with vibrant, bold colors or to `"subtle"` for
* a badge with muted, subtle colors.
*
* Only applies to the `ionic` theme.
*/
@Prop() hue?: 'bold' | 'subtle';

/**
* Set to `"rectangular"` for non-rounded corners.
* Set to `"soft"` for slightly rounded corners.
Expand Down Expand Up @@ -87,7 +95,33 @@ export class Badge implements ComponentInterface {
return size;
}

// The 'subtle' hue is the default for badges containing text or icons
// The 'bold' hue is used when inside of an avatar, button, tab button,
// or when the badge is empty (no text or icon).
private getHue(): string | undefined {
const { hue } = this;

if (hue !== undefined) {
return hue;
}

const inAvatar = hostContext('ion-avatar', this.el);
const inButton = hostContext('ion-button', this.el);
const inTabButton = hostContext('ion-tab-button', this.el);
const hasContent = this.el.textContent?.trim() !== '' || this.el.querySelector('ion-icon') !== null;

// Return 'bold' if the badge is inside an avatar, button, tab button,
// or has no content
if (inAvatar || inButton || inTabButton || !hasContent) {
return 'bold';
}

// Return 'subtle' if the badge contains visible text or an icon
return 'subtle';
}

render() {
const hue = this.getHue();
const shape = this.getShape();
const size = this.getSize();
const theme = getIonTheme(this);
Expand All @@ -96,6 +130,7 @@ export class Badge implements ComponentInterface {
<Host
class={createColorClasses(this.color, {
[theme]: true,
[`badge-${hue}`]: hue !== undefined,
[`badge-${shape}`]: shape !== undefined,
[`badge-${size}`]: size !== undefined,
[`badge-vertical-${this.vertical}`]: this.vertical !== undefined,
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
80 changes: 80 additions & 0 deletions core/src/components/badge/test/hue/badge.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';

configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ config, screenshot, title }) => {
test.describe(title('badge: hue'), () => {
test('should render subtle badges', async ({ page }) => {
await page.setContent(
`
<div id="container">
<ion-badge>99</ion-badge>
<ion-badge color="primary">99</ion-badge>
<ion-badge color="secondary">99</ion-badge>
<ion-badge color="tertiary">99</ion-badge>
<ion-badge color="success">99</ion-badge>
<ion-badge color="warning">99</ion-badge>
<ion-badge color="danger">99</ion-badge>
<ion-badge color="light">99</ion-badge>
<ion-badge color="medium">99</ion-badge>
<ion-badge color="dark">99</ion-badge>

<br>

<ion-badge><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="primary"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="secondary"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="tertiary"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="success"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="warning"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="danger"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="light"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="medium"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="dark"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
</div>
`,
config
);

const container = page.locator('#container');

await expect(container).toHaveScreenshot(screenshot(`badge-hue-subtle`));
});

test('should render bold badges', async ({ page }) => {
await page.setContent(
`
<div id="container">
<ion-badge hue="bold">99</ion-badge>
<ion-badge hue="bold" color="primary">99</ion-badge>
<ion-badge hue="bold" color="secondary">99</ion-badge>
<ion-badge hue="bold" color="tertiary">99</ion-badge>
<ion-badge hue="bold" color="success">99</ion-badge>
<ion-badge hue="bold" color="warning">99</ion-badge>
<ion-badge hue="bold" color="danger">99</ion-badge>
<ion-badge hue="bold" color="light">99</ion-badge>
<ion-badge hue="bold" color="medium">99</ion-badge>
<ion-badge hue="bold" color="dark">99</ion-badge>

<br>

<ion-badge hue="bold"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="primary"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="secondary"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="tertiary"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="success"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="warning"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="danger"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="light"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="medium"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="dark"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
</div>
`,
config
);

const container = page.locator('#container');

await expect(container).toHaveScreenshot(screenshot(`badge-hue-bold`));
});
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
78 changes: 78 additions & 0 deletions core/src/components/badge/test/hue/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Badge - Hue</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head>

<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Badge - Hue</ion-title>
</ion-toolbar>
</ion-header>

<ion-content id="content">
<h2>Badge Hue: Subtle</h2>
<ion-badge>99</ion-badge>
<ion-badge color="primary">99</ion-badge>
<ion-badge color="secondary">99</ion-badge>
<ion-badge color="tertiary">99</ion-badge>
<ion-badge color="success">99</ion-badge>
<ion-badge color="warning">99</ion-badge>
<ion-badge color="danger">99</ion-badge>
<ion-badge color="light">99</ion-badge>
<ion-badge color="medium">99</ion-badge>
<ion-badge color="dark">99</ion-badge>

<br />

<ion-badge><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="primary"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="secondary"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="tertiary"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="success"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="warning"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="danger"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="light"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="medium"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge color="dark"><ion-icon name="logo-ionic"></ion-icon></ion-badge>

<h2>Badge Hue: Bold</h2>
<ion-badge hue="bold">99</ion-badge>
<ion-badge hue="bold" color="primary">99</ion-badge>
<ion-badge hue="bold" color="secondary">99</ion-badge>
<ion-badge hue="bold" color="tertiary">99</ion-badge>
<ion-badge hue="bold" color="success">99</ion-badge>
<ion-badge hue="bold" color="warning">99</ion-badge>
<ion-badge hue="bold" color="danger">99</ion-badge>
<ion-badge hue="bold" color="light">99</ion-badge>
<ion-badge hue="bold" color="medium">99</ion-badge>
<ion-badge hue="bold" color="dark">99</ion-badge>

<br />

<ion-badge hue="bold"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="primary"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="secondary"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="tertiary"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="success"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="warning"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="danger"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="light"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="medium"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
<ion-badge hue="bold" color="dark"><ion-icon name="logo-ionic"></ion-icon></ion-badge>
</ion-content>
</ion-app>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions core/src/css/palettes/dark.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,54 +17,63 @@ $colors: (
contrast: #000,
shade: get-color-shade($primary),
tint: get-color-tint($primary),
foreground: $primary,
),
secondary: (
base: $secondary,
contrast: #000,
shade: get-color-shade($secondary),
tint: get-color-tint($secondary),
foreground: $secondary,
),
tertiary: (
base: $tertiary,
contrast: #000,
shade: get-color-shade($tertiary),
tint: get-color-tint($tertiary),
foreground: $tertiary,
),
success: (
base: $success,
contrast: #000,
shade: get-color-shade($success),
tint: get-color-tint($success),
foreground: $success,
),
warning: (
base: $warning,
contrast: #000,
shade: get-color-shade($warning),
tint: get-color-tint($warning),
foreground: $warning,
),
danger: (
base: $danger,
contrast: #000,
shade: get-color-shade($danger),
tint: get-color-tint($danger),
foreground: $danger,
),
light: (
base: $light,
contrast: #fff,
shade: get-color-shade($light),
tint: get-color-tint($light),
foreground: $light,
),
medium: (
base: $medium,
contrast: #000,
shade: get-color-shade($medium),
tint: get-color-tint($medium),
foreground: $medium,
),
dark: (
base: $dark,
contrast: #000,
shade: get-color-shade($dark),
tint: get-color-tint($dark),
foreground: $dark,
),
);

Expand Down
Loading
Loading