Skip to content
codefastlabs

Command Palette

Search for a command to run...

v0.8.1· Changelog

@codefast/theme

0.8.1

Patch Changes

  • #748 bde6d1b Thanks @thevuong! - Mirror the committed appearance ref in a layout effect so a programmatic setAppearance landing before the passive flush compares against the just-committed appearance instead of the previous one.

0.8.0

Minor Changes

  • #745 5cbb93a Thanks @thevuong! - fix(theme): harden appearance provider/script and tighten public types

    Three correctness fixes plus two type-safety tightenings:

    • Overlapping setAppearance calls with disableTransition enabled could orphan a transition-suppression <style>enableTransitionsRef was 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.
    • AppearanceScript now runtime-validates its appearance prop (as AppearanceProvider already did), so an invalid value can no longer be written to <html> before first paint or diverge from the provider's fallback.
    • applyColorScheme no-ops during SSR instead of throwing on window access, matching its sibling suppressTransitions.
    • Public props on AppearanceProvider and AppearanceScript now accept explicit undefined (?: T | undefined) under exactOptionalPropertyTypes, so JSX callers can pass possibly-undefined expressions such as nonce and appearance.

    Breaking (type surface only, no runtime behavior change): appearances is now ReadonlyArray<Appearance> (was Array<Appearance>), so assigning it to a mutable Appearance[] is now a compile error.

0.7.0

0.6.2

0.6.1

0.6.0

Patch Changes

  • #690 f4b1aa6 Thanks @thevuong! - Report the failures that were being swallowed or mislabelled, and derive the types the build emits.

    @codefast/di:

    • @inject, @postConstruct and @preDestroy on a static member now throw StaticMemberDecoratorError instead of InternalError. All three act on one instance, so this is caller misuse — and InternalError means 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.
    • AsyncResolutionError names 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"; a resolve(App) that fails on an async Database now says so. asyncSourceToken defaults to tokenName for the case where the requested binding is itself the source.
    • A MetadataReader that names a @postConstruct/@preDestroy method the instance does not have raises InvalidMetadataError instead 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 into reason.
    • MissingScopeContextError from ScopeManager names its token instead of "(unknown)", and the scoped read takes one map lookup where it took two.
    • Token, Constructor and InjectionDescriptor declare out Value, so the compiler checks the covariance the engine already relied on.

    Repo-wide: isolatedDeclarations is on for every package that emits declarations, so a public type can always be written down from the source file alone. allowJs is gone from the shared base config — no package has JavaScript sources. @codefast/theme and @codefast/tracking gained explicit annotations on four exported constants to satisfy it; the emitted types are unchanged. @codefast/ui and @codefast/benchmark-viewer opt out for reasons recorded in their configs.

0.5.0

Minor Changes

  • c634b17 Thanks @thevuong! - Rename AppearanceContextType to AppearanceContextValue — the "Type" suffix carried no information (every exported type is a type), while "Value" states the role: the payload provided through AppearanceContext and returned by useAppearance.

  • #547 c7d6818 Thanks @thevuong! - Add a storageKey prop to AppearanceProvider for a fully client-side color scheme path (no server framework, cookie, or loader). When set, the provider reads the persisted preference from localStorage in its initial client render, auto-persists changes there (unless an explicit persistColorScheme is given), and syncs across tabs via the storage event. 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.

    AppearanceScript and AppearanceProvider now also mirror the preference (light / dark / automatic) to a data-appearance attribute 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 fad5a21 Thanks @thevuong! - Add the @codefast/theme/vite plugin. TanStack Start registers the server functions shipped in @codefast/theme/start at the consumer's build time, so the package must not be externalized for SSR nor pre-bundled for the client. The new codefastTheme() plugin applies that configuration automatically, so consumers no longer need to hand-write ssr.noExternal / optimizeDeps.exclude.

