From 77c84815ef74001c888ceafee282b61bb7d72b04 Mon Sep 17 00:00:00 2001
From: "Abdullah." <125115953+mabdullahabaid@users.noreply.github.com>
Date: Sat, 27 Jun 2026 20:41:34 +0500
Subject: [PATCH] feat(website): rework the product-stepper Layout visual
(#22249)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## What
Reworks the **"Layout" step** visual of the product-page stepper (the
floating record-layout-editor scene) to match the Figma references and
read as a premium, layered composition. Data is unchanged — this is
purely the look.
## Figma alignment
- **Fields editor** — the inline-edit field row now keeps the field icon
+ a bordered input + blue **Done** + the eye / ⋮ (instead of a floating
box); **"New fields"** renders as a uniform field row; section headers
get an overflow menu; field types read `Boolean` / `Date & Time`.
Dropped the redundant "Fields widget" header label.
- **"General" widget** — header chevron, the real `anonymousFelix`
avatar on Account Owner (reusing the shared asset the other
product-feature visuals use), a **Revenue** row, and a money-bag icon
for the currency field.
## Premium composition
- The nav, record, and Fields editor read as one elevated **z-stack**
(back → front): the nav is pushed right so its edge tucks under the
record (lowest z-index), the record sits above it, and the editor stays
in front — each casting a progressively stronger shadow so the depth
ordering is unmistakable.
- The **"New record / Enrich / Edit actions"** bar is centered over the
nav–record seam (`translateX(-50%)`) to tie the layered stack together
at the top.
## Testing
- typecheck · oxfmt · oxlint · check-conventions — green.
- Verified visually with headless Playwright on `/product` (Layout
step).
---
.../LayoutVisual/LayoutVisual.tsx | 146 +++++++++++-------
.../LayoutVisual/components/LayoutIcons.tsx | 21 ++-
.../LayoutVisual/components/layout-chrome.ts | 104 ++++++-------
.../LayoutVisual/data/layout-data.ts | 4 +-
.../feature-scenes/product-stepper-scene.ts | 4 +
5 files changed, 161 insertions(+), 118 deletions(-)
diff --git a/packages/twenty-website/src/sections/product-stepper/LayoutVisual/LayoutVisual.tsx b/packages/twenty-website/src/sections/product-stepper/LayoutVisual/LayoutVisual.tsx
index 966a6b0e46..34956aaf5d 100644
--- a/packages/twenty-website/src/sections/product-stepper/LayoutVisual/LayoutVisual.tsx
+++ b/packages/twenty-website/src/sections/product-stepper/LayoutVisual/LayoutVisual.tsx
@@ -8,6 +8,7 @@ import {
type PointerEvent as ReactPointerEvent,
} from 'react';
+import { sharedAssetUrls } from '@/app-preview/data/shared-asset-urls';
import { getElementScale } from '@/platform/motion';
import { PRODUCT_STEPPER_SCENE } from '@/tokens/feature-scenes/product-stepper-scene';
@@ -17,6 +18,7 @@ import { LAYOUT_CHROME } from './components/layout-chrome';
import { LAYOUT_EDITOR_CONTENT } from './data/layout-data';
const {
+ ChevronDown: ChevronDownGlyph,
ChevronLeft: ChevronLeftGlyph,
Dots: DotsGlyph,
Eye: EyeGlyph,
@@ -24,8 +26,8 @@ const {
Grip: GripGlyph,
List: ListGlyph,
Nav: NavGlyph,
+ NewFields: NewFieldsGlyph,
NewSection: NewSectionGlyph,
- Plus: PlusGlyph,
Spark: SparkGlyph,
} = LAYOUT_GLYPHS;
@@ -36,8 +38,8 @@ const {
AddSectionRow,
AddText,
DoneButton,
- EditableRow,
- EditableText,
+ EditField,
+ EditGroup,
FieldDot,
FieldIconBox,
FieldLabels,
@@ -51,10 +53,6 @@ const {
NavPanel,
NavSectionLabel,
NavSubItem,
- NewFieldsColumn,
- NewFieldsDescription,
- NewFieldsRow,
- NewFieldsTitle,
PanelActionButton,
PanelBackButton,
PanelFields,
@@ -64,10 +62,10 @@ const {
PanelSubLabel,
PanelTitleBold,
PanelTitleGroup,
- PanelTitleSub,
RightPanel,
SectionName,
SectionRow,
+ WidgetAvatar,
WidgetChip,
WidgetIcon,
WidgetInner,
@@ -158,7 +156,10 @@ export function LayoutVisual({ active }: { active: boolean }) {
>
- {i18n._(msg`General`)}
+
+ {i18n._(msg`General`)}
+
+
@@ -171,7 +172,14 @@ export function LayoutVisual({ active }: { active: boolean }) {
{i18n._(msg`Account Owner`)}
- Félix Malfait
+
+
+ Félix Malfait
+
@@ -187,6 +195,13 @@ export function LayoutVisual({ active }: { active: boolean }) {
{i18n._(msg`ICP`)}
✓ True
+
+
+
+
+ {i18n._(msg`Revenue`)}
+ $500,000
+
@@ -253,7 +268,6 @@ export function LayoutVisual({ active }: { active: boolean }) {
{i18n._(msg`Fields`)}
- {i18n._(msg`Fields widget`)}
@@ -275,46 +289,59 @@ export function LayoutVisual({ active }: { active: boolean }) {
{i18n._(LAYOUT_EDITOR_CONTENT.sectionLabels[section])}
+
+
+
- {sectionNumber === 0 ? (
-
- {i18n._(msg`Industry`)}
- {i18n._(msg`Done`)}
-
- ) : null}
-
{fields
.filter((field) => field.section === section)
- .map((field) => (
-
- handleDragStart(field.id, event)
- }
- >
-
-
-
-
- {i18n._(field.label)}
- ·
- {i18n._(field.type)}
-
- {
- event.stopPropagation();
- toggleVisibility(field.id);
- }}
+ .map((field, fieldNumber) => {
+ const isEditing =
+ sectionNumber === 0 && fieldNumber === 0;
+
+ return (
+ handleDragStart(field.id, event)
+ }
>
-
-
-
-
-
-
- ))}
+
+
+
+ {isEditing ? (
+
+ {i18n._(msg`General`)}
+ {i18n._(msg`Done`)}
+
+ ) : (
+
+ {i18n._(field.label)}
+ ·
+ {i18n._(field.type)}
+
+ )}
+ {
+ event.stopPropagation();
+ toggleVisibility(field.id);
+ }}
+ >
+
+
+
+
+
+
+ );
+ })}
{sectionNumber > 0 && sectionNumber < sections.length - 1 ? (
@@ -327,17 +354,24 @@ export function LayoutVisual({ active }: { active: boolean }) {
))}
-
-
-
-
-
- {i18n._(msg`New fields`)}
-
+
+
+
+
+
+ {i18n._(msg`New fields`)}
+ ·
+
{i18n._(msg`Default position/visibility for field…`)}
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/packages/twenty-website/src/sections/product-stepper/LayoutVisual/components/LayoutIcons.tsx b/packages/twenty-website/src/sections/product-stepper/LayoutVisual/components/LayoutIcons.tsx
index 3c4976d69a..f5a7aa7612 100644
--- a/packages/twenty-website/src/sections/product-stepper/LayoutVisual/components/LayoutIcons.tsx
+++ b/packages/twenty-website/src/sections/product-stepper/LayoutVisual/components/LayoutIcons.tsx
@@ -2,8 +2,8 @@ import {
IconBuildingSkyscraper,
IconCalendar,
IconCheckbox,
+ IconChevronDown,
IconChevronLeft,
- IconCurrencyDollar,
IconDotsVertical,
IconEye,
IconEyeOff,
@@ -13,8 +13,10 @@ import {
IconLink,
IconList,
IconMapPin,
+ IconMoneybag,
+ IconNewSection,
IconNotes,
- IconPlus,
+ IconRowInsertBottom,
IconSettingsAutomation,
IconSparkles,
IconTargetArrow,
@@ -35,7 +37,7 @@ const FIELD_ICONS: Record = {
calendar: IconCalendar,
link: IconLink,
map: IconMapPin,
- money: IconCurrencyDollar,
+ money: IconMoneybag,
target: IconTargetArrow,
user: IconUserCircle,
users: IconUsers,
@@ -71,6 +73,10 @@ function EyeGlyph({ visible }: { visible: boolean }) {
);
}
+function ChevronDownGlyph() {
+ return ;
+}
+
function ChevronLeftGlyph() {
return ;
}
@@ -92,14 +98,15 @@ function SparkGlyph() {
}
function NewSectionGlyph() {
- return ;
+ return ;
}
-function PlusGlyph() {
- return ;
+function NewFieldsGlyph() {
+ return ;
}
export const LAYOUT_GLYPHS = {
+ ChevronDown: ChevronDownGlyph,
ChevronLeft: ChevronLeftGlyph,
Dots: DotsGlyph,
Eye: EyeGlyph,
@@ -107,7 +114,7 @@ export const LAYOUT_GLYPHS = {
Grip: GripGlyph,
List: ListGlyph,
Nav: NavGlyph,
+ NewFields: NewFieldsGlyph,
NewSection: NewSectionGlyph,
- Plus: PlusGlyph,
Spark: SparkGlyph,
};
diff --git a/packages/twenty-website/src/sections/product-stepper/LayoutVisual/components/layout-chrome.ts b/packages/twenty-website/src/sections/product-stepper/LayoutVisual/components/layout-chrome.ts
index 46774a4bdd..0b6a3f2066 100644
--- a/packages/twenty-website/src/sections/product-stepper/LayoutVisual/components/layout-chrome.ts
+++ b/packages/twenty-website/src/sections/product-stepper/LayoutVisual/components/layout-chrome.ts
@@ -7,18 +7,26 @@ const layout = PRODUCT_STEPPER_SCENE.layout;
const PANEL_RADIUS = '3px';
+const PANEL_SHADOW = layout.panelShadow;
+
+const PANEL_SHADOW_MEDIUM = layout.panelShadowMedium;
+
+const PANEL_SHADOW_ELEVATED = layout.panelShadowElevated;
+
const WidgetPanel = styled.div`
backdrop-filter: blur(5px);
background: ${shell.cardBackground};
border: 0.8px solid ${layout.accent};
border-radius: ${PANEL_RADIUS};
- left: 48%;
+ box-shadow: ${PANEL_SHADOW_MEDIUM};
+ left: 42%;
max-height: 36%;
overflow: hidden;
padding: 6px;
position: absolute;
top: 13%;
width: 38%;
+ z-index: 2;
`;
const WidgetInner = styled.div`
@@ -28,9 +36,12 @@ const WidgetInner = styled.div`
`;
const WidgetSectionLabel = styled.div`
+ align-items: center;
color: ${shell.textTertiary};
+ display: flex;
font-size: 7px;
font-weight: 600;
+ justify-content: space-between;
padding: 0 3px;
`;
@@ -72,6 +83,15 @@ const WidgetValue = styled.span`
white-space: nowrap;
`;
+const WidgetAvatar = styled.img`
+ border-radius: 50%;
+ height: 12px;
+ margin-right: 4px;
+ object-fit: cover;
+ vertical-align: middle;
+ width: 12px;
+`;
+
const WidgetChip = styled.span`
background: ${layout.chipWash};
border: 0.5px solid ${shell.borderStrong};
@@ -86,13 +106,15 @@ const NavPanel = styled.div`
background: ${shell.cardBackground};
border: 0.8px solid ${layout.accent};
border-radius: ${PANEL_RADIUS};
- left: 8%;
+ box-shadow: ${PANEL_SHADOW};
+ left: 14%;
max-height: 68%;
overflow-y: auto;
padding: 8px;
position: absolute;
top: 13%;
width: 30%;
+ z-index: 1;
`;
const NavSectionLabel = styled.div`
@@ -163,12 +185,14 @@ const ActionsBar = styled.div`
background: ${shell.cardBackground};
border: 0.8px solid ${layout.accent};
border-radius: ${PANEL_RADIUS};
+ box-shadow: ${PANEL_SHADOW};
display: flex;
gap: 5px;
- left: 46%;
+ left: 44%;
padding: 5px 6px;
position: absolute;
top: 6%;
+ transform: translateX(-50%);
z-index: 3;
`;
@@ -187,12 +211,13 @@ const RightPanel = styled.div`
border: 0.8px solid ${layout.panelAccent};
border-radius: ${PANEL_RADIUS};
bottom: 3%;
+ box-shadow: ${PANEL_SHADOW_ELEVATED};
display: flex;
flex-direction: column;
left: 42%;
overflow: hidden;
position: absolute;
- top: 36%;
+ top: 38%;
width: 54%;
z-index: 4;
`;
@@ -240,14 +265,6 @@ const PanelTitleBold = styled.span`
font-weight: 600;
`;
-const PanelTitleSub = styled.span`
- color: ${shell.textTertiary};
- font-size: 8px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-`;
-
const PanelSubBar = styled.div`
align-items: center;
border-bottom: 0.8px solid ${shell.borderLight};
@@ -285,21 +302,25 @@ const SectionName = styled.span`
font-weight: 600;
`;
-const EditableRow = styled.div`
+const EditGroup = styled.span`
align-items: center;
+ display: flex;
+ gap: 4px;
+ margin-right: auto;
+ min-width: 0;
+`;
+
+const EditField = styled.span`
background: white;
border: 1px solid ${layout.accent};
border-radius: ${PANEL_RADIUS};
- display: flex;
- gap: 4px;
- margin: 2px 0 4px;
- padding: 4px 6px;
-`;
-
-const EditableText = styled.span`
color: ${shell.text};
- flex: 1;
- font-size: 9px;
+ font-size: 8px;
+ overflow: hidden;
+ padding: 3px 6px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ width: 120px;
`;
const DoneButton = styled.span`
@@ -329,6 +350,11 @@ const FieldRow = styled.div`
&:active {
cursor: grabbing;
}
+
+ &[data-editing],
+ &[data-static] {
+ cursor: default;
+ }
`;
const FieldIconBox = styled.span`
@@ -396,30 +422,6 @@ const AddText = styled.span`
font-size: 8px;
`;
-const NewFieldsRow = styled.div`
- align-items: center;
- border-top: 0.8px solid ${shell.borderMedium};
- display: flex;
- gap: 5px;
- margin-top: 4px;
- padding: 6px 3px 3px;
-`;
-
-const NewFieldsColumn = styled.div`
- display: flex;
- flex-direction: column;
-`;
-
-const NewFieldsTitle = styled.span`
- color: ${shell.textSecondary};
- font-size: 8px;
-`;
-
-const NewFieldsDescription = styled.span`
- color: ${shell.textTertiary};
- font-size: 7px;
-`;
-
export const LAYOUT_CHROME = {
ActionButton,
ActionsBar,
@@ -427,8 +429,8 @@ export const LAYOUT_CHROME = {
AddSectionRow,
AddText,
DoneButton,
- EditableRow,
- EditableText,
+ EditField,
+ EditGroup,
FieldDot,
FieldIconBox,
FieldLabels,
@@ -442,10 +444,6 @@ export const LAYOUT_CHROME = {
NavPanel,
NavSectionLabel,
NavSubItem,
- NewFieldsColumn,
- NewFieldsDescription,
- NewFieldsRow,
- NewFieldsTitle,
PanelActionButton,
PanelBackButton,
PanelFields,
@@ -455,10 +453,10 @@ export const LAYOUT_CHROME = {
PanelSubLabel,
PanelTitleBold,
PanelTitleGroup,
- PanelTitleSub,
RightPanel,
SectionName,
SectionRow,
+ WidgetAvatar,
WidgetChip,
WidgetIcon,
WidgetInner,
diff --git a/packages/twenty-website/src/sections/product-stepper/LayoutVisual/data/layout-data.ts b/packages/twenty-website/src/sections/product-stepper/LayoutVisual/data/layout-data.ts
index b81df635f6..2d2377e810 100644
--- a/packages/twenty-website/src/sections/product-stepper/LayoutVisual/data/layout-data.ts
+++ b/packages/twenty-website/src/sections/product-stepper/LayoutVisual/data/layout-data.ts
@@ -38,7 +38,7 @@ export const LAYOUT_EDITOR_CONTENT: {
id: 'icp',
icon: 'target',
label: msg`ICP`,
- type: msg`True/False`,
+ type: msg`Boolean`,
section: 'Additional',
visible: false,
},
@@ -62,7 +62,7 @@ export const LAYOUT_EDITOR_CONTENT: {
id: 'creation-date',
icon: 'calendar',
label: msg`Creation date`,
- type: msg`Date and Time`,
+ type: msg`Date & Time`,
section: 'Other',
visible: true,
},
diff --git a/packages/twenty-website/src/tokens/feature-scenes/product-stepper-scene.ts b/packages/twenty-website/src/tokens/feature-scenes/product-stepper-scene.ts
index 895b315430..107eeb5710 100644
--- a/packages/twenty-website/src/tokens/feature-scenes/product-stepper-scene.ts
+++ b/packages/twenty-website/src/tokens/feature-scenes/product-stepper-scene.ts
@@ -63,6 +63,10 @@ export const PRODUCT_STEPPER_SCENE = {
fieldInk: FICTION_PALETTE.inkMuted,
eyeInk: FICTION_PALETTE.inkFaint,
eyeHiddenInk: FICTION_PALETTE.inkDisabled,
+ panelShadow: '0 4px 16px rgba(0, 0, 0, 0.32)',
+ panelShadowMedium: '0 8px 28px rgba(0, 0, 0, 0.42)',
+ panelShadowElevated:
+ '0 12px 44px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3)',
},
cardShadow: '0 0 2px rgba(0, 0, 0, 0.08), 0 2px 2px rgba(0, 0, 0, 0.04)',
nodeShadow: '0 0 2px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04)',