@flopay/ui 0.3.0 → 0.4.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/dist/index.cjs CHANGED
@@ -24,8 +24,13 @@ __export(index_exports, {
24
24
  Button: () => Button,
25
25
  Card: () => Card,
26
26
  Container: () => Container,
27
+ EmptyState: () => EmptyState,
28
+ Field: () => Field,
29
+ Input: () => Input,
30
+ Label: () => Label,
27
31
  Section: () => Section,
28
32
  SectionHeader: () => SectionHeader,
33
+ Spinner: () => Spinner,
29
34
  cn: () => cn
30
35
  });
31
36
  module.exports = __toCommonJS(index_exports);
@@ -39,8 +44,21 @@ function cn(...inputs) {
39
44
 
40
45
  // src/components/badge.tsx
41
46
  var import_jsx_runtime = require("react/jsx-runtime");
42
- function Badge({ dot = false, className, children, ...props }) {
43
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: cn("badge", className), ...props, children: [
47
+ var variantClass = {
48
+ neutral: "",
49
+ success: "badge-success",
50
+ warning: "badge-warning",
51
+ danger: "badge-danger",
52
+ info: "badge-info"
53
+ };
54
+ function Badge({
55
+ dot = false,
56
+ variant = "neutral",
57
+ className,
58
+ children,
59
+ ...props
60
+ }) {
61
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: cn("badge", variantClass[variant], className), ...props, children: [
44
62
  dot ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "badge-dot" }) : null,
45
63
  children
46
64
  ] });
@@ -48,19 +66,43 @@ function Badge({ dot = false, className, children, ...props }) {
48
66
 
49
67
  // src/components/button.tsx
50
68
  var import_jsx_runtime2 = require("react/jsx-runtime");
51
- var variantClass = {
69
+ var variantClass2 = {
52
70
  primary: "btn-primary",
53
71
  secondary: "btn-secondary",
54
72
  outline: "btn-outline"
55
73
  };
56
- function Button({ variant = "primary", className, type = "button", ...props }) {
57
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type, className: cn(variantClass[variant], className), ...props });
74
+ function Button({
75
+ variant = "primary",
76
+ size = "md",
77
+ className,
78
+ type = "button",
79
+ ...props
80
+ }) {
81
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
82
+ "button",
83
+ {
84
+ type,
85
+ className: cn(variantClass2[variant], size === "sm" && "btn-sm", className),
86
+ ...props
87
+ }
88
+ );
58
89
  }
59
90
 
60
91
  // src/components/card.tsx
61
92
  var import_jsx_runtime3 = require("react/jsx-runtime");
62
- function Card({ className, ...props }) {
63
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: cn("card", className), ...props });
93
+ function Card({ padding = "md", interactive = false, className, ...props }) {
94
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
95
+ "div",
96
+ {
97
+ className: cn(
98
+ "card",
99
+ padding !== "none" && `card-pad-${padding}`,
100
+ interactive && "card-interactive",
101
+ className
102
+ ),
103
+ ...props
104
+ }
105
+ );
64
106
  }
65
107
 
66
108
  // src/components/container.tsx
@@ -69,16 +111,46 @@ function Container({ className, ...props }) {
69
111
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: cn("container", className), ...props });
70
112
  }
71
113
 
72
- // src/components/section.tsx
114
+ // src/components/feedback.tsx
73
115
  var import_jsx_runtime5 = require("react/jsx-runtime");
116
+ function Spinner({ center = false, className, ...props }) {
117
+ const spinner = /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: cn("spinner", className), role: "status", "aria-label": "Loading", ...props });
118
+ return center ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "spinner-center", children: spinner }) : spinner;
119
+ }
120
+ function EmptyState({ title, description, icon, className }) {
121
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: cn("empty-state", className), children: [
122
+ icon ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "empty-state-icon", children: icon }) : null,
123
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { className: "empty-state-title", children: title }),
124
+ description ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "empty-state-desc", children: description }) : null
125
+ ] });
126
+ }
127
+
128
+ // src/components/field.tsx
129
+ var import_jsx_runtime6 = require("react/jsx-runtime");
130
+ function Input({ className, ...props }) {
131
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("input", { className: cn("field", className), ...props });
132
+ }
133
+ function Label({ className, ...props }) {
134
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: cn("field-label", className), ...props });
135
+ }
136
+ function Field({ label, hint, error, htmlFor, className, children }) {
137
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: cn("field-group", className), children: [
138
+ label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Label, { htmlFor, children: label }) : null,
139
+ children,
140
+ error ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "field-error", children: error }) : hint ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "field-hint", children: hint }) : null
141
+ ] });
142
+ }
143
+
144
+ // src/components/section.tsx
145
+ var import_jsx_runtime7 = require("react/jsx-runtime");
74
146
  function Section({ className, ...props }) {
75
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("section", { className: cn("section", className), ...props });
147
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("section", { className: cn("section", className), ...props });
76
148
  }
77
149
  function SectionHeader({ label, title, description, className }) {
78
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: cn("section-center", className), children: [
79
- label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "section-label", children: label }) : null,
80
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { className: "section-title", children: title }),
81
- description ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "section-desc", children: description }) : null
150
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: cn("section-center", className), children: [
151
+ label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "section-label", children: label }) : null,
152
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { className: "section-title", children: title }),
153
+ description ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "section-desc", children: description }) : null
82
154
  ] });
83
155
  }
84
156
  // Annotate the CommonJS export names for ESM import in node:
@@ -87,8 +159,13 @@ function SectionHeader({ label, title, description, className }) {
87
159
  Button,
88
160
  Card,
89
161
  Container,
162
+ EmptyState,
163
+ Field,
164
+ Input,
165
+ Label,
90
166
  Section,
91
167
  SectionHeader,
168
+ Spinner,
92
169
  cn
93
170
  });
