autoworkflow 3.1.5 → 3.6.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.
Files changed (124) hide show
  1. package/.claude/commands/analyze.md +19 -0
  2. package/.claude/commands/audit.md +26 -0
  3. package/.claude/commands/build.md +39 -0
  4. package/.claude/commands/commit.md +25 -0
  5. package/.claude/commands/fix.md +23 -0
  6. package/.claude/commands/plan.md +18 -0
  7. package/.claude/commands/suggest.md +23 -0
  8. package/.claude/commands/verify.md +18 -0
  9. package/.claude/hooks/post-bash-router.sh +20 -0
  10. package/.claude/hooks/post-commit.sh +140 -0
  11. package/.claude/hooks/post-edit.sh +190 -17
  12. package/.claude/hooks/pre-edit.sh +221 -0
  13. package/.claude/hooks/session-check.sh +90 -0
  14. package/.claude/settings.json +56 -6
  15. package/.claude/settings.local.json +5 -1
  16. package/.claude/skills/actix.md +337 -0
  17. package/.claude/skills/alembic.md +504 -0
  18. package/.claude/skills/angular.md +237 -0
  19. package/.claude/skills/api-design.md +187 -0
  20. package/.claude/skills/aspnet-core.md +377 -0
  21. package/.claude/skills/astro.md +245 -0
  22. package/.claude/skills/auth-clerk.md +327 -0
  23. package/.claude/skills/auth-firebase.md +367 -0
  24. package/.claude/skills/auth-nextauth.md +359 -0
  25. package/.claude/skills/auth-supabase.md +368 -0
  26. package/.claude/skills/axum.md +386 -0
  27. package/.claude/skills/blazor.md +456 -0
  28. package/.claude/skills/chi.md +348 -0
  29. package/.claude/skills/code-review.md +133 -0
  30. package/.claude/skills/csharp.md +296 -0
  31. package/.claude/skills/css-modules.md +325 -0
  32. package/.claude/skills/cypress.md +343 -0
  33. package/.claude/skills/debugging.md +133 -0
  34. package/.claude/skills/diesel.md +392 -0
  35. package/.claude/skills/django.md +301 -0
  36. package/.claude/skills/docker.md +319 -0
  37. package/.claude/skills/doctrine.md +473 -0
  38. package/.claude/skills/documentation.md +182 -0
  39. package/.claude/skills/dotnet.md +409 -0
  40. package/.claude/skills/drizzle.md +293 -0
  41. package/.claude/skills/echo.md +321 -0
  42. package/.claude/skills/eloquent.md +256 -0
  43. package/.claude/skills/emotion.md +426 -0
  44. package/.claude/skills/entity-framework.md +370 -0
  45. package/.claude/skills/express.md +316 -0
  46. package/.claude/skills/fastapi.md +329 -0
  47. package/.claude/skills/fastify.md +299 -0
  48. package/.claude/skills/fiber.md +315 -0
  49. package/.claude/skills/flask.md +322 -0
  50. package/.claude/skills/gin.md +342 -0
  51. package/.claude/skills/git.md +116 -0
  52. package/.claude/skills/github-actions.md +353 -0
  53. package/.claude/skills/go.md +377 -0
  54. package/.claude/skills/gorm.md +409 -0
  55. package/.claude/skills/graphql.md +478 -0
  56. package/.claude/skills/hibernate.md +379 -0
  57. package/.claude/skills/hono.md +306 -0
  58. package/.claude/skills/java.md +400 -0
  59. package/.claude/skills/jest.md +313 -0
  60. package/.claude/skills/jpa.md +282 -0
  61. package/.claude/skills/kotlin.md +347 -0
  62. package/.claude/skills/kubernetes.md +363 -0
  63. package/.claude/skills/laravel.md +414 -0
  64. package/.claude/skills/mcp-browser.md +320 -0
  65. package/.claude/skills/mcp-database.md +219 -0
  66. package/.claude/skills/mcp-fetch.md +241 -0
  67. package/.claude/skills/mcp-filesystem.md +204 -0
  68. package/.claude/skills/mcp-github.md +217 -0
  69. package/.claude/skills/mcp-memory.md +240 -0
  70. package/.claude/skills/mcp-search.md +218 -0
  71. package/.claude/skills/mcp-slack.md +262 -0
  72. package/.claude/skills/micronaut.md +388 -0
  73. package/.claude/skills/mongodb.md +319 -0
  74. package/.claude/skills/mongoose.md +355 -0
  75. package/.claude/skills/mysql.md +281 -0
  76. package/.claude/skills/nestjs.md +335 -0
  77. package/.claude/skills/nextjs-app-router.md +260 -0
  78. package/.claude/skills/nextjs-pages.md +172 -0
  79. package/.claude/skills/nuxt.md +202 -0
  80. package/.claude/skills/openapi.md +489 -0
  81. package/.claude/skills/performance.md +199 -0
  82. package/.claude/skills/php.md +398 -0
  83. package/.claude/skills/playwright.md +371 -0
  84. package/.claude/skills/postgresql.md +257 -0
  85. package/.claude/skills/prisma.md +293 -0
  86. package/.claude/skills/pydantic.md +304 -0
  87. package/.claude/skills/pytest.md +313 -0
  88. package/.claude/skills/python.md +272 -0
  89. package/.claude/skills/quarkus.md +377 -0
  90. package/.claude/skills/react.md +230 -0
  91. package/.claude/skills/redis.md +391 -0
  92. package/.claude/skills/refactoring.md +143 -0
  93. package/.claude/skills/remix.md +246 -0
  94. package/.claude/skills/rest-api.md +490 -0
  95. package/.claude/skills/rocket.md +366 -0
  96. package/.claude/skills/rust.md +341 -0
  97. package/.claude/skills/sass.md +380 -0
  98. package/.claude/skills/sea-orm.md +382 -0
  99. package/.claude/skills/security.md +167 -0
  100. package/.claude/skills/sequelize.md +395 -0
  101. package/.claude/skills/spring-boot.md +416 -0
  102. package/.claude/skills/sqlalchemy.md +269 -0
  103. package/.claude/skills/sqlx-rust.md +408 -0
  104. package/.claude/skills/state-jotai.md +346 -0
  105. package/.claude/skills/state-mobx.md +353 -0
  106. package/.claude/skills/state-pinia.md +431 -0
  107. package/.claude/skills/state-redux.md +337 -0
  108. package/.claude/skills/state-tanstack-query.md +434 -0
  109. package/.claude/skills/state-zustand.md +340 -0
  110. package/.claude/skills/styled-components.md +403 -0
  111. package/.claude/skills/svelte.md +238 -0
  112. package/.claude/skills/sveltekit.md +207 -0
  113. package/.claude/skills/symfony.md +437 -0
  114. package/.claude/skills/tailwind.md +279 -0
  115. package/.claude/skills/terraform.md +394 -0
  116. package/.claude/skills/testing-library.md +371 -0
  117. package/.claude/skills/trpc.md +426 -0
  118. package/.claude/skills/typeorm.md +368 -0
  119. package/.claude/skills/vitest.md +330 -0
  120. package/.claude/skills/vue.md +202 -0
  121. package/.claude/skills/warp.md +365 -0
  122. package/README.md +163 -52
  123. package/package.json +1 -1
  124. package/system/triggers.md +256 -17
