Skip to content

Feat: theme() base API implement #260#261

Merged
github-actions[bot] merged 2 commits intomainfrom
theme-base-api
Oct 22, 2025
Merged

Feat: theme() base API implement #260#261
github-actions[bot] merged 2 commits intomainfrom
theme-base-api

Conversation

@black7375
Copy link
Contributor

@black7375 black7375 commented Oct 22, 2025

Description

theme base api

Related Issue

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive theming system supporting design tokens including colors, dimensions, font families, font weights, durations, and cubic-bezier curves
    • Added CSS variable generation and theme resolution capabilities
  • Refactor

    • Restructured public API exports to provide direct access to theming utilities and type definitions

Additional context

Checklist

@changeset-bot
Copy link

changeset-bot bot commented Oct 22, 2025

⚠️ No Changeset found

Latest commit: c1226f2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions
Copy link
Contributor

Triggered from #261 by @​black7375.

Checking if we can fast forward main (51133c5) to theme-base-api (c1226f2).

Target branch (main):

commit 51133c5b8bc0f9e42d2fe8172000a9e5e6487a75 (HEAD -> main, origin/main)
Author: alstjr7375 <alstjr7375@daum.net>
Date:   Sun Sep 14 00:00:00 2025 +0900

    Chore: refactor dependency ci

Pull request (theme-base-api):

commit c1226f284266fc1d0fa9865e486dd8f2a3a7ab9e (pull_request/theme-base-api)
Author: alstjr7375 <alstjr7375@daum.net>
Date:   Tue Sep 16 00:00:00 2025 +0900

    Feat: `theme()` support dtcg format #260

It is possible to fast forward main (51133c5) to theme-base-api (c1226f2). If you have write access to the target repository, you can add a comment with /fast-forward to fast forward main to theme-base-api.

@coderabbitai
Copy link

coderabbitai bot commented Oct 22, 2025

Walkthrough

Replaces vanilla-extract's theme creation functions with a new custom theming system using CSS variables. Introduces globalTheme and theme functions, comprehensive token type definitions supporting color, dimension, typography, animation, and composite tokens, and adds a camelToKebab utility for CSS variable naming conventions.

Changes

Cohort / File(s) Summary
Export API reorganization
packages/css/src/index.ts
Removes re-exports of createGlobalTheme, createGlobalThemeContract, createTheme, createThemeContract from vanilla-extract. Adds exports for globalTheme and theme functions, plus 13 token-related types from new theme modules.
Core theme implementation
packages/css/src/theme/index.ts
Implements globalTheme(selector, tokens) and theme(tokens, debugId?) functions that generate CSS variables, manage token resolution, support @layer directives, and handle nested token structures with type guards and path-to-CSS-var conversion utilities.
Theme type system
packages/css/src/theme/types.ts
Defines comprehensive token type hierarchy: Theme, ThemeValue, TokenDefinition, and specialized definitions for colors, dimensions, fonts, durations, cubic-beziers, and numbers. Includes ResolveTheme types for mapping token structures to CSS variable outputs.
Utility functions
packages/css/src/utils.ts
Adds camelToKebab(camelCase: string) utility for converting camelCase to kebab-case CSS variable naming.
Type re-exports
packages/transform-to-vanilla/src/index.ts
Re-exports StrictNonNullableString and StrictNonNullableNumber type aliases from @mincho-js/csstype.

Sequence Diagram

sequenceDiagram
    participant User
    participant theme as theme()
    participant globalTheme as globalTheme()
    participant CSS as globalCss()
    participant VE as Vanilla Extract

    User->>theme: theme(tokens, debugId?)
    activate theme
    theme->>theme: Generate unique className
    theme->>globalTheme: Call with selector=className
    deactivate theme
    activate globalTheme
    globalTheme->>globalTheme: assignTokens(tokens)<br/>Extract CSS vars & resolve values
    globalTheme->>globalTheme: Handle @layer if present
    globalTheme->>CSS: Apply global CSS rule<br/>with vars & selectors
    CSS->>VE: Register CSS rule
    globalTheme-->>theme: Return resolvedTokens
    deactivate globalTheme
    theme-->>User: Return [className, tokens]
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

The PR introduces substantial new logic across multiple files including a complete theming system with recursive token resolution, CSS variable generation, type definitions for diverse token categories, and test scaffolding. While individual functions follow clear patterns, the overall architectural scope and interaction between components require careful verification.

