Skip to content

fix(react): back compat theming with context and add ThemeConsumer and createTheme - #712

Merged
owjs3901 merged 7 commits into
mainfrom
fix/compat-theme
Oct 14, 2026
Merged

owjs3901 merged 7 commits into
mainfrom
fix/compat-theme

Conversation

@owjs3901

@owjs3901 owjs3901 commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Refs #689 (C 네 번째 PR). main에서 분기했습니다.

문제

  • RT-21 / SC-07 / EMO-06: compat useTheme·withTheme가 ThemeProvider에 넘긴 theme이 아니라 빈 Proxy를 돌려줬습니다. t.colors.brand는 var(--colors-brand) 문자열이 됐고, JSON.stringify(t)·Object.keys(t)·{...t}는 모두 비어 있었습니다.
  • RT-20 / SC-05 (일부): 중첩된 ThemeProvider의 함수 theme(theme={outer => ...})과 객체 병합이 동작하지 않았습니다.
  • SC-06: ThemeConsumer가 없어 styled-components가 런타임 의존성으로 남았습니다.
  • SC-15: v6.4 createTheme도 alias와 타입이 없어 같은 문제가 있었습니다.
  • RT-23: withTheme이 ref를 넘기지 않았고, 호출자가 준 theme을 덮어썼으며, displayName이 없었습니다.

변경

  • compat ThemeProvider가 React context로 theme 객체를 내려줍니다.
    • 객체 theme은 바깥 theme 위에 병합합니다.
    • 함수 theme은 바깥 theme을 받아 새 theme을 만듭니다.
    • 스타일은 지금처럼 provider가 선언하는 CSS 변수를 읽으며, JS에서 읽는 값만 바뀝니다.
  • useTheme은 가장 가까운 provider의 theme 객체를 돌려주고, provider가 없으면 {}를 돌려줍니다.
  • withTheme
    • ref를 넘깁니다(forwardRef, React 18 호환).
    • 호출자가 theme을 주면 그 값이 우선합니다.
    • displayName은 WithTheme(Name) 형식입니다.
  • ThemeConsumer를 추가했습니다.
  • styled-components v6.4 createTheme을 호환 구현했습니다.
    • 각 leaf는 var(--sc-path, 값)이 됩니다.
    • GlobalStyle, raw, vars, resolve()를 제공합니다.
    • prefix/selector 옵션을 받습니다.
  • ThemeConsumer·createTheme import를 @devup-ui/react/compat로 옮기고, 타입 선언을 추가했습니다.
  • 쓰이지 않게 된 createThemeAccessor를 제거했습니다.

동작 변화

  • compat useTheme().x가 var(--x) 대신 provider가 준 실제 값을 돌려줍니다. 스타일에서 읽는 theme은 그대로 CSS 변수입니다.

새로 생기는 오류

  • 없습니다.

남은 항목

  • RT-20의 display:contents 래퍼는 아직 남아 있습니다. 변수 범위를 DOM 구조 없이 지정하려면 별도 설계가 필요해 K #693에 문서화할 예정입니다.
  • EMO-14: 토큰으로 바꿀 수 없는 theme 읽기를 빌드 에러로 만드는 작업
  • EMO-05: Emotion jsx/ClassNames/CacheProvider
  • 파일 4개에 걸린 Rust 1.99 clippy assert_is_empty 수정은 fix(extractor): let a later css() part replace an earlier one's declarations #706, #709와 같습니다. 그래서 나중에 병합되는 쪽에서 충돌이 날 수 있으며, 내용이 같으므로 어느 쪽을 택해도 됩니다.

검증

  • cargo clippy -D warnings(Rust 1.99), cargo test 통과. 새 테스트 test_styled_components_theme_apis_redirect_to_compat을 추가했습니다.
  • bun test 5481개 통과, 커버리지 100%. 새 브라우저 테스트는 useStyledTheme, withTheme/ThemeConsumer, 중첩 ThemeProvider, createTheme입니다.
  • bun lint 오류 0개. 경고 9개는 React 18 호환을 위해 쓴 forwardRef/useContext에 대한 것입니다(D4).

