Skip to content

Conversation

@kylecarbs
Copy link
Member

Overview

This PR establishes the complete foundation for migrating from @emotion/styled to Tailwind CSS + Shadcn UI, with 2 components fully converted as proof of concept.

What's Included βœ…

Foundation (100% Complete)

  • βœ… Installed Tailwind CSS v3 + PostCSS + autoprefixer
  • βœ… Installed all Radix UI primitives for Shadcn components
  • βœ… Created tailwind.config.ts with 40+ custom color mappings
  • βœ… Created src/styles/globals.css with Tailwind directives + global styles
  • βœ… Created src/lib/utils.ts with cn() utility for class merging
  • βœ… Added Shadcn Button component
  • βœ… Removed @emotion/babel-plugin from Vite config
  • βœ… Updated App.tsx to use globals.css
  • βœ… Build verified working ✨

Converted Components (2/64 = 3%)

  • βœ… ErrorMessage.tsx - Fully converted to Tailwind
  • βœ… ToggleGroup.tsx - Demonstrates conditional styling with cn()

Both components maintain exact visual appearance and behavior.

Migration Status

Remaining work: 62 components still using styled-components

  • See TAILWIND_MIGRATION.md for complete breakdown
  • Estimated effort: 20-30 hours of focused conversion work

Why This Approach?

This PR provides a stable foundation for incremental migration:

  1. Verified working - Build passes, types check, no runtime errors
  2. Zero breaking changes - Emotion and Tailwind coexist peacefully
  3. Clear patterns - Two converted components show the way forward
  4. Safe iteration - Components can be converted one-at-a-time

Migration Pattern

// Before (Emotion)
const Button = styled.button<{ active: boolean }>`
  color: ${props => props.active ? 'white' : 'gray'};
`;

// After (Tailwind)
<button className={cn(
  "base-classes",
  active ? "text-white" : "text-gray-500"
)}>

Next Steps

  1. Convert remaining 62 components systematically
  2. Remove Emotion dependencies when 100% complete
  3. Update Storybook stories
  4. Full integration test pass

See TAILWIND_MIGRATION.md for detailed plan.


Generated with cmux

@kylecarbs kylecarbs force-pushed the migrate-to-tailwind-shadcn branch from 23ef231 to a1afb6c Compare October 22, 2025 16:20
@kylecarbs kylecarbs marked this pull request as ready for review October 22, 2025 16:47
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

πŸ’‘ Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with πŸ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

kylecarbs added a commit that referenced this pull request Oct 22, 2025
- Added legacy --color-* aliases in globals.css that point to new variable names
- Fixes 76 references to var(--color-*) throughout the codebase
- Restores proper colors for plan-mode, exec-mode, borders, tokens, etc.
- Ensures hover/focus accents work correctly in AIView, DiffRenderer, etc.

Addresses Codex P1 comment on PR #379
- Install Tailwind CSS v3, PostCSS, and autoprefixer
- Install Radix UI primitives for Shadcn components
- Create tailwind.config.ts with all custom color mappings
- Add globals.css with Tailwind directives and global styles
- Create cn() utility for class merging
- Add Shadcn Button component
- Update vite.config.ts to remove @emotion/babel-plugin
- Update App.tsx to import globals.css instead of Emotion Global components
- Convert ErrorMessage component to Tailwind as proof of concept
- Add version.ts stub for worktree environment

This establishes the foundation for migrating from @emotion/styled to Tailwind CSS.
Next steps: Convert remaining 67 component files to use Tailwind classes.
- Replace styled-components with Tailwind utility classes
- Use cn() utility for conditional class application
- Maintain exact same visual appearance and behavior
- Demonstrates the migration pattern for other components

Progress: 2/64 components converted
Documents:
- Complete foundation setup (100%)
- 2 components converted (3% of 64 total)
- Detailed list of 62 remaining components
- Migration patterns and examples
- Color mapping reference
- Next steps and estimated effort (20-30 hours)

This PR establishes the foundation. The remaining component conversions
should be done incrementally to minimize risk.
…heckbox to Tailwind

