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