Skip to content

Commit 781c71c

Browse files
Hero variants (#1324)
* Add variants * Style CTA using Group * Support "aside" content in standard layout * Simplify styles * Add comment * Update badge text * Organize imports * Tidy up examples * Constrain variant combinations with TS * Extract reused heading styles to variable * Replace level with headingLevel prop * Add size prop * Control heading size with Heading component * Rename layout prop to variant * Organize imports * Update according to API change * Update .changeset/thick-glasses-fall.md * Remove console.log
1 parent 1356cc1 commit 781c71c

File tree

6 files changed

+316
-44
lines changed

6 files changed

+316
-44
lines changed

.changeset/thick-glasses-fall.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
---
2+
"@obosbbl/grunnmuren-react": minor
3+
---
4+
5+
New variants for the `<Hero>` component. The variant is controlled by the new prop: `variant`.
6+
7+
The `variant` is tied to an intended default heading size, either `xl` or `l`. Which means that each `variant` has it's own default heading styles. So in for the most part, you should never have to set both:
8+
9+
### standard, L heading
10+
``` tsx
11+
// default: variant="standard" is default and heading size `l` is implicit for this variant
12+
<Hero>
13+
<Content>
14+
<Heading level={1}>Dette er en Hero</Heading>
15+
<Description>– et samarbeidsprosjekt med Nordr</Description>
16+
</Content>
17+
<Media>
18+
<img
19+
src="https://res.cloudinary.com/obosit-prd-ch-clry/image/upload/f_auto,c_limit,w_2048,q_auto/v1582122753/Boligprosjekter/Oslo/Ulven/Ulven-N%C3%A6romr%C3%A5de-Oslo-OBOS-Construction-city.jpg"
20+
alt=""
21+
/>
22+
</Media>
23+
</Hero>
24+
```
25+
26+
``` tsx
27+
// heading size `l` is implicit for the `full-bleed` variant
28+
<Hero variant="full-bleed">
29+
<Content>
30+
<Heading level={1}>Dette er en Hero</Heading>
31+
<Description>– et samarbeidsprosjekt med Nordr</Description>
32+
</Content>
33+
<Media>
34+
<img
35+
src="https://res.cloudinary.com/obosit-prd-ch-clry/image/upload/f_auto,c_limit,w_2048,q_auto/v1582122753/Boligprosjekter/Oslo/Ulven/Ulven-N%C3%A6romr%C3%A5de-Oslo-OBOS-Construction-city.jpg"
36+
alt=""
37+
/>
38+
</Media>
39+
</Hero>
40+
```
41+
### two-column
42+
``` tsx
43+
// heading size `xl` is implicit for the `two-column` variant
44+
<Hero variant="two-column">
45+
<Content>
46+
<Heading level={1}>Dette er en Hero</Heading>
47+
<Description>– et samarbeidsprosjekt med Nordr</Description>
48+
</Content>
49+
<Media>
50+
<img
51+
src="https://res.cloudinary.com/obosit-prd-ch-clry/image/upload/f_auto,c_limit,w_2048,q_auto/v1582122753/Boligprosjekter/Oslo/Ulven/Ulven-N%C3%A6romr%C3%A5de-Oslo-OBOS-Construction-city.jpg"
52+
alt=""
53+
/>
54+
</Media>
55+
</Hero>
56+
```
57+
58+
59+
### standard with `xl` heading
60+
``` tsx
61+
// variant="standard" is default so that prop can be omitted, and the heading size set to `xl` on the `<Heading>`
62+
<Hero>
63+
<Content>
64+
<Heading level={1} size="xl">Dette er en Hero</Heading>
65+
<Description>– et samarbeidsprosjekt med Nordr</Description>
66+
</Content>
67+
<Media>
68+
<img
69+
src="https://res.cloudinary.com/obosit-prd-ch-clry/image/upload/f_auto,c_limit,w_2048,q_auto/v1582122753/Boligprosjekter/Oslo/Ulven/Ulven-N%C3%A6romr%C3%A5de-Oslo-OBOS-Construction-city.jpg"
70+
alt=""
71+
/>
72+
</Media>
73+
</Hero>
74+
```
75+
76+
### Heading
77+
To achieve this control of the heading size a new `size` prop has been added to the `<Heading>` component. This way we can implement the same API for the Card component (which should support different heading sizes as well).
78+
79+
### Breaking change to `UNSAFE_Hero`
80+
This introduces a breaking change to the beta version of the `<Hero>`, which is to be expected without a major release. If you are currently using `UNSAFE_Hero` you would now have to pass `variant="full-bleed"` as a prop to your component to get the same design as before.

packages/react/src/content/content.tsx

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,62 @@
1-
import { cx } from 'cva';
1+
import { type VariantProps, cva, cx } from 'cva';
22
import { type HTMLProps, type Ref, createContext } from 'react';
33
import { type ContextValue, useContextProps } from 'react-aria-components';
44

5-
type HeadingProps = HTMLProps<HTMLHeadingElement> & {
6-
children?: React.ReactNode;
7-
/** The level of the heading */
8-
level: 1 | 2 | 3 | 4 | 5 | 6;
9-
/** @private Used internally for slotted components */
10-
_innerWrapper?: (children: React.ReactNode) => React.ReactNode;
11-
/** @private Used internally for slotted components */
12-
_outerWrapper?: (children: React.ReactNode) => React.ReactNode;
13-
/** Ref for the element. */
14-
ref?: Ref<HTMLHeadingElement>;
15-
};
5+
type HeadingProps = Omit<HTMLProps<HTMLHeadingElement>, 'size'> &
6+
VariantProps<typeof headingVariants> & {
7+
children?: React.ReactNode;
8+
/** The semantic level of the heading */
9+
level: 1 | 2 | 3 | 4 | 5 | 6;
10+
/** @private Used internally for slotted components */
11+
_innerWrapper?: (children: React.ReactNode) => React.ReactNode;
12+
/** @private Used internally for slotted components */
13+
_outerWrapper?: (children: React.ReactNode) => React.ReactNode;
14+
/** Ref for the element. */
15+
ref?: Ref<HTMLHeadingElement>;
16+
};
1617

1718
const HeadingContext = createContext<
1819
ContextValue<Partial<HeadingProps>, HTMLHeadingElement>
1920
>({});
2021

22+
const headingVariants = cva({
23+
variants: {
24+
/** The visual text size of the heading */
25+
size: {
26+
xl: 'heading-xl',
27+
l: 'heading-l',
28+
m: 'heading-m',
29+
s: 'heading-s',
30+
xs: 'heading-xs',
31+
},
32+
},
33+
});
34+
2135
const Heading = ({ ref = null, ...props }: HeadingProps) => {
2236
[props, ref] = useContextProps(props, ref, HeadingContext);
2337

2438
const {
2539
children,
2640
level,
41+
size,
2742
className,
2843
_innerWrapper: innerWrapper,
2944
_outerWrapper: outerWrapper,
3045
...restProps
3146
} = props;
3247

48+
const _className = headingVariants({
49+
size,
50+
});
51+
3352
const Element = `h${level}` as const;
3453

3554
const content = (
36-
<Element {...restProps} className={className} data-slot="heading">
55+
<Element
56+
{...restProps}
57+
className={cx(className, _className)}
58+
data-slot="heading"
59+
>
3760
{innerWrapper ? innerWrapper(children) : children}
3861
</Element>
3962
);

packages/react/src/hero/hero.stories.tsx

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
import { ArrowRight, InfoCircle } from '@obosbbl/grunnmuren-icons-react';
12
import type { Meta, StoryObj } from '@storybook/react';
3+
import { Group } from 'react-aria-components';
4+
import { Badge } from '../badge';
5+
import { Button } from '../button';
26
import { Content, Heading, Media } from '../content';
37
import { Description } from '../label';
48
import { VideoLoop } from '../video-loop';
@@ -9,14 +13,19 @@ const meta: Meta<typeof Hero> = {
913
component: Hero,
1014
parameters: {
1115
// disable built in padding in story, because we provide our own
12-
layout: 'fullscreen',
16+
variant: 'fullscreen',
1317
},
1418
render: () => (
1519
<main className="container grid gap-y-8">
1620
<Hero>
1721
<Content>
18-
<Heading level={2}>Dette er en Hero</Heading>
19-
<Description>– et samarbeidsprosjekt med Nordr</Description>
22+
<Heading level={1} size="xl">
23+
Jobb i OBOS
24+
</Heading>
25+
<p className="lead">
26+
Bli med å oppfylle boligdrømmer! Vi søker engasjerte og dyktige
27+
personer som vil ta OBOS videre. Søk på våre ledige stillinger!
28+
</p>
2029
</Content>
2130
<Media>
2231
<img
@@ -33,17 +42,85 @@ export default meta;
3342

3443
type Story = StoryObj<typeof Hero>;
3544

36-
export const WithImage: Story = {
45+
export const StandardWithLeadAndImage: Story = {
3746
args: {},
3847
};
3948

40-
export const WithVideoLoop = () => (
49+
export const TwoColumn = () => (
50+
<main className="container grid gap-y-8">
51+
<Hero variant="two-column">
52+
<Content>
53+
<Heading level={1}>Bank på OBOS-måten</Heading>
54+
<p>
55+
Vi har satt ned renta på flere av boliglånene våre fra 2. april – og
56+
spanderer både etablerings- og tinglysingsgebyret på alle medlemmer
57+
som flytter lånet til oss før 31. mai. Det er bank på OBOS-måten.
58+
</p>
59+
<Group>
60+
<Button href="https://www.obos.no/bank/registrer-deg">
61+
Bli bankkunde
62+
</Button>
63+
<Button
64+
variant="secondary"
65+
href="https://www.obos.no/bank/registrer-deg/derfor-bor-du-velge-obos-banken"
66+
>
67+
Mer om bank på OBOS-måten
68+
</Button>
69+
</Group>
70+
</Content>
71+
<Media>
72+
<img
73+
src="https://res.cloudinary.com/obosit-prd-ch-clry/image/upload/ar_1.234,w_1440,f_auto,q_auto,g_auto,c_fill/v1662557719/Kampanjer/obos-medlem-ungt-par-hjemme.jpg"
74+
alt=""
75+
/>
76+
</Media>
77+
</Hero>
78+
</main>
79+
);
80+
81+
export const StandardPageWithCTA = () => (
4182
<main className="container grid gap-y-8">
4283
<Hero>
4384
<Content>
44-
<Heading level={2}>Dette er en Hero</Heading>
45-
<Description>– et samarbeidsprosjekt med Nordr</Description>
85+
<Heading level={1}>Dette er OBOS</Heading>
86+
</Content>
87+
<Button
88+
className="group"
89+
variant="tertiary"
90+
href="https://www.obos.no/dette-er-obos/nyheter"
91+
>
92+
Nyheter og pressemeldinger
93+
<ArrowRight className="transition-transform group-hover:motion-safe:translate-x-1" />
94+
</Button>
95+
<Media>
96+
<img
97+
src="https://res.cloudinary.com/obosit-prd-ch-clry/f_auto,c_limit,w_1200,q_auto/v1578908385/Samfunnsansvar/OBOS-innovasjon-livet-mellom-husene"
98+
alt=""
99+
/>
100+
</Media>
101+
</Hero>
102+
</main>
103+
);
104+
105+
const Logo = () => (
106+
<img
107+
alt=""
108+
src="https://brauten-eiendom.no/wp-content/uploads/sites/13/2021/08/Nordr.png"
109+
className="h-12"
110+
/>
111+
);
112+
113+
export const FullBleedWithVideoLoop = () => (
114+
<main className="container grid gap-y-8">
115+
<Hero variant="full-bleed">
116+
<Content>
117+
<Heading level={1}>Frysjaparken</Heading>
118+
<Description>
119+
– det gamle industriområdet på Frysja har blitt et ettertraktet
120+
nabolag
121+
</Description>
46122
</Content>
123+
<Logo />
47124
<Media>
48125
<VideoLoop
49126
src="https://res.cloudinary.com/obosit-prd-ch-clry/video/upload/v1732199756/Mellom%20husene/Frysja_Loop2.mp4"
@@ -54,3 +131,23 @@ export const WithVideoLoop = () => (
54131
</Hero>
55132
</main>
56133
);
134+
135+
export const FullBleedWithImageAndBadge = () => (
136+
<main className="container grid gap-y-8">
137+
<Hero variant="full-bleed">
138+
<Content>
139+
<Heading level={1}>Vollebekk</Heading>
140+
<Description>– nabolaget for store og små</Description>
141+
</Content>
142+
<Badge color="sky">
143+
<InfoCircle />I salg
144+
</Badge>
145+
<Media>
146+
<img
147+
src="https://res.cloudinary.com/obosit-prd-ch-clry/image/upload/f_auto,c_limit,w_3840,q_auto:best/v1731662987/Mellom%20husene/Byutvikling/Nabolag/Vollebekk/OBOS_bygulv_vollebekk_vannspeil-og-grontareal-foran-leilighetsbygg.jpg"
148+
alt=""
149+
/>
150+
</Media>
151+
</Hero>
152+
</main>
153+
);

0 commit comments

Comments
 (0)