Skip to content

feat(mix): add MixScope.inherit to combine parent and child tokens#853

Open
tilucasoli wants to merge 3 commits intomainfrom
feat/mix-scope-inherit-tokens
Open

feat(mix): add MixScope.inherit to combine parent and child tokens#853
tilucasoli wants to merge 3 commits intomainfrom
feat/mix-scope-inherit-tokens

Conversation

@tilucasoli
Copy link
Collaborator

@tilucasoli tilucasoli commented Feb 24, 2026

Description

Adds MixScope.inherit, a way to combine tokens from the nearest parent MixScope with additional tokens in a single merged scope. A nested MixScope normally replaces the parent for its subtree; MixScope.inherit instead merges parent + child token maps so both upstream and local tokens resolve. Inner tokens override parent when the same token key is defined in both. Implemented via a Builder that reads the parent scope and builds one MixScope with the merged map (no change to getToken/tokenOf resolution).

Changes

  • packages/mix/lib/src/theme/mix_theme.dart
    • Add static MixScope.inherit() that takes the same token maps as the main factory, uses a Builder to get MixScope.maybeOf(context, 'tokens'), merges {...?parent?.tokens, ...childTokens}, and returns a single MixScope with merged tokens and optional orderOfModifiers (child then parent).
  • packages/mix/test/src/theme/mix_scope_inherit_test.dart
    • New tests: outer + inner tokens both resolve; Material (Fortal-like) + inherit; resolution for .light and .dark; inner overrides parent when same key; inherit with no parent.
  • packages/mix/doc/mix-scope-and-theming.md
    • Note that nested MixScope replaces parent; document MixScope.inherit as the way to combine tokens (parent + yours, yours override). Example with plain outer MixScope and MixScope.inherit.

Review Checklist

  • Testing: Unit/integration tests added for inherit (outer+inner, Material+inherit, light/dark, override, no parent).
  • Breaking Changes: No. New API only.
  • Documentation Updates: mix-scope-and-theming.md updated.
  • Website Updates: N/A unless docs are mirrored on the website.

Additional Information (optional)

Validation (from original what-if): one “Fortal” token and one custom Mix token both resolve in the same subtree; resolution validated for .light and .dark within the outer scope.

- Add MixScope.inherit() factory that merges nearest parent scope tokens
  with provided tokens so both upstream and local tokens resolve in the
  same subtree (additive token scope).
- Document in mix-scope-and-theming.md and add tests for outer+inner
  resolution, Material+inherit, light/dark theme, override behavior,
  and no-parent case.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel
Copy link
Contributor

vercel bot commented Feb 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mix-docs Ready Ready Preview, Comment Feb 25, 2026 0:37am

@docs-page
Copy link

docs-page bot commented Feb 24, 2026

To view this pull requests documentation preview, visit the following URL:

docs.page/btwld/mix~853

Documentation is deployed and generated using docs.page.

Copy link
Collaborator

@leoafarias leoafarias left a comment

Choose a reason for hiding this comment

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

Code review — 2 issues found (details in inline comments below).

builder: (context) {
final parent = MixScope.maybeOf(context);

return MixScope.combine(scopes: [?parent, nestedScope], child: child);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Bug: key parameter is silently ignored

The key passed to inherit() goes into nestedScope, but nestedScope is never inserted into the widget tree — it's only used as a data carrier for combine(). The Builder returned here doesn't receive the key, and combine is called without forwarding it (defaults to null).

A caller passing key: ValueKey('foo') would expect it to affect widget identity, but it has zero effect.

Suggested fix — forward key to combine:

return MixScope.combine(key: key, scopes: [?parent, nestedScope], child: child);

```dart
// Outer scope provides base tokens; inherit combines them with yours
MixScope(
colors: { ColorToken('brand.primary'): Colors.blue },
Copy link
Collaborator

Choose a reason for hiding this comment

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

Doc: Invalid Dart — duplicate named parameter colors:

This constructor call has colors: twice, which is a compile error in Dart. The second entry should be merged into a single map:

MixScope(
  colors: {
    ColorToken('brand.primary'): Colors.blue,
    ColorToken('custom.accent'): Colors.orange,
  },
  child: MixScope.inherit(
    spaces: { SpaceToken('custom.gap'): 12.0 },
    child: MySubtree(),
  ),
)

Expand docs to explain that nesting a MixScope replaces the parent for token resolution and can drop parent tokens; add examples and sections for MixScope.combine (merge order, use-case) and MixScope.inherit (merge with parent at build time). In code, remove the key from the temporary nested scope and pass the key into MixScope.combine in the builder so the combined scope receives the key (ensuring the merged scope is keyed correctly).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation examples mix_generator mix repo

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants