Skip to content

Typography

Four components cover the text in an admin panel: WxHeading for titles, WxText for everything you write in a template, WxLink for links, and WxProse for HTML that arrives from the editor or the CMS.

Headings

Page title

Section

Subsection

A level 2 that has to look small

Text

Body text, the default size and weight.

A hint under a field, muted and small.

Something went wrong.

ORD-2026-000431

Clamped to two lines: it is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout, and this sentence keeps going until it proves the point.

Prose — HTML from the editor

Publishing a page

The editor stores HTML, and WxProse is what gives that HTML the typography of the design system — headings, lists, tables and images included.

  • Every colour and size comes from a token.
  • A pasted table scrolls inside its own box.
  • Images never break out of the column.
A quote keeps its rule on the left and its muted colour.
Sections and their editors
SectionEditorUpdated
NewsMaria12.03.2026
TeamAlexey28.02.2026

Heading

vue
<template>
  <wx-heading :level="1">Page title</wx-heading>
  <wx-heading :level="2" size="md" tone="muted">A section that has to look small</wx-heading>
</template>

level decides the tag — keep it truthful to the document outline — and size decides how it looks, so an h2 can read as a small label without breaking the outline for a screen reader.

Headings carry no margin: spacing belongs to the layout around them, not to the heading itself.

PropTypeDefaultDescription
level1 | 2 | 3 | 4 | 5 | 62Which <h*> is rendered
size'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl'by levelVisual size
toneTextTone'strong'Colour role
align'start' | 'center' | 'end' | 'justify'Text alignment
truncatebooleanfalseOne line with an ellipsis

Text

vue
<template>
  <wx-text as="p">Body text.</wx-text>
  <wx-text as="p" size="sm" tone="muted">A hint under a field.</wx-text>
  <wx-text mono>ORD-2026-000431</wx-text>
  <wx-text :truncate="2">Two lines, then an ellipsis…</wx-text>
</template>

truncate takes true for one line or a number of lines to clamp to. Either way the element needs a width to cut against — a grid cell, a card, a max-width.

PropTypeDefaultDescription
asstring'span'Element to render
size'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Text size
weight'regular' | 'medium' | 'semibold' | 'bold''regular'Font weight
toneTextTone'default'Colour role
align'start' | 'center' | 'end' | 'justify'Text alignment
truncateboolean | numberfalseOne line, or that many lines
italicbooleanfalseItalic
monobooleanfalseMonospace with tabular figures

TextTone is 'default' | 'strong' | 'muted' | 'placeholder' | 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'inverse'.

vue
<template>
  <wx-link href="/admin/pages">Pages</wx-link>
  <wx-link href="https://vuejs.org" external>Vue docs</wx-link>
  <wx-link :as="RouterLink" to="/admin/pages">Pages</wx-link>
</template>

external opens the link in a new tab with rel="noopener noreferrer" and appends a small arrow. as renders through another component — RouterLink, say, with to passed straight through.

A disabled link drops its href and swallows clicks, so it cannot be followed by keyboard either.

PropTypeDefaultDescription
hrefstringTarget
type'default' | 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'muted''default'Colour role
underline'hover' | 'always' | 'never''hover'When the underline shows
size'xs' | 'sm' | 'md' | 'lg' | 'xl'inheritedText size
weight'regular' | 'medium' | 'semibold' | 'bold'inheritedFont weight
disabledbooleanfalseBlocks the link
externalbooleanfalseNew tab, safe rel, arrow icon
asstring | Component'a'Render through another component

Events: click (MouseEvent). Slots: default, icon.

Prose

WxProse styles a block of HTML you did not write in the template — the output of RichText, a CMS field, a rendered Markdown file. Headings, paragraphs, lists, quotes, code, images, tables: all from the tokens, in both themes.

vue
<template>
  <wx-prose :html="page.body" />
</template>

Or with content from the template:

vue
<template>
  <wx-prose>
    <h2>Publishing a page</h2>
    <p>Everything inside gets the same typography.</p>
  </wx-prose>
</template>

WARNING

html is injected as-is. Sanitise it on the server — Laravel's clean(), HTMLPurifier, whatever your stack uses — before it reaches the component.

A pasted table scrolls inside its own box rather than pushing the page sideways, and images are capped at the column width.

PropTypeDefaultDescription
htmlstringMarkup to render; omit to use the slot
size'sm' | 'md' | 'lg''md'Text scale of the whole block
asstring'div'Element to render

The rhythm between blocks is one variable, so a caller can tighten it:

vue
<template>
  <wx-prose :html="body" style="--wx-prose-flow: 10px" />
</template>

Released under the MIT License.