94
171
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/cn.ts","../src/components/badge.tsx","../src/components/button.tsx","../src/components/card.tsx","../src/components/container.tsx","../src/components/section.tsx"],"sourcesContent":["export { cn } from './cn';\nexport type { BadgeProps } from './components/badge';\nexport { Badge } from './components/badge';\nexport type { ButtonProps, ButtonVariant } from './components/button';\nexport { Button } from './components/button';\nexport type { CardProps } from './components/card';\nexport { Card } from './components/card';\nexport type { ContainerProps } from './components/container';\nexport { Container } from './components/container';\nexport type { SectionHeaderProps, SectionProps } from './components/section';\nexport { Section, SectionHeader } from './components/section';\n","import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merge class names, resolving conflicting Tailwind utilities (last wins).\n * Mirrors the helper used across the FloPay frontends so consumers can extend\n * any primitive's classes safely.\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import type { HTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {\n /** Render a leading status dot. */\n dot?: boolean;\n}\n\n/** Pill badge, optionally with a leading status dot. */\nexport function Badge({ dot = false, className, children, ...props }: BadgeProps) {\n return (\n <span className={cn('badge', className)} {...props}>\n {dot ? <span className=\"badge-dot\" /> : null}\n {children}\n </span>\n );\n}\n","import type { ButtonHTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'outline';\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n}\n\nconst variantClass: Record<ButtonVariant, string> = {\n primary: 'btn-primary',\n secondary: 'btn-secondary',\n outline: 'btn-outline',\n};\n\nexport function Button({ variant = 'primary', className, type = 'button', ...props }: ButtonProps) {\n return <button type={type} className={cn(variantClass[variant], className)} {...props} />;\n}\n","import type { HTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport type CardProps = HTMLAttributes<HTMLDivElement>;\n\n/** Bordered surface card with hover elevation. */\nexport function Card({ className, ...props }: CardProps) {\n return <div className={cn('card', className)} {...props} />;\n}\n","import type { HTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport type ContainerProps = HTMLAttributes<HTMLDivElement>;\n\n/** Centered, max-width content wrapper (var(--max-w)) with horizontal padding. */\nexport function Container({ className, ...props }: ContainerProps) {\n return <div className={cn('container', className)} {...props} />;\n}\n","import type { HTMLAttributes, ReactNode } from 'react';\nimport { cn } from '../cn';\n\nexport type SectionProps = HTMLAttributes<HTMLElement>;\n\n/** Vertical page section with max-width and standard padding. */\nexport function Section({ className, ...props }: SectionProps) {\n return <section className={cn('section', className)} {...props} />;\n}\n\nexport interface SectionHeaderProps {\n /** Small eyebrow label above the title (brand colored). */\n label?: ReactNode;\n title: ReactNode;\n description?: ReactNode;\n className?: string;\n}\n\n/** Centered section heading: optional label, title, optional description. */\nexport function SectionHeader({ label, title, description, className }: SectionHeaderProps) {\n return (\n <div className={cn('section-center', className)}>\n {label ? <div className=\"section-label\">{label}</div> : null}\n <h2 className=\"section-title\">{title}</h2>\n {description ? <p className=\"section-desc\">{description}</p> : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAAsC;AACtC,4BAAwB;AAOjB,SAAS,MAAM,QAAsB;AAC1C,aAAO,mCAAQ,kBAAK,MAAM,CAAC;AAC7B;;;ACCI;AAFG,SAAS,MAAM,EAAE,MAAM,OAAO,WAAW,UAAU,GAAG,MAAM,GAAe;AAChF,SACE,6CAAC,UAAK,WAAW,GAAG,SAAS,SAAS,GAAI,GAAG,OAC1C;AAAA,UAAM,4CAAC,UAAK,WAAU,aAAY,IAAK;AAAA,IACvC;AAAA,KACH;AAEJ;;;ACAS,IAAAA,sBAAA;AAPT,IAAM,eAA8C;AAAA,EAClD,SAAS;AAAA,EACT,WAAW;AAAA,EACX,SAAS;AACX;AAEO,SAAS,OAAO,EAAE,UAAU,WAAW,WAAW,OAAO,UAAU,GAAG,MAAM,GAAgB;AACjG,SAAO,6CAAC,YAAO,MAAY,WAAW,GAAG,aAAa,OAAO,GAAG,SAAS,GAAI,GAAG,OAAO;AACzF;;;ACVS,IAAAC,sBAAA;AADF,SAAS,KAAK,EAAE,WAAW,GAAG,MAAM,GAAc;AACvD,SAAO,6CAAC,SAAI,WAAW,GAAG,QAAQ,SAAS,GAAI,GAAG,OAAO;AAC3D;;;ACDS,IAAAC,sBAAA;AADF,SAAS,UAAU,EAAE,WAAW,GAAG,MAAM,GAAmB;AACjE,SAAO,6CAAC,SAAI,WAAW,GAAG,aAAa,SAAS,GAAI,GAAG,OAAO;AAChE;;;ACDS,IAAAC,sBAAA;AADF,SAAS,QAAQ,EAAE,WAAW,GAAG,MAAM,GAAiB;AAC7D,SAAO,6CAAC,aAAQ,WAAW,GAAG,WAAW,SAAS,GAAI,GAAG,OAAO;AAClE;AAWO,SAAS,cAAc,EAAE,OAAO,OAAO,aAAa,UAAU,GAAuB;AAC1F,SACE,8CAAC,SAAI,WAAW,GAAG,kBAAkB,SAAS,GAC3C;AAAA,YAAQ,6CAAC,SAAI,WAAU,iBAAiB,iBAAM,IAAS;AAAA,IACxD,6CAAC,QAAG,WAAU,iBAAiB,iBAAM;AAAA,IACpC,cAAc,6CAAC,OAAE,WAAU,gBAAgB,uBAAY,IAAO;AAAA,KACjE;AAEJ;","names":["import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/cn.ts","../src/components/badge.tsx","../src/components/button.tsx","../src/components/card.tsx","../src/components/container.tsx","../src/components/feedback.tsx","../src/components/field.tsx","../src/components/section.tsx"],"sourcesContent":["export { cn } from './cn';\n\nexport type { BadgeProps, BadgeVariant } from './components/badge';\nexport { Badge } from './components/badge';\nexport type { ButtonProps, ButtonSize, ButtonVariant } from './components/button';\nexport { Button } from './components/button';\nexport type { CardPadding, CardProps } from './components/card';\nexport { Card } from './components/card';\nexport type { ContainerProps } from './components/container';\nexport { Container } from './components/container';\nexport type { EmptyStateProps, SpinnerProps } from './components/feedback';\nexport { EmptyState, Spinner } from './components/feedback';\nexport type { FieldProps, InputProps, LabelProps } from './components/field';\nexport { Field, Input, Label } from './components/field';\nexport type { SectionHeaderProps, SectionProps } from './components/section';\nexport { Section, SectionHeader } from './components/section';\n","import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merge class names, resolving conflicting Tailwind utilities (last wins).\n * Mirrors the helper used across the FloPay frontends so consumers can extend\n * any primitive's classes safely.\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import type { HTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport type BadgeVariant = 'neutral' | 'success' | 'warning' | 'danger' | 'info';\n\nexport interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {\n /** Render a leading status dot. */\n dot?: boolean;\n /** Semantic color. `neutral` is the default outlined pill. */\n variant?: BadgeVariant;\n}\n\nconst variantClass: Record<BadgeVariant, string> = {\n neutral: '',\n success: 'badge-success',\n warning: 'badge-warning',\n danger: 'badge-danger',\n info: 'badge-info',\n};\n\n/** Pill badge — neutral by default, or a semantic variant, optionally with a dot. */\nexport function Badge({\n dot = false,\n variant = 'neutral',\n className,\n children,\n ...props\n}: BadgeProps) {\n return (\n <span className={cn('badge', variantClass[variant], className)} {...props}>\n {dot ? <span className=\"badge-dot\" /> : null}\n {children}\n </span>\n );\n}\n","import type { ButtonHTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'outline';\nexport type ButtonSize = 'md' | 'sm';\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n}\n\nconst variantClass: Record<ButtonVariant, string> = {\n primary: 'btn-primary',\n secondary: 'btn-secondary',\n outline: 'btn-outline',\n};\n\nexport function Button({\n variant = 'primary',\n size = 'md',\n className,\n type = 'button',\n ...props\n}: ButtonProps) {\n return (\n <button\n type={type}\n className={cn(variantClass[variant], size === 'sm' && 'btn-sm', className)}\n {...props}\n />\n );\n}\n","import type { HTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport type CardPadding = 'none' | 'sm' | 'md' | 'lg';\n\nexport interface CardProps extends HTMLAttributes<HTMLDivElement> {\n /** Inner padding. Defaults to `md` (28px); use `none` to supply your own. */\n padding?: CardPadding;\n /** Add pointer cursor + hover elevation (for clickable cards). */\n interactive?: boolean;\n}\n\n/** Bordered surface card. Static by default; opt into hover with `interactive`. */\nexport function Card({ padding = 'md', interactive = false, className, ...props }: CardProps) {\n return (\n <div\n className={cn(\n 'card',\n padding !== 'none' && `card-pad-${padding}`,\n interactive && 'card-interactive',\n className,\n )}\n {...props}\n />\n );\n}\n","import type { HTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport type ContainerProps = HTMLAttributes<HTMLDivElement>;\n\n/** Centered, max-width content wrapper (var(--max-w)) with horizontal padding. */\nexport function Container({ className, ...props }: ContainerProps) {\n return <div className={cn('container', className)} {...props} />;\n}\n","import type { HTMLAttributes, ReactNode } from 'react';\nimport { cn } from '../cn';\n\nexport interface SpinnerProps extends HTMLAttributes<HTMLSpanElement> {\n /** Wrap in a centered, padded block (for loading states in a panel). */\n center?: boolean;\n}\n\n/** Brand-colored loading spinner. */\nexport function Spinner({ center = false, className, ...props }: SpinnerProps) {\n const spinner = (\n <span className={cn('spinner', className)} role=\"status\" aria-label=\"Loading\" {...props} />\n );\n return center ? <div className=\"spinner-center\">{spinner}</div> : spinner;\n}\n\nexport interface EmptyStateProps {\n title: ReactNode;\n description?: ReactNode;\n /** Optional icon rendered in the circular badge above the title. */\n icon?: ReactNode;\n className?: string;\n}\n\n/** Centered \"nothing here yet\" placeholder for empty lists/tables. */\nexport function EmptyState({ title, description, icon, className }: EmptyStateProps) {\n return (\n <div className={cn('empty-state', className)}>\n {icon ? <div className=\"empty-state-icon\">{icon}</div> : null}\n <h3 className=\"empty-state-title\">{title}</h3>\n {description ? <p className=\"empty-state-desc\">{description}</p> : null}\n </div>\n );\n}\n","import type { InputHTMLAttributes, LabelHTMLAttributes, ReactNode } from 'react';\nimport { cn } from '../cn';\n\nexport type InputProps = InputHTMLAttributes<HTMLInputElement>;\n\n/** Token-aligned text input (the `.field` look: border, surface, brand focus ring). */\nexport function Input({ className, ...props }: InputProps) {\n return <input className={cn('field', className)} {...props} />;\n}\n\nexport type LabelProps = LabelHTMLAttributes<HTMLLabelElement>;\n\nexport function Label({ className, ...props }: LabelProps) {\n return <label className={cn('field-label', className)} {...props} />;\n}\n\nexport interface FieldProps {\n label?: ReactNode;\n /** Helper text shown below the control (hidden when `error` is set). */\n hint?: ReactNode;\n /** Error text shown below the control (takes precedence over `hint`). */\n error?: ReactNode;\n /** `htmlFor` for the label, matching the control's id. */\n htmlFor?: string;\n className?: string;\n children: ReactNode;\n}\n\n/** Labelled field wrapper: optional label above and hint/error below the control. */\nexport function Field({ label, hint, error, htmlFor, className, children }: FieldProps) {\n return (\n <div className={cn('field-group', className)}>\n {label ? <Label htmlFor={htmlFor}>{label}</Label> : null}\n {children}\n {error ? (\n <span className=\"field-error\">{error}</span>\n ) : hint ? (\n <span className=\"field-hint\">{hint}</span>\n ) : null}\n </div>\n );\n}\n","import type { HTMLAttributes, ReactNode } from 'react';\nimport { cn } from '../cn';\n\nexport type SectionProps = HTMLAttributes<HTMLElement>;\n\n/** Vertical page section with max-width and standard padding. */\nexport function Section({ className, ...props }: SectionProps) {\n return <section className={cn('section', className)} {...props} />;\n}\n\nexport interface SectionHeaderProps {\n /** Small eyebrow label above the title (brand colored). */\n label?: ReactNode;\n title: ReactNode;\n description?: ReactNode;\n className?: string;\n}\n\n/** Centered section heading: optional label, title, optional description. */\nexport function SectionHeader({ label, title, description, className }: SectionHeaderProps) {\n return (\n <div className={cn('section-center', className)}>\n {label ? <div className=\"section-label\">{label}</div> : null}\n <h2 className=\"section-title\">{title}</h2>\n {description ? <p className=\"section-desc\">{description}</p> : null}\n </div>\n );\n}\n"],"mappings":"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","names":["import_jsx_runtime","variantClass","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime"]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ClassValue } from 'clsx';
2
2
  import * as react from 'react';
3
- import { HTMLAttributes, ButtonHTMLAttributes, ReactNode } from 'react';
3
+ import { HTMLAttributes, ButtonHTMLAttributes, ReactNode, InputHTMLAttributes, LabelHTMLAttributes } from 'react';
4
4
 
5
5
  /**
6
6
  * Merge class names, resolving conflicting Tailwind utilities (last wins).
@@ -9,27 +9,73 @@ import { HTMLAttributes, ButtonHTMLAttributes, ReactNode } from 'react';
9
9
  */
10
10
  declare function cn(...inputs: ClassValue[]): string;
11
11
 
12
+ type BadgeVariant = 'neutral' | 'success' | 'warning' | 'danger' | 'info';
12
13
  interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
13
14
  /** Render a leading status dot. */
14
15
  dot?: boolean;
16
+ /** Semantic color. `neutral` is the default outlined pill. */
17
+ variant?: BadgeVariant;
15
18
  }
16
- /** Pill badge, optionally with a leading status dot. */
17
- declare function Badge({ dot, className, children, ...props }: BadgeProps): react.JSX.Element;
19
+ /** Pill badge — neutral by default, or a semantic variant, optionally with a dot. */
20
+ declare function Badge({ dot, variant, className, children, ...props }: BadgeProps): react.JSX.Element;
18
21
 
19
22
  type ButtonVariant = 'primary' | 'secondary' | 'outline';
23
+ type ButtonSize = 'md' | 'sm';
20
24
  interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
21
25
  variant?: ButtonVariant;
26
+ size?: ButtonSize;
22
27
  }
