@ronnydrori/jupiter-ui 1.0.0

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,483 @@
1
+ # Jupiter UI Design System
2
+
3
+ A comprehensive design system built with React, TypeScript, Mantine v7, and Storybook. This library provides a complete set of design tokens, themes, and reusable components for building consistent user interfaces.
4
+
5
+ ## 🏗️ Architecture
6
+
7
+ ### Directory Structure
8
+
9
+ ```
10
+ libs/jupiter-ui/
11
+ ├── .storybook/ # Storybook configuration
12
+ │ ├── main.ts # Storybook main config
13
+ │ ├── preview.tsx # Global decorators & theme setup
14
+ │ └── preview.css # Preview styling
15
+ ├── src/
16
+ │ ├── design-system/
17
+ │ │ ├── tokens/ # Design tokens (spacing, typography, etc.)
18
+ │ │ ├── theme/ # Theme system & providers
19
+ │ │ ├── components/ # React components
20
+ │ │ ├── hooks/ # Custom React hooks
21
+ │ │ └── utils/ # Utility functions
22
+ │ └── index.ts # Main export file
23
+ └── package.json
24
+ ```
25
+
26
+ ## 🛠️ Technology Choices
27
+
28
+ This design system was built with the following technologies, chosen for their balance of ease of use, customization, and team familiarity:
29
+
30
+ ### UI Foundation: Mantine v7
31
+ - **Why Mantine?** Easy integration with 100+ pre-built components, flexible theme API with CSS variables, and 40+ included hooks. Provides good customization without fighting defaults, and excellent TypeScript support.
32
+ - **Benefits:** Faster development with ready-to-use components and extensible theming.
33
+
34
+ ### CSS Strategy: Emotion
35
+ - **Why Emotion?** Zero learning curve for our CSS-in-JS experienced team. Works seamlessly with Mantine, provides type-safe styling, dynamic theming capabilities, and co-located styles with components.
36
+ - **Benefits:** Full JavaScript power for styling, theme access via props, and easy refactoring.
37
+
38
+ ### File Organization: Feature-Based Structure
39
+ - **Why Feature-Based?** Components are self-contained in their own folders, making it easy to add, modify, or remove features without affecting others. Scales well for medium-sized component libraries.
40
+ - **Benefits:** Intuitive organization, easy maintenance, and clear boundaries between components.
41
+
42
+ ### Icons: Lucide + Phosphor
43
+ - **Why These Libraries?** Both provide excellent style control (size, color, stroke), MIT license, and tree-shaking support. Lucide for general UI icons, Phosphor for weight variants when needed.
44
+ - **Benefits:** Consistent iconography, small bundle size (~200-250b per icon), and flexible styling.
45
+
46
+ ### Overall Stack Rationale
47
+ - **Balanced Approach:** Prioritizes speed of development while maintaining design control and customization.
48
+ - **Team Fit:** Leverages existing CSS-in-JS expertise to minimize learning curve.
49
+ - **Scalability:** Feature-based organization supports growth from 20-40 components to more.
50
+ - **Modern Standards:** TypeScript-first, accessible components, and responsive design.
51
+ - **Dynamic Theming:** Supports runtime palette loading via environment variables for flexible theming (e.g., yanai.json, rotem.json).
52
+
53
+ ## � Design Tokens
54
+
55
+ All design tokens are defined in `src/design-system/tokens/`:
56
+
57
+ - **Spacing** (`spacing.ts`) - Consistent spacing scale (0.25rem - 20rem)
58
+ - **Typography** (`typography.ts`) - Font families, sizes, weights, line heights
59
+ - **Radius** (`radius.ts`) - Border radius values (xs, sm, md, lg, xl)
60
+ - **Borders** (`borders.ts`) - Border width values
61
+ - **Shadows** (`shadows.ts`) - Box shadow definitions (xs, sm, md, lg, xl)
62
+ - **Breakpoints** (`breakpoints.ts`) - Responsive breakpoints (xs, sm, md, lg, xl)
63
+
64
+ ### Example Token File: `spacing.ts`
65
+
66
+ ```typescript
67
+ import type { TokenScaleKey } from './types';
68
+
69
+ export const spacing = {
70
+ 0: '0px',
71
+ 1: '4px',
72
+ 2: '8px',
73
+ 3: '12px',
74
+ 4: '16px',
75
+ 5: '20px',
76
+ 6: '24px',
77
+ 7: '28px',
78
+ 8: '32px',
79
+ 9: '36px',
80
+ } as const;
81
+
82
+ export const semanticSpacing: Partial<Record<TokenScaleKey, string>> = {
83
+ xs: spacing[1], // 4px
84
+ sm: spacing[2], // 8px
85
+ md: spacing[4], // 16px
86
+ lg: spacing[6], // 24px
87
+ xl: spacing[8], // 32px
88
+ } as const;
89
+
90
+ export type SpacingToken = keyof typeof spacing;
91
+ export type SemanticSpacingToken = keyof typeof semanticSpacing;
92
+ ```
93
+
94
+ ## 🎭 Theme System
95
+
96
+ ### Theme Structure
97
+
98
+ The theme system is located in `src/design-system/theme/`:
99
+
100
+ - **types.ts** - TypeScript types for theme structure
101
+ - **theme.ts** - Core theme creator using design tokens
102
+ - **mantine.ts** - Mantine theme configuration
103
+ - **ThemeProvider.tsx** - React context provider for theme management
104
+
105
+ ### Dynamic Palette Loading
106
+
107
+ The design system supports dynamic color palette loading at runtime:
108
+
109
+ - **usePalette Hook**: Loads color palettes asynchronously from JSON files
110
+ - **Environment Variable**: Set `VITE_PROJECT_NAME` to specify the project name (e.g., `yanai` or `rotem`)
111
+ - **Palette Path**: Fetches from `/color-palettes/${VITE_PROJECT_NAME}.json`
112
+ - **Fallback**: Defaults to `defaultPalette.ts` if no environment variable is set or loading fails
113
+
114
+ ```typescript
115
+ // Example: Loading a palette
116
+ const colors = usePalette(); // Loads from /color-palettes/${VITE_PROJECT_NAME}.json or defaults to defaultPalette
117
+ ```
118
+
119
+ ### Using Themes
120
+
121
+ ```typescript
122
+ import { JupiterThemeProvider } from '@libraries/jupiter-ui';
123
+
124
+ function App() {
125
+ return (
126
+ <JupiterThemeProvider>
127
+ {/* Your app */}
128
+ </JupiterThemeProvider>
129
+ );
130
+ }
131
+ ```
132
+
133
+ ### Theme Context Hook
134
+
135
+ ```typescript
136
+ import { useThemeContext } from '@libraries/jupiter-ui';
137
+
138
+ function MyComponent() {
139
+ const { mode, toggleMode, setMode } = useThemeContext();
140
+ // mode: 'light' | 'dark'
141
+ // toggleMode: () => void
142
+ // setMode: (mode: ThemeMode) => void
143
+ }
144
+ ```
145
+
146
+ ## 🧩 Components
147
+
148
+ ### Component Guidelines
149
+
150
+ All components are located in `src/design-system/components/[ComponentName]/`:
151
+
152
+ 1. **No index.ts files** - Import directly from component files
153
+ 2. **Component file**: `ComponentName.tsx`
154
+ 3. **Test file**: `ComponentName.test.tsx` - **REQUIRED** (enforced by ESLint)
155
+ 4. **Stories file**: `ComponentName.stories.tsx`
156
+ 5. **Export from main index**: Update `src/index.ts`
157
+ 6. **Set `displayName`** on exported React components and higher-order components (e.g., `ComponentName.displayName = 'ComponentName'`; internal helper components don't need this)
158
+
159
+ > ⚠️ **Testing Requirement**: Every component must have a corresponding test file. This is enforced by ESLint's `require-test-file` rule and will prevent commits without tests.
160
+
161
+ ### Component Structure
162
+
163
+ ```typescript
164
+ // JpButton.tsx
165
+ import { Button as MantineButton } from '@mantine/core';
166
+ import type { ButtonProps as MantineButtonProps } from '@mantine/core';
167
+ import { buttonVariants, type JupiterVariant } from '../utils/variant-factory';
168
+
169
+ export interface JpButtonProps extends Omit<MantineButtonProps, 'variant'> {
170
+ variant?: JupiterVariant;
171
+ }
172
+
173
+ export function JpButton({ variant = 'primary', ...props }: JpButtonProps) {
174
+ return (
175
+ <MantineButton
176
+ variant={buttonVariants[variant] ?? buttonVariants.primary}
177
+ {...props}
178
+ />
179
+ );
180
+ }
181
+
182
+ // Required: set the display name for better DX in React DevTools, Storybook, and logs
183
+ JpButton.displayName = 'JpButton';
184
+ ```
185
+
186
+ ### Custom Styling with Emotion (Function Syntax)
187
+
188
+ Always use Emotion's function form for `styled` components, not tagged template strings. This ensures better type safety, prop filtering, and consistent patterns across the library.
189
+
190
+ ```typescript
191
+ import styled from '@emotion/styled';
192
+ // Optional: filter invalid DOM props when styling host elements
193
+ // import isPropValid from '@emotion/is-prop-valid';
194
+ import { JpButton, useThemeContext } from '@libraries/jupiter-ui';
195
+
196
+ // Create a styled button with custom styles (function approach)
197
+ const StyledButton = styled(JpButton, { label: 'styled-button' })(() => ({
198
+ backgroundColor: 'hotpink',
199
+ color: 'white',
200
+ '&:hover': {
201
+ backgroundColor: 'deeppink',
202
+ },
203
+ // Use static selectors to style inner elements
204
+ '& .mantine-Button-label': {
205
+ fontWeight: 'bold',
206
+ textTransform: 'uppercase',
207
+ },
208
+ }));
209
+
210
+ // Create a themed button using design tokens (function approach)
211
+ const ThemedButton = styled(JpButton, { label: 'themed-button' })(({ theme }) => ({
212
+ backgroundColor: '#3b82f6',
213
+ color: 'white',
214
+ borderRadius: theme?.radius?.md || '0.5rem',
215
+ padding: `${theme?.spacing?.md || '0.75rem'} ${theme?.spacing?.lg || '1rem'}`,
216
+ fontWeight: theme?.typography?.fontWeights?.semibold || 600,
217
+ '&:hover': {
218
+ backgroundColor: '#2563eb',
219
+ transform: 'translateY(-1px)',
220
+ boxShadow: theme?.shadows?.md || '0 4px 6px rgba(0,0,0,0.1)',
221
+ },
222
+ // Style inner elements
223
+ '& .mantine-Button-label': {
224
+ fontFamily: theme?.typography?.fontFamilies?.body || 'system-ui',
225
+ },
226
+ }));
227
+
228
+
229
+ function Demo() {
230
+ return (
231
+ <div>
232
+ <StyledButton>Styled with Emotion</StyledButton>
233
+ <ThemedButton>
234
+ Themed Button
235
+ </ThemedButton>
236
+ </div>
237
+ );
238
+ }
239
+ ```
240
+
241
+ **Key Points**:
242
+ - Use Emotion's function syntax: `styled(Component, options)(props => styles)`
243
+ - Do not use tagged template strings (e.g., ``styled.div`...``, ``styled(Button)`...``)
244
+ - Use Mantine's class selectors (e.g., `.mantine-Button-label`) to target inner elements
245
+ - Access theme tokens via `props.theme` (pass theme from `useThemeContext()`)
246
+ - Use transient props (`$propName`) to avoid passing custom props to DOM
247
+ - All Mantine component props still work on styled components
248
+ - Styled components can be used in Storybook stories
249
+
250
+ ### Storybook Stories
251
+
252
+ ```typescript
253
+ // ComponentName.stories.tsx
254
+ import type { Meta, StoryObj } from '@storybook/react';
255
+ import { ComponentName } from './ComponentName';
256
+
257
+ const meta = {
258
+ title: 'Components/ComponentName',
259
+ component: ComponentName,
260
+ parameters: {
261
+ layout: 'centered',
262
+ },
263
+ tags: ['autodocs'],
264
+ argTypes: {
265
+ variant: {
266
+ control: 'select',
267
+ options: ['primary', 'secondary', 'outline', 'ghost'],
268
+ },
269
+ },
270
+ } satisfies Meta<typeof ComponentName>;
271
+
272
+ export default meta;
273
+ type Story = StoryObj<typeof meta>;
274
+
275
+ export const Primary: Story = {
276
+ args: {
277
+ variant: 'primary',
278
+ children: 'Component Text',
279
+ },
280
+ };
281
+ ```
282
+
283
+ ## 📚 Storybook
284
+
285
+ ### Configuration
286
+
287
+ - **Version**: v8.6.15
288
+ - **Framework**: React + Vite
289
+ - **Addons**:
290
+ - `storybook-addon-mantine` - Mantine theme integration
291
+ - Standard Storybook addons (essentials, interactions, etc.)
292
+
293
+
294
+
295
+ ### Running Storybook
296
+
297
+ ```bash
298
+ npm run storybook
299
+ ```
300
+
301
+ Storybook will be available at http://localhost:6006
302
+
303
+ ## 🔧 Technology Stack
304
+
305
+ - **React** v18.3.1
306
+ - **TypeScript** v5.5.4 (strict mode)
307
+ - **Mantine** v7.15.1 (UI component library)
308
+ - **Emotion** v11.13.5 (CSS-in-JS styling)
309
+ - **Storybook** v8.6.14
310
+ - **Vite** v5.4.11 (build tool)
311
+ - **Vitest** v3.2.4 (testing framework)
312
+ - **Lucide + Phosphor** (icons)
313
+
314
+ ## 📝 Coding Conventions
315
+
316
+ ### Imports
317
+
318
+ - Use named exports (not default exports except for Storybook meta)
319
+ - Import components directly: `import { Button } from './components/Button/Button'`
320
+ - No intermediate index files in component folders
321
+
322
+ ### TypeScript
323
+
324
+ - Use explicit types for all public APIs
325
+ - Export interface definitions alongside components
326
+ - Use `type` for type aliases, `interface` for object shapes
327
+ - No `any` types allowed
328
+
329
+ ### File Naming
330
+
331
+ - Components: PascalCase (e.g., `Button.tsx`)
332
+ - Stories: `ComponentName.stories.tsx`
333
+ - Utils/Tokens: camelCase (e.g., `colors.ts`)
334
+
335
+ ### Component Variants
336
+
337
+ Use semantic variant names that describe intent, not implementation:
338
+ - ✅ `primary`, `secondary`, `outline`, `ghost`
339
+ - ❌ `filled`, `light`, `subtle` (Mantine internal variants)
340
+
341
+ Map semantic variants to Mantine variants inside components.
342
+
343
+ ## 🎯 Best Practices
344
+
345
+ ### Theme Provider
346
+
347
+ - Always wrap components in `JupiterThemeProvider` when using `useThemeContext()`
348
+ - ThemeProvider provides theme context and token access to all components
349
+
350
+ ### Mantine Integration
351
+
352
+ - Extend Mantine components, don't replace them
353
+ - Override Mantine's theme through `createAppTheme()` function
354
+ - Use Mantine's utility functions (e.g., `rem()`, `em()`)
355
+
356
+ ### State Management
357
+
358
+ - Avoid `useEffect` for syncing props to state
359
+ - Use render-time state updates: `if (prop !== state) setState(prop)`
360
+ - This prevents cascading renders and React act() warnings
361
+
362
+ ## 🚀 Development Workflow
363
+
364
+ 1. **Add new tokens** in `src/design-system/tokens/`
365
+ 2. **Update theme** in `src/design-system/theme/` if needed
366
+ 3. **Create component** in `src/design-system/components/[Name]/`
367
+ 4. **Add stories** in same folder as component
368
+ 5. **Export from main** in `src/index.ts`
369
+ 6. **Test in Storybook** with `npm run storybook`
370
+ 7. **Commit** using conventional commits format
371
+
372
+ ## 📦 Export Pattern
373
+
374
+ All exports go through `src/index.ts`:
375
+
376
+ ```typescript
377
+ // Tokens
378
+ export * from './design-system/tokens/spacing';
379
+ export * from './design-system/tokens/radius';
380
+ export * from './design-system/tokens/borders';
381
+ export * from './design-system/tokens/typography';
382
+ export * from './design-system/tokens/shadows';
383
+ export * from './design-system/tokens/breakpoints';
384
+
385
+ // Theme
386
+ export * from './design-system/theme/types';
387
+ export * from './design-system/theme/mantine';
388
+ export {
389
+ JupiterThemeProvider,
390
+ useThemeContext,
391
+ } from './design-system/theme/ThemeProvider';
392
+ export type { JupiterThemeProviderProps } from './design-system/theme/ThemeProvider';
393
+
394
+ // Utils
395
+ export {
396
+ buttonVariants,
397
+ inputVariants,
398
+ selectVariants,
399
+ checkboxVariants,
400
+ } from './design-system/utils/variant-factory';
401
+ export type { JupiterVariant } from './design-system/utils/variant-factory';
402
+
403
+ // Components
404
+ export { JpButton } from './design-system/components/Button/JpButton';
405
+ export type { JpButtonProps } from './design-system/components/Button/JpButton';
406
+
407
+ // Hooks
408
+ export * from './design-system/hooks/usePalette';
409
+ ```
410
+
411
+ ## Scripts
412
+
413
+ ```bash
414
+ # Development
415
+ npm run storybook # Start Storybook on localhost:6006
416
+ npm run build-storybook # Build static Storybook
417
+
418
+ # Linting
419
+ npm run lint # Run ESLint
420
+ npm run lint:fix # Fix ESLint issues
421
+
422
+ # Type Checking
423
+ npm run typecheck # Run TypeScript compiler check
424
+
425
+ # Testing
426
+ npm run test # Run unit tests with Vitest
427
+ npm run test-storybook # Run storybook tests in the cli
428
+ ```
429
+ ### 📖 Official Documentation
430
+
431
+ - **Storybook Interaction Tests**
432
+ https://storybook.js.org/docs/writing-tests/interaction-testing
433
+
434
+ - **Storybook Vitest Addon**
435
+ https://storybook.js.org/docs/8/writing-tests/test-addon
436
+
437
+ ## 🔒 ESLint Rules
438
+
439
+ This library enforces strict rules to maintain quality and plug-and-play architecture:
440
+
441
+ ### TypeScript Rules
442
+ - ❌ `@typescript-eslint/no-explicit-any`: No `any` types allowed
443
+ - ✅ `@typescript-eslint/consistent-type-imports`: Prefer type imports for better tree-shaking
444
+ - ✅ `@typescript-eslint/prefer-nullish-coalescing`: Use `??` over `||` for null/undefined checks
445
+ - ✅ `@typescript-eslint/prefer-optional-chain`: Use optional chaining (`?.`) instead of manual checks
446
+ - ⚠️ `@typescript-eslint/no-non-null-assertion`: Warn on non-null assertions (`!`)
447
+
448
+ ### Import Restrictions
449
+ - ❌ No imports from other monorepo libraries (`@libraries/*` except `@libraries/jupiter-ui`)
450
+ - ❌ No imports from apps (`apps/*`)
451
+ - ❌ No imports from other libs (`libs/*`)
452
+ - ❌ No relative imports outside `src/` directory
453
+ - ✅ Only allowed external dependencies: `@mantine/*`, `@emotion/*`, `@phosphor-icons/react`, `lucide-react`, `vitest`, `react`, `react-dom`, `@testing-library/*`, `@storybook/*`, `@types/*`
454
+
455
+ ### Component Requirements
456
+ - ✅ Every component must have a corresponding `.test.tsx` file (enforced by `test-file/require-test-file` rule)
457
+ - ✅ Set `displayName` on exported React components for better DX in DevTools and logs
458
+
459
+ ### React Rules
460
+ - ✅ All React recommended rules
461
+ - ✅ React Hooks recommended rules
462
+ - ✅ JSX runtime rules
463
+
464
+ ### Other
465
+ - ✅ Fully type-safe
466
+ - ✅ Plug-and-play architecture
467
+
468
+ ## 🤝 Contributing
469
+
470
+ When adding components:
471
+
472
+ 1. Create component folder in `src/design-system/components/[Name]/`
473
+ 2. Create `Name.tsx` with component implementation
474
+ 3. Create `Name.stories.tsx` with comprehensive stories
475
+ 4. Export component and types from `src/index.ts`
476
+ 5. Ensure no external monorepo dependencies
477
+ 6. Follow semantic variant naming
478
+ 7. Add proper TypeScript types
479
+
480
+ ## 📖 Additional Resources
481
+
482
+ - [Mantine Documentation](https://mantine.dev/)
483
+ - [Storybook Documentation](https://storybook.js.org/)