Login Page Builder
No-code drag-and-drop visual canvas with 3 design modes (Freeform, Grid, Builder), 14 component types, a 12-column responsive grid system, real-time preview sync, and JSON serialization.
The Login Page Builder is SCRIPE's most advanced customization tool — a fully visual, drag-and-drop canvas that allows tenant administrators to build custom login page layouts without writing any code. The builder provides 3 design modes (Freeform, Grid, and Builder), a palette of 14 pre-built component types (from logos and headings to social login buttons and footer links), a responsive 12-column CSS grid system, real-time two-way sync with the preview iframe, and full JSON serialization for persistence. The builder integrates seamlessly with the Login Customizer Studio's design token pipeline, ensuring that builder-created layouts inherit all theme colors, typography, and accessibility settings.
3 Canvas Modes
The builder offers three distinct canvas modes, each providing a different level of control over layout positioning. Users can switch between modes at any time — components are preserved during mode switches.
Freeform Mode
Absolute positioning with pixel-level control. Components can be placed anywhere on the canvas and dragged to exact coordinates. Best for creative, non-standard layouts where design freedom is paramount. Components have x/y position, width, height, and z-index properties.
Grid Mode (12-Column)
Responsive 12-column CSS grid layout. Components are placed into grid cells with configurable column span (1–12), row positioning, alignment, and gap spacing. The grid ensures consistent, responsive layouts that adapt to desktop, tablet, and mobile breakpoints. This is the recommended mode for enterprise deployments where cross-device consistency is critical.
Builder Mode
Structured block-based layout with predefined sections. Components are organized into vertical sections (header, body, footer) with automatic stacking and reordering via drag-and-drop. Best for quick layout assembly with predictable, clean results. Builder mode enforces structural constraints — components snap to section boundaries and maintain consistent spacing.
| Mode | Positioning | Grid | Best For |
|---|---|---|---|
| Freeform | Absolute (x, y) | None | Pixel-perfect creative layouts |
| Grid | Row / Column based | 12-column CSS grid | Responsive enterprise layouts |
| Builder | Section-based | Auto-spaced blocks | Quick structured assembly |
14 Component Types
The component palette provides 14 pre-built, configurable UI components that can be dragged onto the canvas. Each component has a set of editable properties (text content, styling, behavior) accessible via the Properties Panel when selected.
| Component | Type | Grid Span | Key Properties |
|---|---|---|---|
| Logo | logo | 2-4 | src, width, height, alignment |
| Heading | heading | 4-12 | text, level (h1-h6), color, weight |
| Body Text | text | 4-12 | content, fontSize, lineHeight |
| Login Form | form | 4-8 | fields, submitLabel, forgotLink |
| Social Login | social | 4-8 | providers[], layout, separator |
| Image | image | 2-12 | src, alt, objectFit, borderRadius |
| Button | button | 2-6 | label, variant, icon, href |
| Card | card | 4-12 | children[], padding, shadow |
| Divider | divider | 4-12 | style (line/dotted/gradient) |
| Spacer | spacer | 12 | height (px) |
| Badge | badge | 2-4 | text, variant, icon |
| Icon | icon | 1-2 | name, size, color |
| Footer | footer | 12 | links[], copyright, alignment |
| Terms Link | terms | 4-8 | tosUrl, privacyUrl, text |
Logo — Displays the tenant's logo image. Properties: src (URL), alt text, width, height, alignment, link URL. Supports SVG, PNG, and WebP formats.
Heading — Large display text for page titles and headlines. Properties: text content, font size, font weight, color, alignment, HTML tag (h1–h6). Supports dynamic variables ({tenantName}).
Form — The login form component containing email/password inputs and submit button. Properties: show labels, show placeholders, input style, button text, remember me checkbox, forgot password link.
Social Login — Pre-built social authentication buttons (Google, Microsoft, Apple, GitHub). Properties: providers (multi-select), layout (horizontal, vertical, icon-only), separator text.
12-Column Grid System
The Grid Mode uses a responsive 12-column CSS grid layout. Each component occupies a configurable number of columns (1–12) and rows. The grid supports gap spacing, column alignment (start, center, end, stretch), and row alignment. The grid is fully responsive — column spans can be configured independently for desktop (lg), tablet (md), and mobile (sm) breakpoints.
Grid Component Properties
Each component in Grid mode has additional grid-specific properties: colSpan (1–12 columns), rowSpan (number of rows), colStart (starting column), rowStart (starting row), alignment (start/center/end/stretch), and responsive overrides (sm/md/lg column spans). These properties are configured via the Properties Panel.
.builder-canvas-grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: var(--builder-gap, 16px);
padding: var(--builder-padding, 24px);
}
.grid-item[data-span="4"] { grid-column: span 4; }
.grid-item[data-span="6"] { grid-column: span 6; }
.grid-item[data-span="12"] { grid-column: span 12; }
@media (max-width: 768px) {
.builder-canvas-grid { grid-template-columns: repeat(4, 1fr); }
.grid-item { grid-column: span 4 !important; }
}
@media (max-width: 480px) {
.builder-canvas-grid { grid-template-columns: 1fr; }
}Responsive Breakpoints
The grid supports 3 breakpoints: Desktop (lg, ≥1024px), Tablet (md, 768–1023px), and Mobile (sm, <768px). Each component can have independent column spans per breakpoint. For example, a logo might span 4 columns on desktop but 12 columns (full width) on mobile. The preview iframe respects these breakpoints when device toggles are used.
| Breakpoint | Screen | Columns | Behavior |
|---|---|---|---|
| Desktop (lg) | ≥ 1024px | 12 | Full grid — all spans honored |
| Tablet (md) | 768-1023px | 4 | Components collapse to 4-column spans |
| Mobile (sm) | < 480px | 1 | Single column — all components full-width |
Gap Configuration
The grid gap (spacing between cells) is configurable globally: columnGap and rowGap properties, each accepting pixel values (default: 16px). This ensures consistent spacing across all grid items without manual padding on individual components.
Drag-and-Drop Architecture
The builder uses @dnd-kit/core (not react-beautiful-dnd) for drag-and-drop interactions. Components are dragged from the palette sidebar and dropped onto the canvas. The DragOverlay renders a ghost preview of the component during drag. Drop zones are highlighted with blue outlines when a draggable component hovers over them.
Palette → Canvas Flow
1. User drags a component type from the palette sidebar. 2. @dnd-kit creates a DragOverlay with a preview of the component. 3. The canvas renders drop zone indicators (grid cells in Grid mode, free areas in Freeform mode). 4. On drop, a new component instance is created with default properties and added to the builder state. 5. The component is rendered on the canvas at the drop position.
Canvas Reordering
Existing components on the canvas can be reordered by dragging. In Grid mode, components snap to grid cells. In Builder mode, components reorder within their section (header/body/footer). In Freeform mode, components move to the exact drop coordinates.
Component Selection
Clicking a component on the canvas selects it, displaying a blue selection border and resize handles. The Properties Panel on the right sidebar loads the selected component's editable properties. Pressing Delete/Backspace removes the selected component. Escape deselects.
Properties Panel
The Properties Panel is a contextual sidebar that appears when a component is selected on the canvas. It displays all editable properties for the selected component type, organized into sections: Content (text, URLs), Layout (width, height, alignment), Style (colors, borders, shadows), and Grid (column span, row span, responsive breakpoints). Changes in the Properties Panel update the canvas in real-time.
Content Properties
Text inputs for content: headline text, paragraph text, button labels, URLs, alt text. Supports variable interpolation with {variableName} syntax for dynamic tenant data.
Style Properties
Visual styling controls: color pickers, border radius sliders, shadow toggles, opacity controls, font size selectors. Style properties that overlap with theme tokens (e.g., primaryColor) can be set to 'inherit from theme' to maintain consistency.
Grid Properties
Grid-specific layout controls (Grid mode only): column span slider (1–12), row span, column start, row start, alignment select, and responsive breakpoint overrides. A visual grid preview shows the component's position within the 12-column grid.
| Property Type | UI Control | Example |
|---|---|---|
| text | Input field | Heading text, button label |
| number | Slider / number input | Width, height, padding |
| color | Color picker | Text color, background |
| select | Dropdown | Text alignment, font weight |
| boolean | Toggle switch | Show shadow, center align |
| image | File picker / URL | Logo src, background image |
| array | List editor | Social providers, footer links |
Canvas State Management
The builder maintains a flat array of BuilderComponent objects in the StudioDraft. Each component has: id (unique UUID), type (one of 14 types), properties (key-value map), position (x, y for Freeform), gridPosition (col, row, colSpan, rowSpan for Grid), and section (header/body/footer for Builder mode). The entire array is serialized as JSON in the builderComponentsJson field of DraftBrandingJson.
BuilderComponent Schema
Each BuilderComponent is a serializable object: { id: string, type: ComponentType, props: Record<string, unknown>, position: { x: number, y: number }, gridPosition: { colSpan: number, rowSpan: number, colStart: number, rowStart: number }, section: 'header' | 'body' | 'footer', order: number, responsive: { sm: GridOverride, md: GridOverride } }.
export interface CanvasComponent {
id: string; // Unique identifier (UUID v4)
type: CanvasComponentType; // Component type selector
gridColumn: string; // CSS Grid Column (e.g. "1 / 7")
gridRow: string; // CSS Grid Row (e.g. "1 / 2")
alignment: GridAlignment; // Horizontal alignment (start/center/end)
verticalAlignment: GridAlignment;// Vertical alignment (start/center/end)
x: number; // Absolute mode X coordinate (px)
y: number; // Absolute mode Y coordinate (px)
width: number; // Absolute mode width (px)
height: number; // Absolute mode height (px)
locked: boolean; // Lock element from modifications
props: Record<string, unknown>; // Element specific settings (e.g. src, text)
zIndex: number; // Absolute mode stacking z-index
visible: boolean; // Visibility switch
}Undo/Redo Support
The builder maintains a history stack for undo/redo operations. Each action (add, move, resize, delete, property change) pushes a snapshot to the stack. Ctrl+Z undoes the last action, Ctrl+Shift+Z redoes. The history stack has a configurable depth limit (default: 50 actions).
JSON Serialization & Persistence
The builder state is serialized as a JSON array and stored in the builderComponentsJson field of the tenant's DraftBrandingJson. The canvasMode (freeform/grid/builder) is stored as a separate field. On publish, the builder state is promoted to LiveBrandingJson alongside all other branding tokens.
Serialized Schema
The stored JSON follows this structure: { canvasMode: 'freeform' | 'grid' | 'builder', gridConfig: { columns: 12, columnGap: 16, rowGap: 16 }, components: [ { id, type, props, position, gridPosition, section, order, responsive } ] }. The schema is versioned for forward compatibility.
{
"canvasMode": "grid",
"gridConfig": { "columns": 12, "columnGap": 16, "rowGap": 16 },
"components": [
{
"id": "comp-1",
"type": "logo",
"props": { "width": 120, "alignment": "center" },
"gridPosition": { "colSpan": 4, "colStart": 4, "rowStart": 0 }
},
{
"id": "comp-2",
"type": "heading",
"props": { "text": "Welcome", "level": "h1" },
"gridPosition": { "colSpan": 8, "colStart": 2, "rowStart": 1 }
}
]
}Storage Optimization
Default property values are NOT stored — only properties that differ from the component type's defaults are serialized. This keeps the JSON payload compact (typically 2–5KB for a complex layout with 10+ components).
Real-Time Preview Sync
Every canvas change is immediately reflected in the sandboxed preview iframe. The builder emits postMessage events containing the current component array and canvas mode. The preview page receives these events, reconstructs the layout, and renders the components with live CSS variable injection from the active theme.
Two-Way Sync
The sync is bidirectional: canvas changes propagate to preview (via postMessage), and device toggle changes in the preview header propagate back to the builder (updating responsive breakpoint indicators). This ensures the builder canvas accurately reflects how the layout will appear at each breakpoint.
Security Constraints
The Login Page Builder enforces strict security constraints on user-generated content. All text content is HTML-sanitized before rendering (no script injection). Image URLs are validated to prevent SSRF. The builder canvas runs in a sandboxed iframe with restrictive CSP headers. Custom CSS injection is not supported — styling is controlled exclusively through the design token pipeline.
Input Sanitization
All text properties (headings, paragraphs, button labels) are sanitized using DOMPurify before rendering in the preview. HTML tags are stripped — only plain text is stored. URLs are validated against a whitelist of allowed protocols (https, http) to prevent javascript: and data: injection.
Iframe Sandboxing
The preview iframe uses the sandbox attribute with restricted permissions: allow-scripts (for CSS variable injection), allow-same-origin (for postMessage). Forms are non-functional — the preview renders visual-only representations of auth components.
| Threat | Mitigation |
|---|---|
| XSS via text props | DOMPurify sanitization — all HTML stripped, plain text only |
| Script injection in URLs | Protocol whitelist: https://, http://, / only. Block javascript:, data: |
| Oversized canvas | Max 50 components per canvas. Max 10KB serialized JSON |
| Cross-tenant access | Canvas state in TenantSettings.DraftBrandingJson — tenant-scoped |
| Concurrent edits | Optimistic concurrency (SettingsVersion). 409 Conflict on stale write |
Dashboard Theming (Related Feature)
The Dashboard Theming panel in the Customizer Studio allows tenants to configure their admin dashboard's visual defaults. Settings include layout template (8 options), color theme (12 options), theme mode (light/dark/system), default language, and sidebar default state. Dashboard theming uses the same draft/publish workflow as login branding.
8 Layout Templates
Default, Navigation, Classic, Compact, Elegant, Floating, Modern, and Minimal. Each template defines the dashboard shell structure: sidebar position, header style, content width, and navigation pattern.
12 Color Themes
Default, Zinc, Slate, Stone, Neutral, Red, Rose, Orange, Green, Blue, Violet, and Yellow. Each color theme defines the dashboard's accent color palette applied to the sidebar, headers, buttons, and active states.
Storage Format
Dashboard settings are stored in the dashboardThemeJson field of DraftBrandingJson as a JSON object: { layoutTemplate, colorTheme, mode, language, sidebarDefaultState }. On publish, the settings are promoted to the tenant's live configuration.
| Setting | Options | Storage |
|---|---|---|
| Layout Template | 8 templates | dashboardThemeJson.layoutTemplate |
| Color Theme | 12 palettes | dashboardThemeJson.colorTheme |
| Theme Mode | Light / Dark / System | dashboardThemeJson.theme |
| Language | EN / AR (RTL auto) | dashboardThemeJson.language |
| Sidebar | Expanded / Collapsed | dashboardThemeJson.sidebarCollapsed |
Bundle Marketplace (Related Feature)
The Bundle Marketplace allows tenants to save their complete studio configuration (login theme + builder layout + dashboard settings + per-page overrides) as a named bundle, and browse/apply bundles created by other tenants or the system. Bundles are displayed in a dedicated tab within the Theme Gallery.
Bundle Components
BundleGalleryTab (5.6KB) — Gallery tab with type filters (Login, Dashboard, Complete), search, grid, pagination. BundleCard (12.8KB) — Card with accent preview, pricing tier, metadata. BundleDetailModal (8.5KB) — Full detail with apply button. SaveBundleDialog (7.6KB) — Dialog for saving current studio state as a new bundle.
Bundle Types
Login Bundle — Contains only login branding (theme tokens + builder layout). Dashboard Bundle — Contains only dashboard settings. Complete Bundle — Contains everything: login branding, builder layout, per-page overrides, dashboard settings, and accessibility configuration. Complete bundles provide one-click full-workspace setup.
| Bundle Type | Includes | Use Case |
|---|---|---|
| Login | Login theme + builder canvas + per-page overrides | Auth-only branding |
| Dashboard | Dashboard layout + color theme + mode | Admin panel theming |
| Complete | Everything: login + builder + dashboard + accessibility | Full platform rebrand |
Module Architecture
The Login Page Builder follows SCRIPE's standard modular clean architecture. The builder/ directory contains: BuilderCanvas.tsx (main canvas), ComponentPalette.tsx (sidebar palette), PropertiesPanel.tsx (property editor), GridOverlay.tsx (grid visualization), and BuilderToolbar.tsx (mode switcher, undo/redo, zoom controls). State management uses the useBuilderState hook integrated into useStudioViewModel.
src/modules/customization/branding/src/presentation/
├── components/
│ ├── builder/
│ │ ├── BuilderCanvas.tsx # Main canvas — absolute/grid positioning
│ │ ├── BuilderPanel.tsx # Side palette controller
│ │ ├── GridOverlay.tsx # CSS Grid layout helper lines
│ │ └── DraggableCanvasItem.tsx # Draggable item wrapper
│ └── views/
│ └── CustomizerStudioView.tsx # Customizer workspace frame
├── hooks/
│ └── useBuilderDnd.ts # @dnd-kit/core sensor integrations
└── viewmodels/
├── useBuilderStore.ts # Zustand state manager & snap calculation
└── useStudioViewModel.ts # Studio logic bridge & debounced HTTP saves