-
Notifications
You must be signed in to change notification settings - Fork 371
feat(themes,clerk-js): Add support for dark mode theming #6460
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat(themes,clerk-js): Add support for dark mode theming #6460
Conversation
🦋 Changeset detectedLatest commit: 678409f The changes in this PR will be included in the next version bump. This PR includes changesets to release 22 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
📝 WalkthroughWalkthroughThis set of changes introduces a comprehensive theming system with robust dark mode support and tuple-based CSS variable handling. The core theme creation utility in Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (3)
packages/clerk-js/src/ui/customizables/AppearanceContext.tsx (1)
24-35
: Simplify type assertion - BaseThemeTaggedType already includes __internal_globalCssThe type assertion on line 32 is redundant since
BaseThemeTaggedType
already includes__internal_globalCss?: string
as shown in the type definition.- // Cast to the specific type that includes __internal_globalCss - return (theme as BaseThemeTaggedType & { __internal_globalCss?: string }).__internal_globalCss; + return (theme as BaseThemeTaggedType).__internal_globalCss;packages/themes/src/createTheme.ts (2)
9-20
: Consider a more flexible approach for tuple-supported propertiesThe
VariablesWithTuples
type hardcodes specific color properties that support tuples. This approach may require updates when new color properties are added to the theme system.Consider using a more maintainable pattern:
// Define tuple-supported properties in a separate type type TupleSupported = | 'colorBackground' | 'colorPrimary' | 'colorNeutral' | 'colorForeground' | 'colorInput' | 'colorInputForeground' | 'colorPrimaryForeground'; // Use the type for conditional mapping export type VariablesWithTuples = { [K in keyof Variables]: K extends TupleSupported ? string | [string, string] : Variables[K]; };This makes it easier to maintain the list of tuple-supported properties in one place.
298-317
: Document the hybrid theme/factory patternThe
experimental_createTheme
function returns a clever hybrid object that works both as a theme and a factory function. While this maintains backwards compatibility, it may be confusing for developers.Consider adding a more detailed JSDoc comment explaining this pattern:
/** * Creates a theme that can be used directly or called as a factory function. * * @example * // Direct usage (light mode only) * const theme = experimental_createTheme({ variables: {...} }); * <ClerkProvider appearance={{ theme }} /> * * @example * // Factory usage (with dark mode) * const theme = experimental_createTheme({ variables: {...} }); * <ClerkProvider appearance={{ theme: theme({ darkModeSelector: '.dark' }) }} /> */
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
packages/clerk-js/src/ui/customizables/AppearanceContext.tsx
(2 hunks)packages/themes/package.json
(1 hunks)packages/themes/src/__tests__/createTheme.test.ts
(1 hunks)packages/themes/src/createTheme.ts
(1 hunks)packages/themes/src/themes/clerk.ts
(1 hunks)packages/themes/src/themes/index.ts
(1 hunks)packages/themes/src/themes/neobrutalism.ts
(0 hunks)packages/themes/tsup.config.ts
(1 hunks)packages/themes/vitest.config.ts
(1 hunks)packages/types/src/appearance.ts
(1 hunks)
💤 Files with no reviewable changes (1)
- packages/themes/src/themes/neobrutalism.ts
🧰 Additional context used
📓 Path-based instructions (18)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}
: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/themes/tsup.config.ts
packages/themes/src/themes/index.ts
packages/themes/vitest.config.ts
packages/types/src/appearance.ts
packages/themes/src/themes/clerk.ts
packages/clerk-js/src/ui/customizables/AppearanceContext.tsx
packages/themes/src/__tests__/createTheme.test.ts
packages/themes/src/createTheme.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/themes/tsup.config.ts
packages/themes/src/themes/index.ts
packages/themes/vitest.config.ts
packages/types/src/appearance.ts
packages/themes/src/themes/clerk.ts
packages/themes/package.json
packages/clerk-js/src/ui/customizables/AppearanceContext.tsx
packages/themes/src/__tests__/createTheme.test.ts
packages/themes/src/createTheme.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/themes/tsup.config.ts
packages/themes/src/themes/index.ts
packages/themes/vitest.config.ts
packages/types/src/appearance.ts
packages/themes/src/themes/clerk.ts
packages/clerk-js/src/ui/customizables/AppearanceContext.tsx
packages/themes/src/__tests__/createTheme.test.ts
packages/themes/src/createTheme.ts
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/themes/tsup.config.ts
packages/themes/src/themes/index.ts
packages/themes/vitest.config.ts
packages/types/src/appearance.ts
packages/themes/src/themes/clerk.ts
packages/clerk-js/src/ui/customizables/AppearanceContext.tsx
packages/themes/src/__tests__/createTheme.test.ts
packages/themes/src/createTheme.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}
: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidany
type - preferunknown
when type is uncertain, then narrow with type guards
Useinterface
for object shapes that might be extended
Usetype
for unions, primitives, and computed types
Preferreadonly
properties for immutable data structures
Useprivate
for internal implementation details
Useprotected
for inheritance hierarchies
Usepublic
explicitly for clarity in public APIs
Preferreadonly
for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertions
for literal types:as const
Usesatisfies
operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noany
types without justification
Proper error handling with typed errors
Consistent use ofreadonly
for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/themes/tsup.config.ts
packages/themes/src/themes/index.ts
packages/themes/vitest.config.ts
packages/types/src/appearance.ts
packages/themes/src/themes/clerk.ts
packages/clerk-js/src/ui/customizables/AppearanceContext.tsx
packages/themes/src/__tests__/createTheme.test.ts
packages/themes/src/createTheme.ts
packages/*/tsup.config.{js,ts}
📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)
TypeScript compilation and bundling must use tsup.
Files:
packages/themes/tsup.config.ts
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/themes/tsup.config.ts
packages/themes/src/themes/index.ts
packages/themes/vitest.config.ts
packages/types/src/appearance.ts
packages/themes/src/themes/clerk.ts
packages/clerk-js/src/ui/customizables/AppearanceContext.tsx
packages/themes/src/__tests__/createTheme.test.ts
packages/themes/src/createTheme.ts
**/*
⚙️ CodeRabbit Configuration File
**/*
: If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.Whenever reviewing a pull request, if there are any changes that could impact security, always tag
@clerk/security
in the PR.Security-impacting changes include, but are not limited to:
- Changes to authentication logic or mechanisms (e.g. login, session handling, token issuance)
- Any modification to access control, authorization checks, or role-based permissions
- Introduction or modification of hashing algorithms, signature verification, or cryptographic primitives
- Handling of sensitive data (e.g. passwords, tokens, secrets, PII)
- Integration with external identity providers (e.g. SSO, OAuth, OpenID Connect)
- Modifications to security headers, cookie flags, CORS policies, or CSRF protections
- Bypass mechanisms (e.g. feature flags, testing overrides) that could weaken protections
- Changes to rate limiting, abuse prevention, or input validation
If you're unsure whether a change is security-relevant, err on the side of caution and tag
@clerk/security
.Any time that you tag
@clerk/security
, please do so explicitly in a code comment, rather than within a collapsed section in a coderabbit comment, such as the "recent review details" section. If you do use the team name in any thinking or non-direct-code-comment content, it can be referred to as "clerk security team" to avoid accidentally printing the tag which sends a notification to the team.
Files:
packages/themes/tsup.config.ts
packages/themes/src/themes/index.ts
packages/themes/vitest.config.ts
packages/types/src/appearance.ts
packages/themes/src/themes/clerk.ts
packages/themes/package.json
packages/clerk-js/src/ui/customizables/AppearanceContext.tsx
packages/themes/src/__tests__/createTheme.test.ts
packages/themes/src/createTheme.ts
packages/**/index.{js,ts}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Use tree-shaking friendly exports
Files:
packages/themes/src/themes/index.ts
**/index.ts
📄 CodeRabbit Inference Engine (.cursor/rules/react.mdc)
Use index.ts files for clean imports but avoid deep barrel exports
Avoid barrel files (index.ts re-exports) as they can cause circular dependencies
Files:
packages/themes/src/themes/index.ts
packages/types/src/appearance.ts
📄 CodeRabbit Inference Engine (.cursor/rules/clerk-js-ui.mdc)
Add new element descriptor types in packages/types/src/appearance.ts before using them elsewhere
Files:
packages/types/src/appearance.ts
packages/*/package.json
📄 CodeRabbit Inference Engine (.cursor/rules/global.mdc)
All publishable packages should be placed under the packages/ directory
packages/*/package.json
: All publishable packages must be located in the 'packages/' directory.
All packages must be published under the @clerk namespace on npm.
Semantic versioning must be used across all packages.
Files:
packages/themes/package.json
packages/clerk-js/src/ui/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/clerk-js-ui.mdc)
packages/clerk-js/src/ui/**/*.{ts,tsx}
: Element descriptors should always be camelCase
Use element descriptors in UI components to enable consistent theming and styling via appearance.elements
Element descriptors should generate unique, stable CSS classes for theming
Element descriptors should handle state classes (e.g., cl-loading, cl-active, cl-error, cl-open) automatically based on component state
Do not render hard-coded values; all user-facing strings must be localized using provided localization methods
Use the useLocalizations hook and localizationKeys utility for all text and error messages
Use the styled system (sx prop, theme tokens, responsive values) for custom component styling
Use useCardState for card-level state, useFormState for form-level state, and useLoadingStatus for loading states
Always use handleError utility for API errors and use translateError for localized error messages
Use useFormControl for form field state, implement proper validation, and handle loading and error states in forms
Use localization keys for all form labels and placeholders
Use element descriptors for consistent styling and follow the theme token system
Use the Card and FormContainer patterns for consistent UI structure
Files:
packages/clerk-js/src/ui/customizables/AppearanceContext.tsx
**/*.{jsx,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
**/*.{jsx,tsx}
: Use error boundaries in React components
Minimize re-renders in React components
**/*.{jsx,tsx}
: Always use functional components with hooks instead of class components
Follow PascalCase naming for components:UserProfile
,NavigationMenu
Keep components focused on a single responsibility - split large components
Limit component size to 150-200 lines; extract logic into custom hooks
Use composition over inheritance - prefer smaller, composable components
Export components as named exports for better tree-shaking
One component per file with matching filename and component name
Use useState for simple state management
Use useReducer for complex state logic
Implement proper state initialization
Use proper state updates with callbacks
Implement proper state cleanup
Use Context API for theme/authentication
Implement proper state selectors
Use proper state normalization
Implement proper state persistence
Use React.memo for expensive components
Implement proper useCallback for handlers
Use proper useMemo for expensive computations
Implement proper virtualization for lists
Use proper code splitting with React.lazy
Implement proper cleanup in useEffect
Use proper refs for DOM access
Implement proper event listener cleanup
Use proper abort controllers for fetch
Implement proper subscription cleanup
Use proper HTML elements
Implement proper ARIA attributes
Use proper heading hierarchy
Implement proper form labels
Use proper button types
Implement proper focus management
Use proper keyboard shortcuts
Implement proper tab order
Use proper skip links
Implement proper focus traps
Implement proper error boundaries
Use proper error logging
Implement proper error recovery
Use proper error messages
Implement proper error fallbacks
Use proper form validation
Implement proper error states
Use proper error messages
Implement proper form submission
Use proper form reset
Use proper component naming
Implement proper file naming
Use proper prop naming
Implement proper...
Files:
packages/clerk-js/src/ui/customizables/AppearanceContext.tsx
**/*.tsx
📄 CodeRabbit Inference Engine (.cursor/rules/react.mdc)
**/*.tsx
: Use proper type definitions for props and state
Leverage TypeScript's type inference where possible
Use proper event types for handlers
Implement proper generic types for reusable components
Use proper type guards for conditional rendering
Files:
packages/clerk-js/src/ui/customizables/AppearanceContext.tsx
packages/**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)
Unit tests should use Jest or Vitest as the test runner.
Files:
packages/themes/src/__tests__/createTheme.test.ts
packages/{clerk-js,elements,themes}/**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)
Visual regression testing should be performed for UI components.
Files:
packages/themes/src/__tests__/createTheme.test.ts
**/__tests__/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/typescript.mdc)
**/__tests__/**/*.{ts,tsx}
: Create type-safe test builders/factories
Use branded types for test isolation
Implement proper mock types that match interfaces
Files:
packages/themes/src/__tests__/createTheme.test.ts
🧬 Code Graph Analysis (2)
packages/themes/src/themes/clerk.ts (1)
packages/themes/src/createTheme.ts (1)
experimental_createTheme
(306-317)
packages/clerk-js/src/ui/customizables/AppearanceContext.tsx (1)
packages/types/src/appearance.ts (2)
BaseTheme
(812-812)BaseThemeTaggedType
(808-811)
🪛 GitHub Actions: CI
packages/themes/package.json
[error] 1-1: pnpm install failed due to outdated lockfile. The pnpm-lock.yaml is not up to date with packages/themes/package.json. Use 'pnpm install --no-frozen-lockfile' to bypass this in CI.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: semgrep/ci
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (11)
packages/themes/src/themes/index.ts (1)
1-1
: LGTM! Export follows established pattern.The export of the new
clerk
theme is correctly implemented and follows the same pattern as other theme exports in the file.packages/themes/vitest.config.ts (1)
1-8
: LGTM! Appropriate Vitest configuration for the themes package.The configuration correctly uses the Node.js environment and enables globals, which is suitable for testing theme utilities and creation functions.
packages/types/src/appearance.ts (1)
808-811
: LGTM! Well-designed type extension for global CSS support.The optional
__internal_globalCss
property is appropriately named with the internal prefix and maintains backward compatibility while enabling the global CSS injection feature.packages/themes/src/themes/clerk.ts (2)
3-12
: LGTM! Well-structured theme definition with proper light/dark mode support.The color variables are correctly defined as tuples with light and dark values. The color choices provide good contrast between modes.
13-27
: LGTM! Dynamic elements function properly handles dark mode styling.The elements function correctly utilizes the
darkModeSelector
parameter and applies appropriate styling for dark mode. Thefilter: 'invert(1)'
approach for provider icons is a common and effective technique for inverting colors in dark mode.The custom CSS variables for
activeDeviceIcon
follow the established--cl-
naming convention, ensuring consistency with the codebase.packages/themes/tsup.config.ts (1)
6-6
: LGTM! Proper exclusion of test files from build.The updated entry configuration correctly excludes test files and test directories from the build, which is a best practice that ensures only production code is bundled.
packages/clerk-js/src/ui/customizables/AppearanceContext.tsx (1)
37-51
: LGTM! Clean implementation of global CSS injectionThe global CSS extraction and injection logic is well-implemented:
- Correctly checks both
appearance
andglobalAppearance
themes- Conditionally renders the
Global
component only when CSS exists- Properly uses Emotion's
css
template literal for style injectionpackages/themes/src/__tests__/createTheme.test.ts (1)
1-976
: Excellent comprehensive test coverage!This test suite provides thorough coverage of the new theming system with:
- Well-organized test structure using describe blocks
- Complete coverage of all exported functions and utilities
- Good edge case testing (null values, empty strings, error scenarios)
- Proper use of mocks for testing callbacks
- Backwards compatibility validation
The tests effectively validate the complex theming logic including tuple variable processing, dark mode CSS generation, and theme merging.
packages/themes/src/createTheme.ts (3)
71-138
: Well-implemented tuple processing with proper CSS generationThe
processTupleVariables
function correctly handles:
- Tuple detection and CSS variable generation
- Opt-out behavior when dark mode is disabled
- Different selector types (media queries vs regular selectors)
- Proper CSS formatting with consistent indentation
156-185
: Robust element processing with proper error handlingThe
processElements
function has excellent error handling and correctly manages both static and function-based elements. The opt-out behavior for dark mode is well-implemented.
267-296
: Clean factory implementation with proper configuration flowThe
createThemeFactory
function elegantly handles the complete theme creation pipeline:
- Proper dark mode selector normalization
- Base theme resolution with factory support
- Configuration merging with proper precedence
- CSS generation from tuple variables
@clerk/agent-toolkit
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/elements
@clerk/clerk-expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/clerk-react
@clerk/react-router
@clerk/remix
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/themes
@clerk/types
@clerk/upgrade
@clerk/vue
commit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
packages/themes/src/createTheme.ts (1)
37-44
: Good fix for the leading dash issue!The
toKebabCase
function now correctly handles uppercase-starting strings by removing any leading dash. The implementation and JSDoc examples are clear.
🧹 Nitpick comments (4)
packages/themes/src/createTheme.ts (4)
9-20
: Consider extracting the list of tuple-supporting properties for maintainability.The hard-coded list of color properties that support tuples could become difficult to maintain if new color properties are added. Consider extracting this list into a constant or type union.
+// Properties that support light/dark tuples +type TupleSupportingProperties = + | 'colorBackground' + | 'colorPrimary' + | 'colorNeutral' + | 'colorForeground' + | 'colorInput' + | 'colorInputForeground' + | 'colorPrimaryForeground'; + // Extended Variables type that supports tuple values for color properties export type VariablesWithTuples = { - [K in keyof Variables]: K extends - | 'colorBackground' - | 'colorPrimary' - | 'colorNeutral' - | 'colorForeground' - | 'colorInput' - | 'colorInputForeground' - | 'colorPrimaryForeground' + [K in keyof Variables]: K extends TupleSupportingProperties ? string | [string, string] : Variables[K]; };
188-208
: Consider more explicit error handling for failed base theme factories.While the current implementation handles errors gracefully, falling back to treating a failed factory function as a static theme might hide configuration issues and cause unexpected behavior.
} catch (error) { console.warn('Failed to call baseTheme factory function:', error); - return baseTheme as BaseTheme; // Fallback to treating it as static theme + // Return undefined to make the error more visible rather than + // potentially using an invalid theme configuration + return undefined; }
307-318
: Add explicit return type to the implementation signature.While the overloads have proper return types, the implementation function should also have an explicit return type for better type safety and consistency with the coding guidelines.
-export function experimental_createTheme(appearance: any) { +export function experimental_createTheme(appearance: any): BaseTheme & ((options?: ThemeOptions) => BaseTheme) {
1-318
: Consider adding comprehensive tests for the theming system.This is a significant refactor introducing complex theming logic. Please ensure comprehensive test coverage for:
- Tuple variable processing with different dark mode configurations
- Theme merging and inheritance
- Error handling in factory functions
- CSS generation for both media queries and class selectors
- The callable theme pattern
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/clerk-js/bundlewatch.config.json
(1 hunks)packages/themes/src/__tests__/createTheme.test.ts
(1 hunks)packages/themes/src/createTheme.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- packages/clerk-js/bundlewatch.config.json
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/themes/src/tests/createTheme.test.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}
: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/themes/src/createTheme.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/themes/src/createTheme.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/themes/src/createTheme.ts
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/themes/src/createTheme.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}
: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidany
type - preferunknown
when type is uncertain, then narrow with type guards
Useinterface
for object shapes that might be extended
Usetype
for unions, primitives, and computed types
Preferreadonly
properties for immutable data structures
Useprivate
for internal implementation details
Useprotected
for inheritance hierarchies
Usepublic
explicitly for clarity in public APIs
Preferreadonly
for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertions
for literal types:as const
Usesatisfies
operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noany
types without justification
Proper error handling with typed errors
Consistent use ofreadonly
for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/themes/src/createTheme.ts
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/themes/src/createTheme.ts
**/*
⚙️ CodeRabbit Configuration File
**/*
: If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.Whenever reviewing a pull request, if there are any changes that could impact security, always tag
@clerk/security
in the PR.Security-impacting changes include, but are not limited to:
- Changes to authentication logic or mechanisms (e.g. login, session handling, token issuance)
- Any modification to access control, authorization checks, or role-based permissions
- Introduction or modification of hashing algorithms, signature verification, or cryptographic primitives
- Handling of sensitive data (e.g. passwords, tokens, secrets, PII)
- Integration with external identity providers (e.g. SSO, OAuth, OpenID Connect)
- Modifications to security headers, cookie flags, CORS policies, or CSRF protections
- Bypass mechanisms (e.g. feature flags, testing overrides) that could weaken protections
- Changes to rate limiting, abuse prevention, or input validation
If you're unsure whether a change is security-relevant, err on the side of caution and tag
@clerk/security
.Any time that you tag
@clerk/security
, please do so explicitly in a code comment, rather than within a collapsed section in a coderabbit comment, such as the "recent review details" section. If you do use the team name in any thinking or non-direct-code-comment content, it can be referred to as "clerk security team" to avoid accidentally printing the tag which sends a notification to the team.
Files:
packages/themes/src/createTheme.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: Build Packages
- GitHub Check: Formatting | Dedupe | Changeset
- GitHub Check: semgrep/ci
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (6)
packages/themes/src/createTheme.ts (6)
46-64
: Well-designed dark mode selector normalization.The function properly handles explicit opt-out with
false
, defaults to light-only when undefined, and sanitizes input. Good defensive programming.
66-139
: Excellent implementation of tuple variable processing.The function correctly:
- Handles dark mode opt-out by using only light values
- Generates proper CSS custom properties with consistent naming
- Differentiates between media queries and regular selectors for correct CSS output
- Uses clear variable names and good code organization
141-152
: Correct CSS selector transformation for nesting contexts.The function properly handles class selectors by appending
&
for parent-child relationships while leaving media queries and other selectors unchanged.
154-186
: Well-implemented elements processing with proper error handling.The function correctly:
- Handles both static and function-based elements
- Skips function execution when dark mode is opted out
- Includes error handling with appropriate warnings
- Transforms selectors before passing to element functions
210-239
: Clean implementation of theme configuration merging.The function correctly merges configurations with child precedence and avoids creating unnecessary empty objects. Good use of conditional assignment.
241-297
: Well-structured theme object creation and factory implementation.Both functions work together effectively to:
- Create properly typed theme objects with internal properties
- Orchestrate the entire theme generation pipeline
- Handle all configuration options correctly
- Include global CSS when tuple variables are processed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
packages/themes/src/createTheme.ts (1)
278-332
: Sophisticated theme factory implementation with minor type safety concern.The implementation excellently provides:
- Factory pattern for dynamic theme generation
- Overloaded signatures for different use cases
- Backwards compatibility through default theme creation
- Callable theme objects that serve dual purposes
However, consider improving type safety:
- return (baseTheme as any)(options); + return (baseTheme as (options?: ThemeOptions) => BaseTheme)(options);-export function experimental_createTheme(appearance: any) { +export function experimental_createTheme(appearance: Appearance<ThemeConfiguration>) {This would eliminate
any
usage while maintaining functionality.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/themes/src/__tests__/createTheme.test.ts
(1 hunks)packages/themes/src/createTheme.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/themes/src/tests/createTheme.test.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}
: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/themes/src/createTheme.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/themes/src/createTheme.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/themes/src/createTheme.ts
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/themes/src/createTheme.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}
: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidany
type - preferunknown
when type is uncertain, then narrow with type guards
Useinterface
for object shapes that might be extended
Usetype
for unions, primitives, and computed types
Preferreadonly
properties for immutable data structures
Useprivate
for internal implementation details
Useprotected
for inheritance hierarchies
Usepublic
explicitly for clarity in public APIs
Preferreadonly
for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertions
for literal types:as const
Usesatisfies
operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noany
types without justification
Proper error handling with typed errors
Consistent use ofreadonly
for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/themes/src/createTheme.ts
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/themes/src/createTheme.ts
**/*
⚙️ CodeRabbit Configuration File
**/*
: If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.Whenever reviewing a pull request, if there are any changes that could impact security, always tag
@clerk/security
in the PR.Security-impacting changes include, but are not limited to:
- Changes to authentication logic or mechanisms (e.g. login, session handling, token issuance)
- Any modification to access control, authorization checks, or role-based permissions
- Introduction or modification of hashing algorithms, signature verification, or cryptographic primitives
- Handling of sensitive data (e.g. passwords, tokens, secrets, PII)
- Integration with external identity providers (e.g. SSO, OAuth, OpenID Connect)
- Modifications to security headers, cookie flags, CORS policies, or CSRF protections
- Bypass mechanisms (e.g. feature flags, testing overrides) that could weaken protections
- Changes to rate limiting, abuse prevention, or input validation
If you're unsure whether a change is security-relevant, err on the side of caution and tag
@clerk/security
.Any time that you tag
@clerk/security
, please do so explicitly in a code comment, rather than within a collapsed section in a coderabbit comment, such as the "recent review details" section. If you do use the team name in any thinking or non-direct-code-comment content, it can be referred to as "clerk security team" to avoid accidentally printing the tag which sends a notification to the team.
Files:
packages/themes/src/createTheme.ts
🧬 Code Graph Analysis (1)
packages/themes/src/createTheme.ts (3)
packages/types/src/appearance.ts (3)
Variables
(638-806)BaseTheme
(812-812)Appearance
(989-1055)packages/themes/src/themes/dark.ts (1)
dark
(3-24)packages/clerk-js/src/ui/baseTheme.ts (1)
baseTheme
(286-286)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: Build Packages
- GitHub Check: Formatting | Dedupe | Changeset
- GitHub Check: semgrep/ci
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (7)
packages/themes/src/createTheme.ts (7)
1-45
: Excellent type system design for dark mode support.The type definitions are well-structured:
- Proper use of mapped types in
VariablesWithTuples
to selectively allow tuple values for color properties- Clean separation of concerns with dedicated types for different configuration scenarios
- Appropriate use of readonly tuples in
VariableValue
type- Well-organized constants with descriptive names
46-53
: Perfect fix for the leading hyphen issue.The
toKebabCase
function now correctly handles uppercase-starting strings by removing the leading hyphen, addressing the previous review concern. The implementation is clean and well-documented.
55-73
: Robust dark mode selector normalization.The function provides clear semantics:
false
explicitly opts out of dark modeundefined
defaults to light-only (safe default)- String values are properly trimmed and validated
The explicit opt-in design prevents unintended dark mode activation.
75-148
: Well-implemented CSS variable processing with comprehensive dark mode support.The function correctly handles:
- Tuple-to-CSS variable conversion
- Media query vs selector-based dark mode
- Proper CSS custom property naming
- Opt-out scenarios
The logic is sound and the generated CSS structure is appropriate for both media queries and class-based dark mode switching.
150-165
: Clean CSS selector transformation logic.The function properly handles CSS nesting by:
- Appending
&
for class/ID selectors to create parent-child relationships- Avoiding duplicate
&
symbols- Leaving media queries and other selectors unchanged
167-199
: Robust elements configuration resolution with proper error handling.The function correctly:
- Distinguishes between static and function-based elements
- Provides graceful error handling with meaningful warnings
- Implements consistent opt-out behavior for dark mode
- Uses proper type checking and transformation
201-276
: Well-designed theme composition utilities.The helper functions provide solid foundation for theme merging:
resolveBaseTheme
handles both static themes and factory functions with proper error handlingmergeThemeConfigurations
correctly merges configurations with child precedencecreateThemeObject
properly assembles the final theme with internal propertiesThe architecture supports both static and dynamic theme composition effectively.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (1)
packages/themes/src/createTheme.ts (1)
88-155
: Consider validating tuple values for better error handlingThe
convertTuplesToCssVariables
function assumes tuple values are valid strings. Consider adding validation to ensure tuple elements are strings to prevent runtime errors with malformed data.export function convertTuplesToCssVariables( variables: Record<string, string | [string, string] | number | undefined>, darkModeSelector?: string | null, ): ProcessedVariables { const lightRules: string[] = []; const darkRules: string[] = []; const processedVariables: Record<string, string | number | undefined> = {}; for (const [key, value] of Object.entries(variables)) { if (Array.isArray(value) && value.length === 2) { + // Validate tuple values are strings + if (typeof value[0] !== 'string' || typeof value[1] !== 'string') { + console.warn(`[Clerk Theme] Invalid tuple values for variable '${key}': expected [string, string]`); + processedVariables[key] = value; + continue; + } // This is a tuple [light, dark] const [lightValue, darkValue] = value;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/themes/src/__tests__/createTheme.test.ts
(1 hunks)packages/themes/src/createTheme.ts
(1 hunks)packages/themes/src/themes/clerk.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/themes/src/themes/clerk.ts
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}
: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/themes/src/__tests__/createTheme.test.ts
packages/themes/src/createTheme.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/themes/src/__tests__/createTheme.test.ts
packages/themes/src/createTheme.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/themes/src/__tests__/createTheme.test.ts
packages/themes/src/createTheme.ts
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/themes/src/__tests__/createTheme.test.ts
packages/themes/src/createTheme.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}
: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidany
type - preferunknown
when type is uncertain, then narrow with type guards
Useinterface
for object shapes that might be extended
Usetype
for unions, primitives, and computed types
Preferreadonly
properties for immutable data structures
Useprivate
for internal implementation details
Useprotected
for inheritance hierarchies
Usepublic
explicitly for clarity in public APIs
Preferreadonly
for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertions
for literal types:as const
Usesatisfies
operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noany
types without justification
Proper error handling with typed errors
Consistent use ofreadonly
for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/themes/src/__tests__/createTheme.test.ts
packages/themes/src/createTheme.ts
packages/**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)
Unit tests should use Jest or Vitest as the test runner.
Files:
packages/themes/src/__tests__/createTheme.test.ts
packages/{clerk-js,elements,themes}/**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)
Visual regression testing should be performed for UI components.
Files:
packages/themes/src/__tests__/createTheme.test.ts
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/themes/src/__tests__/createTheme.test.ts
packages/themes/src/createTheme.ts
**/__tests__/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/typescript.mdc)
**/__tests__/**/*.{ts,tsx}
: Create type-safe test builders/factories
Use branded types for test isolation
Implement proper mock types that match interfaces
Files:
packages/themes/src/__tests__/createTheme.test.ts
**/*
⚙️ CodeRabbit Configuration File
**/*
: If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.Whenever reviewing a pull request, if there are any changes that could impact security, always tag
@clerk/security
in the PR.Security-impacting changes include, but are not limited to:
- Changes to authentication logic or mechanisms (e.g. login, session handling, token issuance)
- Any modification to access control, authorization checks, or role-based permissions
- Introduction or modification of hashing algorithms, signature verification, or cryptographic primitives
- Handling of sensitive data (e.g. passwords, tokens, secrets, PII)
- Integration with external identity providers (e.g. SSO, OAuth, OpenID Connect)
- Modifications to security headers, cookie flags, CORS policies, or CSRF protections
- Bypass mechanisms (e.g. feature flags, testing overrides) that could weaken protections
- Changes to rate limiting, abuse prevention, or input validation
If you're unsure whether a change is security-relevant, err on the side of caution and tag
@clerk/security
.Any time that you tag
@clerk/security
, please do so explicitly in a code comment, rather than within a collapsed section in a coderabbit comment, such as the "recent review details" section. If you do use the team name in any thinking or non-direct-code-comment content, it can be referred to as "clerk security team" to avoid accidentally printing the tag which sends a notification to the team.
Files:
packages/themes/src/__tests__/createTheme.test.ts
packages/themes/src/createTheme.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Formatting | Dedupe | Changeset
- GitHub Check: Build Packages
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: semgrep/ci
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
packages/themes/src/__tests__/createTheme.test.ts (1)
995-1077
: Excellent comprehensive test coverage for color variables! 👍These tests effectively validate that the theming system correctly identifies and supports tuple values for all non-deprecated color variables. The automatic detection mechanism and the comprehensive list of supported variables ensure the theme system stays in sync with the Variables type from @clerk/types.
packages/themes/src/createTheme.ts (1)
327-338
: Well-designed backwards-compatible API! 👍The hybrid object pattern elegantly maintains backwards compatibility while adding new dark mode functionality. The overloads provide clear type safety for different usage scenarios.
if (typeof baseTheme === 'function') { | ||
try { | ||
return (baseTheme as any)(options); | ||
} catch (error) { | ||
console.warn('[Clerk Theme] Failed to call baseTheme factory function:', error); | ||
return baseTheme as BaseTheme; // Fallback to treating it as static theme | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Avoid using any
type casting
The baseTheme is cast to any
which bypasses TypeScript's type checking. Consider using a type guard or more specific typing to maintain type safety.
// If baseTheme is a factory function, call it with the same options
if (typeof baseTheme === 'function') {
try {
- return (baseTheme as any)(options);
+ // Use type assertion to callable theme factory type
+ return (baseTheme as ((options?: ThemeOptions) => BaseTheme))(options);
} catch (error) {
console.warn('[Clerk Theme] Failed to call baseTheme factory function:', error);
return baseTheme as BaseTheme; // Fallback to treating it as static theme
}
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
if (typeof baseTheme === 'function') { | |
try { | |
return (baseTheme as any)(options); | |
} catch (error) { | |
console.warn('[Clerk Theme] Failed to call baseTheme factory function:', error); | |
return baseTheme as BaseTheme; // Fallback to treating it as static theme | |
} | |
} | |
if (typeof baseTheme === 'function') { | |
try { | |
// Use type assertion to callable theme factory type | |
return (baseTheme as ((options?: ThemeOptions) => BaseTheme))(options); | |
} catch (error) { | |
console.warn('[Clerk Theme] Failed to call baseTheme factory function:', error); | |
return baseTheme as BaseTheme; // Fallback to treating it as static theme | |
} | |
} |
🤖 Prompt for AI Agents
In packages/themes/src/createTheme.ts around lines 217 to 224, the code casts
baseTheme to any to call it as a function, which bypasses TypeScript type
checking. Replace the any cast with a proper type guard that checks if baseTheme
is a function type and then call it with the correct typing. This will maintain
type safety without disabling type checks.
const resolvedBase = (baseTheme as any) || {}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove unnecessary type casting
Similar to the previous issue, avoid casting to any
. Use proper typing or the spread operator safely.
- const resolvedBase = (baseTheme as any) || {};
+ const resolvedBase = baseTheme || {} as Partial<BaseTheme>;
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const resolvedBase = (baseTheme as any) || {}; | |
const resolvedBase = baseTheme || {} as Partial<BaseTheme>; |
🤖 Prompt for AI Agents
In packages/themes/src/createTheme.ts around lines 241 to 242, remove the
unnecessary type casting to 'any' on 'baseTheme'. Instead, ensure 'baseTheme'
has the correct type or use the spread operator safely to create 'resolvedBase'
without casting, improving type safety and code clarity.
import { experimental_createTheme } from '../createTheme'; | ||
|
||
export const clerk = experimental_createTheme({ | ||
variables: { | ||
colorBackground: ['#ffffff', '#212126'], | ||
colorNeutral: ['#000000', '#ffffff'], | ||
colorPrimary: ['#000000', '#ffffff'], | ||
colorPrimaryForeground: ['#ffffff', '#000000'], | ||
colorForeground: ['#000000', '#ffffff'], | ||
colorInputForeground: ['#000000', '#ffffff'], | ||
colorInput: ['#ffffff', '#26262B'], | ||
colorModalBackdrop: ['#000000', '#000000'], | ||
}, | ||
elements: darkModeSelector => { | ||
return { | ||
providerIcon__apple: { [darkModeSelector]: { filter: 'invert(1)' } }, | ||
providerIcon__github: { [darkModeSelector]: { filter: 'invert(1)' } }, | ||
providerIcon__okx_wallet: { [darkModeSelector]: { filter: 'invert(1)' } }, | ||
activeDeviceIcon: { | ||
[darkModeSelector]: { | ||
'--cl-chassis-bottom': '#d2d2d2', | ||
'--cl-chassis-back': '#e6e6e6', | ||
'--cl-chassis-screen': '#e6e6e6', | ||
'--cl-screen': '#111111', | ||
}, | ||
}, | ||
}; | ||
}, | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an example theme to showcase how the default theme and dark theme would work with the supported darkMode functionality.
} | ||
|
||
// Overload for themes with function-based elements | ||
export function experimental_createTheme< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we finally move createTheme
out of experimental?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
packages/themes/src/createTheme.ts (2)
200-216
: Unresolved type safety issue withany
castingThis still uses
any
type casting as flagged in previous reviews. The past comment suggested using proper typing instead of(baseTheme as any)(options)
.The
any
casting bypasses TypeScript's type checking. Consider implementing the suggested fix from the previous review or create a proper type guard.
221-246
: Unresolved type safety issue with baseTheme castingSimilar to the previous issue, this still casts
baseTheme
toany
as flagged in past reviews.Replace
(baseTheme as any) || {}
with proper typing as suggested in previous reviews to maintain type safety.
🧹 Nitpick comments (1)
packages/themes/src/createTheme.ts (1)
67-80
: Consider making dark mode opt-in behavior more explicitThe current logic defaults to null (no dark mode) when
selector
is undefined, which differs from typical theming systems that might default to a media query. While the comment explains this is intentional opt-in behavior, consider if this aligns with user expectations.If you want to provide a more intuitive default, consider:
// If no selector provided, opt out by default (light-only theme) // Dark mode should be explicit opt-in behavior if (selector === undefined) { - return null; + return '@media (prefers-color-scheme: dark)'; // or keep null for explicit opt-in }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/themes/src/createTheme.ts
(1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}
: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/themes/src/createTheme.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/themes/src/createTheme.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/themes/src/createTheme.ts
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/themes/src/createTheme.ts
**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}
: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidany
type - preferunknown
when type is uncertain, then narrow with type guards
Useinterface
for object shapes that might be extended
Usetype
for unions, primitives, and computed types
Preferreadonly
properties for immutable data structures
Useprivate
for internal implementation details
Useprotected
for inheritance hierarchies
Usepublic
explicitly for clarity in public APIs
Preferreadonly
for properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertions
for literal types:as const
Usesatisfies
operator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noany
types without justification
Proper error handling with typed errors
Consistent use ofreadonly
for immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/themes/src/createTheme.ts
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit Inference Engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/themes/src/createTheme.ts
**/*
⚙️ CodeRabbit Configuration File
**/*
: If there are no tests added or modified as part of the PR, please suggest that tests be added to cover the changes.Whenever reviewing a pull request, if there are any changes that could impact security, always tag
@clerk/security
in the PR.Security-impacting changes include, but are not limited to:
- Changes to authentication logic or mechanisms (e.g. login, session handling, token issuance)
- Any modification to access control, authorization checks, or role-based permissions
- Introduction or modification of hashing algorithms, signature verification, or cryptographic primitives
- Handling of sensitive data (e.g. passwords, tokens, secrets, PII)
- Integration with external identity providers (e.g. SSO, OAuth, OpenID Connect)
- Modifications to security headers, cookie flags, CORS policies, or CSRF protections
- Bypass mechanisms (e.g. feature flags, testing overrides) that could weaken protections
- Changes to rate limiting, abuse prevention, or input validation
If you're unsure whether a change is security-relevant, err on the side of caution and tag
@clerk/security
.Any time that you tag
@clerk/security
, please do so explicitly in a code comment, rather than within a collapsed section in a coderabbit comment, such as the "recent review details" section. If you do use the team name in any thinking or non-direct-code-comment content, it can be referred to as "clerk security team" to avoid accidentally printing the tag which sends a notification to the team.
Files:
packages/themes/src/createTheme.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Integration Tests (billing, chrome)
- GitHub Check: Integration Tests (nextjs, chrome, 15)
- GitHub Check: Integration Tests (nextjs, chrome, 14)
- GitHub Check: Integration Tests (generic, chrome)
🔇 Additional comments (7)
packages/themes/src/createTheme.ts (7)
1-28
: Well-structured type system with proper tuple supportThe type definitions effectively support the light/dark mode tuple pattern while correctly excluding deprecated variables. The mapped type approach for
NonDeprecatedColorVariables
is elegant and maintainable.
44-60
: Improved toKebabCase implementation addresses previous feedbackThe function now correctly handles the leading hyphen issue identified in past reviews by using
LEADING_HYPHEN_REGEX
to strip any leading hyphens, ensuring camelCase inputs like 'XMLParser' produce 'x-m-l-parser' instead of '-x-m-l-parser'.
88-145
: Robust CSS variable generation with proper escapingThe tuple conversion logic correctly handles both opt-out scenarios and different selector types. The CSS generation properly formats variables and handles media queries vs. regular selectors.
167-195
: Solid error handling in elements resolutionThe function correctly handles both static and function-based elements with appropriate error handling and opt-out logic for dark mode.
251-270
: Proper theme object creation with internal propertiesThe function correctly creates the theme object with the required
__type
property and conditionally sets variables/elements only when they have content. The global CSS injection mechanism is well-implemented.
275-299
: Well-structured theme factory implementationThe factory function properly orchestrates all the utility functions and handles the theme creation pipeline correctly, including CSS generation and variable processing.
302-317
: Experimental API should be promotedThe overloaded function signature properly handles both static and function-based elements. However, as noted in the past review comment, this functionality appears mature enough to remove the
experimental_
prefix.Consider promoting this API from experimental status. The implementation is comprehensive and well-tested. The past review comment asks about moving
createTheme
out of experimental - has this decision been made?If promoting to stable, update the export name:
-export function experimental_createTheme< +export function createTheme<And maintain backward compatibility by re-exporting:
+export { createTheme as experimental_createTheme };
export function transformSelectorForNesting(selector: string): string { | ||
// If it's a class or ID selector, append & for proper CSS nesting | ||
if ( | ||
(selector.startsWith(CLASS_SELECTOR_PREFIX) || selector.startsWith(ID_SELECTOR_PREFIX)) && | ||
!selector.includes('&') | ||
) { | ||
return `${selector} &`; | ||
} | ||
// For media queries and other selectors, return as-is | ||
return selector; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Selector transformation logic needs refinement
The current logic only checks for class and ID selectors, but the nesting transformation ${selector} &
creates a descendant selector. This may not work correctly for all use cases.
Consider the intended CSS structure. If you want the dark mode class on a parent element affecting child elements, the current logic is correct. However, if you want the dark mode class on the same element, you might need:
export function transformSelectorForNesting(selector: string): string {
// If it's a class or ID selector, append & for proper CSS nesting
if (
(selector.startsWith(CLASS_SELECTOR_PREFIX) || selector.startsWith(ID_SELECTOR_PREFIX)) &&
!selector.includes('&')
) {
- return `${selector} &`;
+ return `${selector}&`; // Adjacent selector, not descendant
}
// For media queries and other selectors, return as-is
return selector;
}
Verify this matches your intended CSS output structure.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export function transformSelectorForNesting(selector: string): string { | |
// If it's a class or ID selector, append & for proper CSS nesting | |
if ( | |
(selector.startsWith(CLASS_SELECTOR_PREFIX) || selector.startsWith(ID_SELECTOR_PREFIX)) && | |
!selector.includes('&') | |
) { | |
return `${selector} &`; | |
} | |
// For media queries and other selectors, return as-is | |
return selector; | |
} | |
export function transformSelectorForNesting(selector: string): string { | |
// If it's a class or ID selector, append & for proper CSS nesting | |
if ( | |
(selector.startsWith(CLASS_SELECTOR_PREFIX) || selector.startsWith(ID_SELECTOR_PREFIX)) && | |
!selector.includes('&') | |
) { | |
return `${selector}&`; // Adjacent selector, not descendant | |
} | |
// For media queries and other selectors, return as-is | |
return selector; | |
} |
🤖 Prompt for AI Agents
In packages/themes/src/createTheme.ts around lines 152 to 162, the
transformSelectorForNesting function currently appends " &" to class or ID
selectors, creating a descendant selector which may not fit all use cases.
Review the intended CSS structure and adjust the transformation logic
accordingly: if the dark mode class should apply to the same element, modify the
selector to prepend "&" instead of appending it, or implement conditional logic
to handle different selector types and nesting scenarios to ensure correct CSS
output.
// If opted out of dark mode, don't call the function at all | ||
// since it's designed to generate dark mode styles | ||
if (darkModeSelector === null) { | ||
return undefined; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems unexpected to me. If no dark mode selector is provided, but elements
is a function, we ignore any elements styling? Should we warn or error in this case if the function syntax requires a darkModeSelector
?
return (baseTheme as any)(options); | ||
} catch (error) { | ||
console.warn('[Clerk Theme] Failed to call baseTheme factory function:', error); | ||
return baseTheme as BaseTheme; // Fallback to treating it as static theme |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
baseTheme
is a function in this case right? This return seems problematic, we're casting a function as BaseTheme
colorPrimary: ['#blue', '#darkblue'] as [string, string], | ||
colorDanger: ['#red', '#darkred'] as [string, string], | ||
colorSuccess: ['#green', '#darkgreen'] as [string, string], | ||
colorNeutral: ['#gray', '#darkgray'] as [string, string], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aren't these invalid values?
expect(theme.variables?.colorModalBackdrop).toBe('var(--clerk-color-modal-backdrop)'); | ||
|
||
// Should generate comprehensive CSS | ||
const css = (theme as any).__internal_globalCss; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A good additional test case might be to actually parse the CSS to ensure it's valid
* @param selector - The dark mode selector, false to opt out, or undefined for media query fallback | ||
* @returns null if opted out, otherwise a valid selector string | ||
*/ | ||
export function normalizeDarkModeSelector(selector?: string | false): string | null { | ||
if (selector === false) { | ||
return null; // Opt out of dark mode CSS generation | ||
} | ||
|
||
// If no selector provided, opt out by default (light-only theme) | ||
// Dark mode should be explicit opt-in behavior | ||
if (selector === undefined) { | ||
return null; | ||
} | ||
|
||
// Use provided selector, trimmed | ||
return selector.trim() || null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The JSDoc and implementation differ:
The dark mode selector, false to opt out, or undefined for media query fallback
however, we opt-out in the implementation when undefined is provided.
lightRules.push(` ${cssVarName}: ${lightValue};`); | ||
darkRules.push(` ${cssVarName}: ${darkValue};`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a benefit to formatting the CSS like this? It might be easier to output in a more minified format
|
||
let cssString: string | null = null; | ||
if (lightRules.length > 0 && darkModeSelector !== null) { | ||
const normalizedSelector = darkModeSelector as string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ why do we need to typecast here?
Description
Expose the ability to create themes that support both light and dark mode.
createTheme
Theme usage
Resolves USER-2487
Checklist
pnpm test
runs as expected.pnpm build
runs as expected.Type of change
Summary by CodeRabbit
Summary by CodeRabbit
New Features
Bug Fixes
Tests
Chores
Documentation