Theme
Design your own
On this page (30)
Component Skill — components/typography/component-skill.md

Native basis

Native HTML text elements: <h1>–<h4>, <p>, <blockquote>, <code>, <small>.

Web Platform APIs

<h1>–<h6><blockquote><code><small>text-wrap: balancetext-wrap: prettyhanging-punctuationLogical propertiesprefers-contrastforced-colors

Classes

.h1.h2.h3.h4.p.lead.large.small.muted.blockquote.inline-code

Accessibility

• Use heading levels in order (h1 → h2 → h3). Do not skip levels.

• Headings create the document outline used by screen readers for navigation.

• <blockquote> is announced as a quote by assistive technology - no extra ARIA needed.

• <code> is announced as code - no extra ARIA needed.

• prefers-contrast: more - removes tight letter-spacing, adds outline to inline code, thickens blockquote border, promotes muted text to foreground color.

• forced-colors: active - blockquote border and inline code adapt to system colors.

Notes

• These are utility classes for prose content - not a component with variants/sizes.

• The .h1–.h4 classes allow applying heading styles to non-heading elements when semantic headings aren't appropriate.

• Typography classes compose freely with other components (Card content, Dialog body, Alert description).

• text-wrap: balance is used on all headings (h1–h4) for better visual line distribution.

• text-wrap: pretty is used on paragraphs and lead text for orphan prevention.

• hanging-punctuation is used on blockquotes for optical quote alignment.

• Blockquote uses logical properties (border-inline-start, padding-inline-start) for automatic RTL support.

• For lists, use the List component (list.css) - typography does not ship its own list styles.

• For prose tables, use the Table component (table.css) - typography does not ship its own table styles.

§Headings

Four heading levels - h1 is extrabold, h2 includes a bottom border, h3 and h4 are semibold.

§Paragraph

Standard body text with generous line height.

§Lead

Larger, muted intro paragraph.

§Large / Small / Muted

Text size variations.

§Blockquote

Quoted block with left border and italic style.

§Inline code

Monospace code snippet within text.

§Font stacks

Three token-driven stacks - components reference the tokens, never a hardcoded family.

§Prose Composition

All typography classes combined in a full prose layout - matching the shadcn/ui demo.

§RTL support

Blockquotes use logical properties (border-inline-start, padding-inline-start) - they flip automatically in RTL contexts.

§Chinese

lang='zh-Hans' switches the paragraph to CJK rules: justified between characters, strict line breaks (no 。or , at a line start) and a line-end 。 or , kept inside a straight right edge (Safari hangs it, Chromium trims its blank half). Latin words and digits get their thin gap automatically (text-autospace); headings drop the Latin tracking, emphasis uses dots under the characters instead of italics.

§Chinese: aligning the line ends

The same paragraph twice in a narrow column. Left: start-aligned, the lines end ragged and a comma may be pushed to the next line's start. Right: the .p rules - justified, strict breaking, the line-end 。/, hung (Safari) or half-width (Chromium) - a straight edge.

§Japanese, top to bottom

text-vertical sets tategaki: columns run top to bottom, right to left. Ruby (furigana) sits beside the kanji, .text-upright stands a two-digit number upright in one cell (tate-chu-yoko), emphasis marks become sesame dots, and the heading, paragraph gap and blockquote rule turn with the text - they are logical properties.

§Traditional Chinese, vertical

The same vertical rules for zh-Hant: punctuation takes its vertical form (。、「」 rotate or reposition), bopomofo / pinyin ruby sits to the right.

§Typesetting (Satz)

Continuous text set like a book or a journal page: one .typeset block, plain paragraphs inside, and each classic rule of the compositor's craft an opt-in attribute. The examples show every rule on its own first, then captions, separators, leading and figure groups, and only then pages that combine them. The --typeset-* properties tune each block; they are this component's knobs, not theme tokens, so a theme stays the tweakcn token set.

§Columns

data-columns='2' sets two columns, data-rule a hairline between them. No column is narrower than --typeset-column-width (18rem): switch the preview to Phone and the block sets one column - no media query. A .typeset-span heading or figure crosses both columns, and the columns below it start level.

§Columns in a sans and a monospace face

The setting is not tied to the serif: the same two ruled columns in the theme's sans (left) and monospace (right) stacks - a technical note or a listing's commentary. The monospace column hyphenates less (hyphens: manual) and keeps the figures lining.

