Themes and Web Design

When you create a new project using our project template as described in the Introduction article, you get a bunch of bootswatch themes included. You can select a theme from the dropdown list under Administration > Site Settings to try the included themes. Of course for most serious projects you will want to create a custom theme.

Custom Theme Quick Start

  1. Create a new folder under /sitefiles/s1/themes - the folder name will be the theme name, don't use spaces or special characters.
  2. Create a "Shared" folder beneath the theme folder.
  3. Copy the _Layout.cshtml file from the main Views/Shared folder into your new theme Shared folder
  4. Now you can select your theme from the dropdown list under Administration > Site Settings
  5. The _Layout.cshtml file is the main file for themes and from there you can change the css links to point to your custom css

Most of the work for a new theme is in the _Layout.cshtml file and in the CSS. In some cases you may want to customize other specific views for specific reasons, but you should start with the layout and the CSS and only override other views if needed.

Where are the Views?

The default views for cloudscribe Core are used directly from embedded resources in cloudscribe.Core.Web.Views.Bootstrap3 Nuget package. You can download any views you want to customize from the github repository and put them in your local file system and those will be used in preference to the embedded views. Similarly the default views for cloudscribe SimpleContent come from embedded resources in the NuGet package cloudscribe.SimpleContent.Web.Views.Bootstrap3 and you can override those in a similar fashion by downloading them from here.

Also any matching views found in the theme folder will be used in preference to views found under the default Views folder, so that views can be customized per installation or per theme. Any views you want to customize for a given theme you can just copy into the theme folder.

My recommendation is only install views locally that you really need to customize because these projects are under active development and the embedded views do change from release to release as we add new features and continue to improve things. If you do install local views for customization you may need to compare them to the embedded views whenever you update to newer NuGet packages to see if anything changed.

When bootstrap 4 is released we intend to make a new set of views for it, that is why we keep the the views in a separate NuGet package from the main code at least for the bigger projects like cloudscribe Core and SimpleContent. It is also possible to create views for other css frameworks and we hope eventually to see community members implement some other alternatives such as material design, although the bootstrap paper theme is also based on the idea of material design. There are a lot of css frameworks out there and new ones seem to come along frequently but bootstrap is still the most commonly used at least from my point of view.

Shared Themes and Per Tenant Themes

With the multi-tenant support it is easy to have some themes shared between sites and other themes private to the site, and it is also possible to keep the static files needed by a theme in a wwwroot folder below the theme folder. Our StarterKit samples all have a bunch of themes in the SharedThemes folder which are available to any of the tenants (aka sites). The themes that are private to a site are kept under /sitesfiles/[aliasid]/themes. Where aliasid is like s1, s2, s3, incrementing whenever a new site is created. You can find the alias id in Site Settings.

Keeping Theme Static Resources with the theme views

Note that if no wwwroot folder is found below the theme folder then no static files middleware is wired up for that theme. But if a wwwroot folder does exist below the theme name folder, then static file middleware will be wired up for that wwwroot folder and you can create subfolders for css, js etc as needed to organize the static resources for your theme. Keeping the static resources of the theme in a sub folder is a great idea because then the theme is self contained with everything it needs and can be packaged to use in other sites.

The MultiTenantOptions are used to configure things. You can customize these settings from config (ie appsettings.json) as shown in this snippet which has the default values.

"MultiTenantOptions": {
"Mode": "FolderName",
"UseRelatedSitesMode": "false",
"RelatedSiteId": "00000000-0000-0000-0000-000000000000",
"UserPerSiteWwwRoot": "true",
"UserPerSiteThemes": "true",
"UseSharedThemes": "true",
"SiteFilesFolderName": "sitefiles",
"SiteThemesFolderName":"themes",
"SiteContentFolderName":"wwwroot",
"SharedThemesFolderName":"SharedThemes",
"ThemeStaticFilesFolderName":"wwwroot"
}

So by default static resources for private tenant themes come from [projectroot]/sitefiles/[aliasid]/themes/[themename]/wwwroot (if that folder exists when the middleware is being wired up).

static resources for shared themes come from [projectroot]/SharedThemes/[themename]/wwwroot (if that folder exists when the middleware is being wired up).

