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.

better card and more input varaints

+612 -149
+1 -1
README.md
··· 39 39 - [ ] File Trigger 40 40 - [ ] File Drop zone 41 41 - [ ] Segmented Control 42 - - [ ] Switch 43 42 - [ ] Slider 44 43 45 44 - [ ] Color Area ··· 75 74 - [ ] Toolbar 76 75 - [ ] Toast 77 76 77 + - [x] Switch 78 78 - [x] Grid 79 79 - [x] Badge 80 80 - [x] Avatar
+32
apps/docs/src/components/aspect-ratio/index.tsx
··· 1 + import * as stylex from "@stylexjs/stylex"; 2 + 3 + const styles = stylex.create({ 4 + aspectRatio: (aspectRatio: number) => ({ aspectRatio }), 5 + container: { 6 + position: "relative", 7 + width: "100%", 8 + }, 9 + }); 10 + 11 + export interface AspectRatioProps 12 + extends Omit<React.ComponentProps<"div">, "style" | "className"> { 13 + style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 14 + aspectRatio?: number; 15 + } 16 + 17 + export function AspectRatio({ 18 + style, 19 + aspectRatio = 1, 20 + ...props 21 + }: AspectRatioProps) { 22 + return ( 23 + <div 24 + {...props} 25 + {...stylex.props( 26 + styles.container, 27 + styles.aspectRatio(aspectRatio), 28 + style, 29 + )} 30 + /> 31 + ); 32 + }
+1 -1
apps/docs/src/components/avatar/index.tsx
··· 5 5 import { slate } from "../theme/colors.stylex"; 6 6 import { radius } from "../theme/radius.stylex"; 7 7 import { spacing } from "../theme/spacing.stylex"; 8 + import { Size } from "../theme/types"; 8 9 import { 9 10 fontSize, 10 11 fontWeight, 11 12 lineHeight, 12 13 fontFamily, 13 14 } from "../theme/typography.stylex"; 14 - import { Size } from "../types"; 15 15 16 16 const styles = stylex.create({ 17 17 wrapper: {
+1 -1
apps/docs/src/components/badge/index.tsx
··· 11 11 warning, 12 12 } from "../theme/semantic-color.stylex"; 13 13 import { spacing } from "../theme/spacing.stylex"; 14 + import { Size } from "../theme/types"; 14 15 import { fontFamily, fontSize, fontWeight } from "../theme/typography.stylex"; 15 - import { Size } from "../types"; 16 16 17 17 const styles = stylex.create({ 18 18 wrapper: {
+1 -1
apps/docs/src/components/button/index.tsx
··· 6 6 ButtonProps as AriaButtonProps, 7 7 } from "react-aria-components"; 8 8 9 + import { Size, ButtonVariant } from "../theme/types"; 9 10 import { useButtonStyles } from "../theme/useButtonStyles"; 10 - import { Size, ButtonVariant } from "../types"; 11 11 12 12 export interface ButtonProps 13 13 extends Omit<AriaButtonProps, "className" | "style"> {
+105 -22
apps/docs/src/components/card/index.tsx
··· 1 1 import * as stylex from "@stylexjs/stylex"; 2 + import { use } from "react"; 2 3 3 - import { Flex } from "../flex"; 4 + import { AspectRatio } from "../aspect-ratio"; 5 + import { SizeContext } from "../context"; 4 6 import { radius } from "../theme/radius.stylex"; 5 7 import { gray } from "../theme/semantic-color.stylex"; 6 8 import { spacing } from "../theme/spacing.stylex"; 9 + import { Size } from "../theme/types"; 7 10 import { fontFamily, fontSize, fontWeight } from "../theme/typography.stylex"; 8 11 9 12 const styles = stylex.create({ ··· 12 15 display: "flex", 13 16 flexDirection: "column", 14 17 fontFamily: fontFamily["sans"], 15 - gap: spacing["6"], 18 + gap: "var(--card-gap)", 19 + }, 20 + smCard: { 21 + "--card-gap": spacing["2"], 22 + "--card-x-padding": spacing["2"], 23 + "--card-y-padding": spacing["2"], 24 + }, 25 + mdCard: { 26 + "--card-gap": spacing["6"], 27 + "--card-x-padding": spacing["6"], 28 + "--card-y-padding": spacing["7"], 29 + }, 30 + lgCard: { 31 + "--card-gap": spacing["9"], 32 + "--card-x-padding": spacing["9"], 33 + "--card-y-padding": spacing["10"], 34 + }, 35 + cardSection: { 36 + boxSizing: "border-box", 37 + paddingBottom: { ":last-child": "var(--card-y-padding)" }, 38 + paddingLeft: "var(--card-x-padding)", 39 + paddingRight: "var(--card-x-padding)", 40 + paddingTop: { ":first-child": "var(--card-y-padding)" }, 16 41 }, 17 42 cardHeader: { 18 - paddingBottom: { ":last-child": spacing["4"] }, 19 - paddingLeft: spacing["4"], 20 - paddingRight: spacing["4"], 21 - paddingTop: spacing["5"], 43 + alignItems: "center", 44 + display: "grid", 45 + gap: "var(--card-gap)", 46 + gridTemplate: ` 47 + 'title action' 48 + 'description action' 49 + `, 50 + }, 51 + cardHeaderAction: { 52 + display: "flex", 53 + gap: spacing["2"], 54 + gridArea: "action", 55 + justifyContent: "flex-end", 22 56 }, 23 57 cardTitle: { 24 - fontSize: fontSize["xl"], 58 + fontSize: { 59 + ":is([data-card-size='sm'] *)": fontSize["lg"], 60 + ":is([data-card-size='md'] *)": fontSize["xl"], 61 + ":is([data-card-size='lg'] *)": fontSize["2xl"], 62 + }, 25 63 fontWeight: fontWeight["bold"], 64 + gridArea: "title", 26 65 }, 27 66 cardDescription: { 28 67 fontSize: fontSize["sm"], 29 68 fontWeight: fontWeight["normal"], 69 + gridArea: "description", 30 70 margin: 0, 31 71 }, 32 72 cardBody: { 33 73 display: "flex", 34 74 flexDirection: "column", 35 - gap: spacing["6"], 36 - paddingBottom: { ":last-child": spacing["4"] }, 37 - paddingLeft: spacing["4"], 38 - paddingRight: spacing["4"], 75 + gap: "var(--card-gap)", 39 76 }, 40 77 cardFooter: { 41 - paddingBottom: spacing["5"], 42 - paddingLeft: spacing["4"], 43 - paddingRight: spacing["4"], 44 - 45 78 display: "flex", 46 79 gap: spacing["2"], 47 80 justifyContent: "flex-end", 48 81 }, 82 + cardHeaderImageWrapper: {}, 83 + cardHeaderImage: { 84 + borderRadius: radius["md"], 85 + height: "100%", 86 + objectFit: "cover", 87 + overflow: "hidden", 88 + width: "100%", 89 + }, 49 90 }); 50 91 51 92 export interface CardProps 52 93 extends Omit<React.ComponentProps<"div">, "style" | "className"> { 53 94 style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 95 + size?: Size; 54 96 } 55 97 56 - export const Card = ({ style, ...props }: CardProps) => { 98 + export const Card = ({ style, size: sizeProp, ...props }: CardProps) => { 99 + const size = sizeProp || use(SizeContext); 100 + 57 101 return ( 58 102 <div 59 103 {...props} 104 + data-card-size={size} 60 105 {...stylex.props( 61 106 styles.card, 62 107 gray.bgSubtle, 63 108 gray.border, 64 109 gray.text, 65 110 style, 111 + styles[`${size}Card`], 66 112 )} 67 113 /> 68 114 ); ··· 75 121 76 122 export const CardHeader = ({ style, ...props }: CardHeaderProps) => { 77 123 return ( 78 - <Flex 124 + <div 79 125 {...props} 80 - style={[styles.cardHeader as unknown as stylex.StyleXStyles, style]} 81 - direction="column" 82 - gap="3" 126 + {...stylex.props(styles.cardSection, styles.cardHeader, style)} 83 127 /> 84 128 ); 85 129 }; ··· 107 151 ); 108 152 }; 109 153 154 + export interface CardHeaderActionProps 155 + extends Omit<React.ComponentProps<"div">, "style" | "className"> { 156 + style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 157 + } 158 + 159 + export const CardHeaderAction = ({ 160 + style, 161 + ...props 162 + }: CardHeaderActionProps) => { 163 + return <div {...props} {...stylex.props(styles.cardHeaderAction, style)} />; 164 + }; 110 165 export interface CardBodyProps 111 166 extends Omit<React.ComponentProps<"div">, "style" | "className"> { 112 167 style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 113 168 } 114 169 115 170 export const CardBody = ({ style, ...props }: CardBodyProps) => { 116 - return <div {...props} {...stylex.props(styles.cardBody, style)} />; 171 + return ( 172 + <div 173 + {...props} 174 + {...stylex.props(styles.cardSection, styles.cardBody, style)} 175 + /> 176 + ); 117 177 }; 118 178 119 179 export interface CardFooterProps ··· 122 182 } 123 183 124 184 export const CardFooter = ({ style, ...props }: CardFooterProps) => { 125 - return <div {...props} {...stylex.props(styles.cardFooter, style)} />; 185 + return ( 186 + <div 187 + {...props} 188 + {...stylex.props(styles.cardSection, styles.cardFooter, style)} 189 + /> 190 + ); 191 + }; 192 + 193 + export interface CardImageProps 194 + extends Omit<React.ComponentProps<"img">, "style" | "className"> { 195 + style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 196 + aspectRatio?: number; 197 + } 198 + 199 + export const CardImage = ({ style, ...props }: CardImageProps) => { 200 + return ( 201 + <AspectRatio 202 + {...props} 203 + style={[styles.cardSection as unknown as stylex.StyleXStyles, style]} 204 + > 205 + {/* eslint-disable-next-line jsx-a11y/alt-text */} 206 + <img {...props} {...stylex.props(styles.cardHeaderImage, style)} /> 207 + </AspectRatio> 208 + ); 126 209 };
+1 -1
apps/docs/src/components/checkbox/index.tsx
··· 17 17 import { radius } from "../theme/radius.stylex"; 18 18 import { gray, primary } from "../theme/semantic-color.stylex"; 19 19 import { spacing } from "../theme/spacing.stylex"; 20 + import { Size } from "../theme/types"; 20 21 import { fontFamily, fontSize, lineHeight } from "../theme/typography.stylex"; 21 - import { Size } from "../types"; 22 22 23 23 const styles = stylex.create({ 24 24 wrapper: {
+4 -2
apps/docs/src/components/color-field/index.tsx
··· 10 10 } from "react-aria-components"; 11 11 12 12 import { Description, Label } from "../label"; 13 + import { InputVariant, Size } from "../theme/types"; 13 14 import { useInputStyles } from "../theme/useInputStyles"; 14 - import { Size } from "../types"; 15 15 16 16 export interface ColorFieldProps 17 17 extends Omit<AriaColorFieldProps, "style" | "className">, ··· 21 21 description?: string; 22 22 errorMessage?: string | ((validation: ValidationResult) => string); 23 23 size?: Size; 24 + variant?: InputVariant; 24 25 prefix?: React.ReactNode; 25 26 suffix?: React.ReactNode; 26 27 } ··· 31 32 errorMessage, 32 33 style, 33 34 size, 35 + variant, 34 36 prefix, 35 37 suffix, 36 38 placeholder, 37 39 ...props 38 40 }: ColorFieldProps) { 39 41 const inputRef = useRef<HTMLInputElement>(null); 40 - const inputStyles = useInputStyles({ size }); 42 + const inputStyles = useInputStyles({ size, variant }); 41 43 42 44 return ( 43 45 <AriaColorField {...props} {...stylex.props(inputStyles.field, style)}>
+4 -2
apps/docs/src/components/combobox/index.tsx
··· 18 18 import { Description, Label } from "../label"; 19 19 import { ListBox } from "../listbox"; 20 20 import { spacing } from "../theme/spacing.stylex"; 21 + import { InputVariant, Size } from "../theme/types"; 21 22 import { useInputStyles } from "../theme/useInputStyles"; 22 23 import { usePopoverStyles } from "../theme/usePopoverStyles"; 23 - import { Size } from "../types"; 24 24 import { SmallBody } from "../typography"; 25 25 26 26 const styles = stylex.create({ ··· 59 59 items?: Iterable<T>; 60 60 children: React.ReactNode | ((item: T) => React.ReactNode); 61 61 size?: Size; 62 + variant?: InputVariant; 62 63 placeholder?: string; 63 64 prefix?: React.ReactNode; 64 65 suffix?: React.ReactNode; ··· 72 73 items, 73 74 style, 74 75 size: sizeProp, 76 + variant, 75 77 shouldCloseOnInteractOutside, 76 78 shouldFlip, 77 79 shouldUpdatePosition, ··· 83 85 ...props 84 86 }: ComboBoxProps<T>) { 85 87 const size = sizeProp || use(SizeContext); 86 - const inputStyles = useInputStyles({ size }); 88 + const inputStyles = useInputStyles({ size, variant }); 87 89 const popoverStyles = usePopoverStyles(); 88 90 89 91 return (
+1 -1
apps/docs/src/components/context-menu/index.tsx
··· 24 24 import { useMenuTriggerState } from "react-stately"; 25 25 26 26 import { SizeContext } from "../context"; 27 + import { Size } from "../theme/types"; 27 28 import { usePopoverStyles } from "../theme/usePopoverStyles"; 28 - import { Size } from "../types"; 29 29 30 30 const ContextMenuTriggerPropsContext = createContext< 31 31 AriaButtonProps & { ref?: React.Ref<HTMLDivElement> }
+1 -1
apps/docs/src/components/context.ts
··· 1 1 import { createContext } from "react"; 2 2 3 - import { Size } from "./types"; 3 + import { Size } from "./theme/types"; 4 4 5 5 export const SizeContext = createContext<Size>("md");
+4 -2
apps/docs/src/components/date-field/index.tsx
··· 11 11 } from "react-aria-components"; 12 12 13 13 import { Description, Label } from "../label"; 14 + import { InputVariant, Size } from "../theme/types"; 14 15 import { useInputStyles } from "../theme/useInputStyles"; 15 - import { Size } from "../types"; 16 16 17 17 export interface DateFieldProps<T extends DateValue> 18 18 extends Omit<AriaDateFieldProps<T>, "style" | "className"> { ··· 21 21 description?: string; 22 22 errorMessage?: string | ((validation: ValidationResult) => string); 23 23 size?: Size; 24 + variant?: InputVariant; 24 25 prefix?: React.ReactNode; 25 26 suffix?: React.ReactNode; 26 27 } ··· 31 32 errorMessage, 32 33 style, 33 34 size, 35 + variant, 34 36 prefix, 35 37 suffix, 36 38 ...props 37 39 }: DateFieldProps<T>) { 38 40 const inputRef = useRef<HTMLInputElement>(null); 39 - const inputStyles = useInputStyles({ size }); 41 + const inputStyles = useInputStyles({ size, variant }); 40 42 41 43 return ( 42 44 <AriaDateField {...props} {...stylex.props(inputStyles.field, style)}>
+1 -1
apps/docs/src/components/dialog/index.tsx
··· 14 14 import { IconButton } from "../icon-button"; 15 15 import { slate } from "../theme/colors.stylex"; 16 16 import { spacing } from "../theme/spacing.stylex"; 17 + import { Size } from "../theme/types"; 17 18 import { typeramp } from "../theme/typography.stylex"; 18 19 import { useDialogStyles } from "../theme/useDialogStyles"; 19 - import { Size } from "../types"; 20 20 21 21 const styles = stylex.create({ 22 22 dialog: {
+1 -1
apps/docs/src/components/icon-button/index.tsx
··· 7 7 import { Button } from "../button"; 8 8 import { SizeContext } from "../context"; 9 9 import { spacing } from "../theme/spacing.stylex"; 10 + import { ButtonVariant, Size } from "../theme/types"; 10 11 import { Tooltip } from "../tooltip"; 11 - import { ButtonVariant, Size } from "../types"; 12 12 13 13 const styles = stylex.create({ 14 14 sm: {
+1 -1
apps/docs/src/components/label/index.tsx
··· 9 9 10 10 import { SizeContext } from "../context"; 11 11 import { gray } from "../theme/semantic-color.stylex"; 12 + import { Size } from "../theme/types"; 12 13 import { fontSize, fontWeight, lineHeight } from "../theme/typography.stylex"; 13 - import { Size } from "../types"; 14 14 15 15 const styles = stylex.create({ 16 16 label: {
+1 -1
apps/docs/src/components/listbox/index.tsx
··· 16 16 import { Separator } from "../separator"; 17 17 import { gray } from "../theme/semantic-color.stylex"; 18 18 import { spacing } from "../theme/spacing.stylex"; 19 + import { Size } from "../theme/types"; 19 20 import { typeramp } from "../theme/typography.stylex"; 20 21 import { useListBoxItemStyles } from "../theme/useListBoxItemStyles"; 21 - import { Size } from "../types"; 22 22 23 23 const styles = stylex.create({ 24 24 listBox: {
+13 -1
apps/docs/src/components/menu/index.tsx
··· 17 17 } from "react-aria-components"; 18 18 19 19 import { SizeContext } from "../context"; 20 + import { Size } from "../theme/types"; 20 21 import { useListBoxItemStyles } from "../theme/useListBoxItemStyles"; 21 22 import { usePopoverStyles } from "../theme/usePopoverStyles"; 22 - import { Size } from "../types"; 23 23 24 24 export interface MenuProps<T extends object> 25 25 extends Omit<MenuTriggerProps, "trigger" | "children">, ··· 32 32 | "placement" 33 33 > { 34 34 trigger: React.ReactNode; 35 + header?: React.ReactNode; 36 + footer?: React.ReactNode; 35 37 items?: Iterable<T>; 36 38 children: React.ReactNode | ((item: T) => React.ReactNode); 37 39 size?: Size; ··· 47 49 shouldFlip, 48 50 shouldUpdatePosition, 49 51 placement, 52 + header, 53 + footer, 50 54 ...props 51 55 }: MenuProps<T>) { 52 56 const popoverStyles = usePopoverStyles(); ··· 67 71 shouldUpdatePosition={shouldUpdatePosition} 68 72 placement={placement} 69 73 > 74 + {header} 70 75 <AriaMenu {...props} {...stylex.props(popoverStyles)} /> 76 + {footer} 71 77 </Popover> 72 78 </MenuTrigger> 73 79 </SizeContext> ··· 85 91 | "placement" 86 92 > { 87 93 trigger: React.ReactElement<MenuTriggerProps>; 94 + header?: React.ReactNode; 95 + footer?: React.ReactNode; 88 96 children: React.ReactNode | ((item: T) => React.ReactNode); 89 97 items?: Iterable<T>; 90 98 size?: Size; ··· 97 105 shouldFlip, 98 106 shouldUpdatePosition, 99 107 placement, 108 + header, 109 + footer, 100 110 ...props 101 111 }: SubMenuProps<T>) { 102 112 const popoverStyles = usePopoverStyles(); ··· 112 122 containerPadding={8} 113 123 offset={-8} 114 124 > 125 + {header} 115 126 <AriaMenu {...props} {...stylex.props(popoverStyles)} /> 127 + {footer} 116 128 </Popover> 117 129 </SubmenuTrigger> 118 130 );
+4 -2
apps/docs/src/components/number-field/index.tsx
··· 16 16 import { slate } from "../theme/colors.stylex"; 17 17 import { gray } from "../theme/semantic-color.stylex"; 18 18 import { spacing } from "../theme/spacing.stylex"; 19 + import { InputVariant, Size } from "../theme/types"; 19 20 import { useInputStyles } from "../theme/useInputStyles"; 20 - import { Size } from "../types"; 21 21 22 22 const styles = stylex.create({ 23 23 buttons: { ··· 59 59 description?: string; 60 60 errorMessage?: string | ((validation: ValidationResult) => string); 61 61 size?: Size; 62 + variant?: InputVariant; 62 63 prefix?: React.ReactNode; 63 64 suffix?: React.ReactNode; 64 65 } ··· 69 70 errorMessage, 70 71 style, 71 72 size, 73 + variant, 72 74 prefix, 73 75 suffix, 74 76 placeholder, 75 77 ...props 76 78 }: NumberFieldProps) { 77 79 const inputRef = useRef<HTMLInputElement>(null); 78 - const inputStyles = useInputStyles({ size }); 80 + const inputStyles = useInputStyles({ size, variant }); 79 81 const buttonStyles = stylex.props( 80 82 styles.button, 81 83 gray.borderInteractive,
+1 -1
apps/docs/src/components/radio/index.tsx
··· 17 17 import { radius } from "../theme/radius.stylex"; 18 18 import { gray, primary } from "../theme/semantic-color.stylex"; 19 19 import { spacing } from "../theme/spacing.stylex"; 20 + import { Size } from "../theme/types"; 20 21 import { fontFamily, fontSize, lineHeight } from "../theme/typography.stylex"; 21 - import { Size } from "../types"; 22 22 23 23 const scaleIn = stylex.keyframes({ 24 24 "0%": {
+4 -2
apps/docs/src/components/search-field/index.tsx
··· 13 13 import { IconButton } from "../icon-button"; 14 14 import { Description, Label } from "../label"; 15 15 import { spacing } from "../theme/spacing.stylex"; 16 + import { InputVariant, Size } from "../theme/types"; 16 17 import { useInputStyles } from "../theme/useInputStyles"; 17 - import { Size } from "../types"; 18 18 19 19 const styles = stylex.create({ 20 20 wrapper: { ··· 39 39 description?: string; 40 40 errorMessage?: string | ((validation: ValidationResult) => string); 41 41 size?: Size; 42 + variant?: InputVariant; 42 43 prefix?: React.ReactNode; 43 44 suffix?: React.ReactNode; 44 45 } ··· 51 52 errorMessage, 52 53 style, 53 54 size, 55 + variant, 54 56 prefix = defaultPrefix, 55 57 suffix, 56 58 placeholder, 57 59 ...props 58 60 }: SearchFieldProps) { 59 61 const inputRef = useRef<HTMLInputElement>(null); 60 - const inputStyles = useInputStyles({ size }); 62 + const inputStyles = useInputStyles({ size, variant }); 61 63 62 64 return ( 63 65 <AriaSearchField {...props} {...stylex.props(inputStyles.field, style)}>
+4 -2
apps/docs/src/components/select/index.tsx
··· 16 16 import { SizeContext } from "../context"; 17 17 import { Description, Label } from "../label"; 18 18 import { ListBox } from "../listbox"; 19 + import { InputVariant, Size } from "../theme/types"; 19 20 import { useInputStyles } from "../theme/useInputStyles"; 20 21 import { usePopoverStyles } from "../theme/usePopoverStyles"; 21 - import { Size } from "../types"; 22 22 23 23 const styles = stylex.create({ 24 24 matchWidth: { ··· 42 42 items?: Iterable<T>; 43 43 children: React.ReactNode | ((item: T) => React.ReactNode); 44 44 size?: Size; 45 + variant?: InputVariant; 45 46 placeholder?: string; 46 47 prefix?: React.ReactNode; 47 48 suffix?: React.ReactNode; ··· 58 59 items, 59 60 style, 60 61 size: sizeProp, 62 + variant, 61 63 shouldCloseOnInteractOutside, 62 64 shouldFlip, 63 65 shouldUpdatePosition, ··· 68 70 ...props 69 71 }: SelectProps<T, M>) { 70 72 const size = sizeProp || use(SizeContext); 71 - const inputStyles = useInputStyles({ size }); 73 + const inputStyles = useInputStyles({ size, variant }); 72 74 const popoverStyles = usePopoverStyles(); 73 75 74 76 return (
+1 -1
apps/docs/src/components/text-area/index.tsx
··· 16 16 import { radius } from "../theme/radius.stylex"; 17 17 import { gray } from "../theme/semantic-color.stylex"; 18 18 import { spacing } from "../theme/spacing.stylex"; 19 + import { Size } from "../theme/types"; 19 20 import { lineHeight, fontSize, fontFamily } from "../theme/typography.stylex"; 20 - import { Size } from "../types"; 21 21 22 22 const styles = stylex.create({ 23 23 wrapper: {
+4 -2
apps/docs/src/components/text-field/index.tsx
··· 13 13 14 14 import { IconButton } from "../icon-button"; 15 15 import { Description, Label } from "../label"; 16 + import { InputVariant, Size } from "../theme/types"; 16 17 import { useInputStyles } from "../theme/useInputStyles"; 17 - import { Size } from "../types"; 18 18 19 19 function PasswordToggle({ 20 20 type, ··· 53 53 description?: string; 54 54 errorMessage?: string | ((validation: ValidationResult) => string); 55 55 size?: Size; 56 + variant?: InputVariant; 56 57 prefix?: React.ReactNode; 57 58 suffix?: React.ReactNode; 58 59 } ··· 63 64 errorMessage, 64 65 style, 65 66 size, 67 + variant, 66 68 prefix, 67 69 suffix, 68 70 placeholder, ··· 73 75 props.type || "text", 74 76 ); 75 77 const isPasswordInput = props.type === "password"; 76 - const inputStyles = useInputStyles({ size }); 78 + const inputStyles = useInputStyles({ size, variant }); 77 79 78 80 return ( 79 81 <AriaTextField
+10
apps/docs/src/components/theme/types.ts
··· 2 2 3 3 export type ThemeKeys<T> = 4 4 T extends stylex.VarGroup<Readonly<infer Tokens>> ? keyof Tokens : never; 5 + 6 + export type Size = "sm" | "md" | "lg"; 7 + export type ButtonVariant = 8 + | "primary" 9 + | "secondary" 10 + | "tertiary" 11 + | "outline" 12 + | "critical" 13 + | "critical-outline"; 14 + export type InputVariant = "primary" | "secondary" | "tertiary";
+1 -1
apps/docs/src/components/theme/useButtonStyles.ts
··· 5 5 6 6 import { ButtonGroupContext } from "../button/context"; 7 7 import { SizeContext } from "../context"; 8 - import { Size, ButtonVariant } from "../types"; 8 + import { Size, ButtonVariant } from "../theme/types"; 9 9 import { slate } from "./colors.stylex"; 10 10 import { radius } from "./radius.stylex"; 11 11 import { critical, gray, primary } from "./semantic-color.stylex";
+1 -1
apps/docs/src/components/theme/useDialogStyles.ts
··· 5 5 import { animations } from "../theme/animations.stylex"; 6 6 import { radius } from "../theme/radius.stylex"; 7 7 import { shadow } from "../theme/shadow.stylex"; 8 - import { Size } from "../types"; 8 + import { Size } from "../theme/types"; 9 9 import { gray } from "./semantic-color.stylex"; 10 10 11 11 const styles = stylex.create({
+26 -13
apps/docs/src/components/theme/useInputStyles.ts
··· 2 2 import { use } from "react"; 3 3 4 4 import { SizeContext } from "../context"; 5 - import { Size } from "../types"; 5 + import { InputVariant, Size } from "../theme/types"; 6 6 import { slate } from "./colors.stylex"; 7 7 import { radius } from "./radius.stylex"; 8 8 import { gray } from "./semantic-color.stylex"; ··· 41 41 }, 42 42 inputWrapper: { 43 43 borderRadius: radius["md"], 44 + borderWidth: 0, 44 45 boxSizing: "border-box", 45 46 display: "flex", 46 47 lineHeight: lineHeight["none"], 47 48 overflow: "hidden", 48 49 padding: 0, 49 - 50 - borderColor: { 51 - default: slate.border2, 52 - ":hover": slate.border3, 53 - ":focus": slate.solid1, 54 - }, 55 - borderStyle: "solid", 56 - borderWidth: 1, 57 - 58 - transitionProperty: "background-color, border-color", 59 50 }, 60 51 input: { 61 52 alignItems: "center", ··· 99 90 paddingLeft: spacing["1"], 100 91 paddingRight: spacing["2"], 101 92 }, 93 + primary: { 94 + borderColor: { 95 + default: slate.border2, 96 + ":hover": slate.border3, 97 + ":focus": slate.solid1, 98 + }, 99 + borderStyle: "solid", 100 + borderWidth: 1, 101 + transitionProperty: "background-color, border-color", 102 + }, 102 103 }); 103 104 104 - export function useInputStyles({ size: sizeProp }: { size?: Size }) { 105 + export function useInputStyles({ 106 + size: sizeProp, 107 + variant = "primary", 108 + }: { 109 + size: Size | undefined; 110 + variant: InputVariant | undefined; 111 + }) { 105 112 const size = sizeProp || use(SizeContext); 106 113 107 114 return { 108 115 field: [styles.field], 109 - wrapper: [styles.inputWrapper, gray.bgUi, gray.text, styles[size]], 116 + wrapper: [ 117 + styles.inputWrapper, 118 + variant === "primary" && [gray.bgUi, styles.primary], 119 + variant === "secondary" && [gray.bgUi], 120 + gray.text, 121 + styles[size], 122 + ], 110 123 input: [styles.input, styles[`${size}Input`]], 111 124 addon: styles.addon as unknown as stylex.StyleXStyles, 112 125 };
+4 -2
apps/docs/src/components/time-field/index.tsx
··· 11 11 } from "react-aria-components"; 12 12 13 13 import { Description, Label } from "../label"; 14 + import { InputVariant, Size } from "../theme/types"; 14 15 import { useInputStyles } from "../theme/useInputStyles"; 15 - import { Size } from "../types"; 16 16 17 17 export interface TimeFieldProps<T extends TimeValue> 18 18 extends Omit<AriaTimeFieldProps<T>, "style" | "className"> { ··· 21 21 description?: string; 22 22 errorMessage?: string | ((validation: ValidationResult) => string); 23 23 size?: Size; 24 + variant?: InputVariant; 24 25 prefix?: React.ReactNode; 25 26 suffix?: React.ReactNode; 26 27 } ··· 31 32 errorMessage, 32 33 style, 33 34 size, 35 + variant, 34 36 prefix, 35 37 suffix, 36 38 ...props 37 39 }: TimeFieldProps<T>) { 38 40 const inputRef = useRef<HTMLInputElement>(null); 39 - const inputStyles = useInputStyles({ size }); 41 + const inputStyles = useInputStyles({ size, variant }); 40 42 41 43 return ( 42 44 <AriaTimeField {...props} {...stylex.props(inputStyles.field, style)}>
+1 -1
apps/docs/src/components/toggle-button/index.tsx
··· 8 8 import { SizeContext } from "../context"; 9 9 import { plum, slate } from "../theme/colors.stylex"; 10 10 import { spacing } from "../theme/spacing.stylex"; 11 + import { ButtonVariant, Size } from "../theme/types"; 11 12 import { useButtonStyles } from "../theme/useButtonStyles"; 12 - import { ButtonVariant, Size } from "../types"; 13 13 14 14 const styles = stylex.create({ 15 15 primarySelected: {
+1 -1
apps/docs/src/components/tree/index.tsx
··· 16 16 import { radius } from "../theme/radius.stylex"; 17 17 import { gray } from "../theme/semantic-color.stylex"; 18 18 import { spacing } from "../theme/spacing.stylex"; 19 + import { Size } from "../theme/types"; 19 20 import { useListBoxItemStyles } from "../theme/useListBoxItemStyles"; 20 - import { Size } from "../types"; 21 21 22 22 const styles = stylex.create({ 23 23 wrapper: {
+26 -3
apps/docs/src/routeTree.gen.ts
··· 12 12 import { Route as DocsRouteImport } from './routes/_docs' 13 13 import { Route as IndexRouteImport } from './routes/index' 14 14 import { Route as DocsInvoiceAppRouteImport } from './routes/_docs.invoice-app' 15 + import { Route as DocsEcommerceAppRouteImport } from './routes/_docs.ecommerce-app' 15 16 16 17 const DocsRoute = DocsRouteImport.update({ 17 18 id: '/_docs', ··· 27 28 path: '/invoice-app', 28 29 getParentRoute: () => DocsRoute, 29 30 } as any) 31 + const DocsEcommerceAppRoute = DocsEcommerceAppRouteImport.update({ 32 + id: '/ecommerce-app', 33 + path: '/ecommerce-app', 34 + getParentRoute: () => DocsRoute, 35 + } as any) 30 36 31 37 export interface FileRoutesByFullPath { 32 38 '/': typeof IndexRoute 39 + '/ecommerce-app': typeof DocsEcommerceAppRoute 33 40 '/invoice-app': typeof DocsInvoiceAppRoute 34 41 } 35 42 export interface FileRoutesByTo { 36 43 '/': typeof IndexRoute 44 + '/ecommerce-app': typeof DocsEcommerceAppRoute 37 45 '/invoice-app': typeof DocsInvoiceAppRoute 38 46 } 39 47 export interface FileRoutesById { 40 48 __root__: typeof rootRouteImport 41 49 '/': typeof IndexRoute 42 50 '/_docs': typeof DocsRouteWithChildren 51 + '/_docs/ecommerce-app': typeof DocsEcommerceAppRoute 43 52 '/_docs/invoice-app': typeof DocsInvoiceAppRoute 44 53 } 45 54 export interface FileRouteTypes { 46 55 fileRoutesByFullPath: FileRoutesByFullPath 47 - fullPaths: '/' | '/invoice-app' 56 + fullPaths: '/' | '/ecommerce-app' | '/invoice-app' 48 57 fileRoutesByTo: FileRoutesByTo 49 - to: '/' | '/invoice-app' 50 - id: '__root__' | '/' | '/_docs' | '/_docs/invoice-app' 58 + to: '/' | '/ecommerce-app' | '/invoice-app' 59 + id: 60 + | '__root__' 61 + | '/' 62 + | '/_docs' 63 + | '/_docs/ecommerce-app' 64 + | '/_docs/invoice-app' 51 65 fileRoutesById: FileRoutesById 52 66 } 53 67 export interface RootRouteChildren { ··· 78 92 preLoaderRoute: typeof DocsInvoiceAppRouteImport 79 93 parentRoute: typeof DocsRoute 80 94 } 95 + '/_docs/ecommerce-app': { 96 + id: '/_docs/ecommerce-app' 97 + path: '/ecommerce-app' 98 + fullPath: '/ecommerce-app' 99 + preLoaderRoute: typeof DocsEcommerceAppRouteImport 100 + parentRoute: typeof DocsRoute 101 + } 81 102 } 82 103 } 83 104 84 105 interface DocsRouteChildren { 106 + DocsEcommerceAppRoute: typeof DocsEcommerceAppRoute 85 107 DocsInvoiceAppRoute: typeof DocsInvoiceAppRoute 86 108 } 87 109 88 110 const DocsRouteChildren: DocsRouteChildren = { 111 + DocsEcommerceAppRoute: DocsEcommerceAppRoute, 89 112 DocsInvoiceAppRoute: DocsInvoiceAppRoute, 90 113 } 91 114
+119
apps/docs/src/routes/_docs.ecommerce-app.tsx
··· 1 + import { Flex } from "@/components/flex"; 2 + import { createFileRoute } from "@tanstack/react-router"; 3 + import * as stylex from "@stylexjs/stylex"; 4 + import { 5 + Card, 6 + CardBody, 7 + CardDescription, 8 + CardHeader, 9 + CardHeaderAction, 10 + CardImage, 11 + CardTitle, 12 + } from "@/components/card"; 13 + import { Button } from "@/components/button"; 14 + import { Text } from "@/components/typography/text"; 15 + import { Separator } from "@/components/separator"; 16 + import { Select, SelectItem } from "@/components/select"; 17 + 18 + const styles = stylex.create({ 19 + grow: { 20 + flexGrow: 1, 21 + flexShrink: 0, 22 + flexBasis: "0%", 23 + minWidth: 0, 24 + }, 25 + skinny: { 26 + flexGrow: 0.5, 27 + flexShrink: 0, 28 + flexBasis: "0%", 29 + }, 30 + }); 31 + 32 + export const Route = createFileRoute("/_docs/ecommerce-app")({ 33 + component: RouteComponent, 34 + }); 35 + 36 + function SmallProductCard() { 37 + return ( 38 + <Card size="sm"> 39 + <CardImage src="https://images.unsplash.com/photo-1620799140408-edc6dcb6d633?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=560&h=424&q=80" /> 40 + <CardHeader> 41 + <CardTitle>Back to basics</CardTitle> 42 + <CardDescription>Simple and versatile</CardDescription> 43 + <CardHeaderAction> 44 + <Button variant="secondary">Show now</Button> 45 + </CardHeaderAction> 46 + </CardHeader> 47 + </Card> 48 + ); 49 + } 50 + 51 + function SmallProductCardWithBuying() { 52 + return ( 53 + <Card size="sm"> 54 + <CardImage src="https://images.unsplash.com/photo-1595950653106-6c9ebd614d3a?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=560&h=540&q=80" /> 55 + <CardBody> 56 + <Flex direction="column" gap="4"> 57 + <Flex direction="column" gap="2"> 58 + <Text size="sm">Footwear</Text> 59 + <Text weight="semibold">Sneakers #12</Text> 60 + <Text size="sm" variant="secondary"> 61 + Love at the first sight for enthusiasts seeking a fresh and 62 + whimsical style. 63 + </Text> 64 + </Flex> 65 + <Separator /> 66 + <Flex align="end" gap="2"> 67 + <Select 68 + label="Color" 69 + defaultValue="red" 70 + style={styles.grow} 71 + variant="secondary" 72 + > 73 + <SelectItem id="red">Red</SelectItem> 74 + <SelectItem id="blue">Blue</SelectItem> 75 + <SelectItem id="green">Green</SelectItem> 76 + <SelectItem id="yellow">Yellow</SelectItem> 77 + <SelectItem id="purple">Purple</SelectItem> 78 + <SelectItem id="orange">Orange</SelectItem> 79 + <SelectItem id="pink">Pink</SelectItem> 80 + <SelectItem id="brown">Brown</SelectItem> 81 + <SelectItem id="gray">Gray</SelectItem> 82 + </Select> 83 + <Select 84 + label="Size" 85 + defaultValue="8" 86 + style={styles.grow} 87 + variant="secondary" 88 + > 89 + <SelectItem id="8">8</SelectItem> 90 + <SelectItem id="9">9</SelectItem> 91 + <SelectItem id="10">10</SelectItem> 92 + <SelectItem id="11">11</SelectItem> 93 + <SelectItem id="12">12</SelectItem> 94 + <SelectItem id="13">13</SelectItem> 95 + <SelectItem id="14">14</SelectItem> 96 + <SelectItem id="15">15</SelectItem> 97 + <SelectItem id="16">16</SelectItem> 98 + </Select> 99 + <Button>Buy</Button> 100 + </Flex> 101 + </Flex> 102 + </CardBody> 103 + </Card> 104 + ); 105 + } 106 + 107 + function RouteComponent() { 108 + return ( 109 + <Flex gap="4"> 110 + <Flex direction="column" gap="4" style={styles.skinny}> 111 + <SmallProductCard /> 112 + <SmallProductCardWithBuying /> 113 + </Flex> 114 + <Flex direction="column" gap="4" style={styles.skinny}></Flex> 115 + <Flex direction="column" gap="4" style={styles.skinny}></Flex> 116 + <Flex direction="column" gap="4" style={styles.grow}></Flex> 117 + </Flex> 118 + ); 119 + }
+2
packages/hip-ui/src/cli/install.tsx
··· 9 9 import * as React from "react"; 10 10 11 11 import { alertDialogConfig } from "../components/alert-dialog/alert-dialog-config.js"; 12 + import { aspectRatioConfig } from "../components/aspect-ratio/aspect-ratio-config.js"; 12 13 import { avatarConfig } from "../components/avatar/avatar-config.js"; 13 14 import { badgeConfig } from "../components/badge/badge-config.js"; 14 15 import { buttonGroupConfig } from "../components/button-group/button-group-config.js"; ··· 84 85 badgeConfig, 85 86 gridConfig, 86 87 switchConfig, 88 + aspectRatioConfig, 87 89 ]; 88 90 89 91 function StringSetting({
+6
packages/hip-ui/src/components/aspect-ratio/aspect-ratio-config.ts
··· 1 + import { ComponentConfig } from "../../types"; 2 + 3 + export const aspectRatioConfig: ComponentConfig = { 4 + name: "aspect-ratio", 5 + filepath: "./index.tsx", 6 + };
+32
packages/hip-ui/src/components/aspect-ratio/index.tsx
··· 1 + import * as stylex from "@stylexjs/stylex"; 2 + 3 + const styles = stylex.create({ 4 + aspectRatio: (aspectRatio: number) => ({ aspectRatio }), 5 + container: { 6 + position: "relative", 7 + width: "100%", 8 + }, 9 + }); 10 + 11 + export interface AspectRatioProps 12 + extends Omit<React.ComponentProps<"div">, "style" | "className"> { 13 + style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 14 + aspectRatio?: number; 15 + } 16 + 17 + export function AspectRatio({ 18 + style, 19 + aspectRatio = 1, 20 + ...props 21 + }: AspectRatioProps) { 22 + return ( 23 + <div 24 + {...props} 25 + {...stylex.props( 26 + styles.container, 27 + styles.aspectRatio(aspectRatio), 28 + style, 29 + )} 30 + /> 31 + ); 32 + }
+1 -1
packages/hip-ui/src/components/avatar/index.tsx
··· 5 5 import { slate } from "../theme/colors.stylex"; 6 6 import { radius } from "../theme/radius.stylex"; 7 7 import { spacing } from "../theme/spacing.stylex"; 8 + import { Size } from "../theme/types"; 8 9 import { 9 10 fontSize, 10 11 fontWeight, 11 12 lineHeight, 12 13 fontFamily, 13 14 } from "../theme/typography.stylex"; 14 - import { Size } from "../types"; 15 15 16 16 const styles = stylex.create({ 17 17 wrapper: {
+1 -1
packages/hip-ui/src/components/badge/index.tsx
··· 11 11 warning, 12 12 } from "../theme/semantic-color.stylex"; 13 13 import { spacing } from "../theme/spacing.stylex"; 14 + import { Size } from "../theme/types"; 14 15 import { fontFamily, fontSize, fontWeight } from "../theme/typography.stylex"; 15 - import { Size } from "../types"; 16 16 17 17 const styles = stylex.create({ 18 18 wrapper: {
+1 -1
packages/hip-ui/src/components/button/button-config.ts
··· 13 13 "../theme/shadow.stylex.tsx", 14 14 "../theme/useButtonStyles.ts", 15 15 "./context.ts", 16 - "../types.ts", 16 + "../theme/types.ts", 17 17 ], 18 18 dependencies: { 19 19 "react-aria-components": "^1.13.0",
+1 -1
packages/hip-ui/src/components/button/index.tsx
··· 6 6 ButtonProps as AriaButtonProps, 7 7 } from "react-aria-components"; 8 8 9 + import { Size, ButtonVariant } from "../theme/types"; 9 10 import { useButtonStyles } from "../theme/useButtonStyles"; 10 - import { Size, ButtonVariant } from "../types"; 11 11 12 12 export interface ButtonProps 13 13 extends Omit<AriaButtonProps, "className" | "style"> {
+105 -22
packages/hip-ui/src/components/card/index.tsx
··· 1 1 import * as stylex from "@stylexjs/stylex"; 2 + import { use } from "react"; 2 3 3 - import { Flex } from "../flex"; 4 + import { AspectRatio } from "../aspect-ratio"; 5 + import { SizeContext } from "../context"; 4 6 import { radius } from "../theme/radius.stylex"; 5 7 import { gray } from "../theme/semantic-color.stylex"; 6 8 import { spacing } from "../theme/spacing.stylex"; 9 + import { Size } from "../theme/types"; 7 10 import { fontFamily, fontSize, fontWeight } from "../theme/typography.stylex"; 8 11 9 12 const styles = stylex.create({ ··· 12 15 display: "flex", 13 16 flexDirection: "column", 14 17 fontFamily: fontFamily["sans"], 15 - gap: spacing["6"], 18 + gap: "var(--card-gap)", 19 + }, 20 + smCard: { 21 + "--card-gap": spacing["2"], 22 + "--card-x-padding": spacing["2"], 23 + "--card-y-padding": spacing["2"], 24 + }, 25 + mdCard: { 26 + "--card-gap": spacing["6"], 27 + "--card-x-padding": spacing["6"], 28 + "--card-y-padding": spacing["7"], 29 + }, 30 + lgCard: { 31 + "--card-gap": spacing["9"], 32 + "--card-x-padding": spacing["9"], 33 + "--card-y-padding": spacing["10"], 34 + }, 35 + cardSection: { 36 + boxSizing: "border-box", 37 + paddingBottom: { ":last-child": "var(--card-y-padding)" }, 38 + paddingLeft: "var(--card-x-padding)", 39 + paddingRight: "var(--card-x-padding)", 40 + paddingTop: { ":first-child": "var(--card-y-padding)" }, 16 41 }, 17 42 cardHeader: { 18 - paddingBottom: { ":last-child": spacing["4"] }, 19 - paddingLeft: spacing["4"], 20 - paddingRight: spacing["4"], 21 - paddingTop: spacing["5"], 43 + alignItems: "center", 44 + display: "grid", 45 + gap: "var(--card-gap)", 46 + gridTemplate: ` 47 + 'title action' 48 + 'description action' 49 + `, 50 + }, 51 + cardHeaderAction: { 52 + display: "flex", 53 + gap: spacing["2"], 54 + gridArea: "action", 55 + justifyContent: "flex-end", 22 56 }, 23 57 cardTitle: { 24 - fontSize: fontSize["xl"], 58 + fontSize: { 59 + ":is([data-card-size='sm'] *)": fontSize["lg"], 60 + ":is([data-card-size='md'] *)": fontSize["xl"], 61 + ":is([data-card-size='lg'] *)": fontSize["2xl"], 62 + }, 25 63 fontWeight: fontWeight["bold"], 64 + gridArea: "title", 26 65 }, 27 66 cardDescription: { 28 67 fontSize: fontSize["sm"], 29 68 fontWeight: fontWeight["normal"], 69 + gridArea: "description", 30 70 margin: 0, 31 71 }, 32 72 cardBody: { 33 73 display: "flex", 34 74 flexDirection: "column", 35 - gap: spacing["6"], 36 - paddingBottom: { ":last-child": spacing["4"] }, 37 - paddingLeft: spacing["4"], 38 - paddingRight: spacing["4"], 75 + gap: "var(--card-gap)", 39 76 }, 40 77 cardFooter: { 41 - paddingBottom: spacing["5"], 42 - paddingLeft: spacing["4"], 43 - paddingRight: spacing["4"], 44 - 45 78 display: "flex", 46 79 gap: spacing["2"], 47 80 justifyContent: "flex-end", 48 81 }, 82 + cardHeaderImageWrapper: {}, 83 + cardHeaderImage: { 84 + borderRadius: radius["md"], 85 + height: "100%", 86 + objectFit: "cover", 87 + overflow: "hidden", 88 + width: "100%", 89 + }, 49 90 }); 50 91 51 92 export interface CardProps 52 93 extends Omit<React.ComponentProps<"div">, "style" | "className"> { 53 94 style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 95 + size?: Size; 54 96 } 55 97 56 - export const Card = ({ style, ...props }: CardProps) => { 98 + export const Card = ({ style, size: sizeProp, ...props }: CardProps) => { 99 + const size = sizeProp || use(SizeContext); 100 + 57 101 return ( 58 102 <div 59 103 {...props} 104 + data-card-size={size} 60 105 {...stylex.props( 61 106 styles.card, 62 107 gray.bgSubtle, 63 108 gray.border, 64 109 gray.text, 65 110 style, 111 + styles[`${size}Card`], 66 112 )} 67 113 /> 68 114 ); ··· 75 121 76 122 export const CardHeader = ({ style, ...props }: CardHeaderProps) => { 77 123 return ( 78 - <Flex 124 + <div 79 125 {...props} 80 - style={[styles.cardHeader as unknown as stylex.StyleXStyles, style]} 81 - direction="column" 82 - gap="3" 126 + {...stylex.props(styles.cardSection, styles.cardHeader, style)} 83 127 /> 84 128 ); 85 129 }; ··· 107 151 ); 108 152 }; 109 153 154 + export interface CardHeaderActionProps 155 + extends Omit<React.ComponentProps<"div">, "style" | "className"> { 156 + style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 157 + } 158 + 159 + export const CardHeaderAction = ({ 160 + style, 161 + ...props 162 + }: CardHeaderActionProps) => { 163 + return <div {...props} {...stylex.props(styles.cardHeaderAction, style)} />; 164 + }; 110 165 export interface CardBodyProps 111 166 extends Omit<React.ComponentProps<"div">, "style" | "className"> { 112 167 style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 113 168 } 114 169 115 170 export const CardBody = ({ style, ...props }: CardBodyProps) => { 116 - return <div {...props} {...stylex.props(styles.cardBody, style)} />; 171 + return ( 172 + <div 173 + {...props} 174 + {...stylex.props(styles.cardSection, styles.cardBody, style)} 175 + /> 176 + ); 117 177 }; 118 178 119 179 export interface CardFooterProps ··· 122 182 } 123 183 124 184 export const CardFooter = ({ style, ...props }: CardFooterProps) => { 125 - return <div {...props} {...stylex.props(styles.cardFooter, style)} />; 185 + return ( 186 + <div 187 + {...props} 188 + {...stylex.props(styles.cardSection, styles.cardFooter, style)} 189 + /> 190 + ); 191 + }; 192 + 193 + export interface CardImageProps 194 + extends Omit<React.ComponentProps<"img">, "style" | "className"> { 195 + style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 196 + aspectRatio?: number; 197 + } 198 + 199 + export const CardImage = ({ style, ...props }: CardImageProps) => { 200 + return ( 201 + <AspectRatio 202 + {...props} 203 + style={[styles.cardSection as unknown as stylex.StyleXStyles, style]} 204 + > 205 + {/* eslint-disable-next-line jsx-a11y/alt-text */} 206 + <img {...props} {...stylex.props(styles.cardHeaderImage, style)} /> 207 + </AspectRatio> 208 + ); 126 209 };
+1 -1
packages/hip-ui/src/components/checkbox/index.tsx
··· 17 17 import { radius } from "../theme/radius.stylex"; 18 18 import { gray, primary } from "../theme/semantic-color.stylex"; 19 19 import { spacing } from "../theme/spacing.stylex"; 20 + import { Size } from "../theme/types"; 20 21 import { fontFamily, fontSize, lineHeight } from "../theme/typography.stylex"; 21 - import { Size } from "../types"; 22 22 23 23 const styles = stylex.create({ 24 24 wrapper: {
+4 -2
packages/hip-ui/src/components/color-field/index.tsx
··· 10 10 } from "react-aria-components"; 11 11 12 12 import { Description, Label } from "../label"; 13 + import { InputVariant, Size } from "../theme/types"; 13 14 import { useInputStyles } from "../theme/useInputStyles"; 14 - import { Size } from "../types"; 15 15 16 16 export interface ColorFieldProps 17 17 extends Omit<AriaColorFieldProps, "style" | "className">, ··· 21 21 description?: string; 22 22 errorMessage?: string | ((validation: ValidationResult) => string); 23 23 size?: Size; 24 + variant?: InputVariant; 24 25 prefix?: React.ReactNode; 25 26 suffix?: React.ReactNode; 26 27 } ··· 31 32 errorMessage, 32 33 style, 33 34 size, 35 + variant, 34 36 prefix, 35 37 suffix, 36 38 placeholder, 37 39 ...props 38 40 }: ColorFieldProps) { 39 41 const inputRef = useRef<HTMLInputElement>(null); 40 - const inputStyles = useInputStyles({ size }); 42 + const inputStyles = useInputStyles({ size, variant }); 41 43 42 44 return ( 43 45 <AriaColorField {...props} {...stylex.props(inputStyles.field, style)}>
+4 -2
packages/hip-ui/src/components/combobox/index.tsx
··· 18 18 import { Description, Label } from "../label"; 19 19 import { ListBox } from "../listbox"; 20 20 import { spacing } from "../theme/spacing.stylex"; 21 + import { InputVariant, Size } from "../theme/types"; 21 22 import { useInputStyles } from "../theme/useInputStyles"; 22 23 import { usePopoverStyles } from "../theme/usePopoverStyles"; 23 - import { Size } from "../types"; 24 24 import { SmallBody } from "../typography"; 25 25 26 26 const styles = stylex.create({ ··· 59 59 items?: Iterable<T>; 60 60 children: React.ReactNode | ((item: T) => React.ReactNode); 61 61 size?: Size; 62 + variant?: InputVariant; 62 63 placeholder?: string; 63 64 prefix?: React.ReactNode; 64 65 suffix?: React.ReactNode; ··· 72 73 items, 73 74 style, 74 75 size: sizeProp, 76 + variant, 75 77 shouldCloseOnInteractOutside, 76 78 shouldFlip, 77 79 shouldUpdatePosition, ··· 83 85 ...props 84 86 }: ComboBoxProps<T>) { 85 87 const size = sizeProp || use(SizeContext); 86 - const inputStyles = useInputStyles({ size }); 88 + const inputStyles = useInputStyles({ size, variant }); 87 89 const popoverStyles = usePopoverStyles(); 88 90 89 91 return (
+1 -1
packages/hip-ui/src/components/context-menu/index.tsx
··· 24 24 import { useMenuTriggerState } from "react-stately"; 25 25 26 26 import { SizeContext } from "../context"; 27 + import { Size } from "../theme/types"; 27 28 import { usePopoverStyles } from "../theme/usePopoverStyles"; 28 - import { Size } from "../types"; 29 29 30 30 const ContextMenuTriggerPropsContext = createContext< 31 31 AriaButtonProps & { ref?: React.Ref<HTMLDivElement> }
+1 -1
packages/hip-ui/src/components/context.ts
··· 1 1 import { createContext } from "react"; 2 2 3 - import { Size } from "./types"; 3 + import { Size } from "./theme/types"; 4 4 5 5 export const SizeContext = createContext<Size>("md");
+4 -2
packages/hip-ui/src/components/date-field/index.tsx
··· 11 11 } from "react-aria-components"; 12 12 13 13 import { Description, Label } from "../label"; 14 + import { InputVariant, Size } from "../theme/types"; 14 15 import { useInputStyles } from "../theme/useInputStyles"; 15 - import { Size } from "../types"; 16 16 17 17 export interface DateFieldProps<T extends DateValue> 18 18 extends Omit<AriaDateFieldProps<T>, "style" | "className"> { ··· 21 21 description?: string; 22 22 errorMessage?: string | ((validation: ValidationResult) => string); 23 23 size?: Size; 24 + variant?: InputVariant; 24 25 prefix?: React.ReactNode; 25 26 suffix?: React.ReactNode; 26 27 } ··· 31 32 errorMessage, 32 33 style, 33 34 size, 35 + variant, 34 36 prefix, 35 37 suffix, 36 38 ...props 37 39 }: DateFieldProps<T>) { 38 40 const inputRef = useRef<HTMLInputElement>(null); 39 - const inputStyles = useInputStyles({ size }); 41 + const inputStyles = useInputStyles({ size, variant }); 40 42 41 43 return ( 42 44 <AriaDateField {...props} {...stylex.props(inputStyles.field, style)}>
+1 -1
packages/hip-ui/src/components/dialog/index.tsx
··· 14 14 import { IconButton } from "../icon-button"; 15 15 import { slate } from "../theme/colors.stylex"; 16 16 import { spacing } from "../theme/spacing.stylex"; 17 + import { Size } from "../theme/types"; 17 18 import { typeramp } from "../theme/typography.stylex"; 18 19 import { useDialogStyles } from "../theme/useDialogStyles"; 19 - import { Size } from "../types"; 20 20 21 21 const styles = stylex.create({ 22 22 dialog: {
+1 -1
packages/hip-ui/src/components/icon-button/index.tsx
··· 7 7 import { Button } from "../button"; 8 8 import { SizeContext } from "../context"; 9 9 import { spacing } from "../theme/spacing.stylex"; 10 + import { ButtonVariant, Size } from "../theme/types"; 10 11 import { Tooltip } from "../tooltip"; 11 - import { ButtonVariant, Size } from "../types"; 12 12 13 13 const styles = stylex.create({ 14 14 sm: {
+1 -1
packages/hip-ui/src/components/label/index.tsx
··· 9 9 10 10 import { SizeContext } from "../context"; 11 11 import { gray } from "../theme/semantic-color.stylex"; 12 + import { Size } from "../theme/types"; 12 13 import { fontSize, fontWeight, lineHeight } from "../theme/typography.stylex"; 13 - import { Size } from "../types"; 14 14 15 15 const styles = stylex.create({ 16 16 label: {
+1 -1
packages/hip-ui/src/components/listbox/index.tsx
··· 16 16 import { Separator } from "../separator"; 17 17 import { gray } from "../theme/semantic-color.stylex"; 18 18 import { spacing } from "../theme/spacing.stylex"; 19 + import { Size } from "../theme/types"; 19 20 import { typeramp } from "../theme/typography.stylex"; 20 21 import { useListBoxItemStyles } from "../theme/useListBoxItemStyles"; 21 - import { Size } from "../types"; 22 22 23 23 const styles = stylex.create({ 24 24 listBox: {
+1 -1
packages/hip-ui/src/components/menu/index.tsx
··· 17 17 } from "react-aria-components"; 18 18 19 19 import { SizeContext } from "../context"; 20 + import { Size } from "../theme/types"; 20 21 import { useListBoxItemStyles } from "../theme/useListBoxItemStyles"; 21 22 import { usePopoverStyles } from "../theme/usePopoverStyles"; 22 - import { Size } from "../types"; 23 23 24 24 export interface MenuProps<T extends object> 25 25 extends Omit<MenuTriggerProps, "trigger" | "children">,
+4 -2
packages/hip-ui/src/components/number-field/index.tsx
··· 16 16 import { slate } from "../theme/colors.stylex"; 17 17 import { gray } from "../theme/semantic-color.stylex"; 18 18 import { spacing } from "../theme/spacing.stylex"; 19 + import { InputVariant, Size } from "../theme/types"; 19 20 import { useInputStyles } from "../theme/useInputStyles"; 20 - import { Size } from "../types"; 21 21 22 22 const styles = stylex.create({ 23 23 buttons: { ··· 59 59 description?: string; 60 60 errorMessage?: string | ((validation: ValidationResult) => string); 61 61 size?: Size; 62 + variant?: InputVariant; 62 63 prefix?: React.ReactNode; 63 64 suffix?: React.ReactNode; 64 65 } ··· 69 70 errorMessage, 70 71 style, 71 72 size, 73 + variant, 72 74 prefix, 73 75 suffix, 74 76 placeholder, 75 77 ...props 76 78 }: NumberFieldProps) { 77 79 const inputRef = useRef<HTMLInputElement>(null); 78 - const inputStyles = useInputStyles({ size }); 80 + const inputStyles = useInputStyles({ size, variant }); 79 81 const buttonStyles = stylex.props( 80 82 styles.button, 81 83 gray.borderInteractive,
+1 -1
packages/hip-ui/src/components/radio/index.tsx
··· 17 17 import { radius } from "../theme/radius.stylex"; 18 18 import { gray, primary } from "../theme/semantic-color.stylex"; 19 19 import { spacing } from "../theme/spacing.stylex"; 20 + import { Size } from "../theme/types"; 20 21 import { fontFamily, fontSize, lineHeight } from "../theme/typography.stylex"; 21 - import { Size } from "../types"; 22 22 23 23 const scaleIn = stylex.keyframes({ 24 24 "0%": {
+4 -2
packages/hip-ui/src/components/search-field/index.tsx
··· 13 13 import { IconButton } from "../icon-button"; 14 14 import { Description, Label } from "../label"; 15 15 import { spacing } from "../theme/spacing.stylex"; 16 + import { InputVariant, Size } from "../theme/types"; 16 17 import { useInputStyles } from "../theme/useInputStyles"; 17 - import { Size } from "../types"; 18 18 19 19 const styles = stylex.create({ 20 20 wrapper: { ··· 39 39 description?: string; 40 40 errorMessage?: string | ((validation: ValidationResult) => string); 41 41 size?: Size; 42 + variant?: InputVariant; 42 43 prefix?: React.ReactNode; 43 44 suffix?: React.ReactNode; 44 45 } ··· 51 52 errorMessage, 52 53 style, 53 54 size, 55 + variant, 54 56 prefix = defaultPrefix, 55 57 suffix, 56 58 placeholder, 57 59 ...props 58 60 }: SearchFieldProps) { 59 61 const inputRef = useRef<HTMLInputElement>(null); 60 - const inputStyles = useInputStyles({ size }); 62 + const inputStyles = useInputStyles({ size, variant }); 61 63 62 64 return ( 63 65 <AriaSearchField {...props} {...stylex.props(inputStyles.field, style)}>
+4 -2
packages/hip-ui/src/components/select/index.tsx
··· 16 16 import { SizeContext } from "../context"; 17 17 import { Description, Label } from "../label"; 18 18 import { ListBox } from "../listbox"; 19 + import { InputVariant, Size } from "../theme/types"; 19 20 import { useInputStyles } from "../theme/useInputStyles"; 20 21 import { usePopoverStyles } from "../theme/usePopoverStyles"; 21 - import { Size } from "../types"; 22 22 23 23 const styles = stylex.create({ 24 24 matchWidth: { ··· 42 42 items?: Iterable<T>; 43 43 children: React.ReactNode | ((item: T) => React.ReactNode); 44 44 size?: Size; 45 + variant?: InputVariant; 45 46 placeholder?: string; 46 47 prefix?: React.ReactNode; 47 48 suffix?: React.ReactNode; ··· 58 59 items, 59 60 style, 60 61 size: sizeProp, 62 + variant, 61 63 shouldCloseOnInteractOutside, 62 64 shouldFlip, 63 65 shouldUpdatePosition, ··· 68 70 ...props 69 71 }: SelectProps<T, M>) { 70 72 const size = sizeProp || use(SizeContext); 71 - const inputStyles = useInputStyles({ size }); 73 + const inputStyles = useInputStyles({ size, variant }); 72 74 const popoverStyles = usePopoverStyles(); 73 75 74 76 return (
+1 -1
packages/hip-ui/src/components/text-area/index.tsx
··· 16 16 import { radius } from "../theme/radius.stylex"; 17 17 import { gray } from "../theme/semantic-color.stylex"; 18 18 import { spacing } from "../theme/spacing.stylex"; 19 + import { Size } from "../theme/types"; 19 20 import { lineHeight, fontSize, fontFamily } from "../theme/typography.stylex"; 20 - import { Size } from "../types"; 21 21 22 22 const styles = stylex.create({ 23 23 wrapper: {
+4 -2
packages/hip-ui/src/components/text-field/index.tsx
··· 13 13 14 14 import { IconButton } from "../icon-button"; 15 15 import { Description, Label } from "../label"; 16 + import { InputVariant, Size } from "../theme/types"; 16 17 import { useInputStyles } from "../theme/useInputStyles"; 17 - import { Size } from "../types"; 18 18 19 19 function PasswordToggle({ 20 20 type, ··· 53 53 description?: string; 54 54 errorMessage?: string | ((validation: ValidationResult) => string); 55 55 size?: Size; 56 + variant?: InputVariant; 56 57 prefix?: React.ReactNode; 57 58 suffix?: React.ReactNode; 58 59 } ··· 63 64 errorMessage, 64 65 style, 65 66 size, 67 + variant, 66 68 prefix, 67 69 suffix, 68 70 placeholder, ··· 73 75 props.type || "text", 74 76 ); 75 77 const isPasswordInput = props.type === "password"; 76 - const inputStyles = useInputStyles({ size }); 78 + const inputStyles = useInputStyles({ size, variant }); 77 79 78 80 return ( 79 81 <AriaTextField
+10
packages/hip-ui/src/components/theme/types.ts
··· 2 2 3 3 export type ThemeKeys<T> = 4 4 T extends stylex.VarGroup<Readonly<infer Tokens>> ? keyof Tokens : never; 5 + 6 + export type Size = "sm" | "md" | "lg"; 7 + export type ButtonVariant = 8 + | "primary" 9 + | "secondary" 10 + | "tertiary" 11 + | "outline" 12 + | "critical" 13 + | "critical-outline"; 14 + export type InputVariant = "primary" | "secondary" | "tertiary";
+1 -1
packages/hip-ui/src/components/theme/useButtonStyles.ts
··· 5 5 6 6 import { ButtonGroupContext } from "../button/context"; 7 7 import { SizeContext } from "../context"; 8 - import { Size, ButtonVariant } from "../types"; 8 + import { Size, ButtonVariant } from "../theme/types"; 9 9 import { slate } from "./colors.stylex"; 10 10 import { radius } from "./radius.stylex"; 11 11 import { critical, gray, primary } from "./semantic-color.stylex";
+1 -1
packages/hip-ui/src/components/theme/useDialogStyles.ts
··· 5 5 import { animations } from "../theme/animations.stylex"; 6 6 import { radius } from "../theme/radius.stylex"; 7 7 import { shadow } from "../theme/shadow.stylex"; 8 - import { Size } from "../types"; 8 + import { Size } from "../theme/types"; 9 9 import { gray } from "./semantic-color.stylex"; 10 10 11 11 const styles = stylex.create({
+27 -13
packages/hip-ui/src/components/theme/useInputStyles.ts
··· 2 2 import { use } from "react"; 3 3 4 4 import { SizeContext } from "../context"; 5 - import { Size } from "../types"; 5 + import { InputVariant, Size } from "../theme/types"; 6 6 import { slate } from "./colors.stylex"; 7 7 import { radius } from "./radius.stylex"; 8 8 import { gray } from "./semantic-color.stylex"; ··· 41 41 }, 42 42 inputWrapper: { 43 43 borderRadius: radius["md"], 44 + borderWidth: 0, 44 45 boxSizing: "border-box", 45 46 display: "flex", 46 47 lineHeight: lineHeight["none"], 47 48 overflow: "hidden", 48 49 padding: 0, 49 - 50 - borderColor: { 51 - default: slate.border2, 52 - ":hover": slate.border3, 53 - ":focus": slate.solid1, 54 - }, 55 - borderStyle: "solid", 56 - borderWidth: 1, 57 - 58 - transitionProperty: "background-color, border-color", 59 50 }, 60 51 input: { 61 52 alignItems: "center", ··· 99 90 paddingLeft: spacing["1"], 100 91 paddingRight: spacing["2"], 101 92 }, 93 + primary: { 94 + borderColor: { 95 + default: slate.border2, 96 + ":hover": slate.border3, 97 + ":focus": slate.solid1, 98 + }, 99 + borderStyle: "solid", 100 + borderWidth: 1, 101 + transitionProperty: "background-color, border-color", 102 + }, 102 103 }); 103 104 104 - export function useInputStyles({ size: sizeProp }: { size?: Size }) { 105 + export function useInputStyles({ 106 + size: sizeProp, 107 + variant = "primary", 108 + }: { 109 + size: Size | undefined; 110 + variant: InputVariant | undefined; 111 + }) { 105 112 const size = sizeProp || use(SizeContext); 106 113 107 114 return { 108 115 field: [styles.field], 109 - wrapper: [styles.inputWrapper, gray.bgUi, gray.text, styles[size]], 116 + wrapper: [ 117 + styles.inputWrapper, 118 + variant === "primary" && [gray.bgUi, styles.primary], 119 + variant === "secondary" && [gray.bgUi], 120 + variant === "tertiary" && [gray.bgGhost], 121 + gray.text, 122 + styles[size], 123 + ], 110 124 input: [styles.input, styles[`${size}Input`]], 111 125 addon: styles.addon as unknown as stylex.StyleXStyles, 112 126 };
+4 -2
packages/hip-ui/src/components/time-field/index.tsx
··· 11 11 } from "react-aria-components"; 12 12 13 13 import { Description, Label } from "../label"; 14 + import { InputVariant, Size } from "../theme/types"; 14 15 import { useInputStyles } from "../theme/useInputStyles"; 15 - import { Size } from "../types"; 16 16 17 17 export interface TimeFieldProps<T extends TimeValue> 18 18 extends Omit<AriaTimeFieldProps<T>, "style" | "className"> { ··· 21 21 description?: string; 22 22 errorMessage?: string | ((validation: ValidationResult) => string); 23 23 size?: Size; 24 + variant?: InputVariant; 24 25 prefix?: React.ReactNode; 25 26 suffix?: React.ReactNode; 26 27 } ··· 31 32 errorMessage, 32 33 style, 33 34 size, 35 + variant, 34 36 prefix, 35 37 suffix, 36 38 ...props 37 39 }: TimeFieldProps<T>) { 38 40 const inputRef = useRef<HTMLInputElement>(null); 39 - const inputStyles = useInputStyles({ size }); 41 + const inputStyles = useInputStyles({ size, variant }); 40 42 41 43 return ( 42 44 <AriaTimeField {...props} {...stylex.props(inputStyles.field, style)}>
+1 -1
packages/hip-ui/src/components/toggle-button/index.tsx
··· 8 8 import { SizeContext } from "../context"; 9 9 import { plum, slate } from "../theme/colors.stylex"; 10 10 import { spacing } from "../theme/spacing.stylex"; 11 + import { ButtonVariant, Size } from "../theme/types"; 11 12 import { useButtonStyles } from "../theme/useButtonStyles"; 12 - import { ButtonVariant, Size } from "../types"; 13 13 14 14 const styles = stylex.create({ 15 15 primarySelected: {
+1 -1
packages/hip-ui/src/components/tree/index.tsx
··· 16 16 import { radius } from "../theme/radius.stylex"; 17 17 import { gray } from "../theme/semantic-color.stylex"; 18 18 import { spacing } from "../theme/spacing.stylex"; 19 + import { Size } from "../theme/types"; 19 20 import { useListBoxItemStyles } from "../theme/useListBoxItemStyles"; 20 - import { Size } from "../types"; 21 21 22 22 const styles = stylex.create({ 23 23 wrapper: {
-8
packages/hip-ui/src/components/types.ts
··· 1 - export type Size = "sm" | "md" | "lg"; 2 - export type ButtonVariant = 3 - | "primary" 4 - | "secondary" 5 - | "tertiary" 6 - | "outline" 7 - | "critical" 8 - | "critical-outline";