lucentia-ui 0.2.24 → 1.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright 2026 Rikiya Matsuda
3
+ Copyright (c) 2026 Rikiya Matsuda
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.ja.md ADDED
@@ -0,0 +1,121 @@
1
+
2
+ # lucentia-ui
3
+
4
+ ニューモーフィズムを基調としたReact UI デザイントークン&コンポーネント。ライト/ダークの2カラーテーマを保持。
5
+
6
+
7
+ ---
8
+
9
+ ## インストール方法
10
+
11
+ ```bash
12
+ npm install lucentia-ui
13
+ ```
14
+
15
+ yarnの場合
16
+
17
+ ```bash
18
+ yarn add lucentia-ui
19
+ ```
20
+
21
+ ---
22
+
23
+ ## 対応環境
24
+
25
+ - React 18+
26
+ - CSS Modules 対応環境(Next.js / Vite / CRA など)
27
+
28
+ ## 基本の使い方
29
+
30
+ ### 1.グローバルスタイルを import(必須)
31
+
32
+ ※lucentia-uiを使用する場合、グローバルスタイルの import が必須です。
33
+ ※ Next.js の場合は `_app.tsx` や `layout.tsx` で import してください
34
+
35
+
36
+ ```tsx
37
+ import "lucentia-ui/styles";
38
+ ```
39
+
40
+
41
+ lucentia-ui のコンポーネントは
42
+ CSS Variables(Design Tokens)を前提に設計されています。
43
+
44
+
45
+ ```css
46
+ :root {
47
+ --color-background: #ffffff;
48
+ --color-text: #111111;
49
+ }
50
+ ```
51
+
52
+ import "lucentia-ui/styles" によって:
53
+
54
+ - Token 定義
55
+ - ベーススタイル
56
+ - 各コンポーネントの見た目
57
+ が有効化されます。
58
+
59
+
60
+ ### 2.コンポーネントを使用
61
+
62
+ ```tsx
63
+ import { Button, Checkbox, Radio } from "lucentia-ui";
64
+
65
+ export default function Example() {
66
+ return (
67
+ <>
68
+ <Button variant="primary">Save</Button>
69
+ <Checkbox label="Accept terms" />
70
+ <Radio label="Option A" />
71
+ </>
72
+ );
73
+ }
74
+ ```
75
+
76
+ - 追加の CSS import は不要
77
+ - デフォルトでは light テーマ が適用されます
78
+
79
+ ---
80
+
81
+
82
+ ### ダークテーマ
83
+
84
+ lucentia-ui では、
85
+ ダークテーマやテーマ切り替えを行う場合のみ ThemeProvider を使用します。
86
+
87
+
88
+ #### テーマ設定例
89
+
90
+ ```tsx
91
+ import { ThemeProvider } from "lucentia-ui";
92
+
93
+ export default function App({ children }: { children: React.ReactNode }) {
94
+ return (
95
+ <ThemeProvider defaultTheme="dark">
96
+ {children}
97
+ </ThemeProvider>
98
+ );
99
+ }
100
+ ```
101
+
102
+ ## コンポーネント設計方針
103
+
104
+ - **HTML ネイティブ要素を尊重**
105
+ - **props extends HTMLAttributes**
106
+ - **状態・色・サイズは Design Tokens に依存**
107
+ - **Tailwind / inline style 不使用**
108
+
109
+ ---
110
+
111
+ ## リリースバージョン
112
+
113
+ lucentia-ui はセマンティック バージョニングに従っています。
114
+ 破壊的変更はメジャーバージョンとしてリリースされます。
115
+
116
+
117
+
118
+
119
+ ## License
120
+
121
+ MIT
package/README.md CHANGED
@@ -1,9 +1,126 @@
1
+
1
2
  # lucentia-ui
2
3
 