Patch Changes

  • #676 641e233 Thanks @thevuong! - Collapse the types and default lanes of package.json#imports from 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_FOUND rather than trying the second. ./dist/*/index.js and ./dist/*/index.d.ts could therefore never be reached, so they read as a safety net that does not exist. The source lane keeps its extension candidates, which only tsc and Vite read and both probe.

  • #550 0feb8cd Thanks @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 SwiftUI ColorScheme and CSS color-scheme). Source files are also flattened so each subpath names what it exports.

    Breaking renames:

    • Types: ColorSchemeAppearance; ResolvedColorSchemeColorScheme; ColorSchemeContextTypeAppearanceContextType; colorSchemeSchemaappearanceSchema; colorSchemesappearances.

    • Hook: useColorScheme()useAppearance(), returning { appearance, colorScheme, setAppearance, isPending } (was { colorScheme, resolvedColorScheme, setColorScheme, isPending }).

    • Provider props: colorSchemeappearance; persistColorSchemepersistAppearance. Script prop: colorSchemeappearance. Context: ColorSchemeContextAppearanceContext.

    • Constants: DEFAULT_COLOR_SCHEMEDEFAULT_APPEARANCE ("automatic"); DEFAULT_RESOLVED_COLOR_SCHEMEDEFAULT_COLOR_SCHEME ("dark"). ⚠️ DEFAULT_COLOR_SCHEME still 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_KEY is now "ui-appearance" (was "ui-theme") — returning visitors fall back to "automatic" once. The internal BroadcastChannel is now "appearance-sync" (was "color-scheme-sync").

    Unchanged: AppearanceProvider / AppearanceScript component names, the data-appearance attribute, resolveColorScheme() / getSystemColorScheme() / applyColorScheme() / suppressTransitions().

  • #549 2d16644 Thanks @thevuong! - Remove all server-side handling — the color scheme preference now lives entirely on the client in localStorage.

    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's ssrColorScheme and syncFromServer props; the provider also no longer re-syncs from a changed colorScheme prop after mount (it is a static fallback now).
    • resolveColorScheme() no longer takes an ssrColorScheme argument; on the server, "automatic" resolves to DEFAULT_RESOLVED_COLOR_SCHEME.
    • Dropped the optional @tanstack/react-start and vite peer dependencies.

    Changed defaults: storageKey now defaults to STORAGE_KEY ("ui-appearance") and colorScheme to DEFAULT_COLOR_SCHEME ("automatic") on both AppearanceScript and AppearanceProvider, so <AppearanceScript /> + <AppearanceProvider> work with zero configuration. persistColorScheme / onPersistError remain for custom persistence.

    Migration: drop the root loader and the codefastTheme() Vite plugin, render the defaults on <html> with suppressHydrationWarning, and let the inline script correct the class before paint — see the README's TanStack Start recipe.

0.5.0-canary.9

Patch Changes

  • #676 641e233 Thanks @thevuong! - Collapse the types and default lanes of package.json#imports from 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_FOUND rather than trying the second. ./dist/*/index.js and ./dist/*/index.d.ts could therefore never be reached, so they read as a safety net that does not exist. The source lane keeps its extension candidates, which only tsc and Vite read and both probe.

0.5.0-canary.8

0.5.0-canary.7

0.5.0-canary.6

Minor Changes

  • c634b17 Thanks @thevuong! - Rename AppearanceContextType to AppearanceContextValue — the "Type" suffix carried no information (every exported type is a type), while "Value" states the role: the payload provided through AppearanceContext and returned by useAppearance.

  • #547 c7d6818 Thanks @thevuong! - Add a storageKey prop to AppearanceProvider for a fully client-side color scheme path (no server framework, cookie, or loader). When set, the provider reads the persisted preference from localStorage in its initial client render, auto-persists changes there (unless an explicit persistColorScheme is given), and syncs across tabs via the storage event. 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.

    AppearanceScript and AppearanceProvider now also mirror the preference (light / dark / automatic) to a data-appearance attribute 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 fad5a21 Thanks @thevuong! - Add the @codefast/theme/vite plugin. TanStack Start registers the server functions shipped in @codefast/theme/start at the consumer's build time, so the package must not be externalized for SSR nor pre-bundled for the client. The new codefastTheme() plugin applies that configuration automatically, so consumers no longer need to hand-write ssr.noExternal / optimizeDeps.exclude.

