Saaaaluuuut little email code geek! I've put together an article just for you: in this article, I'm going to tell you all about converting images into pure HTML and CSS code in an email. How did I come up with this idea? Let me explain: I often think about article topics as I drift off to sleep. Here's one that's been stuck in my head: how can I optimize the rendering of images in a Dark Mode email? Then I always have the same dream: I'm swimming in the Seine and suddenly I swallow a rat. Then I suffocate and sink. At the bottom, there are oysters, and they grab my ankles. So I threw up the rat on the oysters, the rat attacked the oysters, I surfaced, took a barge on the head and woke up.
The next day, I received an e-mail from IRO, which I opened on my dark mode cell phone. It was a perfect example. In this article, I'll be using the IRO brand.
Table of contents
Explanation of how to convert an image into HTML and CSS code
The principle is as follows: in one or more tables (<table>
), each cell (<td>
) represents a pixel of the selected image. CSS patches are applied to these cells to guarantee their width and height (1px
), as well as an HTML attribute bgcolor
attribute to set a background color for the cell. If sister cells have a similar background color, the colspan
with the corresponding value is applied to limit the number of cells in each line, and therefore the final HTML code.
In principle, we're very close to pixel art, a form of digital art in which images are created from small square elements called "pixels". The difference is that pixel art is all about "creating" images without worrying about sharpness, whereas our aim in this article is to "reproduce" an image as faithfully as possible, with one cell for each pixel of the original image.
The benefits of converting images into HTML and CSS code
Optimized rendering on Dark Mode
As a reminder, only JPEG, GIF and PNG are correctly supported on all opening environments. SVG, WEBP, AVIF, BASE 64, BMP, HDR, HEIFPPM, TIFF are good for the scrap heap. And supported formats don't automatically convert to Dark Mode. Why not? Simply because they're not "vector", of course!
The problem with an image is that in Dark Mode in an emailit will remain the same. In other words, an image of a black logo on a transparent background will remain a black logo on a transparent background in Dark Mode. But if the cell containing your image has a white background in Light Mode, how will it behave in Dark Mode? I'll tell you: the background will become dark, and your logo will no longer be visible/readable. Unless you add a drop shadow or border in the same color as your cell background...
So you might be tempted to design your images entirely in HTML and CSS, pixel art-style, so that the hexadecimal color codes are automatically converted when Dark Mode is activated. Legitimate, isn't it?
In short, it's for these and other reasons that it may be worthwhile to look at the conversion of images into HTML and CSS code. Because then, all hexadecimal codes in your cells will be converted. Your logo will then appear white (in principle) on a dark background.
Immediate visual display
You know this message well, don't you? A "nightmare" for many email marketers. Imagine a world where it would no longer be necessary to press the "Show remote content in this message to display the visuals in your email... Wouldn't that be nice, hm? And I'm not talking about putting images in attachments, no no. That's dirty. I'm talking about images coded in HTML and CSS, which would then be displayed instantly in your email.
This is also the case in the code developed here for IRO: no need for hosted images! Just HTML code and zou! The logo is displayed. "It's lively. It has thighs, it attacks the palate dryly, it's not lacking in return. You can serve it!"
Optimized accessibility thanks to limited external resources
When it comes to accessibility, frankly, there's one advantage. It's small, but it's there: the ability to display an image without the need to download images. And therefore, also reducing the number of resources to be downloaded. And also because, as mentioned above, rendering in Dark Mode is optimized.
The disadvantages of converting images into HTML and CSS code
Weight of HTML file generated on conversion
Moooon-God-Noël-my-mother! My code hurts! Given that each pixel corresponds to a table cell, the weight of a simple 31px wide by 35px high visual, simply in black and white, is already quite substantial. The proof, ladies and gentlemen of the jury, is that the single letter "R" coded with this method reaches the staggering weight of 32.6kb! That's quite a lot.
Well, if the weight of the HTML code in our beloved emailings wasn't a constraint, I'd say: let's go crazy! To hell with stinginess! But that's to misunderstand the mischievous world of email marketing: Gmail, Orange... Many "cut" an email as soon as a weight limit is reached. Suffice it to say that with three simple letters weighing in at 8.19kb, 32.6kb and 43.1kb respectively, and a total weight (hang on to your hats) of 83.89kb, there's very little room for maneuver. All the more so as the generated code is not indented, and optimized to the maximum to reduce its weight! And that's saying a lot!
Design limitations
As you'll have understood (or perhaps not, I can't blame you), to obtain a relatively light code, you'll need to apply this conversion to "basic" images, simple, with few colors, and not too large. Hence my next piece of advice: this can only be applied to logos or icons. And sparingly. It's not for nothing that the example chosen in this article is the IRO logo, uncluttered and minimalist as possible.
You should also choose images with sufficient contrast, so that the conversion script can distinguish each pixel as it runs.
Need help?
Reading content isn't everything. The best way is to talk to us.
Hyperlink impossible to apply
This is a nice little trap: to design these images in HTML and CSS, I use arrays (<table>
). However, these arrays are elements of type table
. However, a inline
as the <a>
cannot include an element of type table
. Not only this is not correct HTML-wisebut it can also generate rendering or usage problems. Forget about being able to insert a link on your logo: from now on, you can't make it clickable. You can try, but you're liable to break your teeth... "Put your mouth on the curb... Put your mouth on the kerb...".
HTML code accessibility after conversion
Yes, I include accessibility in both the advantages AND the disadvantages. Why? Because even if the image is automatically downloaded and readable by everyone, it doesn't allow for alternative text. So.., if the rendering is distorted, it is impossible to understand the meaning of the design. Do you understand?
What's more, screen readers will, by default, consider this creation as a presentation HTML table. As recommended by Mark Robbins on the use of SVG in email, you'll probably need to specify a role
attribute with the value presentation to all img
element on your HTML-coded table, and include a
to enter an alternative text.
<table cellpadding="0" cellspacing="0" border="0" role="img">
<tr>
<td>
<title>Alt text</title>
[your HTML code]
</td>
</tr>
</table>
And I'd just as soon tell you that I'm not responsible for the insertion of a
in a <table>
!
Visual update
While changing a logo or an image in PNG, JPG or GIF format is relatively quick (simply modify the visual in question on your design tool or prototyping platform, export it, host it and then change the path of the img tag in the HTML code), changing a logo/visual in HTML and CSS format requires a little more work:
- Visual update
- Exporting visuals
- Script modification
- Running the script
- Optimization of the generated test if necessary
- Testing the generated code
- Application of code in the final HTML file...
Keep this in mind - it's not an insignificant point!
Methodology to adopt
Best practices
- Don't try to perform this type of transformation on complex, very large visuals, or with lots of colors.
- Give preference to extremely simple visualstype logo or icon.
- In the latter case, prepare a visual specific to this design on the prototyping platform of your choice, to simplify the logo design as much as possible.
- Don't try to perform this conversion "manually".Don't forget that a simple image measuring 31px wide by 35px high comprises - I'll put 3 and take 8: 31*35=1085 pixels. So that's as many cells to create. Good luck!
Tools for converting images into HTML and CSS code
I searched high and low for platforms offering conversion of images into HTML tables with inline CSS formatting, but came up empty! I was convinced I'd used this type of service 4 or 5 years ago. But so be it.
I then created my own converter. Well, myself, that is: with the help of artificial intelligence. So I asked ChatGPT what language would allow this feat: in his opinion, Python would do the trick. I then asked him to create a Python script to achieve this conversion, and to explain to me how to execute the script (because, in case I need to point it out, I have no knowledge of Python). I must admit... I feel like a dwarf among giants... Here's the optimized script code:
from PIL import Image
# Write below the name of the HTML file to be produced instead of image_table.html
def image_to_html_table(image_path, output_html='image_table.html', cell_size=1):
# Open the image
image = Image.open(image_path)
image = image.convert('RGB')
# Get the dimensions of the image
width, height = image.size
# Convert the image into a list of pixels
pixels = list(image.getdata())
pixels = [pixels[i * width:(i + 1) * width] for i in range(height)]
# Start the HTML table with 'role="presentation"'
html = '<table cellpadding="0" cellspacing="0" border="0" role="presentation">'
# Process each row of pixels
for row in pixels:
html += '<tr>'
current_color = None
colspan = 1
for pixel in row:
r, g, b = pixel
color = f'#{r:02x}{g:02x}{b:02x}'
if color == current_color:
colspan += 1
else:
if current_color:
style = f'width:{cell_size * colspan}px; height:{cell_size}px; font-size:1px; line-height:1px;'
if current_color != '#ffffff':
html += f'<td bgcolor="{current_color}" style="{style}" colspan="{colspan}"> </td>'
else:
html += f'<td style="{style}" colspan="{colspan}"> </td>'
current_color = color
colspan = 1
# Add the last sequence of pixels in the row
if current_color is not None:
style = f'width:{cell_size * colspan}px; height:{cell_size}px; font-size:1px; line-height:1px;'
if current_color != '#ffffff':
html += f'<td bgcolor="{current_color}" style="{style}" colspan="{colspan}"> </td>'
else:
html += f'<td style="{style}" colspan="{colspan}"> </td>'
html += '</tr>'
html += '</table>'
# Write the HTML table to a file
with open(output_html, 'w') as file:
file.write(html)
# Example of function usage
# Write below the name or path of your .jpg file in place of image.jpg
image_to_html_table('image.jpg')
And, as a gift, here are the steps to follow to execute this script, in case you'd like to test it or implement it on your own. You can keep the change, I don't mind... :
- Copy this code and paste it into a new file on Notepad++..
- Make the necessary changes in the script: change the name of the HTML file to be generated, change the name and/or path of the image concerned...
- Save the file in .py format (for example, script_conversion.py) in a folder named "generateur" on your desktop.
- From your computer :
- Under Windows, click on the search button at the bottom left of the screen; then type
cmd
and press "Enter". This opens a black window in which you can type command instructions. - On Mac, open the "Terminal. You can find it using "Spotlight.
- Under Windows, click on the search button at the bottom left of the screen; then type
- In the black window, use the
cd
followed by the path to the folder where the script is located. For example, if the script is on the Desktop, typecd Office
(for Windows) orcd Desktop
(on Mac) and press Enter. - Still in the black window, type
pip install Pillow
for install the library Pillow. - Then go to the folder containing your scriptby typing, for example
cd Office
and thengenerator cd
. - Finally, run the script by typing
python script_conversion.py
and press "Enter". - Your image is then normally converted into an HTML file with HTML tables and cells in your "generator" folder.
Test the generated code
After an initiative of this kind, I'll tell you right now: it's better to test! I had to create my own generator, add the CSS properties font-size
and line-height
with the value 1px
adapt it to optimize the output code... my final rendering results according to the opening environments are not entirely convincing: strange width at the beginning of the letter " R "slight offset on the letter " O "I'm far from a perfect rendering! And yet the logo selected was as simple as could be...
And I definitely can't be satisfied with such a rendering. And neither can you. No. no no! THE WORLD IS GOING CRAZY! NO ONE BUT ME CARES ABOUT RESPECTING THE RULES ANYMORE 😀
Conclusion: is it a good idea to convert an image into HTML and CSS tables and cells?
I think the time has come to tell you what I've learned, to draw a conclusion, don't you? After many (cold) coffees, multiple tests and script optimization attempts, my conclusions are as follows:
- Converting an image into HTML and CSS can be interesting if you manage to reduce code weight as much as possible generated, and the rendering is tested and approved. However, this in no way solves the accessibility and click/link problems that are impossible to apply.
- This type of manipulation can only be performed on extremely simple visuals graphically speaking: logos, icons...
- I'm leaning towards abandoning this solution in view of the weight generated, which I find difficult to reduce drastically. I'm thinking more along the lines of adaptation of logo to text (when the logo is mainly text-based) with the closest possible websafe typography There are constraints in emailing, so it's not hard to imagine having recommendations right from the start of the email design (logo, icon) with this type of suggestion, right?
- If you prefer to stick to the traditional method, designing a logo in PNG format with a transparent background and a minimum of work for correct rendering in Dark Mode (border or drop shadow in the same color as the background color of the cell) will do just as well.
- In an ideal world, I'd look for a solution that combines an SVG image format with a fallback solution in case the SVG format isn't supported. In fact, I invite you to read Mark Robbins' article about the SVG format in emails.
Nota bene: I'm clearly no expert in Python scripting. I don't even know anything about it, to be honest. So I'm leaning towards optimizing the script and the generated code. So I'm all ears for your advice, opinions and recommendations!
2 réponses
Wow Thomas, what energy on this rarely treated subject 🙂 and well treated at that!!!
Thanks Bruno! I put a lot of energy into it, as I'm always intrigued by this type of testing, R&D 🙂 Unfortunately, the conclusions I draw from these tests tell me that it's not necessarily a solution to adopt 😀 In any case, thanks for your comment!