Layout
UiFooter closes a page. UiHeaderContent renders a page header (title, subtitle, optional back button and actions). UiHeroContent is its page-opening counterpart, with a display-sized title and CTA slot. UiSubheadingContent labels a section inside a page. UiPaneContent is a content container with a row/column layout.
Add a page header
<template>
<UiHeaderContent
title="Projects"
subtitle="Manage your team's work"
back-button
@back="goBack"
>
<template #actions>
<UiButton variant="filled" color="primary" text="New" />
</template>
</UiHeaderContent>
</template>
Wrap content in a pane
<template>
<UiPaneContent is-full-height aria-label="Project list">
<!-- content -->
</UiPaneContent>
</template>
UiHeaderContent props
| Prop | Type | Default | Description |
|---|---|---|---|
as | string | 'h1' | Heading element for the title. Section headers inside a page should step down from the page's own h1. |
headingId | string | generated with useId() | ID for the heading, for external aria-labelledby references. |
headline | string | null | — | Small eyebrow label above the title, rendered with caption text-primary. |
title | string | null | — | Header title. |
subtitle | string | null | — | Header subtitle. |
size | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'sm' | Title size: sm is --cffy-fs-xl, then --cffy-fs-2xl … --cffy-fs-5xl. Each step also takes the line height the type scale pairs with it. |
hideActionsWhenNarrow | boolean | false | Drop the actions from the layout once the header's container is 700px or narrower, instead of wrapping them below the title. |
backButton | boolean | false | Show a back button. |
backButtonLabel | string | 'Go back' | Back button tooltip. |
viewTransitionName | string | null | — | Shared name pairing this header with the matching one on another page for a view transition. |
containerClass | string | string[] | Record<string, boolean> | — | Extra classes. |
Emits: back (back button). Slots: #actions — filling it is what renders the actions area; there is no flag to set.
Header CSS variables
UiHeaderContent (.header-container) reads these. The 2xl to 5xl title sizes keep their own size and line height.
| Variable | Default | Controls |
|---|---|---|
--cffy-header-margin-block-end | --cffy-space-16 | Space below the header |
--cffy-header-gap | --cffy-space-16 | Space between the title and the actions |
--cffy-header-title-font-size / --cffy-header-title-line-height | --cffy-fs-xl / --cffy-fs-xl--line-height | Title |
--cffy-header-description-font-size / --cffy-header-description-color | --cffy-fs-xs / --cffy-muted-a10 | Description |
--cffy-header-description-max-width | 50ch | Description line length |
Open a page with a hero
size uses the same sm–xl vocabulary as UiHeaderContent, mapped onto the
display scale, and align moves the whole block, text and actions together.
<template>
<UiHeroContent
headline="Open source"
title="Build vibrant interfaces"
subtitle="70+ headless components for Vue 3 and Nuxt."
size="lg"
align="center"
>
<template #actions>
<UiButton variant="filled" color="primary" text="Get started" />
<UiButton variant="outline" text="Documentation" />
</template>
</UiHeroContent>
</template>
UiHeroContent props
| Prop | Type | Default | Description |
|---|---|---|---|
headingId | string | generated with useId() | ID for the title; the <section> references it with aria-labelledby. |
headline | string | null | — | Small eyebrow label above the title. |
title | string | null | — | Main title, rendered with the display class. |
subtitle | string | null | — | Supporting description. |
size | 'sm' | 'md' | 'lg' | 'xl' | 'xl' | Title size on the display scale: sm is display-4, xl is display-1. |
align | 'start' | 'center' | 'end' | 'start' | Alignment of the whole hero. |
viewTransitionName | string | null | — | Shared name pairing this hero with the matching element on another page for a view transition. |
customClass | string | string[] | Record<string, boolean> | — | Extra classes. |
Slots: #actions for the call-to-action buttons.
Hero CSS variables
UiHeroContent (.hero-content) reads these.
| Variable | Default | Controls |
|---|---|---|
--cffy-hero-gap | --cffy-space-8 | Space between the title, description and other text |
--cffy-hero-margin-block-end | --cffy-space-32 | Space below the hero |
--cffy-hero-max-width | 60ch | Title and description line length |
--cffy-hero-description-font-size / --cffy-hero-description-line-height | --cffy-fs-lg / --cffy-fs-lg--line-height | Description text |
--cffy-hero-description-color | --cffy-muted-a10 | Description color |
--cffy-hero-actions-offset / --cffy-hero-actions-gap | --cffy-space-24 / --cffy-space-12 | Space above the actions, and between them |
Morph a title across pages
viewTransitionName is the name both pages share, so the browser can pair the
two titles. Nothing is emitted when the prop is omitted.
<template>
<!-- list page -->
<UiHeaderContent title="Atlas" view-transition-name="project-atlas" />
<!-- detail page -->
<UiHeaderContent title="Atlas" view-transition-name="project-atlas" back-button />
</template>
The description derives <name>-description, so one prop covers both lines.
view-transition-name has to be unique within a document — two elements
sharing one make the browser skip the transition entirely. Give each header its
own name, and only name the ones that actually morph.In Nuxt, the transition itself is enabled with experimental.viewTransition in
nuxt.config.ts. If the app also sets pageTransition or layoutTransition,
the Nuxt docs suggest a
global middleware that turns those off where document.startViewTransition
exists, so the two do not animate the same navigation.
Label a section
<template>
<UiSubheadingContent
title="Integrations"
subtitle="Connect the tools your team already uses."
>
<template #actions>
<UiBadge text="3 active" variant="outline" size="sm" />
</template>
</UiSubheadingContent>
</template>
UiSubheadingContent props
| Prop | Type | Default | Description |
|---|---|---|---|
as | string | 'h3' | Heading element, so the page keeps a valid heading order. |
title | string | null | — | Subheading text, rendered with .text-subheading. |
subtitle | string | null | — | Description; picks up the muted styling by being the heading's sibling. |
gutter | 'none' | 'sm' | 'md' | 'md' | Space below the block. The title-to-description gap is fixed and does not follow it. |
customClass | string | string[] | Record<string, boolean> | — | Extra classes for the wrapper. |
Slots: #actions — a badge or control beside the subheading; the wrapper switches to two columns when it is filled.
Subheading CSS variables
UiSubheadingContent (.subheading-content) reads these.
| Variable | Default | Controls |
|---|---|---|
--cffy-subheading-gap | --cffy-space-16 | Space between the title and the actions |
--cffy-subheading-actions-gap | --cffy-space-8 | Space between actions |
Close a page with a footer
UiFooter renders the brand block and an optional bottom bar, UiFooterGroup
lays out each set of links (stacked by default, or in a row) and UiFooterItem
is the link itself.
<template>
<UiFooter title="Colorffy UI" subtitle="MIT licensed">
<UiFooterGroup title="Docs">
<UiFooterItem text="Installation" :as="NuxtLink" to="/install" />
<UiFooterItem text="Components" :as="NuxtLink" to="/components" />
</UiFooterGroup>
<UiFooterGroup title="Social" direction="row">
<UiFooterItem text="GitHub" href="https://github.com" icon="" />
</UiFooterGroup>
<template #bottom>
<p>© 2026 Colorffy</p>
<UiBadge text="v2.5.1" variant="outline" size="sm" />
</template>
</UiFooter>
</template>
A row group with a title keeps its heading on its own line and lays the links
out beside each other; without a title the whole group is one row, which suits
a legal strip. overline renders the heading small and uppercase.
The footer wraps its own content in a .container, so it lines up with the
rest of the page without any extra markup. fluid swaps that for
.container-fluid, the same way UiNavbar does it.
UiFooter props
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | null | — | Brand title. |
subtitle | string | null | — | Supporting line below the title. |
fluid | boolean | false | Use .container-fluid instead of .container. |
customClass | string | string[] | Record<string, boolean> | — | Extra classes. |
Slots: #brand replaces the title/subtitle block, the default slot takes the link groups, and #bottom renders a divided bar under them, split left and right.
UiFooterGroup props
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | null | — | Heading for the group. |
direction | 'col' | 'row' | 'col' | Stacks the links or lays them out in a row. With a title, the heading stays on its own line. |
overline | boolean | false | Renders the title small and uppercase. |
customClass | string | string[] | Record<string, boolean> | — | Extra classes. |
UiFooterItem props
| Prop | Type | Default | Description |
|---|---|---|---|
text | string | null | — | Label text; the default slot overrides it. |
icon | string | null | — | Leading Material Symbols code point. |
to / href | string | object | null | — | Link target. Without one the item renders a plain .anchor-link entry. |
as | string | object | null | — | Link component for client-side routing (NuxtLink in Nuxt, RouterLink in Vue). |
customClass | string | string[] | Record<string, boolean> | — | Extra classes. |
to or href renders a plain <a href> — the package
ships to Vue as well as Nuxt, so it never imports a router link itself. Pass
:as="NuxtLink" (or RouterLink) for client-side navigation, the same way
UiNavbarLink and UiSidebarLink take it. External href values stay plain
anchors even with as set, and get target="_blank" and
rel="noopener noreferrer".Footer CSS variables
Set these on :root or on a wrapper. Row groups (row on UiFooterGroup) keep their wider gap.
:root {
--cffy-footer-bg-color: var(--cffy-surface-container-low);
--cffy-footer-link-hover-color: var(--cffy-accent-a10);
}
| Variable | Default | Controls |
|---|---|---|
--cffy-footer-bg-color / --cffy-footer-color | --cffy-surface-body-pane / --cffy-on-background | Background and text |
--cffy-footer-muted-color | --cffy-muted-a10 | Subtitle, links and bottom-bar text |
--cffy-footer-border-color | --cffy-outline-surface | Bottom-bar divider |
--cffy-footer-padding-inline / --cffy-footer-padding-block | 0 / --cffy-space-32 + --cffy-space-8 | Padding |
--cffy-footer-margin-block-start | the block padding | Space above the footer |
--cffy-footer-gap | --cffy-space-16 | Space between groups and items |
--cffy-footer-title-font-size / --cffy-footer-title-font-weight | --cffy-fs-lg / --cffy-fw-800 | Brand title |
--cffy-footer-link-color / --cffy-footer-link-hover-color | the muted color / --cffy-primary-a10 | Links |
--cffy-footer-link-font-size | --cffy-fs-2xs | Link text size |
--cffy-footer-icon-color | --cffy-on-secondary-container | Link icons |
UiPaneContent props
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | — | DOM id for the <section>. |
isFullHeight | boolean | false | Expand to full height. |
customClass / containerClass | string | string[] | Record<string, boolean> | — | Extra classes. |
ariaLabel / ariaLabelledby / ariaDescribedby | string | — | ARIA wiring. |
Slots: default — the pane content.
Exposed: paneContentRef, the <section> element.