@shohojdhara/atomix 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/atomix.config.ts +12 -0
  2. package/build-tools/webpack-loader.js +5 -4
  3. package/dist/atomix.css +138 -17
  4. package/dist/atomix.css.map +1 -1
  5. package/dist/atomix.min.css +1 -1
  6. package/dist/atomix.min.css.map +1 -1
  7. package/dist/build-tools/webpack-loader.js +5 -4
  8. package/dist/charts.d.ts +23 -23
  9. package/dist/charts.js +40 -37
  10. package/dist/charts.js.map +1 -1
  11. package/dist/config.d.ts +624 -0
  12. package/dist/config.js +59 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/core.d.ts +2 -2
  15. package/dist/core.js +111 -50
  16. package/dist/core.js.map +1 -1
  17. package/dist/forms.d.ts +3 -6
  18. package/dist/forms.js +2 -2
  19. package/dist/forms.js.map +1 -1
  20. package/dist/heavy.d.ts +1 -1
  21. package/dist/heavy.js +173 -111
  22. package/dist/heavy.js.map +1 -1
  23. package/dist/index.d.ts +98 -65
  24. package/dist/index.esm.js +427 -422
  25. package/dist/index.esm.js.map +1 -1
  26. package/dist/index.js +394 -391
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.min.js +1 -1
  29. package/dist/index.min.js.map +1 -1
  30. package/dist/layout.js +59 -60
  31. package/dist/layout.js.map +1 -1
  32. package/dist/theme.js +4 -4
  33. package/dist/theme.js.map +1 -1
  34. package/package.json +14 -9
  35. package/scripts/atomix-cli.js +15 -1
  36. package/scripts/cli/__tests__/complexity-utils.test.js +24 -0
  37. package/scripts/cli/__tests__/detector.test.js +50 -0
  38. package/scripts/cli/__tests__/template-engine.test.js +23 -0
  39. package/scripts/cli/__tests__/test-setup.js +3 -0
  40. package/scripts/cli/commands/doctor.js +15 -3
  41. package/scripts/cli/commands/generate.js +113 -51
  42. package/scripts/cli/internal/ai-engine.js +30 -10
  43. package/scripts/cli/internal/complexity-utils.js +60 -0
  44. package/scripts/cli/internal/component-validator.js +49 -16
  45. package/scripts/cli/internal/generator.js +89 -36
  46. package/scripts/cli/internal/hook-generator.js +5 -2
  47. package/scripts/cli/internal/itcss-generator.js +16 -12
  48. package/scripts/cli/templates/next-templates.js +81 -30
  49. package/scripts/cli/templates/storybook-templates.js +12 -2
  50. package/scripts/cli/utils/detector.js +45 -7
  51. package/scripts/cli/utils/diagnostics.js +78 -0
  52. package/scripts/cli/utils/telemetry.js +13 -0
  53. package/src/components/Accordion/Accordion.stories.tsx +4 -0
  54. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +1 -1
  55. package/src/components/AtomixGlass/__snapshots__/AtomixGlass.test.tsx.snap +219 -0
  56. package/src/components/AtomixGlass/glass-utils.ts +1 -1
  57. package/src/components/Button/Button.tsx +114 -57
  58. package/src/components/Callout/Callout.tsx +4 -4
  59. package/src/components/Chart/ChartRenderer.tsx +1 -1
  60. package/src/components/Chart/DonutChart.tsx +11 -8
  61. package/src/components/EdgePanel/EdgePanel.tsx +119 -115
  62. package/src/components/Form/Select.tsx +4 -4
  63. package/src/components/List/List.tsx +4 -4
  64. package/src/components/Navigation/SideMenu/SideMenu.tsx +6 -6
  65. package/src/components/PhotoViewer/PhotoViewerImage.tsx +1 -1
  66. package/src/components/ProductReview/ProductReview.tsx +4 -2
  67. package/src/components/Rating/Rating.tsx +4 -2
  68. package/src/components/SectionIntro/SectionIntro.tsx +4 -2
  69. package/src/components/Steps/Steps.tsx +1 -1
  70. package/src/components/Tabs/Tabs.tsx +5 -5
  71. package/src/components/Testimonial/Testimonial.tsx +4 -2
  72. package/src/components/VideoPlayer/VideoPlayer.tsx +4 -2
  73. package/src/layouts/CssGrid/CssGrid.stories.tsx +464 -0
  74. package/src/layouts/CssGrid/CssGrid.tsx +215 -0
  75. package/src/layouts/CssGrid/index.ts +8 -0
  76. package/src/layouts/CssGrid/scripts/CssGrid.js +284 -0
  77. package/src/layouts/CssGrid/scripts/index.js +43 -0
  78. package/src/layouts/Grid/scripts/Container.js +139 -0
  79. package/src/layouts/Grid/scripts/Grid.js +184 -0
  80. package/src/layouts/Grid/scripts/GridCol.js +273 -0
  81. package/src/layouts/Grid/scripts/Row.js +154 -0
  82. package/src/layouts/Grid/scripts/index.js +48 -0
  83. package/src/layouts/MasonryGrid/MasonryGrid.tsx +71 -59
  84. package/src/lib/composables/atomix-glass/useGlassSize.ts +1 -1
  85. package/src/lib/composables/useAccordion.ts +5 -5
  86. package/src/lib/composables/useAtomixGlass.ts +3 -3
  87. package/src/lib/composables/useBarChart.ts +2 -2
  88. package/src/lib/composables/useChart.ts +3 -2
  89. package/src/lib/composables/useChartToolbar.ts +48 -66
  90. package/src/lib/composables/useDataTable.ts +1 -1
  91. package/src/lib/composables/useDatePicker.ts +2 -2
  92. package/src/lib/composables/useEdgePanel.ts +45 -54
  93. package/src/lib/composables/useHeroBackgroundSlider.ts +5 -5
  94. package/src/lib/composables/usePhotoViewer.ts +2 -3
  95. package/src/lib/composables/usePieChart.ts +1 -1
  96. package/src/lib/composables/usePopover.ts +151 -139
  97. package/src/lib/composables/useSideMenu.ts +28 -41
  98. package/src/lib/composables/useSlider.ts +2 -6
  99. package/src/lib/composables/useTooltip.ts +2 -2
  100. package/src/lib/config/index.ts +39 -0
  101. package/src/lib/theme/devtools/Comparator.tsx +1 -1
  102. package/src/lib/theme/devtools/Inspector.tsx +1 -1
  103. package/src/lib/theme/devtools/LiveEditor.tsx +1 -1
  104. package/src/lib/theme/runtime/ThemeProvider.tsx +1 -1
  105. package/src/styles/01-settings/_index.scss +1 -0
  106. package/src/styles/01-settings/_settings.atomix-glass.scss +174 -0
  107. package/src/styles/01-settings/_settings.masonry-grid.scss +42 -6
  108. package/src/styles/02-tools/_tools.glass.scss +6 -0
  109. package/src/styles/05-objects/_objects.masonry-grid.scss +162 -24
  110. package/src/styles/06-components/_components.atomix-glass.scss +4 -4
  111. package/src/lib/composables/useBreadcrumb.ts +0 -81
  112. package/src/lib/composables/useChartInteractions.ts +0 -123
  113. package/src/lib/composables/useChartPerformance.ts +0 -347
  114. package/src/lib/composables/useDropdown.ts +0 -338
  115. package/src/lib/composables/useModal.ts +0 -110
  116. package/src/lib/hooks/usePerformanceMonitor.ts +0 -148
  117. package/src/lib/utils/displacement-generator.ts +0 -92
  118. package/src/lib/utils/memoryMonitor.ts +0 -191
  119. package/src/styles/01-settings/_settings.testtypecheck.scss +0 -53
  120. package/src/styles/01-settings/_settings.typedbutton.scss +0 -53
  121. package/src/styles/06-components/_components.testbutton.scss +0 -212
  122. package/src/styles/06-components/_components.testtypecheck.scss +0 -212
  123. package/src/styles/06-components/_components.typedbutton.scss +0 -212