Patch Changes

  • #550 0feb8cd Thanks @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 SwiftUI ColorScheme and CSS color-scheme). Source files are also flattened so each subpath names what it exports.

    Breaking renames:

    • Types: ColorSchemeAppearance; ResolvedColorSchemeColorScheme; ColorSchemeContextTypeAppearanceContextType; colorSchemeSchemaappearanceSchema; colorSchemesappearances.

    • Hook: useColorScheme()useAppearance(), returning { appearance, colorScheme, setAppearance, isPending } (was { colorScheme, resolvedColorScheme, setColorScheme, isPending }).

    • Provider props: colorSchemeappearance; persistColorSchemepersistAppearance. Script prop: colorSchemeappearance. Context: ColorSchemeContextAppearanceContext.

    • Constants: DEFAULT_COLOR_SCHEMEDEFAULT_APPEARANCE ("automatic"); DEFAULT_RESOLVED_COLOR_SCHEMEDEFAULT_COLOR_SCHEME ("dark"). ⚠️ DEFAULT_COLOR_SCHEME still 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_KEY is now "ui-appearance" (was "ui-theme") — returning visitors fall back to "automatic" once. The internal BroadcastChannel is now "appearance-sync" (was "color-scheme-sync").

    Unchanged: AppearanceProvider / AppearanceScript component names, the data-appearance attribute, resolveColorScheme() / getSystemColorScheme() / applyColorScheme() / suppressTransitions().

  • #549 2d16644 Thanks @thevuong! - Remove all server-side handling — the color scheme preference now lives entirely on the client in localStorage.

    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's ssrColorScheme and syncFromServer props; the provider also no longer re-syncs from a changed colorScheme prop after mount (it is a static fallback now).
    • resolveColorScheme() no longer takes an ssrColorScheme argument; on the server, "automatic" resolves to DEFAULT_RESOLVED_COLOR_SCHEME.
    • Dropped the optional @tanstack/react-start and vite peer dependencies.

    Changed defaults: storageKey now defaults to STORAGE_KEY ("ui-appearance") and colorScheme to DEFAULT_COLOR_SCHEME ("automatic") on both AppearanceScript and AppearanceProvider, so <AppearanceScript /> + <AppearanceProvider> work with zero configuration. persistColorScheme / onPersistError remain for custom persistence.

    Migration: drop the root loader and the codefastTheme() Vite plugin, render the defaults on <html> with suppressHydrationWarning, and let the inline script correct the class before paint — see the README's TanStack Start recipe.

1.0.0-canary.7

1.0.0-canary.6

0.5.0-canary.5

0.5.0-canary.4

Minor Changes

  • c634b17 Thanks @thevuong! - Rename AppearanceContextType to AppearanceContextValue — the "Type" suffix carried no information (every exported type is a type), while "Value" states the role: the payload provided through AppearanceContext and returned by useAppearance.

0.5.0-canary.3

0.5.0-canary.2

Patch Changes

  • #550 0feb8cd Thanks @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 SwiftUI ColorScheme and CSS color-scheme). Source files are also flattened so each subpath names what it exports.

    Breaking renames:

    • Types: ColorSchemeAppearance; ResolvedColorSchemeColorScheme; ColorSchemeContextTypeAppearanceContextType; colorSchemeSchemaappearanceSchema; colorSchemesappearances.

    • Hook: useColorScheme()useAppearance(), returning { appearance, colorScheme, setAppearance, isPending } (was { colorScheme, resolvedColorScheme, setColorScheme, isPending }).

    • Provider props: colorSchemeappearance; persistColorSchemepersistAppearance. Script prop: colorSchemeappearance. Context: ColorSchemeContextAppearanceContext.

    • Constants: DEFAULT_COLOR_SCHEMEDEFAULT_APPEARANCE ("automatic"); DEFAULT_RESOLVED_COLOR_SCHEMEDEFAULT_COLOR_SCHEME ("dark"). ⚠️ DEFAULT_COLOR_SCHEME still 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_KEY is now "ui-appearance" (was "ui-theme") — returning visitors fall back to "automatic" once. The internal BroadcastChannel is now "appearance-sync" (was "color-scheme-sync").

    Unchanged: AppearanceProvider / AppearanceScript component names, the data-appearance attribute, resolveColorScheme() / getSystemColorScheme() / applyColorScheme() / suppressTransitions().

  • #549 2d16644 Thanks @thevuong! - Remove all server-side handling — the color scheme preference now lives entirely on the client in localStorage.

    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's ssrColorScheme and syncFromServer props; the provider also no longer re-syncs from a changed colorScheme prop after mount (it is a static fallback now).
    • resolveColorScheme() no longer takes an ssrColorScheme argument; on the server, "automatic" resolves to DEFAULT_RESOLVED_COLOR_SCHEME.
    • Dropped the optional @tanstack/react-start and vite peer dependencies.

    Changed defaults: storageKey now defaults to STORAGE_KEY ("ui-appearance") and colorScheme to DEFAULT_COLOR_SCHEME ("automatic") on both AppearanceScript and AppearanceProvider, so <AppearanceScript /> + <AppearanceProvider> work with zero configuration. persistColorScheme / onPersistError remain for custom persistence.

    Migration: drop the root loader and the codefastTheme() Vite plugin, render the defaults on <html> with suppressHydrationWarning, and let the inline script correct the class before paint — see the README's TanStack Start recipe.

