## Summary
Completes the migration of the frontend styling system from **Emotion**
(`@emotion/styled`, `@emotion/react`) to **Linaria** (`@linaria/react`,
`@linaria/core`), a zero-runtime CSS-in-JS library where styles are
extracted at build time.
This is the final step of the migration — all ~494 files across
`twenty-front`, `twenty-ui`, `twenty-website`, and `twenty-sdk` are now
fully converted.
## Changes
### Styling Migration (across ~480 component files)
- Replaced all `@emotion/styled` imports with `@linaria/react`
- Converted runtime theme access patterns (`({ theme }) => theme.x.y`)
to build-time `themeCssVariables` CSS custom properties
- Replaced `useTheme()` hook (from Emotion) with
`useContext(ThemeContext)` where runtime theme values are still needed
(e.g., passing colors to non-CSS props like icon components)
- Removed `@emotion/react` `css` helper usages in favor of Linaria
template literals
### Dependency & Configuration Changes
- **Removed**: `@emotion/react`, `@emotion/styled` from root
`package.json`
- **Added**: `@wyw-in-js/babel-preset`, `next-with-linaria` (for
twenty-website SSR support)
- Updated Nx generator defaults from `@emotion/styled` to
`@linaria/react` in `nx.json`
- Simplified `vite.config.ts` (removed Emotion-specific configuration)
- Updated `twenty-website/next.config.js` to use `next-with-linaria` for
SSR Linaria support
### Storybook & Testing
- Removed `ThemeProvider` from Emotion in Storybook previews
(`twenty-front`, `twenty-sdk`)
- Now relies solely on `ThemeContextProvider` for theme injection
### Documentation
- Removed the temporary `docs/emotion-to-linaria-migration-plan.md`
(migration complete)
- Updated `CLAUDE.md` and `README.md` to reflect Linaria as the styling
stack
- Updated frontend style guide docs across all locales
## How it works
Linaria extracts styles at build time via the `@wyw-in-js/vite` plugin.
All expressions in `styled` template literals must be **statically
evaluable** — no runtime theme objects or closures over component state.
- **Static styles** use `themeCssVariables` which map to CSS custom
properties (`var(--theme-color-x)`)
- **Runtime theme access** (for non-CSS use cases like icon `color`
props) uses `useContext(ThemeContext)` instead of Emotion's `useTheme()`
Closes [571](https://github.com/twentyhq/core-team-issues/issues/571).
Replicates the behavior of number field and allows specifying the number
of decimals for currency field.
<img width="931" height="858" alt="image"
src="https://github.com/user-attachments/assets/f5100d58-b1b0-4a88-a090-e98b2feeebd0"
/>
Currencies around the world have a maximum of three decimal places -
BHD, KWD etc. However, I have added a maximum of five decimal places in
case someone wants to use the currency field type for displaying things
like `per-second-billing` or `exchange rates`.
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> Adds configurable decimals (0–5) for currency fields, updating
settings UI, types/schema, display/aggregate formatting, and input
precision.
>
> - **Currency field settings**:
> - Add `decimals` (0–5) to `FieldCurrencyMetadata.settings` and
validate via `currencyFieldSettingsSchema`.
> - Update `SettingsDataModelFieldCurrencyForm` to manage `format`
(short/full) and `decimals` with counter when `full`; wire defaults via
`useCurrencySettingsFormInitialValues`.
> - **Display & aggregation**:
> - `CurrencyDisplay` and
`transformAggregateRawValueIntoAggregateDisplayValue` honor `decimals`
when `format` is `full`; keep short format using `formatToShortNumber`.
> - **Input**:
> - Increase currency `IMaskInput` precision with `scale=5`.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
959a8fc1ea726d206548ea32cada28d77c1c6eb9. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
The goal of this PR is to test if Twenty can support a large number of
members, a question which was raised by a large company that is
considering moving away from Salesforce.
I was expecting the currentWorkspaceMembersState to cause a lot more
issue. It would be very hard to get rid of it in the context of actors,
I think that would require a big refactoring. I thought we'd have to do
it but it turns out the perf are pretty good. One thing we need to
improve is the pagination on the roles page, we'll wait for @Bonapara to
update that
This PR prepares the refactor of record field definition and column
definition.
We need to separate the two concepts of FieldInput and FieldContext at
the cell level, and the new RecordField concept that will replace
FieldDefinition at the metadata and state level.
So we create a new ui sub-folder in the already existing record-field
folder, in order to separate those two concepts that are still very
close.
This PR implements sub-field filtering on CURRENCY field type and
improves many related zones.
- Created a ObjectFilterDropdownCurrencySelect dropdown component for
filtering on multiple currencies
- Added currencyCode sub-field to CurrencyFilter type
- Created getDefaultSubFieldNameForCompositeFilterableFieldType to avoid
situation where we don't have any sub field name in sub field filtering
situations.
- Implemented filtering for currencyCode in
computeFilterRecordGqlOperationFilter
- Implemented CURRENCY type in getRecordFilterOperands
- Implemented isMatchingCurrencyFilter for using in
isRecordMatchingFilter for proper optimistic rendering
- Created turnCurrencyIntoSelectableItem to help
ObjectFilterDropdownCurrencySelect
Testing :
- Added test for currency sub fields in getOperandsForFilterType
- Completely reworked isMatchingCurrencyFilter test
Improvements :
- Created a unique CURRENCIES constant to avoid re-creating it at
various places
- Derive the type FilterableFieldType from a constant array
FILTERABLE_FIELD_TYPES, so it's easier to work with
- Added areCompositeTypeSubFieldsFilterable
- Fixed a bug with empty value '[]' that was preventing the auto-removal
of a filter chip
Miscellaneous :
- Created isExpectedSubFieldName util to do a type-safe check of a
subFieldName
- Better naming : renamed isCompositeField to isCompositeFieldType
- Created isCompositeTypeFilterableWithAny to specify which field types
are filterable by any sub field
- Better naming : renamed
ObjectFilterDropdownFilterSelectCompositeFieldSubMenu to
ObjectFilterDropdownSubFieldSelect
- Better naming : renamed ObjectFilterDropdownFilterSelect to
ObjectFilterDropdownFieldSelect
- Created isEmptinessOperand util instead of duplicating the same
hard-coded check in multiple places
- Better naming : used subFieldName instead of compositeFieldName for
consistency
- UseEffect removal : removed unnecessary useEffect in
MultipleSelectDropdown
Fixes a bug where Empty and Not weren't appearing in filter chip in
particular cases
Fixes https://github.com/twentyhq/core-team-issues/issues/498
Fixes https://github.com/twentyhq/twenty/issues/7558
# Introduction
closes https://github.com/twentyhq/core-team-issues/issues/591
Same than for `twenty-shared` made in
https://github.com/twentyhq/twenty/pull/11083.
## TODO
- [x] Manual migrate twenty-website twenty-ui imports
## What's next:
- Generate barrel and migration script factorization within own package
+ tests
- Refactoring using preconstruct ? TimeBox
- Lint circular dependencies
- Lint import from barrel and forbid them
### Preconstruct
We need custom rollup plugins addition, but preconstruct does not expose
its rollup configuration. It might be possible to handle this using the
babel overrides. But was a big tunnel.
We could give it a try afterwards ! ( allowing cjs interop and stuff
like that )
Stuck to vite lib app
Closed related PRs:
- https://github.com/twentyhq/twenty/pull/11294
- https://github.com/twentyhq/twenty/pull/11203
- Removed disableBlur property from dropdown because it is no longer
needed since there's only one OverlayContainer component so there can be
only one blur at a time.
- Removed blur CSS properties from every component that used it because
one standalone OverlayContainer is able to handle all cases if placed
properly.
- Also removed disableBackgroundBlur property from SingleRecordSelect
- Removed FieldInputOverlay and FieldTextAreaOverlay components that
were a first attempt to create something like an OverlayContainer
- Used new unified OverlayContainer in RecordInlineCell and
RecordTableCell
- Fixed ScrollWrapper so that it works well both for dropdown with non
overflowing content and dropdown with overflowing content.
- Removed export default value on SearchVariablesDropdown as it is not
used in this codebase
- Refactored SearchVariablesDropdown function as component anti-pattern
- Refactored SearchVariablesDropdownFieldItems UI problems with
separator and missing ScrollWrapper behavior
- Refactored SearchVariablesDropdownObjectItems with UI problems with
separator and missing ScrollWrapper behavior
- Fixed blur bug on Firefox due to wrong placement of the element that
had the CSS property. Blur works on Firefox it it's on the container
that has the highest level in the tree.
- Fixed bug in ActivityTargetInlineCell by removing an unnecessary
container component StyledSelectContainer
- Unified problems of field height with a new common component
FieldInputContainer, instead of putting width and height at the wrong
abstraction level, width and height are a field's concern not a
dropdown, overlay or low-level input concern.
- Fixed block editor dropdown with new OverlayContainer
- Aligning field dropdown with their anchor on inline and table cells,
there are still many small pixel misalignments that give a low quality
impression.
- Fixed FormDateFieldInput that was missing OverlayContainer
This PR fixes zero being displayed as empty in record cell and show page
in currency field #6802
I checked graphql resquests and the data is stored in the correct form
(0 or null). The problem only lies in the front end and how the field is
null checked.
---------
Co-authored-by: Félix Malfait <felix@twenty.com>
Some parts of the Frontend used theme constants exported from
`modules/ui` while other parts used theme constants exported from
`twenty-ui`.
This PR centralizes theme constants by removing them from `modules/ui`
completely.
- Fix default value sent to backend, using single quotes by default
- Use default value in field definition and column definition so that
field inputs can access it
- Used currency default value in CurrencyFieldInput
---------
Co-authored-by: Charles Bochet <charles@twenty.com>