lucentia-ui 0.2.18 → 0.2.20

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.
@@ -4,6 +4,7 @@
4
4
  font-family: var(--font);
5
5
  font-weight: var(--font-weight-medium);
6
6
  border: 2px solid transparent;
7
+ border-radius: var(--radius-sm);
7
8
  cursor: pointer;
8
9
 
9
10
 
@@ -125,14 +126,12 @@
125
126
  padding: var(--space-xs) var(--space-lg);
126
127
  font-size: var(--font-size-14);
127
128
  box-shadow: var(--shadow-sm);
128
- border-radius: var(--radius-sm);
129
129
  }
130
130
 
131
131
  .md {
132
132
  padding: var(--space-sm) var(--space-2xl);
133
133
  font-size: var(--font-size-16);
134
134
  box-shadow: var(--shadow-md);
135
- border-radius: var(--radius-sm);
136
135
  }
137
136
 
138
137
 
@@ -7,3 +7,4 @@ export declare const Default: Story;
7
7
  export declare const State: Story;
8
8
  export declare const Variants: Story;
9
9
  export declare const Sizes: Story;
10
+ export declare const Width: Story;
@@ -54,3 +54,11 @@ export const Sizes = {
54
54
  gap: 32,
55
55
  }, children: [_jsx(Button, { size: "sm", children: "Small" }), _jsx(Button, { size: "md", children: "Medium" })] })),
56
56
  };
57
+ export const Width = {
58
+ render: () => (_jsx("div", { style: {
59
+ display: "flex",
60
+ flexDirection: "column",
61
+ alignItems: "flex-start",
62
+ gap: 32,
63
+ }, children: _jsx(Button, { size: "md", fullWidth: true, children: "Full Width" }) })),
64
+ };
@@ -28,6 +28,8 @@
28
28
  background-repeat: no-repeat;
29
29
  background-position: center;
30
30
  background-size: 24px 24px;
31
+ outline: 2px solid var(--color-background); /* 背景と同じ色 */
32
+ outline-offset: -1px;
31
33
  transition: background-color 0.2s ease;
32
34
  }
33
35
 
@@ -3,7 +3,7 @@ import React from "react";
3
3
  import clsx from "clsx";
4
4
  import styles from "./IconButton.module.css";