§Blocksatz (justified)

data-align='justify' justifies the lines and hyphenates long words by the rules of the block's language (lang='en'); the last line of each paragraph stays flush to the start. Headings are never justified: they stay flush start, unhyphenated.

§Einzug (indent)

data-indent: paragraphs follow without a gap and indent their first line by --typeset-indent (1em). The first paragraph after a heading starts flush (stumpf) - the heading has already opened it.

§Initiale: drop

data-initial='drop' sinks the first letter three lines deep (initial-letter); its top is level with the top of the first line, and --typeset-initial-gap keeps it clear of the text to its right.

§Initiale: raised

data-initial='raised' stands the letter on the first baseline and lets it rise above the line - here two lines tall (--typeset-initial-lines) in chart colour 1 (--typeset-initial-color).

§Opening line: small capitals or capitals

data-lead='smallcaps' sets the first line in small capitals (Kapitälchen); data-lead='caps' in letterspaced capitals (Versalien). Either opens a chapter without an initial.

§Classic ligatures and old-style figures

data-ligatures='classic' turns on discretionary and historical ligatures (ct, st) and old-style figures (1450, 1469) - where the font carries the features; a font without them sets the plain forms.

§Leading: tight, normal, loose

data-leading moves the line height and the tracking together: tight (1.45, -0.01em) for dense columns, the default (1.65), loose (1.9, +0.01em) for airy reading. Tracking that follows the leading keeps the grey of the column even.

§Separators

A pause in the text is the Separator component - a short rule, a glyph between lines, or a bare glyph (data-line='none'): asterism, dinkus, fleuron. aria-hidden marks the ones that only decorate. Inside .typeset each keeps --typeset-gap from the text, like a figure.

§Numbered captions: picture, table, video, chart

.typeset-numbered numbers every .typeset-caption in order - figures (pictures, videos, charts) and tables on their own counters. Captions are flush start (picture), centred (table, data-align='center') or justified (video, chart, data-align='justify'). Every figure keeps --typeset-gap from the text.

§Figure groups: two charts; a chart, a picture and a table; cards

figure.typeset-group sets two or three figures side by side with lettered sub-captions and one numbered caption; subgrid rows keep the captions of a row level. First: two charts, divided by a hairline, media centred (data-divided, data-align='middle'). Second: a chart, a picture and a table in one box. Third: data-variant='cards' - each figure its own card. Narrow the preview: they stack, and the hairlines run between the rows instead.

§Combined

The rules compose. A book page puts them all together; the same page without the initial is the quieter setting the technical report on the Modern Paper page uses.

§Book page

All rules at once: two ruled columns, Blocksatz, a drop initial, the opening line in capitals (beside an initial the small-caps lead sets capitals, so the line's top meets the initial's), Einzug, classic ligatures. Narrow the preview: below two columns of 18rem the block sets one.

§Book page without an initial

The same rules without data-initial: Blocksatz, Einzug, the opening line in small capitals, classic ligatures - a quieter opening for a report or a paper, where a large letter would compete with the title. The Modern Paper's technical report sets its sections this way.

§CSS view file

/* -- Typography component --------------------------------------- */
/* -- Base text ---------------------------------------------- */
/* Sets the default font size for the entire page.            */
/* All typography classes are sized relative to this baseline. */
@layer base {
  body {
    font-size: 0.875rem;
    line-height: 1.5;
  }
}
@layer components {
  /* -- Headings ---------------------------------------------- */
  .h1 {
    scroll-margin-top: 5rem;
    font-size: 2.25rem;
    font-weight: 800;
    letter-spacing: -0.025em;
    line-height: 1.15;
    text-wrap: balance;
    color: var(--foreground);
  }
  .h2 {
    scroll-margin-top: 5rem;
    font-size: 1.875rem;
    font-weight: 600;
    letter-spacing: -0.02em;
    line-height: 1.25;
    text-wrap: balance;
    color: var(--foreground);
    border-block-end: 1px solid var(--border);
    padding-block-end: 0.5rem;
    &:first-child { margin-block-start: 0; }
  }
  .h3 {
    scroll-margin-top: 5rem;
    font-size: 1.5rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.3;
    text-wrap: balance;
    color: var(--foreground);
  }
  .h4 {
    scroll-margin-top: 5rem;
    font-size: 1.25rem;
    font-weight: 600;
    line-height: 1.4;
    text-wrap: balance;
    color: var(--foreground);
  }
  /* -- Paragraph --------------------------------------------- */
  .p {
    font-size: 0.875rem;
    line-height: 1.75;
    text-wrap: pretty;
    color: var(--foreground);
    &:not(:first-child) { margin-block-start: 1.5rem; }
  }
  /* -- Lead --------------------------------------------------- */
  .lead {
    font-size: 1.25rem;
    line-height: 1.6;
    color: var(--muted-foreground);
    text-wrap: pretty;
  }
  /* -- Large -------------------------------------------------- */
  .large {
    font-size: 1.125rem;
    font-weight: 600;
    color: var(--foreground);
  }
  /* -- Small -------------------------------------------------- */
  .small {
    font-size: 0.875rem;
    font-weight: 500;
    line-height: 1;
    color: var(--foreground);
  }
  /* -- Muted -------------------------------------------------- */
  .muted {
    font-size: 0.875rem;
    color: var(--muted-foreground);
  }
  /* -- Blockquote --------------------------------------------- */
  .blockquote {
    margin-block-start: 1.5rem;
    border-inline-start: 2px solid var(--border);
    padding-inline-start: 1.5rem;
    font-style: italic;
    hanging-punctuation: first last;
    color: var(--foreground);
    & p { margin: 0; }
  }
  /* -- Inline code -------------------------------------------- */
  .inline-code {
    position: relative;
    border-radius: var(--radius-sm);
    background: var(--muted);
    padding: 0.2rem 0.3rem;
    font-family: var(--font-mono);
    font-size: 0.875em;
    font-weight: 600;
    color: var(--foreground);
  }
  /* -- CJK: Chinese, Japanese, Korean ------------------------------
     Keyed on the lang attribute, so one set of classes serves every script.
     Latin tuning is undone (negative tracking, faux italics); paragraphs
     justify between characters with strict line breaking (kinsoku: no 。
     or , at a line start), and sentence-end punctuation may hang past the
     edge (Safari: hanging-punctuation) or trims to half width
     (Chromium: text-spacing-trim) so the justified edge stays straight.
     text-autospace adds the thin gap between CJK and Latin /
     digits that authors used to type by hand. */
  :is(.h1, .h2, .h3, .h4, .lead, .large):is(:lang(zh), :lang(ja), :lang(ko)),
  :is(:lang(zh), :lang(ja), :lang(ko)) :is(.h1, .h2, .h3, .h4, .lead, .large) {
    letter-spacing: 0;
  }
  /* Japanese headings break between phrases (bunsetsu), not mid-word */
  :is(.h1, .h2, .h3, .h4, .lead):lang(ja),
  :lang(ja) :is(.h1, .h2, .h3, .h4, .lead) {
    word-break: auto-phrase;
  }
  :is(.p, .lead, .blockquote):is(:lang(zh), :lang(ja), :lang(ko)),
  :is(:lang(zh), :lang(ja), :lang(ko)) :is(.p, .lead, .blockquote) {
    line-height: 1.9;
    line-break: strict;
    text-autospace: normal;
  }
  :is(.p, .blockquote):is(:lang(zh), :lang(ja)),
  :is(:lang(zh), :lang(ja)) :is(.p, .blockquote) {
    text-align: justify;
    text-justify: inter-character;
    /* the line-end 。,: Safari hangs it past the edge; Chromium trims the
       fullwidth glyph's blank half (text-spacing-trim) - either way the
       justified edge stays straight */
    hanging-punctuation: allow-end;
    text-spacing-trim: normal;
  }
  /* CJK has no italics - a slanted Han character is a synthetic oblique */
  .blockquote:is(:lang(zh), :lang(ja), :lang(ko)),
  :is(:lang(zh), :lang(ja), :lang(ko)) .blockquote {
    font-style: normal;
  }
  /* -- Vertical text (tategaki / 直排) ----------------------------------
     .text-vertical sets top-to-bottom lines flowing right to left. Latin
     runs lie sideways (text-orientation: mixed); .text-upright stands a
     short run (a two-digit number) upright in one cell - tate-chu-yoko.
     The spacing above uses logical properties, so headings, paragraphs and
     the blockquote rule turn with the text. Give the block a height. */
  .text-vertical {
    writing-mode: vertical-rl;
    text-orientation: mixed;
  }
  .text-upright {
    text-combine-upright: all;
  }
  /* -- Emphasis marks (圈点 / 傍点) ---------------------------------------
     Dots beside each character instead of italics: over the text in
     Japanese (right of it when vertical), under it in Chinese. */
  .text-emphasis {
    font-style: normal;
    text-emphasis: filled dot;
    text-emphasis-position: over right;
  }
  .text-emphasis:lang(ja) { text-emphasis-style: filled sesame; }
  .text-emphasis:lang(zh) { text-emphasis-position: under right; }
  /* ruby annotations (furigana, pinyin, zhuyin) read as small, muted type */
  :is(.p, .lead, .blockquote, .h1, .h2, .h3, .h4) rt {
    font-size: 0.5em;
    color: var(--muted-foreground);
    letter-spacing: 0;
  }
  /* -- Typesetting (Satz): book and journal setting ----------------------
     .typeset holds continuous text set like a book or a journal page. Each
     classic rule is an opt-in data attribute, tuned by a --typeset-* knob -
     component properties, not theme tokens (the theme set stays the
     tweakcn shape):
       data-columns="2|3"        columns never narrower than
                                 --typeset-column-width: a phone gets one
       data-rule                 a hairline between the columns
       data-align="justify"      Blocksatz: justified and hyphenated (set
                                 lang=""), the last line flush start
       data-indent               Einzug: paragraphs follow without a gap and
                                 indent their first line; the first one after
                                 a heading starts flush (stumpf)
       data-initial="drop|raised"  Initiale over --typeset-initial-lines lines
       data-lead="smallcaps|caps"  the opening line in small capitals or in
                                 letterspaced capitals (Versalien)
       data-ligatures="classic"  discretionary and historical ligatures,
                                 old-style figures - where the font has them
       data-leading="tight|loose"  compact or airy lines, the tracking matched
     .typeset-span spans every column (a heading, a figure). Figures, tables,
     quotes, groups and Separator breaks keep --typeset-gap from the text -
     the Separator component draws the break, this block only spaces it.
     VERIFIED: (typography.e2e, the typeset checks) every rule as a computed
     style, the gaps, the captions' counters, 44rem two columns vs 20rem one. */
  .typeset {
    --typeset-measure: 68ch;
    --typeset-column-width: 18rem;
    --typeset-column-gap: 2.5em;
    --typeset-indent: 1em;
    --typeset-leading: 1.65;
    --typeset-tracking: 0em;
    --typeset-gap: 1.5em;
    --typeset-initial-lines: 3;
    --typeset-initial-gap: 0.3em;
    --typeset-initial-font: var(--font-serif);
    --typeset-initial-color: var(--primary);
    max-inline-size: var(--typeset-measure);
    line-height: var(--typeset-leading);
    letter-spacing: var(--typeset-tracking);
    text-wrap: pretty;
    hanging-punctuation: first allow-end last;
    font-kerning: normal;
    orphans: 2;
    widows: 2;
    & > p { margin-block: 0 1em; }
    /* every block keeps the same distance from the text around it */
    & > :is(figure, table, blockquote, pre, hr, .separator, .typeset-group, .chart, .diagram, .mockup-code, .table-container) {
      margin-block: var(--typeset-gap);
    }
    /* a figure spans the measure (the browser indents it 40px by default) */
    & > :is(figure, blockquote) { margin-inline: 0; }
    & > :is(h1, h2, h3, h4, .h1, .h2, .h3, .h4) { margin-block: var(--typeset-gap) 0.5em; }
    & > :first-child { margin-block-start: 0; }
    & :is(figure, blockquote, table, pre, .typeset-group) { break-inside: avoid; }
    & :is(h1, h2, h3, h4, .h1, .h2, .h3, .h4) { break-after: avoid; }
    & .typeset-span { column-span: all; }
    /* the columns below a spanning heading or figure start level: the first
       block keeps no top margin - across the column break it cannot collapse
       into the spanner's, so a subheading sat lower than the text beside it */
    &[data-columns] > .typeset-span + :not(.typeset-span) { margin-block-start: 0; }
    &[data-columns] { max-inline-size: none; column-gap: var(--typeset-column-gap); }
    &[data-columns="2"] { columns: 2 var(--typeset-column-width); }
    &[data-columns="3"] { columns: 3 var(--typeset-column-width); }
    &[data-rule] { column-rule: 1px solid var(--border); }
    &[data-align="justify"] {
      text-align: justify;
      text-align-last: start;
      hyphens: auto;
      hyphenate-limit-chars: 6 3 2;
      /* text-align-last inherits: a centered caption inside a justified setting
         kept its last line flush left (a two-line figure caption looked
         off-center) - what centers itself takes its last line from its own alignment */
      & :where(figcaption, caption, .typeset-caption) { text-align-last: auto; }
    }
    /* leading and tracking move together: tight lines close up a little,
       airy lines open up a little, so the grey of the column stays even */
    &[data-leading="tight"] { --typeset-leading: 1.45; --typeset-tracking: -0.01em; }
    &[data-leading="loose"] { --typeset-leading: 1.9; --typeset-tracking: 0.01em; }
    &[data-indent] > p { margin-block: 0; }
    &[data-indent] > p + p { text-indent: var(--typeset-indent); }
    /* text-indent inherits into an inline-block, which then indents ITS first line: a badge,
       a key cap or an epistemic tag at the start of an indented paragraph grew a blank
       inside its left edge */
    &[data-indent] > p :where(.badge, kbd, .paper-tag) { text-indent: 0; }
    /* the gap is a margin: Chromium ignores logical padding on ::first-letter
       (VERIFIED: probe 2026-10-07 - padding-inline-end computed 0, margin-inline-end 2.52px) */
    &[data-initial] > p:first-of-type::first-letter {
      margin-inline-end: var(--typeset-initial-gap);
      font-family: var(--typeset-initial-font);
      font-weight: 600;
      color: var(--typeset-initial-color);
    }
    &[data-initial="drop"] > p:first-of-type::first-letter {
      -webkit-initial-letter: var(--typeset-initial-lines);
      initial-letter: var(--typeset-initial-lines);
    }
    &[data-initial="raised"] > p:first-of-type::first-letter {
      -webkit-initial-letter: var(--typeset-initial-lines) 1;
      initial-letter: var(--typeset-initial-lines) 1;
    }
    &[data-lead="smallcaps"] > p:first-of-type::first-line {
      font-variant-caps: all-small-caps;
      letter-spacing: 0.04em;
    }
    /* letterspaced capitals - and the small-caps lead beside an initial:
       a first line in small capitals moves the initial down a line
       (Chromium), capitals keep its top level with the line's */
    &:is([data-lead="caps"], [data-initial][data-lead="smallcaps"]) > p:first-of-type::first-line {
      font-variant-caps: normal;
      text-transform: uppercase;
      letter-spacing: 0.06em;
    }
    &[data-ligatures="classic"] {
      font-variant-ligatures: common-ligatures discretionary-ligatures historical-ligatures contextual;
      font-variant-numeric: oldstyle-nums proportional-nums;
    }
  }
  /* a heading is never justified: a wrapping heading stretched to the measure
     opened wide gaps between its words. Flush start and unhyphenated - zero
     specificity, so an alignment the heading sets itself still wins */
  :where(.typeset[data-align="justify"]) :where(h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4) {
    text-align: start;
    text-align-last: auto;
    hyphens: manual;
  }
  /* no initial-letter (Firefox): a floated letter as tall as the lines */
  @supports not ((initial-letter: 3) or (-webkit-initial-letter: 3)) {
    .typeset[data-initial] > p:first-of-type::first-letter {
      float: inline-start;
      font-size: calc(var(--typeset-initial-lines) * var(--typeset-leading) * 1em * 0.82);
      line-height: 0.8;
      padding-block-start: 0.08em;
    }
  }
  /* -- Captions (Bildunterschriften), numbered in order ------------------
     .typeset-caption on a figcaption (a picture, a video, a chart) or a
     table's caption. Inside .typeset-numbered, figures and tables count on
     their own: "Figure 1.", "Table 1." - the words are --typeset-figure-label
     and --typeset-table-label ("" leaves the bare number: "1."). A caption
     is flush start; data-align="justify" or "center" sets it otherwise. */
  .typeset-numbered {
    --typeset-figure-label: "Figure ";
    --typeset-table-label: "Table ";
    counter-reset: typeset-figure typeset-table;
  }
  .typeset-caption {
    margin-block-start: 0.6em;
    color: var(--muted-foreground);
    font-size: 0.875em;
    line-height: 1.5;
    text-align: start;
    text-align-last: auto;
    text-wrap: pretty;
    caption-side: top;
    &[data-align="justify"] { text-align: justify; text-align-last: start; hyphens: auto; }
    &[data-align="center"] { text-align: center; text-wrap: balance; }
    table > & { margin-block: 0 0.6em; }
  }
  .typeset-numbered :is(figcaption.typeset-caption)::before {
    counter-increment: typeset-figure;
    content: var(--typeset-figure-label) counter(typeset-figure) ". ";
    color: var(--foreground);
    font-weight: 600;
  }
  .typeset-numbered :is(caption.typeset-caption, figure:has(> table) > figcaption.typeset-caption, figure:has(> .table-container) > figcaption.typeset-caption)::before {
    counter-increment: typeset-table;
    content: var(--typeset-table-label) counter(typeset-table) ". ";
  }
  /* -- Figure groups: several figures in one box ---------------------------
     .typeset-group sets two or three figures (charts, pictures, tables) side
     by side; each figure's caption is lettered "(a)", "(b)" (.typeset-subcaption),
     the group's own figcaption numbers the whole. Every figure spans two
     subgrid rows - media, then caption - so the captions of a row sit at one
     height whatever the media's height. data-align="middle" centres the media
     in their row; data-divided draws hairlines between the figures - the
     1px grid gap over a border-coloured background, so the lines run between
     columns and, once the figures stack, between rows, with no container
     query (a size container would scope the caption counters to the group);
     data-variant="cards" gives each figure its own card instead of one box.
     Narrow pages stack them.
     VERIFIED: (typography.e2e "figure groups") captions level, the 1px lines
     between columns and between stacked rows, the cards' borders. */
  .typeset-group {
    --typeset-group-min: 12rem;
    --typeset-group-gap: 1.25rem;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--typeset-group-min)), 1fr));
    gap: var(--typeset-group-gap);
    margin-inline: 0;
    padding: 1rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--card);
    counter-reset: typeset-sub;
    & > figure {
      display: grid;
      grid-row: span 2;
      grid-template-rows: subgrid;
      row-gap: 0.4em;
      align-content: start;
      margin: 0;
      min-inline-size: 0;
    }
    & > figcaption { grid-column: 1 / -1; margin-block-start: 0.25em; }
    & img, & video { display: block; inline-size: 100%; block-size: auto; border-radius: var(--radius-md); }
    &[data-align="middle"] > figure > :first-child { align-self: center; }
    &[data-variant="cards"] {
      padding: 0;
      border: 0;
      background: none;
      & > figure {
        padding: 0.875rem;
        border: 1px solid var(--border);
        border-radius: var(--radius-lg);
        background: var(--card);
      }
    }
    &[data-divided] {
      gap: 1px;
      padding: 0;
      overflow: hidden;
      background: var(--border);
      & > figure { padding: 1rem; background: var(--card); }
      & > figcaption { margin: 0; padding: 0.75rem 1rem 1rem; background: var(--card); }
    }
  }
  .typeset-subcaption {
    margin: 0;
    color: var(--muted-foreground);
    font-size: 0.8125em;
    line-height: 1.45;
    &::before {
      counter-increment: typeset-sub;
      content: "(" counter(typeset-sub, lower-alpha) ") ";
      color: var(--foreground);
      font-weight: 600;
    }
  }
  /* -- Accessibility ------------------------------------------ */
  @media (prefers-contrast: more) {
    .h1, .h2, .h3, .h4 {
      letter-spacing: 0;
    }
    .typeset {
      --typeset-initial-color: var(--foreground);
    }
    .blockquote {
      border-inline-start-width: 3px;
    }
    .inline-code {
      outline: 1px solid var(--border);
    }
    .muted {
      color: var(--foreground);
    }
  }
  @media (forced-colors: active) {
    .blockquote {
      border-inline-start-color: CanvasText;
    }
    .typeset {
      --typeset-initial-color: CanvasText;
    }
    .typeset[data-rule] {
      column-rule-color: CanvasText;
    }
    .inline-code {
      background: Canvas;
      outline: 1px solid CanvasText;
      color: CanvasText;
    }
  }
}

Comments, ideas or improvements? Edit this page's source on GitHub