diff --git a/playground/app.vue b/playground/app.vue
index ae3add34..aedbd599 100644
--- a/playground/app.vue
+++ b/playground/app.vue
@@ -29,6 +29,7 @@ const components = [
'link',
'modal',
'navigation-menu',
+ 'pagination',
'popover',
'radio-group',
'separator',
diff --git a/playground/pages/pagination.vue b/playground/pages/pagination.vue
new file mode 100644
index 00000000..940d9f2f
--- /dev/null
+++ b/playground/pages/pagination.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/src/runtime/components/Pagination.vue b/src/runtime/components/Pagination.vue
new file mode 100644
index 00000000..4f332178
--- /dev/null
+++ b/src/runtime/components/Pagination.vue
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/runtime/types/index.d.ts b/src/runtime/types/index.d.ts
index 74487d08..a96aaef7 100644
--- a/src/runtime/types/index.d.ts
+++ b/src/runtime/types/index.d.ts
@@ -20,6 +20,7 @@ export * from '../components/Kbd.vue'
export * from '../components/Link.vue'
export * from '../components/Modal.vue'
export * from '../components/NavigationMenu.vue'
+export * from '../components/Pagination.vue'
export * from '../components/Popover.vue'
export * from '../components/RadioGroup.vue'
export * from '../components/Separator.vue'
diff --git a/src/theme/icons.ts b/src/theme/icons.ts
index 6e9290f8..444d088a 100644
--- a/src/theme/icons.ts
+++ b/src/theme/icons.ts
@@ -2,8 +2,11 @@ export default {
chevronDown: 'i-heroicons-chevron-down-20-solid',
chevronLeft: 'i-heroicons-chevron-left-20-solid',
chevronRight: 'i-heroicons-chevron-right-20-solid',
+ chevronDoubleLeft: 'i-heroicons-chevron-double-left-20-solid',
+ chevronDoubleRight: 'i-heroicons-chevron-double-right-20-solid',
check: 'i-heroicons-check-20-solid',
close: 'i-heroicons-x-mark-20-solid',
+ ellipsis: 'i-heroicons-ellipsis-horizontal-20-solid',
empty: 'i-heroicons-circle-stack-20-solid',
loading: 'i-heroicons-arrow-path-20-solid',
minus: 'i-heroicons-minus-20-solid',
diff --git a/src/theme/index.ts b/src/theme/index.ts
index 0b6bc07f..fe744570 100644
--- a/src/theme/index.ts
+++ b/src/theme/index.ts
@@ -18,6 +18,7 @@ export { default as kbd } from './kbd'
export { default as link } from './link'
export { default as modal } from './modal'
export { default as navigationMenu } from './navigation-menu'
+export { default as pagination } from './pagination'
export { default as popover } from './popover'
export { default as radioGroup } from './radio-group'
export { default as separator } from './separator'
diff --git a/src/theme/pagination.ts b/src/theme/pagination.ts
new file mode 100644
index 00000000..28082b03
--- /dev/null
+++ b/src/theme/pagination.ts
@@ -0,0 +1,8 @@
+export default {
+ slots: {
+ root: '',
+ list: 'flex items-center gap-1',
+ ellipsis: 'pointer-events-none',
+ label: 'min-w-5'
+ }
+}
diff --git a/test/components/Pagination.spec.ts b/test/components/Pagination.spec.ts
new file mode 100644
index 00000000..d115f5c5
--- /dev/null
+++ b/test/components/Pagination.spec.ts
@@ -0,0 +1,47 @@
+import { describe, it, expect } from 'vitest'
+import Pagination, { type PaginationProps } from '../../src/runtime/components/Pagination.vue'
+import ComponentRender from '../component-render'
+import theme from '#build/ui/button'
+
+describe('Pagination', () => {
+ const sizes = Object.keys(theme.variants.size) as any
+ const colors = Object.keys(theme.variants.color) as any
+ const variants = Object.keys(theme.variants.variant) as any
+
+ const props = { total: 100 }
+
+ it.each([
+ // Props
+ ['with total', { props }],
+ ['with as', { props: { ...props, as: 'div' } }],
+ ['with defaultPage', { props: { ...props, defaultPage: 2 } }],
+ ['with disabled', { props: { ...props, disabled: true } }],
+ ['with itemsPerPage', { props: { ...props, itemsPerPage: 5 } }],
+ ['with page', { props: { ...props, page: 2 } }],
+ ['with showEdges', { props: { ...props, showEdges: true } }],
+ ['with siblingCount', { props: { ...props, siblingCount: 1, showEdges: true, page: 5 } }],
+ ['without showControls', { props: { ...props, showControls: false } }],
+ ['with firstIcon', { props: { ...props, firstIcon: 'i-heroicons-arrow-left' } }],
+ ['with prevIcon', { props: { ...props, prevIcon: 'i-heroicons-arrow-small-left' } }],
+ ['with nextIcon', { props: { ...props, nextIcon: 'i-heroicons-arrow-small-right' } }],
+ ['with lastIcon', { props: { ...props, lastIcon: 'i-heroicons-arrow-right' } }],
+ ['with ellipsisIcon', { props: { ...props, ellipsisIcon: 'i-heroicons-ellipsis-vertical', siblingCount: 1, showEdges: true, page: 5 } }],
+ ...sizes.map((size: string) => [`with size ${size}`, { props: { ...props, size } }]),
+ ...colors.map((color: string) => [`with color ${color}`, { props: { ...props, color } }]),
+ ...variants.map((variant: string) => [`with variant ${variant}`, { props: { ...props, color: 'primary', variant } }]),
+ ...colors.map((activeColor: string) => [`with active color ${activeColor}`, { props: { ...props, activeColor } }]),
+ ...variants.map((activeVariant: string) => [`with active variant ${activeVariant}`, { props: { ...props, color: 'primary', activeVariant } }]),
+ ['with class', { props: { ...props, class: 'relative' } }],
+ ['with ui', { props: { ...props, ui: { list: 'gap-3' } } }],
+ // Slots
+ ['with first slot', { props, slots: { first: () => 'First slot' } }],
+ ['with prev slot', { props, slots: { prev: () => 'Prev slot' } }],
+ ['with next slot', { props, slots: { next: () => 'Next slot' } }],
+ ['with last slot', { props, slots: { last: () => 'Last slot' } }],
+ ['with ellipsis slot', { props: { ...props, siblingCount: 1, showEdges: true, page: 5 }, slots: { ellipsis: () => 'Ellipsis slot' } }],
+ ['with item slot', { props, slots: { item: () => 'Item slot' } }]
+ ])('renders %s correctly', async (nameOrHtml: string, options: { props?: PaginationProps, slots?: any }) => {
+ const html = await ComponentRender(nameOrHtml, options, Pagination)
+ expect(html).toMatchSnapshot()
+ })
+})
diff --git a/test/components/__snapshots__/Pagination.spec.ts.snap b/test/components/__snapshots__/Pagination.spec.ts.snap
new file mode 100644
index 00000000..a11ebfe7
--- /dev/null
+++ b/test/components/__snapshots__/Pagination.spec.ts.snap
@@ -0,0 +1,2076 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`Pagination > renders with active color black correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with active color gray correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with active color green correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with active color primary correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with active color red correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with active color white correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with active variant ghost correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with active variant link correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with active variant outline correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with active variant soft correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with active variant solid correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with active variant subtle correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with as correctly 1`] = `
+"
+
+
"
+`;
+
+exports[`Pagination > renders with class correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with color black correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with color gray correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with color green correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with color primary correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with color red correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with color white correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with defaultPage correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with disabled correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with ellipsis slot correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with ellipsisIcon correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with first slot correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with firstIcon correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with item slot correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with itemsPerPage correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with last slot correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with lastIcon correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with next slot correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with nextIcon correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with page correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with prev slot correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with prevIcon correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with showEdges correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with siblingCount correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with size lg correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with size md correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with size sm correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with size xl correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with size xs correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with total correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with ui correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with variant ghost correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with variant link correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with variant outline correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with variant soft correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with variant solid correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders with variant subtle correctly 1`] = `
+""
+`;
+
+exports[`Pagination > renders without showControls correctly 1`] = `
+""
+`;