margo-ui 1.1.1 → 1.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "margo-ui",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "A small React UI kit built on Tailwind CSS v4 design tokens, themeable through plain CSS custom properties.",
5
5
  "keywords": [
6
6
  "react",
@@ -0,0 +1,11 @@
1
+ import { Tag } from "react-renderable";
2
+
3
+ import { cn } from "../../utils/cn/cn.js";
4
+ import { blockquoteBaseClassName } from "./style.js";
5
+
6
+ import type { ElementType } from "react";
7
+ import type { BlockquoteProps } from "./type.js";
8
+
9
+ export const Blockquote = <T extends ElementType = "blockquote">({ className, ...rest }: BlockquoteProps<T>) => (
10
+ <Tag {...Tag.forward<T>(rest, "blockquote")} className={cn(blockquoteBaseClassName, className)} />
11
+ );
@@ -0,0 +1 @@
1
+ export const blockquoteBaseClassName = "border-l-2 border-primary-darken py-0.5 pl-4";
@@ -0,0 +1,4 @@
1
+ import type { TagProps } from "react-renderable";
2
+ import type { ElementType } from "react";
3
+
4
+ export type BlockquoteProps<T extends ElementType = "blockquote"> = TagProps<T>;
@@ -21,14 +21,18 @@ export const Button = <T extends ElementType = "button">({
21
21
  clickable = true,
22
22
  active = false,
23
23
  open = false,
24
- blurOnClick = true,
24
+ onClickBlur,
25
25
  className,
26
26
  onClick,
27
27
  ...rest
28
28
  }: ButtonProps<T>) => {
29
29
  const handleClick = (event: MouseEvent<HTMLElement>) => {
30
30
  onClick?.(event);
31
- if (blurOnClick && event.detail > 0) setTimeout(() => event.currentTarget.blur(), 150);
31
+ if (typeof onClickBlur === "function") {
32
+ const target = event.currentTarget;
33
+ onClickBlur(event);
34
+ setTimeout(() => target.blur(), 150);
35
+ }
32
36
  };
33
37
 
34
38
  return (
@@ -1,6 +1,6 @@
1
1
  export const buttonBaseClassName = `group/button flex h-8 w-fit shrink-0 items-center px-2 rounded-lg border-2 text-on-main
2
2
  hover:border-on-main hover:text-on-main focus-visible:margo-border-gradient-primary
3
- focus-visible:outline focus-visible:outline-offset-1 focus-visible:outline-on-main`;
3
+ focus-visible:outline focus-visible:outline-offset-1 focus-visible:outline-on-main shadow-button`;
4
4
 
5
5
  export const buttonSurfaceClassName = "border-border bg-main";
6
6
 
@@ -7,8 +7,8 @@ export type ButtonOwnProps = {
7
7
  clickable?: boolean;
8
8
  active?: boolean;
9
9
  open?: boolean;
10
- blurOnClick?: boolean;
11
10
  onClick?: MouseEventHandler<HTMLElement>;
11
+ onClickBlur?: MouseEventHandler<HTMLElement>;
12
12
  };
13
13
 
14
14
  export type ButtonProps<T extends ElementType = "button"> = TagProps<T, ButtonOwnProps>;
@@ -1,4 +1,4 @@
1
- export const cardBaseClassName = "rounded-lg border-2 border-border bg-main p-5 text-on-main";
1
+ export const cardBaseClassName = "rounded-lg border-2 border-border bg-main p-5 text-on-main shadow-card";
2
2
 
3
3
  export const cardFocusClassName = `focus:margo-border-gradient-primary focus-visible:outline focus-visible:outline-offset-1
4
4
  focus-visible:outline-on-main`;
@@ -1,4 +1,4 @@
1
1
  export const chipBaseClassName =
2
- "bg-secondary px-3 py-0.5 whitespace-nowrap lowercase flex-none leading-none rounded-md text-mc";
2
+ "bg-secondary px-3 py-0.5 whitespace-nowrap lowercase flex-none leading-none rounded-md text-mc shadow-chip";
3
3
 
4
4
  export const chipActiveClassName = "bg-on-main text-main";
@@ -0,0 +1,33 @@
1
+ .margo-progress-bar::before {
2
+ content: "";
3
+ position: absolute;
4
+ inset: 0;
5
+ background: var(
6
+ --margo-progress-bar-fill,
7
+ linear-gradient(to right, var(--color-primary-darken), var(--color-primary))
8
+ );
9
+ }
10
+
11
+ .margo-progress-bar[data-mode="determinate"]::before {
12
+ clip-path: inset(0 calc(100% - var(--margo-progress-bar-value, 0%)) 0 0);
13
+ }
14
+
15
+ .margo-progress-bar[data-mode="determinate"][data-animate="true"]::before {
16
+ transition: clip-path 300ms ease-out;
17
+ }
18
+
19
+ .margo-progress-bar[data-mode="indeterminate"]::before {
20
+ inset: 0 auto 0 0;
21
+ width: 40%;
22
+ animation: margo-progress-bar-indeterminate 800ms ease-in-out infinite;
23
+ }
24
+
25
+ @keyframes margo-progress-bar-indeterminate {
26
+ from {
27
+ transform: translateX(-100%);
28
+ }
29
+
30
+ to {
31
+ transform: translateX(250%);
32
+ }
33
+ }
@@ -0,0 +1,35 @@
1
+ import { Tag } from "react-renderable";
2
+
3
+ import { cn } from "../../utils/cn/cn.js";
4
+ import { progressBarBaseClassName } from "./style.js";
5
+
6
+ import type { CSSProperties, ElementType } from "react";
7
+ import type { ProgressBarProps } from "./type.js";
8
+
9
+ import "./progress_bar.css";
10
+
11
+ export const ProgressBar = <T extends ElementType = "div">({
12
+ animate = true,
13
+ className,
14
+ mode = "indeterminate",
15
+ style,
16
+ value = 0,
17
+ ...rest
18
+ }: ProgressBarProps<T>) => {
19
+ const isDeterminate = mode === "determinate";
20
+ const progress = Math.min(100, Math.max(0, value));
21
+
22
+ return (
23
+ <Tag
24
+ {...Tag.forward<T>(rest)}
25
+ role="progressbar"
26
+ aria-valuemin={isDeterminate ? 0 : undefined}
27
+ aria-valuemax={isDeterminate ? 100 : undefined}
28
+ aria-valuenow={isDeterminate ? progress : undefined}
29
+ data-mode={mode}
30
+ data-animate={animate}
31
+ style={{ ...style, "--margo-progress-bar-value": `${progress}%` } as CSSProperties}
32
+ className={cn(progressBarBaseClassName, className)}
33
+ />
34
+ );
35
+ };
@@ -0,0 +1 @@
1
+ export const progressBarBaseClassName = "margo-progress-bar relative h-0.5 overflow-hidden";
@@ -0,0 +1,12 @@
1
+ import type { TagProps } from "react-renderable";
2
+ import type { ElementType } from "react";
3
+
4
+ export type ProgressBarMode = "determinate" | "indeterminate";
5
+
6
+ export type ProgressBarOwnProps = {
7
+ animate?: boolean;
8
+ mode?: ProgressBarMode;
9
+ value?: number;
10
+ };
11
+
12
+ export type ProgressBarProps<T extends ElementType = "div"> = TagProps<T, ProgressBarOwnProps>;
@@ -0,0 +1,21 @@
1
+ import { Tag } from "react-renderable";
2
+
3
+ import { cn } from "../../utils/cn/cn.js";
4
+ import { spinnerBaseClassName, spinnerIconClassName } from "./style.js";
5
+
6
+ import type { ElementType } from "react";
7
+ import type { SpinnerProps } from "./type.js";
8
+
9
+ export const Spinner = <T extends ElementType = "span">({ className, label = "Loading", ...rest }: SpinnerProps<T>) => (
10
+ <Tag
11
+ {...Tag.forward<T>(rest, "span")}
12
+ role="status"
13
+ aria-label={label}
14
+ className={cn(spinnerBaseClassName, className)}
15
+ >
16
+ <svg viewBox="0 0 24 24" aria-hidden className={spinnerIconClassName}>
17
+ <circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" strokeOpacity="0.25" strokeWidth="3" />
18
+ <path d="M12 3a9 9 0 0 1 9 9" fill="none" stroke="currentColor" strokeLinecap="round" strokeWidth="3" />
19
+ </svg>
20
+ </Tag>
21
+ );
@@ -0,0 +1,3 @@
1
+ export const spinnerBaseClassName = "inline-flex size-4 animate-spin";
2
+
3
+ export const spinnerIconClassName = "size-full";
@@ -0,0 +1,8 @@
1
+ import type { TagProps } from "react-renderable";
2
+ import type { ElementType } from "react";
3
+
4
+ export type SpinnerOwnProps = {
5
+ label?: string;
6
+ };
7
+
8
+ export type SpinnerProps<T extends ElementType = "span"> = Omit<TagProps<T, SpinnerOwnProps>, "aria-label">;
package/src/css/theme.css CHANGED
@@ -23,6 +23,11 @@
23
23
  --color-medium: var(--margo-color-medium);
24
24
  --color-border: var(--margo-color-border);
25
25
 
26
+ /* Shadows */
27
+ --shadow-card: var(--margo-shadow-card);
28
+ --shadow-button: var(--margo-shadow-button);
29
+ --shadow-chip: var(--margo-shadow-chip);
30
+
26
31
  /* Border widths */
27
32
  --border-width-2: var(--margo-border-width-2);
28
33
 
@@ -30,34 +30,44 @@
30
30
  --margo-font-weight-black: 900;
31
31
 
32
32
  --margo-border-width-2: 1.5px;
33
+
34
+ --margo-shadow-card-color: 0 0 0;
35
+ --margo-shadow-button-color: 0 0 0;
36
+ --margo-shadow-chip-color: 0 0 0;
33
37
  }
34
38
 
35
- /* Colors Theme Light */
36
39
  :root {
37
40
  color-scheme: light;
38
41
 
39
42
  --margo-color-neutral: #ffffff;
40
43
  --margo-color-on-neutral: #000000;
41
- --margo-color-main: #e9e9e9;
42
- --margo-color-on-main: #222222;
43
- --margo-color-primary: #2c4ee7;
44
- --margo-color-primary-darken: #1b24a5;
44
+ --margo-color-main: #f6f6f6;
45
+ --margo-color-on-main: #151515;
46
+ --margo-color-primary: #57e550;
47
+ --margo-color-primary-darken: #45b93f;
45
48
  --margo-color-secondary: #d3d3d3;
46
49
  --margo-color-medium: #7e7e7e;
47
- --margo-color-border: #d3d3d3;
50
+ --margo-color-border: #dcdcdc;
51
+
52
+ --margo-shadow-card: 0 4px 12px -4px rgb(var(--margo-shadow-card-color) / 0.1);
53
+ --margo-shadow-button: 0 2px 6px -2px rgb(var(--margo-shadow-button-color) / 0.1);
54
+ --margo-shadow-chip: 0 1px 3px -1px rgb(var(--margo-shadow-chip-color) / 0.5);
48
55
  }
49
56
 
50
- /* Colors Theme Dark */
51
57
  .dark {
52
58
  color-scheme: dark;
53
59
 
54
60
  --margo-color-neutral: #000000;
55
61
  --margo-color-on-neutral: #ffffff;
56
- --margo-color-main: #141414;
57
- --margo-color-on-main: #f9f9f9;
58
- --margo-color-primary: #f5cb77;
59
- --margo-color-primary-darken: #ad9055;
62
+ --margo-color-main: #151515;
63
+ --margo-color-on-main: #eeeeee;
64
+ --margo-color-primary: #57e550;
65
+ --margo-color-primary-darken: #45b93f;
60
66
  --margo-color-secondary: #2c2c2c;
61
67
  --margo-color-medium: #999999;
62
68
  --margo-color-border: #2c2c2c;
69
+
70
+ --margo-shadow-card: 0 4px 12px -4px rgb(var(--margo-shadow-card-color) / 0.5);
71
+ --margo-shadow-button: 0 2px 6px -2px rgb(var(--margo-shadow-button-color) / 0.5);
72
+ --margo-shadow-chip: 0 1px 3px -1px rgb(var(--margo-shadow-chip-color) / 0.5);
63
73
  }
package/src/index.ts CHANGED
@@ -1,6 +1,9 @@
1
+ export { Blockquote } from "./components/blockquote/blockquote.js";
1
2
  export { Button } from "./components/button/button.js";
2
3
  export { Card } from "./components/card/card.js";
3
4
  export { Chip } from "./components/chip/chip.js";
5
+ export { ProgressBar } from "./components/progress_bar/progress_bar.js";
6
+ export { Spinner } from "./components/spinner/spinner.js";
4
7
  export { Tooltip } from "./components/tooltip/tooltip.js";
5
8
 
6
9
  export { BackgroundGlow } from "./hoc/background_glow/background_glow.js";
@@ -14,6 +17,8 @@ export { useMargoTheme } from "./hooks/use_theme.js";
14
17
  export { cn } from "./utils/cn/cn.js";
15
18
  export { margoTheme, margoThemeClient } from "./utils/theme/theme.js";
16
19
 
20
+ export type { BlockquoteProps } from "./components/blockquote/type.js";
21
+
17
22
  export type {
18
23
  ButtonIconLabelProps,
19
24
  ButtonIconLabelSide,
@@ -23,6 +28,8 @@ export type {
23
28
  } from "./components/button/type.js";
24
29
  export type { CardProps } from "./components/card/type.js";
25
30
  export type { ChipProps } from "./components/chip/type.js";
31
+ export type { ProgressBarMode, ProgressBarProps } from "./components/progress_bar/type.js";
32
+ export type { SpinnerProps } from "./components/spinner/type.js";
26
33
  export type { TooltipPosition, TooltipProps } from "./components/tooltip/type.js";
27
34
 
28
35
  export type { BackgroundGlowProps, BackgroundGlowSize } from "./hoc/background_glow/type.js";