Skip to content

feat: re-export MUI primitives so consuming apps drop the direct dependency - #1

Open
kaiomagalhaes wants to merge 3 commits into
mainfrom
feat/mui-passthroughs
Open

feat: re-export MUI primitives so consuming apps drop the direct dependency#1
kaiomagalhaes wants to merge 3 commits into
mainfrom
feat/mui-passthroughs

Conversation

@kaiomagalhaes

Copy link
Copy Markdown
Member

Why

box-bridge-app imports @mui/material directly across 74 files (29 distinct components + 10 icons) even though this kit is itself built on MUI. That leaves MUI as a direct app dependency and splits the design system across two import sources.

This PR exposes the missing primitives here so the app can import from the kit only. The companion app PR removes @mui/* from its package.json entirely.

What

  • 24 passthrough components for the MUI primitives the app uses that had no kit equivalent: Box, Stack, IconButton, Tooltip, InputAdornment, TextField, the Dialog family, Divider, Alert, FormControl, Select, Paper, Chip, Card, InputLabel, Radio, RadioGroup, FormControlLabel, CircularProgress, MenuItem. These are typed re-exports — no styling opinion — so consumers get identical rendering.
  • MuiCheckbox for the raw, unlabeled checkbox. The existing Checkbox requires a text label and wraps in <label>, so it is not a drop-in for checkboxes rendered inside table rows.
  • components/icons re-exporting the 10 glyphs in use, suffixed with Icon to avoid colliding with component names exported from the root (Link, Menu).
  • @mui/material / @mui/icons-material moved from peerDependencies to dependencies. Consumers no longer supply MUI, so the kit must own it.

Two fixes worth a closer look

"use client" banner (vite.config.ts). Every component here wraps MUI, which is client-only. Without the directive, a React Server Component importing the kit pulls the whole bundle — including react-hook-form — into the server graph, where react-hook-form's react-server export condition resolves to a trimmed build with no Controller. That broke the app's build on document-signed/page.tsx. MUI ships the same directive for the same reason.

Button dropped className. It was destructured out of props but never passed to classNames(), so the prop was accepted by the type and silently ignored. Now merged. Note this means any existing caller passing className will start having it applied — in the app that affects the sign-in button, which was passing styles["sign-in-button"] into the void.

Verification

  • npm run lint (tsc --noEmit) clean
  • npm run build clean; all 38 new exports verified present in dist/index.js and dist/index.d.ts
  • Consuming app typechecks, lints (no new problems vs. baseline), and builds green against this branch

🤖 Generated with Claude Code

kaiomagalhaes and others added 2 commits August 9, 2026 19:53
…ndency

The app depends on @mui/material directly for 29 components and 10 icons,
even though this kit is itself built on MUI. Expose those primitives here so
consumers import from the kit only.

- Add passthrough components for the 24 MUI primitives the app uses that had
  no kit equivalent (Box, Stack, IconButton, Tooltip, InputAdornment,
  TextField, the Dialog family, Divider, Alert, FormControl, Select, Paper,
  Chip, Card, InputLabel, Radio, RadioGroup, FormControlLabel,
  CircularProgress, MenuItem).
- Add MuiCheckbox for the raw, unlabeled checkbox. The existing Checkbox
  requires a text label and wraps in <label>, so it is not a drop-in for
  checkboxes rendered inside table rows.
- Add components/icons re-exporting the 10 glyphs in use, suffixed with Icon
  to avoid colliding with component names exported from the root.
- Move @mui/material and @mui/icons-material from peerDependencies to
  dependencies. Consumers no longer supply MUI, so the kit must own it.
- Emit a "use client" banner. Every component here wraps MUI, which is
  client-only; without it a React Server Component importing the kit pulls
  the whole bundle (incl. react-hook-form) into the server graph, where
  react-hook-form's react-server build has no Controller export.
- Fix Button dropping a caller-supplied className. It was destructured out of
  props but never passed to classNames(), so the documented prop was silently
  ignored.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both wrappers set their own props *after* the prop spread, so a caller passing
color, underline, fontSize, variant or disableRipple had it silently
discarded. Consuming call sites that set color="text.secondary" on a Link, or
variant on a Button, were being ignored.

Move those props before the spread so they act as defaults the caller can
override. className stays after the spread in Button because it is merged
rather than replaced.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@kaiomagalhaes

Copy link
Copy Markdown
Member Author

Follow-up commit 209d3ea (0.2.0 → 0.2.1): Link and Button applied their own styling after the prop spread, so callers passing color, underline, fontSize, variant or disableRipple had them silently discarded — the same bug class as the className drop in the original commit. Those props now come before the spread and act as defaults. className stays after it because it is merged, not replaced.

…ency move

Review of this PR found two defects in the earlier commits.

MUI was left declared in both dependencies and devDependencies, and
package-lock.json was never regenerated after the peer -> dependency move. The
lock still carried @mui/material as a dev/peer entry pinned to 7.3.9, which
does not satisfy the newly declared ^7.3.11, so `npm ci` built and typechecked
CI against a version the package does not claim to support. Removed the
duplicate devDependencies entries and re-locked; `npm ci` now installs 7.3.11.

The previous commit moved Button's and Link's own props before the spread so
callers could override them. That fixed the discard but introduced the
opposite bug: a JSX spread writes the key even when the value is undefined, so
`variant={undefined}` or `underline={undefined}` fell through to MUI's own
destructuring default ('text', 'always') instead of the kit's. Resolve with ??
instead of by ordering, which handles both absent and explicitly-undefined.

Also corrects the Button comment, which implied className merging makes kit
styling overridable. It does not: kit rules are written as
[class*=MuiButton-root] (specificity 0,2,0), so a caller's plain class still
loses. Merging is still right, it just buys less than the comment claimed.

README told consumers to install MUI as a peer dependency, contradicting the
purpose of this PR. It now documents importing MUI primitives from the kit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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