diff --git a/pom.xml b/pom.xml
index 9172d331517..467fc4e7368 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,6 +20,7 @@
vaadin-avatar-flow-parent
vaadin-app-layout-flow-parent
vaadin-aura-theme-flow-parent
+ vaadin-badge-flow-parent
vaadin-button-flow-parent
vaadin-card-flow-parent
vaadin-checkbox-flow-parent
diff --git a/vaadin-badge-flow-parent/pom.xml b/vaadin-badge-flow-parent/pom.xml
new file mode 100644
index 00000000000..b8cbe4c4b55
--- /dev/null
+++ b/vaadin-badge-flow-parent/pom.xml
@@ -0,0 +1,31 @@
+
+
+ 4.0.0
+
+ com.vaadin
+ vaadin-flow-components
+ 25.0-SNAPSHOT
+
+ vaadin-badge-flow-parent
+ pom
+ Vaadin Badge Parent
+ Vaadin Badge Parent
+
+ vaadin-badge-flow
+ vaadin-badge-testbench
+
+
+
+
+ default
+
+
+ !release
+
+
+
+ vaadin-badge-flow-integration-tests
+
+
+
+
diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/pom.xml b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/pom.xml
new file mode 100644
index 00000000000..12915d42967
--- /dev/null
+++ b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/pom.xml
@@ -0,0 +1,156 @@
+
+
+ 4.0.0
+
+ com.vaadin
+ vaadin-badge-flow-parent
+ 25.0-SNAPSHOT
+
+ vaadin-badge-integration-tests
+ war
+ Vaadin Badge Integration Tests
+ Vaadin Badge Integration Tests
+
+
+ com.vaadin
+ flow-client
+
+
+ com.vaadin
+ flow-data
+
+
+ com.vaadin
+ flow-html-components
+
+
+ com.vaadin
+ flow-test-util
+ test
+
+
+ com.vaadin
+ vaadin-badge-flow
+ ${project.version}
+
+
+ com.vaadin
+ vaadin-badge-testbench
+ ${project.version}
+ test
+
+
+ com.vaadin
+ vaadin-dev-server
+
+
+ com.vaadin
+ vaadin-flow-components-test-util
+ ${project.version}
+ test
+
+
+ com.vaadin
+ vaadin-lumo-theme
+
+
+ com.vaadin
+ vaadin-testbench-core
+ test
+
+
+ org.slf4j
+ slf4j-simple
+
+
+
+
+
+ maven-clean-plugin
+
+
+
+ ${project.basedir}
+
+ package*.json
+ pnpm*
+ vite.generated.ts
+ types.d.ts
+ tsconfig.json
+ frontend/routes.tsx
+ frontend/App.tsx
+
+
+
+ ${project.basedir}/node_modules
+ ${project.basedir}/frontend/generated
+
+
+
+
+
+ maven-failsafe-plugin
+
+
+ maven-resources-plugin
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+
+ true
+
+
+
+ org.codehaus.mojo
+ properties-maven-plugin
+
+
+ maven-install-plugin
+
+ true
+
+
+
+
+
+
+ build-frontend
+
+
+ !skipFrontend
+
+
+
+
+
+ com.vaadin
+ flow-maven-plugin
+
+ ./frontend
+
+
+
+
+
+
+ run-jetty
+
+
+ !skipJetty
+
+
+
+
+
+ org.eclipse.jetty.ee10
+ jetty-ee10-maven-plugin
+
+ 5
+
+
+
+
+
+
+
diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/main/java/com/vaadin/flow/component/badge/tests/BadgePage.java b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/main/java/com/vaadin/flow/component/badge/tests/BadgePage.java
new file mode 100644
index 00000000000..9ac327fd620
--- /dev/null
+++ b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/main/java/com/vaadin/flow/component/badge/tests/BadgePage.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.badge.tests;
+
+import com.vaadin.flow.component.badge.Badge;
+import com.vaadin.flow.component.badge.BadgeVariant;
+import com.vaadin.flow.component.dependency.StyleSheet;
+import com.vaadin.flow.component.html.Div;
+import com.vaadin.flow.component.html.H3;
+import com.vaadin.flow.component.html.Hr;
+import com.vaadin.flow.component.html.Span;
+import com.vaadin.flow.component.icon.VaadinIcon;
+import com.vaadin.flow.router.Route;
+import com.vaadin.flow.theme.lumo.Lumo;
+
+/**
+ * Comprehensive visual test page for the Badge component showing all variants,
+ * sizes, colors, and use cases.
+ */
+@Route("vaadin-badge")
+// Currently only Lumo is supported
+@StyleSheet(Lumo.STYLESHEET)
+public class BadgePage extends Div {
+
+ public BadgePage() {
+ setWidthFull();
+ getStyle().set("padding", "20px");
+
+ add(new H3("Badge Component - Visual Test Page"));
+
+
+ Span rawOldStyleSpanBadge = new Span(new Span("Pending"));
+ rawOldStyleSpanBadge.getElement().getThemeList().add("badge");
+
+ add(rawOldStyleSpanBadge);
+
+
+ // Basic badges
+ add(new H3("Basic Badges"));
+ Div basicSection = createSection();
+ basicSection.add(new Badge("Default"));
+ basicSection.add(new Badge("New"));
+ basicSection.add(new Badge("Beta"));
+ basicSection.add(new Badge("123"));
+ add(basicSection);
+
+ add(new Hr());
+
+ // Color variants
+ add(new H3("Color Variants"));
+ Div colorSection = createSection();
+
+ Badge primary = new Badge("Primary");
+ primary.addThemeVariants(BadgeVariant.LUMO_PRIMARY);
+ primary.setId("badge-primary");
+ colorSection.add(primary);
+
+ Badge success = new Badge("Success");
+ success.addThemeVariants(BadgeVariant.LUMO_SUCCESS);
+ success.setId("badge-success");
+ colorSection.add(success);
+
+ Badge warning = new Badge("Warning");
+ warning.addThemeVariants(BadgeVariant.LUMO_WARNING);
+ warning.setId("badge-warning");
+ colorSection.add(warning);
+
+ Badge error = new Badge("Error");
+ error.addThemeVariants(BadgeVariant.LUMO_ERROR);
+ error.setId("badge-error");
+ colorSection.add(error);
+
+ Badge contrast = new Badge("Contrast");
+ contrast.addThemeVariants(BadgeVariant.LUMO_CONTRAST);
+ contrast.setId("badge-contrast");
+ colorSection.add(contrast);
+
+ add(colorSection);
+
+ add(new Hr());
+
+ // Small size variant
+ add(new H3("Small Size"));
+ Div smallSection = createSection();
+
+ Badge smallDefault = new Badge("Small");
+ smallDefault.addThemeVariants(BadgeVariant.LUMO_SMALL);
+ smallDefault.setId("badge-small");
+ smallSection.add(smallDefault);
+
+ Badge smallPrimary = new Badge("Small Primary");
+ smallPrimary.addThemeVariants(BadgeVariant.LUMO_SMALL,
+ BadgeVariant.LUMO_PRIMARY);
+ smallSection.add(smallPrimary);
+
+ Badge smallSuccess = new Badge("Small Success");
+ smallSuccess.addThemeVariants(BadgeVariant.LUMO_SMALL,
+ BadgeVariant.LUMO_SUCCESS);
+ smallSection.add(smallSuccess);
+
+ add(smallSection);
+
+ add(new Hr());
+
+ // Pill shape variant
+ add(new H3("Pill Shape"));
+ Div pillSection = createSection();
+
+ Badge pill = new Badge("Pill");
+ pill.addThemeVariants(BadgeVariant.LUMO_PILL);
+ pill.setId("badge-pill");
+ pillSection.add(pill);
+
+ Badge pillPrimary = new Badge("99+");
+ pillPrimary.addThemeVariants(BadgeVariant.LUMO_PILL,
+ BadgeVariant.LUMO_PRIMARY);
+ pillSection.add(pillPrimary);
+
+ Badge pillSmall = new Badge("3");
+ pillSmall.addThemeVariants(BadgeVariant.LUMO_PILL,
+ BadgeVariant.LUMO_SMALL, BadgeVariant.LUMO_ERROR);
+ pillSection.add(pillSmall);
+
+ add(pillSection);
+
+ add(new Hr());
+
+ // Badges with components
+ add(new H3("Badges with Components"));
+ Div componentSection = createSection();
+
+ Badge withSpan = new Badge();
+ withSpan.add(new Span("Status: "), new Span("Active"));
+ withSpan.addThemeVariants(BadgeVariant.LUMO_SUCCESS);
+ withSpan.setId("badge-with-components");
+ componentSection.add(withSpan);
+
+ Badge multiComponent = new Badge();
+ multiComponent.add(new Span("Count: "), new Span("42"));
+ multiComponent.addThemeVariants(BadgeVariant.LUMO_PRIMARY);
+ componentSection.add(multiComponent);
+
+ add(componentSection);
+
+ add(new Hr());
+
+ // Accessibility examples
+ add(new H3("Accessibility Features"));
+ Div accessibilitySection = createSection();
+
+ Badge withAriaLabel = new Badge("!");
+ withAriaLabel.setAriaLabel("Important notification");
+ withAriaLabel.addThemeVariants(BadgeVariant.LUMO_ERROR,
+ BadgeVariant.LUMO_PILL);
+ withAriaLabel.setId("badge-aria-label");
+ accessibilitySection.add(withAriaLabel);
+
+ Badge withTooltip = new Badge("?");
+ withTooltip.setTooltipText("This is a help badge");
+ withTooltip.addThemeVariants(BadgeVariant.LUMO_CONTRAST,
+ BadgeVariant.LUMO_PILL);
+ withTooltip.setId("badge-tooltip");
+ accessibilitySection.add(withTooltip);
+
+ add(accessibilitySection);
+
+ add(new Hr());
+
+ // Real-world use cases
+ add(new H3("Real-world Use Cases"));
+ Div useCasesSection = createSection();
+
+ // Notification badge
+ Span bellIcon = new Span("🔔");
+ Badge notificationBadge = new Badge("5");
+ notificationBadge.addThemeVariants(BadgeVariant.LUMO_ERROR,
+ BadgeVariant.LUMO_SMALL, BadgeVariant.LUMO_PILL);
+ Div notificationExample = new Div(bellIcon, notificationBadge);
+ notificationExample.getStyle().set("display", "inline-flex")
+ .set("align-items", "center").set("gap", "5px");
+ useCasesSection.add(notificationExample);
+
+ // Status badge
+ Badge statusBadge = new Badge("Online");
+ statusBadge.addThemeVariants(BadgeVariant.LUMO_SUCCESS,
+ BadgeVariant.LUMO_SMALL);
+ useCasesSection.add(statusBadge);
+
+ // Count badge
+ Badge countBadge = new Badge("NEW");
+ countBadge.addThemeVariants(BadgeVariant.LUMO_PRIMARY);
+ useCasesSection.add(countBadge);
+
+ add(useCasesSection);
+
+ add(new Hr());
+
+ // Empty badge (for dynamic content)
+ add(new H3("Empty Badge (Dynamic Content)"));
+ Badge emptyBadge = new Badge();
+ emptyBadge.setId("empty-badge");
+ add(emptyBadge);
+ }
+
+ private Div createSection() {
+ Div section = new Div();
+ section.getStyle().set("display", "flex").set("gap", "10px")
+ .set("flex-wrap", "wrap").set("align-items", "center")
+ .set("padding", "10px 0");
+ return section;
+ }
+}
diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/test/java/com/vaadin/flow/component/badge/tests/BadgeIT.java b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/test/java/com/vaadin/flow/component/badge/tests/BadgeIT.java
new file mode 100644
index 00000000000..fc065112ff3
--- /dev/null
+++ b/vaadin-badge-flow-parent/vaadin-badge-flow-integration-tests/src/test/java/com/vaadin/flow/component/badge/tests/BadgeIT.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.badge.tests;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.vaadin.flow.component.badge.testbench.BadgeElement;
+import com.vaadin.flow.testutil.TestPath;
+import com.vaadin.tests.AbstractComponentIT;
+
+@TestPath("vaadin-badge")
+public class BadgeIT extends AbstractComponentIT {
+
+ private BadgeElement badge;
+
+ @Before
+ public void init() {
+ open();
+ badge = $(BadgeElement.class).id("badge-primary");
+ }
+
+ @Test
+ public void rendersBadgeComponent() {
+ boolean hasShadowRoot = (Boolean) executeScript(
+ "return arguments[0].shadowRoot !== null", badge);
+ String componentName = (String) executeScript(
+ "return arguments[0].tagName",
+ badge);
+
+ Assert.assertFalse(hasShadowRoot); // there should be no
+ Assert.assertEquals("VAADIN-BADGE", componentName);
+ Assert.assertNotNull(badge.getText());
+ Assert.assertFalse(badge.getText().isEmpty());
+ }
+
+ @Test
+ public void badgeHasThemeVariants() {
+ BadgeElement successBadge = $(BadgeElement.class)
+ .id("badge-success");
+ String theme = successBadge.getAttribute("theme");
+ Assert.assertTrue(theme.contains("success"));
+ }
+
+ @Test
+ public void badgeCanBeSmall() {
+ BadgeElement smallBadge = $(BadgeElement.class).id("badge-small");
+ String theme = smallBadge.getAttribute("theme");
+ Assert.assertTrue(theme.contains("small"));
+ }
+
+ @Test
+ public void badgeCanBePill() {
+ BadgeElement pillBadge = $(BadgeElement.class).id("badge-pill");
+ String theme = pillBadge.getAttribute("theme");
+ Assert.assertTrue(theme.contains("pill"));
+ }
+}
diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow/pom.xml b/vaadin-badge-flow-parent/vaadin-badge-flow/pom.xml
new file mode 100644
index 00000000000..880d811f2ed
--- /dev/null
+++ b/vaadin-badge-flow-parent/vaadin-badge-flow/pom.xml
@@ -0,0 +1,103 @@
+
+
+ 4.0.0
+
+ com.vaadin
+ vaadin-badge-flow-parent
+ 25.0-SNAPSHOT
+
+ vaadin-badge-flow
+ jar
+ Vaadin Badge
+ Vaadin Badge
+
+
+ com.vaadin
+ flow-html-components
+ provided
+
+
+ com.vaadin
+ flow-test-generic
+ test
+
+
+ com.vaadin
+ flow-test-util
+ test
+
+
+ com.vaadin
+ vaadin-flow-components-base
+ ${project.version}
+
+
+ com.vaadin
+ vaadin-flow-components-test-util
+ ${project.version}
+ test
+
+
+ jakarta.platform
+ jakarta.jakartaee-web-api
+ test
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.slf4j
+ slf4j-simple
+ test
+
+
+
+
+
+ biz.aQute.bnd
+ bnd-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+
+
+
+
+
+
+
+ attach-docs
+
+
+ with-docs
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+
+
+
+
diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/Badge.java b/vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/Badge.java
new file mode 100644
index 00000000000..80b594e8870
--- /dev/null
+++ b/vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/Badge.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.badge;
+
+import com.vaadin.flow.component.Component;
+import com.vaadin.flow.component.HasAriaLabel;
+import com.vaadin.flow.component.HasComponents;
+import com.vaadin.flow.component.HasSize;
+import com.vaadin.flow.component.HasStyle;
+import com.vaadin.flow.component.HasText;
+import com.vaadin.flow.component.Tag;
+import com.vaadin.flow.component.shared.HasThemeVariant;
+import com.vaadin.flow.component.shared.HasTooltip;
+
+/**
+ * Badge is a colored text element for labeling content, displaying metadata,
+ * or highlighting information. Note, currently only Lumo theme supports badges.
+ *
+ * Badges are typically used to show statuses, categories, or counts. They
+ * support text content, icons, and various theme variants for different colors
+ * and sizes.
+ *
+ *
+ * Usage Examples
+ *
+ *
+ * {@code
+ * // Simple badge with text
+ * Badge badge = new Badge("New");
+ *
+ * // Badge with theme variants
+ * Badge successBadge = new Badge("Success");
+ * successBadge.addThemeVariants(BadgeVariant.LUMO_SUCCESS);
+ *
+ * // Small pill-shaped badge
+ * Badge pillBadge = new Badge("3");
+ * pillBadge.addThemeVariants(BadgeVariant.LUMO_SMALL, BadgeVariant.LUMO_PILL);
+ *
+ * // Badge with icon
+ * Badge iconBadge = new Badge();
+ * iconBadge.add(new Icon(VaadinIcon.CHECK), new Span("Verified"));
+ * }
+ *
+ *
+ * Theme Variants
+ *
+ * The component supports several theme variants for styling:
+ *
+ *
+ * - {@link BadgeVariant#LUMO_PRIMARY} - Primary color
+ * - {@link BadgeVariant#LUMO_SUCCESS} - Success/positive color
+ * - {@link BadgeVariant#LUMO_WARNING} - Warning color
+ * - {@link BadgeVariant#LUMO_ERROR} - Error/danger color
+ * - {@link BadgeVariant#LUMO_CONTRAST} - High contrast color
+ * - {@link BadgeVariant#LUMO_SMALL} - Smaller size
+ * - {@link BadgeVariant#LUMO_PILL} - Pill shape with rounded corners
+ *
+ *
+ * Accessibility
+ *
+ * When using icon-only badges or when the badge's visual appearance alone
+ * doesn't convey the full meaning, use {@link #setAriaLabel(String)} to
+ * provide a text alternative for screen readers. Additionally, consider using
+ * {@link #setTooltipText(String)} to provide helpful information to all users.
+ *
+ *
+ * @author Vaadin Ltd
+ */
+@Tag("vaadin-badge")
+public class Badge extends Component implements HasText, HasSize, HasStyle,
+ HasComponents, HasThemeVariant, HasTooltip, HasAriaLabel {
+
+ /**
+ * Creates an empty badge.
+ *
+ * Use {@link #setText(String)} to set the badge text or {@link #add(Component...)}
+ * to add icons or other components.
+ *
+ */
+ public Badge() {
+ getElement().getThemeList().add("badge");
+ }
+
+ /**
+ * Creates a badge with the specified text.
+ *
+ * @param text
+ * the text content of the badge
+ */
+ public Badge(String text) {
+ this();
+ setText(text);
+ }
+}
diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/BadgeVariant.java b/vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/BadgeVariant.java
new file mode 100644
index 00000000000..c8f3c0f6d62
--- /dev/null
+++ b/vaadin-badge-flow-parent/vaadin-badge-flow/src/main/java/com/vaadin/flow/component/badge/BadgeVariant.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.badge;
+
+import com.vaadin.flow.component.shared.ThemeVariant;
+
+/**
+ * Set of theme variants applicable for {@code vaadin-badge} component.
+ */
+public enum BadgeVariant implements ThemeVariant {
+ //@formatter:off
+ LUMO_PRIMARY("primary"),
+ LUMO_SUCCESS("success"),
+ LUMO_WARNING("warning"),
+ LUMO_ERROR("error"),
+ LUMO_CONTRAST("contrast"),
+ LUMO_SMALL("small"),
+ LUMO_PILL("pill"),
+ AURA_PRIMARY("primary"),
+ AURA_SUCCESS("success"),
+ AURA_WARNING("warning"),
+ AURA_ERROR("error"),
+ AURA_CONTRAST("contrast"),
+ AURA_SMALL("small"),
+ AURA_PILL("pill");
+ //@formatter:on
+
+ private final String variant;
+
+ BadgeVariant(String variant) {
+ this.variant = variant;
+ }
+
+ /**
+ * Gets the variant name.
+ *
+ * @return variant name
+ */
+ public String getVariantName() {
+ return variant;
+ }
+}
diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeSerializableTest.java b/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeSerializableTest.java
new file mode 100644
index 00000000000..cccad5f0700
--- /dev/null
+++ b/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeSerializableTest.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.badge;
+
+import com.vaadin.flow.testutil.ClassesSerializableTest;
+
+public class BadgeSerializableTest extends ClassesSerializableTest {
+}
diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeTest.java b/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeTest.java
new file mode 100644
index 00000000000..1efcc7ed1a7
--- /dev/null
+++ b/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.badge;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.vaadin.flow.component.UI;
+import com.vaadin.flow.component.html.Div;
+import com.vaadin.flow.component.html.Span;
+
+/**
+ * Unit tests for the {@link Badge} component.
+ */
+public class BadgeTest {
+
+ private Badge badge;
+
+ @Before
+ public void setup() {
+ var ui = new UI();
+ UI.setCurrent(ui);
+ badge = new Badge();
+ ui.add(badge);
+ }
+
+ @After
+ public void tearDown() {
+ UI.setCurrent(null);
+ }
+
+ @Test
+ public void defaultConstructor_textIsEmpty() {
+ Assert.assertEquals("", badge.getText());
+ }
+
+ @Test
+ public void constructorWithText_textIsSet() {
+ var badgeWithText = new Badge("New");
+ Assert.assertEquals("New", badgeWithText.getText());
+ }
+
+ @Test
+ public void setText_textIsUpdated() {
+ badge.setText("Updated");
+ Assert.assertEquals("Updated", badge.getText());
+ }
+
+ @Test
+ public void setTextNull_textIsEmpty() {
+ badge.setText("Text");
+ badge.setText(null);
+ Assert.assertEquals("", badge.getText());
+ }
+
+ @Test
+ public void addComponent_componentIsAdded() {
+ var span = new Span("Content");
+ badge.add(span);
+ Assert.assertTrue(badge.getChildren().anyMatch(c -> c.equals(span)));
+ Assert.assertTrue(span.isAttached());
+ }
+
+ @Test
+ public void addMultipleComponents_allComponentsAreAdded() {
+ var span1 = new Span("First");
+ var span2 = new Span("Second");
+ badge.add(span1, span2);
+ Assert.assertEquals(2, badge.getChildren().count());
+ Assert.assertTrue(badge.getChildren().anyMatch(c -> c.equals(span1)));
+ Assert.assertTrue(badge.getChildren().anyMatch(c -> c.equals(span2)));
+ }
+
+ @Test
+ public void removeComponent_componentIsRemoved() {
+ var span = new Span("Content");
+ badge.add(span);
+ badge.remove(span);
+ Assert.assertFalse(badge.getChildren().anyMatch(c -> c.equals(span)));
+ Assert.assertFalse(span.isAttached());
+ }
+
+ @Test
+ public void removeAll_allComponentsAreRemoved() {
+ badge.add(new Span("First"), new Span("Second"));
+ badge.removeAll();
+ Assert.assertEquals(0, badge.getChildren().count());
+ }
+
+ @Test
+ public void addThemeVariant_variantIsAdded() {
+ badge.addThemeVariants(BadgeVariant.LUMO_SUCCESS);
+ Assert.assertTrue(badge.getThemeNames().contains("success"));
+ }
+
+ @Test
+ public void addMultipleThemeVariants_allVariantsAreAdded() {
+ badge.addThemeVariants(BadgeVariant.LUMO_SUCCESS,
+ BadgeVariant.LUMO_SMALL);
+ Assert.assertTrue(badge.getThemeNames().contains("success"));
+ Assert.assertTrue(badge.getThemeNames().contains("small"));
+ }
+
+ @Test
+ public void removeThemeVariant_variantIsRemoved() {
+ badge.addThemeVariants(BadgeVariant.LUMO_SUCCESS);
+ badge.removeThemeVariants(BadgeVariant.LUMO_SUCCESS);
+ Assert.assertFalse(badge.getThemeNames().contains("success"));
+ }
+
+ @Test
+ public void setAriaLabel_ariaLabelIsSet() {
+ badge.setAriaLabel("Status badge");
+ Assert.assertEquals("Status badge", badge.getAriaLabel().orElse(null));
+ }
+
+ @Test
+ public void setAriaLabelNull_ariaLabelIsRemoved() {
+ badge.setAriaLabel("Status badge");
+ badge.setAriaLabel(null);
+ Assert.assertTrue(badge.getAriaLabel().isEmpty());
+ }
+
+ @Test
+ public void setTooltipText_tooltipIsSet() {
+ badge.setTooltipText("This is a badge");
+ // Tooltip is set, we can't easily test the content but we can verify no exception
+ Assert.assertNotNull(badge);
+ }
+
+ @Test
+ public void setWidth_widthIsSet() {
+ badge.setWidth("100px");
+ Assert.assertEquals("100px", badge.getWidth());
+ }
+
+ @Test
+ public void setHeight_heightIsSet() {
+ badge.setHeight("50px");
+ Assert.assertEquals("50px", badge.getHeight());
+ }
+
+ @Test
+ public void addClassName_classNameIsAdded() {
+ badge.addClassName("custom-badge");
+ Assert.assertTrue(badge.getClassNames().contains("custom-badge"));
+ }
+}
diff --git a/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeVariantTest.java b/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeVariantTest.java
new file mode 100644
index 00000000000..c1b1e825d5e
--- /dev/null
+++ b/vaadin-badge-flow-parent/vaadin-badge-flow/src/test/java/com/vaadin/flow/component/badge/BadgeVariantTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.badge;
+
+import org.junit.Test;
+
+import com.vaadin.tests.ThemeVariantTestHelper;
+
+public class BadgeVariantTest {
+
+ @Test
+ public void addThemeVariant_themeNamesContainsThemeVariant() {
+ ThemeVariantTestHelper.addThemeVariant_themeNamesContainsThemeVariant(
+ new Badge(), BadgeVariant.LUMO_SUCCESS);
+ }
+
+ @Test
+ public void addThemeVariant_removeThemeVariant_themeNamesDoesNotContainThemeVariant() {
+ ThemeVariantTestHelper
+ .addThemeVariant_removeThemeVariant_themeNamesDoesNotContainThemeVariant(
+ new Badge(), BadgeVariant.LUMO_SUCCESS);
+ }
+}
diff --git a/vaadin-badge-flow-parent/vaadin-badge-testbench/pom.xml b/vaadin-badge-flow-parent/vaadin-badge-testbench/pom.xml
new file mode 100644
index 00000000000..d13978060fd
--- /dev/null
+++ b/vaadin-badge-flow-parent/vaadin-badge-testbench/pom.xml
@@ -0,0 +1,45 @@
+
+
+ 4.0.0
+
+ com.vaadin
+ vaadin-badge-flow-parent
+ 25.0-SNAPSHOT
+
+ vaadin-badge-testbench
+ jar
+ Vaadin Badge Testbench API
+ Vaadin Badge Testbench API
+
+
+ com.vaadin
+ vaadin-testbench-shared
+ provided
+
+
+
+
+
+
+
+ attach-docs
+
+
+ with-docs
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+
+
+
+
diff --git a/vaadin-badge-flow-parent/vaadin-badge-testbench/src/main/java/com/vaadin/flow/component/badge/testbench/BadgeElement.java b/vaadin-badge-flow-parent/vaadin-badge-testbench/src/main/java/com/vaadin/flow/component/badge/testbench/BadgeElement.java
new file mode 100644
index 00000000000..46216d4f8f4
--- /dev/null
+++ b/vaadin-badge-flow-parent/vaadin-badge-testbench/src/main/java/com/vaadin/flow/component/badge/testbench/BadgeElement.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2000-2025 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.flow.component.badge.testbench;
+
+import com.vaadin.testbench.TestBenchElement;
+import com.vaadin.testbench.elementsbase.Element;
+
+/**
+ * A TestBench element representing a <vaadin-badge>
+ * element.
+ */
+@Element("vaadin-badge")
+public class BadgeElement extends TestBenchElement {
+
+ /**
+ * Gets the text content of the badge.
+ *
+ * @return the badge text
+ */
+ public String getText() {
+ return getPropertyString("textContent");
+ }
+}