@@ -0,0 +1,296 @@
1
+ # C# Skill
2
+
3
+ ## Records (Immutable Data Types)
4
+ \`\`\`csharp
5
+ // Record with positional syntax
6
+ public record User(string Id, string Email, string Name, bool IsActive = true);
7
+
8
+ // Record with properties
9
+ public record UserResponse
10
+ {
11
+ public required string Id { get; init; }
12
+ public required string Email { get; init; }
13
+ public required string Name { get; init; }
14
+ public bool IsActive { get; init; } = true;
15
+ }
16
+
17
+ // With-expressions for copying
18
+ var updatedUser = user with { Name = "New Name", IsActive = false };
19
+
20
+ // Deconstruction
21
+ var (id, email, name, _) = user;
22
+
23
+ // Record struct (value type, no heap allocation)
24
+ public readonly record struct Point(int X, int Y);
25
+ \`\`\`
26
+
27
+ ## Nullable Reference Types
28
+ \`\`\`csharp
29
+ // Enable in .csproj: <Nullable>enable</Nullable>
30
+
31
+ // Non-nullable by default
32
+ public string Name { get; set; } // Cannot be null
33
+
34
+ // Explicitly nullable
35
+ public string? MiddleName { get; set; } // Can be null
36
+
37
+ // Null-conditional and null-coalescing
38
+ var displayName = user?.Name ?? "Anonymous";
39
+ var length = user?.Name?.Length ?? 0;
40
+
41
+ // Null-coalescing assignment
42
+ user.Name ??= "Default Name";
43
+
44
+ // Null-forgiving operator (use sparingly)
45
+ var name = user!.Name; // Tell compiler this won't be null
46
+
47
+ // Pattern matching for null checks
48
+ if (user is { Name: not null } u)
49
+ {
50
+ Console.WriteLine(u.Name);
51
+ }
52
+
53
+ // Required members (C# 11+)
54
+ public class User
55
+ {
56
+ public required string Id { get; init; }
57
+ public required string Email { get; init; }
58
+ public string? Name { get; init; }
59
+ }
60
+ \`\`\`
61
+
62
+ ## Pattern Matching
63
+ \`\`\`csharp
64
+ // Type patterns
65
+ object obj = GetValue();
66
+ if (obj is string s)
67
+ {
68
+ Console.WriteLine(s.Length);
69
+ }
70
+
71
+ // Switch expressions
72
+ string GetStatusMessage(UserStatus status) => status switch
73
+ {
74
+ UserStatus.Active => "User is active",
75
+ UserStatus.Pending => "Awaiting verification",
76
+ UserStatus.Suspended => "Account suspended",
77
+ UserStatus.Deleted => "Account deleted",
78
+ _ => throw new ArgumentOutOfRangeException(nameof(status))
79
+ };
80
+
81
+ // Property patterns
82
+ string Describe(User user) => user switch
83
+ {
84
+ { IsActive: true, Role: "Admin" } => "Active admin",
85
+ { IsActive: true } => "Active user",
86
+ { IsActive: false } => "Inactive user",
87
+ null => throw new ArgumentNullException(nameof(user))
88
+ };
89
+
90
+ // Relational and logical patterns
91
+ string GetPriceCategory(decimal price) => price switch
92
+ {
93
+ < 10 => "Cheap",
94
+ >= 10 and < 50 => "Moderate",
95
+ >= 50 and < 100 => "Expensive",
96
+ >= 100 => "Premium",
97
+ };
98
+
99
+ // List patterns (C# 11+)
100
+ int[] numbers = { 1, 2, 3, 4, 5 };
101
+ var result = numbers switch
102
+ {
103
+ [1, 2, ..] => "Starts with 1, 2",
104
+ [.., 4, 5] => "Ends with 4, 5",
105
+ [_, _, _, ..] => "Has at least 3 elements",
106
+ [] => "Empty",
107
+ _ => "Other"
108
+ };
109
+ \`\`\`
110
+
111
+ ## LINQ
112
+ \`\`\`csharp
113
+ // Query syntax
114
+ var activeUsers = from user in users
115
+ where user.IsActive
116
+ orderby user.CreatedAt descending
117
+ select new { user.Id, user.Email };
118
+
119
+ // Method syntax (more common)
120
+ var result = users
121
+ .Where(u => u.IsActive)
122
+ .OrderByDescending(u => u.CreatedAt)
123
+ .Select(u => new { u.Id, u.Email })
124
+ .ToList();
125
+
126
+ // Common operations
127
+ var first = users.FirstOrDefault(u => u.Email == email);
128
+ var any = users.Any(u => u.IsActive);
129
+ var all = users.All(u => u.Email.Contains("@"));
130
+ var count = users.Count(u => u.IsActive);
131
+
132
+ // Grouping
133
+ var byDomain = users
134
+ .GroupBy(u => u.Email.Split('@')[1])
135
+ .Select(g => new { Domain = g.Key, Count = g.Count() });
136
+
137
+ // Join
138
+ var usersWithPosts = users.Join(
139
+ posts,
140
+ u => u.Id,
141
+ p => p.AuthorId,
142
+ (user, post) => new { User = user, Post = post }
143
+ );
144
+
145
+ // Aggregation
146
+ var stats = users
147
+ .GroupBy(u => u.Department)
148
+ .Select(g => new
149
+ {
150
+ Department = g.Key,
151
+ Count = g.Count(),
152
+ AvgSalary = g.Average(u => u.Salary),
153
+ MaxSalary = g.Max(u => u.Salary)
154
+ });
155
+
156
+ // SelectMany for flattening
157
+ var allTags = posts
158
+ .SelectMany(p => p.Tags)
159
+ .Distinct()
160
+ .ToList();
161
+
162
+ // Chunk (C# 11+ / .NET 6+)
163
+ var batches = users.Chunk(100);
164
+ foreach (var batch in batches)
165
+ {
166
+ await ProcessBatch(batch);
167
+ }
168
+ \`\`\`
169
+
170
+ ## Async/Await
171
+ \`\`\`csharp
172
+ // Async method
173
+ public async Task<User?> GetUserAsync(string id, CancellationToken ct = default)
174
+ {
175
+ return await _context.Users
176
+ .FirstOrDefaultAsync(u => u.Id == id, ct);
177
+ }
178
+
179
+ // Multiple async operations
180
+ public async Task<UserWithPosts> GetUserWithPostsAsync(string userId)
181
+ {
182
+ var userTask = GetUserAsync(userId);
183
+ var postsTask = GetPostsAsync(userId);
184
+
185
+ await Task.WhenAll(userTask, postsTask);
186
+
187
+ return new UserWithPosts(userTask.Result!, postsTask.Result);
188
+ }
189
+
190
+ // Async streams
191
+ public async IAsyncEnumerable<User> GetUsersStreamAsync(
192
+ [EnumeratorCancellation] CancellationToken ct = default)
193
+ {
194
+ await foreach (var user in _context.Users.AsAsyncEnumerable().WithCancellation(ct))
195
+ {
196
+ yield return user;
197
+ }
198
+ }
199
+
200
+ // ValueTask for high-performance scenarios
201
+ public ValueTask<User?> GetCachedUserAsync(string id)
202
+ {
203
+ if (_cache.TryGetValue(id, out var user))
204
+ {
205
+ return ValueTask.FromResult<User?>(user);
206
+ }
207
+ return new ValueTask<User?>(FetchUserAsync(id));
208
+ }
209
+
210
+ // ConfigureAwait for library code
211
+ public async Task<string> LibraryMethodAsync()
212
+ {
213
+ var result = await SomeOperationAsync().ConfigureAwait(false);
214
+ return result.ToString();
215
+ }
216
+ \`\`\`
217
+
218
+ ## Collections
219
+ \`\`\`csharp
220
+ // Immutable collections
221
+ using System.Collections.Immutable;
222
+
223
+ ImmutableList<string> names = ImmutableList.Create("Alice", "Bob");
224
+ var newNames = names.Add("Charlie"); // Returns new list
225
+
226
+ // Collection expressions (C# 12+)
227
+ List<int> numbers = [1, 2, 3, 4, 5];
228
+ int[] array = [1, 2, 3];
229
+ Span<int> span = [1, 2, 3];
230
+
231
+ // Spread operator
232
+ int[] combined = [..array1, ..array2];
233
+
234
+ // Dictionary patterns
235
+ var userById = users.ToDictionary(u => u.Id);
236
+ var count = userById.GetValueOrDefault(id)?.Posts.Count ?? 0;
237
+
238
+ // Concurrent collections
239
+ var cache = new ConcurrentDictionary<string, User>();
240
+ var user = cache.GetOrAdd(id, _ => FetchUser(id));
241
+ \`\`\`
242
+
243
+ ## Exception Handling
244
+ \`\`\`csharp
245
+ // Custom exception
246
+ public class NotFoundException : Exception
247
+ {
248
+ public string ResourceType { get; }
249
+ public string ResourceId { get; }
250
+
251
+ public NotFoundException(string resourceType, string resourceId)
252
+ : base($"{resourceType} with id '{resourceId}' not found")
253
+ {
254
+ ResourceType = resourceType;
255
+ ResourceId = resourceId;
256
+ }
257
+ }
258
+
259
+ // Try pattern
260
+ public bool TryGetUser(string id, out User? user)
261
+ {
262
+ user = _users.FirstOrDefault(u => u.Id == id);
263
+ return user is not null;
264
+ }
265
+
266
+ // Exception filters
267
+ try
268
+ {
269
+ await httpClient.GetAsync(url);
270
+ }
271
+ catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
272
+ {
273
+ return null;
274
+ }
275
+ catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.Unauthorized)
276
+ {
277
+ throw new AuthenticationException("Invalid credentials", ex);
278
+ }
279
+ \`\`\`
280
+
281
+ ## ✅ DO
282
+ - Use records for immutable data (DTOs, value objects)
283
+ - Enable nullable reference types
284
+ - Use pattern matching for type checks and deconstruction
285
+ - Use \`async/await\` throughout - avoid \`.Result\` and \`.Wait()\`
286
+ - Pass \`CancellationToken\` to async methods
287
+ - Use collection expressions (C# 12+)
288
+ - Use \`required\` for mandatory properties
289
+
290
+ ## ❌ DON'T
291
+ - Don't use \`async void\` except for event handlers
292
+ - Don't ignore \`CancellationToken\`
293
+ - Don't use \`?.Result\` on tasks (can deadlock)
294
+ - Don't catch \`Exception\` without rethrowing or logging
295
+ - Don't use \`null!\` to silence nullable warnings
296
+ - Don't use mutable collections when immutable suffice
@@ -0,0 +1,325 @@
1
+ # CSS Modules Skill
2
+
3
+ ## Basic Usage
4
+ \`\`\`tsx
5
+ // Button.module.css
6
+ .button {
7
+ padding: 0.5rem 1rem;
8
+ border: none;
9
+ border-radius: 0.375rem;
10
+ cursor: pointer;
11
+ transition: background 0.2s;
12
+ }
13
+
14
+ .primary {
15
+ background: #3b82f6;
16
+ color: white;
17
+ }
18
+
19
+ .primary:hover {
20
+ background: #2563eb;
21
+ }
22
+
23
+ .secondary {
24
+ background: #6b7280;
25
+ color: white;
26
+ }
27
+
28
+ // Button.tsx
29
+ import styles from './Button.module.css';
30
+
31
+ interface ButtonProps {
32
+ variant?: 'primary' | 'secondary';
33
+ children: React.ReactNode;
34
+ }
35
+
36
+ export function Button({ variant = 'primary', children }: ButtonProps) {
37
+ return (
38
+ <button className={\`\${styles.button} \${styles[variant]}\`}>
39
+ {children}
40
+ </button>
41
+ );
42
+ }
43
+ \`\`\`
44
+
45
+ ## Composition (composes)
46
+ \`\`\`css
47
+ /* shared.module.css */
48
+ .flexCenter {
49
+ display: flex;
50
+ align-items: center;
51
+ justify-content: center;
52
+ }
53
+
54
+ .truncate {
55
+ overflow: hidden;
56
+ text-overflow: ellipsis;
57
+ white-space: nowrap;
58
+ }
59
+
60
+ /* Card.module.css */
61
+ .card {
62
+ composes: flexCenter from './shared.module.css';
63
+ padding: 1.5rem;
64
+ border-radius: 0.5rem;
65
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
66
+ }
67
+
68
+ .title {
69
+ composes: truncate from './shared.module.css';
70
+ font-size: 1.25rem;
71
+ font-weight: 600;
72
+ }
73
+
74
+ /* Multiple compositions */
75
+ .header {
76
+ composes: flexCenter truncate from './shared.module.css';
77
+ }
78
+ \`\`\`
79
+
80
+ ## Global Styles
81
+ \`\`\`css
82
+ /* Component.module.css */
83
+
84
+ /* Local class (default) */
85
+ .container {
86
+ padding: 1rem;
87
+ }
88
+
89
+ /* Global class within module */
90
+ :global(.external-library-class) {
91
+ color: red;
92
+ }
93
+
94
+ /* Mix local and global */
95
+ .container :global(.markdown-body) {
96
+ line-height: 1.6;
97
+ }
98
+
99
+ /* Global block */
100
+ :global {
101
+ .some-global-class {
102
+ color: blue;
103
+ }
104
+
105
+ body.dark-mode {
106
+ background: #1a1a1a;
107
+ }
108
+ }
109
+ \`\`\`
110
+
111
+ ## Combining Classes
112
+ \`\`\`tsx
113
+ import styles from './Button.module.css';
114
+ import clsx from 'clsx'; // or classnames library
115
+
116
+ interface ButtonProps {
117
+ variant?: 'primary' | 'secondary';
118
+ size?: 'sm' | 'md' | 'lg';
119
+ disabled?: boolean;
120
+ fullWidth?: boolean;
121
+ className?: string;
122
+ children: React.ReactNode;
123
+ }
124
+
125
+ export function Button({
126
+ variant = 'primary',
127
+ size = 'md',
128
+ disabled,
129
+ fullWidth,
130
+ className,
131
+ children,
132
+ }: ButtonProps) {
133
+ return (
134
+ <button
135
+ className={clsx(
136
+ styles.button,
137
+ styles[variant],
138
+ styles[size],
139
+ {
140
+ [styles.disabled]: disabled,
141
+ [styles.fullWidth]: fullWidth,
142
+ },
143
+ className // Allow external classes
144
+ )}
145
+ disabled={disabled}
146
+ >
147
+ {children}
148
+ </button>
149
+ );
150
+ }
151
+
152
+ // Button.module.css
153
+ .button { /* base styles */ }
154
+ .primary { /* primary variant */ }
155
+ .secondary { /* secondary variant */ }
156
+ .sm { padding: 0.25rem 0.5rem; font-size: 0.875rem; }
157
+ .md { padding: 0.5rem 1rem; }
158
+ .lg { padding: 0.75rem 1.5rem; font-size: 1.125rem; }
159
+ .disabled { opacity: 0.5; cursor: not-allowed; }
160
+ .fullWidth { width: 100%; }
161
+ \`\`\`
162
+
163
+ ## CSS Variables Integration
164
+ \`\`\`css
165
+ /* variables.css (global) */
166
+ :root {
167
+ --color-primary: #3b82f6;
168
+ --color-secondary: #6b7280;
169
+ --color-danger: #ef4444;
170
+ --spacing-sm: 0.5rem;
171
+ --spacing-md: 1rem;
172
+ --spacing-lg: 1.5rem;
173
+ --border-radius: 0.375rem;
174
+ --shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
175
+ }
176
+
177
+ [data-theme='dark'] {
178
+ --color-background: #1a1a1a;
179
+ --color-text: #ffffff;
180
+ }
181
+
182
+ /* Button.module.css */
183
+ .button {
184
+ padding: var(--spacing-sm) var(--spacing-md);
185
+ border-radius: var(--border-radius);
186
+ box-shadow: var(--shadow);
187
+ }
188
+
189
+ .primary {
190
+ background: var(--color-primary);
191
+ }
192
+ \`\`\`
193
+
194
+ ## TypeScript Support
195
+ \`\`\`tsx
196
+ // For TypeScript, you need type declarations
197
+
198
+ // Option 1: Global declaration (global.d.ts)
199
+ declare module '*.module.css' {
200
+ const classes: { [key: string]: string };
201
+ export default classes;
202
+ }
203
+
204
+ declare module '*.module.scss' {
205
+ const classes: { [key: string]: string };
206
+ export default classes;
207
+ }
208
+
209
+ // Option 2: Use typed-css-modules or css-modules-typescript-loader
210
+ // This generates .d.ts files for each module
211
+
212
+ // Button.module.css.d.ts (auto-generated)
213
+ declare const styles: {
214
+ readonly button: string;
215
+ readonly primary: string;
216
+ readonly secondary: string;
217
+ };
218
+ export default styles;
219
+
220
+ // Now TypeScript knows the available classes
221
+ import styles from './Button.module.css';
222
+ styles.button; // OK
223
+ styles.primary; // OK
224
+ styles.invalid; // TypeScript error!
225
+ \`\`\`
226
+
227
+ ## With Sass/SCSS
228
+ \`\`\`scss
229
+ // Button.module.scss
230
+ $primary: #3b82f6;
231
+ $secondary: #6b7280;
232
+
233
+ @mixin button-base {
234
+ padding: 0.5rem 1rem;
235
+ border: none;
236
+ border-radius: 0.375rem;
237
+ cursor: pointer;
238
+ transition: all 0.2s;
239
+ }
240
+
241
+ .button {
242
+ @include button-base;
243
+ }
244
+
245
+ .primary {
246
+ background: $primary;
247
+ color: white;
248
+
249
+ &:hover {
250
+ background: darken($primary, 10%);
251
+ }
252
+ }
253
+
254
+ .secondary {
255
+ background: $secondary;
256
+ color: white;
257
+
258
+ &:hover {
259
+ background: darken($secondary, 10%);
260
+ }
261
+ }
262
+
263
+ // Nesting works as expected
264
+ .card {
265
+ padding: 1.5rem;
266
+
267
+ &__title {
268
+ font-size: 1.25rem;
269
+ }
270
+
271
+ &__content {
272
+ color: #6b7280;
273
+ }
274
+ }
275
+ \`\`\`
276
+
277
+ ## File Structure
278
+ \`\`\`
279
+ src/
280
+ ├── components/
281
+ │ ├── Button/
282
+ │ │ ├── Button.tsx
283
+ │ │ ├── Button.module.css
284
+ │ │ └── index.ts
285
+ │ └── Card/
286
+ │ ├── Card.tsx
287
+ │ ├── Card.module.css
288
+ │ └── index.ts
289
+ ├── styles/
290
+ │ ├── variables.css # CSS custom properties
291
+ │ ├── shared.module.css # Shared compositions
292
+ │ └── global.css # Global styles
293
+ └── App.tsx
294
+ \`\`\`
295
+
296
+ ## Configuration (Vite)
297
+ \`\`\`typescript
298
+ // vite.config.ts
299
+ export default defineConfig({
300
+ css: {
301
+ modules: {
302
+ localsConvention: 'camelCaseOnly', // .my-class → styles.myClass
303
+ generateScopedName: '[name]__[local]___[hash:base64:5]',
304
+ },
305
+ },
306
+ });
307
+
308
+ // Next.js works out of the box with .module.css
309
+ \`\`\`
310
+
311
+ ## ❌ DON'T
312
+ - Use generic class names that don't describe the component
313
+ - Deeply nest selectors (keep flat)
314
+ - Mix global and local styles unnecessarily
315
+ - Forget to handle dynamic class names in TypeScript
316
+ - Create overly long class name strings
317
+
318
+ ## ✅ DO
319
+ - Name files with .module.css suffix
320
+ - Use composes for shared styles
321
+ - Use CSS variables for theming
322
+ - Use clsx/classnames for combining classes
323
+ - Set up TypeScript declarations
324
+ - Keep styles colocated with components
325
+ - Use BEM-like naming within modules for nested elements