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.

sidebar layout

+680 -114
+1
apps/docs/src/components/drawer/index.tsx
··· 110 110 animationTimingFunction: animationTimingFunction.easeInOut, 111 111 }, 112 112 dialog: { 113 + overflow: "auto", 113 114 paddingBottom: spacing["2"], 114 115 paddingTop: spacing["2"], 115 116 },
+5 -1
apps/docs/src/components/footer/index.tsx
··· 42 42 ":is([data-footer-centered] *)": "center", 43 43 [containerBreakpoints.sm]: "center", 44 44 }, 45 + boxSizing: "border-box", 45 46 display: "flex", 46 47 flexDirection: { 47 48 default: "column", ··· 69 70 display: "grid", 70 71 gridTemplateColumns: { 71 72 default: "repeat(2, 1fr)", 72 - [containerBreakpoints.sm]: "repeat(4, 1fr)", 73 + [containerBreakpoints.sm]: { 74 + ":has(nth-child(2))": "repeat(3, 1fr)", 75 + ":has(nth-child(3))": "repeat(4, 1fr)", 76 + }, 73 77 }, 74 78 rowGap: spacing["6"], 75 79 },
+28 -22
apps/docs/src/components/header-layout/index.tsx
··· 2 2 3 3 import * as stylex from "@stylexjs/stylex"; 4 4 5 + import { primaryColor, uiColor } from "../theme/color.stylex"; 6 + import { containerBreakpoints } from "../theme/media-queries.stylex"; 5 7 import { spacing } from "../theme/spacing.stylex"; 6 8 import { StyleXComponentProps } from "../theme/types"; 7 - import { primaryColor, uiColor } from "../theme/color.stylex"; 8 - import { primary } from "../theme/semantic-color.stylex"; 9 - import { containerBreakpoints } from "../theme/media-queries.stylex"; 10 9 11 10 const styles = stylex.create({ 12 11 root: { 13 12 "--page-content-max-width": "1280px", 14 - minHeight: "100vh", 13 + backgroundColor: uiColor.bg, 14 + containerType: "inline-size", 15 15 display: "flex", 16 16 flexDirection: "column", 17 - backgroundColor: uiColor.bg, 18 - containerType: "inline-size", 17 + minHeight: "100vh", 19 18 width: "100cqw", 20 19 }, 21 20 header: { 22 21 flexShrink: 0, 23 22 }, 24 23 page: { 24 + boxSizing: "border-box", 25 25 flexGrow: 1, 26 + marginLeft: "auto", 27 + marginRight: "auto", 28 + maxWidth: "var(--page-content-max-width)", 26 29 minHeight: 0, 27 - paddingTop: { 28 - default: spacing["2"], 29 - [containerBreakpoints.sm]: spacing["6"], 30 - }, 31 30 paddingBottom: { 32 31 default: spacing["10"], 33 32 [containerBreakpoints.sm]: spacing["12"], ··· 40 39 default: spacing["4"], 41 40 [containerBreakpoints.sm]: spacing["8"], 42 41 }, 43 - maxWidth: "var(--page-content-max-width)", 42 + paddingTop: { 43 + default: spacing["2"], 44 + [containerBreakpoints.sm]: spacing["6"], 45 + }, 44 46 width: "100%", 45 - marginLeft: "auto", 46 - marginRight: "auto", 47 - boxSizing: "border-box", 48 47 }, 49 48 footer: { 50 49 flexShrink: 0, 51 50 }, 52 51 hero: { 53 - width: "100%", 54 - boxSizing: "border-box", 55 52 backgroundColor: primaryColor.solid1, 53 + boxSizing: "border-box", 56 54 color: primaryColor.textContrast, 57 - paddingTop: { 55 + paddingBottom: { 58 56 default: spacing["6"], 59 57 [containerBreakpoints.sm]: spacing["12"], 60 58 }, 61 - paddingBottom: { 59 + paddingTop: { 62 60 default: spacing["6"], 63 61 [containerBreakpoints.sm]: spacing["12"], 64 62 }, 63 + width: "100%", 65 64 }, 66 65 heroContent: { 67 - maxWidth: "var(--page-content-max-width)", 68 - width: "100%", 66 + boxSizing: "border-box", 69 67 marginLeft: "auto", 70 68 marginRight: "auto", 71 - boxSizing: "border-box", 69 + maxWidth: "var(--page-content-max-width)", 72 70 paddingLeft: { 73 71 default: spacing["4"], 74 72 [containerBreakpoints.sm]: spacing["8"], ··· 77 75 default: spacing["4"], 78 76 [containerBreakpoints.sm]: spacing["8"], 79 77 }, 78 + width: "100%", 80 79 }, 81 80 }); 82 81 ··· 91 90 style, 92 91 ...props 93 92 }: HeaderLayoutRootProps) => { 94 - return <div {...props} {...stylex.props(styles.root, style)} />; 93 + return ( 94 + <div 95 + {...props} 96 + {...stylex.props(styles.root, style)} 97 + data-header-layout={true} 98 + /> 99 + ); 95 100 }; 96 101 97 102 /** ··· 159 164 /** 160 165 * Header layout component with subcomponents. 161 166 */ 167 + // eslint-disable-next-line react-refresh/only-export-components 162 168 export const HeaderLayout = { 163 169 Root: HeaderLayoutRoot, 164 170 Header: HeaderLayoutHeader,
+23 -14
apps/docs/src/components/image-cropper/index.tsx
··· 1 1 "use client"; 2 2 3 + import type { ComponentProps } from "react"; 4 + 3 5 import { Cropper as OriginCropper } from "@origin-space/image-cropper"; 6 + import { useEffectEvent } from "@react-aria/utils"; 4 7 import * as stylex from "@stylexjs/stylex"; 5 - import { useEffect, useRef, type ComponentProps } from "react"; 8 + import { useEffect, useState } from "react"; 9 + 10 + import type { StyleXComponentProps } from "../theme/types"; 6 11 7 12 import { uiColor } from "../theme/color.stylex"; 8 13 import { radius } from "../theme/radius.stylex"; 9 - import { StyleXComponentProps } from "../theme/types"; 10 14 11 15 const styles = stylex.create({ 12 16 root: { ··· 162 166 children, 163 167 ...props 164 168 }: ImageCropperRootProps) { 165 - const imageUrlRef = useRef<string | null>(null); 166 - const imageUrl = 167 - typeof image === "string" ? image : URL.createObjectURL(image); 169 + const [imageUrl, setImageUrl] = useState<string>(() => ""); 168 170 169 171 const handleError = () => { 170 172 onCropChange?.(null); 171 173 }; 172 174 173 - // Store the object URL for cleanup 174 - useEffect(() => { 175 - if (typeof image !== "string") { 176 - imageUrlRef.current = imageUrl; 177 - } 175 + const createImageUrl = useEffectEvent(() => { 176 + // Create new URL for new image 177 + const newUrl = 178 + typeof image === "string" ? image : URL.createObjectURL(image); 179 + 180 + // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect 181 + setImageUrl(newUrl); 182 + 178 183 return () => { 179 - if (imageUrlRef.current) { 180 - URL.revokeObjectURL(imageUrlRef.current); 181 - imageUrlRef.current = null; 184 + if (newUrl.startsWith("blob:")) { 185 + URL.revokeObjectURL(newUrl); 182 186 } 183 187 }; 184 - }, [image, imageUrl]); 188 + }); 189 + 190 + // Update URL when image changes and clean up previous one 191 + useEffect(() => { 192 + return createImageUrl(); 193 + }, [image]); 185 194 186 195 return ( 187 196 <OriginCropper.Root
+165
apps/docs/src/components/sidebar-layout/index.tsx
··· 1 + "use client"; 2 + 3 + import * as stylex from "@stylexjs/stylex"; 4 + import { ArrowRightFromLineIcon } from "lucide-react"; 5 + 6 + import { Drawer } from "../drawer"; 7 + import { IconButton } from "../icon-button"; 8 + import { uiColor } from "../theme/color.stylex"; 9 + import { containerBreakpoints } from "../theme/media-queries.stylex"; 10 + import { spacing } from "../theme/spacing.stylex"; 11 + import { StyleXComponentProps } from "../theme/types"; 12 + 13 + const styles = stylex.create({ 14 + wrapper: { 15 + backgroundColor: uiColor.bgSubtle, 16 + position: "relative", 17 + width: "100cqw", 18 + }, 19 + root: { 20 + "--page-content-max-width": "1600px", 21 + containerType: "inline-size", 22 + display: "flex", 23 + flexDirection: "row", 24 + marginLeft: "auto", 25 + marginRight: "auto", 26 + maxWidth: "var(--page-content-max-width)", 27 + minHeight: "100vh", 28 + width: "100%", 29 + }, 30 + sidebar: { 31 + overflow: "auto", 32 + overscrollBehavior: "contain", 33 + boxSizing: "border-box", 34 + display: { 35 + default: "none", 36 + [containerBreakpoints.md]: "block", 37 + }, 38 + flexShrink: 0, 39 + position: "sticky", 40 + borderRightColor: uiColor.border1, 41 + borderRightStyle: "solid", 42 + borderRightWidth: 1, 43 + height: "100vh", 44 + overflowX: "hidden", 45 + overflowY: "auto", 46 + top: 0, 47 + }, 48 + drawer: { 49 + display: { 50 + default: "flex", 51 + [containerBreakpoints.md]: "none", 52 + }, 53 + position: "absolute", 54 + left: spacing["2"], 55 + top: spacing["2"], 56 + }, 57 + page: { 58 + backgroundColor: uiColor.bg, 59 + boxSizing: "border-box", 60 + containerType: "inline-size", 61 + flexGrow: 1, 62 + minHeight: 0, 63 + minWidth: 0, 64 + paddingBottom: { 65 + default: spacing["10"], 66 + [containerBreakpoints.sm]: spacing["20"], 67 + ":has(> [data-header-layout=true])": "0 !important", 68 + }, 69 + paddingLeft: { 70 + default: spacing["4"], 71 + [containerBreakpoints.sm]: spacing["16"], 72 + ":has(> [data-header-layout=true])": "0 !important", 73 + }, 74 + paddingRight: { 75 + default: spacing["4"], 76 + [containerBreakpoints.sm]: spacing["16"], 77 + ":has(> [data-header-layout=true])": "0 !important", 78 + }, 79 + paddingTop: { 80 + default: spacing["2"], 81 + [containerBreakpoints.sm]: spacing["10"], 82 + ":has(> [data-header-layout=true])": "0 !important", 83 + }, 84 + width: "100%", 85 + }, 86 + }); 87 + 88 + /** 89 + * Sidebar layout root component. Main container for the sidebar layout. 90 + */ 91 + export interface SidebarLayoutRootProps extends StyleXComponentProps< 92 + React.ComponentProps<"div"> 93 + > {} 94 + 95 + export const SidebarLayoutRoot = ({ 96 + style, 97 + children, 98 + ...props 99 + }: SidebarLayoutRootProps) => { 100 + return ( 101 + <div {...props} {...stylex.props(styles.wrapper, style)}> 102 + <div {...stylex.props(styles.root)}>{children}</div> 103 + </div> 104 + ); 105 + }; 106 + 107 + /** 108 + * Sidebar layout sidebar component. Slot for sidebar content. 109 + */ 110 + export interface SidebarLayoutSidebarProps extends StyleXComponentProps< 111 + React.ComponentProps<"aside"> 112 + > {} 113 + 114 + export const SidebarLayoutSidebar = ({ 115 + style, 116 + children, 117 + ...props 118 + }: SidebarLayoutSidebarProps) => { 119 + return ( 120 + <> 121 + <aside {...props} {...stylex.props(styles.sidebar, style)}> 122 + {children} 123 + </aside> 124 + <Drawer 125 + trigger={ 126 + <IconButton 127 + label="Open Navigation" 128 + variant="outline" 129 + style={styles.drawer as unknown as stylex.StyleXStyles} 130 + > 131 + <ArrowRightFromLineIcon /> 132 + </IconButton> 133 + } 134 + direction="left" 135 + size="sm" 136 + > 137 + {children} 138 + </Drawer> 139 + </> 140 + ); 141 + }; 142 + 143 + /** 144 + * Sidebar layout page component. Slot for main page content. 145 + */ 146 + export interface SidebarLayoutPageProps extends StyleXComponentProps< 147 + React.ComponentProps<"main"> 148 + > {} 149 + 150 + export const SidebarLayoutPage = ({ 151 + style, 152 + ...props 153 + }: SidebarLayoutPageProps) => { 154 + return <main {...props} {...stylex.props(styles.page, style)} />; 155 + }; 156 + 157 + /** 158 + * Sidebar layout component with subcomponents. 159 + */ 160 + // eslint-disable-next-line react-refresh/only-export-components 161 + export const SidebarLayout = { 162 + Root: SidebarLayoutRoot, 163 + Sidebar: SidebarLayoutSidebar, 164 + Page: SidebarLayoutPage, 165 + };
+9 -3
apps/docs/src/components/sidebar/index.tsx
··· 44 44 alignItems: "center", 45 45 display: "flex", 46 46 justifyContent: "space-between", 47 - marginBottom: spacing["4"], 48 47 }, 49 48 sidebarHeaderLink: { 50 49 textDecoration: "none", ··· 53 52 height: spacing["6"], 54 53 paddingLeft: spacing["3"], 55 54 paddingRight: spacing["3"], 56 - paddingTop: spacing["4"], 55 + paddingTop: { 56 + ":is([data-sidebar-group] *)": spacing["4"], 57 + }, 57 58 }, 58 59 sidebarSectionList: { 59 60 margin: 0, ··· 80 81 ":is([data-hovered=true])": uiColor.component2, 81 82 ":is([data-pressed=true])": uiColor.component3, 82 83 }, 84 + boxSizing: "border-box", 83 85 color: uiColor.text2, 84 86 display: "flex", 85 87 fontSize: fontSize["sm"], ··· 92 94 height: spacing["8"], 93 95 paddingLeft: spacing["3"], 94 96 paddingRight: spacing["3"], 97 + width: "100%", 95 98 }, 96 99 sidebarItemActive: { 97 100 backgroundColor: { ··· 245 248 {title} 246 249 </Button> 247 250 </Heading> 248 - <DisclosurePanel {...stylex.props(styles.sidebarGroupPanel)}> 251 + <DisclosurePanel 252 + {...stylex.props(styles.sidebarGroupPanel)} 253 + data-sidebar-group 254 + > 249 255 <div {...stylex.props(styles.sidebarGroupPanelContent)}>{children}</div> 250 256 </DisclosurePanel> 251 257 </Disclosure>
+17
apps/docs/src/components/theme/media-queries.stylex.tsx
··· 16 16 "2xl": "@container (min-width: 96rem)", 17 17 }); 18 18 19 + /** 20 + * Named container breakpoints that query parent containers only. 21 + * Use these when you want to query a parent container by name, 22 + * preventing elements with containerType from querying themselves. 23 + * 24 + * Usage: 25 + * 1. Set container-name on the parent: containerName: "parent" 26 + * 2. Use parentContainerBreakpoints in child styles 27 + */ 28 + export const parentContainerBreakpoints = stylex.defineConsts({ 29 + sm: "@container parent (min-width: 40rem)", 30 + md: "@container parent (min-width: 48rem)", 31 + lg: "@container parent (min-width: 64rem)", 32 + xl: "@container parent (min-width: 80rem)", 33 + "2xl": "@container parent (min-width: 96rem)", 34 + }); 35 + 19 36 export const maxBreakpoints = stylex.defineConsts({ 20 37 sm: "@media (max-width: 40rem)", 21 38 });
+1 -1
apps/docs/src/components/toast/Toast.tsx
··· 59 59 color: { 60 60 default: uiColor.text1, 61 61 ":is([data-variant=critical] *)": criticalColor.textContrast, 62 - ":is([data-variant=success] *)": successColor.textContrast, 62 + ":is([data-variant=success] *)": successColor.text2, 63 63 ":is([data-variant=warning] *)": warningColor.text2, 64 64 }, 65 65 fontWeight: 600,
+40
apps/docs/src/docs/components/navigation/sidebar-layout.mdx
··· 1 + --- 2 + title: SidebarLayout 3 + description: A flexible page layout component with unstyled slots for sidebar and page content. 4 + --- 5 + 6 + import { PropDocs } from '../../../lib/PropDocs' 7 + import { Example } from '../../../lib/Example' 8 + import { Basic } from '../../../examples/sidebar-layout/basic' 9 + import { WithHeaderLayout } from '../../../examples/sidebar-layout/with-header-layout' 10 + 11 + <Example src={Basic} noPadding /> 12 + 13 + ## Installation 14 + 15 + Run the following command to add the sidebar layout component to your project. 16 + 17 + ```bash 18 + pnpm hip install sidebar-layout 19 + ``` 20 + 21 + ## Props 22 + 23 + This is a custom component built for page layouts with sidebar and content slots. 24 + 25 + <PropDocs components={["SidebarLayoutRoot", "SidebarLayoutSidebar", "SidebarLayoutPage"]} /> 26 + 27 + ## Features 28 + 29 + ### With Header Layout 30 + 31 + You can combine the sidebar layout with the header layout to create a page layout with a sidebar and header. 32 + 33 + <Example src={WithHeaderLayout} noPadding /> 34 + 35 + ## Related Components 36 + 37 + - [Flex](/docs/components/flex) - For flexible layouts within slots 38 + - [Grid](/docs/components/grid) - For two-dimensional layouts within slots 39 + - [Sidebar](/docs/components/sidebar) - For navigation in the sidebar slot 40 +
+53
apps/docs/src/examples/sidebar-layout/basic.tsx
··· 1 + import { SidebarLayout } from "@/components/sidebar-layout"; 2 + import { 3 + Sidebar, 4 + SidebarHeader, 5 + SidebarItem, 6 + SidebarSection, 7 + } from "@/components/sidebar"; 8 + import { Link } from "@/components/link"; 9 + import { Text } from "@/components/typography/text"; 10 + import { Body, Heading1 } from "@/components/typography"; 11 + import { Content } from "@/components/content"; 12 + 13 + function Logo() { 14 + return ( 15 + <svg 16 + width="32" 17 + height="32" 18 + viewBox="0 0 120 120" 19 + fill="black" 20 + xmlns="http://www.w3.org/2000/svg" 21 + > 22 + <circle cx="60" cy="60" r="50" stroke="black" strokeWidth="2" /> 23 + </svg> 24 + ); 25 + } 26 + 27 + export function Basic() { 28 + return ( 29 + <SidebarLayout.Root> 30 + <SidebarLayout.Sidebar> 31 + <Sidebar> 32 + <SidebarHeader href="/"> 33 + <Logo /> 34 + </SidebarHeader> 35 + <SidebarSection title="Navigation"> 36 + <SidebarItem>Dashboard</SidebarItem> 37 + <SidebarItem>Projects</SidebarItem> 38 + <SidebarItem>Settings</SidebarItem> 39 + </SidebarSection> 40 + </Sidebar> 41 + </SidebarLayout.Sidebar> 42 + <SidebarLayout.Page> 43 + <Content> 44 + <Heading1>Page Content</Heading1> 45 + <Body> 46 + This is the main page content area. The sidebar is on the left, and 47 + this content area takes up the remaining space. 48 + </Body> 49 + </Content> 50 + </SidebarLayout.Page> 51 + </SidebarLayout.Root> 52 + ); 53 + }
+85
apps/docs/src/examples/sidebar-layout/with-header-layout.tsx
··· 1 + import { SidebarLayout } from "@/components/sidebar-layout"; 2 + import { 3 + Sidebar, 4 + SidebarHeader, 5 + SidebarItem, 6 + SidebarSection, 7 + } from "@/components/sidebar"; 8 + import { HeaderLayout } from "@/components/header-layout"; 9 + import { 10 + Navbar, 11 + NavbarLink, 12 + NavbarLogo, 13 + NavbarNavigation, 14 + } from "@/components/navbar"; 15 + import { Footer } from "@/components/footer"; 16 + import { Content } from "@/components/content"; 17 + import { Heading1 } from "@/components/typography"; 18 + import { Body } from "@/components/typography"; 19 + 20 + function Logo() { 21 + return ( 22 + <svg 23 + width="32" 24 + height="32" 25 + viewBox="0 0 120 120" 26 + fill="black" 27 + xmlns="http://www.w3.org/2000/svg" 28 + > 29 + <circle cx="60" cy="60" r="50" stroke="black" strokeWidth="2" /> 30 + </svg> 31 + ); 32 + } 33 + 34 + export function WithHeaderLayout() { 35 + return ( 36 + <SidebarLayout.Root> 37 + <SidebarLayout.Sidebar> 38 + <Sidebar> 39 + <SidebarHeader href="/"> 40 + <Logo /> 41 + </SidebarHeader> 42 + <SidebarSection title="Navigation"> 43 + <SidebarItem>Dashboard</SidebarItem> 44 + <SidebarItem>Projects</SidebarItem> 45 + <SidebarItem>Settings</SidebarItem> 46 + </SidebarSection> 47 + </Sidebar> 48 + </SidebarLayout.Sidebar> 49 + <SidebarLayout.Page> 50 + <HeaderLayout.Root> 51 + <HeaderLayout.Header> 52 + <Navbar> 53 + <NavbarLogo> 54 + <Logo /> 55 + </NavbarLogo> 56 + <NavbarNavigation justify="right"> 57 + <NavbarLink href="/dashboard">Dashboard</NavbarLink> 58 + <NavbarLink href="/projects">Projects</NavbarLink> 59 + <NavbarLink href="/settings">Settings</NavbarLink> 60 + </NavbarNavigation> 61 + </Navbar> 62 + </HeaderLayout.Header> 63 + <HeaderLayout.Page> 64 + <Content> 65 + <Heading1>Page Content</Heading1> 66 + <Body> 67 + This is the main page content area. The sidebar is on the left, 68 + and this content area takes up the remaining space. 69 + </Body> 70 + </Content> 71 + </HeaderLayout.Page> 72 + <HeaderLayout.Footer> 73 + <Footer.Root> 74 + <Footer.Section> 75 + <Footer.Copyright> 76 + © 2025 Company Name. All rights reserved. 77 + </Footer.Copyright> 78 + </Footer.Section> 79 + </Footer.Root> 80 + </HeaderLayout.Footer> 81 + </HeaderLayout.Root> 82 + </SidebarLayout.Page> 83 + </SidebarLayout.Root> 84 + ); 85 + }
+6 -3
apps/docs/src/lib/TableOfContents.tsx
··· 6 6 import { primaryColor, uiColor } from "../components/theme/color.stylex"; 7 7 import { spacing } from "../components/theme/spacing.stylex"; 8 8 import { fontSize } from "../components/theme/typography.stylex"; 9 + import { StyleXComponentProps } from "@/components/theme/types"; 9 10 10 11 const ActiveHeaderIdContext = createContext<string | null>(null); 11 12 const LevelContext = createContext(1); ··· 21 22 marginTop: spacing["12"], 22 23 overflow: "auto", 23 24 paddingBottom: spacing["20"], 24 - paddingRight: spacing["8"], 25 25 paddingTop: spacing["12"], 26 26 position: "sticky", 27 27 top: 0, ··· 115 115 ); 116 116 } 117 117 118 - export function TableOfContents({ toc }: { toc: Toc }) { 118 + export function TableOfContents({ 119 + toc, 120 + style, 121 + }: StyleXComponentProps<{ toc: Toc }>) { 119 122 const [activeHeaderId, setActiveHeaderId] = useState<string | null>(null); 120 123 121 124 useEffect(() => { ··· 140 143 141 144 return ( 142 145 <ActiveHeaderIdContext value={activeHeaderId}> 143 - <nav {...stylex.props(styles.wrapper)}> 146 + <nav {...stylex.props(styles.wrapper, style)}> 144 147 <LevelContext value={1}> 145 148 <ul {...stylex.props(styles.itemList)}> 146 149 {toc.map((item) => (
+20 -18
apps/docs/src/routes/docs.$.tsx
··· 30 30 import { modules, pages } from "virtual:content"; 31 31 32 32 import { Flex } from "@/components/flex"; 33 - import { Grid } from "@/components/grid"; 34 33 import { LinkProps, Link as TypographyLink } from "@/components/link"; 35 34 import { 36 35 Blockquote, ··· 55 54 import { uiColor } from "../components/theme/color.stylex"; 56 55 import { spacing } from "../components/theme/spacing.stylex"; 57 56 import { Content } from "@/components/content"; 57 + import { containerBreakpoints } from "../components/theme/media-queries.stylex"; 58 58 59 59 const TypographyRouterLink = createLink(TypographyLink); 60 60 61 61 const styles = stylex.create({ 62 62 root: { 63 63 width: "100%", 64 - }, 65 - main: { 66 - flexGrow: 1, 67 - maxWidth: "100ch", 68 - minWidth: 0, 69 - paddingBottom: spacing["20"], 70 - paddingLeft: spacing["16"], 71 - paddingRight: spacing["16"], 72 - paddingTop: spacing["20"], 64 + gap: { 65 + default: spacing["4"], 66 + [containerBreakpoints.lg]: spacing["8"], 67 + }, 68 + display: "grid", 69 + gridTemplateColumns: { 70 + default: "minmax(0, 1fr)", 71 + [containerBreakpoints.lg]: "minmax(0, 1fr) 240px", 72 + }, 73 73 }, 74 74 pre: { 75 75 borderColor: uiColor.border2, ··· 116 116 "@media (prefers-reduced-motion: reduce)": "none", 117 117 }, 118 118 transitionTimingFunction: "ease-in-out", 119 + }, 120 + tableOfContents: { 121 + display: { 122 + default: "none", 123 + [containerBreakpoints.lg]: "block", 124 + }, 119 125 }, 120 126 }); 121 127 ··· 289 295 } 290 296 291 297 return ( 292 - <Grid 293 - columns="minmax(0, max-content) 240px" 294 - columnGap="4" 295 - style={styles.root} 296 - > 297 - <Content style={styles.main}> 298 + <div {...stylex.props(styles.root)}> 299 + <Content> 298 300 <Flex direction="column" gap="4" style={styles.header}> 299 301 <Heading1>{doc.title}</Heading1> 300 302 <Text size="xl" variant="secondary"> ··· 305 307 <Page components={components} /> 306 308 </Suspense> 307 309 </Content> 308 - {toc && <TableOfContents toc={toc} />} 309 - </Grid> 310 + {toc && <TableOfContents toc={toc} style={styles.tableOfContents} />} 311 + </div> 310 312 ); 311 313 }
+7 -27
apps/docs/src/routes/docs.tsx
··· 1 - import * as stylex from "@stylexjs/stylex"; 2 1 import { 3 2 createFileRoute, 4 3 LinkProps, ··· 11 10 import { Moon, Sun } from "lucide-react"; 12 11 import { useEffect, useState } from "react"; 13 12 14 - import { Grid } from "@/components/grid"; 15 13 import { IconButton } from "@/components/icon-button"; 16 14 import { 17 15 Sidebar, ··· 22 20 } from "@/components/sidebar"; 23 21 import { Text } from "@/components/typography/text"; 24 22 25 - import { uiColor } from "../components/theme/color.stylex"; 26 23 import { Flex } from "@/components/flex"; 27 24 import { ThemePicker } from "@/lib/ThemePicker"; 25 + import { SidebarLayout } from "@/components/sidebar-layout"; 28 26 29 27 const SidebarItemLink = createLink(SidebarItem); 30 - 31 - const styles = stylex.create({ 32 - root: { 33 - width: "100%", 34 - }, 35 - aside: { 36 - backgroundColor: uiColor.bgSubtle, 37 - borderRightColor: uiColor.border2, 38 - borderRightStyle: "solid", 39 - borderRightWidth: 1, 40 - boxSizing: "border-box", 41 - height: "100vh", 42 - overflow: "auto", 43 - overscrollBehavior: "contain", 44 - position: "sticky", 45 - top: 0, 46 - }, 47 - }); 48 28 49 29 interface SidebarItem { 50 30 id: string; ··· 244 224 245 225 function RouteComponent() { 246 226 return ( 247 - <Grid columns="max-content 1fr" columnGap="4" style={styles.root}> 248 - <aside {...stylex.props(styles.aside)}> 227 + <SidebarLayout.Root> 228 + <SidebarLayout.Sidebar> 249 229 <DocSidebar /> 250 - </aside> 251 - <main> 230 + </SidebarLayout.Sidebar> 231 + <SidebarLayout.Page> 252 232 <Outlet /> 253 - </main> 254 - </Grid> 233 + </SidebarLayout.Page> 234 + </SidebarLayout.Root> 255 235 ); 256 236 }
+2
packages/hip-ui/src/cli/install.tsx
··· 46 46 import { gridConfig } from "../components/grid/grid-config.js"; 47 47 import { headerLayoutConfig } from "../components/header-layout/header-layout-config.js"; 48 48 import { hoverCardConfig } from "../components/hover-card/hover-card-config.js"; 49 + import { sidebarLayoutConfig } from "../components/sidebar-layout/sidebar-layout-config.js"; 49 50 import { iconButtonConfig } from "../components/icon-button/icon-button-config.js"; 50 51 import { imageCropperConfig } from "../components/image-cropper/image-cropper-config.js"; 51 52 import { kbdConfig } from "../components/kbd/kbd-config.js"; ··· 143 144 footerConfig, 144 145 formConfig, 145 146 headerLayoutConfig, 147 + sidebarLayoutConfig, 146 148 tableConfig, 147 149 sliderConfig, 148 150 tagGroupConfig,
+1
packages/hip-ui/src/components/drawer/index.tsx
··· 110 110 animationTimingFunction: animationTimingFunction.easeInOut, 111 111 }, 112 112 dialog: { 113 + overflow: "auto", 113 114 paddingBottom: spacing["2"], 114 115 paddingTop: spacing["2"], 115 116 },
+28 -22
packages/hip-ui/src/components/header-layout/index.tsx
··· 2 2 3 3 import * as stylex from "@stylexjs/stylex"; 4 4 5 + import { primaryColor, uiColor } from "../theme/color.stylex"; 6 + import { containerBreakpoints } from "../theme/media-queries.stylex"; 5 7 import { spacing } from "../theme/spacing.stylex"; 6 8 import { StyleXComponentProps } from "../theme/types"; 7 - import { primaryColor, uiColor } from "../theme/color.stylex"; 8 - import { primary } from "../theme/semantic-color.stylex"; 9 - import { containerBreakpoints } from "../theme/media-queries.stylex"; 10 9 11 10 const styles = stylex.create({ 12 11 root: { 13 12 "--page-content-max-width": "1280px", 14 - minHeight: "100vh", 13 + backgroundColor: uiColor.bg, 14 + containerType: "inline-size", 15 15 display: "flex", 16 16 flexDirection: "column", 17 - backgroundColor: uiColor.bg, 18 - containerType: "inline-size", 17 + minHeight: "100vh", 19 18 width: "100cqw", 20 19 }, 21 20 header: { 22 21 flexShrink: 0, 23 22 }, 24 23 page: { 24 + boxSizing: "border-box", 25 25 flexGrow: 1, 26 + marginLeft: "auto", 27 + marginRight: "auto", 28 + maxWidth: "var(--page-content-max-width)", 26 29 minHeight: 0, 27 - paddingTop: { 28 - default: spacing["2"], 29 - [containerBreakpoints.sm]: spacing["6"], 30 - }, 31 30 paddingBottom: { 32 31 default: spacing["10"], 33 32 [containerBreakpoints.sm]: spacing["12"], ··· 40 39 default: spacing["4"], 41 40 [containerBreakpoints.sm]: spacing["8"], 42 41 }, 43 - maxWidth: "var(--page-content-max-width)", 42 + paddingTop: { 43 + default: spacing["2"], 44 + [containerBreakpoints.sm]: spacing["6"], 45 + }, 44 46 width: "100%", 45 - marginLeft: "auto", 46 - marginRight: "auto", 47 - boxSizing: "border-box", 48 47 }, 49 48 footer: { 50 49 flexShrink: 0, 51 50 }, 52 51 hero: { 53 - width: "100%", 54 - boxSizing: "border-box", 55 52 backgroundColor: primaryColor.solid1, 53 + boxSizing: "border-box", 56 54 color: primaryColor.textContrast, 57 - paddingTop: { 55 + paddingBottom: { 58 56 default: spacing["6"], 59 57 [containerBreakpoints.sm]: spacing["12"], 60 58 }, 61 - paddingBottom: { 59 + paddingTop: { 62 60 default: spacing["6"], 63 61 [containerBreakpoints.sm]: spacing["12"], 64 62 }, 63 + width: "100%", 65 64 }, 66 65 heroContent: { 67 - maxWidth: "var(--page-content-max-width)", 68 - width: "100%", 66 + boxSizing: "border-box", 69 67 marginLeft: "auto", 70 68 marginRight: "auto", 71 - boxSizing: "border-box", 69 + maxWidth: "var(--page-content-max-width)", 72 70 paddingLeft: { 73 71 default: spacing["4"], 74 72 [containerBreakpoints.sm]: spacing["8"], ··· 77 75 default: spacing["4"], 78 76 [containerBreakpoints.sm]: spacing["8"], 79 77 }, 78 + width: "100%", 80 79 }, 81 80 }); 82 81 ··· 91 90 style, 92 91 ...props 93 92 }: HeaderLayoutRootProps) => { 94 - return <div {...props} {...stylex.props(styles.root, style)} />; 93 + return ( 94 + <div 95 + {...props} 96 + {...stylex.props(styles.root, style)} 97 + data-header-layout={true} 98 + /> 99 + ); 95 100 }; 96 101 97 102 /** ··· 159 164 /** 160 165 * Header layout component with subcomponents. 161 166 */ 167 + // eslint-disable-next-line react-refresh/only-export-components 162 168 export const HeaderLayout = { 163 169 Root: HeaderLayoutRoot, 164 170 Header: HeaderLayoutHeader,
+165
packages/hip-ui/src/components/sidebar-layout/index.tsx
··· 1 + "use client"; 2 + 3 + import * as stylex from "@stylexjs/stylex"; 4 + import { ArrowRightFromLineIcon } from "lucide-react"; 5 + 6 + import { Drawer } from "../drawer"; 7 + import { IconButton } from "../icon-button"; 8 + import { uiColor } from "../theme/color.stylex"; 9 + import { containerBreakpoints } from "../theme/media-queries.stylex"; 10 + import { spacing } from "../theme/spacing.stylex"; 11 + import { StyleXComponentProps } from "../theme/types"; 12 + 13 + const styles = stylex.create({ 14 + wrapper: { 15 + backgroundColor: uiColor.bgSubtle, 16 + position: "relative", 17 + width: "100cqw", 18 + }, 19 + root: { 20 + "--page-content-max-width": "1600px", 21 + containerType: "inline-size", 22 + display: "flex", 23 + flexDirection: "row", 24 + marginLeft: "auto", 25 + marginRight: "auto", 26 + maxWidth: "var(--page-content-max-width)", 27 + minHeight: "100vh", 28 + width: "100%", 29 + }, 30 + sidebar: { 31 + overflow: "auto", 32 + overscrollBehavior: "contain", 33 + boxSizing: "border-box", 34 + display: { 35 + default: "none", 36 + [containerBreakpoints.md]: "block", 37 + }, 38 + flexShrink: 0, 39 + position: "sticky", 40 + borderRightColor: uiColor.border1, 41 + borderRightStyle: "solid", 42 + borderRightWidth: 1, 43 + height: "100vh", 44 + overflowX: "hidden", 45 + overflowY: "auto", 46 + top: 0, 47 + }, 48 + drawer: { 49 + display: { 50 + default: "flex", 51 + [containerBreakpoints.md]: "none", 52 + }, 53 + position: "absolute", 54 + left: spacing["2"], 55 + top: spacing["2"], 56 + }, 57 + page: { 58 + backgroundColor: uiColor.bg, 59 + boxSizing: "border-box", 60 + containerType: "inline-size", 61 + flexGrow: 1, 62 + minHeight: 0, 63 + minWidth: 0, 64 + paddingBottom: { 65 + default: spacing["10"], 66 + [containerBreakpoints.sm]: spacing["20"], 67 + ":has(> [data-header-layout=true])": "0 !important", 68 + }, 69 + paddingLeft: { 70 + default: spacing["4"], 71 + [containerBreakpoints.sm]: spacing["16"], 72 + ":has(> [data-header-layout=true])": "0 !important", 73 + }, 74 + paddingRight: { 75 + default: spacing["4"], 76 + [containerBreakpoints.sm]: spacing["16"], 77 + ":has(> [data-header-layout=true])": "0 !important", 78 + }, 79 + paddingTop: { 80 + default: spacing["2"], 81 + [containerBreakpoints.sm]: spacing["10"], 82 + ":has(> [data-header-layout=true])": "0 !important", 83 + }, 84 + width: "100%", 85 + }, 86 + }); 87 + 88 + /** 89 + * Sidebar layout root component. Main container for the sidebar layout. 90 + */ 91 + export interface SidebarLayoutRootProps extends StyleXComponentProps< 92 + React.ComponentProps<"div"> 93 + > {} 94 + 95 + export const SidebarLayoutRoot = ({ 96 + style, 97 + children, 98 + ...props 99 + }: SidebarLayoutRootProps) => { 100 + return ( 101 + <div {...props} {...stylex.props(styles.wrapper, style)}> 102 + <div {...stylex.props(styles.root)}>{children}</div> 103 + </div> 104 + ); 105 + }; 106 + 107 + /** 108 + * Sidebar layout sidebar component. Slot for sidebar content. 109 + */ 110 + export interface SidebarLayoutSidebarProps extends StyleXComponentProps< 111 + React.ComponentProps<"aside"> 112 + > {} 113 + 114 + export const SidebarLayoutSidebar = ({ 115 + style, 116 + children, 117 + ...props 118 + }: SidebarLayoutSidebarProps) => { 119 + return ( 120 + <> 121 + <aside {...props} {...stylex.props(styles.sidebar, style)}> 122 + {children} 123 + </aside> 124 + <Drawer 125 + trigger={ 126 + <IconButton 127 + label="Open Navigation" 128 + variant="outline" 129 + style={styles.drawer as unknown as stylex.StyleXStyles} 130 + > 131 + <ArrowRightFromLineIcon /> 132 + </IconButton> 133 + } 134 + direction="left" 135 + size="sm" 136 + > 137 + {children} 138 + </Drawer> 139 + </> 140 + ); 141 + }; 142 + 143 + /** 144 + * Sidebar layout page component. Slot for main page content. 145 + */ 146 + export interface SidebarLayoutPageProps extends StyleXComponentProps< 147 + React.ComponentProps<"main"> 148 + > {} 149 + 150 + export const SidebarLayoutPage = ({ 151 + style, 152 + ...props 153 + }: SidebarLayoutPageProps) => { 154 + return <main {...props} {...stylex.props(styles.page, style)} />; 155 + }; 156 + 157 + /** 158 + * Sidebar layout component with subcomponents. 159 + */ 160 + // eslint-disable-next-line react-refresh/only-export-components 161 + export const SidebarLayout = { 162 + Root: SidebarLayoutRoot, 163 + Sidebar: SidebarLayoutSidebar, 164 + Page: SidebarLayoutPage, 165 + };
+15
packages/hip-ui/src/components/sidebar-layout/sidebar-layout-config.ts
··· 1 + import { ComponentConfig } from "../../types"; 2 + 3 + export const sidebarLayoutConfig: ComponentConfig = { 4 + name: "sidebar-layout", 5 + filepath: "./index.tsx", 6 + hipDependencies: [ 7 + "../theme/spacing.stylex.tsx", 8 + "../theme/color.stylex.tsx", 9 + "../theme/semantic-color.stylex.tsx", 10 + "../theme/media-queries.stylex.tsx", 11 + "../theme/types.ts", 12 + ], 13 + dependencies: {}, 14 + }; 15 +
+9 -3
packages/hip-ui/src/components/sidebar/index.tsx
··· 44 44 alignItems: "center", 45 45 display: "flex", 46 46 justifyContent: "space-between", 47 - marginBottom: spacing["4"], 48 47 }, 49 48 sidebarHeaderLink: { 50 49 textDecoration: "none", ··· 53 52 height: spacing["6"], 54 53 paddingLeft: spacing["3"], 55 54 paddingRight: spacing["3"], 56 - paddingTop: spacing["4"], 55 + paddingTop: { 56 + ":is([data-sidebar-group] *)": spacing["4"], 57 + }, 57 58 }, 58 59 sidebarSectionList: { 59 60 margin: 0, ··· 80 81 ":is([data-hovered=true])": uiColor.component2, 81 82 ":is([data-pressed=true])": uiColor.component3, 82 83 }, 84 + boxSizing: "border-box", 83 85 color: uiColor.text2, 84 86 display: "flex", 85 87 fontSize: fontSize["sm"], ··· 92 94 height: spacing["8"], 93 95 paddingLeft: spacing["3"], 94 96 paddingRight: spacing["3"], 97 + width: "100%", 95 98 }, 96 99 sidebarItemActive: { 97 100 backgroundColor: { ··· 245 248 {title} 246 249 </Button> 247 250 </Heading> 248 - <DisclosurePanel {...stylex.props(styles.sidebarGroupPanel)}> 251 + <DisclosurePanel 252 + {...stylex.props(styles.sidebarGroupPanel)} 253 + data-sidebar-group 254 + > 249 255 <div {...stylex.props(styles.sidebarGroupPanelContent)}>{children}</div> 250 256 </DisclosurePanel> 251 257 </Disclosure>