Un élément de texte qui porte l'un des rôles typographiques. Chaque rôle est une recette complète de tokens, si bien qu'un titre, un libellé ou une légende se nomment au lieu de se décrire à la main.
Utilisation
vue
A design system is a contract, not a folder of components.
<script setup lang="ts">
import { VTypography } from 'vectis-ui'
</script>
<template>
<VTypography>A design system is a contract, not a folder of components.</VTypography>
</template>
Exemples
Variantes
variant propose quatorze rôles, chacun étant une recette complète plutôt qu'une taille : la graisse, la hauteur de ligne et, là où le rôle le demande, l'interlettrage et une famille à chasse fixe viennent avec.
vue
display
Pack my box with five dozen jugs.
heading-1
Pack my box with five dozen jugs.
heading-2
Pack my box with five dozen jugs.
heading-3
Pack my box with five dozen jugs.
heading-4
Pack my box with five dozen jugs.
subtitle
Pack my box with five dozen jugs.
body-xl
Pack my box with five dozen jugs.
body-lg
Pack my box with five dozen jugs.
body-md
Pack my box with five dozen jugs.
body-sm
Pack my box with five dozen jugs.
label
Pack my box with five dozen jugs.
caption
Pack my box with five dozen jugs.
overline
Pack my box with five dozen jugs.
code
Pack my box with five dozen jugs.
<script setup lang="ts">
import { VTypography, type TypographyVariant } from 'vectis-ui'
const variants: TypographyVariant[] = [
'display',
'heading-1',
'heading-2',
'heading-3',
'heading-4',
'subtitle',
'body-xl',
'body-lg',
'body-md',
'body-sm',
'label',
'caption',
'overline',
'code',
]
</script>
<template>
<!-- Each role is a complete recipe: size, weight, line height, and where the role
calls for it the letter spacing and a monospaced family. Nothing here is set one
property at a time. -->
<div class="table">
<template v-for="variant in variants" :key="variant">
<VTypography variant="code" as="span" tone="muted">{{ variant }}</VTypography>
<VTypography :variant="variant" as="p">Pack my box with five dozen jugs.</VTypography>
</template>
</div>
</template>
<style scoped>
.table {
display: grid;
grid-template-columns: max-content 1fr;
align-items: baseline;
gap: var(--vectis-space-3) var(--vectis-space-5);
}
</style>
Tonalités
tone est la couleur du texte, dite comme un sens. default ne pose aucune couleur, le texte hérite donc de ce qui l'entoure, et on-inverse nomme au contraire son fond, pour un texte auquel la couleur héritée ne survivrait pas.
vue
default: pack my box with five dozen jugs.
muted: pack my box with five dozen jugs.
subtle: pack my box with five dozen jugs.
accent: pack my box with five dozen jugs.
danger: pack my box with five dozen jugs.
success: pack my box with five dozen jugs.
warning: pack my box with five dozen jugs.
on-inverse: pack my box with five dozen jugs.
<script setup lang="ts">
import { VTypography, type TypographyTone } from 'vectis-ui'
// `on-inverse` is left out of the loop: it only means anything against a dark ground,
// so it is shown on one below.
const tones: TypographyTone[] = [
'default',
'muted',
'subtle',
'accent',
'danger',
'success',
'warning',
]
</script>
<template>
<div class="demo">
<!-- `default` sets no colour at all, so the text takes whatever surrounds it. That
is what lets the same component sit inside a coloured toast or on an inverted
surface without being told which one it is on. -->
<VTypography v-for="tone in tones" :key="tone" :tone="tone">
{{ tone }}: pack my box with five dozen jugs.
</VTypography>
<!-- The one tone that names its ground rather than its meaning, for text the
inherited colour would not survive. -->
<div class="inverse">
<VTypography tone="on-inverse">on-inverse: pack my box with five dozen jugs.</VTypography>
</div>
</div>
</template>
<style scoped>
.demo {
display: grid;
justify-items: start;
gap: var(--vectis-space-2);
}
.inverse {
margin-block-start: var(--vectis-space-2);
padding: var(--vectis-space-2) var(--vectis-space-3);
border-radius: var(--vectis-radius-surface);
background: var(--vectis-color-surface-inverse);
}
</style>
La balise rendue
Chaque variante rend déjà une balise sensée, h1 à h4 pour les titres, code pour le code et p ou span pour le reste. as sert aux cas où le sens et l'apparence divergent, et tout le reste traverse jusqu'à cette balise.
vue
Rendered as an h4, which is its default
Reads as an h4, is really an h2
<script setup lang="ts">
import { VInput, VTypography } from 'vectis-ui'
</script>
<template>
<div class="demo">
<!-- Each variant already renders a sensible tag: h1 to h4 for the headings, p for
the body roles, span for the small ones, code for code. -->
<VTypography variant="heading-4">Rendered as an h4, which is its default</VTypography>
<!-- `as` is for the cases where the meaning and the look part ways: a section that
is an h2 in the document but should read at the size of an h4. -->
<VTypography variant="heading-4" as="h2">Reads as an h4, is really an h2</VTypography>
<!-- With a single root and no attributes of its own, everything falls through. A
label variant rendered as a real label, pointing at a real field, is a working
pair rather than a resemblance. -->
<VTypography variant="label" as="label" for="reference">Reference</VTypography>
<VInput id="reference" model-value="INV-2481" />
</div>
</template>
<style scoped>
.demo {
display: grid;
justify-items: start;
gap: var(--vectis-space-3);
max-inline-size: 22rem;
}
</style>
Tronquer sur une ligne
truncate tient le texte sur une ligne, terminée par des points de suspension. L'élément a besoin d'une largeur sur laquelle être coupé, et sans rien à déborder le texte reste simplement entier.
vue
A title far too long to sit on one line of a narrow column without being cut
A title far too long to sit on one line of a narrow column without being cut
<script setup lang="ts">
import { VTypography } from 'vectis-ui'
const LONG = 'A title far too long to sit on one line of a narrow column without being cut'
</script>
<template>
<div class="demo">
<!-- One line, ended with an ellipsis. The element needs a width to be cut against:
as a block or a flex item it takes its parent's, which is what the box below
gives it. -->
<div class="box">
<VTypography truncate>{{ LONG }}</VTypography>
</div>
<!-- With nothing to overflow, there is nothing to cut and the text simply stays
whole: the same element, left to size itself. -->
<VTypography truncate>{{ LONG }}</VTypography>
</div>
</template>
<style scoped>
.demo {
display: grid;
justify-items: start;
gap: var(--vectis-space-4);
}
.box {
inline-size: 16rem;
padding: var(--vectis-space-3);
border: 1px dashed var(--vectis-color-border);
border-radius: var(--vectis-radius-surface);
}
</style>
Un bloc de texte
Le composant ne porte aucune marge : l'espace entre deux morceaux de texte appartient à la mise en page qui les contient, en général une grille ou une colonne flex avec un écart.
vue
Release notes
What changed in this version
A short standfirst, set one step above the body so the eye knows where to start.
The body role carries the line height a paragraph needs at its size, and long words break rather than push a column out of shape. An inline role sits inside it without disturbing the line: a value such as --vectis-text-body-md-size keeps its own family and size while the leading stays the paragraph's.
Published on 12 September 2026
<script setup lang="ts">
import { VTypography } from 'vectis-ui'
</script>
<template>
<!-- Several roles composed into one block. The component carries no margin of its
own, deliberately: the space between two pieces of text belongs to the layout
holding them, so stacking these without a gap here would leave them touching. -->
<article class="article">
<VTypography variant="overline" tone="muted">Release notes</VTypography>
<VTypography variant="heading-3" as="h2">What changed in this version</VTypography>
<VTypography variant="body-lg" tone="muted">
A short standfirst, set one step above the body so the eye knows where to start.
</VTypography>
<VTypography variant="body-md">
The body role carries the line height a paragraph needs at its size, and long words break
rather than push a column out of shape. An inline role sits inside it without disturbing the
line: a value such as
<VTypography variant="code" as="span">--vectis-text-body-md-size</VTypography>
keeps its own family and size while the leading stays the paragraph's.
</VTypography>
<VTypography variant="caption" tone="subtle">Published on 12 September 2026</VTypography>
</article>
</template>
<style scoped>
.article {
display: grid;
justify-items: start;
gap: var(--vectis-space-3);
max-inline-size: 34rem;
}
</style>
Le rôle que joue le texte, qui sélectionne une recette complète de tokens typographiques : taille, graisse, interligne et, quand le rôle le demande, interlettrage et famille à chasse fixe.
as
string
aucune
La balise HTML à rendre. Chaque variante a déjà une valeur par défaut sensée (h1 à h4, p, span, code) : cette prop sert aux cas où le sens et l'apparence divergent, un sous-titre qui est en réalité un h2, ou un libellé attaché à un champ.
La couleur du texte. default n'en pose aucune, si bien que le texte hérite de ce qui l'entoure : c'est ce qui permet au même composant de se poser sur une surface inversée ou dans une notification colorée.
truncate
boolean
false
Coupe le texte à une ligne et le termine par des points de suspension. L'élément a besoin d'une largeur contre laquelle être coupé, en bloc ou en item flex ; sinon il n'y a rien à déborder et le texte reste entier.