diff --git a/site/src/App/routes/examples/basic-form/basic-form.tsx b/site/src/App/routes/examples/basic-form/basic-form.tsx index 0b6da24dfdc..424c47026b1 100644 --- a/site/src/App/routes/examples/basic-form/basic-form.tsx +++ b/site/src/App/routes/examples/basic-form/basic-form.tsx @@ -97,7 +97,7 @@ const page: Page = { using Playroom. - + At any stage you can click the “Open in Playroom” button under the examples to view the design across themes and viewports. diff --git a/site/src/App/routes/examples/job-summary/job-summary.tsx b/site/src/App/routes/examples/job-summary/job-summary.tsx index d3d0b5d595d..a1de9bc20fc 100644 --- a/site/src/App/routes/examples/job-summary/job-summary.tsx +++ b/site/src/App/routes/examples/job-summary/job-summary.tsx @@ -112,7 +112,7 @@ const page: Page = { using Playroom. - + At any stage you can click the “Open in Playroom” button under the examples to view the design across themes and viewports. diff --git a/site/src/App/routes/examples/marketing-banner/marketing-banner.tsx b/site/src/App/routes/examples/marketing-banner/marketing-banner.tsx index 9893f5bc2dc..c15bc58101e 100644 --- a/site/src/App/routes/examples/marketing-banner/marketing-banner.tsx +++ b/site/src/App/routes/examples/marketing-banner/marketing-banner.tsx @@ -102,7 +102,7 @@ const page: Page = { using Playroom. - + At any stage you can click the “Open in Playroom” button under the examples to view the design across themes and viewports. diff --git a/site/src/App/routes/examples/page-structure/page-structure.tsx b/site/src/App/routes/examples/page-structure/page-structure.tsx index c85278f8ae4..27812c79361 100644 --- a/site/src/App/routes/examples/page-structure/page-structure.tsx +++ b/site/src/App/routes/examples/page-structure/page-structure.tsx @@ -16,28 +16,30 @@ import { import { TextStack } from '../../../TextStack/TextStack'; import { Placeholder } from 'braid-src/lib/playroom/components'; import Code from '../../../Code/Code'; -import type { ReactNodeNoStrings } from 'braid-src/lib/components/private/ReactNodeNoStrings'; import { PageTitle } from '../../../Seo/PageTitle'; import { LinkableHeading } from '../../../LinkableHeading/LinkableHeading'; import { ContainerForPageDocs } from 'braid-src/lib/components/Page/Page.docs'; +import { fullHeight } from 'braid-src/lib/components/Page/Page.css'; interface StepProps { heading?: string; - detail: ReactNodeNoStrings; + detail: ComponentProps['children']; children: ComponentProps['children']; + conclusion?: ComponentProps['children']; } -const Step = ({ heading, detail, children }: StepProps) => ( +const Step = ({ heading, detail, children, conclusion }: StepProps) => ( {heading ? {heading} : null} {detail} {children} + {conclusion} ); const widths = { - medium: '60%', - large: '85%', + medium: '300px', + large: '400px', }; -const PageBlock = ({ +const ContentBlock = ({ width, children, }: { @@ -46,37 +48,28 @@ const PageBlock = ({ }) => ( {children} ); - -type PageDensity = 'tight' | 'loose'; -const scaledSpace: Record['space']> = - { - tight: 'xxlarge', - loose: 'xxxlarge', - }; -const scaledHeading: Record< - PageDensity, - ComponentProps['level'] -> = { - tight: '2', - loose: '1', -}; - -const scaleExamples = ({ - code, - density, +const PageBlock = ({ + width, + children, }: { - code: string; - density: PageDensity; -}) => { + width: keyof typeof widths; + children: ReactElement; +}) => ( + + {children} + +); + +const scaleExamples = ({ code }: { code: string }) => { let newCode = code - // Increase Stack space to recommended value from `scaledSpace` above - .replace(/(space=\")\w+([\"])/, `$1${scaledSpace[density]}$2`) + // Increase Stack space to recommended starting value for page section spacing + .replace('space="large"', 'space="xxlarge"') // Replace Text component with real Heading component .replace(/Text/g, 'Heading') // Remove now irrelevant `size` prop (and leading space) .replace(/\ssize=\".+\"/, '') - // Replace now irrelevant `weight` value with recommended value from `scaledHeading` above - .replace('weight="strong"', `level="${scaledHeading[density]}"`); + // Replace now irrelevant `weight` value with recommended page title heading level + .replace('weight="strong"', `level="2"`); // Find Placeholders with height props and double them for non-docs site usage const placeholderHeights = newCode.matchAll(/height={(\d+)}/g); @@ -117,7 +110,7 @@ const page: PageType = { {() => { const { code, value } = source( } + footer={} footerPosition="belowFold" > @@ -139,10 +132,7 @@ const page: PageType = { ); return { - code: scaleExamples({ - code, - density: 'loose', - }), + code: scaleExamples({ code }), value: {value}, }; }} @@ -161,7 +151,7 @@ const page: PageType = { using Playroom. - + At any stage you can click the “Open in Playroom” button under the examples to view the design across themes and viewports. @@ -170,93 +160,120 @@ const page: PageType = { - To get started, we’ll use a{' '} - Page component to - establish the top-level layout. We will pass a{' '} - Placeholder to use in the footer{' '} - slot, which ensures that the footer is at least placed at the - bottom of the screen, if not beyond as the page content grows. + Let’s start by adding some placeholder content to our page — + a header, a page heading, a couple of content sections and a + footer. } + conclusion={ + <> + + You’ll notice that the footer is sitting unexpectedly high + — it’s half way up the page! This is a common problem when + laying out a page with limited content. Ideally, the footer + would sit at least at the bottom of the screen, if not beyond as + the content grows. + + Let’s address this next. + + } > {() => { const { code, value } = source( - }> + <> - , + Page Heading + + + + , ); return { - code, - value: {value}, + code: scaleExamples({ code }), + value: ( + + {value} + + ), }; }} - For pages with dynamic content, it is recommended to place the - footer out of view by setting the footerPosition{' '} - prop to belowFold. This prevents the footer from - popping in and out of view when the page content changes, e.g. - toggling between a loading indicator and content. - + <> + + Braid provides a{' '} + Page component to + establish a top-level layout. All that is required, is to wrap + it around the content, and pass the footer to the{' '} + footer prop. + + + This will ensure that the footer is at least placed at the + bottom of the screen, if not beyond as the page content grows. + + } > {() => { const { code, value } = source( - } - footerPosition="belowFold" - > + }> + Page Heading + + , ); return { - code, + code: scaleExamples({ code }), value: {value}, }; }} + + If your page has dynamic content, such as search results that + change when filtered, it is recommended to place the footer out + of view permanently to prevent it from popping in and out of + view when the results change. + + + We do this by setting the footerPosition prop + to belowFold. + + + } + conclusion={ - Now let’s add a page title using the{' '} - Heading component, - as well as some additional{' '} - - Placeholders - {' '} - to demonstrate the sections of our page. + With the footer position decided, we now can turn our attention + back to the content, which is in need of some space — the question + is how much? } > {() => { const { code, value } = source( } + footer={} footerPosition="belowFold" > - Page Heading - - , ); return { - code: scaleExamples({ - code, - density: 'loose', - }), + code: scaleExamples({ code }), value: {value}, }; }} @@ -267,19 +284,20 @@ const page: PageType = { detail={ <> - You’ll notice that there is no space between the content. - This is actually a good thing! We now get to consider the - density of our page and adjust the spacing accordingly. To - achieve this, we’ll use a{' '} - Stack component - which applies space evenly between its child elements. + To adjust vertical spacing we’ll use the{' '} + Stack component, + which applies space evenly between its child elements. Wrapping + our content in this way, allows the vertical rhythm between + sections to be controlled consistently by specifying the{' '} + space prop. - The specified heading level of the page title is a good guide - for determining how much space to use — if - using a heading level 1 consider using xxxlarge - , if using a heading level 2 consider using{' '} - xxlarge. + The amount of space will ultimately depend on the desired + density of content being laid out. A good starting point for + page sections is xxlarge, stepping up to{' '} + xxxlarge for larger, content heavy areas (such + as dashboards or column layouts) that may require more vertical + separation to delineate sections. } @@ -287,7 +305,7 @@ const page: PageType = { {() => { const { code, value } = source( } + footer={} footerPosition="belowFold" > @@ -303,10 +321,7 @@ const page: PageType = { ); return { - code: scaleExamples({ - code, - density: 'loose', - }), + code: scaleExamples({ code }), value: {value}, }; }} @@ -317,27 +332,127 @@ const page: PageType = { detail={ <> - With the vertical space now handled, we can now turn our - attention to the content width and establish consistent - responsive gutters to the edge of the screen. For this we will - use the{' '} + With vertical space now handled, let’s focus on the + content width. We can use the{' '} + + ContentBlock + {' '} + component, which will provide a centered block for our content + with a choice of maximum width. + + + + Choose the width based on the content and its + layout. Are you using a column layout? What is the maximum line + length of the content? For this example we will use{' '} + large. + + + + Wrapping each section separately enables having different max + widths, or highlighting in a container with a background colour. + + + } + conclusion={ + <> + + We are pretty close now. You may notice however, that the + content is touching the edge of the screen in the mobile + preview. This occurs when the maximum width of the content width + is greater than the screen width. + + + Let’s establish some screen gutters next. + + } + > + {() => { + const { code, value } = source( + } + footerPosition="belowFold" + > + + + + + Page Heading + + + + + + + + + + + , + ); + + return { + code: scaleExamples({ code }), + value: ( + + + + + MOBILE + + {value} + + + + + + + DESKTOP + + + {value} + + + + + ), + }; + }} + + + + + While a ContentBlock is great fot constraining content width + within containers that already have their own padding, for + top-level containers we want to establish consistent gutters + between the content and the edge of the screen. For this Braid + provides the{' '} PageBlock{' '} - component, providing a centered block for the content with a - choice of max width. + component, recommended in favour of ContentBlock for top-level + page sections. - Note that each section of the page content is wrapped - separately. This allow sections to have different max widths, - while maintaining a common screen gutter on small devices. + Let’s go ahead and replace our usage of{' '} + “ContentBlock” with{' '} + “PageBlock”. } + conclusion={ + + And that’s it! We have successfully established our + top-level page structure. + + } > {() => { const { code, value } = source( } + footer={} footerPosition="belowFold" > @@ -359,10 +474,7 @@ const page: PageType = { ); return { - code: scaleExamples({ - code, - density: 'loose', - }), + code: scaleExamples({ code }), value: ( @@ -404,10 +516,8 @@ const page: PageType = { You may want to consider: - - Grouping page sections by adjusting their surrounding space - - Adding a full bleed coloured box + Adding additional content to the page sections + Adding a full bleed coloured container to a section