Skip to content

Commit 834e1b3

Browse files
committed
Copying over changes to CTA and related components
1 parent 1bbb0ab commit 834e1b3

File tree

14 files changed

+583
-131
lines changed

14 files changed

+583
-131
lines changed

components/Cta/Cta.styles.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { type CtaIconLeftMarginType } from './Cta.types';
2+
3+
export const cta = 'group/cta transition-all';
4+
export const buttonBase = 'block font-normal w-fit no-underline hocus:underline';
5+
// hocus to plum dark gradient instead of solid plum dark to avoid a flash of white background on hocus
6+
export const gradientButtonBase = 'bg-gradient-to-tr hocus:from-plum-dark hocus:to-plum-dark text-white hocus:text-white';
7+
export const textLinkBase = 'block font-semibold w-fit no-underline text-18 md:text-20';
8+
export const gradientTextLinkBase = 'bg-clip-text bg-gradient-to-tr text-transparent hocus:text-transparent';
9+
10+
// Maps to linkButtonStyle props in SbCtaLink. Only used for the Button style.
11+
export const ctaButtonStyles = {
12+
// Primary
13+
'ood-cta__button--primary su-after-bg-white': 'bg-bay-dark text-white hocus:bg-palo-alto hocus:text-white',
14+
// Secondary
15+
'ood-cta__button--secondary su-after-bg-bay-dark su-after-bg-hocus-white': 'bg-white text-bay-dark shadow-bay-dark shadow-[inset_0_0_0_1px] after:text-bay-dark after:bg-bay-dark hocus:bg-bay-dark hocus:text-white hocus:after:text-white ',
16+
// Give Now Button
17+
'su-bg-digital-red su-bg-hocus-plum-dark su-text-white su-text-hocus-white su-after-bg-white su-after-bg-hocus-white': 'bg-digital-red hocus:bg-plum-dark text-white hocus:text-white',
18+
// Secondary Give Now Button
19+
'su-bg-white su-bg-hocus-plum-dark su-text-digital-red su-text-hocus-white su-after-bg-digital-red su-after-bg-hocus-white': 'bg-white hocus:bg-plum-dark text-digital-red hocus:text-white after:bg-digital-red ',
20+
// Ghost Button
21+
'ood-cta__button--ghost su-after-bg-white': 'text-white bg-transparent shadow-white shadow-[inset_0_0_0_1px] transition-shadow hocus:text-white hocus:shadow-[inset_0_0_0_3px]',
22+
// Solid Campaign Plum Button
23+
'su-bg-plum su-bg-hocus-plum-dark su-text-white su-text-hocus-white su-after-bg-white su-after-bg-hocus-white': 'bg-plum hocus:bg-plum-dark text-white hocus:text-white',
24+
// Gradient Campaign Buttons
25+
'su-bg-cardinal-dark-to-spirited-dark su-bg-hocus-plum-dark su-text-white su-text-hocus-white su-after-bg-white su-after-bg-hocus-white su-transition-none': `${gradientButtonBase} from-cardinal-red-dark to-spirited-dark`,
26+
'su-bg-plum-to-digital-red su-bg-hocus-plum-dark su-text-white su-text-hocus-white su-after-bg-white su-after-bg-hocus-white su-transition-none': `${gradientButtonBase} from-plum to-digital-red`,
27+
'su-bg-plum-to-spirited-dark su-bg-hocus-plum-dark su-text-white su-text-hocus-white su-after-bg-white su-after-bg-hocus-white su-transition-none': `${gradientButtonBase} from-plum to-spirited-dark`,
28+
'su-bg-palo-alto-dark-to-palo-verde-dark su-bg-hocus-plum-dark su-text-white su-text-hocus-white su-after-bg-white su-after-bg-hocus-white su-transition-none': `${gradientButtonBase} from-palo-alto-dark to-palo-verde-dark`,
29+
'su-bg-sky-dark-to-olive-dark su-bg-hocus-plum-dark su-text-white su-text-hocus-white su-after-bg-white su-after-bg-hocus-white su-transition-none': `${gradientButtonBase} from-sky-dark to-olive-dark`,
30+
'su-bg-sky-dark-to-bay-dark su-bg-hocus-plum-dark su-text-white su-text-hocus-white su-after-bg-white su-after-bg-hocus-white su-transition-none': `${gradientButtonBase} from-sky-dark to-bay-dark`,
31+
};
32+
33+
// Maps to linkTextColor prop in SbCtaLink. Only used for the text link style.
34+
export const ctaTextColors = {
35+
'su-text-digital-red su-after-bg-digital-red su-text-hocus-sky-dark su-after-bg-hocus-sky-dark': 'text-digital-red hocus:text-sky-dark',
36+
'su-text-white su-text-hocus-white su-hocus-underline su-after-bg-white su-after-bg-hocus-white': 'text-white hocus:text-white',
37+
// Gradient text links for Campaign pages
38+
'ood-cta__link-gradient su-bg-sky-dark-to-bay-dark su-after-bg-sky-dark-to-bay-dark': `${gradientTextLinkBase} from-sky-dark to-bay-dark *:[&_svg]:text-bay-dark *:[&_svg]:hocus:text-bay-dark`,
39+
'ood-cta__link-gradient su-bg-cardinal-dark-to-spirited-dark su-after-bg-cardinal-dark-to-spirited-dark': `${gradientTextLinkBase} from-cardinal-red-dark to-spirited-dark *:[&_svg]:text-spirited-dark *:[&_svg]:hocus:text-spirited-dark`,
40+
'ood-cta__link-gradient su-bg-plum-to-digital-red su-after-bg-plum-to-digital-red': `${gradientTextLinkBase} from-plum to-digital-red *:[&_svg]:text-digital-red *:[&_svg]:hocus:text-digital-red`,
41+
'ood-cta__link-gradient su-bg-plum-to-spirited-dark su-after-bg-plum-to-spirited-dark': `${gradientTextLinkBase} from-plum to-spirited-dark *:[&_svg]:text-spirited-dark *:[&_svg]:hocus:text-spirited-dark`,
42+
'ood-cta__link-gradient su-bg-palo-alto-dark-to-palo-verde-dark su-after-bg-palo-alto-dark-to-palo-verde-dark': `${gradientTextLinkBase} from-palo-alto-dark to-palo-verde-dark *:[&_svg]:text-palo-verde-dark *:[&_svg]:hocus:text-palo-verde-dark`,
43+
'ood-cta__link-gradient su-bg-sky-dark-to-olive-dark su-after-bg-sky-dark-to-olive-dark': `${gradientTextLinkBase} from-sky-dark to-olive-dark *:[&_svg]:text-olive-dark *:[&_svg]:hocus:text-olive-dark`,
44+
// Has an extra su-after-bg-sky-dark-to-bay-dark in SB, but it seems to work here without the dupe string
45+
'ood-cta__link-gradient su-bg-sky-dark-to-bay-dark': `${gradientTextLinkBase} from-sky-dark to-bay-dark *:[&_svg]:text-bay-dark *:[&_svg]:hocus:text-bay-dark`,
46+
/**
47+
* Campaign page only solid text colors - seems on live site the intent was to use plum-dark as the hocus color, but it was overridden by the base link hocus color
48+
* Here we honor the original intent by using plum-dark as the hocus color
49+
*/
50+
'su-text-lagunita-dark su-after-bg-lagunita-dark su-text-hocus-plum-dark su-after-bg-hocus-plum-dark': 'text-lagunita-dark hocus:text-plum-dark',
51+
'su-text-palo-verde su-after-bg-palo-verde su-text-hocus-plum-dark su-after-bg-hocus-plum-dark': 'text-palo-verde hocus:text-plum-dark',
52+
'su-text-plum su-after-bg-plum su-text-hocus-plum-dark su-after-bg-hocus-plum-dark': 'text-plum hocus:text-plum-dark',
53+
'su-text-brick su-after-bg-brick su-text-hocus-plum-dark su-after-bg-hocus-plum-dark': 'text-brick hocus:text-plum-dark',
54+
'su-text-cardinal-red su-after-bg-cardinal-red su-text-hocus-plum-dark su-after-bg-hocus-plum-dark': 'text-cardinal-red hocus:text-plum-dark',
55+
'su-text-palo-alto su-after-bg-palo-alto su-text-hocus-plum-dark su-after-bg-hocus-plum-dark': 'text-palo-alto hocus:text-plum-dark',
56+
'su-text-bay-dark su-after-bg-bay-dark su-text-hocus-plum-dark su-after-bg-hocus-plum-dark': 'text-bay-dark hocus:text-plum-dark',
57+
'su-text-sky-dark su-after-bg-sky-dark su-text-hocus-plum-dark su-after-bg-hocus-plum-dark': 'text-sky-dark hocus:text-plum-dark',
58+
'su-text-lagunita su-after-bg-lagunita su-text-hocus-plum-dark su-after-bg-hocus-plum-dark': 'text-lagunita hocus:text-plum-dark',
59+
};
60+
61+
// Additional CTA variants we use for this site, e.g., as subcomponents for other components. These include styles for sizes, colors, icon styles, and other properties.
62+
export const ctaVariants = {
63+
'local-footer': 'text-digital-red hocus:text-black underline leading-snug font-normal text-16 md:text-18 *:[&_svg]:hocus:text-digital-red',
64+
};
65+
66+
// Maps to linkButtonSize prop in SbCtaLink. Only used for the button styles
67+
export const ctaButtonSizes = {
68+
default: 'pt-11 pb-12 px-30 text-18 md:text-20',
69+
'ood-cta__button--medium': 'pt-11 pb-12 px-30 md:py-14 md:px-34 text-20 md:text-24',
70+
'ood-cta__button--large': 'py-16 px-30 md:py-20 md:px-36 text-22 md:text-28',
71+
};
72+
73+
// Maps to linkIcon prop in SbCtaLink
74+
export const ctaIcons = {
75+
'su-link--action': 'chevron-right',
76+
'su-link--jump': 'chevron-down',
77+
'su-link--external': 'external',
78+
'su-link--internal': 'lock',
79+
'su-link--download': 'download',
80+
'su-link--video': 'video',
81+
'su-link--no-icon': '',
82+
};
83+
84+
// Common styles for CTA icons
85+
export const icon = 'inline-block will-change-transform transition-transform stroke-2';
86+
87+
// Icons have left margins
88+
// Only add to this map if left margin is different from default class ml-04em
89+
export const iconLeftMarginDefault = 'ml-04em';
90+
export const iconLeftMargin: CtaIconLeftMarginType = {
91+
'su-link--action': 'ml-03em',
92+
};
93+
94+
// Maps to linkIcon prop in SbCtaLink. Animation preselected based on the icon type
95+
export const iconAnimations = {
96+
'su-link--action': 'group-hover/cta:translate-x-02em group-focus-visible/cta:translate-x-02em',
97+
'su-link--jump': 'group-hover/cta:translate-y-02em group-focus-visible/cta:translate-y-02em',
98+
'su-link--external': 'group-hover/cta:translate-x-01em group-focus-visible/cta:translate-x-01em group-hover/cta:-translate-y-01em group-focus-visible/cta:-translate-y-01em',
99+
'su-link--internal': 'group-hover/cta:fill-current',
100+
'su-link--download': 'group-hover/cta:translate-y-02em group-focus-visible/cta:translate-y-02em',
101+
'su-link--video': 'group-hover/cta:translate-x-02em group-focus-visible/cta:translate-x-02em',
102+
'su-link--no-icon': '',
103+
};
104+
105+
export const ctaAligns = {
106+
left: 'su-text-left',
107+
center: 'su-text-center mx-auto',
108+
right: 'su-text-right ml-auto mr-0',
109+
};

