Using a Theme with the Example App

This tutorial discusses how you would take a downloaded website theme and integrate it with the Scrivito Example App. The tutorial focuses on one possible workflow and presents the nuts and bolts of an integration while trying to keep it short and readable.

Website themes can be found in many places on the web; just google "website themes". When choosing a theme for the Scrivito Example App, it is recommended to choose one compatible with Bootstrap or a grid layout. We chose a fairly simple theme so anyone can follow along. The theme is called “Directive” and is offered free from the folks over at html5up.net. If you are following along:

There are two main steps to take, illustrated by the screenshots below: integrating the theme’s CSS and layout, and making the content editable.

After step 1: The theme has been integrated
After step 2: Scrivito’s in-place editing functionality is available

Integrating the styles and the layout

The Scrivito Example App repository is the basis for our project as it includes integration of Scrivito, routing, SEO as well as setup and packaging to deploy easily to the web.

You do not need all the files from the Directive download as some of them are redundant. We are going to use the “javascript”, “css/images” and “sass/libs”folders, as well as the “main.scss” file. So, from the Directive download we are going to copy

  • /html5up-directive/assets/js/
  • /html5up-directive/assets/css/images/
  • /html5up-directive/assets/sass/libs/
  • /html5up-directive/assets/sass/main.scss

and paste them into our folder: /src/assets/stylesheets/

Add the styles

Next, we want to add the “main.scss” to our “index.scss” file to include the styles. So, in “index.scss” at about line 114, just underneath @import "_grid_layout_editor"; add @import "main";. And we can remove the /* Style section from “index.scss.”

Since we already have Font Awesome included in the Example App and did not copy over the redundant package from Directive, we can remove line 4 from “main.scss” (@import url("font-awesome.min.css");)

The Directive layout has a header image which is not in the “css/images” folder so we need to add one. You can find the sample image intended for the theme: “/html5up-directive/images/header.jpg”. Copy and paste it into ”/src/assets/images”:

Then change the background-image URL in “main.scss” from url('../../images/header.jpg'); to url('images/header.jpg'); to reflect its current location. This string needs to be updated in three places, at about line 724, 1051, and 1228.

Include the layout

To get started, we are going to copy the code inside the  <body> section of the “index.html” file from Directive for a static homepage in the Example App. First, open “src/Objs/Homepage/HomepageComponent.js” and paste the HTML markup inside the <body> tags of the “html5up-directive/index.html” file below the Scrivito.ContentTag. As we are using JSX, we need to make sure the code is wrapped in a single element, so wrap everything in a <div>. It will look like this:

src/Objs/Homepage/HomepageComponent.js
Copy
Scrivito.provideComponent('Homepage', ({ page }) =>
  <div>
    <Scrivito.ContentTag tag="div" content={ page } attribute="body" />
    // include the code from inside the <body> tags of Directive's index.html here...
  </div>
);

Next, since class is a reserved word in JavaScript, we need to update all the instances of class= to className=. Finally, if you want to keep the comments, they need to be wrapped in brackets and commented for JavaScript like so: { /* Header */ } For this tutorial, you can remove the large commented out section in the middle of the Directive “index.html” file you copied, starting at about line 66 to about 213. Finally, if you what to have static images you will need to import them as follows:

Copy
import Pic01 from "../../assets/images/pic01.jpg";

Then replace the img src with the new variable surrounded in brackets like so:

Copy
<img src={ Pic01 } alt="" />

Preparing your Scrivito Content

When you create a website with Scrivito, whether it is a trial or a paid instance, we provide you with a working website which includes sample content. This is to help make Scrivito easier to implement. The sample content includes the framework for a robust website as well as navigation, SEO, etc. You can decide to replace the content or remove it. Removing it in parts or completely is also an option. For this tutorial, we are going to keep it simple and remove all content.

Be aware that if you have added, replaced or modified the content in your Scrivito instance, the code below will remove all of it. Before you run the command, be sure that this is what you wish to do.

You will need to be in editing mode and in a working copy. Just add “/scrivito” to the URL (after the hostname) and choose a name for your working copy. Then open the browser console and switch the execution context from top to scrivito_application.

Copy
//To Remove All content, run in your browser console:

Scrivito.load(() => {
    const all = [...Scrivito.Obj.all()];
      return all;
  }).then(objs => {
    objs.forEach(o => {
      console.log(`Deleting ${o.id()}...`);
      o.destroy();
    });
    console.log('Done deleting all objs.');
  });

Once the command above is finished running, you can continue to work in your working copy. Once you publish this working copy, the content will only be available as a snapshot in your publishing history. Once you have published more times than the number of archived copies you have in your plan, it will be gone.