3
- React UI design token and component system based on neumorphism, featuring two color themes: light and dark.
4
+ A React UI design token and component library based on neumorphism, featuring both light and dark color themes.
5
+
6
+
7
+ 日本語版はこちら → README.ja.md
8
+
9
+
10
+ ---
11
+
12
+ ## Installation
13
+
14
+ Using npm:
15
+
16
+ ```bash
17
+ npm install lucentia-ui
18
+ ```
19
+
20
+ Using yarn:
21
+
22
+ ```bash
23
+ yarn add lucentia-ui
24
+ ```
25
+
26
+ ---
27
+
28
+ ## Requirements
29
+
30
+ - React 18+
31
+ - Environment that supports CSS Modules(Next.js / Vite / CRA, etc.)
32
+
33
+ ## Usage
34
+
35
+ ### Import global styles (required)
36
+
37
+ Important: Importing global styles is required when using lucentia-ui.
38
+
39
+
40
+ ```tsx
41
+ import "lucentia-ui/styles";
42
+ ```
43
+
44
+ lucentia-ui components are designed around
45
+ CSS Variables (Design Tokens).
46
+
47
+ ```css
48
+ :root {
49
+ --color-background: #ffffff;
50
+ --color-text: #111111;
51
+ }
52
+ ```
53
+
54
+ By importing import "lucentia-ui/styles";
55
+
56
+ - the following are enabled:
57
+ - Design token definitions
58
+ - Base styles
59
+
60
+ Default component appearance
61
+
62
+ > For Next.js, import this in app/layout.tsx or pages/_app.tsx.
63
+
64
+
65
+ ### 2.Use components
66
+
67
+ ```tsx
68
+ import { Button, Checkbox, Radio } from "lucentia-ui";
69
+
70
+ export default function Example() {
71
+ return (
72
+ <>
73
+ <Button variant="primary">Save</Button>
74
+ <Checkbox label="Accept terms" />
75
+ <Radio label="Option A" />
76
+ </>
77
+ );
78
+ }
79
+ ```
80
+
81
+ - No additional CSS imports are required
82
+ - The light theme is applied by default
83
+
84
+
85
+ ---
86
+
87
+
88
+ ### ダークテーマ
89
+
90
+ Use ThemeProvider only when enabling dark mode or theme switching.
91
+
92
+
93
+ #### Theme example
94
+
95
+ ```tsx
96
+ import { ThemeProvider } from "lucentia-ui";
97
+
98
+ export default function App({ children }: { children: React.ReactNode }) {
99
+ return (
100
+ <ThemeProvider defaultTheme="dark">
101
+ {children}
102
+ </ThemeProvider>
103
+ );
104
+ }
105
+ ```
106
+
107
+ ## コンポーネント設計方針
108
+
109
+ - **Respect native HTML elements**
110
+ - **props extends HTMLAttributes**
111
+ - **Props extend standard HTML attributes**
112
+ - **No Tailwind CSS or inline styles**
4
113
 
5
114
  ---
6
115
 
7
- Not stable until version 1.00
116
+ ## Versioning
117
+
118
+ lucentia-ui follows Semantic Versioning.
119
+ Breaking changes are released as major versions.
120
+
121
+
122
+
123
+
124
+ ## License
8
125
 
9
- ---
126
+ MIT
@@ -13,7 +13,7 @@
13
13
  background: var(--color-background);
14
14
  color: var(--color-on-surface);
15
15
  border-radius: var(--radius-md);
16
- box-shadow: var(--shadow-surface-lg);
16
+ box-shadow: var(--shadow-modal);
17
17
  padding: var(--space-2xl);
18
18
  min-width: 320px;
19
19
  }
@@ -1,12 +1,11 @@
1
1
  .input {
2
2
  font-family: var(--font);
3
- padding: var(--space-md) var(--space-lg);
4
3
  border-radius: var(--radius-sm);
5
4
  border: 2px solid var(--color-background);
6
5
  outline: none;
7
6
  background: var(--color-surface-container);
8
7
  color: var(--color-on-surface);
9
-
8
+ box-sizing: border-box;
10
9
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
11
10
  }
12
11
 
@@ -23,7 +22,7 @@
23
22
  cursor: text;
24
23
  }
25
24
 
26
- .input:read-only:not(:disabled) :focus {
25
+ .input:read-only:not(:disabled):focus {
27
26
  border: 2px solid var(--color-surface);
28
27
  }
29
28
 
@@ -4,7 +4,6 @@
4
4
  gap: var(--gap-sm);
5
5
  margin: 0;
6
6
  list-style: none;
7
- padding: var(--space-lg) 0;
8
7
  }
9
8
 
