Back to Top

DNN Details 002: Fine Tuning Your HTML Output

7/7/2020 DNN Details
DNN Details 002: Fine Tuning Your HTML Output

An approach to crafting better HTML output in DNN Platform that will make your front-end developer very happy

DNN's HTML output hasn't changed much over the years. But HTML/CSS has and a variety of best practices have come and gone. Fine-tuning very basic things are often critical, or at least greatly desired for some reason or other. Here is what DNN's "out of the box" HTML looks like:

<html  lang="en-US">
<head id="Head">...<title>Home</title>
<body id="Body">
<form method="post" action="/" id="Form" ...

I was approached by a frustrated front-end developer who was used to "decorating" this output down to the last detail, and he couldn't see any way to accomplish this in DNN. Sure enough, DNN's theme scheme (skins and containers) doesn't directly allow for getting at the things like the attributes of the HTML and BODY tags. I asked to see exactly what he wanted (needed?) and got this, additions highlighted:

<html class="no-js" lang="en-US">
<head id="Head">...<title>Home</title>
<body id="Body" class="site" data-component="Site">
  <form method="post" action="/" id="Form" ...

Not much difference, right? A class added to the HTML tag and more added to the BODY tag. Being a responsible manager and wondering if this was really necessary, I asked why (and in case you were wondering) these were the answers.

  1. <html class="no-js"> - is sort of best practice, an easy way to write fallback CSS to make sure critical features work if JS is busted.
  2. <body class="site" ... > - is for how we are proposing to tweak layout CSS — especially stuff that is in the theme but not included in Bootstrap. 
  3. <body ... data-component="Site"> - is for a little JS component framework we are using.

So was any of it strictly necessary? Who cares, he said the magic word, "Bootstrap." Let's get this done.

This is going to require some ASP.NET knowledge and code. We want this code to live in the right place, so we decided to keep it part of our custom theme (at Accuraty we are an agency and build most of our sites using our own custom DNN theme, you can see it here if you are interested, the code below was added to the bottom of includes/_registers.ascx).

The Solution

We need a hook into the page output getting built (we'll use Page_Init()) and for this to work, we'll need to change the default AutoEventWireup to from False to True:

<%@ Control language="C#" AutoEventWireup="true" ...

Now we can add this code in our skin's ascx file:

<script runat="server">
  // Important: for this to work, AutoEventWireup (above) needs to be True
  protected void Page_Init(object sender, EventArgs e) {
    // get a pointer to the Page
    DotNetNuke.Framework.CDefault thisPage = (DotNetNuke.Framework.CDefault)this.Page;
    // see DNN's /Default.aspx; note that this could break someday, 
    // see HtmlAttributes in Default.aspx.cs
    // get a pointer to the Literal inside the <html> tag
    var litHtml = (System.Web.UI.WebControls.Literal)thisPage.FindControl("attributeList");
    litHtml.Text = "class=\"no-js\" " + litHtml.Text.Trim();
    // get a pointer to the <body> tag (control)
    var tagBody = (System.Web.UI.HtmlControls.HtmlGenericControl)thisPage.FindControl("Body");
    AppendCssClass(tagBody, "site");
    tagBody.Attributes["data-component"] = "Site";
  public static void AppendCssClass(HtmlGenericControl control, string cssClass) {
      // Ensure CSS class is definied
      if (string.IsNullOrEmpty(cssClass)) return;
      // Append CSS class
      if (string.IsNullOrEmpty(control.Attributes["class"])) {
        // Set our CSS Class as only one
        control.Attributes["class"] = cssClass;
      } else {
        // Append new CSS class with space as separator
        control.Attributes["class"] += (" " + cssClass);

That was it. If you are used to ASP.NET, this is surprisingly readable. In English, we hook the page initialization, get a pointer to the attribute list of the HTML tag's control, and insert our new class. Then we get a pointer to the BODY tag's control and add both our new attributes. 

The results are a perfect match for what was requested. They also quickly brought half a tear to our front-end developer's eye.


Why not just edit DNN's Default.aspx?
It is actually a pretty bad idea. Yes, it works, but the very next time you upgrade DNN to the latest version, that file - and therefore your changes - will get overwritten and everything depending on your customizations will simply stop working.

Are there other ways to approach this?
There is an awesome project call StyleHelper by, we've used it reliably for over a decade in our custom themes. It has token/attributes like AddHtmlAttribute, AddBodyClass and AddToBodyClass and may be worth experimenting with a) if you are already familiar StyleHelper or if b) coding (like our example solution above) is not in your wheelhouse.

Will this type of dynamic modification cause problems with DNN upgrades in the future?
In our approach we chose to insert or add to existing elements. Barring a significant change in the default DNN behavior in the future, this should continue working and do no harm. However, we do recommend that you document and validate this in your upgrade scenario/reference.

Thanks: Daniel Valadas, Chris Lusk, and Will Strohl

Read more DNN Details posts... More about Accuraty...

‹ Back to List

With the design scheme of our new website, we feel that our visitors naturally take in more of the website and have more interest in exploring.

DEBUGGING OUTPUT (isDebug is true), WAN IP:

isIpSpecial() begins...
GetAllowedIps() begins...
GetAllowedIps() Found in cache; GetAllowedIps() completed with Count=7, List:,,,,,, 2601:41:c77f:7a0:4536:f8e4:9644:ca23
isIpSpecial() completed