@ashwa-io/ui 0.0.2 → 0.0.3

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 (40) hide show
  1. package/dist/components/atomic/Badge/Badge.d.ts +11 -0
  2. package/dist/components/atomic/Badge/Badge.js +31 -0
  3. package/dist/components/atomic/Badge/index.d.ts +2 -0
  4. package/dist/components/atomic/Button/Button.d.ts +3 -2
  5. package/dist/components/atomic/Button/Button.js +3 -10
  6. package/dist/components/atomic/Button/buttonVariant.d.ts +3 -0
  7. package/dist/components/atomic/Button/buttonVariant.js +10 -0
  8. package/dist/components/atomic/Button/index.d.ts +2 -1
  9. package/dist/components/atomic/Divider/Divider.d.ts +8 -0
  10. package/dist/components/atomic/Divider/Divider.js +12 -0
  11. package/dist/components/atomic/Divider/index.d.ts +2 -0
  12. package/dist/components/atomic/Heading/Heading.d.ts +12 -0
  13. package/dist/components/atomic/Heading/Heading.js +30 -0
  14. package/dist/components/atomic/Heading/index.d.ts +2 -0
  15. package/dist/components/atomic/IconButton/IconButton.d.ts +21 -0
  16. package/dist/components/atomic/IconButton/IconButton.js +34 -0
  17. package/dist/components/atomic/IconButton/index.d.ts +2 -0
  18. package/dist/components/atomic/Skeleton/Skeleton.d.ts +8 -0
  19. package/dist/components/atomic/Skeleton/Skeleton.js +17 -0
  20. package/dist/components/atomic/Skeleton/index.d.ts +2 -0
  21. package/dist/components/atomic/Spinner/Spinner.d.ts +10 -0
  22. package/dist/components/atomic/Spinner/Spinner.js +18 -0
  23. package/dist/components/atomic/Spinner/index.d.ts +2 -0
  24. package/dist/components/atomic/Text/Text.d.ts +20 -0
  25. package/dist/components/atomic/Text/Text.js +33 -0
  26. package/dist/components/atomic/Text/index.d.ts +2 -0
  27. package/dist/components/layout/Stack/Stack.d.ts +22 -0
  28. package/dist/components/layout/Stack/Stack.js +62 -0
  29. package/dist/components/layout/Stack/index.d.ts +2 -0
  30. package/dist/components/ui/badge.js +26 -0
  31. package/dist/components/ui/button.d.ts +2 -0
  32. package/dist/components/ui/button.js +1 -1
  33. package/dist/components/ui/separator.js +13 -0
  34. package/dist/components/ui/skeleton.js +11 -0
  35. package/dist/components/ui/spinner.js +14 -0
  36. package/dist/index.d.ts +20 -3
  37. package/dist/index.js +10 -1
  38. package/dist/lib/types.d.ts +1 -0
  39. package/package.json +1 -1
  40. package/src/styles/index.css +11 -0
