;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const DotBadge: Story = {
+ name: 'Dot Badge',
+ args: {
+ srText: 'Has Notifications'
+ },
+ argTypes: {
+ number: {
+ control: { type: null }
+ },
+ },
+ render: (args) => html`
+
+ `,
+}
+
+export const NumberedBadge: Story = {
+ name: 'Numbered Badge',
+ args: {
+ number: 5,
+ srText: 'updates'
+ },
+ render: (args) => html`
+
+ `,
+}
+
diff --git a/packages/craftcms-cp/src/components/badge-indicator/badge-indicator.styles.ts b/packages/craftcms-cp/src/components/badge-indicator/badge-indicator.styles.ts
new file mode 100644
index 00000000000..7fbbd2ede58
--- /dev/null
+++ b/packages/craftcms-cp/src/components/badge-indicator/badge-indicator.styles.ts
@@ -0,0 +1,26 @@
+import {css} from 'lit';
+
+export default css`
+ .badge-indicator {
+ display: inline-flex;
+ width: var(--c-size-icon-xs);
+ height: var(--c-size-icon-xs);
+ justify-content: center;
+ align-items: center;
+ background-color: var(--c-color-accent-bg-emphasis);
+ color: white;
+ border-radius: var(--c-radius-full);
+ }
+
+ .badge-indicator--with-number {
+ padding: 8px;
+
+ }
+
+ .number {
+ font-size: var(--c-text-xs);
+ font-weight: 600;
+ line-height: 1;
+ display: inline-block;
+ }
+`;
diff --git a/packages/craftcms-cp/src/components/badge-indicator/badge-indicator.ts b/packages/craftcms-cp/src/components/badge-indicator/badge-indicator.ts
new file mode 100644
index 00000000000..4023c56d0a0
--- /dev/null
+++ b/packages/craftcms-cp/src/components/badge-indicator/badge-indicator.ts
@@ -0,0 +1,73 @@
+import {html, css, LitElement, nothing} from 'lit';
+import {property} from 'lit/decorators.js';
+import styles from './badge-indicator.styles.js';
+import {classMap} from 'lit/directives/class-map.js';
+import '@shoelace-style/shoelace/dist/components/visually-hidden/visually-hidden.js';
+
+/**
+ * @summary A badge indicator component. Used in various places to indicate that
+ * something is new or has been updated. The indicator can have an optional
+ * notification count.
+ */
+
+export default class CraftBadgeIndicator extends LitElement {
+ static override styles = [styles];
+
+ /** Number of notifications */
+ @property() number: number | null = null;
+
+ /** Accessible text for screen reader users */
+ @property() srText: string | null = null;
+
+ @property()
+ override id: string;
+
+ constructor() {
+ super();
+ this.id = this.id || Math.floor(Math.random() * 1000000000).toString();
+ }
+
+ private truncatedNumber() {
+ if (!this.number) {
+ return null;
+ }
+
+ if (this.number > 99) {
+ return '99+';
+ } else {
+ return this.number.toString();
+ }
+ }
+ override render() {
+ const hasNumber = this.number !== null;
+ const badgeId = this.id ?? nothing;
+ const labelId = badgeId ? `${badgeId}-label` : nothing;
+
+ return html`
+
+ ${hasNumber
+ ? html`${this.truncatedNumber()}`
+ : nothing}
+ ${this.srText}
+
+ `;
+ }
+}
+
+if (!customElements.get('craft-badge-indicator')) {
+ customElements.define('craft-badge-indicator', CraftBadgeIndicator);
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'craft-badge-indicator': CraftBadgeIndicator;
+ }
+}
diff --git a/packages/craftcms-cp/src/components/indicator/indicator.ts b/packages/craftcms-cp/src/components/indicator/indicator.ts
index 99f9e240c16..8d37645216f 100644
--- a/packages/craftcms-cp/src/components/indicator/indicator.ts
+++ b/packages/craftcms-cp/src/components/indicator/indicator.ts
@@ -36,7 +36,9 @@ export default class CraftIndicator extends LitElement {
'indicator--warning': this.variant === Variant.Warning,
'indicator--info': this.variant === Variant.Info,
})}"
- >`;
+ >
+
+ `;
}
}
diff --git a/packages/craftcms-cp/src/components/nav-item/nav-item.stories.ts b/packages/craftcms-cp/src/components/nav-item/nav-item.stories.ts
index 52592b95062..d02801a7724 100644
--- a/packages/craftcms-cp/src/components/nav-item/nav-item.stories.ts
+++ b/packages/craftcms-cp/src/components/nav-item/nav-item.stories.ts
@@ -3,7 +3,7 @@ import type {Meta, StoryObj} from '@storybook/web-components-vite';
import {html} from 'lit';
import '../icon/icon.js';
-import '../navigation/navigation.js';
+import '../nav-list/nav-list.js';
import '../button/button.js';
import './nav-item.js';
diff --git a/packages/craftcms-cp/src/styles/shared/tokens.css b/packages/craftcms-cp/src/styles/shared/tokens.css
index fa624db52f7..d34cbd69a30 100644
--- a/packages/craftcms-cp/src/styles/shared/tokens.css
+++ b/packages/craftcms-cp/src/styles/shared/tokens.css
@@ -3,6 +3,7 @@
--c-text-lg: calc(16rem / 16);
--c-text-base: calc(14rem / 16);
--c-text-sm: calc(11rem / 16);
+ --c-text-xs: calc(9rem / 16);
--c-leading-normal: 1.42;