Test it out

At this point, we should have what we need from the theme to display a static page within the Example App. Be sure you have included your SCRIVITO_TENANT in your “.env” file, then start your project with npm start.

The Example App will start up, and you will see an “Ooops” page as there is no homepage defined yet, which is expected as we deleted all the content above.

Again in Edit mode, we need to create a homepage using the browser console. So open the console and update the execution context from top to scrivito_application. In the console, enter: 

Copy
h = Scrivito.getClass("Homepage").create({ title: "This is Directive" });

Then set the app’s homepage to this new page by assigning the root path, “/”, to it. 

Copy
h.update({ _path: "/" });

Refresh your page and you will now see the static version of the Directive page within the Scrivito App.

Finally, publish your working copy to set the homepage.

We are taking some license here to keep the tutorial simple. As the “Homepage” page type includes many options for the entire site, including SEO, it is intended to be used only once in a site. If you wish to use the Directive theme for other pages you should create a “Directive” page type as well.

Making content editable

Let’s get started breaking up the page into parts. Looking at the code we transferred from Directive's “index.html”, we have three <div> elements with ids indicating that they are unique, header, main, footer.

Since the styling of these sections constitutes the main layout of the Directive theme, we keep these as the three sections of our page. Using several of the built-in widgets, we will show how to build up a page to match the original design of Directive. Finally, we will set up a couple of new widgets to handle the content on the the page. Ready? Buckle up, here we go....

Header section

The first section of the page, the <div id="header">, provides quite a bit of CSS for the page and should be considered a layout element. The <span>, <h1> and <p> elements are content elements we will convert to make them editable.

The Example App ships with an IconWidget we can use here. So we will convert the <span> tag to

Copy
<Scrivito.ContentTag tag="span" className="logo icon" content={ page } attribute="icon" />
The className="logo icon" part adds these CSS classes from the Directive theme to the element. We also need to add the new icon attribute to our “HomepageObjClass.js” file like this: icon: ['widgetlist', { only: 'IconWidget' }],. This will prevent the editors from choosing widgets other than the IconWidget for this element. Once added, selecting the widget properties of this widget, you can choose the fa-paper-plane icon from the list to make the element match the Directive example.

Next up are the <h1> and <p> elements. We will make this whole section accept any combination of widgets by rendering the header widgetlist attribute here. So replace the <h1> and the <p> elements with:

src/objs/Homepage/HomepageComponent.js
Copy
{ /*  Header */ }
      <div id="header">
        <span className="logo icon fa-paper-plane-o"></span>
        <Scrivito.ContentTag content={ page } attribute="header" />

// ...

Now add the header attribute to the Homepage class definition:

src/objs/Homepage/HomepageObjClass.js
Copy
const Homepage = Scrivito.provideObjClass('Homepage', {
  attributes: {
    // ...
    header: 'widgetlist', // <--
    // ...
  },
});

Go ahead and add, for example, a HeadlineWidget and a TextWidget to the homepage. 

Main section

Moving to the <div id="main"> section of the page, we want to make this completely editable. The “main” section has two unique elements, the “major container” and the “box alt container”. Since these have some unique styling we may want to include elsewhere, we are going to create a widget for each one. This will allow us to just put a widgetlist in the entire “main” section and allow our editors to customize it how they like.

To begin, we are going to remove the markup in the “HomepageComponent.js” file in the <div id="main"> section and replace it as follows:

src/Objs/Homepage/HomepageComponent.js
Copy
// ...
  <div id="main">
    <div className="box alt container">
      <Scrivito.ContentTag
        content={ page }
        attribute="main"
      />
    </div>
  </div>
// ...

Then add the main attribute to the object class:

src/objs/Homepage/HomepageObjClass.js
Copy
// Imports

const Homepage = Scrivito.provideObjClass('Homepage', {
  attributes: {
    // ...
    main: 'widgetlist', // <--
    // ...
  },
});

This will make the page more flexible and allow the editors to add widgets. 

Adding widgets

To keep things consistent and easier for the editors to add content that matches the original theme, we can provide some widgets. Below are examples of the widgets for this section. Feel free to add them, or don't. If you want to dig deeper into building widgets, check out the Creating a Custom Widget Type documentation.

ADD A BANNERWIDGET

Create the BannerWidget

The first widget we’ll create is a BannerWidget that lets editors add stylish text to the “header” or “footer” section. In The “major container” sections, we simply want to pull in the styling and allow the addition of widgets. The two major container sections have slightly different CSS, based on whether it is at the top, “header”, or the bottom, “footer”. We can account for this by adding an option for our editors to choose from. Once again, we’ll provide a widgetlist attribute for the content but apply Directive’s “major container” classes to it.

