@meonode/ui 0.1.121 โ†’ 0.2.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 CHANGED
@@ -1,55 +1,58 @@
1
1
  # @meonode/ui
2
2
 
3
- [![NPM version](https://img.shields.io/npm/v/@meonode/ui.svg?style=flat)](https://www.npmjs.com/package/@meonode/ui)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@meonode/ui)](https://bundlephobia.com/package/@meonode/ui)
3
+ [](https://www.npmjs.com/package/@meonode/ui)
4
+ [](https://opensource.org/licenses/MIT)
5
+ [](https://bundlephobia.com/package/@meonode/ui)
6
6
 
7
7
  ## **Build React UIs with Type-Safe Fluency Without JSX Syntax**
8
8
 
9
- A structured approach to component composition, direct CSS-first prop styling, built-in theming, smart prop handling (including raw property pass-through), and dynamic children.
9
+ A revolutionary approach to React component composition featuring function-based syntax, direct CSS-first prop styling, built-in theming system, smart prop handling with raw property pass-through, dynamic children management, and powerful portal capabilities.
10
+
11
+ ### โœจ Quick Start Example
10
12
 
11
- ### Quick Example
12
13
  ```tsx
13
- // Quick Start Example
14
- import { Component, Div, H1, Button } from '@meonode/ui';
14
+ import { Component, Root, Center, Column, H1, Button, Text } from '@meonode/ui';
15
15
 
16
- // Create a reusable styled component
17
- const BlueButton = Component(({ children, ...props }) =>
18
- Button(children, {
19
- padding: '12px 24px',
20
- borderRadius: '8px',
21
- backgroundColor: 'dodgerblue',
22
- color: 'white',
23
- ...props // Merge with incoming props
24
- })
25
- );
16
+ const theme = {
17
+ primary: { default: '#FF6B6B', content: '#4A0000' },
18
+ secondary: { default: '#6BCB77', content: '#0A3B0F' },
19
+ base: { default: '#F8F8F8', content: '#333333' }
20
+ };
26
21
 
27
- // Compose your app
28
22
  const App = Component(() =>
29
- Div({
30
- padding: '40px',
31
- children: [
32
- H1('Welcome to Meonode', { fontSize: '2rem' }),
33
- BlueButton({
34
- onClick: () => alert('Hello World!'),
35
- children: 'Get Started'
36
- })
37
- ]
38
- })
23
+ Root({
24
+ theme,
25
+ backgroundColor: 'theme.base.default',
26
+ children: Center({
27
+ padding: 40,
28
+ children: Column({
29
+ gap: 24,
30
+ textAlign: 'center',
31
+ children: [
32
+ H1('Welcome to MeoNode', {
33
+ fontSize: '3rem',
34
+ color: 'theme.primary.default',
35
+ marginBottom: 16
36
+ }),
37
+ Button('Get Started', {
38
+ backgroundColor: 'theme.primary.default',
39
+ color: 'theme.primary.content',
40
+ padding: '12px 24px',
41
+ borderRadius: 8,
42
+ fontSize: '1.1rem',
43
+ cursor: 'pointer',
44
+ onClick: () => alert('Hello MeoNode!')
45
+ })
46
+ ]
47
+ })
48
+ })
49
+ })
39
50
  );
40
51
  ```
41
52
 
42
- ## Why @meonode/ui?
53
+ -----
43
54
 
44
- - ๐ŸŽฏ **Type-Safe Design** - Full TypeScript support with autocomplete for styles, props, and theme paths
45
- - ๐ŸŽจ **Theme-Aware Styles** - Write styles directly in props with dynamic theme resolution
46
- - ๐Ÿงฉ **Component-First Architecture** - Compose UIs from structured nodes instead of JSX
47
- - ๐Ÿงฉ **CSS-First Prop Styling** - Style components directly via props using CSS property names
48
- - ๐ŸŒ **Contextual Theming** - Theme values propagate automatically through nested components
49
- - โšก **Optimized Bundle** - Efficient core with tree-shaking support
50
- - ๐Ÿ”„ **Seamless React Integration** - Works with hooks, HOCs, and React 18+ features
51
-
52
- ## Installation
55
+ ## ๐Ÿ“ฆ Installation
53
56
 
54
57
  ```bash
55
58
  # Using npm
@@ -62,198 +65,1511 @@ yarn add @meonode/ui react
62
65
  pnpm add @meonode/ui react
63
66
  ```
64
67
 
65
- ---
68
+ -----
69
+
70
+ ## โš™๏ธ CSS Engine Architecture
71
+
72
+ MeoNode UI is powered by **@emotion/react** under the hood, providing a robust and performant CSS-in-JS solution that enables all the advanced styling capabilities you see in the library.
73
+
74
+ ### How It Works
75
+
76
+ ```tsx
77
+ // Behind the scenes, MeoNode transforms this:
78
+ const StyledComponent = Component(() =>
79
+ Div({
80
+ padding: '20px',
81
+ backgroundColor: 'theme.primary.default',
82
+ css: {
83
+ '&:hover': {
84
+ transform: 'scale(1.05)',
85
+ boxShadow: '0 8px 16px rgba(0,0,0,0.2)'
86
+ },
87
+ '@media (max-width: 768px)': {
88
+ padding: '12px'
89
+ }
90
+ }
91
+ })
92
+ );
93
+
94
+ // Into this Emotion-powered component:
95
+ import { css } from '@emotion/react';
66
96
 
67
- ## Core Concepts
97
+ const emotionStyles = css`
98
+ padding: 20px;
99
+ background-color: ${theme.primary.default};
100
+
101
+ &:hover {
102
+ transform: scale(1.05);
103
+ box-shadow: 0 8px 16px rgba(0,0,0,0.2);
104
+ }
105
+
106
+ @media (max-width: 768px) {
107
+ padding: 12px;
108
+ }
109
+ `;
110
+ ```
68
111
 
69
- ### 1. Component Creation
112
+ ### Key Benefits of Emotion Integration
70
113
 
71
- Create elements using either the `Node` factory or pre-built components:
114
+ - **๐ŸŽฏ Performance**: Automatic CSS optimization and dead code elimination
115
+ - **๐Ÿงฉ Dynamic Styling**: Runtime theme value resolution and conditional styles
116
+ - **๐Ÿ”„ Server-Side Rendering**: Full SSR support with automatic critical CSS extraction
117
+ - **๐Ÿ“ฑ Responsive Design**: Native media query support with optimal performance
118
+ - **๐ŸŽจ Advanced Features**: Pseudo-classes, keyframe animations, and complex selectors
119
+
120
+ ### Theme Resolution Engine
72
121
 
73
122
  ```tsx
74
- import { Node, Div, H1 } from '@meonode/ui';
123
+ // MeoNode's theme engine automatically resolves nested theme paths:
124
+ const theme = {
125
+ colors: {
126
+ primary: {
127
+ 500: '#3B82F6',
128
+ 600: '#2563EB'
129
+ }
130
+ }
131
+ };
132
+
133
+ // This syntax:
134
+ backgroundColor: 'theme.colors.primary.500'
135
+
136
+ // Gets resolved by the engine to:
137
+ backgroundColor: '#3B82F6'
138
+
139
+ // The resolution happens through Emotion's theming system:
140
+ import { ThemeProvider } from '@emotion/react';
75
141
 
76
- // Method 1: Node factory for custom elements
77
- const Card = Node('div', {
78
- padding: '20px',
79
- borderRadius: '8px',
80
- boxShadow: '0 2px 12px rgba(0,0,0,0.1)'
142
+ // MeoNode wraps your components automatically:
143
+ const ThemedComponent = () => (
144
+ <ThemeProvider theme={theme}>
145
+ <div css={{
146
+ backgroundColor: theme.colors.primary[500], // Resolved automatically
147
+ '&:hover': {
148
+ backgroundColor: theme.colors.primary[600]
149
+ }
150
+ }}>
151
+ Content
152
+ </div>
153
+ </ThemeProvider>
154
+ );
155
+ ```
156
+
157
+ ### Style Processing Pipeline
158
+
159
+ 1. **Parse Props**: MeoNode separates CSS properties from DOM attributes
160
+ 2. **Resolve Theme**: Theme path strings are resolved to actual values
161
+ 3. **Generate Emotion CSS**: Styles are converted to Emotion's CSS format
162
+ 4. **Optimize**: Emotion handles deduplication, vendor prefixing, and optimization
163
+ 5. **Inject**: Styles are injected into the document head with unique class names
164
+
165
+ ### Advanced Emotion Features Exposed
166
+
167
+ ```tsx
168
+ // Access to Emotion's advanced features through MeoNode:
169
+ const AnimatedComponent = Component(() =>
170
+ Div({
171
+ css: {
172
+ // Emotion's css prop supports all CSS features
173
+ animation: 'slideIn 0.5s ease-out',
174
+
175
+ // Advanced selectors
176
+ '& > *:nth-of-type(odd)': {
177
+ backgroundColor: '#f0f0f0'
178
+ },
179
+
180
+ // CSS custom properties
181
+ '--primary-color': '#3B82F6',
182
+ color: 'var(--primary-color)',
183
+
184
+ // Container queries (when supported)
185
+ '@container (min-width: 400px)': {
186
+ padding: '2rem'
187
+ },
188
+
189
+ // Emotion's interpolation functions
190
+ [`@media (min-width: ${theme.breakpoints.md})`]: {
191
+ fontSize: '1.2rem'
192
+ }
193
+ }
194
+ })
195
+ );
196
+ ```
197
+
198
+ ### Performance Optimizations
199
+
200
+ ```tsx
201
+ // MeoNode leverages Emotion's performance features:
202
+
203
+ // 1. Automatic style memoization
204
+ const MemoizedStyles = Component(() => {
205
+ // These styles are automatically memoized by Emotion
206
+ const expensiveStyles = {
207
+ background: `linear-gradient(45deg,
208
+ ${theme.colors.primary.default},
209
+ ${theme.colors.secondary.default}
210
+ )`,
211
+ css: {
212
+ // Complex animations are optimized
213
+ '@keyframes complexAnimation': {
214
+ '0%': { transform: 'rotate(0deg) scale(1)' },
215
+ '50%': { transform: 'rotate(180deg) scale(1.2)' },
216
+ '100%': { transform: 'rotate(360deg) scale(1)' }
217
+ },
218
+ animation: 'complexAnimation 2s infinite'
219
+ }
220
+ };
221
+
222
+ return Div(expensiveStyles);
81
223
  });
82
224
 
83
- // Method 2: Pre-built semantic components
84
- const Header = () =>
225
+ // 2. Critical CSS extraction for SSR
226
+ // MeoNode automatically handles Emotion's SSR setup:
227
+ import { CacheProvider } from '@emotion/react';
228
+ import createEmotionServer from '@emotion/server/create-instance';
229
+ import createCache from '@emotion/cache';
230
+
231
+ // This is handled internally by MeoNode for optimal SSR performance
232
+ ```
233
+
234
+ -----
235
+
236
+ ## ๐Ÿ“š Core Concepts
237
+
238
+ ### 1\. ๐Ÿ—๏ธ Component Architecture
239
+
240
+ MeoNode uses function-based component creation for maximum flexibility:
241
+
242
+ ```tsx
243
+ import { Node, Component, Div, H1, P } from '@meonode/ui';
244
+
245
+ // Method 1: Direct element creation
246
+ const SimpleCard = () =>
85
247
  Div({
86
248
  padding: '20px',
87
- backgroundColor: 'navy',
88
- children: H1('App Header', { color: 'white' })
249
+ borderRadius: '12px',
250
+ backgroundColor: 'white',
251
+ boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
252
+ children: [
253
+ H1('Card Title', { marginBottom: 8 }),
254
+ P('Card content goes here...')
255
+ ]
89
256
  });
257
+
258
+ // Method 2: Custom element factory
259
+ const Card = Node('article', {
260
+ padding: '24px',
261
+ borderRadius: '16px',
262
+ backgroundColor: 'white',
263
+ border: '1px solid #e0e0e0'
264
+ });
265
+
266
+ // Method 3: Reusable component with props
267
+ const UserCard = Component<{ user: { name: string; role: string } }>(({ user }) =>
268
+ Card({
269
+ children: [
270
+ H1(user.name, { fontSize: '1.5rem', marginBottom: 4 }),
271
+ P(user.role, { color: '#666', fontSize: '0.9rem' })
272
+ ]
273
+ })
274
+ );
90
275
  ```
91
276
 
92
- ### 2. Theming System
277
+ ### 2\. ๐ŸŽจ Advanced Theming System
93
278
 
94
- Define themes and access values using dot-notation:
279
+ Create comprehensive design systems with nested theme objects:
95
280
 
96
281
  ```tsx
97
- // theme.ts
98
- export const theme = {
282
+ // Enhanced theme configuration
283
+ const theme = {
99
284
  colors: {
100
- primary: '#2196F3',
285
+ primary: {
286
+ 50: '#E3F2FD',
287
+ 500: '#2196F3',
288
+ 900: '#0D47A1',
289
+ gradient: 'linear-gradient(135deg, #2196F3, #21CBF3)'
290
+ },
291
+ semantic: {
292
+ success: '#4CAF50',
293
+ warning: '#FF9800',
294
+ error: '#F44336'
295
+ },
101
296
  text: {
102
- primary: '#1A237E',
103
- secondary: '#455A64'
297
+ primary: '#212121',
298
+ secondary: '#757575',
299
+ disabled: '#BDBDBD'
104
300
  }
105
301
  },
106
302
  spacing: {
303
+ xs: '4px',
304
+ sm: '8px',
107
305
  md: '16px',
108
- lg: '24px'
306
+ lg: '24px',
307
+ xl: '32px'
308
+ },
309
+ typography: {
310
+ fontFamily: '"Inter", -apple-system, sans-serif',
311
+ sizes: {
312
+ xs: '0.75rem',
313
+ sm: '0.875rem',
314
+ base: '1rem',
315
+ lg: '1.125rem',
316
+ xl: '1.25rem',
317
+ '2xl': '1.5rem'
318
+ }
319
+ },
320
+ borderRadius: {
321
+ sm: '4px',
322
+ md: '8px',
323
+ lg: '12px',
324
+ xl: '16px'
109
325
  }
110
326
  };
111
327
 
112
- // ThemedComponent.tsx
113
- import { Component, Div, H1, P } from '@meonode/ui';
114
- import { theme } from './theme';
328
+ // Usage with theme paths
329
+ const ThemedButton = Component(() =>
330
+ Button('Primary Action', {
331
+ theme, // Provide theme context
332
+ backgroundColor: 'theme.colors.primary.500',
333
+ color: 'white',
334
+ padding: 'theme.spacing.md theme.spacing.lg',
335
+ borderRadius: 'theme.borderRadius.md',
336
+ fontFamily: 'theme.typography.fontFamily',
337
+ fontSize: 'theme.typography.sizes.base'
338
+ })
339
+ );
340
+ ```
341
+
342
+ ### 3\. ๐ŸŽฏ Advanced CSS Properties & Selectors
115
343
 
116
- const ThemedCard = Component(() =>
344
+ MeoNode provides powerful CSS manipulation through the `css` property for complex styling scenarios:
345
+
346
+ ```tsx
347
+ import { Component, Div, Button, H2, P } from '@meonode/ui';
348
+
349
+ const InteractiveCard = Component(() =>
117
350
  Div({
118
- theme, // Provide theme context
119
- padding: 'theme.spacing.lg',
120
- backgroundColor: 'theme.colors.primary',
351
+ padding: '24px',
352
+ borderRadius: '12px',
353
+ backgroundColor: 'white',
354
+ transition: 'all 0.3s ease',
355
+
356
+ // Advanced CSS with pseudo-classes, hover states, and nested selectors
357
+ css: {
358
+ // Hover effects on the card itself
359
+ '&:hover': {
360
+ transform: 'translateY(-4px)',
361
+ boxShadow: '0 12px 24px rgba(0,0,0,0.15)'
362
+ },
363
+
364
+ // Style child buttons within this card
365
+ '& button': {
366
+ borderRadius: '8px',
367
+ border: 'none',
368
+ transition: 'all 0.2s ease'
369
+ },
370
+
371
+ // Specific button hover states
372
+ '& button:hover': {
373
+ backgroundColor: '#2196F3',
374
+ color: 'white',
375
+ transform: 'scale(1.05)',
376
+ boxShadow: '0 4px 12px rgba(33, 150, 243, 0.3)'
377
+ },
378
+
379
+ // Focus states for accessibility
380
+ '& button:focus': {
381
+ outline: '2px solid #2196F3',
382
+ outlineOffset: '2px'
383
+ },
384
+
385
+ // Media queries for responsive design
386
+ '@media (max-width: 768px)': {
387
+ padding: '16px',
388
+ margin: '8px'
389
+ },
390
+
391
+ // Advanced selectors
392
+ '& h2 + p': {
393
+ marginTop: '8px',
394
+ color: '#666'
395
+ },
396
+
397
+ // Pseudo-elements
398
+ '&::before': {
399
+ content: '""',
400
+ position: 'absolute',
401
+ top: 0,
402
+ left: 0,
403
+ right: 0,
404
+ height: '4px',
405
+ background: 'linear-gradient(90deg, #FF6B6B, #6BCB77)',
406
+ borderRadius: '12px 12px 0 0'
407
+ }
408
+ },
409
+
121
410
  children: [
122
- H1('Themed Title', { color: 'theme.colors.text.primary' }),
123
- P('Content...', { color: 'theme.colors.text.secondary' })
411
+ H2('Interactive Component', {
412
+ fontSize: '1.5rem',
413
+ marginBottom: 12,
414
+ color: '#333'
415
+ }),
416
+ P('This card demonstrates advanced CSS capabilities with hover effects, responsive design, and complex selectors.', {
417
+ lineHeight: 1.6,
418
+ marginBottom: 16
419
+ }),
420
+ Button('Try Hovering!', {
421
+ padding: '10px 20px',
422
+ backgroundColor: '#f5f5f5',
423
+ color: '#333',
424
+ cursor: 'pointer'
425
+ })
124
426
  ]
125
427
  })
126
- )
428
+ );
127
429
  ```
128
430
 
129
- ### 3. Prop Handling
431
+ ### 4\. ๐Ÿ”ง Smart Prop Handling
130
432
 
131
- Automatic separation of CSS props from DOM attributes:
433
+ Automatic differentiation between CSS properties and DOM attributes:
132
434
 
133
435
  ```tsx
134
- type User = {
135
- name: string
436
+ interface CardProps {
437
+ title: string;
438
+ urgent?: boolean;
136
439
  }
137
440
 
138
- const ProfileCard = Component<{ user: User }>(({ user }) =>
441
+ const SmartCard = Component<CardProps>(({ title, urgent, ...restProps }) =>
139
442
  Div({
140
- // CSS Props
443
+ // CSS Properties (automatically recognized)
141
444
  padding: '20px',
142
445
  borderRadius: '8px',
143
-
144
- // DOM Props
145
- 'aria-role': 'article',
446
+ backgroundColor: urgent ? '#FFF3E0' : 'white',
447
+ borderLeft: urgent ? '4px solid #FF9800' : '4px solid transparent',
448
+
449
+ // DOM Attributes (passed through)
450
+ 'data-testid': 'smart-card',
451
+ 'aria-label': `${title} card`,
452
+ role: 'article',
146
453
  tabIndex: 0,
454
+
455
+ // Event Handlers
456
+ onClick: (e) => console.log('Card clicked:', title),
457
+ onKeyDown: (e) => {
458
+ if (e.key === 'Enter') console.log('Card activated:', title);
459
+ },
460
+
461
+ // Merge any additional props
462
+ ...restProps,
463
+
464
+ children: [
465
+ H2(title, {
466
+ color: urgent ? '#E65100' : '#333',
467
+ marginBottom: 12
468
+ }),
469
+ P('Smart prop handling automatically separates CSS from DOM attributes.')
470
+ ]
471
+ })
472
+ );
473
+ ```
474
+
475
+ ### 5\. ๐ŸŒŸ Dynamic Children & Composition
147
476
 
148
- // Children
149
- children: `Welcome ${user.name}!`
477
+ Handle complex child patterns with ease:
478
+
479
+ ```tsx
480
+ const DynamicList = Component<{ items: string[]; header?: string }>(({ items, header }) =>
481
+ Div({
482
+ children: [
483
+ // Conditional header
484
+ header && H2(header, { marginBottom: 16 }),
485
+
486
+ // Dynamic list items
487
+ ...items.map((item, index) =>
488
+ Div({
489
+ key: index,
490
+ padding: '12px',
491
+ borderBottom: index < items.length - 1 ? '1px solid #eee' : 'none',
492
+ children: P(item)
493
+ })
494
+ ),
495
+
496
+ // Conditional footer
497
+ items.length === 0 && P('No items found', {
498
+ color: '#999',
499
+ fontStyle: 'italic',
500
+ textAlign: 'center',
501
+ padding: '20px'
502
+ })
503
+ ]
150
504
  })
151
- )
505
+ );
152
506
  ```
153
507
 
154
- ---
155
-
156
- ### 4. Passing Context Wrapper To Portal
157
-
158
- Learn how to wrap a portal-rendered component with a context provider (like Redux Provider) using the `Portal` API from `@meonode/ui`. This lets portal components access context (such as the Redux store) even when rendered outside the main React tree.
159
-
160
- ```ts
161
- import { Provider, useSelector } from 'react-redux'
162
- import store from '/path/to/redux/store'
163
-
164
- /**
165
- * ReduxProvider
166
- *
167
- * A wrapper component that integrates the Redux store with a React application.
168
- * It utilizes the `Provider` component from `react-redux` to make the Redux store
169
- * available to the entire component tree.
170
- *
171
- * @constant
172
- * @type {NodeElement}
173
- * @param {Object} store - The Redux store instance to be passed down to the application.
174
- */
175
- const ReduxProvider = Node(Provider, { store })
176
-
177
- /**
178
- * Represents a modal component that is implemented using a portal
179
- * and wrapped with a Redux provider. This component retrieves state
180
- * from the Redux store and responds to state changes.
181
- *
182
- * The `Modal` leverages the Redux `useSelector` hook to access specific
183
- * Redux state values and ensure dynamic behavior within the modal based
184
- * on the application's state.
185
- *
186
- * Dependencies:
187
- * - ReduxProvider: Ensures the modal has access to the Redux store.
188
- * - Portal: Renders the modal outside of its parent hierarchy and provides
189
- * control methods such as `unmount` for cleaning up the modal.
190
- * - useSelector: Accesses specific data from the Redux state.
191
- *
192
- * Side Effects:
193
- * - The component logs the specific Redux state changes to the console
194
- * when the state is updated.
195
- * - The modal listens for specific user interactions (e.g., clicking outside
196
- * the modal area) and programmatically unmounts itself using `portal.unmount()`.
197
- *
198
- * Props:
199
- * - portal: Includes a method `unmount` to remove the modal from the DOM.
200
- */
201
- const Modal = Portal(ReduxProvider, ({ portal }) => {
202
- const someReduxState = useSelector(state => state.someReduxState)
203
-
204
- useEffect(() => {
205
- console.log('Redux State value: ', someReduxState)
206
- }, [])
508
+ ### 6\. ๐Ÿšช Portal System with Context Integration
509
+
510
+ Create modals, tooltips, and overlays with full context access:
511
+
512
+ ```tsx
513
+ import { Portal, Center, Column, Button, Text } from '@meonode/ui';
514
+ import { Provider, useSelector } from 'react-redux';
515
+ import store from './store';
516
+
517
+ // Redux Provider wrapper for portal components
518
+ const ReduxProvider = Node(Provider, { store });
519
+
520
+ // Modal with Redux integration
521
+ const NotificationModal = Portal<{ message: string }>(
522
+ ReduxProvider, // Context provider wrapper
523
+ ({ portal, message }) => {
524
+ const userPreferences = useSelector(state => state.user.preferences);
525
+
526
+ useEffect(() => {
527
+ // Auto-close after 3 seconds
528
+ const timer = setTimeout(portal.unmount, 3000);
529
+ return () => clearTimeout(timer);
530
+ }, [portal]);
531
+
532
+ return Center({
533
+ position: 'fixed',
534
+ top: 0,
535
+ left: 0,
536
+ right: 0,
537
+ bottom: 0,
538
+ backgroundColor: 'rgba(0,0,0,0.5)',
539
+ backdropFilter: 'blur(8px)',
540
+ zIndex: 1000,
541
+
542
+ // Click outside to close
543
+ onClick: (e) => {
544
+ if (e.currentTarget === e.target) portal.unmount();
545
+ },
546
+
547
+ children: Column({
548
+ backgroundColor: 'white',
549
+ borderRadius: '16px',
550
+ padding: '32px',
551
+ maxWidth: '400px',
552
+ margin: '20px',
553
+ boxShadow: '0 20px 40px rgba(0,0,0,0.15)',
554
+
555
+ css: {
556
+ // Entrance animation
557
+ animation: 'slideIn 0.3s ease-out',
558
+ '@keyframes slideIn': {
559
+ from: {
560
+ opacity: 0,
561
+ transform: 'translateY(-20px) scale(0.95)'
562
+ },
563
+ to: {
564
+ opacity: 1,
565
+ transform: 'translateY(0) scale(1)'
566
+ }
567
+ }
568
+ },
569
+
570
+ children: [
571
+ Text(message, {
572
+ fontSize: '1.2rem',
573
+ marginBottom: 20,
574
+ textAlign: 'center',
575
+ color: userPreferences.darkMode ? '#fff' : '#333'
576
+ }),
577
+ Button('Close', {
578
+ backgroundColor: '#2196F3',
579
+ color: 'white',
580
+ padding: '10px 20px',
581
+ borderRadius: '8px',
582
+ cursor: 'pointer',
583
+ onClick: portal.unmount
584
+ })
585
+ ]
586
+ })
587
+ });
588
+ }
589
+ );
590
+
591
+ // Usage
592
+ const App = Component(() => {
593
+ const showModal = () => NotificationModal({
594
+ message: 'Portal with Redux context access!'
595
+ });
596
+
597
+ return Button('Show Modal', { onClick: showModal });
598
+ });
599
+ ```
600
+
601
+ -----
602
+
603
+ ## ๐ŸŽฏ Why Choose @meonode/ui?
604
+
605
+ ### **Revolutionary Development Experience**
606
+
607
+ - **๐ŸŽฏ Type-Safe by Design** - Complete TypeScript integration with intelligent autocomplete for styles, props, and theme paths
608
+ - **๐ŸŽจ Theme-Aware Everything** - Write styles directly in props with automatic theme value resolution and inheritance
609
+ - **๐Ÿงฉ Function-Based Composition** - Intuitive component building using structured function calls instead of JSX complexity
610
+ - **๐Ÿ’ซ Advanced CSS Control** - Full CSS capabilities including pseudo-classes, media queries, and complex selectors
611
+ - **๐ŸŒ Contextual Theming** - Theme values propagate automatically through nested component hierarchies
612
+ - **โšก Performance Optimized** - Fast theme resolution and efficient CSS in JS powered by @emotion/react
613
+
614
+ ### **Enterprise-Ready Features**
615
+
616
+ - **๐Ÿ”„ React Ecosystem Compatible** - Seamless integration with hooks, HOCs, context, and React 18+ concurrent features
617
+ - **๐Ÿšช Powerful Portal System** - Advanced portal management with context provider integration for modals and overlays
618
+ - **๐Ÿ“ฑ Responsive by Default** - Built-in responsive design patterns with media query support
619
+ - **โ™ฟ Accessibility First** - Semantic HTML output with ARIA support and keyboard navigation
620
+
621
+ -----
622
+
623
+ ## ๐Ÿ“– API Reference
624
+
625
+ ### Core Functions
626
+
627
+ | Function | Signature | Description |
628
+ | :--- | :--- | :--- |
629
+ | `Node` | `(element: string \| ComponentType, baseProps?: object) => NodeFactory` | Creates a configurable UI node factory that supports flexible properties, dynamic styling, and theme resolution. |
630
+ | `Component` | `(render: (props: P) => ComponentNode) => React.Component<P>` | Transforms node trees into reusable React components with built-in type safety, prop handling, and seamless React integration. |
631
+ | `Portal` | `(component: (props: P & PortalProps) => ComponentNode) \| (provider: NodeElement, component: (props: P & PortalProps) => ComponentNode)` | Creates React Portal components with optional context provider wrapping. Components receive portal controls for programmatic mounting/unmounting and lifecycle management. |
632
+
633
+ ### Pre-built Components
634
+
635
+ | Component Category | Components | Description |
636
+ | :--- | :--- | :--- |
637
+ | **Layout** | `Root`, `Center`, `Column`, `Row`, `Div`, `Section`, `Header`, `Footer`, `Main`, `Nav` | Semantic layout primitives with flexbox and grid support |
638
+ | **Typography** | `H1`, `H2`, `H3`, `H4`, `H5`, `H6`, `P`, `Text`, `Span`, `Strong`, `Em` | Typography elements with theme-aware styling |
639
+ | **Interactive** | `Button`, `Link`, `Input`, `Select`, `Textarea`, `Checkbox`, `Radio` | Form controls and interactive elements |
640
+ | **Media** | `Img`, `Video`, `Audio`, `Canvas`, `Svg` | Media and graphics components |
641
+ | **Semantic** | `Article`, `Aside`, `Details`, `Summary`, `Figure`, `Figcaption` | HTML5 semantic elements |
642
+
643
+ ### PortalProps Interface
644
+
645
+ ```tsx
646
+ interface PortalProps {
647
+ portal: {
648
+ unmount: () => void;
649
+ mount: (component: ComponentNode) => void;
650
+ isVisible: boolean;
651
+ }
652
+ }
653
+ ```
207
654
 
655
+ -----
656
+
657
+ ## ๐ŸŽจ Design Patterns
658
+
659
+ ### 1\. Theme-First Design System
660
+
661
+ ```tsx
662
+ // Complete design system setup
663
+ const designSystem = {
664
+ colors: {
665
+ brand: {
666
+ primary: '#6366F1',
667
+ secondary: '#8B5CF6',
668
+ accent: '#F59E0B'
669
+ },
670
+ neutral: {
671
+ 50: '#F9FAFB',
672
+ 100: '#F3F4F6',
673
+ 500: '#6B7280',
674
+ 900: '#111827'
675
+ },
676
+ semantic: {
677
+ success: '#10B981',
678
+ warning: '#F59E0B',
679
+ error: '#EF4444',
680
+ info: '#3B82F6'
681
+ }
682
+ },
683
+ typography: {
684
+ fontFamily: {
685
+ sans: '"Inter", system-ui, sans-serif',
686
+ mono: '"Fira Code", Consolas, monospace'
687
+ },
688
+ scale: {
689
+ xs: '0.75rem',
690
+ sm: '0.875rem',
691
+ base: '1rem',
692
+ lg: '1.125rem',
693
+ xl: '1.25rem',
694
+ '2xl': '1.5rem',
695
+ '3xl': '1.875rem',
696
+ '4xl': '2.25rem'
697
+ },
698
+ weight: {
699
+ normal: 400,
700
+ medium: 500,
701
+ semibold: 600,
702
+ bold: 700
703
+ }
704
+ },
705
+ spacing: {
706
+ 0: '0px',
707
+ 1: '0.25rem',
708
+ 2: '0.5rem',
709
+ 3: '0.75rem',
710
+ 4: '1rem',
711
+ 6: '1.5rem',
712
+ 8: '2rem',
713
+ 12: '3rem',
714
+ 16: '4rem'
715
+ },
716
+ shadows: {
717
+ sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
718
+ md: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
719
+ lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',
720
+ xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1)'
721
+ },
722
+ borderRadius: {
723
+ none: '0',
724
+ sm: '0.125rem',
725
+ md: '0.375rem',
726
+ lg: '0.5rem',
727
+ xl: '0.75rem',
728
+ full: '9999px'
729
+ }
730
+ };
731
+
732
+ // Usage in components
733
+ const ThemedCard = Component<{ variant?: 'default' | 'success' | 'warning' | 'error' }>(
734
+ ({ variant = 'default', children }) => {
735
+ const variantStyles = {
736
+ default: {
737
+ border: '1px solid theme.neutral.200',
738
+ backgroundColor: 'theme.neutral.50'
739
+ },
740
+ success: {
741
+ border: '1px solid theme.semantic.success',
742
+ backgroundColor: '#F0FDF4'
743
+ },
744
+ warning: {
745
+ border: '1px solid theme.semantic.warning',
746
+ backgroundColor: '#FFFBEB'
747
+ },
748
+ error: {
749
+ border: '1px solid theme.semantic.error',
750
+ backgroundColor: '#FEF2F2'
751
+ }
752
+ };
753
+
754
+ return Div({
755
+ theme: designSystem,
756
+ padding: 'theme.spacing.6',
757
+ borderRadius: 'theme.borderRadius.lg',
758
+ boxShadow: 'theme.shadows.md',
759
+ ...variantStyles[variant],
760
+ children
761
+ });
762
+ }
763
+ );
764
+ ```
765
+
766
+ ### 2\. Responsive Design Patterns
767
+
768
+ ```tsx
769
+ const ResponsiveGrid = Component<{ items: Array<{ title: string; content: string }> }>(
770
+ ({ items }) =>
771
+ Div({
772
+ css: {
773
+ display: 'grid',
774
+ gap: '24px',
775
+
776
+ // Responsive grid
777
+ '@media (min-width: 640px)': {
778
+ gridTemplateColumns: 'repeat(2, 1fr)'
779
+ },
780
+ '@media (min-width: 1024px)': {
781
+ gridTemplateColumns: 'repeat(3, 1fr)'
782
+ },
783
+ '@media (min-width: 1280px)': {
784
+ gridTemplateColumns: 'repeat(4, 1fr)'
785
+ },
786
+
787
+ // Grid item styling
788
+ '& > *': {
789
+ transition: 'transform 0.2s ease, box-shadow 0.2s ease'
790
+ },
791
+
792
+ '& > *:hover': {
793
+ transform: 'translateY(-4px)',
794
+ boxShadow: '0 12px 24px rgba(0,0,0,0.15)'
795
+ }
796
+ },
797
+
798
+ children: items.map((item, index) =>
799
+ ThemedCard({
800
+ key: index,
801
+ children: [
802
+ H2(item.title, {
803
+ fontSize: '1.25rem',
804
+ fontWeight: 'bold',
805
+ marginBottom: 8,
806
+ color: 'theme.neutral.900'
807
+ }),
808
+ P(item.content, {
809
+ color: 'theme.neutral.600',
810
+ lineHeight: 1.6
811
+ })
812
+ ]
813
+ })
814
+ )
815
+ })
816
+ );
817
+ ```
818
+
819
+ ### 3\. Form Composition
820
+
821
+ ```tsx
822
+ const ContactForm = Component(() => {
823
+ const [formData, setFormData] = useState({
824
+ name: '',
825
+ email: '',
826
+ message: ''
827
+ });
828
+
829
+ const handleSubmit = (e: React.FormEvent) => {
830
+ e.preventDefault();
831
+ console.log('Form submitted:', formData);
832
+ };
833
+
834
+ return Column({
835
+ theme: designSystem,
836
+ gap: 'theme.spacing.4',
837
+ maxWidth: '500px',
838
+
839
+ css: {
840
+ '& input, & textarea': {
841
+ width: '100%',
842
+ padding: 'theme.spacing.3',
843
+ borderRadius: 'theme.borderRadius.md',
844
+ border: '1px solid theme.neutral.300',
845
+ fontSize: 'theme.typography.scale.base',
846
+ transition: 'border-color 0.2s ease, box-shadow 0.2s ease'
847
+ },
848
+
849
+ '& input:focus, & textarea:focus': {
850
+ outline: 'none',
851
+ borderColor: 'theme.colors.brand.primary',
852
+ boxShadow: '0 0 0 3px rgba(99, 102, 241, 0.1)'
853
+ },
854
+
855
+ '& label': {
856
+ fontSize: 'theme.typography.scale.sm',
857
+ fontWeight: 'theme.typography.weight.medium',
858
+ color: 'theme.neutral.700',
859
+ marginBottom: 'theme.spacing.2'
860
+ }
861
+ },
862
+
863
+ children: [
864
+ // Form element wrapper
865
+ Node('form', {
866
+ onSubmit: handleSubmit,
867
+ children: [
868
+ Column({
869
+ gap: 'theme.spacing.2',
870
+ children: [
871
+ Node('label', { children: 'Name' }),
872
+ Node('input', {
873
+ type: 'text',
874
+ value: formData.name,
875
+ onChange: (e) => setFormData(prev => ({ ...prev, name: e.target.value })),
876
+ placeholder: 'Enter your name'
877
+ })
878
+ ]
879
+ }),
880
+
881
+ Column({
882
+ gap: 'theme.spacing.2',
883
+ children: [
884
+ Node('label', { children: 'Email' }),
885
+ Node('input', {
886
+ type: 'email',
887
+ value: formData.email,
888
+ onChange: (e) => setFormData(prev => ({ ...prev, email: e.target.value })),
889
+ placeholder: 'Enter your email'
890
+ })
891
+ ]
892
+ }),
893
+
894
+ Column({
895
+ gap: 'theme.spacing.2',
896
+ children: [
897
+ Node('label', { children: 'Message' }),
898
+ Node('textarea', {
899
+ value: formData.message,
900
+ onChange: (e) => setFormData(prev => ({ ...prev, message: e.target.value })),
901
+ placeholder: 'Enter your message',
902
+ rows: 4
903
+ })
904
+ ]
905
+ }),
906
+
907
+ Button('Send Message', {
908
+ type: 'submit',
909
+ backgroundColor: 'theme.colors.brand.primary',
910
+ color: 'white',
911
+ padding: 'theme.spacing.3 theme.spacing.6',
912
+ borderRadius: 'theme.borderRadius.md',
913
+ fontWeight: 'theme.typography.weight.semibold',
914
+ cursor: 'pointer',
915
+ marginTop: 'theme.spacing.4'
916
+ })
917
+ ]
918
+ })
919
+ ]
920
+ });
921
+ });
922
+ ```
923
+
924
+ -----
925
+
926
+ ## ๐Ÿ”ง Advanced Techniques
927
+
928
+ ### Custom Hook Integration
929
+
930
+ ```tsx
931
+ import { useState, useCallback } from 'react';
932
+
933
+ const useToggle = (initialValue = false) => {
934
+ const [value, setValue] = useState(initialValue);
935
+ const toggle = useCallback(() => setValue(v => !v), []);
936
+ return [value, toggle] as const;
937
+ };
938
+
939
+ const ToggleCard = Component(() => {
940
+ const [isExpanded, toggleExpanded] = useToggle(false);
941
+
208
942
  return Div({
209
- padding: 10,
943
+ theme: designSystem,
944
+ borderRadius: 'theme.borderRadius.lg',
210
945
  backgroundColor: 'white',
211
- boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
946
+ boxShadow: 'theme.shadows.md',
947
+ overflow: 'hidden',
948
+
949
+ css: {
950
+ transition: 'all 0.3s ease',
951
+ cursor: 'pointer',
952
+
953
+ '&:hover': {
954
+ boxShadow: 'theme.shadows.lg',
955
+ transform: 'translateY(-2px)'
956
+ }
957
+ },
958
+
959
+ onClick: toggleExpanded,
960
+
212
961
  children: [
213
- P('Content...', { color: 'theme.colors.text.secondary' }),
214
- Button({ children: 'Close', onClick: () => portal.unmount() })
962
+ Div({
963
+ padding: 'theme.spacing.6',
964
+ children: [
965
+ H2('Expandable Card', {
966
+ fontSize: 'theme.typography.scale.xl',
967
+ marginBottom: 8
968
+ }),
969
+ P('Click to expand/collapse', {
970
+ color: 'theme.neutral.600'
971
+ })
972
+ ]
973
+ }),
974
+
975
+ // Conditional expanded content
976
+ isExpanded && Div({
977
+ padding: 'theme.spacing.6',
978
+ paddingTop: 0,
979
+ borderTop: '1px solid theme.neutral.200',
980
+
981
+ css: {
982
+ animation: 'slideDown 0.3s ease',
983
+ '@keyframes slideDown': {
984
+ from: {
985
+ opacity: 0,
986
+ maxHeight: 0,
987
+ transform: 'translateY(-10px)'
988
+ },
989
+ to: {
990
+ opacity: 1,
991
+ maxHeight: '200px',
992
+ transform: 'translateY(0)'
993
+ }
994
+ }
995
+ },
996
+
997
+ children: P('This is the expanded content that appears when the card is clicked. It demonstrates conditional rendering with smooth animations.')
998
+ })
215
999
  ]
1000
+ });
1001
+ });
1002
+ ```
1003
+
1004
+ ### Error Boundaries Integration
1005
+
1006
+ ```tsx
1007
+ import { Component, Div, H2, P, Button } from '@meonode/ui';
1008
+ import { ErrorBoundary } from 'react-error-boundary';
1009
+
1010
+ const ErrorFallback = Component<{ error: Error; resetErrorBoundary: () => void }>(
1011
+ ({ error, resetErrorBoundary }) =>
1012
+ Div({
1013
+ theme: designSystem,
1014
+ padding: 'theme.spacing.8',
1015
+ backgroundColor: 'theme.semantic.error',
1016
+ color: 'white',
1017
+ borderRadius: 'theme.borderRadius.lg',
1018
+ textAlign: 'center',
1019
+
1020
+ children: [
1021
+ H2('Something went wrong', {
1022
+ fontSize: 'theme.typography.scale.2xl',
1023
+ marginBottom: 16
1024
+ }),
1025
+ P(error.message, {
1026
+ marginBottom: 24,
1027
+ opacity: 0.9
1028
+ }),
1029
+ Button('Try Again', {
1030
+ backgroundColor: 'white',
1031
+ color: 'theme.semantic.error',
1032
+ padding: '12px 24px',
1033
+ borderRadius: 'theme.borderRadius.md',
1034
+ fontWeight: 'bold',
1035
+ cursor: 'pointer',
1036
+ onClick: resetErrorBoundary
1037
+ })
1038
+ ]
1039
+ })
1040
+ );
1041
+
1042
+ const SafeApp = Component(() =>
1043
+ Node(ErrorBoundary, {
1044
+ FallbackComponent: ErrorFallback,
1045
+ onError: (error, errorInfo) => {
1046
+ console.error('App Error:', error, errorInfo);
1047
+ },
1048
+ children: YourMainApp()
216
1049
  })
217
- })
1050
+ );
218
1051
  ```
219
1052
 
220
- ---
1053
+ -----
221
1054
 
222
- ## Repo Example Usage
1055
+ ## ๐ŸŒŸ Real-World Example Application
223
1056
 
224
- This section provides a practical example of how to integrate `@meonode/ui` within a Next.js application. The linked repository showcases proper theme handling, especially when utilizing Redux with a preloaded state, and demonstrates its usage within a Server Component (RootLayout) environment. Crucially, it also illustrates how to effectively manage **conditional components that contain React hooks**, providing a robust pattern for dynamic UI rendering. This example is particularly useful for understanding how to set up a robust UI system with `@meonode/ui` in a complex React ecosystem like Next.js.
1057
+ ```tsx
1058
+ import { Component, Root, Center, Column, Row, H1, H2, P, Button, Text, Portal } from '@meonode/ui';
1059
+ import { useState, useEffect } from 'react';
225
1060
 
226
- [Example Usage Of React Meonode in NextJS](https://github.com/l7aromeo/react-meonode)
1061
+ // Complete theme system
1062
+ const appTheme = {
1063
+ colors: {
1064
+ primary: {
1065
+ default: '#FF6B6B',
1066
+ content: '#4A0000',
1067
+ light: '#FFB3B3',
1068
+ dark: '#CC5555'
1069
+ },
1070
+ secondary: {
1071
+ default: '#6BCB77',
1072
+ content: '#0A3B0F',
1073
+ light: '#B3E6BC',
1074
+ dark: '#55A862'
1075
+ },
1076
+ base: {
1077
+ default: '#F8F8F8',
1078
+ content: '#333333',
1079
+ accent: '#88B04B',
1080
+ border: '#E0E0E0'
1081
+ }
1082
+ },
1083
+ spacing: {
1084
+ xs: '8px',
1085
+ sm: '12px',
1086
+ md: '16px',
1087
+ lg: '24px',
1088
+ xl: '32px',
1089
+ '2xl': '48px'
1090
+ },
1091
+ typography: {
1092
+ family: '"Inter", -apple-system, BlinkMacSystemFont, sans-serif',
1093
+ sizes: {
1094
+ sm: '0.875rem',
1095
+ base: '1rem',
1096
+ lg: '1.125rem',
1097
+ xl: '1.25rem',
1098
+ '2xl': '1.5rem',
1099
+ '3xl': '2rem',
1100
+ '4xl': '2.5rem'
1101
+ }
1102
+ }
1103
+ };
227
1104
 
228
- ---
1105
+ const notifications = [
1106
+ { id: 1, type: 'success', message: 'Component created successfully!' },
1107
+ { id: 2, type: 'info', message: 'MeoNode UI is theme-aware by default.' },
1108
+ { id: 3, type: 'warning', message: 'Remember to handle responsive design.' },
1109
+ { id: 4, type: 'error', message: 'This is just a demo error message.' }
1110
+ ];
229
1111
 
230
- ## API Reference
1112
+ // Enhanced Modal with animations and context
1113
+ const NotificationModal = Portal<{ notification: typeof notifications[0] }>(
1114
+ ({ portal, notification }) => {
1115
+ useEffect(() => {
1116
+ const timer = setTimeout(portal.unmount, 4000);
1117
+ return () => clearTimeout(timer);
1118
+ }, [portal]);
231
1119
 
232
- ### Core Functions
1120
+ const typeStyles = {
1121
+ success: { bg: '#F0FDF4', border: '#22C55E', text: '#15803D' },
1122
+ info: { bg: '#EFF6FF', border: '#3B82F6', text: '#1D4ED8' },
1123
+ warning: { bg: '#FFFBEB', border: '#F59E0B', text: '#D97706' },
1124
+ error: { bg: '#FEF2F2', border: '#EF4444', text: '#DC2626' }
1125
+ }[notification.type];
233
1126
 
234
- | Function | Parameters | Description |
235
- |-------------|---------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
236
- | `Node` | `element: NodeElement \| React.ComponentType`, `baseProps: object` | Constructs a configurable UI node that supports flexible properties and dynamic styling. |
237
- | `Component` | `(props: P) => ComponentNode` | Transforms node trees into reusable React components with built-in type safety and seamless integration. |
238
- | `Portal` | โ€ข `(component: (props: P) => ComponentNode)` or <br/> โ€ข `(provider: NodeElement, component: (props: P) => ComponentNode)` | Creates a React Portal component. Accepts either a component function directly, or a provider (e.g. Redux Provider) and the component. The component receives portal controls for mounting/unmounting. |
1127
+ return Center({
1128
+ theme: appTheme,
1129
+ position: 'fixed',
1130
+ top: 0,
1131
+ left: 0,
1132
+ right: 0,
1133
+ bottom: 0,
1134
+ backgroundColor: 'rgba(0,0,0,0.4)',
1135
+ backdropFilter: 'blur(8px)',
1136
+ zIndex: 1000,
1137
+
1138
+ onClick: (e) => {
1139
+ if (e.currentTarget === e.target) portal.unmount();
1140
+ },
1141
+
1142
+ children: Column({
1143
+ backgroundColor: typeStyles.bg,
1144
+ borderRadius: 'theme.spacing.md',
1145
+ padding: 'theme.spacing.xl',
1146
+ margin: 'theme.spacing.lg',
1147
+ maxWidth: '400px',
1148
+ border: `2px solid ${typeStyles.border}`,
1149
+
1150
+ css: {
1151
+ animation: 'modalSlideIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1)',
1152
+
1153
+ '@keyframes modalSlideIn': {
1154
+ from: {
1155
+ opacity: 0,
1156
+ transform: 'scale(0.8) translateY(-40px)'
1157
+ },
1158
+ to: {
1159
+ opacity: 1,
1160
+ transform: 'scale(1) translateY(0)'
1161
+ }
1162
+ }
1163
+ },
1164
+
1165
+ children: [
1166
+ H2(`${notification.type.toUpperCase()} Notification`, {
1167
+ color: typeStyles.text,
1168
+ fontSize: 'theme.typography.sizes.xl',
1169
+ marginBottom: 'theme.spacing.md',
1170
+ textAlign: 'center',
1171
+ textTransform: 'capitalize'
1172
+ }),
1173
+
1174
+ P(notification.message, {
1175
+ color: typeStyles.text,
1176
+ lineHeight: 1.6,
1177
+ textAlign: 'center',
1178
+ marginBottom: 'theme.spacing.lg'
1179
+ }),
1180
+
1181
+ Row({
1182
+ gap: 'theme.spacing.sm',
1183
+ justifyContent: 'center',
1184
+ children: [
1185
+ Button('Dismiss', {
1186
+ backgroundColor: 'transparent',
1187
+ color: typeStyles.text,
1188
+ border: `1px solid ${typeStyles.border}`,
1189
+ padding: 'theme.spacing.sm theme.spacing.md',
1190
+ borderRadius: 'theme.spacing.xs',
1191
+ cursor: 'pointer',
1192
+ onClick: portal.unmount
1193
+ }),
1194
+
1195
+ Button('OK', {
1196
+ backgroundColor: typeStyles.border,
1197
+ color: 'white',
1198
+ padding: 'theme.spacing.sm theme.spacing.md',
1199
+ borderRadius: 'theme.spacing.xs',
1200
+ cursor: 'pointer',
1201
+ fontWeight: 'bold',
1202
+ onClick: portal.unmount
1203
+ })
1204
+ ]
1205
+ })
1206
+ ]
1207
+ })
1208
+ });
1209
+ }
1210
+ );
1211
+
1212
+ // Main application component
1213
+ const MeoNodeShowcase = Component(() => {
1214
+ const [selectedMessage, setSelectedMessage] = useState('');
1215
+ const [currentTheme, setCurrentTheme] = useState<'light' | 'dark'>('light');
1216
+
1217
+ const surprises = [
1218
+ 'MeoNode makes UI development delightful! ๐ŸŽ‰',
1219
+ 'Intuitive design meets pure simplicity.',
1220
+ 'Build beautiful interfaces effortlessly with type safety.',
1221
+ 'Composable, theme-aware, and a joy to use!'
1222
+ ];
1223
+
1224
+ const getRandomSurprise = () => {
1225
+ const randomMessage = surprises[Math.floor(Math.random() * surprises.length)];
1226
+ setSelectedMessage(randomMessage);
1227
+ };
1228
+
1229
+ const showNotification = (type: 'success' | 'info' | 'warning' | 'error') => {
1230
+ const notification = notifications.find(n => n.type === type);
1231
+ if (notification) {
1232
+ NotificationModal({ notification });
1233
+ }
1234
+ };
1235
+
1236
+ return Root({
1237
+ theme: appTheme,
1238
+ backgroundColor: currentTheme === 'light' ? 'theme.base.default' : '#1a1a1a',
1239
+ color: currentTheme === 'light' ? 'theme.base.content' : '#ffffff',
1240
+ minHeight: '100vh',
1241
+ fontFamily: 'theme.typography.family',
1242
+
1243
+ children: Center({
1244
+ padding: 'theme.spacing.2xl',
1245
+ children: Column({
1246
+ gap: 'theme.spacing.xl',
1247
+ maxWidth: '800px',
1248
+ textAlign: 'center',
1249
+
1250
+ css: {
1251
+ // Global button styling
1252
+ '& button': {
1253
+ transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
1254
+ border: 'none',
1255
+ outline: 'none'
1256
+ },
1257
+
1258
+ '& button:hover': {
1259
+ transform: 'translateY(-2px)',
1260
+ boxShadow: '0 8px 25px rgba(0,0,0,0.15)'
1261
+ },
1262
+
1263
+ '& button:active': {
1264
+ transform: 'translateY(0)',
1265
+ transition: 'transform 0.1s ease'
1266
+ },
1267
+
1268
+ // Responsive adjustments
1269
+ '@media (max-width: 768px)': {
1270
+ gap: 'theme.spacing.lg',
1271
+ padding: 'theme.spacing.lg',
1272
+
1273
+ '& h1': {
1274
+ fontSize: 'theme.typography.sizes.3xl !important'
1275
+ }
1276
+ }
1277
+ },
1278
+
1279
+ children: [
1280
+ // Header section
1281
+ Column({
1282
+ gap: 'theme.spacing.md',
1283
+ children: [
1284
+ H1('MeoNode UI Showcase', {
1285
+ fontSize: 'theme.typography.sizes.4xl',
1286
+ background: 'linear-gradient(135deg, theme.colors.primary.default, theme.colors.secondary.default)',
1287
+ backgroundClip: 'text',
1288
+ WebkitBackgroundClip: 'text',
1289
+ WebkitTextFillColor: 'transparent',
1290
+ fontWeight: 'bold'
1291
+ }),
1292
+
1293
+ P('Experience the power of function-based React components with advanced theming and CSS capabilities.', {
1294
+ fontSize: 'theme.typography.sizes.lg',
1295
+ color: currentTheme === 'light' ? 'theme.base.content' : '#cccccc',
1296
+ lineHeight: 1.6,
1297
+ maxWidth: '600px',
1298
+ margin: '0 auto'
1299
+ })
1300
+ ]
1301
+ }),
1302
+
1303
+ // Interactive message display
1304
+ Div({
1305
+ padding: 'theme.spacing.lg',
1306
+ backgroundColor: currentTheme === 'light' ? 'white' : '#2a2a2a',
1307
+ borderRadius: 'theme.spacing.md',
1308
+ border: currentTheme === 'light' ? '1px solid theme.base.border' : '1px solid #404040',
1309
+ minHeight: '80px',
1310
+ display: 'flex',
1311
+ alignItems: 'center',
1312
+ justifyContent: 'center',
1313
+
1314
+ css: {
1315
+ transition: 'all 0.3s ease',
1316
+ '&:hover': {
1317
+ backgroundColor: currentTheme === 'light' ? '#fafafa' : '#333333'
1318
+ }
1319
+ },
1320
+
1321
+ children: Text(selectedMessage || 'Click a button below to see MeoNode in action! โœจ', {
1322
+ fontSize: 'theme.typography.sizes.lg',
1323
+ color: currentTheme === 'light' ? 'theme.base.content' : '#ffffff',
1324
+ textAlign: 'center',
1325
+ lineHeight: 1.5
1326
+ })
1327
+ }),
1328
+
1329
+ // Action buttons grid
1330
+ Row({
1331
+ gap: 'theme.spacing.md',
1332
+ justifyContent: 'center',
1333
+ flexWrap: 'wrap',
1334
+ children: [
1335
+ Button('๐ŸŽ‰ Random Surprise', {
1336
+ backgroundColor: 'theme.colors.primary.default',
1337
+ color: 'theme.colors.primary.content',
1338
+ padding: 'theme.spacing.md theme.spacing.lg',
1339
+ borderRadius: 'theme.spacing.sm',
1340
+ fontSize: 'theme.typography.sizes.base',
1341
+ fontWeight: 'bold',
1342
+ cursor: 'pointer',
1343
+ onClick: getRandomSurprise
1344
+ }),
1345
+
1346
+ Button('๐ŸŒ™ Toggle Theme', {
1347
+ backgroundColor: currentTheme === 'light' ? '#333333' : '#ffffff',
1348
+ color: currentTheme === 'light' ? '#ffffff' : '#333333',
1349
+ padding: 'theme.spacing.md theme.spacing.lg',
1350
+ borderRadius: 'theme.spacing.sm',
1351
+ fontSize: 'theme.typography.sizes.base',
1352
+ fontWeight: 'bold',
1353
+ cursor: 'pointer',
1354
+ onClick: () => setCurrentTheme(t => t === 'light' ? 'dark' : 'light')
1355
+ })
1356
+ ]
1357
+ }),
1358
+
1359
+ // Notification demo buttons
1360
+ Column({
1361
+ gap: 'theme.spacing.sm',
1362
+ children: [
1363
+ H2('Portal Notifications Demo', {
1364
+ fontSize: 'theme.typography.sizes.xl',
1365
+ marginBottom: 'theme.spacing.md',
1366
+ color: currentTheme === 'light' ? 'theme.base.content' : '#ffffff'
1367
+ }),
1368
+
1369
+ Row({
1370
+ gap: 'theme.spacing.sm',
1371
+ justifyContent: 'center',
1372
+ flexWrap: 'wrap',
1373
+ children: [
1374
+ Button('โœ… Success', {
1375
+ backgroundColor: 'theme.colors.semantic.success',
1376
+ color: 'white',
1377
+ padding: 'theme.spacing.sm theme.spacing.md',
1378
+ borderRadius: 'theme.spacing.xs',
1379
+ cursor: 'pointer',
1380
+ onClick: () => showNotification('success')
1381
+ }),
1382
+
1383
+ Button('โ„น๏ธ Info', {
1384
+ backgroundColor: 'theme.colors.semantic.info',
1385
+ color: 'white',
1386
+ padding: 'theme.spacing.sm theme.spacing.md',
1387
+ borderRadius: 'theme.spacing.xs',
1388
+ cursor: 'pointer',
1389
+ onClick: () => showNotification('info')
1390
+ }),
1391
+
1392
+ Button('โš ๏ธ Warning', {
1393
+ backgroundColor: 'theme.colors.semantic.warning',
1394
+ color: 'white',
1395
+ padding: 'theme.spacing.sm theme.spacing.md',
1396
+ borderRadius: 'theme.spacing.xs',
1397
+ cursor: 'pointer',
1398
+ onClick: () => showNotification('warning')
1399
+ }),
1400
+
1401
+ Button('โŒ Error', {
1402
+ backgroundColor: 'theme.colors.semantic.error',
1403
+ color: 'white',
1404
+ padding: 'theme.spacing.sm theme.spacing.md',
1405
+ borderRadius: 'theme.spacing.xs',
1406
+ cursor: 'pointer',
1407
+ onClick: () => showNotification('error')
1408
+ })
1409
+ ]
1410
+ })
1411
+ ]
1412
+ })
1413
+ ]
1414
+ })
1415
+ })
1416
+ });
1417
+ });
1418
+
1419
+ export default MeoNodeShowcase;
1420
+ ```
1421
+
1422
+ -----
1423
+
1424
+ ## ๐Ÿ“‹ Best Practices
1425
+
1426
+ ### 1\. **Theme Organization**
1427
+
1428
+ - Structure themes hierarchically with logical groupings
1429
+ - Use semantic naming for colors (primary, secondary, success, etc.)
1430
+ - Include both light and dark variants in your theme system
1431
+ - Define spacing and typography scales for consistency
1432
+
1433
+ ### 2\. **Component Composition**
1434
+
1435
+ - Keep components focused and single-purpose
1436
+ - Use the `Component` wrapper for reusable elements
1437
+ - Leverage prop spreading for flexible component APIs
1438
+ - Combine multiple simple components to create complex interfaces
1439
+
1440
+ ### 3\. **Performance Optimization**
1441
+
1442
+ - Use React.memo for expensive components when needed
1443
+ - Leverage the built-in tree-shaking capabilities
1444
+ - Minimize theme object recreations in render functions
1445
+ - Use CSS-in-JS patterns responsibly for optimal performance
1446
+
1447
+ ### 4\. **CSS Architecture**
1448
+
1449
+ - Prefer theme values over hardcoded styles
1450
+ - Use the `css` property for complex selectors and animations
1451
+ - Implement responsive design through media queries
1452
+ - Maintain consistent hover and focus states across components
1453
+
1454
+ -----
1455
+
1456
+ ## ๐Ÿ”— Integration Examples
1457
+
1458
+ ### Next.js Integration
1459
+
1460
+ ```tsx
1461
+ // app/layout.tsx
1462
+ import { Component, Root } from '@meonode/ui';
1463
+ import { theme } from './theme';
1464
+
1465
+ const RootLayout = Component<{ children: React.ReactNode }>(({ children }) =>
1466
+ Root({
1467
+ theme,
1468
+ className: 'min-h-screen',
1469
+ children
1470
+ })
1471
+ );
1472
+ ```
1473
+
1474
+ ### Redux Integration
1475
+
1476
+ ```tsx
1477
+ import { Provider } from 'react-redux';
1478
+ import { Node } from '@meonode/ui';
1479
+ import store from './store';
1480
+
1481
+ const ReduxProvider = Node(Provider, { store });
1482
+
1483
+ const AppWithRedux = Component(() =>
1484
+ ReduxProvider({
1485
+ children: YourApp()
1486
+ })
1487
+ );
1488
+ ```
239
1489
 
240
- ---
1490
+ -----
241
1491
 
242
- ## Contributing
1492
+ ## ๐ŸŒ Community & Resources
243
1493
 
244
- We welcome contributions! Please follow these steps:
1494
+ ### Example Repository
245
1495
 
246
- 1. **Fork** the repository
247
- 2. **Clone** your fork: `git clone https://github.com/your-username/meonode-ui.git`
248
- 3. **Install dependencies**: `yarn install` (or npm/pnpm)
249
- 4. **Create a feature branch**: `git checkout -b feature/amazing-feature`
250
- 5. **Commit changes** with descriptive messages
251
- 6. **Push** to your branch: `git push origin feature/amazing-feature`
252
- 7. **Open a Pull Request**
1496
+ Explore a complete Next.js application showcasing MeoNode UI best practices...
253
1497
 
254
- For major changes, please open an issue first to discuss your proposal.
1498
+ **[๐Ÿ”— MeoNode + Next.js Example](https://github.com/l7aromeo/react-meonode)**
1499
+ **[๐Ÿ”— Open in CodeSandbox](https://codesandbox.io/p/github/l7aromeo/react-meonode/main?import=true)**
1500
+
1501
+ This repository demonstrates:
1502
+
1503
+ - Server Component integration
1504
+ - Redux state management with preloaded state
1505
+ - Responsive design patterns
1506
+ - Advanced portal usage
1507
+ - Theme system implementation
1508
+
1509
+ -----
1510
+
1511
+ ## ๐Ÿค Contributing
1512
+
1513
+ We welcome contributions from the community\! Here's how to get started:
1514
+
1515
+ ### Development Setup
1516
+
1517
+ ```bash
1518
+ # 1. Fork and clone the repository
1519
+ git clone https://github.com/your-username/meonode-ui.git
1520
+ cd meonode-ui
255
1521
 
256
- ---
1522
+ # 2. Install dependencies
1523
+ npm install
1524
+ # or
1525
+ yarn install
1526
+ # or
1527
+ pnpm install
1528
+
1529
+ # 3. Start development server
1530
+ npm run dev
1531
+
1532
+ # 4. Run tests
1533
+ npm test
1534
+
1535
+ # 5. Build the project
1536
+ npm run build
1537
+ ```
1538
+
1539
+ ### Contribution Guidelines
1540
+
1541
+ 1. **๐Ÿด Fork** the repository and create your feature branch
1542
+ 2. **๐Ÿ”ง Install dependencies** and ensure tests pass
1543
+ 3. **โœจ Create your feature** with comprehensive tests
1544
+ 4. **๐Ÿ“ Update documentation** for any new features
1545
+ 5. **๐Ÿงช Test thoroughly** across different scenarios
1546
+ 6. **๐Ÿ“ค Submit a Pull Request** with a clear description
1547
+
1548
+ ### Development Standards
1549
+
1550
+ - **TypeScript First** - All code must include proper type definitions
1551
+ - **Test Coverage** - Maintain \>95% test coverage for new features
1552
+ - **Documentation** - Update README and examples for new features
1553
+ - **Performance** - Ensure no performance regressions
1554
+ - **Accessibility** - Follow WCAG guidelines for new components
1555
+
1556
+ For major changes or new features, please open an issue first to discuss your proposal with the maintainers.
1557
+
1558
+ -----
1559
+
1560
+ ## ๐Ÿ“„ License & Support
257
1561
 
258
1562
  **MIT Licensed** | Copyright ยฉ 2024 Ukasyah Rahmatullah Zada
259
- *Empowering developers to build better UIs*
1563
+
1564
+ ### Getting Help
1565
+
1566
+ - ๐Ÿ“š **Documentation**: Full API reference and examples
1567
+ - ๐Ÿ› **Issues**: [GitHub Issues](https://github.com/l7aromeo/meonode-ui/issues)
1568
+ - ๐Ÿ’ฌ **Discussions**: [GitHub Discussions](https://github.com/l7aromeo/meonode-ui/discussions)
1569
+ - ๐Ÿ“ง **Discord**: [l7aromeo](https://discord.com/users/704803255561224264)
1570
+
1571
+ -----
1572
+
1573
+ *Empowering developers to build exceptional UIs with type-safe, theme-aware, function-based React components.*
1574
+
1575
+ **MeoNode UI - Where Function Meets Beauty** โœจ