Possibly related PRs

  • Chore: Update package.json for publish #60 #64: Introduces @mincho-js/csstype as a dependency and upgrades related package metadata; this PR builds on that foundation by re-exporting types from csstype within the transform-to-vanilla module.

Poem

🐰 A hop and a skip, new themes take flight,
CSS variables dancing in the night,
Tokens resolve with a flourish so grand,
Types ensure we're always on brand! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request description does not adequately meet the template requirements. While the Related Issue section is properly filled with "#260," the critical Description section contains only "theme base api"—two vague words that essentially restate the title without providing the clear and concise explanation required by the template. The Additional context and Checklist sections are completely empty with only placeholder comments. The description lacks meaningful information about what the PR accomplishes, why it was needed, implementation details, or guidance for reviewers.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "Feat: theme() base API implement #260" directly addresses the primary change in the pull request, which is the implementation of a new theme base API system. The changes include creating globalTheme() and theme() functions, establishing a comprehensive type system for theming tokens, and updating the public exports accordingly. The title is concise, specific, and clearly communicates the main objective without unnecessary verbosity or vagueness.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch theme-base-api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@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 (5)
packages/css/src/utils.ts (1)

31-33: Deduplicate camelToKebab across packages and align vendor prefix handling.

This implementation is correct, but we now have similar logic in transform-to-vanilla. Consider centralizing to a shared util to avoid drift and add cases like "MsTransform" → "-ms-transform" to mirror your kebabToCamel special-case.

packages/css/src/theme/index.ts (3)

132-141: Loosen TokenDimensionValue guard (avoid brittle key-count check).

Object.keys(value).length === 2 can misclassify valid objects (e.g., metadata). Check shape only.

 function isTokenUnitValue(value: unknown): value is TokenDimensionValue {
-  return (
-    typeof value === "object" &&
-    value != null &&
-    "value" in value &&
-    "unit" in value &&
-    typeof value.value === "number" &&
-    typeof value.unit === "string" &&
-    Object.keys(value).length === 2
-  );
+  return (
+    typeof value === "object" &&
+    value != null &&
+    "value" in (value as any) &&
+    "unit" in (value as any) &&
+    typeof (value as any).value === "number" &&
+    typeof (value as any).unit === "string"
+  );
 }

210-226: Color extractor is too generic for modern color spaces.

Commas won’t be valid for color() spaces (e.g., srgb, oklch). Prefer:

  • rgb()/hsl(): comma or space forms
  • others: color(${space} ${components.join(' ')} / alpha)
    Add tests for TokenColorValue objects.

281-283: dimension case calls extractDurationValue with wrong type; add dedicated extractor.

Types mismatch; create an extractDimensionValue and use it here.

-    case "dimension":
-      // TokenDimensionValue is already handled by existing logic
-      return extractDurationValue($value as TokenDurationValue); // Same structure
+    case "dimension":
+      return extractDimensionValue($value as TokenDimensionValue);

Add near other extractors:

function extractDimensionValue(value: TokenDimensionValue): CSSVarValue {
  return `${value.value}${value.unit}` as CSSVarValue;
}
packages/css/src/theme/types.ts (1)

50-64: Consider narrowing units to specific string unions.

TokenDimensionValue.unit and TokenDurationValue.unit are plain string; narrowing to "px" | "rem" and "ms" | "s" would improve safety.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51133c5 and c1226f2.

📒 Files selected for processing (5)
  • packages/css/src/index.ts (1 hunks)
  • packages/css/src/theme/index.ts (1 hunks)
  • packages/css/src/theme/types.ts (1 hunks)
  • packages/css/src/utils.ts (1 hunks)
  • packages/transform-to-vanilla/src/index.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
packages/css/src/utils.ts (2)
packages/css-additional-types/codegen/utils.ts (1)
  • kebabToCamel (2-13)
packages/transform-to-vanilla/src/utils/string.ts (2)
  • camelToKebab (2-4)
  • convertToCSSVar (6-10)
packages/css/src/theme/index.ts (4)
packages/css/src/theme/types.ts (12)
  • Theme (9-11)
  • ResolveTheme (152-158)
  • TokenPrimitiveValue (148-148)
  • TokenDimensionValue (55-64)
  • TokenCompositeValue (139-142)
  • TokenDefinition (19-23)
  • ThemeValue (13-17)
  • TokenFontFamilyValue (71-71)
  • TokenDurationValue (114-123)
  • TokenColorValue (30-47)
  • TokenFontWeightValue (78-107)
  • TokenValue (138-138)
