diff --git a/.changeset/add-skeleton-recipe.md b/.changeset/add-skeleton-recipe.md new file mode 100644 index 00000000..98713eee --- /dev/null +++ b/.changeset/add-skeleton-recipe.md @@ -0,0 +1,14 @@ +--- +"@styleframe/theme": minor +"styleframe": minor +"@styleframe/core": patch +--- + +Add Skeleton recipe with pulse animation and supporting utilities + +- Add `useSkeletonRecipe` with size (`xs`, `sm`, `md`, `lg`, `xl`) and rounded (`true`, `false`) variants, pulse animation, and dark mode support +- Add granular animation utilities: `useAnimationNameUtility`, `useAnimationDurationUtility`, `useAnimationTimingFunctionUtility`, `useAnimationIterationCountUtility` +- Switch `useWidthUtility` and `useHeightUtility` to `createUseSpacingUtility` for `@N` multiplier support +- Add compound keyframe selector support in core engine (e.g. `"0%, 100%"`) +- Add Skeleton storybook component, grid previews, and stories +- Add Skeleton documentation page diff --git a/apps/docs/content/docs/06.components/02.composables/08.skeleton.md b/apps/docs/content/docs/06.components/02.composables/08.skeleton.md new file mode 100644 index 00000000..7eb87aa6 --- /dev/null +++ b/apps/docs/content/docs/06.components/02.composables/08.skeleton.md @@ -0,0 +1,358 @@ +--- +title: Skeleton +description: A loading placeholder component that displays a pulsing gray block to indicate content is being loaded. Supports multiple sizes and a rounded option through the recipe system. +--- + +## Overview + +The **Skeleton** is a loading placeholder element used to indicate that content is being fetched or processed. The `useSkeletonRecipe()` composable creates a fully configured [recipe](/docs/api/recipes) with size and rounded options, plus a built-in pulse animation via keyframes — no additional CSS required. + +The Skeleton recipe integrates directly with the default [design tokens preset](/docs/design-tokens/presets) and generates type-safe utility classes at build time with zero runtime CSS. + +## Why use the Skeleton recipe? + +The Skeleton recipe helps you: + +- **Ship faster with sensible defaults**: Get 5 sizes and a rounded option out of the box with a single composable call. +- **Animate without extra CSS**: The pulse keyframes animation is registered automatically when you use the recipe — no manual `@keyframes` definition needed. +- **Maintain consistency**: All skeleton placeholders share the same animation timing, colors, and border radius across your application. +- **Customize without forking**: Override base styles, default variants, or filter out options you don't need — all through the options API. +- **Stay type-safe**: Full TypeScript support means your editor catches invalid size values at compile time. +- **Integrate with your tokens**: Every value references the design tokens preset, so theme changes propagate automatically. +- **Support dark mode**: Background colors adapt automatically between light and dark color schemes. + +## Usage + +::steps{level="4"} + +#### Register the recipe + +Add the Skeleton recipe to a local Styleframe instance. The global `styleframe.config.ts` provides design tokens and utilities, while the component-level file registers the recipe itself: + +:::code-tree{default-value="src/components/skeleton.styleframe.ts"} + +```ts [src/components/skeleton.styleframe.ts] +import { styleframe } from 'virtual:styleframe'; +import { useSkeletonRecipe } from '@styleframe/theme'; + +const s = styleframe(); + +const skeleton = useSkeletonRecipe(s); + +export default s; +``` + +```ts [styleframe.config.ts] +import { styleframe } from 'styleframe'; +import { useDesignTokensPreset, useUtilitiesPreset } from '@styleframe/theme'; + +const s = styleframe(); + +useDesignTokensPreset(s); +useUtilitiesPreset(s); + +export default s; +``` + +::: + +#### Build the component + +Import the `skeleton` runtime function from the virtual module and pass variant props to compute class names: + +:::tabs +::::tabs-item{icon="i-devicon-react" label="React"} + +```ts [src/components/Skeleton.tsx] +import { skeleton } from "virtual:styleframe"; + +interface SkeletonProps { + size?: "xs" | "sm" | "md" | "lg" | "xl"; + rounded?: boolean; + className?: string; +} + +export function Skeleton({ + size = "md", + rounded = false, + className, +}: SkeletonProps) { + return ( +