Prose
proseStyles rich-text descendants with consistent semantic typography tokens.
Usage
Basic typesetting
Insert the rendered rich text (HTML/JSX/MDX output) into Prose, and the title/paragraph/list/link/inline code uniformly eats the semantics of token.
Hulian typesetting container Prose
Prose takes over the rendered rich text (markdown→HTML, MDX output or handwritten JSX) into a consistent reading layout. Title, paragraph, list,Link,Inline code All references to semantics token, Automatic adaptation of light and dark themes.
Unordered list
- Zero dependency, can be rendered in RSC (use client is not added to the ontology)
- Used for emphasis Bold with Italic
- All colors and rounded corners go to token, don't write it to death
Code Block
import { Prose } from "@hulianui/ui";
<Prose>{htmlContent}</Prose>;Typography is a silent design - the container unifies the rules, and the content only cares about semantics.
Bottom paragraph, verify that the margins of the first and last sub-elements converge.
<Prose>
<h1>Hulian typesetting container Prose</h1>
<p>
Titles, paragraphs, lists, <a href="#">links</a>, <code>inline code</code> and quotes
All semantics token, light and dark themes are automatically adapted.
</p>
<ul>
<li>Zero dependency, can be rendered in RSC</li>
<li> emphasize with <strong>bold</strong> and <em>italic</em></li>
</ul>
<blockquote> Typography is a silent design. </blockquote>
</Prose>Compact size
size="sm" Reduce the standard font size to text-sm, which is suitable for sidebar descriptions and long text in cards.
Compact layout
Suitable for dense scenarios such as sidebar descriptions and long text in cards. The rest of the formatting rules remain the same.
- Sidebar Documentation
- Rich text in the card
<Prose size="sm">
<h2>Compact layout</h2>
<p> is suitable for dense scenes such as sidebar descriptions and long text in cards. The rest of the layout rules remain consistent. </p>
<ul>
<li>Sidebar Document</li>
<li>Rich text in the card</li>
</ul>
</Prose>Collapsible blocks (incl. nested)
GFM details / summary shares the same visual family as code blocks; nested blocks take the subtle background to separate from the outer level.
Collapsible details / summary
GFM collapsible blocks in markdown output inherit Prose typography, sharing the same visual family as code blocks.
Show answer
A list comprehension computes every result into memory at once; a generator expression yields items one at a time while iterating, so the latter never pulls an entire large file into memory.
Show how to read the traceback (one nested level)
Nested collapsible blocks take the subtle background, one step apart from the outer level, so the hierarchy reads in both light and dark themes.
Show full code (collapsed by default)
with open("data.txt") as f:
total = sum(int(line) for line in f)<Prose>
<details open>
<summary>Show answer</summary>
<p>A generator expression yields items one at a time while iterating, never loading the whole dataset into memory.</p>
<details>
<summary>Show how to read the traceback (one nested level)</summary>
<p>Nested collapsible blocks take the subtle background, one step apart from the outer level.</p>
</details>
</details>
</Prose>Wide-table horizontal scrolling
scrollableTables lets many-column tables scroll horizontally inside themselves instead of breaking the measure; headers stop wrapping as a result (otherwise columns collapse to one character wide and never scroll). The trade-off is that table width follows content.
| Timestamp | Upstream channel | Model | Requests | Failure rate | Avg latency |
|---|---|---|---|---|---|
| 2026-08-11 | East China primary | claude-opus-5 | 12,345 | 0.12% | 820ms |
| 2026-08-10 | North China fallback | claude-sonnet-5 | 8,901 | 0.31% | 640ms |
<Prose scrollableTables>
<table>{/* Six-column wide table: scrolls horizontally inside a narrow container */}</table>
</Prose>When to use
Use Prose around already-rendered rich text such as Markdown-to-HTML output, MDX, or handwritten JSX. Descendant selectors give headings, paragraphs, lists, links, inline code, and blockquotes consistent typography across light and dark themes. If the content is a Markdown source string, use Markdown, which already wraps Prose. Use Text or Heading for atomic content rather than wrapping a single sentence.
Import
import { Prose } from "@hulianui/ui"Props
| Name | Type | Default | Description |
|---|---|---|---|
| as | ElementType | "article" | Container element to render. |
| size | "sm" | "base" | "base" | Typography scale. sm uses a text-sm base for long content in sidebars or cards. |
| scrollableTables | boolean | false | Wide-table fallback: turns the table itself into a horizontal scroller so many-column tables no longer break out of the measure (headers stop wrapping as part of this). In exchange the table sizes to its content instead of always filling the measure. |
Inherits HTMLAttributes<HTMLElement> (className / style, etc.).
Slots
| Slot | Type | Description |
|---|---|---|
| children | ReactNode | Rich text content (HTML/JSX) |
Example
<Prose className="max-w-2xl">
<h1>HulianUI typography with Prose</h1>
<p>Rendered rich text receives consistent styling for <a href="#">links</a> and <code>inline code</code>.</p>
<blockquote>Typography sets the rules while content preserves its semantics.</blockquote>
</Prose>Compact content:
<Prose size="sm" className="max-w-2xl">{/* Sidebar or card description */}</Prose>Collapsible blocks (GFM <details>/<summary>; Markdown output is styled as-is, no extra wrapper):
<Prose>
<details open>
<summary>Show the answer</summary>
<p>A generator expression yields items lazily and never loads the whole file.</p>
<details>
<summary>Show how to read the traceback</summary>
<p>A nested block uses the subtle background so it separates from the outer surface.</p>
</details>
</details>
</Prose>Wide tables (the table scrolls inside itself instead of breaking out of the measure):
<Prose scrollableTables>{/* A table with six or more columns */}</Prose>Usage guidelines
scrollableTablesswitches thetabletodisplay: blockand addswhitespace-nowrapto headers. Non-wrapping headers are not decoration, they are what makes scrolling happen at all: withoverflow-x-autoalone the browser squeezes every column down to its min-content width (CJK collapses to one glyph per line, rows grow several times taller), the content never exceeds the scroller, and nothing scrolls. It looks like a bad font size or breakpoint instead. Body cells keep wrapping: one long non-wrapping description would drag the table so wide that the other columns become unreachable.- With
scrollableTableson, the table sizes to its content and no longer always fills the measure (a narrow table shrinks to its content width). Turn it on only for tables that genuinely overflow. - The reason
scrollableTablesexists applies only to the HTML string form: when content arrives throughdangerouslySetInnerHTML, Prose never sees the table node and cannot wrap it in a scroll container, so the only place left to act is thetableitself. With children (JSX nodes), prefer wrapping the wide table in your ownoverflow-x-autocontainer: it targets just the table that overflows and leaves every other table at full width. - See [[chat-bubble-max-w-prose-overflows-narrow-column]]:
max-w-prose(65ch, approximately 398 px) is an absolute maximum that does not account for parent width. In a narrow flex column it can overflow or clip. Usemax-w-[min(65ch,100%)]and addmin-w-0to flex ancestors. Do not combinemax-w-prose max-w-full; both set the same property, so stylesheet order decides which wins.
Related
Text · Heading · Markdown · AuroraText · AnimatedShinyText · AnimatedGradientText
Playground
Hulian Prose
Unified take over rich text typesetting, eat semantics token,Link with code Consistently presented.
Show answer
Collapsible blocks share the same visual family as code blocks; summary text is not selectable.
Containers have unified rules, and the content only cares about semantics.
<Prose>{/* Rich text HTML/JSX */}</Prose>