diff --git a/packages/cxl-lumo-styles/scss/global.scss b/packages/cxl-lumo-styles/scss/global.scss
index c8f8c0db3..a80427924 100644
--- a/packages/cxl-lumo-styles/scss/global.scss
+++ b/packages/cxl-lumo-styles/scss/global.scss
@@ -6,6 +6,8 @@ html {
--cxl-content-width: 36em;
--cxl-wrap-width: 72rem;
--cxl-wrap-padding: var(--lumo-space-m);
+ --cxl-color-light-gray: hsla(0, 0%, 96%, 1);
+ --cxl-color-black-50pct: hsla(0, 0%, 0%, 0.5);
/**
* Lumo Icons have a documented 4px "safe area" around them. Vaadin Icons don't, for unknown reasons.
diff --git a/packages/cxl-ui/scss/cxl-stats.scss b/packages/cxl-ui/scss/cxl-stats.scss
new file mode 100644
index 000000000..98e7ae826
--- /dev/null
+++ b/packages/cxl-ui/scss/cxl-stats.scss
@@ -0,0 +1,17 @@
+@use "~@conversionxl/cxl-lumo-styles/scss/mq";
+
+:host {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: var(--lumo-space-m);
+ padding: var(--lumo-space-xl) var(--lumo-space-m) var(--lumo-space-m);
+ border-bottom: 1px solid var(--cxl-color-light-gray);
+
+ @media #{mq.$small} {
+ padding: var(--lumo-space-xl) var(--lumo-space-l) var(--lumo-space-l);
+ }
+
+ @media #{mq.$medium} {
+ grid-template-columns: repeat(4, 1fr);
+ }
+}
diff --git a/packages/cxl-ui/scss/global/cxl-stats.scss b/packages/cxl-ui/scss/global/cxl-stats.scss
new file mode 100644
index 000000000..7baa05f50
--- /dev/null
+++ b/packages/cxl-ui/scss/global/cxl-stats.scss
@@ -0,0 +1,16 @@
+cxl-stats {
+ .stats-title {
+ margin-top: 0;
+ font-size: var(--lumo-font-size-xs);
+ font-weight: 400;
+ line-height: 1;
+ color: var(--cxl-color-black-50pct);
+ text-transform: uppercase;
+ }
+
+ .stats-count {
+ margin: 0;
+ font-size: var(--lumo-font-size-xxl);
+ font-weight: 700;
+ }
+}
diff --git a/packages/cxl-ui/src/components/cxl-stats.js b/packages/cxl-ui/src/components/cxl-stats.js
new file mode 100644
index 000000000..324bfaef6
--- /dev/null
+++ b/packages/cxl-ui/src/components/cxl-stats.js
@@ -0,0 +1,28 @@
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { LitElement, html } from 'lit';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { customElement } from 'lit/decorators.js';
+import { registerGlobalStyles } from '@conversionxl/cxl-lumo-styles/src/utils';
+import '@conversionxl/cxl-lumo-styles';
+import cxlStatsStyles from '../styles/cxl-stats-css.js';
+import cxlStatsGlobalStyles from '../styles/global/cxl-stats-css.js';
+
+@customElement('cxl-stats')
+export class CXLStatsElement extends LitElement {
+ static get styles() {
+ return [cxlStatsStyles];
+ }
+
+ render() {
+ return html``;
+ }
+
+ firstUpdated(_changedProperties) {
+ super.firstUpdated(_changedProperties);
+
+ // Global styles.
+ registerGlobalStyles(cxlStatsGlobalStyles, {
+ moduleId: 'cxl-stats',
+ });
+ }
+}
diff --git a/packages/cxl-ui/src/index-core.js b/packages/cxl-ui/src/index-core.js
index 6ce2a0833..25a1ec1ab 100644
--- a/packages/cxl-ui/src/index-core.js
+++ b/packages/cxl-ui/src/index-core.js
@@ -17,6 +17,7 @@ export { CXLCardElement } from './components/cxl-card.js';
export { CXLCheckoutDetailsElement } from './components/cxl-checkout-details.js';
export { CXLMarketingNavElement } from './components/cxl-marketing-nav.js';
export { CXLSectionElement } from './components/cxl-section.js';
+export { CXLStatsElement } from './components/cxl-stats.js';
export { CXLTabsSliderElement } from './components/cxl-tabs-slider.js';
export { CXLNotification } from './components/cxl-notification.js';
diff --git a/packages/cxl-ui/src/index-storybook.js b/packages/cxl-ui/src/index-storybook.js
index 58042c9c5..2d17ec740 100644
--- a/packages/cxl-ui/src/index-storybook.js
+++ b/packages/cxl-ui/src/index-storybook.js
@@ -6,6 +6,7 @@ export { CXLAppLayoutElement } from './components/cxl-app-layout.js';
export { CXLCardElement } from './components/cxl-card.js';
export { CXLMarketingNavElement } from './components/cxl-marketing-nav.js';
export { CXLSectionElement } from './components/cxl-section.js';
+export { CXLStatsElement } from './components/cxl-stats.js';
export { CXLTabsSliderElement } from './components/cxl-tabs-slider.js';
// Order matters.
diff --git a/packages/storybook/cxl-ui/cxl-stats/cxl-stats.data.json b/packages/storybook/cxl-ui/cxl-stats/cxl-stats.data.json
new file mode 100644
index 000000000..54eaef5c9
--- /dev/null
+++ b/packages/storybook/cxl-ui/cxl-stats/cxl-stats.data.json
@@ -0,0 +1,18 @@
+[
+ {
+ "title": "Categories
completed",
+ "count": "1"
+ },
+ {
+ "title": "Courses
completed",
+ "count": "30"
+ },
+ {
+ "title": "Lessons
completed",
+ "count": "95"
+ },
+ {
+ "title": "Complete
library",
+ "count": "12%"
+ }
+]
diff --git a/packages/storybook/cxl-ui/cxl-stats/default.stories.js b/packages/storybook/cxl-ui/cxl-stats/default.stories.js
new file mode 100644
index 000000000..06f94aba2
--- /dev/null
+++ b/packages/storybook/cxl-ui/cxl-stats/default.stories.js
@@ -0,0 +1,40 @@
+import { html } from 'lit';
+import { unsafeHTML } from 'lit/directives/unsafe-html.js';
+import '@conversionxl/cxl-lumo-styles';
+import '@conversionxl/cxl-ui/src/components/cxl-stats.js';
+import statsData from './cxl-stats.data.json';
+
+export default {
+ title: 'CXL UI/cxl-stats',
+};
+
+export const CXLStats = ({ statsCount }) => {
+ for (let i = 0; i < statsCount; i += 1) {
+ const newItem = {
+ title: 'Complete
library',
+ count: '12%',
+ };
+
+ statsData.push(newItem);
+ }
+
+ return html`
+
+ ${statsData.slice(0, statsCount).map(
+ (el) => html`
+
+
${unsafeHTML(el.title)}
+
${el.count}
+
+ `
+ )}
+
+ `;
+};
+
+Object.assign(CXLStats, {
+ args: {
+ statsCount: 4,
+ },
+ storyName: 'CXL Stats',
+});