Heuristics-based translation of canvas-like drag and drop into optimized html & css

Mor Philosoph
6 min readDec 16, 2020

How EditorX produces code without loss of accuracy

INTRODUCTION

Editor X was designed from day one to both provide a pixel perfect drag and drop editing experience, and to output modern CSS based layouts that are fluid, responsive and make sense. Today it’s the only website creation platform combining both of these traits.

Workspace & Stage

Considering a whole new responsive editing experience, we set ourselves the following goals

  • Editing on a live CSS driven preview (with the possible exception of dynamic behaviours like interactions and animations).
  • Allowing for any number of viewport media queries to be managed with maximal freedom.
  • Letting the stage be freely resizable so that different viewports can be experienced during editing.
  • Provide a pixel-perfect drag and drop experience that has become synonymous with Wix.
  • Finally, and really the linchpin of the whole project, designers need to modify the document in a safe way. We cannot have the stage wildly mutate when dragging content around (for example some containers are exactly as tall as their content and we do not want them to collapse when you start dragging content outside).

The key to stage interactions

The Editor X stage is composed of the live document being edited with an interaction/indication layer, that abstracts the selection and manipulation of the elements from the actual DOM. Document.elementFromPoint calls provide a low-cost mechanism to interact with elements and provide highlights and measurements.

Dragging without breaking the document

Dragging elements on a static, absolute-sized 2D canvas has no side effects; elements’ position can be updated based on the drag movement without affecting other elements which share the same containing ancestor. That said, on the Editor X canvas we regularly interact with content-based or relatively sized containers, so we need to protect the environment during drag.

This is done by temporarily locking the layout by setting all the dragged element’s ancestors to have a fixed size (px) for the duration of the drag.

.container {height: auto;}
Locking the sizes of elements while dragging causes content based containers to collapse only after dragging has ended.

All containers are grid containers

The default Editor X container (page sections included) is a single grid cell, laying the foundation for refining the 2D layout by splitting the area into more columns and rows. Elements thrown into grids are positioned exactly as dropped, but are actually set relatively to the cells they occupy.

The role of element docking

Elements are ‘docked’ to the center or the edge of their containers. Docking is a CSS combination of margin and justify-self. Margins set the distance to the docking point and justify sets the reference of direction from which the element is being positioned. This affects the fluid behaviour of the element while the screen is resized i.e. if & how it will “move” while resizing the canvas. The default docking is automatically suggested according to simple geometric rules, but we constantly measure the users’ acceptance/override of default docking to improve this behavior.

Managing and editing grid definitions

CSS Grid layouts are extremely powerful tools. Editor X gives designers complete control over grids, but guides their hand to choices that make for better responsive behaviours.

Sizing and grid-area

Rows and Columns in Editor X grids default to combine FR units, minimal pixel sizes and max-content. This means a cell grows to accommodate its content. For example, fluid text elements will grow vertically when the viewport shrinks, and the grid row will grow with it.

Dropping an element on more than one cell will give it a grid-area property of those underlying cells (the whole “grow by contents” behavior happens on grid-areas too).

.container {
display: grid;
grid-template-columns: 1fr
minmax(200px, max-content)
minmax(200px, max-content)
1fr
}
/*Before dragging*/
.stack {grid-column: 1/2}
/*After dragging*/
.stack {grid-column: 2/3}
Moving an item to occupy a 2 column grid area. The middle columns sizes are content based, which causes them to change after repositioning the item

.

The tablecloth approach

When a designer redefines grids in an existing container (adding or removing columns and rows), elements will drop exactly where they were, as if someone “pulled the grid from underneath them” very very fast. In effect elements are being re-droped and re-docked to the new grid, preserving the layout in a new grid.

Column and row resize

There are two possible ways to interpret what it means when a designer resizes a column: (1) changing the split between two neighbouring columns OR (2) rebalancing the entire grid. This has very different effects on layout. We implemented both, as shown in the clip and snippets below:

/*Original grid state*/
.container {
Display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
/*rebalancing sizes of remaining columns*/
Display: grid;
grid-template-columns: 4fr 1fr 1fr;
}
/*changing the split between neighbouring column*/
Display: grid;
grid-template-columns: 1.6fr 0.4fr 1fr;
}
Two methods of column resizing in a grid

Smart grid layout suggestions

An algorithm for adjusting content on grids

A higher level algorithm is evolving on Editor X, aimed at providing an easier way to switch an existing grid layout — at a smaller viewport breakpoint or at the same one. An analysis of the content in the grid is used to suggest better suited layouts (as seen in the clip below). We track users’ choices over time and adjust our cluster analysis to improve our recommendations.

All containers are grids… but some containers are not

While CSS grid containers provide maximal freedom on a 2D canvas, Editor X provides an alternative layouting solution, based on flexbox technology.

.container {
display: flex;
}

The role of flexbox on Editor X is to provide a simpler, self-organizing layouting solution with fewer degrees of freedom. Flex layouts are uni-dimensional so there is less for the designer to worry about. Specifically, work is needed in different media-queries. There are two distinct uses in our stage, both guiding the user towards building a flexbox layout, and ensuring it behaves well at different viewport sizes. Unlike grid based layouts, here our heuristics are guiding the user to drop elements in hot-zones. The editor highlights the nearest gap between neighboring containers/elements and guides their hand to dropping elements in the gap, adjusting the flex-order as he does.

Stacking containers

Stacking containers implement display: flex and affect any element placed inside them as a flexbox parent container. In fact, the stage employs some automatic behaviors to suggest creating such a stack when an element is dragged below or above another free floating element.
Like with automatic docking, we measure our users’ reactions to automatic stacking to optimize our recommendations.

Free drag & drop means we support overlapping stacked elements by translating the global position of the elements to their equivalent position in their new flexbox parent. At the same time, we set vertical margins to reflect the distance between the elements, and a flex-order to reflect their order.

Elements inside a stack, positioned with vertical margins from one another.
.stack {
display:flex;
flex-direction: row;
}
stack-item:nth-child(1) {
order: 1;
margin-top: 0;
}
.stack-item:nth-child(2) {
order: 2;
margin-top: 50px;
}
stack-item:nth-child(3) {
order: 3;
margin-top: 50px;
}

Layouting containers

Layouting containers only host other containers (‘items’), with no overlaps, providing for several fluid layouting options that behave well across viewport sizes.

.stack {display:flex;}
/*Mosaic*/
.stack {flex-direction: row;}
.stack-item {flex: 1 0 30%;}
/*Rows*/
.stack {flex-direction: column;}
.stack-item {flex: 1 0 100%;}
/*Columns*/
.stack {flex-direction: row;}
.stack-item {flex: 1 0 25%;} /*[number of items/100%]*/
Changing layouts by using different flexbox properties
Rearranging items by dragging them, assigns a new flex-order for them

What is all comes down to

Layouting responsive websites requires planning and careful execution. All available tools and methods will represent some trade-off between freedom (like writing HTML/CSS by hand) and the safety of a design tool. EditorX deploys all the tricks described above (and more) to break this trade off and deliver an experience that is both extremely free and very safe.

--

--

Mor Philosoph

Longtime product enthusiast, disrupting website creation in Wix.com, since 2014. Currently as head of EditorX.com, the advanced creation platform.