5
5
  export const IconButton = React.forwardRef((props, ref) => {
6
- const { icon, children, variant = "ghost", size = "md", className, ...rest } = props;
6
+ const { icon, children, variant = "ghost", size = "md", fullWidth = false, className, ...rest } = props;
7
7
  // a11y guard (dev only)
8
8
  if (process.env.NODE_ENV !== "production" &&
9
9
  !children &&
@@ -13,6 +13,7 @@ export const IconButton = React.forwardRef((props, ref) => {
13
13
  return (_jsxs("button", { ref: ref, className: clsx(styles.iconButton, styles[variant], styles[size], {
14
14
  [styles.iconOnly]: !children,
15
15
  [styles.withText]: !!children,
16
+ [styles.fullWidth]: !!fullWidth,
16
17
  }, className), ...rest, children: [_jsx("span", { className: styles.icon, children: icon }), children && _jsx("span", { className: styles.text, children: children })] }));
17
18
  });
18
19
  IconButton.displayName = "IconButton";
@@ -23,11 +23,27 @@
23
23
 
24
24
  /* ===== Variant ===== */
25
25
  .ghost {
26
- background: var(--color-surface);
26
+ background: var(--color-surface-container);
27
27
  color: var(--color-on-surface);
28
28
  }
29
- .ghost:hover:not(:disabled) {
30
- background: var(--color-surface-container);
29
+
30
+ .sm.ghost:hover:not(:disabled) {
31
+ border: 2px solid var(--color-border);
32
+ box-shadow:
33
+ 0 0 2px var(--color-border),
34
+ 0 0 4px var(--color-border);
35
+ }
36
+
37
+ .md.ghost:hover:not(:disabled) {
38
+ border: 2px solid var(--color-border);
39
+ box-shadow:
40
+ 0 0 4px var(--color-border),
41
+ 0 0 8px var(--color-border);
42
+ }
43
+
44
+ .sm.ghost:active:not(:disabled),
45
+ .md.ghost:active:not(:disabled) {
46
+ box-shadow: none;
31
47
  }
32
48
 
33
49
 
@@ -50,13 +66,12 @@
50
66
  0 0 8px var(--color-primary);
51
67
  }
52
68
 
53
- .md.primary:hover:not(:disabled) {
54
- border: 2px solid var(--color-primary);
55
- box-shadow:
56
- 0 0 4px var(--color-primary),
57
- 0 0 8px var(--color-primary);
69
+ .sm.primary:active:not(:disabled),
70
+ .md.primary:active:not(:disabled) {
71
+ box-shadow: none;
58
72
  }
59
73
 
74
+
60
75
  .secondary {
61
76
  background: var(--color-secondary-container);
62
77
  color: var(--color-on-secondary-container);
@@ -76,6 +91,14 @@
76
91
  0 0 8px var(--color-secondary);
77
92
  }
78
93
 
94
+ .sm.secondary:active:not(:disabled),
95
+ .md.secondary:active:not(:disabled) {
96
+ box-shadow: none;
97
+ }
98
+
99
+ .md.danger:active:not(:disabled) {
100
+ box-shadow: none;
101
+ }
79
102
 
80
103
  .danger {
81
104
  background: var(--color-error-container);
@@ -96,11 +119,14 @@
96
119
  0 0 8px var(--color-error);
97
120
  }
98
121
 
122
+ .sm.danger:active:not(:disabled),
99
123
  .md.danger:active:not(:disabled) {
100
124
  box-shadow: none;
101
125
  }
102
126
 
103
127
 
128
+
129
+
104
130
  /* ===== Size ===== */
105
131
 
106
132
  .sm {
@@ -136,6 +162,14 @@
136
162
 
137
163
 
138
164
 
165
+
166
+ /* ===== full width ===== */
167
+ .fullWidth {
168
+ width: 100%;
169
+ }
170
+
171
+
172
+
139
173
  /* ===== State ===== */
140
174
 
141
175
  .sm.iconButton:active:not(:disabled),
@@ -8,6 +8,8 @@ export interface IconButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLBut
8
8
  /** Inherit from Button */
9
9
  variant?: ButtonVariant;
10
10
  size?: ButtonSize;
11
+ /** Full width button */
12
+ fullWidth?: boolean;
11
13
  /** icon-only の場合に必須 */
12
14
  "aria-label"?: string;
13
15
  }
@@ -3,6 +3,8 @@
3
3
  display: inline-block;
4
4
  width: 64px;
5
5
  height: 36px;
6
+ outline: 2px solid var(--color-background); /* 背景と同じ色 */
7
+ outline-offset: -1px;
6
8
  }
7
9
 
8
10
  /* input は見えなくするが状態は保持 */
@@ -40,6 +42,8 @@
40
42
  /* ===== Checked ===== */
41
43
  .switch input:checked + .slider {
42
44
  background: var(--color-primary);
45
+ outline: 2px solid var(--color-background); /* 背景と同じ色 */
46
+ outline-offset: -1px;
43
47
  }
44
48
 
45
49
  .switch input:checked + .slider::before {
@@ -4,5 +4,5 @@ type Props = {
4
4
  persist?: boolean;
5
5
  children: React.ReactNode;
6
6
  };
7
- export declare function ThemeProvider({ defaultTheme, persist, children, }: Props): import("react/jsx-runtime").JSX.Element;
7
+ export declare function ThemeProvider({ defaultTheme, persist, children, }: Props): import("react/jsx-runtime").JSX.Element | null;
8
8
  export {};
@@ -3,17 +3,18 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useEffect, useState, useCallback } from "react";
4
4
  import { ThemeContext } from "./context";
5
5
  export function ThemeProvider({ defaultTheme = "light", persist = false, children, }) {
6
- const [theme, setTheme] = useState(defaultTheme);
6
+ const [theme, setTheme] = useState(persist ? null : defaultTheme);
7
7
  // 初期化
8
8
  useEffect(() => {
9
9
  if (!persist)
10
10
  return;
11
11
  const stored = localStorage.getItem("theme");
12
- if (stored)
13
- setTheme(stored);
14
- }, [persist]);
12
+ setTheme(stored !== null && stored !== void 0 ? stored : defaultTheme);
13
+ }, [persist, defaultTheme]);
15
14
  // html[data-theme] 反映 + 永続化
16
15
  useEffect(() => {
16
+ if (!theme)
17
+ return;
17
18
  document.documentElement.dataset.theme = theme;
18
19
  if (persist) {
19
20
  localStorage.setItem("theme", theme);
@@ -22,5 +23,8 @@ export function ThemeProvider({ defaultTheme = "light", persist = false, childre
22
23
  const toggleTheme = useCallback(() => {
23
24
  setTheme((prev) => (prev === "dark" ? "light" : "dark"));
24
25
  }, []);
26
+ // ★ theme が確定するまで描画しない
27
+ if (!theme)
28
+ return null;
25
29
  return (_jsx(ThemeContext.Provider, { value: { theme, setTheme, toggleTheme }, children: children }));
26
30
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lucentia-ui",
3
- "version": "0.2.18",
3
+ "version": "0.2.20",
4
4
  "description": "React UI design token and component system based on neumorphism, featuring two color themes: light and dark.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",