Repository: Khan/wonder-blocks
Pull request #2623: Adjust BodyText and Heading docs and defaults
Author: login: marcysutton
Created: 2025-05-28T01:19:41Z | Merged: 2025-05-29T23:23:02Z

## Summary:
While integrating the new WB Typography components into webapp, I found a few things needing updates for a smooth transition.

Issue: WB-1966

## Test plan:
1. Ensure tests pass
2. Test integration into webapp


Diff:
diff --git a/.changeset/thirty-toes-do.md b/.changeset/thirty-toes-do.md
new file mode 100644
index 0000000000..174c16d086
--- /dev/null
+++ b/.changeset/thirty-toes-do.md
@@ -0,0 +1,5 @@
+---
+"@khanacademy/wonder-blocks-typography": minor
+---
+
+Adjust styles in BodyText and Heading components for backwards compatibility
diff --git a/__docs__/wonder-blocks-typography/body-text.stories.tsx b/__docs__/wonder-blocks-typography/body-text.stories.tsx
index 83b30e68f4..5b17303bf8 100644
--- a/__docs__/wonder-blocks-typography/body-text.stories.tsx
+++ b/__docs__/wonder-blocks-typography/body-text.stories.tsx
@@ -7,25 +7,59 @@ import ComponentInfo from "../components/component-info";
 import TypographyArgTypes from "./typography.argtypes";
 
 import {View} from "@khanacademy/wonder-blocks-core";