src/widgets/BannerWidget/BannerWidgetClass.js
Copy
// Imports

const BannerWidget = Scrivito.provideWidgetClass('BannerWidget', {
  attributes: {
    content: 'widgetlist',
    location: ['enum', { values: ['header', 'footer'] }],
  },
});

export default BannerWidget;
src/widgets/BannerWidget/BannerWidgetComponent.js
Copy
// Imports

Scrivito.provideComponent('BannerWidget', ({ widget }) => {
  const location = widget.get('location') || 'header';

  return (
    <Scrivito.ContentTag
      tag={ location }
      className="major container 75%"
      content={ widget }
      attribute="content"
    />
  );
});
src/widgets/BannerWidget/BannerWidgetEditingConfig.js
Copy
// Imports

Scrivito.provideEditingConfig('BannerWidget', {
  title: 'Banner',
  description: 'A Directive widget with formatting.',
  attributes: {
    location: {
      title: 'Wiget location',
      description: 'Styled for either top or bottom of the page. Default: top',
      values: [
        { value: 'header', title: 'Top' },
        { value: 'footer', title: 'Bottom' },
      ],
    },
  },
  properties: [
    'location',
  ],
});

This BannerWidget can now be placed on the page and styled based on whether it is towards the top or the bottom of the page. The location portion of this is optional but keeps the layout true to the original Directive design.  

CREATE AN IMAGEICONWIDGET

Create an ImageIconWidget

The next part of the main section is the area where the images and text are. These could be made as several widgets, but as the styling dictates both the image and the text, we will keep it all together. So here here we have several pieces: 
  • an icon
  • an image
  • a headline
  • a text field 

Looking at the HTML and the CSS, we also need a link attribute for linking the image, plus a selector for specifying the widget’s orientation, left or right. Those items will be our widget class attributes:

src/widgets/ImageIconWidget/ImageIconWidgetClass.js
Copy
// Imports

const ImageIconWidget = Scrivito.provideWidgetClass('ImageIconWidget', {
  attributes: {
    icon: 'string',
    image: 'reference',
    headline: 'string',
    content: 'string',
    link: 'link',
    alignment: ['enum', { values: ['left', 'right'] }],
  },
});

export default ImageIconWidget;

Our component looks like this:

src/widgets/ImageIconWidget/ImageIconWidgetComponent.js
Copy
// Other imports

import InPlaceEditingPlaceholder from 'Components/InPlaceEditingPlaceholder';

Scrivito.provideComponent('ImageIconWidget', ({ widget }) => {
  const icon = widget.get('icon');
  const link = widget.get('link');
  const alignment = widget.get('alignment') || 'left';

  return (
    <section className={ `feature ${alignment}` }>
      <Scrivito.LinkTag className={ `image fit icon ${icon}` } to={ link }>
        { (widget.get('image'))
          ? <Scrivito.ImageTag content={ widget } attribute="image" />
          : <InPlaceEditingPlaceholder center={ true }>
              Select an image and icon in the widget properties.
            </InPlaceEditingPlaceholder>
        }
      </Scrivito.LinkTag>
      <div className="content">
        <Scrivito.ContentTag tag="h3" content={ widget }
          attribute="headline"
        />
        <Scrivito.ContentTag tag="p" content={ widget } attribute="content" />
      </div>
    </section>
  );
});

The placeholder code (“InPlaceEditingPlaceholder”) is a nice optional touch to let your editors know how to use the widget when there is no content. Here we have replaced the original link tag with a Scrivito.LinkTag and use interpolation to position and scale the icon which can be set in the widget properties. We can do this as the CSS handles the styling and placement already. Next, the image is included with an Scrivito.ImageTag using a ternary which checks if an image is set and if not, provides the editor clues via the placeholder. Finally, we provide a place to input the headline and text content.

You can now add as many of these widgets to the page as you want; the icon and image placeholder will remind you to open the widget properties to add an icon and an image. In the widget properties, you can also align the image and the icon, and specify where they should link to.

Here’s the editing configuration for the properties dialog of the widget instances:

src/widgets/ImageIconWidget/ImageIconWidgetEditingConfig.js
Copy
// Imports

