Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .lastsync

This file was deleted.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openstax/ui-components",
"version": "1.23.1",
"version": "1.23.2",
"license": "MIT",
"sideEffects": [
"**/*.css"
Expand Down
10 changes: 9 additions & 1 deletion src/components/Text.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { H2, H3, Paragraph } from "./Text";
import { H2, H3, Heading as HeadingComponent, Paragraph } from "./Text";

export const PText = () => (
<>
Expand All @@ -12,3 +12,11 @@ export const Heading = () => (
<H3>H3 Heading text</H3>
</>
);

// Semantic level and visual style are independent: this renders an <h2>
// element (correct document outline) styled like an h3.
export const HeadingStyleOverride = () => (
<HeadingComponent level={2} variant="h3">
h2 element with h3 style
</HeadingComponent>
);
40 changes: 34 additions & 6 deletions src/components/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,46 @@ import React from 'react';
import classNames from 'classnames';
import './Text.css';

export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;

/**
* Visual style applied to a heading, decoupled from its semantic level.
* Maps to the `text-*` classes in Text.css.
*/
export type HeadingVariant = 'h2' | 'h3';

export interface HeadingProps extends React.ComponentPropsWithoutRef<'h2'> {
/** Semantic heading level — controls which `h1`–`h6` element is rendered. */
level: HeadingLevel;
/** Visual style to apply. Defaults to match `level` (e.g. `level={2}` → `h2`). */
variant?: HeadingVariant;
}

/**
* Renders a heading whose semantic level (the rendered `h1`–`h6` tag) is
* independent of its visual style (`variant`). This lets consumers preserve a
* correct document outline while reusing an existing heading style — e.g.
* `<Heading level={2} variant="h3">` is an `<h2>` styled like an h3.
*/
export const Heading = React.forwardRef<HTMLHeadingElement, HeadingProps>(
({ level, variant, className, ...props }, ref) => {
const Tag = `h${level}` as const;
// Default the visual style to match the semantic level.
const resolvedVariant = variant ?? (`h${level}` as HeadingVariant);
return <Tag ref={ref} className={classNames(`text-${resolvedVariant}`, className)} {...props} />;
Comment thread
RoyEJohnson marked this conversation as resolved.
}
);
Comment thread
RoyEJohnson marked this conversation as resolved.

Heading.displayName = 'Heading';

export const H2 = React.forwardRef<HTMLHeadingElement, React.ComponentPropsWithoutRef<'h2'>>(
({ className, ...props }, ref) => (
<h2 ref={ref} className={classNames('text-h2', className)} {...props} />
)
(props, ref) => <Heading level={2} ref={ref} {...props} />
Comment thread
RoyEJohnson marked this conversation as resolved.
);

H2.displayName = 'H2';

export const H3 = React.forwardRef<HTMLHeadingElement, React.ComponentPropsWithoutRef<'h3'>>(
({ className, ...props }, ref) => (
<h3 ref={ref} className={classNames('text-h3', className)} {...props} />
)
(props, ref) => <Heading level={3} ref={ref} {...props} />
Comment thread
RoyEJohnson marked this conversation as resolved.
);

H3.displayName = 'H3';
Expand Down
Loading