Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 1074329
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 16, 20262026-05-16T21:06:33+00:00 2026-05-16T21:06:33+00:00

I need to inline css from a stylesheet in c#. Like how this works.

  • 0

I need to inline css from a stylesheet in c#.

Like how this works.

http://www.mailchimp.com/labs/inlinecss.php

The css is simple, just classes, no fancy selectors.

I was contemplating using a regex (?<rule>(?<selector>[^{}]+){(?<style>[^{}]+)})+ to strip the rules from the css, and then attempting to do simple string replaces where the classes are called, but some of the html elements already have a style tag, so I’d have to account for that as well.

Is there a simpler approach? Or something already written in c#?

UPDATE – Sep 16, 2010

I’ve been able to come up with a simple CSS inliner provided your html is also valid xml. It uses a regex to get all the styles in your <style /> element. Then converts the css selectors to xpath expressions, and adds the style inline to the matching elements, before any pre-existing inline style.

Note, that the CssToXpath is not fully implemented, there are some things it just can’t do… yet.

CssInliner.cs

using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Xml.XPath;

namespace CssInliner
{
    public class CssInliner
    {
        private static Regex _matchStyles = new Regex("\\s*(?<rule>(?<selector>[^{}]+){(?<style>[^{}]+)})",
                                                RegexOptions.IgnoreCase
                                                | RegexOptions.CultureInvariant
                                                | RegexOptions.IgnorePatternWhitespace
                                                | RegexOptions.Compiled
                                            );

        public List<Match> Styles { get; private set; }
        public string InlinedXhtml { get; private set; }

        private XElement XhtmlDocument { get; set; }

        public CssInliner(string xhtml)
        {
            XhtmlDocument = ParseXhtml(xhtml);
            Styles = GetStyleMatches();

            foreach (var style in Styles)
            {
                if (!style.Success)
                    return;

                var cssSelector = style.Groups["selector"].Value.Trim();
                var xpathSelector = CssToXpath.Transform(cssSelector);
                var cssStyle = style.Groups["style"].Value.Trim();

                foreach (var element in XhtmlDocument.XPathSelectElements(xpathSelector))
                {
                    var inlineStyle = element.Attribute("style");

                    var newInlineStyle = cssStyle + ";";
                    if (inlineStyle != null && !string.IsNullOrEmpty(inlineStyle.Value))
                    {
                        newInlineStyle += inlineStyle.Value;
                    }

                    element.SetAttributeValue("style", newInlineStyle.Trim().NormalizeCharacter(';').NormalizeSpace());
                }
            }

            XhtmlDocument.Descendants("style").Remove();
            InlinedXhtml = XhtmlDocument.ToString();
        }

        private List<Match> GetStyleMatches()
        {
            var styles = new List<Match>();

            var styleElements = XhtmlDocument.Descendants("style");
            foreach (var styleElement in styleElements)
            {
                var matches = _matchStyles.Matches(styleElement.Value);

                foreach (Match match in matches)
                {
                    styles.Add(match);
                }
            }

            return styles;
        }

        private static XElement ParseXhtml(string xhtml)
        {
            return XElement.Parse(xhtml);
        }
    }
}

CssToXpath.cs

using System.Text.RegularExpressions;

namespace CssInliner
{
    public static class CssToXpath
    {
        public static string Transform(string css)
        {
            #region Translation Rules
            // References:  http://ejohn.org/blog/xpath-css-selectors/
            //              http://code.google.com/p/css2xpath/source/browse/trunk/src/css2xpath.js
            var regexReplaces = new[] {
                                          // add @ for attribs
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([^\]~\$\*\^\|\!]+)(=[^\]]+)?\]", RegexOptions.Multiline),
                                              Replace = @"[@$1$2]"
                                          },
                                          //  multiple queries
                                          new RegexReplace {
                                              Regex = new Regex(@"\s*,\s*", RegexOptions.Multiline),
                                              Replace = @"|"
                                          },
                                          // , + ~ >
                                          new RegexReplace {
                                              Regex = new Regex(@"\s*(\+|~|>)\s*", RegexOptions.Multiline),
                                              Replace = @"$1"
                                          },
                                          //* ~ + >
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*])~([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
                                              Replace = @"$1/following-sibling::$2"
                                          },
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*])\+([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
                                              Replace = @"$1/following-sibling::*[1]/self::$2"
                                          },
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*])>([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
                                              Replace = @"$1/$2"
                                          },
                                          // all unescaped stuff escaped
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([^=]+)=([^'|""][^\]]*)\]", RegexOptions.Multiline),
                                              Replace = @"[$1='$2']"
                                          },
                                          // all descendant or self to //
                                          new RegexReplace {
                                              Regex = new Regex(@"(^|[^a-zA-Z0-9_\-\*])(#|\.)([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
                                              Replace = @"$1*$2$3"
                                          },
                                          new RegexReplace {
                                              Regex = new Regex(@"([\>\+\|\~\,\s])([a-zA-Z\*]+)", RegexOptions.Multiline),
                                              Replace = @"$1//$2"
                                          },
                                          new RegexReplace {
                                              Regex = new Regex(@"\s+\/\/", RegexOptions.Multiline),
                                              Replace = @"//"
                                          },
                                          // :first-child
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):first-child", RegexOptions.Multiline),
                                              Replace = @"*[1]/self::$1"
                                          },
                                          // :last-child
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):last-child", RegexOptions.Multiline),
                                              Replace = @"$1[not(following-sibling::*)]"
                                          },
                                          // :only-child
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):only-child", RegexOptions.Multiline),
                                              Replace = @"*[last()=1]/self::$1"
                                          },
                                          // :empty
                                          new RegexReplace {
                                              Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):empty", RegexOptions.Multiline),
                                              Replace = @"$1[not(*) and not(normalize-space())]"
                                          },
                                          // |= attrib
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\|=([^\]]+)\]", RegexOptions.Multiline),
                                              Replace = @"[@$1=$2 or starts-with(@$1,concat($2,'-'))]"
                                          },
                                          // *= attrib
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\*=([^\]]+)\]", RegexOptions.Multiline),
                                              Replace = @"[contains(@$1,$2)]"
                                          },
                                          // ~= attrib
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)~=([^\]]+)\]", RegexOptions.Multiline),
                                              Replace = @"[contains(concat(' ',normalize-space(@$1),' '),concat(' ',$2,' '))]"
                                          },
                                          // ^= attrib
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\^=([^\]]+)\]", RegexOptions.Multiline),
                                              Replace = @"[starts-with(@$1,$2)]"
                                          },
                                          // != attrib
                                          new RegexReplace {
                                              Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\!=([^\]]+)\]", RegexOptions.Multiline),
                                              Replace = @"[not(@$1) or @$1!=$2]"
                                          },
                                          // ids
                                          new RegexReplace {
                                              Regex = new Regex(@"#([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
                                              Replace = @"[@id='$1']"
                                          },
                                          // classes
                                          new RegexReplace {
                                              Regex = new Regex(@"\.([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
                                              Replace = @"[contains(concat(' ',normalize-space(@class),' '),' $1 ')]"
                                          },
                                          // normalize multiple filters
                                          new RegexReplace {
                                              Regex = new Regex(@"\]\[([^\]]+)", RegexOptions.Multiline),
                                              Replace = @" and ($1)"
                                          },

                                      };
            #endregion

            foreach (var regexReplace in regexReplaces)
            {
                css = regexReplace.Regex.Replace(css, regexReplace.Replace);
            }

            return "//" + css;
        }
    }

    struct RegexReplace
    {
        public Regex Regex;
        public string Replace;
    }
}

