--- myst: html_meta: "description": "" "property=og:description": "" "property=og:title": "" "keywords": "" --- # Theme Package III: Customizations In the previous sections we {doc}`prepared our setup ` and {doc}`created our custom theme, adjusted the template and modified and added some Diazo rules `. We will add some final customizations to the theme using CSS and define theme attributes. ## Understanding And Using The Grunt Build System Before we begin customizing the CSS for our theme we have to take a look at the {term}`Grunt` Task Runner. We already have a `Gruntfile.js` - generated by `bobtemplates.plone` - in the top level directory of our theme package. For our example we have to adjust the configuration for the `browserSync:html` and `browserSync:plone` tasks. This allows us to get automatic reloads in the browser when we work on the HTML template (with or without Plone) and change any of the configured files (more on that later in this chapter). ```{code-block} js :emphasize-lines: 55-57,72-75 module.exports = function (grunt) { 'use strict'; grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), // we could just concatenate everything, really // but we like to have it the complex way. // also, in this way we do not have to worry // about putting files in the correct order // (the dependency tree is walked by r.js) less: { dist: { options: { paths: [], strictMath: false, sourceMap: true, outputSourceFiles: true, sourceMapFileInline: true, sourceMapURL: '++theme++ploneconf-theme/less/theme-compiled.less.map', sourceMapFilename: 'less/theme-compiled.less.map', modifyVars: { "isPlone": "false" } }, files: { 'less/theme-compiled.css': 'less/theme.local.less', } } }, postcss: { options: { map: true, processors: [ require('autoprefixer')({ browsers: ['last 2 versions'] }) ] }, dist: { src: 'less/*.css' } }, watch: { scripts: { files: [ 'less/*.less', 'barceloneta/less/*.less' ], tasks: ['less', 'postcss'] } }, browserSync: { html: { bsFiles: { src : [ 'css/*.css', 'js/*.js', '*.html' ] }, options: { watchTask: true, debugInfo: true, online: true, server: { baseDir: "." }, } }, plone: { bsFiles: { src : [ 'less/*.less', 'barceloneta/less/*.less', '*.html', '*.xml' ] }, options: { watchTask: true, debugInfo: true, proxy: "localhost:8080", reloadDelay: 3000, // reloadDebounce: 2000, online: true } } } }); // grunt.loadTasks('tasks'); grunt.loadNpmTasks('grunt-browser-sync'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-less'); grunt.loadNpmTasks('grunt-postcss'); // CWD to theme folder grunt.file.setBase('./src/ploneconf/theme/theme'); grunt.registerTask('compile', ['less', 'postcss']); grunt.registerTask('default', ['compile']); grunt.registerTask('bsync', ["browserSync:html", "watch"]); grunt.registerTask('plone-bsync', ["browserSync:plone", "watch"]); }; ``` At the end of the file we can see some registered {term}`Grunt` tasks. We can use these tasks to control what happens when we run the {command}`grunt` command. By default, {command}`grunt` will run the {command}`compile` task, which compiles the configured Less files into CSS and afterwards transforms the generated CSS with {command}`postcss`: ```console $ grunt Running "less:dist" (less) task >> 1 style sheet created. Running "postcss:dist" (postcss) task >> 1 processed style sheet created. Done, without errors. ``` We can automate this task by running {command}`grunt watch`, which will check the configured Less files for changes. If you change on of the Less files, you will see the output on the command line: ```{code-block} console :emphasize-lines: 4-11 $ grunt watch Running "watch" task Waiting... >> File "less/custom.less" changed. Running "less:dist" (less) task >> 1 style sheet created. Running "postcss:dist" (postcss) task >> 1 processed style sheet created. Done, without errors. ``` There are also two `browserSync` tasks pre-configured. The first one, `bsync` will watch for changes on the template files and reload the browser window for you automatically. This is especially useful when you want to make bigger changes on the template itself, without the Plone related Diazo rules. ```{code-block} console :emphasize-lines: 16 $ grunt bsync Running "browserSync:html" (browserSync) task [Browsersync] Access URLs: --------------------------------------- Local: http://localhost:3000 External: http://192.168.178.30:3000 --------------------------------------- UI: http://localhost:3001 UI External: http://192.168.178.30:3001 --------------------------------------- [Browsersync] Serving files from: . [Browsersync] Watching files... Running "watch" task Waiting... [Browsersync] Reloading Browsers... ``` The next task, `plone-bsync`, will connect to your development Plone instance and open another browser window for you. If you now change one of the configured theme files in the {file}`less` or {file}`barceloneta` folder, the files will be compiled to CSS and your browser window will reload. A change of the HTML template file {file}`index.html` or Diazo rules file {file}`rules.xml` will reload your browser as well: ```{code-block} console :emphasize-lines: 16-26 $ grunt plone-bsync Running "browserSync:plone" (browserSync) task [Browsersync] Proxying: http://localhost:8080 [Browsersync] Access URLs: --------------------------------------- Local: http://localhost:3000 External: http://192.168.178.30:3000 --------------------------------------- UI: http://localhost:3001 UI External: http://192.168.178.30:3001 --------------------------------------- [Browsersync] Watching files... Running "watch" task Waiting... [Browsersync] Reloading Browsers... >> File "less/custom.less" changed. Running "less:dist" (less) task >> 1 style sheet created. Running "postcss:dist" (postcss) task >> 1 processed style sheet created. Done, without errors. Completed in 2.149s at Tue Sep 26 2017 12:56:21 GMT+0200 (CEST) - Waiting... [Browsersync] Reloading Browsers... ``` ```{note} Don't forget to start your Plone instance. ``` ```{note} If you use other ports or IP addresses for your Plone backend, you have to adjust the proxy settings in the {file}`Gruntfile.js` to match your Plone configuration. ``` ## Theme {file}`manifest.cfg` Settings for our theme are declared in the file {file}`manifest.cfg`. It contains settings for CSS files to use for development and production, a CSS file for the content editor TinyMCE and several other optional settings. The one we get from `bobtemplates.plone` looks like this: ```cfg [theme] title = Plone Theme: Ploneconf theme description = A Diazo based Plone theme doctype = rules = /++theme++ploneconf-theme/rules.xml prefix = /++theme++ploneconf-theme enabled-bundles = disabled-bundles = development-css = /++theme++ploneconf-theme/less/theme.less production-css = /++theme++ploneconf-theme/less/theme-compiled.css tinymce-content-css = /++theme++ploneconf-theme/less/theme-compiled.css # development-js = /++theme++ploneconf-theme/js/theme.js # production-js = /++theme++ploneconf-theme/js/theme-compiled.js [theme:overrides] directory = template-overrides [theme:parameters] # portal_url = python: portal.absolute_url() ``` The {file}`development-css` file is used when Plone is running in development mode, otherwise the file defined in {file}`production-css` will be used. The file {file}`tinymce-content-css` tells Plone to load that particular CSS file inside TinyMCE, whenever a TinyMCE rich text field is displayed. ```{hint} After making changes to the file {file}`manifest.cfg`, we need to deactivate/activate the theme for them to take effect. ``` ```{note} You can read more about the {file}`manifest.cfg` and the available options in the [plone.app.theming documentation](https://5.docs.plone.org/external/plone.app.theming/docs/index.html#the-manifest-file). ``` ## Final CSS Customization Our example theme already looks pretty good. But with the help of some CSS we can give it the final touch. We will re-use the definition of the `box` class from the file {file}`theme/css/business-casual.css` for portlets in the left and right portlet column. Replace the example content with the following Less code in the file {file}`custom.less`: ```{code-block} less :emphasize-lines: 36-39 /* Custom Less file that is included from the theme.less file. */ .brand-name { margin-top: 0.5em; } .documentDescription{ margin-top: 1em; } .clearFix{ clear: both; } #left-sidebar { padding-left: 0; } #right-sidebar { padding-right: 0; } #content { label, .label { color: #333; font-size: 100%; } } .pat-autotoc.autotabs, .autotabs { border-width: 0; } #portal-column-one .portlet, #portal-column-two .portlet { .box; } footer { margin-top: 1em; .portlet { padding: 1em 0; margin-bottom: 0; border: 0; background: transparent; box-shadow: none; p { padding: 1em 0; } .portletContent { border: 0; background: transparent; ul { padding-left: 0; list-style-type: none; .portletItem { display: inline-block; &:not(:last-child) { padding-right: 0.5em; margin-right: 0.5em; border-right: 1px solid; } &:hover { background-color: transparent; } a { color: #000; padding: 0; text-decoration: none; &:hover { background-color: transparent; } &::before { content: none; } } } } } } } body.mce-content-body { background-image: none; } ``` (install-ext-packages-with-npm)= ### Install External CSS And JavaScript Libraries With npm And Use Them In Your Theme As our theme is based on `Bootstrap`, we want to install `Bootstrap` with {program}`npm` to have more flexibility, for example to use the Less file of Bootstrap. To do that, we use {program}`npm`, which we already {doc}`installed in the preparations `. ```{note} The following steps are already included in the `bobtemplates.plone` template We show them here for documentation reasons, so you see how you can install and use external packages like `Bootstrap`. ``` To install `Bootstrap` with {program}`npm`, run the following command inside the theme folder: ```shell cd src/ploneconf/theme/theme npm install bootstrap --save ``` The `--save` option will add the `bootstrap` package to the file {file}`package.json` in the theme folder for us. Now we can install all dependencies on any other system by running the following command from inside of our theme folder: ```shell npm install ``` Now that we have installed bootstrap using {command}`npm` we have all the `bootstrap` components available in the subfolder called {file}`node_modules`: ```console tree node_modules/bootstrap/ node_modules/bootstrap/ ├── CHANGELOG.md ├── Gruntfile.js ├── LICENSE ├── README.md ├── dist │   ├── css │   │   ├── bootstrap-theme.css │   │   ├── bootstrap-theme.css.map │   │   ├── bootstrap-theme.min.css │   │   ├── bootstrap-theme.min.css.map │   │   ├── bootstrap.css │   │   ├── bootstrap.css.map │   │   ├── bootstrap.min.css │   │   └── bootstrap.min.css.map │   ├── fonts │   │   ├── glyphicons-halflings-regular.eot │   │   ├── glyphicons-halflings-regular.svg │   │   ├── glyphicons-halflings-regular.ttf │   │   ├── glyphicons-halflings-regular.woff │   │   └── glyphicons-halflings-regular.woff2 │   └── js │   ├── bootstrap.js │   ├── bootstrap.min.js │   └── npm.js ├── fonts │   ├── glyphicons-halflings-regular.eot │   ├── glyphicons-halflings-regular.svg │   ├── glyphicons-halflings-regular.ttf │   ├── glyphicons-halflings-regular.woff │   └── glyphicons-halflings-regular.woff2 ├── grunt │   ├── bs-commonjs-generator.js │   ├── bs-glyphicons-data-generator.js │   ├── bs-lessdoc-parser.js │   ├── bs-raw-files-generator.js │   ├── change-version.js │   ├── configBridge.json │   ├── npm-shrinkwrap.json │   └── sauce_browsers.yml ├── js │   ├── affix.js │   ├── alert.js │   ├── button.js │   ├── carousel.js │   ├── collapse.js │   ├── dropdown.js │   ├── modal.js │   ├── popover.js │   ├── scrollspy.js │   ├── tab.js │   ├── tooltip.js │   └── transition.js ├── less │   ├── alerts.less │   ├── badges.less │   ├── bootstrap.less │   ├── breadcrumbs.less │   ├── button-groups.less │   ├── buttons.less │   ├── carousel.less │   ├── close.less │   ├── code.less │   ├── component-animations.less │   ├── dropdowns.less │   ├── forms.less │   ├── glyphicons.less │   ├── grid.less │   ├── input-groups.less │   ├── jumbotron.less │   ├── labels.less │   ├── list-group.less │   ├── media.less │   ├── mixins │   │   ├── alerts.less │   │   ├── background-variant.less │   │   ├── border-radius.less │   │   ├── buttons.less │   │   ├── center-block.less │   │   ├── clearfix.less │   │   ├── forms.less │   │   ├── gradients.less │   │   ├── grid-framework.less │   │   ├── grid.less │   │   ├── hide-text.less │   │   ├── image.less │   │   ├── labels.less │   │   ├── list-group.less │   │   ├── nav-divider.less │   │   ├── nav-vertical-align.less │   │   ├── opacity.less │   │   ├── pagination.less │   │   ├── panels.less │   │   ├── progress-bar.less │   │   ├── reset-filter.less │   │   ├── reset-text.less │   │   ├── resize.less │   │   ├── responsive-visibility.less │   │   ├── size.less │   │   ├── tab-focus.less │   │   ├── table-row.less │   │   ├── text-emphasis.less │   │   ├── text-overflow.less │   │   └── vendor-prefixes.less │   ├── mixins.less │   ├── modals.less │   ├── navbar.less │   ├── navs.less │   ├── normalize.less │   ├── pager.less │   ├── pagination.less │   ├── panels.less │   ├── popovers.less │   ├── print.less │   ├── progress-bars.less │   ├── responsive-embed.less │   ├── responsive-utilities.less │   ├── scaffolding.less │   ├── tables.less │   ├── theme.less │   ├── thumbnails.less │   ├── tooltip.less │   ├── type.less │   ├── utilities.less │   ├── variables.less │   └── wells.less └── package.json 9 directories, 117 files ``` We will include the "carousel" part and some other bootstrap components which our downloaded theme uses. To do this, we include the required bootstrap components in our {file}`theme.less` file (they were already added from `bobtemplates.plone`): ```{code-block} less :emphasize-lines: 75-86 // theme.less file that will be compiled /* ### PLONE IMPORTS ### */ @barceloneta_path: "barceloneta/less"; // Core variables and mixins @import "@{barceloneta_path}/fonts.plone.less"; @import "@{barceloneta_path}/variables.plone.less"; @import "@{barceloneta_path}/mixin.prefixes.plone.less"; @import "@{barceloneta_path}/mixin.tabfocus.plone.less"; @import "@{barceloneta_path}/mixin.images.plone.less"; @import "@{barceloneta_path}/mixin.forms.plone.less"; @import "@{barceloneta_path}/mixin.borderradius.plone.less"; @import "@{barceloneta_path}/mixin.buttons.plone.less"; @import "@{barceloneta_path}/mixin.clearfix.plone.less"; // @import "@{barceloneta_path}/mixin.gridframework.plone.less"; //grid Bootstrap @import "@{barceloneta_path}/mixin.grid.plone.less"; //grid Bootstrap @import "@{barceloneta_path}/normalize.plone.less"; @import "@{barceloneta_path}/print.plone.less"; @import "@{barceloneta_path}/code.plone.less"; // Core CSS @import "@{barceloneta_path}/grid.plone.less"; @import "@{barceloneta_path}/scaffolding.plone.less"; @import "@{barceloneta_path}/type.plone.less"; @import "@{barceloneta_path}/tables.plone.less"; @import "@{barceloneta_path}/forms.plone.less"; @import "@{barceloneta_path}/buttons.plone.less"; @import "@{barceloneta_path}/states.plone.less"; // Components @import "@{barceloneta_path}/breadcrumbs.plone.less"; @import "@{barceloneta_path}/pagination.plone.less"; @import "@{barceloneta_path}/formtabbing.plone.less"; //pattern @import "@{barceloneta_path}/views.plone.less"; @import "@{barceloneta_path}/thumbs.plone.less"; @import "@{barceloneta_path}/alerts.plone.less"; @import "@{barceloneta_path}/portlets.plone.less"; @import "@{barceloneta_path}/controlpanels.plone.less"; @import "@{barceloneta_path}/tags.plone.less"; @import "@{barceloneta_path}/contents.plone.less"; // Patterns @import "@{barceloneta_path}/accessibility.plone.less"; @import "@{barceloneta_path}/toc.plone.less"; @import "@{barceloneta_path}/dropzone.plone.less"; @import "@{barceloneta_path}/modal.plone.less"; @import "@{barceloneta_path}/pickadate.plone.less"; @import "@{barceloneta_path}/sortable.plone.less"; @import "@{barceloneta_path}/tablesorter.plone.less"; @import "@{barceloneta_path}/tooltip.plone.less"; @import "@{barceloneta_path}/tree.plone.less"; // Structure @import "@{barceloneta_path}/header.plone.less"; @import "@{barceloneta_path}/sitenav.plone.less"; @import "@{barceloneta_path}/main.plone.less"; @import "@{barceloneta_path}/footer.plone.less"; @import "@{barceloneta_path}/loginform.plone.less"; @import "@{barceloneta_path}/sitemap.plone.less"; // Products @import "@{barceloneta_path}/event.plone.less"; @import "@{barceloneta_path}/image.plone.less"; @import "@{barceloneta_path}/behaviors.plone.less"; @import "@{barceloneta_path}/discussion.plone.less"; @import "@{barceloneta_path}/search.plone.less"; /* ### END OF PLONE IMPORTS ### */ /* ### UTILS ### */ // import bootstrap files: @bootstrap_path: "node_modules/bootstrap/less"; @import "@{bootstrap_path}/variables.less"; @import "@{bootstrap_path}/mixins.less"; @import "@{bootstrap_path}/utilities.less"; @import "@{bootstrap_path}/grid.less"; @import "@{bootstrap_path}/type.less"; @import "@{bootstrap_path}/forms.less"; @import "@{bootstrap_path}/navs.less"; @import "@{bootstrap_path}/navbar.less"; @import "@{bootstrap_path}/carousel.less"; /* ### END OF UTILS ### */ @import (less) "../css/business-casual.css"; // include our custom css/less @import "custom.less"; ``` Here you can see how we include the resources like `@import "@{bootstrap_path}/carousel.less";` in our Less file. But before they can be used, it is important to add the path to the less files: ```{code-block} css :emphasize-lines: 2 // import bootstrap files: @bootstrap_path: "node_modules/bootstrap/less"; ``` This defines the path to the `bootstrap` files, so that we can use it in all bootstrap includes. ```{note} Don't forget to run {command}`grunt compile` after you changed any of the Less files or use {command}`grunt watch` to do this automatically after every file change. ``` ## More Diazo And plone.app.theming Details For more information on how to build a Diazo based theme look at [the diazo documentation](http://docs.diazo.org/en/latest/) and [the plone.app.theming manual](https://5.docs.plone.org/external/plone.app.theming/docs/index.html). In the next part we will {doc}`take a look at template customizations ` for our theme and {ref}`make the slider dynamic and let users change the pictures for the slider `.