@@ -9,6 +9,26 @@ import { join } from 'path';
9
9
  import { logger } from '../utils/logger.js';
10
10
  import { AtomixCLIError } from '../utils/error.js';
11
11
 
12
+ /**
13
+ * Next.js server components (no "use client") do not use forwardRef / client hooks.
14
+ * @param {string} content
15
+ */
16
+ function isNextServerOnlyComponent(content) {
17
+ if (content.includes("'use client'") || content.includes('"use client"')) {
18
+ return false;
19
+ }
20
+ if (content.includes('forwardRef')) {
21
+ return false;
22
+ }
23
+ if (/export\s+default\s+async\s+function/.test(content) || /export\s+default\s+function/.test(content)) {
24
+ return true;
25
+ }
26
+ if (/async\s+function\s+\w+/.test(content) && /export\s+default\s+\w+/.test(content)) {
27
+ return true;
28
+ }
29
+ return false;
30
+ }
31
+
12
32
  /**
13
33
  * Component validation rule severity levels
14
34
  */
@@ -31,7 +51,11 @@ export const COMPONENT_RULES = {
31
51
  severity: COMPONENT_SEVERITY.ERROR,
32
52
  validate: (content) => {
33
53
  const issues = [];
34
-
54
+
55
+ if (isNextServerOnlyComponent(content)) {
56
+ return issues;
57
+ }
58
+
35
59
  if (!content.includes('forwardRef')) {
36
60
  issues.push({
37
61
  rule: 'forward-ref-required',
@@ -40,7 +64,7 @@ export const COMPONENT_RULES = {
40
64
  severity: COMPONENT_SEVERITY.ERROR
41
65
  });
42
66
  }
43
-
67
+
44
68
  return issues;
45
69
  }
46
70
  },
@@ -200,7 +224,11 @@ export const COMPONENT_RULES = {
200
224
  severity: COMPONENT_SEVERITY.INFO,
201
225
  validate: (content) => {
202
226
  const issues = [];
203
-
227
+
228
+ if (isNextServerOnlyComponent(content)) {
229
+ return issues;
230
+ }
231
+
204
232
  if (!content.includes('memo') && !content.includes('React.memo')) {
205
233
  issues.push({
206
234
  rule: 'memo-usage',
@@ -209,7 +237,7 @@ export const COMPONENT_RULES = {
209
237
  severity: COMPONENT_SEVERITY.INFO
210
238
  });
211
239
  }
212
-
240
+
213
241
  return issues;
214
242
  }
215
243
  },
@@ -223,9 +251,13 @@ export const COMPONENT_RULES = {
223
251
  severity: COMPONENT_SEVERITY.INFO,
224
252
  validate: (content, componentName) => {
225
253
  const issues = [];
226
-
254
+
255
+ if (isNextServerOnlyComponent(content)) {
256
+ return issues;
257
+ }
258
+
227
259
  const hookPattern = new RegExp(`use${componentName}`);
228
-
260
+
229
261
  if (!hookPattern.test(content)) {
230
262
  issues.push({
231
263
  rule: 'composable-hook-pattern',
@@ -234,7 +266,7 @@ export const COMPONENT_RULES = {
234
266
  severity: COMPONENT_SEVERITY.INFO
235
267
  });
236
268
  }
237
-
269
+
238
270
  return issues;
239
271
  }
240
272
  },
@@ -248,13 +280,14 @@ export const COMPONENT_RULES = {
248
280
  severity: COMPONENT_SEVERITY.INFO,
249
281
  validate: (content) => {
250
282
  const issues = [];
251
-
252
- // Check for ThemeNaming import or usage
283
+
284
+ if (isNextServerOnlyComponent(content)) {
285
+ return issues;
286
+ }
287
+
253
288
  const hasThemeNaming = /ThemeNaming\./.test(content) || /themeNaming\./.test(content);
254
-
255
- // Check for variantClass, sizeClass, stateClass patterns
256
289
  const hasVariantPattern = /(variant|size|state)Class/.test(content);
257
-
290
+
258
291
  if (!hasThemeNaming && !hasVariantPattern) {
259
292
  issues.push({
260
293
  rule: 'theme-naming-usage',
@@ -263,7 +296,7 @@ export const COMPONENT_RULES = {
263
296
  severity: COMPONENT_SEVERITY.INFO
264
297
  });
265
298
  }
266
-
299
+
267
300
  return issues;
268
301
  }
269
302
  }
@@ -339,10 +372,8 @@ export class ComponentValidator {
339
372
  const issues = rule.validate(content, componentName);
340
373
 
341
374
  if (issues.length > 0) {
342
- results.valid = false;
343
375
  results.issues.push(...issues);
344
-
345
- // Update summary
376
+
346
377
  for (const issue of issues) {
347
378
  if (issue.severity === COMPONENT_SEVERITY.ERROR) {
348
379
  results.summary.errors++;
@@ -363,6 +394,8 @@ export class ComponentValidator {
363
394
  }
364
395
  }
365
396
 
397
+ results.valid = results.summary.errors === 0;
398
+
366
399
  return results;
367
400
  }
368
401
 
@@ -7,6 +7,7 @@ import { readFile } from 'fs/promises';
7
7
  import { existsSync } from 'fs';
8
8
  import { join } from 'path';
9
9
  import { templateEngine, COMPLEXITY_LEVELS } from './template-engine.js';
10
+ import { resolveEffectiveComplexity } from './complexity-utils.js';
10
11
  import { detectFramework } from '../utils/detector.js';
11
12
  import { filesystem } from './filesystem.js';
12
13
  import { aiEngine } from './ai-engine.js';
@@ -21,6 +22,7 @@ import { tokenValidator } from './tokens/token-validator.js';
21
22
  import { componentValidator } from './component-validator.js';
22
23
  import { generateComponentStylesPackage } from './itcss-generator.js';
23
24
  import { generateHookFile } from './hook-generator.js';
25
+ import { configLoader } from './config-loader.js';
24
26
 
25
27
  export { COMPLEXITY_LEVELS };
26
28
 
@@ -33,8 +35,23 @@ export const COMPONENT_FEATURES = {
33
35
  ACCESSIBILITY: { name: 'accessibility', default: true }
34
36
  };
35
37
 
36
- // Global rate limiter for AI operations
37
- const aiRateLimiter = new RateLimiter(5, 60000); // 5 requests per minute
38
+ // Global rate limiter for AI operations - initialized with defaults, will be updated from config
39
+ let aiRateLimiter = new RateLimiter(5, 60000); // 5 requests per minute default
40
+
41
+ /**
42
+ * Initialize rate limiter from config
43
+ */
44
+ async function initializeRateLimiter() {
45
+ const config = configLoader.get('ai') || {};
46
+ const rateLimitConfig = config.rateLimit;
47
+
48
+ if (rateLimitConfig) {
49
+ aiRateLimiter = new RateLimiter(
50
+ rateLimitConfig.requests || 5,
51
+ rateLimitConfig.windowMs || 60000
52
+ );
53
+ }
54
+ }
38
55
 
39
56
  export const generator = {
40
57
  /**
@@ -47,9 +64,13 @@ export const generator = {
47
64
  async generateComponent(name, options = {}) {
48
65
  const {
49
66
  outputPath,
50
- complexity = 'medium',
67
+ complexity: complexityInput,
51
68
  features = [],
52
- logger
69
+ logger,
70
+ framework: frameworkOverride,
71
+ prefix: designTokenPrefix = 'atomix',
72
+ storybookCssImport,
73
+ hookOutputDir
53
74
  } = options;
54
75
 
55
76
  // Sanitize and validate component name
@@ -67,10 +88,12 @@ export const generator = {
67
88
  );
68
89
  }
69
90
 
91
+ const projectRoot = process.cwd();
92
+
70
93
  // Detect framework
71
94
  let framework;
72
95
  try {
73
- framework = await detectFramework();
96
+ framework = await detectFramework(projectRoot, { framework: frameworkOverride });
74
97
  if (logger) logger.debug(`Detected framework: ${framework}`);
75
98
  } catch (error) {
76
99
  throw new AtomixCLIError(
@@ -84,6 +107,8 @@ export const generator = {
84
107
  );
85
108
  }
86
109
 
110
+ const complexity = resolveEffectiveComplexity(framework, complexityInput);
111
+
87
112
  // Load design tokens if available
88
113
  let availableTokens = {};
89
114
  try {
@@ -94,7 +119,7 @@ export const generator = {
94
119
  ];
95
120
 
96
121
  for (const tokenPath of tokenPaths) {
97
- if (existsSync(join(process.cwd(), tokenPath))) {
122
+ if (existsSync(join(projectRoot, tokenPath))) {
98
123
  availableTokens = await tokenProvider.loadTokens(tokenPath);
99
124
  if (logger) logger.debug(`Loaded design tokens from ${tokenPath}`);
100
125
  break;
@@ -171,7 +196,9 @@ export const generator = {
171
196
  if (features.includes('storybook')) {
172
197
  try {
173
198
  const storyTemplateFn = templateEngine.selectTemplate(framework, complexity, 'story');
174
- const storyContent = templateEngine.render(storyTemplateFn, sanitizedName);
199
+ const storyContent = templateEngine.render(storyTemplateFn, sanitizedName, {
200
+ storybookCssImport
201
+ });
175
202
  await filesystem.writeFile(join(componentPath, `${sanitizedName}.stories.tsx`), storyContent, 'utf8');
176
203
  if (logger) logger.debug(`Created ${sanitizedName}.stories.tsx`);
177
204
  } catch (error) {
@@ -206,31 +233,12 @@ export const generator = {
206
233
  }
207
234
  }
208
235
 
209
- if (features.includes('hook') && framework !== 'vanilla') {
210
- try {
211
- const hookDir = join(outputPath, '..', 'lib', 'composables');
212
- const hookTemplateFn = templateEngine.selectTemplate(framework, complexity, 'hook');
213
- const hookContent = templateEngine.render(hookTemplateFn, sanitizedName);
214
- await filesystem.writeFile(join(hookDir, `use${sanitizedName}.ts`), hookContent, 'utf8');
215
- if (logger) logger.debug(`Created use${sanitizedName}.ts`);
216
- } catch (error) {
217
- throw new AtomixCLIError(
218
- `Failed to generate composable hook: ${error.message}`,
219
- 'HOOK_GENERATION_FAILED',
220
- [
221
- 'Check hook template exists',
222
- 'Verify hook feature is supported for this framework',
223
- 'Try generating without --hook flag'
224
- ]
225
- );
226
- }
227
- }
228
-
229
236
  // 4. Styles (ITCSS) - Enhanced with auto-generation
230
237
  if (features.includes('styles')) {
231
238
  try {
232
- const stylesResult = await generateComponentStylesPackage(sanitizedName, process.cwd(), {
233
- force: options.force || false
239
+ const stylesResult = await generateComponentStylesPackage(sanitizedName, projectRoot, {
240
+ force: options.force || false,
241
+ prefix: designTokenPrefix
234
242
  });
235
243
 
236
244
  if (logger && stylesResult.created.length > 0) {
@@ -249,11 +257,12 @@ export const generator = {
249
257
  }
250
258
  }
251
259
 
252
- // 5. Composable Hook - Enhanced generation
260
+ // 5. Composable hook (single path: lib/composables + barrel updates)
253
261
  if (features.includes('hook') && framework !== 'vanilla') {
254
262
  try {
255
- const hookResult = await generateHookFile(sanitizedName, process.cwd(), {
256
- force: options.force || false
263
+ const hookResult = await generateHookFile(sanitizedName, projectRoot, {
264
+ force: options.force || false,
265
+ outputDir: hookOutputDir || 'src/lib/composables'
257
266
  });
258
267
 
259
268
  if (logger && hookResult.created.length > 0) {
@@ -279,12 +288,15 @@ export const generator = {
279
288
  * Generates component files using AI based on a prompt
280
289
  * @param {string} name - Component name
281
290
  * @param {string} prompt - AI prompt
282
- * @param {Object} options - Generation options
291
+ * @param {Object} options - Generation options (aiProvider, aiModel, aiTemperature, aiMaxTokens, aiPreview)
283
292
  * @returns {Promise<string>} Path to generated component
284
293
  * @throws {AtomixCLIError} If generation fails
285
294
  */
286
295
  async generateAIComponent(name, prompt, options = {}) {
287
- const { outputPath, logger } = options;
296
+ const { outputPath, logger, aiProvider, aiModel, aiTemperature, aiMaxTokens, aiPreview } = options;
297
+
298
+ // Initialize rate limiter from config
299
+ await initializeRateLimiter();
288
300
 
289
301
  // Apply rate limiting for AI operations
290
302
  const userId = process.env.USER || 'anonymous';
@@ -319,10 +331,15 @@ export const generator = {
319
331
 
320
332
  const componentPath = join(outputPath, sanitizedName);
321
333
 
322
- // Call AI Engine
334
+ // Call AI Engine with override options
323
335
  let generated;
324
336
  try {
325
- generated = await aiEngine.generateComponent(name, prompt);
337
+ generated = await aiEngine.generateComponent(name, prompt, {
338
+ provider: aiProvider,
339
+ model: aiModel,
340
+ temperature: aiTemperature,
341
+ maxTokens: aiMaxTokens
342
+ });
326
343
  } catch (error) {
327
344
  throw new AtomixCLIError(
328
345
  `AI generation failed: ${error.message}`,
@@ -336,6 +353,42 @@ export const generator = {
336
353
  );
337
354
  }
338
355
 
356
+ // Preview mode: show output without writing files
357
+ if (aiPreview) {
358
+ logger.info('\n' + '='.repeat(60));
359
+ logger.info('📝 AI PREVIEW MODE - Generated content will not be saved');
360
+ logger.info('='.repeat(60));
361
+
362
+ logger.info('\n📄 Component File:');
363
+ logger.info(generated.component || 'No component generated');
364
+
365
+ if (generated.styles) {
366
+ logger.info('\n🎨 Styles File:');
367
+ logger.info(generated.styles);
368
+ }
369
+
370
+ if (generated.tests) {
371
+ logger.info('\n🧪 Test File:');
372
+ logger.info(generated.tests);
373
+ }
374
+
375
+ if (generated.stories) {
376
+ logger.info('\n📚 Stories File:');
377
+ logger.info(generated.stories);
378
+ }
379
+
380
+ if (generated.readme) {
381
+ logger.info('\n📖 README:');
382
+ logger.info(generated.readme);
383
+ }
384
+
385
+ logger.info('\n' + '='.repeat(60));
386
+ logger.info('Preview complete. Run without --ai-preview to save files.');
387
+ logger.info('='.repeat(60));
388
+
389
+ return componentPath; // Return path without writing
390
+ }
391
+
339
392
  // Write component file
340
393
  try {
341
394
  await filesystem.writeFile(join(componentPath, `${sanitizedName}.tsx`), generated.component, 'utf8');
@@ -334,8 +334,11 @@ export async function generateHookFile(componentName, projectRoot, options = {})
334
334
  await filesystem.writeFile(indexFile, updatedContent, 'utf8');
335
335
  }
336
336
  } else {
337
- // Create new index
338
- const indexContent = generateComposablesIndex();
337
+ const indexContent = `/**
338
+ * Composable hooks (Atomix CLI)
339
+ */
340
+ export { ${hookName} } from './${hookName}';
341
+ `;
339
342
  await filesystem.writeFile(indexFile, indexContent, 'utf8');
340
343
  created.push(`${outputDir}/index.ts`);
341
344
  }
@@ -63,10 +63,11 @@ export const ITCSS_LAYERS = {
63
63
  * Generate SCSS settings file for a component
64
64
  */
65
65
  export function generateSettingsFile(componentName, options = {}) {
66
+ const dsPrefix = options.prefix || 'atomix';
66
67
  const {
67
- primaryColor = '--atomix-color-primary',
68
- spacingBase = '--atomix-spacing-4',
69
- radiusBase = '--atomix-radius-md'
68
+ primaryColor = `--${dsPrefix}-color-primary`,
69
+ spacingBase = `--${dsPrefix}-spacing-4`,
70
+ radiusBase = `--${dsPrefix}-radius-md`
70
71
  } = options;
71
72
 
72
73
  const componentPrefix = componentName.toLowerCase().replace(/([A-Z])/g, '-$1').replace(/^-/, '');
@@ -97,19 +98,19 @@ export function generateSettingsFile(componentName, options = {}) {
97
98
  --${componentPrefix}-radius: var(${radiusBase});
98
99
 
99
100
  // Typography
100
- --${componentPrefix}-font-size: var(--atomix-font-size-base);
101
- --${componentPrefix}-font-weight: var(--atomix-font-weight-medium);
102
- --${componentPrefix}-line-height: var(--atomix-line-height-tight);
101
+ --${componentPrefix}-font-size: var(--${dsPrefix}-font-size-base);
102
+ --${componentPrefix}-font-weight: var(--${dsPrefix}-font-weight-medium);
103
+ --${componentPrefix}-line-height: var(--${dsPrefix}-line-height-tight);
103
104
 
104
105
  // States
105
106
  --${componentPrefix}-disabled-opacity: 0.5;
106
- --${componentPrefix}-focus-ring: 0 0 0 2px var(--atomix-color-primary-200);
107
+ --${componentPrefix}-focus-ring: 0 0 0 2px var(--${dsPrefix}-color-primary-200);
107
108
  }
108
109
 
109
110
  // Dark mode overrides
110
111
  .dark {
111
112
  :root {
112
- --${componentPrefix}-focus-ring: 0 0 0 2px var(--atomix-color-primary-800);
113
+ --${componentPrefix}-focus-ring: 0 0 0 2px var(--${dsPrefix}-color-primary-800);
113
114
  }
114
115
  }
115
116
 
@@ -131,6 +132,7 @@ $${componentPrefix}-config: (
131
132
  * Generate SCSS component styles file
132
133
  */
133
134
  export function generateComponentStyles(componentName, options = {}) {
135
+ const dsPrefix = options.prefix || 'atomix';
134
136
  const {
135
137
  baseClass = `c-${componentName.toLowerCase().replace(/([A-Z])/g, '-$1').replace(/^-/, '')}`,
136
138
  hasVariants = true,
@@ -141,7 +143,7 @@ export function generateComponentStyles(componentName, options = {}) {
141
143
 
142
144
  const componentPrefix = componentName.toLowerCase().replace(/([A-Z])/g, '-$1').replace(/^-/, '');
143
145
 
144
- return `// ${componentName} Component Styles
146
+ const scss = `// ${componentName} Component Styles
145
147
  // Generated by Atomix CLI
146
148
  // ITCSS Layer: Components
147
149
  // =============================================================================
@@ -354,6 +356,7 @@ export function generateComponentStyles(componentName, options = {}) {
354
356
  }
355
357
  }
356
358
  `;
359
+ return scss.replaceAll('--atomix-', `--${dsPrefix}-`);
357
360
  }
358
361
 
359
362
  /**
@@ -483,7 +486,8 @@ export async function generateComponentStylesPackage(componentName, projectRoot,
483
486
  const {
484
487
  skipSettings = false,
485
488
  skipComponents = false,
486
- force = false
489
+ force = false,
490
+ prefix = 'atomix'
487
491
  } = options;
488
492
 
489
493
  const created = [];
@@ -498,7 +502,7 @@ export async function generateComponentStylesPackage(componentName, projectRoot,
498
502
  const settingsFile = join(settingsPath, `_settings.${componentPrefix}.scss`);
499
503
 
500
504
  if (force || !existsSync(settingsFile)) {
501
- const settingsContent = generateSettingsFile(componentName);
505
+ const settingsContent = generateSettingsFile(componentName, { prefix });
502
506
  await filesystem.writeFile(settingsFile, settingsContent, 'utf8');
503
507
  created.push(`src/styles/01-settings/_settings.${componentPrefix}.scss`);
504
508
  logger.debug(`Created settings file: ${settingsFile}`);
@@ -513,7 +517,7 @@ export async function generateComponentStylesPackage(componentName, projectRoot,
513
517
  const componentFile = join(componentsPath, `_components.${componentPrefix}.scss`);
514
518
 
515
519
  if (force || !existsSync(componentFile)) {
516
- const componentContent = generateComponentStyles(componentName);
520
+ const componentContent = generateComponentStyles(componentName, { prefix });
517
521
  await filesystem.writeFile(componentFile, componentContent, 'utf8');
518
522
  created.push(`src/styles/06-components/_components.${componentPrefix}.scss`);
519
523
  logger.debug(`Created component styles: ${componentFile}`);
@@ -3,22 +3,40 @@
3
3
  * Optimized for Server Components and App Router
4
4
  */
5
5
 
6
+ /**
7
+ * Simple Server Component — valid displayName, Props, JSDoc, a11y; no forwardRef (server).
8
+ */
6
9
  export const nextTemplates = {
7
- /**
8
- * Simple Server Component
9
- */
10
- simple: (name) => `/**
11
- * ${name} Component (Server Component)
10
+ simple: (name) => `import React from 'react';
11
+
12
+ export interface ${name}Props {
13
+ /** Optional heading override */
14
+ title?: string;
15
+ children?: React.ReactNode;
16
+ }
17
+
18
+ /**
19
+ * ${name} — presentational server component (Atomix scaffold).
20
+ *
21
+ * @param props - ${name} properties
12
22
  */
13
- import React from 'react';
23
+ export default function ${name}(props: ${name}Props) {
24
+ const { title = '${name}', children } = props;
14
25
 
15
- export default function ${name}() {
16
26
  return (
17
- <div className="${name.toLowerCase()}">
18
- <h1>${name} Component</h1>
19
- </div>
27
+ <section
28
+ className={\`c-${name.toLowerCase()}\`}
29
+ role="region"
30
+ aria-label={title}
31
+ data-testid="${name.toLowerCase()}-root"
32
+ >
33
+ <h1 className={\`c-${name.toLowerCase()}__title\`}>{title}</h1>
34
+ {children}
35
+ </section>
20
36
  );
21
37
  }
38
+
39
+ ${name}.displayName = '${name}';
22
40
  `,
23
41
 
24
42
  /**
@@ -26,47 +44,80 @@ export default function ${name}() {
26
44
  */
27
45
  client: (name) => `'use client';
28
46
 
47
+ import React, { forwardRef, useState } from 'react';
48
+
49
+ export interface ${name}Props {
50
+ title?: string;
51
+ children?: React.ReactNode;
52
+ className?: string;
53
+ }
54
+
29
55
  /**
30
- * ${name} Component (Client Component)
56
+ * ${name} client component with local state (Atomix scaffold).
31
57
  */
32
- import React, { useState } from 'react';
33
-
34
- export default function ${name}() {
58
+ const ${name}Inner = forwardRef<HTMLDivElement, ${name}Props>(function ${name}Inner(
59
+ { title = '${name}', children, className = '' },
60
+ ref
61
+ ) {
35
62
  const [active, setActive] = useState(false);
36
63
 
37
64
  return (
38
- <div className="${name.toLowerCase()}">
39
- <h1>${name} Component</h1>
40
- <button onClick={() => setActive(!active)}>
65
+ <section
66
+ ref={ref}
67
+ className={[\`c-${name.toLowerCase()}\`, className].filter(Boolean).join(' ')}
68
+ role="region"
69
+ aria-label={title}
70
+ data-testid="${name.toLowerCase()}-root"
71
+ >
72
+ <h1 className={\`c-${name.toLowerCase()}__title\`}>{title}</h1>
73
+ {children}
74
+ <button type="button" onClick={() => setActive(!active)} aria-pressed={active}>
41
75
  Toggle: {active ? 'On' : 'Off'}
42
76
  </button>
43
- </div>
77
+ </section>
44
78
  );
45
- }
79
+ });
80
+
81
+ ${name}Inner.displayName = '${name}';
82
+
83
+ export default ${name}Inner;
46
84
  `,
47
85
 
48
86
  /**
49
87
  * Complex Server Component with async data
50
88
  */
51
- complex: (name) => `/**
52
- * ${name} Component (Server Component with Data)
53
- */
54
- import React from 'react';
89
+ complex: (name) => `import React from 'react';
55
90
 
56
- async function getData() {
57
- // Simulating data fetch
91
+ export interface ${name}Props {
92
+ title?: string;
93
+ }
94
+
95
+ async function getData(): Promise<{ title: string }> {
58
96
  return { title: '${name}' };
59
97
  }
60
98
 
61
- export default async function ${name}() {
99
+ /**
100
+ * ${name} — async server component (Atomix scaffold).
101
+ */
102
+ async function ${name}(props: ${name}Props) {
62
103
  const data = await getData();
104
+ const title = props.title ?? data.title;
63
105
 
64
106
  return (
65
- <div className="${name.toLowerCase()}">
66
- <h1>{data.title}</h1>
67
- <p>This is a complex Next.js server component.</p>
68
- </div>
107
+ <section
108
+ className={\`c-${name.toLowerCase()}\`}
109
+ role="region"
110
+ aria-label={title}
111
+ data-testid="${name.toLowerCase()}-root"
112
+ >
113
+ <h1>{title}</h1>
114
+ <p>This is a complex Next.js server component scaffold.</p>
115
+ </section>
69
116
  );
70
117
  }
118
+
119
+ ${name}.displayName = '${name}';
120
+
121
+ export default ${name};
71
122
  `
72
123
  };
@@ -5,8 +5,13 @@
5
5
 
6
6
  /**
7
7
  * Basic Storybook story template
8
+ * @param {string} name - Component name
9
+ * @param {{ storybookCssImport?: string }} [opts]
8
10
  */
9
- export const basicStoryTemplate = (name) => `import type { Meta, StoryObj } from '@storybook/react';
11
+ export const basicStoryTemplate = (name, opts = {}) => {
12
+ const cssImport = opts.storybookCssImport || '@shohojdhara/atomix/scss';
13
+ return `import '${cssImport}';
14
+ import type { Meta, StoryObj } from '@storybook/react';
10
15
  import { ${name} } from './${name}';
11
16
 
12
17
  const meta: Meta<typeof ${name}> = {
@@ -66,11 +71,15 @@ export const Glass: Story = {
66
71
  },
67
72
  };
68
73
  `;
74
+ };
69
75
 
70
76
  /**
71
77
  * Enhanced Storybook story template with detailed documentation
72
78
  */
73
- export const enhancedStoryTemplate = (name) => `import type { Meta, StoryObj } from '@storybook/react';
79
+ export const enhancedStoryTemplate = (name, opts = {}) => {
80
+ const cssImport = opts.storybookCssImport || '@shohojdhara/atomix/scss';
81
+ return `import '${cssImport}';
82
+ import type { Meta, StoryObj } from '@storybook/react';
74
83
  import { ${name} } from './${name}';
75
84
 
76
85
  const meta: Meta<typeof ${name}> = {
@@ -226,6 +235,7 @@ export const CustomContent: Story = {
226
235
  },
227
236
  };
228
237
  `;
238
+ };
229
239
 
230
240
  /**
231
241
  * Storybook templates object