Skip to content

Commit 95cac9f

Browse files
committed
build out markdown wrapper component, update typography to use adapt version, adjust existing headings
1 parent 7d2f7ab commit 95cac9f

File tree

19 files changed

+1555
-581
lines changed

19 files changed

+1555
-581
lines changed

.github/workflows/build_lint.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ jobs:
1414
node-version-file: '.nvmrc'
1515
- name: Enable Corepack
1616
run: corepack enable
17-
- uses: bahmutov/npm-install@v1
18-
with:
19-
useLockFile: false
17+
- name: Configure git for Yarn
18+
run: git config --global core.autocrlf false
19+
- name: Install dependencies
20+
run: yarn install --immutable
2021
- name: Lint
2122
run: yarn lint
2223
- name: Typecheck

app/page.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,32 @@
11
import ActionLink from "@/components/Cta/ActionLink";
2-
import { H1, H2 } from "@/components/Typography/Headers";
2+
import { Heading } from "@/components/Typography/Heading";
33
import { Text } from "@/components/Typography/Text";
44
import GlobalPage from "@/components/Layout/GlobalPage";
55
import { Section } from "@/components/Section/Section";
66
import ThreeColumn from "@/components/Row/ThreeColumn";
77
import TwoColumn from "@/components/Row/TwoColumn";
88
import Image from "next/image";
9+
import { Container } from "@/components/Container";
10+
import { Markdown } from "@/components/Markdown/Markdown";
911

