I am using Postal to render MVC Razor views and send them via email. I have a custom CSS that I have defined specifically for the email views. Currently I am including them as follows:
@Styles.Render("~/Content/EmailStyles.css")
However, this only includes the relative link to the stylesheet, which will not work in an email:
<link href="/Content/EmailStyles.css" rel="stylesheet"/>
I want to include the stylesheet inline so that it functions properly in the email. What is the best way to render the contents of a file-based resource within an MVC view?
All you have to do is to place a style sheet in the Pages folder alongside the page that it is intended to affect. You just need to follow a specific naming convention, which is the Razor page file name with . css on the end. The bundled file itself is placed in the wwwroot folder when the application is published.
razor. css file is a scoped CSS file. For an Example component in an Example. razor file, create a file alongside the component named Example.
I had the same question myself, and came across Premailer.Net. It looks like the library you need. Here's what you'd have to do:
Create an extension method to help you embed your CSS into your page; there's an answer on a question on how to embed HTML in a Razor view that should help you. I've modified it for embedding CSS:
public static class HtmlHelpers { public static MvcHtmlString EmbedCss(this HtmlHelper htmlHelper, string path) { // take a path that starts with "~" and map it to the filesystem. var cssFilePath = HttpContext.Current.Server.MapPath(path); // load the contents of that file try { var cssText = System.IO.File.ReadAllText(cssFilePath); var styleElement = new TagBuilder("style"); styleElement.InnerHtml = cssText; return MvcHtmlString.Create(styleElement.ToString()); } catch (Exception ex) { // return nothing if we can't read the file for any reason return null; } } }
Then in your Razor template, just go:
@Html.EmbedCss("~/Content/EmailStyles.css")
to embed your CSS text.
Install the Premailer.Net package in your project; you can get it through NuGet.
Render your Razor view into a string (I guess that's what Postal is for in your process? I believe RazorEngine can also do that).
Run the string through Premailer.Net:
PreMailer pm = new PreMailer(); string premailedOutput = pm.MoveCssInline(htmlSource, false);
Send as an e-mail!
I've been using this technique in production for a while now, and it seems to be working quite well.
Edit: Remember that styles on pseudo-elements can't be inlined because they don't exist in the markup. I've also noticed the odd little bug in Premailer.Net -- I think their specificity and cascade rules aren't perfectly conformant. Still, it's pretty good and it's one more piece of code I didn't have to write!
Upvoted Paul d'Aoust's answer, but I found this version of his helper method to work a little better for me (wouldn't try to encode things like quotes in the CSS):
public static class CssHelper { public static IHtmlString EmbedCss(this HtmlHelper htmlHelper, string path) { // take a path that starts with "~" and map it to the filesystem. var cssFilePath = HttpContext.Current.Server.MapPath(path); // load the contents of that file try { var cssText = File.ReadAllText(cssFilePath); return htmlHelper.Raw("<style>\n" + cssText + "\n</style>"); } catch { // return nothing if we can't read the file for any reason return null; } } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With