[DevXP] Improve Linaria pre-build speed (#18382)

## Summary

This PR improves Linaria/WYW pre-build speed and continues the migration
of `twenty-ui` components away from runtime `ThemeContext` reads toward
static CSS variables and theme constants.

### Linaria/WYW profiling plugin improvements (`twenty-shared`)

- **Babel JIT warmup**: added a `buildStart` warmup step that triggers
WYW's Babel JIT compilation before the real build starts, so the first
real file doesn't pay the cold-start penalty
- **`configResolved` hook**: detects dev vs prod mode and resolves the
correct warmup file path relative to `config.root`
- **Dev-only per-file logging**: slow file warnings are now gated behind
`isDevMode`, keeping production/CI build output clean
- **`closeBundle` summary**: moved the final top-slow-files report to
`closeBundle` for accurate end-of-build reporting
- **Removed noisy progress interval logging** in favor of the warmup log
+ final summary

### Migration from `ThemeContext` to static CSS variables / constants

Across `twenty-ui`, replaced runtime `useTheme()` reads with:
- `themeCssVariables` CSS custom properties (colors, spacing)
- Hard-coded design-system constants (`ICON.size.md` → `16`,
`ICON.stroke.sm` → `1.6`) so components no longer need a React context
at render time — enabling Linaria static extraction

**Components migrated:**
- `Button`, `AnimatedButton`, `LightButton`, `LightIconButton`,
`AnimatedLightIconButton`, `ButtonIcon`, `ButtonSoon`
- `ProgressBar` (Framer Motion width animation → CSS `transition`)
- `Info`, `HorizontalSeparator`, `LinkChip`
- `MenuPicker`, `MenuItemLeftContent`, `MenuItemIconWithGripSwap`,
`NavigationBarItem`
- `JsonArrow`, `JsonNestedNode`
- `ModalHeader`

### Other
- Added `aria-valuenow` to `ProgressBar` for accessibility
- `VisibilityHidden` component updated to inline accessibility styles
This commit is contained in:
Charles Bochet
2026-03-04 17:04:16 +01:00
committed by GitHub
parent be01a85d67
commit eda905f271
55 changed files with 691 additions and 650 deletions
@@ -4,21 +4,37 @@ import { themeCssVariables } from 'twenty-ui/theme-constants';
type TableCellProps = {
align?: 'left' | 'center' | 'right';
color?: string;
gap?: string;
height?: string;
maxWidth?: string;
minWidth?: string;
overflow?: string;
padding?: string;
textOverflow?: string;
whiteSpace?: string;
clickable?: boolean;
};
const StyledTableCell = styled.div<TableCellProps>`
align-items: center;
color: ${({ color }) => color || themeCssVariables.font.color.secondary};
cursor: ${({ clickable }) => (clickable === true ? 'pointer' : 'default')};
display: flex;
height: ${themeCssVariables.spacing[8]};
gap: ${({ gap }) => gap ?? 'normal'};
height: ${({ height }) => height ?? themeCssVariables.spacing[8]};
justify-content: ${({ align }) =>
align === 'right'
? 'flex-end'
: align === 'center'
? 'center'
: 'flex-start'};
padding: 0 ${themeCssVariables.spacing[2]};
max-width: ${({ maxWidth }) => maxWidth ?? 'none'};
min-width: ${({ minWidth }) => minWidth ?? 'auto'};
overflow: ${({ overflow }) => overflow ?? 'visible'};
padding: ${({ padding }) => padding ?? `0 ${themeCssVariables.spacing[2]}`};
text-align: ${({ align }) => align ?? 'left'};
text-overflow: ${({ textOverflow }) => textOverflow ?? 'clip'};
white-space: ${({ whiteSpace }) => whiteSpace ?? 'normal'};
`;
export { StyledTableCell as TableCell };
@@ -4,6 +4,7 @@ import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledTableHeader = styled.div<{
align?: 'left' | 'center' | 'right';
onClick?: () => void;
padding?: string;
}>`
gap: ${themeCssVariables.spacing[1]};
align-items: center;
@@ -18,7 +19,7 @@ const StyledTableHeader = styled.div<{
: align === 'center'
? 'center'
: 'flex-start'};
padding: 0 ${themeCssVariables.spacing[2]};
padding: ${({ padding }) => padding ?? `0 ${themeCssVariables.spacing[2]}`};
text-align: ${({ align }) => align ?? 'left'};
cursor: ${({ onClick }) => (onClick ? 'pointer' : 'default')};
`;
@@ -4,15 +4,24 @@ import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants';
const StyledTableRow = styled.div<{
isSelected?: boolean;
isExpanded?: boolean;
onClick?: () => void;
to?: string;
gridAutoColumns?: string;
gridTemplateColumns?: string;
mobileGridAutoColumns?: string;
height?: string;
cursor?: string;
hoverBackgroundColor?: string;
}>`
background-color: ${({ isSelected }) =>
isSelected ? themeCssVariables.accent.quaternary : 'transparent'};
background-color: ${({ isSelected, isExpanded }) =>
isSelected
? themeCssVariables.accent.quaternary
: isExpanded === true
? themeCssVariables.background.transparent.light
: 'transparent'};
border-radius: ${themeCssVariables.border.radius.sm};
cursor: ${({ cursor }) => cursor ?? 'default'};
display: grid;
grid-auto-columns: ${({ gridAutoColumns }) => gridAutoColumns ?? '1fr'};
grid-template-columns: ${({ gridTemplateColumns }) =>
@@ -23,6 +32,7 @@ const StyledTableRow = styled.div<{
mobileGridAutoColumns ?? gridAutoColumns ?? '1fr'};
}
height: ${({ height }) => height ?? 'auto'};
grid-auto-flow: column;
transition: background-color
calc(${themeCssVariables.animation.duration.normal} * 1s);
@@ -30,11 +40,13 @@ const StyledTableRow = styled.div<{
text-decoration: none;
&:hover {
background-color: ${({ onClick, to }) =>
onClick || to
background-color: ${({ onClick, to, hoverBackgroundColor }) =>
hoverBackgroundColor ??
(onClick || to
? themeCssVariables.background.transparent.light
: 'transparent'};
cursor: ${({ onClick, to }) => (onClick || to ? 'pointer' : 'default')};
: 'transparent')};
cursor: ${({ onClick, to, cursor }) =>
cursor ?? (onClick || to ? 'pointer' : 'default')};
}
&[data-clickable='true'] {
@@ -44,6 +56,7 @@ const StyledTableRow = styled.div<{
type TableRowProps = {
isSelected?: boolean;
isExpanded?: boolean;
isClickable?: boolean;
onClick?: () => void;
to?: string;
@@ -52,10 +65,14 @@ type TableRowProps = {
gridAutoColumns?: string;
gridTemplateColumns?: string;
mobileGridAutoColumns?: string;
height?: string;
cursor?: string;
hoverBackgroundColor?: string;
};
export const TableRow = ({
isSelected,
isExpanded,
isClickable,
onClick,
to,
@@ -65,9 +82,13 @@ export const TableRow = ({
gridAutoColumns,
gridTemplateColumns,
mobileGridAutoColumns,
height,
cursor,
hoverBackgroundColor,
}: React.PropsWithChildren<TableRowProps>) => (
<StyledTableRow
isSelected={isSelected}
isExpanded={isExpanded}
onClick={onClick}
gridAutoColumns={gridAutoColumns}
gridTemplateColumns={gridTemplateColumns}
@@ -75,6 +96,9 @@ export const TableRow = ({
style={style}
data-clickable={isClickable}
mobileGridAutoColumns={mobileGridAutoColumns}
height={height}
cursor={cursor}
hoverBackgroundColor={hoverBackgroundColor}
to={to}
as={to ? Link : 'div'}
>