
Multi-axis faux absolute positioning in HTML email
Multi-axis faux absolute positioning in HTML email
Achieving complex layouts with layered and intersecting elements is no longer a dream. Here is a live example we built for a client using the power of CSS to trick the HTML into layering elements without any image heavy workarounds. Learn more →
With the incredible work jointly released by Steven Sayo and Mark Robbins on mimicking absolute positioning in email, it was only natural that their findings would open a new door for achieving more complex layouts in our email designs.
🔗 Steven’s post
🔗 Mark’s post
Here is a recent challenge our amazing designer at Mailix, posed to us for a client’s Salesforce template build:

What’s so difficult about this, I hear you ask…
Combine the blue tile & the gentleman into one image which we can use as a background and then drop a 55% (or so) width <table>
/ <div>
, which includes the text & button.
Boom! Easy.
But how do you account for dark mode?
What if the text needs to be amended?
What if the the gentleman needs to be swapped out?
What if the tile colour needs to be changed in the future?
At Mailix, we strive to natively build in a way that allows our clients to manage their modules as much as possible, when it makes sense.
Using the example above, we not only want the text to be editable, but the gentleman also. Heck, we could even allow the light blue to be swapped out using a specified group of on brand colours.
*** This module was actually built for use in Salesforce Marketing Cloud, where we’re using variables to set a lot of the settings, including colours, fonts, font sizes etc.
With these points in mind:
1. Full content management
2. Dark mode optimisation
3. Accessibility control
… I needed to investigate the possibility of somehow having three elements overlap each other, including in Outlook.
🛑 The Problem:
Here is a diagram to help make sense of the problem at hand:

The text + gentleman group sitting on top of the blue tile shouldn’t be an issue. Using the faux absolute position technique, we can easily pull the blue tile up behind the content group or push the content group down, depending on how we order the elements in build.
The hard part comes with intersecting the image and text blocks.
Negative margins or the position property will do the the trick in most places however, through my testing and the help of Can I Email, I discovered Outlook & Yahoo webmail clients don’t support negative values on margin and top/bottom position properties, so we’ll need to do some experimentation to find a solution.
And our old friend, Outlook for Windows desktop (when I mention Outlook going forward, this is the client I’m specifically referring to) is absolutely going to make life difficult. The ability to nest VML would be great right now, but we all know that isn’t an option. We’ll most likely lose the rounded button, in any case.
✅ The Solution:
🎉🎉 Vertical & horizontal faux absolute positioning, including two intersecting <v:rect>
elements, for Outlook 🎉🎉
There may also be a few classes or minor declarations that I’ve left in which I don’t mention, but these are probably from the client build for mobile. Just ignore these.
<table border="0" cellspacing="0" cellpadding="0" class="m-lq" style="width:560px" role="presentation"> <tr> <td> <div class="content-block" style="max-height:20px;"> <table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation"> <tr> <!--[if mso]> <td> <v:rect stroked="f" filled="false" style="position:absolute; top:-16px; width:255pt;"> <v:textbox inset="0,0,0,0" style="mso-fit-shape-to-text:true;"> <table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation"> <tr> <![endif]--> <td valign="top" class="text-col" style="font-family:Arial, Helvetica, sans-serif;font-size:16px;line-height:22px;color:#333;font-weight:400;text-align:left; padding:0 0 10px 20px; max-width:225px;"> <div class="text-col-inner" style="width:310px;"> <h2 style="margin:40px 0 0 0; font-family:Arial, Helvetica, sans-serif;font-size:35px;line-height:42px;color:#333;font-weight:600;">Wit et stat liti ci blant volorep.</h2> <p style="margin:10px 0 20px 0">Xerumquod quatus, erchicit autatur sin nit asimo velestiis eum re, officaborro blate volor aut abo. Ets es loers.</p> <table align="left" border="0" cellpadding="0" cellspacing="0" role="presentation"> <tr> <!--[if mso]> <td style="display:none; width:20px;"> </td> <![endif]--> <td> <a class="button" href="#." style="font-family:Arial, Helvetica, sans-serif; font-size:16px;line-height:16px; font-weight:600; background:#FF7500; text-decoration:none; padding:16px 52px; color:#fff; border-radius:10px; display:block; mso-padding-alt:0;"><!--[if mso]><i style="letter-spacing: 52px;mso-font-width:-100%;mso-text-raise:20pt"> </i><![endif]--><span style="mso-text-raise:11pt;">Lorem</span><!--[if mso]></span><i style="letter-spacing: 52px;mso-font-width:-100%"> </i><![endif]--></a> </td> </tr> </table> </div> </td> <!--[if mso]> </tr> </table> </v:textbox> </v:rect> </td> <td> <v:rect stroked="f" filled="false" style="position:absolute; top:-16px; left:-15px; width:203pt;"> <v:textbox inset="0,0,0,0" style="mso-fit-shape-to-text:true;"> <table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation"> <tr> <td> <![endif]--> <!--[if !mso]><!--> <td class="man-wrapper" valign="top"> <!--<![endif]--> <img class="man" alt="" src="https://litmus-builder.s3.amazonaws.com/ce439b8e-d4ff-4af1-ae42-52a73c9c4394" style="width:271px" height="300" /> </td> <!--[if mso]> </tr> </table> </v:textbox> </v:rect> </td> <![endif]--> </tr> </table> </div> <div class="background-block"> <!--[if !mso]><!--> <div style="background:#E6EFFF; border-radius:10px; height:280px;"></div> <!--<![endif]--> <!--[if mso]> <v:roundrect fill="true" stroked="f" arcsize="4%" style="width:420pt; height:214pt;"> <v:fill type="gradient" color="#E6EFFF" color2="#E6EFFF" colors="50% #E6EFFF,50% #E6EFFF"/> </v:roundrect> <![endif]--> </div> </td> </tr> </table>
🛠️ The Process:
The elements start out with the content blocks on top and the background tile below.

