fix(twenty-front): new layout fast-follows (#21360)

Fast-follows for the new layout / flat redesign (master:
twentyhq/core-team-issues#2478).

## Changes
- **Main navbar 48px** (twentyhq/core-team-issues#2479) —
`SIDE_PANEL_TOP_BAR_HEIGHT` 40 → 48, so `PageCardHeader` matches the
Figma target. The side panel top bar shares this constant and stays
aligned.
- **Content panel 12px radius** (twentyhq/core-team-issues#2480) —
`PageCardLayout` card gets a full border + 12px radius and an 8px inset
(`spacing[2]`) so it floats on the shell instead of square full-bleed.
- **Square three-dots button** (twentyhq/core-team-issues#2481) — added
a `square` option to `AnimatedButton`; the page-header side-panel toggle
now renders a 24×24 square icon button instead of a 32×24 pill.
- **Table checkbox sizing** (twentyhq/core-team-issues#2482) — restored
`box-sizing: content-box` on the checkbox box. Its border is declared
outside the label size, so the global `border-box` reset (#21349) was
shrinking it (14px → 12px). Same fix pattern as #21349.
- **Tertiary navbar background** (twentyhq/core-team-issues#2483) — left
navbar / app shell use `background/tertiary` instead of the noisy
surface (`DefaultLayout`, `UserOrMetadataLoader`).
- **Skeleton loading** (twentyhq/core-team-issues#2484) — metadata +
content loading now match the new layout: tertiary shell, 12px rounded
content panel, 48px navbar, sparse bars, empty body (removed the dense
full-width rows). Left-panel skeleton bars use `quaternary` so they stay
visible on the tertiary shell.

## Verification
- typecheck (tsgo) + oxlint + oxfmt pass for all changed files.
- Verified live against a running workspace: measured navbar = 48px,
three-dots = 24×24, checkbox box-sizing = content-box (14px), card
radius = 12px, shell background = tertiary (no noisy image).
Content-loading skeleton matches the target.
This commit is contained in:
Félix Malfait
2026-06-09 16:10:36 +02:00
committed by GitHub
parent c27c8c88b0
commit 02aa086866
9 changed files with 33 additions and 26 deletions
@@ -66,7 +66,7 @@ export const LeftPanelSkeletonLoader = () => {
<StyledItemsContainer>
<StyledSkeletonTitleContainer>
<SkeletonTheme
baseColor={theme.background.tertiary}
baseColor={theme.background.quaternary}
highlightColor={theme.background.transparent.lighter}
borderRadius={4}
>
@@ -1,20 +1,9 @@
import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader';
import { PageCardHeader } from '@/ui/layout/page/components/PageCardHeader';
import { PageCardLayout } from '@/ui/layout/page/components/PageCardLayout';
import { styled } from '@linaria/react';
import { type ReactNode, useContext } from 'react';
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';
import {
ThemeContext,
themeCssVariables,
} from 'twenty-ui-deprecated/theme-constants';
const StyledBody = styled.div`
display: flex;
flex-direction: column;
gap: ${themeCssVariables.spacing[2]};
padding: ${themeCssVariables.spacing[3]};
`;
import { ThemeContext } from 'twenty-ui-deprecated/theme-constants';
type PageContentSkeletonLoaderProps = {
secondaryBar?: ReactNode;
@@ -41,17 +30,18 @@ export const PageContentSkeletonLoader = ({
height={SKELETON_LOADER_HEIGHT_SIZES.standard.s}
/>
}
actionButton={
<Skeleton
width={80}
height={SKELETON_LOADER_HEIGHT_SIZES.standard.s}
/>
}
/>
}
secondaryBar={secondaryBar}
showInformationBanner={false}
>
<StyledBody>
<Skeleton
count={8}
height={SKELETON_LOADER_HEIGHT_SIZES.standard.l}
/>
</StyledBody>
{null}
</PageCardLayout>
</SkeletonTheme>
);
@@ -12,7 +12,7 @@ import { LeftPanelSkeletonLoader } from '~/loading/components/LeftPanelSkeletonL
import { PageContentSkeletonLoader } from '~/loading/components/PageContentSkeletonLoader';
const StyledContainer = styled.div`
background: ${themeCssVariables.background.noisy};
background: ${themeCssVariables.background.tertiary};
box-sizing: border-box;
display: flex;
flex-direction: row;
@@ -152,6 +152,7 @@ export const SidePanelToggleButton = () => {
dataClickOutsideId={PAGE_HEADER_SIDE_PANEL_BUTTON_CLICK_OUTSIDE_ID}
dataTestId="page-header-side-panel-button"
size={isMobile ? 'medium' : 'small'}
square
variant="secondary"
accent="default"
ariaLabel={ariaLabel}
@@ -1 +1 @@
export const SIDE_PANEL_TOP_BAR_HEIGHT = 40;
export const SIDE_PANEL_TOP_BAR_HEIGHT = 48;
@@ -25,7 +25,7 @@ import { AnimatePresence, LayoutGroup } from 'framer-motion';
import { Outlet } from 'react-router-dom';
import { themeCssVariables } from 'twenty-ui-deprecated/theme-constants';
const StyledLayout = styled.div`
background: ${themeCssVariables.background.noisy};
background: ${themeCssVariables.background.tertiary};
display: flex;
flex-direction: column;
height: 100dvh;
@@ -28,6 +28,7 @@ const StyledMainCardWrapper = styled.div`
const StyledCard = styled.div`
background: ${themeCssVariables.background.primary};
border-left: 1px solid ${themeCssVariables.border.color.medium};
border-radius: 12px 0 0 12px;
border-right: 1px solid ${themeCssVariables.border.color.medium};
box-sizing: border-box;
display: flex;
@@ -20,6 +20,8 @@ export type AnimatedButtonProps = ButtonProps &
Pick<MotionProps, 'animate' | 'transition'> & {
animatedSvg: React.ReactNode;
soonLabel?: string;
// Renders a square icon-only button (width matches the size-based height).
square?: boolean;
};
type AnimatedButtonDynamicStyles = {
@@ -315,7 +317,7 @@ const StyledButton = styled.button<
| 'target'
| 'dataClickOutsideId'
| 'dataGloballyPreventClickOutside'
>
> & { square?: boolean }
>`
align-items: center;
background: var(--abtn-bg);
@@ -354,14 +356,23 @@ const StyledButton = styled.button<
font-size: ${themeCssVariables.font.size.md};
gap: ${themeCssVariables.spacing[1]};
height: ${({ size }) => (size === 'small' ? '24px' : '32px')};
justify-content: ${({ justify }) => justify ?? ''};
padding: 0 ${themeCssVariables.spacing[2]};
justify-content: ${({ justify, square }) =>
square ? 'center' : (justify ?? '')};
padding: ${({ square }) =>
square ? '0' : `0 ${themeCssVariables.spacing[2]}`};
transition: background 0.1s ease;
white-space: nowrap;
width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')};
width: ${({ fullWidth, square, size }) =>
square
? size === 'small'
? '24px'
: '32px'
: fullWidth
? '100%'
: 'auto'};
&:focus {
outline: none;
@@ -428,6 +439,7 @@ export const AnimatedButton = ({
inverted = false,
size = 'medium',
accent = 'default',
square = false,
position = 'standalone',
soon = false,
disabled = false,
@@ -476,6 +488,7 @@ export const AnimatedButton = ({
as={ButtonComponent}
fullWidth={fullWidth}
size={size}
square={square}
position={position}
disabled={isDisabled}
justify={justify}
@@ -159,6 +159,8 @@ const StyledCheckboxContainer = styled.div<InputProps>`
border-radius: var(--checkbox-border-radius);
border-style: solid;
border-width: var(--checkbox-border-width);
// The border sits outside the declared label size, so size against the content box.
box-sizing: content-box;
content: '';
cursor: var(--checkbox-cursor);
display: inline-block;