0.8.1
Patch Changes
- #748
bde6d1bThanks @thevuong! - Mirror the committed appearance ref in a layout effect so a programmaticsetAppearancelanding before the passive flush compares against the just-committed appearance instead of the previous one.
0.8.0
Minor Changes
#745
5cbb93aThanks @thevuong! - fix(theme): harden appearance provider/script and tighten public typesThree correctness fixes plus two type-safety tightenings:
- Overlapping
setAppearancecalls withdisableTransitionenabled could orphan a transition-suppression<style>—enableTransitionsRefwas overwritten before the previous cleanup ran — leaving*{transition:none}in<head>forever and permanently disabling CSS transitions. Each call now flushes the pending suppression before injecting a new one. AppearanceScriptnow runtime-validates itsappearanceprop (asAppearanceProvideralready did), so an invalid value can no longer be written to<html>before first paint or diverge from the provider's fallback.applyColorSchemeno-ops during SSR instead of throwing onwindowaccess, matching its siblingsuppressTransitions.- Public props on
AppearanceProviderandAppearanceScriptnow accept explicitundefined(?: T | undefined) underexactOptionalPropertyTypes, so JSX callers can pass possibly-undefined expressions such asnonceandappearance.
Breaking (type surface only, no runtime behavior change):
appearancesis nowReadonlyArray<Appearance>(wasArray<Appearance>), so assigning it to a mutableAppearance[]is now a compile error.- Overlapping
0.7.0
0.6.2
0.6.1
0.6.0
Patch Changes
#690
f4b1aa6Thanks @thevuong! - Report the failures that were being swallowed or mislabelled, and derive the types the build emits.@codefast/di:@inject,@postConstructand@preDestroyon a static member now throwStaticMemberDecoratorErrorinstead ofInternalError. All three act on one instance, so this is caller misuse — andInternalErrormeans the library broke, which sent anyone catching it to file a bug against their own mistake. SPEC §10 already recorded that mistake for predicate ambiguity.AsyncResolutionErrornames the token the caller asked for and the token whose factory is async, which is what SPEC has always specified. Every throw site passed the same token twice, so the message read "Token 'X' requires async resolution because 'X' in its dependency chain has an async factory"; aresolve(App)that fails on an asyncDatabasenow says so.asyncSourceTokendefaults totokenNamefor the case where the requested binding is itself the source.- A
MetadataReaderthat names a@postConstruct/@preDestroymethod the instance does not have raisesInvalidMetadataErrorinstead of skipping the hook — a hook that silently never runs is the failure a caller cannot see.InvalidMetadataError's message no longer says "constructor", since it now covers both answers; the specifics moved intoreason. MissingScopeContextErrorfromScopeManagernames its token instead of"(unknown)", and the scoped read takes one map lookup where it took two.Token,ConstructorandInjectionDescriptordeclareout Value, so the compiler checks the covariance the engine already relied on.
Repo-wide:
isolatedDeclarationsis on for every package that emits declarations, so a public type can always be written down from the source file alone.allowJsis gone from the shared base config — no package has JavaScript sources.@codefast/themeand@codefast/trackinggained explicit annotations on four exported constants to satisfy it; the emitted types are unchanged.@codefast/uiand@codefast/benchmark-vieweropt out for reasons recorded in their configs.
0.5.0
Minor Changes
c634b17Thanks @thevuong! - RenameAppearanceContextTypetoAppearanceContextValue— the "Type" suffix carried no information (every exported type is a type), while "Value" states the role: the payload provided throughAppearanceContextand returned byuseAppearance.#547
c7d6818Thanks @thevuong! - Add astorageKeyprop toAppearanceProviderfor a fully client-side color scheme path (no server framework, cookie, or loader). When set, the provider reads the persisted preference fromlocalStoragein its initial client render, auto-persists changes there (unless an explicitpersistColorSchemeis given), and syncs across tabs via thestorageevent. Paired with<AppearanceScript storageKey>using the same key, the inline script applies the stored value before first paint — so there is no flash even on statically prerendered / CDN-served pages, where an httpOnly cookie can't reach the pre-paint script and a post-mount server round-trip would otherwise flip the appearance.AppearanceScriptandAppearanceProvidernow also mirror the preference (light/dark/automatic) to adata-appearanceattribute on<html>— set by the inline script before first paint and kept in sync by the provider. This lets preference-aware UI (e.g. a 3-state theme toggle that shows a distinct "system" icon) render the correct state purely from CSS, with no hydration flash from reading client-only state.#526
fad5a21Thanks @thevuong! - Add the@codefast/theme/viteplugin. TanStack Start registers the server functions shipped in@codefast/theme/startat the consumer's build time, so the package must not be externalized for SSR nor pre-bundled for the client. The newcodefastTheme()plugin applies that configuration automatically, so consumers no longer need to hand-writessr.noExternal/optimizeDeps.exclude.
Patch Changes
#676
641e233Thanks @thevuong! - Collapse thetypesanddefaultlanes ofpackage.json#importsfrom fallback arrays to single strings.Node resolves an imports array by taking the first candidate it can parse, without checking that the file exists and without falling through — a specifier whose first candidate is missing throws
ERR_MODULE_NOT_FOUNDrather than trying the second../dist/*/index.jsand./dist/*/index.d.tscould therefore never be reached, so they read as a safety net that does not exist. Thesourcelane keeps its extension candidates, which onlytscand Vite read and both probe.#550
0feb8cdThanks @thevuong! - Rename the public API to follow Apple's Human Interface Guidelines vocabulary: appearance is the user's preference ("light" | "dark" | "automatic", like macOS System Settings → Appearance), color scheme is the resolved light-or-dark value actually applied (like SwiftUIColorSchemeand CSScolor-scheme). Source files are also flattened so each subpath names what it exports.Breaking renames:
Types:
ColorScheme→Appearance;ResolvedColorScheme→ColorScheme;ColorSchemeContextType→AppearanceContextType;colorSchemeSchema→appearanceSchema;colorSchemes→appearances.Hook:
useColorScheme()→useAppearance(), returning{ appearance, colorScheme, setAppearance, isPending }(was{ colorScheme, resolvedColorScheme, setColorScheme, isPending }).Provider props:
colorScheme→appearance;persistColorScheme→persistAppearance. Script prop:colorScheme→appearance. Context:ColorSchemeContext→AppearanceContext.Constants:
DEFAULT_COLOR_SCHEME→DEFAULT_APPEARANCE("automatic");DEFAULT_RESOLVED_COLOR_SCHEME→DEFAULT_COLOR_SCHEME("dark"). ⚠️DEFAULT_COLOR_SCHEMEstill exists but now means the resolved fallback — the prop renames make stale usage a compile error.Subpaths:
./types→./appearance;./core/provider→./appearance-provider;./core/use-theme→./use-appearance;./core/context→./appearance-context;./script/theme-script→./appearance-script;./utils/system→./color-scheme;./utils/dom→./dom.Persisted values: the default
STORAGE_KEYis now"ui-appearance"(was"ui-theme") — returning visitors fall back to"automatic"once. The internalBroadcastChannelis now"appearance-sync"(was"color-scheme-sync").
Unchanged:
AppearanceProvider/AppearanceScriptcomponent names, thedata-appearanceattribute,resolveColorScheme()/getSystemColorScheme()/applyColorScheme()/suppressTransitions().#549
2d16644Thanks @thevuong! - Remove all server-side handling — the color scheme preference now lives entirely on the client inlocalStorage.Breaking:
- Removed
@codefast/theme/start,@codefast/theme/vite, and@codefast/theme/adapters/tanstack-start/server— the TanStack Start server functions, httpOnly cookie persistence, and the Vite plugin that wired them into consumer builds. - Removed
AppearanceProvider'sssrColorSchemeandsyncFromServerprops; the provider also no longer re-syncs from a changedcolorSchemeprop after mount (it is a static fallback now). resolveColorScheme()no longer takes anssrColorSchemeargument; on the server,"automatic"resolves toDEFAULT_RESOLVED_COLOR_SCHEME.- Dropped the optional
@tanstack/react-startandvitepeer dependencies.
Changed defaults:
storageKeynow defaults toSTORAGE_KEY("ui-appearance") andcolorSchemetoDEFAULT_COLOR_SCHEME("automatic") on bothAppearanceScriptandAppearanceProvider, so<AppearanceScript />+<AppearanceProvider>work with zero configuration.persistColorScheme/onPersistErrorremain for custom persistence.Migration: drop the root loader and the
codefastTheme()Vite plugin, render the defaults on<html>withsuppressHydrationWarning, and let the inline script correct the class before paint — see the README's TanStack Start recipe.- Removed
0.5.0-canary.9
Patch Changes
#676
641e233Thanks @thevuong! - Collapse thetypesanddefaultlanes ofpackage.json#importsfrom fallback arrays to single strings.Node resolves an imports array by taking the first candidate it can parse, without checking that the file exists and without falling through — a specifier whose first candidate is missing throws
ERR_MODULE_NOT_FOUNDrather than trying the second../dist/*/index.jsand./dist/*/index.d.tscould therefore never be reached, so they read as a safety net that does not exist. Thesourcelane keeps its extension candidates, which onlytscand Vite read and both probe.
0.5.0-canary.8
0.5.0-canary.7
0.5.0-canary.6
Minor Changes
c634b17Thanks @thevuong! - RenameAppearanceContextTypetoAppearanceContextValue— the "Type" suffix carried no information (every exported type is a type), while "Value" states the role: the payload provided throughAppearanceContextand returned byuseAppearance.#547
c7d6818Thanks @thevuong! - Add astorageKeyprop toAppearanceProviderfor a fully client-side color scheme path (no server framework, cookie, or loader). When set, the provider reads the persisted preference fromlocalStoragein its initial client render, auto-persists changes there (unless an explicitpersistColorSchemeis given), and syncs across tabs via thestorageevent. Paired with<AppearanceScript storageKey>using the same key, the inline script applies the stored value before first paint — so there is no flash even on statically prerendered / CDN-served pages, where an httpOnly cookie can't reach the pre-paint script and a post-mount server round-trip would otherwise flip the appearance.AppearanceScriptandAppearanceProvidernow also mirror the preference (light/dark/automatic) to adata-appearanceattribute on<html>— set by the inline script before first paint and kept in sync by the provider. This lets preference-aware UI (e.g. a 3-state theme toggle that shows a distinct "system" icon) render the correct state purely from CSS, with no hydration flash from reading client-only state.#526
fad5a21Thanks @thevuong! - Add the@codefast/theme/viteplugin. TanStack Start registers the server functions shipped in@codefast/theme/startat the consumer's build time, so the package must not be externalized for SSR nor pre-bundled for the client. The newcodefastTheme()plugin applies that configuration automatically, so consumers no longer need to hand-writessr.noExternal/optimizeDeps.exclude.
Patch Changes
#550
0feb8cdThanks @thevuong! - Rename the public API to follow Apple's Human Interface Guidelines vocabulary: appearance is the user's preference ("light" | "dark" | "automatic", like macOS System Settings → Appearance), color scheme is the resolved light-or-dark value actually applied (like SwiftUIColorSchemeand CSScolor-scheme). Source files are also flattened so each subpath names what it exports.Breaking renames:
Types:
ColorScheme→Appearance;ResolvedColorScheme→ColorScheme;ColorSchemeContextType→AppearanceContextType;colorSchemeSchema→appearanceSchema;colorSchemes→appearances.Hook:
useColorScheme()→useAppearance(), returning{ appearance, colorScheme, setAppearance, isPending }(was{ colorScheme, resolvedColorScheme, setColorScheme, isPending }).Provider props:
colorScheme→appearance;persistColorScheme→persistAppearance. Script prop:colorScheme→appearance. Context:ColorSchemeContext→AppearanceContext.Constants:
DEFAULT_COLOR_SCHEME→DEFAULT_APPEARANCE("automatic");DEFAULT_RESOLVED_COLOR_SCHEME→DEFAULT_COLOR_SCHEME("dark"). ⚠️DEFAULT_COLOR_SCHEMEstill exists but now means the resolved fallback — the prop renames make stale usage a compile error.Subpaths:
./types→./appearance;./core/provider→./appearance-provider;./core/use-theme→./use-appearance;./core/context→./appearance-context;./script/theme-script→./appearance-script;./utils/system→./color-scheme;./utils/dom→./dom.Persisted values: the default
STORAGE_KEYis now"ui-appearance"(was"ui-theme") — returning visitors fall back to"automatic"once. The internalBroadcastChannelis now"appearance-sync"(was"color-scheme-sync").
Unchanged:
AppearanceProvider/AppearanceScriptcomponent names, thedata-appearanceattribute,resolveColorScheme()/getSystemColorScheme()/applyColorScheme()/suppressTransitions().#549
2d16644Thanks @thevuong! - Remove all server-side handling — the color scheme preference now lives entirely on the client inlocalStorage.Breaking:
- Removed
@codefast/theme/start,@codefast/theme/vite, and@codefast/theme/adapters/tanstack-start/server— the TanStack Start server functions, httpOnly cookie persistence, and the Vite plugin that wired them into consumer builds. - Removed
AppearanceProvider'sssrColorSchemeandsyncFromServerprops; the provider also no longer re-syncs from a changedcolorSchemeprop after mount (it is a static fallback now). resolveColorScheme()no longer takes anssrColorSchemeargument; on the server,"automatic"resolves toDEFAULT_RESOLVED_COLOR_SCHEME.- Dropped the optional
@tanstack/react-startandvitepeer dependencies.
Changed defaults:
storageKeynow defaults toSTORAGE_KEY("ui-appearance") andcolorSchemetoDEFAULT_COLOR_SCHEME("automatic") on bothAppearanceScriptandAppearanceProvider, so<AppearanceScript />+<AppearanceProvider>work with zero configuration.persistColorScheme/onPersistErrorremain for custom persistence.Migration: drop the root loader and the
codefastTheme()Vite plugin, render the defaults on<html>withsuppressHydrationWarning, and let the inline script correct the class before paint — see the README's TanStack Start recipe.- Removed
1.0.0-canary.7
1.0.0-canary.6
0.5.0-canary.5
0.5.0-canary.4
Minor Changes
c634b17Thanks @thevuong! - RenameAppearanceContextTypetoAppearanceContextValue— the "Type" suffix carried no information (every exported type is a type), while "Value" states the role: the payload provided throughAppearanceContextand returned byuseAppearance.
0.5.0-canary.3
0.5.0-canary.2
Patch Changes
#550
0feb8cdThanks @thevuong! - Rename the public API to follow Apple's Human Interface Guidelines vocabulary: appearance is the user's preference ("light" | "dark" | "automatic", like macOS System Settings → Appearance), color scheme is the resolved light-or-dark value actually applied (like SwiftUIColorSchemeand CSScolor-scheme). Source files are also flattened so each subpath names what it exports.Breaking renames:
Types:
ColorScheme→Appearance;ResolvedColorScheme→ColorScheme;ColorSchemeContextType→AppearanceContextType;colorSchemeSchema→appearanceSchema;colorSchemes→appearances.Hook:
useColorScheme()→useAppearance(), returning{ appearance, colorScheme, setAppearance, isPending }(was{ colorScheme, resolvedColorScheme, setColorScheme, isPending }).Provider props:
colorScheme→appearance;persistColorScheme→persistAppearance. Script prop:colorScheme→appearance. Context:ColorSchemeContext→AppearanceContext.Constants:
DEFAULT_COLOR_SCHEME→DEFAULT_APPEARANCE("automatic");DEFAULT_RESOLVED_COLOR_SCHEME→DEFAULT_COLOR_SCHEME("dark"). ⚠️DEFAULT_COLOR_SCHEMEstill exists but now means the resolved fallback — the prop renames make stale usage a compile error.Subpaths:
./types→./appearance;./core/provider→./appearance-provider;./core/use-theme→./use-appearance;./core/context→./appearance-context;./script/theme-script→./appearance-script;./utils/system→./color-scheme;./utils/dom→./dom.Persisted values: the default
STORAGE_KEYis now"ui-appearance"(was"ui-theme") — returning visitors fall back to"automatic"once. The internalBroadcastChannelis now"appearance-sync"(was"color-scheme-sync").
Unchanged:
AppearanceProvider/AppearanceScriptcomponent names, thedata-appearanceattribute,resolveColorScheme()/getSystemColorScheme()/applyColorScheme()/suppressTransitions().#549
2d16644Thanks @thevuong! - Remove all server-side handling — the color scheme preference now lives entirely on the client inlocalStorage.Breaking:
- Removed
@codefast/theme/start,@codefast/theme/vite, and@codefast/theme/adapters/tanstack-start/server— the TanStack Start server functions, httpOnly cookie persistence, and the Vite plugin that wired them into consumer builds. - Removed
AppearanceProvider'sssrColorSchemeandsyncFromServerprops; the provider also no longer re-syncs from a changedcolorSchemeprop after mount (it is a static fallback now). resolveColorScheme()no longer takes anssrColorSchemeargument; on the server,"automatic"resolves toDEFAULT_RESOLVED_COLOR_SCHEME.- Dropped the optional
@tanstack/react-startandvitepeer dependencies.
Changed defaults:
storageKeynow defaults toSTORAGE_KEY("ui-appearance") andcolorSchemetoDEFAULT_COLOR_SCHEME("automatic") on bothAppearanceScriptandAppearanceProvider, so<AppearanceScript />+<AppearanceProvider>work with zero configuration.persistColorScheme/onPersistErrorremain for custom persistence.Migration: drop the root loader and the
codefastTheme()Vite plugin, render the defaults on<html>withsuppressHydrationWarning, and let the inline script correct the class before paint — see the README's TanStack Start recipe.- Removed
0.5.0-canary.1
Minor Changes
#547
c7d6818Thanks @thevuong! - Add astorageKeyprop toAppearanceProviderfor a fully client-side color scheme path (no server framework, cookie, or loader). When set, the provider reads the persisted preference fromlocalStoragein its initial client render, auto-persists changes there (unless an explicitpersistColorSchemeis given), and syncs across tabs via thestorageevent. Paired with<AppearanceScript storageKey>using the same key, the inline script applies the stored value before first paint — so there is no flash even on statically prerendered / CDN-served pages, where an httpOnly cookie can't reach the pre-paint script and a post-mount server round-trip would otherwise flip the appearance.AppearanceScriptandAppearanceProvidernow also mirror the preference (light/dark/automatic) to adata-appearanceattribute on<html>— set by the inline script before first paint and kept in sync by the provider. This lets preference-aware UI (e.g. a 3-state theme toggle that shows a distinct "system" icon) render the correct state purely from CSS, with no hydration flash from reading client-only state.
0.5.0-canary.0
Minor Changes
- #526
fad5a21Thanks @thevuong! - Add the@codefast/theme/viteplugin. TanStack Start registers the server functions shipped in@codefast/theme/startat the consumer's build time, so the package must not be externalized for SSR nor pre-bundled for the client. The newcodefastTheme()plugin applies that configuration automatically, so consumers no longer need to hand-writessr.noExternal/optimizeDeps.exclude.
0.4.0
Patch Changes
2397801Thanks @thevuong! - chore: align package config globs17c6445Thanks @thevuong! - Security, correctness, and performance improvements toThemeProviderandThemeScript.Security
- Validate
BroadcastChannelmessages withthemeSchema.safeParsebefore applying — prevents injection from browser extensions or other same-origin scripts - Use
JSON.stringifyinThemeScriptinline script to safely serialise the theme value
Correctness
ThemeProvidernow re-syncs internal state when thethemeprop changes after mount (e.g. router re-runs the root loader), making the server the authoritative source of truth- Fixed
disableTransitionOnChangetiming: animation cleanup now fires afterapplyThemecommits to the DOM instead of prematurely in the async persistfinallyblock - Added last-write-wins guard (
intentRef) so rapidsetThemecalls only commit the most recent intent
Performance
setThemecallback is now stable across theme commits (readscommittedThemeRefinstead of capturingthemein deps) — prevents unnecessary re-renders in consumersBroadcastChannelis reused from a shared ref instead of opening a new channel on everysetThemecall- Removed obsolete
-moz-,-o-,-ms-vendor prefixes from thedisableAnimationCSS injection
Features
ThemeScriptaccepts a newstorageKeyprop: when set, the inline script readslocalStoragebefore first paint for FOUC-free client-only apps- Runtime Zod validation of the
themeprop inThemeProviderguards against invalid values bypassing TypeScript
Docs / Tests
- Fixed README:
THEME_STORAGE_KEYimport example now correctly points to@codefast/theme/constants - Added integration tests for the TanStack Start adapter server functions
- New test coverage for all security and correctness fixes above
- Validate
f79b333Thanks @thevuong! - feat(dev): enable source condition for zero-rebuild HMR in apps/docs5c82b5eThanks @thevuong! - Use minimum>=ranges in peerDependencies instead of^caret ranges.ebdf9e3Thanks @thevuong! - feat(web): refactor theme management to color scheme system6c3ac44Thanks @thevuong! - Normalize import statement order and package.json key order repo-wide via the new oxfmtsortImports/sortPackageJsonsettings — purely mechanical, no runtime behavior change.b2cad2eThanks @thevuong! - fix(theme): migratesetColorSchemeServerFnto TanStack Start.validatorAPI
Showing the latest 20 releases; the full changelog on GitHub has 26 more releases.