Not pretty and you’ll see the content blocks (outlined in blue) are collectively, wider than the background tile. While unintentional, the two respective columns are sitting at the width they each need to be and the steps below will fix the visual inconsistency.
To start, we need to pull that background tile up to sit behind the content blocks. On the ‘content-block’ div, we need to zero out the height with max-height:0;
.
And to pull this off in Outlook, we use a <v:rect>
with position:absolute;
. In our case, we’re going to use two <v:rect>
elements. This is to facilitate the next step where we pull the two content columns together to achieve the intersection.
<table align="center" border="0" cellspacing="0" cellpadding="0" class="m-lq m-h-pd-df" style="width:560px" role="presentation"> <tr> <td> <div class="content-block" style="max-height:0;"> <table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation"> <tr> <!--[if mso]> <td> <v:rect stroked="t" strokecolor="blue" filled="false" style="position:absolute; top:0; width:289px;"> <v:textbox inset="0,0,0,0" style="mso-fit-shape-to-text:true;"> ... </v:textbox> </v:rect> </td> <td> <v:rect stroked="t" strokecolor="blue" filled="false" style="position:absolute; top:0; width:271px;"> <v:textbox inset="0,0,0,0" style="mso-fit-shape-to-text:true;"> ... </v:textbox> </v:rect> </td> <![endif]--> </tr> </table> </div> <div class="background-block"> ... </div> </td> </tr> </table>
Result:

One final step on the y axis is to pull our two content blocks up to overlap the background tile. This is actually quite easy for most email clients including Outlook.
Going back to a point I made earlier around negative values on some properties in Outlook & Yahoo webmail clients, we need to find an alternative that can work across the board (excluding Outlook for desktop Windows).
There is an ingenious way of doing this. We simply add the required space we need the content blocks to shift upward, to the max-height:0;
declaration.
In my case it was 20px:
<div class="content-block" style="max-height:20px;">
...
</div>
For Outlook, we have two options. We could utilise the top position property which we now have access to thanks to our position:absolute;
declaration in our VML (yes the position property works in Outlook, when declared in the VML), or we could use the <v:textbox>
inset property.
Examples of both options:
-- OPTION 1-- <!--[if mso]> <v:rect stroked="t" strokecolor="blue" filled="false" style="position:absolute; top:-16px; width:289px;"> <v:textbox inset="0,0,0,0" style="mso-fit-shape-to-text:true;"> <![endif]--> -- OPTION 2 -- <!--[if mso]> <v:rect stroked="t" strokecolor="blue" filled="false" style="position:absolute; top:0; width:289px;"> <v:textbox inset="0,-16px,0,0" style="mso-fit-shape-to-text:true;"> <![endif]-->
I didn’t find any adverse effects with either, but for simplicity, I opted for the first option, utilising the top
property.
You may be wondering why I’ve set -16px in the VML, but we set max-height:20px;
for all other email clients. This is simply down to how Outlook is determining the rendering of our value. 20px was actually too much and a gap appeared underneath the image. Trial & error determined 16px to be the correct value for Outlook.
Result:

