Skip to content

Introduce a ThemeProvider for the shared component library #85

@SjaakSchilperoort

Description

@SjaakSchilperoort

User story

As a developer,
I want the components library to provide a ThemeProvider,
So that our apps can apply custom themes while sharing consistent default styling.

Context

The component library is used by two apps. Both apps have partially overlapping design systems. Currently, the components library has its own design system.

We want:

  • Shared defaults in the library
  • App-level overrides where needed
  • Strong TypeScript typing
  • No direct imports of static color/style constants inside components

This setup gives us one central place to control how an app looks. Each app can adjust its own styling, while the shared components stay consistent and don’t need to change. In the future, it will make it much easier to update the design, add things like dark mode, or support new styling needs, without rewriting existing components.

Functional requirements

Components library responsibilities

The components library:

  1. Defines a Theme type
  2. Defines a defaultTheme of type Theme
  3. Implements a ThemeProvider that:
    • Accepts a theme prop of type Partial<Theme>
    • Merges the provided theme with the internal defaultTheme.
    • Ensures the resulting theme is a complete Theme
  4. Creates a ThemeContext internally
  5. Exposes a types useTheme() hook that returns a fully merged Theme

App responsibilities

Each app:

  1. Defines an app-specific theme of type Partial<Theme>
  2. Only includes values that override defaults
  3. Wraps the root component tree in ThemeProvider
  4. Does not import default theme values directly

Component usage

Library components and app components and screens:

  • Must use const theme = useTheme()
  • Must not import static theme definitions
  • Must retrieve all styling values from the theme

Out of scope (for this story)

  • Runtime theme switching
  • Dark mode
  • Multiple theme variants
  • Dynamic theme updates

Definition of Done

  • Theme type defined and exported
  • defaultTheme implemented
  • ThemeProvider implemented
  • useTheme() implemented and typed
  • At least one existing component refactored to use useTheme()
  • One app successfully overrides at least one theme property
  • TypeScript validates incorrect overrides

Implementation phases

1. Introduce a Theme type

  • Move non-theme primitives (rounded, shadow, layout, etc.) out of theme
  • Centralize shared static styles in the component library
  • Ensured both apps conform to the Theme type
  • Still use static imports

Done in:

2. Introduce a runtime Theme context

  • Create ThemeContext in the component library
  • Wrap apps with ThemeProvider
  • Migrate shared components, apps remain static.

User story:

3. Refactor text/textStyle to be theme-aware

Introduce theme‑driven typography by moving text styles into the shared theme. This enables apps to override or extend text styles, and ensures shared components automatically use the correct text styles for each app. The component library will be updated to support dynamic text styles. The apps will follow later, after switching to semantic color names.

User story:

4. Use semantic color names

  • Move color definitions to a palette definition
  • For theme colors, use semantic names for colors, that resolve to a palette entry
  • This way, you can use ThemeProvider to switch themes from one set ("Light") to the other ("Dark")

5. Switch the apps from static theme to dynamic theme

6. Move ad hoc colors (e.g. in species info) to theme

  • Species info: rarity, status

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions