@tonycodes-ui/mortar 0.0.0-stage → 0.11.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 (104) hide show
  1. package/AGENTS.md +10 -0
  2. package/CHANGELOG.md +276 -0
  3. package/CONTRIBUTING.md +48 -0
  4. package/DESIGN.md +51 -0
  5. package/LICENSE +21 -0
  6. package/README.md +155 -2
  7. package/TESTING.md +83 -0
  8. package/baseline-policy.json +16 -0
  9. package/bin/mortar.mjs +258 -0
  10. package/catalog.json +9703 -0
  11. package/cli/audit.mjs +943 -0
  12. package/cli/data.mjs +176 -0
  13. package/cli/mcp.mjs +216 -0
  14. package/cli/migrations.mjs +128 -0
  15. package/cli/packet.mjs +267 -0
  16. package/cli/search.mjs +246 -0
  17. package/cli/tools.mjs +229 -0
  18. package/design-principles.json +30 -0
  19. package/dist/agent-data.json +1 -0
  20. package/dist/application.d.ts +340 -0
  21. package/dist/application.js +240 -0
  22. package/dist/auth.d.ts +121 -0
  23. package/dist/auth.js +97 -0
  24. package/dist/brand.d.ts +69 -0
  25. package/dist/brand.js +266 -0
  26. package/dist/calendar.d.ts +75 -0
  27. package/dist/calendar.js +310 -0
  28. package/dist/choice.d.ts +36 -0
  29. package/dist/choice.js +36 -0
  30. package/dist/composer.d.ts +28 -0
  31. package/dist/composer.js +47 -0
  32. package/dist/display.d.ts +136 -0
  33. package/dist/display.js +75 -0
  34. package/dist/docs.d.ts +145 -0
  35. package/dist/docs.js +141 -0
  36. package/dist/ecommerce.d.ts +261 -0
  37. package/dist/ecommerce.js +177 -0
  38. package/dist/feedback.d.ts +80 -0
  39. package/dist/feedback.js +98 -0
  40. package/dist/foundation.d.ts +42 -0
  41. package/dist/foundation.js +121 -0
  42. package/dist/heading.d.ts +5 -0
  43. package/dist/heading.js +9 -0
  44. package/dist/icons.d.ts +50 -0
  45. package/dist/icons.js +70 -0
  46. package/dist/index.d.ts +34 -0
  47. package/dist/index.js +17 -0
  48. package/dist/inspection.d.ts +24 -0
  49. package/dist/inspection.js +14 -0
  50. package/dist/internal.d.ts +492 -0
  51. package/dist/internal.js +273 -0
  52. package/dist/layout.d.ts +107 -0
  53. package/dist/layout.js +66 -0
  54. package/dist/marketing-sections.d.ts +161 -0
  55. package/dist/marketing-sections.js +58 -0
  56. package/dist/marketing.d.ts +254 -0
  57. package/dist/marketing.js +150 -0
  58. package/dist/navigation.d.ts +35 -0
  59. package/dist/navigation.js +44 -0
  60. package/dist/primitives.d.ts +258 -0
  61. package/dist/primitives.js +244 -0
  62. package/dist/showcase.d.ts +29 -0
  63. package/dist/showcase.js +10 -0
  64. package/dist/workspace.d.ts +80 -0
  65. package/dist/workspace.js +48 -0
  66. package/docs/agents.md +127 -0
  67. package/docs/auth-flows.md +71 -0
  68. package/docs/migrating-to-0.6.md +94 -0
  69. package/docs/theming.md +170 -0
  70. package/package.json +94 -4
  71. package/skills/mortar/SKILL.md +36 -0
  72. package/src/application.css +1487 -0
  73. package/src/application.tsx +1413 -0
  74. package/src/auth.tsx +452 -0
  75. package/src/brand.ts +335 -0
  76. package/src/calendar.css +468 -0
  77. package/src/calendar.tsx +868 -0
  78. package/src/choice.tsx +240 -0
  79. package/src/composer.tsx +158 -0
  80. package/src/data.css +690 -0
  81. package/src/display.tsx +452 -0
  82. package/src/docs.css +704 -0
  83. package/src/docs.tsx +737 -0
  84. package/src/ecommerce.css +810 -0
  85. package/src/ecommerce.tsx +1033 -0
  86. package/src/feedback.tsx +388 -0
  87. package/src/foundation.css +1867 -0
  88. package/src/foundation.tsx +227 -0
  89. package/src/heading.ts +13 -0
  90. package/src/icons.tsx +237 -0
  91. package/src/index.ts +230 -0
  92. package/src/inspection.tsx +91 -0
  93. package/src/internal.tsx +614 -0
  94. package/src/layout.tsx +342 -0
  95. package/src/marketing-sections.tsx +621 -0
  96. package/src/marketing.css +2400 -0
  97. package/src/marketing.tsx +1156 -0
  98. package/src/navigation.tsx +118 -0
  99. package/src/overlays.css +350 -0
  100. package/src/primitives.tsx +1135 -0
  101. package/src/showcase.tsx +120 -0
  102. package/src/tokens.css +449 -0
  103. package/src/workspace.tsx +333 -0
  104. package/styles.css +56 -0