And some tests

    [TestMethod]
    public void TestCssToXpathRules()
    {
        var translations = new Dictionary<string, string>
                               {
                                   { "*", "//*" }, 
                                   { "p", "//p" }, 
                                   { "p > *", "//p/*" }, 
                                   { "#foo", "//*[@id='foo']" }, 
                                   { "*[title]", "//*[@title]" }, 
                                   { ".bar", "//*[contains(concat(' ',normalize-space(@class),' '),' bar ')]" }, 
                                   { "div#test .note span:first-child", "//div[@id='test']//*[contains(concat(' ',normalize-space(@class),' '),' note ')]//*[1]/self::span" }
                               };

        foreach (var translation in translations)
        {
            var expected = translation.Value;
            var result = CssInliner.CssToXpath.Transform(translation.Key);

            Assert.AreEqual(expected, result);
        }
    }

    [TestMethod]
    public void HtmlWithMultiLineClassStyleReturnsInline()
    {
        #region var html = ...
        var html = XElement.Parse(@"<html>
                                        <head>
                                            <title>Hello, World Page!</title>
                                            <style>
                                                .redClass { 
                                                    background: red; 
                                                    color: purple; 
                                                }
                                            </style>
                                        </head>
                                        <body>
                                            <div class=""redClass"">Hello, World!</div>
                                        </body>
                                    </html>").ToString();
        #endregion

        #region const string expected ...
        var expected = XElement.Parse(@"<html>
                                            <head>
                                                <title>Hello, World Page!</title>
                                            </head>
                                            <body>
                                                <div class=""redClass"" style=""background: red; color: purple;"">Hello, World!</div>
                                            </body>
                                        </html>").ToString();
        #endregion

        var result = new CssInliner.CssInliner(html);

        Assert.AreEqual(expected, result.InlinedXhtml);
    }

There are more tests, but, they import html files for the input and expected output and I’m not posting all that!

But I should post the Normalize extension methods!

private static readonly Regex NormalizeSpaceRegex = new Regex(@"\s{2,}", RegexOptions.None);
public static string NormalizeSpace(this string data)
{
    return NormalizeSpaceRegex.Replace(data, @" ");
}

public static string NormalizeCharacter(this string data, char character)
{
    var normalizeCharacterRegex = new Regex(character + "{2,}", RegexOptions.None);
    return normalizeCharacterRegex.Replace(data, character.ToString());
}
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-16T21:06:34+00:00Added an answer on May 16, 2026 at 9:06 pm

    Since you’re already 90% of the way there with your current implementation, why don’t you use your existing framework but replace the XML parsing with an HTML parser instead? One of the more popular ones out there is the HTML Agility Pack. It supports XPath queries and even has a LINQ interface similar to the standard .NET interface provided for XML so it should be a fairly straightforward replacement.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

So I'm trying to make an incrementer similar to this: http://www.usagain.com/ but I'm apparently
Here is nice piece of code that works fair in all browsers: http://www.imaputz.com/cssStuff/bigFourVersion.html Since
Need to apply a filter to a file like this: TUPAC_0006:1:1:2554:2356#0/1 0 * 0
Need a map reduce function by mongo in php This my mongo structure [_id]
In my user control I got this inline CSS in which I have only
I need to grab inline script tags inside html pages. The regex will eventually
Situation is simple. I need to wrap text around two inline objects (buttons with
Need just a push in the right direction with this. I'm building a multi-language
In Javascript/JQuery I have a need to copy one elements CSS style object to
I have an inline styles in my page as : <head> <meta http-equiv=Content-Type content=text/html;

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.