Skip to content

Design Reference

Oliver Fabel edited this page Jan 5, 2023 · 24 revisions

This chapter acts as a reference for design creation. It covers all from specification to templating and assets management. Be aware, that this chapter does not cover the template features in detail. If you want to know more about templating, we refer to the BSI CX design documentation. For a quick overview about what features you can use in which version of BSI CX you can checkout the feature matrix.

Specification

One mandatory and very important artifact in a design ZIP file is the design.json file (or design.properties in the legacy format). To generate a design.json trough this build, you have to place a design.js file in your template root folder. This has to be a CommonJS module, exporting an object in the required structure.

Here you have two options: Creating the structure by hand or using the builder classes. We recommend you to use the builder classes, because you have IntelliSense support while writing your code and don't have to know the exact names of all properties.

To create builder objects, you can use the cx factory object. Just require the cx constant and use the properties to create new builder instances of all types.

Design

The design object specifies the whole design: What content element groups and elements are available, what styles can be used, what editor configs are available, and many more. Take a look at the following example:

const {cx, Locale} = require('@bsi-cx/design-build');

module.exports = cx.design
  .withTitle('BSI Landingpage')
  .withAuthor('John Doe')
  .withDate('18.08.2021')
  .withPreviewImage(require('./preview.png'))
  .withDefaultLocale(Locale.EN)
  .withLocales(
    Locale.EN,
    Locale.DE,
    Locale.DE_DE,
    Locale.DE_CH)
  .withContentElementGroups(
    cx.contentElementGroup
      .withGroupId('content')
      .withLabel('Content')
      .withContentElements(
        require('./content-elements/content/title'),
        require('./content-elements/content/text')))
  .withStyleConfigs(
    require('./styles/background-color'),
    require('./styles/text-color'))
  .withHtmlEditorConfigs(
    require('./html-editor-configs/full'),
    require('./html-editor-configs/normal'))
  .withNLS(...require('./nls'));

Since this is proper JavaScript you can take full advantage of all language features. For example, it is a good idea to split your design specification into several files and stack them together using require(file). So it's recommended to create a separate folder for each content element, containing the specification, the template, stylesheets and possible other element specific assets.

Content Element Group

Use the cx builder factory to create a new content element group builder objects:

module.exports = cx.contentElementGroup
  .withGroupId('content')
  .withLabel('Inhalt')
  .withContentElements(
    require('./content-elements/content/title'),
    require('./content-elements/content/text'));

Content Element

Use the cx builder factory to create new content element builder objects:

require('./styles.less');

const {cx, Icon} = require('@bsi-cx/design-build');

module.exports = cx.contentElement
  .withElementId('content-title')
  .withIcon(Icon.HEADING)
  .withLabel('Title')
  .withFile(require('./template.hbs.twig'))
  .withParts(
    cx.part.plainText
      .withLabel('Title'));

Content Element Part

You can also create content element part builder objects using the cx.part factory. Take a look at the following example:

const {cx, Icon} = require('@bsi-cx/design-build');

module.exports = cx.contentElement
  .withElementId('content-image-with-text')
  .withIcon(Icon.HEADING)
  .withLabel('Image with Text')
  .withFile(require('./template.twig'))
  .withParts(
    cx.part.image
      .withLabel('Image'),
    cx.part.plainText
      .withLabel('Text'));

Order and Identifiers

Be aware, that the part definitions have to be in the same order they appear in the template markup. In large and complex elements, this can be very tricky. To solve this problem, you can give a unique identifier to each part. Set the identifier with the withId(id) method and use the same identifier as value in the data-bsi-element-part attribute in your template. It's strongly recommended using a UUID as unique identifier. You can always create new UUIDs using duckduckgo.com or any other UUID generator. Take a look at the following example:

const {cx, Icon} = require('@bsi-cx/design-build');

