@serendie/ui 3.1.1-dev.202603310916 → 3.1.1-dev.202603311053
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/dist/client.js +5 -5
- package/dist/components/Avatar/Avatar.js +5 -5
- package/dist/components/Badge/Badge.js +1 -1
- package/dist/components/Banner/Banner.js +4 -4
- package/dist/components/BottomNavigation/BottomNavigationItem.js +4 -4
- package/dist/components/Button/Button.js +6 -6
- package/dist/components/CheckBox/CheckBox.js +4 -4
- package/dist/components/ChoiceBox/ChoiceBox.js +1 -1
- package/dist/components/DashboardWidget/DashboardWidget.js +4 -4
- package/dist/components/DataTable/DataTableComponent.js +17 -16
- package/dist/components/DataTable/table/HeaderCell.js +4 -4
- package/dist/components/DataTable/table/Root.js +4 -4
- package/dist/components/DatePicker/DatePicker.js +2 -2
- package/dist/components/Divider/Divider.js +4 -4
- package/dist/components/Drawer/Drawer.js +1 -1
- package/dist/components/IconButton/IconButton.js +4 -4
- package/dist/components/List/ListItem.js +1 -1
- package/dist/components/ModalDialog/ModalDialog.js +10 -10
- package/dist/components/NotificationBadge/NotificationBadge.js +1 -1
- package/dist/components/Pagination/Pagination.js +6 -6
- package/dist/components/RadioButton/RadioButton.js +4 -4
- package/dist/components/Search/Search.js +12 -12
- package/dist/components/Select/Select.js +7 -7
- package/dist/components/Switch/Switch.js +11 -11
- package/dist/components/Tabs/TabItem.js +6 -6
- package/dist/components/Tabs/Tabs.js +5 -5
- package/dist/components/TextArea/TextArea.js +4 -4
- package/dist/components/TextField/TextField.js +1 -1
- package/dist/components/Toast/Toast.js +6 -6
- package/dist/components/Tooltip/Tooltip.js +12 -12
- package/dist/components/TopAppBar/TopAppBar.js +4 -4
- package/dist/index.js +5 -5
- package/dist/node_modules/@ark-ui/react/dist/components/accordion/use-accordion-item-context.js +3 -2
- package/dist/node_modules/@ark-ui/react/dist/components/combobox/use-combobox-item-context.js +3 -2
- package/dist/node_modules/@ark-ui/react/dist/components/combobox/use-combobox-item-group-props-context.js +3 -2
- package/dist/node_modules/@ark-ui/react/dist/components/menu/use-menu-item-context.js +3 -2
- package/dist/node_modules/@ark-ui/react/dist/components/menu/use-menu-item-group-context.js +3 -2
- package/dist/node_modules/@ark-ui/react/dist/components/menu/use-menu-option-item-props-context.js +3 -2
- package/dist/node_modules/@ark-ui/react/dist/components/menu/use-menu-trigger-item-context.js +3 -2
- package/dist/node_modules/@ark-ui/react/dist/components/select/use-select-item-context.js +3 -2
- package/dist/node_modules/@ark-ui/react/dist/components/select/use-select-item-props-context.js +3 -2
- package/dist/node_modules/@floating-ui/dom/dist/floating-ui.dom.js +1 -1
- package/dist/node_modules/@zag-js/accordion/dist/index.js +4 -4
- package/dist/node_modules/@zag-js/combobox/dist/index.js +1 -1
- package/dist/node_modules/@zag-js/date-picker/dist/index.js +13 -13
- package/dist/node_modules/@zag-js/menu/dist/index.js +11 -11
- package/dist/node_modules/@zag-js/react/dist/index.js +1 -1
- package/dist/node_modules/@zag-js/remove-scroll/dist/index.js +3 -3
- package/dist/node_modules/@zag-js/tabs/dist/index.js +4 -4
- package/dist/node_modules/@zag-js/toast/dist/index.js +4 -4
- package/dist/node_modules/@zag-js/tooltip/dist/index.js +3 -3
- package/package.json +13 -2
- package/skills/serendie-overview/SKILL.md +274 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createAnatomy as de } from "../../anatomy/dist/index.js";
|
|
2
2
|
import { mergeProps as ue, createMachine as ce, createGuards as he } from "../../core/dist/index.js";
|
|
3
|
-
import { dataAttr as C, isDownloadingEvent as J, isOpeningInNewTab as Q, isSelfTarget as pe, getEventTarget as B, isValidTabEvent as Oe, getEventKey as Z, isPrintableKey as Ee, isModifierKey as me, isEditableElement as re, isContextMenuEvent as Te, getEventPoint as b, ariaAttr as Ie,
|
|
3
|
+
import { dataAttr as C, isDownloadingEvent as J, isOpeningInNewTab as Q, isSelfTarget as pe, getEventTarget as B, isValidTabEvent as Oe, getEventKey as Z, isPrintableKey as Ee, isModifierKey as me, isEditableElement as re, isContextMenuEvent as Te, getEventPoint as b, ariaAttr as Ie, isAnchorElement as fe, isHTMLElement as Pe, raf as D, observeAttributes as ye, addDomEvent as Ce, getByTypeahead as oe, clickIfLink as Re, getWindow as ve, queryAll as Se, getInitialFocus as Ne, contains as U, scrollIntoView as ke } from "../../dom-query/dist/index.js";
|
|
4
4
|
import { getPlacementStyles as Le, getPlacementSide as Ae, getPlacement as z } from "../../popper/dist/index.js";
|
|
5
|
-
import { hasProp as W, cast as ee,
|
|
5
|
+
import { hasProp as W, cast as ee, last as Me, first as be, isEqual as De, prev as _e, next as He } from "../../utils/dist/index.js";
|
|
6
6
|
import { trackDismissableElement as we } from "../../dismissable/dist/index.js";
|
|
7
7
|
import { getElementPolygon as Ge, isPointInPolygon as Ve } from "../../rect-utils/dist/index.js";
|
|
8
8
|
import { createProps as k } from "../../types/dist/index.js";
|
|
@@ -45,16 +45,16 @@ var Fe = de("menu").parts(
|
|
|
45
45
|
}, I = (e) => e.getById(S(e)), ne = (e) => e.getById(se(e)), _ = (e) => e.getById(w(e)), le = (e, t) => t ? e.getById(N(e, t)) : null, x = (e) => e.getById(ae(e)), L = (e) => {
|
|
46
46
|
const n = `[role^="menuitem"][data-ownedby=${CSS.escape(S(e))}]:not([data-disabled])`;
|
|
47
47
|
return Se(I(e), n);
|
|
48
|
-
}, xe = (e) =>
|
|
48
|
+
}, xe = (e) => be(L(e)), Be = (e) => Me(L(e)), $ = (e, t) => t ? e.id === t || e.dataset.value === t : !1, Ke = (e, t) => {
|
|
49
49
|
const n = L(e), i = n.findIndex((s) => $(s, t.value));
|
|
50
|
-
return
|
|
50
|
+
return He(n, i, { loop: t.loop ?? t.loopFocus });
|
|
51
51
|
}, $e = (e, t) => {
|
|
52
52
|
const n = L(e), i = n.findIndex((s) => $(s, t.value));
|
|
53
|
-
return
|
|
53
|
+
return _e(n, i, { loop: t.loop ?? t.loopFocus });
|
|
54
54
|
}, Xe = (e, t) => {
|
|
55
55
|
const n = L(e), i = n.find((s) => $(s, t.value));
|
|
56
56
|
return oe(n, { state: t.typeaheadState, key: t.key, activeId: (i == null ? void 0 : i.id) ?? null });
|
|
57
|
-
}, H = (e) =>
|
|
57
|
+
}, H = (e) => Pe(e) && (e.dataset.disabled === "" || e.hasAttribute("disabled")), Ye = (e) => {
|
|
58
58
|
var t;
|
|
59
59
|
return !!((t = e == null ? void 0 : e.getAttribute("role")) != null && t.startsWith("menuitem")) && !!(e != null && e.hasAttribute("aria-controls"));
|
|
60
60
|
}, K = "menu:select";
|
|
@@ -315,7 +315,7 @@ function at(e, t) {
|
|
|
315
315
|
},
|
|
316
316
|
Enter() {
|
|
317
317
|
var h;
|
|
318
|
-
i({ type: "ENTER" }), v != null &&
|
|
318
|
+
i({ type: "ENTER" }), v != null && fe(E) && ((h = o("navigate")) == null || h({ value: v, node: E, href: E.href }));
|
|
319
319
|
},
|
|
320
320
|
Space(h) {
|
|
321
321
|
var M;
|
|
@@ -414,7 +414,7 @@ var { not: m, and: R, or: je } = he(), st = ce({
|
|
|
414
414
|
composite: !0,
|
|
415
415
|
loopFocus: !1,
|
|
416
416
|
navigate(t) {
|
|
417
|
-
|
|
417
|
+
Re(t.node);
|
|
418
418
|
},
|
|
419
419
|
...e,
|
|
420
420
|
positioning: {
|
|
@@ -981,7 +981,7 @@ var { not: m, and: R, or: je } = he(), st = ce({
|
|
|
981
981
|
g.context.set("suspendPointer", !0);
|
|
982
982
|
});
|
|
983
983
|
const o = t.getDoc();
|
|
984
|
-
return
|
|
984
|
+
return Ce(o, "pointermove", (l) => {
|
|
985
985
|
Je(e.get("intentPolygon"), {
|
|
986
986
|
x: l.clientX,
|
|
987
987
|
y: l.clientY
|
|
@@ -994,7 +994,7 @@ var { not: m, and: R, or: je } = he(), st = ce({
|
|
|
994
994
|
const g = t.getById(n("highlightedId")), o = I(t);
|
|
995
995
|
ke(g, { rootEl: o, block: "nearest" });
|
|
996
996
|
};
|
|
997
|
-
return D(() => i()),
|
|
997
|
+
return D(() => i()), ye(() => I(t), {
|
|
998
998
|
defer: !0,
|
|
999
999
|
attributes: ["aria-activedescendant"],
|
|
1000
1000
|
callback: i
|
|
@@ -1003,7 +1003,7 @@ var { not: m, and: R, or: je } = he(), st = ce({
|
|
|
1003
1003
|
},
|
|
1004
1004
|
actions: {
|
|
1005
1005
|
setAnchorPoint({ context: e, event: t }) {
|
|
1006
|
-
e.set("anchorPoint", (n) =>
|
|
1006
|
+
e.set("anchorPoint", (n) => De(n, t.point) ? n : t.point);
|
|
1007
1007
|
},
|
|
1008
1008
|
setSubmenuPlacement({ context: e, computed: t, refs: n }) {
|
|
1009
1009
|
if (!e.get("isSubmenu")) return;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createScope as Y, MachineStatus as V, INIT_STATE as L } from "../../core/dist/index.js";
|
|
2
2
|
import { mergeProps as bt } from "../../core/dist/index.js";
|
|
3
3
|
import { compact as Z, ensure as h, identity as tt, isFunction as j, warn as H, toArray as et, isString as rt } from "../../utils/dist/index.js";
|
|
4
|
-
import { useMemo as nt, useRef as d,
|
|
4
|
+
import { useMemo as nt, useRef as d, useLayoutEffect as ut, useEffect as w, useState as K } from "react";
|
|
5
5
|
import { flushSync as O } from "react-dom";
|
|
6
6
|
import { createNormalizer as ot } from "../../types/dist/index.js";
|
|
7
7
|
import "react/jsx-runtime";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { setStyleProperty as x, setStyle as p, isIos as L } from "../../dom-query/dist/index.js";
|
|
2
2
|
var i = "data-scroll-lock";
|
|
3
3
|
function m(r) {
|
|
4
4
|
const n = r.getBoundingClientRect().left;
|
|
@@ -9,7 +9,7 @@ function $(r) {
|
|
|
9
9
|
if (e.hasAttribute(i)) return;
|
|
10
10
|
const l = c.innerWidth - s.clientWidth;
|
|
11
11
|
e.setAttribute(i, "");
|
|
12
|
-
const h = () =>
|
|
12
|
+
const h = () => x(s, "--scrollbar-width", `${l}px`), f = m(s), u = () => p(e, {
|
|
13
13
|
overflow: "hidden",
|
|
14
14
|
[f]: `${l}px`
|
|
15
15
|
}), y = () => {
|
|
@@ -24,7 +24,7 @@ function $(r) {
|
|
|
24
24
|
return () => {
|
|
25
25
|
d == null || d(), c.scrollTo({ left: t, top: a, behavior: "instant" });
|
|
26
26
|
};
|
|
27
|
-
}, b = [h(),
|
|
27
|
+
}, b = [h(), L() ? y() : u()];
|
|
28
28
|
return () => {
|
|
29
29
|
b.forEach((t) => t == null ? void 0 : t()), e.removeAttribute(i);
|
|
30
30
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createAnatomy as L } from "../../anatomy/dist/index.js";
|
|
2
|
-
import { dataAttr as d, isOpeningInNewTab as N, isSafari as P, isSelfTarget as S, isComposingEvent as U, getEventKey as $, isAnchorElement as D, trackElementRect as k, nextTick as W, raf as v, getFocusables as x,
|
|
2
|
+
import { dataAttr as d, isOpeningInNewTab as N, isSafari as P, isSelfTarget as S, isComposingEvent as U, getEventKey as $, isAnchorElement as D, trackElementRect as k, nextTick as W, raf as v, getFocusables as x, prevById as M, nextById as X, clickIfLink as H, itemById as K, queryAll as Y } from "../../dom-query/dist/index.js";
|
|
3
3
|
import { last as q, first as j } from "../../utils/dist/index.js";
|
|
4
4
|
import { setup as G } from "../../core/dist/index.js";
|
|
5
5
|
import { createProps as F } from "../../types/dist/index.js";
|
|
@@ -21,13 +21,13 @@ var J = L("tabs").parts("root", "list", "trigger", "content", "indicator"), b =
|
|
|
21
21
|
}, Z = (e) => e.getById(y(e)), z = (e, t) => e.getById(E(e, t)), h = (e, t) => e.getById(f(e, t)), _ = (e) => e.getById(w(e)), V = (e) => {
|
|
22
22
|
const o = `[role=tab][data-ownedby='${CSS.escape(y(e))}']:not([disabled])`;
|
|
23
23
|
return Y(Z(e), o);
|
|
24
|
-
}, ee = (e) => j(V(e)), te = (e) => q(V(e)), ae = (e, t) =>
|
|
24
|
+
}, ee = (e) => j(V(e)), te = (e) => q(V(e)), ae = (e, t) => X(V(e), f(e, t.value), t.loopFocus), oe = (e, t) => M(V(e), f(e, t.value), t.loopFocus), O = (e) => ({
|
|
25
25
|
left: (e == null ? void 0 : e.offsetLeft) ?? 0,
|
|
26
26
|
top: (e == null ? void 0 : e.offsetTop) ?? 0,
|
|
27
27
|
width: (e == null ? void 0 : e.offsetWidth) ?? 0,
|
|
28
28
|
height: (e == null ? void 0 : e.offsetHeight) ?? 0
|
|
29
29
|
}), re = (e, t) => {
|
|
30
|
-
const o =
|
|
30
|
+
const o = K(V(e), f(e, t));
|
|
31
31
|
return B(O(o));
|
|
32
32
|
}, B = (e) => ({
|
|
33
33
|
width: `${e.width}px`,
|
|
@@ -205,7 +205,7 @@ var { createMachine: ie } = G(), fe = ie({
|
|
|
205
205
|
loopFocus: !0,
|
|
206
206
|
composite: !0,
|
|
207
207
|
navigate(t) {
|
|
208
|
-
|
|
208
|
+
H(t.node);
|
|
209
209
|
},
|
|
210
210
|
defaultValue: null,
|
|
211
211
|
...e
|
|
@@ -2,7 +2,7 @@ import { contains as q, MAX_Z_INDEX as X, raf as N, dataAttr as D, addDomEvent a
|
|
|
2
2
|
import { createAnatomy as Q } from "../../anatomy/dist/index.js";
|
|
3
3
|
import { createMachine as K, createGuards as Y } from "../../core/dist/index.js";
|
|
4
4
|
import { trackDismissableBranch as Z } from "../../dismissable/dist/index.js";
|
|
5
|
-
import {
|
|
5
|
+
import { compact as J, warn as z, runIfFn as C, uuid as _, setRafTimeout as w, ensureProps as tt } from "../../utils/dist/index.js";
|
|
6
6
|
var et = Q("toast").parts(
|
|
7
7
|
"group",
|
|
8
8
|
"root",
|
|
@@ -460,7 +460,7 @@ function Ot(t, e) {
|
|
|
460
460
|
}
|
|
461
461
|
var { not: dt } = Y(), Rt = K({
|
|
462
462
|
props({ props: t }) {
|
|
463
|
-
return
|
|
463
|
+
return tt(t, ["id", "type", "parent", "removeDelay"], "toast"), {
|
|
464
464
|
closable: !0,
|
|
465
465
|
...t,
|
|
466
466
|
duration: A(t.duration, t.type)
|
|
@@ -679,7 +679,7 @@ function B(t, e) {
|
|
|
679
679
|
const { id: i, height: s } = e;
|
|
680
680
|
t.context.set("heights", (r) => r.find((c) => c.id === i) ? r.map((c) => c.id === i ? { ...c, height: s } : c) : [{ id: i, height: s }, ...r]);
|
|
681
681
|
}
|
|
682
|
-
var gt = (t, e) => ({ ...e, ...
|
|
682
|
+
var gt = (t, e) => ({ ...e, ...J(t) });
|
|
683
683
|
function kt(t) {
|
|
684
684
|
const e = gt(t, {
|
|
685
685
|
placement: "bottom",
|
|
@@ -738,7 +738,7 @@ function kt(t) {
|
|
|
738
738
|
getCount: () => s.length,
|
|
739
739
|
promise: (n, o, I = {}) => {
|
|
740
740
|
if (!o || !o.loading) {
|
|
741
|
-
|
|
741
|
+
z("[zag-js > toast] toaster.promise() requires at least a 'loading' option to be specified");
|
|
742
742
|
return;
|
|
743
743
|
}
|
|
744
744
|
const v = u({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createAnatomy as D } from "../../anatomy/dist/index.js";
|
|
2
|
-
import { addDomEvent as u, getOverflowAncestors as M,
|
|
2
|
+
import { addDomEvent as u, getOverflowAncestors as M, dataAttr as E, isLeftClick as I, isComposingEvent as V } from "../../dom-query/dist/index.js";
|
|
3
3
|
import { trackFocusVisible as S, isFocusVisible as T } from "../../focus-visible/dist/index.js";
|
|
4
4
|
import { getPlacement as k, getPlacementStyles as F } from "../../popper/dist/index.js";
|
|
5
5
|
import { createStore as A } from "../../utils/dist/index.js";
|
|
@@ -36,7 +36,7 @@ function W(e, t) {
|
|
|
36
36
|
...d.trigger.attrs,
|
|
37
37
|
id: h,
|
|
38
38
|
dir: n("dir"),
|
|
39
|
-
"data-expanded":
|
|
39
|
+
"data-expanded": E(p),
|
|
40
40
|
"data-state": p ? "open" : "closed",
|
|
41
41
|
"aria-describedby": p ? y : void 0,
|
|
42
42
|
onClick(i) {
|
|
@@ -51,7 +51,7 @@ function W(e, t) {
|
|
|
51
51
|
i.defaultPrevented || c || f === l.get("id") && r({ type: "close", src: "trigger.blur" });
|
|
52
52
|
},
|
|
53
53
|
onPointerDown(i) {
|
|
54
|
-
i.defaultPrevented || c ||
|
|
54
|
+
i.defaultPrevented || c || I(i) && n("closeOnPointerDown") && f === l.get("id") && r({ type: "close", src: "trigger.pointerdown" });
|
|
55
55
|
},
|
|
56
56
|
onPointerMove(i) {
|
|
57
57
|
i.defaultPrevented || c || i.pointerType !== "touch" && r({ type: "pointer.move" });
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@serendie/ui",
|
|
3
3
|
"description": "Adaptive UI component library as part of Serendie Design System by Mitsubishi Electric",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "3.1.1-dev.
|
|
5
|
+
"version": "3.1.1-dev.202603311053",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"sideEffects": [
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"@pandacss/dev": "^0.53.0",
|
|
56
56
|
"@pandacss/eslint-plugin": "^0.1.1",
|
|
57
57
|
"@serendie/design-token": "^1.4.2",
|
|
58
|
+
"@tanstack/intent": "^0.0.27",
|
|
58
59
|
"@storybook/addon-designs": "^8.0.3",
|
|
59
60
|
"@storybook/addon-essentials": "^8.2.4",
|
|
60
61
|
"@storybook/addon-interactions": "^8.2.4",
|
|
@@ -136,9 +137,19 @@
|
|
|
136
137
|
},
|
|
137
138
|
"./styles.css": "./dist/styles.css"
|
|
138
139
|
},
|
|
140
|
+
"keywords": [
|
|
141
|
+
"tanstack-intent"
|
|
142
|
+
],
|
|
143
|
+
"intent": {
|
|
144
|
+
"version": 1,
|
|
145
|
+
"repo": "serendie/serendie",
|
|
146
|
+
"docs": "https://serendie.design"
|
|
147
|
+
},
|
|
139
148
|
"files": [
|
|
140
149
|
"dist",
|
|
141
|
-
"styled-system"
|
|
150
|
+
"styled-system",
|
|
151
|
+
"skills",
|
|
152
|
+
"!skills/_artifacts"
|
|
142
153
|
],
|
|
143
154
|
"lint-staged": {
|
|
144
155
|
"*.{ts,tsx}": [
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: serendie-overview
|
|
3
|
+
description: Serendie Design System(@serendie/ui, @serendie/symbols, @serendie/design-token)の概要・セットアップ手順・デザイントークンの使い方を提供し、詳細情報を Serendie MCP から得られるように案内する。Serendieを使った実装、コンポーネントやアイコンの使い方、デザイントークンの選び方、PandaCSS連携、テーマ切り替え、SerendieProviderの設定など、Serendieに少しでも関連する質問でトリガーすること。@serendie/ui のインポートがコード内に存在する場合も必ずトリガーすること。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Serendieユーザーガイド
|
|
7
|
+
|
|
8
|
+
## このスキルの対象
|
|
9
|
+
|
|
10
|
+
**Serendieが提供するライブラリの利用者**(Serendie Design System に準拠したアプリケーションを構築する開発者)を対象としている。ライブラリ自体の開発やデザインシステムの運用に関する情報は含まない。
|
|
11
|
+
|
|
12
|
+
## Serendieライブラリとは
|
|
13
|
+
|
|
14
|
+
三菱電機のSerendie Design Systemが提供するWebフロントエンド向けのライブラリ群。ReactベースのUIライブラリ `@serendie/ui` など、複数の関連パッケージから構成される。
|
|
15
|
+
|
|
16
|
+
### 関連パッケージ
|
|
17
|
+
|
|
18
|
+
Serendieが提供・メンテナンスしているライブラリは下記の通り。
|
|
19
|
+
|
|
20
|
+
| パッケージ | 役割 |
|
|
21
|
+
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
22
|
+
| `@serendie/ui` | Serendie UIと呼ばれる。UIコンポーネントを提供する中核ライブラリ。Figma上のデザインライブラリ (Serendie UI Kit) と対応している。 |
|
|
23
|
+
| `@serendie/design-token` | デザイントークンを提供する。 Panda CSS用トークンの他に、CSS Variables形式などでも提供されており、Serendie UIとは独立して使用も可能。React外の環境でデザイントークンのみ利用する場合を想定。 |
|
|
24
|
+
| `@serendie/symbols` | Serendie Symbolsと呼ばれる。300種類以上のアイコン(React環境前提)を提供。Serendie UIに同梱されるが、独立して使用も可能 |
|
|
25
|
+
|
|
26
|
+
### 依存パッケージ
|
|
27
|
+
|
|
28
|
+
Serendie UIは、Ark UI(ヘッドレスUIライブラリ)およびPanda CSS(スタイリングライブラリ)に基づき開発されている。特に各コンポーネントをユーザー環境に合わせたカスタマイズをする際に、下記のAPI Docsを参照すること。
|
|
29
|
+
|
|
30
|
+
| パッケージ | ユーザーへの影響 | API Docs |
|
|
31
|
+
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
|
|
32
|
+
| `@ark-ui/react` | 同梱されるためユーザーがプロジェクトに導入する必要は無し。Serendie UIの各コンポーネントはArk UIを継承するため、適宜Ark UIのドキュメントを参照すること。 | https://ark-ui.com/llms.txt |
|
|
33
|
+
| `@pandacss/dev` | ユーザーのプロジェクト導入は任意だが、スタイリング時にSerendie UIとの親和性は高い。 | https://panda-css.com/llms.txt |
|
|
34
|
+
|
|
35
|
+
## Serendie MCP
|
|
36
|
+
|
|
37
|
+
リモートMCPサーバーであるSerendie MCP (https://serendie.design/mcp) が提供されている。詳細な情報は Serendie MCPの各ツールから取得すること。
|
|
38
|
+
|
|
39
|
+
| ツール | 用途 |
|
|
40
|
+
| --------------------------- | ---------------------------------------------------------------- |
|
|
41
|
+
| `get-components` | コンポーネント一覧の取得 |
|
|
42
|
+
| `get-component-detail` | 特定コンポーネントの Props・使用例の取得 |
|
|
43
|
+
| `get-design-tokens` | デザイントークン一覧の取得 |
|
|
44
|
+
| `get-design-token-detail` | 特定デザイントークンの値・使用例の取得 |
|
|
45
|
+
| `get-symbols` | アイコン一覧の取得 |
|
|
46
|
+
| `get-symbol-detail` | 特定アイコンの詳細・使用例の取得 |
|
|
47
|
+
| `search-serendie-guideline` | 設計指針やアセットの使い方などガイドラインの検索 |
|
|
48
|
+
|
|
49
|
+
なお、同等の情報をドキュメントサイトおよびStorybookとしても提供している。
|
|
50
|
+
|
|
51
|
+
- ドキュメント: https://serendie.design/
|
|
52
|
+
- Storybook: https://storybook.serendie.design/
|
|
53
|
+
|
|
54
|
+
## 基本セットアップ
|
|
55
|
+
|
|
56
|
+
### 1.インストール
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npm install @serendie/ui
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 2.[重要] CSSの設定
|
|
63
|
+
|
|
64
|
+
CSSのルートに以下を追加すること。**この設定が抜けると正しくCSSが適用されない。`@layer` の宣言順序がスタイルの優先度を決定するため、順序を変えるとスタイルが壊れたり意図しない上書きが発生する。**
|
|
65
|
+
|
|
66
|
+
```css
|
|
67
|
+
@layer reset, base, tokens, recipes, utilities;
|
|
68
|
+
@import "@serendie/ui/styles.css";
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
なお、**Reset CSS は @serendie/ui に同梱済みのため、別途追加してはいけない。**
|
|
72
|
+
|
|
73
|
+
### 3.インポートパス
|
|
74
|
+
|
|
75
|
+
ユーザーの環境に合わせて、必要なコンポーネントをインポートして利用する。
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
// Serendie UIのインポート (通常)
|
|
79
|
+
import { TextField, Button, Select } from "@serendie/ui";
|
|
80
|
+
|
|
81
|
+
// use client 適用済みのSerendie UI(Next.js環境)
|
|
82
|
+
import { TextField, Button } from "@serendie/ui/client";
|
|
83
|
+
|
|
84
|
+
// PandaCSS スタイルユーティリティ(PandaCSS導入時)
|
|
85
|
+
import { css } from "@serendie/ui/css";
|
|
86
|
+
|
|
87
|
+
// PandaCSS レイアウトコンポーネント(PandaCSS導入時)
|
|
88
|
+
import { Box, Center, Flex, Stack, VStack } from "@serendie/ui/jsx";
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Serendie Symbols (アイコン) を使う
|
|
92
|
+
|
|
93
|
+
`@serendie/ui` の依存パッケージとしてインストールされるため、追加インストールは不要。`@serendie/ui` を使わずアイコンのみ利用する場合は `npm install @serendie/symbols` で個別導入する。
|
|
94
|
+
|
|
95
|
+
使用例:
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
import {
|
|
99
|
+
SerendieSymbolHome, // homeアイコン (outline)
|
|
100
|
+
SerendieSymbolSettingsFilled, // 設定アイコン (filled)
|
|
101
|
+
} from "@serendie/symbols";
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
利用可能なアイコンはSerendie MCPの `get-symbols` / `get-symbol-detail` で確認すること。
|
|
105
|
+
|
|
106
|
+
## PandaCSS の導入(推奨)
|
|
107
|
+
|
|
108
|
+
ユーザープロジェクトに、PandaCSS を導入すると、デザイントークンを JSX 内で直接利用できるなど、よりシームレスにSerendie UIを利用できる。
|
|
109
|
+
`panda init` は親ディレクトリに既存の panda.config.ts がある場合、生成をスキップすることがある。その場合は手動で panda.config.ts を作成すること。
|
|
110
|
+
|
|
111
|
+
インストール:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npm install -D @pandacss/dev
|
|
115
|
+
npx panda init --postcss
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
package.json に `"prepare": "panda codegen"` を追加し、panda.config.ts で **`SerendiePreset` を presets に指定する**。これによりデザイントークンやレシピがPandaCSSから利用可能になる。
|
|
119
|
+
その他のPandaCSS設定は公式ドキュメント (https://panda-css.com/llms.txt) を参照のこと。
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import { SerendiePreset } from "@serendie/ui";
|
|
123
|
+
|
|
124
|
+
export default defineConfig({
|
|
125
|
+
presets: [SerendiePreset],
|
|
126
|
+
jsxFramework: "react",
|
|
127
|
+
include: ["./src/**/*.{js,jsx,ts,tsx}"],
|
|
128
|
+
// その他はPandaCSSのドキュメントに従って設定
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
この設定により、下記のようにデザイントークン名をコード内で扱うことができる。なお、このデザイントークン名は、Figmaのデザインライブラリ (Serendie UI Kit) のデザイントークン名 (Figma Variables) と一致する。
|
|
133
|
+
|
|
134
|
+
```jsx
|
|
135
|
+
<Box my="sd.system.dimension.spacing.sixExtraLarge">
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
また、css()メソッドを利用して同様のことができる。
|
|
139
|
+
|
|
140
|
+
```jsx
|
|
141
|
+
import { css } from "@serendie/ui/css";
|
|
142
|
+
// ...snip...
|
|
143
|
+
<div className={css({ my: "sd.system.dimension.spacing.sixExtraLarge" })}>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## コンポーネントの概要
|
|
147
|
+
|
|
148
|
+
コンポーネントは以下のカテゴリに分類される。一覧・詳細は Serendie MCP の `get-components` / `get-component-detail` で取得すること。
|
|
149
|
+
|
|
150
|
+
- **Actions**: Button, IconButton, BottomNavigation など
|
|
151
|
+
- **Inputs**: TextField, PasswordField, Select, Switch など
|
|
152
|
+
- **Layout**: Accordion, Tabs, Divider など
|
|
153
|
+
- **Display**: Avatar, Badge, ProgressIndicator など
|
|
154
|
+
- **Feedback**: Toast, ModalDialog, Pagination など
|
|
155
|
+
- **Other**: その他
|
|
156
|
+
|
|
157
|
+
バリアント Props 名はコンポーネントごとに異なる(例: Button は `styleType`、Badge は `styleColor`)。 **他のUIライブラリでみられる `variant` ではないので、必ず `get-component-detail` または TypeScript の型定義で確認すること。**
|
|
158
|
+
|
|
159
|
+
## デザイントークンの概要
|
|
160
|
+
|
|
161
|
+
- [重要] デザイントークンは、Serendie UIを扱う際のデザインにおける基本単位。**ユーザーから指定が無い限り、px値やHEXカラーなど直値の指定は禁止であり、デザイントークンを原則使うこと。**
|
|
162
|
+
- 利用可能なデザイントークンは Serendie MCP の `get-design-tokens` / `get-design-token-detail` で確認すること
|
|
163
|
+
|
|
164
|
+
### デザイントークンの基礎
|
|
165
|
+
|
|
166
|
+
より詳細はSerendie MCPの `search-serendie-guideline`でキーワード検索して確認すること。
|
|
167
|
+
|
|
168
|
+
- デザイントークンには「システムトークン(`sd.system.*`)」と「リファレンストークン(`sd.reference.*`)」の2層から構成される。システムトークンはリファレンストークンを参照する。
|
|
169
|
+
- 通常ユーザープロジェクトでは、**システムトークンを最優先で使用する。** リファレンストークンを直接扱うのは理由が無い限り避けるのがベストプラクティス。
|
|
170
|
+
- デザイントークンは、タイプとロールという概念を持ち、デザイントークン文字列の内部で表現される。
|
|
171
|
+
- タイプ: デザイントークンのカテゴリ。カラー、書体、寸法など、適用範囲が分かる。
|
|
172
|
+
- Color, Typography, Dimension, Elevationなど。`sd.system.dimension` のように3階層目で表現される。
|
|
173
|
+
- ロール: タイプをさらに細分化したもの。システムトークンのみ持つ情報であり、デザイントークンの適用箇所を表す。
|
|
174
|
+
- `sd.system.dimension.spacing`のように4階層目で表現される
|
|
175
|
+
- Colorロール: impression(ブランドカラー), component(UI構造色), interaction(状態変化色: hovered, disabled等。装飾目的で流用しないこと)など
|
|
176
|
+
- Typographyロール: title, headlineなど
|
|
177
|
+
- Dimensionロール: spacing, radiusなど
|
|
178
|
+
- デザイントークンの末尾につくsuffix (`expanded`, `compact`) は、デバイス環境を示す。`expanded`はPC/Laptop環境、`compact`はスマートフォン環境に対応している。レスポンシブデザインの場合は、breakpointごとに使い分けるなど、ユーザーのプロジェクトに合わせて使い分けること。
|
|
179
|
+
|
|
180
|
+
### よくある誤りと正しい例
|
|
181
|
+
|
|
182
|
+
**NG: px値やHEXカラーを直接指定している**
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
css({
|
|
186
|
+
padding: "16px",
|
|
187
|
+
margin: 8,
|
|
188
|
+
color: "#333",
|
|
189
|
+
fontSize: "16px",
|
|
190
|
+
borderRadius: "8px",
|
|
191
|
+
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**OK: デザイントークンを使用している**
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
css({
|
|
199
|
+
p: "sd.system.dimension.spacing.medium",
|
|
200
|
+
m: "sd.system.dimension.spacing.small",
|
|
201
|
+
color: "sd.system.color.component.onSurface",
|
|
202
|
+
textStyle: "sd.system.typography.headline.small_expanded", // Panda CSSのText Styleを利用 (後述)
|
|
203
|
+
borderRadius: "sd.system.dimension.radius.medium",
|
|
204
|
+
boxShadow: "sd.system.elevation.shadow.level2",
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`textStyle` は PandaCSS の [Text Styles](https://panda-css.com/docs/theming/text-styles) 機能であり、fontSize / fontWeight / lineHeight 等を個別に指定するのではなく `textStyle` で一括指定する。必須ではないが、記述が簡潔になるため、PandaCSSを利用する場合は利用が推奨される。
|
|
209
|
+
|
|
210
|
+
**NG: リファレンストークンやセマンティクスの合わないトークンを使っている**
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
css({
|
|
214
|
+
color: "sd.reference.color.scale.gray.500", // リファレンストークンを直接使用
|
|
215
|
+
borderColor: "sd.system.color.component.onSurface", // テキスト用トークンをボーダーに (セマンティクスの不一致)
|
|
216
|
+
bg: "sd.system.color.interaction.disabled", // interactionロールは状態変化専用。「グレーの背景が欲しいから」と装飾目的で流用してはいけない
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**OK: 用途に合ったシステムトークンを使っている**
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
css({
|
|
224
|
+
color: "sd.system.color.component.onSurface", // テキスト色にはonSurface
|
|
225
|
+
borderColor: "sd.system.color.component.outline", // ボーダーにはoutline
|
|
226
|
+
bg: "sd.system.color.component.surface", // 背景にはsurface
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### [重要] 適切なデザイントークンが見つからない場合
|
|
231
|
+
|
|
232
|
+
- タイプおよびロールは、セマンティクスに従って適切に使うことが最も重要であり、**見た目を重視したデザイントークンの誤用は禁止**
|
|
233
|
+
- デザイントークンの用途やセマンティクスが不明なときは、Serendie MCPを活用するか、**Serendie UIコンポーネントの既存実装での使い方を調べること。**
|
|
234
|
+
- 適切なトークンが見つからない場合は、無理に誤用するのではなく、**デザイントークンをユーザープロジェクト内で新規定義することを検討する。** 例えば、PandaCSSの[Theming機能](https://panda-css.com/llms.txt/theming)を利用して、ユーザープロジェクト内の独自トークンを定義することができる。誤用よりも分離して定義する方が、将来的にSerendie UI側で適切なトークンが追加された際の移行も容易になる。
|
|
235
|
+
|
|
236
|
+
## 発展
|
|
237
|
+
|
|
238
|
+
### カラーテーマ
|
|
239
|
+
|
|
240
|
+
- Serendie UIは、組み込みで5つのカラーテーマ (konjo, asagi, kurikawa, sumire, tsutsuji) を持ち、デフォルトはkonjo
|
|
241
|
+
- htmlタグなどに、data-panda-theme属性を付与することで、CSS 環境であってもテーマを切り替えることができる
|
|
242
|
+
|
|
243
|
+
```html
|
|
244
|
+
<html data-panda-theme="asagi"></html>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### カラーモードおよび多言語対応
|
|
248
|
+
|
|
249
|
+
- Serendie UIは、前述のカラーテーマのほか、カラーモード (端末に応じたライト/ダークモード)や、言語切替 (日英) もサポート
|
|
250
|
+
- カラーモードと言語切替を利用する場合は、 `SerendieProvider` をアプリケーションのルートで利用すること。カラーテーマも `SerendieProvider` から指定可能
|
|
251
|
+
- SSR 環境(Next.js等)では `ColorSchemeScript` を `<head>` に配置して FOUC(テーマのちらつき)を防止する
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
import { SerendieProvider } from "@serendie/ui";
|
|
255
|
+
|
|
256
|
+
<SerendieProvider lang="ja" colorTheme="konjo" colorMode="system">
|
|
257
|
+
{/* アプリケーション全体 */}
|
|
258
|
+
</SerendieProvider>;
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
コンポーネント内で現在のテーマ情報を取得したい場合は `useThemeContext()` フックが利用可能。また、現時点ではダークモードは `konjo-dark` テーマのみ実装されており、他のカラーテーマではダークモード指定時に `konjo-dark` へフォールバックする。
|
|
262
|
+
|
|
263
|
+
詳細は`@serendie/ui`のREADME の「テーマ切り替え」「多言語対応」セクションを参照。
|
|
264
|
+
|
|
265
|
+
### CSS Variablesを利用する
|
|
266
|
+
|
|
267
|
+
プロジェクトの制約によりPandaCSSを利用できないが、Serendieのデザイントークンを適用したい場合は、CSS Variablesが利用可能。
|
|
268
|
+
|
|
269
|
+
```css
|
|
270
|
+
.my-class {
|
|
271
|
+
color: var(--sd-system-color-impression-primary);
|
|
272
|
+
margin: var(--sd-system-dimension-spacing-sixExtraLarge);
|
|
273
|
+
}
|
|
274
|
+
```
|