This repo is a small, self-contained showcase of two cooperating UI components extracted from a larger application. It is meant to demonstrate responsive layout, context-driven coordination, and dynamic content placement for code showcasing.
column-wrapper— a responsive grid wrapper that orchestrates layout and shares state via React Contextcolumn-wrapper/column-wrapper.jsx— main layout component (ColumnWrapper)column-wrapper/components/CwProvider.js— context provider and hookcolumn-wrapper/components/DynamicallyPlacedContent.js— renders a consolidated “eyebrow”/title area from shared contextcolumn-wrapper/components/DynamicallySourcedImage.js— renders an image when its source is provided by contextcolumn-wrapper/column-wrapper.scss— styles supporting classnames used by the wrapper and its children
content— a flexible renderer for column contentcontent/content.jsx— main content component (Content)content/content.query.js— GraphQL fragment assembly used by the original app (included for data shape reference)
-
ColumnWrapperlays out a CSS grid based on provided widths and alignment, and exposes a Context that children can use to coordinate behavior. -
Each column is rendered by
Content. Depending on the data,Contentmay “promote” certain bits of heading/eyebrow UI (icon, intro text, pill, title) into shared context so thatDynamicallyPlacedContentcan render them in a single consolidated location above the grid on small screens. -
For image-first experiences,
Contentcan render aDynamicallySourcedImageusing a dynamic image source provided through the same shared context. On mobile, this image can instead be deferred to an accordion (outside this sample) based on the same flags.
Together, the two components demonstrate a clean separation of concerns:
ColumnWrappercontrols layout and shared state.Contentfocuses on rendering per-column data and feeding shared UI state when appropriate.
-
column-wrapper/column-wrapper.jsx- Accepts
columnsand layout props (columnWidths,wrapperWidth,columnHorizontalAlignment,verticalAlignment, etc.). - Computes responsive grid CSS with Emotion, using width fractions and wrapper alignment.
- Derives behavior flags such as
shouldMakeHeadingDynamic(true when columns include images/videos) andshouldRenderDynamicImageInAccordionOnMobile. - Provides context values:
dynamicContent,setDynamicContent,dynamicImageSource,setDynamicImageSource, and the behavior flags. - Renders
DynamicallyPlacedContentwhen shareddynamicContent.titleexists, then renders each column viaContent.
- Accepts
-
column-wrapper/components/CwProvider.js- Minimal React Context wrapper used by the wrapper and content components.
-
column-wrapper/components/DynamicallyPlacedContent.js- Reads
dynamicContentfrom context and renders, in order:leadingIcon,introText,pill,title. - Uses helpers (from the original app) to colorize/format WYSIWYG markup.
- Reads
-
column-wrapper/components/DynamicallySourcedImage.js- Reads
dynamicImageSourcefrom context and renders anImagewhen present.
- Reads
-
content/content.jsx- Renders a column’s UI (pill, eyebrow/intro, image, components, rich text) using the data shape from Contentful.
- When
shouldMakeHeadingDynamicis true, on small screens it moves parts of the heading (icon, intro text, pill) into shareddynamicContentsoDynamicallyPlacedContentcan place them above the grid. - If the current item is a “dynamically sourced image,” it will render
DynamicallySourcedImage(unlessshouldRenderDynamicImageInAccordionOnMobilesuggests deferring on mobile). - Applies background and alignment classes, and uses Emotion for inline computed styles.
-
content/content.query.js- Provides the GraphQL fragments used by the original app. Included here to show the expected data shape for
Content(images, pills, intro text, components, references, etc.).
- Provides the GraphQL fragments used by the original app. Included here to show the expected data shape for
-
ColumnWrapper(selected props)columns(array) — the column data passed through toContentper indexcolumnWidthsorcolumnWidthValues— fractional widths (e.g.,"50 / 50") for grid template generationcolumnHorizontalAlignment— left/center/right alignment of the grid within the wrapperverticalAlignment— top/center/bottom alignment for items within each columnwrapperWidth— overall wrapper width mode (e.g., Fill Width)stackColumnsOnTablet— toggles stacked layout at tablet widthsroundedCorners,customClasses,mobileMedia,section,tfaFormData— feature toggles and pass-through data
-
Content(selected props)- Heading:
pill,leadingIcon,introText,introTextTag,introTextTagStyle - Media:
desktopImage,tabletImage,mobileImage,large,medium - Layout:
alignment,horizontalAlignment,backgroundColor,cssInJs - Rich text:
textwithreferences - Components:
components/componentsCollection - Dynamic image item:
__typename === 'ContentfulDynamicallySourcedImage'
- Heading:
- When any column includes an image/video (or a nested image/video component),
ColumnWrappersetsshouldMakeHeadingDynamic. - On small screens,
Contentmoves eyebrow bits (icon, intro text, pill) intodynamicContent;DynamicallyPlacedContentrenders them once above the grid. - For “dynamically sourced images,”
Contenteither renders the image inline viaDynamicallySourcedImageor, on mobile, defers to be rendered within an external accordion (as suggested byshouldRenderDynamicImageInAccordionOnMobile).
import ColumnWrapper from './column-wrapper';
const columns = [
{
__typename: 'ContentfulContent',
pill: { /* pill props */ },
leadingIcon: { /* icon props */ },
introText: 'Short intro',
text: { /* rich text data */ },
components: [
{ __typename: 'ContentfulImage', /* image data */ },
{ __typename: 'ContentfulCtaButton', /* CTA data */ }
]
},
{
__typename: 'ContentfulDynamicallySourcedImage',
/* image source data; used by DynamicallySourcedImage */
}
];
export default function Example() {
return (
<ColumnWrapper
columns={columns}
columnWidths="50 / 50"
wrapperWidth="Fill Width"
columnHorizontalAlignment="left"
verticalAlignment="top"
stackColumnsOnTablet
roundedCorners
/>
);
}- This sample intentionally references helper utilities and design tokens (e.g., Emotion CSS helpers, color utilities, breakpoints) from the original application for authenticity. Not all of those underlying modules are included here, as the goal is to show architecture and cooperation patterns rather than provide a runnable app.
- If you want an even quicker skim, start with
column-wrapper/column-wrapper.jsxandcontent/content.jsxto see the context/data handoff and rendering logic end-to-end.