Heading
Titles for a page and for the sections inside it, with the HTML level set apart from the size.
Webhooks
Your endpoints receive an event each time an invoice in northwind changes.
Endpoints
Three endpoints receive events in live mode.
import { Heading, Subheading } from '@/components/heading'
import { Text } from '@/components/text'
export default function Example() {
return (
<div>
<Heading>Webhooks</Heading>
<Text>Your endpoints receive an event each time an invoice in northwind changes.</Text>
<Subheading className="mt-8">Endpoints</Subheading>
<Text>Three endpoints receive events in live mode.</Text>
</div>
)
}<script setup lang="ts">
import Heading from '@/components/heading.vue'
import Subheading from '@/components/subheading.vue'
import Text from '@/components/text.vue'
</script>
<template>
<div>
<Heading>Webhooks</Heading>
<Text>Your endpoints receive an event each time an invoice in northwind changes.</Text>
<Subheading class="mt-8">Endpoints</Subheading>
<Text>Three endpoints receive events in live mode.</Text>
</div>
</template>Installation
Copy these files into the folder where you keep your components. They import each other with relative paths, so the folder can live anywhere. The packages they rely on are listed on the Installation page.
import clsx from 'clsx'
import type React from 'react'
type Level = 1 | 2 | 3 | 4 | 5 | 6
const headingText = 'font-semibold text-zinc-950 dark:text-white'
type HeadingProps = { level?: Level } & React.ComponentPropsWithoutRef<`h${Level}`>
export function Heading({ className, level = 1, ...props }: HeadingProps) {
let Tag = `h${level}` as const
return <Tag {...props} className={clsx(className, headingText, 'text-2xl/8 sm:text-xl/8')} />
}
export function Subheading({ className, level = 2, ...props }: HeadingProps) {
let Tag = `h${level}` as const
return <Tag {...props} className={clsx(className, headingText, 'text-base/7 sm:text-sm/6')} />
}<script setup lang="ts">
withDefaults(defineProps<{ level?: 1 | 2 | 3 | 4 | 5 | 6 }>(), { level: 1 })
</script>
<template>
<component :is="`h${level}`" class="font-semibold text-2xl/8 sm:text-xl/8 text-zinc-950 dark:text-white">
<slot />
</component>
</template>Component API
| Prop | Default | Description |
|---|---|---|
Headingrenders a <h1> element | ||
level | 1 | Rendered tag, from h1 to h6. The size does not change. |
Subheadingrenders a <h2> element | ||
level | 2 | Rendered tag, from h1 to h6. The size does not change. |
Usage
Heading names the screen, once per page. Subheading names each block inside it: a card, a form section, a table. Both are a step smaller from the sm breakpoint up, which keeps dense desktop screens calm while titles stay large enough on a phone.
Page title with a primary action
Put the one action the page exists for on the same line as its title, and let the row wrap on narrow screens:
Customers
import { Button } from '@/components/button'
import { Heading } from '@/components/heading'
export default function Example() {
return (
<div className="flex flex-wrap items-center justify-between gap-4">
<Heading>Customers</Heading>
<Button>Add customer</Button>
</div>
)
}<script setup lang="ts">
import Button from '@/components/button.vue'
import Heading from '@/components/heading.vue'
</script>
<template>
<div class="flex flex-wrap items-center justify-between gap-4">
<Heading>Customers</Heading>
<Button>Add customer</Button>
</div>
</template>Title a section
A Subheading followed by a line of Text introduces a block without competing with the page title:
Payment methods
Cards and bank accounts charged when an invoice is due.
import { Subheading } from '@/components/heading'
import { Text } from '@/components/text'
export default function Example() {
return (
<section>
<Subheading>Payment methods</Subheading>
<Text>Cards and bank accounts charged when an invoice is due.</Text>
</section>
)
}<script setup lang="ts">
import Subheading from '@/components/subheading.vue'
import Text from '@/components/text.vue'
</script>
<template>
<section>
<Subheading>Payment methods</Subheading>
<Text>Cards and bank accounts charged when an invoice is due.</Text>
</section>
</template>Accessibility
Screen reader users move through a page by its headings, so the levels must follow the structure of the page, even when the visual sizes do not.
Nest a title inside a section
A card inside a section needs an h3, though it looks like any other subheading. level={3} changes the tag and keeps the size:
A card inside a section needs an h3, though it looks like any other subheading. :level="3" changes the tag and keeps the size:
Usage this month
API requests
1.2 million of 2 million included in the Scale plan.
<Subheading>Usage this month</Subheading>
<div className="mt-4 rounded-lg border border-zinc-950/10 p-4 dark:border-white/10">
<Subheading level={3}>API requests</Subheading>
<Text>1.2 million of 2 million included in the Scale plan.</Text>
</div><Subheading>Usage this month</Subheading>
<div class="mt-4 rounded-lg border border-zinc-950/10 p-4 dark:border-white/10">
<Subheading :level="3">API requests</Subheading>
<Text>1.2 million of 2 million included in the Scale plan.</Text>
</div>