diff --git a/.changeset/edit-intellisenseeventicon.md b/.changeset/edit-intellisenseeventicon.md
new file mode 100644
index 000000000..6706a9ba0
--- /dev/null
+++ b/.changeset/edit-intellisenseeventicon.md
@@ -0,0 +1,5 @@
+---
+'@vapor-ui/icons': patch
+---
+
+Check the center alignment of the IntelliSenseEventIcon
diff --git a/.changeset/shiny-skeleton-loading.md b/.changeset/shiny-skeleton-loading.md
new file mode 100644
index 000000000..a7ec6cd71
--- /dev/null
+++ b/.changeset/shiny-skeleton-loading.md
@@ -0,0 +1,7 @@
+---
+'@vapor-ui/core': minor
+---
+
+New Skeleton component
+
+You can see more details about the Skeleton component in the [Skeleton documentation](https://vapor-ui.goorm.io/docs/components/skeleton).
diff --git a/apps/website/content/docs/components/skeleton.mdx b/apps/website/content/docs/components/skeleton.mdx
new file mode 100644
index 000000000..4ec8f6a29
--- /dev/null
+++ b/apps/website/content/docs/components/skeleton.mdx
@@ -0,0 +1,82 @@
+---
+title: 'Skeleton'
+site_name: 'Skeleton - Vapor Core'
+description: '콘텐츠가 로딩 중임을 나타내는 플레이스홀더 요소입니다.'
+---
+
+
+```json doc-gen:file
+{
+ "file": "./src/components/demo/examples/skeleton/default-skeleton.tsx",
+ "codeblock": true
+}
+```
+
+
+## Property
+
+---
+
+### Shape
+
+Skeleton의 모서리 둥글기를 설정합니다.
+
+
+```json doc-gen:file
+{
+ "file": "./src/components/demo/examples/skeleton/skeleton-shape.tsx",
+ "codeblock": true
+}
+```
+
+
+### Animation
+
+Skeleton의 애니메이션 스타일을 설정합니다.
+
+
+```json doc-gen:file
+{
+ "file": "./src/components/demo/examples/skeleton/skeleton-animation.tsx",
+ "codeblock": true
+}
+```
+
+
+### Size
+
+Skeleton의 높이를 설정합니다.
+
+
+```json doc-gen:file
+{
+ "file": "./src/components/demo/examples/skeleton/skeleton-size.tsx",
+ "codeblock": true
+}
+```
+
+
+## Examples
+
+---
+
+### Profile Card
+
+여러 Skeleton 요소를 조합하여 프로필 카드 플레이스홀더를 구성하는 예제입니다.
+
+
+```json doc-gen:file
+{
+ "file": "./src/components/demo/examples/skeleton/skeleton-composition.tsx",
+ "codeblock": true
+}
+```
+
+
+## Props Table
+
+---
+
+### Skeleton
+
+
diff --git a/apps/website/public/components/generated/skeleton.json b/apps/website/public/components/generated/skeleton.json
new file mode 100644
index 000000000..305751cc1
--- /dev/null
+++ b/apps/website/public/components/generated/skeleton.json
@@ -0,0 +1,35 @@
+{
+ "name": "Skeleton",
+ "displayName": "Skeleton",
+ "description": "콘텐츠가 로딩 중임을 나타내는 placeholder 요소입니다. `
` 요소를 렌더링합니다.",
+ "props": [
+ {
+ "name": "animation",
+ "type": ["none", "shimmer", "pulse"],
+ "required": false,
+ "description": "skeleton의 애니메이션 스타일을 제어합니다.\n\n- `shimmer`: 빛이 스치는 효과\n- `pulse`: 깜빡이는 효과\n- `none`: 애니메이션 비활성화",
+ "defaultValue": "shimmer"
+ },
+ {
+ "name": "shape",
+ "type": ["square", "rounded"],
+ "required": false,
+ "description": "skeleton의 테두리 반경을 제어합니다.",
+ "defaultValue": "rounded"
+ },
+ {
+ "name": "size",
+ "type": ["sm", "md", "lg", "xl"],
+ "required": false,
+ "description": "skeleton의 높이를 제어합니다.",
+ "defaultValue": "md"
+ },
+ {
+ "name": "render",
+ "type": ["ReactElement", "(props: HTMLProps) => ReactElement"],
+ "required": false,
+ "description": "component의 HTML element를 다른 태그로 교체하거나 다른 component와 조합할 수 있습니다.\n\nReactElement 또는 렌더링할 요소를 반환하는 함수를 받습니다."
+ }
+ ],
+ "defaultElement": "div"
+}
diff --git a/apps/website/src/components/demo/examples/skeleton/default-skeleton.tsx b/apps/website/src/components/demo/examples/skeleton/default-skeleton.tsx
new file mode 100644
index 000000000..1c8e87f91
--- /dev/null
+++ b/apps/website/src/components/demo/examples/skeleton/default-skeleton.tsx
@@ -0,0 +1,5 @@
+import { Skeleton } from '@vapor-ui/core';
+
+export default function DefaultSkeleton() {
+ return
;
+}
diff --git a/apps/website/src/components/demo/examples/skeleton/skeleton-animation.tsx b/apps/website/src/components/demo/examples/skeleton/skeleton-animation.tsx
new file mode 100644
index 000000000..3b6b3b342
--- /dev/null
+++ b/apps/website/src/components/demo/examples/skeleton/skeleton-animation.tsx
@@ -0,0 +1,26 @@
+import { HStack, Skeleton, Text, VStack } from '@vapor-ui/core';
+
+export default function SkeletonAnimation() {
+ return (
+
+
+
+ shimmer
+
+
+
+
+
+ pulse
+
+
+
+
+
+ none
+
+
+
+
+ );
+}
diff --git a/apps/website/src/components/demo/examples/skeleton/skeleton-composition.tsx b/apps/website/src/components/demo/examples/skeleton/skeleton-composition.tsx
new file mode 100644
index 000000000..f9f67c69b
--- /dev/null
+++ b/apps/website/src/components/demo/examples/skeleton/skeleton-composition.tsx
@@ -0,0 +1,28 @@
+import { Box, HStack, Skeleton, VStack } from '@vapor-ui/core';
+
+export default function SkeletonComposition() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/website/src/components/demo/examples/skeleton/skeleton-shape.tsx b/apps/website/src/components/demo/examples/skeleton/skeleton-shape.tsx
new file mode 100644
index 000000000..a8350aa94
--- /dev/null
+++ b/apps/website/src/components/demo/examples/skeleton/skeleton-shape.tsx
@@ -0,0 +1,20 @@
+import { HStack, Skeleton, Text, VStack } from '@vapor-ui/core';
+
+export default function SkeletonShape() {
+ return (
+
+
+
+ rounded
+
+
+
+
+
+ square
+
+
+
+
+ );
+}
diff --git a/apps/website/src/components/demo/examples/skeleton/skeleton-size.tsx b/apps/website/src/components/demo/examples/skeleton/skeleton-size.tsx
new file mode 100644
index 000000000..e5325ebd0
--- /dev/null
+++ b/apps/website/src/components/demo/examples/skeleton/skeleton-size.tsx
@@ -0,0 +1,32 @@
+import { HStack, Skeleton, Text, VStack } from '@vapor-ui/core';
+
+export default function SkeletonSize() {
+ return (
+
+
+
+ sm
+
+
+
+
+
+ md
+
+
+
+
+
+ lg
+
+
+
+
+
+ xl
+
+
+
+
+ );
+}
diff --git a/packages/core/__tests__/screenshots/skeleton--test-bed-1-chrome-darwin-.png b/packages/core/__tests__/screenshots/skeleton--test-bed-1-chrome-darwin-.png
new file mode 100644
index 000000000..b55c26917
Binary files /dev/null and b/packages/core/__tests__/screenshots/skeleton--test-bed-1-chrome-darwin-.png differ
diff --git a/packages/core/__tests__/screenshots/skeleton--test-bed-1-edge-darwin-.png b/packages/core/__tests__/screenshots/skeleton--test-bed-1-edge-darwin-.png
new file mode 100644
index 000000000..b55c26917
Binary files /dev/null and b/packages/core/__tests__/screenshots/skeleton--test-bed-1-edge-darwin-.png differ
diff --git a/packages/core/__tests__/screenshots/skeleton--test-bed-1-firefox-darwin-.png b/packages/core/__tests__/screenshots/skeleton--test-bed-1-firefox-darwin-.png
new file mode 100644
index 000000000..6524be814
Binary files /dev/null and b/packages/core/__tests__/screenshots/skeleton--test-bed-1-firefox-darwin-.png differ
diff --git a/packages/core/__tests__/screenshots/skeleton--test-bed-1-safari-darwin-.png b/packages/core/__tests__/screenshots/skeleton--test-bed-1-safari-darwin-.png
new file mode 100644
index 000000000..e28a80e89
Binary files /dev/null and b/packages/core/__tests__/screenshots/skeleton--test-bed-1-safari-darwin-.png differ
diff --git a/packages/core/src/components/skeleton/index.ts b/packages/core/src/components/skeleton/index.ts
new file mode 100644
index 000000000..25c51ad04
--- /dev/null
+++ b/packages/core/src/components/skeleton/index.ts
@@ -0,0 +1 @@
+export * from './skeleton';
diff --git a/packages/core/src/components/skeleton/skeleton.css.ts b/packages/core/src/components/skeleton/skeleton.css.ts
new file mode 100644
index 000000000..9bdd1e1d4
--- /dev/null
+++ b/packages/core/src/components/skeleton/skeleton.css.ts
@@ -0,0 +1,79 @@
+import { keyframes } from '@vanilla-extract/css';
+import type { RecipeVariants } from '@vanilla-extract/recipes';
+import { recipe } from '@vanilla-extract/recipes';
+
+import { layerStyle } from '~/styles/mixins/layer-style.css';
+import { vars } from '~/styles/themes.css';
+
+const shimmerKeyframes = keyframes({
+ to: { backgroundPosition: 'right -6.25rem top 0' },
+});
+
+const pulseKeyframes = keyframes({
+ '0%, 100%': { opacity: 1 },
+ '50%': { opacity: 0.5 },
+});
+
+/**
+ * Style variants for the Skeleton component.
+ */
+export const root = recipe({
+ base: layerStyle('components', {
+ display: 'block',
+ overflow: 'hidden',
+ width: '100%',
+ backgroundColor: vars.color.gray['100'],
+
+ '@media': {
+ '(prefers-reduced-motion: reduce)': {
+ animation: 'none',
+ },
+ },
+ }),
+
+ defaultVariants: { shape: 'rounded', size: 'md', animation: 'shimmer' },
+ variants: {
+ /**
+ * Controls the border radius of the skeleton.
+ */
+ shape: {
+ rounded: layerStyle('components', {
+ borderRadius: '9999px',
+ }),
+ square: layerStyle('components', {
+ borderRadius: vars.size.borderRadius['300'],
+ }),
+ },
+
+ /**
+ * Controls the height of the skeleton.
+ */
+ size: {
+ sm: layerStyle('components', { height: vars.size.dimension['200'] }),
+ md: layerStyle('components', { height: vars.size.dimension['300'] }),
+ lg: layerStyle('components', { height: vars.size.dimension['400'] }),
+ xl: layerStyle('components', { height: vars.size.dimension['500'] }),
+ },
+
+ /**
+ * Controls the animation style of the skeleton.
+ */
+ animation: {
+ shimmer: layerStyle('components', {
+ backgroundImage: `linear-gradient(90deg, ${vars.color.gray['100']}, ${vars.color.gray['050']}, ${vars.color.gray['100']})`,
+ backgroundPosition: 'left -6.25rem top 0',
+ backgroundSize: '6.25rem 100%',
+ backgroundRepeat: 'no-repeat',
+ animation: `${shimmerKeyframes} 1s ease-in-out infinite`,
+ }),
+ pulse: layerStyle('components', {
+ animation: `${pulseKeyframes} 1.2s ease-in-out infinite`,
+ }),
+ none: layerStyle('components', {
+ animation: 'none',
+ }),
+ },
+ },
+});
+
+export type SkeletonVariants = NonNullable
>;
diff --git a/packages/core/src/components/skeleton/skeleton.stories.tsx b/packages/core/src/components/skeleton/skeleton.stories.tsx
new file mode 100644
index 000000000..01888001e
--- /dev/null
+++ b/packages/core/src/components/skeleton/skeleton.stories.tsx
@@ -0,0 +1,80 @@
+import type { Meta, StoryObj } from '@storybook/react-vite';
+
+import { Box } from '../box';
+import { HStack } from '../h-stack';
+import { VStack } from '../v-stack';
+import { Skeleton } from './skeleton';
+
+export default {
+ title: 'Skeleton',
+ argTypes: {
+ shape: { control: 'inline-radio', options: ['rounded', 'square'] },
+ size: { control: 'inline-radio', options: ['sm', 'md', 'lg', 'xl'] },
+ animation: { control: 'inline-radio', options: ['shimmer', 'pulse', 'none'] },
+ },
+} as Meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ render: (args) => ,
+};
+
+export const TestBed: Story = {
+ render: () => (
+
+
+ Shape
+
+
+
+
+
+
+
+
+ Size
+
+
+
+
+
+
+
+
+
+ Animation
+
+
+
+
+
+
+
+
+ Composition Example - Profile Card
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ),
+};
diff --git a/packages/core/src/components/skeleton/skeleton.test.tsx b/packages/core/src/components/skeleton/skeleton.test.tsx
new file mode 100644
index 000000000..e9f791fa4
--- /dev/null
+++ b/packages/core/src/components/skeleton/skeleton.test.tsx
@@ -0,0 +1,13 @@
+import { render } from '@testing-library/react';
+import { axe } from 'vitest-axe';
+
+import { Skeleton } from './skeleton';
+
+describe('Skeleton', () => {
+ it('should have no a11y violations', async () => {
+ const rendered = render();
+ const result = await axe(rendered.container);
+
+ expect(result).toHaveNoViolations();
+ });
+});
diff --git a/packages/core/src/components/skeleton/skeleton.tsx b/packages/core/src/components/skeleton/skeleton.tsx
new file mode 100644
index 000000000..f0f8ff757
--- /dev/null
+++ b/packages/core/src/components/skeleton/skeleton.tsx
@@ -0,0 +1,39 @@
+import { forwardRef } from 'react';
+
+import { useRender } from '@base-ui/react/use-render';
+import clsx from 'clsx';
+
+import { createSplitProps } from '~/utils/create-split-props';
+import { resolveStyles } from '~/utils/resolve-styles';
+import type { VComponentProps } from '~/utils/types';
+
+import type { SkeletonVariants } from './skeleton.css';
+import * as styles from './skeleton.css';
+
+/**
+ * A placeholder element that indicates content is loading. Renders a `` element.
+ */
+export const Skeleton = forwardRef
((props, ref) => {
+ const { render, className, ...componentProps } = resolveStyles(props);
+ const [variantsProps, otherProps] = createSplitProps()(componentProps, [
+ 'shape',
+ 'size',
+ 'animation',
+ ]);
+
+ return useRender({
+ ref,
+ render: render || ,
+ props: {
+ className: clsx(styles.root(variantsProps), className),
+ ...otherProps,
+ },
+ });
+});
+Skeleton.displayName = 'Skeleton';
+
+export namespace Skeleton {
+ type SkeletonPrimitiveProps = VComponentProps<'div'>;
+
+ export interface Props extends SkeletonPrimitiveProps, SkeletonVariants {}
+}
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 046bd77b5..0469061f9 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -26,6 +26,7 @@ export * from './components/radio';
export * from './components/radio-group';
export * from './components/select';
export * from './components/sheet';
+export * from './components/skeleton';
export * from './components/switch';
export * from './components/table';
export * from './components/tabs';