packages/css/src/types.ts (1)
  • Resolve (1-3)
packages/transform-to-vanilla/src/types/style-rule.ts (3)
  • PureCSSVarKey (117-117)
  • PureCSSVarFunction (122-124)
  • CSSVarValue (119-119)
packages/css/src/utils.ts (1)
  • camelToKebab (31-33)
packages/css/src/theme/types.ts (2)
packages/css/src/types.ts (1)
  • Resolve (1-3)
packages/transform-to-vanilla/src/types/style-rule.ts (1)
  • PureCSSVarFunction (122-124)
🔇 Additional comments (2)
packages/css/src/index.ts (1)

42-60: Review comment is based on incorrect assumptions; no breaking changes detected.

The code snippet (lines 42-60) shows custom mincho-js theming exports (globalTheme, theme), not removal of vanilla-extract theme utilities. The vanilla-extract functions like createTheme, createGlobalTheme, etc. are not exported from the main index.ts and were never removed—they remain available in compat.ts. No codebase usage expects these functions from @mincho-js/css, and no breaking change occurs here.

Likely an incorrect or invalid review comment.

packages/transform-to-vanilla/src/index.ts (1)

9-12: Type alias re-exports look fine; confirm upstream types exist.

Aliasing NonNullableString/NonNullableNumber to “Strict” is clear. Just ensure @mincho-js/csstype indeed exports both names to avoid CI/type breaks.