23
- declare function Button({ variant, className, type, ...props }: ButtonProps): react.JSX.Element;
28
+ declare function Button({ variant, size, className, type, ...props }: ButtonProps): react.JSX.Element;
24
29
 
25
- type CardProps = HTMLAttributes<HTMLDivElement>;
26
- /** Bordered surface card with hover elevation. */
27
- declare function Card({ className, ...props }: CardProps): react.JSX.Element;
30
+ type CardPadding = 'none' | 'sm' | 'md' | 'lg';
31
+ interface CardProps extends HTMLAttributes<HTMLDivElement> {
32
+ /** Inner padding. Defaults to `md` (28px); use `none` to supply your own. */
33
+ padding?: CardPadding;
34
+ /** Add pointer cursor + hover elevation (for clickable cards). */
35
+ interactive?: boolean;
36
+ }
37
+ /** Bordered surface card. Static by default; opt into hover with `interactive`. */
38
+ declare function Card({ padding, interactive, className, ...props }: CardProps): react.JSX.Element;
28
39
 
29
40
  type ContainerProps = HTMLAttributes<HTMLDivElement>;
30
41
  /** Centered, max-width content wrapper (var(--max-w)) with horizontal padding. */
31
42
  declare function Container({ className, ...props }: ContainerProps): react.JSX.Element;
32
43
 
44
+ interface SpinnerProps extends HTMLAttributes<HTMLSpanElement> {
45
+ /** Wrap in a centered, padded block (for loading states in a panel). */
46
+ center?: boolean;
47
+ }
48
+ /** Brand-colored loading spinner. */
49
+ declare function Spinner({ center, className, ...props }: SpinnerProps): react.JSX.Element;
50
+ interface EmptyStateProps {
51
+ title: ReactNode;
52
+ description?: ReactNode;
53
+ /** Optional icon rendered in the circular badge above the title. */
54
+ icon?: ReactNode;
55
+ className?: string;
56
+ }
57
+ /** Centered "nothing here yet" placeholder for empty lists/tables. */
58
+ declare function EmptyState({ title, description, icon, className }: EmptyStateProps): react.JSX.Element;
59
+
60
+ type InputProps = InputHTMLAttributes<HTMLInputElement>;
61
+ /** Token-aligned text input (the `.field` look: border, surface, brand focus ring). */
62
+ declare function Input({ className, ...props }: InputProps): react.JSX.Element;
63
+ type LabelProps = LabelHTMLAttributes<HTMLLabelElement>;
64
+ declare function Label({ className, ...props }: LabelProps): react.JSX.Element;
65
+ interface FieldProps {
66
+ label?: ReactNode;
67
+ /** Helper text shown below the control (hidden when `error` is set). */
68
+ hint?: ReactNode;
69
+ /** Error text shown below the control (takes precedence over `hint`). */
70
+ error?: ReactNode;
71
+ /** `htmlFor` for the label, matching the control's id. */
72
+ htmlFor?: string;
73
+ className?: string;
74
+ children: ReactNode;
75
+ }
76
+ /** Labelled field wrapper: optional label above and hint/error below the control. */
77
+ declare function Field({ label, hint, error, htmlFor, className, children }: FieldProps): react.JSX.Element;
78
+
33
79
  type SectionProps = HTMLAttributes<HTMLElement>;
34
80
  /** Vertical page section with max-width and standard padding. */
35
81
  declare function Section({ className, ...props }: SectionProps): react.JSX.Element;
@@ -43,4 +89,4 @@ interface SectionHeaderProps {
43
89
  /** Centered section heading: optional label, title, optional description. */
44
90
  declare function SectionHeader({ label, title, description, className }: SectionHeaderProps): react.JSX.Element;
45
91
 
46
- export { Badge, type BadgeProps, Button, type ButtonProps, type ButtonVariant, Card, type CardProps, Container, type ContainerProps, Section, SectionHeader, type SectionHeaderProps, type SectionProps, cn };
92
+ export { Badge, type BadgeProps, type BadgeVariant, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Card, type CardPadding, type CardProps, Container, type ContainerProps, EmptyState, type EmptyStateProps, Field, type FieldProps, Input, type InputProps, Label, type LabelProps, Section, SectionHeader, type SectionHeaderProps, type SectionProps, Spinner, type SpinnerProps, cn };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ClassValue } from 'clsx';
2
2
  import * as react from 'react';
3
- import { HTMLAttributes, ButtonHTMLAttributes, ReactNode } from 'react';
3
+ import { HTMLAttributes, ButtonHTMLAttributes, ReactNode, InputHTMLAttributes, LabelHTMLAttributes } from 'react';
4
4
 
5
5
  /**
6
6
  * Merge class names, resolving conflicting Tailwind utilities (last wins).
@@ -9,27 +9,73 @@ import { HTMLAttributes, ButtonHTMLAttributes, ReactNode } from 'react';
9
9
  */
10
10
  declare function cn(...inputs: ClassValue[]): string;
11
11
 
12
+ type BadgeVariant = 'neutral' | 'success' | 'warning' | 'danger' | 'info';
12
13
  interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
13
14
  /** Render a leading status dot. */
14
15
  dot?: boolean;
16
+ /** Semantic color. `neutral` is the default outlined pill. */
17
+ variant?: BadgeVariant;
15
18
  }
16
- /** Pill badge, optionally with a leading status dot. */
17
- declare function Badge({ dot, className, children, ...props }: BadgeProps): react.JSX.Element;
19
+ /** Pill badge — neutral by default, or a semantic variant, optionally with a dot. */
20
+ declare function Badge({ dot, variant, className, children, ...props }: BadgeProps): react.JSX.Element;
18
21
 
19
22
  type ButtonVariant = 'primary' | 'secondary' | 'outline';
23
+ type ButtonSize = 'md' | 'sm';
20
24
  interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
21
25
  variant?: ButtonVariant;
26
+ size?: ButtonSize;
22
27
  }
23
- declare function Button({ variant, className, type, ...props }: ButtonProps): react.JSX.Element;
28
+ declare function Button({ variant, size, className, type, ...props }: ButtonProps): react.JSX.Element;
24
29
 
25
- type CardProps = HTMLAttributes<HTMLDivElement>;
26
- /** Bordered surface card with hover elevation. */
27
- declare function Card({ className, ...props }: CardProps): react.JSX.Element;
30
+ type CardPadding = 'none' | 'sm' | 'md' | 'lg';
31
+ interface CardProps extends HTMLAttributes<HTMLDivElement> {
32
+ /** Inner padding. Defaults to `md` (28px); use `none` to supply your own. */
33
+ padding?: CardPadding;
34
+ /** Add pointer cursor + hover elevation (for clickable cards). */
35
+ interactive?: boolean;
36
+ }
37
+ /** Bordered surface card. Static by default; opt into hover with `interactive`. */
38
+ declare function Card({ padding, interactive, className, ...props }: CardProps): react.JSX.Element;
28
39
 
29
40
  type ContainerProps = HTMLAttributes<HTMLDivElement>;
30
41
  /** Centered, max-width content wrapper (var(--max-w)) with horizontal padding. */
31
42
  declare function Container({ className, ...props }: ContainerProps): react.JSX.Element;