components/Cta/Cta.types.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { type HeroIconProps } from '@/components/HeroIcon';
2+
import { type MarginType } from '@/utilities/datasource';
3+
import * as styles from './Cta.styles';
4+
5+
export type CtaButtonStyleType = keyof typeof styles.ctaButtonStyles;
6+
export type CtaTextColorType = keyof typeof styles.ctaTextColors;
7+
export type CtaButtonSizeType = keyof typeof styles.ctaButtonSizes;
8+
export type CtaVariantType = keyof typeof styles.ctaVariants;
9+
export type CtaAlignType = keyof typeof styles.ctaAligns;
10+
11+
export type CtaIconType = keyof typeof styles.ctaIcons;
12+
export type IconAnimationType = keyof typeof styles.iconAnimations | '';
13+
14+
export type CtaIconLeftMarginType = Partial<{
15+
[Key in CtaIconType]: string;
16+
}>;
17+
18+
export interface CtaCommonProps {
19+
srText?: string;
20+
icon?: CtaIconType;
21+
isButton?: boolean;
22+
textColor?: CtaTextColorType;
23+
buttonSize?: CtaButtonSizeType;
24+
buttonStyle?: CtaButtonStyleType;
25+
variant?: CtaVariantType;
26+
align?: CtaAlignType;
27+
iconProps?: Omit<HeroIconProps, 'icon'> & React.ComponentProps<'svg'>;
28+
mt?: MarginType;
29+
mb?: MarginType;
30+
children?: React.ReactNode;
31+
};