2026-10-08 업데이트: main 11790bef를 merge commit으로 병합했습니다; 충돌 파일: 없음; 충돌 없이 자동 병합했고, 이미 포함된 squash 원본은 조상 확인 후 ours 전략으로 중복 적용하지 않았습니다.

owjs3901 and others added 2 commits October 2, 2026 08:03
…d createTheme

Refs #689

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Refs #689

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Changepacks

@devup-ui/wasm@1.0.83 → 1.0.84 - bindings/devup-ui-wasm/package.json

Patch

  • The styled-components and Emotion compat ThemeProvider now gives its theme through React context: useTheme, withTheme and the new ThemeConsumer return the real theme object (an object merged over the outer theme, or what a theme function makes of it) instead of an empty Proxy of var() references, withTheme forwards refs, lets a caller's theme win and sets a displayName. Styles keep reading the CSS variables the provider declares. styled-components v6.4 createTheme is available from the compat entry, and ThemeConsumer and createTheme imports are redirected there instead of staying a runtime dependency on styled-components
  • style-order-range only checks styleOrder where the build reads it: a prop of a Devup UI component and a top-level key of a style object passed to css/globalCss/keyframes. A styleOrder key in nested data, in a prop value such as an element passed as a prop, or in a call of a function sharing a name with an Object.prototype member is no longer reported. The array, typography and media rules already skip non-style data, and css-utils-literal-only accepts a css()/keyframes() result held in a const inside a function. No new build errors.
  • Tailwind classes in a className: each class compiles on its own and every class Tailwind does not define (custom, plugin and analytics classes) stays as written, where any recognized class used to rebuild the whole className without them and could rewrite part of another class; a class touching a template interpolation (icon-${name}) is left as written, and template classNames no longer get spaces inserted around interpolations. A class whose variant is not understood stays as written instead of applying unconditionally; variants follow Tailwind v4: stacked variants compose (hover:focus: matches the element itself), hover is guarded by @media (hover: hover), group-/peer- use the group/peer marker classes (and group-hover/name), and data-, aria-, has-, nth-, , **, supports-[…] and [&…]/[@media …] variants compile. Utilities emit all their declarations (size-, rounded-t-, truncate, sr-only, not-sr-only, text-sm with its line height, text-sm/6, leading-), translate/rotate/scale/skew compose through Tailwind's individual properties and registered variables (@Property) with negative values kept negative, and arbitrary values follow Tailwind's grammar: spaces for underscores except in url() and var() names, _ for an underscore, spaces around math operators, type hints, [property:value] and (--variable) shorthands. Words that only share a utility's prefix (select-wrapper, order-summary, cursor-foo) are no longer compiled

@devup-ui/bun-plugin@1.0.22 → 1.0.23 - packages/bun-plugin/package.json

Patch

  • Bun plugin works with Bun.build: DevupUI() (from @devup-ui/bun-plugin/register) serves the generated stylesheet to the bundler's CSS loader once every other module is loaded, so the build emits a CSS output with the styles of the whole bundle instead of an empty module; class names are short there and readable under the runtime (debug option). Files importing the packages Devup UI takes the place of (@emotion/react, @emotion/styled, styled-components, @vanilla-extract/css), @stylexjs/stylex, or a subpath or re-export of these or @devup-ui/react, are compiled instead of skipped

@devup-ui/components@0.1.60 → 0.1.61 - packages/components/package.json

Patch

  • Auto-update: depends on '@devup-ui/react' via a local workspace dependency

@devup-ui/eslint-plugin@1.0.22 → 1.0.23 - packages/eslint-plugin/package.json

Patch

  • style-order-range only checks styleOrder where the build reads it: a prop of a Devup UI component and a top-level key of a style object passed to css/globalCss/keyframes. A styleOrder key in nested data, in a prop value such as an element passed as a prop, or in a call of a function sharing a name with an Object.prototype member is no longer reported. The array, typography and media rules already skip non-style data, and css-utils-literal-only accepts a css()/keyframes() result held in a const inside a function. No new build errors.

@devup-ui/next-plugin@1.0.90 → 1.0.91 - packages/next-plugin/package.json

Patch

  • Auto-update: depends on '@devup-ui/plugin-utils' via a local workspace dependency

