A design system in a box. hip-ui.tngl.io/docs/introduction
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

skeleton

+503 -4
+1
.cursor/rules/hip.mdc
··· 7 7 - Use components from @apps/docs/src/components/ 8 8 - Prefer to use the @apps/docs/src/components/flex/index.tsx or @apps/docs/src/components/grid/index.tsx for layout 9 9 - Dont write custom styles for text, instead use the @apps/docs/src/components/typography/text.tsx component. 10 + - Always document props interfaces with jsdoc 10 11 11 12 ## Custom styles 12 13
+2 -2
README.md
··· 6 6 7 7 - [ ] Add Virtualizer 8 8 - [ ] add borderless variant for input fields 9 - - [ ] Field erros 10 9 - [ ] perfect inset border radii 11 10 12 11 ### Components ··· 22 21 - [ ] Empty 23 22 - [ ] Input OTP 24 23 - [ ] Resizable 25 - - [ ] Skeleton 26 24 27 25 #### OTher Wrappers 28 26 ··· 41 39 42 40 ### Done 43 41 42 + - [x] Skeleton 43 + - [x] Field errors 44 44 - [x] Window Splitter 45 45 - [x] Toast 46 46 - [x] Drawer
+21 -2
apps/docs/eslint.config.mjs
··· 1 + import { config as reactInternalConfig } from "@repo/eslint-config/react-internal"; 2 + import { createTypeScriptImportResolver } from "eslint-import-resolver-typescript"; 3 + import { defineConfig } from "eslint/config"; 4 + import path from "node:path"; 5 + import { fileURLToPath } from "node:url"; 6 + 7 + const __dirname = path.dirname(fileURLToPath(import.meta.url)); 8 + 1 9 /** @type {import("eslint").Linter.Config} */ 2 - 3 - export { config as default } from "@repo/eslint-config/react-internal"; 10 + export default defineConfig([ 11 + ...reactInternalConfig, 12 + { 13 + settings: { 14 + "import-x/resolver": [ 15 + createTypeScriptImportResolver({ 16 + alwaysTryTypes: true, 17 + project: path.resolve(__dirname, "./tsconfig.json"), 18 + }), 19 + ], 20 + }, 21 + }, 22 + ]);
+155
apps/docs/src/components/skeleton/index.tsx
··· 1 + "use client"; 2 + 3 + import * as stylex from "@stylexjs/stylex"; 4 + 5 + import { radius } from "../theme/radius.stylex"; 6 + import { uiColor } from "../theme/semantic-color.stylex"; 7 + import { shadow } from "../theme/shadow.stylex"; 8 + import { spacing } from "../theme/spacing.stylex"; 9 + import { Size, StyleXComponentProps } from "../theme/types"; 10 + 11 + const shimmer = stylex.keyframes({ 12 + "0%": { 13 + transform: "translateX(-65%)", 14 + }, 15 + "100%": { 16 + transform: "translateX(0%)", 17 + }, 18 + }); 19 + 20 + const styles = stylex.create({ 21 + group: {}, 22 + base: { 23 + backgroundColor: uiColor.component1, 24 + boxShadow: shadow["insetSm"], 25 + overflow: "hidden", 26 + position: "relative", 27 + }, 28 + shimmer: { 29 + // eslint-disable-next-line @stylexjs/valid-styles 30 + animationDuration: "1.7s", 31 + animationIterationCount: "infinite", 32 + animationName: shimmer, 33 + animationTimingFunction: "linear", 34 + backgroundImage: `linear-gradient( 35 + -80deg, 36 + transparent 0%, 37 + transparent 30%, 38 + ${uiColor.component3} 50%, 39 + transparent 70%, 40 + transparent 100% 41 + )`, 42 + backgroundSize: "100%", 43 + bottom: 0, 44 + left: 0, 45 + position: "absolute", 46 + right: 0, 47 + top: 0, 48 + width: "300%", 49 + }, 50 + circle: { 51 + borderRadius: radius.full, 52 + }, 53 + rectangle: { 54 + borderRadius: radius.md, 55 + }, 56 + sizeSm: { 57 + height: spacing["8"], 58 + width: spacing["8"], 59 + }, 60 + sizeMd: { 61 + height: spacing["12"], 62 + width: spacing["12"], 63 + }, 64 + sizeLg: { 65 + height: spacing["16"], 66 + width: spacing["16"], 67 + }, 68 + height: (height: string | undefined) => ({ 69 + height: height || spacing["4"], 70 + }), 71 + width: (width: string | undefined) => ({ 72 + width: width || "100%", 73 + }), 74 + }); 75 + 76 + export interface SkeletonGroupProps 77 + extends StyleXComponentProps<React.ComponentProps<"div">> { 78 + children: React.ReactNode; 79 + } 80 + 81 + export function SkeletonGroup({ 82 + children, 83 + style, 84 + ...props 85 + }: SkeletonGroupProps) { 86 + return ( 87 + <div {...props} {...stylex.props(styles.group, style)}> 88 + {children} 89 + </div> 90 + ); 91 + } 92 + 93 + export type SkeletonVariant = "circle" | "rectangle"; 94 + 95 + interface SkeletonBaseProps 96 + extends StyleXComponentProps<React.ComponentProps<"div">> {} 97 + 98 + interface SkeletonCircleProps extends SkeletonBaseProps { 99 + variant: "circle"; 100 + size: Size; 101 + height?: never; 102 + width?: string; 103 + } 104 + 105 + interface SkeletonRectangleProps extends SkeletonBaseProps { 106 + variant: "rectangle"; 107 + height?: string; 108 + size?: never; 109 + width?: string; 110 + } 111 + 112 + type SkeletonProps = SkeletonCircleProps | SkeletonRectangleProps; 113 + 114 + export function Skeleton({ 115 + variant, 116 + size, 117 + height, 118 + width, 119 + style, 120 + ...props 121 + }: SkeletonProps) { 122 + if (variant === "circle") { 123 + return ( 124 + <div 125 + {...props} 126 + {...stylex.props( 127 + styles.base, 128 + styles.circle, 129 + size === "sm" && styles.sizeSm, 130 + size === "md" && styles.sizeMd, 131 + size === "lg" && styles.sizeLg, 132 + width ? styles.width(width) : null, 133 + style, 134 + )} 135 + > 136 + <div {...stylex.props(styles.shimmer)} /> 137 + </div> 138 + ); 139 + } 140 + 141 + return ( 142 + <div 143 + {...props} 144 + {...stylex.props( 145 + styles.base, 146 + styles.rectangle, 147 + styles.height(height), 148 + styles.width(width), 149 + style, 150 + )} 151 + > 152 + <div {...stylex.props(styles.shimmer)} /> 153 + </div> 154 + ); 155 + }
+4
apps/docs/src/components/theme/shadow.stylex.tsx
··· 9 9 xl: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)", 10 10 "2xl": "0 25px 50px -12px rgb(0 0 0 / 0.25)", 11 11 none: "0 0 #0000", 12 + inset: "inset 0 0 4px 0 rgb(0 0 0 / 0.1)", 13 + insetSm: "inset 0 0 4px 0 rgb(0 0 0 / 0.05)", 14 + insetMd: "inset 0 0 4px 0 rgb(0 0 0 / 0.1)", 15 + insetLg: "inset 0 0 8px 0 rgb(0 0 0 / 0.15)", 12 16 });
+53
apps/docs/src/docs/components/skeleton.mdx
··· 1 + --- 2 + title: Skeleton 3 + description: A loading placeholder component with circle and rectangle variants. 4 + --- 5 + 6 + import { PropDocs } from '../../lib/PropDocs' 7 + import { Example } from '../../lib/Example' 8 + import { Basic } from '../../examples/skeleton/basic' 9 + import { Variants } from '../../examples/skeleton/variants' 10 + import { CircleSizes } from '../../examples/skeleton/circle-sizes' 11 + import { RectangleHeight } from '../../examples/skeleton/rectangle-height' 12 + 13 + <Example src={Basic} /> 14 + 15 + ## Installation 16 + 17 + Run the following command to add the skeleton component to your project. 18 + 19 + ```bash 20 + pnpm hip install skeleton 21 + ``` 22 + 23 + ## Props 24 + 25 + This is a custom component built for loading placeholders. 26 + 27 + <PropDocs components={["Skeleton", "SkeletonGroup"]} /> 28 + 29 + ## Features 30 + 31 + ### Variants 32 + 33 + The skeleton supports two variants: circle and rectangle. 34 + 35 + <Example src={Variants} /> 36 + 37 + ### Circle Sizes 38 + 39 + When using the circle variant, you must provide a size prop. The skeleton supports three sizes: sm, md, and lg. 40 + 41 + <Example src={CircleSizes} /> 42 + 43 + ### Rectangle Height 44 + 45 + When using the rectangle variant, you can optionally specify a height. If no height is provided, the skeleton will take the full width of its container. 46 + 47 + <Example src={RectangleHeight} /> 48 + 49 + ## Related Components 50 + 51 + - [Spinner](/docs/components/spinner) - For loading indicators 52 + - [ProgressBar](/docs/components/progress-bar) - For progress indicators 53 +
+25
apps/docs/src/examples/skeleton/basic.tsx
··· 1 + import { Flex } from "@/components/flex"; 2 + import { Skeleton, SkeletonGroup } from "@/components/skeleton"; 3 + import { spacing } from "@/components/theme/spacing.stylex"; 4 + 5 + export function Basic() { 6 + return ( 7 + <SkeletonGroup> 8 + <Flex direction="column" gap="4"> 9 + <Flex align="center" gap="3"> 10 + <Skeleton variant="circle" size="md" /> 11 + <Flex direction="column" gap="2"> 12 + <Skeleton variant="rectangle" height={spacing["4"]} width="80px" /> 13 + <Skeleton variant="rectangle" height={spacing["3"]} width="120px" /> 14 + </Flex> 15 + </Flex> 16 + <Flex direction="column" gap="2"> 17 + <Skeleton variant="rectangle" height={spacing["3"]} width="100%" /> 18 + <Skeleton variant="rectangle" height={spacing["3"]} width="100%" /> 19 + <Skeleton variant="rectangle" height={spacing["3"]} width="100%" /> 20 + <Skeleton variant="rectangle" height={spacing["3"]} width="120px" /> 21 + </Flex> 22 + </Flex> 23 + </SkeletonGroup> 24 + ); 25 + }
+22
apps/docs/src/examples/skeleton/circle-sizes.tsx
··· 1 + import { Flex } from "@/components/flex"; 2 + import { Skeleton } from "@/components/skeleton"; 3 + import { Text } from "@/components/typography/text"; 4 + 5 + export function CircleSizes() { 6 + return ( 7 + <Flex gap="8" align="center"> 8 + <Flex direction="column" gap="2" align="center"> 9 + <Text weight="semibold">Small</Text> 10 + <Skeleton variant="circle" size="sm" /> 11 + </Flex> 12 + <Flex direction="column" gap="2" align="center"> 13 + <Text weight="semibold">Medium</Text> 14 + <Skeleton variant="circle" size="md" /> 15 + </Flex> 16 + <Flex direction="column" gap="2" align="center"> 17 + <Text weight="semibold">Large</Text> 18 + <Skeleton variant="circle" size="lg" /> 19 + </Flex> 20 + </Flex> 21 + ); 22 + }
+27
apps/docs/src/examples/skeleton/rectangle-height.tsx
··· 1 + import { Flex } from "@/components/flex"; 2 + import { Skeleton } from "@/components/skeleton"; 3 + import { Text } from "@/components/typography/text"; 4 + import { spacing } from "@/components/theme/spacing.stylex"; 5 + 6 + export function RectangleHeight() { 7 + return ( 8 + <Flex direction="column" gap="4"> 9 + <Flex direction="column" gap="2"> 10 + <Text weight="semibold">Default height (no height prop)</Text> 11 + <Skeleton variant="rectangle" width="300px" /> 12 + </Flex> 13 + <Flex direction="column" gap="2"> 14 + <Text weight="semibold">Custom height: spacing["4"]</Text> 15 + <Skeleton variant="rectangle" height={spacing["4"]} width="300px" /> 16 + </Flex> 17 + <Flex direction="column" gap="2"> 18 + <Text weight="semibold">Custom height: spacing["8"]</Text> 19 + <Skeleton variant="rectangle" height={spacing["8"]} width="300px" /> 20 + </Flex> 21 + <Flex direction="column" gap="2"> 22 + <Text weight="semibold">Custom height: spacing["12"]</Text> 23 + <Skeleton variant="rectangle" height={spacing["12"]} width="300px" /> 24 + </Flex> 25 + </Flex> 26 + ); 27 + }
+19
apps/docs/src/examples/skeleton/variants.tsx
··· 1 + import { Flex } from "@/components/flex"; 2 + import { Skeleton } from "@/components/skeleton"; 3 + import { spacing } from "@/components/theme/spacing.stylex"; 4 + import { Text } from "@/components/typography/text"; 5 + 6 + export function Variants() { 7 + return ( 8 + <Flex gap="8" align="center"> 9 + <Flex direction="column" gap="2"> 10 + <Text weight="semibold">Circle</Text> 11 + <Skeleton variant="circle" size="md" /> 12 + </Flex> 13 + <Flex direction="column" gap="2"> 14 + <Text weight="semibold">Rectangle</Text> 15 + <Skeleton variant="rectangle" height={spacing["8"]} width="150px" /> 16 + </Flex> 17 + </Flex> 18 + ); 19 + }
+2
packages/hip-ui/src/cli/install.tsx
··· 60 60 import { segmentedControlConfig } from "../components/segmented-control/segmented-control-config.js"; 61 61 import { selectConfig } from "../components/select/select-config.js"; 62 62 import { separatorConfig } from "../components/separator/separator-config.js"; 63 + import { skeletonConfig } from "../components/skeleton/skeleton-config.js"; 63 64 import { sidebarConfig } from "../components/sidebar/sidebar-config.js"; 64 65 import { sliderConfig } from "../components/slider/slider-config.js"; 65 66 import { switchConfig } from "../components/switch/switch-config.js"; ··· 97 98 checkboxConfig, 98 99 radioConfig, 99 100 separatorConfig, 101 + skeletonConfig, 100 102 textAreaConfig, 101 103 selectConfig, 102 104 toggleButtonConfig,
+155
packages/hip-ui/src/components/skeleton/index.tsx
··· 1 + "use client"; 2 + 3 + import * as stylex from "@stylexjs/stylex"; 4 + 5 + import { radius } from "../theme/radius.stylex"; 6 + import { uiColor } from "../theme/semantic-color.stylex"; 7 + import { shadow } from "../theme/shadow.stylex"; 8 + import { spacing } from "../theme/spacing.stylex"; 9 + import { Size, StyleXComponentProps } from "../theme/types"; 10 + 11 + const shimmer = stylex.keyframes({ 12 + "0%": { 13 + transform: "translateX(-65%)", 14 + }, 15 + "100%": { 16 + transform: "translateX(0%)", 17 + }, 18 + }); 19 + 20 + const styles = stylex.create({ 21 + group: {}, 22 + base: { 23 + backgroundColor: uiColor.component1, 24 + boxShadow: shadow["insetSm"], 25 + overflow: "hidden", 26 + position: "relative", 27 + }, 28 + shimmer: { 29 + // eslint-disable-next-line @stylexjs/valid-styles 30 + animationDuration: "1.7s", 31 + animationIterationCount: "infinite", 32 + animationName: shimmer, 33 + animationTimingFunction: "linear", 34 + backgroundImage: `linear-gradient( 35 + -80deg, 36 + transparent 0%, 37 + transparent 30%, 38 + ${uiColor.component3} 50%, 39 + transparent 70%, 40 + transparent 100% 41 + )`, 42 + backgroundSize: "100%", 43 + bottom: 0, 44 + left: 0, 45 + position: "absolute", 46 + right: 0, 47 + top: 0, 48 + width: "300%", 49 + }, 50 + circle: { 51 + borderRadius: radius.full, 52 + }, 53 + rectangle: { 54 + borderRadius: radius.md, 55 + }, 56 + sizeSm: { 57 + height: spacing["8"], 58 + width: spacing["8"], 59 + }, 60 + sizeMd: { 61 + height: spacing["12"], 62 + width: spacing["12"], 63 + }, 64 + sizeLg: { 65 + height: spacing["16"], 66 + width: spacing["16"], 67 + }, 68 + height: (height: string | undefined) => ({ 69 + height: height || spacing["4"], 70 + }), 71 + width: (width: string | undefined) => ({ 72 + width: width || "100%", 73 + }), 74 + }); 75 + 76 + export interface SkeletonGroupProps 77 + extends StyleXComponentProps<React.ComponentProps<"div">> { 78 + children: React.ReactNode; 79 + } 80 + 81 + export function SkeletonGroup({ 82 + children, 83 + style, 84 + ...props 85 + }: SkeletonGroupProps) { 86 + return ( 87 + <div {...props} {...stylex.props(styles.group, style)}> 88 + {children} 89 + </div> 90 + ); 91 + } 92 + 93 + export type SkeletonVariant = "circle" | "rectangle"; 94 + 95 + interface SkeletonBaseProps 96 + extends StyleXComponentProps<React.ComponentProps<"div">> {} 97 + 98 + interface SkeletonCircleProps extends SkeletonBaseProps { 99 + variant: "circle"; 100 + size: Size; 101 + height?: never; 102 + width?: string; 103 + } 104 + 105 + interface SkeletonRectangleProps extends SkeletonBaseProps { 106 + variant: "rectangle"; 107 + height?: string; 108 + size?: never; 109 + width?: string; 110 + } 111 + 112 + type SkeletonProps = SkeletonCircleProps | SkeletonRectangleProps; 113 + 114 + export function Skeleton({ 115 + variant, 116 + size, 117 + height, 118 + width, 119 + style, 120 + ...props 121 + }: SkeletonProps) { 122 + if (variant === "circle") { 123 + return ( 124 + <div 125 + {...props} 126 + {...stylex.props( 127 + styles.base, 128 + styles.circle, 129 + size === "sm" && styles.sizeSm, 130 + size === "md" && styles.sizeMd, 131 + size === "lg" && styles.sizeLg, 132 + width ? styles.width(width) : null, 133 + style, 134 + )} 135 + > 136 + <div {...stylex.props(styles.shimmer)} /> 137 + </div> 138 + ); 139 + } 140 + 141 + return ( 142 + <div 143 + {...props} 144 + {...stylex.props( 145 + styles.base, 146 + styles.rectangle, 147 + styles.height(height), 148 + styles.width(width), 149 + style, 150 + )} 151 + > 152 + <div {...stylex.props(styles.shimmer)} /> 153 + </div> 154 + ); 155 + }
+13
packages/hip-ui/src/components/skeleton/skeleton-config.ts
··· 1 + import { ComponentConfig } from "../../types"; 2 + 3 + export const skeletonConfig: ComponentConfig = { 4 + name: "skeleton", 5 + filepath: "./index.tsx", 6 + hipDependencies: [ 7 + "../theme/spacing.stylex.tsx", 8 + "../theme/radius.stylex.tsx", 9 + "../theme/semantic-color.stylex.tsx", 10 + "../theme/types.ts", 11 + ], 12 + }; 13 +
+4
packages/hip-ui/src/components/theme/shadow.stylex.tsx
··· 9 9 xl: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)", 10 10 "2xl": "0 25px 50px -12px rgb(0 0 0 / 0.25)", 11 11 none: "0 0 #0000", 12 + inset: "inset 0 0 4px 0 rgb(0 0 0 / 0.1)", 13 + insetSm: "inset 0 0 4px 0 rgb(0 0 0 / 0.05)", 14 + insetMd: "inset 0 0 4px 0 rgb(0 0 0 / 0.1)", 15 + insetLg: "inset 0 0 8px 0 rgb(0 0 0 / 0.15)", 12 16 });