- StatusIndicator: Convert styled div to Tailwind with conditional classes
- Tooltip: Convert portal-based tooltip with collision detection
- ChatToggles: Simple flex container conversion
- Context1MCheckbox: Complex checkbox styling with pseudo-elements in Tailwind
- HelpIndicator: Convert from styled.span to functional component

Progress: 6/64 components converted (9%)
- ModalOverlay, ModalContent, ModalSubtitle: Convert to functional components
- ModalInfo, ModalActions: Add proper React.FC types with className support
- ErrorSection, ErrorLabel, ErrorCodeBlock: Convert error display components
- WarningBox, WarningTitle, WarningText: Convert warning components
- Button, CancelButton, PrimaryButton, DangerButton: Convert all button variants
- Maintain backwards compatibility with proper prop spreading

This is a large file with many exported components used throughout the app.

Progress: 7/64 components converted (11%)
- DirectorySelectModal: Convert input field and error text to inline Tailwind
- ToolPrimitives: Major conversion of all shared tool components
  - ToolContainer, ToolHeader, ExpandIcon, ToolName: Basic layout components
  - StatusIndicator: Complex color logic with status-based styling
  - ToolDetails, DetailSection, DetailLabel, DetailContent: Content display
  - LoadingDots: Animation support (needs CSS keyframe workaround)
  - HeaderButton: Interactive button with active state

All components now use React.FC with proper TypeScript types and prop spreading.

Progress: 9/64 components converted (14%)
- HistoryHiddenMessage: Simple info banner for hidden history
- TerminalOutput: Terminal output display with error state

Progress: 11/64 components converted (17%), 53 remaining
- CompactingMessageContent: Mask gradient for fade effect using inline styles
- TypewriterMarkdown: Remove styled-component, use markdown-content class

Note: markdown-content class needs to be added to globals.css with proper styles

Progress: 13/64 components (20%), 51 remaining
Updated TAILWIND_MIGRATION.md with all completed components and revised counts.

Progress breakdown:
- 13 components fully converted to Tailwind
- 51 components remaining (includes all the most complex files)
- Foundation 100% complete and working
- Build passing, types checking

The remaining components include the largest/most complex files:
- App.tsx (924 LoC), ProjectSidebar (990 LoC)
- ChatInput (907 LoC), AIView (649 LoC)
- ReviewPanel (984 LoC), CostsTab (557 LoC)
- Plus 45 more components of varying complexity
- Convert LeftSidebarContainer with responsive width and mobile overlay behavior
- Convert Overlay backdrop with conditional visibility
- Convert HamburgerButton with mobile-only display and hover/active states
- Maintain all responsive breakpoints and transitions

Progress: 14/64 components (22%), 50 remaining
- Convert ErrorContainer, ErrorTitle, ErrorDetails, ResetButton
- Maintain error styling with proper colors and spacing
- Keep all error handling logic unchanged

Progress: 15/64 components (23%), 49 remaining
Final conversion session results:
- 15 components fully converted to Tailwind (23%)
- 49 components remaining (77%)
- All converted components tested and type-checked
- Build passing, app functional

Session converted:
- ErrorMessage, ToggleGroup, StatusIndicator
- Tooltip, ChatToggles, Context1MCheckbox
- Modal (10+ exports), DirectorySelectModal
- ToolPrimitives (10+ exports)
- 4 Message components
- LeftSidebar, ErrorBoundary

Next session should tackle remaining message/tool components,
then move to complex layout files.
- Fixed font-[var(--font-primary)] β†’ font-primary in CostsTab, RightSidebar, ReviewControls, UntrackedStatus
- Fixed font-[var(--font-monospace)] β†’ font-mono in ReviewControls, UntrackedStatus
- Incorrect syntax was causing font-family to not be applied correctly
- Should resolve monospace font appearing in costs sidebar and chat messages

Fixes issue reported by user
- Add --font-primary and --font-monospace CSS variables to :root
- Add 'primary' to Tailwind fontFamily config
- Fixes issue where font-primary class and var(--font-primary) were undefined
- Ensures all text uses correct IBM Plex Sans font
CSS custom properties don't need quotes around font family names.
The quotes are only needed in direct font-family declarations, not in variable definitions.
The font-mono class on the main AIView container was cascading down to all child
elements (chat messages, costs tab, etc.), making everything display in monospace.