components/Cta/CtaButton.tsx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
'use client';
2+
import React from 'react';
3+
import { cnb } from 'cnbuilder';
4+
import { CtaContent } from './CtaContent';
5+
import { type CtaCommonProps } from './Cta.types';
6+
import { marginTops, marginBottoms } from '@/utilities/datasource';
7+
import * as styles from './Cta.styles';
8+
9+
export type CtaButtonProps = React.ComponentPropsWithoutRef<'button'> & CtaCommonProps;
10+
11+
export const CtaButton = React.forwardRef<HTMLButtonElement, CtaButtonProps>((props, ref) => {
12+
const {
13+
type = 'button',
14+
isButton,
15+
buttonStyle = 'ood-cta__button--primary su-after-bg-white',
16+
buttonSize = 'default',
17+
textColor = 'su-text-digital-red su-after-bg-digital-red su-text-hocus-sky-dark su-after-bg-hocus-sky-dark',
18+
variant,
19+
align,
20+
icon,
21+
iconProps,
22+
srText,
23+
mt,
24+
mb,
25+
children,
26+
className,
27+
...rest
28+
} = props;
29+
30+
return (
31+
<button
32+
{...rest}
33+
type={type}
34+
ref={ref as React.ForwardedRef<HTMLButtonElement>}
35+
className={cnb(
36+
styles.cta,
37+
styles.ctaAligns[align],
38+
isButton ? styles.buttonBase : '',
39+
!isButton && !variant ? styles.textLinkBase : '',
40+
isButton ? styles.ctaButtonStyles[buttonStyle] : '',
41+
isButton ? styles.ctaButtonSizes[buttonSize] : '',
42+
!isButton && !variant ? styles.ctaTextColors[textColor] : '',
43+
variant ? styles.ctaVariants[variant] : '',
44+
mt ? marginTops[mt] : '',
45+
mb ? marginBottoms[mb] : '',
46+
className,
47+
)}
48+
>
49+
<CtaContent
50+
buttonStyle={buttonStyle}
51+
icon={icon}
52+
iconProps={iconProps}
53+
srText={srText}
54+
align={align}
55+
>
56+
{children}
57+
</CtaContent>
58+
</button>
59+
);
60+
});

