Skip to content

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

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

alexcarpenter
Copy link
Member

@alexcarpenter alexcarpenter commented Aug 1, 2025

Description

Expose the ability to create themes that support both light and dark mode.

createTheme

import { experimental_createTheme as createTheme } from '@clerk/themes';

export const clerk = createTheme({
  variables: {
    // variables use a tuple where the first value is light mode,
    // and the second value is for dark mode
    colorBackground: ['#ffffff', '#212126'],
  },
  // Elements returns the dark mode selector allowing you to target
  // elements styles when in dark mode
  elements: darkModeSelector => {
    return {
      providerIcon__apple: {
        [darkModeSelector]: {
          filter: 'invert(1)',
        },
      },
    };
  },
});

Theme usage

import { clerk } from '@clerk/themes'

// Defaults to light mode
<ClerkProvider
  appearance={{
    theme: clerk,
  }}
/>

// Opt-in to dark mode via media query
<ClerkProvider
  appearance={{
    theme: clerk({
      darkModeSelector: '@media (prefers-color-scheme: dark)'
    }),
  }}
/>

// Opt-in to dark mode via css selector
<ClerkProvider
  appearance={{
    theme: clerk({
      darkModeSelector: '.dark'
    }),
  }}
/>

// Opt-out of dark mode functionality
<ClerkProvider
  appearance={{
    theme: clerk({
      darkModeSelector: false
    }),
  }}
/>

Resolves USER-2487

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced a new "clerk" theme with light and dark mode support, including custom color variables and UI element overrides.
    • Enhanced theming system to support tuple-based variables for light/dark modes and flexible element definitions.
    • Added global CSS injection for themes in supported components.
  • Bug Fixes

    • Improved handling and processing of theme variables and element overrides, especially for dark mode.
  • Tests

    • Added comprehensive unit tests for theme creation utilities, variable processing, and dark mode support.
  • Chores

    • Added and configured Vitest for testing in the themes package.
    • Updated build configuration to exclude test files from production builds.
  • Documentation

    • Updated type definitions to reflect new theme capabilities and global CSS support.

Copy link

changeset-bot bot commented Aug 1, 2025

🦋 Changeset detected

Latest commit: 678409f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 22 packages
Name Type
@clerk/clerk-js Minor
@clerk/themes Minor
@clerk/types Minor
@clerk/chrome-extension Patch
@clerk/clerk-expo Patch
@clerk/clerk-react Patch
@clerk/agent-toolkit Patch
@clerk/astro Patch
@clerk/backend Patch
@clerk/elements Patch
@clerk/expo-passkeys Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/localizations Patch
@clerk/nextjs Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/remix Patch
@clerk/shared Patch
@clerk/tanstack-react-start Patch
@clerk/testing Patch
@clerk/vue Patch

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

Copy link

vercel bot commented Aug 1, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
clerk-js-sandbox ✅ Ready (Inspect) Visit Preview 💬 Add feedback Aug 4, 2025 2:53pm

Copy link
Contributor

coderabbitai bot commented Aug 1, 2025

📝 Walkthrough

Walkthrough

This 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 packages/themes/src/createTheme.ts is refactored to support variables with light and dark values, function-based elements, and global CSS extraction. A new clerk theme is added, specifying color variables and dark mode element overrides. The package configuration is updated to support Vitest-based testing, and a new test suite is introduced to cover the theming utilities. Build configuration is refined to exclude test files, and type definitions are updated to allow global CSS strings within theme objects. The theming system's exports are expanded, and minor code cleanups are made.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Complexity: The review involves a major refactor of the theme creation system, introduction of new types and processing utilities, comprehensive new test coverage, and updates to package and build configurations. The changes span multiple files with high interdependency and require careful attention to type safety, CSS generation logic, and backwards compatibility.

Assessment against linked issues

Objective Addressed Explanation
Enable light/dark mode support for default theme (USER-2487)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Addition of Vitest testing setup and test files (packages/themes/vitest.config.ts, packages/themes/src/__tests__/createTheme.test.ts) These changes relate to testing infrastructure and coverage, which are not directly related to the dark mode theming objective.
Build configuration update to exclude test files (packages/themes/tsup.config.ts) This change concerns build optimization and is unrelated to the theming or dark mode support objective.
Minor export addition for the new clerk theme (packages/themes/src/themes/index.ts) This is a necessary addition to expose the new theme but is not directly related to the dark mode support objective itself.
Removal of a TypeScript directive comment in neobrutalism theme (packages/themes/src/themes/neobrutalism.ts) This is a minor cleanup unrelated to the theming or dark mode support objective.
Addition of global CSS injection in AppearanceProvider (packages/clerk-js/src/ui/customizables/AppearanceContext.tsx) While related to appearance, this change is outside the scope of the theming system changes and linked issue focused on default theme dark mode support.

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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_globalCss

