@bitbaum/sitekit 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +129 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +12 -0
- package/dist/model.d.ts +28 -0
- package/dist/model.js +42 -0
- package/dist/provenance.d.ts +60 -0
- package/dist/provenance.js +61 -0
- package/dist/react/NavAutoScroll.d.ts +3 -0
- package/dist/react/NavAutoScroll.js +26 -0
- package/dist/react/SiteChrome.d.ts +25 -0
- package/dist/react/SiteChrome.js +21 -0
- package/dist/react/SiteNav.d.ts +22 -0
- package/dist/react/SiteNav.js +32 -0
- package/dist/react/SiteSections.d.ts +16 -0
- package/dist/react/SiteSections.js +42 -0
- package/dist/react/index.d.ts +19 -0
- package/dist/react/index.js +19 -0
- package/dist/react/link.d.ts +18 -0
- package/dist/react/link.js +11 -0
- package/dist/react/sections/BusinessSections.d.ts +41 -0
- package/dist/react/sections/BusinessSections.js +33 -0
- package/dist/react/sections/DataSections.d.ts +19 -0
- package/dist/react/sections/DataSections.js +45 -0
- package/dist/react/sections/FigureSections.d.ts +26 -0
- package/dist/react/sections/FigureSections.js +22 -0
- package/dist/react/sections/HeroSection.d.ts +13 -0
- package/dist/react/sections/HeroSection.js +9 -0
- package/dist/react/sections/Primitives.d.ts +32 -0
- package/dist/react/sections/Primitives.js +21 -0
- package/dist/react/sections/ProseSections.d.ts +25 -0
- package/dist/react/sections/ProseSections.js +22 -0
- package/dist/schema.d.ts +573 -0
- package/dist/schema.js +288 -0
- package/package.json +72 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sections a small business needs that a research publication does not:
|
|
3
|
+
* the founder story, where the shop actually is, and the questions every
|
|
4
|
+
* customer asks. Added for the Camille rebuild (the first real site the
|
|
5
|
+
* union was tested against), which is exactly how the union is meant to
|
|
6
|
+
* grow — a kind lands here with its schema and tests when a real site
|
|
7
|
+
* cannot be expressed without it.
|
|
8
|
+
*/
|
|
9
|
+
import type { SiteSection } from '../../schema.js';
|
|
10
|
+
/**
|
|
11
|
+
* A figure beside a story. The one section on a small-business site that is
|
|
12
|
+
* allowed to be personal.
|
|
13
|
+
*/
|
|
14
|
+
export declare function FeatureSection({ section, index, }: {
|
|
15
|
+
section: Extract<SiteSection, {
|
|
16
|
+
kind: 'feature';
|
|
17
|
+
}>;
|
|
18
|
+
index?: number;
|
|
19
|
+
}): import("react").JSX.Element;
|
|
20
|
+
/**
|
|
21
|
+
* Where the business actually is. Address, phone and hours are the fields
|
|
22
|
+
* the provenance rules exist to protect — this renderer shows exactly what
|
|
23
|
+
* the data says and nothing more, including simply omitting hours that were
|
|
24
|
+
* never found.
|
|
25
|
+
*/
|
|
26
|
+
export declare function ContactSection({ section, index, }: {
|
|
27
|
+
section: Extract<SiteSection, {
|
|
28
|
+
kind: 'contact';
|
|
29
|
+
}>;
|
|
30
|
+
index?: number;
|
|
31
|
+
}): import("react").JSX.Element;
|
|
32
|
+
/**
|
|
33
|
+
* Native <details> disclosure — works with JavaScript disabled, which a
|
|
34
|
+
* generated site for a small business should.
|
|
35
|
+
*/
|
|
36
|
+
export declare function FaqSection({ section, index, }: {
|
|
37
|
+
section: Extract<SiteSection, {
|
|
38
|
+
kind: 'faq';
|
|
39
|
+
}>;
|
|
40
|
+
index?: number;
|
|
41
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Sections a small business needs that a research publication does not:
|
|
4
|
+
* the founder story, where the shop actually is, and the questions every
|
|
5
|
+
* customer asks. Added for the Camille rebuild (the first real site the
|
|
6
|
+
* union was tested against), which is exactly how the union is meant to
|
|
7
|
+
* grow — a kind lands here with its schema and tests when a real site
|
|
8
|
+
* cannot be expressed without it.
|
|
9
|
+
*/
|
|
10
|
+
import { Blurb, SectionBody, SectionHeading } from './Primitives.js';
|
|
11
|
+
/**
|
|
12
|
+
* A figure beside a story. The one section on a small-business site that is
|
|
13
|
+
* allowed to be personal.
|
|
14
|
+
*/
|
|
15
|
+
export function FeatureSection({ section, index, }) {
|
|
16
|
+
return (_jsxs("section", { id: section.anchor, className: "scroll-mt-24", children: [section.heading && _jsx(SectionHeading, { index: index, children: section.heading }), _jsx(SectionBody, { children: _jsxs("div", { className: section.image ? 'grid items-center gap-10 lg:grid-cols-2' : 'max-w-prose', children: [section.image && (_jsx("img", { src: section.image.src, alt: section.image.alt, className: "w-full rounded-2xl border border-subtle object-cover" })), _jsxs("div", { children: [_jsx("div", { className: "max-w-prose space-y-4", children: section.paragraphs.map((paragraph, i) => (_jsx("p", { className: "text-base leading-relaxed text-fg-secondary", children: paragraph }, i))) }), section.quote && (_jsxs("blockquote", { className: "mt-6 border-l-4 border-accent pl-4 italic text-fg-secondary", children: ["\u00AB", section.quote, "\u00BB"] })), section.cta && (_jsxs("a", { href: section.cta.href, className: "mt-6 inline-block font-medium text-accent hover:underline", children: [section.cta.label, " \u2192"] }))] })] }) })] }));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Where the business actually is. Address, phone and hours are the fields
|
|
20
|
+
* the provenance rules exist to protect — this renderer shows exactly what
|
|
21
|
+
* the data says and nothing more, including simply omitting hours that were
|
|
22
|
+
* never found.
|
|
23
|
+
*/
|
|
24
|
+
export function ContactSection({ section, index, }) {
|
|
25
|
+
return (_jsxs("section", { id: section.anchor, className: "scroll-mt-24", children: [section.heading && _jsx(SectionHeading, { index: index, children: section.heading }), section.blurb && _jsx("div", { className: "sm:pl-10", children: _jsx(Blurb, { children: section.blurb }) }), _jsx(SectionBody, { children: _jsx("div", { className: "grid gap-6 md:grid-cols-2", children: section.locations.map((location) => (_jsxs("div", { className: "rounded-2xl border border-subtle bg-surface-raised p-6 sm:p-8", children: [location.note && (_jsx("p", { className: "mb-1 font-mono text-xs uppercase tracking-caps text-fg-tertiary", children: location.note })), _jsx("h3", { className: "font-heading text-2xl font-semibold text-fg-primary", children: location.name }), _jsx("address", { className: "mt-3 not-italic leading-relaxed text-fg-secondary", children: location.address }), location.phone && (_jsx("p", { className: "text-fg-secondary", children: _jsx("a", { href: `tel:${location.phone.replace(/\s+/g, '')}`, className: "hover:text-accent", children: location.phone }) })), location.hours && location.hours.length > 0 && (_jsx("ul", { className: "mt-4 space-y-1 text-sm text-fg-muted", children: location.hours.map((line) => (_jsx("li", { children: line }, line))) }))] }, location.name))) }) })] }));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Native <details> disclosure — works with JavaScript disabled, which a
|
|
29
|
+
* generated site for a small business should.
|
|
30
|
+
*/
|
|
31
|
+
export function FaqSection({ section, index, }) {
|
|
32
|
+
return (_jsxs("section", { id: section.anchor, className: "scroll-mt-24", children: [section.heading && _jsx(SectionHeading, { index: index, children: section.heading }), _jsx(SectionBody, { children: _jsx("div", { className: "max-w-prose space-y-4", children: section.items.map((item) => (_jsxs("details", { className: "group rounded-xl border border-subtle bg-surface-raised open:border-accent", children: [_jsxs("summary", { className: "flex min-h-11 cursor-pointer list-none items-center justify-between gap-4 px-5 py-4 font-medium text-fg-primary", children: [item.question, _jsx("span", { "aria-hidden": true, className: "text-xl text-accent transition-transform group-open:rotate-45", children: "+" })] }), _jsx("p", { className: "px-5 pb-4 text-sm leading-relaxed text-fg-secondary", children: item.answer })] }, item.question))) }) })] }));
|
|
33
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sections that present data itself: the jump index and the tables.
|
|
3
|
+
*
|
|
4
|
+
* Codes, counts and statuses are set in mono so a reader can scan a column;
|
|
5
|
+
* the status cell carries a dot because a status keyword is the one word on a
|
|
6
|
+
* page a reader must never skim past.
|
|
7
|
+
*/
|
|
8
|
+
import type { SiteSection } from '../../schema.js';
|
|
9
|
+
export declare function IndexSection({ section, index, }: {
|
|
10
|
+
section: Extract<SiteSection, {
|
|
11
|
+
kind: 'index';
|
|
12
|
+
}>;
|
|
13
|
+
index?: number;
|
|
14
|
+
}): import("react").JSX.Element;
|
|
15
|
+
export declare function TableSection({ section }: {
|
|
16
|
+
section: Extract<SiteSection, {
|
|
17
|
+
kind: 'table';
|
|
18
|
+
}>;
|
|
19
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Sections that present data itself: the jump index and the tables.
|
|
4
|
+
*
|
|
5
|
+
* Codes, counts and statuses are set in mono so a reader can scan a column;
|
|
6
|
+
* the status cell carries a dot because a status keyword is the one word on a
|
|
7
|
+
* page a reader must never skim past.
|
|
8
|
+
*/
|
|
9
|
+
import { Blurb, SectionBody, SectionHeading } from './Primitives.js';
|
|
10
|
+
/** Status keyword → dot colour. Anything unrecognised stays neutral. */
|
|
11
|
+
const STATUS_DOT = {
|
|
12
|
+
sourced: 'bg-status-positive',
|
|
13
|
+
'unverified lead': 'bg-status-warning',
|
|
14
|
+
taken: 'bg-fg-muted',
|
|
15
|
+
done: 'bg-status-positive',
|
|
16
|
+
'in progress': 'bg-status-warning',
|
|
17
|
+
'not started': 'bg-fg-muted',
|
|
18
|
+
// Scarcity grades. Red reads as "constrained", green as "no constraint here".
|
|
19
|
+
chokepoint: 'bg-status-negative',
|
|
20
|
+
concentrated: 'bg-status-warning',
|
|
21
|
+
competitive: 'bg-status-positive',
|
|
22
|
+
};
|
|
23
|
+
export function IndexSection({ section, index, }) {
|
|
24
|
+
return (_jsxs("section", { children: [section.heading && _jsx(SectionHeading, { index: index, children: section.heading }), section.blurb && _jsx("div", { className: "sm:pl-10", children: _jsx(Blurb, { children: section.blurb }) }), _jsx(SectionBody, { children: _jsx("ol", { className: "divide-y divide-subtle border-y border-subtle", children: section.entries.map((entry, i) => (_jsx("li", { children: _jsxs("a", { href: `#${entry.anchor}`, className: "group flex items-baseline gap-4 py-2.5 transition-colors hover:text-fg-primary", children: [_jsx("span", { className: "font-mono text-xs tabular-nums text-fg-muted", children: String(i + 1).padStart(2, '0') }), _jsx("span", { className: "flex-1 text-sm text-fg-secondary group-hover:text-fg-primary", children: entry.label }), entry.meta && (_jsx("span", { className: "font-mono text-xs tabular-nums text-fg-muted", children: entry.meta }))] }) }, entry.anchor))) }) })] }));
|
|
25
|
+
}
|
|
26
|
+
export function TableSection({ section }) {
|
|
27
|
+
const mono = new Set(section.monoColumns ?? []);
|
|
28
|
+
// Fixed layout with declared widths, because a page can stack fifteen tables
|
|
29
|
+
// with the same columns. Auto layout sizes each to its own longest cell, so
|
|
30
|
+
// one long name shifts every column out of line with the table above it.
|
|
31
|
+
// Aligned columns are what make the set read as one instrument.
|
|
32
|
+
const restWidth = section.columns.length > 1 ? 66 / (section.columns.length - 1) : 100;
|
|
33
|
+
return (_jsxs("section", { id: section.anchor, className: "scroll-mt-24", children: [section.heading && (_jsx("h3", { className: "font-heading text-lg font-semibold text-fg-primary", children: section.heading })), section.blurb && _jsx(Blurb, { children: section.blurb }), _jsx("div", { className: "mt-4 overflow-x-auto", children: _jsxs("table", { className: "w-full table-fixed border-collapse text-left", style: { minWidth: '560px' }, children: [_jsx("colgroup", { children: section.columns.map((_, i) => (_jsx("col", { style: { width: i === 0 ? '34%' : `${restWidth}%` } }, i))) }), _jsx("thead", { children: _jsx("tr", { className: "border-b border-strong", children: section.columns.map((column) => (_jsx("th", { scope: "col", className: "py-2.5 pr-4 font-mono text-xs font-medium uppercase tracking-caps text-fg-tertiary", children: column }, column))) }) }), _jsx("tbody", { className: "divide-y divide-subtle", children: section.rows.map((row, rowIndex) => (_jsx("tr", { children: row.map((cell, cellIndex) => {
|
|
34
|
+
const isStatus = section.statusColumn === cellIndex;
|
|
35
|
+
const dot = isStatus ? STATUS_DOT[cell.trim().toLowerCase()] : undefined;
|
|
36
|
+
return (_jsx("td", { className: [
|
|
37
|
+
'py-2.5 pr-4 align-top text-sm leading-relaxed',
|
|
38
|
+
mono.has(cellIndex)
|
|
39
|
+
? 'font-mono text-xs tabular-nums text-fg-secondary'
|
|
40
|
+
: cellIndex === 0
|
|
41
|
+
? 'text-fg-primary'
|
|
42
|
+
: 'text-fg-secondary',
|
|
43
|
+
].join(' '), children: isStatus ? (_jsxs("span", { className: "inline-flex items-baseline gap-2", children: [_jsx("span", { "aria-hidden": true, className: `inline-block h-1.5 w-1.5 shrink-0 self-center rounded-full ${dot ?? 'bg-fg-muted'}` }), cell] })) : (cell) }, cellIndex));
|
|
44
|
+
}) }, rowIndex))) })] }) }), section.note && (_jsx("p", { className: "mt-3 max-w-prose text-xs leading-relaxed text-fg-muted", children: section.note }))] }));
|
|
45
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sections that render a number.
|
|
3
|
+
*
|
|
4
|
+
* Both use tabular mono figures so digits line up column to column — the
|
|
5
|
+
* difference between a page that looks measured and one that looks typed.
|
|
6
|
+
*/
|
|
7
|
+
import type { SiteSection } from '../../schema.js';
|
|
8
|
+
export declare function StatsSection({ section, index, }: {
|
|
9
|
+
section: Extract<SiteSection, {
|
|
10
|
+
kind: 'stats';
|
|
11
|
+
}>;
|
|
12
|
+
index?: number;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
14
|
+
/**
|
|
15
|
+
* One number, drawn.
|
|
16
|
+
*
|
|
17
|
+
* The bar is intentionally literal: when nothing is sourced it renders empty,
|
|
18
|
+
* because an empty bar is the honest picture of an unfinished phase. Anything
|
|
19
|
+
* that dressed a 0% up as progress would be the page lying.
|
|
20
|
+
*/
|
|
21
|
+
export declare function MeterSection({ section, index, }: {
|
|
22
|
+
section: Extract<SiteSection, {
|
|
23
|
+
kind: 'meter';
|
|
24
|
+
}>;
|
|
25
|
+
index?: number;
|
|
26
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Sections that render a number.
|
|
4
|
+
*
|
|
5
|
+
* Both use tabular mono figures so digits line up column to column — the
|
|
6
|
+
* difference between a page that looks measured and one that looks typed.
|
|
7
|
+
*/
|
|
8
|
+
import { SectionBody, SectionHeading } from './Primitives.js';
|
|
9
|
+
export function StatsSection({ section, index, }) {
|
|
10
|
+
return (_jsxs("section", { children: [section.heading && _jsx(SectionHeading, { index: index, children: section.heading }), _jsx("dl", { className: "mt-6 grid grid-cols-1 divide-y divide-subtle border-y border-subtle sm:grid-cols-3 sm:divide-x sm:divide-y-0", children: section.stats.map((stat) => (_jsxs("div", { className: "px-0 py-6 sm:px-6 sm:first:pl-0", children: [_jsx("dt", { className: "font-mono text-xs uppercase tracking-caps text-fg-tertiary", children: stat.label }), _jsx("dd", { className: "mt-3 font-heading text-4xl font-medium tabular-nums text-fg-primary", children: stat.value }), stat.note && _jsx("p", { className: "mt-2 text-sm leading-relaxed text-fg-muted", children: stat.note })] }, stat.label))) })] }));
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* One number, drawn.
|
|
14
|
+
*
|
|
15
|
+
* The bar is intentionally literal: when nothing is sourced it renders empty,
|
|
16
|
+
* because an empty bar is the honest picture of an unfinished phase. Anything
|
|
17
|
+
* that dressed a 0% up as progress would be the page lying.
|
|
18
|
+
*/
|
|
19
|
+
export function MeterSection({ section, index, }) {
|
|
20
|
+
const percent = section.of > 0 ? Math.round((section.value / section.of) * 100) : 0;
|
|
21
|
+
return (_jsxs("section", { children: [section.heading && _jsx(SectionHeading, { index: index, children: section.heading }), _jsxs(SectionBody, { children: [_jsxs("div", { className: "flex items-baseline justify-between gap-6", children: [_jsx("span", { className: "font-mono text-xs uppercase tracking-caps text-fg-tertiary", children: section.label }), _jsxs("span", { className: "font-mono text-sm tabular-nums text-fg-secondary", children: [percent, "%"] })] }), _jsxs("p", { className: "mt-3 font-heading text-4xl font-medium tabular-nums text-fg-primary", children: [section.value, _jsxs("span", { className: "text-fg-muted", children: [" / ", section.of] })] }), _jsx("div", { className: "mt-4 h-1.5 w-full overflow-hidden rounded-full bg-surface-raised", role: "progressbar", "aria-valuenow": section.value, "aria-valuemin": 0, "aria-valuemax": section.of, "aria-label": section.label, children: _jsx("div", { className: "h-full rounded-full bg-accent", style: { width: `${percent}%` } }) }), section.caption && (_jsx("p", { className: "mt-3 max-w-prose text-sm leading-relaxed text-fg-muted", children: section.caption }))] })] }));
|
|
22
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The opening of a page that has one.
|
|
3
|
+
*
|
|
4
|
+
* An eyebrow, one display statement, and the lead — the shape a serious
|
|
5
|
+
* publication uses, and deliberately not a marketing hero: no gradient, no
|
|
6
|
+
* illustration, no button. The claim is the design.
|
|
7
|
+
*/
|
|
8
|
+
import type { SiteSection } from '../../schema.js';
|
|
9
|
+
export declare function HeroSection({ section }: {
|
|
10
|
+
section: Extract<SiteSection, {
|
|
11
|
+
kind: 'hero';
|
|
12
|
+
}>;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export function HeroSection({ section }) {
|
|
3
|
+
return (_jsxs("section", { className: "border-b border-subtle pb-12", children: [section.eyebrow && (_jsx("p", { className: "font-mono text-xs uppercase tracking-caps text-fg-tertiary", children: section.eyebrow })), _jsx("h1", { className: "mt-5 max-w-4xl font-heading text-4xl font-semibold leading-tight tracking-display text-fg-primary sm:text-5xl lg:text-6xl", children: section.statement }), _jsx("div", { className: "mt-7 max-w-prose space-y-4", children: section.lead.map((paragraph, index) => (_jsx("p", { className: "text-lg leading-relaxed text-fg-secondary", children: paragraph }, index))) }), section.actions && section.actions.length > 0 && (_jsx("div", { className: "mt-8 flex flex-wrap gap-3", children: section.actions.map((action, i) => (_jsx("a", { href: action.href, className: [
|
|
4
|
+
'inline-flex min-h-11 items-center justify-center rounded-full px-6 py-2.5 text-sm font-medium transition-colors',
|
|
5
|
+
i === 0
|
|
6
|
+
? 'bg-accent text-surface-page hover:opacity-90'
|
|
7
|
+
: 'border-2 border-strong text-fg-primary hover:border-accent hover:text-accent',
|
|
8
|
+
].join(' '), children: action.label }, action.href))) }))] }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared type primitives for site sections.
|
|
3
|
+
*
|
|
4
|
+
* The whole visual system of a generated site lives in these components plus
|
|
5
|
+
* the section files beside them. They emit SEMANTIC utility classes only —
|
|
6
|
+
* `text-fg-primary`, `bg-surface-page`, `font-heading` — and ship no colours,
|
|
7
|
+
* no faces, no radii. Each consumer defines those tokens in its own
|
|
8
|
+
* stylesheet, which is how fifty sites share one renderer without looking
|
|
9
|
+
* like one company. The full class contract is in the README.
|
|
10
|
+
*/
|
|
11
|
+
import type { ReactNode } from 'react';
|
|
12
|
+
/**
|
|
13
|
+
* A numbered section heading. Numbering makes a long page navigable and
|
|
14
|
+
* signals that the document has a structure rather than being a stack of
|
|
15
|
+
* blocks.
|
|
16
|
+
*/
|
|
17
|
+
export declare function SectionHeading({ index, children }: {
|
|
18
|
+
index?: number;
|
|
19
|
+
children: ReactNode;
|
|
20
|
+
}): import("react").JSX.Element;
|
|
21
|
+
/** Standfirst under a heading. Kept at measure — it is prose, not layout. */
|
|
22
|
+
export declare function Blurb({ children }: {
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
}): import("react").JSX.Element;
|
|
25
|
+
/**
|
|
26
|
+
* Indents a section's body to sit under the heading text rather than under its
|
|
27
|
+
* number. Small thing; it is what makes the numbering read as a margin note
|
|
28
|
+
* instead of a bullet.
|
|
29
|
+
*/
|
|
30
|
+
export declare function SectionBody({ children }: {
|
|
31
|
+
children: ReactNode;
|
|
32
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* A numbered section heading. Numbering makes a long page navigable and
|
|
4
|
+
* signals that the document has a structure rather than being a stack of
|
|
5
|
+
* blocks.
|
|
6
|
+
*/
|
|
7
|
+
export function SectionHeading({ index, children }) {
|
|
8
|
+
return (_jsxs("div", { className: "flex items-baseline gap-4", children: [index !== undefined && (_jsx("span", { "aria-hidden": true, className: "font-mono text-xs tabular-nums text-fg-muted", children: String(index).padStart(2, '0') })), _jsx("h2", { className: "font-heading text-2xl font-semibold tracking-display text-fg-primary sm:text-3xl", children: children })] }));
|
|
9
|
+
}
|
|
10
|
+
/** Standfirst under a heading. Kept at measure — it is prose, not layout. */
|
|
11
|
+
export function Blurb({ children }) {
|
|
12
|
+
return _jsx("p", { className: "mt-3 max-w-prose text-base leading-relaxed text-fg-secondary", children: children });
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Indents a section's body to sit under the heading text rather than under its
|
|
16
|
+
* number. Small thing; it is what makes the numbering read as a margin note
|
|
17
|
+
* instead of a bullet.
|
|
18
|
+
*/
|
|
19
|
+
export function SectionBody({ children }) {
|
|
20
|
+
return _jsx("div", { className: "mt-6 sm:pl-10", children: children });
|
|
21
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sections made of words: running prose, top-ruled cards, and definitions.
|
|
3
|
+
*
|
|
4
|
+
* Cards are ruled rather than boxed. A grid of bordered boxes reads as a
|
|
5
|
+
* product page; a grid of hairline-topped columns reads as a document.
|
|
6
|
+
*/
|
|
7
|
+
import type { SiteSection } from '../../schema.js';
|
|
8
|
+
export declare function ProseSection({ section, index, }: {
|
|
9
|
+
section: Extract<SiteSection, {
|
|
10
|
+
kind: 'prose';
|
|
11
|
+
}>;
|
|
12
|
+
index?: number;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
14
|
+
export declare function CardsSection({ section, index, }: {
|
|
15
|
+
section: Extract<SiteSection, {
|
|
16
|
+
kind: 'cards';
|
|
17
|
+
}>;
|
|
18
|
+
index?: number;
|
|
19
|
+
}): import("react").JSX.Element;
|
|
20
|
+
export declare function DefinitionsSection({ section, index, }: {
|
|
21
|
+
section: Extract<SiteSection, {
|
|
22
|
+
kind: 'definitions';
|
|
23
|
+
}>;
|
|
24
|
+
index?: number;
|
|
25
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Sections made of words: running prose, top-ruled cards, and definitions.
|
|
4
|
+
*
|
|
5
|
+
* Cards are ruled rather than boxed. A grid of bordered boxes reads as a
|
|
6
|
+
* product page; a grid of hairline-topped columns reads as a document.
|
|
7
|
+
*/
|
|
8
|
+
import { Blurb, SectionBody, SectionHeading } from './Primitives.js';
|
|
9
|
+
export function ProseSection({ section, index, }) {
|
|
10
|
+
return (_jsxs("section", { id: section.anchor, className: "scroll-mt-24", children: [section.heading && _jsx(SectionHeading, { index: index, children: section.heading }), _jsx("div", { className: section.heading ? 'mt-6 sm:pl-10' : '', children: _jsx("div", { className: "max-w-prose space-y-4", children: section.paragraphs.map((paragraph, i) => (_jsx("p", { className: "text-base leading-relaxed text-fg-secondary", children: paragraph }, i))) }) })] }));
|
|
11
|
+
}
|
|
12
|
+
export function CardsSection({ section, index, }) {
|
|
13
|
+
const columns = section.columns === 4
|
|
14
|
+
? 'lg:grid-cols-4'
|
|
15
|
+
: section.columns === 3
|
|
16
|
+
? 'lg:grid-cols-3'
|
|
17
|
+
: 'lg:grid-cols-2';
|
|
18
|
+
return (_jsxs("section", { id: section.anchor, className: "scroll-mt-24", children: [section.heading && _jsx(SectionHeading, { index: index, children: section.heading }), section.blurb && _jsx("div", { className: "sm:pl-10", children: _jsx(Blurb, { children: section.blurb }) }), _jsx(SectionBody, { children: _jsx("div", { className: `grid grid-cols-1 gap-x-8 gap-y-9 sm:grid-cols-2 ${columns}`, children: section.cards.map((card) => (_jsxs("article", { className: "flex h-full flex-col border-t border-strong pt-4", children: [card.icon && (_jsx("span", { "aria-hidden": true, className: "mb-2 text-3xl", children: card.icon })), _jsx("h3", { className: "font-heading text-base font-semibold text-fg-primary", children: card.title }), _jsx("p", { className: "mt-2 flex-1 text-sm leading-relaxed text-fg-secondary", children: card.body }), card.meta && (_jsx("p", { className: "mt-4 font-mono text-xs leading-relaxed text-fg-muted", children: card.meta }))] }, card.title))) }) })] }));
|
|
19
|
+
}
|
|
20
|
+
export function DefinitionsSection({ section, index, }) {
|
|
21
|
+
return (_jsxs("section", { id: section.anchor, className: "scroll-mt-24", children: [section.heading && _jsx(SectionHeading, { index: index, children: section.heading }), section.blurb && _jsx("div", { className: "sm:pl-10", children: _jsx(Blurb, { children: section.blurb }) }), _jsx(SectionBody, { children: _jsx("dl", { className: "divide-y divide-subtle border-y border-subtle", children: section.items.map((item) => (_jsxs("div", { className: "grid grid-cols-1 gap-1 py-4 sm:grid-cols-3 sm:gap-6", children: [_jsx("dt", { className: "text-sm font-semibold text-fg-primary", children: item.term }), _jsx("dd", { className: "text-sm leading-relaxed text-fg-secondary sm:col-span-2", children: item.detail })] }, item.term))) }) })] }));
|
|
22
|
+
}
|