Patterns
Section Heading
When to use:
- a page or panel needs a visible structural title
Composition notes:
- choose
asfrom the document outline first - choose
sizesecond based on visual rhythm
Trade-offs:
- preserves semantics and visual flexibility
- requires discipline when multiple headings are present
import { Heading, Text } from '@vega-ui/react';
export default function DocumentOutline() {
return (
<article style={{ display: 'grid', gap: 'var(--spacing-12)', width: '100%', maxWidth: '60ch' }}>
<div style={{ display: 'grid', gap: 'var(--spacing-3)' }}>
<Heading as='h1'>Getting started</Heading>
<Text size={3} style={{ color: 'var(--label-secondary)' }}>Set up your workspace in three steps.</Text>
</div>
<div style={{ display: 'grid', gap: 'var(--spacing-3)' }}>
<Heading as='h2'>Invite your team</Heading>
<Text size={3}>Members get access to every public project.</Text>Compact Hierarchy In Dense Panels
When to use:
- a dense settings page needs smaller visual headings without changing semantic level
Composition notes:
- keep the correct
as - reduce
sizeonly if the hierarchy remains clear
Trade-offs:
- saves space
- can weaken visual hierarchy if overused
import { Card, Heading, Text } from '@vega-ui/react';
const keys = [
{ name: 'Production', created: 'Created Jan 12', lastUsed: 'Used 2 minutes ago' },
{ name: 'CI pipeline', created: 'Created Feb 3', lastUsed: 'Used yesterday' },
];
export default function CompactHierarchyInDensePanels() {
return (
<Card asChild size='sm' style={{ display: 'grid', gap: 'var(--spacing-6)', width: '100%' }}>
<section aria-labelledby='api-keys-title'>
<Heading as='h3' size={4} id='api-keys-title'>API keys</Heading>Heading With Supporting Copy
When to use:
- the section title needs a short description
Composition notes:
- keep
Headingas the title only - place supporting text in
Text
Trade-offs:
- clear separation of roles
- requires one extra text element
<hgroup> keeps the description tied to the heading without making it part of the heading text.
import { Heading, Text } from '@vega-ui/react';
import type { CSSProperties } from 'react';
export default function HeadingWithSupportingCopy() {
return (
<hgroup style={{ display: 'grid', gap: 'var(--spacing-2)', width: '100%', margin: 0 }}>
<Heading as='h2' size={6}>Billing contacts</Heading>
<Text asChild size={3} style={{ '--t-color': 'var(--label-secondary)' } as CSSProperties}>
<p>Invoices and payment reminders are sent to these people.</p>
</Text>
</hgroup>
);Comparison
Quick Decision Rule
Use Heading when the text defines document or section structure. Use Text for prose and Label for form fields.
Heading vs Text
- Use
Headingfor navigable section structure. - Use
Textfor body copy.
Heading vs Label
- Use
Headingfor sections or pages. - Use
Labelfor one control.
Heading vs FieldsetLegend
- Use
Headingfor document structure. - Use
FieldsetLegendfor native form group labeling.
Choose This Component When
- the content should appear in the page or section outline
- visual size and semantic level need to stay independent
Do Not Choose This Component When
- the text labels a form field
- the content is just descriptive copy
Troubleshooting
The UI Looks Structured But Screen Readers Do Not See Headings
Symptom
Large styled text appears as section titles visually, but assistive technology does not announce headings.
Likely Cause
Text or a generic element was used instead of Heading.
How To Verify
- inspect the rendered element
- confirm it is not
h1throughh6
Fix
Use Heading and choose the correct as level.
The Heading Hierarchy Feels Broken
Symptom
Users see headings, but the document outline jumps unpredictably.
Likely Cause
Heading levels were chosen by desired font size instead of structure.
How To Verify
- read only the heading levels in order
- confirm they form a logical outline
Fix
Set as from the document hierarchy first, then adjust size for visuals.
Headings And Labels Are Being Mixed
Symptom
Form sections and individual fields both use the same heading primitive, making semantics unclear.
Likely Cause
Heading is being used where Label or FieldsetLegend should be used.
How To Verify
- inspect whether the text names one field or a whole section
Fix
Use Label for single controls and FieldsetLegend for grouped controls.