@supertype.ai/foundations 0.1.24

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.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +369 -0
  3. package/bin/foundations.mjs +713 -0
  4. package/dist/blocks/accordion.d.ts +23 -0
  5. package/dist/blocks/accordion.js +59 -0
  6. package/dist/blocks/callout.d.ts +57 -0
  7. package/dist/blocks/callout.js +61 -0
  8. package/dist/blocks/card.d.ts +34 -0
  9. package/dist/blocks/card.js +56 -0
  10. package/dist/blocks/index.d.ts +7 -0
  11. package/dist/blocks/index.js +7 -0
  12. package/dist/blocks/interactive-accordion.d.ts +13 -0
  13. package/dist/blocks/interactive-accordion.js +27 -0
  14. package/dist/blocks/segment.d.ts +37 -0
  15. package/dist/blocks/segment.js +37 -0
  16. package/dist/blocks/steps.d.ts +10 -0
  17. package/dist/blocks/steps.js +13 -0
  18. package/dist/blocks/tabs.d.ts +32 -0
  19. package/dist/blocks/tabs.js +69 -0
  20. package/dist/cjs/eslint.js +146 -0
  21. package/dist/cjs/package.json +3 -0
  22. package/dist/cn.d.ts +2 -0
  23. package/dist/cn.js +5 -0
  24. package/dist/contrast.d.ts +47 -0
  25. package/dist/contrast.js +255 -0
  26. package/dist/eslint.d.ts +74 -0
  27. package/dist/eslint.js +138 -0
  28. package/dist/essay/contents.d.ts +10 -0
  29. package/dist/essay/contents.js +17 -0
  30. package/dist/essay/essay.d.ts +125 -0
  31. package/dist/essay/essay.js +92 -0
  32. package/dist/essay/index.d.ts +7 -0
  33. package/dist/essay/index.js +9 -0
  34. package/dist/essay/layout.d.ts +72 -0
  35. package/dist/essay/layout.js +77 -0
  36. package/dist/essay/rail.d.ts +15 -0
  37. package/dist/essay/rail.js +26 -0
  38. package/dist/essay/reading.d.ts +17 -0
  39. package/dist/essay/reading.js +31 -0
  40. package/dist/essay/scroll.d.ts +8 -0
  41. package/dist/essay/scroll.js +78 -0
  42. package/dist/essay/toc.d.ts +23 -0
  43. package/dist/essay/toc.js +50 -0
  44. package/dist/index.d.ts +2 -0
  45. package/dist/index.js +33 -0
  46. package/dist/injection.d.ts +8 -0
  47. package/dist/injection.js +1 -0
  48. package/dist/mdx.d.ts +47 -0
  49. package/dist/mdx.js +68 -0
  50. package/dist/og.d.ts +18 -0
  51. package/dist/og.js +50 -0
  52. package/dist/rehype.d.ts +18 -0
  53. package/dist/rehype.js +41 -0
  54. package/dist/seo.d.ts +174 -0
  55. package/dist/seo.js +152 -0
  56. package/dist/typography/as.d.ts +15 -0
  57. package/dist/typography/as.js +8 -0
  58. package/dist/typography/header.d.ts +44 -0
  59. package/dist/typography/header.js +119 -0
  60. package/dist/typography/highlight.d.ts +33 -0
  61. package/dist/typography/highlight.js +98 -0
  62. package/dist/typography/index.d.ts +4 -0
  63. package/dist/typography/index.js +3 -0
  64. package/dist/typography/paragraph.d.ts +157 -0
  65. package/dist/typography/paragraph.js +229 -0
  66. package/llms.txt +125 -0
  67. package/package.json +140 -0
  68. package/src/prose.css +12 -0
  69. package/src/shiki.css +23 -0
  70. package/src/theme.css +272 -0
  71. package/src/tokens.css +43 -0
  72. package/src/type.css +73 -0
@@ -0,0 +1,157 @@
1
+ import { type VariantProps } from "class-variance-authority";
2
+ import type { ComponentProps, ReactNode } from "react";
3
+ import { type WithAs } from "./as.js";
4
+ /** The body layer: two axes and no more. A paragraph picks a rung and an ink; a
5
+ * caption is always secondary ink and picks a size. `lead` was a third rung a
6
+ * breakpoint away from `prose`, and its standfirst role is now the eyebrow's. */
7
+ declare const pVariants: (props?: ({
8
+ variant?: "ui" | "prose" | null | undefined;
9
+ tone?: "default" | "muted" | null | undefined;
10
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
11
+ export type ParagraphVariants = VariantProps<typeof pVariants>;
12
+ export declare function TypographyP({ className, variant, tone, children, ...props }: ComponentProps<"p"> & ParagraphVariants): import("react").JSX.Element;
13
+ /** A preset's props: its base's, minus the axes it has decided. `keyof Pins` reads
14
+ * the exclusion off the pinned object the preset also spreads, so the two cannot
15
+ * drift — `<TypographyMuted tone="default">` used to compile and un-mute it. */
16
+ type Preset<Base, Pins> = Omit<Base, keyof Pins>;
17
+ type ParagraphProps = ComponentProps<"p"> & ParagraphVariants;
18
+ /** The UI rung in the secondary ink. */
19
+ declare const MUTED: {
20
+ readonly tone: "muted";
21
+ };
22
+ export declare function TypographyMuted(props: Preset<ParagraphProps, typeof MUTED>): import("react").JSX.Element;
23
+ /** Reading-size body copy. `TypographyMuted` is the same ink one rung down. */
24
+ declare const PROSE: {
25
+ readonly variant: "prose";
26
+ readonly tone: "muted";
27
+ };
28
+ export declare function TypographyProse(props: Preset<ParagraphProps, typeof PROSE>): import("react").JSX.Element;
29
+ export type ListProps = ComponentProps<"ul"> & Pick<ParagraphVariants, "variant"> & {
30
+ ordered?: boolean;
31
+ };
32
+ export declare function TypographyList({ className, children, ordered, variant, ...props }: ListProps): import("react").JSX.Element;
33
+ /**
34
+ * The reading rung, pinned. The name predates the axis and keeps every call site
35
+ * that already had it; `Preset` stops one re-opening the rung it names.
36
+ */
37
+ declare const PROSE_LIST: {
38
+ readonly variant: "prose";
39
+ };
40
+ export declare function TypographyProseList(props: Preset<ListProps, typeof PROSE_LIST>): import("react").JSX.Element;
41
+ /**
42
+ * Meta beside content: timestamps, counts, bylines, the key in a key-value row.
43
+ *
44
+ * Always the secondary ink and never a weight — a caption is secondary because
45
+ * it is muted, and 500 on top would have the colour and the weight arguing.
46
+ * `sm` is the default because meta is separated from body by ink, not size;
47
+ * the smaller rungs are a deliberate step down, not the norm.
48
+ *
49
+ * Leading is pinned per size rather than left to the rung. Plenty of captions
50
+ * are a wrapped sentence, and the ramp's tight setting sets those cramped —
51
+ * descenders nearly on the caps below. `leading-normal` writes `--tw-leading`,
52
+ * the variable the `text-*` step reads, so 1.5 wins at every rung.
53
+ *
54
+ * `inherit` is the parenthetical inside a heading, an eyebrow or a stat. It
55
+ * takes the size of whatever set it and resets the weight, because the only
56
+ * reason to sit there is to be quieter than the thing you qualify — and every
57
+ * container that sets a size for you sets a weight too.
58
+ */
59
+ declare const captionVariants: (props?: ({
60
+ size?: "inherit" | "sm" | "xs" | "2xs" | null | undefined;
61
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
62
+ export type CaptionVariants = VariantProps<typeof captionVariants>;
63
+ /**
64
+ * `as` covers the one thing that genuinely differs between call sites: whether
65
+ * the run is inline beside its subject or a block under it.
66
+ */
67
+ export declare function TypographyCaption({ className, size, as, children, ...props }: WithAs<CaptionVariants>): import("react").JSX.Element;
68
+ /**
69
+ * Small print set as a block: a note under the thing it annotates, rather than
70
+ * an aside inline with it. Same rung and same ink as the caption — small print
71
+ * is small because it is muted, and dropping it a rung as well is what made
72
+ * both apps hand-roll their own.
73
+ */
74
+ declare const BLOCK: {
75
+ readonly as: "p";
76
+ };
77
+ export declare function TypographySmall(props: Preset<WithAs<CaptionVariants>, typeof BLOCK>): import("react").JSX.Element;
78
+ /**
79
+ * The label role: a form label, a column header, the key a reader scans for.
80
+ * 500 is the only weight bump a dense surface needs below a heading.
81
+ *
82
+ * The rungs are the caption's, deliberately. A label and a caption are one pair
83
+ * — the key and the value, the name and the note — and a pair that cannot be set
84
+ * at one size is not a pair. Pinning the label to `sm` while the caption had an
85
+ * axis is what sent a key next to an `xs` value out to `font-medium text-xs` in
86
+ * a className, leaving the weight arguing with the rung it landed on.
87
+ *
88
+ * `as` is here for the same reason it is on `TypographyEyebrow`: a config panel
89
+ * names its sections at this size, and those names are the page's outline.
90
+ */
91
+ declare const labelVariants: (props?: ({
92
+ size?: "inherit" | "sm" | "xs" | "2xs" | null | undefined;
93
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
94
+ export type LabelVariants = VariantProps<typeof labelVariants>;
95
+ export declare function TypographyLabel({ className, size, as, children, ...props }: WithAs<LabelVariants>): import("react").JSX.Element;
96
+ /**
97
+ * A numeric readout. Size and colour ride in via className per use.
98
+ *
99
+ * Tabular is right in a column and wrong in a headline, so it is an axis rather
100
+ * than a constant. Keep `tabular` anywhere a value updates in place.
101
+ */
102
+ declare const statVariants: (props?: ({
103
+ size?: "inherit" | "display" | "card" | "panel" | "page" | null | undefined;
104
+ figures?: "tabular" | "proportional" | null | undefined;
105
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
106
+ export type StatVariants = VariantProps<typeof statVariants>;
107
+ export declare function TypographyStat({ className, size, figures, children, ...props }: ComponentProps<"span"> & StatVariants): import("react").JSX.Element;
108
+ /**
109
+ * A run of code inside a sentence: a command, a field name, a trigger.
110
+ *
111
+ * Everything is in `em`, not a rung: the chip has to sit in whatever size the
112
+ * sentence around it is set at, and the ramp differs per surface. The 0.9 is an
113
+ * optical correction — the mono face carries a taller x-height than the sans.
114
+ */
115
+ export declare function TypographyInlineCode({ className, children, ...props }: ComponentProps<"code">): import("react").JSX.Element;
116
+ /**
117
+ * A statement about the surface, not the link: `foreground` inside a paragraph,
118
+ * `primary` when the link is the point of the line, `secondary` for a note
119
+ * beneath a hero where `primary` would compete with the CTA beside it.
120
+ */
121
+ declare const LINK_TONES: {
122
+ readonly foreground: "font-medium text-foreground";
123
+ readonly primary: "font-medium text-primary";
124
+ readonly secondary: "text-secondary-ink";
125
+ };
126
+ export type LinkTone = keyof typeof LINK_TONES;
127
+ type TypographyLinkProps = Omit<ComponentProps<"a">, "href"> & {
128
+ href: string;
129
+ children: ReactNode;
130
+ tone?: LinkTone;
131
+ /** Defaults on for an off-site link. Turn it off for one that starts a flow the reader should stay in. */
132
+ newTab?: boolean;
133
+ /**
134
+ * A trailing arrow, for a link that ends a sentence and leads somewhere. The
135
+ * glyph follows the href: `↗` when the link leaves the site, `→` when it does
136
+ * not. That is the convention, and it is not a call site's to get wrong.
137
+ */
138
+ addArrow?: boolean;
139
+ };
140
+ /**
141
+ * The inline link.
142
+ *
143
+ * Internal and external are decided from the href, never at the call site: an
144
+ * href with a scheme renders a plain anchor and, if it is http(s), opens away
145
+ * with `rel="noopener noreferrer"`; everything else routes through the router's
146
+ * Link. `newTab` is the one override, for an off-site href that starts a flow
147
+ * the reader should stay in. Call-site props apply last, so a passed
148
+ * `target`/`rel` still wins.
149
+ *
150
+ * The router is `next-view-transitions`, imported rather than injected. Every
151
+ * project on this package is a Next app and wants the same link, and a factory
152
+ * bought router-agnosticism nobody used at the price of a component that could
153
+ * not be imported by name. One call site ended up on the unbound version that
154
+ * way and lost its decoration.
155
+ */
156
+ export declare function TypographyLink({ href, children, tone, newTab, addArrow, className, ...props }: TypographyLinkProps): import("react").JSX.Element;
157
+ export {};
@@ -0,0 +1,229 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Link } from "next-view-transitions";
3
+ import { cva } from "class-variance-authority";
4
+ import { cn } from "../cn.js";
5
+ import { TextAs } from "./as.js";
6
+ /** The body layer: two axes and no more. A paragraph picks a rung and an ink; a
7
+ * caption is always secondary ink and picks a size. `lead` was a third rung a
8
+ * breakpoint away from `prose`, and its standfirst role is now the eyebrow's. */
9
+ const pVariants = cva("", {
10
+ variants: {
11
+ /**
12
+ * `ui` is interface copy. `prose` is the reading rung, stated here and
13
+ * nowhere else — it had drifted to five copies once already. `--text-lg`
14
+ * resolves larger on an editorial subtree, by design.
15
+ */
16
+ variant: {
17
+ ui: "text-sm",
18
+ prose: "text-pretty text-lg leading-relaxed",
19
+ },
20
+ tone: {
21
+ default: "text-foreground",
22
+ muted: "text-muted-foreground",
23
+ },
24
+ },
25
+ defaultVariants: { variant: "ui", tone: "default" },
26
+ });
27
+ export function TypographyP({ className, variant, tone, children, ...props }) {
28
+ return (_jsx("p", { className: cn(pVariants({ variant, tone }), className), ...props, children: children }));
29
+ }
30
+ /** The UI rung in the secondary ink. */
31
+ const MUTED = { tone: "muted" };
32
+ export function TypographyMuted(props) {
33
+ return _jsx(TypographyP, { ...props, ...MUTED });
34
+ }
35
+ /** Reading-size body copy. `TypographyMuted` is the same ink one rung down. */
36
+ const PROSE = { variant: "prose", tone: "muted" };
37
+ export function TypographyProse(props) {
38
+ return _jsx(TypographyP, { ...props, ...PROSE });
39
+ }
40
+ /**
41
+ * A list, on the same rung axis as the paragraph beside it.
42
+ *
43
+ * The rung composes `pVariants` rather than restating one, so a list cannot
44
+ * drift from the copy it sits under. The old `proseClass` held that property for
45
+ * the prose rung alone.
46
+ *
47
+ * `variant` is here because a list is not always reading copy. A tier card or a
48
+ * bento cell sets its paragraphs in `ui`, and a list pinned to `prose` inside one
49
+ * lands two rungs above the sentence introducing it, with no prop to say so. Call
50
+ * sites answered that by hand-rolling `<ul className="list-disc text-sm">`, which
51
+ * is the shape this replaces.
52
+ *
53
+ * Tone stays pinned to `muted`: a list is body copy, secondary for the same
54
+ * reason the paragraph presets are, and a weight-or-ink argument at the marker
55
+ * is not a thing a call site should be able to start.
56
+ */
57
+ const listClass = (variant, ordered) => cn("my-4 flex flex-col gap-1 pl-6 [&>li]:pl-1.5", ordered ? "list-decimal" : "list-disc", pVariants({ variant, tone: "muted" }));
58
+ export function TypographyList({ className, children, ordered, variant, ...props }) {
59
+ const List = ordered ? "ol" : "ul";
60
+ return (_jsx(List, { className: cn(listClass(variant, ordered), className), ...props, children: children }));
61
+ }
62
+ /**
63
+ * The reading rung, pinned. The name predates the axis and keeps every call site
64
+ * that already had it; `Preset` stops one re-opening the rung it names.
65
+ */
66
+ const PROSE_LIST = { variant: "prose" };
67
+ export function TypographyProseList(props) {
68
+ return _jsx(TypographyList, { ...props, ...PROSE_LIST });
69
+ }
70
+ /**
71
+ * Meta beside content: timestamps, counts, bylines, the key in a key-value row.
72
+ *
73
+ * Always the secondary ink and never a weight — a caption is secondary because
74
+ * it is muted, and 500 on top would have the colour and the weight arguing.
75
+ * `sm` is the default because meta is separated from body by ink, not size;
76
+ * the smaller rungs are a deliberate step down, not the norm.
77
+ *
78
+ * Leading is pinned per size rather than left to the rung. Plenty of captions
79
+ * are a wrapped sentence, and the ramp's tight setting sets those cramped —
80
+ * descenders nearly on the caps below. `leading-normal` writes `--tw-leading`,
81
+ * the variable the `text-*` step reads, so 1.5 wins at every rung.
82
+ *
83
+ * `inherit` is the parenthetical inside a heading, an eyebrow or a stat. It
84
+ * takes the size of whatever set it and resets the weight, because the only
85
+ * reason to sit there is to be quieter than the thing you qualify — and every
86
+ * container that sets a size for you sets a weight too.
87
+ */
88
+ const captionVariants = cva("text-muted-foreground", {
89
+ variants: {
90
+ size: {
91
+ sm: "text-sm leading-normal",
92
+ xs: "text-xs leading-normal",
93
+ "2xs": "text-2xs leading-normal",
94
+ inherit: "font-normal",
95
+ },
96
+ },
97
+ defaultVariants: { size: "sm" },
98
+ });
99
+ /**
100
+ * `as` covers the one thing that genuinely differs between call sites: whether
101
+ * the run is inline beside its subject or a block under it.
102
+ */
103
+ export function TypographyCaption({ className, size, as, children, ...props }) {
104
+ return (_jsx(TextAs, { as: as, className: cn(captionVariants({ size }), className), ...props, children: children }));
105
+ }
106
+ /**
107
+ * Small print set as a block: a note under the thing it annotates, rather than
108
+ * an aside inline with it. Same rung and same ink as the caption — small print
109
+ * is small because it is muted, and dropping it a rung as well is what made
110
+ * both apps hand-roll their own.
111
+ */
112
+ const BLOCK = { as: "p" };
113
+ export function TypographySmall(props) {
114
+ return _jsx(TypographyCaption, { ...props, ...BLOCK });
115
+ }
116
+ /**
117
+ * The label role: a form label, a column header, the key a reader scans for.
118
+ * 500 is the only weight bump a dense surface needs below a heading.
119
+ *
120
+ * The rungs are the caption's, deliberately. A label and a caption are one pair
121
+ * — the key and the value, the name and the note — and a pair that cannot be set
122
+ * at one size is not a pair. Pinning the label to `sm` while the caption had an
123
+ * axis is what sent a key next to an `xs` value out to `font-medium text-xs` in
124
+ * a className, leaving the weight arguing with the rung it landed on.
125
+ *
126
+ * `as` is here for the same reason it is on `TypographyEyebrow`: a config panel
127
+ * names its sections at this size, and those names are the page's outline.
128
+ */
129
+ const labelVariants = cva("font-medium text-foreground", {
130
+ variants: {
131
+ size: {
132
+ sm: "text-sm",
133
+ xs: "text-xs",
134
+ "2xs": "text-2xs",
135
+ /** Inside a heading or a chip, where the container has already set one. */
136
+ inherit: "",
137
+ },
138
+ },
139
+ defaultVariants: { size: "sm" },
140
+ });
141
+ export function TypographyLabel({ className, size, as, children, ...props }) {
142
+ return (_jsx(TextAs, { as: as, className: cn(labelVariants({ size }), className), ...props, children: children }));
143
+ }
144
+ /**
145
+ * A numeric readout. Size and colour ride in via className per use.
146
+ *
147
+ * Tabular is right in a column and wrong in a headline, so it is an axis rather
148
+ * than a constant. Keep `tabular` anywhere a value updates in place.
149
+ */
150
+ const statVariants = cva("font-semibold tracking-tight", {
151
+ variants: {
152
+ /**
153
+ * Named rungs, because the ramp was reachable only by spelling a class. The
154
+ * three here are the ones call sites actually converged on: `display` is the
155
+ * figure a section is built around, `page` and `panel` ride the heading
156
+ * ladder so a stat and the heading beside it step together — and therefore
157
+ * retune together on an editorial surface, which a literal never would.
158
+ *
159
+ * `inherit` is the default and writes nothing: a stat inside a heading, a
160
+ * chip or a sentence takes the size that set it, and every existing call
161
+ * site keeps the size it passed.
162
+ */
163
+ size: {
164
+ inherit: "",
165
+ card: "text-h4",
166
+ panel: "text-h3",
167
+ page: "text-h1",
168
+ display: "text-6xl font-black",
169
+ },
170
+ figures: {
171
+ /** Even advances stop a value jittering as it refreshes. */
172
+ tabular: "tabular-nums",
173
+ /** A headline figure wants its drawn spacing: a lone 1 is not an 8. */
174
+ proportional: "proportional-nums",
175
+ },
176
+ },
177
+ defaultVariants: { size: "inherit", figures: "tabular" },
178
+ });
179
+ export function TypographyStat({ className, size, figures, children, ...props }) {
180
+ return (_jsx("span", { className: cn(statVariants({ size, figures }), className), ...props, children: children }));
181
+ }
182
+ /**
183
+ * A run of code inside a sentence: a command, a field name, a trigger.
184
+ *
185
+ * Everything is in `em`, not a rung: the chip has to sit in whatever size the
186
+ * sentence around it is set at, and the ramp differs per surface. The 0.9 is an
187
+ * optical correction — the mono face carries a taller x-height than the sans.
188
+ */
189
+ export function TypographyInlineCode({ className, children, ...props }) {
190
+ return (_jsx("code", { className: cn("rounded-[3px] bg-foreground/[0.03] px-[0.3em] py-[0.1em] font-mono text-[0.9em] text-secondary-ink", className), ...props, children: children }));
191
+ }
192
+ /**
193
+ * A statement about the surface, not the link: `foreground` inside a paragraph,
194
+ * `primary` when the link is the point of the line, `secondary` for a note
195
+ * beneath a hero where `primary` would compete with the CTA beside it.
196
+ */
197
+ const LINK_TONES = {
198
+ foreground: "font-medium text-foreground",
199
+ primary: "font-medium text-primary",
200
+ secondary: "text-secondary-ink",
201
+ };
202
+ const LINK_DECORATION = "underline decoration-dotted decoration-1 decoration-muted-foreground decoration-skip-ink-none underline-offset-2 hover:decoration-solid hover:decoration-current/70";
203
+ const linkClass = (tone = "foreground", className) => cn(LINK_TONES[tone], LINK_DECORATION, className);
204
+ /**
205
+ * The inline link.
206
+ *
207
+ * Internal and external are decided from the href, never at the call site: an
208
+ * href with a scheme renders a plain anchor and, if it is http(s), opens away
209
+ * with `rel="noopener noreferrer"`; everything else routes through the router's
210
+ * Link. `newTab` is the one override, for an off-site href that starts a flow
211
+ * the reader should stay in. Call-site props apply last, so a passed
212
+ * `target`/`rel` still wins.
213
+ *
214
+ * The router is `next-view-transitions`, imported rather than injected. Every
215
+ * project on this package is a Next app and wants the same link, and a factory
216
+ * bought router-agnosticism nobody used at the price of a component that could
217
+ * not be imported by name. One call site ended up on the unbound version that
218
+ * way and lost its decoration.
219
+ */
220
+ export function TypographyLink({ href, children, tone = "foreground", newTab, addArrow, className, ...props }) {
221
+ const style = linkClass(tone, className);
222
+ const external = /^[a-z][a-z0-9+.-]*:/i.test(href);
223
+ const body = (_jsxs(_Fragment, { children: [children, addArrow && (_jsx("svg", { "aria-hidden": "true", className: "ml-1 inline size-3.5 align-middle", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: external ? "M7 17 17 7M7 7h10v10" : "M5 12h14M12 5l7 7-7 7" }) }))] }));
224
+ if (external) {
225
+ const away = newTab ?? href.startsWith("http");
226
+ return (_jsx("a", { href: href, className: style, ...(away ? { target: "_blank", rel: "noopener noreferrer" } : {}), ...props, children: body }));
227
+ }
228
+ return (_jsx(Link, { href: href, className: style, ...props, children: body }));
229
+ }
package/llms.txt ADDED
@@ -0,0 +1,125 @@
1
+ # @supertype.ai/foundations
2
+
3
+ The shared design layer for Supertype's Next apps: typography primitives,
4
+ content blocks, a long-form essay shell, token and theme CSS, and build-time
5
+ tooling for SEO, OG cards, lint rules and contrast checks.
6
+
7
+ This file is for coding agents working in an app that consumes the package. It
8
+ covers what to import and the mistakes that do not produce an error. Full
9
+ reference: https://github.com/supertypeai/foundations
10
+
11
+ ## Rules
12
+
13
+ 1. **Never write type styles by hand.** If you are about to write `text-sm`,
14
+ `text-xs`, `font-medium`, `leading-tight`, `text-muted-foreground` or similar
15
+ on a text element, there is a primitive for it. Use that instead.
16
+ 2. **Retune with CSS variables, not classes.** The package owns its classnames.
17
+ Change a `--text-*` rung, `--heading-weight` or a colour token. Do not
18
+ override the package's utilities.
19
+ 3. **Import from the right entry point.** `blocks`, `essay`, `mdx`, `seo`, `og`,
20
+ `eslint`, `rehype` and `contrast` are subpaths, not part of the root barrel.
21
+ 4. **Use colour tokens, never literal colours.** `bg-card`, `text-foreground`,
22
+ `border-border`. No hex values, no `bg-zinc-800`, no `dark:` overrides that
23
+ swap one token for another.
24
+
25
+ ## Which component
26
+
27
+ | you want | use | from |
28
+ |---|---|---|
29
+ | a page title | `TypographyH1` (add `variant="display"` on marketing pages) | root |
30
+ | a section heading | `TypographyH2` (add `divider` for a rule under it) | root |
31
+ | a subheading | `TypographyH3`, `TypographyH4` | root |
32
+ | a kicker above a heading | `TypographyEyebrow` | root |
33
+ | interface copy, 13px | `TypographyP` | root |
34
+ | the same, secondary ink | `TypographyMuted` | root |
35
+ | reading copy, 18px | `TypographyProse` | root |
36
+ | a bulleted or numbered list | `TypographyList`, `TypographyProseList` | root |
37
+ | a timestamp, count or value | `TypographyCaption` | root |
38
+ | a form label or column header | `TypographyLabel` | root |
39
+ | small print as a block | `TypographySmall` | root |
40
+ | a number or metric | `TypographyStat` | root |
41
+ | inline code | `TypographyInlineCode` | root |
42
+ | a link | `TypographyLink` | root |
43
+ | a highlighted phrase | `TypographyHighlight` | root |
44
+ | a link card, or a grid of them | `Card`, `Cards` | `/blocks` |
45
+ | an aside, warning or note | `Callout` | `/blocks` |
46
+ | numbered instructions | `Steps`, `Step` | `/blocks` |
47
+ | an FAQ or collapsible, no JS | `Disclosure`, `DisclosureGroup` | `/blocks` |
48
+ | an animated accordion | `Accordion` (client) | `/blocks` |
49
+ | tabbed content | `Tabs` or `TabGroup` (client) | `/blocks` |
50
+ | a long-form article page | `EssayHeader` + `EssayLayout` + `EssaySection` | `/essay` |
51
+ | a post meta row (date, read time, tags) | `PostMetaRow` and friends | `/essay` |
52
+ | a table of contents | `TableOfContents`, `ReadingRail` | `/essay` |
53
+ | page metadata and JSON-LD | `createSeo` | `/seo` |
54
+ | an OG image | `ogCard`, `OG_SIZE` | `/og` |
55
+ | to merge classnames | `cn` | root |
56
+
57
+ ## Entry points
58
+
59
+ | import | exports |
60
+ |---|---|
61
+ | `@supertype.ai/foundations` | `cn`, `TypographyH1`, `TypographyH2`, `TypographyH3`, `TypographyH4`, `TypographyEyebrow`, `TypographyP`, `TypographyMuted`, `TypographyProse`, `TypographyList`, `TypographyProseList`, `TypographyCaption`, `TypographySmall`, `TypographyLabel`, `TypographyStat`, `TypographyInlineCode`, `TypographyLink`, `TypographyHighlight`, `headingClass`, `eyebrowClass`. Types: `TypographyTag`, `ParagraphVariants`, `ListProps`, `CaptionVariants`, `LabelVariants`, `StatVariants`, `LinkTone`, `HighlightTone` |
62
+ | `@supertype.ai/foundations/blocks` | `Cards`, `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `Callout`, `Steps`, `Step`, `Disclosure`, `DisclosureGroup`, `Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent`, `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`, `TabGroup`, `Tab`, `SEGMENT` |
63
+ | `@supertype.ai/foundations/mdx` | `proseMdxComponents` |
64
+ | `@supertype.ai/foundations/essay` | `createEssay`, `EssayHeader`, `EssayLayout`, `EssaySection`, `EssayPullQuote`, `EssayFigure`, `EssayMovements`, `EssayDocument`, `EssayColumns`, `TableOfContents`, `ReadingRail`, `ReadingProgressBar`, `Rail`, `RailLink`, `PostMetaRow`, `PostDate`, `ReadTime`, `TagPills`, `MetaDot`, `formatPostDate`, `extractHeadings`, `readingTime`, `createSlugger`, `useReadingProgress`, `useScrollSpy`. Types: `TocHeading`, `EssayDecorations`, `EssayIndexEntry`, `EssayDocSection`, `EssayMovement`, `PostDateFormat` |
65
+ | `@supertype.ai/foundations/seo` | `createSeo`. Types: `SeoConfig`, `ArticleAuthor`, `ArticleOptions`, `PageMetadata` |
66
+ | `@supertype.ai/foundations/og` | `ogCard`, `OG_SIZE`. Types: `OgCardOptions` |
67
+ | `@supertype.ai/foundations/eslint` | `designConfig` (the whole set as one flat-config entry), plus `colourRules`, `typographyRules`, `themeOverrideRules`, `surfaceAsInkRules`, `renamedTokenRules` for building your own. Types: `FlatConfigEntry`, `DesignConfigOptions`, `RestrictedSyntax`, `ColourOptions`, `TypographyOptions` |
68
+ | `@supertype.ai/foundations/rehype` | `rehypeProseCode`, `proseCodeOptions`, `PROSE_LANGS`, `PROSE_THEMES`. Build-time only, must not resolve React |
69
+ | `@supertype.ai/foundations/contrast` | `checkLegibility` (inks at 4.5:1), `checkSignals` (fills at 3:1, tinted inks at 4.5:1, labels against their own fill), `resolveTokens`, `formatFailures`, `specificity`, `parseColor`, `luminance`, `contrast`. Types: `Rgb`, `Theme`, `LegibilityFailure`. Build-time only |
70
+
71
+ ## Props worth knowing
72
+
73
+ - `TypographyH1`, `TypographyH3`: `variant?: "default" | "display"`.
74
+ - `TypographyH2`: `variant?`, plus `divider?: boolean` for a rule underneath.
75
+ - `TypographyP`: `variant?: "ui" | "prose"` (default `ui`), `tone?: "default" | "muted"`.
76
+ - `TypographyMuted`, `TypographyProse`, `TypographyProseList` are presets. The
77
+ prop each one pins is removed from its type, so passing it will not compile.
78
+ - `TypographyCaption`, `TypographyLabel`, `TypographySmall`: `size?: "sm" | "xs" | "2xs" | "inherit"`, `as?`.
79
+ - `TypographyEyebrow`: `tone?: "heading" | "label"`, `as?`.
80
+ - `TypographyStat`: `size?: "inherit" | "card" | "panel" | "page" | "display"`, `figures?: "tabular" | "proportional"`. Keep tabular where a value updates in place.
81
+ - `TypographyLink`: `href` (required), `tone?: "foreground" | "primary" | "secondary"`, `addArrow?`, `newTab?`. Internal versus external is decided from the href.
82
+ - `TypographyHighlight`: `tone?: "primary" | "success" | "ochre" | "terracotta" | "sage" | "fig"`, `seed?: number`.
83
+ - `Card`: `href`, `title`, `description`, `icon`, `external`. An href makes the whole card a link.
84
+ - `Callout`: `tone?: "muted" | "destructive" | "warn" | "accent"`, `density?: "compact" | "editorial"`, `title`, `icon`, `action`.
85
+ - `DisclosureGroup`: `type?: "multiple" | "single"`, `defaultValue` (matches the title string).
86
+ - `TabsList`: `variant?: "default" | "line"`.
87
+
88
+ ## Common mistakes
89
+
90
+ - `<p className="text-sm text-muted-foreground">` — use `<TypographyMuted>`.
91
+ - `<h2 className="text-lg font-semibold">` — use `<TypographyH2>`, or
92
+ `headingClass()` if you must pass classes to someone else's component.
93
+ - Importing `Card` or `Callout` from `@supertype.ai/foundations`. They live in
94
+ `@supertype.ai/foundations/blocks`.
95
+ - `<TypographyMuted tone="default">` — a type error. Use `TypographyP`.
96
+ - Binding fonts with `font.className` instead of `font.variable`. The className
97
+ form sets `font-family` on the element and leaves the roles unresolved.
98
+ - Omitting `@source '../node_modules/@supertype.ai/foundations/dist/**/*.js'` from
99
+ the CSS entry. Tailwind then purges every class the package ships.
100
+ - Omitting `@import "@supertype.ai/foundations/theme.css"`. `tokens.css` names the
101
+ colour roles but holds no values, so the whole palette resolves to nothing.
102
+ - Using `Accordion` for a static FAQ. `Disclosure` needs no JavaScript.
103
+ - Adding a second `@custom-variant dark`. `tokens.css` already binds it.
104
+
105
+ Run `npx foundations doctor` in the app to check the last four.
106
+
107
+ ## Surfaces
108
+
109
+ Two classes change how everything renders, and both go on `<html>` or a subtree:
110
+
111
+ - `.dark` re-points every colour token. It is bound by `tokens.css`, not by the
112
+ OS setting.
113
+ - `.editorial` gives the heading role to the serif at weight 400 and retunes the
114
+ whole heading ladder against an 18px body, instead of the product's 13px.
115
+
116
+ ## Docs
117
+
118
+ Read from `node_modules`, so these are absolute:
119
+
120
+ - README: https://github.com/supertypeai/foundations#readme
121
+ - Typography: https://github.com/supertypeai/foundations/blob/main/docs/typography.md
122
+ - Blocks and MDX: https://github.com/supertypeai/foundations/blob/main/docs/blocks.md
123
+ - The essay shell: https://github.com/supertypeai/foundations/blob/main/docs/essay.md
124
+ - Build-time tooling: https://github.com/supertypeai/foundations/blob/main/docs/tooling.md
125
+ - The CLI: https://github.com/supertypeai/foundations/blob/main/docs/cli.md
package/package.json ADDED
@@ -0,0 +1,140 @@
1
+ {
2
+ "name": "@supertype.ai/foundations",
3
+ "version": "0.1.24",
4
+ "license": "MIT",
5
+ "publishConfig": {
6
+ "access": "public",
7
+ "registry": "https://registry.npmjs.org/"
8
+ },
9
+ "description": "Shared typography, prose, essay, and building blocks for Supertype projects.",
10
+ "keywords": [
11
+ "design-system",
12
+ "design-tokens",
13
+ "typography",
14
+ "prose",
15
+ "essay",
16
+ "mdx",
17
+ "react",
18
+ "nextjs",
19
+ "tailwind",
20
+ "css",
21
+ "accessibility",
22
+ "contrast",
23
+ "eslint-plugin"
24
+ ],
25
+ "type": "module",
26
+ "sideEffects": [
27
+ "*.css"
28
+ ],
29
+ "files": [
30
+ "bin",
31
+ "dist",
32
+ "llms.txt",
33
+ "src/*.css",
34
+ "LICENSE"
35
+ ],
36
+ "bin": {
37
+ "foundations": "./bin/foundations.mjs"
38
+ },
39
+ "exports": {
40
+ ".": {
41
+ "types": "./dist/index.d.ts",
42
+ "default": "./dist/index.js"
43
+ },
44
+ "./blocks": {
45
+ "types": "./dist/blocks/index.d.ts",
46
+ "default": "./dist/blocks/index.js"
47
+ },
48
+ "./mdx": {
49
+ "types": "./dist/mdx.d.ts",
50
+ "default": "./dist/mdx.js"
51
+ },
52
+ "./essay": {
53
+ "types": "./dist/essay/index.d.ts",
54
+ "default": "./dist/essay/index.js"
55
+ },
56
+ "./seo": {
57
+ "types": "./dist/seo.d.ts",
58
+ "default": "./dist/seo.js"
59
+ },
60
+ "./contrast": {
61
+ "types": "./dist/contrast.d.ts",
62
+ "default": "./dist/contrast.js"
63
+ },
64
+ "./eslint": {
65
+ "types": "./dist/eslint.d.ts",
66
+ "require": "./dist/cjs/eslint.js",
67
+ "default": "./dist/eslint.js"
68
+ },
69
+ "./rehype": {
70
+ "types": "./dist/rehype.d.ts",
71
+ "default": "./dist/rehype.js"
72
+ },
73
+ "./tokens.css": "./src/tokens.css",
74
+ "./theme.css": "./src/theme.css",
75
+ "./type.css": "./src/type.css",
76
+ "./prose.css": "./src/prose.css",
77
+ "./shiki.css": "./src/shiki.css",
78
+ "./og": {
79
+ "types": "./dist/og.d.ts",
80
+ "default": "./dist/og.js"
81
+ }
82
+ },
83
+ "scripts": {
84
+ "build": "tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && node scripts/cjs-marker.mjs && node scripts/check-llms.mjs && node scripts/pins.mjs",
85
+ "dev": "tsc -p tsconfig.json --watch",
86
+ "test": "yarn build && vitest run",
87
+ "test:watch": "vitest",
88
+ "check:llms": "node scripts/check-llms.mjs",
89
+ "check:pins": "node scripts/pins.mjs",
90
+ "release": "node scripts/release.mjs",
91
+ "sync": "node scripts/sync.mjs",
92
+ "dev:sync": "node scripts/sync.mjs --watch",
93
+ "example": "node scripts/sync.mjs examples/site && yarn --cwd examples/site dev",
94
+ "example:install": "yarn --cwd examples/site install",
95
+ "example:build": "node scripts/sync.mjs examples/site && yarn --cwd examples/site build",
96
+ "example:pinned": "node scripts/pinned.mjs",
97
+ "example:static": "node scripts/sync.mjs examples/site && yarn --cwd examples/site build:static",
98
+ "prepublishOnly": "yarn build"
99
+ },
100
+ "dependencies": {
101
+ "@shikijs/rehype": "^2.3.2",
102
+ "class-variance-authority": "^0.7.1",
103
+ "clsx": "^2.1.1",
104
+ "shiki": "^2.3.2",
105
+ "tailwind-merge": "^3.0.0"
106
+ },
107
+ "peerDependencies": {
108
+ "@base-ui/react": ">=1.4",
109
+ "next": ">=15",
110
+ "next-view-transitions": ">=0.3",
111
+ "react": ">=19"
112
+ },
113
+ "devDependencies": {
114
+ "@base-ui/react": "^1.4.1",
115
+ "@types/react": "^19.0.0",
116
+ "next": "^15.5.7",
117
+ "next-view-transitions": "^0.3.5",
118
+ "react": "^19.0.0",
119
+ "typescript": "^5.7.0",
120
+ "vitest": "4.1.11"
121
+ },
122
+ "author": {
123
+ "name": "Samuel Chan",
124
+ "url": "https://supertype.ai/p/samuel"
125
+ },
126
+ "repository": {
127
+ "type": "git",
128
+ "url": "git+https://github.com/supertypeai/foundations.git"
129
+ },
130
+ "homepage": "https://github.com/supertypeai/foundations#readme",
131
+ "contributors": [
132
+ {
133
+ "name": "Supertype Pte. Ltd.",
134
+ "url": "https://supertype.ai"
135
+ }
136
+ ],
137
+ "bugs": {
138
+ "url": "https://github.com/supertypeai/foundations/issues"
139
+ }
140
+ }
package/src/prose.css ADDED
@@ -0,0 +1,12 @@
1
+ /* `code` fires for both an inline span and the <code> in a fence, and the
2
+ `language-*` class that tells them apart does not survive a host swapping in
3
+ its own code block. A child combinator knows what the component cannot. */
4
+
5
+ :not(pre) > code {
6
+ border-radius: 0.25rem;
7
+ background-color: var(--muted);
8
+ padding: 0.125rem 0.375rem;
9
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
10
+ font-size: 0.85em;
11
+ color: var(--foreground);
12
+ }