@you-agent-factory/components 0.0.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.
Files changed (92) hide show
  1. package/README.md +223 -0
  2. package/docs/README.md +83 -0
  3. package/docs/button.md +119 -0
  4. package/docs/charts.md +325 -0
  5. package/docs/data-display-code-panel.md +120 -0
  6. package/docs/feedback-alert-panel.md +117 -0
  7. package/docs/feedback-skeleton.md +54 -0
  8. package/docs/forms-form-field.md +490 -0
  9. package/docs/forms-input-primitives.md +311 -0
  10. package/docs/forms-select-primitives.md +362 -0
  11. package/docs/graphs.md +250 -0
  12. package/docs/layout-display-primitives.md +211 -0
  13. package/docs/overlays.md +414 -0
  14. package/docs/table-data-table.md +331 -0
  15. package/docs/typography-roles.md +182 -0
  16. package/docs/widget-frame-recipes.md +273 -0
  17. package/package.json +161 -0
  18. package/src/category-paths.ts +20 -0
  19. package/src/charts/chart-state-panel.tsx +103 -0
  20. package/src/charts/chart.tsx +287 -0
  21. package/src/charts/index.ts +21 -0
  22. package/src/data-display/code-panel.tsx +67 -0
  23. package/src/data-display/data-table.tsx +180 -0
  24. package/src/data-display/description-list.tsx +24 -0
  25. package/src/data-display/index.ts +34 -0
  26. package/src/data-display/table-layout.ts +12 -0
  27. package/src/data-display/table.tsx +131 -0
  28. package/src/feedback/alert-panel-semantics.ts +75 -0
  29. package/src/feedback/alert-panel.tsx +187 -0
  30. package/src/feedback/index.ts +21 -0
  31. package/src/feedback/skeleton.tsx +16 -0
  32. package/src/forms/index.ts +73 -0
  33. package/src/forms/package-checkbox.tsx +50 -0
  34. package/src/forms/package-enum-select.tsx +185 -0
  35. package/src/forms/package-file-input.tsx +25 -0
  36. package/src/forms/package-form-field.tsx +202 -0
  37. package/src/forms/package-input.tsx +25 -0
  38. package/src/forms/package-native-select.tsx +25 -0
  39. package/src/forms/package-select.tsx +209 -0
  40. package/src/forms/package-textarea.tsx +35 -0
  41. package/src/forms/select-icons.tsx +35 -0
  42. package/src/graphs/graph-edge-path.ts +241 -0
  43. package/src/graphs/graph-edge.tsx +130 -0
  44. package/src/graphs/graph-interactive-example.tsx +187 -0
  45. package/src/graphs/graph-node-button.tsx +51 -0
  46. package/src/graphs/graph-node-handle-badge.tsx +108 -0
  47. package/src/graphs/graph-node-handle.ts +28 -0
  48. package/src/graphs/graph-node-shell.tsx +111 -0
  49. package/src/graphs/graph-node-state-indicator.tsx +47 -0
  50. package/src/graphs/graph-node-state.ts +111 -0
  51. package/src/graphs/graph-viewport-surface.tsx +27 -0
  52. package/src/graphs/index.ts +41 -0
  53. package/src/icons/index.ts +4 -0
  54. package/src/index.ts +209 -0
  55. package/src/layout/action-row.tsx +53 -0
  56. package/src/layout/index.ts +9 -0
  57. package/src/layout/surface-panel.tsx +91 -0
  58. package/src/navigation/index.ts +4 -0
  59. package/src/overlays/collapsible.tsx +22 -0
  60. package/src/overlays/dialog.tsx +148 -0
  61. package/src/overlays/index.ts +31 -0
  62. package/src/overlays/overlay-layout.ts +9 -0
  63. package/src/overlays/popover.tsx +29 -0
  64. package/src/overlays/scroll-area.tsx +78 -0
  65. package/src/primitives/button-link.tsx +22 -0
  66. package/src/primitives/button.tsx +258 -0
  67. package/src/primitives/icon-button-shell.tsx +49 -0
  68. package/src/primitives/index.ts +31 -0
  69. package/src/primitives/package-text.tsx +29 -0
  70. package/src/primitives/typography-roles.ts +12 -0
  71. package/src/primitives/typography.tsx +165 -0
  72. package/src/recipes/index.ts +61 -0
  73. package/src/recipes/widget-frame-content.tsx +114 -0
  74. package/src/recipes/widget-frame-disclosure.tsx +161 -0
  75. package/src/recipes/widget-frame-layout.ts +40 -0
  76. package/src/recipes/widget-frame-skeleton.tsx +16 -0
  77. package/src/recipes/widget-frame-states.tsx +99 -0
  78. package/src/recipes/widget-frame-typography.ts +19 -0
  79. package/src/recipes/widget-frame.tsx +84 -0
  80. package/src/styles/color-palette-presets.css +167 -0
  81. package/src/styles/color-role-tokens.css +163 -0
  82. package/src/styles/layout-role-tokens.css +27 -0
  83. package/src/styles/text-color-role-tokens.css +29 -0
  84. package/src/styles/typography-role-tokens.css +72 -0
  85. package/src/styles/typography-role-utilities.css +73 -0
  86. package/src/styles/typography-roles.css +53 -0
  87. package/src/styles.css +32 -0
  88. package/src/testing/index.ts +13 -0
  89. package/src/testing/render.tsx +16 -0
  90. package/src/tokens/index.ts +4 -0
  91. package/src/utilities/cn.ts +5 -0
  92. package/src/utilities/index.ts +6 -0
@@ -0,0 +1,114 @@
1
+ import type { ElementType, HTMLAttributes, ReactNode } from "react";
2
+
3
+ import { cn } from "../utilities/cn";
4
+
5
+ import {
6
+ WIDGET_FRAME_BODY_TEXT_CLASS,
7
+ WIDGET_FRAME_SECTION_HEADING_CLASS,
8
+ WIDGET_FRAME_SUBTITLE_CLASS,
9
+ } from "./widget-frame-typography";
10
+
11
+ const WIDGET_SUBTITLE_CLASS = cn("mt-0", WIDGET_FRAME_SUBTITLE_CLASS);
12
+ const DETAIL_COPY_CLASS = cn("m-0", WIDGET_FRAME_BODY_TEXT_CLASS);
13
+ const EMPTY_STATE_CLASS =
14
+ "grid min-h-60 items-start gap-1.5 rounded-2xl border border-dashed border-outline-variant bg-surface-container-low p-5 [&_h3]:m-0";
15
+ const EMPTY_STATE_COMPACT_CLASS = "min-h-0";
16
+
17
+ export interface WidgetSubtitleProps extends HTMLAttributes<HTMLElement> {
18
+ as?: ElementType;
19
+ children: ReactNode;
20
+ }
21
+
22
+ export function WidgetSubtitle({
23
+ as: Component = "p",
24
+ children,
25
+ className,
26
+ ...props
27
+ }: WidgetSubtitleProps) {
28
+ return (
29
+ <Component className={cn(WIDGET_SUBTITLE_CLASS, className)} {...props}>
30
+ {children}
31
+ </Component>
32
+ );
33
+ }
34
+
35
+ export interface WidgetDetailCopyProps
36
+ extends HTMLAttributes<HTMLParagraphElement> {
37
+ children: ReactNode;
38
+ }
39
+
40
+ export function WidgetDetailCopy({
41
+ children,
42
+ className,
43
+ ...props
44
+ }: WidgetDetailCopyProps) {
45
+ return (
46
+ <p className={cn(DETAIL_COPY_CLASS, className)} {...props}>
47
+ {children}
48
+ </p>
49
+ );
50
+ }
51
+
52
+ export interface WidgetEmptyStateProps extends HTMLAttributes<HTMLDivElement> {
53
+ children: ReactNode;
54
+ compact?: boolean;
55
+ }
56
+
57
+ export function WidgetEmptyState({
58
+ children,
59
+ className,
60
+ compact = false,
61
+ ...props
62
+ }: WidgetEmptyStateProps) {
63
+ return (
64
+ <div
65
+ className={cn(
66
+ EMPTY_STATE_CLASS,
67
+ compact && EMPTY_STATE_COMPACT_CLASS,
68
+ className,
69
+ )}
70
+ {...props}
71
+ >
72
+ {children}
73
+ </div>
74
+ );
75
+ }
76
+
77
+ export interface WidgetEmptyStateTitleProps
78
+ extends HTMLAttributes<HTMLElement> {
79
+ as?: ElementType;
80
+ children: ReactNode;
81
+ }
82
+
83
+ export function WidgetEmptyStateTitle({
84
+ as: Component = "h3",
85
+ children,
86
+ className,
87
+ ...props
88
+ }: WidgetEmptyStateTitleProps) {
89
+ return (
90
+ <Component
91
+ className={cn(WIDGET_FRAME_SECTION_HEADING_CLASS, className)}
92
+ {...props}
93
+ >
94
+ {children}
95
+ </Component>
96
+ );
97
+ }
98
+
99
+ export interface WidgetEmptyStateTextProps
100
+ extends HTMLAttributes<HTMLParagraphElement> {
101
+ children: ReactNode;
102
+ }
103
+
104
+ export function WidgetEmptyStateText({
105
+ children,
106
+ className,
107
+ ...props
108
+ }: WidgetEmptyStateTextProps) {
109
+ return (
110
+ <p className={cn("m-0", WIDGET_FRAME_BODY_TEXT_CLASS, className)} {...props}>
111
+ {children}
112
+ </p>
113
+ );
114
+ }
@@ -0,0 +1,161 @@
1
+ import {
2
+ forwardRef,
3
+ type ButtonHTMLAttributes,
4
+ type HTMLAttributes,
5
+ type ReactNode,
6
+ } from "react";
7
+
8
+ import { cn } from "../utilities/cn";
9
+
10
+ import { WIDGET_FRAME_BODY_TEXT_CLASS } from "./widget-frame-typography";
11
+
12
+ const WIDGET_FRAME_DISCLOSURE_CLASS = "grid min-w-0 gap-2.5";
13
+ const WIDGET_FRAME_DISCLOSURE_TRIGGER_CLASS = cn(
14
+ "inline-flex min-h-9 shrink-0 cursor-pointer items-center justify-center gap-1.5 rounded-lg border border-outline bg-surface-container-high px-2.5 py-2 text-on-surface-variant transition hover:border-outline-variant hover:bg-af-overlay hover:text-on-surface focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-af-accent disabled:cursor-not-allowed disabled:border-outline disabled:bg-surface-container-low disabled:text-on-surface-disabled",
15
+ WIDGET_FRAME_BODY_TEXT_CLASS,
16
+ );
17
+ const WIDGET_FRAME_DISCLOSURE_ICON_CLASS =
18
+ "h-3.5 w-3.5 shrink-0 text-current transition-transform duration-150";
19
+
20
+ export interface WidgetFrameDisclosureProps
21
+ extends HTMLAttributes<HTMLDivElement> {
22
+ children: ReactNode;
23
+ }
24
+
25
+ export function WidgetFrameDisclosure({
26
+ children,
27
+ className,
28
+ ...props
29
+ }: WidgetFrameDisclosureProps) {
30
+ return (
31
+ <div className={cn(WIDGET_FRAME_DISCLOSURE_CLASS, className)} {...props}>
32
+ {children}
33
+ </div>
34
+ );
35
+ }
36
+
37
+ export interface WidgetFrameDisclosureIconProps {
38
+ expanded: boolean;
39
+ className?: string;
40
+ }
41
+
42
+ export function WidgetFrameDisclosureIcon({
43
+ className,
44
+ expanded,
45
+ }: WidgetFrameDisclosureIconProps) {
46
+ return (
47
+ <svg
48
+ aria-hidden="true"
49
+ className={cn(
50
+ WIDGET_FRAME_DISCLOSURE_ICON_CLASS,
51
+ expanded ? "rotate-180" : "rotate-0",
52
+ className,
53
+ )}
54
+ fill="none"
55
+ focusable="false"
56
+ viewBox="0 0 16 16"
57
+ xmlns="http://www.w3.org/2000/svg"
58
+ >
59
+ <path
60
+ d="M4 6l4 4 4-4"
61
+ stroke="currentColor"
62
+ strokeLinecap="round"
63
+ strokeLinejoin="round"
64
+ strokeWidth="1.8"
65
+ />
66
+ </svg>
67
+ );
68
+ }
69
+
70
+ type WidgetFrameDisclosureTriggerBaseProps = Omit<
71
+ ButtonHTMLAttributes<HTMLButtonElement>,
72
+ "aria-expanded" | "aria-controls" | "children" | "type"
73
+ > & {
74
+ controlsID: string;
75
+ expanded: boolean;
76
+ onExpandedChange?: (expanded: boolean) => void;
77
+ type?: "button";
78
+ };
79
+
80
+ type WidgetFrameDisclosureTriggerWithLabelProps =
81
+ WidgetFrameDisclosureTriggerBaseProps & {
82
+ children: ReactNode;
83
+ "aria-label"?: string;
84
+ };
85
+
86
+ type WidgetFrameDisclosureTriggerIconOnlyProps =
87
+ WidgetFrameDisclosureTriggerBaseProps & {
88
+ children?: never;
89
+ "aria-label": string;
90
+ };
91
+
92
+ export type WidgetFrameDisclosureTriggerProps =
93
+ | WidgetFrameDisclosureTriggerWithLabelProps
94
+ | WidgetFrameDisclosureTriggerIconOnlyProps;
95
+
96
+ export const WidgetFrameDisclosureTrigger = forwardRef<
97
+ HTMLButtonElement,
98
+ WidgetFrameDisclosureTriggerProps
99
+ >(function WidgetFrameDisclosureTrigger(
100
+ {
101
+ children,
102
+ className,
103
+ controlsID,
104
+ expanded,
105
+ onClick,
106
+ onExpandedChange,
107
+ type = "button",
108
+ ...props
109
+ },
110
+ ref,
111
+ ) {
112
+ const handleClick: ButtonHTMLAttributes<HTMLButtonElement>["onClick"] = (
113
+ event,
114
+ ) => {
115
+ onClick?.(event);
116
+ if (!event.defaultPrevented) {
117
+ onExpandedChange?.(!expanded);
118
+ }
119
+ };
120
+
121
+ return (
122
+ <button
123
+ aria-controls={controlsID}
124
+ aria-expanded={expanded}
125
+ className={cn(WIDGET_FRAME_DISCLOSURE_TRIGGER_CLASS, className)}
126
+ onClick={handleClick}
127
+ ref={ref}
128
+ type={type}
129
+ {...props}
130
+ >
131
+ <WidgetFrameDisclosureIcon expanded={expanded} />
132
+ {children}
133
+ </button>
134
+ );
135
+ });
136
+
137
+ export interface WidgetFrameDisclosurePanelProps
138
+ extends HTMLAttributes<HTMLDivElement> {
139
+ children: ReactNode;
140
+ expanded: boolean;
141
+ id: string;
142
+ }
143
+
144
+ export function WidgetFrameDisclosurePanel({
145
+ children,
146
+ className,
147
+ expanded,
148
+ id,
149
+ ...props
150
+ }: WidgetFrameDisclosurePanelProps) {
151
+ return (
152
+ <div
153
+ className={cn("min-w-0", className)}
154
+ hidden={!expanded}
155
+ id={id}
156
+ {...props}
157
+ >
158
+ {children}
159
+ </div>
160
+ );
161
+ }
@@ -0,0 +1,40 @@
1
+ import type { CSSProperties } from "react";
2
+
3
+ import { cn } from "../utilities/cn";
4
+
5
+ import { WIDGET_FRAME_SUPPORTING_LABELS_CLASS } from "./widget-frame-typography";
6
+
7
+ export const WIDGET_FRAME_MIN_WIDTH_CLASS = "min-w-0";
8
+
9
+ export const WIDGET_FRAME_WIDE_BODY_CLASS = "min-h-72";
10
+
11
+ export const WIDGET_FRAME_RESPONSIVE_SHELL_CLASS = "min-w-0 w-full";
12
+
13
+ export const WIDGET_FRAME_STORY_SHELL_DATA_ATTR = "data-widget-frame-story-shell";
14
+
15
+ export const WIDGET_FRAME_OVERFLOW_TOLERANCE_PX = 1;
16
+
17
+ /** Detail-card layout recipe for description lists inside widget frames. */
18
+ export const widgetFrameDetailCardClass = cn(
19
+ "[&_dd]:m-0 [&_dl]:m-0 [&_dl]:grid [&_dl]:gap-3 [&_dl_div:first-child]:border-t-0 [&_dl_div:first-child]:pt-0 [&_dl_div]:border-t [&_dl_div]:border-outline [&_dl_div]:pt-3 [&_dt]:mb-1 [&_h3]:mt-0",
20
+ WIDGET_FRAME_SUPPORTING_LABELS_CLASS,
21
+ );
22
+
23
+ export function widgetFrameStoryShellStyle(maxWidth: string): {
24
+ style: CSSProperties;
25
+ } {
26
+ return {
27
+ style: {
28
+ maxWidth,
29
+ padding: "1rem",
30
+ width: "100%",
31
+ },
32
+ };
33
+ }
34
+
35
+ export function widgetFrameHasNoHorizontalOverflow(
36
+ element: HTMLElement,
37
+ tolerancePx = WIDGET_FRAME_OVERFLOW_TOLERANCE_PX,
38
+ ): boolean {
39
+ return element.scrollWidth <= element.clientWidth + tolerancePx;
40
+ }
@@ -0,0 +1,16 @@
1
+ import type { HTMLAttributes } from "react";
2
+
3
+ import { cn } from "../utilities/cn";
4
+
5
+ export function WidgetFrameSkeleton({
6
+ className,
7
+ ...props
8
+ }: HTMLAttributes<HTMLDivElement>) {
9
+ return (
10
+ <div
11
+ aria-hidden="true"
12
+ className={cn("animate-pulse rounded-xl bg-af-overlay", className)}
13
+ {...props}
14
+ />
15
+ );
16
+ }
@@ -0,0 +1,99 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+
3
+ import { cn } from "../utilities/cn";
4
+
5
+ import { WidgetFrameSkeleton } from "./widget-frame-skeleton";
6
+
7
+ const WIDGET_STATE_PANEL_CLASS = "grid gap-2";
8
+ const WIDGET_ERROR_STATE_CLASS =
9
+ "rounded-2xl border border-af-danger-border bg-error-container p-5 text-on-error-container";
10
+ const WIDGET_SUCCESS_STATE_CLASS =
11
+ "rounded-2xl border border-af-success-border bg-success-container p-5 text-on-success-container";
12
+ const WIDGET_LOADING_PLACEHOLDER_CLASS = "grid gap-2 pt-2";
13
+
14
+ export interface WidgetLoadingStateProps extends HTMLAttributes<HTMLDivElement> {
15
+ children: ReactNode;
16
+ placeholder?: ReactNode;
17
+ showDefaultPlaceholder?: boolean;
18
+ }
19
+
20
+ export function WidgetLoadingState({
21
+ children,
22
+ className,
23
+ placeholder,
24
+ showDefaultPlaceholder = true,
25
+ ...props
26
+ }: WidgetLoadingStateProps) {
27
+ const resolvedPlaceholder =
28
+ placeholder ??
29
+ (showDefaultPlaceholder ? (
30
+ <>
31
+ <WidgetFrameSkeleton className="h-4 w-full max-w-48" />
32
+ <WidgetFrameSkeleton className="h-24 w-full" />
33
+ <WidgetFrameSkeleton className="h-4 w-full max-w-48" />
34
+ </>
35
+ ) : null);
36
+
37
+ return (
38
+ <div
39
+ aria-busy="true"
40
+ className={cn(WIDGET_STATE_PANEL_CLASS, className)}
41
+ role="status"
42
+ {...props}
43
+ >
44
+ {children}
45
+ {resolvedPlaceholder ? (
46
+ <div
47
+ aria-hidden="true"
48
+ className={WIDGET_LOADING_PLACEHOLDER_CLASS}
49
+ >
50
+ {resolvedPlaceholder}
51
+ </div>
52
+ ) : null}
53
+ </div>
54
+ );
55
+ }
56
+
57
+ export interface WidgetErrorStateProps extends HTMLAttributes<HTMLDivElement> {
58
+ children: ReactNode;
59
+ }
60
+
61
+ export function WidgetErrorState({
62
+ children,
63
+ className,
64
+ ...props
65
+ }: WidgetErrorStateProps) {
66
+ return (
67
+ <div
68
+ className={cn(WIDGET_ERROR_STATE_CLASS, WIDGET_STATE_PANEL_CLASS, className)}
69
+ role="alert"
70
+ {...props}
71
+ >
72
+ {children}
73
+ </div>
74
+ );
75
+ }
76
+
77
+ export interface WidgetSuccessStateProps extends HTMLAttributes<HTMLDivElement> {
78
+ children: ReactNode;
79
+ }
80
+
81
+ export function WidgetSuccessState({
82
+ children,
83
+ className,
84
+ ...props
85
+ }: WidgetSuccessStateProps) {
86
+ return (
87
+ <div
88
+ className={cn(
89
+ WIDGET_SUCCESS_STATE_CLASS,
90
+ WIDGET_STATE_PANEL_CLASS,
91
+ className,
92
+ )}
93
+ role="status"
94
+ {...props}
95
+ >
96
+ {children}
97
+ </div>
98
+ );
99
+ }
@@ -0,0 +1,19 @@
1
+ /** Section heading typography for widget frame titles and empty-state headings. */
2
+ export const WIDGET_FRAME_SECTION_HEADING_CLASS =
3
+ "text-title-large uppercase text-on-surface";
4
+
5
+ /** Body copy typography for widget frame detail regions. */
6
+ export const WIDGET_FRAME_BODY_TEXT_CLASS =
7
+ "text-body-medium text-on-surface-variant";
8
+
9
+ /** Monospace subtitle typography for metric and metadata values. */
10
+ export const WIDGET_FRAME_SUBTITLE_CLASS =
11
+ "font-mono text-display-small text-on-surface-variant [overflow-wrap:anywhere]";
12
+
13
+ /** Supporting label typography for description-list terms. */
14
+ export const WIDGET_FRAME_SUPPORTING_LABEL_CLASS =
15
+ "text-label-medium uppercase text-on-surface-subtle";
16
+
17
+ /** Description-list label styling for widget detail cards. */
18
+ export const WIDGET_FRAME_SUPPORTING_LABELS_CLASS =
19
+ "[&_dt]:text-label-medium [&_dt]:uppercase [&_dt]:text-on-surface-subtle";
@@ -0,0 +1,84 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+
3
+ import { cn } from "../utilities/cn";
4
+
5
+ import { WIDGET_FRAME_BODY_TEXT_CLASS } from "./widget-frame-typography";
6
+ import {
7
+ WIDGET_FRAME_MIN_WIDTH_CLASS,
8
+ WIDGET_FRAME_WIDE_BODY_CLASS,
9
+ widgetFrameDetailCardClass,
10
+ } from "./widget-frame-layout";
11
+
12
+ const WIDGET_FRAME_SHELL_CLASS =
13
+ "flex min-w-0 flex-col rounded-lg border border-outline bg-surface-container-high text-on-surface shadow-af-card";
14
+ const WIDGET_FRAME_SCROLL_CLASS = "overflow-hidden";
15
+ const WIDGET_FRAME_HEADER_CLASS =
16
+ "relative z-10 flex min-h-13 shrink-0 items-center justify-between gap-3 border-b border-outline bg-surface-container-high px-3.5 py-3";
17
+ const WIDGET_FRAME_HEADER_TOOLS_CLASS =
18
+ "flex min-w-0 shrink-0 items-center gap-2";
19
+ const WIDGET_FRAME_BODY_SCROLL_CLASS = "min-h-0 flex-1 overflow-auto";
20
+ const WIDGET_FRAME_BODY_CLASS = cn(
21
+ "grid min-h-0 gap-2.5 px-3.5 pt-3.5 pb-4 [&>*]:pb-1 [&_p]:m-0",
22
+ WIDGET_FRAME_BODY_TEXT_CLASS,
23
+ );
24
+
25
+ export interface WidgetFrameProps {
26
+ bodyClassName?: string;
27
+ bodyProps?: HTMLAttributes<HTMLDivElement>;
28
+ bodyScroll?: boolean;
29
+ children: ReactNode;
30
+ className?: string;
31
+ headerAction?: ReactNode;
32
+ title: string;
33
+ wide?: boolean;
34
+ }
35
+
36
+ export function WidgetFrame({
37
+ bodyClassName,
38
+ bodyProps,
39
+ bodyScroll = true,
40
+ children,
41
+ className,
42
+ headerAction,
43
+ title,
44
+ wide = false,
45
+ }: WidgetFrameProps) {
46
+ const cardClassName = cn(
47
+ WIDGET_FRAME_SHELL_CLASS,
48
+ WIDGET_FRAME_MIN_WIDTH_CLASS,
49
+ widgetFrameDetailCardClass,
50
+ wide && WIDGET_FRAME_WIDE_BODY_CLASS,
51
+ bodyScroll && WIDGET_FRAME_SCROLL_CLASS,
52
+ className,
53
+ );
54
+ const cardBodyClassName = cn(WIDGET_FRAME_BODY_CLASS, bodyClassName);
55
+ const bodyClassNames = cn(
56
+ "min-h-0 flex-1",
57
+ bodyScroll && WIDGET_FRAME_BODY_SCROLL_CLASS,
58
+ cardBodyClassName,
59
+ );
60
+
61
+ return (
62
+ <article aria-label={title} className={cardClassName}>
63
+ <header className={WIDGET_FRAME_HEADER_CLASS}>
64
+ <div className="min-w-0 flex-1">
65
+ <h3 className="m-0 min-w-0 flex-1 text-title-large uppercase text-on-surface [overflow-wrap:anywhere]">
66
+ {title}
67
+ </h3>
68
+ </div>
69
+ <div className={WIDGET_FRAME_HEADER_TOOLS_CLASS}>
70
+ {headerAction ?? (
71
+ <span
72
+ aria-hidden="true"
73
+ className="block h-10 w-10 shrink-0"
74
+ data-widget-frame-header-action-spacer="true"
75
+ />
76
+ )}
77
+ </div>
78
+ </header>
79
+ <div className={bodyClassNames} {...bodyProps}>
80
+ {children}
81
+ </div>
82
+ </article>
83
+ );
84
+ }
@@ -0,0 +1,167 @@
1
+ /*
2
+ * Predefined Material-style palette presets (US-008).
3
+ *
4
+ * Each preset overrides af-foundation-* keys on :root via data-color-palette.
5
+ * Role tokens in color-role-tokens.css and aliases cascade from these keys.
6
+ */
7
+
8
+ :root,
9
+ [data-color-palette="factory-dark"] {
10
+ --color-af-foundation-background: #050b10;
11
+ --color-af-foundation-background-start: #090f15;
12
+ --color-af-foundation-background-mid: #0c151d;
13
+ --color-af-foundation-canvas: #050b10;
14
+ --color-af-foundation-surface: #181f2b;
15
+ --color-af-foundation-ink: #f7f2e8;
16
+ --color-af-foundation-code-ink: #d5fbff;
17
+ --color-af-foundation-overlay: #ffffff;
18
+ --color-af-foundation-accent: #f5c76f;
19
+ --color-af-foundation-accent-strong: #ecbf58;
20
+ --color-af-foundation-accent-ink: #1a2228;
21
+ --color-af-foundation-info: #5ccadd;
22
+ --color-af-foundation-info-strong: #5097be;
23
+ --color-af-foundation-info-bright: #7dd3fc;
24
+ --color-af-foundation-info-ink: #b5edf4;
25
+ --color-af-foundation-secondary-accent: #507f8c;
26
+ --color-af-foundation-secondary-accent-ink: #8aaeb8;
27
+ --color-af-foundation-tertiary-accent: #6d6290;
28
+ --color-af-foundation-tertiary-accent-ink: #a89ec4;
29
+ --color-af-foundation-success: #57c18b;
30
+ --color-af-foundation-success-ink: #a7f0c4;
31
+ --color-af-foundation-warning: #f0b15a;
32
+ --color-af-foundation-warning-ink: #ffd8a3;
33
+ --color-af-foundation-danger: #f05f5f;
34
+ --color-af-foundation-danger-bright: #ff8a8a;
35
+ --color-af-foundation-danger-ink: #ffb2b2;
36
+ --color-af-foundation-worker: #b68cff;
37
+ --color-af-foundation-worker-ink: #dfccff;
38
+ --color-af-shadow: #000000;
39
+ }
40
+
41
+ [data-color-palette="factory-light"] {
42
+ --color-af-foundation-background: #f4f1e8;
43
+ --color-af-foundation-background-start: #ece8dc;
44
+ --color-af-foundation-background-mid: #e4ded0;
45
+ --color-af-foundation-canvas: #faf8f3;
46
+ --color-af-foundation-surface: #ffffff;
47
+ --color-af-foundation-ink: #1a2228;
48
+ --color-af-foundation-code-ink: #0f4c5c;
49
+ --color-af-foundation-overlay: #000000;
50
+ --color-af-foundation-accent: #f5c76f;
51
+ --color-af-foundation-accent-strong: #c9972e;
52
+ --color-af-foundation-accent-ink: #1a2228;
53
+ --color-af-foundation-info: #2f8fad;
54
+ --color-af-foundation-info-strong: #256f89;
55
+ --color-af-foundation-info-bright: #4aa9c9;
56
+ --color-af-foundation-info-ink: #1f6178;
57
+ --color-af-foundation-secondary-accent: #4a7a87;
58
+ --color-af-foundation-secondary-accent-ink: #355962;
59
+ --color-af-foundation-tertiary-accent: #5c5280;
60
+ --color-af-foundation-tertiary-accent-ink: #433b61;
61
+ --color-af-foundation-success: #2f9a66;
62
+ --color-af-foundation-success-ink: #1f6f49;
63
+ --color-af-foundation-warning: #c9852d;
64
+ --color-af-foundation-warning-ink: #8f5d16;
65
+ --color-af-foundation-danger: #d94848;
66
+ --color-af-foundation-danger-bright: #ef6b6b;
67
+ --color-af-foundation-danger-ink: #9f2f2f;
68
+ --color-af-foundation-worker: #7f63b8;
69
+ --color-af-foundation-worker-ink: #5d4788;
70
+ --color-af-shadow: #1a2228;
71
+ }
72
+
73
+ [data-color-palette="material-baseline"] {
74
+ --color-af-foundation-background: #141218;
75
+ --color-af-foundation-background-start: #17151b;
76
+ --color-af-foundation-background-mid: #1b1920;
77
+ --color-af-foundation-canvas: #0f0d12;
78
+ --color-af-foundation-surface: #1d1b20;
79
+ --color-af-foundation-ink: #e6e0e9;
80
+ --color-af-foundation-code-ink: #c8f0ff;
81
+ --color-af-foundation-overlay: #ffffff;
82
+ --color-af-foundation-accent: #f5c76f;
83
+ --color-af-foundation-accent-strong: #ecbf58;
84
+ --color-af-foundation-accent-ink: #1a2228;
85
+ --color-af-foundation-info: #67cbe0;
86
+ --color-af-foundation-info-strong: #4f9db8;
87
+ --color-af-foundation-info-bright: #8adcf0;
88
+ --color-af-foundation-info-ink: #b8edf8;
89
+ --color-af-foundation-secondary-accent: #5a8894;
90
+ --color-af-foundation-secondary-accent-ink: #94b4bd;
91
+ --color-af-foundation-tertiary-accent: #7a7198;
92
+ --color-af-foundation-tertiary-accent-ink: #b0a8c8;
93
+ --color-af-foundation-success: #63c995;
94
+ --color-af-foundation-success-ink: #a8f0c8;
95
+ --color-af-foundation-warning: #efb865;
96
+ --color-af-foundation-warning-ink: #ffddb0;
97
+ --color-af-foundation-danger: #ef6a6a;
98
+ --color-af-foundation-danger-bright: #ff9494;
99
+ --color-af-foundation-danger-ink: #ffb8b8;
100
+ --color-af-foundation-worker: #c0a8f0;
101
+ --color-af-foundation-worker-ink: #e4d8ff;
102
+ --color-af-shadow: #000000;
103
+ }
104
+
105
+ [data-color-palette="slate"] {
106
+ --color-af-foundation-background: #0d1117;
107
+ --color-af-foundation-background-start: #101620;
108
+ --color-af-foundation-background-mid: #131b28;
109
+ --color-af-foundation-canvas: #070b10;
110
+ --color-af-foundation-surface: #161b22;
111
+ --color-af-foundation-ink: #e6edf3;
112
+ --color-af-foundation-code-ink: #c7e7ff;
113
+ --color-af-foundation-overlay: #ffffff;
114
+ --color-af-foundation-accent: #f5c76f;
115
+ --color-af-foundation-accent-strong: #ecbf58;
116
+ --color-af-foundation-accent-ink: #1a2228;
117
+ --color-af-foundation-info: #58b8d8;
118
+ --color-af-foundation-info-strong: #468ea8;
119
+ --color-af-foundation-info-bright: #7ecbf0;
120
+ --color-af-foundation-info-ink: #b0e0f2;
121
+ --color-af-foundation-secondary-accent: #6e9bae;
122
+ --color-af-foundation-secondary-accent-ink: #9bb8c8;
123
+ --color-af-foundation-tertiary-accent: #7b8cde;
124
+ --color-af-foundation-tertiary-accent-ink: #adb6e8;
125
+ --color-af-foundation-success: #56b888;
126
+ --color-af-foundation-success-ink: #a4e8c4;
127
+ --color-af-foundation-warning: #e8ad58;
128
+ --color-af-foundation-warning-ink: #ffd49a;
129
+ --color-af-foundation-danger: #e86262;
130
+ --color-af-foundation-danger-bright: #ff8e8e;
131
+ --color-af-foundation-danger-ink: #ffb0b0;
132
+ --color-af-foundation-worker: #a898f0;
133
+ --color-af-foundation-worker-ink: #d8d0ff;
134
+ --color-af-shadow: #000000;
135
+ }
136
+
137
+ [data-color-palette="olive"] {
138
+ --color-af-foundation-background: #13150f;
139
+ --color-af-foundation-background-start: #171912;
140
+ --color-af-foundation-background-mid: #1b1e15;
141
+ --color-af-foundation-canvas: #0a0c08;
142
+ --color-af-foundation-surface: #1a1d15;
143
+ --color-af-foundation-ink: #eef2e4;
144
+ --color-af-foundation-code-ink: #d0f5e8;
145
+ --color-af-foundation-overlay: #ffffff;
146
+ --color-af-foundation-accent: #f5c76f;
147
+ --color-af-foundation-accent-strong: #ecbf58;
148
+ --color-af-foundation-accent-ink: #1a2228;
149
+ --color-af-foundation-info: #58b0a0;
150
+ --color-af-foundation-info-strong: #458a7d;
151
+ --color-af-foundation-info-bright: #7ecfb8;
152
+ --color-af-foundation-info-ink: #b0e8d8;
153
+ --color-af-foundation-secondary-accent: #6a8f72;
154
+ --color-af-foundation-secondary-accent-ink: #98b8a0;
155
+ --color-af-foundation-tertiary-accent: #8a7d5c;
156
+ --color-af-foundation-tertiary-accent-ink: #b8aa88;
157
+ --color-af-foundation-success: #62b878;
158
+ --color-af-foundation-success-ink: #a8e8bc;
159
+ --color-af-foundation-warning: #e8b058;
160
+ --color-af-foundation-warning-ink: #ffd898;
161
+ --color-af-foundation-danger: #e06868;
162
+ --color-af-foundation-danger-ink: #ffb0b0;
163
+ --color-af-foundation-danger-bright: #ff9090;
164
+ --color-af-foundation-worker: #b0a070;
165
+ --color-af-foundation-worker-ink: #e0d4b0;
166
+ --color-af-shadow: #000000;
167
+ }