module.exports = cx.contentElement
  .withElementId('content-teaser')
  .withIcon(Icon.HEADING)
  .withLabel('Teaser')
  .withFile(require('./template.hbs.twig'))
  .withParts(
    cx.part.formattedText
      .withId('22235837-cf9e-49e9-8918-294b22e2854d')
      .withLabel('Text')
      .withHtmlEditorConfig(full),
    cx.part.plainText
      .withId('6849e817-c96d-42aa-a0a8-0d2f2410197e')
      .withLabel('Subtitle'),
    cx.part.plainText
      .withId('cc246a71-e563-412b-9a7e-9c3fc0475f2e')
      .withLabel('Title'));

The corresponding template to the code snippet above would look like this:

<section data-bsi-element="content-teaser" class="element content-teaser">
  <h1 data-bsi-element-part="cc246a71-e563-412b-9a7e-9c3fc0475f2e">
    Lorem ipsum dolor sit amet
  </h1>
  <h2 data-bsi-element-part="6849e817-c96d-42aa-a0a8-0d2f2410197e">
    Vestibulum pulvinar ex eget magna porttitor
  </h2>
  <div data-bsi-element-part="22235837-cf9e-49e9-8918-294b22e2854d">
    Pellentesque iaculis ornare rhoncus. Proin id iaculis odio, id euismod elit.
  </div>
</section>

The rendered template in the design ZIP would then look like this:

<section data-bsi-element="content-teaser" class="element content-teaser">
  <h1 data-bsi-element-part="plain-text">
    Lorem ipsum dolor sit amet
  </h1>
  <h2 data-bsi-element-part="plain-text">
    Vestibulum pulvinar ex eget magna porttitor
  </h2>
  <div data-bsi-element-part="formatted-text">
    Pellentesque iaculis ornare rhoncus. Proin id iaculis odio, id euismod elit.
  </div>
</section>

Custom Content Element Parts

In case you hava to use a custom/non-standard or project specific content element part, use a raw part:

const {cx} = require('@bsi-cx/design-build');

module.exports = cx.contentElement
  .withElementId('chart')
  .withLabel('Chart')
  .withFile(require('./template.twig'))
  .withParts(
    cx.part.plainText
      .withId('6849e817-c96d-42aa-a0a8-0d2f2410197e')
      .withLabel('Subtitle'),
    cx.part.raw('chart')
      .withId('22235837-cf9e-49e9-8918-294b22e2854d')
      .withLabel('Chart')
      .withProperty('type', 'pie')
      .withProperty('data', [24, 32, 42]));

You can pass your own properties by using the withProperty(name, value) method. The value must be JSON serializable.

Dropzones

Dropzones can be specified on design level and on content element level. Dropzones inside includes will be specified on design level. To define a dropzone inside your template, it's sufficient to place a data-bsi-dropzone attribute containing a UUID:

<div data-bsi-dropzone="93a899b6-4435-4399-b352-61e347f79038">
  {% include '../../title/h1/template.twig' %}
</div>

Regardless the level this dropzone is placed, you can always use the .withDropzones(...dropzones) method on the current builder object:

.withDropzones(
  cx.dropzone
    .withDropzone('93a899b6-4435-4399-b352-61e347f79038')
    .withMaxAllowedElements(1) // this is optional
    .withCopyAllowed(false) // this is optional
    .withAllowedElements(
      require('./content-elements/title/h1'),
      require('./content-elements/content/text')))

The data-bsi-dropzone-* attributes will be placed and filled automatically during the build.

Extending Dropzones

When you are using one of our web or email starter templates, you might want to add further elements to an existing dropzone. This can be done by using the withExtendedDropzone(id, ...elements) method on the design, content element and include builder objects:

.withExtendedDropzone(
  '93a899b6-4435-4399-b352-61e347f79038', // this is the ID of the dropzone you like to extend
  require('./content-elements/title/h1'), // these elements will be appended to the list of allowed elements
  require('./content-elements/content/text'))

Style Configuration

You can create style configuration builder objects using the cx.style factory. For the corresponding CSS class objects you can use the cx.cssClass factory:

const {cx} = require('@bsi-cx/design-build');

