Gesso is a Sass-based starter theme that outputs accessible HTML5 markup. It uses a mobile-first responsive approach and leverages SMACSS to organize styles. This encourages a component-based approach to theming through the creation of discrete, reusable UI elements. Gesso is heavily integrated with Storybook and Single Directory Components, allowing Drupal and Storybook to share the same markup.
Visit the Gesso Storybook demo site.
For more information, view the Gesso Drupal project page or Gesso GitHub repo. To submit bug reports or feature requests, visit the Gesso issue queue.
The following packages need to be installed on your system in order to compile and use Gesso.
-
Place the Gesso theme in your site’s theme directory. (e.g., themes/gesso) Read documentation on installing themes for more information.
-
Enable the Gesso Helper module. This module comes packaged with the theme, but must be manually enabled for the theme to function.
-
Install the Component Libraries module. Since many of the Drupal templates reference twig files inside Storybook using Twig namespaces, this module is required for the theme to function.
-
Install the Twig Tweak module.
-
Optional: Install the Twig Field Value module. This is not required, but it can make working with Twig templates easier. Please note, however, that using the
|field_valueTwig filter from this module will break Drupal’s QuickEdit functionality. -
Optional: Install the Background Images Formatter module and its Responsive Background Images Formatter submodule. This is not required, but it will allow you to use images uploaded to Drupal as background images, with different image sizes at different breakpoints.
Because Gesso is a starter theme, you may want to rename the Gesso directory or copy its contents to a new custom theme directory based on the name of your project.
The easiest way to accomplish this is to use
Drush. Type drush help gesso for more
information. If you get an error that the gesso command is not defined, make
sure you have enabled the Gesso Helper module.
If you can’t use Drush, then manually replace all instances of gesso within
this directory with a machine-readable name of your choice, including folder
names, filenames, and all occurrences within files. This custom name must start
with a letter and may only contain lowercase letters, numbers, and underscores.
Edit the .info.yml file and update the theme name and description. You can
also change the screenshot image (images/screenshot.png) shown on the
Appearance admin page.
For development, you can set the theme up as part of a Drupal site or work only in Storybook. Gesso includes npm tasks to compile design tokens, CSS, JS, Storybook, and the SVG sprite using webpack.
To use these tasks, first run the following npm command in the theme folder to install node dependencies.
npm iTo compile the theme, start Storybook, and watch for changes run the following command in the theme directory:
npm run devOpen localhost:6006 to view Storybook. If you’re using Docker (or some other container engine) for local development, this might be mapped to a custom domain or a port on a custom domain such as storybook.ddev.site or site.ddev.site:6006.
If you add new SCSS and/or JS files, you will need to restart webpack by
canceling and then re-running npm run dev. New files will not be processed
until webpack restarts. Errors will also be shown for duplicate filenames.
To initiate the build tasks only (without watching for changes), run the following command in the theme directory:
npm run buildRun npm run component to create boilerplate files for a new component. This is
the recommended approach as it will set up basic Twig and Storybook files that
you can modify.
Running the command without arguments will prompt you for the component details:
npm run componentYou can also pass arguments to skip the prompts:
npm run component -- --name my-component --folder components| Option | Description |
|---|---|
--name <name> |
Component name (required) |
--folder <folder> |
SDC location: components, layouts, or templates (required) |
--title <title> |
Human-readable title (defaults to Capital Case of name) |
--subfolder <name> |
Optional subfolder within the component location |
--js |
Include a JavaScript file |
--help, -h |
Show help message |
Gesso organizes its source files across three top-level directories:
-
components/: Single Directory Components (SDCs), subdivided by category:components/components/,components/layouts/, andcomponents/templates/. Each component lives in its own subdirectory and contains all of its files (Twig template, SCSS, JS, Storybook story, and the required[component-name].component.ymlmetadata file). -
source/: Design tokens and global styles, subdivided by ordered category:source/00-config/(design tokens, functions, mixins),source/01-global/(base HTML element styles, fonts, normalize, etc.), andsource/02-utility/(utility classes and JS helpers). -
templates/: Drupal.html.twigtemplates, organized by template type (block, content, field, form, etc.). These are standard Drupal theme templates and are separate from the Twig files insidecomponents/.
Note that the components/templates/ subdirectory contains SDC Storybook
templates for full-page layouts (page, homepage, landing page, etc.). These are
not Drupal templates — they are SDC components used for node detail pages.
Name your stories files [component].stories.jsx. See menu.stories.jsx for
an example.
To match Storybook to your site’s branding, change the colors in
.storybook/manager.js. Any fonts can be added in
.storybook/manager-head.html. See the Storybook
docs for more
information about and examples of theming.
Storybook 10 validates the Host header on dev-server requests. When you access
Storybook through the DDEV router (https://<project-name>.ddev.site:6006), the
hostname must be allowlisted in .storybook/main.js.
By default, Gesso reads DDEV_HOSTNAME or VIRTUAL_HOST from the environment
(DDEV sets VIRTUAL_HOST in the Storybook container) and uses that hostname. If
neither variable is set, any *.ddev.site hostname is allowed instead. Access
via localhost:6006 does not require additional configuration.
If you use a different reverse proxy or custom local domain, add its hostname to
core.allowedHosts in .storybook/main.js. See the Storybook core
docs for
details.
Sass can be compiled as part of the global styles.css file or to individual
CSS files scoped to a component.
@use is used to import Sass variables, mixins, and/or functions into
individual SCSS files. @import is discouraged by the Sass team and will
eventually be phased out..
This means that most files will start with @use '00-config' as *;. This allows
you to use the design token accessor functions without an additional namespace.
Other functions and mixins can be used similarly. Note that to avoid namespace
collisions, only Gesso-related variables, mixins, and functions should be used
with *.
All Sass files that are compiled to individual CSS files must have a unique filename, even if they are in different directories.
Prefix the name of your Sass file with _, e.g. _my-styles.scss. Place it in
the appropriate subdirectory under source/01-global/, then add a @use entry
for it in the nearest _index.scss file.
Name your component SCSS file [component-name].source.scss, e.g.
button.source.scss. The .source.scss suffix tells the build system to
compile this file to its own CSS file (button.css) rather than including it
in the global stylesheet. Import it at the top of your Storybook file. See
dropdown-menu.stories.jsx for an example.
Component CSS and JS assets are declared in the component’s
[component-name].component.yml file via the libraryOverrides key rather
than in gesso.libraries.yml. Drupal’s SDC system auto-discovers the
[component-name].source.scss and [component-name].source.js files. You only
need to declare dependencies, but you must include gesso/global as a dependency.
libraryOverrides:
dependencies:
- gesso/global
- core/drupal
- core/onceStylelint and Prettier are used to lint CSS and SCSS files. Warnings will break the build, so if you have a valid reason to break Stylelint rules you can have it ignore code in two ways:
-
Add
// stylelint-disable-next-lineto the line just before where the Stylelint warning is triggered. -
To ignore several lines, add
// stylelint-disablebefore the code in question and add// stylelint-enableafterwards.
In both cases above, please add a comment about the valid reason to disable the Stylelint rule(s) in your use case.
The Stylelint rules can be changed in the .stylelintrc.yml file. By default,
Gesso follows the
sass-guideline.es
and Prettier’s recommended
guidelines, with some
additional customizations.
The Prettier config can be changed in the .prettierrc file.
JavaScript can be compiled to individual JS files scoped to a component or
included as a shared utility. JavaScript files should go in the component’s directory under components/ (e.g.,
components/components/dropdown-menu/ for dropdown-menu JavaScript).
All JavaScript files must have a unique filename, even if they are in different directories.
Prefix the name of your JavaScript file with _, e.g. _Menu.es6.js. Place
helper modules in a modules/ subdirectory inside the component directory.
Import them into the component’s entry point script.
Name your component JS entry point [component-name].source.js, e.g.
dropdown-menu.source.js. DO NOT prefix it with _. Import your JS file at the
top of your Storybook file. See dropdown-menu.stories.jsx for an example.
As with CSS, component JS assets are auto-discovered by Drupal’s SDC system via
the [component-name].source.js filename. Declare dependencies in
[component-name].component.yml under libraryOverrides rather than adding an
entry to gesso.libraries.yml.
The common JS file is created using the Webpack SplitChunksPlugin.
To change how it behaves, update webpack.production.js. You may also need to
update gesso_library_info_build in libraries.inc to change what files are
included in the gesso/common library. We recommend using the default setup
unless you have a specific use case that requires advanced configuration.
gesso/common is already a dependency of gesso/global, so if you include
gesso/global as a dependency of your component (and you should), then it will
be included.
ESLint and Prettier are used to lint JavaScript files. If you have a valid reason to break one of the rules, you can ignore a specific line using any of the options in the ESLint documentation.
Please add a comment about the valid reason to disable the ESLint rule(s) in your use case.
The ESLint config can be changed in the eslint.config.js file. Gesso follows
the Forum One JavaScript standards,
which mostly follow the ESLint recommended config. For React files, there are
additional JSX-specific linting rules;
The Prettier config can be changed in the .prettierrc file.
Gesso itself does not include any jQuery dependencies and does not ship with jQuery. However, some Drupal modules still rely on jQuery, so you may need to add it if, for example, you need to create and trigger a jQuery event.
To add jQuery to Storybook:
- Install jQuery with
npm i -D jquery @types/jquery. - Add jQuery to
config.externalsin lines 78-82 of.storybook/main.jsconfig.externals = { drupal: 'Drupal', drupalSettings: 'drupalSettings', once: 'once', jquery: 'jQuery', };
- Add a jQuery stub similar to
stubs/once.jsand import it in.storybook/preview.jsimport jQuery from 'jquery'; window.jQuery = jQuery;
import './stubs/jquery.js'
To add jQuery to Drupal:
- Add jQuery to
externalsin lines 170-174 ofwebpack.common.jsexternals: { drupal: 'Drupal', drupalSettings: 'drupalSettings', once: 'once', jquery: 'jQuery' }
- Ensure that
core/jqueryis added as a dependency in the component's[component-name].component.yml:libraryOverrides: dependencies: - gesso/global - core/jquery
You can then import jQuery at the top of a file, the same way Drupal and once
are typically imported, and use it as needed.
Unit tests use Vitest and live alongside the code they
test as *.test.js/*.test.ts files. Run them with:
ddev gesso npm run test:unitThe global environment for Vitest is node, which is faster for tests that do
not require a browser. To use the jsdom environment for a test instead,
add a comment at the top of the file: // @vitest-environment jsdom
Playwright is installed for end-to-end tests. You will need to have your
local site up and running before you can run Playwright tests. Name your test [name].spec.ts
and place it in the e2e directory. You can remove the existing e2e test once
you've written some of your own-- it is are provided primarily as an example of
a test spec.
By default, tests run against https://YOUR-PROJECT.ddev.site if you run
ddev gesso test:e2e If you want to run against a different URL, set the
PLAYWRIGHT_BASE_URL environment variable.
Note: If you are running Gesso as a separate service (you probably are),
check that your .ddev/docker-compose.gesso.yml includes:
environment:
- VIRTUAL_HOST=$DDEV_HOSTNAMEIf it doesn't, add it and run ddev restart. This will allow Playwright to
find your ddev URL for testing.
When you run the test:e2e script, it will first run playwright install to
install all needed dependencies on your environment. By default, this is only
the chromium browser. If you change your Playwright config to run tests in
additional browsers, you will need to change
playwright install --with-deps chromium. The simplest, if you are using
multiple browsers, is likely to change it to playwright install --with-deps,
which installs all dependencies.
ddev gesso npm run test:e2eGesso uses the configuration file source/00-config/config.design-tokens.yml to
manage the theme’s design tokens. The npm build and dev tasks will automatically
generate a global Sass map to easily pull design tokens into individual SCSS
files.
The following Sass functions can be used to access the tokens defined in
config.design-tokens.yml.
Output a shadow value from the box-shadow token list.
box-shadow: gesso-box-shadow(1);Output a size value from the breakpoints token list.
@include breakpoint(gesso-breakpoint(desktop)) {
display: flex;
}
@include breakpoint-max(gesso-breakpoint(mobile), true) {
display: none;
}
@include breakpoint-min-max(
gesso-breakpoint(mobile),
gesso-breakpoint(tablet),
true
) {
display: block;
}Output a color value from the palette brand token list.
color: gesso-brand(blue, light);Output a color value from the colors token list.
color: gesso-color(text, primary);Output a size value from the constrains token list.
max-width: gesso-constrain(sm);Output a timing value from the transitions duration token list.
transition-duration: gesso-duration(short);Output an easing value from the transitions ease token list.
transition-timing-function: gesso-easing(ease-in-out);Output a stack value from the font-family token list.
font-family: gesso-font-family(primary);Output a size value from the font-size token list.
font-size: gesso-font-size(2);Output a weight value from the font-weight token list.
font-weight: gesso-font-weight(semibold);Output a color value from the palette grayscale token list.
color: gesso-grayscale(gray-2);Output a height value from the line-height token list.
line-height: gesso-line-height(tight);Output a size value from the spacing token list.
margin-bottom: gesso-spacing(8);Output an index value from the z-index token list.
z-index: gesso-z-index(modal);The values in Gesso’s configuration file are also exported to JavaScript objects
so that the same values can be used in CSS and JS. The JS objects can be found
in source/00-config/_GESSO.es6.js. This file is also rebuilt whenever
npm run dev or npm run build are run.
For example, to use a breakpoint in a script:
import { BREAKPOINTS } from '../../../00-config/_GESSO.es6';
if (window.matchMedia(`min-width: ${BREAKPOINTS.desktop}`).matches) {
// Some script that should only run on larger screens.
}This will use the same breakpoint as breakpoint(gesso-breakpoint(desktop)) in
your Sass.
Gesso uses custom mixins to specify viewport width based media queries:
breakpoint: min-width queriesbreakpoint-max: max-width queriesbreakpoint-min-max: queries with both a min and max width
Each mixin takes one or two width parameters, which can be a straight value
(e.g., 800px, 40em) or a design token value called using the gesso-breakpoint
function (e.g., gesso-breakpoint(tablet-lg)). The breakpoint-max and
breakpoint-min-max mixins can also take an optional parameter to subtract one
pixel from the max-width value, which can be useful when you want your query to
go up to the value but not to include it, such as when using Gesso breakpoint
token values.
Output a min-width based media query.
@include breakpoint(800px) {
display: flex;
}
@include breakpoint(gesso-breakpoint(desktop)) {
display: none;
}Output a max-width based media query. The optional $subtract_1_from_max
parameter will subtract 1px from the width value if set to true (default:
false).
@include breakpoint-max(900px) {
display: block;
}
@include breakpoint-max(gesso-breakpoint(mobile), true) {
display: none;
}Output a media query with both a min-width and max-width. The optional
$subtract_1_from_max parameter will subtract 1px from the max-width value if
set to true (default: false).
@include breakpoint-min-max(400px, 700px) {
display: flex;
}
@include breakpoint-min-max(
gesso-breakpoint(mobile),
gesso-breakpoint(tablet),
true
) {
display: block;
}Gesso uses custom mixins to specify container queries:
container-query: min-width container queriescontainer-query-max: max-width container queriescontainer-query-min-max: container queries with both a min and max width
Each mixin takes one or two width parameters, which can be a straight value
(e.g., 800px, 40em) or a design token value called using the gesso-breakpoint
function (e.g., gesso-breakpoint(tablet-lg)). The container-max and
container-min-max mixins can also take an optional parameter to subtract one
pixel from the max-width value, which can be useful when you want your query to
go up to the value but not to include it, such as when using Gesso breakpoint
token values.
In order for container queries to work, you need to set a containment context on a parent element.
container-type: inline-size;container: container-name / inline-size;Output a min-width based media query.
@include container-query(800px) {
display: flex;
}
@include container-query(gesso-breakpoint(desktop)) {
display: none;
}Output a max-width based container query. The optional $subtract_1_from_max
parameter will subtract 1px from the width value if set to true (default:
false).
@include container-query-max(900px) {
display: block;
}
@include container-query-max(gesso-breakpoint(mobile), true) {
display: none;
}Output a container query with both a min-width and max-width. The optional
$subtract_1_from_max parameter will subtract 1px from the max-width value if
set to true (default: false).
@include container-query-min-max(400px, 700px) {
display: flex;
}
@include container-query-min-max(
gesso-breakpoint(mobile),
gesso-breakpoint(tablet),
true
) {
display: block;
}Gesso includes some additional filters and functions that can be used in Twig templates.
Fork of Drupal Pattern Lab's add_attribute Twig function.
Allows Twig templates to add attributes that, in Drupal, will be merged with the Drupal attributes object
while also rendering in Storybook.
<div {{ add_attributes(
{
class: 'your-class-one your-class-two',
'data-foo': 'bar'
}
) }}>...</div>Twig filter to sort an object by key alphabetically.
{% for key, value in your_object|keysort %}
...
{% endfor %}Twig filter to transform a heading tag to the next level down (h2 -> h3, h3 -> h4, etc.) Used when the parent heading level can vary but, to maintain accessibility, the component's heading or subheading should change accordingly.
{% set subheading_element = title_element|subheading_level %}
<{{ subheading_element|default('h3') }}>...</{{ subheading_element|default('h3') }}>Twig function that returns Drupal's current asset query string (the same value
appended to CSS and JS URLs), which changes whenever caches are flushed. Use it
to cache-bust static theme assets, like the SVG sprite, after a deployment. It
returns an empty string in Storybook. In JS, the same value is available as
drupalSettings.gesso.assetVersion.
{% set version = asset_version() %}
<use href="{{ image_path }}/sprite.artifact.svg{{ version ? '?v=' ~ version : '' }}#{{ icon_name }}"></use>A static Storybook site can be built with npm run build-storybook. You will
then be able to view Storybook at
YOUR_URL/themes/gesso/storybook/index.html.
Some aspects of Gesso can be configured in the theme settings. These include the Back to Top component, Breadcrumb options, and Button styles for links.
For the buttons, put the classes that should be added for each button size
and button style on each line, with classes separated with ., similar to how
you would add custom classes to the WYSIWYG editor.
c-button|Primary
c-button.c-button--secondary|Secondary
c-button.c-button--tertiary|Tertiary
To use these classes, select Gesso Button as the formatter for a link field under the entity's display settings.
Please use the Github issue queue https://github.com/forumone/gesso/issues for discussion, bug reports, feature requests, etc.
Submitted pull requests should be against the latest release candidate branch,
such as 5.x-RC.
The Gesso theme is maintained by Corey Lafferty, KJ Monahan, Dan Mouyard (@dcmouyard), and Tommy Alter.