😲 Winning
🤘🏾 Time for the intersection
For all non-Outlook email clients, we can simply use the faux absolute technique again, this time with widths.
First off, we add max-width:225px;
on the cell wrapping the text column.
225px is an arbitrary number, so don’t expect this was as a result of an equation to subtract the image width from the module width + padding, etc.
225px is simply small enough to allow the image column to fill the space available (as table cells naturally do) and give us the right amount of space between the image and the edge of the module, since the image is centred.
<td valign="top" class="text-col" style="font-family:Arial, Helvetica, sans-serif;font-size:16px;line-height:22px;color:#333;font-weight:400;text-align:left; padding:0 0 10px 20px; max-width:225px;">
Next, we add the wider width of 310px to the first nested <div>
(which has a class of ‘text-col-inner’ in my example which will allow the text to flow wider than the wrapping cell, giving us the intersection.
Again, an arbitrary number which allows the text to flow and break to match the design. Again, trial & error are key to finding the best fit to match your design.
<td valign="top" class="text-col" style="font-family:Arial, Helvetica, sans-serif;font-size:16px;line-height:22px;color:#333;font-weight:400;text-align:left; padding:0 0 10px 20px; max-width:225px;">
<div class="text-col-inner" style="width:310px;">
For Outlook we need to bump up our max-width:289px;
to 340px, to really exaggerate the intersection to match the other email clients.
<!--[if mso]>
<td>
<v:rect stroked="t" strokecolor="blue" filled="false" style="position:absolute; top:-16px; width:340px;">
<v:textbox inset="0,0,0,0" style="mso-fit-shape-to-text:true;">
<table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
<tr>
<![endif]-->
<td valign="top" class="text-col" style="font-family:Arial, Helvetica, sans-serif;font-size:16px;line-height:22px;color:#333;font-weight:400;text-align:left; padding:0 0 10px 20px; max-width:225px;">
<div class="text-col-inner" style="width:310px;">
...
</div>
</td>
<!--[if mso]>
</tr>
</table>
</v:textbox>
</v:rect>
<![endif]-->
Result:

We now have the intersection working in the majority of email clients INCLUDING OUTLOOK (for Windows desktop)! WHOOOOOOOOOOO 🎉
We then just need to pull the image to the left slightly in Outlook. We already have the position:absolute;
on the <v:rect>
wrapping the image, so we add the left
property and a negative value.
<!--[if mso]> <v:rect stroked="t" strokecolor="blue" filled="false" style="position:absolute; top:-16px; left:-15px; width:271px;"> <v:textbox inset="0,0,0,0" style="mso-fit-shape-to-text:true;"> <table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation"> <![endif]-->
And there you go.

I mentioned earlier that we’ll lose the rounded button in Outlook because we can’t nest the required <v:roundrect>
inside of another VML element.
Honestly, I think the button is fine considering we pulled this layout off without using a background image and the client was happy with it.
I am however, currently experimenting with techniques to keep the rounded button in Outlook. My thinking is, by splitting the left hand content column up into two separate VML elements (giving us three in total), we can then position these as we need and the button can utilise a <v:roundrect>
element for the rounded corners.
So far, this is proving problematic in Outlook due to the need to zero out the height of all of the content blocks. Doing this essentially pulls the content blocks out of the flow and their height isn’t being considered. Yes I could add a height to the text <v:rect>
, but that then removes the dynamic nature of the module, allowing the client to enter as much or as little text as they wish.
More testing to do but for now, this is working and we’re super pleased with the result.
🦈 Outlook bites again… Twice
Unfortunately, there was a weird quirk with the image on load. I didn’t see this in Litmus, I actually only saw this when sending to my own test mailbox for Outlook.

It corrects itself if you opened the email in a new window, even just the double click of the email in the inbox list would right the image in the preview pane which you would see for a split second before the new window opened over the top.
Switching between light/dark mode on the message itself also fixed the issue.
A height being calculated on load and not updating by the time the image downloads? Maybe.
The fix was to add a height to the image using the HTML attribute. Yes, you read that correctly. I tried a CSS height on the all wrapping containers, including the image + an explicit display:block;
declaration, but nothing worked until I only added the html attribute with the image’s intended height 🤨
And then when swapping the image out, the height will be set by SFMC anyway, so that should be future proofed.
Finally, on running this through a test in Outlook 120dpi, I got this:

Simple fix for this, which took me far too long to figure out… Convert all width & height pixel values to points. I’ve never had to do this before with VML content (like, ever!), but considering how well Outlook is playing ball with this module, I shouldn’t really complain. This conversion fixed everything instantly. No further tweaks necessary.
I didn’t have the faintest idea of the conversion rate to points, but the NinjaUnits unit converter took care of that for me!
Result:

🧪 Trial & Error
I should note, this build did consist of a lot of experimentation with different ways to group and orientate the elements to make this work in Outlook. This version is the best of the lot.
For testing, I added borders to the VML to make it easier to see how everything is interacting on render. I also alternated the border colours for every second test to clearly see which results were for which test.
In tests 2 and 4, I declared the background tile above the content which resulted in the borders for the content appearing below the tile, yet the actual content appeared correctly in place on top of the tile. It’s weird. I can’t explain it, but those were the results.

I also experimented with both content columns in one <v:rect>
and then using a negative text-indent to pull the text into the image column. The result allowed the text to flow wider, but because I was stuck with pulling two table cells together, the image was then cut off due to overflow of the content not being available.
🤝🏾Email Client Support
I don’t know how, but support was great across the board.
Only issues were in Lotus Notes/IBM Notes. Considering how poor these clients are in general and the actual market share, I’m going to ignore these.
I also noticed no issues in Mail.ru or T-Online.de, which were both flagged as problematic in the earlier versions of this technique. This is absolutely down to the differences in my implementation compared to Steven’s & Mark’s. So less that I’;ve stumbled on a fix, but more that my implementation happened to skirt around the issues they encountered.
I love it when that happens 😄
So… We have multi-axis faux absolute positioning & intersection working in Outlook. That’s huge!
👩🏾🦯 Accessibility
I am only using the Litmus accessibility checker for this, so how representative of all situations this is, I can’t confirm but I got great results.
Passed all five audits and the text version of the audio read:
heading
level 2
Wit et stat liti ci blant volorep.Xerumquod quatus, erchicit autatur sin nit asimo velestiis eum re, officaborro blate volor aut abo. Ets es loers.
link
Loremgraphic
A guy holding a tablet. That’s basically it.
I have to admit, this was fun to build. It took a while to figure out but I was determined to find a solution that avoided using a background image which would most likely be problematic in dark mode and an annoyance when updating.
We fulfilled the client’s brief to hand over a set of templates that they can fully manage themselves, including right down to the colour options, fonts, font sizes and more.
Upon reflection – Steven, Mark & Remi only scratched the surface of what this new technique means for the industry. At the time of build, I thought this was an extreme further expansion on their initial examples and while it is a big jump, I have already identified other more extreme (and more fun!) ideas that this technique can help with.
👏🏾 A round of applause for you three.
This is easily one of my favourite advancements in email development. It’s a technique I now default to over background images.
I hope this is helpful for your future projects. Reach out if anything is unclear and subscribe to the Mailix blog for more of these how-to write-ups.
This is the first of many that we have planned, including some interactive email showcases of which some examples of our work you can find in our ‘Best of Interactive Email’ showcase on our website.