@tangle-network/ui 11.35.0 → 11.37.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/CHANGELOG.md +20 -0
- package/README.md +1 -1
- package/dist/code-presentation.d.ts +2 -0
- package/dist/code-presentation.js +2 -0
- package/dist/index.d.ts +8 -5
- package/dist/index.js +6 -3
- package/dist/lib/code-presentation.d.ts +92 -0
- package/dist/lib/code-presentation.js +437 -0
- package/dist/lib/control-presentation.d.ts +34 -1
- package/dist/lib/control-presentation.js +1 -1
- package/dist/lib/focus.d.ts +2 -2
- package/dist/lib/focus.js +3 -2
- package/dist/lib/motion.d.ts +4 -0
- package/dist/lib/motion.js +7 -0
- package/dist/lib/surface-motion.js +105 -0
- package/dist/markdown/api-schema.d.ts +9 -0
- package/dist/markdown/api-schema.js +20 -0
- package/dist/markdown/code-block.d.ts +15 -7
- package/dist/markdown/code-block.js +120 -268
- package/dist/markdown.d.ts +4 -2
- package/dist/markdown.js +4 -2
- package/dist/presentation.d.ts +88 -0
- package/dist/presentation.js +183 -0
- package/dist/primitives/alert-dialog.js +1 -1
- package/dist/primitives/alert.d.ts +1 -2
- package/dist/primitives/button.d.ts +2 -5
- package/dist/primitives/button.js +5 -31
- package/dist/primitives/card.js +3 -7
- package/dist/primitives/code-block.d.ts +1 -1
- package/dist/primitives/date-picker.d.ts +1 -1
- package/dist/primitives/dialog.js +29 -16
- package/dist/primitives/empty-state.js +6 -5
- package/dist/primitives/heading.d.ts +1 -0
- package/dist/primitives/heading.js +3 -2
- package/dist/primitives/help-text.js +2 -1
- package/dist/primitives/input.d.ts +1 -4
- package/dist/primitives/input.js +3 -24
- package/dist/primitives/label.js +1 -2
- package/dist/primitives/metric-strip.js +7 -12
- package/dist/primitives/page-header.d.ts +1 -0
- package/dist/primitives/page-header.js +7 -6
- package/dist/primitives/page-shell.d.ts +8 -2
- package/dist/primitives/page-shell.js +21 -2
- package/dist/primitives/pagination.js +1 -1
- package/dist/primitives/popover.js +11 -3
- package/dist/primitives/sheet.js +29 -19
- package/dist/primitives/skeleton.js +1 -1
- package/dist/primitives/status-pill.d.ts +3 -1
- package/dist/primitives/status-pill.js +12 -3
- package/dist/primitives/table.js +10 -9
- package/dist/primitives/tabs.js +3 -12
- package/dist/primitives/tooltip.js +11 -3
- package/dist/primitives.d.ts +3 -2
- package/dist/primitives.js +2 -1
- package/dist/utils.d.ts +1 -1
- package/package.json +20 -5
- package/src/code-presentation.ts +2 -0
- package/src/lib/code-presentation.ts +257 -0
- package/src/lib/control-presentation.ts +2 -4
- package/src/lib/focus.ts +3 -2
- package/src/lib/motion.ts +7 -0
- package/src/lib/surface-motion.tsx +85 -0
- package/src/markdown/api-presentation.test.tsx +63 -0
- package/src/markdown/api-schema.tsx +9 -0
- package/src/markdown/code-block.tsx +54 -185
- package/src/markdown/index.ts +2 -1
- package/src/presentation.ts +205 -0
- package/src/primitives/alert.tsx +1 -2
- package/src/primitives/button.tsx +6 -41
- package/src/primitives/card.tsx +5 -10
- package/src/primitives/checkbox-separator-sheet-tooltip.test.tsx +2 -1
- package/src/primitives/code-block.tsx +1 -1
- package/src/primitives/control-presentation.test.tsx +2 -7
- package/src/primitives/dialog.tsx +19 -18
- package/src/primitives/empty-state.tsx +6 -5
- package/src/primitives/heading.tsx +4 -2
- package/src/primitives/help-text.tsx +3 -2
- package/src/primitives/input.tsx +3 -31
- package/src/primitives/label.tsx +2 -4
- package/src/primitives/metric-strip.tsx +8 -17
- package/src/primitives/page-header.tsx +8 -5
- package/src/primitives/page-shell.tsx +13 -2
- package/src/primitives/popover.tsx +6 -8
- package/src/primitives/sheet.tsx +16 -11
- package/src/primitives/skeleton.tsx +1 -1
- package/src/primitives/status-pill.test.tsx +1 -0
- package/src/primitives/status-pill.tsx +10 -4
- package/src/primitives/table.tsx +10 -9
- package/src/primitives/tabs.tsx +5 -17
- package/src/primitives/tooltip.tsx +6 -5
- package/src/styles/compat.css +12 -0
- package/src/styles/presentation.css +38 -0
- package/src/primitives/page-shell.test.tsx +0 -24
|
@@ -26,7 +26,8 @@ export type StatusTone =
|
|
|
26
26
|
| "danger"
|
|
27
27
|
| "info"
|
|
28
28
|
| "neutral"
|
|
29
|
-
| "running"
|
|
29
|
+
| "running"
|
|
30
|
+
| "violet";
|
|
30
31
|
|
|
31
32
|
export interface StatusPillProps
|
|
32
33
|
extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
|
|
@@ -46,10 +47,13 @@ export interface StatusPillProps
|
|
|
46
47
|
* For a control that supplies its own surface (a chip, a selected row).
|
|
47
48
|
*/
|
|
48
49
|
bare?: boolean;
|
|
50
|
+
/** Decorative caller glyph; the visible text remains the accessible status. */
|
|
51
|
+
icon?: React.ReactNode;
|
|
49
52
|
size?: "sm" | "md";
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
const TONE_SURFACE: Record<StatusTone, string> = {
|
|
56
|
+
violet: "bg-[var(--surface-violet-bg)] text-[var(--surface-violet-text)] border-[var(--surface-violet-border)]",
|
|
53
57
|
success:
|
|
54
58
|
"bg-[var(--surface-success-bg)] text-[var(--surface-success-text)] border-[var(--surface-success-border)]",
|
|
55
59
|
warning:
|
|
@@ -77,6 +81,7 @@ const TONE_SURFACE: Record<StatusTone, string> = {
|
|
|
77
81
|
* has left — the glyph — with nothing to signal that it happened.
|
|
78
82
|
*/
|
|
79
83
|
const TONE_TEXT: Record<StatusTone, string> = {
|
|
84
|
+
violet: "text-[var(--surface-violet-text)]",
|
|
80
85
|
success: "text-[var(--surface-success-text)]",
|
|
81
86
|
warning: "text-[var(--surface-warning-text)]",
|
|
82
87
|
danger: "text-[var(--surface-danger-text)]",
|
|
@@ -100,6 +105,7 @@ function ToneGlyph({ tone }: { tone: StatusTone }) {
|
|
|
100
105
|
className: "size-2 shrink-0",
|
|
101
106
|
};
|
|
102
107
|
switch (tone) {
|
|
108
|
+
case "violet": return <svg {...common} aria-hidden="true" fill="currentColor"><path d="M4 .5 7.5 4 4 7.5 .5 4Z"/></svg>;
|
|
103
109
|
// Settled and good: a solid disc.
|
|
104
110
|
case "success":
|
|
105
111
|
return (
|
|
@@ -165,7 +171,7 @@ function ToneGlyph({ tone }: { tone: StatusTone }) {
|
|
|
165
171
|
}
|
|
166
172
|
|
|
167
173
|
const StatusPill = React.forwardRef<HTMLSpanElement, StatusPillProps>(
|
|
168
|
-
({ className, tone, children, bare = false, size = "sm", ...props }, ref) => (
|
|
174
|
+
({ className, tone, children, icon, bare = false, size = "sm", ...props }, ref) => (
|
|
169
175
|
<span
|
|
170
176
|
ref={ref}
|
|
171
177
|
className={cn(
|
|
@@ -180,8 +186,8 @@ const StatusPill = React.forwardRef<HTMLSpanElement, StatusPillProps>(
|
|
|
180
186
|
>
|
|
181
187
|
{/* In `bare` mode the tone rides on the glyph alone, so it is scoped to
|
|
182
188
|
the wrapper the glyph sits in rather than applied to the whole pill. */}
|
|
183
|
-
<span className={cn("inline-flex", bare && TONE_TEXT[tone])}>
|
|
184
|
-
<ToneGlyph tone={tone} />
|
|
189
|
+
<span aria-hidden="true" className={cn("inline-flex [&>svg]:size-3 [&>svg]:shrink-0", bare && TONE_TEXT[tone])}>
|
|
190
|
+
{icon ?? <ToneGlyph tone={tone} />}
|
|
185
191
|
</span>
|
|
186
192
|
{children}
|
|
187
193
|
</span>
|
package/src/primitives/table.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tablePresentation } from "../presentation";
|
|
1
2
|
import * as React from "react";
|
|
2
3
|
import { cn } from "../lib/utils";
|
|
3
4
|
|
|
@@ -17,7 +18,7 @@ const Table = React.forwardRef<HTMLTableElement, TableProps>(
|
|
|
17
18
|
const table = (
|
|
18
19
|
<table
|
|
19
20
|
ref={ref}
|
|
20
|
-
className={cn(
|
|
21
|
+
className={cn(tablePresentation.root, className)}
|
|
21
22
|
{...props}
|
|
22
23
|
/>
|
|
23
24
|
);
|
|
@@ -27,7 +28,7 @@ const Table = React.forwardRef<HTMLTableElement, TableProps>(
|
|
|
27
28
|
return (
|
|
28
29
|
<div
|
|
29
30
|
{...attributes}
|
|
30
|
-
className={cn(
|
|
31
|
+
className={cn(tablePresentation.wrapper, wrapperClassName)}
|
|
31
32
|
>
|
|
32
33
|
{table}
|
|
33
34
|
</div>
|
|
@@ -40,7 +41,7 @@ const TableHeader = React.forwardRef<
|
|
|
40
41
|
HTMLTableSectionElement,
|
|
41
42
|
React.HTMLAttributes<HTMLTableSectionElement>
|
|
42
43
|
>(({ className, ...props }, ref) => (
|
|
43
|
-
<thead ref={ref} className={cn(
|
|
44
|
+
<thead ref={ref} className={cn(tablePresentation.head, className)} {...props} />
|
|
44
45
|
));
|
|
45
46
|
TableHeader.displayName = "TableHeader";
|
|
46
47
|
|
|
@@ -50,7 +51,7 @@ const TableBody = React.forwardRef<
|
|
|
50
51
|
>(({ className, ...props }, ref) => (
|
|
51
52
|
<tbody
|
|
52
53
|
ref={ref}
|
|
53
|
-
className={cn(
|
|
54
|
+
className={cn(tablePresentation.body, className)}
|
|
54
55
|
{...props}
|
|
55
56
|
/>
|
|
56
57
|
));
|
|
@@ -63,7 +64,7 @@ const TableFooter = React.forwardRef<
|
|
|
63
64
|
<tfoot
|
|
64
65
|
ref={ref}
|
|
65
66
|
className={cn(
|
|
66
|
-
|
|
67
|
+
tablePresentation.footer,
|
|
67
68
|
className,
|
|
68
69
|
)}
|
|
69
70
|
{...props}
|
|
@@ -78,7 +79,7 @@ const TableRow = React.forwardRef<
|
|
|
78
79
|
<tr
|
|
79
80
|
ref={ref}
|
|
80
81
|
className={cn(
|
|
81
|
-
|
|
82
|
+
tablePresentation.row,
|
|
82
83
|
className,
|
|
83
84
|
)}
|
|
84
85
|
{...props}
|
|
@@ -93,7 +94,7 @@ const TableHead = React.forwardRef<
|
|
|
93
94
|
<th
|
|
94
95
|
ref={ref}
|
|
95
96
|
className={cn(
|
|
96
|
-
|
|
97
|
+
tablePresentation.columnHeader,
|
|
97
98
|
className,
|
|
98
99
|
)}
|
|
99
100
|
{...props}
|
|
@@ -107,7 +108,7 @@ const TableCell = React.forwardRef<
|
|
|
107
108
|
>(({ className, ...props }, ref) => (
|
|
108
109
|
<td
|
|
109
110
|
ref={ref}
|
|
110
|
-
className={cn(
|
|
111
|
+
className={cn(tablePresentation.cell, className)}
|
|
111
112
|
{...props}
|
|
112
113
|
/>
|
|
113
114
|
));
|
|
@@ -119,7 +120,7 @@ const TableCaption = React.forwardRef<
|
|
|
119
120
|
>(({ className, ...props }, ref) => (
|
|
120
121
|
<caption
|
|
121
122
|
ref={ref}
|
|
122
|
-
className={cn(
|
|
123
|
+
className={cn(tablePresentation.caption, className)}
|
|
123
124
|
{...props}
|
|
124
125
|
/>
|
|
125
126
|
));
|
package/src/primitives/tabs.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { focusRing } from "../lib/focus";
|
|
4
|
-
import {
|
|
4
|
+
import { tabsPresentation } from "../presentation";
|
|
5
5
|
import { cn } from "../lib/utils";
|
|
6
6
|
|
|
7
7
|
const Tabs = TabsPrimitive.Root;
|
|
@@ -12,17 +12,12 @@ const TabsList = React.forwardRef<
|
|
|
12
12
|
variant?: "default" | "pills" | "underline";
|
|
13
13
|
}
|
|
14
14
|
>(({ className, variant = "default", ...props }, ref) => {
|
|
15
|
-
|
|
16
|
-
default:
|
|
17
|
-
"inline-flex h-10 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
|
|
18
|
-
pills: "inline-flex items-center gap-2",
|
|
19
|
-
underline: "inline-flex items-center gap-4 border-b border-border",
|
|
20
|
-
};
|
|
15
|
+
|
|
21
16
|
|
|
22
17
|
return (
|
|
23
18
|
<TabsPrimitive.List
|
|
24
19
|
ref={ref}
|
|
25
|
-
className={cn(
|
|
20
|
+
className={cn(tabsPresentation.list[variant], className)}
|
|
26
21
|
{...props}
|
|
27
22
|
/>
|
|
28
23
|
);
|
|
@@ -38,19 +33,12 @@ const TabsTrigger = React.forwardRef<
|
|
|
38
33
|
// Selection is the shared selected state (lib/interaction.ts): a tint and an
|
|
39
34
|
// inset ring for default and pills, a 2px underline for underline, whose
|
|
40
35
|
// border-b-2 slot stays transparent.
|
|
41
|
-
|
|
42
|
-
default:
|
|
43
|
-
`inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 ${ixControlSoft} ${ixSelected}`,
|
|
44
|
-
pills:
|
|
45
|
-
`inline-flex items-center justify-center whitespace-nowrap rounded-full px-4 py-2 text-sm font-medium transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 data-[state=inactive]:text-muted-foreground ${ixControlSoft} ${ixSelected}`,
|
|
46
|
-
underline:
|
|
47
|
-
`inline-flex items-center justify-center whitespace-nowrap rounded-t-md pb-3 text-sm font-medium transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 border-b-2 border-transparent data-[state=inactive]:text-muted-foreground ${ixTabUnderline}`,
|
|
48
|
-
};
|
|
36
|
+
|
|
49
37
|
|
|
50
38
|
return (
|
|
51
39
|
<TabsPrimitive.Trigger
|
|
52
40
|
ref={ref}
|
|
53
|
-
className={cn(
|
|
41
|
+
className={cn(tabsPresentation.trigger[variant], className)}
|
|
54
42
|
{...props}
|
|
55
43
|
/>
|
|
56
44
|
);
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
4
4
|
import * as React from "react";
|
|
5
|
+
import { SurfaceMotion } from "../lib/surface-motion";
|
|
6
|
+
import { surfaceMotion } from "../lib/motion";
|
|
5
7
|
import { cn } from "../lib/utils";
|
|
6
8
|
|
|
7
9
|
/**
|
|
@@ -33,20 +35,19 @@ const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
|
33
35
|
const TooltipContent = React.forwardRef<
|
|
34
36
|
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
35
37
|
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
|
36
|
-
>(({ className, sideOffset = 6, ...props }, ref) => (
|
|
38
|
+
>(({ className, sideOffset = 6, asChild, children, ...props }, ref) => (
|
|
37
39
|
<TooltipPrimitive.Portal>
|
|
38
40
|
<TooltipPrimitive.Content
|
|
39
41
|
ref={ref}
|
|
40
42
|
sideOffset={sideOffset}
|
|
41
43
|
className={cn(
|
|
42
44
|
"z-50 max-w-xs origin-(--radix-tooltip-content-transform-origin) rounded-md bg-foreground px-2.5 py-1.5 text-background text-xs leading-snug shadow-md",
|
|
43
|
-
|
|
44
|
-
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
45
|
-
"duration-150 ease-out motion-reduce:animate-none",
|
|
45
|
+
surfaceMotion,
|
|
46
46
|
className,
|
|
47
47
|
)}
|
|
48
48
|
{...props}
|
|
49
|
-
|
|
49
|
+
asChild
|
|
50
|
+
><SurfaceMotion kind="tooltip" asChild={asChild}>{children}</SurfaceMotion></TooltipPrimitive.Content>
|
|
50
51
|
</TooltipPrimitive.Portal>
|
|
51
52
|
));
|
|
52
53
|
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/* Generated by scripts/generate-presentation-css.mjs from shared primitive recipes. Requires Tailwind + Brand theme. */
|
|
2
|
+
@layer components {
|
|
3
|
+
.btn { @apply inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg font-medium [transition:color_var(--motion-control),background-color_var(--motion-control),border-color_var(--motion-control),box-shadow_var(--motion-control),transform_var(--motion-control),scale_var(--motion-control),opacity_var(--motion-control)] motion-reduce:transition-none focus-visible:outline-1 focus-visible:outline-[var(--focus-border)] focus-visible:ring-4 focus-visible:ring-[var(--focus-halo)] focus-visible:outline-offset-0 disabled:pointer-events-none disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 motion-reduce:active:scale-100 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 h-[var(--control-height,2.25rem)] text-[length:var(--control-text-md,0.875rem)] rounded-lg px-4 py-2; }
|
|
4
|
+
.btn-primary { @apply inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg font-medium [transition:color_var(--motion-control),background-color_var(--motion-control),border-color_var(--motion-control),box-shadow_var(--motion-control),transform_var(--motion-control),scale_var(--motion-control),opacity_var(--motion-control)] motion-reduce:transition-none focus-visible:outline-1 focus-visible:outline-[var(--focus-border)] focus-visible:ring-4 focus-visible:ring-[var(--focus-halo)] focus-visible:outline-offset-0 disabled:pointer-events-none disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 motion-reduce:active:scale-100 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-primary text-primary-foreground hover:bg-primary/90 active:scale-[0.97] hover:border-[var(--ix-hover-border)] hover:shadow-[var(--ix-hover)] focus:not-focus-visible:border-[var(--ix-hover-border)] focus:not-focus-visible:shadow-[var(--ix-after)] active:border-[hsl(var(--hsl-ring))] active:shadow-[var(--ix-press)] focus-visible:shadow-none h-[var(--control-height,2.25rem)] text-[length:var(--control-text-md,0.875rem)] rounded-lg px-4 py-2; }
|
|
5
|
+
.btn-secondary { @apply inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg font-medium [transition:color_var(--motion-control),background-color_var(--motion-control),border-color_var(--motion-control),box-shadow_var(--motion-control),transform_var(--motion-control),scale_var(--motion-control),opacity_var(--motion-control)] motion-reduce:transition-none focus-visible:outline-1 focus-visible:outline-[var(--focus-border)] focus-visible:ring-4 focus-visible:ring-[var(--focus-halo)] focus-visible:outline-offset-0 disabled:pointer-events-none disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 motion-reduce:active:scale-100 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-muted border border-border text-foreground hover:bg-muted/80 active:scale-[0.97] hover:border-[var(--ix-hover-border)] hover:shadow-[var(--ix-hover)] focus:not-focus-visible:border-[var(--ix-hover-border)] focus:not-focus-visible:shadow-[var(--ix-after)] active:border-[hsl(var(--hsl-ring))] active:shadow-[var(--ix-press)] focus-visible:shadow-none h-[var(--control-height,2.25rem)] text-[length:var(--control-text-md,0.875rem)] rounded-lg px-4 py-2; }
|
|
6
|
+
.btn-danger { @apply inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg font-medium [transition:color_var(--motion-control),background-color_var(--motion-control),border-color_var(--motion-control),box-shadow_var(--motion-control),transform_var(--motion-control),scale_var(--motion-control),opacity_var(--motion-control)] motion-reduce:transition-none focus-visible:outline-1 focus-visible:outline-[var(--focus-border)] focus-visible:ring-4 focus-visible:ring-[var(--focus-halo)] focus-visible:outline-offset-0 disabled:pointer-events-none disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 motion-reduce:active:scale-100 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 bg-destructive text-destructive-foreground hover:bg-destructive/90 active:scale-[0.97] hover:border-[var(--ix-hover-border)] hover:shadow-[var(--ix-hover)] focus:not-focus-visible:border-[var(--ix-hover-border)] focus:not-focus-visible:shadow-[var(--ix-after)] active:border-[hsl(var(--hsl-ring))] active:shadow-[var(--ix-press)] focus-visible:shadow-none h-[var(--control-height,2.25rem)] text-[length:var(--control-text-md,0.875rem)] rounded-lg px-4 py-2; }
|
|
7
|
+
.input { @apply flex w-full border py-1 placeholder:text-[var(--text-dim)] disabled:cursor-not-allowed disabled:opacity-50 file:border-0 file:bg-transparent file:font-medium focus:outline-hidden focus:border-[var(--focus-border)] focus:ring-3 focus:ring-[var(--focus-halo)] bg-[var(--field-surface,var(--well))] border-[var(--field-border)] shadow-[var(--shadow-well)] hover:border-[var(--ix-hover-border)] hover:shadow-[var(--shadow-well),var(--ix-hover)] [transition:color_var(--motion-control),background-color_var(--motion-control),border-color_var(--motion-control),box-shadow_var(--motion-control),transform_var(--motion-control),scale_var(--motion-control),opacity_var(--motion-control)] motion-reduce:transition-none min-w-0 text-foreground aria-invalid:border-[var(--surface-danger-border)] aria-invalid:hover:border-[var(--surface-danger-border)] aria-invalid:focus:border-[var(--focus-border-danger)] aria-invalid:focus:ring-[var(--focus-halo-danger)] autofill:shadow-[inset_0_0_0_1000px_var(--field-surface,var(--well))] autofill:[-webkit-text-fill-color:var(--text-primary)] autofill:caret-[var(--text-primary)] pointer-coarse:text-base h-[var(--control-height,2.25rem)] text-[length:var(--control-text-md,0.875rem)] rounded-lg px-3; }
|
|
8
|
+
.card { @apply rounded-[var(--radius-lg)] border text-card-foreground [transition:color_var(--motion-control),background-color_var(--motion-control),border-color_var(--motion-control),box-shadow_var(--motion-control),transform_var(--motion-control),scale_var(--motion-control),opacity_var(--motion-control)] motion-reduce:transition-none bg-card border-border shadow-[var(--edge-highlight),var(--shadow-card)] p-4; }
|
|
9
|
+
.stat-card { @apply rounded-[var(--radius-lg)] border text-card-foreground [transition:color_var(--motion-control),background-color_var(--motion-control),border-color_var(--motion-control),box-shadow_var(--motion-control),transform_var(--motion-control),scale_var(--motion-control),opacity_var(--motion-control)] motion-reduce:transition-none bg-card border-border shadow-[var(--edge-highlight),var(--shadow-card)] min-w-0 border-border p-4 sm:p-5; }
|
|
10
|
+
.table-header { @apply h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0; }
|
|
11
|
+
.table-cell { @apply p-4 align-middle [&:has([role=checkbox])]:pr-0; }
|
|
12
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* Requires Brand styles; the same contracts serve React and Hono. */
|
|
2
|
+
.tangle-heading { color: hsl(var(--foreground)); font-family: var(--font-display); font-weight: 600; line-height: var(--line-height-heading); letter-spacing: var(--tracking-tight); }
|
|
3
|
+
.tangle-heading[data-variant="display"] { font-size: var(--font-size-display); line-height: var(--line-height-display); }
|
|
4
|
+
.tangle-heading[data-variant="hero"] { font-size: var(--font-size-hero); line-height: var(--line-height-display); }
|
|
5
|
+
.tangle-heading[data-variant="page"] { font-size: var(--font-size-3xl); }
|
|
6
|
+
.tangle-heading[data-variant="section"] { font-size: var(--font-size-xl); }
|
|
7
|
+
.tangle-heading[data-variant="subsection"] { font-size: var(--font-size-lg); }
|
|
8
|
+
.tangle-heading[data-variant="eyebrow"] { font-size: var(--font-size-sm); text-transform: uppercase; letter-spacing: var(--tracking-wide); color: hsl(var(--muted-foreground)); }
|
|
9
|
+
.tangle-page-header { min-width: 0; margin-bottom: 1.5rem; }
|
|
10
|
+
.tangle-page-header-main { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 1.5rem; }
|
|
11
|
+
.tangle-intro .tangle-page-header-main { display: block; }
|
|
12
|
+
.tangle-reading-shell { display: grid; grid-template-columns: minmax(0,1fr); gap: 2rem; padding: 2rem var(--hero-gutter); max-width: var(--content-max-width); margin: auto; min-width: 0; }
|
|
13
|
+
.tangle-reading-shell > * { min-width: 0; }
|
|
14
|
+
.tangle-reading-article { width: 100%; max-width: var(--reading-max-width); overflow-wrap: anywhere; }
|
|
15
|
+
.tangle-reading-nav a, .tangle-reading-outline a { display: block; padding: 0.5rem 0; color: hsl(var(--muted-foreground)); text-decoration: none; }
|
|
16
|
+
.tangle-reading-nav a[aria-current]:not([aria-current="false"]), .tangle-reading-outline a[aria-current]:not([aria-current="false"]) { color: var(--accent-text); font-weight: 600; }
|
|
17
|
+
@media(min-width: 64rem) {
|
|
18
|
+
.tangle-reading-shell { grid-template-columns: 13rem minmax(0,1fr); }
|
|
19
|
+
.tangle-reading-outline { display: none; }
|
|
20
|
+
.tangle-reading-nav { position: sticky; top: var(--shell-header-height); align-self: start; max-height: calc(100dvh - var(--shell-header-height)); overflow: auto; }
|
|
21
|
+
}
|
|
22
|
+
@media(min-width: 80rem) {
|
|
23
|
+
.tangle-reading-shell:has(.tangle-reading-outline) { grid-template-columns: 13rem minmax(0,1fr) 11rem; }
|
|
24
|
+
.tangle-reading-outline { display: block; position: sticky; top: var(--shell-header-height); align-self: start; }
|
|
25
|
+
}
|
|
26
|
+
/* Native dialog has the same solid reading surface as React's Dialog/Sheet. */
|
|
27
|
+
.tangle-native-dialog { max-width: min(32rem,calc(100% - 2rem)); max-height: calc(100dvh - 2rem); overflow: auto; margin: auto; padding: 1.5rem; border: 1px solid hsl(var(--border)); border-radius: var(--radius-lg); background: hsl(var(--card)); color: hsl(var(--card-foreground)); }
|
|
28
|
+
.tangle-native-dialog::backdrop { background: var(--scrim); }
|
|
29
|
+
|
|
30
|
+
.tangle-api-meta { font-family: var(--font-mono); font-size: var(--font-size-sm); color: hsl(var(--muted-foreground)); overflow-wrap: anywhere; }
|
|
31
|
+
.tangle-api-schema { margin: 1rem 0; }
|
|
32
|
+
.tangle-api-schema > div { padding-block: 0.75rem; border-bottom: 1px solid hsl(var(--border)); overflow-wrap: anywhere; }
|
|
33
|
+
.tangle-api-schema dt { font-family: var(--font-mono); font-size: var(--font-size-label); }
|
|
34
|
+
.tangle-api-schema dt span, .tangle-api-schema dd { color: hsl(var(--muted-foreground)); }
|
|
35
|
+
.tangle-api-schema dd { margin: 0.25rem 0 0; font-size: var(--font-size-label); }
|
|
36
|
+
.tangle-page-description { margin-top: 0.25rem; max-width: 65ch; color: hsl(var(--muted-foreground)); font-size: var(--font-size-label); overflow-wrap: anywhere; }
|
|
37
|
+
.tangle-code-tabs { overflow-x: auto; max-width: 100%; }
|
|
38
|
+
.tangle-code-tabs [role="tab"] { flex: none; min-height: var(--control-height-lg); }
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { render, screen } from "@testing-library/react";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { PageShell } from "./page-shell";
|
|
4
|
-
|
|
5
|
-
describe("PageShell", () => {
|
|
6
|
-
it("ports Sandbox's width, gutters and rhythm without imposing a landmark or grid", () => {
|
|
7
|
-
const { container } = render(<PageShell><p>Content</p></PageShell>);
|
|
8
|
-
const shell = container.firstElementChild;
|
|
9
|
-
expect(shell?.tagName).toBe("DIV");
|
|
10
|
-
expect(shell).toHaveClass("mx-auto", "max-w-6xl", "space-y-8", "px-6", "py-8", "lg:px-8");
|
|
11
|
-
expect(shell).not.toHaveAttribute("role");
|
|
12
|
-
expect(shell).not.toHaveClass("grid");
|
|
13
|
-
expect(screen.queryByRole("main")).toBeNull();
|
|
14
|
-
expect(screen.getByText("Content")).toBeInTheDocument();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("allows a caller-owned fluid layout without introducing shell behavior", () => {
|
|
18
|
-
const { container } = render(<main><PageShell className="mx-0 max-w-none px-0 lg:px-0"><p>Workbench</p></PageShell></main>);
|
|
19
|
-
const shell = container.querySelector("main > div");
|
|
20
|
-
expect(shell).toHaveClass("mx-0", "max-w-none", "px-0", "lg:px-0");
|
|
21
|
-
expect(shell).not.toHaveClass("mx-auto", "max-w-6xl", "px-6", "lg:px-8");
|
|
22
|
-
expect(screen.getAllByRole("main")).toHaveLength(1);
|
|
23
|
-
});
|
|
24
|
-
});
|