components/Cta/CtaContent.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { cnb } from 'cnbuilder';
2+
import { HeroIcon, type IconType } from '@/components/HeroIcon';
3+
import { SrOnlyText } from '@/components/Typography';
4+
import * as styles from './Cta.styles';
5+
import * as types from './Cta.types';
6+
7+
type CtaContentProps = Omit<types.CtaCommonProps, 'buttonSize' | 'textColor'>;
8+
9+
export const CtaContent = ({
10+
icon = 'su-link--action',
11+
iconProps,
12+
srText,
13+
children,
14+
}: CtaContentProps) => {
15+
const heroIcon = icon ? styles.ctaIcons[icon] as IconType : undefined;
16+
const iconMarginLeft = children && icon ? styles.iconLeftMargin[icon] || styles.iconLeftMarginDefault : '';
17+
const { className: iconClasses, ...iProps } = iconProps || {};
18+
const iconAnimate = icon ? styles.iconAnimations[icon] : '';
19+
20+
return (
21+
<>
22+
{children}
23+
{/* Use this whitespace-nowrap trick so icon won't get pushed to the next line on its own */}
24+
{heroIcon && (
25+
<span className="whitespace-nowrap">
26+
&#65279;
27+
<HeroIcon
28+
icon={heroIcon}
29+
className={cnb(styles.icon, iconAnimate, iconMarginLeft, iconClasses)}
30+
{...iProps}
31+
/>
32+
</span>
33+
)}
34+
{srText && <SrOnlyText>{srText}</SrOnlyText>}
35+
</>
36+
);
37+
};

