Feature
Put full-height media and a content column side by side in one band, mirrored or themed, stacking on phones.
On this page
Feature is a full-width band with media on one half and content on the other. The media runs edge to edge on its side, and the content column lines up with the page container on the other. Below the md breakpoint the two stack, media first.
import {
Button,
Feature,
FeatureContent,
FeatureMedia,
Heading,
Image,
Paragraph,
} from '@systhemaui/next'
export default function Demo() {
return (
<Feature layoutBackground="alternative">
<FeatureMedia>
<Image
src="/demo-assets/workspace.webp"
alt="A desk with a laptop and design sketches"
width={1800}
height={1200}
/>
</FeatureMedia>
<FeatureContent>
<Paragraph type="label">Studio</Paragraph>
<Heading.h2>Built around how you work</Heading.h2>
<Paragraph>
The media fills its half of the band at any height, and the content column aligns with the
page container on the other side.
</Paragraph>
<Paragraph>
<Button.a href="#" variant="primary">
Meet the team
</Button.a>
</Paragraph>
</FeatureContent>
</Feature>
)
}ImportLink to this section
import { Feature, FeatureContent, FeatureMedia } from '@systhemaui/next'In a React app without Next.js, import them from @systhemaui/react.
Parts and tagsLink to this section
Feature: the<section class="feature">band. It takestheme,layoutBackgroundandreversed.FeatureMedia: the media half, at least--feature-media-min-heighttall (320px when the token is unset). Put anImage,VideoorMediaWrapperinside it, or render the media element itself with a tag variant:FeatureMedia.img,FeatureMedia.video,FeatureMedia.iframeorFeatureMedia.div.astakes any other element.FeatureContent: the content half, a rich-text box (feature-content richtext).FeatureContent.divandaschange its element.
Media inside FeatureMedia loses its border radius and shadow and is cropped with object-fit: cover, so it reads as part of the band.
ExamplesLink to this section
ReversedLink to this section
reversed puts the media on the right from md up. On a phone the media stays on top.
import { Feature, FeatureContent, FeatureMedia, Heading, Image, Paragraph } from '@systhemaui/next'
export default function Demo() {
return (
<Feature reversed>
<FeatureMedia>
<Image
src="/demo-assets/forest-portrait.webp"
alt="Sunlight through a pine forest"
width={1200}
height={1600}
/>
</FeatureMedia>
<FeatureContent>
<Heading.h2>Media on the right</Heading.h2>
<Paragraph>
Alternate reversed and regular features down a page to give a long story some rhythm.
</Paragraph>
</FeatureContent>
</Feature>
)
}ThemeLink to this section
theme switches the band to another color-system mode, and layoutBackground picks the main or alternative background of that mode.
import { Feature, FeatureContent, FeatureMedia, Heading, Paragraph } from '@systhemaui/next'
export default function Demo() {
return (
<Feature theme="dark" layoutBackground="main">
<FeatureMedia.img src="/demo-assets/city.webp" alt="A city skyline at night" />
<FeatureContent>
<Heading.h2>After dark</Heading.h2>
<Paragraph>
theme="dark" sets data-theme on the band. Here the media is rendered as the img element
itself with FeatureMedia.img.
</Paragraph>
</FeatureContent>
</Feature>
)
}Features in a rowLink to this section
Consecutive features inside an Article sit edge to edge: a feature is a <section>, and the article puts one section padding above and below a run of sections and none between them.
import {
Article,
Feature,
FeatureContent,
FeatureMedia,
Heading,
Image,
Paragraph,
} from '@systhemaui/next'
export default function Demo() {
return (
<Article>
<Feature>
<FeatureMedia>
<Image src="/demo-assets/dunes-portrait.webp" alt="Sand dunes" width={1200} height={1600} />
</FeatureMedia>
<FeatureContent>
<Heading.h3>Research</Heading.h3>
<Paragraph>We start with the people who will use the site.</Paragraph>
</FeatureContent>
</Feature>
<Feature reversed layoutBackground="alternative">
<FeatureMedia>
<Image
src="/demo-assets/abstract-square.webp"
alt="Abstract colored shapes"
width={1200}
height={1200}
/>
</FeatureMedia>
<FeatureContent>
<Heading.h3>Design</Heading.h3>
<Paragraph>Then we turn what we learned into a design system.</Paragraph>
</FeatureContent>
</Feature>
</Article>
)
}PropsLink to this section
FeatureLink to this section
All other props go to the <section> element.
| Prop | Type | Default | Description |
|---|---|---|---|
theme | ColorSystem (dark, default) | - | |
layoutBackground | LayoutBackground (main, alternative) | - | |
reversed | boolean | false | |
…and all <section> attributes |
FeatureMediaLink to this section
| Prop | Type | Default | Description |
|---|---|---|---|
as | ElementType | 'div' | |
className | string | - | |
…and all <div> attributes |
FeatureContentLink to this section
| Prop | Type | Default | Description |
|---|---|---|---|
as | ElementType | 'div' | |
className | string | - | |
…and all <div> attributes |
HTML and CSSLink to this section
The same band in plain HTML:
<section class="feature feature-reversed bg-layout-alternative" data-theme="dark">
<img class="feature-media" src="/images/studio.webp" alt="Our studio" />
<div class="feature-content richtext">
<h2 class="color-heading text-h2">Built around how you work</h2>
<p class="text-body color-body">Media on one half, content on the other.</p>
</div>
</section>The content padding comes from the --feature-content-padding-side, --feature-content-padding-center and --feature-content-padding-y tokens, and the media height from --feature-media-min-height:
| Class | Styles |
|---|---|
feature | & { width: 100%; display: flex; flex-direction: row; justify-content: flex-start; }@media (width < 640px) { & { flex-direction: column; } }@media (width >= 640px) { &.feature-reversed { flex-direction: row-reverse; } }@media (width >= 640px) { &.feature-reversed .feature-content { margin-right: 0; margin-left: var(--container-margin); padding-left: var(--feature-content-padding-side); padding-right: var(--feature-content-padding-center); } } |
feature-media | & { flex-grow: 0; flex-shrink: 0; display: flex; width: 100%; min-height: var(--feature-media-min-height, 320px); }@media (width >= 640px) { & { width: 50%; } }&:is(img, svg, video, iframe, .media, .media-wrapper), .feature-media > *:is(img, svg, video, iframe, .media, .media-wrapper) { height: unset; object-fit: cover; object-position: center; border-radius: 0; box-shadow: none; } |
feature-content | & { width: 100%; padding-left: var(--feature-content-padding-center); padding-right: var(--feature-content-padding-side); padding-block: var(--feature-content-padding-y, 48px); align-self: stretch; }@media (max-width: 639px) { & { width: calc(100% - var(--container-margin) * 2); max-width: var(--container-width); margin-inline: auto; } }@media (width >= 640px) { & { width: calc(var(--container-width) / 2); margin-right: var(--container-margin); align-self: center; } } |
feature-reversed | @media (width >= 640px) { .feature.feature-reversed { flex-direction: row-reverse; } }@media (width >= 640px) { .feature.feature-reversed .feature-content { margin-right: 0; margin-left: var(--container-margin); padding-left: var(--feature-content-padding-side); padding-right: var(--feature-content-padding-center); } } |
Next.jsLink to this section
@systhemaui/next re-exports Feature, FeatureMedia and FeatureContent from @systhemaui/react unchanged. Put the @systhemaui/next Image inside FeatureMedia to get next/image optimization; the tag variant FeatureMedia.img renders a plain <img>.
AccessibilityLink to this section
The media comes first in the source and the content second, whichever side reversed shows them on, so screen readers always read media then text. Give informative images an alt; pass alt="" when the image only decorates the band.