The 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 properties

The 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 pattern

The 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

📥 Commits

Reviewing files that changed from the base of the PR and between 0b165f8 and a9a9ab3.

📒 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
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly 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
Use const assertions for literal types: as const
Use satisfies 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 ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly 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. The filter: '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 injection

The global CSS extraction and injection logic is well-implemented:

  • Correctly checks both appearance and globalAppearance themes
  • Conditionally renders the Global component only when CSS exists
  • Properly uses Emotion's css template literal for style injection
packages/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 generation

The 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 handling

The 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 flow

The 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

Copy link

pkg-pr-new bot commented Aug 1, 2025

Open in StackBlitz

@clerk/agent-toolkit

npm i https://pkg.pr.new/@clerk/agent-toolkit@6460

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@6460

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@6460

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@6460

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@6460

@clerk/dev-cli

npm i https://pkg.pr.new/@clerk/dev-cli@6460

@clerk/elements

npm i https://pkg.pr.new/@clerk/elements@6460

@clerk/clerk-expo

npm i https://pkg.pr.new/@clerk/clerk-expo@6460

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@6460

@clerk/express

npm i https://pkg.pr.new/@clerk/express@6460

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@6460

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@6460

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@6460

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@6460

@clerk/clerk-react

npm i https://pkg.pr.new/@clerk/clerk-react@6460

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@6460

@clerk/remix

npm i https://pkg.pr.new/@clerk/remix@6460

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@6460

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@6460

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@6460

@clerk/themes

npm i https://pkg.pr.new/@clerk/themes@6460

@clerk/types

npm i https://pkg.pr.new/@clerk/types@6460

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@6460

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@6460

commit: 678409f

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between bc3728f and e454587.

📒 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
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly 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
Use const assertions for literal types: as const
Use satisfies 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 ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly 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

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between e454587 and 1cf6ecc.

📒 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
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly 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
Use const assertions for literal types: as const
Use satisfies 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 ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly 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 mode
  • undefined 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 handling
  • mergeThemeConfigurations correctly merges configurations with child precedence
  • createThemeObject properly assembles the final theme with internal properties

The architecture supports both static and dynamic theme composition effectively.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 handling

The 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

📥 Commits

Reviewing files that changed from the base of the PR and between 1cf6ecc and e554956.

📒 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
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly 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
Use const assertions for literal types: as const
Use satisfies 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 ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly 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.

Comment on lines +217 to +224
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
}
}
Copy link
Contributor

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.

Suggested change
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.

Comment on lines +241 to +242
const resolvedBase = (baseTheme as any) || {};

Copy link
Contributor

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.

Suggested change
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.

Comment on lines +1 to +29
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',
},
},
};
},
});
Copy link
Member Author

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<
Copy link
Member Author

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?

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 with any casting

This 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 casting

Similar to the previous issue, this still casts baseTheme to any 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 explicit

The 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

📥 Commits

Reviewing files that changed from the base of the PR and between e554956 and 678409f.

📒 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
Avoid any type - prefer unknown when type is uncertain, then narrow with type guards
Use interface for object shapes that might be extended
Use type for unions, primitives, and computed types
Prefer readonly properties for immutable data structures
Use private for internal implementation details
Use protected for inheritance hierarchies
Use public explicitly for clarity in public APIs
Prefer readonly 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
Use const assertions for literal types: as const
Use satisfies 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 ...
No any types without justification
Proper error handling with typed errors
Consistent use of readonly 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 support

The 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 feedback

The 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 escaping

The 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 resolution

The 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 properties

The 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 implementation

The 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 promoted

The 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 };

Comment on lines +152 to +162
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;
}
Copy link
Contributor

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.

Suggested change
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.

Comment on lines +177 to +181
// 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;
}
Copy link
Member

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
Copy link
Member

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

Comment on lines +1002 to +1005
colorPrimary: ['#blue', '#darkblue'] as [string, string],
colorDanger: ['#red', '#darkred'] as [string, string],
colorSuccess: ['#green', '#darkgreen'] as [string, string],
colorNeutral: ['#gray', '#darkgray'] as [string, string],
Copy link
Member

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;
Copy link
Member

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

Comment on lines +64 to +80
* @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;
}
Copy link
Member

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.

Comment on lines +105 to +106
lightRules.push(` ${cssVarName}: ${lightValue};`);
darkRules.push(` ${cssVarName}: ${darkValue};`);
Copy link
Member

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;
Copy link
Member

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants