diff --git a/fluent/src/commonMain/kotlin/com/konyaco/fluent/background/Layer.kt b/fluent/src/commonMain/kotlin/com/konyaco/fluent/background/Layer.kt
index 99d036f9..afc9acfe 100644
--- a/fluent/src/commonMain/kotlin/com/konyaco/fluent/background/Layer.kt
+++ b/fluent/src/commonMain/kotlin/com/konyaco/fluent/background/Layer.kt
@@ -172,18 +172,26 @@ private fun Modifier.layer(
 @Immutable
 @JvmInline
 internal value class BackgroundPaddingShape(private val borderShape: CornerBasedShape) : Shape {
-
     override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline {
         return with(density) {
-            createInnerOutline(size, density, layoutDirection, borderShape.calculateBorderPadding(density))
+            LayerShapeHelper.createInnerOutline(borderShape, size, density, layoutDirection, borderShape.calculateBorderPadding(density))
         }
     }
+}
+
+internal object LayerShapeHelper {
 
     /**
      * Fork from [CornerBasedShape.createOutline], add padding to corner size and outline rect size.
      */
-    private fun createInnerOutline(size: Size, density: Density, layoutDirection: LayoutDirection, paddingPx: Float) =
-        borderShape.run {
+    fun createInnerOutline(
+        outsideShape: CornerBasedShape,
+        size: Size,
+        density: Density,
+        layoutDirection: LayoutDirection,
+        paddingPx: Float,
+    ): Outline {
+        return outsideShape.run {
             val cornerPaddingPx = if (this is CutCornerShape) {
                 /** padding for cut corner shape */
                 (paddingPx / sqrt(2f)).toInt().toFloat()
@@ -192,10 +200,10 @@ internal value class BackgroundPaddingShape(private val borderShape: CornerBased
             }
             val innerSize = Size(size.width - 2 * paddingPx, size.height - 2 * paddingPx)
             /** add padding to corner size */
-            var topStart = (borderShape.topStart.toPx(size, density) - cornerPaddingPx).coerceAtLeast(0f)
-            var topEnd = (borderShape.topEnd.toPx(size, density) - cornerPaddingPx).coerceAtLeast(0f)
-            var bottomEnd = (borderShape.bottomEnd.toPx(size, density) - cornerPaddingPx).coerceAtLeast(0f)
-            var bottomStart = (borderShape.bottomStart.toPx(size, density) - cornerPaddingPx).coerceAtLeast(0f)
+            var topStart = (outsideShape.topStart.toPx(size, density) - cornerPaddingPx).coerceAtLeast(0f)
+            var topEnd = (outsideShape.topEnd.toPx(size, density) - cornerPaddingPx).coerceAtLeast(0f)
+            var bottomEnd = (outsideShape.bottomEnd.toPx(size, density) - cornerPaddingPx).coerceAtLeast(0f)
+            var bottomStart = (outsideShape.bottomStart.toPx(size, density) - cornerPaddingPx).coerceAtLeast(0f)
             val minDimension = innerSize.minDimension
             if (topStart + bottomStart > minDimension) {
                 val scale = minDimension / (topStart + bottomStart)
@@ -227,6 +235,7 @@ internal value class BackgroundPaddingShape(private val borderShape: CornerBased
                 is Outline.Generic -> Outline.Generic(oldOutline.path.apply { translate(Offset(paddingPx, paddingPx)) })
             }
         }
+    }
 }
 
 /**
@@ -234,33 +243,33 @@ internal value class BackgroundPaddingShape(private val borderShape: CornerBased
  * when density is not integer or `(density % 1) < 0.5`
  */
 @Stable
-private fun calcPadding(density: Density): Dp {
+private fun calcPadding(density: Density, borderSize: Dp): Dp {
     val remainder = density.density % 1f
 
     return with(density) {
         when {
-            remainder == 0f -> 1.dp
-            else -> (1.dp.toPx() - remainder + 1).toDp()
+            remainder == 0f -> borderSize
+            else -> (borderSize.toPx() - remainder + 1).toDp()
         }
     }
 }
 
 @Stable
-private fun calcCircularPadding(density: Density): Dp {
+private fun calcCircularPadding(density: Density, borderSize: Dp): Dp {
     val remainder = density.density % 1f
 
     return with(density) {
-        if (remainder == 0f) 1.dp
-        else (1.dp.toPx() - remainder + 1).toDp()
+        if (remainder == 0f) borderSize
+        else (borderSize.toPx() - remainder + 1).toDp()
     }
 }
 
-internal fun Shape.calculateBorderPadding(density: Density): Float {
+internal fun Shape.calculateBorderPadding(density: Density, borderSize: Dp = 1.dp): Float {
     val circular = this == CircleShape
     return with(density) {
         when {
-            circular -> calcCircularPadding(density)
-            else -> calcPadding(density)
+            circular -> calcCircularPadding(density, borderSize)
+            else -> calcPadding(density, borderSize)
         }.toPx()
     }
 }
\ No newline at end of file
diff --git a/fluent/src/commonMain/kotlin/com/konyaco/fluent/component/FlipView.kt b/fluent/src/commonMain/kotlin/com/konyaco/fluent/component/FlipView.kt
new file mode 100644
index 00000000..c6d95302
--- /dev/null
+++ b/fluent/src/commonMain/kotlin/com/konyaco/fluent/component/FlipView.kt
@@ -0,0 +1,410 @@
+package com.konyaco.fluent.component
+
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.TargetedFlingBehavior
+import androidx.compose.foundation.gestures.snapping.SnapPosition
+import androidx.compose.foundation.hoverable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsHoveredAsState
+import androidx.compose.foundation.interaction.collectIsPressedAsState
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.PageSize
+import androidx.compose.foundation.pager.PagerDefaults
+import androidx.compose.foundation.pager.PagerScope
+import androidx.compose.foundation.pager.PagerState
+import androidx.compose.foundation.pager.VerticalPager
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.konyaco.fluent.ExperimentalFluentApi
+import com.konyaco.fluent.FluentTheme
+import com.konyaco.fluent.LocalContentAlpha
+import com.konyaco.fluent.LocalContentColor
+import com.konyaco.fluent.LocalTextStyle
+import com.konyaco.fluent.animation.FluentDuration
+import com.konyaco.fluent.animation.FluentEasing
+import com.konyaco.fluent.background.Material
+import com.konyaco.fluent.background.MaterialContainer
+import com.konyaco.fluent.background.MaterialContainerScope
+import com.konyaco.fluent.background.MaterialDefaults
+import com.konyaco.fluent.icons.Icons
+import com.konyaco.fluent.icons.filled.CaretDown
+import com.konyaco.fluent.icons.filled.CaretLeft
+import com.konyaco.fluent.icons.filled.CaretRight
+import com.konyaco.fluent.icons.filled.CaretUp
+import com.konyaco.fluent.scheme.PentaVisualScheme
+import com.konyaco.fluent.scheme.VisualStateScheme
+import com.konyaco.fluent.scheme.collectVisualState
+import kotlinx.coroutines.launch
+
+@OptIn(ExperimentalFluentApi::class)
+@Composable
+fun HorizontalFlipView(
+    state: PagerState,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    pageButtonColors: VisualStateScheme<PageButtonColor> = FlipViewDefaults.pageButtonColors(),
+    contentPadding: PaddingValues = PaddingValues(0.dp),
+    pageSize: PageSize = PageSize.Fill,
+    beyondViewportPageCount: Int = PagerDefaults.BeyondViewportPageCount,
+    pageSpacing: Dp = 0.dp,
+    verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
+    flingBehavior: TargetedFlingBehavior = PagerDefaults.flingBehavior(state = state),
+    userScrollEnabled: Boolean = true,
+    reverseLayout: Boolean = false,
+    key: ((index: Int) -> Any)? = null,
+    pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(
+        state,
+        Orientation.Horizontal
+    ),
+    snapPosition: SnapPosition = SnapPosition.Start,
+    pageContent: @Composable PagerScope.(index: Int) -> Unit
+) {
+    val scope = rememberCoroutineScope()
+    HorizontalFlipViewContainer(
+        nextPageVisible = remember(state) { derivedStateOf { state.currentPage < state.pageCount - 1 } }.value,
+        previousPageVisible = remember(state) { derivedStateOf { state.currentPage > 0 } }.value,
+        onNextPageClick = {
+            scope.launch {
+                state.animateScrollToPage(
+                    page = state.currentPage + 1,
+                    animationSpec = FlipViewDefaults.scrollAnimationSpec()
+                )
+            }
+        },
+        onPreviousPageClick = {
+            scope.launch {
+                state.animateScrollToPage(
+                    page = state.currentPage - 1,
+                    animationSpec = FlipViewDefaults.scrollAnimationSpec()
+                )
+            }
+        },
+        modifier = modifier,
+        enabled = enabled,
+        pageButtonColors = pageButtonColors
+    ) {
+        HorizontalPager(
+            state = state,
+            modifier = Modifier.fillMaxSize(),
+            contentPadding = contentPadding,
+            pageSize = pageSize,
+            beyondViewportPageCount = beyondViewportPageCount,
+            pageSpacing = pageSpacing,
+            verticalAlignment = verticalAlignment,
+            flingBehavior = flingBehavior,
+            userScrollEnabled = userScrollEnabled,
+            reverseLayout = reverseLayout,
+            key = key,
+            pageNestedScrollConnection = pageNestedScrollConnection,
+            snapPosition = snapPosition,
+            pageContent = pageContent
+        )
+    }
+}
+
+@OptIn(ExperimentalFluentApi::class)
+@Composable
+fun VerticalFlipView(
+    state: PagerState,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    pageButtonColors: VisualStateScheme<PageButtonColor> = FlipViewDefaults.pageButtonColors(),
+    contentPadding: PaddingValues = PaddingValues(0.dp),
+    pageSize: PageSize = PageSize.Fill,
+    beyondViewportPageCount: Int = PagerDefaults.BeyondViewportPageCount,
+    pageSpacing: Dp = FlipViewDefaults.verticalPageSpacing,
+    horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+    flingBehavior: TargetedFlingBehavior = PagerDefaults.flingBehavior(state = state),
+    userScrollEnabled: Boolean = true,
+    reverseLayout: Boolean = false,
+    key: ((index: Int) -> Any)? = null,
+    pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection(
+        state,
+        Orientation.Horizontal
+    ),
+    snapPosition: SnapPosition = SnapPosition.Start,
+    pageContent: @Composable PagerScope.(index: Int) -> Unit
+) {
+    val scope = rememberCoroutineScope()
+    VerticalFlipViewContainer(
+        nextPageVisible = remember(state) { derivedStateOf { state.currentPage < state.pageCount - 1 } }.value,
+        previousPageVisible = remember(state) { derivedStateOf { state.currentPage > 0 } }.value,
+        onNextPageClick = {
+            scope.launch {
+                state.animateScrollToPage(
+                    page = state.currentPage + 1,
+                    animationSpec = FlipViewDefaults.scrollAnimationSpec()
+                )
+            }
+        },
+        onPreviousPageClick = {
+            scope.launch {
+                state.animateScrollToPage(
+                    page = state.currentPage - 1,
+                    animationSpec = FlipViewDefaults.scrollAnimationSpec()
+                )
+            }
+        },
+        modifier = modifier,
+        enabled = enabled,
+        pageButtonColors = pageButtonColors
+    ) {
+        VerticalPager(
+            state = state,
+            modifier = Modifier.fillMaxSize(),
+            contentPadding = contentPadding,
+            pageSize = pageSize,
+            beyondViewportPageCount = beyondViewportPageCount,
+            pageSpacing = pageSpacing,
+            horizontalAlignment = horizontalAlignment,
+            flingBehavior = flingBehavior,
+            userScrollEnabled = userScrollEnabled,
+            reverseLayout = reverseLayout,
+            key = key,
+            pageNestedScrollConnection = pageNestedScrollConnection,
+            snapPosition = snapPosition,
+            pageContent = pageContent
+        )
+    }
+}
+
+@ExperimentalFluentApi
+@Composable
+fun VerticalFlipViewContainer(
+    onNextPageClick: () -> Unit,
+    onPreviousPageClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    nextPageVisible: Boolean = true,
+    previousPageVisible: Boolean = true,
+    pageButtonColors: VisualStateScheme<PageButtonColor> = FlipViewDefaults.pageButtonColors(),
+    content: @Composable () -> Unit
+) {
+    FlipViewContainer(
+        onNextPageClick = onNextPageClick,
+        onPreviousPageClick = onPreviousPageClick,
+        modifier = modifier,
+        isVertical = true,
+        enabled = enabled,
+        nextPageVisible = nextPageVisible,
+        previousPageVisible = previousPageVisible,
+        pageButtonColors = pageButtonColors,
+        content = content
+    )
+}
+
+@ExperimentalFluentApi
+@Composable
+fun HorizontalFlipViewContainer(
+    onNextPageClick: () -> Unit,
+    onPreviousPageClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    nextPageVisible: Boolean = true,
+    previousPageVisible: Boolean = true,
+    pageButtonColors: VisualStateScheme<PageButtonColor> = FlipViewDefaults.pageButtonColors(),
+    content: @Composable () -> Unit
+) {
+    FlipViewContainer(
+        onNextPageClick = onNextPageClick,
+        onPreviousPageClick = onPreviousPageClick,
+        modifier = modifier,
+        isVertical = false,
+        enabled = enabled,
+        pageButtonColors = pageButtonColors,
+        nextPageVisible = nextPageVisible,
+        previousPageVisible = previousPageVisible,
+        content = content
+    )
+}
+
+@ExperimentalFluentApi
+@Composable
+private fun FlipViewContainer(
+    onNextPageClick: () -> Unit,
+    onPreviousPageClick: () -> Unit,
+    nextPageVisible: Boolean,
+    previousPageVisible: Boolean,
+    modifier: Modifier,
+    isVertical: Boolean,
+    enabled: Boolean,
+    pageButtonColors: VisualStateScheme<PageButtonColor>,
+    content: @Composable () -> Unit
+) {
+    MaterialContainer(modifier = modifier.clip(FluentTheme.shapes.overlay)) {
+        val interactionSource = remember { MutableInteractionSource() }
+        val isHovered = interactionSource.collectIsHoveredAsState()
+        Box(
+            propagateMinConstraints = true,
+            modifier = Modifier.behindMaterial()
+                .hoverable(interactionSource, enabled = enabled)
+        ) {
+            content()
+        }
+        if (isVertical) {
+            PageButton(
+                colors = pageButtonColors,
+                isVertical = true,
+                vector = Icons.Filled.CaretDown,
+                glyph = '\uEDDC',
+                onClick = onNextPageClick,
+                visible = nextPageVisible && isHovered.value,
+                modifier = Modifier
+                    .align(Alignment.BottomCenter)
+                    .padding(bottom = 1.dp)
+                    .hoverable(interactionSource),
+            )
+            PageButton(
+                colors = pageButtonColors,
+                isVertical = true,
+                vector = Icons.Filled.CaretUp,
+                glyph = '\uEDDB',
+                onClick = onPreviousPageClick,
+                visible = previousPageVisible && isHovered.value,
+                modifier = Modifier
+                    .align(Alignment.TopCenter)
+                    .padding(top = 1.dp)
+                    .hoverable(interactionSource),
+            )
+        } else {
+            PageButton(
+                colors = pageButtonColors,
+                isVertical = false,
+                vector = Icons.Filled.CaretRight,
+                glyph = '\uEDDA',
+                onClick = onNextPageClick,
+                visible = nextPageVisible && isHovered.value,
+                modifier = Modifier
+                    .align(Alignment.CenterEnd)
+                    .padding(end = 1.dp)
+                    .hoverable(interactionSource),
+            )
+
+            PageButton(
+                colors = pageButtonColors,
+                isVertical = false,
+                vector = Icons.Filled.CaretLeft,
+                glyph = '\uEDD9',
+                onClick = onPreviousPageClick,
+                visible = previousPageVisible && isHovered.value,
+                modifier = Modifier
+                    .align(Alignment.CenterStart)
+                    .padding(start = 1.dp)
+                    .hoverable(interactionSource),
+            )
+        }
+    }
+}
+
+@OptIn(ExperimentalFluentApi::class)
+@Composable
+private fun MaterialContainerScope.PageButton(
+    colors: VisualStateScheme<PageButtonColor>,
+    modifier: Modifier = Modifier,
+    isVertical: Boolean,
+    visible: Boolean,
+    onClick: () -> Unit,
+    glyph: Char = '\uEDDA',
+    vector: ImageVector = Icons.Filled.CaretRight,
+) {
+    if (visible) {
+        val interactionSource = remember { MutableInteractionSource() }
+        val currentColor = colors.schemeFor(interactionSource.collectVisualState(false))
+        Box(
+            contentAlignment = Alignment.Center,
+            modifier = modifier
+                .clip(FluentTheme.shapes.control)
+                .materialOverlay(currentColor.background)
+                .size(if (isVertical) VerticalButtonSize else HorizontalButtonSize)
+                .clickable(
+                    indication = null,
+                    interactionSource = interactionSource,
+                    onClick = onClick
+                )
+        ) {
+            CompositionLocalProvider(
+                LocalContentColor provides currentColor.contentColor,
+                LocalContentAlpha provides currentColor.contentColor.alpha,
+                LocalTextStyle provides LocalTextStyle.current.copy(color = currentColor.contentColor)
+            ) {
+                val isPressed = interactionSource.collectIsPressedAsState()
+                val scale = animateFloatAsState(
+                    targetValue = if (isPressed.value) { 7/8f } else 1f,
+                    animationSpec = tween(FluentDuration.ShortDuration, easing = FluentEasing.FastInvokeEasing)
+                )
+                FontIcon(
+                    glyph = glyph,
+                    vector = vector,
+                    contentDescription = null,
+                    iconSize = 8.sp,
+                    vectorSize = 14.dp,
+                    modifier = Modifier
+                        .graphicsLayer {
+                            scaleX = scale.value
+                            scaleY = scale.value
+                            transformOrigin = TransformOrigin(0.5f, 0.5f)
+                        }
+                )
+            }
+        }
+    }
+}
+
+data class PageButtonColor(
+    val background: Material,
+    val contentColor: Color
+)
+
+object FlipViewDefaults {
+
+    val verticalPageSpacing = 4.dp
+
+    @Composable
+    @Stable
+    fun pageButtonColors(
+        default: PageButtonColor = PageButtonColor(
+            background = MaterialDefaults.acrylicDefault(),
+            contentColor = FluentTheme.colors.controlStrong.default
+        ),
+        hovered: PageButtonColor = PageButtonColor(
+            background = MaterialDefaults.acrylicDefault(),
+            contentColor = FluentTheme.colors.text.text.secondary
+        ),
+        pressed: PageButtonColor = hovered,
+    ) = PentaVisualScheme(
+        default = default,
+        hovered = hovered,
+        pressed = pressed,
+        disabled = default
+    )
+
+    fun scrollAnimationSpec() =
+        tween<Float>(FluentDuration.LongDuration, easing = FluentEasing.FastInvokeEasing)
+}
+
+private val VerticalButtonSize = DpSize(38.dp, 16.dp)
+private val HorizontalButtonSize = DpSize(16.dp, 38.dp)
\ No newline at end of file
diff --git a/fluent/src/commonMain/kotlin/com/konyaco/fluent/component/GridViewItem.kt b/fluent/src/commonMain/kotlin/com/konyaco/fluent/component/GridViewItem.kt
new file mode 100644
index 00000000..e58c7537
--- /dev/null
+++ b/fluent/src/commonMain/kotlin/com/konyaco/fluent/component/GridViewItem.kt
@@ -0,0 +1,255 @@
+package com.konyaco.fluent.component
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.foundation.shape.CornerBasedShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+import com.konyaco.fluent.FluentTheme
+import com.konyaco.fluent.background.LayerShapeHelper
+import com.konyaco.fluent.background.calculateBorderPadding
+import com.konyaco.fluent.scheme.PentaVisualScheme
+import com.konyaco.fluent.scheme.VisualStateScheme
+import com.konyaco.fluent.scheme.collectVisualState
+import kotlin.jvm.JvmInline
+
+@Composable
+fun MultiSelectGridViewItem(
+    selected: Boolean,
+    onSelectedChange: (Boolean) -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    colors: VisualStateScheme<GridViewItemColor> = if (selected) {
+        GridViewItemDefaults.selectedColors()
+    } else {
+        GridViewItemDefaults.defaultColors()
+    },
+    checkBoxColorScheme: VisualStateScheme<CheckBoxColor> = if (selected) {
+        GridViewItemDefaults.selectedCheckBoxColors()
+    } else {
+        GridViewItemDefaults.defaultCheckBoxColors()
+    },
+    interactionSource: MutableInteractionSource? = null,
+    content: @Composable () -> Unit
+) {
+    GridViewItem(
+        selected = selected,
+        onSelectedChange = onSelectedChange,
+        modifier = modifier,
+        enabled = enabled,
+        interactionSource = interactionSource,
+        colors = colors,
+        content = { content() },
+        overlay = {
+            CheckBox(
+                checked = selected,
+                onCheckStateChange = onSelectedChange,
+                colors = checkBoxColorScheme,
+                enabled = enabled,
+                modifier = Modifier
+                    .align(Alignment.TopEnd)
+                    .padding(top = 4.dp, end = 4.dp)
+            )
+        }
+    )
+}
+
+@Composable
+fun GridViewItem(
+    selected: Boolean,
+    onSelectedChange: (Boolean) -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    interactionSource: MutableInteractionSource? = null,
+    colors: VisualStateScheme<GridViewItemColor> = if (selected) {
+        GridViewItemDefaults.selectedColors()
+    } else {
+        GridViewItemDefaults.defaultColors()
+    },
+    content: @Composable () -> Unit
+) {
+    GridViewItem(
+        selected = selected,
+        onSelectedChange = onSelectedChange,
+        modifier = modifier,
+        enabled = enabled,
+        interactionSource = interactionSource,
+        colors = colors,
+        content = { content() },
+        overlay = null
+    )
+}
+
+@Stable
+data class GridViewItemColor(
+    val borderColor: Color,
+    val backgroundColor: Color
+)
+
+typealias GridViewItemColorScheme = PentaVisualScheme<GridViewItemColor>
+
+object GridViewItemDefaults {
+
+    val spacing = 4.dp
+
+    @Stable
+    @Composable
+    fun defaultColors(
+        default: GridViewItemColor = GridViewItemColor(
+            borderColor = Color.Transparent,
+            backgroundColor = FluentTheme.colors.subtleFill.transparent
+        ),
+        hovered: GridViewItemColor = GridViewItemColor(
+            borderColor = FluentTheme.colors.stroke.control.onAccentTertiary,
+            backgroundColor = FluentTheme.colors.subtleFill.secondary
+        ),
+        pressed: GridViewItemColor = GridViewItemColor(
+            borderColor = Color.Transparent,
+            backgroundColor = FluentTheme.colors.subtleFill.tertiary
+        ),
+        disabled: GridViewItemColor = default,
+    ): GridViewItemColorScheme = PentaVisualScheme(
+        default = default,
+        hovered = hovered,
+        pressed = pressed,
+        disabled = disabled
+    )
+
+    @Stable
+    @Composable
+    fun selectedColors(
+        default: GridViewItemColor = GridViewItemColor(
+            borderColor = FluentTheme.colors.fillAccent.default,
+            backgroundColor = FluentTheme.colors.subtleFill.tertiary
+        ),
+        hovered: GridViewItemColor = GridViewItemColor(
+            borderColor = FluentTheme.colors.fillAccent.secondary,
+            backgroundColor = FluentTheme.colors.subtleFill.secondary
+        ),
+        pressed: GridViewItemColor = GridViewItemColor(
+            borderColor = FluentTheme.colors.fillAccent.tertiary,
+            backgroundColor = FluentTheme.colors.subtleFill.tertiary
+        ),
+        disabled: GridViewItemColor = GridViewItemColor(
+            borderColor = FluentTheme.colors.fillAccent.disabled,
+            backgroundColor = FluentTheme.colors.subtleFill.secondary
+        ),
+    ): GridViewItemColorScheme = PentaVisualScheme(
+        default = default,
+        hovered = hovered,
+        pressed = pressed,
+        disabled = disabled
+    )
+
+    //TODO ColorOnImage
+    @Stable
+    @Composable
+    fun defaultCheckBoxColors() = CheckBoxDefaults.defaultCheckBoxColors()
+
+    //TODO ColorOnImage
+    @Stable
+    @Composable
+    fun selectedCheckBoxColors() = CheckBoxDefaults.selectedCheckBoxColors()
+}
+
+@Composable
+private fun GridViewItem(
+    selected: Boolean,
+    onSelectedChange: (Boolean) -> Unit,
+    modifier: Modifier,
+    enabled: Boolean,
+    colors: VisualStateScheme<GridViewItemColor>,
+    interactionSource: MutableInteractionSource?,
+    overlay: (@Composable BoxScope.() -> Unit)?,
+    content: @Composable BoxScope.() -> Unit
+) {
+    val itemShape = FluentTheme.shapes.control
+    val targetInteractionSource = interactionSource ?: remember { MutableInteractionSource() }
+    val currentColor = colors.schemeFor(targetInteractionSource.collectVisualState(!enabled))
+    Box(
+        propagateMinConstraints = true,
+        modifier = modifier
+            .clip(itemShape)
+            .selectable(
+                enabled = enabled,
+                selected = selected,
+                onClick = { onSelectedChange(!selected) },
+                indication = null,
+                interactionSource = targetInteractionSource
+            )
+            .border(SelectedItemBorderSize, currentColor.borderColor, shape = itemShape)
+    ) {
+        Box(
+            propagateMinConstraints = true,
+            modifier = if (selected) {
+                val innerShape = remember(itemShape) {
+                    if (itemShape is CornerBasedShape) {
+                        GridViewItemInnerShape(itemShape)
+                    } else {
+                        itemShape
+                    }
+                }
+                Modifier
+                    .clip(innerShape)
+                    .background(
+                        color = currentColor.backgroundColor,
+                        shape = innerShape
+                    )
+            } else {
+                Modifier.background(
+                    color = currentColor.backgroundColor,
+                    shape = itemShape
+                )
+            }
+        ) {
+            content()
+        }
+        if (overlay != null) {
+            Box(
+                content = overlay,
+                modifier = Modifier
+                    .matchParentSize()
+            )
+        }
+
+    }
+}
+
+@Stable
+@Immutable
+@JvmInline
+private value class GridViewItemInnerShape(private val itemShape: CornerBasedShape): Shape {
+    override fun createOutline(
+        size: Size,
+        layoutDirection: LayoutDirection,
+        density: Density
+    ): Outline {
+        return LayerShapeHelper.createInnerOutline(
+            outsideShape = itemShape,
+            size = size,
+            density = density,
+            layoutDirection = layoutDirection,
+            paddingPx = itemShape.calculateBorderPadding(density, InnerPaddingSize)
+        )
+    }
+}
+
+private val SelectedItemBorderSize = 2.dp
+private val InnerPaddingSize = 3.dp
\ No newline at end of file
diff --git a/fluent/src/commonMain/kotlin/com/konyaco/fluent/component/PipsPager.kt b/fluent/src/commonMain/kotlin/com/konyaco/fluent/component/PipsPager.kt
new file mode 100644
index 00000000..d574d7c8
--- /dev/null
+++ b/fluent/src/commonMain/kotlin/com/konyaco/fluent/component/PipsPager.kt
@@ -0,0 +1,441 @@
+package com.konyaco.fluent.component
+
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.animateScrollBy
+import androidx.compose.foundation.hoverable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsHoveredAsState
+import androidx.compose.foundation.interaction.collectIsPressedAsState
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.pager.PagerState
+import androidx.compose.foundation.relocation.BringIntoViewRequester
+import androidx.compose.foundation.relocation.bringIntoViewRequester
+import androidx.compose.foundation.selection.selectable
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.TransformOrigin
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.konyaco.fluent.ExperimentalFluentApi
+import com.konyaco.fluent.FluentTheme
+import com.konyaco.fluent.LocalContentAlpha
+import com.konyaco.fluent.LocalContentColor
+import com.konyaco.fluent.LocalTextStyle
+import com.konyaco.fluent.animation.FluentDuration
+import com.konyaco.fluent.animation.FluentEasing
+import com.konyaco.fluent.icons.Icons
+import com.konyaco.fluent.icons.filled.CaretDown
+import com.konyaco.fluent.icons.filled.CaretLeft
+import com.konyaco.fluent.icons.filled.CaretRight
+import com.konyaco.fluent.icons.filled.CaretUp
+import com.konyaco.fluent.scheme.PentaVisualScheme
+import com.konyaco.fluent.scheme.VisualState
+import com.konyaco.fluent.scheme.VisualStateScheme
+import com.konyaco.fluent.scheme.collectVisualState
+import kotlinx.coroutines.launch
+import kotlin.math.abs
+
+@Composable
+fun HorizontalPipsPager(
+    state: PagerState,
+    modifier: Modifier = Modifier,
+    scrollAnimationSpec: AnimationSpec<Float> = FlipViewDefaults.scrollAnimationSpec(),
+    visibleCount: Int = 5,
+    enabled: Boolean = true,
+    pageButtonVisibleStrategy: PageButtonVisibleStrategy = PageButtonVisibleStrategy.Never,
+    pipsColors: VisualStateScheme<Color> = PipsPagerDefaults.pipsColors(),
+) {
+    val scope = rememberCoroutineScope()
+    PipsPager(
+        selectedIndex = state.currentPage,
+        onSelectedIndexChange = {
+            scope.launch {
+                if (abs(state.currentPage - it) == 1) {
+                    state.animateScrollToPage(it, animationSpec = scrollAnimationSpec)
+                } else {
+                    state.scrollToPage(it)
+                }
+            }
+        },
+        count = state.pageCount,
+        isVertical = false,
+        pipsColors = pipsColors,
+        visibleCount = visibleCount,
+        pageButtonVisibleStrategy = pageButtonVisibleStrategy,
+        enabled = enabled,
+        modifier = modifier
+    )
+}
+
+@Composable
+fun VerticalPipsPager(
+    state: PagerState,
+    modifier: Modifier = Modifier,
+    scrollAnimationSpec: AnimationSpec<Float> = FlipViewDefaults.scrollAnimationSpec(),
+    visibleCount: Int = 5,
+    enabled: Boolean = true,
+    pageButtonVisibleStrategy: PageButtonVisibleStrategy = PageButtonVisibleStrategy.Never,
+    pipsColors: VisualStateScheme<Color> = PipsPagerDefaults.pipsColors(),
+) {
+    val scope = rememberCoroutineScope()
+    PipsPager(
+        selectedIndex = state.currentPage,
+        onSelectedIndexChange = {
+            scope.launch {
+                if (abs(state.currentPage - it) == 1) {
+                    state.animateScrollToPage(it, animationSpec = scrollAnimationSpec)
+                } else {
+                    state.scrollToPage(it)
+                }
+            }
+        },
+        count = state.pageCount,
+        isVertical = true,
+        pipsColors = pipsColors,
+        visibleCount = visibleCount,
+        pageButtonVisibleStrategy = pageButtonVisibleStrategy,
+        enabled = enabled,
+        modifier = modifier
+    )
+}
+
+@Composable
+fun HorizontalPipsPager(
+    selectedIndex: Int,
+    onSelectedIndexChange: (Int) -> Unit,
+    count: Int,
+    modifier: Modifier = Modifier,
+    visibleCount: Int = 5,
+    enabled: Boolean = true,
+    pageButtonVisibleStrategy: PageButtonVisibleStrategy = PageButtonVisibleStrategy.Never,
+    pipsColors: VisualStateScheme<Color> = PipsPagerDefaults.pipsColors(),
+) {
+    PipsPager(
+        selectedIndex = selectedIndex,
+        onSelectedIndexChange = onSelectedIndexChange,
+        count = count,
+        isVertical = false,
+        pipsColors = pipsColors,
+        visibleCount = visibleCount,
+        pageButtonVisibleStrategy = pageButtonVisibleStrategy,
+        enabled = enabled,
+        modifier = modifier
+    )
+}
+
+@Composable
+fun VerticalPipsPager(
+    selectedIndex: Int,
+    onSelectedIndexChange: (Int) -> Unit,
+    count: Int,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    pageButtonVisibleStrategy: PageButtonVisibleStrategy = PageButtonVisibleStrategy.Never,
+    visibleCount: Int = 5,
+    pipsColors: VisualStateScheme<Color> = PipsPagerDefaults.pipsColors(),
+) {
+    PipsPager(
+        selectedIndex = selectedIndex,
+        onSelectedIndexChange = onSelectedIndexChange,
+        count = count,
+        isVertical = true,
+        pipsColors = pipsColors,
+        visibleCount = visibleCount,
+        pageButtonVisibleStrategy = pageButtonVisibleStrategy,
+        enabled = enabled,
+        modifier = modifier
+    )
+}
+
+@Composable
+private fun PipsPager(
+    selectedIndex: Int,
+    onSelectedIndexChange: (Int) -> Unit,
+    count: Int,
+    isVertical: Boolean,
+    pipsColors: VisualStateScheme<Color>,
+    pageButtonVisibleStrategy: PageButtonVisibleStrategy,
+    enabled: Boolean,
+    visibleCount: Int,
+    modifier: Modifier,
+) {
+    val size = PipsItemWidth * minOf(visibleCount, count)
+    val listState = rememberLazyListState()
+    val interactionSource = remember { MutableInteractionSource() }
+    val isHovered = interactionSource.collectIsHoveredAsState()
+    val pageButtonVisible = when (pageButtonVisibleStrategy) {
+        PageButtonVisibleStrategy.Always -> true
+        PageButtonVisibleStrategy.VisibleOnHovered -> isHovered.value
+        PageButtonVisibleStrategy.Never -> false
+    }
+    if (isVertical) {
+        Column(modifier = modifier.width(PipsPagerContainerHeight).hoverable(interactionSource)) {
+
+            PageButton(
+                colors = pipsColors,
+                vector = Icons.Filled.CaretUp,
+                glyph = '\uEDDB',
+                onClick = { onSelectedIndexChange(selectedIndex - 1) },
+                enabled = enabled && selectedIndex > 0,
+                visible = pageButtonVisible,
+                modifier = Modifier.hoverable(interactionSource)
+            )
+            LazyColumn(
+                state = listState,
+                modifier = Modifier
+                    .fillMaxWidth()
+                    .height(size)
+            ) {
+                items(count) { index ->
+                    Pips(
+                        selected = index == selectedIndex,
+                        onSelectedChange = { onSelectedIndexChange(index) },
+                        isVertical = isVertical,
+                        colors = pipsColors,
+                        enabled = enabled,
+                        modifier = Modifier
+                    )
+                }
+            }
+            PageButton(
+                colors = pipsColors,
+                vector = Icons.Filled.CaretDown,
+                glyph = '\uEDDC',
+                onClick = { onSelectedIndexChange(selectedIndex + 1) },
+                enabled = enabled && selectedIndex < count - 1,
+                visible = pageButtonVisible,
+                modifier = Modifier
+            )
+        }
+    } else {
+        Row(modifier = modifier.height(PipsPagerContainerHeight).hoverable(interactionSource)) {
+
+            PageButton(
+                colors = pipsColors,
+                vector = Icons.Filled.CaretLeft,
+                glyph = '\uEDD9',
+                onClick = { onSelectedIndexChange(selectedIndex - 1) },
+                enabled = enabled && selectedIndex > 0,
+                visible = pageButtonVisible,
+                modifier = Modifier.hoverable(interactionSource)
+            )
+
+            LazyRow(
+                state = listState,
+                modifier = Modifier
+                    .fillMaxHeight()
+                    .width(size)
+            ) {
+                items(count) { index ->
+                    Pips(
+                        selected = index == selectedIndex,
+                        onSelectedChange = { onSelectedIndexChange(index) },
+                        isVertical = isVertical,
+                        colors = pipsColors,
+                        enabled = enabled,
+                        modifier = Modifier
+                    )
+                }
+            }
+
+            PageButton(
+                colors = pipsColors,
+                vector = Icons.Filled.CaretRight,
+                glyph = '\uEDDA',
+                onClick = { onSelectedIndexChange(selectedIndex + 1) },
+                enabled = enabled && selectedIndex < count - 1,
+                visible = pageButtonVisible,
+                modifier = Modifier.hoverable(interactionSource)
+            )
+        }
+    }
+    LaunchedEffect(selectedIndex, listState.layoutInfo.visibleItemsInfo) {
+        val item = listState.layoutInfo.visibleItemsInfo.firstOrNull { it.index == selectedIndex }
+        if (item == null) {
+            listState.animateScrollToItem(selectedIndex)
+        } else {
+            val itemSize = item.size
+            val viewportSize = if (isVertical) {
+                listState.layoutInfo.viewportSize.height
+            } else {
+                listState.layoutInfo.viewportSize.width
+            }
+            val centerOffset = (viewportSize - itemSize) / 2f
+            val scrollOffset = item.offset - centerOffset
+            listState.animateScrollBy(
+                scrollOffset,
+                animationSpec = FlipViewDefaults.scrollAnimationSpec()
+            )
+        }
+    }
+}
+
+typealias PipsColorScheme = PentaVisualScheme<Color>
+
+object PipsPagerDefaults {
+
+    @Stable
+    @Composable
+    fun pipsColors(
+        default: Color = FluentTheme.colors.controlStrong.default,
+        hovered: Color = FluentTheme.colors.text.text.secondary,
+        pressed: Color = FluentTheme.colors.text.text.secondary,
+        disabled: Color = FluentTheme.colors.controlStrong.disabled
+    ) = PipsColorScheme(
+        default = default,
+        hovered = hovered,
+        pressed = pressed,
+        disabled = disabled
+    )
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+private fun Pips(
+    selected: Boolean,
+    onSelectedChange: (Boolean) -> Unit,
+    isVertical: Boolean,
+    colors: VisualStateScheme<Color>,
+    enabled: Boolean,
+    modifier: Modifier
+) {
+    val interactionSource = remember { MutableInteractionSource() }
+    val bringIntoViewRequester = remember { BringIntoViewRequester() }
+    val visualState = interactionSource.collectVisualState(!enabled)
+    val currentColor = colors.schemeFor(visualState)
+    val size = animateDpAsState(
+        targetValue = when (visualState) {
+            VisualState.Hovered -> 5.dp
+            VisualState.Pressed -> 3.dp
+            else -> 4.dp
+        } + if (selected) {
+            2.dp
+        } else {
+            0.dp
+        }
+    )
+    LaunchedEffect(selected) {
+        if (selected) {
+            bringIntoViewRequester.bringIntoView()
+        }
+    }
+    Box(
+        modifier = modifier
+            .then(
+                if (isVertical) {
+                    Modifier.fillMaxWidth()
+                        .height(PipsItemWidth)
+                } else {
+                    Modifier.fillMaxHeight()
+                        .width(PipsItemWidth)
+                }
+            )
+            .bringIntoViewRequester(bringIntoViewRequester)
+            .selectable(
+                selected = selected,
+                indication = null,
+                interactionSource = interactionSource,
+                enabled = enabled,
+                onClick = { onSelectedChange(!selected) }
+            )
+            .wrapContentSize(Alignment.Center)
+            .size(size.value)
+            .background(currentColor, shape = CircleShape)
+    )
+}
+
+enum class PageButtonVisibleStrategy {
+    Always,
+    VisibleOnHovered,
+    Never
+}
+
+@OptIn(ExperimentalFluentApi::class)
+@Composable
+private fun PageButton(
+    colors: VisualStateScheme<Color>,
+    modifier: Modifier = Modifier,
+    enabled: Boolean,
+    visible: Boolean = true,
+    onClick: () -> Unit,
+    glyph: Char = '\uEDDA',
+    vector: ImageVector = Icons.Filled.CaretRight,
+) {
+    if (visible) {
+        val interactionSource = remember { MutableInteractionSource() }
+        val currentColor = colors.schemeFor(interactionSource.collectVisualState(!enabled))
+        Box(
+            contentAlignment = Alignment.Center,
+            modifier = modifier
+                .size(PipsPagerContainerHeight)
+                .clickable(
+                    indication = null,
+                    interactionSource = interactionSource,
+                    onClick = onClick,
+                    enabled = enabled
+                )
+        ) {
+            CompositionLocalProvider(
+                LocalContentColor provides currentColor,
+                LocalContentAlpha provides currentColor.alpha,
+                LocalTextStyle provides LocalTextStyle.current.copy(color = currentColor)
+            ) {
+                val isPressed = interactionSource.collectIsPressedAsState()
+                val scale = animateFloatAsState(
+                    targetValue = if (isPressed.value) {
+                        7 / 8f
+                    } else 1f,
+                    animationSpec = tween(
+                        FluentDuration.ShortDuration,
+                        easing = FluentEasing.FastInvokeEasing
+                    )
+                )
+                FontIcon(
+                    glyph = glyph,
+                    vector = vector,
+                    contentDescription = null,
+                    iconSize = 8.sp,
+                    vectorSize = 14.dp,
+                    modifier = Modifier
+                        .graphicsLayer {
+                            scaleX = scale.value
+                            scaleY = scale.value
+                            transformOrigin = TransformOrigin(0.5f, 0.5f)
+                        }
+                )
+            }
+        }
+    } else {
+        Box(modifier = modifier.size(PipsPagerContainerHeight))
+    }
+}
+
+private val PipsPagerContainerHeight = 20.dp
+private val PipsItemWidth = 12.dp
\ No newline at end of file
diff --git a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/component/ComponentGroupInfo.kt b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/component/ComponentGroupInfo.kt
index 0eb1284b..ab73993e 100644
--- a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/component/ComponentGroupInfo.kt
+++ b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/component/ComponentGroupInfo.kt
@@ -41,7 +41,7 @@ object ComponentGroupInfo {
     @ComponentGroup("Navigation", index = 10, packageMap = "$screenPackage.navigation")
     const val Navigation = "Navigation"
 
-    @ComponentGroup("ArrowSort", index = 11)
+    @ComponentGroup("ArrowSort", index = 11, packageMap = "$screenPackage.scrolling")
     const val Scrolling = "Scrolling"
 
     @ComponentGroup("ChatMultiple", index = 12, packageMap = "$screenPackage.status")
diff --git a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/collections/FlipViewScreen.kt b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/collections/FlipViewScreen.kt
new file mode 100644
index 00000000..003b72e3
--- /dev/null
+++ b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/collections/FlipViewScreen.kt
@@ -0,0 +1,72 @@
+package com.konyaco.fluent.gallery.screen.collections
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.konyaco.fluent.component.HorizontalFlipView
+import com.konyaco.fluent.component.VerticalFlipView
+import com.konyaco.fluent.gallery.annotation.Component
+import com.konyaco.fluent.gallery.annotation.Sample
+import com.konyaco.fluent.gallery.component.ComponentPagePath
+import com.konyaco.fluent.gallery.component.GalleryPage
+import com.konyaco.fluent.source.generated.FluentSourceFile
+
+@Component(description = "Presents a collection of items that the user can flip through, one item at a time.")
+@Composable
+fun FlipViewScreen() {
+    GalleryPage(
+        title = "FlipView",
+        description = "The FlipView lets you flip through a collection of items, one at a time. " +
+                "It's great for displaying images from a gallery, pages of a magazine, or similar items.",
+        componentPath = FluentSourceFile.FlipView,
+        galleryPath = ComponentPagePath.FlipViewScreen
+    ) {
+        Section(
+            title = "HorizontalFlipView sample",
+            sourceCode = sourceCodeOfHorizontalFlipViewSample,
+            content = { HorizontalFlipViewSample() }
+        )
+
+        Section(
+            title = "VerticalFlipView sample",
+            sourceCode = sourceCodeOfVerticalFlipViewSample,
+            content = { VerticalFlipViewSample() }
+        )
+    }
+}
+
+@Sample
+@Composable
+private fun HorizontalFlipViewSample() {
+    val items = randomBrushItems()
+    HorizontalFlipView(
+        state = rememberPagerState { items.size },
+        modifier = Modifier
+            .widthIn(max = 400.dp)
+            .height(180.dp)
+    ) {
+        Box(modifier = Modifier.fillMaxSize().background(items[it]))
+    }
+}
+
+@Sample
+@Composable
+private fun VerticalFlipViewSample() {
+    val items = randomBrushItems()
+    VerticalFlipView(
+        state = rememberPagerState { items.size },
+        modifier = Modifier
+            .widthIn(max = 400.dp)
+            .height(180.dp)
+    ) {
+        Box(modifier = Modifier.fillMaxSize().background(items[it]))
+    }
+}
\ No newline at end of file
diff --git a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/collections/GridViewItemScreen.kt b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/collections/GridViewItemScreen.kt
new file mode 100644
index 00000000..a14cfeb1
--- /dev/null
+++ b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/collections/GridViewItemScreen.kt
@@ -0,0 +1,138 @@
+package com.konyaco.fluent.gallery.screen.collections
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.lazy.grid.itemsIndexed
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import com.konyaco.fluent.component.GridViewItem
+import com.konyaco.fluent.component.GridViewItemDefaults
+import com.konyaco.fluent.component.MultiSelectGridViewItem
+import com.konyaco.fluent.gallery.annotation.Component
+import com.konyaco.fluent.gallery.annotation.Sample
+import com.konyaco.fluent.gallery.component.ComponentPagePath
+import com.konyaco.fluent.gallery.component.GalleryPage
+import com.konyaco.fluent.source.generated.FluentSourceFile
+import kotlin.random.Random
+import kotlin.uuid.ExperimentalUuidApi
+import kotlin.uuid.Uuid
+
+@Component(description = "Selectable item template in grids.")
+@Composable
+fun GridViewItemScreen() {
+    GalleryPage(
+        title = "GridViewItem",
+        description = "Selectable item template in grids.",
+        componentPath = FluentSourceFile.GridViewItem,
+        galleryPath = ComponentPagePath.GridViewItemScreen
+    ) {
+        Section(
+            title = "Basic GridViewItem",
+            sourceCode = sourceCodeOfBasicGridViewItemSample,
+            content = { BasicGridViewItemSample() }
+        )
+
+        Section(
+            title = "MultiSelect GridViewItem",
+            sourceCode = sourceCodeOfMultiSelectGridViewItemSample,
+            content = { MultiSelectGridViewItemSample() }
+        )
+    }
+}
+
+@Sample
+@Composable
+private fun BasicGridViewItemSample() {
+    val items = randomBrushItems()
+    var selectedIndex by remember { mutableStateOf(-1) }
+    LazyVerticalGrid(
+        columns = GridCells.Adaptive(112.dp),
+        horizontalArrangement = Arrangement.spacedBy(GridViewItemDefaults.spacing),
+        verticalArrangement = Arrangement.spacedBy(GridViewItemDefaults.spacing),
+        contentPadding = PaddingValues(GridViewItemDefaults.spacing),
+        modifier = Modifier.height(300.dp)
+    ) {
+        itemsIndexed(items) { index, item ->
+            GridViewItem(
+                selected = index == selectedIndex,
+                onSelectedChange = {
+                    selectedIndex = if (it) index else -1
+                },
+                content = {
+                    Box(modifier = Modifier.background(item))
+                },
+                modifier = Modifier.fillMaxWidth()
+                    .aspectRatio(1f)
+            )
+        }
+    }
+}
+
+@Sample
+@Composable
+private fun MultiSelectGridViewItemSample() {
+    val items = randomBrushItems()
+    val selectedIndices = remember { mutableStateListOf<Int>() }
+    LazyVerticalGrid(
+        columns = GridCells.Adaptive(112.dp),
+        horizontalArrangement = Arrangement.spacedBy(GridViewItemDefaults.spacing),
+        verticalArrangement = Arrangement.spacedBy(GridViewItemDefaults.spacing),
+        contentPadding = PaddingValues(GridViewItemDefaults.spacing),
+        modifier = Modifier.height(300.dp)
+    ) {
+        itemsIndexed(items) { index, item ->
+            MultiSelectGridViewItem(
+                selected = selectedIndices.contains(index),
+                onSelectedChange = {
+                    if (it) {
+                        selectedIndices.add(index)
+                    } else {
+                        selectedIndices.remove(index)
+                    }
+                },
+                content = {
+                    Box(modifier = Modifier.background(item))
+                },
+                modifier = Modifier.fillMaxWidth()
+                    .aspectRatio(1f)
+            )
+        }
+    }
+}
+
+@OptIn(ExperimentalUuidApi::class)
+@Composable
+internal fun randomBrushItems(): List<Brush> {
+    val random = remember { Random(Uuid.random().toLongs { a, b -> a + b }) }
+    return List(12) {
+        Brush.linearGradient(
+            colors = randomColor(random)
+        )
+    }
+}
+
+private fun randomColor(random: Random): List<Color> {
+    val count = random.nextInt(2, 5)
+    return List(count) {
+        Color(
+            red = random.nextInt(0, 255),
+            green = random.nextInt(0, 255),
+            blue = random.nextInt(0, 255)
+        )
+    }
+}
\ No newline at end of file
diff --git a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/design/IconsScreen.kt b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/design/IconsScreen.kt
index 370c1a0a..2a5740f8 100644
--- a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/design/IconsScreen.kt
+++ b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/design/IconsScreen.kt
@@ -35,7 +35,6 @@ import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.RectangleShape
-import androidx.compose.ui.graphics.SolidColor
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.style.TextOverflow
@@ -45,6 +44,9 @@ import com.konyaco.fluent.FluentTheme
 import com.konyaco.fluent.FluentThemeConfiguration
 import com.konyaco.fluent.background.BackgroundSizing
 import com.konyaco.fluent.background.Layer
+import com.konyaco.fluent.component.GridViewItem
+import com.konyaco.fluent.component.GridViewItemColor
+import com.konyaco.fluent.component.GridViewItemDefaults
 import com.konyaco.fluent.component.Icon
 import com.konyaco.fluent.component.RadioButton
 import com.konyaco.fluent.component.ScrollbarContainer
@@ -59,8 +61,6 @@ import com.konyaco.fluent.icons.Icons
 import com.konyaco.fluent.source.generated.FluentSourceFile
 import com.konyaco.fluent.source.generated.fluentIconCoreItems
 import com.konyaco.fluent.source.generated.fluentIconExtendedItems
-import com.konyaco.fluent.surface.Card
-import com.konyaco.fluent.surface.CardDefaults
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.debounce
@@ -144,21 +144,20 @@ fun IconsScreen() {
                                 adapter = adapter,
                                 modifier = Modifier.weight(1f).fillMaxHeight()
                             ) {
-                                val defaultColors = CardDefaults.cardColors()
-                                val cardColors = defaultColors.copy(
-                                    default = defaultColors.default.copy(
-                                        borderBrush = SolidColor(Color.Transparent)
-                                    ),
-                                    hovered = defaultColors.hovered.copy(
-                                        borderBrush = SolidColor(Color.Transparent)
-                                    ),
-                                    pressed = defaultColors.pressed.copy(
-                                        borderBrush = SolidColor(Color.Transparent)
-                                    ),
-                                    focused = defaultColors.focused.copy(
-                                        borderBrush = defaultColors.default.borderBrush
+
+                                val selectedColors = GridViewItemDefaults.selectedColors(
+                                    default = GridViewItemColor(
+                                        borderColor = FluentTheme.colors.fillAccent.default,
+                                        backgroundColor = FluentTheme.colors.subtleFill.transparent
                                     )
                                 )
+                                val defaultColors = GridViewItemDefaults.defaultColors(
+                                    hovered= GridViewItemColor(
+                                        borderColor = Color.Transparent,
+                                        backgroundColor = FluentTheme.colors.subtleFill.secondary
+                                    ),
+                                )
+
                                 LazyVerticalGrid(
                                     state = listState,
                                     columns = GridCells.Adaptive(96.dp),
@@ -173,20 +172,23 @@ fun IconsScreen() {
                                         key = { (name, _) -> name }
                                     ) { item ->
                                         val (name, icon) = item
-                                        val interactionSource =
-                                            remember { MutableInteractionSource() }
-                                        Card(
-                                            onClick = {
-                                                selectedItem.value = item
-                                            },
-                                            cardColors = if (selectedItem.value == item) {
-                                                defaultColors
+                                        val interactionSource = remember { MutableInteractionSource() }
+                                        GridViewItem(
+                                            selected = selectedItem.value == item,
+                                            onSelectedChange = { selectedItem.value = item },
+                                            interactionSource = interactionSource,
+                                            colors = if (selectedItem.value == item) {
+                                                selectedColors
                                             } else {
-                                                cardColors
+                                                defaultColors
                                             },
-                                            interactionSource = interactionSource,
-                                            modifier = Modifier.fillMaxWidth()
+                                            modifier = Modifier
+                                                .fillMaxWidth()
                                                 .aspectRatio(1f)
+                                                .background(
+                                                    color = FluentTheme.colors.background.card.default,
+                                                    shape = FluentTheme.shapes.control
+                                                )
                                         ) {
                                             val isHovered by interactionSource.collectIsHoveredAsState()
                                             Box(
@@ -216,7 +218,6 @@ fun IconsScreen() {
                                                         )
                                                 )
                                             }
-
                                         }
                                     }
                                 }
diff --git a/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/scrolling/PipsPagerScreen.kt b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/scrolling/PipsPagerScreen.kt
new file mode 100644
index 00000000..bc679774
--- /dev/null
+++ b/gallery/src/commonMain/kotlin/com/konyaco/fluent/gallery/screen/scrolling/PipsPagerScreen.kt
@@ -0,0 +1,159 @@
+package com.konyaco.fluent.gallery.screen.scrolling
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.pager.rememberPagerState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.konyaco.fluent.component.DropDownButton
+import com.konyaco.fluent.component.HorizontalFlipView
+import com.konyaco.fluent.component.HorizontalPipsPager
+import com.konyaco.fluent.component.MenuFlyoutContainer
+import com.konyaco.fluent.component.MenuFlyoutItem
+import com.konyaco.fluent.component.PageButtonVisibleStrategy
+import com.konyaco.fluent.component.Text
+import com.konyaco.fluent.component.VerticalPipsPager
+import com.konyaco.fluent.gallery.annotation.Component
+import com.konyaco.fluent.gallery.annotation.Sample
+import com.konyaco.fluent.gallery.component.ComponentPagePath
+import com.konyaco.fluent.gallery.component.GalleryPage
+import com.konyaco.fluent.gallery.screen.collections.randomBrushItems
+import com.konyaco.fluent.source.generated.FluentSourceFile
+
+@Component(description = "A control to let the user navigate through a paginated collection when the page numbers do not need to be visually known.")
+@Composable
+fun PipsPagerScreen() {
+    GalleryPage(
+        title = "PipsPager",
+        description = "A PipsPager allows the user to navigate through a paginated collection and is independent of the content shown. " +
+                "Use this control when the content in the layout is not explicitly ordered by relevancy or you desire a glyph-based representation of numbered pages. " +
+                "PipsPagers are commonly used in photo viewers, app lists, carousels, and when display space is limited.",
+        componentPath = FluentSourceFile.PipsPager,
+        galleryPath = ComponentPagePath.PipsPagerScreen
+    ) {
+        Section(
+            title = "PipsPager integrated with FlipView sample",
+            sourceCode = sourceCodeOfPipsPagerIntegratedWithFlipViewSample,
+            content = { PipsPagerIntegratedWithFlipViewSample() }
+        )
+
+        var isVertical = remember { mutableStateOf(false) }
+        var pageButtonVisibleStrategy = remember { mutableStateOf(PageButtonVisibleStrategy.Never) }
+        Section(
+            title = "PipsPager with options",
+            sourceCode = sourceCodeOfPipsPagerWithOptions,
+            content = {
+                PipsPagerWithOptions(
+                    isVertical = isVertical.value,
+                    pageButtonVisibleStrategy = pageButtonVisibleStrategy.value
+                )
+            },
+            options = {
+                Text("Orientation")
+                MenuFlyoutContainer(
+                    flyout = {
+                        MenuFlyoutItem(
+                            selected = isVertical.value,
+                            onSelectedChanged = {
+                                isVertical.value = true
+                                isFlyoutVisible = false
+                            },
+                            text = { Text("Vertical") },
+                        )
+                        MenuFlyoutItem(
+                            selected = !isVertical.value,
+                            onSelectedChanged = {
+                                isVertical.value = false
+                                isFlyoutVisible = false
+                            },
+                            text = { Text("Horizontal") }
+                        )
+                    },
+                    content = {
+                        DropDownButton(
+                            onClick = { isFlyoutVisible = true },
+                            content = { Text(if (isVertical.value) "Vertical" else "Horizontal") }
+                        )
+                    }
+                )
+                Text("Page button visibility")
+                MenuFlyoutContainer(
+                    flyout = {
+                        PageButtonVisibleStrategy.entries.forEach { item ->
+                            MenuFlyoutItem(
+                                selected = pageButtonVisibleStrategy.value == item,
+                                onSelectedChanged = {
+                                    pageButtonVisibleStrategy.value = item
+                                    isFlyoutVisible = false
+                                },
+                                text = { Text(item.name) }
+                            )
+                        }
+                    },
+                    content = {
+                        DropDownButton(
+                            onClick = { isFlyoutVisible = true },
+                            content = { Text(pageButtonVisibleStrategy.value.name) }
+                        )
+                    }
+                )
+            }
+        )
+    }
+}
+
+@Sample
+@Composable
+private fun PipsPagerIntegratedWithFlipViewSample() {
+    val items = randomBrushItems()
+    val pagerState = rememberPagerState { items.size }
+    Column(
+        horizontalAlignment = Alignment.CenterHorizontally
+    ) {
+        HorizontalFlipView(
+            state = pagerState,
+            modifier = Modifier
+                .widthIn(max = 400.dp)
+                .height(180.dp)
+        ) {
+            Box(modifier = Modifier.fillMaxSize().background(items[it]))
+        }
+        HorizontalPipsPager(state = pagerState)
+    }
+}
+
+@Sample
+@Composable
+private fun PipsPagerWithOptions(
+    isVertical: Boolean,
+    pageButtonVisibleStrategy: PageButtonVisibleStrategy
+) {
+    val count = 7
+    var selectedIndex by remember { mutableIntStateOf(0) }
+    if (isVertical) {
+        VerticalPipsPager(
+            selectedIndex = selectedIndex,
+            onSelectedIndexChange = { selectedIndex = it },
+            count = count,
+            pageButtonVisibleStrategy = pageButtonVisibleStrategy
+        )
+    } else {
+        HorizontalPipsPager(
+            selectedIndex = selectedIndex,
+            onSelectedIndexChange = { selectedIndex = it },
+            count = count,
+            pageButtonVisibleStrategy = pageButtonVisibleStrategy
+        )
+    }
+}
\ No newline at end of file