-import {BodyText} from "@khanacademy/wonder-blocks-typography";
+import {
+    BodyText,
+    LabelXSmall,
+    LabelSmall,
+    LabelMedium,
+    LabelLarge,
+    Body,
+} from "@khanacademy/wonder-blocks-typography";
 import {spacing} from "@khanacademy/wonder-blocks-tokens";
 import {allModes} from "../../.storybook/modes";
 
 /**
 New component for Thunderblocks body text.
 
-When wrapped in `<ThemeSwitcher theme="classroom">`, this component will use the
+When wrapped in `<ThemeSwitcher theme="classroom">`, `BodyText` will use the
 Plus Jakarta Sans font family.
 
+## Props
+
+### `size`
+
 The `size` prop will select a font size token based on our
 [REM font sizing scale](/?path=/docs/packages-tokens-typography--docs&globals=theme:thunderblocks#size).
 A corresponding line-height token will be automatically selected from our
 [line-height scale](/?path=/docs/packages-tokens-typography--docs&globals=theme:thunderblocks#lineHeight).
 
-The `weight` prop will match a font weight token based on the available
+Each size resolves to the following font-size and automatic line-height using `font.body` tokens:
+
+- xsmall: `sizing.size_120` / `sizing.size_160` (`LabelXSmall`)
+- small: `sizing.size_140` / `sizing.size_180` (`LabelSmall`)
+- medium (default): `sizing.size_160` / `sizing.size_200` (`LabelMedium`, `LabelLarge`, `Body`)
+
+With no `size` prop set, `BodyText` will default to `medium` font size and line height.
+
+### `weight`
+
+The `weight` prop will match a font weight token for Jakarta based on the available
 [font weights](/?path=/docs/packages-tokens-typography--docs&globals=theme:thunderblocks#weight).
 
-Heading text should utilize the `Heading` component.
+- medium (default): `500`
+- semi: `600`
+- bold: `700`
+
+With no `weight` prop set, `BodyText` will default to `medium` weight.
+
+### `tag`
+
+The `tag` prop will set a tagName, such as `tag="span"`.
+
+`BodyText` renders with a `p` tag by default.
+
+> Note: Heading text should utilize the `Heading` component. If the size or weight you're
+looking for doesn't exist in `BodyText`, consider making it a `Heading`!
 **/
 
 export default {
@@ -110,6 +144,43 @@ export const SizesAndWeights = {
     name: "Sizes and weights",
 };
 
+/**
+ * A table showing the conversion from Classic Typography components to BodyText.
+ */
+export const ClassicConversionGuide = {
+    parameters: {
+        chromatic: {
+            // Disabling because all typography components are covered together
+            // in the Typography stories
+            disableSnapshot: true,
+        },
+    },
+    render: () => (
+        <View style={[styles.grid, styles.conversionGuide]}>
+            <View style={styles.row}>
+                <LabelXSmall tag="p">LabelXSmall</LabelXSmall>
+                <BodyText size="xsmall">BodyText size=xsmall</BodyText>
+            </View>
+            <View style={styles.row}>
+                <LabelSmall tag="p">LabelSmall</LabelSmall>
+                <BodyText size="small">BodyText size=small</BodyText>
+            </View>
+            <View style={styles.row}>
+                <LabelMedium tag="p">LabelMedium</LabelMedium>
+                <BodyText>BodyText</BodyText>
+            </View>
+            <View style={styles.row}>
+                <LabelLarge tag="p">LabelLarge</LabelLarge>
+                <BodyText weight="bold">BodyText weight=bold</BodyText>
+            </View>
+            <View style={styles.row}>
+                <Body tag="p">Body</Body>
+                <BodyText>BodyText</BodyText>
+            </View>
+        </View>
+    ),
+};
+
 const styles = StyleSheet.create({
     grid: {
         display: "grid",
@@ -118,6 +189,10 @@ const styles = StyleSheet.create({
         rowGap: spacing.medium_16,
         columnGap: spacing.large_24,
     },
+    conversionGuide: {
+        justifyContent: "flex-start",
+        gridTemplateColumns: "max-content max-content",
+    },
     row: {
         display: "contents",
     },
diff --git a/__docs__/wonder-blocks-typography/heading.stories.tsx b/__docs__/wonder-blocks-typography/heading.stories.tsx
index 26ed0f4c86..14039ce5ce 100644
--- a/__docs__/wonder-blocks-typography/heading.stories.tsx
+++ b/__docs__/wonder-blocks-typography/heading.stories.tsx
@@ -7,24 +7,73 @@ import ComponentInfo from "../components/component-info";
 import TypographyArgTypes from "./typography.argtypes";
 
 import {View} from "@khanacademy/wonder-blocks-core";
-import {Heading} from "@khanacademy/wonder-blocks-typography";
+import {
+    Heading,
+    HeadingLarge,
+    HeadingMedium,
+    HeadingSmall,
+    HeadingXSmall,
+    Tagline,
+    Title,
+} from "@khanacademy/wonder-blocks-typography";
 import {spacing} from "@khanacademy/wonder-blocks-tokens";
 import {allModes} from "../../.storybook/modes";
 
 /**
 New component for Thunderblocks headings.
 
-When wrapped in `<ThemeSwitcher theme="classroom">`, this component will use the
+When wrapped in `<ThemeSwitcher theme="classroom">`, `Heading` will use the
 Plus Jakarta Sans font family.
 
+## Props
+
+### `size`
+
 The `size` prop will select a font size token based on our
 [REM font sizing scale](/?path=/docs/packages-tokens-typography--docs&globals=theme:thunderblocks#size).
 A corresponding line-height token will be automatically selected from our
 [line-height scale](/?path=/docs/packages-tokens-typography--docs&globals=theme:thunderblocks#lineHeight).
 
-The `weight` prop will match a font weight token based on the available
+Each size resolves to the following font-size and automatic line-height using `font.heading` tokens:
+
+- small: `sizing.size_160` / `sizing.size_200` (`HeadingXSmall`)
+- medium: `sizing.size_180` / `sizing.size_240` (`HeadingSmall`)
+- large (default): `sizing.size_200` / `sizing.size_280` (`HeadingMedium`, `Tagline`)
+- xlarge: `sizing.size_240` / `sizing.size_320` (`HeadingLarge`)
+- xxlarge: `sizing.size_320` / `sizing.size_400` (`Title`)
+
+With no `size` prop set, `Heading` will default to `large` font-size and line-height.
+
+With no `size` or `tag` props set, `Heading` will default to `h2`.
+
+### `weight`
+
+The `weight` prop will match a font weight token for Jakarta based on the available
 [font weights](/?path=/docs/packages-tokens-typography--docs&globals=theme:thunderblocks#weight).
-Only `h1` through `h6` are allowed as tagNames for accessibility purposes.
+
+- medium: `500`
+- semi: `600`
+- bold (default): `700`
+
+With no `weight` prop set, `Heading` will default to `bold` weight.
+
+### `tag`
+
+You can override the heading level for a given content hierarchy with the `tag` prop,
+such as `<Heading size="xlarge" tag="h3">`.
+
+For each `size`, `Heading` will automatically set a heading `tagName` with a default level:
+
+- xxlarge: `"h1"`
+- xlarge: `"h2"`
+- large: `"h3"`
+- medium: `"h4"`
+- small: `"h4"`
+
+If `tag` is specified, that `h1`-`h6` value will be used in favor of the automatic algorithm.
+
+Note: only `h1` through `h6` tags are allowed for accessibility purposes. For other
+use cases, talk to us in the `#wonder-blocks` channel in Slack or raise an issue.
 
 Other content should utilize `BodyText`, primarily in `p` tags.
 **/
@@ -121,6 +170,49 @@ export const SizesAndWeights = {
     name: "Sizes and weights",
 };
 
+/**
+ * A table showing the conversion from Classic Typography components to Heading.
+ */
+export const ClassicConversionGuide = {
+    parameters: {
+        chromatic: {
+            // Disabling because all typography components are covered together
+            // in the Typography stories
+            disableSnapshot: true,
+        },
+    },
+    render: () => (
+        <View style={[styles.grid, styles.conversionGuide]}>
+            <View style={styles.row}>
+                <HeadingXSmall>HeadingXSmall</HeadingXSmall>
+                <Heading size="small">Heading size=small</Heading>
+            </View>
+            <View style={styles.row}>
+                <HeadingSmall>HeadingSmall</HeadingSmall>
+                <Heading size="medium">Heading size=medium</Heading>
+            </View>
+            <View style={styles.row}>
+                <HeadingMedium>HeadingMedium</HeadingMedium>
+                <Heading size="large">Heading size=large</Heading>
+            </View>
+            <View style={styles.row}>
+                <Tagline>Tagline</Tagline>
+                <Heading size="large" weight="medium">
+                    Heading size=large, weight=medium
+                </Heading>
+            </View>
+            <View style={styles.row}>
+                <HeadingLarge>HeadingLarge</HeadingLarge>
+                <Heading size="xlarge">Heading size=xlarge</Heading>
+            </View>
+            <View style={styles.row}>
+                <Title>Title</Title>
+                <Heading size="xxlarge">Heading size=xxlarge</Heading>
+            </View>
+        </View>
+    ),
+};
+
 const styles = StyleSheet.create({
     grid: {
         display: "grid",
@@ -128,6 +220,10 @@ const styles = StyleSheet.create({
         rowGap: spacing.medium_16,
         columnGap: spacing.large_24,
     },
+    conversionGuide: {
+        justifyContent: "flex-start",
+        gridTemplateColumns: "max-content max-content",
+    },
     row: {
         display: "contents",
     },
diff --git a/packages/wonder-blocks-typography/src/components/__tests__/heading.test.tsx b/packages/wonder-blocks-typography/src/components/__tests__/heading.test.tsx
new file mode 100644
index 0000000000..8c21abd1d0
--- /dev/null
+++ b/packages/wonder-blocks-typography/src/components/__tests__/heading.test.tsx
@@ -0,0 +1,98 @@
+import * as React from "react";
+import {render, screen} from "@testing-library/react";
+
+import Heading from "../heading";
+
+describe("Heading", () => {
+    test("defaults to an h2 element", () => {
+        // Arrange
+        render(<Heading>This is a Heading</Heading>);
+
+        // Act
+        const heading = screen.getByRole("heading");
+
+        // Assert
+        expect(heading.tagName).toBe("H2");
+    });
+
+    test("accepts a tag prop for an h1-h6 heading", () => {
+        // Arrange
+        render(<Heading tag="h1">This is a Heading</Heading>);
+
+        // Act
+        const heading = screen.getByRole("heading");
+
+        // Assert
+        expect(heading.tagName).toBe("H1");
+    });
+
+    test("defaults to h1 for size=xxlarge", () => {
+        // Arrange
+        render(<Heading size="xxlarge">This is a Heading</Heading>);
+
+        // Act
+        const heading = screen.getByRole("heading");
+
+        // Assert
+        expect(heading.tagName).toBe("H1");
+    });
+
+    test("defaults to h2 for size=xlarge", () => {
+        // Arrange
+        render(<Heading size="xlarge">This is a Heading</Heading>);
+
+        // Act
+        const heading = screen.getByRole("heading");
+
+        // Assert
+        expect(heading.tagName).toBe("H2");
+    });
+
+    test("defaults to h3 for size=large", () => {
+        // Arrange
+        render(<Heading size="large">This is a Heading</Heading>);
+
+        // Act
+        const heading = screen.getByRole("heading");
+
+        // Assert
+        expect(heading.tagName).toBe("H3");
+    });
+
+    test("defaults to h4 for size=medium", () => {
+        // Arrange
+        render(<Heading size="medium">This is a Heading</Heading>);
+
+        // Act
+        const heading = screen.getByRole("heading");
+
+        // Assert
+        expect(heading.tagName).toBe("H4");
+    });
+
+    test("defaults to h4 for size=small", () => {
+        // Arrange
+        render(<Heading size="small">This is a Heading</Heading>);
+
+        // Act
+        const heading = screen.getByRole("heading");
+
+        // Assert
+        expect(heading.tagName).toBe("H4");
+    });
+
+    test("uses the tag prop over a default size", () => {
+        // Arrange
+        render(
+            <Heading size="xlarge" tag="h4">
+                This is a Heading
+            </Heading>,
+        );
+
+        // Act
+        const heading = screen.getByRole("heading");
+
+        // Assert
+        expect(heading.tagName).toBe("H4");
+    });
+});
diff --git a/packages/wonder-blocks-typography/src/components/body-text.tsx b/packages/wonder-blocks-typography/src/components/body-text.tsx
index dfcded2aa8..48abd199e4 100644
--- a/packages/wonder-blocks-typography/src/components/body-text.tsx
+++ b/packages/wonder-blocks-typography/src/components/body-text.tsx
@@ -10,8 +10,8 @@ type Props = PropsFor<typeof Text> & {
 
 const BodyText = React.forwardRef(function BodyText(
     {
-        size = "small",
-        weight = "semi",
+        size = "medium",
+        weight = "medium",
         style,
         children,
         tag = "p",
diff --git a/packages/wonder-blocks-typography/src/components/heading.tsx b/packages/wonder-blocks-typography/src/components/heading.tsx
index ce8360c50d..7889d4f98e 100644
--- a/packages/wonder-blocks-typography/src/components/heading.tsx
+++ b/packages/wonder-blocks-typography/src/components/heading.tsx
@@ -3,33 +3,42 @@ import {PropsFor, Text} from "@khanacademy/wonder-blocks-core";
 import {font} from "@khanacademy/wonder-blocks-tokens";
 import styles from "../util/styles";
 
+const tagMap = {
+    xxlarge: "h1",
+    xlarge: "h2",
+    large: "h3",
+    medium: "h4",
+    small: "h4",
+} as const;
+
+type HeadingSize = keyof typeof tagMap;
+
 type Props = PropsFor<typeof Text> & {
-    size?: "small" | "medium" | "large" | "xlarge" | "xxlarge";
+    size?: HeadingSize;
     tag?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
-    weight?: "semi" | "bold";
+    weight?: "medium" | "semi" | "bold";
 };
 
-const Heading = React.forwardRef(function Heading(
-    {
-        size = "large",
-        weight = "bold",
-        style,
-        children,
-        tag = "h2",
-        ...otherProps
-    }: Props,
-    ref,
-) {
+const Heading = React.forwardRef(function Heading(props: Props, ref) {
+    const {size, weight = "bold", style, children, tag, ...otherProps} = props;
+
+    // Determine final size for style purposes
+    const finalSize = size ?? "large";
+
+    // Resolve tag: prefer explicit `tag`, fallback to size-based tag if size was provided, or "h2" if neither
+    const resolvedTag = tag ?? (size ? tagMap[size] : "h2");
+
     // map props to theme and global token defaults for CSS styles
     const themeHeading = {
-        fontSize: font.heading.size[size],
+        fontSize: font.heading.size[finalSize],
         fontWeight: font.weight[weight],
-        lineHeight: font.heading.lineHeight[size],
+        lineHeight: font.heading.lineHeight[finalSize],
     };
+
     return (
         <Text
             {...otherProps}
-            tag={tag}
+            tag={resolvedTag}
             style={[styles.Heading, themeHeading, style]}
             ref={ref}
         >


Reviews:
user: login: jandrade
body: This looks great! just left some comments around docs/comments. Thanks for the quick follow up on this 🚢 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-28T17:43:44Z
user: login: beaesguerra
body: Looks great! Left a question about light font weights :) 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-28T17:49:36Z
user: login: marcysutton
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-29T20:39:33Z
user: login: marcysutton
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-29T20:40:24Z
user: login: marcysutton
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-29T20:40:56Z
user: login: marcysutton
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-29T22:04:56Z
user: login: marcysutton
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-29T22:07:03Z

Review comments:
diff_hunk: @@ -3,33 +3,42 @@ import {PropsFor, Text} from "@khanacademy/wonder-blocks-core";
 import {font} from "@khanacademy/wonder-blocks-tokens";
 import styles from "../util/styles";
 
+const tagMap = {
+    xxlarge: "h1",
+    xlarge: "h2",
+    large: "h3",
+    medium: "h4",
+    small: "h4",
+} as const;
path: packages/wonder-blocks-typography/src/components/heading.tsx
user: login: jandrade
body: **praise:** This is great!!
created_at: 2025-05-28T17:41:31Z
author_association: MEMBER
diff_hunk: @@ -3,33 +3,42 @@ import {PropsFor, Text} from "@khanacademy/wonder-blocks-core";
 import {font} from "@khanacademy/wonder-blocks-tokens";
 import styles from "../util/styles";
 
+const tagMap = {
+    xxlarge: "h1",
+    xlarge: "h2",
+    large: "h3",
+    medium: "h4",
+    small: "h4",
+} as const;
+
+type HeadingSize = keyof typeof tagMap;
+
 type Props = PropsFor<typeof Text> & {
-    size?: "small" | "medium" | "large" | "xlarge" | "xxlarge";
+    size?: HeadingSize;
     tag?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
     weight?: "semi" | "bold";
 };
 
-const Heading = React.forwardRef(function Heading(
-    {
-        size = "large",
-        weight = "bold",
-        style,
-        children,
-        tag = "h2",
-        ...otherProps
-    }: Props,
-    ref,
-) {
+const Heading = React.forwardRef(function Heading(props: Props, ref) {
+    const {size, weight = "bold", style, children, tag, ...otherProps} = props;
+
+    // Determine final size for style purposes
+    const finalSize = size ?? "large";
+
+    // Resolve tag: prefer explicit `tag`, fallback to size-based tag if size was provided, or "h2" if neither
+    const resolvedTag = tag ?? (size ? tagMap[size] : "h2");
path: packages/wonder-blocks-typography/src/components/heading.tsx
user: login: jandrade
body: **question/thought:** I wonder if there's documentation around this. If not, I think it would be helpful for devs to understand what the default tags are for each size, and also understand how to override this.
created_at: 2025-05-28T17:42:21Z
author_association: MEMBER
diff_hunk: @@ -5,16 +5,16 @@ import styles from "../util/styles";
 
 type Props = PropsFor<typeof Text> & {
     size?: "xsmall" | "small" | "medium";
-    weight?: "medium" | "semi" | "bold";
+    weight?: "light" | "medium" | "semi" | "bold";
 };
 
 const BodyText = React.forwardRef(function BodyText(
     {
-        size = "small",
-        weight = "semi",
+        size = "medium",
+        weight = "medium",
         style,
         children,
-        tag = "p",
+        tag = "span",
path: packages/wonder-blocks-typography/src/components/body-text.tsx
user: login: jandrade
body: **question:** Is there a particular reason why this changed to `span`? if so, would be a good idea to document the why?
created_at: 2025-05-28T17:43:10Z
author_association: MEMBER
diff_hunk: @@ -5,16 +5,16 @@ import styles from "../util/styles";
 
 type Props = PropsFor<typeof Text> & {
     size?: "xsmall" | "small" | "medium";
-    weight?: "medium" | "semi" | "bold";
+    weight?: "light" | "medium" | "semi" | "bold";
path: packages/wonder-blocks-typography/src/components/body-text.tsx
user: login: beaesguerra
body: **question:** Is `light` a font-weight we want to continue to support? If not, could we update existing `light` usage to `medium`?
created_at: 2025-05-28T17:47:00Z
author_association: MEMBER
diff_hunk: @@ -5,16 +5,16 @@ import styles from "../util/styles";
 
 type Props = PropsFor<typeof Text> & {
     size?: "xsmall" | "small" | "medium";
-    weight?: "medium" | "semi" | "bold";
+    weight?: "light" | "medium" | "semi" | "bold";
 };
 
 const BodyText = React.forwardRef(function BodyText(
     {
-        size = "small",
-        weight = "semi",
+        size = "medium",
+        weight = "medium",
         style,
         children,
-        tag = "p",
+        tag = "span",
path: packages/wonder-blocks-typography/src/components/body-text.tsx
user: login: marcysutton
body: Initially I changed it to `span` to match the expectations of `Body`. But seeing how that tag is used, I think it would be better to create paragraphs by default and document the change. I changed it back to `p` in this PR!!
created_at: 2025-05-29T20:39:33Z
author_association: MEMBER
diff_hunk: @@ -5,16 +5,16 @@ import styles from "../util/styles";
 
 type Props = PropsFor<typeof Text> & {
     size?: "xsmall" | "small" | "medium";
-    weight?: "medium" | "semi" | "bold";
+    weight?: "light" | "medium" | "semi" | "bold";
path: packages/wonder-blocks-typography/src/components/body-text.tsx
user: login: marcysutton
body: There is an instance of `light` in Classroom, which is why I added it here! Otherwise devs will have to use `font.weight.light` and miss out on these props.
created_at: 2025-05-29T20:40:23Z
author_association: MEMBER
diff_hunk: @@ -3,33 +3,42 @@ import {PropsFor, Text} from "@khanacademy/wonder-blocks-core";
 import {font} from "@khanacademy/wonder-blocks-tokens";
 import styles from "../util/styles";
 
+const tagMap = {
+    xxlarge: "h1",
+    xlarge: "h2",
+    large: "h3",
+    medium: "h4",
+    small: "h4",
+} as const;
+
+type HeadingSize = keyof typeof tagMap;
+
 type Props = PropsFor<typeof Text> & {
-    size?: "small" | "medium" | "large" | "xlarge" | "xxlarge";
+    size?: HeadingSize;
     tag?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
     weight?: "semi" | "bold";
 };
 
-const Heading = React.forwardRef(function Heading(
-    {
-        size = "large",
-        weight = "bold",
-        style,
-        children,
-        tag = "h2",
-        ...otherProps
-    }: Props,
-    ref,
-) {
+const Heading = React.forwardRef(function Heading(props: Props, ref) {
+    const {size, weight = "bold", style, children, tag, ...otherProps} = props;
+
+    // Determine final size for style purposes
+    const finalSize = size ?? "large";
+
+    // Resolve tag: prefer explicit `tag`, fallback to size-based tag if size was provided, or "h2" if neither
+    const resolvedTag = tag ?? (size ? tagMap[size] : "h2");
path: packages/wonder-blocks-typography/src/components/heading.tsx
user: login: marcysutton
body: Good idea! Docs will be helpful for this. I'll add them now.
created_at: 2025-05-29T20:40:56Z
author_association: MEMBER
diff_hunk: @@ -5,16 +5,16 @@ import styles from "../util/styles";
 
 type Props = PropsFor<typeof Text> & {
     size?: "xsmall" | "small" | "medium";
-    weight?: "medium" | "semi" | "bold";
+    weight?: "light" | "medium" | "semi" | "bold";
path: packages/wonder-blocks-typography/src/components/body-text.tsx
user: login: marcysutton
body: Removed this change for now!
created_at: 2025-05-29T22:04:56Z
author_association: MEMBER
diff_hunk: @@ -3,33 +3,42 @@ import {PropsFor, Text} from "@khanacademy/wonder-blocks-core";
 import {font} from "@khanacademy/wonder-blocks-tokens";
 import styles from "../util/styles";
 
+const tagMap = {
+    xxlarge: "h1",
+    xlarge: "h2",
+    large: "h3",
+    medium: "h4",
+    small: "h4",
+} as const;
+
+type HeadingSize = keyof typeof tagMap;
+
 type Props = PropsFor<typeof Text> & {
-    size?: "small" | "medium" | "large" | "xlarge" | "xxlarge";
+    size?: HeadingSize;
     tag?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
     weight?: "semi" | "bold";
 };
 
-const Heading = React.forwardRef(function Heading(
-    {
-        size = "large",
-        weight = "bold",
-        style,
-        children,
-        tag = "h2",
-        ...otherProps
-    }: Props,
-    ref,
-) {
+const Heading = React.forwardRef(function Heading(props: Props, ref) {
+    const {size, weight = "bold", style, children, tag, ...otherProps} = props;
+
+    // Determine final size for style purposes
+    const finalSize = size ?? "large";
+
+    // Resolve tag: prefer explicit `tag`, fallback to size-based tag if size was provided, or "h2" if neither
+    const resolvedTag = tag ?? (size ? tagMap[size] : "h2");
path: packages/wonder-blocks-typography/src/components/heading.tsx
user: login: marcysutton
body: Added some docs via the stories for this component and `BodyText`!
created_at: 2025-05-29T22:07:03Z
author_association: MEMBER

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-05-28T01:19:44Z
body: ###  🦋  Changeset detected

Latest commit: e86515d4db6a08faa1b3c714b147c0634b5eb811

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 16 packages</summary>

  | Name                                       | Type  |
| ------------------------------------------ | ----- |
| @khanacademy/wonder-blocks-typography      | Minor |
| @khanacademy/wonder-blocks-accordion       | Patch |
| @khanacademy/wonder-blocks-banner          | Patch |
| @khanacademy/wonder-blocks-birthday-picker | Patch |
| @khanacademy/wonder-blocks-button          | Patch |
| @khanacademy/wonder-blocks-cell            | Patch |
| @khanacademy/wonder-blocks-dropdown        | Patch |
| @khanacademy/wonder-blocks-form            | Patch |
| @khanacademy/wonder-blocks-labeled-field   | Patch |
| @khanacademy/wonder-blocks-modal           | Patch |
| @khanacademy/wonder-blocks-pill            | Patch |
| @khanacademy/wonder-blocks-popover         | Patch |
| @khanacademy/wonder-blocks-search-field    | Patch |
| @khanacademy/wonder-blocks-tabs            | Patch |
| @khanacademy/wonder-blocks-toolbar         | Patch |
| @khanacademy/wonder-blocks-tooltip         | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/Khan/wonder-blocks/new/WB-1966?filename=.changeset/warm-seas-study.md&value=---%0A%22%40khanacademy%2Fwonder-blocks-typography%22%3A%20patch%0A---%0A%0AAdjust%20BodyText%20and%20Heading%20docs%20and%20defaults%0A)


author_association: NONE
user: login: khan-actions-bot
created_at: 2025-05-28T01:19:55Z
body: # Gerald

<details>
<summary><b>Required Reviewers</b></summary>

* `@Khan/wonder-blocks` for changes to `.changeset/thirty-toes-do.md`, `__docs__/wonder-blocks-typography/body-text.stories.tsx`, `__docs__/wonder-blocks-typography/heading.stories.tsx`, `packages/wonder-blocks-typography/src/components/body-text.tsx`, `packages/wonder-blocks-typography/src/components/heading.tsx`, `packages/wonder-blocks-typography/src/components/__tests__/heading.test.tsx`
</details>


__________________________________________________________________________________________________________________________________
_Don't want to be involved in this pull request? Comment `#removeme` and we won't notify you of further changes._
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-28T01:20:54Z
body: **Size Change:** +96 B (+0.1%) 

**Total Size:** 92 kB

| Filename | Size | Change |  |
| :--- | :---: | :---: | :---: |
| `packages/wonder-blocks-typography/dist/es/index.js` | 1.14 kB | +96 B (+9.21%) | 🔍 |

<details><summary>ℹ️ <strong>View Unchanged</strong></summary>

| Filename | Size |
| :--- | :---: |
| `packages/wonder-blocks-accordion/dist/es/index.js` | 2.99 kB |
| `packages/wonder-blocks-announcer/dist/es/index.js` | 1.74 kB |
| `packages/wonder-blocks-banner/dist/es/index.js` | 1.42 kB |
| `packages/wonder-blocks-birthday-picker/dist/es/index.js` | 1.91 kB |
| `packages/wonder-blocks-breadcrumbs/dist/es/index.js` | 723 B |
| `packages/wonder-blocks-button/dist/es/index.js` | 2.96 kB |
| `packages/wonder-blocks-cell/dist/es/index.js` | 2.06 kB |
| `packages/wonder-blocks-clickable/dist/es/index.js` | 2.67 kB |
| `packages/wonder-blocks-core/dist/es/index.js` | 2.48 kB |
| `packages/wonder-blocks-data/dist/es/index.js` | 5.48 kB |
| `packages/wonder-blocks-dropdown/dist/es/index.js` | 16.6 kB |
| `packages/wonder-blocks-form/dist/es/index.js` | 4.94 kB |
| `packages/wonder-blocks-grid/dist/es/index.js` | 1.24 kB |
| `packages/wonder-blocks-icon-button/dist/es/index.js` | 1.9 kB |
| `packages/wonder-blocks-icon/dist/es/index.js` | 690 B |
| `packages/wonder-blocks-labeled-field/dist/es/index.js` | 1.64 kB |
| `packages/wonder-blocks-layout/dist/es/index.js` | 1.63 kB |
| `packages/wonder-blocks-link/dist/es/index.js` | 1.64 kB |
| `packages/wonder-blocks-modal/dist/es/index.js` | 4.99 kB |
| `packages/wonder-blocks-pill/dist/es/index.js` | 1.31 kB |
| `packages/wonder-blocks-popover/dist/es/index.js` | 4.32 kB |
| `packages/wonder-blocks-progress-spinner/dist/es/index.js` | 1.48 kB |
| `packages/wonder-blocks-search-field/dist/es/index.js` | 1.09 kB |
| `packages/wonder-blocks-styles/dist/es/index.js` | 467 B |
| `packages/wonder-blocks-switch/dist/es/index.js` | 1.7 kB |
| `packages/wonder-blocks-tabs/dist/es/index.js` | 3.67 kB |
| `packages/wonder-blocks-testing-core/dist/es/index.js` | 3.51 kB |
| `packages/wonder-blocks-testing/dist/es/index.js` | 985 B |
| `packages/wonder-blocks-theming/dist/es/index.js` | 577 B |
| `packages/wonder-blocks-timing/dist/es/index.js` | 1.37 kB |
| `packages/wonder-blocks-tokens/dist/es/index.js` | 3.3 kB |
| `packages/wonder-blocks-toolbar/dist/es/index.js` | 900 B |
| `packages/wonder-blocks-tooltip/dist/es/index.js` | 6.42 kB |

</details>



<a href="https://github.com/preactjs/compressed-size-action"><sub>compressed-size-action</sub></a>
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-28T01:22:25Z
body: ## npm Snapshot: Published
🎉 Good news!! We've packaged up the latest commit from this PR (6b8d8c5c) and published all packages with changesets to npm.

You can install the packages in webapp by running:
```sh
./services/static/dev/tools/deploy_wonder_blocks.js --tag="PR2623"
```

Packages can also be installed manually by running:
```sh
pnpm add @khanacademy/wonder-blocks-<package-name>@PR2623
```
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-28T01:23:22Z
body: # A new build was pushed to Chromatic! :rocket:
https://5e1bf4b385e3fb0020b7073c-qfnizqwqmt.chromatic.com/

## Chromatic results:

| Metric | Total |
|---|---|
| Captured snapshots | 350 |
| Tests with visual changes | 0 |
| Total stories | 580 |
| Inherited (not captured) snapshots [TurboSnap] | 0 |
| Tests on the build | 350 |
author_association: CONTRIBUTOR
user: login: codecov[bot]
created_at: 2025-05-29T23:23:37Z
body: ## [Codecov](https://app.codecov.io/gh/Khan/wonder-blocks/pull/2623?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Khan) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 0.00%. Comparing base [(`72db0fc`)](https://app.codecov.io/gh/Khan/wonder-blocks/commit/72db0fcf7398fd76a27b7c1f7c065e4e7aee0f6e?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Khan) to head [(`e86515d`)](https://app.codecov.io/gh/Khan/wonder-blocks/commit/e86515d4db6a08faa1b3c714b147c0634b5eb811?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Khan).
> Report is 14 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/Khan/wonder-blocks/pull/2623/graphs/tree.svg?width=650&height=150&src=pr&token=ny8PAxdjV4&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Khan)](https://app.codecov.io/gh/Khan/wonder-blocks/pull/2623?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Khan)

```diff
@@     Coverage Diff      @@
##   main   #2623   +/-   ##
============================
============================

```

------

[Continue to review full report in Codecov by Sentry](https://app.codecov.io/gh/Khan/wonder-blocks/pull/2623?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Khan).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Khan)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://app.codecov.io/gh/Khan/wonder-blocks/pull/2623?dropdown=coverage&src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Khan). Last update [72db0fc...e86515d](https://app.codecov.io/gh/Khan/wonder-blocks/pull/2623?dropdown=coverage&src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Khan). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Khan).

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE