Theme

Overview

Designers can fully customize Fanray’s public facing site through a theme. A theme is consisted of the following,

  • Theme manifest file theme.json
  • Theme class that derives from the base Theme.cs which stores your particular theme’s options
  • Theme UI
    • Client artifacts such as CSS, images, JavaScript, SCSS etc. You can use whatever client-side technologies you want
    • The wwwroot folder that contains the output client artifacts
    • Razor Views the .cshtml files in Views folder
  • Thumb image named theme.png which is 1200 x 900 placed under wwwroot folder

Let’s take a look at the default Clarity theme.

Clarity Theme Project

Theme Project

A theme project is a Razor Class Library, the .csproj file looks like below. When you build the project it will copy the theme manifest theme.json to the webapp project Fan.WebApp.

<Project Sdk="Microsoft.NET.Sdk.Razor">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <AddRazorSupportForMvc>true</AddRazorSupportForMvc>
  </PropertyGroup>

  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\..\Core\Fan.Web\Fan.Web.csproj" />
  </ItemGroup>

  <ItemGroup>
    <EmbeddedResource Include="wwwroot\**\*" />
  </ItemGroup>

  <Target Name="CopyArtifacts" AfterTargets="Build">
    <PropertyGroup>
      <CopyToDir>..\..\Core\Fan.WebApp\Themes\Clarity</CopyToDir>
    </PropertyGroup>

    <ItemGroup>
      <ManifestToCopy Include="$(OutputPath)\theme.json" />
    </ItemGroup>

    <RemoveDir Directories="$(CopyToDir)" />
    <MakeDir Directories="$(CopyToDir)" Condition="!Exists('$(CopyToDir)')" />
    <Copy SourceFiles="@(ManifestToCopy)" DestinationFiles="$(CopyToDir)\%(FileName)%(Extension)" />
  </Target>
</Project>

Theme Manifest File

Each theme requires a manifest file called theme.json to be at the root of the theme project. The system loads the theme based on this file, here is the default theme Clarity’s manifest file.

{
  "name": "Clarity",
  "description": "A Bootstrap 4 based theme for Fanray blog.",
  "version": "1.1.0",
  "themeUrl": "https://www.fanray.com",
  "author": "Ray Fan",
  "authorUrl": "https://www.fanray.com",
  "license": "Apache 2.0",
  "licenseUrl": "https://www.apache.org/licenses/LICENSE-2.0",
  "tags": [ "responsive", "right-sidebar" ],
  "menus": [
    {
      "id": "menu1",
      "name": "Main Menu",
      "description": "The top navigation menu"
    }
  ],
  "pageLayouts": [
    {
      "id": "layout1",
      "name": "Default",
      "description": "Default layout with a right sidebar"
    },
    {
      "id": "layout2",
      "name": "Full",
      "description": "For full page design with no sidebars, e.g. a home page"
    },
    {
      "id": "layout3",
      "name": "Multipage",
      "description": "For pages with child pages, e.g. documentation pages"
    }
  ],
  "widgetAreas": [
    {
      "id": "blog-sidebar1",
      "name": "Blog Sidebar"
    },
    {
      "id": "blog-after-post",
      "name": "Blog After Post Content"
    },
    {
      "id": "page-sidebar1",
      "name": "Page Default Layout Sidebar"
    },
    {
      "id": "page-sidebar2",
      "name": "Page Docs Layout Sidebar"
    }
  ]
}

The name and description will be displayed in the Admin Panel > Themes along with the theme.png image. There are three special properties worth explaining menus, pageLayouts and widgetAreas below.

Menus

The menus property of the theme.json represents the navigational menus on the site. The desinger is free to have zero or more menus and place them anywhere on the theme they desire. These menus are registered here.

  ...
  "menus": [
    {
      "id": "menu1",
      "name": "Main Menu",
      "description": "The top navigation menu"
    }
  ],
  ...

The id has a pre-defined value of menu1, menu2 and so on, the name and description are displayed on the Admin Panel > Navigation .

The Clarity theme, for example, has just one menu with id menu1 and it’s placed in the Header.cshtml file like below.

<menu id="Menu1" />

Page Layouts

The pageLayouts property of the theme.json represents the different layouts pages could have. Pages can have different layouts to fit different needs. The theme has full control on what layouts to provide and how many different layouts there are. For example the Clarity theme has three different layouts

  ...
  "pageLayouts": [
    {
      "id": "layout1",
      "name": "Default",
      "description": "Default layout with a right sidebar"
    },
    {
      "id": "layout2",
      "name": "Full",
      "description": "For full page design with no sidebars, e.g. a home page"
    },
    {
      "id": "layout3",
      "name": "Multipage",
      "description": "For pages with child pages, e.g. documentation pages"
    }
  ],
  ...
  • Full layout: This is suitable for like a site’s home page, it may show a carousel of product images, highlights of major functionality etc. It gives user full control on the design of the page, it has no sidebar or page titles.
  • Multipage layout: This is suitable for documentation pages, where a page has many child page. A page navigation is present as a widget in the sidebar with links to all the pages. The user could customize the navigation with nested lists, have headings etc.
  • Default layout: If a page is not in either of the above mentioned cases, here is a default layout, it has a sidebar of widgets etc.

These layouts are in the Views/Shared folder _PageLayout1.cshtml, _PageLayout2.cshtml and _PageLayout3.cshtml.

Widget Areas

The widgetAreas property in the theme.json represents the Widget Areas designers want to use on their theme. Designers are free to decide what and how many areas they want and where to place them on their theme. For example, the Clarity theme is using four areas.

  ...
  "widgetAreas": [
    {
      "id": "blog-sidebar1",
      "name": "Blog Sidebar"
    },
    {
      "id": "blog-after-post",
      "name": "Blog After Post Content"
    },
    {
      "id": "page-sidebar1",
      "name": "Page Default Layout Sidebar"
    },
    {
      "id": "page-sidebar2",
      "name": "Page Docs Layout Sidebar"
    }
  ]
  ...

The id property is used by the designers to put a particular area inside their theme, for example in Clarity theme’s _BlogPostLayout.cshtml, there is a sidebar next to the blog’s main content. Each Widget Area renders out a div html.

<div class="row">
    <div class="col-md-9 blog-main">
        @RenderBody()
    </div>
    <aside class="col-md-3">
        <widget-area id="blog-sidebar1" />
    </aside>
</div>

The areas specified in the theme manifest will show up in Admin Panel > Widgets. The name property is purely suggestive and can be anything, it’s used as a guide in the Admin Panel for users to drag and drop widgets to these areas.

Widget Areas

There are two kinds of widget areas, System Defined and Theme Defined.

System Defined Areas

Fanray pre-defines a set of areas for commonly used scenarios, like sidebars and footers etc. System Defined areas have the advantage of maintaining the areas’ existing widgets when user switches themes.

For System Defined areas, the id property has pre-defined values, they include the following

  • blog-sidebar1 intends to be the primary sidebar for posts when theme has only 1 sidebar
  • blog-sidebar2 intends to be the secondary sidebar for posts when theme supports 2 sidebars
  • blog-before-post intends to show up before a single blog post
  • blog-after-post intends to show up after a single blog post
  • blog-before-post-list intends to show up before a list of blog post, like on the index page
  • blog-after-post-list intends to show up after a list of blog post, like on the index page
  • page-sidebar1 intends to be the primary sidebar for pages when theme has only 1 sidebar
  • page-sidebar2 intends to be the secondary sidebar for pages when theme supports 2 sidebars
  • page-before-content intends to show up before a page main content
  • page-after-content intends to show up after a page main content
  • footer1 site footer
  • footer2 when site footer has two columns
  • footer3 when site footer has three columns

Using System Defined areas has the advantage of maintaining existing widgets when user switches to a different theme.

Theme Defined Areas

While the System Defined areas try to cover common scenarios, designers still have the freedom to create whatever widget areas, i.e. Theme Defined areas, they want and place these anywhere they want.

For example, a designer could add the following to the widgetAreas array, the id value here is given by the designer.

{
  "id": "my-area",
  "name": "My area."
}

After that the designer can place it anywhere on her theme like below, and when the site restarts Fanray will register my-area into the system.

<widget-area id="my-area" />

Just be aware

  1. Once a Theme Defined widget area is registered with the id you give, any update to the id will be considered and yield a new area.
  2. If the user switches to a different theme, the Theme Defined widgets will not be carried over to the new theme.

Views

A theme has a list Razor Views inside the theme’s Views folder. Below is a break down of what these files are

Files Description
Blog
   _PostInfo post info, date, info, tags etc.
   Archive posts for a particular year, month
   Index site home
   Page a page
   Post a single post
   Rsd blog RSD used by MetaWeblog API
   Tag posts for a tag
Shared
   _BlogPostLayout blog post layout
   _Layout the main layout
   _PageLayout1 page layout 1
   _PageLayout2 page layout 2
   _PageLayout3 page layout 3
   404 page not found
   Analytics google analytics
   CookieConsent cookie consent alert
   Error error page other than 404
   Footer footer
   Header header
   Menu1 menu 1

These view files have to be named as they are, except for the partial view on post info. These views contain HTML and presentational logic to display the View Models passed in by the controllers. Designers have the freedom to change the HTML markups as they wish.

Styles

Desingers can use whatever font-end libraries and technologies they like. For Clarity, I’m using NPM, SCSS, Bootstrap 4 etc. All client artifacts are built into the theme’s wwwroot folder.

There are two CSS files need attention styles.css and content.css. All Fanray themes must provide these 2 CSS files,

  • style.css: the theme styles for the public facing site.
  • content.css: the content styles for the editor, it includes typography and the content width from the theme styles. This CSS file enables users to see the exact content style while composing their post.

Architecture
Plugin