Differences Between Private Themes and Shared Themes

To make the theme use static resources from the theme specific wwwroot folder requires using some extra attributes when referencing the resources, and there are differences in the needed attributes for private per tenant theme vs shared themes.

You will see the difference in the layout files for private themes included in the starter kits which have this at the top for css:

<environment names="Development">
    <link rel="stylesheet" href="~/css/bootstrap.css" cs-resolve-theme-resource="true" cs-tenant="@Tenant" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</environment>
<environment names="Staging,Production">
    <link rel="stylesheet" href="~/css/bootstrap.min.css" cs-resolve-theme-resource="true" cs-tenant="@Tenant" />
    <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>

and this at the bottom:

<environment names="Development">
    <script src="~/js/jquery.js" cs-resolve-theme-resource="true" cs-tenant="@Tenant"></script>
    <script src="~/js/bootstrap.js" cs-resolve-theme-resource="true" cs-tenant="@Tenant"></script>
</environment>
<environment names="Staging,Production">
    <script src="~/js/jquery.min.js" cs-resolve-theme-resource="true" cs-tenant="@Tenant"></script>
    <script src="~/js/bootstrap.min.js" cs-resolve-theme-resource="true" cs-tenant="@Tenant"></script>
</environment>

Whereas Shared Themes require an extra attribute for the TagHelpers to know where to map the files from, like this at the top (Note the extra cs-shared-theme="true" attribute needed for shared themes.):

<environment names="Development">
    <link rel="stylesheet" href="~/css/bootstrap.css" cs-resolve-theme-resource="true" cs-shared-theme="true" cs-tenant="@Tenant" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</environment>
<environment names="Staging,Production">
    <link rel="stylesheet" href="~/css/bootstrap.min.css" cs-resolve-theme-resource="true" cs-shared-theme="true" cs-tenant="@Tenant" />
    <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>

and this at the bottom:

<environment names="Development">
    <script src="~/js/jquery.js" cs-resolve-theme-resource="true" cs-shared-theme="true" cs-tenant="@Tenant"></script>
    <script src="~/js/bootstrap.js" cs-resolve-theme-resource="true" cs-shared-theme="true" cs-tenant="@Tenant">
</script>
</environment>
<environment names="Staging,Production">
    <script src="~/js/jquery.min.js" cs-resolve-theme-resource="true" cs-shared-theme="true" cs-tenant="@Tenant">
</script>
    <script src="~/js/bootstrap.min.js" cs-resolve-theme-resource="true" cs-shared-theme="true" cs-tenant="@Tenant">
</script>
</environment>

For shared themes, all the taghelper does based on those attributes is prefix the theme name to the url for the resource. If using host name based tenants then private themes also are prefixed with the themename. If using folder tenants then the urls are prefixed with [foldersegment]/[themename]/ 

You could add those segments to the url yourself instead of using the attributes and letting the taghelper take care of it. I did not implement a taghelper for images, so if you want to reference images in the layout from the theme specific wwwroot folder then you would need to manually prepend the theme name to the url for the image.

Having theme static resources within the theme specific wwwroot folder is nice because it makes a theme portable and self contained, but you don't have to always do that. In some cases you may want to have some common resources shared across themes. For that you can just put the resources in the main wwwroot folder of the app and leave out the attributes mentioned above so the taghelpers are not invoked for those, and the urls starting with ~/ should resolve to the main wwwroot folder.

Basic Steps to Create a Theme

If you want a private theme, create a folder under /sitefiles/s1/themes, if you want a theme that is shared across multiple sites create a folder beneath /SharedThemes. Don't use any special characters or spaces in the folder name, the folder name will be the theme name that will appear in the dropdown list under Administration > Site Settings. Then create a sub folder named "Shared" and copy the _Layout.cshtml file from /Views/Shared into you "Shared" sub folder. Now you are ready to start customizing by pointing to a custom css file from the layout file.

If you create multiple sites/tenants each site will be represented by a subfolder beneath /sitefiles that corresponds to the site alias id, which is just an incrementing value like s1, s2, s3 and so on, calculated as "count of sites plus 1" at the time of site creation.

If you want to view or submit comments you must accept the cookie consent.