Remove all styled(Component) patterns in favor of parent wrappers and props (#18430)

## Summary

Eliminates all ~350 `styled(Component)` usages across `twenty-front` and
`twenty-ui` (212 files changed). Each was replaced following these
rules:

- **Margin/layout CSS** (margin, padding, flex, align-self, width) →
wrapped in a `styled.div`/`styled.span` parent container
- **Third-party components** (Link, TextareaAutosize,
ReactPhoneNumberInput, Handle, etc.) → parent container with child CSS
selectors (`> a`, `> textarea`, `> input`, etc.)
- **Intrinsic behavior via existing props** (TableRow
`gridTemplateColumns`, TableCell `color`/`align`) → replaced
`styled(TableRow)` / `styled(TableCell)` with direct prop usage
- **Other visual overrides on twenty-ui components** (Card, Section,
TabList, Button, MenuItem, ScrollWrapper, etc.) → parent wrappers with
`> div` / `> *` child selectors
- **Extending styled.div/span** → merged all CSS into a single
`styled.div`/`styled.span`

Also adds `overflow: hidden` to parent containers wrapping
`ScrollWrapper` so scroll activates correctly with the new wrapper
structure.

### Migration patterns

| Before | After |
|--------|-------|
| `styled(Avatar)` with `margin-right` | `<StyledAvatarContainer><Avatar
/></StyledAvatarContainer>` |
| `styled(Link)` with `text-decoration: none` |
`<StyledLinkContainer><Link /></StyledLinkContainer>` with `> a { ... }`
|
| `styled(TableRow)` with `grid-template-columns` | `<TableRow
gridTemplateColumns="..." />` |
| `styled(TableCell)` with `color` / `align` | `<TableCell color={...}
align="right" />` |
| `styled(Card)` with `margin-top` | `<StyledCardContainer><Card
/></StyledCardContainer>` |
| `styled(TabList)` with `background` |
`<StyledTabListContainer><TabList /></StyledTabListContainer>` with `>
div { ... }` |
| `styled(StyledBase)` extending a `styled.div` | Single merged
`styled.div` with all styles inlined |
This commit is contained in:
Charles Bochet
2026-03-05 18:16:25 +01:00
committed by GitHub
parent e5e3132ddd
commit c53a13417e
224 changed files with 4765 additions and 3837 deletions
@@ -1,5 +1,3 @@
import { styled } from '@linaria/react';
import {
formatExpiration,
isExpired,
@@ -7,23 +5,11 @@ import {
import { TableCell } from '@/ui/layout/table/components/TableCell';
import { TableRow } from '@/ui/layout/table/components/TableRow';
import { IconChevronRight } from 'twenty-ui/display';
import { styled } from '@linaria/react';
import { useContext } from 'react';
import { MOBILE_VIEWPORT, ThemeContext } from 'twenty-ui/theme-constants';
import { ThemeContext } from 'twenty-ui/theme-constants';
import { type ApiKey } from '~/generated-metadata/graphql';
export const StyledApisFieldTableRow = styled(TableRow)`
@media (max-width: ${MOBILE_VIEWPORT}px) {
width: 100%;
}
`;
const StyledTruncatedCell = styled(TableCell)`
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
`;
const StyledEllipsisLabel = styled.div`
white-space: nowrap;
text-overflow: ellipsis;
@@ -49,24 +35,40 @@ export const SettingsApiKeysFieldItemTableRow = ({
const gridColumns = '5fr 2fr 3fr 1fr';
return (
<StyledApisFieldTableRow gridAutoColumns={gridColumns} to={to}>
<StyledTruncatedCell color={theme.font.color.primary}>
<TableRow gridAutoColumns={gridColumns} to={to}>
<TableCell
color={theme.font.color.primary}
whiteSpace="nowrap"
overflow="hidden"
textOverflow="ellipsis"
clickable
>
<StyledEllipsisLabel>{apiKey.name}</StyledEllipsisLabel>
</StyledTruncatedCell>
</TableCell>
<StyledTruncatedCell color={theme.font.color.tertiary}>
<TableCell
color={theme.font.color.tertiary}
whiteSpace="nowrap"
overflow="hidden"
textOverflow="ellipsis"
clickable
>
<StyledEllipsisLabel>{apiKey.role?.label || '-'}</StyledEllipsisLabel>
</StyledTruncatedCell>
</TableCell>
<StyledTruncatedCell
<TableCell
color={
isExpired(apiKey.expiresAt || null)
? theme.font.color.danger
: theme.font.color.tertiary
}
whiteSpace="nowrap"
overflow="hidden"
textOverflow="ellipsis"
clickable
>
<StyledEllipsisLabel>{formattedExpiration}</StyledEllipsisLabel>
</StyledTruncatedCell>
</TableCell>
<TableCell align="right">
<IconChevronRight
@@ -74,6 +76,6 @@ export const SettingsApiKeysFieldItemTableRow = ({
color={theme.font.color.tertiary}
/>
</TableCell>
</StyledApisFieldTableRow>
</TableRow>
);
};
@@ -10,7 +10,7 @@ import { getSettingsPath } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useGetApiKeysQuery } from '~/generated-metadata/graphql';
const StyledTableBody = styled(TableBody)`
const StyledTableBodyContainer = styled.div`
border-bottom: 1px solid ${themeCssVariables.border.color.light};
`;
@@ -36,17 +36,19 @@ export const SettingsApiKeysTable = () => {
<TableHeader></TableHeader>
</TableRow>
{!!apiKeys?.length && (
<StyledTableBody>
{apiKeys.map((apiKey) => (
<SettingsApiKeysFieldItemTableRow
key={apiKey.id}
apiKey={apiKey}
to={getSettingsPath(SettingsPath.ApiKeyDetail, {
apiKeyId: apiKey.id,
})}
/>
))}
</StyledTableBody>
<StyledTableBodyContainer>
<TableBody>
{apiKeys.map((apiKey) => (
<SettingsApiKeysFieldItemTableRow
key={apiKey.id}
apiKey={apiKey}
to={getSettingsPath(SettingsPath.ApiKeyDetail, {
apiKeyId: apiKey.id,
})}
/>
))}
</TableBody>
</StyledTableBodyContainer>
)}
</Table>
);
@@ -11,23 +11,12 @@ import { useContext } from 'react';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
import { type Webhook } from '~/generated-metadata/graphql';
export const StyledApisFieldTableRow = styled(TableRow)`
grid-template-columns: 1fr 28px;
`;
const WEBHOOK_TABLE_ROW_GRID_TEMPLATE_COLUMNS = '1fr 28px';
const StyledIconTableCell = styled(TableCell)`
justify-content: center;
padding-right: ${themeCssVariables.spacing[1]};
padding-left: 0;
`;
const StyledUrlTableCell = styled(TableCell)`
color: ${themeCssVariables.font.color.primary};
overflow: hidden;
`;
const StyledIconChevronRight = styled(IconChevronRight)`
const StyledIconChevronRightContainer = styled.span`
align-items: center;
color: ${themeCssVariables.font.color.tertiary};
display: flex;
`;
export const SettingsDevelopersWebhookTableRow = ({
@@ -42,8 +31,11 @@ export const SettingsDevelopersWebhookTableRow = ({
}) => {
const { theme } = useContext(ThemeContext);
return (
<StyledApisFieldTableRow to={to}>
<StyledUrlTableCell>
<TableRow
gridTemplateColumns={WEBHOOK_TABLE_ROW_GRID_TEMPLATE_COLUMNS}
to={to}
>
<TableCell color={themeCssVariables.font.color.primary} overflow="hidden">
<OverflowingTextWithTooltip
text={
isValidUrl(webhook.targetUrl)
@@ -51,13 +43,18 @@ export const SettingsDevelopersWebhookTableRow = ({
: webhook.targetUrl
}
/>
</StyledUrlTableCell>
<StyledIconTableCell>
<StyledIconChevronRight
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
</StyledIconTableCell>
</StyledApisFieldTableRow>
</TableCell>
<TableCell
align="center"
padding={`0 ${themeCssVariables.spacing[1]} 0 0`}
>
<StyledIconChevronRightContainer>
<IconChevronRight
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
</StyledIconChevronRightContainer>
</TableCell>
</TableRow>
);
};
@@ -10,16 +10,12 @@ import { getSettingsPath } from 'twenty-shared/utils';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useGetWebhooksQuery } from '~/generated-metadata/graphql';
const StyledTableBody = styled(TableBody)`
const StyledTableBodyContainer = styled.div`
border-bottom: 1px solid ${themeCssVariables.border.color.light};
max-height: 260px;
overflow-y: auto;
`;
const StyledTableRow = styled(TableRow)`
grid-template-columns: 444px 68px;
`;
export const SettingsWebhooksTable = () => {
const { data: webhooksData } = useGetWebhooksQuery();
@@ -27,22 +23,24 @@ export const SettingsWebhooksTable = () => {
return (
<Table>
<StyledTableRow>
<TableRow gridTemplateColumns="444px 68px">
<TableHeader>URL</TableHeader>
<TableHeader></TableHeader>
</StyledTableRow>
</TableRow>
{!!webhooks?.length && (
<StyledTableBody>
{webhooks.map((webhookFieldItem) => (
<SettingsDevelopersWebhookTableRow
key={webhookFieldItem.id}
webhook={webhookFieldItem}
to={getSettingsPath(SettingsPath.WebhookDetail, {
webhookId: webhookFieldItem.id,
})}
/>
))}
</StyledTableBody>
<StyledTableBodyContainer>
<TableBody>
{webhooks.map((webhookFieldItem) => (
<SettingsDevelopersWebhookTableRow
key={webhookFieldItem.id}
webhook={webhookFieldItem}
to={getSettingsPath(SettingsPath.WebhookDetail, {
webhookId: webhookFieldItem.id,
})}
/>
))}
</TableBody>
</StyledTableBodyContainer>
)}
</Table>
);
@@ -60,13 +60,15 @@ const StyledControlLabel = styled.span`
white-space: nowrap;
`;
const StyledControlIconChevronDown = styled(IconChevronDown)<{
const StyledControlIconChevronDownContainer = styled.span<{
disabled?: boolean;
}>`
align-items: center;
color: ${({ disabled }) =>
disabled
? themeCssVariables.font.color.extraLight
: themeCssVariables.font.color.tertiary};
display: flex;
`;
type WebhookEntitySelectProps = {
@@ -166,10 +168,9 @@ export const WebhookEntitySelect = ({
clickableComponent={
<StyledControlContainer disabled={disabled}>
<StyledControlLabel>{getSelectedLabel()}</StyledControlLabel>
<StyledControlIconChevronDown
disabled={disabled}
size={theme.icon.size.md}
/>
<StyledControlIconChevronDownContainer disabled={disabled}>
<IconChevronDown size={theme.icon.size.md} />
</StyledControlIconChevronDownContainer>
</StyledControlContainer>
}
dropdownComponents={