module.exports = cx.style
  .withIdentifier('background-color')
  .withLabel('Background Color')
  .withCssClasses(
    cx.cssClass
      .withLabel('Black')
      .withCssClass('black-background'),
    cx.cssClass
      .withLabel('Blue')
      .withCssClass('blue-background'),
    cx.cssClass
      .withLabel('Red')
      .withCssClass('red-background'),
    cx.cssClass
      .withLabel('White')
      .withCssClass('white-background'));

There is also a shortcut available to create a style configuration. The above configuration could be simplified using the cx.h helper factory:

const {cx} = require('@bsi-cx/design-build');

module.exports = cx.h.style(
  'background-color',
  'Background Color',
  cx.h.cssClass('black-background','Black'),
  cx.h.cssClass('blue-background','Blue'),
  cx.h.cssClass('red-background','Red'),
  cx.h.cssClass('white-background','White'));

To apply a style configuration to a content element, just use the withStyleConfigs(...styleConfigs) method on the content element builder object:

const {cx, Icon} = require('@bsi-cx/design-build');

module.exports = cx.contentElement
  .withElementId('content-title')
  .withIcon(Icon.HEADING)
  .withLabel('Title')
  .withStyleConfigs(
    require('../../../styles/text-color'),
    require('../../../styles/background-color'))
  .withFile(require('./template.hbs.twig'))
  .withParts(
    cx.part.plainText
      .withLabel('Title'));

HTML Editor Configuration

You can define a HTML editor configuration for the formatted-text content element part by using the cx.htmlEditorConfig factory:

const {EnterMode, Feature, Format, FontSizeUnit, cx} = require('@bsi-cx/design-build');

module.exports = cx.htmlEditorConfig
  .withIdentifier('extended')
  .withFeatures(
    Feature.BOLD,
    Feature.ITALIC,
    Feature.UNDERLINE,
    Feature.STRIKE_THROUGH,
    Feature.SUBSCRIPT,
    Feature.SUPERSCRIPT,
    Feature.FONT_SIZE,
    Feature.LINE_HEIGHT,
    Feature.TEXT_COLOR,
    Feature.BACKGROUND_COLOR,
    Feature.ALIGN_LEFT,
    Feature.ALIGN_CENTER,
    Feature.ALIGN_RIGHT,
    Feature.ALIGN_JUSTIFY,
    Feature.FORMAT_OL,
    Feature.FORMAT_UL,
    Feature.OUTDENT,
    Feature.INDENT,
    Feature.PARAGRAPH_FORMAT,
    Feature.QUOTE,
    Feature.SPECIAL_CHARACTERS,
    Feature.EMOTICONS,
    Feature.INSERT_LINK)
  .withTextColors('#0082a1', '#bb9f84', '#fe9915', '#ffffff', '#666666')
  .withBackgroundColors('#0082a1', '#bb9f84', '#fe9915', '#ffffff', '#666666')
  .withFormats(Format.P, Format.H1, Format.H2, Format.H3, Format.H4, Format.H5, Format.H6, Format.PRE)
  .withFontSizes(8, 9, 10, 11, 12, 14, 16, 18, 24, 30, 36, 48, 72)
  .withFontSizeUnit(FontSizeUnit.PX)
  .withFontSizeDefault(16)
  .withLineHeights(1, 1.15, 1.5, 2)
  .withEnterMode(EnterMode.P);

To apply the configuration to a formatted-text content element part, you can use the withHtmlEditorConfig(config) method on the desired content element part builder object:

const {Icon, cx} = require('@bsi-cx/design-build');

module.exports = cx.contentElement
  .withElementId('Text')
  .withIcon(Icon.TEXT)
  .withLabel('Text')
  .withFile(require('./template.hbs.twig'))
  .withParts(
    cx.part.formattedText
      .withLabel('Text')
      .withHtmlEditorConfig(require('../../../html-editor-configs/extended')));

Websites

The following example illustrates how you can configure a website:

const {cx} = require('@bsi-cx/design-build');

