Day 23: Choosing Your Own Look-And-Feel

This is an installment in a 30 day series on Bootstrap and the MVC Framework. To see more, check out Day 0 for an index.

Here’s the thing: everybody’s site is going to look the same if everybody’s site uses Bootstrap, right? Well, if we did that to users, we’d sure be missing the point as developers, and for two key reasons: 1) With the LESS and SASS source it’s easy to customize, and 2) Because it’s easy to customize, there’s already people creating all kinds of alternate themes for Bootstrap.

So, we aren’t headed down a slippery slope here, we just need to make a little effort to give our users some unique looking sites.  Other posts will show you how to replace the Bootstrap theme, but this one will show you how to let your users choose from a list you’ve pre-built.

Download a Free Substitute

There are some great, free alternatives located at Bootswatch.com. So, start there and pick one or two to download, I chose Amelia and Darkly. We need to create a folder structure to organize our themes, and move the CSS into those folders. Mine ended up working like this:

image

Note that I also pushed a copy of the stock CSS for bootstrap into this structure. This allows us to simplify our code for theme switching, allowing users to pick the base theme if they like.

Shameless plug: If you’re looking for professionally designed themes to replace your palette, you can help out this blogger (me!) by purchasing one over at {wrap}bootstrap. They have a selection of great looking Bootstrap themes. A very affordable alternative to taking the time to create your own theme.

Creating a Helper Class

Next, we create a class called Bootstrap.cs (I put mine in \Helpers) so that we can programmatically work with the themes. This class is responsible for presenting the list of supported themes and resolving the path to them when we try to load the bundles.

public class Bootstrap
{
    public const string BundleBase = "~/Content/css/";

    public class Theme
    {
        public const string Stock = "Stock";
        public const string Amelia = "Amelia";
        public const string Darkly = "Darkly";
    }

    public static HashSet<string> Themes = new HashSet<string>
    {
        Theme.Stock,
        Theme.Amelia,
        Theme.Darkly
    };

    public static string Bundle(string themename)
    {
        return BundleBase + themename;
    }
}
This is a simple class, but it prevents us from duplicating code all over the place or unnecessary spelling mistakes. The other nice thing is that if you choose to add another theme to your project, you will just have to modify this one file and the rest will fall into place.  For that to happen, we’ll need to modify our startup to generate all the appropriate bundles. ## Updating Our Startup Bits Head into BundleConfig.cs (inside of \App_Startup) and replace the code that creates the Bootstrap bundle with the following:
foreach (var theme in Bootstrap.Themes)
{
    var stylePath = string.Format("~/Content/Themes/{0}/bootstrap.css", theme);

    bundles.Add(new StyleBundle(Bootstrap.Bundle(theme)).Include(
                stylePath,
                "~/Content/bootstrap.custom.css",
                "~/Content/site.css"));
}
We’re simply looping over that handle collection we created so that we could generate a bundle for every installed theme.  We always want all the themes – startup is only run at as the application starts, so we need them all there – as different users may wish to select different themes. ## Making Our Layout “Themeable” What we really need to do here is just a quick update to figure out the user’s current theme, and then figure out what the correct bundle to use is.
@{
    var theme = Session["CssTheme"] as string ?? Bootstrap.Theme.Stock;
}
@Styles.Render(Bootstrap.Bundle(theme))
I’m using Session for now, but I’ll explain why in a bit that is bad idea. ![Smile](https://jcblogimages.blob.core.windows.net/img/2014/06/wlEmoticon-smile3.png) Note that, at this point, you could set that theme default – right now set to Bootstrap.Theme.Stock – to whichever theme you like and run your app. The mechanics of building the bundle and the class to resolve it are all in place. ## Letting the User Choose a Theme Once again we’re going to revisit the _LoginPartial.cshtml file (in Views\Shared). In this round, we’re going to update the text that shows the logged in user’s email address (which, by default, is also their username). The LI for the username is now going to be a dropdown box in the navbar.
<li class="dropdown">
    <a href="#" class="dropdown-toggle" data-toggle="dropdown">Hello @username! <span class="caret"></span></a>
    <ul class="dropdown-menu" role="menu">
        <li>
            @Html.ActionLink("Manage Account", "Manage", "Account", routeValues: null, htmlAttributes: new { title = "Manage" })
        </li>
        <li class="divider"></li>
        @foreach (var theme in Bootstrap.Themes)
        {
            <li><a href="@Url.Action("ChangeTheme", "Profile", new { themename = theme})">@theme</a></li>
        }
    </ul>
</li>
I’ve taken that LI that was there – all it had was the user name, which was a link to Manage Account – and replaced it will all of the above code. We put in a divider and iterate over the list of known themes.  Each will be a link to a “ChangeTheme” action on the “Profile” controller. ## Adding our New Profile Controller Throw ProfileController.cs in your \Controllers directory with the following, lone action in the class:
public ActionResult ChangeTheme(string themename)
{
    Session["CssTheme"] = themename;
    if (Request.UrlReferrer != null)
    {
        var returnUrl = Request.UrlReferrer.ToString();
        return new RedirectResult(returnUrl);
    }
    return RedirectToAction("Index", "Home");
}

If we have a referring URL we can push the user back to the same page, otherwise we ship them home. The preference of theme is set in the Session.

And there you have it: users can now pick their own theme on your site:

image

Next Steps

This data doesn’t really belong in the user’s session…whenever the app pool is recycled or the website or IIS is restarted or their session expires they will lose this choice. Even worse, session and Identity aren’t on the same lifecycle, so when they log out the session persists and they’ll still see the theme they chose when they logged in.

So, where should it be stored? Tune in next time as we answer this question and more. Smile with tongue out

Happy coding! Smile