margo-ui 1.5.1 → 2.0.1

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.5.1",
3
+ "version": "2.0.1",
4
4
  "description": "React UI kit built on Tailwind CSS v4 design tokens, themeable through plain CSS custom properties.",
5
5
  "keywords": [
6
6
  "react",
@@ -11,7 +11,6 @@ import {
11
11
  buttonGridReverseClassName,
12
12
  buttonIconClassName,
13
13
  buttonLabelClassName,
14
- buttonNotClickableClassName,
15
14
  buttonSlotClassName,
16
15
  buttonSurfaceClassName,
17
16
  } from "./style.js";
@@ -46,12 +45,11 @@ export const Button = <T extends ElementType = "button">({
46
45
  data-margo-open={open}
47
46
  data-margo-disabled={disabled}
48
47
  aria-disabled={disabled || undefined}
49
- tabIndex={disabled || !clickable ? -1 : undefined}
48
+ inert={disabled || !clickable || undefined}
50
49
  onClick={handleClick}
51
50
  className={cn(
52
51
  buttonBaseClassName,
53
52
  active ? buttonActiveClassName : buttonSurfaceClassName,
54
- !clickable && buttonNotClickableClassName,
55
53
  disabled && buttonDisabledClassName,
56
54
  className,
57
55
  )}
@@ -1,15 +1,13 @@
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
- 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 shadow-button
4
- select-none origin-center transition-transform duration-[160ms] ease-out active:scale-[0.95]`;
2
+ hover:border-on-main hover:text-on-main focus-visible:border-primary
3
+ focus-visible:outline focus-visible:outline-offset-1 focus-visible:outline-on-main
4
+ select-none origin-center transition-transform duration-[160ms] ease-out active:scale-[0.95] bg-main`;
5
5
 
6
- export const buttonSurfaceClassName = "border-border bg-main";
6
+ export const buttonSurfaceClassName = "border-border";
7
7
 
8
- export const buttonActiveClassName = "margo-border-gradient-primary";
8
+ export const buttonActiveClassName = "border-primary shadow-button";
9
9
 
10
- export const buttonNotClickableClassName = "pointer-events-none cursor-auto";
11
-
12
- export const buttonDisabledClassName = "pointer-events-none cursor-auto opacity-40";
10
+ export const buttonDisabledClassName = "opacity-40";
13
11
 
14
12
  export const buttonIconClassName =
15
13
  "relative z-10 flex size-4 min-w-0 shrink-0 items-center justify-center [direction:ltr]";
@@ -18,7 +16,7 @@ export const buttonLabelClassName =
18
16
  "relative z-10 flex items-center text-sm whitespace-nowrap margo-text-box-trim lowercase [direction:ltr] px-0.5";
19
17
 
20
18
  export const buttonGridClassName =
21
- "grid items-center gap-0 transition-[grid-template-columns,gap] duration-[180ms] ease-in-out";
19
+ "grid items-center gap-0 whitespace-nowrap transition-[grid-template-columns,gap] duration-[180ms] ease-in-out";
22
20
 
23
21
  export const buttonGridForwardClassName = `grid-cols-[1rem_0fr] group-hover/button:grid-cols-[1rem_1fr] group-hover/button:gap-(--margo-button-gap)
24
22
  group-focus-visible/button:grid-cols-[1rem_1fr] group-focus-visible/button:gap-(--margo-button-gap)
@@ -28,5 +26,5 @@ export const buttonGridReverseClassName = `grid-cols-[auto_0rem] group-hover/but
28
26
  group-focus-visible/button:grid-cols-[auto_1rem] group-focus-visible/button:gap-(--margo-button-gap)
29
27
  group-data-[margo-open=true]/button:grid-cols-[auto_1rem] group-data-[margo-open=true]/button:gap-(--margo-button-gap)`;
30
28
 
31
- export const buttonSlotClassName = `min-w-0 overflow-hidden opacity-0 transition-opacity duration-[180ms] ease-in
29
+ export const buttonSlotClassName = `min-w-0 overflow-hidden whitespace-nowrap opacity-0 transition-opacity duration-[180ms] ease-in
32
30
  group-hover/button:opacity-100 group-focus-visible/button:opacity-100 group-data-[margo-open=true]/button:opacity-100`;
@@ -1,6 +1,6 @@
1
1
  export const buttonMicroBaseClassName = `w-fit text-xs underline underline-offset-2 outline-none
2
- focus-visible:text-on-main`;
2
+ focus-visible:text-on-main hover:text-on-main`;
3
3
 
4
- export const buttonMicroSurfaceClassName = "text-medium hover:text-on-main";
4
+ export const buttonMicroSurfaceClassName = "text-medium";
5
5
 
6
6
  export const buttonMicroActiveClassName = "text-primary-darken";
@@ -1,4 +1,4 @@
1
1
  export const cardBaseClassName = "rounded-lg border-2 border-border bg-main p-5 text-on-main shadow-card";
2
2
 
3
- export const cardFocusClassName = `focus:margo-border-gradient-primary focus-visible:outline focus-visible:outline-offset-1
3
+ export const cardFocusClassName = `focus:border-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 margo-text-box-trim flex items-center justify-center px-3 py-0.5 whitespace-nowrap lowercase flex-none leading-none rounded-md text-mc shadow-chip";
2
+ "bg-on-main/8 margo-text-box-trim flex items-center justify-center 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
+ import { MdSearch } from "react-icons/md";
2
+
3
+ import { Tag } from "react-renderable";
4
+
5
+ import { cn } from "../../utils/cn/cn.js";
6
+ import { inputActiveClassName, inputBaseClassName, inputIconClassName, inputTextClassName } from "./style.js";
7
+
8
+ import type { ElementType } from "react";
9
+ import type { InputIconProps, InputProps, InputTextProps } from "./type.js";
10
+
11
+ export const Input = <T extends ElementType = "div">({
12
+ active = false,
13
+ clickable = true,
14
+ className,
15
+ ...rest
16
+ }: InputProps<T>) => (
17
+ <Tag
18
+ {...Tag.forward<T>(rest, "div")}
19
+ data-margo-active={active}
20
+ inert={!clickable || undefined}
21
+ className={cn(inputBaseClassName, active && inputActiveClassName, className)}
22
+ />
23
+ );
24
+
25
+ Input.Icon = ({ icon = <MdSearch className="text-md" />, className }: InputIconProps) => (
26
+ <span data-margo-input-slot={true} className={cn(inputIconClassName, className)}>
27
+ {icon}
28
+ </span>
29
+ );
30
+
31
+ Input.Text = ({ className, ...rest }: InputTextProps) => (
32
+ <input {...rest} className={cn(inputTextClassName, className)} />
33
+ );
@@ -0,0 +1,13 @@
1
+ export const inputBaseClassName = `group/input flex w-full cursor-text items-center gap-1 rounded-lg border-2 px-3 py-2 text-on-main
2
+ text-sm select-none min-h-[2.5rem] bg-low
3
+ border-transparent text-medium hover:border-on-main hover:text-on-main hover:shadow-input
4
+ focus-within:border-primary focus-within:text-on-main
5
+ has-[input:disabled]:pointer-events-none has-[input:disabled]:cursor-auto has-[input:disabled]:opacity-40`;
6
+
7
+ export const inputActiveClassName = "border-primary";
8
+
9
+ export const inputIconClassName = `ml-auto flex size-4 flex-none shrink-0 items-center justify-center first:ml-0
10
+ [[data-margo-input-slot]+&]:ml-0`;
11
+
12
+ export const inputTextClassName = `min-w-0 flex-1 truncate border-none bg-transparent text-left text-on-main outline-none
13
+ placeholder:text-medium disabled:cursor-auto`;
@@ -0,0 +1,16 @@
1
+ import type { TagProps } from "react-renderable";
2
+ import type { ComponentPropsWithoutRef, ElementType, ReactNode } from "react";
3
+
4
+ export type InputOwnProps = {
5
+ active?: boolean;
6
+ clickable?: boolean;
7
+ };
8
+
9
+ export type InputProps<T extends ElementType = "div"> = TagProps<T, InputOwnProps>;
10
+
11
+ export type InputIconProps = {
12
+ icon?: ReactNode;
13
+ className?: string;
14
+ };
15
+
16
+ export type InputTextProps = ComponentPropsWithoutRef<"input">;
@@ -9,7 +9,6 @@ import {
9
9
  itemDisabledClassName,
10
10
  itemIconClassName,
11
11
  itemLabelClassName,
12
- itemNotClickableClassName,
13
12
  itemSurfaceClassName,
14
13
  } from "./style.js";
15
14
 
@@ -44,12 +43,11 @@ export const Item = <T extends ElementType = "button">({
44
43
  data-margo-active={active}
45
44
  data-margo-disabled={disabled}
46
45
  aria-disabled={disabled || undefined}
47
- tabIndex={disabled || !clickable ? -1 : undefined}
46
+ inert={disabled || !clickable || undefined}
48
47
  onClick={handleClick}
49
48
  className={cn(
50
49
  itemBaseClassName,
51
50
  active ? itemActiveClassName : itemSurfaceClassName,
52
- !clickable && itemNotClickableClassName,
53
51
  disabled && itemDisabledClassName,
54
52
  className,
55
53
  )}
@@ -1,16 +1,14 @@
1
1
  export const itemBaseClassName = `group/item flex w-full cursor-pointer items-center gap-1 rounded-lg border-2 px-3 py-2 text-on-main
2
- text-sm focus-visible:margo-border-gradient-primary focus-visible:outline select-none
3
- focus-visible:outline-offset-1 focus-visible:outline-on-main
4
- origin-center bg-border/20 transition-transform duration-[160ms] ease-out active:scale-[0.98]`;
2
+ text-sm focus-visible:border-primary focus-visible:outline select-none
3
+ focus-visible:outline-offset-1 min-h-[2.5rem] hover:border-on-main focus-visible:outline-on-main
4
+ origin-center bg-low transition-transform duration-[160ms] ease-out active:scale-[0.98]`;
5
5
 
6
- export const itemSurfaceClassName = `border-transparent text-medium hover:border-border hover:text-on-main
6
+ export const itemSurfaceClassName = `border-transparent text-medium hover:border-on-main hover:text-on-main
7
7
  hover:shadow-item focus-visible:shadow-item`;
8
8
 
9
- export const itemActiveClassName = "margo-border-gradient-primary text-on-main shadow-item";
9
+ export const itemActiveClassName = "border-primary";
10
10
 
11
- export const itemNotClickableClassName = "pointer-events-none cursor-auto";
12
-
13
- export const itemDisabledClassName = "pointer-events-none cursor-auto opacity-40";
11
+ export const itemDisabledClassName = "opacity-40";
14
12
 
15
13
  export const itemIconClassName = `ml-auto flex size-4 shrink-0 items-center justify-center first:ml-0
16
14
  [[data-margo-item-slot]+&]:ml-0`;
@@ -0,0 +1,15 @@
1
+ import { Tag } from "react-renderable";
2
+
3
+ import { cn } from "../../utils/cn/cn.js";
4
+ import { labelBaseClassName, labelDisabledClassName } from "./style.js";
5
+
6
+ import type { ElementType } from "react";
7
+ import type { LabelProps } from "./type.js";
8
+
9
+ export const Label = <T extends ElementType = "label">({ disabled = false, className, ...rest }: LabelProps<T>) => (
10
+ <Tag
11
+ {...Tag.forward<T>(rest, "label")}
12
+ data-margo-disabled={disabled}
13
+ className={cn(labelBaseClassName, disabled && labelDisabledClassName, className)}
14
+ />
15
+ );
@@ -0,0 +1,3 @@
1
+ export const labelBaseClassName = "block text-mc uppercase tracking-widest text-medium truncate pl-3 mb-1";
2
+
3
+ export const labelDisabledClassName = "opacity-40";
@@ -0,0 +1,8 @@
1
+ import type { TagProps } from "react-renderable";
2
+ import type { ElementType } from "react";
3
+
4
+ export type LabelOwnProps = {
5
+ disabled?: boolean;
6
+ };
7
+
8
+ export type LabelProps<T extends ElementType = "label"> = TagProps<T, LabelOwnProps>;
package/src/css/theme.css CHANGED
@@ -6,6 +6,7 @@
6
6
  --breakpoint-900: 900px;
7
7
  --breakpoint-1080: 1080px;
8
8
  --breakpoint-1280: 1280px;
9
+ --breakpoint-1440: 1440px;
9
10
  --breakpoint-1920: 1920px;
10
11
 
11
12
  /* Fonts */
@@ -19,7 +20,8 @@
19
20
  --color-on-main: var(--margo-color-on-main);
20
21
  --color-primary: var(--margo-color-primary);
21
22
  --color-primary-darken: var(--margo-color-primary-darken);
22
- --color-secondary: var(--margo-color-secondary);
23
+ --color-low: var(--margo-color-low);
24
+ --color-low-alpha: var(--margo-color-low-alpha);
23
25
  --color-medium: var(--margo-color-medium);
24
26
  --color-border: var(--margo-color-border);
25
27
 
@@ -27,6 +29,7 @@
27
29
  --shadow-card: var(--margo-shadow-card);
28
30
  --shadow-button: var(--margo-shadow-button);
29
31
  --shadow-item: var(--margo-shadow-item);
32
+ --shadow-input: var(--margo-shadow-input);
30
33
  --shadow-chip: var(--margo-shadow-chip);
31
34
 
32
35
  /* Durations */
@@ -84,5 +87,4 @@
84
87
  --text-8xl--line-height: 0.75;
85
88
  --text-9xl: var(--margo-font-size-9xl);
86
89
  --text-9xl--line-height: 0.75;
87
-
88
90
  }
@@ -1,10 +1,3 @@
1
- @utility margo-border-gradient-primary {
2
- border-color: transparent;
3
- background:
4
- linear-gradient(var(--margo-color-main), var(--margo-color-main)) padding-box,
5
- linear-gradient(to bottom right, var(--margo-color-primary-darken), var(--margo-color-primary)) border-box;
6
- }
7
-
8
1
  @utility margo-text-box-trim {
9
2
  text-box: trim-start cap alphabetic;
10
3
  }
@@ -48,17 +48,19 @@
48
48
 
49
49
  --margo-color-neutral: #ffffff;
50
50
  --margo-color-on-neutral: #000000;
51
- --margo-color-main: #e7e7e7;
51
+ --margo-color-main: #edecec;
52
52
  --margo-color-on-main: #151515;
53
53
  --margo-color-primary: #57e550;
54
54
  --margo-color-primary-darken: #45b93f;
55
- --margo-color-secondary: #d3d3d3;
55
+ --margo-color-low: color-mix(in srgb, var(--margo-color-on-main) 4%, var(--margo-color-main));
56
+ --margo-color-low-alpha: color-mix(in srgb, var(--margo-color-on-main) 4%, transparent);
56
57
  --margo-color-medium: #808080;
57
58
  --margo-color-border: #cecece;
58
59
 
59
60
  --margo-shadow-card: 0 8px 6px -4px rgb(220, 220, 220, 0.5);
60
- --margo-shadow-button: 0px 2px 3px -1px rgb(220, 220, 220, 0.6);
61
- --margo-shadow-item: 0px 2px 3px -1px rgb(220, 220, 220, 0.6);
61
+ --margo-shadow-button: 0 2px 3px -1px rgb(220, 220, 220, 0.6);
62
+ --margo-shadow-item: 0 3px 3px -3px rgba(139, 139, 139, 0.6);
63
+ --margo-shadow-input: 0 3px 3px -3px rgba(139, 139, 139, 0.6);
62
64
  --margo-shadow-chip: 0 1px 3px -1px rgb(0, 0, 0, 0.6);
63
65
 
64
66
  --margo-code-surface: #fbfbfb;
@@ -84,13 +86,15 @@
84
86
  --margo-color-on-main: #eeeeee;
85
87
  --margo-color-primary: #57e550;
86
88
  --margo-color-primary-darken: #45b93f;
87
- --margo-color-secondary: #2c2c2c;
89
+ --margo-color-low: color-mix(in srgb, var(--margo-color-on-main) 2.5%, var(--margo-color-main));
90
+ --margo-color-low-alpha: color-mix(in srgb, var(--margo-color-on-main) 2.5%, transparent);
88
91
  --margo-color-medium: #999999;
89
92
  --margo-color-border: #2c2c2c;
90
93
 
91
94
  --margo-shadow-card: 0 4px 12px -4px rgb(0, 0, 0, 0.5);
92
95
  --margo-shadow-button: 0 2px 6px -2px rgb(0, 0, 0, 0.5);
93
- --margo-shadow-item: 0 2px 6px -2px rgb(0, 0, 0, 0.5);
96
+ --margo-shadow-item: 0 2px 3px -2px rgb(0, 0, 0, 0.5);
97
+ --margo-shadow-input: 0 2px 3px -2px rgb(0, 0, 0, 0.5);
94
98
  --margo-shadow-chip: 0 1px 3px -1px rgb(0, 0, 0, 0.5);
95
99
 
96
100
  --margo-code-surface: #0d0d0d;
@@ -28,14 +28,14 @@
28
28
  transition: opacity 300ms ease-out;
29
29
  }
30
30
 
31
- .margo-background-glow:hover::before,
32
- .margo-background-glow:focus-within::before {
33
- opacity: var(--margo-background-glow-opacity, 0.1);
34
- }
31
+ @media (hover: hover) and (pointer: fine) {
32
+ .margo-background-glow:hover::before {
33
+ opacity: var(--margo-background-glow-opacity, 0.1);
34
+ }
35
35
 
36
- .margo-background-glow:hover::after,
37
- .margo-background-glow:focus-within::after {
38
- opacity: var(--margo-background-glow-noise-opacity, 0.04);
36
+ .margo-background-glow:hover::after {
37
+ opacity: var(--margo-background-glow-noise-opacity, 0.04);
38
+ }
39
39
  }
40
40
 
41
41
 
@@ -10,6 +10,8 @@ import "./background_glow.css";
10
10
 
11
11
  export const BackgroundGlow = ({ children, tolerance = 1, opacity = 0.2, noise = 0.04 }: BackgroundGlowProps) => {
12
12
  const handlePointerMove = (event: PointerEvent<HTMLElement>) => {
13
+ if (event.pointerType !== "mouse") return children.props.onPointerMove?.(event);
14
+
13
15
  const bounds = event.currentTarget.getBoundingClientRect();
14
16
  const spread = Math.max(bounds.width, bounds.height) * Math.min(Math.max(tolerance, 0), 1);
15
17
 
@@ -6,15 +6,16 @@
6
6
  opacity: 0;
7
7
  background: radial-gradient(
8
8
  var(--margo-border-glow-size, 480px) circle at var(--margo-border-glow-x, 70%) var(--margo-border-glow-y, 70%),
9
- var(--color-primary-darken),
9
+ var(--color-primary),
10
10
  transparent 70%
11
11
  );
12
12
  transition: opacity 300ms ease-out;
13
13
  }
14
14
 
15
- .margo-border-glow:hover::before,
16
- .margo-border-glow:focus-within::before {
17
- opacity: 1;
15
+ @media (hover: hover) and (pointer: fine) {
16
+ .margo-border-glow:hover::before {
17
+ opacity: 1;
18
+ }
18
19
  }
19
20
 
20
21
  .margo-border-glow--ring::before {
@@ -10,6 +10,8 @@ import "./border_glow.css";
10
10
 
11
11
  export const BorderGlow = ({ children, position = "all", tolerance = 1 }: BorderGlowProps) => {
12
12
  const handlePointerMove = (event: PointerEvent<HTMLElement>) => {
13
+ if (event.pointerType !== "mouse") return children.props.onPointerMove?.(event);
14
+
13
15
  const bounds = event.currentTarget.getBoundingClientRect();
14
16
  const x = event.clientX - bounds.left;
15
17
  const y = event.clientY - bounds.top;
package/src/index.ts CHANGED
@@ -6,7 +6,9 @@ export { Chip } from "./components/chip/chip.js";
6
6
  export { Code } from "./components/code/code.js";
7
7
  export { Dialog } from "./components/dialog/dialog.js";
8
8
  export { Header } from "./components/header/header.js";
9
+ export { Label } from "./components/label/label.js";
9
10
  export { Layer } from "./components/layer/layer.js";
11
+ export { Input } from "./components/input/input.js";
10
12
  export { Item } from "./components/item/item.js";
11
13
  export { NavigationBar } from "./components/navigation_bar/navigation_bar.js";
12
14
  export { ProgressBar } from "./components/progress_bar/progress_bar.js";
@@ -51,7 +53,9 @@ export type {
51
53
  HeaderTitleProps,
52
54
  HeaderTrailingProps,
53
55
  } from "./components/header/type.js";
56
+ export type { LabelOwnProps, LabelProps } from "./components/label/type.js";
54
57
  export type { LayerProps } from "./components/layer/type.js";
58
+ export type { InputIconProps, InputProps, InputTextProps } from "./components/input/type.js";
55
59
  export type { ItemIconProps, ItemLabelProps, ItemProps } from "./components/item/type.js";
56
60
  export type { NavigationBarProps } from "./components/navigation_bar/type.js";
57
61
  export type { ProgressBarMode, ProgressBarProps } from "./components/progress_bar/type.js";