components/Cta/CtaExternalLink.tsx

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
'use client';
2+
import React, { useEffect, useState } from 'react';
3+
import { cnb } from 'cnbuilder';
4+
import { CtaContent } from './CtaContent';
5+
import { type CtaCommonProps } from './Cta.types';
6+
import { type SbLinkType } from '../Storyblok/Storyblok.types';
7+
import { marginTops, marginBottoms } from '@/utilities/datasource';
8+
import * as styles from './Cta.styles';
9+
import useUTMs from '@/hooks/useUTMs';
10+
11+
export type CtaExternalLinkProps = React.ComponentPropsWithoutRef<'a'> & CtaCommonProps & {
12+
sbLink?: SbLinkType;
13+
href?: string;
14+
rel?: string;
15+
};
16+
17+
export const CtaExternalLink = React.forwardRef<HTMLAnchorElement, CtaExternalLinkProps>((props, ref) => {
18+
const {
19+
isButton,
20+
buttonStyle = 'ood-cta__button--primary su-after-bg-white',
21+
buttonSize = 'default',
22+
textColor = 'su-text-digital-red su-after-bg-digital-red su-text-hocus-sky-dark su-after-bg-hocus-sky-dark',
23+
variant,
24+
icon,
25+
iconProps,
26+
align,
27+
srText,
28+
rel,
29+
mt,
30+
mb,
31+
children,
32+
className,
33+
href,
34+
...rest
35+
} = props;
36+
37+
// Add UTM params to Stanford URLs.
38+
const { isStanfordUrl, addUTMsToUrl } = useUTMs();
39+
const [myHref, setMyHref] = useState<string>(href);
40+
useEffect(() => {
41+
if (typeof window === 'undefined') return;
42+
if (isStanfordUrl(href)) {
43+
setMyHref(addUTMsToUrl(href));
44+
}
45+
}, [href, isStanfordUrl, addUTMsToUrl]);
46+
47+
return (
48+
<a
49+
{...rest}
50+
href={myHref}
51+
rel={rel}
52+
ref={ref as React.ForwardedRef<HTMLAnchorElement>}
53+
className={cnb(
54+
styles.cta,
55+
styles.ctaAligns[align],
56+
isButton ? styles.buttonBase : '',
57+
!isButton && !variant ? styles.textLinkBase : '',
58+
isButton ? styles.ctaButtonStyles[buttonStyle] : '',
59+
isButton ? styles.ctaButtonSizes[buttonSize] : '',
60+
!isButton && !variant ? styles.ctaTextColors[textColor] : '',
61+
variant ? styles.ctaVariants[variant] : '',
62+
mt ? marginTops[mt] : '',
63+
mb ? marginBottoms[mb] : '',
64+
className,
65+
)}
66+
>
67+
<CtaContent
68+
buttonStyle={buttonStyle}
69+
icon={icon}
70+
iconProps={iconProps}
71+
srText={srText}
72+
align={align}
73+
>
74+
{children}
75+
</CtaContent>
76+
</a>
77+
);
78+
});

0 commit comments

Comments
 (0)