@@ -0,0 +1,11 @@
1
+ import { IconComponent, Tone } from "../../../lib/types.js";
2
+ import React from "react";
3
+ export interface BadgeProps {
4
+ children: React.ReactNode;
5
+ tone?: Tone;
6
+ /** Leading icon, decorative (aria-hidden). */
7
+ icon?: IconComponent;
8
+ id?: string;
9
+ 'data-testid'?: string;
10
+ }
11
+ export declare const Badge: React.FC<BadgeProps>;
@@ -0,0 +1,31 @@
1
+ import { Badge as Badge$1 } from "../../ui/badge.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ // src/components/atomic/Badge/Badge.tsx
4
+ const PRIMITIVE_VARIANT = {
5
+ neutral: "secondary",
6
+ brand: "secondary",
7
+ success: "secondary",
8
+ warning: "secondary",
9
+ danger: "destructive",
10
+ info: "secondary"
11
+ };
12
+ const TONE_CLASS = {
13
+ brand: "bg-brand-tint text-brand-tint-foreground",
14
+ success: "bg-success/10 text-success dark:bg-success/20",
15
+ warning: "bg-warning/10 text-warning dark:bg-warning/20",
16
+ info: "bg-info/10 text-info dark:bg-info/20"
17
+ };
18
+ const Badge = (props) => {
19
+ const { children, tone = "neutral", icon: Icon, id, "data-testid": testId } = props;
20
+ return /* @__PURE__ */ jsxs(Badge$1, {
21
+ id,
22
+ "data-testid": testId,
23
+ variant: PRIMITIVE_VARIANT[tone],
24
+ className: TONE_CLASS[tone],
25
+ children: [Icon && /* @__PURE__ */ jsx(Icon, {
26
+ "data-icon": "inline-start",
27
+ "aria-hidden": true
28
+ }), children]
29
+ });
30
+ };
31
+ export { Badge };
@@ -0,0 +1,2 @@
1
+ import { Badge, BadgeProps } from "./Badge.js";
2
+ export { Badge, type BadgeProps };
@@ -1,6 +1,6 @@
1
1
  import { IconComponent, Size } from "../../../lib/types.js";
2
+ import { ButtonVariant } from "./buttonVariant.js";
2
3
  import React from "react";
3
- export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link';
4
4
  export interface ButtonProps {
5
5
  variant?: ButtonVariant;
6
6
  size?: Size;
@@ -19,4 +19,5 @@ export interface ButtonProps {
19
19
  'aria-describedby'?: string;
20
20
  'data-testid'?: string;
21
21
  }
22
- export declare const Button: React.FC<ButtonProps>;
22
+ export declare const Button: React.FC<ButtonProps>;
23
+ export type { ButtonVariant };
@@ -1,16 +1,9 @@
1
1
  import { Button as Button$1 } from "../../ui/button.js";
2
2
  import { cn } from "../../../lib/utils.js";
3
- import { LoaderCircleIcon } from "lucide-react";
3
+ import { BUTTON_VARIANT } from "./buttonVariant.js";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { LoaderCircleIcon } from "lucide-react";
5
6
  // src/components/atomic/Button/Button.tsx
6
- const VARIANT = {
7
- primary: "default",
8
- secondary: "secondary",
9
- outline: "outline",
10
- ghost: "ghost",
11
- destructive: "destructive",
12
- link: "link"
13
- };
14
7
  const VARIANT_CLASS = { link: "text-brand-text" };
15
8
  const SIZE = {
16
9
  sm: "sm",
@@ -24,7 +17,7 @@ const Button = (props) => {
24
17
  ref,
25
18
  id,
26
19
  type,
27
- variant: VARIANT[variant],
20
+ variant: BUTTON_VARIANT[variant],
28
21
  size: SIZE[size],
29
22
  className: cn(VARIANT_CLASS[variant], fullWidth && "w-full"),
30
23
  disabled: disabled || loading,
@@ -0,0 +1,3 @@
1
+ import "../../ui/button.js";
2
+ import "react";
3
+ export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link';
@@ -0,0 +1,10 @@
1
+ // src/components/atomic/Button/buttonVariant.ts
2
+ const BUTTON_VARIANT = {
3
+ primary: "default",
4
+ secondary: "secondary",
5
+ outline: "outline",
6
+ ghost: "ghost",
7
+ destructive: "destructive",
8
+ link: "link"
9
+ };
10
+ export { BUTTON_VARIANT };
@@ -1,2 +1,3 @@
1
- import { Button, ButtonProps, ButtonVariant } from "./Button.js";
1
+ import { ButtonVariant } from "./buttonVariant.js";
2
+ import { Button, ButtonProps } from "./Button.js";
2
3
  export { Button, type ButtonProps, type ButtonVariant };
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ export interface DividerProps {
3
+ /** A vertical divider stretches to its flex row's height. */
4
+ orientation?: 'horizontal' | 'vertical';
5
+ id?: string;
6
+ 'data-testid'?: string;
7
+ }
8
+ export declare const Divider: React.FC<DividerProps>;
@@ -0,0 +1,12 @@
1
+ import { Separator } from "../../ui/separator.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+ // src/components/atomic/Divider/Divider.tsx
4
+ const Divider = (props) => {
5
+ const { orientation = "horizontal", id, "data-testid": testId } = props;
6
+ return /* @__PURE__ */ jsx(Separator, {
7
+ orientation,
8
+ id,
9
+ "data-testid": testId
10
+ });
11
+ };
12
+ export { Divider };
@@ -0,0 +1,2 @@
1
+ import { Divider, DividerProps } from "./Divider.js";
2
+ export { Divider, type DividerProps };
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
3
+ export interface HeadingProps {
4
+ children: React.ReactNode;
5
+ /** Renders h1–h6; the visual size follows the level. */
6
+ level: HeadingLevel;
7
+ /** One line with an ellipsis; the parent must constrain the width. */
8
+ truncate?: boolean;
9
+ id?: string;
10
+ 'data-testid'?: string;
11
+ }
12
+ export declare const Heading: React.FC<HeadingProps>;
@@ -0,0 +1,30 @@
1
+ import { cn } from "../../../lib/utils.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+ // src/components/atomic/Heading/Heading.tsx
4
+ const ELEMENT = {
5
+ 1: "h1",
6
+ 2: "h2",
7
+ 3: "h3",
8
+ 4: "h4",
9
+ 5: "h5",
10
+ 6: "h6"
11
+ };
12
+ const SIZE = {
13
+ 1: "text-2xl tracking-tight",
14
+ 2: "text-xl tracking-tight",
15
+ 3: "text-lg",
16
+ 4: "text-base",
17
+ 5: "text-sm",
18
+ 6: "text-xs"
19
+ };
20
+ const Heading = (props) => {
21
+ const { children, level, truncate = false, id, "data-testid": testId } = props;
22
+ const Element = ELEMENT[level];
23
+ return /* @__PURE__ */ jsx(Element, {
24
+ id,
25
+ "data-testid": testId,
26
+ className: cn("font-heading text-foreground font-semibold", SIZE[level], truncate && "truncate"),
27
+ children
28
+ });
29
+ };
30
+ export { Heading };
@@ -0,0 +1,2 @@
1
+ import { Heading, HeadingLevel, HeadingProps } from "./Heading.js";
2
+ export { Heading, type HeadingLevel, type HeadingProps };
@@ -0,0 +1,21 @@
1
+ import { ButtonVariant } from "../Button/buttonVariant.js";
2
+ import { IconComponent, Size } from "../../../lib/types.js";
3
+ import React from "react";
4
+ export type IconButtonVariant = Exclude<ButtonVariant, 'link'>;
5
+ export interface IconButtonProps {
6
+ icon: IconComponent;
7
+ /** Required: an icon-only button has no other accessible name. */
8
+ 'aria-label': string;
9
+ variant?: IconButtonVariant;
10
+ size?: Size;
11
+ /** Disables the button, sets aria-busy and swaps the icon for a spinner. */
12
+ loading?: boolean;
13
+ disabled?: boolean;
14
+ type?: 'button' | 'submit';
15
+ onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
16
+ ref?: React.Ref<HTMLButtonElement>;
17
+ id?: string;
18
+ 'aria-describedby'?: string;
19
+ 'data-testid'?: string;
20
+ }
21
+ export declare const IconButton: React.FC<IconButtonProps>;
@@ -0,0 +1,34 @@
1
+ import { BUTTON_VARIANT } from "../Button/buttonVariant.js";
2
+ import { Button } from "../../ui/button.js";
3
+ import { cn } from "../../../lib/utils.js";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import { LoaderCircleIcon } from "lucide-react";
6
+ // src/components/atomic/IconButton/IconButton.tsx
7
+ const SIZE = {
8
+ sm: "icon-sm",
9
+ md: "icon",
10
+ lg: "icon-lg"
11
+ };
12
+ const IconButton = (props) => {
13
+ const { icon, "aria-label": ariaLabel, variant = "ghost", size = "md", loading = false, disabled = false, type = "button", onClick, ref, id, "aria-describedby": ariaDescribedBy, "data-testid": testId } = props;
14
+ const Icon = loading ? LoaderCircleIcon : icon;
15
+ return /* @__PURE__ */ jsx(Button, {
16
+ ref,
17
+ id,
18
+ type,
19
+ variant: BUTTON_VARIANT[variant],
20
+ size: SIZE[size],
21
+ disabled: disabled || loading,
22
+ focusableWhenDisabled: loading,
23
+ "aria-busy": loading || void 0,
24
+ "aria-label": ariaLabel,
25
+ "aria-describedby": ariaDescribedBy,
26
+ "data-testid": testId,
27
+ onClick,
28
+ children: /* @__PURE__ */ jsx(Icon, {
29
+ "aria-hidden": true,
30
+ className: cn(loading && "animate-spin")
31
+ })
32
+ });
33
+ };
34
+ export { IconButton };
@@ -0,0 +1,2 @@
1
+ import { IconButton, IconButtonProps, IconButtonVariant } from "./IconButton.js";
2
+ export { IconButton, type IconButtonProps, type IconButtonVariant };
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ export type SkeletonShape = 'text' | 'circle' | 'block';
3
+ export interface SkeletonProps {
4
+ /** Fills the wrapper's width; the app sizes it with a plain div. */
5
+ shape?: SkeletonShape;
6
+ 'data-testid'?: string;
7
+ }
8
+ export declare const Skeleton: React.FC<SkeletonProps>;
@@ -0,0 +1,17 @@
1
+ import { Skeleton as Skeleton$1 } from "../../ui/skeleton.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+ // src/components/atomic/Skeleton/Skeleton.tsx
4
+ const SHAPE = {
5
+ text: "h-4 w-full",
6
+ circle: "aspect-square w-full rounded-full",
7
+ block: "size-full"
8
+ };
9
+ const Skeleton = (props) => {
10
+ const { shape = "text", "data-testid": testId } = props;
11
+ return /* @__PURE__ */ jsx(Skeleton$1, {
12
+ "aria-hidden": true,
13
+ "data-testid": testId,
14
+ className: SHAPE[shape]
15
+ });
16
+ };
17
+ export { Skeleton };
@@ -0,0 +1,2 @@
1
+ import { Skeleton, SkeletonProps, SkeletonShape } from "./Skeleton.js";
2
+ export { Skeleton, type SkeletonProps, type SkeletonShape };
@@ -0,0 +1,10 @@
1
+ import { Size } from "../../../lib/types.js";
2
+ import React from "react";
3
+ export interface SpinnerProps {
4
+ size?: Size;
5
+ /** Accessible name announced by screen readers. */
6
+ label?: string;
7
+ id?: string;
8
+ 'data-testid'?: string;
9
+ }
10
+ export declare const Spinner: React.FC<SpinnerProps>;
@@ -0,0 +1,18 @@
1
+ import { Spinner as Spinner$1 } from "../../ui/spinner.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+ // src/components/atomic/Spinner/Spinner.tsx
4
+ const SIZE = {
5
+ sm: "size-4",
6
+ md: "size-5",
7
+ lg: "size-8"
8
+ };
9
+ const Spinner = (props) => {
10
+ const { size = "md", label = "Loading", id, "data-testid": testId } = props;
11
+ return /* @__PURE__ */ jsx(Spinner$1, {
12
+ id,
13
+ "data-testid": testId,
14
+ "aria-label": label,
15
+ className: SIZE[size]
16
+ });
17
+ };
18
+ export { Spinner };
@@ -0,0 +1,2 @@
1
+ import { Spinner, SpinnerProps } from "./Spinner.js";
2
+ export { Spinner, type SpinnerProps };
@@ -0,0 +1,20 @@
1
+ import { Tone } from "../../../lib/types.js";
2
+ import React from "react";
3
+ export type TextSize = 'xs' | 'sm' | 'md' | 'lg';
4
+ export type TextTone = Tone | 'muted';
5
+ export type TextWeight = 'normal' | 'medium' | 'semibold';
6
+ export interface TextProps {
7
+ children: React.ReactNode;
8
+ as?: 'p' | 'span' | 'div';
9
+ /** Leave unset to inherit the parent's size (the page default is md). */
10
+ size?: TextSize;
11
+ /** Leave unset to inherit the parent's colour. */
12
+ tone?: TextTone;
13
+ /** Leave unset to inherit the parent's weight. */
14
+ weight?: TextWeight;
15
+ /** One line with an ellipsis; the parent must constrain the width. */
16
+ truncate?: boolean;
17
+ id?: string;
18
+ 'data-testid'?: string;
19
+ }
20
+ export declare const Text: React.FC<TextProps>;
@@ -0,0 +1,33 @@
1
+ import { cn } from "../../../lib/utils.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+ // src/components/atomic/Text/Text.tsx
4
+ const SIZE = {
5
+ xs: "text-xs",
6
+ sm: "text-sm",
7
+ md: "text-base",
8
+ lg: "text-lg"
9
+ };
10
+ const TONE = {
11
+ neutral: "text-foreground",
12
+ muted: "text-muted-foreground",
13
+ brand: "text-brand-text",
14
+ success: "text-success",
15
+ warning: "text-warning",
16
+ danger: "text-destructive",
17
+ info: "text-info"
18
+ };
19
+ const WEIGHT = {
20
+ normal: "font-normal",
21
+ medium: "font-medium",
22
+ semibold: "font-semibold"
23
+ };
24
+ const Text = (props) => {
25
+ const { children, as: Element = "p", size, tone, weight, truncate = false, id, "data-testid": testId } = props;
26
+ return /* @__PURE__ */ jsx(Element, {
27
+ id,
28
+ "data-testid": testId,
29
+ className: cn(size && SIZE[size], tone && TONE[tone], weight && WEIGHT[weight], truncate && "block truncate"),
30
+ children
31
+ });
32
+ };
33
+ export { Text };
@@ -0,0 +1,2 @@
1
+ import { Text, TextProps, TextSize, TextTone, TextWeight } from "./Text.js";
2
+ export { Text, type TextProps, type TextSize, type TextTone, type TextWeight };
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ export type StackGap = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 8;
3
+ export type StackAlign = 'start' | 'center' | 'end' | 'stretch' | 'baseline';
4
+ export type StackJustify = 'start' | 'center' | 'end' | 'between';
5
+ export interface StackProps {
6
+ children: React.ReactNode;
7
+ direction?: 'row' | 'column';
8
+ /** Tailwind spacing step (1 = 4px). */
9
+ gap?: StackGap;
10
+ /** Cross axis. */
11
+ align?: StackAlign;
12
+ /** Main axis. */
13
+ justify?: StackJustify;
14
+ wrap?: boolean;
15
+ id?: string;
16
+ 'data-testid'?: string;
17
+ }
18
+ export type HStackProps = Omit<StackProps, 'direction'>;
19
+ export type VStackProps = Omit<StackProps, 'direction'>;
20
+ export declare const Stack: React.FC<StackProps>;
21
+ export declare const HStack: React.FC<HStackProps>;
22
+ export declare const VStack: React.FC<VStackProps>;
@@ -0,0 +1,62 @@
1
+ import { cn } from "../../../lib/utils.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+ // src/components/layout/Stack/Stack.tsx
4
+ const GAP = {
5
+ 0: "gap-0",
6
+ 1: "gap-1",
7
+ 2: "gap-2",
8
+ 3: "gap-3",
9
+ 4: "gap-4",
10
+ 5: "gap-5",
11
+ 6: "gap-6",
12
+ 8: "gap-8"
13
+ };
14
+ const ALIGN = {
15
+ start: "items-start",
16
+ center: "items-center",
17
+ end: "items-end",
18
+ stretch: "items-stretch",
19
+ baseline: "items-baseline"
20
+ };
21
+ const JUSTIFY = {
22
+ start: "justify-start",
23
+ center: "justify-center",
24
+ end: "justify-end",
25
+ between: "justify-between"
26
+ };
27
+ const Stack = (props) => {
28
+ const { children, direction = "column", gap = 0, align, justify, wrap = false, id, "data-testid": testId } = props;
29
+ return /* @__PURE__ */ jsx("div", {
30
+ id,
31
+ "data-testid": testId,
32
+ className: cn("flex", direction === "row" ? "flex-row" : "flex-col", GAP[gap], align && ALIGN[align], justify && JUSTIFY[justify], wrap && "flex-wrap"),
33
+ children
34
+ });
35
+ };
36
+ const HStack = (props) => {
37
+ const { children, gap, align, justify, wrap, id, "data-testid": testId } = props;
38
+ return /* @__PURE__ */ jsx(Stack, {
39
+ direction: "row",
40
+ gap,
41
+ align,
42
+ justify,
43
+ wrap,
44
+ id,
45
+ "data-testid": testId,
46
+ children
47
+ });
48
+ };
49
+ const VStack = (props) => {
50
+ const { children, gap, align, justify, wrap, id, "data-testid": testId } = props;
51
+ return /* @__PURE__ */ jsx(Stack, {
52
+ direction: "column",
53
+ gap,
54
+ align,
55
+ justify,
56
+ wrap,
57
+ id,
58
+ "data-testid": testId,
59
+ children
60
+ });
61
+ };
62
+ export { HStack, Stack, VStack };
@@ -0,0 +1,2 @@
1
+ import { HStack, HStackProps, Stack, StackAlign, StackGap, StackJustify, StackProps, VStack, VStackProps } from "./Stack.js";
2
+ export { HStack, type HStackProps, Stack, type StackAlign, type StackGap, type StackJustify, type StackProps, VStack, type VStackProps };
@@ -0,0 +1,26 @@
1
+ import { mergeProps } from "@base-ui/react/merge-props";
2
+ import { useRender } from "@base-ui/react/use-render";
3
+ import { cva } from "class-variance-authority";
4
+ import { cn } from "cn";
5
+ // src/components/ui/badge.tsx
6
+ const badgeVariants = cva("group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", {
7
+ variants: { variant: {
8
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
9
+ secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
10
+ destructive: "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
11
+ outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
12
+ ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
13
+ link: "text-primary underline-offset-4 hover:underline"
14
+ } },
15
+ defaultVariants: { variant: "default" }
16
+ });
17
+ const Badge = ({ className, variant = "default", render, ...props }) => useRender({
18
+ defaultTagName: "span",
19
+ props: mergeProps({ className: cn(badgeVariants({ variant }), className) }, props),
20
+ render,
21
+ state: {
22
+ slot: "badge",
23
+ variant
24
+ }
25
+ });
26
+ export { Badge, badgeVariants };
@@ -0,0 +1,2 @@
1
+ import "class-variance-authority";
2
+ import "@base-ui/react/button";
@@ -1,7 +1,7 @@
1
- import { Button } from "@base-ui/react/button";
2
1
  import { cva } from "class-variance-authority";
3
2
  import { cn } from "cn";
4
3
  import { jsx } from "react/jsx-runtime";
4
+ import { Button } from "@base-ui/react/button";
5
5
  // src/components/ui/button.tsx
6
6
  const buttonVariants = cva("group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
7
7
  variants: {
@@ -0,0 +1,13 @@
1
+ import { cn } from "cn";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { Separator } from "@base-ui/react/separator";
4
+ // src/components/ui/separator.tsx
5
+ const Separator$1 = ({ className, orientation = "horizontal", ...props }) => {
6
+ return /* @__PURE__ */ jsx(Separator, {
7
+ "data-slot": "separator",
8
+ orientation,
9
+ className: cn("bg-border shrink-0 data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch", className),
10
+ ...props
11
+ });
12
+ };
13
+ export { Separator$1 as Separator };
@@ -0,0 +1,11 @@
1
+ import { cn } from "cn";
2
+ import { jsx } from "react/jsx-runtime";
3
+ // src/components/ui/skeleton.tsx
4
+ const Skeleton = ({ className, ...props }) => {
5
+ return /* @__PURE__ */ jsx("div", {
6
+ "data-slot": "skeleton",
7
+ className: cn("bg-muted animate-pulse rounded-md", className),
8
+ ...props
9
+ });
10
+ };
11
+ export { Skeleton };
@@ -0,0 +1,14 @@
1
+ import { cn } from "cn";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { Loader2Icon } from "lucide-react";
4
+ // src/components/ui/spinner.tsx
5
+ const Spinner = ({ className, ...props }) => {
6
+ return /* @__PURE__ */ jsx(Loader2Icon, {
7
+ "data-slot": "spinner",
8
+ role: "status",
9
+ "aria-label": "Loading",
10
+ className: cn("size-4 animate-spin", className),
11
+ ...props
12
+ });
13
+ };
14
+ export { Spinner };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,21 @@
1
+ import "./components/atomic/Badge/index.js";
2
+ import { Badge, BadgeProps } from "./components/atomic/Badge/Badge.js";
1
3
  import "./components/atomic/Button/index.js";
2
- import { Button, ButtonProps, ButtonVariant } from "./components/atomic/Button/Button.js";
3
- import { IconComponent, Size } from "./lib/types.js";
4
- export { Button, type ButtonProps, type ButtonVariant, type IconComponent, type Size };
4
+ import { ButtonVariant } from "./components/atomic/Button/buttonVariant.js";
5
+ import { Button, ButtonProps } from "./components/atomic/Button/Button.js";
6
+ import "./components/atomic/Divider/index.js";
7
+ import { Divider, DividerProps } from "./components/atomic/Divider/Divider.js";
8
+ import "./components/atomic/Heading/index.js";
9
+ import { Heading, HeadingLevel, HeadingProps } from "./components/atomic/Heading/Heading.js";
10
+ import "./components/atomic/Text/index.js";
11
+ import { Text, TextProps, TextSize, TextTone, TextWeight } from "./components/atomic/Text/Text.js";
12
+ import "./components/atomic/IconButton/index.js";
13
+ import { IconButton, IconButtonProps, IconButtonVariant } from "./components/atomic/IconButton/IconButton.js";
14
+ import "./components/atomic/Skeleton/index.js";
15
+ import { Skeleton, SkeletonProps, SkeletonShape } from "./components/atomic/Skeleton/Skeleton.js";
16
+ import "./components/atomic/Spinner/index.js";
17
+ import { Spinner, SpinnerProps } from "./components/atomic/Spinner/Spinner.js";
18
+ import "./components/layout/Stack/index.js";
19
+ import { HStack, HStackProps, Stack, StackAlign, StackGap, StackJustify, StackProps, VStack, VStackProps } from "./components/layout/Stack/Stack.js";
20
+ import { IconComponent, Size, Tone } from "./lib/types.js";
21
+ export { Badge, type BadgeProps, Button, type ButtonProps, type ButtonVariant, Divider, type DividerProps, HStack, type HStackProps, Heading, type HeadingLevel, type HeadingProps, IconButton, type IconButtonProps, type IconButtonVariant, type IconComponent, type Size, Skeleton, type SkeletonProps, type SkeletonShape, Spinner, type SpinnerProps, Stack, type StackAlign, type StackGap, type StackJustify, type StackProps, Text, type TextProps, type TextSize, type TextTone, type TextWeight, type Tone, VStack, type VStackProps };
package/dist/index.js CHANGED
@@ -1 +1,10 @@
1
- export { Button } from "./components/atomic/Button/Button.js";
1
+ import { Badge } from "./components/atomic/Badge/Badge.js";
2
+ import { Button } from "./components/atomic/Button/Button.js";
3
+ import { Divider } from "./components/atomic/Divider/Divider.js";
4
+ import { Heading } from "./components/atomic/Heading/Heading.js";
5
+ import { Text } from "./components/atomic/Text/Text.js";
6
+ import { IconButton } from "./components/atomic/IconButton/IconButton.js";
7
+ import { Skeleton } from "./components/atomic/Skeleton/Skeleton.js";
8
+ import { Spinner } from "./components/atomic/Spinner/Spinner.js";
9
+ import { HStack, Stack, VStack } from "./components/layout/Stack/Stack.js";
10
+ export { Badge, Button, Divider, HStack, Heading, IconButton, Skeleton, Spinner, Stack, Text, VStack };
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  /** Shared size scale for every public component. */
3
3
  export type Size = 'sm' | 'md' | 'lg';
4
+ export type Tone = 'neutral' | 'brand' | 'success' | 'warning' | 'danger' | 'info';
4
5
  /** An icon component (lucide-react, svgr): the design system controls its size and colour. */
5
6
  export type IconComponent = React.ComponentType<{
6
7
  className?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ashwa-io/ui",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Ashwa design system: locked React components on shadcn/ui (Base UI) and Tailwind v4, with per-org brand theming.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -29,6 +29,9 @@
29
29
  --color-input: var(--input);
30
30
  --color-border: var(--border);
31
31
  --color-destructive: var(--destructive);
32
+ --color-success: var(--success);
33
+ --color-warning: var(--warning);
34
+ --color-info: var(--info);
32
35
  --color-accent-foreground: var(--accent-foreground);
33
36
  --color-accent: var(--accent);
34
37
  --color-muted-foreground: var(--muted-foreground);
@@ -85,6 +88,11 @@
85
88
  /* Tailwind red-700, darker than base-nova's red-600: destructive text on its 10% tint
86
89
  (Button destructive) must reach WCAG AA 4.5:1; red-600 only reached 4:1. */
87
90
  --destructive: oklch(0.505 0.213 27.518);
91
+ /* Status text colours (Tailwind green-800, amber-800, blue-700): readable as text and on their
92
+ 10% tints (Badge). Fixed, never per org. */
93
+ --success: oklch(0.448 0.119 151.328);
94
+ --warning: oklch(0.473 0.137 46.201);
95
+ --info: oklch(0.488 0.243 264.376);
88
96
  --border: oklch(0.922 0 0);
89
97
  --input: oklch(0.922 0 0);
90
98
  --ring: var(--brand);
@@ -125,6 +133,9 @@
125
133
  --accent: oklch(0.269 0 0);
126
134
  --accent-foreground: oklch(0.985 0 0);
127
135
  --destructive: oklch(0.704 0.191 22.216);
136
+ --success: oklch(0.792 0.209 151.711);
137
+ --warning: oklch(0.828 0.189 84.429);
138
+ --info: oklch(0.707 0.165 254.624);
128
139
  --border: oklch(1 0 0 / 10%);
129
140
  --input: oklch(1 0 0 / 15%);
130
141
  --chart-2: oklch(0.556 0 0);