Comment on lines +56 to +66
export function theme<ThemeTokens extends Theme>(
tokens: WithOptionalLayer<ThemeTokens>,
debugId?: string
): [string, Resolve<ResolveTheme<ThemeTokens>>] {
const themeClassName = generateIdentifier(debugId);
registerClassName(themeClassName, getFileScope());

const resolvedTokens = globalTheme(themeClassName, tokens);

return [themeClassName, resolvedTokens];
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

theme(): ensure selector format passed to globalTheme is correct.

You pass the raw class name. If globalCss expects a CSS selector, this should be .${themeClassName}.


🏁 Script executed:

#!/bin/bash
# Inspect globalCss signature and usage to confirm expected selector format
rg -nP -C3 'export\s+function\s+globalCss\s*\(\s*([a-zA-Z_$][\w$]*)\s*:' packages/css/src/css
rg -nP -C2 '\bglobalCss\s*\(' --type=ts

Length of output: 4012



---

🏁 Script executed:

```shell
#!/bin/bash
# Find globalTheme function definition and understand its selector parameter
rg -nP 'export\s+function\s+globalTheme\s*\(' packages/css/src --type=ts -A5
# Also look for how selector is used inside globalTheme
rg -nP '\bglobalTheme\s*\(' packages/css/src --type=ts -B2 -A2

Length of output: 334



---

🏁 Script executed:

```shell
#!/bin/bash
# Check imports and broader search for globalTheme
head -20 packages/css/src/theme/index.ts
rg -nP 'globalTheme' packages/css/src --type=ts -B1 -A3

Length of output: 1465



---

🏁 Script executed:

```shell
#!/bin/bash
# Get the full globalTheme implementation
sed -n '32,54p' packages/css/src/theme/index.ts

Length of output: 555


theme(): format the class name as a CSS selector before passing to globalTheme.

The themeClassName is a generated identifier like "m1a2b3c", which is not a valid CSS selector. It should be passed as .${themeClassName} to properly target the class. The selector is passed through globalTheme() directly to globalCss(), which expects valid CSS selector syntax.

🤖 Prompt for AI Agents
In packages/css/src/theme/index.ts around lines 56–66, the generated
themeClassName (e.g. "m1a2b3c") is not a valid CSS selector when passed to
globalTheme; prefix the class name with a dot (i.e. pass `.${themeClassName}` to
globalTheme) while keeping registerClassName(themeClassName, ...) unchanged so
registration uses the raw identifier, and ensure you don't double-prefix if the
value is already a selector.

Comment on lines +395 to +402
for (const [propKey, propValue] of Object.entries(value)) {
if (propKey === "resolved") continue;

const propPath = `${varPath}-${camelToKebab(propKey)}`;
const propCssVar = pathToCSSVar(propPath);
vars[propCssVar] = extractCSSValue(propValue as TokenValue);
resolvedComposite[propKey] = pathToVarReference(propPath);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Arrays inside TokenCompositeValue properties will throw. Handle them explicitly.

Currently extractCSSValue(array) throws. Support arrays on composite props like you do for top-level arrays.

-    for (const [propKey, propValue] of Object.entries(value)) {
+    for (const [propKey, propValue] of Object.entries(value)) {
       if (propKey === "resolved") continue;
-
-      const propPath = `${varPath}-${camelToKebab(propKey)}`;
-      const propCssVar = pathToCSSVar(propPath);
-      vars[propCssVar] = extractCSSValue(propValue as TokenValue);
-      resolvedComposite[propKey] = pathToVarReference(propPath);
+      const propPath = `${varPath}-${camelToKebab(propKey)}`;
+      if (Array.isArray(propValue)) {
+        // index each array item
+        propValue.forEach((item, idx) => {
+          const indexedPath = `${propPath}-${idx}`;
+          const cssVar = pathToCSSVar(indexedPath);
+          vars[cssVar] = extractCSSValue(item as TokenValue);
+        });
+        // Expose array of var()s to mirror shape
+        resolvedComposite[propKey] = propValue.map((_, idx) =>
+          pathToVarReference(`${propPath}-${idx}`)
+        ) as unknown as PureCSSVarFunction;
+      } else {
+        const propCssVar = pathToCSSVar(propPath);
+        vars[propCssVar] = extractCSSValue(propValue as TokenValue);
+        resolvedComposite[propKey] = pathToVarReference(propPath);
+      }
     }

Add tests for arrays within composites.

Comment on lines +180 to +184
type ResolveCompositeValue<T extends TokenCompositeValue> = Resolve<
{ resolved: PureCSSVarFunction } & {
[K in Exclude<keyof T, "resolved">]: PureCSSVarFunction;
}
>;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make resolved readonly in ResolveCompositeValue to match getter semantics.

Aligns with TokenCompositeValue’s getter and your test expectations.

-type ResolveCompositeValue<T extends TokenCompositeValue> = Resolve<
-  { resolved: PureCSSVarFunction } & {
+type ResolveCompositeValue<T extends TokenCompositeValue> = Resolve<
+  { readonly resolved: PureCSSVarFunction } & {
     [K in Exclude<keyof T, "resolved">]: PureCSSVarFunction;
   }
 >;
📝 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
type ResolveCompositeValue<T extends TokenCompositeValue> = Resolve<
{ resolved: PureCSSVarFunction } & {
[K in Exclude<keyof T, "resolved">]: PureCSSVarFunction;
}
>;
type ResolveCompositeValue<T extends TokenCompositeValue> = Resolve<
{ readonly resolved: PureCSSVarFunction } & {
[K in Exclude<keyof T, "resolved">]: PureCSSVarFunction;
}
>;
🤖 Prompt for AI Agents
In packages/css/src/theme/types.ts around lines 180 to 184, the
ResolveCompositeValue type should mark the resolved property as readonly to
match the getter semantics on TokenCompositeValue and tests; change the
intersection so that the resolved property is declared readonly (e.g. readonly
resolved: PureCSSVarFunction) while leaving the other indexed properties as-is.

@black7375
Copy link
Contributor Author

/fast-forward

@github-actions
Copy link
Contributor

Triggered from #261 (comment) by @​black7375.

Trying to fast forward main (51133c5) to theme-base-api (c1226f2).

Target branch (main):

commit 51133c5b8bc0f9e42d2fe8172000a9e5e6487a75 (HEAD -> main, origin/main)
Author: alstjr7375 <alstjr7375@daum.net>
Date:   Sun Sep 14 00:00:00 2025 +0900

    Chore: refactor dependency ci

Pull request (theme-base-api):

commit c1226f284266fc1d0fa9865e486dd8f2a3a7ab9e (pull_request/theme-base-api)
Author: alstjr7375 <alstjr7375@daum.net>
Date:   Tue Sep 16 00:00:00 2025 +0900

    Feat: `theme()` support dtcg format #260

Fast forwarding main (51133c5) to theme-base-api (c1226f2).

$ git push origin c1226f284266fc1d0fa9865e486dd8f2a3a7ab9e:main
To https://github.com/mincho-js/mincho.git
   51133c5..c1226f2  c1226f284266fc1d0fa9865e486dd8f2a3a7ab9e -> main

@github-actions github-actions bot merged commit c1226f2 into main Oct 22, 2025
11 of 12 checks passed
@github-actions github-actions bot deleted the theme-base-api branch October 22, 2025 17:16
@coderabbitai coderabbitai bot mentioned this pull request Nov 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant