when as='code'", () => {
+ const { container } = render(
+
+ const x = 1
+ ,
+ );
+ expect(container.querySelector("code")).toBeTruthy();
+ });
+
+ it("renders as when as='pre'", () => {
+ const { container } = render(
+
+ preformatted
+ ,
+ );
+ expect(container.querySelector("pre")).toBeTruthy();
+ });
+
// Type-level enforcement of the required `as` prop for heading variants
// lives in `text.type-spec.tsx`. That file is included in the regular
// tsconfig glob, so `pnpm typecheck` evaluates every `@ts-expect-error`
diff --git a/packages/kumo/src/components/text/text.tsx b/packages/kumo/src/components/text/text.tsx
index 292a86249f..a3d2b1767b 100644
--- a/packages/kumo/src/components/text/text.tsx
+++ b/packages/kumo/src/components/text/text.tsx
@@ -148,7 +148,20 @@ export type TextElement =
| "h5"
| "h6"
| "p"
- | "span";
+ | "span"
+ | "label"
+ | "dt"
+ | "dd"
+ | "li"
+ | "figcaption"
+ | "legend"
+ | "pre"
+ | "code"
+ | "em"
+ | "strong"
+ | "small"
+ | "abbr"
+ | "time";
type BaseTextProps = Omit<
ComponentPropsWithoutRef<"span">,
@@ -238,7 +251,10 @@ export interface TextProps {
/** Whether to truncate overflowing text with an ellipsis. Adds `truncate min-w-0` classes. */
truncate?: boolean;
/**
- * The HTML element to render (`"h1"`–`"h6"`, `"p"`, or `"span"`).
+ * The HTML element to render. Accepts headings (`"h1"`–`"h6"`), block text
+ * (`"p"`, `"pre"`), inline text (`"span"`, `"code"`, `"em"`, `"strong"`,
+ * `"small"`, `"abbr"`, `"time"`), form-related (`"label"`, `"legend"`),
+ * list/definition (`"dt"`, `"dd"`, `"li"`), and `"figcaption"`.
*
* - **Required** for heading variants (`"heading1"`, `"heading2"`,
* `"heading3"`) — pick the element that reflects this text's place in
@@ -276,7 +292,7 @@ function _Text(
as,
...props
}: TextPropsInternal,
- ref: ForwardedRef,
+ ref: ForwardedRef,
) {
const isCopy = ["body", "secondary", "success", "error"].includes(variant);
const isMono = ["mono", "mono-secondary"].includes(variant);
@@ -294,7 +310,10 @@ function _Text(
return (
}
className={cn(
"text-kumo-default",
KUMO_TEXT_VARIANTS.variant[variant].classes,
diff --git a/packages/kumo/src/components/text/text.type-spec.tsx b/packages/kumo/src/components/text/text.type-spec.tsx
index 7b939eda0b..18b4f673d5 100644
--- a/packages/kumo/src/components/text/text.type-spec.tsx
+++ b/packages/kumo/src/components/text/text.type-spec.tsx
@@ -42,6 +42,21 @@ const _error = Broken;
const _mono = console.log();
const _monoSecondary = comment;
+// Non-standard text elements — `as` accepts definition list, label, pre, code, etc.
+const _dt = Term;
+const _dd = Definition;
+const _label = Field label;
+const _code = const x = 1;
+const _pre = preformatted;
+const _li = List item;
+const _figcaption = Caption;
+const _legend = Fieldset legend;
+const _em = Emphasized;
+const _strong = Important;
+const _small = Fine print;
+const _time = 2026-04-27;
+const _headingAsLabel = Form heading;
+
// ---------------------------------------------------------------------------
// Negative cases — these MUST NOT compile. The `@ts-expect-error` directive
// asserts that tsc produces an error on the following line; if it doesn't,
@@ -75,6 +90,19 @@ export const __typeSpec = {
_error,
_mono,
_monoSecondary,
+ _dt,
+ _dd,
+ _label,
+ _code,
+ _pre,
+ _li,
+ _figcaption,
+ _legend,
+ _em,
+ _strong,
+ _small,
+ _time,
+ _headingAsLabel,
_missingAsH1,
_missingAsH2,
_missingAsH3,