Scrivito.provideEditingConfig('ImageIconWidget', {
 title: 'Image with icon overlay',
 description: 'A widget with an image.',
 attributes: {
   alignment: {
     title: 'Alignment',
     description: 'Default: Left',
     values: [
       { value: 'left', title: 'Left' },
       { value: 'right', title: 'Right' },
     ],
   },
   image: {
     title: 'Image',
   },
   icon: {
     title: 'Font Awesome icon',
     description: 'E.g. "fa-address-book". See http://fontawesome.io/icons/ for all icons.',
   },
   link: {
     title: 'Link (optional)',
     description: 'The link where this icon should lead.',
   },
 },
 properties: [
   'alignment',
   'image',
   'icon',
   'link',
 ],
 titleForContent: widget => widget.get('headline'),
});

A note about styling

As the Example App has its own styling for many parts, not all the styling will look just right. In these cases it is simply a matter of updating the CSS, or changing the classes for the elements which do not meet your preference. One example is the button.

CHANGE THE BUTTON STYLE

Styling the button

In the second banner widget, when adding a ButtonWidget, you will see that it does not match up with the styling of the Directive example. This is because the ButtonWidget has its own styles, inherited from Bootstrap standard classes. To make it match up, you could create a new Directive button widget, or you can update the current ButtonWidget to work with your CSS. We are going update the current ButtonWidget for this tutorial.

The Directive button is given just a single CSS class, “button”. So all we need to do is hardcode the className in the component since there is only one type of button in this theme. Also, we will clean up the unneeded caret icon and style selection code. The ButtonWidget files will then look like this:

src/widgets/ButtonWidget/ButtonWidgetClass.js
Copy
const ButtonWidget = Scrivito.provideWidgetClass('ButtonWidget', {
  attributes: {
    target: 'link',
    alignment: ['enum', { values: ['left', 'center', 'right'] }],
  },
});

export default ButtonWidget;

In the component file, add the className to the Scrivito.LinkTag component:

src/widgets/ButtonWidget/ButtonWidgetComponent.js
Copy
import InPlaceEditingPlaceholder from 'Components/InPlaceEditingPlaceholder';

const ButtonWidgetComponent = Scrivito.connect(({ widget }) => {
  // ...

  return (
    <Scrivito.LinkTag to={ target } className="button">
      { text }
    </Scrivito.LinkTag>
  );
});

// ...

Finally, In the “ButtonWidgetEditingConfig.js” file, just remove the style attribute and its content as well as the initialContent style value. This will update the button widget to use the styling from the “main.scss” and match the Directive styling.

Footer section

The <div id="footer"> contains a comment form, the social icons, and the copyright notice. We will make this whole section editable as we did with the “main” section.

The Example App’s Homepage class already includes a footer attribute which the App function renders on every page by invoking the Footer component:

src/App.js
Copy
export default function App() {
  return (
    <ErrorBoundary>
      <div>
        <div className="content-wrapper">
          <Navigation />
          <Scrivito.CurrentPage />
          <NotFoundErrorPage />
        </div>
        <Footer />
        <GoogleAnalytics />
        <CurrentPageMetaData />
        <Intercom />
      </div>
    </ErrorBoundary>
  );
}

For this footer to be displayed in the Directive layout on our Homepage instance, we simply replace the original markup with a Scrivito.ContentTag for rendering the footer attribute:

src/objs/Homepage/HomepageComponent.js
Copy
<div id="footer">
  <div className="container 75%">
    <Scrivito.ContentTag
      content={ page }
      attribute="footer"
    />
  </div>
</div>

Since we don’t want the footer to be rendered twice (in the Directive layout and by the Footer component at the page bottom), we’ll exclude the Homepage when rendering the default Footer:

src/components/Footer.js
Copy
function Footer() {
  const root = Scrivito.Obj.root();

// Don't render standard footer if Homepage is the current page or not present 
  if (!Scrivito.currentPage()) {
    return null;
  } else if (!root || (Scrivito.currentPage().id() === root.id())) { 
    return null; }
  return (
    <Scrivito.ContentTag content={ root } attribute="footer" tag="footer" />
  );
}

export default Scrivito.connect(Footer);

Note that you wouldn’t need this exception if all of the Example App’s page types had the Directive layout. In this case, the Footer component could simply render the <div id="footer"> we’ve put in the Homepage component above. You will have to decide what works best for your project.

As with the “header” and “main” sections, editors can now customize the footer by adding widgets to it. For widgets to match the content included in the Directive theme, simply change their styles, or create new widgets as we have exampled above.

Final words

We have illustrated how to integrate a downloaded theme and use it with the Example App. Admittedly, this is not exhaustive, and depending on how you integrate your theme and the options you wish to include in your website, there are some steps we have glossed over, such as the navigation bar at the top. This tutorial is already long enough, and you will find the answers to your questions in our documentation section or by contacting us