module.exports = cx.design
  // ...
  .withWebsite(
    cx.website
      .withMaxNavigationLevel(2)
      .withIncludes(
        cx.pageInclude
          .withName('Template for new content')
          .withEditable(true)
          .withFile(require('./includes/page.html')),
        cx.include
          .withIdentifier('footer')
          .withName('Footer')
          .withEditable(true)
          .withFile(require('./includes/footer.twig')),
        cx.include
          .withIdentifier('header')
          .withName('Header')
          .withEditable(true)
          .withFile(require('./includes/header.twig')),
        cx.include
          .withIdentifier('navigation')
          .withName('Navigation')
          .withEditable(false)
          .withFile(require('./includes/navigation.hbs'))))
  // ...

Translations

If you want to translate certain aspects of your design, you must specify some NLS objects. See the following example for details:

const {cx, Locale} = require('@bsi-cx/design-build');

module.exports = [
  cx.nls
    .withIdentifier('action')
    .withTranslations(
      cx.translation
        .withLocale(Locale.WILDCARD)
        .withTranslation('Action'),
      cx.translation
        .withLocale(Locale.DE)
        .withTranslation('Aktion')),
  cx.nls
    .withIdentifier('banner')
    .withTranslations(
      cx.translation
        .withLocale(Locale.WILDCARD)
        .withTranslation('Please support our friends!'),
      cx.translation
        .withLocale(Locale.DE)
        .withTranslation('Bitte unterstütze unsere Freunde!'))
];

There is also a cx.h shortcut available for faster and simpler NLS object creation:

const {cx, Locale} = require('@bsi-cx/design-build');

module.exports = [
  cx.h.nls(
    'action',
    cx.h.t('action'),
    cx.h.t('de', 'Aktion'),
    cx.h.t(Locale.DE_CH, 'Aktion')),
  cx.h.nls(
    'contact',
    cx.h.t('contact'),
    cx.h.t('de', 'Kontakt'),
    cx.h.t(Locale.DE_CH, 'Kontakt'))
];

It is recommended to outsource your NLS array to a separate Java Script file. However, you may find it too complicated to define your translations using Java Script. Sure you can also use JSON (or a CommonJS module, exporting an equivalent object) for this:

{
  "action": {
    "*": "Action",
    "de": "Aktion"
  }
}

Once you have written your translation file, you must register it in your design builder object:

const {cx, Locale} = require('@bsi-cx/design-build');

module.exports = cx.design
  .withDefaultLocale(Locale.EN)
  .withLocales(
    Locale.EN,
    Locale.DE,
    Locale.DE_DE,
    Locale.DE_CH)
  // ...
  .withNLS(...require('./nls')); // register Java Script NLS definitions (mind the spread operator ...)

If you defined your translations as JSON or plain Java Script object, you have to register them as raw value:

const {cx, Locale} = require('@bsi-cx/design-build');

module.exports = cx.design
  .withDefaultLocale(Locale.EN)
  .withLocales(
    Locale.EN,
    Locale.DE,
    Locale.DE_DE,
    Locale.DE_CH)
  // ...
  .withRawNLS(require('./nls')); // register plain NLS definitions (no spread operator required)

Using Raw Values

Most of the with... methods also have a withRaw... pendant. Using the raw method allows you to set a property without the use of any builder objects. This may can be useful if you want to use custom or bleeding edge features. Be aware, that if you decide to set a raw value, you can't use any builder objects inside the structure of that raw value (a workaround for this would be invoking the build() method on the nested builder object).

The following example illustrates the use of raw values for styles and HTML editor configurations:

const {Icon, cx} = require('@bsi-cx/design-build');

module.exports = cx.contentElement
  .withElementId('Text')
  .withIcon(Icon.TEXT)
  .withLabel('Text')
  .withFile(require('./template.hbs.twig'))
  .withRawStyleConfigs('background-color','text-color')
  .withParts(
    cx.part.formattedText
      .withLabel('Text')
      .withRawHtmlEditorConfig('full'));

Be aware, that you also must register your style and HTML editor configurations in the design specification builder object:

const {cx} = require('@bsi-cx/design-build');

module.exports = cx.design
  // ...
  .withStyleConfigs(
    require('./styles/background-color'),
    require('./styles/text-color'))
  .withHtmlEditorConfigs(
    require('./html-editor-configs/full'))
  // ...