1012
export default function Home() {
1113
return (
1214
<GlobalPage>
13-
<H1 className="sr-only">Stanford Web Services Home</H1>
14-
<div className="w-full cc xl:p-0 xl:mx-auto xl:max-w-[1000px] rs-mt-6 rs-mb-7">
15-
<Text size={4} font="serif">
16-
Vestibulum massa nibh, fermentum ut orci iaculis, placerat viverra
17-
augue. In mauris sapien, vulputate non eros ac, malesuada.
18-
</Text>
19-
</div>
15+
<Heading as="h1" className="sr-only">
16+
Stanford Web Services Home
17+
</Heading>
18+
<Container mb={7} mt={6}>
19+
<div className="max-w-900">
20+
<Markdown size={2}>{`
21+
# Our work
22+
23+
Donec efficitur lectus dolor, id lobortis nulla posuere
24+
vel. Phasellus pulvinar tincidunt tortor, sit amet iaculis nisi
25+
dignissim egestas. Morbi cursus, felis id dictum eleifend, urna diam
26+
volutpat mi, ac suscipit enim metus ac enim.
27+
`}</Markdown>
28+
</div>
29+
</Container>
2030
<Section
2131
src="/images/stanford_dish.jpg"
2232
hasBgImage
@@ -29,7 +39,9 @@ export default function Home() {
2939
<Text variant="card" className="rs-mb-8">
3040
Lasting Partnerships
3141
</Text>
32-
<H2 className="font-serif font-normal type-3">Momentum</H2>
42+
<Heading as="h2" size={3} weight="normal" font="serif">
43+
Momentum
44+
</Heading>
3345
<Text>
3446
Mauris vel nunc rutrum, semper neque a, venenatis metus. Nulla
3547
egestas, enim ut blandit pulvinar, lorem tellus pulvinar ex, a

components/Accordion/Accordion.tsx

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
import { HTMLAttributes, JSX, useId } from "react";
44
import { useBoolean } from "usehooks-ts";
5-
import {
6-
H2, H3, H4, H5,
7-
} from "@/components/Typography/Headers";
5+
import { Heading } from "@/components/Typography/Heading";
86
import { ChevronDownIcon } from "@heroicons/react/20/solid";
97
import { clsx } from "clsx";
108
import twMerge from "@/utilities/utils/twMerge";
@@ -68,27 +66,9 @@ const Accordion = ({
6866
// When the accordion is externally controlled.
6967
const isExpanded = onClick ? isVisible : expanded;
7068

71-
let Heading;
72-
switch (headingLevel) {
73-
case "h3":
74-
Heading = H3;
75-
break;
76-
77-
case "h4":
78-
Heading = H4;
79-
break;
80-
81-
case "h5":
82-
Heading = H5;
83-
break;
84-
85-
default:
86-
Heading = H2;
87-
}
88-
8969
return (
9070
<section aria-labelledby={`${id}-button`} {...props}>
91-
<Heading id={`${id}-button`}>
71+
<Heading as={headingLevel} id={`${id}-button`}>
9272
<button
9373
{...buttonProps}
9474
className={twMerge(

components/CardParagraph/CardParagraph.tsx

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { HtmlHTMLAttributes } from "react";
2-
import { H2, H3, H4 } from "@/components/Typography/Headers";
2+
import { Heading } from "@/components/Typography/Heading";
33
import ActionLink from "@/components/Cta/ActionLink";
44
import Button from "@/components/Cta/Button";
55
import ImageCard from "@/components/ImageCard/ImageCard";
@@ -52,22 +52,11 @@ const CardParagraph = ({
5252
>
5353
{header && (
5454
<>
55-
{headingLevel === "h2" && (
56-
<H2 id={id} className={headerClasses}>
55+
{headingLevel !== "div" ? (
56+
<Heading as={headingLevel} id={id} className={headerClasses}>
5757
{header}
58-
</H2>
59-
)}
60-
{headingLevel === "h3" && (
61-
<H3 id={id} className={headerClasses}>
62-
{header}
63-
</H3>
64-
)}
65-
{headingLevel === "h4" && (
66-
<H4 id={id} className={headerClasses}>
67-
{header}
68-
</H4>
69-
)}
70-
{headingLevel === "div" && (
58+
</Heading>
59+
) : (
7160
<div className={headerClasses}>{header}</div>
7261
)}
7362
</>
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import {
2+
ArrowPathIcon,
3+
ArrowUpIcon,
4+
ArrowDownIcon,
5+
ArrowDownTrayIcon,
6+
ArrowLeftIcon,
7+
ArrowRightIcon,
8+
ArrowsPointingOutIcon,
9+
ArrowUpRightIcon,
10+
Bars3Icon,
11+
ChevronDownIcon,
12+
ChevronLeftIcon,
13+
ChevronRightIcon,
14+
ChevronUpIcon,
15+
CursorArrowRaysIcon,
16+
DocumentDuplicateIcon,
17+
EnvelopeIcon,
18+
LinkIcon,
19+
MapPinIcon,
20+
MinusIcon,
21+
PlayCircleIcon,
22+
PlusIcon,
23+
XMarkIcon,
24+
} from "@heroicons/react/24/outline";
25+
import { CheckIcon } from "@heroicons/react/16/solid";
26+
import { PlayIcon, PauseIcon } from "@heroicons/react/20/solid";
27+
28+
export const iconMap = {
29+
action: ChevronRightIcon,
30+
"arrow-right": ArrowRightIcon,
31+
"arrow-left": ArrowLeftIcon,
32+
"arrow-up": ArrowUpIcon,
33+
"arrow-down": ArrowDownIcon,
34+
back: ArrowLeftIcon,
35+
copy: DocumentDuplicateIcon,
36+
check: CheckIcon,
37+
"chevron-down": ChevronDownIcon,
38+
"chevron-left": ChevronLeftIcon,
39+
"chevron-right": ChevronRightIcon,
40+
"chevron-up": ChevronUpIcon,
41+
download: ArrowDownTrayIcon,
42+
expand: ArrowsPointingOutIcon,
43+
"triangle-down": PlayIcon,
44+
"triangle-right": PlayIcon,
45+
"triangle-up": PlayIcon,
46+
cursor: CursorArrowRaysIcon,
47+
close: XMarkIcon,
48+
email: EnvelopeIcon,
49+
external: ArrowUpRightIcon,
50+
flip: ArrowPathIcon,
51+
left: ArrowLeftIcon,
52+
link: LinkIcon,
53+
location: MapPinIcon,
54+
menu: Bars3Icon,
55+
minus: MinusIcon,
56+
more: ArrowRightIcon,
57+
pause: PauseIcon,
58+
play: PlayIcon,
59+
"play-outline": PlayCircleIcon,
60+
plus: PlusIcon,
61+
right: ArrowRightIcon,
62+
up: ArrowUpIcon,
63+
};
64+
export type IconType = keyof typeof iconMap;
65+
66+
/**
67+
* Normalized base size and position of each icon (finetuned manually) for use in eg, buttons
68+
* Only add to this map if different from default class w-1em
69+
* If you wish to use the HeroIcon without any base styles, set the noBaseStyle boolean prop to true
70+
*/
71+
72+
// This basically means that the keys from iconBaseStyle are from the keys of iconMap
73+
type IconBaseStyleType = Partial<{
74+
[Key in IconType]: string;
75+
}>;
76+
77+
export const iconBaseStyleDefault = "w-1em";
78+
export const iconBaseStyle: IconBaseStyleType = {
79+
"arrow-left": "w-09em -mt-01em",
80+
"arrow-right": "w-09em -mt-01em",
81+
"triangle-right": "w-09em scale-x-90 mt-01em",
82+
"triangle-down": "w-09em scale-x-90 rotate-90 mt-01em",
83+
"triangle-up": "w-09em scale-x-90 -rotate-90 mt-02em",
84+
download: "w-09em",
85+
expand: "w-1em -mt-02em",
86+
external: "w-08em stroke-[2.5]",
87+
left: "w-08em",
88+
link: "w-09em -mt-01em",
89+
more: "w-08em",
90+
plus: "w-08em",
91+
right: "w-08em",
92+
};

components/HeroIcon/HeroIcon.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import * as styles from "./HeroIcon.styles";
2+
import { clsx } from "clsx";
3+
4+
export type HeroIconProps = Omit<React.SVGProps<SVGSVGElement>, "ref"> & {
5+
icon: styles.IconType;
6+
// Title for the SVG for accessibility
7+
title?: string;
8+
noBaseStyle?: boolean;
9+
};
10+
11+
export const HeroIcon = ({
12+
icon,
13+
title,
14+
noBaseStyle,
15+
className,
16+
...props
17+
}: HeroIconProps) => {
18+
const Icon = styles.iconMap[icon];
19+
20+
// Set default base style so icon has reasonable size if used out of the box
21+
// noBaseStyle boolean allows for user to not attach any base styles if needed
22+
const baseStyle = noBaseStyle
23+
? ""
24+
: styles.iconBaseStyle[icon] || styles.iconBaseStyleDefault;
25+
const heroIconStyle = clsx("transition", baseStyle);
26+
27+
return (
28+
<Icon
29+
title={title}
30+
role={!!title ? "img" : undefined}
31+
// If a title for the SVG is provided, unhide the SVG from screen readers
32+
aria-hidden={!title}
33+
className={clsx(heroIconStyle, className)}
34+
{...props}
35+
/>
36+
);
37+
};

components/HeroIcon/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./HeroIcon";
2+
export * from "./HeroIcon.styles";

components/Markdown/Markdown.tsx

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import ReactMarkdown from "react-markdown";
2+
import { Heading } from "@/components/Typography/Heading";
3+
import { Text, type TypographyProps } from "@/components/Typography/Text";
4+
5+
type MarkdownProps = {
6+
/** Raw markdown string to render */
7+
children: string;
8+
className?: string;
9+
} & Pick<TypographyProps, "font" | "size" | "weight" | "color" | "leading">;
10+
11+
function dedent(str: string): string {
12+
const lines = str
13+
.replace(/^\n/, "")
14+
.replace(/\n\s*$/, "")
15+
.split("\n");
16+
const indent = Math.min(
17+
...lines
18+
.filter((l) => l.trim())
19+
.map((l) => l.match(/^ */)?.[0].length ?? 0),
20+
);
21+
return lines.map((l) => l.slice(indent)).join("\n");
22+
}
23+
24+
export const Markdown = ({
25+
children,
26+
font,
27+
size,
28+
weight,
29+
color,
30+
leading,
31+
}: MarkdownProps) => (
32+
<ReactMarkdown
33+
components={{
34+
h1: ({ children }) => <Heading as="h1">{children}</Heading>,
35+
h2: ({ children }) => <Heading as="h2">{children}</Heading>,
36+
h3: ({ children }) => <Heading as="h3">{children}</Heading>,
37+
h4: ({ children }) => <Heading as="h4">{children}</Heading>,
38+
h5: ({ children }) => <Heading as="h5">{children}</Heading>,
39+
h6: ({ children }) => <Heading as="h6">{children}</Heading>,
40+
p: ({ children }) => (
41+
<Text
42+
as="p"
43+
font={font}
44+
size={size}
45+
weight={weight}
46+
color={color}
47+
leading={leading}
48+
>
49+
{children}
50+
</Text>
51+
),
52+
strong: ({ children }) => (
53+
<Text as="strong" weight="bold">
54+
{children}
55+
</Text>
56+
),
57+
em: ({ children }) => (
58+
<Text as="em" italic>
59+
{children}
60+
</Text>
61+
),
62+
blockquote: ({ children }) => (
63+
<Text as="blockquote" font="serif">
64+
{children}
65+
</Text>
66+
),
67+
ul: ({ children }) => (
68+
<Text as="ul" font={font} size={size} color={color} leading={leading}>
69+
{children}
70+
</Text>
71+
),
72+
ol: ({ children }) => (
73+
<Text as="ol" font={font} size={size} color={color} leading={leading}>
74+
{children}
75+
</Text>
76+
),
77+
li: ({ children }) => <Text as="li">{children}</Text>,
78+
a: ({ href, children }) => (
79+
<a
80+
href={href}
81+
className="text-digital-red hover:text-black hover:underline focus:text-black focus:underline"
82+
>
83+
{children}
84+
</a>
85+
),
86+
pre: ({ children }) => <Text as="pre">{children}</Text>,
87+
del: ({ children }) => <Text as="del">{children}</Text>,
88+
sub: ({ children }) => <Text as="sub">{children}</Text>,
89+
sup: ({ children }) => <Text as="sup">{children}</Text>,
90+
small: ({ children }) => (
91+
<Text as="small">
92+
{children}
93+
</Text>
94+
),
95+
}}
96+
>
97+
{dedent(children)}
98+
</ReactMarkdown>
99+
);

0 commit comments

Comments
 (0)