Drupal 8 theming

advertisement
Creating a Drupal 8 theme
from scratch
Devsigner 2015
(Hashtag #devsigner on the internets)
So you wanna build
a website…
And you want
people to like it…
Step 1:
Make it pretty
Step 2:
Don’t make it ugly
So…
Learn you an HTML!
Learn you a CSS!
Learn you a jQuery!
BUT WAIT
THERE’S MORE
You’re using a CMS?
Drupal, you say?
You need a theme
What is a theme?
What is a theme?
•
Themes control appearance
•
Themes don’t provide functionality
•
Usually specific to a single site
•
Contains HTML (templates), CSS, Javascript, and a
little PHP
What about Drupal 8?
Changes for Drupal 8
•
Drupal 8 loves abstractions
•
Drupal 8 loves themers and front end developers
•
Kinda
•
Documentation is still somewhat sparse
•
Drupal 8 is still changing
Setup for Drupal 8
•
Make sure you use newer versions of Drush, 8.x
minimum
•
Clear the cache, a lot
•
Disable page cache, and CSS/JS aggregation, these are
now enabled by default
Let’s get started
Themes now go in
themes directory
No more sites/all/
themes
Add a
themename.info.yml file
What is .yml?
YAML…
•
YAML Ain’t Markup Language
•
Readable by machines
•
But also readable by humans, which is cool since
most front end developers are humans
key: value
strings: "Quoted for special characters or spaces"
collection:
- "Indented with spaces"
- "Start with dash (-)"
collections:
can: "specify key"
nested:
items: "Work as well"
another: "See?"
boolean: true
or: false
•
Make a directory for your theme with it’s name
•
Add a themename.info.yml file
demo.info.yml
name: Demo
type: theme
description: 'A demo theme.'
core: 8.x
Let’s install it!
Wow, that looks like
the web in like 1996
Want to remove some of
Drupal’s default
stylesheets?
demo.info.yml
name: Demo
type: theme
description: 'A demo theme.'
core: 8.x
libraries:
- demo/global-styling
stylesheets-remove:
- core/assets/vendor/normalize-css/normalize.css
We need some CSS
No more drupal_add_css()
or drupal_add_js()
Libraries
Drupal 8 Libraries
•
Drupal 8 is all about abstraction and reuse, even with
front end code
•
Libraries represent external components
•
Libraries can have CSS, Javascript, and dependencies
•
Examples: normalize, html5shiv, jQuery, modernizer,
backbone, underscore, CKEditor, etc
Libraries are defined in
themename.libraries.yml
demo.libraries.yml
global-styling:
version: 8.x-1.x
css:
theme:
css/styles.css: {}
demo.info.yml
name: Demo
type: theme
description: 'A demo theme.'
core: 8.x
libraries:
- demo/global-styling
Specify additional
options, such as media
demo.libraries.yml
global-styling:
version: 8.x-1.x
css:
theme:
css/styles.css: {}
css/print.css: { media: print }
Let’s add some
Javascript
demo.libraries.yml
global-styling:
version: 8.x-1.x
css:
theme:
css/styles.css: {}
css/print.css: { media: print }
js:
js/script.js: {}
dependencies:
- core/jquery
- core/drupal
Note the dependencies,
jQuery is no longer included
on every page by default
What else can you
do with libraries?
demo.libraries.yml
global-styling:
version: 8.x-1.x
css:
theme:
css/styles.css: {}
landing-pages:
version: 8.x-1.x
css:
theme:
css/landing-pages.css: {}
How to include additional
libraries?
•
Specify globally in theme info.yml file
•
Add in preprocess function with #attached
•
Add via Twig function
•
Add to render array…
demo.info.yml
name: Demo
type: theme
description: 'A demo theme.'
core: 8.x
libraries:
- demo/global-styling
- demo/landing-pages
Where do I put
preprocess functions?
Wait, Drupal 8 still has
preprocess functions????
Adding a
themename.theme file
themes
└── demo
├── css
│ ├── print.css
│ └── styles.css
├── demo.info.yml
├── demo.libraries.yml
└── demo.theme
Think of .theme as a
replacement for
template.php
OH MY GOD ITS
FULL OF PHP
demo.theme
<?php
/**
* @file
* Contains preprocess functions for Demo theme.
*/
/**
* Preprocess for node pages.
*/
function demo_preprocess_node(&$variables) {
$variables['#attached']['library'][] = 'demo/landing-pages';
}
Don’t forget to rebuild
caches when changing
theme files
Moving right along
Templates, what are
they good for?
Twig
What’s so great about Twig
•
Flexible
•
Easy to use
•
Similar to other template systems (jinja2, swig, etc)
•
SECURE
Twig Examples
{# All twig tags use braces #}
{# Comment use the hash/pound #}
{{ this_tag_prints_output }}
{# Tags with percent signs, are control blocks #}
{% if variable %}
{{ variable }}
{% endif %}
{% set variable = 'Special value.' %}
{{ variable|clean_class }}
{# Prints 'special-value' #}
Example filters
•
Part of Twig — capitalize, date, escape, first, length,
lower, number_format, trim, etc
•
Drupal provided — t, trans, passthrough,
placeholder, drupal_escape, safe_join, without,
clean_class, clean_id, render
{{ 'This text will be translated'|t }}
<title>{{ head_title|safe_join(' | ') }}</title>
{# Prints out '<title>Homepage | Demo site</title>' #}
region.html.twig
{%
set classes = [
'region',
'region-' ~ region|clean_class,
]
%}
{% if content %}
<div{{ attributes.addClass(classes) }}>
{{ content }}
</div>
{% endif %}
Working with
Drupal regions
page.html.twig
{% if page.sidebar_first %}
<aside class="layout-sidebar-first" role="complementary">
{{ page.sidebar_first }}
</aside>
{% endif %}
Templates are
cached!
Fast websites are good,
but caching sucks when
you’re developing
Disable render caching in
sites/default/settings.php
sites/default/settings.php
/**
* Disable CSS and JS aggregation.
*/
$config['system.performance']['css']['preprocess'] = FALSE;
$config['system.performance']['js']['preprocess'] = FALSE;
/**
* Disable the render cache (this includes the page cache).
*
* This setting disables the render cache by using the Null cache back-end
* defined by the development.services.yml file above.
*
* Do not use this setting until after the site is installed.
*/
$settings['cache']['bins']['render'] = 'cache.backend.null';
Disable caching in
sites/default/services.yml
sites/default/services.yml
parameters:
twig.config:
# Twig auto-reload:
#
# Automatically recompile Twig templates whenever the source code changes.
# If you don't provide a value for auto_reload, it will be determined
# based on the value of debug.
#
# Not recommended in production environments
# @default null
auto_reload: null
# Twig cache:
#
# By default, Twig templates will be compiled and stored in the filesystem
# to increase performance. Disabling the Twig cache will recompile the
# templates from source each time they are used. In most cases the
# auto_reload setting above should be enabled rather than disabling the
# Twig cache.
#
# Not recommended in production environments
# @default true
cache: true
sites/default/services.yml
parameters:
twig.config:
# Twig debugging:
#
# When debugging is enabled:
# - The markup of each Twig template is surrounded by HTML comments that
#
contain theming information, such as template file name suggestions.
# - Note that this debugging markup will cause automated tests that directly
#
check rendered HTML to fail. When running automated tests, 'debug'
#
should be set to FALSE.
# - The dump() function can be used in Twig templates to output information
#
about template variables.
# - Twig templates are automatically recompiled whenever the source code
#
changes (see auto_reload below).
#
# For more information about debugging Twig templates, see
# http://drupal.org/node/1906392.
#
# Not recommended in production environments
# @default false
debug: false
Lets try it
(after clearing the
cache, of course)
<!-<!-<!-*
x
-->
<!--
THEME DEBUG -->
THEME HOOK: 'menu__main' -->
FILE NAME SUGGESTIONS:
menu--main.html.twig
menu.html.twig
BEGIN OUTPUT from 'core/modules/system/templates/menu.html.twig' -->
<ul class="menu">
<li class="menu-item">
<a href="/" data-drupal-link-system-path="<front>">Home</a>
</li>
</ul>
<!-- END OUTPUT from 'core/modules/system/templates/menu.html.twig' -->
Debug features
•
We can see template suggestions
•
We can see what the default or current used
template is
•
We can also use the dump() function to inspect
variables
themes
└── demo
├── css
│ └── styles.css
├── demo.info.yml
├── demo.libraries.yml
├── demo.theme
└── templates
└── region.html.twig
templates/region.html.twig
{%
set classes = [
'region',
'region-' ~ region|clean_class,
]
%}
{{ dump(attributes) }}
{% if content %}
<div{{ attributes.addClass(classes) }}>
{{ content }}
</div>
{% endif %}
Guess what?
The existence of a
template file is still cached,
even with debug enabled :(
templates/node.html.twig
<article{{ attributes }}>
{{ title_prefix }}
{% if not page %}
<h2{{ title_attributes }}>
<a href="{{ url }}" rel="bookmark">{{ label }}</a>
</h2>
{% endif %}
{{ title_suffix }}
{% if display_submitted %}
<footer>
{{ author_picture }}
<div{{ author_attributes }}>
{% trans %}Submitted by {{ author_name }} on {{ date }}{% endtrans %}
{{ metadata }}
</div>
</footer>
{% endif %}
<div{{ content_attributes }}>
{{ dump(content) }}
{{ content }}
</div>
</article>
array (size=5)
'field_image' =>
array (size=2)
'#cache' =>
array (size=3)
'contexts' =>
array (size=0)
...
'tags' =>
array (size=0)
...
'max-age' => int -1
'#weight' => int -1
'body' =>
array (size=16)
'#theme' => string 'field' (length=5)
'#title' => string 'Body' (length=4)
'#label_display' => string 'hidden' (length=6)
'#view_mode' => string 'full' (length=4)
'#language' => string 'en' (length=2)
'#field_name' => string 'body' (length=4)
'#field_type' => string 'text_with_summary' (length=17)
'#field_translatable' => boolean true
'#entity_type' => string 'node' (length=4)
'#bundle' => string 'article' (length=7)
...
<div{{ content_attributes }}>
<div class="for-the-tags">
{{ content.field_tags }}
</div>
{{ content|without('field_tags') }}
</div>
But what variables
are defined?
{{ dump() }}
Other fun stuff to
do with templates
{{ attach_library('demo/landing-pages') }}
{% import _self as menus %}
{#
We call a macro which calls itself to render the full tree.
@see http://twig.sensiolabs.org/doc/tags/macro.html
#}
{{ menus.menu_links(items, attributes, 0) }}
{% macro menu_links(items, attributes, menu_level) %}
{% import _self as menus %}
{% if items %}
{% if menu_level == 0 %}
<ul{{ attributes.addClass('menu') }}>
{% else %}
<ul class="menu">
{% endif %}
{% for item in items %}
<li{{ item.attributes }}>
{{ link(item.title, item.url) }}
{% if item.below %}
{{ menus.menu_links(item.below, attributes, menu_level + 1) }}
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endmacro %}
So you’ve got a
handle on templates
You know how to find
template names,
suggestions, but
<body class="layout-one-sidebar layout-sidebar-first user-logged-in path-frontpage">
Not included by
default with Drupal 8
But what if you
liked those?
Enter the Classy
theme
Classy theme
•
Provides a base theme that just adds classes for
other themes to build on
•
Literally only has about 40 lines of CSS
•
No PHP at all
•
But over a hundred templates!
•
Bartik uses Classy as a base theme
What’s with the
Drupal core CSS files?
</style>
<style media="all">
@import url("/core/modules/contextual/css/contextual.toolbar.css?nqlozl");
@import url("/core/modules/editor/css/editor.css?nqlozl");
@import url("/core/modules/filter/css/filter.caption.css?nqlozl");
@import url("/core/modules/ckeditor/css/plugins/drupalimagecaption/ckeditor.drupalimagecaption.css?nqlozl");
@import url("/core/modules/toolbar/css/toolbar.menu.css?nqlozl");
@import url("/core/modules/ckeditor/css/ckeditor.css?nqlozl");
@import url("/core/modules/contextual/css/contextual.theme.css?nqlozl");
@import url("/core/modules/contextual/css/contextual.icons.theme.css?nqlozl");
@import url("/core/assets/vendor/jquery.ui/themes/base/theme.css?nqlozl");
@import url("/core/misc/dialog.theme.css?nqlozl");
@import url("/core/modules/quickedit/css/quickedit.theme.css?nqlozl");
@import url("/core/modules/quickedit/css/quickedit.icons.theme.css?nqlozl");
@import url("/core/themes/seven/css/components/quickedit.css?nqlozl");
@import url("/core/modules/toolbar/css/toolbar.theme.css?nqlozl");
@import url("/core/modules/toolbar/css/toolbar.icons.theme.css?nqlozl");
@import url("/core/modules/user/css/user.icons.theme.css?nqlozl");
@import url("/core/modules/shortcut/css/shortcut.theme.css?nqlozl");
@import url("/core/modules/shortcut/css/shortcut.icons.theme.css?nqlozl");
@import url("/core/modules/filter/css/filter.admin.css?nqlozl");
</style>
<style media="all">
@import url("/core/themes/bartik/css/base/elements.css?nqlozl");
@import url("/core/themes/bartik/css/layout.css?nqlozl");
@import url("/core/themes/bartik/css/components/admin.css?nqlozl");
@import url("/core/themes/bartik/css/components/block.css?nqlozl");
@import url("/core/themes/bartik/css/components/book.css?nqlozl");
@import url("/core/themes/bartik/css/components/breadcrumb.css?nqlozl");
@import url("/core/themes/bartik/css/components/captions.css?nqlozl");
@import url("/core/themes/bartik/css/components/comments.css?nqlozl");
@import url("/core/themes/bartik/css/components/content.css?nqlozl");
@import url("/core/themes/bartik/css/components/contextual.css?nqlozl");
@import url("/core/themes/bartik/css/components/dropbutton.component.css?nqlozl");
@import url("/core/themes/bartik/css/components/featured-top.css?nqlozl");
@import url("/core/themes/bartik/css/components/feed-icon.css?nqlozl");
@import url("/core/themes/bartik/css/components/form.css?nqlozl");
@import url("/core/themes/bartik/css/components/forum.css?nqlozl");
@import url("/core/themes/bartik/css/components/header.css?nqlozl");
@import url("/core/themes/bartik/css/components/region-help.css?nqlozl");
@import url("/core/themes/bartik/css/components/item-list.css?nqlozl");
@import url("/core/themes/bartik/css/components/list-group.css?nqlozl");
@import url("/core/themes/bartik/css/components/node-preview.css?nqlozl");
@import url("/core/themes/bartik/css/components/pager.css?nqlozl");
@import url("/core/themes/bartik/css/components/panel.css?nqlozl");
@import url("/core/themes/bartik/css/components/primary-menu.css?nqlozl");
@import url("/core/themes/bartik/css/components/search.css?nqlozl");
@import url("/core/themes/bartik/css/components/search-results.css?nqlozl");
@import url("/core/themes/bartik/css/components/secondary-menu.css?nqlozl");
@import url("/core/themes/bartik/css/components/shortcut.css?nqlozl");
@import url("/core/themes/bartik/css/components/skip-link.css?nqlozl");
@import url("/core/themes/bartik/css/components/sidebar.css?nqlozl");
@import url("/core/themes/bartik/css/components/site-footer.css?nqlozl");
@import url("/core/themes/bartik/css/components/table.css?nqlozl");
</style>
That’s a lot of CSS!
Drupal 8 uses
SMACSS
Scalable and Modular CSS
•
It’s a book! By Jonathan Snook!
•
It’s also a workshop!
•
It’s a guide for organizing CSS
SMACSS in Drupal 8
•
Base — CSS reset/normalize plus HTML element styling.
•
Layout — macro arrangement of a web page, including
any grid systems.
•
Component — discrete, reusable UI elements.
•
State — styles that deal with client-side changes to
components.
•
Theme — purely visual styling (“look-and-feel”) for a
component.
But it’s really up to
you in your theme
What are you likely to use in
your theme?
•
Base
•
Layouts
•
Component overrides
•
Theme styles
css
├──
│ ├──
│ │ │ │ │ │ │ │ │ │ │ │ │ ├──
├──
└──
base
└── elements.css
components
├── admin.css
├── book.css
├── breadcrumb.css
├── buttons.css
├── comments.css
├── content.css
├── contextual.css
├── form.css
├── forum.css
├── sidebar.css
├── skip-link.css
├── tabs.css
└── views.css
layout.css
maintenance-page.css
print.css
Standards for organizing CSS
https://www.drupal.org/node/1887922
Best practices for CSS architecture in Drupal
8
https://www.drupal.org/node/1887918
Thank you!
Please evaluate this session
http://devsignercon.com/eval
Extras
(time permitting)
Breakpoints
Allows you to expose your
CSS breakpoints to the
Drupal admin UI
demo.breakpoint.yml
demo.mobile:
label: mobile
mediaQuery: ''
weight: 2
multipliers:
- 2x
demo.narrow:
label: narrow
mediaQuery: 'all and (min-width: 560px) and (max-width: 850px)'
weight: 1
multipliers:
- 2x
demo.wide:
label: wide
mediaQuery: 'all and (min-width: 851px)'
weight: 0
multipliers:
- 2x
<picture>
<!--[if IE 9]><video style="display: none;"><![endif]-->
<source srcset="styles/large/Billy_Mays.jpg 1x, styles/
large_960x_960_/Billy_Mays.jpg 2x" media="all and (min-width: 851px)"
type="image/jpeg">
<source srcset="styles/medium/Billy_Mays.jpg 1x, styles/
medium_440x440_/Billy_Mays.jpg 2x" media="all and (min-width: 560px) and
(max-width: 850px)" type="image/jpeg">
<source srcset="styles/thumbnail/Billy_Mays.jpg 1x" type="image/jpeg">
<!--[if IE 9]></video><![endif]-->
<img property="schema:image" srcset="" alt="Billy Mays here"
typeof="foaf:Image" data-pfsrcset="styles/large_960x_960_/
Billy_Mays.jpg" src="styles/large_960x_960_/Billy_Mays.jpg" style=""
width="350">
</picture>
Screenshots
This is ugly
Add a screenshot
•
Make a PNG
•
Make it 588 x 438
•
Call it screenshot.png
•
BOOM
But really, there are directions
https://www.drupal.org/node/647754
Thank you!
Please evaluate this session
http://devsignercon.com/eval
Download