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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user