10
9
  .item {
@@ -1,2 +1,2 @@
1
1
  import { SelectProps } from "./types";
2
- export declare const Select: ({ state, className, children, ...props }: SelectProps) => import("react/jsx-runtime").JSX.Element;
2
+ export declare const Select: ({ options, value, placeholder, state, disabled, onChange, className, }: SelectProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,6 +1,116 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import styles from "./Select.module.css";
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useRef, useEffect } from "react";
3
4
  import clsx from "clsx";
4
- export const Select = ({ state = "default", className, children, ...props }) => {
5
- return (_jsx("select", { className: clsx(styles.select, styles[state], className), ...props, children: children }));
5
+ import styles from "./Select.module.css";
6
+ export const Select = ({ options, value, placeholder = "Select...", state = "default", disabled, onChange, className, }) => {
7
+ var _a;
8
+ const [open, setOpen] = useState(false);
9
+ const [activeIndex, setActiveIndex] = useState(-1);
10
+ const wrapperRef = useRef(null);
11
+ const triggerRef = useRef(null);
12
+ const selectedIndex = options.findIndex((o) => o.value === value);
13
+ const selected = selectedIndex >= 0 ? options[selectedIndex] : undefined;
14
+ const optionRefs = useRef([]);
15
+ const resetActiveIndex = () => {
16
+ setActiveIndex(selectedIndex >= 0 ? selectedIndex : -1);
17
+ };
18
+ /* --------------------
19
+ * 外クリックで閉じる
20
+ * -------------------*/
21
+ useEffect(() => {
22
+ const onClick = (e) => {
23
+ var _a;
24
+ if (!((_a = wrapperRef.current) === null || _a === void 0 ? void 0 : _a.contains(e.target))) {
25
+ setOpen(false);
26
+ resetActiveIndex();
27
+ }
28
+ };
29
+ document.addEventListener("mousedown", onClick);
30
+ return () => document.removeEventListener("mousedown", onClick);
31
+ }, [selectedIndex]);
32
+ /* --------------------
33
+ * キーボード操作
34
+ * -------------------*/
35
+ const onKeyDown = (e) => {
36
+ var _a, _b;
37
+ if (disabled)
38
+ return;
39
+ switch (e.key) {
40
+ case "ArrowDown": {
41
+ e.preventDefault();
42
+ setOpen(true);
43
+ setActiveIndex((i) => {
44
+ if (i === -1) {
45
+ return selectedIndex >= 0 ? selectedIndex : 0;
46
+ }
47
+ return i < options.length - 1 ? i + 1 : 0;
48
+ });
49
+ break;
50
+ }
51
+ case "ArrowUp": {
52
+ e.preventDefault();
53
+ setOpen(true);
54
+ setActiveIndex((i) => {
55
+ if (i === -1) {
56
+ return selectedIndex >= 0 ? selectedIndex : options.length - 1;
57
+ }
58
+ return i > 0 ? i - 1 : options.length - 1;
59
+ });
60
+ break;
61
+ }
62
+ case "Enter": {
63
+ e.preventDefault();
64
+ if (!open) {
65
+ setOpen(true);
66
+ resetActiveIndex();
67
+ return;
68
+ }
69
+ if (activeIndex >= 0) {
70
+ onChange === null || onChange === void 0 ? void 0 : onChange(options[activeIndex].value);
71
+ setOpen(false);
72
+ resetActiveIndex();
73
+ (_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
74
+ }
75
+ break;
76
+ }
77
+ case "Escape": {
78
+ setOpen(false);
79
+ resetActiveIndex();
80
+ (_b = triggerRef.current) === null || _b === void 0 ? void 0 : _b.focus();
81
+ break;
82
+ }
83
+ case "Tab": {
84
+ setOpen(false);
85
+ resetActiveIndex();
86
+ break;
87
+ }
88
+ }
89
+ };
90
+ useEffect(() => {
91
+ if (!open)
92
+ return;
93
+ if (activeIndex < 0)
94
+ return;
95
+ const el = optionRefs.current[activeIndex];
96
+ el === null || el === void 0 ? void 0 : el.scrollIntoView({
97
+ block: "center",
98
+ });
99
+ }, [activeIndex, open]);
100
+ return (_jsxs("div", { ref: wrapperRef, className: clsx(styles.wrapper, styles[state], disabled && styles.disabled, className), children: [_jsxs("button", { ref: triggerRef, type: "button", className: styles.trigger, disabled: disabled, onKeyDown: onKeyDown, onClick: () => {
101
+ setOpen((v) => {
102
+ const next = !v;
103
+ if (next)
104
+ resetActiveIndex();
105
+ return next;
106
+ });
107
+ }, "aria-haspopup": "listbox", "aria-expanded": open, children: [_jsx("span", { className: clsx(!selected && styles.placeholder), children: (_a = selected === null || selected === void 0 ? void 0 : selected.label) !== null && _a !== void 0 ? _a : placeholder }), _jsx("span", { className: clsx(styles.arrow, open && styles.open), children: _jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M16.9697 8.96973C17.2626 8.67684 17.7374 8.67684 18.0303 8.96973C18.3232 9.26262 18.3232 9.73738 18.0303 10.0303L12.5303 15.5303C12.2374 15.8232 11.7626 15.8232 11.4697 15.5303L5.96973 10.0303C5.67684 9.73738 5.67684 9.26262 5.96973 8.96973C6.26262 8.67684 6.73738 8.67684 7.03028 8.96973L12 13.9395L16.9697 8.96973Z", fill: "currentColor" }) }) })] }), open && (_jsx("ul", { className: styles.dropdown, role: "listbox", children: options.map((option, index) => (_jsx("li", { ref: (el) => {
108
+ optionRefs.current[index] = el;
109
+ }, role: "option", "aria-selected": option.value === value, className: clsx(styles.option, index === activeIndex && styles.active, option.value === value && styles.selected), onMouseDown: (e) => e.preventDefault(), onClick: () => {
110
+ var _a;
111
+ onChange === null || onChange === void 0 ? void 0 : onChange(option.value);
112
+ setOpen(false);
113
+ resetActiveIndex();
114
+ (_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
115
+ }, children: option.label }, option.value))) }))] }));
6
116
  };
@@ -1,43 +1,107 @@
1
- .select {
2
- font-family: var(--font);
1
+ .wrapper {
2
+ position: relative;
3
+ display: inline-block;
4
+ width: auto;
5
+ }
6
+
7
+ .trigger {
8
+ /* all: unset; は避け、必要なものだけリセットする */
9
+ width: 100%;
10
+ appearance: none;
11
+ background: none;
12
+ color: inherit;
13
+ border: none;
14
+ padding: 0;
15
+ font: inherit;
16
+ cursor: pointer;
17
+ outline: none;
18
+ /* 以降、既存のスタイルを調整 */
19
+ display: flex; /* 中の要素を整列させやすくするため */
20
+ align-items: center;
21
+ justify-content: space-between;
22
+ gap: var(--gap-sm);
23
+ box-sizing: border-box;
3
24
  padding: var(--space-sm) var(--space-md);
4
25
  border: 2px solid transparent;
5
26
  border-radius: var(--radius-sm);
6
27
  box-shadow: var(--shadow-md);
7
-
8
28
  background: var(--color-surface-container);
9
29
  color: var(--color-on-surface);
30
+ }
10
31
 
11
- appearance: none;
12
- -webkit-appearance: none;
32
+ .trigger:not(:disabled):hover {
33
+ border: 2px solid var(--color-border);
34
+ box-shadow:
35
+ 0 0 4px var(--color-border),
36
+ 0 0 8px var(--color-border);
37
+ }
13
38
 
14
- background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%3E%3Cpath%20d%3D%22M6.5%209.5L12%2015L18%209.5%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E");
39
+ .trigger:not(:disabled):active{
40
+ box-shadow: none;
41
+ }
15
42
 
16
- background-repeat: no-repeat;
17
- background-position: right var(--space-sm) center;
18
- background-size: 24px;
43
+ .placeholder {
44
+ color: var(--color-on-surface);
45
+ }
19
46
 
20
- padding-right: calc(var(--space-md) + 32px + var(--space-sm));
47
+ .arrow {
48
+ transition: transform 0.2s ease;
49
+ color: var(--color-on-surface);
50
+ }
21
51
 
52
+ .arrow.open {
53
+ transform: rotate(180deg);
54
+ }
55
+
56
+ .dropdown {
57
+ position: absolute;
58
+ z-index: 10;
59
+ width: 100%;
60
+ max-height: 300px;
61
+ margin-top: var(--space-sm);
62
+ border-radius: var(--radius-sm);
63
+ background: var(--color-surface);
64
+ box-shadow: var(--shadow-lg);
65
+ backdrop-filter: var(--blur);
66
+ overflow: hidden;
67
+ overflow-y: auto;
68
+ }
69
+
70
+ .option {
71
+ padding: 8px 12px;
22
72
  cursor: pointer;
73
+ list-style: none;
23
74
  }
24
75
 
25
- select:has(option[value=""]:checked) {
26
- color: var(--color-on-surface);
76
+ .option:hover {
77
+ background: var(--color-surface-container);
27
78
  }
28
79
 
29
- /* focus */
30
- .select:focus {
31
- outline: none;
80
+ /* ★重要:キーボードで選択中の項目のスタイル */
81
+ .option.active {
82
+ background: var(--color-primary-container);
83
+ color: var(--color-on-primary-container);
84
+ }
85
+
86
+ .selected {
87
+ background: var(--color-primary-container);
88
+ color: var(--color-on-primary-container);
32
89
  }
33
90
 
34
- /* error */
35
- .error {
36
- border: 2px solid var(--color-error);
91
+ .dropdown:has(.option.active) .option.selected:not(.active) {
92
+ background: transparent;
93
+ color: var(--color-on-surface); /* 通常の文字色に戻す */
94
+ }
95
+
96
+ /* states */
97
+
98
+ .error .trigger {
99
+ border-color: var(--color-error);
37
100
  }
38
101
 
39
- /* disabled */
40
- .select:disabled {
102
+ .disabled .trigger:hover,
103
+ .trigger:disabled:hover,
104
+ .disabled, .disabled > .trigger {
41
105
  opacity: 0.5;
42
106
  cursor: not-allowed;
43
107
  }
@@ -4,4 +4,5 @@ declare const meta: Meta<typeof Select>;
4
4
  export default meta;
5
5
  type Story = StoryObj<typeof Select>;
6
6
  export declare const Default: Story;
7
+ export declare const Selected: Story;
7
8
  export declare const State: Story;
@@ -1,22 +1,51 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
2
3
  import { Select } from "./Select";
3
4
  const meta = {
4
5
  title: "Components/Select",
5
6
  component: Select,
6
7
  };
7
8
  export default meta;
9
+ const OPTIONS = [
10
+ { label: "Option A", value: "a" },
11
+ { label: "Option B", value: "b" },
12
+ { label: "Option C", value: "c" },
13
+ ];
14
+ /* --------------------------------
15
+ * Default
16
+ * -------------------------------- */
8
17
  export const Default = {
9
- args: {
10
- children: (_jsxs(_Fragment, { children: [_jsx("option", { value: "", children: "\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044" }), _jsx("option", { value: "a", children: "Option A" }), _jsx("option", { value: "b", children: "Option B" })] })),
18
+ render: () => {
19
+ const [value, setValue] = useState();
20
+ return (_jsx("div", { style: {
21
+ display: "flex",
22
+ flexDirection: "column",
23
+ gap: 32,
24
+ width: 240,
25
+ }, children: _jsx(Select, { options: OPTIONS, value: value, placeholder: "\u672A\u9078\u629E", onChange: setValue }) }));
26
+ },
27
+ };
28
+ /* --------------------------------
29
+ * With Selected Value
30
+ * -------------------------------- */
31
+ export const Selected = {
32
+ render: () => {
33
+ const [value, setValue] = useState("b");
34
+ return _jsx(Select, { options: OPTIONS, value: value, onChange: setValue });
11
35
  },
12
36
  };
37
+ /* --------------------------------
38
+ * States
39
+ * -------------------------------- */
13
40
  export const State = {
14
41
  render: () => {
42
+ const [value1, setValue1] = useState();
43
+ const [value2, setValue2] = useState();
15
44
  return (_jsxs("div", { style: {
16
45
  display: "flex",
17
46
  flexDirection: "column",
18
- alignItems: "flex-start",
19
47
  gap: 32,
20
- }, children: [_jsxs(Select, { children: [_jsx("option", { value: "", children: "Default State" }), _jsx("option", { value: "a", children: "Option A" }), _jsx("option", { value: "b", children: "Option B" })] }), _jsxs(Select, { state: "error", required: true, children: [_jsx("option", { value: "", disabled: true, selected: true, hidden: true, children: "Error State" }), _jsx("option", { value: "a", children: "Option A" }), _jsx("option", { value: "b", children: "Option B" })] }), _jsxs(Select, { disabled: true, children: [_jsx("option", { value: "", children: "Disabled State" }), _jsx("option", { value: "a", children: "Option A" }), _jsx("option", { value: "b", children: "Option B" })] })] }));
48
+ width: 240,
49
+ }, children: [_jsx(Select, { options: OPTIONS, value: value1, placeholder: "Default State", onChange: setValue1 }), _jsx(Select, { options: OPTIONS, value: value2, placeholder: "Error State", state: "error", onChange: setValue2 }), _jsx(Select, { options: OPTIONS, placeholder: "Disabled State", disabled: true })] }));
21
50
  },
22
51
  };
@@ -1,4 +1,14 @@
1
1
  export type SelectState = "default" | "error";
2
- export interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
2
+ export type SelectOption = {
3
+ label: string;
4
+ value: string;
5
+ };
6
+ export interface SelectProps {
7
+ options: SelectOption[];
8
+ value?: string;
9
+ placeholder?: string;
3
10
  state?: SelectState;
11
+ disabled?: boolean;
12
+ onChange?: (value: string) => void;
13
+ className?: string;
4
14
  }
@@ -25,12 +25,12 @@
25
25
  }
26
26
 
27
27
  /* ===== Readonly ===== */
28
- .textarea:read-only {
28
+ .textarea:read-only:not(:disabled) {
29
29
  box-shadow: none;
30
30
  cursor: text;
31
31
  }
32
32
 
33
- .textarea:read-only:focus {
33
+ .textarea:read-only:not(:disabled):focus {
34
34
  border: 2px solid var(--color-surface);
35
35
  }
36
36
 
@@ -4,4 +4,4 @@ export type AsProp<E extends React.ElementType> = {
4
4
  };
5
5
  export type PolymorphicProps<E extends React.ElementType, P> = P & AsProp<E> & Omit<React.ComponentPropsWithoutRef<E>, keyof P | "as">;
6
6
  export type TypographyVariant = "display-lg" | "display-md" | "display-sm" | "heading-xl" | "heading-lg" | "heading-md" | "heading-sm" | "heading-xs" | "body-md" | "body-sm" | "label-md" | "label-sm";
7
- export type TypographyColor = "default" | "muted" | "primary" | "secondary" | "error" | "success";
7
+ export type TypographyColor = "default" | "surface" | "muted" | "primary" | "secondary" | "error" | "success";
@@ -33,25 +33,25 @@
33
33
  .heading-lg {
34
34
  font-size: var(--font-size-24);
35
35
  line-height: var(--line-snug);
36
- font-weight: var(--font-weight-medium);
36
+ font-weight: var(--font-weight-bold);
37
37
  }
38
38
 
39
39
  .heading-md {
40
40
  font-size: var(--font-size-20);
41
41
  line-height: var(--line-snug);
42
- font-weight: var(--font-weight-medium);
42
+ font-weight: var(--font-weight-bold);
43
43
  }
44
44
 
45
45
  .heading-sm {
46
46
  font-size: var(--font-size-18);
47
47
  line-height: var(--line-snug);
48
- font-weight: var(--font-weight-medium);
48
+ font-weight: var(--font-weight-bold);
49
49
  }
50
50
 
51
51
  .heading-xs {
52
52
  font-size: var(--font-size-16);
53
53
  line-height: var(--line-snug);
54
- font-weight: var(--font-weight-medium);
54
+ font-weight: var(--font-weight-bold);
55
55
  }
56
56
 
57
57
  /* ========================================
@@ -61,13 +61,13 @@
61
61
  .body-md {
62
62
  font-size: var(--font-size-16);
63
63
  line-height: var(--line-normal);
64
- font-weight: var(--font-weight-regular);
64
+ font-weight: var(--font-weight-medium);
65
65
  }
66
66
 
67
67
  .body-sm {
68
68
  font-size: var(--font-size-14);
69
69
  line-height: var(--line-normal);
70
- font-weight: var(--font-weight-regular);
70
+ font-weight: var(--font-weight-medium);
71
71
  }
72
72
 
73
73
  /* ========================================
@@ -77,13 +77,13 @@
77
77
  .label-md {
78
78
  font-size: var(--font-size-16);
79
79
  line-height: var(--line-snug);
80
- font-weight: var(--font-weight-medium);
80
+ font-weight: var(--font-weight-regular);
81
81
  }
82
82
 
83
83
  .label-sm {
84
84
  font-size: var(--font-size-14);
85
85
  line-height: var(--line-snug);
86
- font-weight: var(--font-weight-medium);
86
+ font-weight: var(--font-weight-regular);
87
87
  }
88
88
 
89
89
  /* ========================================
@@ -94,6 +94,10 @@
94
94
  color: var(--color-on-background);
95
95
  }
96
96
 
97
+ .color-surface {
98
+ color: var(--color-on-surface);
99
+ }
100
+
97
101
  .color-muted {
98
102
  color: var(--color-on-surface-variant);
99
103
  }
@@ -1,133 +1,126 @@
1
1
  :root {
2
- /* font */
3
- --font-size-12: 12px;
4
- --font-size-14: 14px;
5
- --font-size-16: 16px;
6
- --font-size-18: 18px;
7
- --font-size-20: 20px;
8
- --font-size-24: 24px;
9
- --font-size-28: 28px;
10
- --font-size-32: 32px;
11
- --font-size-40: 40px;
12
- --font-size-48: 48px;
13
-
14
- --line-tight: 1.2;
15
- --line-snug: 1.35;
16
- --line-normal: 1.6;
17
-
18
- --font-weight-regular: 400;
19
- --font-weight-medium: 500;
20
- --font-weight-bold: 600;
21
-
22
- /* ===== Radius ===== */
23
- --radius-xs: 4px;
24
- --radius-sm: 8px;
25
- --radius-md: 16px;
26
- --radius-max: 999px;
27
-
28
- /* ===== Space ===== */
29
- --space-xs: 4px;
30
- --space-sm: 8px;
31
- --space-md: 12px;
32
- --space-lg: 16px;
33
- --space-xl: 24px;
34
- --space-2xl: 32px;
35
- --space-3xl: 48px;
36
- --space-4xl: 64px;
37
- --space-5xl: 96px;
38
- --gap-sm: 16px;
39
- --gap-md: 32px;
40
- --gap-lg: 64px;
41
-
42
- /* ===== Layout ===== */
43
- --container: 1120px;
44
-
45
- /* ===== Color ===== */
46
- --color-primary: #00a1a1;
47
- --color-on-primary: #ffffff;
48
- --color-primary-container: #9cf1f0;
49
- --color-on-primary-container: #004f4f;
50
-
51
- --color-secondary: #7b9695;
52
- --color-on-secondary: #ffffff;
53
- --color-secondary-container: #cce8e7;
54
- --color-on-secondary-container: #324b4b;
55
-
56
- --color-error: #ff5449;
57
- --color-on-error: #ffffff;
58
- --color-error-container: #ffdad6;
59
- --color-on-error-container: #93000a;
60
-
61
- --color-background: #f4fbfa;
62
- --color-on-background: #161d1d;
63
-
64
- --color-surface: #f4fbfabf;
65
- --color-on-surface: #3a4444;
66
- --color-on-surface-variant: #161d1d80;
67
- --color-surface-container: #e9efeebf;
68
-
69
- --color-border: #bbcccc;
70
- --color-scrim: rgba(129, 129, 129, 0.25);
71
-
72
- --color-shadow-l: rgba(255, 255, 255, 1);
73
- --color-shadow-d: rgba(0, 0, 0, 0.2);
74
-
75
- /* ===== Effect ===== */
76
- --blur: blur(8px);
77
-
78
- --shadow-sm:
79
- -2px -2px 2px 1px var(--color-shadow-l),
80
- 2px 2px 2px 1px var(--color-shadow-d);
81
-
82
- --shadow-sm-in:
83
- inset -2px -2px 2px var(--color-shadow-l),
84
- inset 2px 2px 2px var(--color-shadow-d);
85
-
86
- --shadow-md:
87
- -2px -2px 4px 2px var(--color-shadow-l),
88
- 2px 2px 4px 2px var(--color-shadow-d);
89
-
90
- --shadow-md-in:
91
- inset -4px -4px 4px var(--color-shadow-l),
92
- inset 4px 4px 4px var(--color-shadow-d);
93
-
94
- --shadow-lg:
95
- -4px -4px 16px 8px var(--color-shadow-l),
96
- 4px 4px 16px 8px var(--color-shadow-d);
97
-
98
- --shadow-surface-lg:
99
- 0px 4px 16px 8px var(--color-shadow-d);
2
+ /* font */
3
+ --font-size-12: 12px;
4
+ --font-size-14: 14px;
5
+ --font-size-16: 16px;
6
+ --font-size-18: 18px;
7
+ --font-size-20: 20px;
8
+ --font-size-24: 24px;
9
+ --font-size-28: 28px;
10
+ --font-size-32: 32px;
11
+ --font-size-40: 40px;
12
+ --font-size-48: 48px;
13
+
14
+ --line-tight: 1.2;
15
+ --line-snug: 1.35;
16
+ --line-normal: 1.6;
17
+
18
+ --font-weight-regular: 400;
19
+ --font-weight-medium: 500;
20
+ --font-weight-bold: 600;
21
+
22
+ /* ===== Radius ===== */
23
+ --radius-sm: 8px;
24
+ --radius-md: 16px;
25
+ --radius-max: 999px;
26
+
27
+ /* ===== Space ===== */
28
+ --space-xs: 4px;
29
+ --space-sm: 8px;
30
+ --space-md: 12px;
31
+ --space-lg: 16px;
32
+ --space-xl: 24px;
33
+ --space-2xl: 32px;
34
+ --space-3xl: 48px;
35
+ --space-4xl: 64px;
36
+ --space-5xl: 96px;
37
+ --gap-sm: 16px;
38
+ --gap-md: 32px;
39
+ --gap-lg: 64px;
40
+
41
+ /* ===== Color ===== */
42
+ --color-primary: #00a1a1;
43
+ --color-on-primary: #ffffff;
44
+ --color-primary-container: #9cf1f0;
45
+ --color-on-primary-container: #004f4f;
46
+
47
+ --color-secondary: #7b9695;
48
+ --color-on-secondary: #ffffff;
49
+ --color-secondary-container: #cce8e7;
50
+ --color-on-secondary-container: #324b4b;
51
+
52
+ --color-error: #ff5449;
53
+ --color-on-error: #ffffff;
54
+ --color-error-container: #ffdad6;
55
+ --color-on-error-container: #93000a;
56
+
57
+ --color-background: #f4fbfa;
58
+ --color-on-background: #161d1d;
59
+
60
+ --color-surface: #f4fbfabf;
61
+ --color-on-surface: #3a4444;
62
+ --color-on-surface-variant: #161d1d80;
63
+ --color-surface-container: #e9efeebf;
64
+
65
+ --color-border: #bbcccc;
66
+ --color-scrim: rgba(129, 129, 129, 0.25);
67
+
68
+ --color-shadow-l: rgba(255, 255, 255, 1);
69
+ --color-shadow-d: rgba(0, 0, 0, 0.2);
70
+
71
+ /* ===== Effect ===== */
72
+ --blur: blur(8px);
73
+
74
+ --shadow-sm:
75
+ -2px -2px 2px 1px var(--color-shadow-l),
76
+ 2px 2px 2px 1px var(--color-shadow-d);
77
+
78
+ --shadow-sm-in:
79
+ inset -2px -2px 2px var(--color-shadow-l),
80
+ inset 2px 2px 2px var(--color-shadow-d);
81
+
82
+ --shadow-md:
83
+ -2px -2px 4px 2px var(--color-shadow-l),
84
+ 2px 2px 4px 2px var(--color-shadow-d);
85
+
86
+ --shadow-md-in:
87
+ inset -4px -4px 4px var(--color-shadow-l),
88
+ inset 4px 4px 4px var(--color-shadow-d);
89
+
90
+ --shadow-lg:
91
+ -4px -4px 16px 8px var(--color-shadow-l),
92
+ 4px 4px 16px 8px var(--color-shadow-d);
93
+
94
+ --shadow-modal: 0px 4px 16px 8px var(--color-shadow-d);
100
95
  }
101
96
 
102
-
103
-
104
97
  html[data-theme="dark"] {
105
- --color-primary: #80d5d4;
106
- --color-on-primary: #003737;
107
- --color-primary-container: #004f4f;
108
- --color-on-primary-container: #9cf1f0;
109
-
110
- --color-secondary: #b0cccb;
111
- --color-on-secondary: #1b3534;
112
- --color-secondary-container: #324b4b;
113
- --color-on-secondary-container: #cce8e7;
114
-
115
- --color-error: #ffb4ab;
116
- --color-on-error: #690005;
117
- --color-error-container: #93000a;
118
- --color-on-error-container: #ffdad6;
119
-
120
- --color-background: #1E2927;
121
- --color-on-background: #dde4e3;
122
-
123
- --color-surface: #1E2927bf;
124
- --color-on-surface: #c3cbca;
125
- --color-on-surface-variant: #dde4e380;
126
- --color-surface-container: #262d2d;
127
-
128
- --color-border: #889392;
129
- --color-scrim: rgba(0, 0, 0, 0.25);
130
-
131
- --color-shadow-l: rgba(255, 255, 255, 0.25);
132
- --color-shadow-d: rgba(0, 0, 0, 1);
133
- }
98
+ --color-primary: #80d5d4;
99
+ --color-on-primary: #003737;
100
+ --color-primary-container: #004f4f;
101
+ --color-on-primary-container: #9cf1f0;
102
+
103
+ --color-secondary: #b0cccb;
104
+ --color-on-secondary: #1b3534;
105
+ --color-secondary-container: #324b4b;
106
+ --color-on-secondary-container: #cce8e7;
107
+
108
+ --color-error: #ffb4ab;
109
+ --color-on-error: #690005;
110
+ --color-error-container: #93000a;
111
+ --color-on-error-container: #ffdad6;
112
+
113
+ --color-background: #1e2927;
114
+ --color-on-background: #dde4e3;
115
+
116
+ --color-surface: #1e2927bf;
117
+ --color-on-surface: #c3cbca;
118
+ --color-on-surface-variant: #dde4e380;
119
+ --color-surface-container: #262d2d;
120
+
121
+ --color-border: #889392;
122
+ --color-scrim: rgba(0, 0, 0, 0.25);
123
+
124
+ --color-shadow-l: rgba(255, 255, 255, 0.25);
125
+ --color-shadow-d: rgba(0, 0, 0, 1);
126
+ }
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "lucentia-ui",
3
- "version": "0.2.24",
3
+ "version": "1.0.1",
4
4
  "description": "React UI design token and component system based on neumorphism, featuring two color themes: light and dark.",
5
+ "homepage": "https://lucentia.rikiyamatsuda.com/en",
5
6
  "main": "dist/index.js",
6
7
  "types": "dist/index.d.ts",
7
8
  "exports": {