@devup-ui/plugin-utils@1.0.17 → 1.0.18 - packages/plugin-utils/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles

@devup-ui/react@1.0.44 → 1.0.45 - packages/react/package.json

Patch

  • The styled-components and Emotion compat ThemeProvider now gives its theme through React context: useTheme, withTheme and the new ThemeConsumer return the real theme object (an object merged over the outer theme, or what a theme function makes of it) instead of an empty Proxy of var() references, withTheme forwards refs, lets a caller's theme win and sets a displayName. Styles keep reading the CSS variables the provider declares. styled-components v6.4 createTheme is available from the compat entry, and ThemeConsumer and createTheme imports are redirected there instead of staying a runtime dependency on styled-components

@devup-ui/reset-css@1.0.31 → 1.0.32 - packages/reset-css/package.json

Patch

  • Auto-update: depends on '@devup-ui/react' via a local workspace dependency

@devup-ui/rsbuild-plugin@1.0.67 → 1.0.68 - packages/rsbuild-plugin/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles

@devup-ui/vite-plugin@1.0.73 → 1.0.74 - packages/vite-plugin/package.json

Patch

  • Auto-update: depends on '@devup-ui/plugin-utils' via a local workspace dependency

@devup-ui/webpack-plugin@1.0.71 → 1.0.72 - packages/webpack-plugin/package.json

Patch

  • Webpack and Rsbuild builds hold every style in their stylesheets: a stylesheet module is built on its first import, so styles extracted later (the global styles of files webpack or Rsbuild reached late, often outside src) were missing from the output. The files the entries reach under src are now extracted before the build, the shared base on disk is kept current, and when a stylesheet still changed during the build it is compiled once more, writing only the complete result. Rsbuild serves each stylesheet from the current sheet instead of a module-wide string kept only when it grew, which left edits that shortened the CSS and later builds in the same process stale. plugin-utils adds computeReachableFiles

@codecov

codecov Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
libs/extractor/src/import_alias_visit.rs 100.00% <100.00%> (ø)
packages/react/src/compat/index.ts 100.00% <100.00%> (ø)
packages/react/src/components/ThemeProvider.tsx 100.00% <100.00%> (ø)
packages/react/src/hooks/use-styled-theme.ts 100.00% <100.00%> (ø)
packages/react/src/utils/create-theme.tsx 100.00% <100.00%> (ø)
packages/react/src/utils/theme-context.ts 100.00% <100.00%> (ø)
packages/react/src/utils/theme-vars.ts 100.00% <ø> (ø)
packages/react/src/utils/with-theme.tsx 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Merge main history while preserving this PR implementation. No manual conflict resolutions.
Merge main history while preserving this PR implementation. No manual conflict resolutions.
Merge main history while preserving this PR implementation. No manual conflict resolutions.
Merge main history while preserving this PR implementation. No manual conflict resolutions.
Merge main history while preserving this PR implementation. No manual conflict resolutions.
@owjs3901
owjs3901 merged commit 925e6c7 into main Oct 14, 2026
5 checks passed
@owjs3901
owjs3901 deleted the fix/compat-theme branch October 14, 2026 06:33
owjs3901 added a commit that referenced this pull request Oct 15, 2026
Refs #684

2026-10-15: 갱신된 부모 #730 b28816c2를 두 부모 병합으로 통합했습니다. package.json은 모든 타입 fixture를 검사하는 프로젝트 명령으로 합쳤고, styled-components 선언의 중복 re-export만 제거하면서 확장 가능한 테마와 브랜드 및 부모의 createTheme·정규 ClassNamesArg를 보존했습니다. 승인된 #712 타입 보정으로 theme 함수의 undefined outer, 함수형 ThemeConsumer, withTheme의 theme override/ref를 실제 부모 구현과 일치시켰습니다. 같은 새 타입 증명이 보정 전 13개 오류를 재현하고 보정 후 통과했으며, React 18/19의 실제 배포 dist 소비자 검사도 통과했습니다. 런타임·Rust·캡처·프로토콜 변경은 없습니다. 최신 main a6ecc12 반영은 코디네이터 지시에 따라 MAIN SETTLED 이후 전파 단계로 남겨 둡니다.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant