By Mathieu Spillebeen
@MathieuSpil
mathieu.spillebeen@gmail.com
Frontend United.org
Compony.io
Freelance Drupal Frontend developer
DO YOURSELF
New theme structure
Compony.io
Download theme-structure + tools
_sass-essentials
compony
components
compony.info.yml
compony.libraries.yml
compony.theme
gulpfile.js
.nvmrc
package.json
package-lock.json
yarn.lock
Tooling workflow
_sass-essentials
compony
components
compony.info.yml
compony.libraries.yml
compony.theme
gulpfile.js
core
gulpfile.js
config.js
index.js
local.config.js
project.config.js
module.exports = {
gulpthemes: [
{
path: 'web/themes/compony',
with_styleguide: false
},
],
features: {
autoprefixer: {
enable: true,
options: {
browsers: ['last 2 versions', 'ie 9', '> 0.2%'],
cascade: false
},
},
browserify: {
enable: true,
debug_mode: false,
},
clean_css: {
enable: false,
},
css_mapping: {
enable: false,
},
image_optimise: {
enable: true,
gifsicle: {
interlaced: true,
optimizationLevel: 3
},
optipng: {
optimizationLevel: 5
},
jpegtran: {
progressive: true
},
svgo: {
plugins: [
{
removeViewBox: false
},
{
removeDimensions: true
}
]
}
},
sass_includes: {
bourbon: false,
bourbonNeat: false,
breakpoint: false
},
// Deprecated
kss: {
enable: false,
},
},
};
project.config.js
module.exports = {
features: {
auto_rebuild_drupal_cache: {
enable: false,
cache_rebuild_command: 'drush cr'
},
browsersync: {
enable: false,
localhost_url: "https://local.dev/"
},
validate_yml: {
enable: true,
},
lint_php: {
enable: true,
},
lint_html: {
enable: false,
}
},
notifications: {
html: {
linting_errors: false,
},
css: {
sass_errors: true
},
javascript: {
browserify_errors: true,
uglify_errors: true,
},
yml: {
validation_errors: true,
},
php: {
linting_errors: true,
},
internal: {
cache_rebuilding_status: true,
cache_rebuild_error: true,
}
}
};
local.config.js
$ cd path/to/theme
$ nvm use
$ npm install
$ gulp
Command line
DO YOURSELF
_sass-essentials
my-theme
components
my-theme.info.yml
my-theme.libraries.yml
my-theme.theme
gulpfile.js
.nvmrc
package.json
package-lock.json
yarn.lock
module.exports = {
options: {
...
gulpthemes: [
{
path: 'themes/custom/my-first-theme',
},
{
path: 'themes/custom/my-second-theme',
},
]
},
};
project.config.js
Compony.io
Download theme-structure + tools
+ rename the theme!
slideshow
components
toggle-thingy
blogs
node--article--teaser
components
taxonomy-term--tag
user--full
node
_global
node.html.twig
node.css
libraries.yml
node--article
node--article.html.twig
components
node
_global
node.html.twig
node.css
libraries.yml
node--article
node--article.html.twig
components
node
node
components
node
node--article
node--article-full
node--article--teaser
node--blog
node--blog--full
node--blog--teaser
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'menu__anonymous_menu' -->
<!-- FILE NAME SUGGESTIONS:
* menu--anonymous-menu.html.twig
x menu.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/compony/components/menu/menu/menu.html.twig' -->
<ul class="menu-main__container">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'menu-item' -->
<!-- FILE NAME SUGGESTIONS:
x menu-item.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/compony/components/menu/menu-item.html.twig' -->
<li>
<a href="/user/login" class="">Login</a>
</li>
<!-- END OUTPUT from 'themes/compony/components/menu/menu-item.html.twig' -->
</ul>
<!-- END OUTPUT from 'themes/compony/components/menu/menu/menu.html.twig' -->
menu
libraries.yml
menu.html.twig
menu-item.html.twig
menu.js
menu.scss
my-component
dist
my-component.css
my-component.scss
$c-grey: #333;
@mixin button {
color: red;
}
// Empy file
my-component
dist
my-component.css
my-component.scss
.my-component {
color: $c-grey;
}
.my-component {
color: #CCC;
}
_sass-essentials
compony
components
my-theme.info.yml
my-theme.libraries.yml
my-theme.theme
non-CSS-generating Sass
CSS-generating Sass
my-component
my-component.scss
@include "sass-essentials";
.my-component {
color: $c-grey;
}
dist
sass-essentials
_colors.scss
sass-essentials.scss
components
DO YOURSELF
_sass-essentials
compony
components
my-theme.info.yml
my-theme.libraries.yml
my-theme.theme
CSS-generating Sass
_global
non-CSS-generating Sass
_sass-partials
templates
style.scss
Split up your Sass in to different files
DO YOURSELF
node--article
dist
libraries.yml
smile.svg
node--article.html.twig
smile.svg
node--article.css
node--article.scss
.node--article {
background: url('smile.svg');
}
node--article
libraries.yml
smile.svg
node--article.html.twig
node--article.scss
node--blog
libraries.yml
smile.svg
node--blog.html.twig
node--blog.scss
/some-page
node--article
libraries.yml
smile.svg
node--article.html.twig
node--article.scss
node--blog
libraries.yml
node--blog.html.twig
node--blog.scss
node
node.html.twig
.node--blog {
//background-image: url('smile.svg);
background-image: url('../../node/dist/smile.svg');
}
.node--article {
//background-image: url('smile.svg);
background-image: url('../../node/dist/smile.svg');
}
/some-page
node--article
libraries.yml
smile.svg
node--article.html.twig
node--article.scss
node--blog
libraries.yml
node--blog.html.twig
node--blog.scss
node
node.html.twig
.node--blog {
//background-image: url('smile.svg);
background-image: url('../../node/dist/smile.svg');
}
.node--article {
//background-image: url('smile.svg);
background-image: url('../../node/dist/smile.svg');
}
/some-page
smile.svg
node
node.html.twig
still in use? ¯\_(ツ)_/¯
node--article
libraries.yml
smile.svg
node--article.html.twig
node--article.scss
node
node.html.twig
node--article
smile.svg
node
node.html.twig
node--article--full
libraries.yml
node--article--full.html.twig
node--article--full.scss
node--article--blog
.node--article {
background: url('../../node/dist/smile.svg');
}
.node--article {
background: url('¯\_(ツ)_/¯');
}
node--article
smile.svg
node--article.html.twig
_global
styles.scss
.smiley--happy {
background: url('smile.svg');
}
<div class="smiley--happy">...</div>
Re-usability: (╯°□°)╯︵ ┻━┻
Class-based theming
node--article
smile.svg
node--article.html.twig
smileys
smileys.scss
.smiley--happy {
background: url('smile.svg');
}
{% extends 'node.html.twig' %}
{% block before %}
{{ attach_library("compony/node--article") }}
{% endblock %}
{% block content %}
{{ attach_library("compony/smileys") }}
<div class="my-messages smiley--happy">...</div>
{% endblock %}
Re-usable
Class-based theming
libraries.yml
CSS-only component
node--article
smile.svg
node--article.html.twig
smileys
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 368 368"><path d="M261.336 226.04c-3.296-2.952-8.36-2.664-11.296.624C233.352 245.312 209.288 256 184 256c-25.28 0-49.352-10.688-66.04-29.336-2.952-3.288-8-3.576-11.296-.624-3.296 2.944-3.568 8-.624 11.296C125.76 259.368 154.176 272 184 272c29.832 0 58.248-12.64 77.96-34.664 2.944-3.296 2.664-8.352-.624-11.296z"/><path d="M184 0C82.544 0 0 82.544 0 184s82.544 184 184 184 184-82.544 184-184S285.456 0 184 0zm0 352c-92.64 0-168-75.36-168-168S91.36 16 184 16s168 75.36 168 168-75.36 168-168 168z"/><path d="M248 128c-22.056 0-40 17.944-40 40 0 4.416 3.584 8 8 8s8-3.584 8-8c0-13.232 10.768-24 24-24s24 10.768 24 24c0 4.416 3.584 8 8 8s8-3.584 8-8c0-22.056-17.944-40-40-40zm-104 40c0 4.416 3.584 8 8 8s8-3.584 8-8c0-22.056-17.944-40-40-40s-40 17.944-40 40c0 4.416 3.584 8 8 8s8-3.584 8-8c0-13.232 10.768-24 24-24s24 10.768 24 24z"/></svg>
{% extends 'node.html.twig' %}
{% block content %}
<div class="my-messages smileys smileys--smile">
{% include smile.html.twig %}
</div>
{% endblock %}
Re-usable
Inlining SVG
HTML-only component
smile.html.twig
node--article
libraries.yml
smile.svg
node--article.html.twig
node--article.scss
.node--article {
background: url('data: ... ');
fill: blue;
}
100 bytes
130 bytes
won't work
Image is not accessible
Image won't be cached by browser, but CSS-file will
page
libraries.yml
page.html.twig
page.scss
.home h1 {
content: 'Welcome to the homepage');
}
(╯°□°)╯︵ ┻━┻
Content
Styling
sass-essentials
_breakpoints.scss
menu--main
menu--main.scss
views-view--news
$bp-m: 30rem;
$bp-l: 50rem;
.menu {
margin: 0 auto;
@media(min-width: $bp-m) {
margin: 0;
}
}
sass-essentials
_breakpoints.scss
menu--main
views-view--news
views-view--news.scss
$bp-m: 30rem;
$bp-l: 50rem;
$bp-m: 35rem;
.views-view--news {
display: block;
@media(min-width: $bp-m) {
display: flex;
}
}
if(google.isOffline()) {
die();
}
(╯°□°)╯︵ ┻━┻
my-component
libraries.yml
my-component:
css:
component:
https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css: { type: external, minified: true}
my-component
libraries.yml
bootstrap.min.css
reliable
fragile
Secured by Compony
DO YOURSELF
status-messages
libraries.yml
status-messages.html.twig
status-messages.scss
.component.yml
component:
name: 'Status messages'
folder_name: status-messages
details: 'https://www.compony.io/component/status-messages'
downloaded_on: 07-26-2019
built_upon:
project: Drupal
version: '8'
specific:
type: core
link: false
repository:
details: 'https://gitlab.com/componies/flat-design/Core/status-messages'
gitlab_project_id: '7511021'
version: 'https://gitlab.com/componies/flat-design/Core/status-messages/commit/ae7c929d9e3a8ca2122facc2170ad0b0b4dd54de'
version_sha: ae7c929d9e3a8ca2122facc2170ad0b0b4dd54de
version_sha_short: ae7c929d
diff_with_latest_version: 'https://gitlab.com/componies/flat-design/Core/status-messages/compare/ae7c929d9e3a8ca2122facc2170ad0b0b4dd54de...master'
clone_this_version: 'git clone https://gitlab.com/componies/flat-design/Core/status-messages.git
status-messages && cd status-messages && git checkout ae7c929d9e3a8ca2122facc2170ad0b0b4dd54de'
contributors:
1:
name: Compony
details: 'https://www.compony.io/user/1'
collection:
name: 'Bare essentials'
details: 'https://www.compony.io/collection/bare-essentials'
contributor:
1:
name: Compony
details: 'https://www.compony.io/user/1'
node--article
node--article.html.twig
{% block content %}
{{ content.field_c }}
{{ content.field_a }}
{{ content.field_b }}
{{ content.field_d }}
{% endblock %}
node--article
node--article.html.twig
{% block content %}
{{ content.field_c }}
{{ content.field_a }}
{{ content|without('field_c', 'field_a') }}
{% endblock %}
my-old-theme
dist
smile.svg
global.css
@charset "UTF-8";@import url(https://fonts.googleapis.com/css?family=Roboto+Condensed:300,400,700|Roboto:100,300,400);@font-face{font-family:Dupla-Bold;src:url(../fonts/dupla/dupla_bold-webfont.eot);src:url(../fonts/dupla/dupla_bold-webfont.eot?#iefix) format("embedded-opentype"),url(../fonts/dupla/dupla_bold-webfont.woff2) format("woff2"),url(../fonts/dupla/dupla_bold-webfont.woff) format("woff"),url(../fonts/dupla/dupla_bold-webfont.ttf) format("truetype")}@font-face{font-family:DuplaDemibold...
- @charset "UTF-8";@import url(https://fonts.googleapis.com/css
+ @charset "UTF-8";@import url(https://fonts.googleapis.com/css
¯\_(ツ)_/¯
Node/7
Template files
{# page.html.twig #}
<div class="page-content clearfix">
{{ page.header }}
<main id="main-content" role="main">
{{ page.help }}
{{ page.content }}
</main>
{{ page.footer }}
</div>
Re-ordering
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'node' -->
<!-- FILE NAME SUGGESTIONS:
* node--page--full.html.twig
* node--page.html.twig
* node--full.html.twig
x node.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/compony/components/node/node.html.twig' -->
<article>...</article>
<!-- END OUTPUT from 'themes/compony/components/node/node.html.twig' -->
The only 4 hooks you need:
1
Theme-name
Lego-name
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'node' -->
<!-- FILE NAME SUGGESTIONS:
* node--page--full.html.twig
* node--page.html.twig
* node--full.html.twig
x node.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/compony/components/node/node.html.twig' -->
<article>...</article>
<!-- END OUTPUT from 'themes/compony/components/node/node.html.twig' -->
components
node
node.html.twig
node.theme
function compony_preprocess_node(&$variables) {
// DO stuff here...
}
<article (( attributes ))>
<h2><a href="{{ url }}" rel="bookmark">{{ label }}</a></h2>
{{ content }}
</article>
components
node
node.html.twig
node.theme
function compony_preprocess_node(&$variables) {
$variables['party'] = 'jipie';
}
<article {{ attributes }}>
<h2><a href="{{ url }}" rel="bookmark">{{ label }}</a></h2>
{{ content }}
{{ party }}
</article>
components
node
node.html.twig
node.theme
function compony_preprocess_node(&$variables) {
$variables['attributes']['class'][] = 'new-class';
}
<article {{ attributes.addClass('new-class') }}>
<h2><a href="{{ url }}" rel="bookmark">{{ label }}</a></h2>
{{ content }}
</article>
components
node
node.html.twig
node.theme
function compony_preprocess_node(&$variables) {
$variables['#attached']['library'][] = 'compony/node';
}
{{ attach_library("compony/node") }}
<article {{ attributes }}>
<h2><a href="{{ url }}" rel="bookmark">{{ label }}</a></h2>
{{ content }}
</article>
DO YOURSELF
The only 4 hooks you need:
2
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'node' -->
<!-- FILE NAME SUGGESTIONS:
* node--blog--full.html.twig
* node--blog.html.twig
* node--full.html.twig
x node.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/compony/components/node/node.html.twig' -->
<article>...</article>
<!-- END OUTPUT from 'themes/compony/components/node/node.html.twig' -->
This one!
components
node
node.html.twig
node.theme
function compony_theme_suggestions_node_alter(array &$suggestions, array $variables) {
$suggestions[] = 'node__party_pooper';
}
<!-- THEME HOOK: 'node' -->
<!-- FILE NAME SUGGESTIONS:
* node--party-pooper.html.twig
* node--blog--full.html.twig
* node--blog.html.twig
* node--full.html.twig
x node.html.twig
-->
New suggestion
components
node
node.html.twig
node.theme
function compony_theme_suggestions_node_alter(array &$suggestions, array $variables) {
$suggestions[] = 'node__party_pooper';
}
node--party-pooper
node--party-pooper.html.twig
_ -
Instead of
form
form.html.twig
<form{{ attributes.addClass(classes) }}>
{% if element['#form_id'] == 'register_form' %}
Some custom header
{% endif %}
{{ children }}
{% if element['#form_id'] == 'contact_form' %}
Reach us on 00468 638295
{% endif %}
</form>
form
form.html.twig
form.theme
function compony_theme_suggestions_form_alter(&$variables) {
$suggestions[] = 'form__'.$variables['element']['#form_id'];
}
form--register-form
form--contact-form
form--register-form.html.twig
form--contact-form.html.twig
DO YOURSELF
The only 4 hooks you need:
3
function compony_library_info_alter__table(&$libraries, $extension) {
// Remove all the weird table functionality no-one wants.
unset($libraries['drupal.tabbingmanager']);
unset($libraries['drupal.tabledrag']);
unset($libraries['drupal.tableresponsive']);
unset($libraries['drupal.tableselect']);
}
DO YOURSELF
The only 4 hooks you need:
4
text-dropdown
text-dropdown.theme
function compony_theme__text_dropdown($existing, $type, $theme, $path) {
return [
'template' => 'text-dropdown',
'variables' => [
'toggle' => NULL,
'content' => NULL,
'classes' => NULL,
],
];
}
text-dropdown
text-dropdown.theme
{{ attach_library('compony/text-dropdown') }}
<div class='text-dropdown'>
<button type="button" class="text-dropdown__toggle">
<span>{{ toggle }}</span>
</button>
<div class="text-dropdown__content">
<p>{{ content }}</p>
</div>
</div>
text-dropdown.html.twig
text-dropdown
text-dropdown.theme
text-dropdown:
version: VERSION
css:
component:
dist/text-dropdown.css: {}
text-dropdown.html.twig
dist
text-dropdown.scss
libraries.yml
text-dropdown
text-dropdown.theme
text-dropdown.html.twig
text-dropdown.scss
libraries.yml
function compony_theme__text_dropdown($existing, $type, $theme, $path) {
function compony_preprocess_text_dropdown(&$variables) {...}
function compony_library_info_alter__text_dropdown(&$libraries, $extension) {...}
function compony_theme_suggestions_text_dropdown_alter(&$variables) {
some-module
some-module.module
$build['my_dropdown'] = [
'#theme' => 'text_dropdown',
'#toggle' => 'Text for the toggle',
'#classes' => ['my-extra-class'], // optional
'#content' => [
'#markup' => 'The text that should be shown when clicked',
],
];
Text
module code, don't write this yourself
DO YOURSELF
/news
The news
Newsitem 2
Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her
Newsitem 3
Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her
Newsitem 1
Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her
Help! The news view is not
sorting the items on publication
date!
And visitors can't see the date
of each news item!
/news
The news
Newsitem 3
Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her
Newsitem 2
Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her
Newsitem 1
Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her Dolor lorem ipsum her
3 weeks ago
1 week ago
2 hours ago
Business logic
Presentational logic
Backend
Frontend
my-component.theme
DO YOURSELF
bootstrap
libraries.yml
bootstrap.min.css
bootstrap:
version: VERSION
css:
component:
dist/bootstrap.min.css: {}
_sass-essentials
compony
components
my-theme.info.yml
my-theme.libraries.yml
my-theme.theme
name: Compony
core: 8.x
libraries:
- compony/global
- compony/bootstrap
node--article
dist
libraries.yml
node--article.html.twig
node--article.css
node--article.scss
node--article:
css:
component:
dist/node--article.css: {}
dependencies:
- compony/bootstrap
photoswipe
dist
libraries.yml
main.css
photoswipe.min.js
node--article:
css:
component:
dist/main.css: {}
js:
photoswipe.min.js: {}
main.scss
node--article
libraries.yml
node--article.js
node--article:
css:
component:
dist/node--article.css: {}
js:
dist/node--article.js: {}
node--article.scss
photoswipe
node--article.html.twig
DO YOURSELF
Broken by Paragraphs, Views
<html>
<head>
<link rel="stylesheet" media="all" href="/sites/default/files/css/css_Ew7mHsG94pEUKReBZ_SsV4PxDlHo5Q6R284l2o0Pdew.css?pyte6e">
<link rel="stylesheet" media="all" href="/sites/default/files/css/css_zHBfDe5PZOuUeNbGmUprwXTvq--9nJnmYm9ZhSIOGPI.css?pyte6e">
</head>
<body>...</body>
</html>
<html>
<head>
<link rel="stylesheet" href="global.css">
</head>
<body>...</body>
</html>
contains 90% too much CSS for any given page
<html>
<head>
<link rel="stylesheet" media="all" href="/sites/default/files/css/css_Ew7mHsG94pEUKReBZ_SsV4PxDlHo5Q6R284l2o0Pdew.css?pyte6e">
<link rel="stylesheet" media="all" href="/sites/default/files/css/css_zHBfDe5PZOuUeNbGmUprwXTvq--9nJnmYm9ZhSIOGPI.css?pyte6e">
</head>
<body>...</body>
</html>
contains only the CSS needed!
<html>
<head>
<link rel="stylesheet" media="all" href="/sites/default/files/css/css_Ew7mHsG94pEUKReBZ_SsV4PxDlHo5Q6R284l2o0Pdew.css?pyte6e">
<link rel="stylesheet" media="all" href="/sites/default/files/css/css_zHBfDe5PZOuUeNbGmUprwXTvq--9nJnmYm9ZhSIOGPI.css?pyte6e">
</head>
<body>...</body>
</html>
contains only the CSS needed!
40% of the CSS is 100% render blocking
1 change in component, will cache-bust the entire CSS
<html>
<head>
<link rel="stylesheet" href="critical.css" >
<link rel="stylesheet" href="non-critical.css">
</head>
<body>...</body>
</html>
<html>
<head>
<link rel="stylesheet" href="critical.css">
<script>
// make a stylesheet link
var myCSS = document.createElement( "link" );
myCSS.rel = "stylesheet";
myCSS.href = "non-critical.css";
// insert it at the end of the head in a legacy-friendly manner
document.head.insertBefore( myCSS, document.head.childNodes[ document.head.childNodes.length - 1 ].nextSibling );
</script>
</head>
<body>...</body>
</html>
<html>
<head>
<link href="critical.css" rel="stylesheet">
<link href="non-critical.css" rel="stylesheet">
</head>
<body>...</body>
</html>
<html>
<head>
<link rel="stylesheet" href="critical.css" >
<link rel="alternate stylesheet" href="non-critical.css" onload="this.rel='stylesheet'">
</head>
<body>...</body>
</html>
40% of the CSS is now render blocking instead of 100%
1 change in component, will cache-bust the entire CSS
Global CSS
Menu CSS
Banner CSS
node--teaser CSS
fonts!
Only desktop
Search!
Status messages
Region CSS
Block CSS
Maintainability: (╯°□°)╯︵ ┻━┻
<html>
<head>
<link href="critical.css" rel="stylesheet">
<link href="non-critical.css" rel="stylesheet">
</head>
<body>...</body>
</html>
<html>
<head>
<link rel="stylesheet" href="core.css" />
<link rel="stylesheet" href="site-header.css" />
<link rel="stylesheet" href="site-nav.css" />
<link rel="stylesheet" href="content.css" />
<link rel="stylesheet" href="content-primary.css" />
<link rel="stylesheet" href="date-picker.css" />
<link rel="stylesheet" href="content-secondary.css" />
<link rel="stylesheet" href="ads.css" />
<link rel="stylesheet" href="site-footer.css" />
</head>
<body>...</body>
</html>
a change in
a component CSS,
will cache-bust the component CSS
Non-critical CSS:
async loading!
Still complex differentiation
<html>
<head>
<link rel="stylesheet" href="core.css" />
</head>
<body>
<link rel="stylesheet" href="site-header.css" />
<header class="site-header">
<link rel="stylesheet" href="site-nav.css" />
<nav class="site-nav">...</nav>
</header>
<link rel="stylesheet" href="content.css" />
<main class="content">
<link rel="stylesheet" href="content-primary.css" />
<section class="content-primary">
<h1>...</h1>
<link rel="stylesheet" href="date-picker.css" />
<div class="date-picker">...</div>
</section>
<link rel="stylesheet" href="content-secondary.css" />
<aside class="content-secondary">
<link rel="stylesheet" href="ads.css" />
<div class="ads">...</div>
</aside>
</main>
<link rel="stylesheet" href="site-footer.css" />
<footer class="site-footer"></footer>
</body>
status-messages
libraries.yml
status-messages.html.twig
status-messages.scss
{{ attach_library("compony/status-messages") }}
<div class="status-messages">
{{ message }}
</div>
dist
<link rel="stylesheet" href="{{ _self|split('/', -1)|join('/') }}/dist/status-messages.css" />
<div class="status-messages">
{{ message }}
</div>
@MathieuSpil
mathieu.spillebeen@gmail.com
Freelance Drupal Frontend developer