Browse Source

banner

master
rdrew 3 weeks ago
parent
commit
b41f7814ac
  1. 61
      CLAUDE.md
  2. 24
      css/base/fonts.css
  3. 133
      css/base/variables.css
  4. 57
      css/components/navigation/pcss/menu-sidebar.pcss.css
  5. 102
      css/components/navigation/pcss/nav-button-mobile.pcss.css
  6. 131
      css/components/navigation/pcss/nav-primary-button.pcss.css
  7. 150
      css/components/navigation/pcss/nav-primary-no-js.pcss.css
  8. 225
      css/components/navigation/pcss/nav-primary-wide.pcss.css
  9. 201
      css/components/navigation/pcss/nav-primary.pcss.css
  10. 114
      css/components/navigation/pcss/nav-secondary.pcss.css
  11. 109
      css/components/navigation/pcss/wide-nav-expand.pcss.css
  12. BIN
      fonts/IM_Fell_Great_Primer/IMFellGreatPrimer-Italic.woff2
  13. BIN
      fonts/IM_Fell_Great_Primer/IMFellGreatPrimer-Regular.woff2
  14. 93
      fonts/IM_Fell_Great_Primer/OFL.txt
  15. BIN
      fonts/IM_Fell_Great_Primer_SC/IMFellGreatPrimerSC-Regular.woff2
  16. 93
      fonts/IM_Fell_Great_Primer_SC/OFL.txt
  17. BIN
      fonts/LibreBodoni-Bold.woff
  18. BIN
      fonts/LibreBodoni-Bold.woff2
  19. BIN
      fonts/LibreBodoni-Italic.woff
  20. BIN
      fonts/LibreBodoni-Italic.woff2
  21. BIN
      fonts/LibreBodoni-Regular.woff
  22. BIN
      fonts/LibreBodoni-Regular.woff2
  23. BIN
      fonts/SourceSans3-Bold.woff
  24. BIN
      fonts/SourceSans3-Bold.woff2
  25. BIN
      fonts/SourceSans3-Italic.woff
  26. BIN
      fonts/SourceSans3-Italic.woff2
  27. BIN
      fonts/SourceSans3-Regular.woff
  28. BIN
      fonts/SourceSans3-Regular.woff2
  29. 42
      package.json
  30. 15
      scripts/css/changeOrAdded.js
  31. 23
      scripts/css/check.js
  32. 88
      scripts/css/compile.js
  33. 4
      scripts/css/log.js
  34. 49
      scripts/css/postcss-build.js
  35. 43
      scripts/css/postcss-watch.js
  36. 5
      scripts/js/.eslintrc.json
  37. 49
      scripts/js/babel-es6-build.js
  38. 43
      scripts/js/babel-es6-watch.js
  39. 15
      scripts/js/changeOrAdded.js
  40. 23
      scripts/js/check.js
  41. 31
      scripts/js/compile.js
  42. 35
      scripts/js/eslint-stats-by-type.js
  43. 4
      scripts/js/log.js

61
CLAUDE.md

@ -0,0 +1,61 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## What this is
A Drupal 10 custom theme (`olivesbooks`) forked from Drupal core's Olivero theme, deployed to the "Book Lives @UPEI" site (booklives2.islandarchives.ca) — part of the Islandimagined / IslandArchives ecosystem. The `description` in `olivesbooks.info.yml` literally says "THIS IS A CLONE OF OLIVERO FOR ISLANDIMAGINED." Expect to see Olivero's structure and naming throughout.
Treat the theme as internal-and-forked: there is no upstream sync workflow. Modifications happen directly in this repo.
## Commands
Node toolchain is pinned to Node 12 via `.nvmrc` (legacy — `nvm use` before running scripts). Package manager is Yarn 4 (`packageManager` field in `package.json`).
- `node bs.js` — Browsersync dev server that proxies the live site (`https://booklives2.islandarchives.ca`) and live-reloads on CSS changes. It rewrites `/themes/custom/olivesbooks` URLs so local CSS edits show up immediately against the remote site. **This is the dev workflow.**
There is no build step. CSS is plain `.css`, JS is plain ES5 — edit files directly and Drupal serves them as-is. The legacy Olivero PostCSS/Babel pipeline (build/watch/lint scripts, `scripts/` dir, `.pcss.css` files) has been removed. The babel/postcss/stylelint/eslint entries in `devDependencies` are leftover from that pipeline and can be pruned when convenient.
## Deployment
`./deploy.sh "commit message"` — commits all local changes, pushes, then SSHes into `booklives2.islandarchives.ca`, `git pull`s the theme directory, and runs `drush cr` to clear caches. The server paths are hardcoded at the top of the script. This is a single-environment workflow — there is no staging.
## Architecture
### PHP side (Drupal hooks)
- `olivesbooks.theme` — the bulk of theme logic: `hook_preprocess_*` for html, page-title, maintenance-page, node, block, menu, form, menu-local-tasks, form-element, input, etc., plus `hook_theme_suggestions_*_alter` (notably for blocks and menus, which add region-aware suggestions like `block__sidebar` / `menu__sidebar`).
- `theme-settings.php` — adds the custom theme-settings form (mobile-menu toggle, site-branding bg color, color picker with named schemes). The primary color is stored as hex and converted to HSL at preprocess time so CSS can use the hue/sat/lightness components via custom properties.
- `olivesbooks.post_update.php` — Drupal post-update hooks (e.g., default primary color).
- `src/OlivesbooksPreRender.php``TrustedCallbackInterface` implementations used as `#pre_render` callbacks (text_format wrappers, message placeholder fallback).
### CSS
Authored CSS lives under `css/{base,components,layout,theme}/` and is **plain vanilla CSS**. Edit `.css` files directly — there is no preprocessor.
### Libraries
`olivesbooks.libraries.yml` defines ~30+ small libraries, each scoping a few CSS/JS files. The `global-styling` library is attached via `info.yml`; others are attached on demand from `hook_preprocess_*` in `olivesbooks.theme` (e.g., the search library is chosen based on which region the search block lives in — `primary_menu` → narrow, `secondary_menu` → wide).
`libraries-override` in `olivesbooks.info.yml` aggressively replaces Drupal core CSS (admin, dropbutton, vertical-tabs, layout_builder columns, oembed formatter). `libraries-extend` layers theme CSS/JS on top of core libraries (messages, collapse, dialog, progress, layout_discovery, node preview, content_moderation). When adding a new component, check these two sections before assuming core styles will load.
### Templates
`templates/` overrides Drupal core templates organized by category (block, content, dataset, field, filter, form, layout, navigation, user, views, etc.). The two top-level overrides worth noting: `maintenance-page.html.twig` (custom maintenance page) and `mirador.html.twig` (IIIF viewer).
### Config
- `config/install/olivesbooks.settings.yml` — default theme settings (loaded on install)
- `config/schema/olivesbooks.schema.yml` — schema for those settings
- `config/optional/` — Drupal block placement defaults, installed if dependencies are met
- `config/mirador_config/`**not** Drupal config; this is the Mirador IIIF viewer's own JS config (`main.js`, `settings.js`) referenced by the mirador integration.
### JS
Plain ES5/jQuery-ish JS under `js/`. The `navigation-*`, `messages`, `search`, `tabs`, `comments`, `color-picker`, `checkbox`, `mirador-mods` files map 1:1 to entries in `olivesbooks.libraries.yml`. Nothing is bundled — files are loaded as-is by Drupal.
## Gotchas
- The CSS color system is driven by a single hex value (`base_primary_color`) converted to HSL at preprocess time and exposed as `--color--primary-hue/saturation/lightness` on `<html>`. All other color tokens derive from this. Adding a "new color" means deriving it from the hue, not hardcoding.
- `mobile_menu_all_widths` theme setting toggles the `is-always-mobile-nav` body class. CSS that targets mobile nav needs to respect this state.
- The theme is marked `@internal` in `olivesbooks.info.yml` (inherited from Olivero) and is not backwards-compatible across minor versions — but since this is a fork, that warning applies to the upstream Olivero, not to us.