32
43
 
44
+ interface SpinnerProps extends HTMLAttributes<HTMLSpanElement> {
45
+ /** Wrap in a centered, padded block (for loading states in a panel). */
46
+ center?: boolean;
47
+ }
48
+ /** Brand-colored loading spinner. */
49
+ declare function Spinner({ center, className, ...props }: SpinnerProps): react.JSX.Element;
50
+ interface EmptyStateProps {
51
+ title: ReactNode;
52
+ description?: ReactNode;
53
+ /** Optional icon rendered in the circular badge above the title. */
54
+ icon?: ReactNode;
55
+ className?: string;
56
+ }
57
+ /** Centered "nothing here yet" placeholder for empty lists/tables. */
58
+ declare function EmptyState({ title, description, icon, className }: EmptyStateProps): react.JSX.Element;
59
+
60
+ type InputProps = InputHTMLAttributes<HTMLInputElement>;
61
+ /** Token-aligned text input (the `.field` look: border, surface, brand focus ring). */
62
+ declare function Input({ className, ...props }: InputProps): react.JSX.Element;
63
+ type LabelProps = LabelHTMLAttributes<HTMLLabelElement>;
64
+ declare function Label({ className, ...props }: LabelProps): react.JSX.Element;
65
+ interface FieldProps {
66
+ label?: ReactNode;
67
+ /** Helper text shown below the control (hidden when `error` is set). */
68
+ hint?: ReactNode;
69
+ /** Error text shown below the control (takes precedence over `hint`). */
70
+ error?: ReactNode;
71
+ /** `htmlFor` for the label, matching the control's id. */
72
+ htmlFor?: string;
73
+ className?: string;
74
+ children: ReactNode;
75
+ }
76
+ /** Labelled field wrapper: optional label above and hint/error below the control. */
77
+ declare function Field({ label, hint, error, htmlFor, className, children }: FieldProps): react.JSX.Element;
78
+
33
79
  type SectionProps = HTMLAttributes<HTMLElement>;
34
80
  /** Vertical page section with max-width and standard padding. */
35
81
  declare function Section({ className, ...props }: SectionProps): react.JSX.Element;