Compatible Versions and Designs

Some features of CX are introduced in later versions and not all features are available in all design types. The design build can exclude incompatible features automatically, but you can also specify compatible versions and design types for your template manually. To do so, use the withMinVersion(version), withMaxVersion(version) and withAllowedTypes(...types) builder methods:

const {cx, Version, DesignType} = require('@bsi-cx/design-build');

module.exports = cx.contentElement
  // ...
  .withMinVersion(Version.CX_1_3)
  .withMaxVersion(Version.CX_22_0) // max version is not mandatory
  .withAllowedTypes(DesignType.WEBSITE)
  // ...

If you have to implement more complex logic regarding the current target version or design type, you can use the Version.TARGET and DesignType.TARGET constants:

const {Version, DesignType} = require('@bsi-cx/design-build');

if(Version.TARGET >= Version.CX_1_3 && DesignType.TARGET === DesignType.WEBSITE) {
  // do something when target version is >= CX 1.3 and design type is website
} else {
  // do something else
}

You can also check for the target version and design type inside your Twig templates:

{% if cx.version.TARGET >= cx.version.CX_1_3 and cx.design.TARGET == cx.design.WEBSITE %}
  {# ... #}
{% endif %}

The current target version is available as cx.version.TARGET. To compare the version, use the constants from cx.version: STUDIO_1_0, STUDIO_1_1, STUDIO_1_2, CX_1_3 and CX_22_0. Besides, the versions are also printable:

<p>Built for BSI CX {{ cx.version.TARGET.toString() }}</p>

The current target design type is available as cx.design.TARGET. To compare the design type, use the constants from cx.design: WEBSITE, LANDINGPAGE and EMAIL.

Properties

To parameterize your design build, it is possible to pass properties to your underlying design. The properties will be available inside your stylesheets (Less, Sass), Twig templates and template definitions during the build process. The properties file itself is a simple CommonJS module, exporting a plain Java Script object with keys and values. The values can also be nested objects.

const {css} = require('@bsi-cx/design-build');

module.exports = {
  build: Date.now(),
  contact: require('./contact'),
  styles: {
    secondaryColor: css.color('#abc'),
    primaryColor: '#ff00ff',
    background: css.url(__dirname, 'content-elements', 'content', 'title', 'placeholder.png'),
    foreground: css.dataUri(__dirname, 'content-elements', 'content', 'title', 'placeholder.png'),
    flag: true,
    margin: css.number('10px')
  }
};

Since Webpack does not interpret the properties file it's not possible to import assets with require. You have to use the css helper functions to do so. Besides, using the helper functions has other advantages:

  • You can describe what kind of property this is (color, url, numeric value with dimension).
  • Because the helper functions return an object you can access the stored value in different formats.
  • You can use Less and Sass functions to modify color and numeric values.

To access the properties within your templates you need to use the properties object:

<div style="background-image: {{ properties.styles.background }}"></div>

Within your stylesheets, you need to use the bsiProperty accessor function:

$flag: bsiProperty('styles.flag');

@if $flag {
  .element.content-text {
    color: #ff00ff;
    content: bsiProperty('styles.secondaryColor');
    background: bsiProperty('styles.background');
    margin: bsiProperty('styles.margin');
  }
}
@margin: bsiProperty('styles.margin');

.element.content-title {
  > h2 {
    background-color: bsiProperty('styles.secondaryColor')
  }

  background: bsiProperty('styles.background') repeat-x;
  margin: @margin;
}

It is considered good practice to store the accessed property's value in a variable instead of inlining the bsiProperty function call.

The bsiProperty function also accepts a fallback value as a second parameter. If you don't provide a fallback value and the property does not exist in the properties model, an error will occur during the design build.

$color: bsiProperty('unknown', #f0f);
@color: bsiProperty('unknown', #f0f);

Since there is no bsiProperty accessor function for Twig templates, you simply use the ternary operator:

{% set color = properties.unknown ?: '#ff00ff' %}

It is also possible to use the bsiProperty function inside your template definition JavaScript files:

const {cx, Locale, bsiProperty} = require('@bsi-cx/design-build');

const author = bsiProperty('author');
const date = bsiProperty('date');

module.exports = cx.design
  .withTitle('BSI Landingpage')
  .withAuthor(author)
  .withDate(date)
  .withPreviewImage(require('./preview.png'))
  .withDefaultLocale(Locale.EN)
  .withLocales(Locale.EN)
  .withContentElementGroups(
    cx.contentElementGroup
      .withGroupId('content')
      .withLabel('Content')
      .withContentElements(...require('./content-elements/content')));

Colors

To define a property as a color value, use css.color helper:

const {css} = require('@bsi-cx/design-build');

module.exports = {
  primaryColor: css.color('#ff00ff'), // standard hex encoding
  secondaryColor: css.color('#abc'), // shorthand hex encoding
  tertiaryColor: css.color('#ff00ffaa'), // hex encoding with additonal alpha channel
  backgroundColor: css.color('rgb(255,128,0)'), // RGB encoding
  backgroundColor: css.color('rgba(255,128,0,0.5)') // RGB with alpha channel encoding
};

Once you have defined the color in one of the acceptable forms above, you can use it in your Twig templates:

<div style="color: {{ properties.primaryColor }}; background: {{ properties.secondaryColor.rgba }};">
  <!-- ... -->
</div>

Since primaryColor is a color object, you can access its attributes:

  • .rgba gives you the specified color in the rgba(red,green,blue,alpha) format.
  • .hex gives you the specified color in the #rrggbb or #rrggbbaa format.

If you don't access an attribute, you will get the color in the hex format.

Within your stylesheets, you can use color properties in the following way:

$color: bsiProperty('primaryColor');
$backgroundColor: bsiProperty('secondaryColor');

h1 {
  color: darken($color, 10%);
}

.header {
  background-color: $backgroundColor;
}

URLs

To define a property as a URL, use the css.url or css.dataUri helpers:

const {css} = require('@bsi-cx/design-build');

module.exports = {
  background: css.url(__dirname, 'content-elements', 'content', 'title', 'placeholder.png'),
  foreground: css.dataUri(__dirname, 'content-elements', 'content', 'title', 'placeholder.png'),
};

Once you have defined the URL in one of the acceptable forms above, you can use it in your templates:

<div style="background-image: {{ properties.background.externalRefCss }}"></div>
<div style="background-image: {{ properties.foreground }}"></div>

Since background and foreground are objects, you can directly access their attributes:

  • .externalRef gives you the path to the resource, e.g. ../assets/placeholder-04f7dbccedf0c8a7dd7d.png
  • .inlineRef gives you a data URI of the resource.
  • .inlineRefCss gives you the CSS url() representation with a data URI.
  • .externalRefCss gives you the CSS url() representation with a path to the resource.
  • .css gives you the CSS url() representation. If you used css.dataUri() to construct the object, you will receive a data URI.

If you don't access an attribute, you will receive the value of .css.

Within your stylesheets, you can use URL properties in the following way:

$background: bsiProperty('background');

.background {
  background-image: $background;
}

Numeric Values

To define a property as a numeric value with a unit, use the css.number helper:

const {css} = require('@bsi-cx/design-build');

module.exports = {
  margin: css.number('10px'),
  padding: css.number('1rem'),
  width: css.number('80%')
};

All standard CSS units are allowed. The use in templates and stylesheets is straightforward and similar to the other properties.

Templates

The HTML and Handlebars Templates this build produces, can be written in plain HTML or Twig. Using Twig has many advantages when it comes to code reuse and parameterizing your design. Coding Twig is simple and straightforward. This chapter gives you a quick overview on this package's custom Twig functions and how to use them.

Include Assets

To include an asset in your template, you can use the bsi_cx_asset(file, ?inline) function:

<figure data-bsi-element="32f88ae6-f475-4fe5-90c0-b48f7355bd8d">
  <img src="{{ bsi_cx_asset('./placeholder.png') }}" alt="Vestibulum pulvinar ex eget magna porttitor."/>
  <figcaption data-bsi-element-part="3065924d-3847-43e4-9527-5c8f3485a60c">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  </figcaption>
</div>

The function accepts two parameters:

  • path is mandatory and specifies the path to the asset to include, relative to the current template.
  • inline is optional and can be set to true to inline the specified asset as data URI. The default is false.

The function returns a string, which will be a URL or a data URI (if inline is set to true) of the specified asset.

Include Stylesheets

The build uses the mini-css-extract-plugin to extract all produced CSS to a single file. You should include this CSS file in the root template of your design. To do so, you can use the bsi_cx_css_href() or bsi_cx_css_inline() function. Use bsi_cx_css_href() to place a URL to the CSS file:

<link rel="stylesheet" href="{{ bsi_cx_css_href() }}"/>

Use bsi_cx_css_inline() to directly include the content of the CSS file inside your template:

<style>
  {{ bsi_cx_css_inline() }}
</style>

The bsi_cx_css_inline() function might be especially useful for email templates.

Include JavaScript

Ordinary JavaScript files can be included like normal assets by using the bsi_cx_asset function. In case of JavaScript modules, you need to use the following functions:

  • Use bsi_cx_js_module_href() or bsi_cx_js_module_inline() to include a single module.
  • Use bsi_cx_js_module_missing_chunks_import() or bsi_cx_js_module_missing_chunks_inline() to include the related chunks.
  • Use bsi_cx_js_module_runtime_href() or bsi_cx_js_module_runtime_inline() to include the related runtime chunk.

The *_href() functions will return a URL, while the *_inline() functions return the file's content. The following example should illustrate the usage:

<!-- runtime -->
<script src="{{ bsi_cx_js_module_runtime_href() }}" defer="defer" data-bsi-remove-if="draft"></script>
<!-- modules -->
<script src="{{ bsi_cx_js_module_href('foo') }}" defer="defer" data-bsi-remove-if="draft"></script>
<script src="{{ bsi_cx_js_module_href('bar') }}" defer="defer" data-bsi-remove-if="draft"></script>
<!-- chunks -->
{{ bsi_cx_js_module_missing_chunks_import() }}

This will result in the following HTML:

<!-- runtime -->
<script src="./shared/runtime.js" defer="defer" data-bsi-remove-if="draft"></script>
<!-- modules -->
<script src="./modules/foo-e3b14801095c68975223.js" defer="defer" data-bsi-remove-if="draft"></script>
<script src="./modules/bar-0d874ea6e842d60b50c5.js" defer="defer" data-bsi-remove-if="draft"></script>
<!-- chunks -->
<script src="./vendors/85a0aa6070182a717e14.js" defer="defer" data-bsi-remove-if="draft"></script>
<script src="./vendors/76f6031a12ddc80a264b.js" defer="defer" data-bsi-remove-if="draft"></script>
<script src="./shared/59b150d5ab88386f4d73.js" defer="defer" data-bsi-remove-if="draft"></script>

As you can see, bsi_cx_js_module_missing_chunks_import() directly creates the required script tags. When using JavaScript modules, you should always include the runtime and missing chunks with the corresponding functions.

Sample Text Provider

The BSI CX build has a builtin Lorem Ipsum sample text generator. Just use the bsi_cx_lorem(?words) Twig function. The function accepts one optional parameter, which defines the amount of words the output should contain. Without any limitation, the function will output the whole internal buffer (about 750 characters).

<p>{{ bsi_cx_lorem(9) }}</p>
<p>Vivamus dapibus lobortis risus, nec fringilla lectus consectetur at.</p>

Twig and Handlebars

If you have to use Handlebars inside your Twig templates, keep the following things in mind:

  • Use .hbs.twig as file extension. This will trigger the build to produce a .hbs file.
  • Wrap your Handlebars code with {% verbatim %} and {% endverbatim %}.

See the following example for details:

<div data-bsi-element="content-text" class="element content-text">
  <p data-bsi-element-part="plain-text">
    {% verbatim %}{{bsi.nls 'text_'}}{% endverbatim %}
  </p>
</div>
Clone this wiki locally