Home components
Three shadow-DOM web components make up the landing layout: <dfk-hero> (logo,
title, tagline, actions, badges), <dfk-features> (a section title and a card
grid) and <dfk-next-steps> (link cards). The live demo is this site's
home page — everything above the code showcase is these components.
Content goes through setters
They are retained-mode elements, not attribute-driven ones: the structure is built once in the constructor and each setter mutates only the nodes it owns. From React, mount them through a callback ref — React 19 reconciles only string props onto custom elements, so an object payload would never survive hydration:
import {createElement} from 'react';
import type {ReactNode} from 'react';
import {DfkNextSteps, type NextStepItem} from 'duckfn-docs-kit';
function mountSteps(node: HTMLElement): void {
const steps = node as DfkNextSteps;
steps.setSectionTitle('Where to go next');
steps.setSteps([
{href: '/docs/intro', title: 'Introduction', details: 'What duckfn is.'},
] satisfies NextStepItem[]);
}
const element: ReactNode = createElement('dfk-next-steps', {ref: mountSteps});
The content types come from the barrel — HeroAction, HeroBadge and
HeroLink for the hero, FeatureItem for the grid, NextStepItem for the
cards:
import type {FeatureItem, HeroAction, HeroBadge, HeroLink, NextStepItem} from 'duckfn-docs-kit';
Registering the elements
The dfk-* tags have to be defined once per app boot with
registerDfkElements() — it also registers the official <iconify-icon>
element used for every glyph. A site that configures the extension preloading
gets the registration injected on every page; a site that only uses the home
components calls it at module scope on its home page:
import {registerDfkElements} from 'duckfn-docs-kit';
registerDfkElements();
Styling and theming
- The components carry their own styles inside the JS bundle and inject them
into their shadow roots (
adoptedStyleSheets), so nothing leaks either way. - The
--duckfn-*brand tokens come fromsrc/theme/tokens.css, part ofkit.css; the site's Infima (--ifm-*) variables inherit into the shadow trees, so dark mode and the site palette apply automatically. - Icons are Iconify names (
'lucide:sparkles'), rendered by the official<iconify-icon>element; sizes followfont-size.
Known trade-off
The components render client-side, so their prerendered HTML is an empty shell until hydration. This is deliberate: it is what keeps the light DOM empty and hydration mismatch-free, at the cost of one height jump on first paint.