@@ -43,4 +89,4 @@ interface SectionHeaderProps {
43
89
  /** Centered section heading: optional label, title, optional description. */
44
90
  declare function SectionHeader({ label, title, description, className }: SectionHeaderProps): react.JSX.Element;
45
91
 
46
- export { Badge, type BadgeProps, Button, type ButtonProps, type ButtonVariant, Card, type CardProps, Container, type ContainerProps, Section, SectionHeader, type SectionHeaderProps, type SectionProps, cn };
92
+ export { Badge, type BadgeProps, type BadgeVariant, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Card, type CardPadding, type CardProps, Container, type ContainerProps, EmptyState, type EmptyStateProps, Field, type FieldProps, Input, type InputProps, Label, type LabelProps, Section, SectionHeader, type SectionHeaderProps, type SectionProps, Spinner, type SpinnerProps, cn };
package/dist/index.mjs CHANGED
@@ -4,8 +4,21 @@ import {
4
4
 
5
5
  // src/components/badge.tsx
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
- function Badge({ dot = false, className, children, ...props }) {
8
- return /* @__PURE__ */ jsxs("span", { className: cn("badge", className), ...props, children: [
7
+ var variantClass = {
8
+ neutral: "",
9
+ success: "badge-success",
10
+ warning: "badge-warning",
11
+ danger: "badge-danger",
12
+ info: "badge-info"
13
+ };
14
+ function Badge({
15
+ dot = false,
16
+ variant = "neutral",
17
+ className,
18
+ children,
19
+ ...props
20
+ }) {
21
+ return /* @__PURE__ */ jsxs("span", { className: cn("badge", variantClass[variant], className), ...props, children: [
9
22
  dot ? /* @__PURE__ */ jsx("span", { className: "badge-dot" }) : null,
10
23
  children
11
24
  ] });
@@ -13,19 +26,43 @@ function Badge({ dot = false, className, children, ...props }) {
13
26
 
14
27
  // src/components/button.tsx
15
28
  import { jsx as jsx2 } from "react/jsx-runtime";
16
- var variantClass = {
29
+ var variantClass2 = {
17
30
  primary: "btn-primary",
18
31
  secondary: "btn-secondary",
19
32
  outline: "btn-outline"
20
33
  };
21
- function Button({ variant = "primary", className, type = "button", ...props }) {
22
- return /* @__PURE__ */ jsx2("button", { type, className: cn(variantClass[variant], className), ...props });
34
+ function Button({
35
+ variant = "primary",
36
+ size = "md",
37
+ className,
38
+ type = "button",
39
+ ...props
40
+ }) {
41
+ return /* @__PURE__ */ jsx2(
42
+ "button",
43
+ {
44
+ type,
45
+ className: cn(variantClass2[variant], size === "sm" && "btn-sm", className),
46
+ ...props
47
+ }
48
+ );
23
49
  }
24
50
 
25
51
  // src/components/card.tsx
26
52
  import { jsx as jsx3 } from "react/jsx-runtime";
27
- function Card({ className, ...props }) {
28
- return /* @__PURE__ */ jsx3("div", { className: cn("card", className), ...props });
53
+ function Card({ padding = "md", interactive = false, className, ...props }) {
54
+ return /* @__PURE__ */ jsx3(
55
+ "div",
56
+ {
57
+ className: cn(
58
+ "card",
59
+ padding !== "none" && `card-pad-${padding}`,
60
+ interactive && "card-interactive",
61
+ className
62
+ ),
63
+ ...props
64
+ }
65
+ );
29
66
  }
30
67
 
31
68
  // src/components/container.tsx
@@ -34,16 +71,46 @@ function Container({ className, ...props }) {
34
71
  return /* @__PURE__ */ jsx4("div", { className: cn("container", className), ...props });
35
72
  }
36
73
 
37
- // src/components/section.tsx
74
+ // src/components/feedback.tsx
38
75
  import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
76
+ function Spinner({ center = false, className, ...props }) {
77
+ const spinner = /* @__PURE__ */ jsx5("span", { className: cn("spinner", className), role: "status", "aria-label": "Loading", ...props });
78
+ return center ? /* @__PURE__ */ jsx5("div", { className: "spinner-center", children: spinner }) : spinner;
79
+ }
80
+ function EmptyState({ title, description, icon, className }) {
81
+ return /* @__PURE__ */ jsxs2("div", { className: cn("empty-state", className), children: [
82
+ icon ? /* @__PURE__ */ jsx5("div", { className: "empty-state-icon", children: icon }) : null,
83
+ /* @__PURE__ */ jsx5("h3", { className: "empty-state-title", children: title }),
84
+ description ? /* @__PURE__ */ jsx5("p", { className: "empty-state-desc", children: description }) : null
85
+ ] });
86
+ }
87
+
88
+ // src/components/field.tsx
89
+ import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
90
+ function Input({ className, ...props }) {
91
+ return /* @__PURE__ */ jsx6("input", { className: cn("field", className), ...props });
92
+ }
93
+ function Label({ className, ...props }) {
94
+ return /* @__PURE__ */ jsx6("label", { className: cn("field-label", className), ...props });
95
+ }
96
+ function Field({ label, hint, error, htmlFor, className, children }) {
97
+ return /* @__PURE__ */ jsxs3("div", { className: cn("field-group", className), children: [
98
+ label ? /* @__PURE__ */ jsx6(Label, { htmlFor, children: label }) : null,
99
+ children,
100
+ error ? /* @__PURE__ */ jsx6("span", { className: "field-error", children: error }) : hint ? /* @__PURE__ */ jsx6("span", { className: "field-hint", children: hint }) : null
101
+ ] });
102
+ }
103
+
104
+ // src/components/section.tsx
105
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
39
106
  function Section({ className, ...props }) {
40
- return /* @__PURE__ */ jsx5("section", { className: cn("section", className), ...props });
107
+ return /* @__PURE__ */ jsx7("section", { className: cn("section", className), ...props });
41
108
  }
42
109
  function SectionHeader({ label, title, description, className }) {
43
- return /* @__PURE__ */ jsxs2("div", { className: cn("section-center", className), children: [
44
- label ? /* @__PURE__ */ jsx5("div", { className: "section-label", children: label }) : null,
45
- /* @__PURE__ */ jsx5("h2", { className: "section-title", children: title }),
46
- description ? /* @__PURE__ */ jsx5("p", { className: "section-desc", children: description }) : null
110
+ return /* @__PURE__ */ jsxs4("div", { className: cn("section-center", className), children: [
111
+ label ? /* @__PURE__ */ jsx7("div", { className: "section-label", children: label }) : null,
112
+ /* @__PURE__ */ jsx7("h2", { className: "section-title", children: title }),
113
+ description ? /* @__PURE__ */ jsx7("p", { className: "section-desc", children: description }) : null
47
114
  ] });
48
115
  }
49
116
  export {
@@ -51,8 +118,13 @@ export {
51
118
  Button,
52
119
  Card,
53
120
  Container,
121
+ EmptyState,
122
+ Field,
123
+ Input,
124
+ Label,
54
125
  Section,
55
126
  SectionHeader,
127
+ Spinner,
56
128
  cn
57
129
  };
58
130
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/badge.tsx","../src/components/button.tsx","../src/components/card.tsx","../src/components/container.tsx","../src/components/section.tsx"],"sourcesContent":["import type { HTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {\n /** Render a leading status dot. */\n dot?: boolean;\n}\n\n/** Pill badge, optionally with a leading status dot. */\nexport function Badge({ dot = false, className, children, ...props }: BadgeProps) {\n return (\n <span className={cn('badge', className)} {...props}>\n {dot ? <span className=\"badge-dot\" /> : null}\n {children}\n </span>\n );\n}\n","import type { ButtonHTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'outline';\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n}\n\nconst variantClass: Record<ButtonVariant, string> = {\n primary: 'btn-primary',\n secondary: 'btn-secondary',\n outline: 'btn-outline',\n};\n\nexport function Button({ variant = 'primary', className, type = 'button', ...props }: ButtonProps) {\n return <button type={type} className={cn(variantClass[variant], className)} {...props} />;\n}\n","import type { HTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport type CardProps = HTMLAttributes<HTMLDivElement>;\n\n/** Bordered surface card with hover elevation. */\nexport function Card({ className, ...props }: CardProps) {\n return <div className={cn('card', className)} {...props} />;\n}\n","import type { HTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport type ContainerProps = HTMLAttributes<HTMLDivElement>;\n\n/** Centered, max-width content wrapper (var(--max-w)) with horizontal padding. */\nexport function Container({ className, ...props }: ContainerProps) {\n return <div className={cn('container', className)} {...props} />;\n}\n","import type { HTMLAttributes, ReactNode } from 'react';\nimport { cn } from '../cn';\n\nexport type SectionProps = HTMLAttributes<HTMLElement>;\n\n/** Vertical page section with max-width and standard padding. */\nexport function Section({ className, ...props }: SectionProps) {\n return <section className={cn('section', className)} {...props} />;\n}\n\nexport interface SectionHeaderProps {\n /** Small eyebrow label above the title (brand colored). */\n label?: ReactNode;\n title: ReactNode;\n description?: ReactNode;\n className?: string;\n}\n\n/** Centered section heading: optional label, title, optional description. */\nexport function SectionHeader({ label, title, description, className }: SectionHeaderProps) {\n return (\n <div className={cn('section-center', className)}>\n {label ? <div className=\"section-label\">{label}</div> : null}\n <h2 className=\"section-title\">{title}</h2>\n {description ? <p className=\"section-desc\">{description}</p> : null}\n </div>\n );\n}\n"],"mappings":";;;;;AAWI,SACS,KADT;AAFG,SAAS,MAAM,EAAE,MAAM,OAAO,WAAW,UAAU,GAAG,MAAM,GAAe;AAChF,SACE,qBAAC,UAAK,WAAW,GAAG,SAAS,SAAS,GAAI,GAAG,OAC1C;AAAA,UAAM,oBAAC,UAAK,WAAU,aAAY,IAAK;AAAA,IACvC;AAAA,KACH;AAEJ;;;ACAS,gBAAAA,YAAA;AAPT,IAAM,eAA8C;AAAA,EAClD,SAAS;AAAA,EACT,WAAW;AAAA,EACX,SAAS;AACX;AAEO,SAAS,OAAO,EAAE,UAAU,WAAW,WAAW,OAAO,UAAU,GAAG,MAAM,GAAgB;AACjG,SAAO,gBAAAA,KAAC,YAAO,MAAY,WAAW,GAAG,aAAa,OAAO,GAAG,SAAS,GAAI,GAAG,OAAO;AACzF;;;ACVS,gBAAAC,YAAA;AADF,SAAS,KAAK,EAAE,WAAW,GAAG,MAAM,GAAc;AACvD,SAAO,gBAAAA,KAAC,SAAI,WAAW,GAAG,QAAQ,SAAS,GAAI,GAAG,OAAO;AAC3D;;;ACDS,gBAAAC,YAAA;AADF,SAAS,UAAU,EAAE,WAAW,GAAG,MAAM,GAAmB;AACjE,SAAO,gBAAAA,KAAC,SAAI,WAAW,GAAG,aAAa,SAAS,GAAI,GAAG,OAAO;AAChE;;;ACDS,gBAAAC,MAcL,QAAAC,aAdK;AADF,SAAS,QAAQ,EAAE,WAAW,GAAG,MAAM,GAAiB;AAC7D,SAAO,gBAAAD,KAAC,aAAQ,WAAW,GAAG,WAAW,SAAS,GAAI,GAAG,OAAO;AAClE;AAWO,SAAS,cAAc,EAAE,OAAO,OAAO,aAAa,UAAU,GAAuB;AAC1F,SACE,gBAAAC,MAAC,SAAI,WAAW,GAAG,kBAAkB,SAAS,GAC3C;AAAA,YAAQ,gBAAAD,KAAC,SAAI,WAAU,iBAAiB,iBAAM,IAAS;AAAA,IACxD,gBAAAA,KAAC,QAAG,WAAU,iBAAiB,iBAAM;AAAA,IACpC,cAAc,gBAAAA,KAAC,OAAE,WAAU,gBAAgB,uBAAY,IAAO;AAAA,KACjE;AAEJ;","names":["jsx","jsx","jsx","jsx","jsxs"]}
1
+ {"version":3,"sources":["../src/components/badge.tsx","../src/components/button.tsx","../src/components/card.tsx","../src/components/container.tsx","../src/components/feedback.tsx","../src/components/field.tsx","../src/components/section.tsx"],"sourcesContent":["import type { HTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport type BadgeVariant = 'neutral' | 'success' | 'warning' | 'danger' | 'info';\n\nexport interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {\n /** Render a leading status dot. */\n dot?: boolean;\n /** Semantic color. `neutral` is the default outlined pill. */\n variant?: BadgeVariant;\n}\n\nconst variantClass: Record<BadgeVariant, string> = {\n neutral: '',\n success: 'badge-success',\n warning: 'badge-warning',\n danger: 'badge-danger',\n info: 'badge-info',\n};\n\n/** Pill badge — neutral by default, or a semantic variant, optionally with a dot. */\nexport function Badge({\n dot = false,\n variant = 'neutral',\n className,\n children,\n ...props\n}: BadgeProps) {\n return (\n <span className={cn('badge', variantClass[variant], className)} {...props}>\n {dot ? <span className=\"badge-dot\" /> : null}\n {children}\n </span>\n );\n}\n","import type { ButtonHTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'outline';\nexport type ButtonSize = 'md' | 'sm';\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n}\n\nconst variantClass: Record<ButtonVariant, string> = {\n primary: 'btn-primary',\n secondary: 'btn-secondary',\n outline: 'btn-outline',\n};\n\nexport function Button({\n variant = 'primary',\n size = 'md',\n className,\n type = 'button',\n ...props\n}: ButtonProps) {\n return (\n <button\n type={type}\n className={cn(variantClass[variant], size === 'sm' && 'btn-sm', className)}\n {...props}\n />\n );\n}\n","import type { HTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport type CardPadding = 'none' | 'sm' | 'md' | 'lg';\n\nexport interface CardProps extends HTMLAttributes<HTMLDivElement> {\n /** Inner padding. Defaults to `md` (28px); use `none` to supply your own. */\n padding?: CardPadding;\n /** Add pointer cursor + hover elevation (for clickable cards). */\n interactive?: boolean;\n}\n\n/** Bordered surface card. Static by default; opt into hover with `interactive`. */\nexport function Card({ padding = 'md', interactive = false, className, ...props }: CardProps) {\n return (\n <div\n className={cn(\n 'card',\n padding !== 'none' && `card-pad-${padding}`,\n interactive && 'card-interactive',\n className,\n )}\n {...props}\n />\n );\n}\n","import type { HTMLAttributes } from 'react';\nimport { cn } from '../cn';\n\nexport type ContainerProps = HTMLAttributes<HTMLDivElement>;\n\n/** Centered, max-width content wrapper (var(--max-w)) with horizontal padding. */\nexport function Container({ className, ...props }: ContainerProps) {\n return <div className={cn('container', className)} {...props} />;\n}\n","import type { HTMLAttributes, ReactNode } from 'react';\nimport { cn } from '../cn';\n\nexport interface SpinnerProps extends HTMLAttributes<HTMLSpanElement> {\n /** Wrap in a centered, padded block (for loading states in a panel). */\n center?: boolean;\n}\n\n/** Brand-colored loading spinner. */\nexport function Spinner({ center = false, className, ...props }: SpinnerProps) {\n const spinner = (\n <span className={cn('spinner', className)} role=\"status\" aria-label=\"Loading\" {...props} />\n );\n return center ? <div className=\"spinner-center\">{spinner}</div> : spinner;\n}\n\nexport interface EmptyStateProps {\n title: ReactNode;\n description?: ReactNode;\n /** Optional icon rendered in the circular badge above the title. */\n icon?: ReactNode;\n className?: string;\n}\n\n/** Centered \"nothing here yet\" placeholder for empty lists/tables. */\nexport function EmptyState({ title, description, icon, className }: EmptyStateProps) {\n return (\n <div className={cn('empty-state', className)}>\n {icon ? <div className=\"empty-state-icon\">{icon}</div> : null}\n <h3 className=\"empty-state-title\">{title}</h3>\n {description ? <p className=\"empty-state-desc\">{description}</p> : null}\n </div>\n );\n}\n","import type { InputHTMLAttributes, LabelHTMLAttributes, ReactNode } from 'react';\nimport { cn } from '../cn';\n\nexport type InputProps = InputHTMLAttributes<HTMLInputElement>;\n\n/** Token-aligned text input (the `.field` look: border, surface, brand focus ring). */\nexport function Input({ className, ...props }: InputProps) {\n return <input className={cn('field', className)} {...props} />;\n}\n\nexport type LabelProps = LabelHTMLAttributes<HTMLLabelElement>;\n\nexport function Label({ className, ...props }: LabelProps) {\n return <label className={cn('field-label', className)} {...props} />;\n}\n\nexport interface FieldProps {\n label?: ReactNode;\n /** Helper text shown below the control (hidden when `error` is set). */\n hint?: ReactNode;\n /** Error text shown below the control (takes precedence over `hint`). */\n error?: ReactNode;\n /** `htmlFor` for the label, matching the control's id. */\n htmlFor?: string;\n className?: string;\n children: ReactNode;\n}\n\n/** Labelled field wrapper: optional label above and hint/error below the control. */\nexport function Field({ label, hint, error, htmlFor, className, children }: FieldProps) {\n return (\n <div className={cn('field-group', className)}>\n {label ? <Label htmlFor={htmlFor}>{label}</Label> : null}\n {children}\n {error ? (\n <span className=\"field-error\">{error}</span>\n ) : hint ? (\n <span className=\"field-hint\">{hint}</span>\n ) : null}\n </div>\n );\n}\n","import type { HTMLAttributes, ReactNode } from 'react';\nimport { cn } from '../cn';\n\nexport type SectionProps = HTMLAttributes<HTMLElement>;\n\n/** Vertical page section with max-width and standard padding. */\nexport function Section({ className, ...props }: SectionProps) {\n return <section className={cn('section', className)} {...props} />;\n}\n\nexport interface SectionHeaderProps {\n /** Small eyebrow label above the title (brand colored). */\n label?: ReactNode;\n title: ReactNode;\n description?: ReactNode;\n className?: string;\n}\n\n/** Centered section heading: optional label, title, optional description. */\nexport function SectionHeader({ label, title, description, className }: SectionHeaderProps) {\n return (\n <div className={cn('section-center', className)}>\n {label ? <div className=\"section-label\">{label}</div> : null}\n <h2 className=\"section-title\">{title}</h2>\n {description ? <p className=\"section-desc\">{description}</p> : null}\n </div>\n );\n}\n"],"mappings":"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","names":["jsx","variantClass","jsx","jsx","jsx","jsxs","jsx","jsxs","jsx","jsxs"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flopay/ui",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "packageManager": "pnpm@9.15.0",
6
6
  "description": "FloPay shared design system — design tokens, a Tailwind v4 preset, and React primitives shared across landing, demo, docs, and dashboard.",
@@ -116,6 +116,13 @@
116
116
  border-color: var(--border-hover);
117
117
  }
118
118
 
119
+ /* Small / dense modifier — compose with a variant (e.g. "btn-primary btn-sm"). */
120
+ .btn-sm {
121
+ gap: 5px;
122
+ padding: 8px 14px;
123
+ font-size: 13px;
124
+ }
125
+
119
126
  /* ── Badge ── */
120
127
  .badge {
121
128
  display: inline-flex;
@@ -137,16 +144,58 @@
137
144
  flex-shrink: 0;
138
145
  }
139
146
 
147
+ /* Semantic variants — compose with .badge (e.g. "badge badge-success"). */
148
+ .badge-success {
149
+ border-color: transparent;
150
+ background: color-mix(in srgb, var(--success) 12%, transparent);
151
+ color: var(--success);
152
+ }
153
+
154
+ .badge-warning {
155
+ border-color: transparent;
156
+ background: var(--warning-bg);
157
+ color: var(--warning-fg);
158
+ }
159
+
160
+ .badge-danger {
161
+ border-color: transparent;
162
+ background: color-mix(in srgb, var(--danger) 12%, transparent);
163
+ color: var(--danger);
164
+ }
165
+
166
+ .badge-info {
167
+ border-color: transparent;
168
+ background: color-mix(in srgb, var(--brand) 12%, transparent);
169
+ color: var(--brand);
170
+ }
171
+
140
172
  /* ── Card ── */
173
+ /* Surface only — opt into padding (.card-pad-*) and hover (.card-interactive). */
141
174
  .card {
142
- background: var(--bg);
175
+ background: var(--bg-card);
143
176
  border: 1px solid var(--border);
144
177
  border-radius: var(--radius-lg);
145
- padding: 28px;
146
178
  transition: box-shadow 0.2s, border-color 0.2s;
147
179
  }
148
180
 
149
- .card:hover {
181
+ .card-pad-sm {
182
+ padding: 16px;
183
+ }
184
+
185
+ .card-pad-md {
186
+ padding: 28px;
187
+ }
188
+
189
+ .card-pad-lg {
190
+ padding: 36px;
191
+ }
192
+
193
+ .card-interactive {
194
+ cursor: pointer;
195
+ }
196
+
197
+ .card-interactive:hover {
198
+ border-color: var(--border-hover);
150
199
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
151
200
  }
152
201
 
@@ -334,3 +383,109 @@
334
383
  gap: 12px;
335
384
  }
336
385
  }
386
+
387
+ /* ── Form fields ── */
388
+ .field {
389
+ width: 100%;
390
+ border: 1px solid var(--border);
391
+ background: var(--bg-card);
392
+ border-radius: var(--radius);
393
+ padding: 9px 12px;
394
+ font-family: var(--font);
395
+ font-size: 14px;
396
+ color: var(--text);
397
+ transition: border-color 0.15s, box-shadow 0.15s;
398
+ }
399
+
400
+ .field::placeholder {
401
+ color: var(--text-tertiary);
402
+ }
403
+
404
+ .field:focus {
405
+ outline: none;
406
+ border-color: var(--brand);
407
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 20%, transparent);
408
+ }
409
+
410
+ .field[aria-invalid='true'] {
411
+ border-color: var(--danger);
412
+ }
413
+
414
+ .field[aria-invalid='true']:focus {
415
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 20%, transparent);
416
+ }
417
+
418
+ .field-group {
419
+ display: flex;
420
+ flex-direction: column;
421
+ gap: 6px;
422
+ }
423
+
424
+ .field-label {
425
+ font-size: 13px;
426
+ font-weight: 500;
427
+ color: var(--text);
428
+ }
429
+
430
+ .field-hint {
431
+ font-size: 12px;
432
+ color: var(--text-tertiary);
433
+ }
434
+
435
+ .field-error {
436
+ font-size: 12px;
437
+ color: var(--danger);
438
+ }
439
+
440
+ /* ── Spinner ── */
441
+ .spinner {
442
+ display: inline-block;
443
+ width: 24px;
444
+ height: 24px;
445
+ border: 2px solid var(--border);
446
+ border-top-color: var(--brand);
447
+ border-radius: 50%;
448
+ animation: fp-spin 0.6s linear infinite;
449
+ }
450
+
451
+ @keyframes fp-spin {
452
+ to {
453
+ transform: rotate(360deg);
454
+ }
455
+ }
456
+
457
+ .spinner-center {
458
+ display: flex;
459
+ justify-content: center;
460
+ padding: 64px 0;
461
+ }
462
+
463
+ /* ── Empty state ── */
464
+ .empty-state {
465
+ text-align: center;
466
+ padding: 64px 24px;
467
+ }
468
+
469
+ .empty-state-icon {
470
+ display: inline-flex;
471
+ align-items: center;
472
+ justify-content: center;
473
+ width: 48px;
474
+ height: 48px;
475
+ border-radius: 50%;
476
+ background: var(--bg-muted);
477
+ color: var(--text-tertiary);
478
+ margin-bottom: 16px;
479
+ }
480
+
481
+ .empty-state-title {
482
+ font-size: 14px;
483
+ font-weight: 500;
484
+ color: var(--text);
485
+ }
486
+
487
+ .empty-state-desc {
488
+ font-size: 14px;
489
+ color: var(--text-secondary);
490
+ margin-top: 4px;
491
+ }
package/styles/preset.css CHANGED
@@ -45,6 +45,8 @@
45
45
  --color-success: var(--success);
46
46
  --color-danger: var(--danger);
47
47
  --color-warning: var(--warning);
48
+ --color-warning-bg: var(--warning-bg);
49
+ --color-warning-fg: var(--warning-fg);
48
50
 
49
51
  /* Typography */
50
52
  --font-sans: var(--font);
package/styles/tokens.css CHANGED
@@ -45,6 +45,11 @@
45
45
  --danger: #dc2626;
46
46
  --warning: #f5a623;
47
47
 
48
+ /* Readable warning pair for soft surfaces (badges/callouts) — the solid
49
+ --warning is too light to use as text. */
50
+ --warning-bg: #fffbeb;
51
+ --warning-fg: #b45309;
52
+
48
53
  /* Radius */
49
54
  --radius: 8px;
50
55
  --radius-lg: 12px;
@@ -78,4 +83,7 @@
78
83
 
79
84
  --accent: #fafafa;
80
85
  --accent-hover: #e4e4e7;
86
+
87
+ --warning-bg: #451a03;
88
+ --warning-fg: #fcd34d;
81
89
  }