24
css/base/fonts.css

@ -121,3 +121,27 @@
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "IM Fell Great Primer";
src: url("../../fonts/IM_Fell_Great_Primer/IMFellGreatPrimer-Regular.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "IM Fell Great Primer";
src: url("../../fonts/IM_Fell_Great_Primer/IMFellGreatPrimer-Italic.woff2") format("woff2");
font-weight: normal;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "IM Fell Great Primer SC";
src: url("../../fonts/IM_Fell_Great_Primer_SC/IMFellGreatPrimerSC-Regular.woff2") format("woff2");
font-weight: normal;
font-style: normal;
font-display: swap;
}

133
css/base/variables.css

@ -12,10 +12,12 @@
/* stylelint-disable */
:root {
--font-im-fell: "IM Fell Great Primer", "georgia", serif;
--font-im-fell-sc: "IM Fell Great Primer SC", "georgia", serif;
/* --font-sans: "metropolis", sans-serif; */
--font-sans: "metropolis", sans-serif;
/* --font-serif: "Lora", "georgia", serif; */
--font-serif: "Lora", "georgia", serif;
--font-sans: var(--font-im-fell);
--font-serif: var(--font-im-fell-sc);
/* Typography helpers. */
--font-size-base: 1rem;
@ -47,8 +49,13 @@
--grid-col-count: 6;
--grid-gap: var(--sp);
--grid-gap-count: calc(var(--grid-col-count) - 1); /* Count of grid-gaps. */
--grid-full-width: calc(100vw - var(--sp2) - var(--scrollbar-width)); /* Width of the entire grid. */
--grid-col-width: calc((var(--grid-full-width) - (var(--grid-gap-count) * var(--grid-gap))) / var(--grid-col-count));
--grid-full-width: calc(
100vw - var(--sp2) - var(--scrollbar-width)
); /* Width of the entire grid. */
--grid-col-width: calc(
(var(--grid-full-width) - (var(--grid-gap-count) * var(--grid-gap))) /
var(--grid-col-count)
);
/* Layout helpers */
--sp0-25: calc(0.25 * var(--sp));
@ -76,16 +83,56 @@
*/
--color--gray-hue: 201;
--color--gray-saturation: 15%;
--color--gray-5: hsl(var(--color--gray-hue), var(--color--gray-saturation), 5%); /* Black */
--color--gray-10: hsl(var(--color--gray-hue), var(--color--gray-saturation), 11%);
--color--gray-20: hsl(var(--color--gray-hue), var(--color--gray-saturation), 20%); /* Black 2 */
--color--gray-45: hsl(var(--color--gray-hue), var(--color--gray-saturation), 44%); /* Gray Dark */
--color--gray-60: hsl(var(--color--gray-hue), var(--color--gray-saturation), 57%); /* Gray medium */
--color--gray-65: hsl(var(--color--gray-hue), var(--color--gray-saturation), 63%); /* Black 4 */
--color--gray-70: hsl(var(--color--gray-hue), var(--color--gray-saturation), 72%); /* Gray medium 2 */
--color--gray-90: hsl(var(--color--gray-hue), var(--color--gray-saturation), 88%); /* Gray light */
--color--gray-95: hsl(var(--color--gray-hue), var(--color--gray-saturation), 93%); /* Gray light 1 */
--color--gray-100: hsl(var(--color--gray-hue), var(--color--gray-saturation), 97%);
--color--gray-5: hsl(
var(--color--gray-hue),
var(--color--gray-saturation),
5%
); /* Black */
--color--gray-10: hsl(
var(--color--gray-hue),
var(--color--gray-saturation),
11%
);
--color--gray-20: hsl(
var(--color--gray-hue),
var(--color--gray-saturation),
20%
); /* Black 2 */
--color--gray-45: hsl(
var(--color--gray-hue),
var(--color--gray-saturation),
44%
); /* Gray Dark */
--color--gray-60: hsl(
var(--color--gray-hue),
var(--color--gray-saturation),
57%
); /* Gray medium */
--color--gray-65: hsl(
var(--color--gray-hue),
var(--color--gray-saturation),
63%
); /* Black 4 */
--color--gray-70: hsl(
var(--color--gray-hue),
var(--color--gray-saturation),
72%
); /* Gray medium 2 */
--color--gray-90: hsl(
var(--color--gray-hue),
var(--color--gray-saturation),
88%
); /* Gray light */
--color--gray-95: hsl(
var(--color--gray-hue),
var(--color--gray-saturation),
93%
); /* Gray light 1 */
--color--gray-100: hsl(
var(--color--gray-hue),
var(--color--gray-saturation),
97%
);
/**
* Primary colors.
@ -95,11 +142,55 @@
--color--primary-hue: 202;
--color--primary-saturation: 79%;
--color--primary-lightness: 50;
--color--primary-30: hsl(var(--color--primary-hue), var(--color--primary-saturation), calc(1% * (var(--color--primary-lightness) - (0.36 * var(--color--primary-lightness)))));
--color--primary-40: hsl(var(--color--primary-hue), var(--color--primary-saturation), calc(1% * (var(--color--primary-lightness) - (0.24 * var(--color--primary-lightness))))); /* Blue dark */
--color--primary-50: hsl(var(--color--primary-hue), var(--color--primary-saturation), calc(1% * var(--color--primary-lightness))); /* Blue medium */
--color--primary-60: hsl(var(--color--primary-hue), var(--color--primary-saturation), calc(1% * (var(--color--primary-lightness) + (0.24 * (100 - var(--color--primary-lightness)))))); /* Blue bright */
--color--primary-80: hsl(var(--color--primary-hue), var(--color--primary-saturation), calc(1% * (var(--color--primary-lightness) + (0.85 * (100 - var(--color--primary-lightness))))));
--color--primary-30: hsl(
var(--color--primary-hue),
var(--color--primary-saturation),
calc(
1% *
(
var(--color--primary-lightness) -
(0.36 * var(--color--primary-lightness))
)
)
);
--color--primary-40: hsl(
var(--color--primary-hue),
var(--color--primary-saturation),
calc(
1% *
(
var(--color--primary-lightness) -
(0.24 * var(--color--primary-lightness))
)
)
); /* Blue dark */
--color--primary-50: hsl(
var(--color--primary-hue),
var(--color--primary-saturation),
calc(1% * var(--color--primary-lightness))
); /* Blue medium */
--color--primary-60: hsl(
var(--color--primary-hue),
var(--color--primary-saturation),
calc(
1% *
(
var(--color--primary-lightness) +
(0.24 * (100 - var(--color--primary-lightness)))
)
)
); /* Blue bright */
--color--primary-80: hsl(
var(--color--primary-hue),
var(--color--primary-saturation),
calc(
1% *
(
var(--color--primary-lightness) +
(0.85 * (100 - var(--color--primary-lightness)))
)
)
);
/**
* Variables specific to text.
@ -159,7 +250,9 @@
@media ((((min-width: 60rem)))) {
:root {
--grid-full-width: calc(100vw - var(--scrollbar-width) - var(--content-left) - var(--sp4));
--grid-full-width: calc(
100vw - var(--scrollbar-width) - var(--content-left) - var(--sp4)
);
}
}

57
css/components/navigation/pcss/menu-sidebar.pcss.css

@ -1,57 +0,0 @@
/**
* @file
* Styles for menu placed in sidebar region.
*/
@import "../../base/media-queries.pcss.css";
.menu--sidebar {
list-style: none;
& .menu {
list-style: none;
}
& .menu--level-1 {
margin: 0;
}
& .menu__link {
position: relative;
display: block;
padding-block: var(--sp0-75);
padding-inline-start: 0;
padding-inline-end: 0;
font-family: var(--font-serif);
font-size: 18px;
/* Bottom divider line. */
&::after {
position: absolute;
inset-block-end: 0;
inset-inline-start: 0;
width: var(--sp4);
height: 0;
content: "";
border-block-start: solid 2px var(--color--gray-95);
}
}
& .menu__link--link {
text-decoration: none;
color: var(--color-text-neutral-loud);
font-weight: 600;
&:hover {
color: var(--color--primary-50);
}
}
/* No bottom divider line for last menu item. */
& .menu__item--level-1:last-child > .menu__link:last-child,
& .menu__item--level-1:last-child > .menu__item--level-2:last-child > .menu__link:last-child {
&::after {
content: none;
}
}
}

102
css/components/navigation/pcss/nav-button-mobile.pcss.css

@ -1,102 +0,0 @@
/**
* @file
* Nav Button Mobile.
*/
@import "../../base/media-queries.pcss.css";
.mobile-nav-button {
position: relative;
z-index: 505; /* Appear above mobile nav. */
display: flex;
align-items: center;
align-self: center;
width: var(--sp2);
height: var(--sp2);
margin-inline-start: auto;
margin-inline-end: -6px;
padding-block: 0;
padding-inline-start: 6px;
padding-inline-end: 6px;
cursor: pointer;
border: none;
background: transparent;
appearance: none;
&:focus {
outline: solid 2px var(--color--primary-40);
}
&:active {
color: inherit; /* Override Safari's default UA styles. */
}
@media (--sm) {
display: inline-flex;
width: auto;
padding-inline-start: var(--sp);
}
}
/* Text that says "menu". */
.mobile-nav-button__label {
position: absolute;
display: block;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
width: 1px;
height: 1px;
word-wrap: normal;
@media (--sm) {
position: static;
overflow: visible;
clip: auto;
width: auto;
height: auto;
margin-inline-end: 12px;
letter-spacing: 0.05em;
font-size: 14px;
font-weight: 600;
}
}
.mobile-nav-button__icon {
position: relative;
display: block;
width: var(--sp2);
height: 0;
border-top: solid 3px var(--color--primary-50);
&::before,
&::after {
position: absolute;
inset-block-start: 0;
inset-inline-start: 0;
width: 100%;
height: 0;
content: "";
transition: transform 0.2s;
border-top: solid 3px var(--color--primary-50);
}
&::before {
transform: translateY(-11px);
}
&::after {
transform: translateY(5px);
}
}
.mobile-nav-button[aria-expanded="true"] .mobile-nav-button__icon {
border-top-color: transparent;
&::before {
transform: translateY(-3px) rotate(-45deg);
}
&::after {
transform: translateY(-3px) rotate(45deg);
}
}

131
css/components/navigation/pcss/nav-primary-button.pcss.css

@ -1,131 +0,0 @@
/**
* @file
* Button that expands second level nav when clicked.
*/
@import "../../base/media-queries.pcss.css";
.primary-nav__button-toggle {
position: relative;
overflow: hidden;
width: var(--sp2);
height: var(--sp2);
margin-block-start: var(--sp0-5); /* Visually align button with menu link text. */
padding-block: 0;
padding-inline-start: 0;
padding-inline-end: 0;
cursor: pointer;
text-indent: -999px;
border: 0;
background: transparent;
-webkit-appearance: none;
&:focus {
outline: auto 2px var(--color--primary-50);
outline-offset: 2px;
}
& .icon--menu-toggle {
position: absolute;
/* stylelint-disable csstools/use-logical */
top: 50%;
left: 50%;
/* stylelint-enable csstools/use-logical */
width: 16px;
height: 16px;
transition: background-color 0.2s;
transform: translate(-50%, -50%);
border-radius: 2px;
&::before,
&::after {
position: absolute;
/* stylelint-disable csstools/use-logical */
top: 50%;
left: 50%;
/* stylelint-enable csstools/use-logical */
width: var(--sp);
height: 0;
content: "";
transform: translate(-50%, -50%);
/* Intentionally not using CSS logical properties. */
border-top: solid 3px var(--color--primary-50);
}
&::after {
transition: opacity 0.2s;
transform: translate(-50%, -50%) rotate(90deg);
}
}
&[aria-expanded="true"] .icon--menu-toggle::after {
opacity: 0;
}
/* aria-hidden attribute is removed by JS. Button is non-functional
until JS is enabled.
*/
&[aria-hidden="true"] {
pointer-events: none;
}
}
body:not(.is-always-mobile-nav) {
@media (--nav) {
& .primary-nav__button-toggle {
flex-shrink: 0;
align-self: stretch;
width: calc(var(--sp2) + 8px);
height: auto;
margin-block-start: 0;
margin-inline-end: calc(-1 * var(--sp2));
&:focus {
border: 0;
outline: 0;
& .icon--menu-toggle {
border: solid 2px var(--color--primary-40);
}
}
&:active {
/* Necessary for Safari. */
color: currentColor;
}
&[aria-expanded="true"] .icon--menu-toggle::after {
opacity: 0.8;
}
& .icon--menu-toggle {
inset-inline-start: 3px;
width: 18px;
transform: translateY(-50%);
border-radius: 4px;
background-color: var(--color--white);
&::before {
content: none;
}
&::after {
/* stylelint-disable csstools/use-logical */
top: calc(50% - 2px);
left: 3px;
/* stylelint-enable csstools/use-logical */
width: 8px;
height: 8px;
content: "";
transform: translateY(-50%) rotate(45deg);
opacity: 0.8;
/* Intentionally not using CSS logical properties. */
border-top: none;
border-right: solid 2px currentColor;
border-bottom: solid 2px currentColor;
background: transparent;
}
}
}
}
}

150
css/components/navigation/pcss/nav-primary-no-js.pcss.css

@ -1,150 +0,0 @@
/**
* @file
* Styles for primary navigation when JavaScript is disabled.
*/
@import "../../base/media-queries.pcss.css";
:root {
--no-js-nav-column-width: 300px;
--no-js-nav-column-gap: var(--sp2);
}
html:not(.js) {
/**
* Mobile styles for primary navigation when JS is disabled.
*/
@media (--max-nav) {
& .primary-nav__menu--level-1 {
column-width: var(--no-js-nav-column-width);
column-gap: var(--no-js-nav-column-gap);
}
& .primary-nav__menu-item {
break-inside: avoid;
}
& .site-header__inner__container {
flex-wrap: wrap;
}
& .mobile-buttons {
display: none;
}
& .header-nav {
position: static;
visibility: visible;
flex-basis: 100%;
width: 100%;
max-width: none;
margin-block: var(--sp2) 0;
margin-inline-start: var(--sp2);
margin-inline-end: var(--sp2);
padding-block: var(--sp2) 0;
padding-inline-start: var(--sp2);
padding-inline-end: var(--sp2);
transform: none;
border: solid 1px var(--color--gray-95) !important;
box-shadow: 0 0 36px var(--color--gray-90);
}
& .primary-nav__menu--level-2 {
border-inline-start: 0;
}
& .primary-nav__button-toggle {
display: none;
}
& .primary-nav__menu-link--button.primary-nav__menu-link--has-children {
&::before,
&::after {
content: none;
}
}
}
@media (--nav) {
/**
* Styles for 'always on mobile navigation' when JS is disabled.
*/
& body.is-always-mobile-nav {
& .primary-nav__menu--level-1 {
column-width: var(--no-js-nav-column-width);
column-gap: var(--no-js-nav-column-gap);
}
& .primary-nav__menu-item {
break-inside: avoid;
}
& .site-header__inner__container {
flex-wrap: wrap;
}
& .mobile-buttons {
display: none;
}
& .header-nav {
position: static;
visibility: visible;
flex-basis: 100%;
width: 100%;
max-width: none;
margin-block: var(--sp2) 0;
margin-inline-start: var(--sp2);
margin-inline-end: var(--sp2);
padding-block: var(--sp2) 0;
padding-inline-start: var(--sp2);
padding-inline-end: var(--sp2);
transform: none;
border: solid 1px var(--color--gray-95) !important;
box-shadow: 0 0 36px var(--color--gray-90);
}
& .primary-nav__menu--level-2 {
border-inline-start: 0;
}
& .primary-nav__button-toggle {
display: none;
}
& .primary-nav__menu-link--button.primary-nav__menu-link--has-children {
&::before,
&::after {
content: none;
}
}
}
/**
* Styles for traditional dropdown primary navigation when JS is disabled.
*/
& body:not(.is-always-mobile-nav) {
& .primary-nav__menu-item--level-1:hover {
& .primary-nav__menu--level-2,
& .primary-nav__menu-🥕 {
visibility: visible;
transform: translate(-50%, 0);
opacity: 1;
}
}
/*
* Cannot combine the focus-within pseudo selector with other selectors,
* because it will break IE11 and earlier versions of MS Edge.
*/
& .primary-nav__menu-item--level-1:focus-within {
& .primary-nav__menu--level-2,
& .primary-nav__menu-🥕 {
visibility: visible;
transform: translate(-50%, 0);
opacity: 1;
}
}
}
}
}

225
css/components/navigation/pcss/nav-primary-wide.pcss.css

@ -1,225 +0,0 @@
/**
* @file
* Nav Primary (wide widths).
*/
@import "../../base/media-queries.pcss.css";
body:not(.is-always-mobile-nav) {
@media (--nav) {
& .primary-nav__menu-item {
flex-wrap: nowrap; /* Ensure that sub navigation toggle button doesn't wrap underneath link. */
&.primary-nav__menu-item--has-children {
& .primary-nav__menu-link--link,
& .primary-nav__menu-link--nolink {
flex-basis: auto;
}
/* Remove hover state if submenu exists. */
& .primary-nav__menu-link--level-1 .primary-nav__menu-link-inner::after {
content: none;
}
}
}
& .primary-nav__menu-link {
letter-spacing: 0.02em;
font-size: 16px;
line-height: var(--sp1-5);
&:focus {
position: relative;
outline: 0;
&::before {
position: absolute;
top: 50%;
left: 50%;
width: calc(100% + var(--sp));
height: var(--sp3);
content: "";
transform: translate(-50%, -50%);
border: solid 2px var(--color--primary-50);
border-radius: 4px;
}
}
}
& .primary-nav__menu-link--button {
&.primary-nav__menu-link--has-children {
overflow: visible; /* Necessary to view icon in IE11 */
padding-inline-end: 9px;
&:focus::before {
width: calc(100% + var(--sp1-5));
content: "";
}
&::before {
content: none;
}
/* Chevron icon for desktop navigation. */
&::after {
position: absolute;
inset-block-start: 50%;
inset-inline-start: calc(100% - 3px);
width: 8px;
height: 8px;
margin-block-start: -2px;
transform: translateY(-50%) rotate(45deg);
/* Intentionally not using CSS logical properties. */
border-top: 0;
border-right: solid 2px currentColor;
border-bottom: solid 2px currentColor;
}
&[aria-expanded="true"]::after {
opacity: 1;
}
}
}
& .primary-nav__menu-link-inner {
padding-block: var(--sp2);
padding-inline-start: 0;
padding-inline-end: 0;
&::after {
transform-origin: center;
border-top-width: var(--sp0-5);
}
}
& .primary-nav__menu--level-1 {
display: flex;
align-items: stretch;
margin-inline-end: var(--sp);
}
& .primary-nav__menu-item--level-1 {
position: relative; /* Anchor secondary menu */
display: flex;
align-items: center;
width: max-content;
max-width: 200px;
margin-block: 0;
margin-inline-start: 0;
margin-inline-end: 0;
&:not(:last-child) {
margin-inline-end: var(--sp2);
}
}
& .primary-nav__menu--level-2 {
position: absolute;
z-index: 105; /* Appear above search container. */
top: calc(100% - (0.5 * var(--sp)));
left: 50%;
visibility: hidden;
overflow: auto;
width: 250px;
/* Ensure that long level-2 menus will never overflow viewport (focused
* elements should always be in viewport per accessibility guidelines). */
max-height: calc(100vh - var(--site-header-height-wide) - var(--drupal-displace-offset-top, 0px) - var(--drupal-displace-offset-bottom, 0px) - var(--sp));
margin-block-start: 0;
margin-inline-start: 0;
padding-block: calc(3 * var(--sp));
padding-inline-start: var(--sp2);
padding-inline-end: var(--sp2);
transition: none;
transform: translate(-50%, -20px);
opacity: 0;
/* Intentionally not using CSS logical properties. */
border-top: solid var(--color--primary-50) var(--sp0-5);
border-right: solid 1px transparent; /* Transparent borders useful for Windows High Contrast mode. */
border-bottom: solid 1px transparent;
border-left: solid 1px transparent;
border-radius: 0 0 2px 2px;
background: var(--color--white);
box-shadow: 0 1px 36px rgba(0, 0, 0, 0.08);
&.is-active-menu-parent {
visibility: visible;
margin-block-start: 0;
transform: translate(-50%, 0);
opacity: 1;
}
}
& .primary-nav__menu-link--level-2 {
display: block;
&:focus::before {
top: 0;
left: calc(var(--sp0-5) * -1);
height: 100%;
transform: none;
}
& .primary-nav__menu-link-inner {
padding-block: var(--sp0-5);
padding-inline-start: 0;
padding-inline-end: 0;
&::after {
transform-origin: left; /* LTR */
border-top-width: 3px;
&:dir(rtl) {
transform-origin: right;
}
}
}
}
/**
* Arrow is placed outside of submenu because the submenu has the
* `overflow: hidden` CSS rule applied.
*/
& .primary-nav__menu-🥕 {
position: absolute;
z-index: 105; /* Match level 2 menus. */
top: calc(100% - var(--sp));
left: 50%;
visibility: hidden;
width: 0;
height: 0;
transform: translate(-50%, -20px);
opacity: 0;
/* Intentionally not using CSS logical properties. */
border-right: solid 10px transparent;
border-bottom: solid 10px var(--color--primary-50);
border-left: solid 10px transparent;
&.is-active-menu-parent {
visibility: visible;
transform: translate(-50%, 0);
opacity: 1;
}
}
/**
* When ensuring that long menus don't overflow viewport, we can give a
* little extra room when the toolbar is fixed (and is shorter).
*/
& .is-fixed .primary-nav__menu--level-2 {
max-height: calc(100vh - var(--site-header-height-wide) - var(--drupal-displace-offset-top, 0px) - var(--drupal-displace-offset-bottom, 0px) - var(--sp) + var(--sp4));
}
}
}
/*
* Only apply transition styles to menu when JS is loaded. This
* works around https://bugs.chromium.org/p/chromium/issues/detail?id=332189
*/
html.js body:not(.is-always-mobile-nav) {
@media (--nav) {
& .primary-nav__menu--level-2,
& .primary-nav__menu-🥕 {
transition: visibility 0.2s, transform 0.2s, opacity 0.2s;
}
}
}

201
css/components/navigation/pcss/nav-primary.pcss.css

@ -1,201 +0,0 @@
/**
* @file
* Nav Primary.
*/
@import "../../base/media-queries.pcss.css";
.primary-nav__menu {
flex-wrap: wrap;
list-style: none;
}
.primary-nav__menu-item {
margin-block-end: var(--sp0-5);
&:last-child {
margin-block-end: 0;
}
&.primary-nav__menu-item--has-children {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
& .primary-nav__menu-link--link,
& .primary-nav__menu-link--nolink {
/* Ensure that long text doesn't make the mobile expand button wrap. */
flex-basis: calc(100% - var(--sp3));
}
}
}
.primary-nav__menu-link {
flex-grow: 1;
text-decoration: none;
color: var(--color-text-neutral-loud);
font-size: 28px;
font-weight: bold;
line-height: var(--sp2);
&:hover {
color: inherit;
}
&:focus {
outline: auto 2px var(--color--primary-50);
outline-offset: 2px;
}
}
.primary-nav__menu-link--nolink {
padding-block: var(--sp0-5);
padding-inline-start: 0;
padding-inline-end: 0;
color: var(--color-text-neutral-soft);
font-weight: normal;
}
.primary-nav__menu-link--button {
position: relative;
padding-block: 0;
padding-inline-start: 0;
padding-inline-end: 0;
cursor: pointer;
text-align: start;
border: 0;
background: transparent;
/* Plus icon for mobile navigation. */
&.primary-nav__menu-link--has-children {
padding-inline-end: var(--sp3); /* Ensure text does not overlap icon. */
&::before,
&::after {
position: absolute;
inset-inline-end: 9px;
inset-block-start: calc(var(--sp0-5) + 17px); /* Visually align button with menu link text. */
width: 18px;
height: 0;
content: "";
/* Intentionally not using CSS logical properties. */
border-top: solid 3px var(--color--primary-50);
}
&::after {
transition: opacity 0.2s;
transform: rotate(90deg);
}
&[aria-expanded="true"]::after {
opacity: 0;
}
}
}
.primary-nav__menu-link-inner {
position: relative;
display: inline-flex;
align-items: center;
padding-block: var(--sp0-5);
padding-inline-start: 0;
padding-inline-end: 0;
&::after {
position: absolute;
inset-block-end: 0;
inset-inline-start: 0;
width: 100%;
height: 0;
content: "";
transition: transform 0.2s;
transform: scaleX(0);
transform-origin: left;
/* Intentionally not using CSS logical properties. */
border-top: solid 5px var(--color--primary-50);
}
&::after {
@nest .primary-nav__menu-link:hover & {
transform: scaleX(1);
}
}
}
/*
Top level specific styles.
*/
.primary-nav__menu--level-1 {
margin-block: 0;
margin-inline-start: 0;
margin-inline-end: 0;
padding-block: 0;
padding-inline-start: 0;
padding-inline-end: 0;
}
.primary-nav__menu-link--level-1 {
position: relative;
display: flex;
letter-spacing: -1px;
}
/*
Secondary menu specific styles.
*/
.primary-nav__menu--level-2 {
visibility: hidden;
overflow: hidden;
flex-basis: 100%;
max-height: 0;
margin-block: 0;
margin-inline-start: calc(-1 * var(--sp));
padding-inline-start: var(--sp2-5);
transition: opacity 0.2s, visibility 0.2s, max-height 0.2s;
opacity: 0;
border-inline-start: solid var(--sp) var(--color--primary-50);
&.is-active-menu-parent {
visibility: visible;
max-height: none;
margin-block-start: var(--sp1-5);
opacity: 1;
}
@media (--md) {
margin-inline-start: calc(-1 * var(--sp3));
padding-inline-start: var(--sp3);
}
}
/*
* Olivesbooks doesn't officially support nested tertiary submenus, but this
* ensures that it doesn't break all the way.
*
* @see https://www.drupal.org/project/drupal/issues/3221399
*/
.primary-nav__menu--level-2 .primary-nav__menu-item--has-children {
display: block;
}
.primary-nav__menu-link--level-2 {
font-size: 16px;
font-weight: normal;
line-height: var(--sp);
}
html:not(.js) {
& .primary-nav__menu--level-2 {
visibility: visible;
max-height: none;
opacity: 1;
}
}
[dir="rtl"] {
& .primary-nav__menu-link-inner {
&::after {
transform-origin: right;
}
}
}

114
css/components/navigation/pcss/nav-secondary.pcss.css

@ -1,114 +0,0 @@
/**
* @file
* Secondary navigation styling.
*/
@import "../../base/media-queries.pcss.css";
.secondary-nav {
letter-spacing: 0.02em;
font-size: var(--font-size-s);
font-weight: 600;
}
.secondary-nav__menu {
display: flex;
align-items: center;
margin-block: 0;
margin-inline-start: 0;
margin-inline-end: 0;
padding-block: 0;
padding-inline-start: 0;
padding-inline-end: 0;
list-style: none;
}
.secondary-nav__menu-item {
/* Parent element is set to flex-basis: 0. We
* don't want text to wrap unless it goes over a
* certain arbitrary width.
*/
/* @todo should this be scoped to desktop nav? */
width: max-content;
max-width: 200px;
&:not(:last-child) {
margin-inline-end: var(--sp1-5);
}
}
.secondary-nav__menu-link {
position: relative;
display: inline-flex;
align-items: center;
height: var(--sp2);
text-decoration: none;
color: inherit;
&::after {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 0;
content: "";
transition: opacity 0.2s, transform 0.2s;
transform: translateY(5px);
opacity: 0;
/* Intentionally not using CSS logical properties. */
border-top: solid 2px currentColor;
}
&:hover {
&::after {
transform: translateY(0);
opacity: 0.8;
}
}
}
body:not(.is-always-mobile-nav) {
@media (--nav) {
& .secondary-nav {
position: relative;
display: flex;
margin-inline-start: var(--sp);
padding-inline-start: var(--sp2);
&::before {
position: absolute;
inset-block-start: 50%;
inset-inline-start: 0;
width: 2px;
height: var(--sp2);
content: "";
transform: translateY(-50%);
background-color: var(--color--gray-90);
}
}
& .secondary-nav__menu-item:not(:last-child) {
margin-inline-end: var(--sp2);
}
& .secondary-nav__menu-link {
&:focus {
position: relative;
outline: 0;
&::before {
position: absolute;
top: 50%;
left: 50%;
width: calc(100% + var(--sp));
height: var(--sp3);
content: "";
transform: translate(-50%, -50%);
border: solid 2px var(--color--primary-50);
border-radius: 4px;
}
}
}
}
}

109
css/components/navigation/pcss/wide-nav-expand.pcss.css

@ -1,109 +0,0 @@
/**
* @file
* Button which expands the navigation at wide viewport widths.
*/
@import "../../base/media-queries.pcss.css";
.wide-nav-expand {
display: none;
@media (--nav) {
display: flex;
visibility: hidden;
flex-shrink: 0;
align-items: center;
justify-content: center;
width: var(--content-left);
height: var(--sp6);
cursor: pointer;
pointer-events: auto;
color: var(--color--white);
border: 0;
background-color: var(--color--primary-50);
&:focus {
outline: solid 2px currentColor;
outline-offset: -4px;
}
}
}
body:not(.is-always-mobile-nav) .is-fixed .wide-nav-expand {
@media (--nav) {
visibility: visible;
}
}
body.is-always-mobile-nav .wide-nav-expand {
@media (--nav) {
visibility: hidden;
}
}
.wide-nav-expand__icon {
position: relative;
width: var(--sp2);
height: 21px;
transition: opacity 0.2s;
pointer-events: none;
opacity: 0;
transform-style: preserve-3d;
& > span {
display: block;
height: 0;
/* Intentionally not using CSS logical properties. */
border-top: solid 3px currentColor;
&:nth-child(1) {
position: absolute;
inset-block-start: 0;
inset-inline-start: 0;
width: 100%;
height: 0;
transition: transform 0.2s;
background-color: currentColor;
}
&:nth-child(2) {
position: absolute;
inset-block-start: 9px;
inset-inline-start: 0;
width: 100%;
height: 0;
transition: opacity 0.2s;
background-color: currentColor;
}
&:nth-child(3) {
position: absolute;
inset-block: auto 0;
inset-inline-start: 0;
width: 100%;
height: 0;
transition: transform 0.2s;
background-color: currentColor;
}
}
}
.is-fixed .wide-nav-expand__icon {
opacity: 1;
}
[aria-expanded="true"] .wide-nav-expand__icon {
& > span:nth-child(1) {
inset-block-start: 9px;
transform: rotate(-45deg);
}
& > span:nth-child(2) {
opacity: 0;
}
& > span:nth-child(3) {
inset-block-start: 9px;
transform: rotate(45deg);
}
}

BIN
fonts/IM_Fell_Great_Primer/IMFellGreatPrimer-Italic.woff2

Binary file not shown.

BIN
fonts/IM_Fell_Great_Primer/IMFellGreatPrimer-Regular.woff2

Binary file not shown.

93
fonts/IM_Fell_Great_Primer/OFL.txt

@ -0,0 +1,93 @@
Copyright (c) 2010, Igino Marini (mail@iginomarini.com)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

BIN
fonts/IM_Fell_Great_Primer_SC/IMFellGreatPrimerSC-Regular.woff2

Binary file not shown.

93
fonts/IM_Fell_Great_Primer_SC/OFL.txt

@ -0,0 +1,93 @@
Copyright (c) 2010, Igino Marini (mail@iginomarini.com)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

BIN
fonts/LibreBodoni-Bold.woff

Binary file not shown.

BIN
fonts/LibreBodoni-Bold.woff2

Binary file not shown.

BIN
fonts/LibreBodoni-Italic.woff

Binary file not shown.

BIN
fonts/LibreBodoni-Italic.woff2

Binary file not shown.

BIN
fonts/LibreBodoni-Regular.woff

Binary file not shown.

BIN
fonts/LibreBodoni-Regular.woff2

Binary file not shown.

BIN
fonts/SourceSans3-Bold.woff

Binary file not shown.

BIN
fonts/SourceSans3-Bold.woff2

Binary file not shown.

BIN
fonts/SourceSans3-Italic.woff

Binary file not shown.

BIN
fonts/SourceSans3-Italic.woff2

Binary file not shown.

BIN
fonts/SourceSans3-Regular.woff

Binary file not shown.

BIN
fonts/SourceSans3-Regular.woff2

Binary file not shown.

42
package.json

@ -4,19 +4,7 @@
"yarn": ">= 1.6",
"node": ">= 12.0"
},
"scripts": {
"build": "yarn build:css & yarn build:js",
"watch": "yarn watch:css & yarn watch:js",
"build:css": "cross-env BABEL_ENV=legacy node ./scripts/css/postcss-build.js",
"watch:css": "cross-env BABEL_ENV=legacy node ./scripts/css/postcss-watch.js",
"build:js": "cross-env BABEL_ENV=legacy node ./scripts/js/babel-es6-build.js",
"build:js-dev": "cross-env NODE_ENV=development BABEL_ENV=legacy node ./scripts/js/babel-es6-build.js",
"watch:js": "cross-env BABEL_ENV=legacy node ./scripts/js/babel-es6-watch.js",
"watch:js-dev": "cross-env NODE_ENV=development BABEL_ENV=legacy node ./scripts/js/babel-es6-watch.js",
"lint:css": "stylelint \"**/*.css\"",
"lint:css-checkstyle": "stylelint \"**/*.css\" --custom-formatter ./node_modules/stylelint-checkstyle-formatter/index.js",
"prettier": "prettier --write \"./**/*.es6.js\" \"./tests/Drupal/Nightwatch/**/*.js\""
},
"scripts": {},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
@ -54,34 +42,6 @@
"stylelint-order": "^4.0.0",
"terser": "^5.3.4"
},
"//": "'development is the default environment, and legacy is for transpiling the old jQuery codebase",
"babel": {
"env": {
"development": {
"presets": [
[
"@babel/preset-env",
{
"modules": "commonjs",
"targets": {
"node": "current"
}
}
]
]
},
"legacy": {
"presets": [
[
"@babel/preset-env",
{
"modules": false
}
]
]
}
}
},
"browserslist": [
"last 2 Chrome major versions",
"last 2 Firefox major versions",

15
scripts/css/changeOrAdded.js

@ -1,15 +0,0 @@
const fs = require('fs');
const log = require('./log');
const compile = require('./compile');
module.exports = (filePath) => {
log(`'${filePath}' is being processed.`);
// Transform the file.
compile(filePath, function write(code) {
const fileName = filePath.slice(0, -9);
// Write the result to the filesystem.
fs.writeFile(`${fileName}.css`, code, () => {
log(`'${filePath}' is finished.`);
});
});
};

23
scripts/css/check.js

@ -1,23 +0,0 @@
const chalk = require('chalk');
const fs = require('fs');
const log = require('./log');
const compile = require('./compile');
module.exports = (filePath) => {
log(`'${filePath}' is being checked.`);
// Transform the file.
compile(filePath, function check(code) {
const fileName = filePath.slice(0, -9);
fs.readFile(`${fileName}.css`, function read(err, data) {
if (err) {
log(chalk.red(err));
process.exitCode = 1;
return;
}
if (code !== data.toString()) {
log(chalk.red(`'${filePath}' is not updated.`));
process.exitCode = 1;
}
});
});
};

88
scripts/css/compile.js

@ -1,88 +0,0 @@
const chalk = require('chalk');
const log = require('./log');
const fs = require('fs');
const postcss = require('postcss');
const postcssCalc = require("postcss-calc");
const postcssImport = require('postcss-import');
const postcssHeader = require('postcss-header');
const postcssUrl = require('postcss-url');
const postcssPresetEnv = require('postcss-preset-env');
// cspell:ignore pxtorem
const postcssPixelsToRem = require('postcss-pxtorem');
module.exports = (filePath, callback) => {
// Transform the file.
fs.readFile(filePath, (err, css) => {
postcss([
postcssImport({
plugins: [
// On import, remove the comments from variables.pcss.css so they don't
// appear as useless comments at the top files that import these
// variables.
postcss.plugin('remove-unwanted-comments-from-variables', (options) => {
return css => {
if (css.source.input.file.indexOf('variables.pcss.css') !== -1) {
css.walk(node => {
if (node.type === 'comment') {
node.remove();
}
});
}
};
}),
],
}),
postcssPresetEnv({
stage: 1,
preserve: false,
autoprefixer: {
cascade: false,
grid: 'no-autoplace',
},
features: {
'blank-pseudo-class': false,
'focus-visible-pseudo-class': false,
'focus-within-pseudo-class': false,
'has-pseudo-class': false,
'image-set-function': false,
'prefers-color-scheme-query': false,
}
}),
postcssCalc,
postcssPixelsToRem({
propList: [
'*',
'!background-position',
'!border',
'!border-width',
'!box-shadow',
'!border-top*',
'!border-right*',
'!border-bottom*',
'!border-left*',
'!border-start*',
'!border-end*',
'!outline*',
],
mediaQuery: true,
minPixelValue: 3,
}),
postcssHeader({
header: `/*\n * DO NOT EDIT THIS FILE.\n * See the following change record for more information,\n * https://www.drupal.org/node/3084859\n * @preserve\n */\n`,
}),
postcssUrl({
filter: '**/*.svg',
url: 'inline',
optimizeSvgEncode: true,
})
])
.process(css, { from: filePath })
.then(result => {
callback(result.css);
})
.catch(error => {
log(chalk.red(error));
process.exitCode = 1;
});
});
};

4
scripts/css/log.js

@ -1,4 +0,0 @@
module.exports = (message) => {
// Logging human-readable timestamp.
console.log(`[${new Date().toTimeString().slice(0, 8)}] ${message}`);
};

49
scripts/css/postcss-build.js

@ -1,49 +0,0 @@
/**
* @file
*
* Provides the build:css command to compile *.pcss.css files to CSS.
*
* Run build:css with --file to only parse a specific file. Using the --check
* flag build:css can be run to check if files are compiled correctly.
* @example <caption>Only process misc/drupal.es6.js and misc/drupal.init.es6.js</caption
* yarn run build:css -- --file misc/drupal.pcss.css --file misc/drupal.init.pcss.css
* @example <caption>Check if all files have been compiled correctly</caption
* yarn run build:css -- --check
*
* @internal This file is part of the core CSS build process and is only
* designed to be used in that context.
*/
'use strict';
const glob = require('glob');
const argv = require('minimist')(process.argv.slice(2));
const changeOrAdded = require('./changeOrAdded');
const check = require('./check');
const log = require('./log');
// Match only on .pcss.css files.
const fileMatch = './**/*.pcss.css';
// Ignore everything in node_modules
const globOptions = {
ignore: './node_modules/**'
};
const processFiles = (error, filePaths) => {
if (error) {
process.exitCode = 1;
}
// Process all the found files.
let callback = changeOrAdded;
if (argv.check) {
callback = check;
}
filePaths.forEach(callback);
};
if (argv.file) {
processFiles(null, [].concat(argv.file));
}
else {
glob(fileMatch, globOptions, processFiles);
}
process.exitCode = 0;

43
scripts/css/postcss-watch.js

@ -1,43 +0,0 @@
/**
* @file
*
* Watch changes to *.pcss.css files and compile them to CSS during development.
*
* @internal This file is part of the core CSS build process and is only
* designed to be used in that context.
*/
'use strict';
const fs = require('fs');
const path = require('path');
const chokidar = require('chokidar');
const changeOrAdded = require('./changeOrAdded');
const log = require('./log');
// Match only on .pcss.css files.
const fileMatch = './**/*.pcss.css';
// Ignore everything in node_modules
const watcher = chokidar.watch(fileMatch, {
ignoreInitial: true,
ignored: './node_modules/**'
});
const unlinkHandler = (err) => {
if (err) {
log(err);
}
};
// Watch for filesystem changes.
watcher
.on('add', changeOrAdded)
.on('change', changeOrAdded)
.on('unlink', (filePath) => {
const fileName = filePath.slice(0, -9);
fs.stat(`${fileName}.css`, () => {
fs.unlink(`${fileName}.css`, unlinkHandler);
});
})
.on('ready', () => log(`Watching '${fileMatch}' for changes.`));

5
scripts/js/.eslintrc.json

@ -1,5 +0,0 @@
{
"rules": {
"strict": [2, "global"]
}
}

49
scripts/js/babel-es6-build.js

@ -1,49 +0,0 @@
/**
* @file
*
* Provides the build:js command to compile *.es6.js files to ES5.
*
* Run build:js with --file to only parse a specific file. Using the --check
* flag build:js can be run to check if files are compiled correctly.
* @example <caption>Only process misc/drupal.es6.js and misc/drupal.init.es6.js</caption
* yarn run build:js -- --file misc/drupal.es6.js --file misc/drupal.init.es6.js
* @example <caption>Check if all files have been compiled correctly</caption
* yarn run build:js -- --check
*
* @internal This file is part of the core javascript build process and is only
* meant to be used in that context.
*/
'use strict';
const glob = require('glob');
const argv = require('minimist')(process.argv.slice(2));
const changeOrAdded = require('./changeOrAdded');
const check = require('./check');
const log = require('./log');
// Match only on .es6.js files.
const fileMatch = './**/*.es6.js';
// Ignore everything in node_modules
const globOptions = {
ignore: './node_modules/**'
};
const processFiles = (error, filePaths) => {
if (error) {
process.exitCode = 1;
}
// Process all the found files.
let callback = changeOrAdded;
if (argv.check) {
callback = check;
}
filePaths.forEach(callback);
};
if (argv.file) {
processFiles(null, [].concat(argv.file));
}
else {
glob(fileMatch, globOptions, processFiles);
}
process.exitCode = 0;

43
scripts/js/babel-es6-watch.js

@ -1,43 +0,0 @@
/**
* @file
*
* Watch changes to *.es6.js files and compile them to ES5 during development.
*
* @internal This file is part of the core javascript build process and is only
* meant to be used in that context.
*/
'use strict';
const fs = require('fs');
const path = require('path');
const chokidar = require('chokidar');
const changeOrAdded = require('./changeOrAdded');
const log = require('./log');
// Match only on .es6.js files.
const fileMatch = './**/*.es6.js';
// Ignore everything in node_modules
const watcher = chokidar.watch(fileMatch, {
ignoreInitial: true,
ignored: './node_modules/**'
});
const unlinkHandler = (err) => {
if (err) {
log(err);
}
};
// Watch for filesystem changes.
watcher
.on('add', changeOrAdded)
.on('change', changeOrAdded)
.on('unlink', (filePath) => {
const fileName = filePath.slice(0, -7);
fs.stat(`${fileName}.js`, () => {
fs.unlink(`${fileName}.js`, unlinkHandler);
});
})
.on('ready', () => log(`Watching '${fileMatch}' for changes.`));

15
scripts/js/changeOrAdded.js

@ -1,15 +0,0 @@
const fs = require('fs');
const log = require('./log');
const compile = require('./compile');
module.exports = (filePath) => {
log(`'${filePath}' is being processed.`);
// Transform the file.
compile(filePath, function write(code) {
const fileName = filePath.slice(0, -7);
// Write the result to the filesystem.
fs.writeFile(`${fileName}.js`, code, () => {
log(`'${filePath}' is finished.`);
});
});
}

23
scripts/js/check.js

@ -1,23 +0,0 @@
const chalk = require('chalk');
const fs = require('fs');
const log = require('./log');
const compile = require('./compile');
module.exports = (filePath) => {
log(`'${filePath}' is being checked.`);
// Transform the file.
compile(filePath, function check(code) {
const fileName = filePath.slice(0, -7);
fs.readFile(`${fileName}.js`, function read(err, data) {
if (err) {
log(chalk.red(err));
process.exitCode = 1;
return;
}
if (code !== data.toString()) {
log(chalk.red(`'${filePath}' is not updated.`));
process.exitCode = 1;
}
});
});
}

31
scripts/js/compile.js

@ -1,31 +0,0 @@
const chalk = require('chalk');
const log = require('./log');
const babel = require('@babel/core');
module.exports = (filePath, callback) => {
// Transform the file.
// Check process.env.NODE_ENV to see if we should create sourcemaps.
babel.transformFile(
filePath,
{
sourceMaps: process.env.NODE_ENV === 'development' ? 'inline' : false,
comments: false,
plugins: [
['add-header-comment', {
'header': [
`DO NOT EDIT THIS FILE.\nSee the following change record for more information,\nhttps://www.drupal.org/node/2815083\n@preserve`
]
}]
]
},
(err, result) => {
if (err) {
log(chalk.red(err));
process.exitCode = 1;
}
else {
callback(result.code);
}
}
);
};

35
scripts/js/eslint-stats-by-type.js

@ -1,35 +0,0 @@
module.exports = function (results) {
results = results || [];
const errorType = {
warnings: {},
errors: {},
};
results.reduce((result, current) => {
current.messages.forEach((msg) => {
if (msg.severity === 1) {
errorType.warnings[msg.ruleId] = errorType.warnings[msg.ruleId] + 1 || 1
}
if (msg.severity === 2) {
errorType.errors[msg.ruleId] = errorType.errors[msg.ruleId] + 1 || 1
}
});
return result;
});
const reduceErrorCounts = (errorType) => Object.entries(errorType).sort((a, b) => b[1] - a[1])
.reduce((result, current) => result.concat([`${current[0]}: ${current[1]}`]), []).join('\n');
const warnings = reduceErrorCounts(errorType.warnings);
const errors = reduceErrorCounts(errorType.errors);
return `
Errors:
${'='.repeat(30)}
${errors}
${'\n'.repeat(4)}
Warnings:
${'='.repeat(30)}
${warnings}`;
};

4
scripts/js/log.js

@ -1,4 +0,0 @@
module.exports = (message) => {
// Logging human-readable timestamp.
console.log(`[${new Date().toTimeString().slice(0, 8)}] ${message}`);
}
Loading…
Cancel
Save