Removed font-mono and text-xs from the container div since those should be applied
to specific elements that need them, not inherited globally.
- Updated to tailwindcss@next (v4.0.0) and @tailwindcss/vite@next
- Replaced PostCSS config with Vite plugin approach
- Converted globals.css to use @import 'tailwindcss' (v4 format)
- Removed tailwind.config.ts (v4 uses CSS-first configuration)
- Updated Vite to v7 for ESM compatibility
- Renamed vite.config.ts to vite.config.mts for proper ESM handling

Dev mode works, but production builds fail with Tailwind plugin error.
This appears to be a bug in the Tailwind v4 beta.
- Removed @layer base wrapper (not needed in v4)
- Replaced @apply directives with regular CSS
- Fixed indentation issues outside :root
- Removed extra closing brace that was causing parse errors

Dev mode works perfectly, production build still fails with Tailwind v4 plugin error.
Fixed all CSS syntax issues:
- Removed @layer base wrapper (v4 doesn't use it)
- Replaced @apply directives with plain CSS
- Fixed all indentation issues
- Removed extra closing braces
- Used Prettier to validate CSS syntax

βœ… Dev mode works perfectly
❌ Production builds fail with 'Cannot convert undefined or null to object'

This is a known bug in @tailwindcss/vite v4.0.0 beta. Even a minimal CSS file
with just @import 'tailwindcss' and one rule produces the same error in production
builds, while dev mode works fine.

Issue appears to be in the Vite plugin's production build code path, not our CSS.
- Update tailwindcss and @tailwindcss/vite from 4.0.0 to 4.1.15
- Set components.json config field to empty string for Tailwind v4
- Fixes production build error: 'Cannot convert undefined or null to object'

The error only occurred in production builds due to a bug in v4.0.0.
Version 4.1.15 resolves the issue and builds successfully.

Generated with `cmux`
The renameWorkspace tests were bypassing IPC by manually manipulating config,
creating duplicate workspace entries. This caused rename operations to fail in CI.

Fixed by removing all manual config.saveConfig() calls - setupWorkspace() already
creates workspaces via WORKSPACE_CREATE IPC, which properly adds them to config.

_Generated with `cmux`_
Change worker.plugins from function to array to match main branch.
The function syntax was breaking worker initialization in E2E tests.
Explicitly exclude *.worker.ts files from Tailwind scanning and add
worker rollupOptions to ensure proper module format.
Use plugin apply() filter to exclude workers from Tailwind processing.
This ensures workers are built with only the explicit worker.plugins.
Remove the apply() filter on Tailwind plugin as it may be too restrictive.
The function syntax plugins: () => [...] prevents workers from initializing properly.
Main branch uses plugins: [...] which works correctly.
Changed @[700px]:hidden to max-@[700px]:hidden to match original logic.
Original: hide when container <= 700px (mobile)
Was: hide when container >= 700px (desktop) ❌
Now: hide when container <= 700px (mobile) βœ…
@kylecarbs kylecarbs force-pushed the migrate-to-tailwind-shadcn branch 3 times, most recently from 898e42a to 9fa0a88 Compare October 23, 2025 00:15
@kylecarbs kylecarbs force-pushed the migrate-to-tailwind-shadcn branch from 9fa0a88 to fc9c414 Compare October 23, 2025 00:15
Apply changes that landed in main after our rebase base point:

1. Review Panel (0a1aea1):
   - Remove extractCommonPrefix import and usage
   - Remove commonPrefix state
   - Simplify file tree to show full directory structure

2. FileTree (0a1aea1):
   - Remove commonPrefix prop from component interface
   - Remove startNode computation logic
   - Remove CommonPrefix display element
   - Render from root node directly

3. ProjectSidebar (f147297):
   - Wrap Add Project button in TooltipWrapper
   - Show tooltip on hover matching other UI elements
   - Remove redundant title attribute

These are non-styling logic changes that need to be preserved
in the Tailwind migration.
@kylecarbs kylecarbs merged commit 7e7d5c7 into main Oct 23, 2025
13 checks passed
@kylecarbs kylecarbs deleted the migrate-to-tailwind-shadcn branch October 23, 2025 02:25
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