package/src/layout.tsx ADDED
@@ -0,0 +1,342 @@
1
+ import { useId, type ElementType, type ReactNode } from "react";
2
+ import { type HeadingLevel } from "./heading.js";
3
+ import {
4
+ componentRoot,
5
+ cx,
6
+ classedIntroSlots,
7
+ titledIntroSlots,
8
+ } from "./internal.js";
9
+
10
+ type Space = "none" | "xs" | "sm" | "md" | "lg" | "xl";
11
+
12
+ export interface StackProps {
13
+ as?: ElementType;
14
+ gap?: Space;
15
+ direction?: "column" | "row";
16
+ align?: "start" | "center" | "end" | "stretch";
17
+ justify?: "start" | "center" | "end" | "between";
18
+ wrap?: boolean;
19
+ children: ReactNode;
20
+ className?: string;
21
+ }
22
+ /** Vertical or horizontal rhythm without consumer flex/gap utilities. */
23
+ export function Stack({
24
+ as: Tag = "div",
25
+ gap = "md",
26
+ direction = "column",
27
+ align,
28
+ justify,
29
+ wrap = false,
30
+ children,
31
+ className,
32
+ }: StackProps) {
33
+ return (
34
+ <Tag
35
+ {...componentRoot("Stack")}
36
+ className={cx("m-layout-stack", className)}
37
+ data-gap={gap === "md" ? undefined : gap}
38
+ data-direction={direction === "column" ? undefined : direction}
39
+ data-align={align}
40
+ data-justify={justify}
41
+ data-wrap={wrap || undefined}
42
+ >
43
+ {children}
44
+ </Tag>
45
+ );
46
+ }
47
+
48
+ export interface ContainerProps {
49
+ as?: ElementType;
50
+ size?: "sm" | "md" | "lg" | "xl";
51
+ padded?: boolean;
52
+ children: ReactNode;
53
+ className?: string;
54
+ }
55
+ /** Centred page width with the shared gutters used by marketing and application pages. */
56
+ export function Container({
57
+ as: Tag = "div",
58
+ size = "lg",
59
+ padded = true,
60
+ children,
61
+ className,
62
+ }: ContainerProps) {
63
+ return (
64
+ <Tag
65
+ {...componentRoot("Container")}
66
+ className={cx("m-container", className)}
67
+ data-size={size === "lg" ? undefined : size}
68
+ data-padded={padded || undefined}
69
+ >
70
+ {children}
71
+ </Tag>
72
+ );
73
+ }
74
+
75
+ export interface TextProps {
76
+ as?: ElementType;
77
+ /** Text colour; `data-tone` stays reserved for status and surface tones. */
78
+ color?: "default" | "secondary" | "muted";
79
+ size?: "sm" | "md" | "lg";
80
+ weight?: "regular" | "medium" | "semibold";
81
+ children: ReactNode;
82
+ className?: string;
83
+ }
84
+ /** Body copy with semantic colour and size, instead of text-muted/text-ink utilities. */
85
+ export function Text({
86
+ as: Tag = "p",
87
+ color = "default",
88
+ size = "md",
89
+ weight = "regular",
90
+ children,
91
+ className,
92
+ }: TextProps) {
93
+ return (
94
+ <Tag
95
+ {...componentRoot("Text")}
96
+ className={cx("m-text", className)}
97
+ data-color={color === "default" ? undefined : color}
98
+ data-size={size === "md" ? undefined : size}
99
+ data-weight={weight === "regular" ? undefined : weight}
100
+ >
101
+ {children}
102
+ </Tag>
103
+ );
104
+ }
105
+
106
+ export type SectionTone = "default" | "muted" | "inverse";
107
+
108
+ export interface SectionProps {
109
+ id?: string;
110
+ eyebrow?: string;
111
+ title?: string;
112
+ description?: string;
113
+ tone?: SectionTone;
114
+ align?: "start" | "center";
115
+ headingLevel?: HeadingLevel;
116
+ children?: ReactNode;
117
+ className?: string;
118
+ }
119
+ /** Full-bleed marketing band for content that is not a grid or split feature. */
120
+ export function Section({
121
+ id,
122
+ eyebrow,
123
+ title,
124
+ description,
125
+ tone = "default",
126
+ align = "start",
127
+ headingLevel = 2,
128
+ children,
129
+ className,
130
+ }: SectionProps) {
131
+ return (
132
+ <section
133
+ id={id}
134
+ className={cx("m-marketing-section m-section", className)}
135
+ data-tone={tone === "default" ? undefined : tone}
136
+ data-align={align === "start" ? undefined : align}
137
+ {...componentRoot("Section")}
138
+ >
139
+ {...titledIntroSlots({
140
+ eyebrow,
141
+ title: title || undefined,
142
+ description,
143
+ headingLevel,
144
+ })}
145
+ {children && <div className="m-section-body">{children}</div>}
146
+ </section>
147
+ );
148
+ }
149
+
150
+ /** Brand mark: a supplied logo node, or the neutral placeholder square. */
151
+ export function BrandMark({ logo }: { logo?: ReactNode }) {
152
+ return logo ? (
153
+ <span className="m-logo" aria-hidden="true">
154
+ {logo}
155
+ </span>
156
+ ) : (
157
+ <span className="m-mark" aria-hidden="true" />
158
+ );
159
+ }
160
+
161
+ export interface DividerProps {
162
+ label?: string;
163
+ soft?: boolean;
164
+ spacing?: "none" | "sm" | "md" | "lg";
165
+ className?: string;
166
+ }
167
+ /** Horizontal rule between groups; a label names the break for assistive technology. */
168
+ export function Divider({
169
+ label,
170
+ soft = false,
171
+ spacing = "md",
172
+ className,
173
+ }: DividerProps) {
174
+ const attributes = {
175
+ ...componentRoot("Divider"),
176
+ className: cx("m-divider", className),
177
+ "data-soft": soft || undefined,
178
+ "data-spacing": spacing === "md" ? undefined : spacing,
179
+ };
180
+ return label ? (
181
+ <div {...attributes} role="separator" aria-label={label}>
182
+ <span aria-hidden="true">{label}</span>
183
+ </div>
184
+ ) : (
185
+ <hr {...attributes} />
186
+ );
187
+ }
188
+
189
+ export interface CardProps {
190
+ as?: "div" | "section" | "article";
191
+ title?: string;
192
+ description?: string;
193
+ headingLevel?: HeadingLevel;
194
+ actions?: ReactNode;
195
+ footer?: ReactNode;
196
+ padding?: "default" | "compact" | "none";
197
+ /** `muted` sets the card on the sunken surface. */
198
+ tone?: "default" | "muted";
199
+ children?: ReactNode;
200
+ id?: string;
201
+ className?: string;
202
+ }
203
+ /** Bordered surface with optional heading, header actions and footer. */
204
+ export function Card({
205
+ as: Tag = "div",
206
+ title,
207
+ description,
208
+ headingLevel = 2,
209
+ actions,
210
+ footer,
211
+ padding = "default",
212
+ tone = "default",
213
+ children,
214
+ id,
215
+ className,
216
+ }: CardProps) {
217
+ const headingId = useId();
218
+ return (
219
+ <Tag
220
+ id={id}
221
+ {...componentRoot("Card")}
222
+ className={cx("m-card", className)}
223
+ data-padding={padding === "default" ? undefined : padding}
224
+ data-tone={tone === "default" ? undefined : tone}
225
+ aria-labelledby={title && Tag !== "div" ? headingId : undefined}
226
+ >
227
+ {(title || actions) && (
228
+ <header className="m-card-header">
229
+ {title && (
230
+ <div className="m-card-heading">
231
+ {...classedIntroSlots(
232
+ {
233
+ title,
234
+ description,
235
+ id: headingId,
236
+ headingLevel,
237
+ descriptionClassName: null,
238
+ },
239
+ "m-card-title",
240
+ )}
241
+ </div>
242
+ )}
243
+ {actions && <div className="m-card-actions">{actions}</div>}
244
+ </header>
245
+ )}
246
+ {children && <div className="m-card-body">{children}</div>}
247
+ {footer && <footer className="m-card-footer">{footer}</footer>}
248
+ </Tag>
249
+ );
250
+ }
251
+
252
+ export interface MediaObjectProps {
253
+ media: ReactNode;
254
+ title?: string;
255
+ align?: "start" | "center";
256
+ reverse?: boolean;
257
+ children?: ReactNode;
258
+ className?: string;
259
+ }
260
+ /** Image, avatar or icon beside text, e.g. comments and list rows. */
261
+ export function MediaObject({
262
+ media,
263
+ title,
264
+ align = "start",
265
+ reverse = false,
266
+ children,
267
+ className,
268
+ }: MediaObjectProps) {
269
+ return (
270
+ <div
271
+ {...componentRoot("MediaObject")}
272
+ className={cx("m-media-object", className)}
273
+ data-align={align === "start" ? undefined : align}
274
+ data-reverse={reverse || undefined}
275
+ >
276
+ <div className="m-media-object-media">{media}</div>
277
+ <div className="m-media-object-body">
278
+ {title && <p className="m-media-object-title">{title}</p>}
279
+ {children}
280
+ </div>
281
+ </div>
282
+ );
283
+ }
284
+
285
+ export interface SectionHeadingProps {
286
+ title: string;
287
+ description?: string;
288
+ actions?: ReactNode;
289
+ headingLevel?: HeadingLevel;
290
+ divider?: boolean;
291
+ /** Page tabs (e.g. TabNav) below the title row; their underline replaces the divider. */
292
+ tabs?: ReactNode;
293
+ id?: string;
294
+ className?: string;
295
+ }
296
+ /** Heading for a section within a page, with optional description and actions. */
297
+ export function SectionHeading({
298
+ title,
299
+ description,
300
+ actions,
301
+ headingLevel = 2,
302
+ divider = true,
303
+ tabs,
304
+ id,
305
+ className,
306
+ }: SectionHeadingProps) {
307
+ return (
308
+ <div
309
+ id={id}
310
+ {...componentRoot("SectionHeading")}
311
+ className={cx("m-section-heading", className)}
312
+ data-divider={divider || undefined}
313
+ >
314
+ <div className="m-section-heading-text">
315
+ {...classedIntroSlots(
316
+ {
317
+ title,
318
+ description,
319
+ headingLevel,
320
+ descriptionClassName: null,
321
+ },
322
+ "m-section-heading-title",
323
+ )}
324
+ </div>
325
+ {actions && <div className="m-actions">{actions}</div>}
326
+ {tabs && <div className="m-section-heading-tabs">{tabs}</div>}
327
+ </div>
328
+ );
329
+ }
330
+
331
+ export interface ProseProps {
332
+ children: ReactNode;
333
+ className?: string;
334
+ }
335
+ /** Long-form copy with the kit's typographic rhythm. */
336
+ export function Prose({ children, className }: ProseProps) {
337
+ return (
338
+ <div {...componentRoot("Prose")} className={cx("m-prose", className)}>
339
+ {children}
340
+ </div>
341
+ );
342
+ }