@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.
Files changed (53) hide show
  1. package/dist/client.js +5 -5
  2. package/dist/components/Avatar/Avatar.js +5 -5
  3. package/dist/components/Badge/Badge.js +1 -1
  4. package/dist/components/Banner/Banner.js +4 -4
  5. package/dist/components/BottomNavigation/BottomNavigationItem.js +4 -4
  6. package/dist/components/Button/Button.js +6 -6
  7. package/dist/components/CheckBox/CheckBox.js +4 -4
  8. package/dist/components/ChoiceBox/ChoiceBox.js +1 -1
  9. package/dist/components/DashboardWidget/DashboardWidget.js +4 -4
  10. package/dist/components/DataTable/DataTableComponent.js +17 -16
  11. package/dist/components/DataTable/table/HeaderCell.js +4 -4
  12. package/dist/components/DataTable/table/Root.js +4 -4
  13. package/dist/components/DatePicker/DatePicker.js +2 -2
  14. package/dist/components/Divider/Divider.js +4 -4
  15. package/dist/components/Drawer/Drawer.js +1 -1
  16. package/dist/components/IconButton/IconButton.js +4 -4
  17. package/dist/components/List/ListItem.js +1 -1
  18. package/dist/components/ModalDialog/ModalDialog.js +10 -10
  19. package/dist/components/NotificationBadge/NotificationBadge.js +1 -1
  20. package/dist/components/Pagination/Pagination.js +6 -6
  21. package/dist/components/RadioButton/RadioButton.js +4 -4
  22. package/dist/components/Search/Search.js +12 -12
  23. package/dist/components/Select/Select.js +7 -7
  24. package/dist/components/Switch/Switch.js +11 -11
  25. package/dist/components/Tabs/TabItem.js +6 -6
  26. package/dist/components/Tabs/Tabs.js +5 -5
  27. package/dist/components/TextArea/TextArea.js +4 -4
  28. package/dist/components/TextField/TextField.js +1 -1
  29. package/dist/components/Toast/Toast.js +6 -6
  30. package/dist/components/Tooltip/Tooltip.js +12 -12
  31. package/dist/components/TopAppBar/TopAppBar.js +4 -4
  32. package/dist/index.js +5 -5
  33. package/dist/node_modules/@ark-ui/react/dist/components/accordion/use-accordion-item-context.js +3 -2
  34. package/dist/node_modules/@ark-ui/react/dist/components/combobox/use-combobox-item-context.js +3 -2
  35. package/dist/node_modules/@ark-ui/react/dist/components/combobox/use-combobox-item-group-props-context.js +3 -2
  36. package/dist/node_modules/@ark-ui/react/dist/components/menu/use-menu-item-context.js +3 -2
  37. package/dist/node_modules/@ark-ui/react/dist/components/menu/use-menu-item-group-context.js +3 -2
  38. package/dist/node_modules/@ark-ui/react/dist/components/menu/use-menu-option-item-props-context.js +3 -2
  39. package/dist/node_modules/@ark-ui/react/dist/components/menu/use-menu-trigger-item-context.js +3 -2
  40. package/dist/node_modules/@ark-ui/react/dist/components/select/use-select-item-context.js +3 -2
  41. package/dist/node_modules/@ark-ui/react/dist/components/select/use-select-item-props-context.js +3 -2
  42. package/dist/node_modules/@floating-ui/dom/dist/floating-ui.dom.js +1 -1
  43. package/dist/node_modules/@zag-js/accordion/dist/index.js +4 -4
  44. package/dist/node_modules/@zag-js/combobox/dist/index.js +1 -1
  45. package/dist/node_modules/@zag-js/date-picker/dist/index.js +13 -13
  46. package/dist/node_modules/@zag-js/menu/dist/index.js +11 -11
  47. package/dist/node_modules/@zag-js/react/dist/index.js +1 -1
  48. package/dist/node_modules/@zag-js/remove-scroll/dist/index.js +3 -3
  49. package/dist/node_modules/@zag-js/tabs/dist/index.js +4 -4
  50. package/dist/node_modules/@zag-js/toast/dist/index.js +4 -4
  51. package/dist/node_modules/@zag-js/tooltip/dist/index.js +3 -3
  52. package/package.json +13 -2
  53. 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, isHTMLElement as fe, raf as D, observeAttributes as Pe, addDomEvent as ye, getByTypeahead as oe, clickIfLink as Ce, isAnchorElement as Re, getWindow as ve, queryAll as Se, getInitialFocus as Ne, contains as U, scrollIntoView as ke } from "../../dom-query/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, 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, isEqual as Me, prev as be, next as De, last as _e, first as He } from "../../utils/dist/index.js";
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) => He(L(e)), Be = (e) => _e(L(e)), $ = (e, t) => t ? e.id === t || e.dataset.value === t : !1, Ke = (e, t) => {
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 De(n, i, { loop: t.loop ?? t.loopFocus });
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 be(n, i, { loop: t.loop ?? t.loopFocus });
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) => fe(e) && (e.dataset.disabled === "" || e.hasAttribute("disabled")), Ye = (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 && Re(E) && ((h = o("navigate")) == null || h({ value: v, node: E, href: E.href }));
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
- Ce(t.node);
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 ye(o, "pointermove", (l) => {
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()), Pe(() => I(t), {
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) => Me(n, t.point) ? n : t.point);
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, useState as K, useLayoutEffect as ut, useEffect as w } from "react";
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 { isIos as x, setStyleProperty as L, setStyle as p } from "../../dom-query/dist/index.js";
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 = () => L(s, "--scrollbar-width", `${l}px`), f = m(s), u = () => p(e, {
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(), x() ? y() : u()];
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, clickIfLink as M, itemById as X, prevById as H, nextById as K, queryAll as Y } from "../../dom-query/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, 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) => K(V(e), f(e, t.value), t.loopFocus), oe = (e, t) => H(V(e), f(e, t.value), t.loopFocus), O = (e) => ({
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 = X(V(e), f(e, t));
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
- M(t.node);
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 { setRafTimeout as w, ensureProps as J, compact as z, warn as tt, runIfFn as C, uuid as _ } from "../../utils/dist/index.js";
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 J(t, ["id", "type", "parent", "removeDelay"], "toast"), {
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, ...z(t) });
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
- tt("[zag-js > toast] toaster.promise() requires at least a 'loading' option to be specified");
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, isLeftClick as E, dataAttr as I, isComposingEvent as V } from "../../dom-query/dist/index.js";
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": I(p),
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 || E(i) && n("closeOnPointerDown") && f === l.get("id") && r({ type: "close", src: "trigger.pointerdown" });
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.202603310916",
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
+ ```