0.5.0-canary.1

Minor Changes

  • #547 c7d6818 Thanks @thevuong! - Add a storageKey prop to AppearanceProvider for a fully client-side color scheme path (no server framework, cookie, or loader). When set, the provider reads the persisted preference from localStorage in its initial client render, auto-persists changes there (unless an explicit persistColorScheme is given), and syncs across tabs via the storage event. 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.

    AppearanceScript and AppearanceProvider now also mirror the preference (light / dark / automatic) to a data-appearance attribute 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 fad5a21 Thanks @thevuong! - Add the @codefast/theme/vite plugin. TanStack Start registers the server functions shipped in @codefast/theme/start at the consumer's build time, so the package must not be externalized for SSR nor pre-bundled for the client. The new codefastTheme() plugin applies that configuration automatically, so consumers no longer need to hand-write ssr.noExternal / optimizeDeps.exclude.

0.4.0

Patch Changes

  • 2397801 Thanks @thevuong! - chore: align package config globs

  • 17c6445 Thanks @thevuong! - Security, correctness, and performance improvements to ThemeProvider and ThemeScript.

    Security

    • Validate BroadcastChannel messages with themeSchema.safeParse before applying — prevents injection from browser extensions or other same-origin scripts
    • Use JSON.stringify in ThemeScript inline script to safely serialise the theme value

    Correctness

    • ThemeProvider now re-syncs internal state when the theme prop changes after mount (e.g. router re-runs the root loader), making the server the authoritative source of truth
    • Fixed disableTransitionOnChange timing: animation cleanup now fires after applyTheme commits to the DOM instead of prematurely in the async persist finally block
    • Added last-write-wins guard (intentRef) so rapid setTheme calls only commit the most recent intent

    Performance

    • setTheme callback is now stable across theme commits (reads committedThemeRef instead of capturing theme in deps) — prevents unnecessary re-renders in consumers
    • BroadcastChannel is reused from a shared ref instead of opening a new channel on every setTheme call
    • Removed obsolete -moz-, -o-, -ms- vendor prefixes from the disableAnimation CSS injection

    Features

    • ThemeScript accepts a new storageKey prop: when set, the inline script reads localStorage before first paint for FOUC-free client-only apps
    • Runtime Zod validation of the theme prop in ThemeProvider guards against invalid values bypassing TypeScript

    Docs / Tests

    • Fixed README: THEME_STORAGE_KEY import 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
  • f79b333 Thanks @thevuong! - feat(dev): enable source condition for zero-rebuild HMR in apps/docs

  • 5c82b5e Thanks @thevuong! - Use minimum >= ranges in peerDependencies instead of ^ caret ranges.

  • ebdf9e3 Thanks @thevuong! - feat(web): refactor theme management to color scheme system

  • 6c3ac44 Thanks @thevuong! - Normalize import statement order and package.json key order repo-wide via the new oxfmt sortImports/sortPackageJson settings — purely mechanical, no runtime behavior change.

  • b2cad2e Thanks @thevuong! - fix(theme): migrate setColorSchemeServerFn to TanStack Start .validator API


Showing the latest 20 releases; the full changelog on GitHub has 26 more releases.