@supertype.ai/foundations 0.1.28 → 0.1.30
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/README.md +27 -23
- package/bin/foundations.mjs +5 -2
- package/dist/blocks/accordion.js +2 -1
- package/dist/blocks/badge.d.ts +27 -0
- package/dist/blocks/badge.js +86 -0
- package/dist/blocks/button.d.ts +32 -0
- package/dist/blocks/button.js +127 -0
- package/dist/blocks/callout.d.ts +2 -39
- package/dist/blocks/callout.js +13 -38
- package/dist/blocks/card.d.ts +5 -6
- package/dist/blocks/card.js +28 -12
- package/dist/blocks/focus.d.ts +9 -0
- package/dist/blocks/focus.js +9 -0
- package/dist/blocks/index.d.ts +3 -1
- package/dist/blocks/index.js +3 -1
- package/dist/blocks/interactive-accordion.js +2 -1
- package/dist/blocks/render-as.d.ts +11 -0
- package/dist/blocks/render-as.js +22 -0
- package/dist/blocks/segment.d.ts +22 -14
- package/dist/blocks/segment.js +23 -14
- package/dist/blocks/tabs.d.ts +52 -17
- package/dist/blocks/tabs.js +86 -37
- package/dist/cjs/eslint.js +49 -14
- package/dist/contrast.d.ts +56 -0
- package/dist/contrast.js +144 -12
- package/dist/eslint.d.ts +42 -8
- package/dist/eslint.js +47 -14
- package/dist/essay/essay.js +14 -6
- package/dist/essay/index.d.ts +1 -1
- package/dist/essay/index.js +1 -1
- package/dist/essay/layout.d.ts +37 -1
- package/dist/essay/layout.js +33 -1
- package/dist/essay/rail.d.ts +11 -1
- package/dist/essay/reading.d.ts +1 -1
- package/dist/essay/scroll.js +1 -1
- package/dist/href.d.ts +42 -0
- package/dist/href.js +63 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +9 -0
- package/dist/mdx.d.ts +26 -4
- package/dist/mdx.js +30 -4
- package/dist/tone.d.ts +130 -0
- package/dist/tone.js +140 -0
- package/dist/typography/header.d.ts +32 -2
- package/dist/typography/header.js +10 -5
- package/dist/typography/highlight.d.ts +7 -6
- package/dist/typography/highlight.js +15 -9
- package/dist/typography/paragraph.d.ts +12 -25
- package/dist/typography/paragraph.js +40 -29
- package/llms.txt +99 -22
- package/package.json +2 -1
- package/src/theme.css +143 -46
- package/src/tokens.css +5 -4
- package/src/type.css +1 -1
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
import { type VariantProps } from "class-variance-authority";
|
|
2
2
|
import { type WithAs } from "./as.js";
|
|
3
|
+
/**
|
|
4
|
+
* The heading ladder. Four levels, one rung each.
|
|
5
|
+
*
|
|
6
|
+
* A heading does not pick its size — `--text-h1`…`--text-h4` in type.css do, and
|
|
7
|
+
* `.editorial` retunes all four together. That is the whole design: size is a
|
|
8
|
+
* property of the SURFACE, and level is the only thing a call site knows. When
|
|
9
|
+
* the rungs were shared with body copy the call site had to know both, which is
|
|
10
|
+
* how `larger` and `entry` appeared — variants whose entire job was to climb out
|
|
11
|
+
* of a rung that read fine in the product and landed under the paragraph on a
|
|
12
|
+
* marketing page. Retuning a surface now means editing two lines of CSS.
|
|
13
|
+
*
|
|
14
|
+
* `display` is a role rather than a size: the landing-page heading that has to
|
|
15
|
+
* outrank the same level in the docs. Size is all it changes — the slant it once
|
|
16
|
+
* carried turned out to be unwanted on every surface.
|
|
17
|
+
*
|
|
18
|
+
* Tailwind scans comments — never spell a class out here or it becomes a real
|
|
19
|
+
* utility.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* The heading face, stated once. Anything wearing it composes this rather than
|
|
23
|
+
* respelling it: a second literal here is a level that forked, and a literal
|
|
24
|
+
* weight beside the face survives into `.editorial` and synthesises the
|
|
25
|
+
* single-weight serif. viably asserts there is exactly one of these strings.
|
|
26
|
+
*
|
|
27
|
+
* Exported so a non-heading can ask for the face alone. `headingClass()` below is the whole
|
|
28
|
+
* h2 ramp — face, anchor offset, margin reset, rung — and is right only for something that
|
|
29
|
+
* IS a heading wearing another tag. A pull quote is not, and took the ramp anyway because
|
|
30
|
+
* the face had no door, arriving with a `scroll-m-20` for an anchor it does not have.
|
|
31
|
+
*/
|
|
32
|
+
export declare const headingFace = "font-heading font-[number:var(--heading-weight)]";
|
|
3
33
|
declare const h1Variants: (props?: ({
|
|
4
34
|
variant?: "default" | "display" | null | undefined;
|
|
5
35
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
@@ -29,8 +59,8 @@ declare const eyebrowVariants: (props?: ({
|
|
|
29
59
|
/**
|
|
30
60
|
* The eyebrow's ramp as a class, for a caller that cannot render our element —
|
|
31
61
|
* a dialog title primitive, a motion element. Same escape hatch as
|
|
32
|
-
* `headingClass
|
|
33
|
-
* hand-
|
|
62
|
+
* `headingClass`. It exists so a consumer needing the class can take ours rather
|
|
63
|
+
* than hand-rolling a copy that drifts from the component.
|
|
34
64
|
*/
|
|
35
65
|
export declare const eyebrowClass: (tone?: VariantProps<typeof eyebrowVariants>["tone"]) => string;
|
|
36
66
|
/**
|
|
@@ -25,9 +25,14 @@ import { TextAs } from "./as.js";
|
|
|
25
25
|
* respelling it: a second literal here is a level that forked, and a literal
|
|
26
26
|
* weight beside the face survives into `.editorial` and synthesises the
|
|
27
27
|
* single-weight serif. viably asserts there is exactly one of these strings.
|
|
28
|
+
*
|
|
29
|
+
* Exported so a non-heading can ask for the face alone. `headingClass()` below is the whole
|
|
30
|
+
* h2 ramp — face, anchor offset, margin reset, rung — and is right only for something that
|
|
31
|
+
* IS a heading wearing another tag. A pull quote is not, and took the ramp anyway because
|
|
32
|
+
* the face had no door, arriving with a `scroll-m-20` for an anchor it does not have.
|
|
28
33
|
*/
|
|
29
|
-
const
|
|
30
|
-
const HEADING_BASE = `scroll-m-20 ${
|
|
34
|
+
export const headingFace = "font-heading font-[number:var(--heading-weight)]";
|
|
35
|
+
const HEADING_BASE = `scroll-m-20 ${headingFace} text-[color:var(--ink,var(--foreground))]`;
|
|
31
36
|
const h1Variants = cva(`${HEADING_BASE} tracking-tight`, {
|
|
32
37
|
variants: {
|
|
33
38
|
variant: {
|
|
@@ -93,7 +98,7 @@ const eyebrowVariants = cva("block uppercase tracking-wider", {
|
|
|
93
98
|
* Primary ink, stated not inherited — an eyebrow names the section under it,
|
|
94
99
|
* and one that turns red from its surroundings is not a heading.
|
|
95
100
|
*/
|
|
96
|
-
heading: "text-xs font-semibold text-foreground",
|
|
101
|
+
heading: "text-xs font-semibold text-[color:var(--ink,var(--foreground))]",
|
|
97
102
|
/** Stat cards invert it: the figure is the headline, so the label yields. */
|
|
98
103
|
label: "text-2xs font-medium text-accent-foreground",
|
|
99
104
|
},
|
|
@@ -103,8 +108,8 @@ const eyebrowVariants = cva("block uppercase tracking-wider", {
|
|
|
103
108
|
/**
|
|
104
109
|
* The eyebrow's ramp as a class, for a caller that cannot render our element —
|
|
105
110
|
* a dialog title primitive, a motion element. Same escape hatch as
|
|
106
|
-
* `headingClass
|
|
107
|
-
* hand-
|
|
111
|
+
* `headingClass`. It exists so a consumer needing the class can take ours rather
|
|
112
|
+
* than hand-rolling a copy that drifts from the component.
|
|
108
113
|
*/
|
|
109
114
|
export const eyebrowClass = (tone) => eyebrowVariants({ tone });
|
|
110
115
|
/**
|
|
@@ -5,8 +5,7 @@ import type { ComponentProps } from "react";
|
|
|
5
5
|
* hue for nothing. That is why the earth tones enter as `-foreground`: those are
|
|
6
6
|
* the ink-grade pair, mixed to hold at text weight in both themes.
|
|
7
7
|
*
|
|
8
|
-
* Emphasis, never status. Warn and info and destructive are absent on purpose
|
|
9
|
-
* a swipe of red under a phrase says less than the words do.
|
|
8
|
+
* Emphasis, never status. Warn and info and destructive are absent on purpose.
|
|
10
9
|
*
|
|
11
10
|
* The four earth tones are one palette, not a menu: each is a different hue,
|
|
12
11
|
* because two a reader cannot tell apart are one tone with two names.
|
|
@@ -14,16 +13,18 @@ import type { ComponentProps } from "react";
|
|
|
14
13
|
declare const MARKER_TONES: {
|
|
15
14
|
readonly primary: "var(--primary)";
|
|
16
15
|
readonly success: "var(--success)";
|
|
17
|
-
readonly ochre: "var(--ochre-
|
|
18
|
-
readonly terracotta: "var(--terracotta-
|
|
19
|
-
readonly sage: "var(--sage-
|
|
20
|
-
readonly fig: "var(--fig-
|
|
16
|
+
readonly ochre: "var(--ochre-ink)";
|
|
17
|
+
readonly terracotta: "var(--terracotta-ink)";
|
|
18
|
+
readonly sage: "var(--sage-ink)";
|
|
19
|
+
readonly fig: "var(--fig-ink)";
|
|
21
20
|
};
|
|
22
21
|
export type HighlightTone = keyof typeof MARKER_TONES;
|
|
23
22
|
/**
|
|
24
23
|
* Marker highlight for inline text. Painted as the run's own background, never a
|
|
25
24
|
* mask — a mask would shave the glyph tops. `luminosity` lets letters borrow the
|
|
26
25
|
* marker's hue while keeping their own lightness, so contrast holds in both themes.
|
|
26
|
+
* That lightness comes from `--marker-ink`, a deepened `--foreground`, so the run
|
|
27
|
+
* still reads as the emphasised one rather than sinking into its own wash.
|
|
27
28
|
*/
|
|
28
29
|
export declare function TypographyHighlight({ tone, seed, className, style, children, ...props }: ComponentProps<"span"> & {
|
|
29
30
|
tone?: HighlightTone;
|
|
@@ -6,8 +6,7 @@ import { cn } from "../cn.js";
|
|
|
6
6
|
* hue for nothing. That is why the earth tones enter as `-foreground`: those are
|
|
7
7
|
* the ink-grade pair, mixed to hold at text weight in both themes.
|
|
8
8
|
*
|
|
9
|
-
* Emphasis, never status. Warn and info and destructive are absent on purpose
|
|
10
|
-
* a swipe of red under a phrase says less than the words do.
|
|
9
|
+
* Emphasis, never status. Warn and info and destructive are absent on purpose.
|
|
11
10
|
*
|
|
12
11
|
* The four earth tones are one palette, not a menu: each is a different hue,
|
|
13
12
|
* because two a reader cannot tell apart are one tone with two names.
|
|
@@ -15,10 +14,10 @@ import { cn } from "../cn.js";
|
|
|
15
14
|
const MARKER_TONES = {
|
|
16
15
|
primary: "var(--primary)",
|
|
17
16
|
success: "var(--success)",
|
|
18
|
-
ochre: "var(--ochre-
|
|
19
|
-
terracotta: "var(--terracotta-
|
|
20
|
-
sage: "var(--sage-
|
|
21
|
-
fig: "var(--fig-
|
|
17
|
+
ochre: "var(--ochre-ink)",
|
|
18
|
+
terracotta: "var(--terracotta-ink)",
|
|
19
|
+
sage: "var(--sage-ink)",
|
|
20
|
+
fig: "var(--fig-ink)",
|
|
22
21
|
};
|
|
23
22
|
/** Wash density at a fraction of the theme's base alpha. */
|
|
24
23
|
const ink = (weight) => `color-mix(in srgb, var(--marker) calc(var(--marker-alpha) * ${weight}), transparent)`;
|
|
@@ -41,7 +40,8 @@ const DABS = [
|
|
|
41
40
|
// Integer-only: `Math.sin` is implementation-defined, so Node and the browser
|
|
42
41
|
// disagreed in the last bits and the swipe hydrated as a mismatch.
|
|
43
42
|
const hash = (seed, i) => {
|
|
44
|
-
let h = Math.imul(seed ^ 0x9e3779b9, 0x85ebca6b) ^
|
|
43
|
+
let h = Math.imul(seed ^ 0x9e3779b9, 0x85ebca6b) ^
|
|
44
|
+
Math.imul(i + 0x165667b1, 0xc2b2ae35);
|
|
45
45
|
h ^= h >>> 15;
|
|
46
46
|
h = Math.imul(h, 0x2545f491);
|
|
47
47
|
h ^= h >>> 13;
|
|
@@ -88,11 +88,17 @@ const markerFill = (seed) => {
|
|
|
88
88
|
* Marker highlight for inline text. Painted as the run's own background, never a
|
|
89
89
|
* mask — a mask would shave the glyph tops. `luminosity` lets letters borrow the
|
|
90
90
|
* marker's hue while keeping their own lightness, so contrast holds in both themes.
|
|
91
|
+
* That lightness comes from `--marker-ink`, a deepened `--foreground`, so the run
|
|
92
|
+
* still reads as the emphasised one rather than sinking into its own wash.
|
|
91
93
|
*/
|
|
92
94
|
export function TypographyHighlight({ tone = "primary", seed = 3, className, style, children, ...props }) {
|
|
93
|
-
return (_jsx("span", { className: cn("isolate inline px-[0.3em] py-[0.06em]", "[-webkit-box-decoration-break:clone] [box-decoration-break:clone]", "[--marker-alpha:44%] dark:[--marker-alpha:58%]",
|
|
95
|
+
return (_jsx("span", { className: cn("isolate inline px-[0.3em] py-[0.06em]", "[-webkit-box-decoration-break:clone] [box-decoration-break:clone]", "[--marker-alpha:44%] dark:[--marker-alpha:58%]",
|
|
96
|
+
// The blend keeps a glyph's lightness and nothing else, so a run over the
|
|
97
|
+
// wash reads flatter than the same words beside it. Pushing the lightness
|
|
98
|
+
// past --foreground buys that punch back without touching the hue borrow.
|
|
99
|
+
"[--marker-ink:color-mix(in_oklab,var(--foreground)_85%,black)]", "dark:[--marker-ink:color-mix(in_oklab,var(--foreground)_88%,white)]", className), style: {
|
|
94
100
|
"--marker": MARKER_TONES[tone],
|
|
95
101
|
backgroundImage: markerFill(seed),
|
|
96
102
|
...style,
|
|
97
|
-
}, ...props, children: _jsx("span", { className: "[mix-blend-mode:luminosity]", children: children }) }));
|
|
103
|
+
}, ...props, children: _jsx("span", { className: "text-(color:--marker-ink) [mix-blend-mode:luminosity]", children: children }) }));
|
|
98
104
|
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { type LinkBehavior } from "../href.js";
|
|
1
2
|
import { type VariantProps } from "class-variance-authority";
|
|
2
3
|
import type { ComponentProps, ReactNode } from "react";
|
|
4
|
+
import { type Tone } from "../tone.js";
|
|
3
5
|
import { type WithAs } from "./as.js";
|
|
4
6
|
/** The body layer: two axes and no more. A paragraph picks a rung and an ink; a
|
|
5
7
|
* caption is always secondary ink and picks a size. `lead` was a third rung a
|
|
6
8
|
* breakpoint away from `prose`, and its standfirst role is now the eyebrow's. */
|
|
7
9
|
declare const pVariants: (props?: ({
|
|
8
10
|
variant?: "ui" | "prose" | null | undefined;
|
|
9
|
-
tone?: "
|
|
11
|
+
tone?: "muted" | "default" | null | undefined;
|
|
10
12
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
11
13
|
export type ParagraphVariants = VariantProps<typeof pVariants>;
|
|
12
14
|
export declare function TypographyP({ className, variant, tone, children, ...props }: ComponentProps<"p"> & ParagraphVariants): import("react").JSX.Element;
|
|
@@ -100,7 +102,7 @@ export declare function TypographyLabel({ className, size, as, children, ...prop
|
|
|
100
102
|
* than a constant. Keep `tabular` anywhere a value updates in place.
|
|
101
103
|
*/
|
|
102
104
|
declare const statVariants: (props?: ({
|
|
103
|
-
size?: "inherit" | "
|
|
105
|
+
size?: "inherit" | "page" | "display" | "card" | "panel" | null | undefined;
|
|
104
106
|
figures?: "tabular" | "proportional" | null | undefined;
|
|
105
107
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
106
108
|
export type StatVariants = VariantProps<typeof statVariants>;
|
|
@@ -113,39 +115,24 @@ export declare function TypographyStat({ className, size, figures, children, ...
|
|
|
113
115
|
* optical correction — the mono face carries a taller x-height than the sans.
|
|
114
116
|
*/
|
|
115
117
|
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"> & {
|
|
118
|
+
type TypographyLinkProps = Omit<ComponentProps<"a">, "href"> & LinkBehavior & {
|
|
128
119
|
href: string;
|
|
129
120
|
children: ReactNode;
|
|
130
|
-
tone?:
|
|
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;
|
|
121
|
+
tone?: Tone;
|
|
133
122
|
/**
|
|
134
123
|
* A trailing arrow, for a link that ends a sentence and leads somewhere. The
|
|
135
124
|
* glyph follows the href: `↗` when the link leaves the site, `→` when it does
|
|
136
|
-
* not.
|
|
125
|
+
* not. The href picks it, so a call site never has to.
|
|
137
126
|
*/
|
|
138
127
|
addArrow?: boolean;
|
|
139
128
|
};
|
|
140
129
|
/**
|
|
141
130
|
* The inline link.
|
|
142
131
|
*
|
|
143
|
-
* Internal and external are decided from the href, never at the call site
|
|
144
|
-
* href
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
* the reader should stay in. Call-site props apply last, so a passed
|
|
148
|
-
* `target`/`rel` still wins.
|
|
132
|
+
* Internal and external are decided from the href, never at the call site —
|
|
133
|
+
* ../href.ts holds that decision, and Button, Badge and Card make the same one.
|
|
134
|
+
* `newTab` and `external` are the overrides. Call-site props apply last, so a
|
|
135
|
+
* passed `target`/`rel` still wins.
|
|
149
136
|
*
|
|
150
137
|
* The router is `next-view-transitions`, imported rather than injected. Every
|
|
151
138
|
* project on this package is a Next app and wants the same link, and a factory
|
|
@@ -153,5 +140,5 @@ type TypographyLinkProps = Omit<ComponentProps<"a">, "href"> & {
|
|
|
153
140
|
* not be imported by name. One call site ended up on the unbound version that
|
|
154
141
|
* way and lost its decoration.
|
|
155
142
|
*/
|
|
156
|
-
export declare function TypographyLink({ href, children, tone, newTab, addArrow, className, ...props }: TypographyLinkProps): import("react").JSX.Element;
|
|
143
|
+
export declare function TypographyLink({ href, children, tone, external: leavesApp, newTab, addArrow, className, ...props }: TypographyLinkProps): import("react").JSX.Element;
|
|
157
144
|
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { resolveLink } from "../href.js";
|
|
3
3
|
import { cva } from "class-variance-authority";
|
|
4
4
|
import { cn } from "../cn.js";
|
|
5
|
+
import { toneClass } from "../tone.js";
|
|
5
6
|
import { TextAs } from "./as.js";
|
|
6
7
|
/** The body layer: two axes and no more. A paragraph picks a rung and an ink; a
|
|
7
8
|
* caption is always secondary ink and picks a size. `lead` was a third rung a
|
|
@@ -18,8 +19,8 @@ const pVariants = cva("", {
|
|
|
18
19
|
prose: "text-pretty text-lg leading-relaxed",
|
|
19
20
|
},
|
|
20
21
|
tone: {
|
|
21
|
-
default: "text-foreground",
|
|
22
|
-
muted: "text-muted-foreground",
|
|
22
|
+
default: "text-[color:var(--ink,var(--foreground))]",
|
|
23
|
+
muted: "text-[color:var(--ink-muted,var(--muted-foreground))]",
|
|
23
24
|
},
|
|
24
25
|
},
|
|
25
26
|
defaultVariants: { variant: "ui", tone: "default" },
|
|
@@ -33,7 +34,10 @@ export function TypographyMuted(props) {
|
|
|
33
34
|
return _jsx(TypographyP, { ...props, ...MUTED });
|
|
34
35
|
}
|
|
35
36
|
/** Reading-size body copy. `TypographyMuted` is the same ink one rung down. */
|
|
36
|
-
const PROSE = {
|
|
37
|
+
const PROSE = {
|
|
38
|
+
variant: "prose",
|
|
39
|
+
tone: "muted",
|
|
40
|
+
};
|
|
37
41
|
export function TypographyProse(props) {
|
|
38
42
|
return _jsx(TypographyP, { ...props, ...PROSE });
|
|
39
43
|
}
|
|
@@ -85,7 +89,7 @@ export function TypographyProseList(props) {
|
|
|
85
89
|
* reason to sit there is to be quieter than the thing you qualify — and every
|
|
86
90
|
* container that sets a size for you sets a weight too.
|
|
87
91
|
*/
|
|
88
|
-
const captionVariants = cva("text-muted-foreground", {
|
|
92
|
+
const captionVariants = cva("text-[color:var(--ink-muted,var(--muted-foreground))]", {
|
|
89
93
|
variants: {
|
|
90
94
|
size: {
|
|
91
95
|
sm: "text-sm leading-normal",
|
|
@@ -126,7 +130,7 @@ export function TypographySmall(props) {
|
|
|
126
130
|
* `as` is here for the same reason it is on `TypographyEyebrow`: a config panel
|
|
127
131
|
* names its sections at this size, and those names are the page's outline.
|
|
128
132
|
*/
|
|
129
|
-
const labelVariants = cva("font-medium text-foreground", {
|
|
133
|
+
const labelVariants = cva("font-medium text-[color:var(--ink,var(--foreground))]", {
|
|
130
134
|
variants: {
|
|
131
135
|
size: {
|
|
132
136
|
sm: "text-sm",
|
|
@@ -187,29 +191,37 @@ export function TypographyStat({ className, size, figures, children, ...props })
|
|
|
187
191
|
* optical correction — the mono face carries a taller x-height than the sans.
|
|
188
192
|
*/
|
|
189
193
|
export function TypographyInlineCode({ className, children, ...props }) {
|
|
190
|
-
return (_jsx("code", { className: cn("rounded-[3px] bg-
|
|
194
|
+
return (_jsx("code", { className: cn("rounded-[3px] bg-current/[0.06] px-[0.3em] py-[0.1em] font-mono text-[0.9em] text-[color:var(--ink,var(--secondary-ink))]", className), ...props, children: children }));
|
|
191
195
|
}
|
|
192
196
|
/**
|
|
193
|
-
* A statement about the surface, not the link: `
|
|
194
|
-
* `primary` when the link is the
|
|
195
|
-
* beneath a hero where `primary` would compete with the CTA beside it.
|
|
197
|
+
* A statement about the surface, not the link: `muted` inside a paragraph,
|
|
198
|
+
* `primary` when the link is the main thing on the line, `secondary` for a note
|
|
199
|
+
* beneath a hero where `primary` would compete with the CTA beside it. The other
|
|
200
|
+
* four come free, and a link inside a warning should be able to say so.
|
|
201
|
+
*
|
|
202
|
+
* This was `foreground | primary | secondary`, a private list whose first member
|
|
203
|
+
* was "no meaning at all" spelled a third way — `foreground` here, `muted` in
|
|
204
|
+
* Callout, `default` on a button. Now it is ../tone.ts, the same seven the other
|
|
205
|
+
* two take, and the weight is uniform: `secondary` alone used to skip
|
|
206
|
+
* `font-medium`, which read as a lighter link rather than a differently-coloured
|
|
207
|
+
* one.
|
|
196
208
|
*/
|
|
197
|
-
const
|
|
198
|
-
foreground: "font-medium text-foreground",
|
|
199
|
-
primary: "font-medium text-primary",
|
|
200
|
-
secondary: "text-secondary-ink",
|
|
201
|
-
};
|
|
209
|
+
const INHERITED_INK = "text-[color:var(--ink,var(--foreground))]";
|
|
202
210
|
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
|
-
|
|
211
|
+
/**
|
|
212
|
+
* No tone stated means "read the surface": the ink comes from whatever painted
|
|
213
|
+
* the ground, which on a page is `--foreground` and inside a filled control is
|
|
214
|
+
* that control's label ink. The old default spelled this `muted`, whose hue is
|
|
215
|
+
* `--foreground` — identical on a page, and 2.28:1 on a filled button.
|
|
216
|
+
*/
|
|
217
|
+
const linkClass = (tone, className) => cn(tone ? cn(toneClass(tone), "text-(color:--tone-hue)") : INHERITED_INK, "font-medium", LINK_DECORATION, className);
|
|
204
218
|
/**
|
|
205
219
|
* The inline link.
|
|
206
220
|
*
|
|
207
|
-
* Internal and external are decided from the href, never at the call site
|
|
208
|
-
* href
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
* the reader should stay in. Call-site props apply last, so a passed
|
|
212
|
-
* `target`/`rel` still wins.
|
|
221
|
+
* Internal and external are decided from the href, never at the call site —
|
|
222
|
+
* ../href.ts holds that decision, and Button, Badge and Card make the same one.
|
|
223
|
+
* `newTab` and `external` are the overrides. Call-site props apply last, so a
|
|
224
|
+
* passed `target`/`rel` still wins.
|
|
213
225
|
*
|
|
214
226
|
* The router is `next-view-transitions`, imported rather than injected. Every
|
|
215
227
|
* project on this package is a Next app and wants the same link, and a factory
|
|
@@ -217,13 +229,12 @@ const linkClass = (tone = "foreground", className) => cn(LINK_TONES[tone], LINK_
|
|
|
217
229
|
* not be imported by name. One call site ended up on the unbound version that
|
|
218
230
|
* way and lost its decoration.
|
|
219
231
|
*/
|
|
220
|
-
export function TypographyLink({ href, children, tone = "
|
|
232
|
+
export function TypographyLink({ href, children, tone = "muted", external: leavesApp, newTab, addArrow, className, ...props }) {
|
|
221
233
|
const style = linkClass(tone, className);
|
|
222
|
-
const external =
|
|
234
|
+
const { Component, props: link, external } = resolveLink(href, {
|
|
235
|
+
external: leavesApp,
|
|
236
|
+
newTab,
|
|
237
|
+
});
|
|
223
238
|
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
|
-
|
|
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 }));
|
|
239
|
+
return (_jsx(Component, { className: style, ...link, ...props, children: body }));
|
|
229
240
|
}
|
package/llms.txt
CHANGED
|
@@ -5,8 +5,8 @@ content blocks, a long-form essay shell, token and theme CSS, and build-time
|
|
|
5
5
|
tooling for SEO, OG cards, lint rules and contrast checks.
|
|
6
6
|
|
|
7
7
|
This file is for coding agents working in an app that consumes the package. It
|
|
8
|
-
covers what to import
|
|
9
|
-
reference: https://github.com/supertypeai/foundations
|
|
8
|
+
covers what to import, how the copy should read, and the mistakes that pass
|
|
9
|
+
silently. Full reference: https://github.com/supertypeai/foundations
|
|
10
10
|
|
|
11
11
|
## Rules
|
|
12
12
|
|
|
@@ -21,6 +21,11 @@ reference: https://github.com/supertypeai/foundations
|
|
|
21
21
|
4. **Use colour tokens, never literal colours.** `bg-card`, `text-foreground`,
|
|
22
22
|
`border-border`. No hex values, no `bg-zinc-800`, no `dark:` overrides that
|
|
23
23
|
swap one token for another.
|
|
24
|
+
5. **Paint a surface, hand down its ink.** Any element you give a background
|
|
25
|
+
needs `INK_ON_FILL` (a tone fill) or `inkOnSurface(token)` (a tinted one), or
|
|
26
|
+
the type inside it keeps the page's ink and fails contrast silently.
|
|
27
|
+
6. **Write the words like a person would.** See Writing copy below. It applies
|
|
28
|
+
to every string a reader sees and to the comments you leave behind.
|
|
24
29
|
|
|
25
30
|
## Which component
|
|
26
31
|
|
|
@@ -40,14 +45,18 @@ reference: https://github.com/supertypeai/foundations
|
|
|
40
45
|
| a number or metric | `TypographyStat` | root |
|
|
41
46
|
| inline code | `TypographyInlineCode` | root |
|
|
42
47
|
| a link | `TypographyLink` | root |
|
|
48
|
+
| where an href goes, without a component | `resolveLink` | root |
|
|
43
49
|
| a highlighted phrase | `TypographyHighlight` | root |
|
|
50
|
+
| a button, or a link that looks like one | `Button` | `/blocks` |
|
|
51
|
+
| a status label, count or chip | `Badge` | `/blocks` |
|
|
44
52
|
| a link card, or a grid of them | `Card`, `Cards` | `/blocks` |
|
|
45
53
|
| an aside, warning or note | `Callout` | `/blocks` |
|
|
46
54
|
| numbered instructions | `Steps`, `Step` | `/blocks` |
|
|
47
55
|
| an FAQ or collapsible, no JS | `Disclosure`, `DisclosureGroup` | `/blocks` |
|
|
48
56
|
| an animated accordion | `Accordion` (client) | `/blocks` |
|
|
49
|
-
| tabbed content | `
|
|
57
|
+
| tabbed content | `TabGroup` when the tabs are data, `Tabs` to compose by hand (client) | `/blocks` |
|
|
50
58
|
| a long-form article page | `EssayHeader` + `EssayLayout` + `EssaySection` | `/essay` |
|
|
59
|
+
| an article whose body is prose or MDX | `EssayHeader` + `ReadingLayout` | `/essay` |
|
|
51
60
|
| a post meta row (date, read time, tags) | `PostMetaRow` and friends | `/essay` |
|
|
52
61
|
| a table of contents | `TableOfContents`, `ReadingRail` | `/essay` |
|
|
53
62
|
| page metadata and JSON-LD | `createSeo` | `/seo` |
|
|
@@ -58,15 +67,15 @@ reference: https://github.com/supertypeai/foundations
|
|
|
58
67
|
|
|
59
68
|
| import | exports |
|
|
60
69
|
|---|---|
|
|
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`, `
|
|
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`, `
|
|
70
|
+
| `@supertype.ai/foundations` | `cn`, `TypographyH1`, `TypographyH2`, `TypographyH3`, `TypographyH4`, `TypographyEyebrow`, `TypographyP`, `TypographyMuted`, `TypographyProse`, `TypographyList`, `TypographyProseList`, `TypographyCaption`, `TypographySmall`, `TypographyLabel`, `TypographyStat`, `TypographyInlineCode`, `TypographyLink`, `TypographyHighlight`, `headingClass`, `headingFace`, `eyebrowClass`, `toneClass`, `impliedTone`, `resolveLink`, `isExternalHref`. Types: `TypographyTag`, `ParagraphVariants`, `ListProps`, `CaptionVariants`, `LabelVariants`, `StatVariants`, `Tone`, `HighlightTone`, `LinkBehavior`, `ResolvedLink` |
|
|
71
|
+
| `@supertype.ai/foundations/blocks` | `Cards`, `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `Callout`, `Button`, `buttonVariants`, `Badge`, `badgeVariants`, `Steps`, `Step`, `Disclosure`, `DisclosureGroup`, `Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent`, `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`, `TabGroup`, `SEGMENT`. Types: `ButtonLook`, `BadgeLook`, `TabItem` |
|
|
63
72
|
| `@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` |
|
|
73
|
+
| `@supertype.ai/foundations/essay` | `createEssay`, `EssayHeader`, `EssayLayout`, `EssaySection`, `EssayPullQuote`, `EssayFigure`, `EssayMovements`, `EssayDocument`, `EssayColumns`, `EssayAside`, `EssayBody`, `ReadingLayout`, `TableOfContents`, `ReadingRail`, `ReadingProgressBar`, `Rail`, `RailLink`, `PostMetaRow`, `PostDate`, `ReadTime`, `TagPills`, `MetaDot`, `formatPostDate`, `extractHeadings`, `readingTime`, `createSlugger`, `useReadingProgress`, `useScrollSpy`. Types: `TocHeading`, `EssayDecorations`, `EssayIndexEntry`, `EssayDocSection`, `EssayMovement`, `PostDateFormat` |
|
|
65
74
|
| `@supertype.ai/foundations/seo` | `createSeo`. Types: `SeoConfig`, `ArticleAuthor`, `ArticleOptions`, `PageMetadata` |
|
|
66
75
|
| `@supertype.ai/foundations/og` | `ogCard`, `OG_SIZE`. Types: `OgCardOptions` |
|
|
67
|
-
| `@supertype.ai/foundations/eslint` | `designConfig` (the
|
|
76
|
+
| `@supertype.ai/foundations/eslint` | `designRules` (every rule as one array, the one to spread), `designConfig` (the same set wrapped as a flat-config entry). The builders `colourRules`, `typographyRules`, `linkRules`, `themeOverrideRules`, `surfaceAsInkRules`, `renamedTokenRules` are exported too, though spreading them by hand is how a consumer ends up missing one. Types: `FlatConfigEntry`, `DesignRuleOptions`, `DesignConfigOptions`, `RestrictedSyntax`, `ColourOptions`, `TypographyOptions` |
|
|
68
77
|
| `@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
|
|
78
|
+
| `@supertype.ai/foundations/contrast` | `checkLegibility` (inks at 4.5:1), `checkSignals` (fills at 3:1 — status hues, the categorical earth hues and the six chart series alike — tinted inks at 4.5:1, `--subtle-foreground` at the 3:1 it is documented for, labels against their own fill), `checkHairlines` (`--border` and `--input` at 1.4:1 on `--background` and `--card`, `--sidebar-border` on `--sidebar`: a rule is exempt from the ink and mark bars, but it still has to read as the same weight in both themes), `resolveTokens`, `formatFailures`, `specificity`, `parseColor`, `luminance`, `contrast` (WCAG ratio), `lc` (APCA lightness contrast: polarity-aware, for checking that an ink ramp is perceptually ordered rather than merely ordered by ratio), `tokenCuts` (which cuts a token ships: fill, the label printed on it, the hue as words, the taxonomy `checkSignals` measures against). Types: `Rgb`, `Theme`, `LegibilityFailure`, `TokenCuts`. Build-time only |
|
|
70
79
|
|
|
71
80
|
## Props worth knowing
|
|
72
81
|
|
|
@@ -74,32 +83,100 @@ reference: https://github.com/supertypeai/foundations
|
|
|
74
83
|
- `TypographyH2`: `variant?`, plus `divider?: boolean` for a rule underneath.
|
|
75
84
|
- `TypographyP`: `variant?: "ui" | "prose"` (default `ui`), `tone?: "default" | "muted"`.
|
|
76
85
|
- `TypographyMuted`, `TypographyProse`, `TypographyProseList` are presets. The
|
|
77
|
-
prop each one pins is
|
|
86
|
+
prop each one pins is dropped from its type, so passing it fails to compile.
|
|
78
87
|
- `TypographyCaption`, `TypographyLabel`, `TypographySmall`: `size?: "sm" | "xs" | "2xs" | "inherit"`, `as?`.
|
|
79
88
|
- `TypographyEyebrow`: `tone?: "heading" | "label"`, `as?`.
|
|
80
89
|
- `TypographyStat`: `size?: "inherit" | "card" | "panel" | "page" | "display"`, `figures?: "tabular" | "proportional"`. Keep tabular where a value updates in place.
|
|
81
|
-
- `TypographyLink`: `href` (required), `tone?:
|
|
82
|
-
- `TypographyHighlight`: `tone?: "primary" | "success" | "ochre" | "terracotta" | "sage" | "fig"`, `seed?: number`.
|
|
90
|
+
- `TypographyLink`: `href` (required), `tone?: Tone` (default `muted`), `addArrow?`, `newTab?`. The href decides internal versus external.
|
|
91
|
+
- `TypographyHighlight`: `tone?: HighlightTone`, one of `"primary" | "success" | "ochre" | "terracotta" | "sage" | "fig"`, plus `seed?: number`. A separate type from `Tone` on purpose: this axis is categorical (which one it is) where `Tone` is semantic (what it means), the same split theme.css draws between the earth swatches and the status tokens.
|
|
83
92
|
- `Card`: `href`, `title`, `description`, `icon`, `external`. An href makes the whole card a link.
|
|
84
|
-
- `Callout`: `
|
|
93
|
+
- `Tone` is the one semantic colour vocabulary, shared by `Button`, `Badge`, `Callout`, `TypographyLink` and `TabsList`: `"muted" | "primary" | "secondary" | "brand" | "success" | "warn" | "destructive"`, defaulting to `muted` everywhere except a solid `Button`. Seven tones, seven tokens, one to one, which is the bar for adding one. Four names map onto others: `neutral` and `foreground` are `muted`, the word the rest of the package uses; `accent` is `--primary`'s hover tint, so a washed `primary` renders the same thing; `info` is covered by the `success | warn | destructive` triad. `brand` falls back to `--primary` in an app that defines no `--brand`.
|
|
94
|
+
- **Ink is handed down by whatever paints.** `toneClass(tone)` is a palette and sets no ink. A surface that fills adds `INK_ON_FILL`; a tinted one uses `inkOnSurface("--card-foreground")`. Both declare `--ink` and `--ink-muted`, which every type primitive reads, falling back to the page. Paint a background without them and a nested `TypographyLabel` prints `--foreground` on your fill, which measures 2.34:1 on `--primary`. On a hue fill `--ink-muted` equals `--ink`: a filled control has one ink, and wanting a second rung means wanting a tinted surface.
|
|
95
|
+
- `Button`: `variant?: "solid" | "soft" | "outline" | "ghost" | "link"` (default `solid`), `tone?: Tone` (defaults to `primary` on a solid button and `muted` on every other variant — filling a button in is how a page says this is the action), `size?: "xs" | "sm" | "md" | "lg" | "xl"` (default `md`), `icon?: boolean` for a square glyph box, `pill?: boolean` for full-round corners, `href` to make it a link, `render` for an element that is neither a button nor a link. Variant is how much ink it spends and tone is what the ink means, on separate axes, so a quiet delete is `variant="ghost" tone="destructive"`.
|
|
96
|
+
- `Badge`: `variant?: "solid" | "soft" | "outline" | "ghost"` (default `solid`), `tone?: Tone`, `size?: "xs" | "sm"` (default `sm`), `pill?: boolean`, `href` for a badge that leads somewhere. Same axes and same spellings as `Button`, minus `link`, which belongs to things you click. `warning` and `supertype` were `tone="warn"` and `tone="brand"` under invented names.
|
|
97
|
+
- `Callout`: `tone?: Tone`, `density?: "compact" | "editorial"`, `title`, `icon`, `action`.
|
|
85
98
|
- `DisclosureGroup`: `type?: "multiple" | "single"`, `defaultValue` (matches the title string).
|
|
86
|
-
- `TabsList`: `variant?: "default" | "line"
|
|
99
|
+
- `TabsList`: `variant?: "default" | "line"`, `tone?: Tone` (default `primary`). The tone
|
|
100
|
+
inks the marker alone, which on `line` means the underline and the active icon. The
|
|
101
|
+
boxed track keeps its marker flat, so a tone there paints nothing.
|
|
102
|
+
- `TabGroup`: `tabs: readonly TabItem[]` (`{ value, label, icon?, content }`), `defaultValue?`,
|
|
103
|
+
`value?`, `onValueChange?`, `variant?`, `tone?`, `iconPosition?: "inline-start" | "inline-end"`.
|
|
104
|
+
`icon` is an element (`<Icons.Mic />`) that the trigger sizes and inks. Reach for it
|
|
105
|
+
whenever the tabs are data, and compose `Tabs`/`TabsTrigger` by hand only for a shape
|
|
106
|
+
it cannot express.
|
|
107
|
+
|
|
108
|
+
## Writing copy
|
|
109
|
+
|
|
110
|
+
Every string a reader sees goes through this: page copy, section notes, button
|
|
111
|
+
labels, callout titles, error messages, and the comments you leave in the file.
|
|
112
|
+
Write the way a person writes when they know the subject and respect the reader's
|
|
113
|
+
time.
|
|
114
|
+
|
|
115
|
+
- **Say the thing.** State what is true and move on. Skip the setup sentence, the
|
|
116
|
+
restatement, and the closing summary of what you just said.
|
|
117
|
+
- **Define forwards.** "A tone sets three variables and the component paints from
|
|
118
|
+
them" over "the component does not branch on the tone". Reach for a negative
|
|
119
|
+
only when the absence is the point.
|
|
120
|
+
- **One em dash per page, at most.** Prefer a full stop, a comma, or a colon. The
|
|
121
|
+
same goes for a parenthetical aside mid-sentence.
|
|
122
|
+
- **No arrow glyphs in prose.** Write "3.44:1 in light, 6.75:1 in dark" rather
|
|
123
|
+
than joining the two with an arrow. Where a UI genuinely points at something,
|
|
124
|
+
render a real icon.
|
|
125
|
+
- **Watch the tacked-on clause.** "…, and it is the shape to reach for", "…, and
|
|
126
|
+
it exists so that…". The second half is usually the point: lead with it, or
|
|
127
|
+
make it its own sentence.
|
|
128
|
+
- **Break the triples.** "X, Y, and Z" as a rhetorical flourish, three clauses
|
|
129
|
+
joined by "and", and the "not just A, but B" shape all read as machine prose.
|
|
130
|
+
Two items, or a list.
|
|
131
|
+
- **Cut the throat-clearing.** No "it is worth noting", "it is important to",
|
|
132
|
+
"simply", "just", "of course", "in other words". No "this is not X, it is Y".
|
|
133
|
+
- **Skip the rationale.** A note explains what the reader needs to use the thing.
|
|
134
|
+
Design history, near-misses, and the case for a decision go in the commit
|
|
135
|
+
message or the docs, not in the copy.
|
|
136
|
+
- **Concrete over abstract.** Name the class, the prop, the file. "Five
|
|
137
|
+
components take the prop" beats "several components".
|
|
138
|
+
- **Vary the sentences.** A paragraph of same-length clauses reads as generated
|
|
139
|
+
even when every sentence is true.
|
|
140
|
+
- **Headings are labels.** "How it stays current", not "Why it cannot go stale".
|
|
141
|
+
|
|
142
|
+
Code samples and commit messages stay conventional. This is about prose.
|
|
87
143
|
|
|
88
144
|
## Common mistakes
|
|
89
145
|
|
|
90
|
-
- `<p className="text-sm text-muted-foreground"
|
|
91
|
-
- `<h2 className="text-lg font-semibold"
|
|
92
|
-
`headingClass()`
|
|
93
|
-
- Importing `Card` or `
|
|
94
|
-
`@supertype.ai/foundations/blocks`.
|
|
95
|
-
- `<
|
|
146
|
+
- `<p className="text-sm text-muted-foreground">`: use `<TypographyMuted>`.
|
|
147
|
+
- `<h2 className="text-lg font-semibold">`: use `<TypographyH2>`, or
|
|
148
|
+
`headingClass()` to pass classes into someone else's component.
|
|
149
|
+
- Importing `Card`, `Callout` or `Button` from `@supertype.ai/foundations`. They
|
|
150
|
+
live in `@supertype.ai/foundations/blocks`.
|
|
151
|
+
- `<Button variant="destructive">` or `variant="secondary"`: those are tones.
|
|
152
|
+
Write `variant="soft" tone="destructive"`, `variant="solid" tone="secondary"`.
|
|
153
|
+
- `<Button size="icon-sm">`: one size ladder. Write `size="sm" icon`.
|
|
154
|
+
- `<Badge variant="warning">` or `variant="supertype"`: tones, and misspelled ones.
|
|
155
|
+
Write `variant="outline" tone="warn"` and `variant="solid" tone="brand"`.
|
|
156
|
+
- `tone="neutral"`, `tone="foreground"` or `tone="accent"`: all three went when the
|
|
157
|
+
tone lists merged. Write `tone="muted"` for the first two, `tone="primary"` for
|
|
158
|
+
the third.
|
|
159
|
+
- An `<a>` or `<Link>` styled by hand to look like a button, and equally
|
|
160
|
+
`render={<a href="/x" />}` or `render={<Link href="/x" />}` on anything that
|
|
161
|
+
takes an `href`. Write `<Button href="/x">`. `Button`, `Badge`, `Card` and
|
|
162
|
+
`TypographyLink` all take `href` and route it through one rule
|
|
163
|
+
(`resolveLink`): a scheme leaves the app and gets `target`/`rel`, a `#hash`
|
|
164
|
+
stays a plain anchor, everything else goes through the router's `Link` and
|
|
165
|
+
keeps the view transition. A hand-passed anchor skips all of that. `render` is
|
|
166
|
+
for an element that is genuinely not a link — and for `RailLink`, which takes a
|
|
167
|
+
router `Link` that way on purpose, since `essay/rail.tsx` has to import without
|
|
168
|
+
Next.
|
|
169
|
+
- Exporting a constant from a `"use client"` module and importing it into a server
|
|
170
|
+
component. It crosses the boundary as a stub, not a string, so an `href` built that
|
|
171
|
+
way arrives as a function. Keep shared constants in a plain module.
|
|
172
|
+
- `<TypographyMuted tone="default">`: a type error. Use `TypographyP`.
|
|
96
173
|
- Binding fonts with `font.className` instead of `font.variable`. The className
|
|
97
174
|
form sets `font-family` on the element and leaves the roles unresolved.
|
|
98
175
|
- Omitting `@source '../node_modules/@supertype.ai/foundations/dist/**/*.js'` from
|
|
99
176
|
the CSS entry. Tailwind then purges every class the package ships.
|
|
100
177
|
- Omitting `@import "@supertype.ai/foundations/theme.css"`. `tokens.css` names the
|
|
101
178
|
colour roles but holds no values, so the whole palette resolves to nothing.
|
|
102
|
-
- Using `Accordion` for a static FAQ. `Disclosure`
|
|
179
|
+
- Using `Accordion` for a static FAQ. `Disclosure` runs on the browser alone.
|
|
103
180
|
- Adding a second `@custom-variant dark`. `tokens.css` already binds it.
|
|
104
181
|
|
|
105
182
|
Run `npx foundations doctor` in the app to check the last four.
|
|
@@ -108,8 +185,8 @@ Run `npx foundations doctor` in the app to check the last four.
|
|
|
108
185
|
|
|
109
186
|
Two classes change how everything renders, and both go on `<html>` or a subtree:
|
|
110
187
|
|
|
111
|
-
- `.dark` re-points every colour token.
|
|
112
|
-
OS setting.
|
|
188
|
+
- `.dark` re-points every colour token. `tokens.css` binds it to the class, so
|
|
189
|
+
the OS setting leaves it alone.
|
|
113
190
|
- `.editorial` gives the heading role to the serif at weight 400 and retunes the
|
|
114
191
|
whole heading ladder against an 18px body, instead of the product's 13px.
|
|
115
192
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supertype.ai/foundations",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -117,6 +117,7 @@
|
|
|
117
117
|
"next": "^15.5.7",
|
|
118
118
|
"next-view-transitions": "^0.3.5",
|
|
119
119
|
"react": "^19.0.0",
|
|
120
|
+
"react-dom": "^19.0.0",
|
|
120
121
|
"typescript": "^5.7.0",
|
|
121
122
|
"vitest": "4.1.11"
|
|
122
123
|
},
|