@defisaver/ui 0.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/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # @defisaver/ui
2
+
3
+ DeFi Saver design system — React components written in TypeScript and styled with [StyleX](https://stylexjs.com), **pre-compiled** so consumer apps need no StyleX tooling at all.
4
+
5
+ > Local-only for now: the package is `private` and consumed via a packed tarball. npm publishing is a documented follow-up (see Roadmap).
6
+
7
+ ## How it works
8
+
9
+ StyleX needs a compiler, and defisaver-app's esbuild pipeline doesn't have one. So this repo compiles StyleX at **library build time** (`@stylexjs/rollup-plugin`): `dist/` contains plain ESM JavaScript with the atomic class names already inlined, plus a single static `dist/styles.css`. Consumers just do:
10
+
11
+ ```tsx
12
+ import { Panel, PanelHeader, PanelTitle, PanelFooter } from '@defisaver/ui';
13
+ import '@defisaver/ui/styles.css'; // once, at the app entry
14
+ ```
15
+
16
+ That works identically under esbuild, Vite, and Next.js — no Babel, no plugins.
17
+
18
+ ### Customizing components from the app
19
+
20
+ Library styles are emitted inside `@layer`, and unlayered CSS beats layered CSS regardless of specificity. So the escape hatch is the one the app already knows: pass a `className` and write plain (S)CSS — a single class selector is enough, and media queries work like anywhere else:
21
+
22
+ ```scss
23
+ // ChartPanel.module.scss — wins over the library defaults, no !important
24
+ .chartHeader {
25
+ min-height: 44px;
26
+ justify-content: flex-start;
27
+
28
+ @media (max-width: 750px) {
29
+ flex-direction: column;
30
+ align-items: flex-start;
31
+ }
32
+ }
33
+ ```
34
+
35
+ ```tsx
36
+ <PanelHeader className={styles.chartHeader}>…</PanelHeader>
37
+ ```
38
+
39
+ Only the properties you declare are overridden; everything else keeps the library value. The flip side: the library also yields to careless broad selectors (`.someParent span { … }`), same as any app-local component — keep overrides scoped to a class on the component itself.
40
+
41
+ **A repeated override is a bug report against the design system.** If several call sites override the same property (say, three headers all forcing `min-height: 44px`), that's not customization — it's a missing variant or token. Request it here instead of copying the override.
42
+
43
+ ### Design tokens
44
+
45
+ Tokens live in `src/tokens/*.stylex.ts` (`colors`, `space`, `radius`, `text`). The **spacing and radius scales are fully defined** — they're stable primitives, and as `defineConsts` they're inlined at compile time (zero CSS cost when unused). **Colors and typography are deliberately minimal**: a token is added the moment a component being ported needs it, never speculatively. The old app's full token inventory was audited (see git history / `~/.claude/plans` analysis) but not imported wholesale — most of it is legacy design debt, and every unused `defineVars` entry would ship as dead CSS to consumers. **Figma is the intended long-term source of truth for the token set** (sync pipeline TBD).
46
+
47
+ Each value defers to defisaver-app's custom property with a standalone fallback, e.g. `var(--surface, #1F272E)` — inside the app, components follow `theme.scss` including `[data-theme]` switching with zero migration; standalone, the app's dark-theme fallbacks apply. Names mirror the app's (`--text-color-secondary` → `colors.textSecondary`) so porting stays mechanical.
48
+
49
+ `src/tokens/tokens.test.ts` compiles every token file through a real `stylex.create()` as a regression guard.
50
+
51
+ StyleX constraint: `defineVars`/`defineConsts` must live in `*.stylex.ts` files containing nothing else, named exports only.
52
+
53
+ ## Commands
54
+
55
+ ```bash
56
+ npm run dev # Storybook on :6006 (dev playground + docs)
57
+ npm run test # Vitest + React Testing Library
58
+ npm run lint # ESLint (org config + @stylexjs plugin), autofixes
59
+ npm run typecheck # tsc --noEmit
60
+ npm run build # Rollup (pre-compile StyleX, emit styles.css) + d.ts
61
+ npm run pack:local # build + npm pack → defisaver-ui-x.y.z.tgz
62
+ ```
63
+
64
+ ## Consuming from defisaver-app (local, for now)
65
+
66
+ ```bash
67
+ # here
68
+ npm run pack:local
69
+
70
+ # in defisaver-app/client
71
+ npm i ../../defisaver-ui/defisaver-ui-0.0.1.tgz
72
+ ```
73
+
74
+ Then import components and add `import '@defisaver/ui/styles.css'` once at the app entry.
75
+
76
+ ## Adding a component
77
+
78
+ 1. Create `src/components/<Name>/` with `<Name>.tsx`, `<Name>.stories.tsx`, `<Name>.test.tsx`, `index.ts` (stories/tests are excluded from the build).
79
+ 2. Style with `stylex.create()`, using tokens from `src/tokens/` — add missing tokens there with `var(--app-var, fallback)` values, mirroring `defisaver-app/client/src/common/theme.scss`. Raw color literals in component styles **fail lint** (`propLimits` on `@stylexjs/valid-styles`) — a missing color means "add a token", never "inline a hex".
80
+ 3. Export it from `src/index.ts`.
81
+ 4. Verify: `npm run lint && npm run typecheck && npm test && npm run build`.
82
+
83
+ When porting from `defisaver-app/client/src/elements/`, keep the public prop API identical so migration is a drop-in import swap.
84
+
85
+ ## Repo layout
86
+
87
+ ```
88
+ src/
89
+ ├── tokens/ # *.stylex.ts — defineConsts/defineVars design tokens
90
+ ├── components/
91
+ │ └── Panel/ # component + stories + tests, co-located
92
+ └── index.ts # public barrel export
93
+ .storybook/ # Storybook 10, react-vite, uses vite.config.ts
94
+ rollup.config.mjs # publish build (pre-compiled JS + styles.css)
95
+ vite.config.ts # internal toolchain: Storybook + Vitest (StyleX unplugin)
96
+ ```
97
+
98
+ ## Roadmap (deferred, in rough order)
99
+
100
+ - Migrate components from `defisaver-app/client/src/elements/` in dependency order (Icon → primitives → composites)
101
+ - GitHub Actions CI (lint / typecheck / test / build / build-storybook)
102
+ - Changesets versioning + npm publishing under `@defisaver` (drop `"private": true`)
103
+ - `./tokens.stylex` source subpath export for StyleX-enabled consumers (future Next.js site)
104
+ - `createTheme`-based ThemeProvider so themes work without the app's `theme.scss`
@@ -0,0 +1,30 @@
1
+ import type { ComponentPropsWithoutRef, ReactNode } from 'react';
2
+ export type PanelSize = 's' | 'm';
3
+ interface PanelSectionProps extends ComponentPropsWithoutRef<'div'> {
4
+ children: ReactNode;
5
+ }
6
+ interface PanelTitleProps extends ComponentPropsWithoutRef<'span'> {
7
+ children: ReactNode;
8
+ }
9
+ interface UncontrolledCollapseProps {
10
+ collapsible?: boolean;
11
+ defaultCollapsed?: boolean;
12
+ collapsed?: never;
13
+ onToggle?: never;
14
+ }
15
+ interface ControlledCollapseProps {
16
+ collapsed: boolean;
17
+ onToggle: () => void;
18
+ collapsible?: never;
19
+ defaultCollapsed?: never;
20
+ }
21
+ type PanelRootProps = Omit<ComponentPropsWithoutRef<'div'>, 'onToggle'> & {
22
+ children: ReactNode;
23
+ size?: PanelSize;
24
+ } & (UncontrolledCollapseProps | ControlledCollapseProps);
25
+ export declare const Panel: import("react").ForwardRefExoticComponent<PanelRootProps & import("react").RefAttributes<HTMLDivElement>>;
26
+ export declare const PanelHeader: import("react").ForwardRefExoticComponent<PanelSectionProps & import("react").RefAttributes<HTMLDivElement>>;
27
+ export declare const PanelTitle: import("react").ForwardRefExoticComponent<PanelTitleProps & import("react").RefAttributes<HTMLSpanElement>>;
28
+ export declare const PanelBody: import("react").ForwardRefExoticComponent<PanelSectionProps & import("react").RefAttributes<HTMLDivElement>>;
29
+ export declare const PanelFooter: import("react").ForwardRefExoticComponent<PanelSectionProps & import("react").RefAttributes<HTMLDivElement>>;
30
+ export {};
@@ -0,0 +1,277 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { createContext, forwardRef, useState, useCallback, useId, useMemo, useContext } from 'react';
3
+ import * as stylex from '@stylexjs/stylex';
4
+
5
+ const PanelContext = createContext({
6
+ size: 'm',
7
+ collapsible: false,
8
+ collapsed: false,
9
+ onToggle: () => {},
10
+ labelId: ''
11
+ });
12
+ const styles = {
13
+ header: {
14
+ kOIVth: "dsui-l10ott",
15
+ kmkexE: "dsui-1f1tjq8 dsui-9kvfbb",
16
+ kGNEyG: "dsui-6s0dn4",
17
+ kB7OPa: "dsui-9f619",
18
+ k1xSpc: "dsui-78zum5",
19
+ kjj79g: "dsui-1qughib",
20
+ kwRFfy: "dsui-shju75",
21
+ kZCmMZ: "dsui-67n0tv",
22
+ kVAEAm: "dsui-1n2onr6",
23
+ kL6WhQ: "dsui-1dy3hxy",
24
+ kfdmCh: "dsui-1q0q8m5",
25
+ kt9PQ7: "dsui-so031l dsui-1t1lzn6",
26
+ kIxVMA: "dsui-1vc0b6c",
27
+ ksF3WI: "dsui-16hifh2",
28
+ $$css: true
29
+ },
30
+ headerCollapsibleS: {
31
+ kZCmMZ: "dsui-izuyw3",
32
+ $$css: true
33
+ },
34
+ headerCollapsibleM: {
35
+ kZCmMZ: "dsui-ciflc3",
36
+ $$css: true
37
+ },
38
+ headerCollapsed: {
39
+ kmkexE: "dsui-1gj7el4 dsui-9kvfbb",
40
+ kL6WhQ: "dsui-16stqrj",
41
+ $$css: true
42
+ },
43
+ headerS: {
44
+ k8WAf4: "dsui-8p8ka2",
45
+ kAzted: "dsui-1gg8mnh",
46
+ $$css: true
47
+ },
48
+ headerM: {
49
+ k8WAf4: "dsui-1qmqf70",
50
+ kAzted: "dsui-bktkl8",
51
+ $$css: true
52
+ }
53
+ };
54
+ // stylex.props() produces the atomic class list (and sometimes inline vars);
55
+ // fold the caller's className/style in after it so external overrides
56
+ // survive the merge.
57
+ const mergeExternal = (sx, className, style) => ({
58
+ ...sx,
59
+ className: [sx.className, className].filter(Boolean).join(' '),
60
+ ...(sx.style || style ? {
61
+ style: {
62
+ ...sx.style,
63
+ ...style
64
+ }
65
+ } : null)
66
+ });
67
+ // Private to Panel — a visual detail of the component, not a public icon.
68
+ // Rendered with currentColor so it follows the toggle's token color.
69
+ // Default (expanded) points down; collapsed rotates to point right.
70
+ const Chevron = ({
71
+ size,
72
+ collapsed
73
+ }) => {
74
+ const px = size === 'm' ? 16 : 14;
75
+ return jsx("svg", {
76
+ width: px,
77
+ height: px,
78
+ viewBox: "0 0 12 12",
79
+ fill: "none",
80
+ xmlns: "http://www.w3.org/2000/svg",
81
+ "aria-hidden": "true",
82
+ ...{
83
+ 0: {
84
+ className: "dsui-1vlsjmz dsui-9kvfbb"
85
+ },
86
+ 1: {
87
+ className: "dsui-1mz1xk5 dsui-9kvfbb dsui-9tu13d"
88
+ }
89
+ }[!!collapsed << 0],
90
+ children: jsx("path", {
91
+ d: "M9 5L6.09642 7.90358C6.04317 7.95683 5.95683 7.95683 5.90358 7.90358L3 5",
92
+ stroke: "currentColor",
93
+ strokeLinecap: "round"
94
+ })
95
+ });
96
+ };
97
+ const Panel = forwardRef(({
98
+ className,
99
+ style,
100
+ children,
101
+ size = 'm',
102
+ collapsible = false,
103
+ defaultCollapsed,
104
+ collapsed: controlledCollapsed,
105
+ onToggle,
106
+ ...rest
107
+ }, ref) => {
108
+ const [ownCollapsed, setOwnCollapsed] = useState(defaultCollapsed ?? false);
109
+ const toggleOwn = useCallback(() => setOwnCollapsed(c => !c), []);
110
+ const labelId = useId();
111
+ const isControlled = controlledCollapsed !== undefined;
112
+ const context = useMemo(() => ({
113
+ size,
114
+ collapsible: isControlled || collapsible || defaultCollapsed !== undefined,
115
+ collapsed: isControlled ? controlledCollapsed : ownCollapsed,
116
+ onToggle: onToggle ?? toggleOwn,
117
+ labelId
118
+ }), [size, isControlled, collapsible, defaultCollapsed, controlledCollapsed, ownCollapsed, onToggle, toggleOwn, labelId]);
119
+ return jsx(PanelContext.Provider, {
120
+ value: context,
121
+ children: jsx("div", {
122
+ ref: ref,
123
+ ...rest,
124
+ ...mergeExternal({
125
+ className: "dsui-glcc71 dsui-zba1ul dsui-1y0btm7 dsui-mkeg23 dsui-b3r6kr dsui-1vg3ier dsui-78zum5 dsui-dt5ytf dsui-h8yej3"
126
+ }, className, style),
127
+ children: children
128
+ })
129
+ });
130
+ });
131
+ Panel.displayName = 'Panel';
132
+ const PanelHeader = forwardRef(({
133
+ className,
134
+ style,
135
+ children,
136
+ ...rest
137
+ }, ref) => {
138
+ const {
139
+ size,
140
+ collapsible,
141
+ collapsed,
142
+ onToggle,
143
+ labelId
144
+ } = useContext(PanelContext);
145
+ return jsxs("div", {
146
+ ref: ref,
147
+ ...rest,
148
+ ...mergeExternal(stylex.props(styles.header, size === 'm' ? styles.headerM : styles.headerS, collapsible && (size === 'm' ? styles.headerCollapsibleM : styles.headerCollapsibleS), collapsed && styles.headerCollapsed), className, style),
149
+ children: [collapsible &&
150
+ // Named by the title text when a PanelTitle is present (labelledby
151
+ // pointing at nothing is skipped), otherwise the aria-label fallback
152
+ // applies — so title-less headers still get a usable name.
153
+ jsx("button", {
154
+ type: "button",
155
+ "aria-expanded": !collapsed,
156
+ "aria-label": "Toggle panel",
157
+ "aria-labelledby": labelId,
158
+ onClick: onToggle,
159
+ ...{
160
+ 0: {
161
+ className: "dsui-1717udv dsui-ng3xce dsui-wdigwf dsui-9kvfbb dsui-6s0dn4 dsui-jbqb8w dsui-uuh3xk dsui-83l8j1 dsui-1ypdohk dsui-3nfvp2 dsui-rotz4w dsui-l56j7k dsui-10l6tqk dsui-1hsy5kr dsui-1adtzmj dsui-3sa99s dsui-wa60dl dsui-1cpjm7i dsui-1hmns74 dsui-7z7f22 dsui-1qx5ct2 dsui-w4jnvo dsui-1xcb4hf"
162
+ },
163
+ 1: {
164
+ className: "dsui-1717udv dsui-ng3xce dsui-wdigwf dsui-9kvfbb dsui-6s0dn4 dsui-jbqb8w dsui-uuh3xk dsui-83l8j1 dsui-1ypdohk dsui-3nfvp2 dsui-rotz4w dsui-l56j7k dsui-10l6tqk dsui-1hsy5kr dsui-1adtzmj dsui-3sa99s dsui-wa60dl dsui-1cpjm7i dsui-1hmns74 dsui-3s33r9 dsui-1fgtraw dsui-gd8bvy dsui-1rd3ygt"
165
+ }
166
+ }[!!(size === 'm') << 0],
167
+ children: jsx(Chevron, {
168
+ size: size,
169
+ collapsed: collapsed
170
+ })
171
+ }), children]
172
+ });
173
+ });
174
+ PanelHeader.displayName = 'PanelHeader';
175
+ const PanelTitle = forwardRef(({
176
+ className,
177
+ style,
178
+ children,
179
+ ...rest
180
+ }, ref) => {
181
+ const {
182
+ size,
183
+ labelId
184
+ } = useContext(PanelContext);
185
+ return jsx("span", {
186
+ ref: ref,
187
+ ...rest,
188
+ ...mergeExternal({
189
+ 0: {
190
+ className: "dsui-xyeboe dsui-6s0dn4 dsui-83l8j1 dsui-3nfvp2 dsui-1kblbho dsui-sx9v3k dsui-1wfe3co"
191
+ },
192
+ 1: {
193
+ className: "dsui-xyeboe dsui-6s0dn4 dsui-83l8j1 dsui-3nfvp2 dsui-1kblbho dsui-1aor607 dsui-1fc57z9"
194
+ }
195
+ }[!!(size === 'm') << 0], className, style),
196
+ children: jsx("span", {
197
+ id: labelId,
198
+ children: children
199
+ })
200
+ });
201
+ });
202
+ PanelTitle.displayName = 'PanelTitle';
203
+ // Folds (rather than unmounts) while the panel is collapsed — see
204
+ // styles.fold for the mechanism. Content stays mounted, so child state
205
+ // (inputs, scroll positions) survives a collapse, like native <details>;
206
+ // content that must truly unmount can be conditionally rendered by the
207
+ // consumer in controlled mode. The consumer-facing div is the innermost
208
+ // one, so className/style/ref behave exactly as before the fold existed.
209
+ const PanelBody = forwardRef(({
210
+ children,
211
+ ...rest
212
+ }, ref) => {
213
+ const {
214
+ collapsed
215
+ } = useContext(PanelContext);
216
+ return jsx("div", {
217
+ ...{
218
+ 0: {
219
+ className: "dsui-b3r6kr dsui-px34qp dsui-9kvfbb dsui-rvj5dj dsui-1tu4anv dsui-npuxes"
220
+ },
221
+ 1: {
222
+ className: "dsui-b3r6kr dsui-rvj5dj dsui-1j9udwn dsui-9kvfbb dsui-ihq33y dsui-lshs6z"
223
+ }
224
+ }[!!collapsed << 0],
225
+ children: jsx("div", {
226
+ ...{
227
+ className: "dsui-2lwn1j"
228
+ },
229
+ children: jsx("div", {
230
+ ref: ref,
231
+ ...rest,
232
+ children: children
233
+ })
234
+ })
235
+ });
236
+ });
237
+ PanelBody.displayName = 'PanelBody';
238
+ const PanelFooter = forwardRef(({
239
+ className,
240
+ style,
241
+ children,
242
+ ...rest
243
+ }, ref) => {
244
+ // A collapsed panel shows only its header — footer actions (pagination
245
+ // etc.) operate on the hidden body, so the footer folds in sync with it.
246
+ // Its top border rides on the inner content div, where the fold clips it
247
+ // away only as the row closes completely.
248
+ const {
249
+ collapsed
250
+ } = useContext(PanelContext);
251
+ return jsx("div", {
252
+ ...{
253
+ 0: {
254
+ className: "dsui-b3r6kr dsui-px34qp dsui-9kvfbb dsui-rvj5dj dsui-1tu4anv dsui-npuxes"
255
+ },
256
+ 1: {
257
+ className: "dsui-b3r6kr dsui-rvj5dj dsui-1j9udwn dsui-9kvfbb dsui-ihq33y dsui-lshs6z"
258
+ }
259
+ }[!!collapsed << 0],
260
+ children: jsx("div", {
261
+ ...{
262
+ className: "dsui-2lwn1j"
263
+ },
264
+ children: jsx("div", {
265
+ ref: ref,
266
+ ...rest,
267
+ ...mergeExternal({
268
+ className: "dsui-l10ott dsui-h5mxk0 dsui-114gu5o dsui-6s0dn4 dsui-9f619 dsui-78zum5 dsui-1qughib dsui-1gzt5ks dsui-13fuv20 dsui-178xt8z dsui-u0aao5"
269
+ }, className, style),
270
+ children: children
271
+ })
272
+ })
273
+ });
274
+ });
275
+ PanelFooter.displayName = 'PanelFooter';
276
+
277
+ export { Panel, PanelBody, PanelFooter, PanelHeader, PanelTitle };
@@ -0,0 +1 @@
1
+ export { Panel, PanelHeader, PanelTitle, PanelBody, PanelFooter, } from './Panel';
@@ -0,0 +1 @@
1
+ export { Panel, PanelHeader, PanelTitle, PanelBody, PanelFooter, } from './components/Panel';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { Panel, PanelBody, PanelFooter, PanelHeader, PanelTitle } from './components/Panel/Panel.js';
@@ -0,0 +1,343 @@
1
+ @layer priority1 {
2
+ :root, .dsui-1cp4su5 {
3
+ --dsui-1vymh5z: var(--surface, #1f272e);
4
+ --dsui-1ov11o: var(--surface-shade, #181f25);
5
+ --dsui-z4f854: var(--surface-border-surface, #252f37);
6
+ --dsui-1c3ib43: var(--text-color-secondary, #b2c1cc);
7
+ --dsui-1ew4bd3: var(--interaction-white-5-hover, #ffffff0d);
8
+ }
9
+ }
10
+
11
+ @layer priority2 {
12
+ .dsui-1717udv {
13
+ padding: 0;
14
+ }
15
+ }
16
+
17
+ @layer priority3 {
18
+ .dsui-glcc71 {
19
+ border-color: var(--dsui-z4f854);
20
+ }
21
+
22
+ .dsui-7z7f22 {
23
+ border-radius: var(--border-radius-medium, 6px);
24
+ }
25
+
26
+ .dsui-zba1ul {
27
+ border-radius: var(--border-radius-xl, 12px);
28
+ }
29
+
30
+ .dsui-3s33r9 {
31
+ border-radius: var(--border-radius-large, 8px);
32
+ }
33
+
34
+ .dsui-ng3xce {
35
+ border-style: none;
36
+ }
37
+
38
+ .dsui-1y0btm7 {
39
+ border-style: solid;
40
+ }
41
+
42
+ .dsui-mkeg23 {
43
+ border-width: 1px;
44
+ }
45
+
46
+ .dsui-xyeboe {
47
+ gap: var(--space-1-5, 6px);
48
+ }
49
+
50
+ .dsui-l10ott {
51
+ gap: var(--space-2, 8px);
52
+ }
53
+
54
+ .dsui-b3r6kr {
55
+ overflow: hidden;
56
+ }
57
+
58
+ .dsui-h5mxk0 {
59
+ padding-block: var(--space-1, 4px);
60
+ }
61
+
62
+ .dsui-8p8ka2 {
63
+ padding-block: var(--space-2, 8px);
64
+ }
65
+
66
+ .dsui-1qmqf70 {
67
+ padding-block: var(--space-2-5, 10px);
68
+ }
69
+
70
+ .dsui-114gu5o {
71
+ padding-inline: var(--space-2, 8px);
72
+ }
73
+
74
+ .dsui-wdigwf {
75
+ transition: background-color .2s, scale .15s ease-out;
76
+ }
77
+
78
+ .dsui-1f1tjq8 {
79
+ transition: border-bottom-color .25s cubic-bezier(.165, .84, .44, 1);
80
+ }
81
+
82
+ .dsui-1gj7el4 {
83
+ transition: border-bottom-color .2s cubic-bezier(.165, .84, .44, 1);
84
+ }
85
+
86
+ .dsui-px34qp {
87
+ transition: grid-template-rows .25s cubic-bezier(.165, .84, .44, 1), visibility .25s;
88
+ }
89
+
90
+ .dsui-1j9udwn {
91
+ transition: grid-template-rows .2s cubic-bezier(.165, .84, .44, 1), visibility .2s;
92
+ }
93
+
94
+ .dsui-1vlsjmz {
95
+ transition: transform .25s cubic-bezier(.165, .84, .44, 1);
96
+ }
97
+
98
+ .dsui-1mz1xk5 {
99
+ transition: transform .2s cubic-bezier(.165, .84, .44, 1);
100
+ }
101
+
102
+ @media (prefers-reduced-motion: reduce) {
103
+ .dsui-9kvfbb.dsui-9kvfbb {
104
+ transition: none;
105
+ }
106
+ }
107
+ }
108
+
109
+ @layer priority4 {
110
+ .dsui-6s0dn4 {
111
+ align-items: center;
112
+ }
113
+
114
+ .dsui-jbqb8w {
115
+ background-color: #0000;
116
+ }
117
+
118
+ .dsui-1vg3ier {
119
+ background-color: var(--dsui-1ov11o);
120
+ }
121
+
122
+ .dsui-9f619 {
123
+ box-sizing: border-box;
124
+ }
125
+
126
+ .dsui-83l8j1 {
127
+ color: var(--dsui-1c3ib43);
128
+ }
129
+
130
+ .dsui-1ypdohk {
131
+ cursor: pointer;
132
+ }
133
+
134
+ .dsui-78zum5 {
135
+ display: flex;
136
+ }
137
+
138
+ .dsui-rvj5dj {
139
+ display: grid;
140
+ }
141
+
142
+ .dsui-3nfvp2 {
143
+ display: inline-flex;
144
+ }
145
+
146
+ .dsui-dt5ytf {
147
+ flex-direction: column;
148
+ }
149
+
150
+ .dsui-sx9v3k {
151
+ font-size: var(--text-size-small, 12px);
152
+ }
153
+
154
+ .dsui-1aor607 {
155
+ font-size: var(--text-size-regular, 14px);
156
+ }
157
+
158
+ .dsui-1kblbho {
159
+ font-weight: var(--text-weight-semi-bold, 500);
160
+ }
161
+
162
+ .dsui-ihq33y {
163
+ grid-template-rows: 0fr;
164
+ }
165
+
166
+ .dsui-1tu4anv {
167
+ grid-template-rows: 1fr;
168
+ }
169
+
170
+ .dsui-rotz4w {
171
+ inset-inline-start: 8px;
172
+ }
173
+
174
+ .dsui-l56j7k {
175
+ justify-content: center;
176
+ }
177
+
178
+ .dsui-1qughib {
179
+ justify-content: space-between;
180
+ }
181
+
182
+ .dsui-1wfe3co {
183
+ line-height: 12px;
184
+ }
185
+
186
+ .dsui-1fc57z9 {
187
+ line-height: 20px;
188
+ }
189
+
190
+ .dsui-shju75 {
191
+ padding-inline-end: var(--space-2, 8px);
192
+ }
193
+
194
+ .dsui-izuyw3 {
195
+ padding-inline-start: 34px;
196
+ }
197
+
198
+ .dsui-ciflc3 {
199
+ padding-inline-start: 42px;
200
+ }
201
+
202
+ .dsui-67n0tv {
203
+ padding-inline-start: var(--space-3, 12px);
204
+ }
205
+
206
+ .dsui-10l6tqk {
207
+ position: absolute;
208
+ }
209
+
210
+ .dsui-1n2onr6 {
211
+ position: relative;
212
+ }
213
+
214
+ .dsui-1hsy5kr {
215
+ scale: 1;
216
+ }
217
+
218
+ .dsui-9tu13d {
219
+ transform: rotate(-90deg);
220
+ }
221
+
222
+ .dsui-3sa99s {
223
+ translate: 0 -50%;
224
+ }
225
+
226
+ .dsui-lshs6z {
227
+ visibility: hidden;
228
+ }
229
+
230
+ .dsui-npuxes {
231
+ visibility: visible;
232
+ }
233
+
234
+ .dsui-uuh3xk:hover {
235
+ background-color: var(--dsui-1ew4bd3);
236
+ }
237
+
238
+ .dsui-1adtzmj:active {
239
+ scale: .96;
240
+ }
241
+ }
242
+
243
+ @layer priority5 {
244
+ .dsui-16stqrj {
245
+ border-bottom-color: #0000;
246
+ }
247
+
248
+ .dsui-1dy3hxy {
249
+ border-bottom-color: var(--dsui-z4f854);
250
+ }
251
+
252
+ .dsui-1q0q8m5 {
253
+ border-bottom-style: solid;
254
+ }
255
+
256
+ .dsui-so031l {
257
+ border-bottom-width: 1px;
258
+ }
259
+
260
+ .dsui-1gzt5ks {
261
+ border-top-color: var(--dsui-z4f854);
262
+ }
263
+
264
+ .dsui-1vc0b6c {
265
+ border-top-left-radius: calc(var(--border-radius-xl, 12px) - 1px);
266
+ }
267
+
268
+ .dsui-16hifh2 {
269
+ border-top-right-radius: calc(var(--border-radius-xl, 12px) - 1px);
270
+ }
271
+
272
+ .dsui-13fuv20 {
273
+ border-top-style: solid;
274
+ }
275
+
276
+ .dsui-178xt8z {
277
+ border-top-width: 1px;
278
+ }
279
+
280
+ .dsui-1qx5ct2 {
281
+ height: 20px;
282
+ }
283
+
284
+ .dsui-1fgtraw {
285
+ height: 28px;
286
+ }
287
+
288
+ .dsui-2lwn1j {
289
+ min-height: 0;
290
+ }
291
+
292
+ .dsui-u0aao5 {
293
+ min-height: 36px;
294
+ }
295
+
296
+ .dsui-1gg8mnh {
297
+ min-height: 44px;
298
+ }
299
+
300
+ .dsui-bktkl8 {
301
+ min-height: 56px;
302
+ }
303
+
304
+ .dsui-wa60dl {
305
+ top: 50%;
306
+ }
307
+
308
+ .dsui-h8yej3 {
309
+ width: 100%;
310
+ }
311
+
312
+ .dsui-w4jnvo {
313
+ width: 20px;
314
+ }
315
+
316
+ .dsui-gd8bvy {
317
+ width: 28px;
318
+ }
319
+
320
+ .dsui-1t1lzn6:last-child {
321
+ border-bottom-width: 0;
322
+ }
323
+ }
324
+
325
+ @layer priority6 {
326
+ .dsui-1xcb4hf:before {
327
+ inset: -10px;
328
+ }
329
+
330
+ .dsui-1rd3ygt:before {
331
+ inset: -6px;
332
+ }
333
+ }
334
+
335
+ @layer priority7 {
336
+ .dsui-1cpjm7i:before {
337
+ content: "";
338
+ }
339
+
340
+ .dsui-1hmns74:before {
341
+ position: absolute;
342
+ }
343
+ }
@@ -0,0 +1,8 @@
1
+ import * as stylex from '@stylexjs/stylex';
2
+ export declare const colors: stylex.VarGroup<Readonly<{
3
+ surface: string;
4
+ surfaceShade: string;
5
+ surfaceBorderSurface: string;
6
+ textSecondary: string;
7
+ white5Hover: string;
8
+ }>, symbol>;
@@ -0,0 +1,9 @@
1
+ export declare const radius: {
2
+ small: string;
3
+ medium: string;
4
+ large: string;
5
+ xl: string;
6
+ xxl: string;
7
+ xxxl: string;
8
+ fullyRounded: string;
9
+ };
@@ -0,0 +1,26 @@
1
+ export declare const space: {
2
+ px1: string;
3
+ px2: string;
4
+ px4: string;
5
+ px6: string;
6
+ px8: string;
7
+ px10: string;
8
+ px12: string;
9
+ px16: string;
10
+ px20: string;
11
+ px24: string;
12
+ px28: string;
13
+ px32: string;
14
+ px36: string;
15
+ px40: string;
16
+ px44: string;
17
+ px48: string;
18
+ px52: string;
19
+ px56: string;
20
+ px60: string;
21
+ px64: string;
22
+ px68: string;
23
+ px72: string;
24
+ px76: string;
25
+ px80: string;
26
+ };
@@ -0,0 +1,5 @@
1
+ export declare const text: {
2
+ sizeSmall: string;
3
+ sizeRegular: string;
4
+ weightSemiBold: string;
5
+ };
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@defisaver/ui",
3
+ "version": "0.0.1",
4
+ "description": "DeFi Saver design system — React components styled with StyleX, pre-compiled so consumers need no StyleX tooling.",
5
+ "type": "module",
6
+ "sideEffects": [
7
+ "**/*.css"
8
+ ],
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./styles.css": "./dist/styles.css",
18
+ "./package.json": "./package.json"
19
+ },
20
+ "scripts": {
21
+ "build": "rollup -c && tsc -p tsconfig.build.json --declaration --emitDeclarationOnly --outDir dist",
22
+ "dev": "storybook dev -p 6006",
23
+ "build-storybook": "storybook build",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest",
26
+ "lint": "eslint . --fix",
27
+ "typecheck": "tsc --noEmit",
28
+ "pack:local": "npm run build && npm pack"
29
+ },
30
+ "engines": {
31
+ "node": ">=22.0.0"
32
+ },
33
+ "dependencies": {
34
+ "@stylexjs/stylex": "^0.19.0"
35
+ },
36
+ "devDependencies": {
37
+ "@defisaver/eslint-config": "^2.1.0",
38
+ "@rollup/plugin-node-resolve": "^16.0.3",
39
+ "@rollup/plugin-typescript": "^12.3.0",
40
+ "@storybook/addon-vitest": "^10.4.6",
41
+ "@storybook/react-vite": "^10.4.6",
42
+ "@stylexjs/eslint-plugin": "^0.19.0",
43
+ "@stylexjs/rollup-plugin": "^0.19.0",
44
+ "@stylexjs/unplugin": "^0.19.0",
45
+ "@testing-library/jest-dom": "^6.9.1",
46
+ "@testing-library/react": "^16.3.2",
47
+ "@testing-library/user-event": "^14.6.1",
48
+ "@types/node": "^22.20.0",
49
+ "@types/react": "^18.3.31",
50
+ "@types/react-dom": "^18.3.7",
51
+ "@vitejs/plugin-react": "^6.0.3",
52
+ "@vitest/browser-playwright": "^4.1.9",
53
+ "@vitest/coverage-v8": "^4.1.9",
54
+ "eslint": "^9.39.4",
55
+ "jsdom": "^29.1.1",
56
+ "playwright": "^1.61.1",
57
+ "prettier": "^3.9.4",
58
+ "react": "^18.3.1",
59
+ "react-dom": "^18.3.1",
60
+ "rollup": "^4.62.2",
61
+ "storybook": "^10.4.6",
62
+ "tslib": "^2.8.1",
63
+ "typescript": "^5.9.3",
64
+ "vite": "^8.1.3",
65
+ "vitest": "^4.1.9"
66
+ }
67
+ }