diff --git a/packages/react-components/src/App.tsx b/packages/react-components/src/App.tsx
index 10c81c60..67a08db6 100644
--- a/packages/react-components/src/App.tsx
+++ b/packages/react-components/src/App.tsx
@@ -18,6 +18,7 @@ import {
RadioGroupPage,
SelectPage,
TagGroupPage,
+ TextPage,
TextAreaPage,
TextFieldPage,
SwitchPage,
@@ -169,6 +170,7 @@ function App() {
+
+ );
+}
diff --git a/packages/react-components/src/components/Heading/index.ts b/packages/react-components/src/components/Heading/index.ts
new file mode 100644
index 00000000..25159477
--- /dev/null
+++ b/packages/react-components/src/components/Heading/index.ts
@@ -0,0 +1,2 @@
+export { default } from "./Heading";
+export type { HeadingProps } from "./Heading";
diff --git a/packages/react-components/src/components/Text/Text.css b/packages/react-components/src/components/Text/Text.css
new file mode 100644
index 00000000..cce57a6d
--- /dev/null
+++ b/packages/react-components/src/components/Text/Text.css
@@ -0,0 +1,64 @@
+/* Medium size (default) */
+.bcds-react-aria-Text.medium {
+ font: var(--typography-regular-body);
+}
+
+.bcds-react-aria-Text.medium b,
+.bcds-react-aria-Text.medium strong {
+ font: var(--typography-bold-body);
+}
+
+.bcds-react-aria-Text.medium i,
+.bcds-react-aria-Text.medium em {
+ font: var(--typography-italic-body);
+}
+
+/* Small size */
+.bcds-react-aria-Text.small {
+ font: var(--typography-regular-small-body);
+}
+
+.bcds-react-aria-Text.small b,
+.bcds-react-aria-Text.small strong {
+ font: var(--typography-bold-small-body);
+}
+
+.bcds-react-aria-Text.small i,
+.bcds-react-aria-Text.small em {
+ font: var(--typography-italic-small-body);
+}
+
+/* Large size */
+.bcds-react-aria-Text.large {
+ font: var(--typography-regular-large-body);
+}
+
+.bcds-react-aria-Text.large b,
+.bcds-react-aria-Text.large strong {
+ font: var(--typography-bold-large-body);
+}
+
+.bcds-react-aria-Text.large i,
+.bcds-react-aria-Text.large em {
+ font: var(--typography-italic-large-body);
+}
+
+/* Text color */
+.bcds-react-aria-Text.primary {
+ color: var(--typography-color-primary);
+}
+.bcds-react-aria-Text.primaryInvert {
+ color: var(--typography-color-primary-invert);
+}
+.bcds-react-aria-Text.secondary {
+ color: var(--typography-color-secondary);
+}
+.bcds-react-aria-Text.secondaryInvert {
+ color: var(--typography-color-secondary-invert);
+}
+.bcds-react-aria-Text.disabled {
+ color: var(--typography-color-disabled);
+}
+.bcds-react-aria-Text.danger {
+ color: var(--typography-color-danger);
+}
diff --git a/packages/react-components/src/components/Text/Text.tsx b/packages/react-components/src/components/Text/Text.tsx
new file mode 100644
index 00000000..70a75057
--- /dev/null
+++ b/packages/react-components/src/components/Text/Text.tsx
@@ -0,0 +1,41 @@
+import {
+ Text as ReactAriaText,
+ TextProps as ReactAriaTextProps,
+} from "react-aria-components";
+
+import "./Text.css";
+
+export interface TextProps extends ReactAriaTextProps {
+ /* Sets text size, defaults to medium */
+ size?: "small" | "medium" | "large";
+ /* Sets text color, defaults to primary */
+ color?:
+ | "primary"
+ | "primaryInvert"
+ | "secondary"
+ | "secondaryInvert"
+ | "disabled"
+ | "danger";
+ /* If true, renders component without CSS class */
+ isUnstyled?: boolean;
+}
+
+export default function Text({
+ elementType = "span",
+ size = "medium",
+ color = "primary",
+ isUnstyled = false,
+ ...props
+}: TextProps) {
+ return (
+
+ {props.children}
+
+ );
+}
diff --git a/packages/react-components/src/components/Text/index.ts b/packages/react-components/src/components/Text/index.ts
new file mode 100644
index 00000000..246a4c44
--- /dev/null
+++ b/packages/react-components/src/components/Text/index.ts
@@ -0,0 +1,2 @@
+export { default } from "./Text";
+export type { TextProps } from "./Text";
diff --git a/packages/react-components/src/components/index.ts b/packages/react-components/src/components/index.ts
index fdcd2fcd..36709786 100644
--- a/packages/react-components/src/components/index.ts
+++ b/packages/react-components/src/components/index.ts
@@ -13,6 +13,7 @@ export { default as Dialog, DialogTrigger } from "./Dialog";
export { default as Footer, FooterLinks } from "./Footer";
export { default as Form } from "./Form";
export { default as Header } from "./Header";
+export { default as Heading } from "./Heading";
export { default as InlineAlert } from "./InlineAlert";
export { default as Link } from "./Link";
export { default as Modal } from "./Modal";
@@ -35,6 +36,7 @@ export { default as SvgTooltipArrowUp } from "./Icons/SvgTooltipArrowUp";
export { default as Tag } from "./Tag";
export { default as TagGroup } from "./TagGroup";
export { default as TagList } from "./TagList";
+export { default as Text } from "./Text";
export { default as TextArea } from "./TextArea";
export { default as TextField } from "./TextField";
export { default as Switch } from "./Switch";
diff --git a/packages/react-components/src/pages/TextPage/TextPage.tsx b/packages/react-components/src/pages/TextPage/TextPage.tsx
new file mode 100644
index 00000000..b92b3682
--- /dev/null
+++ b/packages/react-components/src/pages/TextPage/TextPage.tsx
@@ -0,0 +1,55 @@
+import { Heading, Text } from "@/components";
+
+export default function TextPage() {
+ return (
+ <>
+ Headings and text
+
+ The Heading and Text components provide an easy way to populate sections
+ of content. They implement the B.C. Design System's typescale.
+
+ Headings
+
+ The Heading component supports standard HTML heading levels (H1 to H6).
+
+ Text size
+ The text component supports 3 different sizes:
+
+
+ Large
+
+
+ Medium (default)
+
+
+ Small
+
+
+ Text styling
+
+ The text component also supports standard text styling, like bold{" "}
+ and italic.
+
+ Text colour
+ You can modify the colour of text via prop:
+
+
+ Primary (default)
+
+
+ Secondary
+
+
+ Disabled
+
+
+ Danger
+
+
+
+ Inverted versions of the primary and secondary colours for use on dark
+ backgrounds are also supported.
+
+ >
+ );
+}
diff --git a/packages/react-components/src/pages/TextPage/index.ts b/packages/react-components/src/pages/TextPage/index.ts
new file mode 100644
index 00000000..296cb694
--- /dev/null
+++ b/packages/react-components/src/pages/TextPage/index.ts
@@ -0,0 +1,3 @@
+import TextPage from "./TextPage";
+
+export default TextPage;
diff --git a/packages/react-components/src/pages/index.ts b/packages/react-components/src/pages/index.ts
index 196bc8e2..c8639294 100644
--- a/packages/react-components/src/pages/index.ts
+++ b/packages/react-components/src/pages/index.ts
@@ -9,6 +9,7 @@ import ModalDialogPage from "./ModalDialog";
import RadioGroupPage from "./RadioGroup";
import SelectPage from "./Select";
import TagGroupPage from "./TagGroup";
+import TextPage from "./TextPage";
import TextAreaPage from "./TextArea";
import TextFieldPage from "./TextField";
import SwitchPage from "./Switch";
@@ -26,6 +27,7 @@ export {
RadioGroupPage,
SelectPage,
TagGroupPage,
+ TextPage,
TextAreaPage,
TextFieldPage,
SwitchPage,
diff --git a/packages/react-components/src/stories/Heading.mdx b/packages/react-components/src/stories/Heading.mdx
new file mode 100644
index 00000000..c57e26f0
--- /dev/null
+++ b/packages/react-components/src/stories/Heading.mdx
@@ -0,0 +1,61 @@
+{/* Heading.mdx */}
+
+import {
+ Canvas,
+ Controls,
+ Meta,
+ Primary,
+ Source,
+ Story,
+ Subtitle,
+} from "@storybook/blocks";
+
+import * as HeadingStories from "./Heading.stories";
+
+
+
+# Headings
+
+Utility content for creating section headings
+
+
+
+## Usage and resources
+
+Heading is a utility component that creates a text heading for a section of content. It renders a standard heading element (`
` to `
`), styled to align with the [B.C. Design System typescale](https://www2.gov.bc.ca/gov/content?id=72C2CD6E05494C84B9A072DD9C6A5342).
+
+## Controls
+
+
+
+
+## Configuration
+
+Heading text is populated via the `children` slot.
+
+### Styling headings
+
+You can override the default styling of a heading by either:
+
+- Passing a `className` to apply your own CSS class
+- Setting the `isUnstyled` prop to render a heading without any additional CSS, allowing it to inherit its styles from its parent element
+
+
+
+Use the `color` prop to set the text colour, from the options defined in the design system colour palette:
+
+
+
+### Heading level
+
+Use the `level` prop to set the heading level. `level` expects a number from 1 to 6:
+
+
+
+
+
+
+
diff --git a/packages/react-components/src/stories/Heading.stories.tsx b/packages/react-components/src/stories/Heading.stories.tsx
new file mode 100644
index 00000000..6fe8a92f
--- /dev/null
+++ b/packages/react-components/src/stories/Heading.stories.tsx
@@ -0,0 +1,107 @@
+import type { Meta, StoryObj } from "@storybook/react";
+
+import { Heading } from "../components";
+import { HeadingProps } from "../components/Heading";
+
+const meta = {
+ title: "Utility/Headings and text/Headings",
+ component: Heading,
+ parameters: { layout: "centered" },
+ argTypes: {
+ level: {
+ control: { type: "number" },
+ description: "Heading level, expects a number between 1 and 6",
+ },
+ children: {
+ control: { type: "object" },
+ description: "Populates heading text",
+ },
+ color: {
+ control: {
+ type: "select",
+ options: [
+ "primary",
+ "primaryInvert",
+ "secondary",
+ "secondaryInvert",
+ "disabled",
+ "danger",
+ ],
+ },
+ },
+ isUnstyled: {
+ control: { type: "boolean" },
+ description:
+ "Removes CSS class, allowing component to inherit its styles",
+ },
+ className: {
+ control: { type: "text" },
+ description: "Overrides CSS class",
+ },
+ },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const HeadingTemplate: Story = {
+ args: { level: 1, children: ["This is a heading"] },
+ render: ({ ...args }: HeadingProps) => ,
+};
+
+export const PrimaryInvert: Story = {
+ args: {
+ level: 2,
+ color: "primaryInvert",
+ children: ["Heading with inverted primary colour"],
+ },
+ render: ({ ...args }: HeadingProps) => (
+
+
+
+ ),
+};
+
+export const UnstyledHeading: Story = {
+ ...HeadingTemplate,
+ args: {
+ isUnstyled: true,
+ level: 1,
+ children: ["This heading is unstyled, and can inherit its styling"],
+ },
+};
+
+export const Heading1: Story = {
+ ...HeadingTemplate,
+ args: { level: 1, children: ["Heading 1"] },
+};
+
+export const Heading2: Story = {
+ ...HeadingTemplate,
+ args: { level: 2, children: ["Heading 2"] },
+};
+
+export const Heading3: Story = {
+ ...HeadingTemplate,
+ args: { level: 3, children: ["Heading 3"] },
+};
+
+export const Heading4: Story = {
+ ...HeadingTemplate,
+ args: { level: 4, children: ["Heading 4"] },
+};
+
+export const Heading5: Story = {
+ ...HeadingTemplate,
+ args: { level: 5, children: ["Heading 5"] },
+};
+
+export const Heading6: Story = {
+ ...HeadingTemplate,
+ args: { level: 6, children: ["Heading 6"] },
+};
diff --git a/packages/react-components/src/stories/Text.mdx b/packages/react-components/src/stories/Text.mdx
new file mode 100644
index 00000000..56cf3a7a
--- /dev/null
+++ b/packages/react-components/src/stories/Text.mdx
@@ -0,0 +1,66 @@
+{/* Text.mdx */}
+
+import {
+ Canvas,
+ Controls,
+ Meta,
+ Primary,
+ Source,
+ Story,
+ Subtitle,
+} from "@storybook/blocks";
+
+import * as TextStories from "./Text.stories";
+
+
+
+# Text
+
+Utility component for populating text content
+
+
+
+## Usage and resources
+
+Text is a utility component used to create sections of text content within an interface. It is also used internally by other components.
+
+It is styled to align with the [B.C. Design System typescale](https://www2.gov.bc.ca/gov/content?id=72C2CD6E05494C84B9A072DD9C6A5342).
+
+## Controls
+
+
+
+
+## Configuration
+
+Text content is populated via the `children` slot.
+
+By default, Text renders a ``. Use the `elementType` prop to render a different HTML element.
+
+### Text size
+
+Use the `size` prop to toggle between small, medium (default) and large text sizes:
+
+
+
+
+
+### Styling text
+
+By default, Text is styled to align with the [B.C. Design System typescale](https://www2.gov.bc.ca/gov/content?id=72C2CD6E05494C84B9A072DD9C6A5342). If needed, you can override the default styling by either:
+
+- Passing a `className` to apply your own CSS class
+- Setting the `isUnstyled` prop to render text without any additional CSS, allowing it to inherit its styles from its parent element
+
+
+
+Use the `color` prop to set the text colour, from the options defined in the design system colour palette:
+
+
+
+
+
+You can also use the `style` prop to apply inline CSS to text.
diff --git a/packages/react-components/src/stories/Text.stories.tsx b/packages/react-components/src/stories/Text.stories.tsx
new file mode 100644
index 00000000..9359de9d
--- /dev/null
+++ b/packages/react-components/src/stories/Text.stories.tsx
@@ -0,0 +1,104 @@
+import type { Meta, StoryObj } from "@storybook/react";
+
+import { Text } from "../components";
+import { TextProps } from "../components/Text";
+
+const meta = {
+ title: "Utility/Headings and text/Text",
+ component: Text,
+ parameters: {
+ layout: "centered",
+ },
+ argTypes: {
+ children: {
+ control: { type: "object" },
+ description: "Populates the component's content",
+ },
+ elementType: {
+ control: { type: "text" },
+ description: "Defines the type of HTML element that Text renders",
+ },
+ color: {
+ control: {
+ type: "select",
+ options: [
+ "primary",
+ "primaryInvert",
+ "secondary",
+ "secondaryInvert",
+ "disabled",
+ "danger",
+ ],
+ },
+ },
+ isUnstyled: {
+ control: { type: "boolean" },
+ description:
+ "Removes CSS class, allowing component to inherit its styles",
+ },
+ className: {
+ control: { type: "text" },
+ description: "Overrides CSS class",
+ },
+ },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const TextTemplate: Story = {
+ args: { elementType: "span", children: ["This is some text"] },
+ render: ({ ...args }: TextProps) => ,
+};
+
+export const SmallText: Story = {
+ args: { size: "small", children: ["This text uses the small size"] },
+};
+
+export const MediumText: Story = {
+ args: { size: "medium", children: ["This text uses the medium size"] },
+};
+
+export const LargeText: Story = {
+ args: { size: "large", children: ["This text uses the large size"] },
+};
+
+export const UnstyledText: Story = {
+ ...TextTemplate,
+ args: {
+ isUnstyled: true,
+ children: ["This text is unstyled, and can inherit its styling"],
+ },
+};
+
+export const DisabledText: Story = {
+ ...TextTemplate,
+ args: {
+ color: "disabled",
+ children: ["This text uses the 'disabled' colour"],
+ },
+ render: ({ ...args }: TextProps) => ,
+};
+
+export const DangerText: Story = {
+ ...TextTemplate,
+ args: { color: "danger", children: ["This text uses the 'danger' colour"] },
+};
+
+export const PrimaryInvert: Story = {
+ ...TextTemplate,
+ args: {
+ color: "primaryInvert",
+ children: ["This text uses the inverted primary colour"],
+ },
+ render: ({ ...args }: TextProps) => (
+