├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .nvmrc ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── generators ├── app │ ├── index.js │ ├── mc-logo.js │ └── templates │ │ ├── _README.md │ │ ├── _gulp-tasks │ │ ├── clean.js │ │ ├── compile.js │ │ ├── compress.js │ │ ├── concat.js │ │ ├── format.js │ │ ├── lint.js │ │ ├── move.js │ │ └── prettier.js │ │ ├── _gulpfile.js │ │ ├── _helper-components │ │ └── icons │ │ │ ├── assets │ │ │ ├── facebook.svg │ │ │ ├── instagram.svg │ │ │ ├── pinterest.svg │ │ │ ├── twitter.svg │ │ │ └── youtube.svg │ │ │ ├── icons-macro.md │ │ │ ├── icons-macro.twig │ │ │ ├── icons.json │ │ │ ├── icons.md │ │ │ └── icons.twig │ │ ├── _package.json │ │ ├── _patternlab-config.json │ │ ├── _screenshot.png │ │ ├── _src │ │ ├── favicon.ico │ │ ├── patterns │ │ │ ├── components │ │ │ │ ├── .gitkeep │ │ │ │ └── hidden │ │ │ │ │ └── hidden.scss │ │ │ ├── global │ │ │ │ ├── colors │ │ │ │ │ ├── _colors.scss │ │ │ │ │ ├── colors.json │ │ │ │ │ └── colors.twig │ │ │ │ ├── fonts │ │ │ │ │ ├── _fonts.scss │ │ │ │ │ ├── assets │ │ │ │ │ │ ├── lato-bold-webfont.woff │ │ │ │ │ │ ├── lato-bold-webfont.woff2 │ │ │ │ │ │ ├── lato-regular-webfont.woff │ │ │ │ │ │ ├── lato-regular-webfont.woff2 │ │ │ │ │ │ ├── lato-regularitalic-webfont.woff │ │ │ │ │ │ └── lato-regularitalic-webfont.woff2 │ │ │ │ │ └── fonts.twig │ │ │ │ ├── forms │ │ │ │ │ ├── checkbox.twig │ │ │ │ │ ├── inputs.twig │ │ │ │ │ ├── radio-buttons.twig │ │ │ │ │ └── select-menu.twig │ │ │ │ ├── global.scss │ │ │ │ ├── images │ │ │ │ │ ├── landscape-16x9.twig │ │ │ │ │ ├── landscape-4x3.twig │ │ │ │ │ ├── loading-icon.twig │ │ │ │ │ └── square.twig │ │ │ │ ├── lists │ │ │ │ │ ├── definition.twig │ │ │ │ │ ├── ordered.twig │ │ │ │ │ └── unordered.twig │ │ │ │ ├── tables │ │ │ │ │ └── table.twig │ │ │ │ ├── text │ │ │ │ │ ├── blockquote.twig │ │ │ │ │ ├── headings.twig │ │ │ │ │ ├── hr.twig │ │ │ │ │ ├── inline-elements.twig │ │ │ │ │ ├── paragraph.twig │ │ │ │ │ ├── preformatted-text.twig │ │ │ │ │ └── time.twig │ │ │ │ └── utils │ │ │ │ │ ├── _breakpoints.scss │ │ │ │ │ ├── _functions.scss │ │ │ │ │ ├── _init.scss │ │ │ │ │ ├── _mixins.scss │ │ │ │ │ ├── _typography.scss │ │ │ │ │ └── _variables.scss │ │ │ ├── layout │ │ │ │ └── .gitkeep │ │ │ └── pages │ │ │ │ └── .gitkeep │ │ ├── styleguide │ │ │ ├── annotations │ │ │ │ └── annotations.js │ │ │ ├── data │ │ │ │ ├── data.json │ │ │ │ └── listitems.json │ │ │ ├── drupal-twig │ │ │ │ ├── PatternLabTwigExtensions │ │ │ │ │ └── BasicTwigExtensions.php │ │ │ │ └── alter-twig.php │ │ │ ├── meta │ │ │ │ ├── _foot.mustache │ │ │ │ ├── _foot.twig │ │ │ │ ├── _head.mustache │ │ │ │ └── _head.twig │ │ │ └── pattern-scaffolding.scss │ │ └── templates │ │ │ ├── .gitkeep │ │ │ └── field │ │ │ └── field.html.twig │ │ ├── _theme_name.breakpoints.yml │ │ ├── _theme_name.info.yml │ │ ├── _theme_name.libraries.yml │ │ ├── _theme_name.theme │ │ ├── editorconfig │ │ ├── eslintrc.json │ │ ├── gitignore │ │ ├── gitkeep │ │ ├── prettierrc.json │ │ └── stylelintrc.yml ├── component │ ├── index.js │ └── templates │ │ └── _component │ │ ├── _component.ejs │ │ ├── _component.json │ │ ├── _component.scss │ │ └── _component.twig └── starter-kit │ ├── add-dependency.js │ ├── add-third-party.js │ ├── build-components.js │ ├── index.js │ └── templates │ ├── accordion │ ├── accordion-item.md │ ├── accordion-item.twig │ ├── accordion.js │ ├── accordion.json │ ├── accordion.md │ ├── accordion.scss │ ├── accordion.twig │ └── images │ │ └── accordion__arrow.svg │ ├── button │ ├── button.json │ ├── button.md │ ├── button.scss │ ├── button.twig │ ├── button~disabled.json │ └── button~primary.json │ ├── card-list │ ├── card-list.json │ ├── card-list.md │ ├── card-list.scss │ ├── card-list.twig │ └── templates │ │ ├── paragraph--card--card-list.html.twig │ │ └── paragraph--card-list.html.twig │ ├── card │ ├── card.json │ ├── card.md │ ├── card.scss │ ├── card.twig │ └── templates │ │ └── paragraph--card.html.twig │ ├── carousel │ ├── carousel-item.md │ ├── carousel-item.twig │ ├── carousel.js │ ├── carousel.json │ ├── carousel.scss │ └── carousel.twig │ ├── eyebrow │ ├── eyebrow.json │ ├── eyebrow.md │ ├── eyebrow.scss │ └── eyebrow.twig │ ├── heading │ ├── heading.json │ ├── heading.md │ ├── heading.scss │ └── heading.twig │ ├── hero │ ├── hero.json │ ├── hero.md │ ├── hero.scss │ ├── hero.twig │ └── hero~video.json │ ├── media-item │ ├── media-item.json │ ├── media-item.md │ ├── media-item.scss │ ├── media-item.twig │ └── media-item~video.json │ ├── message │ ├── images │ │ ├── check.svg │ │ ├── error.svg │ │ └── warning.svg │ ├── message.json │ ├── message.md │ ├── message.scss │ ├── message.twig │ └── templates │ │ └── status-messages.html.twig │ └── tabs │ ├── tabs.json │ ├── tabs.md │ ├── tabs.scss │ ├── tabs.twig │ └── templates │ ├── menu-local-task.html.twig │ └── menu-local-tasks.html.twig ├── gulpfile.js ├── package-lock.json ├── package.json └── test ├── app.js ├── component.js └── starter-kit.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | generators/app/templates/_src/styleguide/annotations/annotations.js 2 | generators/app/templates/eslintrc.json 3 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | // http://eslint.org/docs/rules/ 3 | // By default, ESLint will look for configuration files in all parent 4 | // folders up to the root directory. Prevent this by telling ESLint 5 | // that this is the root of the project. 6 | "root": true, 7 | 8 | // Which environments your script is designed to run in. 9 | // Each environment brings with it a certain set of predefined global variables. 10 | "env": { 11 | "node": true, 12 | "browser": true, 13 | "es6": true, 14 | "jquery": true, 15 | "mocha": true 16 | }, 17 | "globals": { 18 | // Let ESLint know about defined global variables. 19 | "Drupal": true, 20 | "drupalSettings": true 21 | }, 22 | "parserOptions": { 23 | "ecmaVersion": 9 24 | }, 25 | // Inherit settings from ESLint Recommended config. 26 | // Rules above override any rules configured here. 27 | "extends": "eslint:recommended", 28 | "plugins": ["ejs"], 29 | "rules": { 30 | // Two space indentation. 31 | "indent": ["error", 2, { "SwitchCase": 1 }], 32 | 33 | // Prefer single quotes over double. 34 | "quotes": ["error", "single"], 35 | 36 | // Specify Unix line endings. 37 | "linebreak-style": ["error", "unix"], 38 | 39 | // Enforce using semicolons. 40 | "semi": ["error", "always"], 41 | 42 | // Enforce camelcase for variables. 43 | "camelcase": "error", 44 | 45 | // Prohibit use of == and != in favor of === and !==. 46 | "eqeqeq": "error", 47 | 48 | // Prohibit use of a variable before it is defined. 49 | "no-undef": "error", 50 | 51 | // Enforce line length to 80 characters 52 | "max-len": ["error", { 53 | "code": 80, 54 | "ignoreUrls": true, 55 | "ignoreStrings": true, 56 | "ignoreRegExpLiterals": true, 57 | "ignoreTemplateLiterals": true 58 | }], 59 | 60 | // Require capitalized names for constructor functions. 61 | "new-cap": "error", 62 | 63 | // Warn when variables are defined but never used. 64 | "no-unused-vars": "warn", 65 | 66 | // Require one var declaration for each variable and 67 | // declare each variable on a newline. 68 | "one-var": ["error", "never"], 69 | 70 | // Enforce stroustrup style for braces. 71 | "brace-style": ["error", "stroustrup"], 72 | 73 | // Validates JSDoc comments are syntactically correct 74 | "valid-jsdoc": "error", 75 | 76 | // Treat var as Block Scoped 77 | "block-scoped-var": "warn", 78 | 79 | // Require Following Curly Brace Conventions 80 | "curly": "error", 81 | 82 | // Disallow Use of Alert 83 | "no-alert": "warn", 84 | 85 | // Disallow eval() 86 | "no-eval": "error", 87 | 88 | // Disallow the type conversion with shorter notations 89 | "no-implicit-coercion": "error", 90 | 91 | // Disallow Functions in Loops 92 | "no-loop-func": "error", 93 | 94 | // Disallow Script URLs 95 | "no-script-url": "error", 96 | 97 | // Disallow Use of the Comma Operator 98 | "no-sequences": "error", 99 | 100 | // Disallow unnecessary concatenation of strings 101 | "no-useless-concat": "error", 102 | 103 | // Disallow Early Use 104 | "no-use-before-define": "error", 105 | 106 | // Require spacing in object literals. 107 | // "object-curly-spacing": ["error", "always"], 108 | 109 | // Require file to end with single newline 110 | "eol-last": "error", 111 | 112 | // Disallow trailing spaces at the end of lines 113 | "no-trailing-spaces": "error", 114 | 115 | // Disallow Dangling Underscores in Identifiers 116 | "no-underscore-dangle": "error", 117 | 118 | // Require JSDoc comment 119 | "require-jsdoc": "error", 120 | 121 | // Require Or Disallow Space Before Blocks 122 | "space-before-blocks": "error", 123 | 124 | // Disallow Yoda Conditions 125 | "yoda": "error" 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v14.16.1 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - v6 4 | - v5 5 | - v4 6 | - '0.12' 7 | - '0.10' 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Misc Updates (2021-05-24) 4 | 5 | ### New features 6 | - Drupal 8 and 9 compliant. Defaults to Drupal 9. 7 | - Node v14.16.1 8 | - Pattern Lab v5.14.3 9 | - Scss sourcemas 10 | - Using `.md` to hide patterns 11 | 12 | ### Bug fix 13 | - Updates to Pattern Lab's UIKit 14 | - More stable due to NodeJS updates 15 | 16 | ## 😷 2.3.0 (2020-04-08) 17 | 18 | ### Bug Fix 19 | * Bumps project dependencies. 20 | * Fixes PHP Codesniffer violations for PHP files used by Pattern Lab Node. 21 | 22 | ### New Feature 23 | * Now watch command triggers on changes to SVGs. Thanks Tim for the [issue](https://github.com/mediacurrent/theme_generator_8/issues/53). 24 | * Backed out the automated composer install for SlickJS if someone selected the carousel as a starter component. This caused more problems than it was worth. We now tell folks how to install it vs trying to do it for them. 25 | * Updated ESlint config so max-len doesn't flag urls, strings, regex and template literals. Woooooo. 26 | * Added additional flexibility to the start kit "heading" component so it can accept any element not just headings. Sometimes you don't need a `

` and you really need a `

`. 27 | * Added Drupal templates and integration for the Card and Card-List components. 28 | 29 | ### Documentation 30 | * Updated the `README.md` to note which Starter Kit components are preconfigured to work with the Drupal [Rain Install Profile](https://www.drupal.org/project/rain/). 31 | * Added information about theme module dependencies. 32 | 33 | ## ☕️ 2.2.1 (2020-03-03) 34 | 35 | ### Bug Fix 36 | * Fixed StyleLint errors. Thanks Tim! 37 | * Fixed an issue where a starter kit component was misnamed. 38 | * Starter Kit components fail more gracefully for projects that don't use composer. 39 | 40 | ## ☕️ 2.2.0 (2020-01-22) 41 | 42 | ### Bug Fix 43 | * Fixed a bug with ESlint not being able to tell the difference between node (gulp) and Drupal JS files. This would throw the wrong 'use strict' error. 44 | 45 | ### New Feature 46 | * Moved from Sass Lint to StyleLint. **Thanks for all your hard work on this Tim!** 47 | * Added a new component subgenerator that can be run as a normal build script `npm run generate`. 48 | * Added more start-kit components and modified them so they can be easily added to an existing Drupal theme. **Thanks for your help with this too Tim!** 49 | * Added a default `field.html.twig` template that's got less markup in it so that we start new themes in a cleaner state. 50 | * Added Prettier support for theme JS files. 51 | 52 | ### Documentation 53 | * Updated the `README.md` with examples of how to use the new component generator and starter-kit. 54 | 55 | ## ⚔️ 2.1.0 (2019-11-04) 56 | 57 | ### Bug Fix 58 | * Removed patches used by Pattern Lab since the latest release merges in those fixes. 59 | * Updated readme ESlintrc file link to point to new `.json` file. 60 | * Updated theme build tool dependencies. 61 | 62 | ### New Feature 63 | * Added in support for using the `|clean_id` filter in Pattern Lab the same way you can in Drupal Twig. 64 | * Migrated eslintrc file from yaml to json. 65 | 66 | ## 🏍 2.0.1 (2019-09-27) 67 | 68 | ### Bug Fix 69 | * Repatched @pattern-lab+engine-twig-php so includes work correctly. 70 | 71 | ## 🏍 2.0.0 (2019-09-13) 72 | 73 | ### New Feature 74 | * Switched from KSS to Pattern Lab. 75 | * Added optional example components (Drupal Tabs, Drupal Messages and buttons). 76 | * Added optional `.gitignore` for compiled files. 77 | * Added concat task for JS files so you don't have to manually add them to the style guide. 78 | * Upgraded Gulp to v4. 79 | * Added an easier way to display color swatches. 80 | * Updated base / Global CSS. 81 | * Removed breakpoint / singularity options. 82 | * Added example breakpoint mixin that provides similar functionality. 83 | * Added an example of how to use `@font-face` and locally hosted fonts. 84 | * Added prettier config. 85 | * Likely more stuff I forgot about. 86 | 87 | ### Documentation 88 | * Updated `README.md` with information about Pattern Lab and advice on starting a new project using the generator. 89 | 90 | ## 🕷 1.4.0 (2017-09-01) 91 | 92 | ### Bug Fix 93 | * Back in **1.3.3** I fixed the layout folder but didn't quite get it in the right spot. This is now fixed. 94 | 95 | ### New Feature 96 | * Updated Babel to use [env](https://github.com/babel/babel-preset-env) instead of a preset. This means you can simplify browser support a bit by adding browsers needed to the project's `package.json`. 97 | * Moved Autoprefixer config to the `package.json` file similar to Babel env. This sets us up for when both Babel env and Autoprefixer use the same config. 98 | * Added in error handling for JS and SCSS compiling. Now the watch task won't fail completely but will show you the error and keep watching files. This is a feature right? 99 | 100 | ### Documentation 101 | * Add best practice about using the generator via [npx](https://www.npmjs.com/package/npx). 102 | * Added theme documentation that points out Autoprefixer and Babel project config. 103 | 104 | ## 👾 1.3.4 (2017-07-12) 105 | 106 | ### Bug Fix 107 | * Added a default description that's basically a reminder to update the theme description later. Thanks to Kevin for finding that Drupal chokes if there's no description provided. 108 | 109 | **Thanks Kevin** for the [issue](https://github.com/mediacurrent/theme_generator_8/issues/11)! 110 | * Updated eslint to the new format. (Added a `.yml` extension.) Also fixed an issue where eslint will check the parent directories looking for a config file. This meant it could find other eslint config files and try to use those too. 111 | 112 | [issue](https://github.com/mediacurrent/theme_generator_8/issues/9) 113 | * Fixed macro namespace for icon macro. This made it p much broken out of the box. 114 | 115 | **Thanks Tim and Eric** for the [issue](https://github.com/mediacurrent/theme_generator_8/issues/7)! 116 | * Fixed issue with npm shrinkwrapping before all dev dependencies are installed. 117 | * Added `drupal.init.js` to KSS builder template. 118 | 119 | **Thanks Eric** for the [issue](https://github.com/mediacurrent/theme_generator_8/issues/10)! 120 | * Updated all build tool dev dependencies to the latest version. 121 | 122 | ## 🎈1.3.3 (2017-04-07) 123 | 124 | ### Bug Fix 125 | * Added a `.gitkeep` to the layout folder. If the theme was created and checked into the repo _without_ anything being added to the layout dir... errors my friend. Errors. 126 | 127 | **Thanks Tim** for the [issue](https://github.com/mediacurrent/theme_generator_8/issues/4)! 128 | * Added in default breakpoints in `*.breakpoints.yml`. Having this file contain only comments was bugging out some environments. 129 | 130 | **Thanks Carie** for the [issue](https://github.com/mediacurrent/theme_generator_8/issues/2)! 131 | 132 | ## 1.3.2 (2017-03-15) 133 | 134 | ### New Feature 135 | * Updated version of Node / npm used with project. The old version was keeping the readme from showing up on [https://www.npmjs.com/package/generator-mc-d8-theme](npm). 136 | 137 | ## ✈️ 1.3.1 (2017-02-14) 138 | 139 | ### New Feature 140 | * Moved the project from Bitbucket to Github and NPM. 141 | 142 | ### Documentation 143 | * Updated README to reflect new install instructions. 144 | 145 | ## 🔧 1.3.0 (2016-12-28) 146 | 147 | ### New Feature 148 | * Added a warning at the end of the theme generation to remind you to delete any unused code. If you don't need it, delete it! ☠️ 149 | * Added a concat task if KSS node is used. **Thanks Tobias and Mario!** This should allow you to not worry about adding a new CSS file for every new component within the style guide. Everything should work by default. Note that the generated `all.css` file may need some modification if CSS files are being concatenated in the wrong order. I've provided an example of how to do that within the concat.js gulp task. Also, **Don't** use the `all.css` file for production **only the style guide.** Leverage [Drupal's Libraries](https://www.drupal.org/docs/8/theming-drupal-8/adding-stylesheets-css-and-javascript-js-to-a-drupal-8-theme) to add CSS / JS files as needed. 150 | * https://bitbucket.org/mediacurrent/mis_theme_generator_8/issues/7/add-the-ability-to-concat-css-for-the 151 | * https://bitbucket.org/mediacurrent/mis_theme_generator_8/issues/8/dynamically-add-reference-to-allcss-to 152 | 153 | ### Bug Fix 154 | * Removed duplicate style guide task include within the gulp file. This caused breakage if you didn't select the KSS option. 155 | 156 | ### Documentation 157 | * Added link to CHANGELOG.md from the main README. 158 | 159 | ## 1.2.1 (2016-11-27) 160 | 161 | ### Bug Fix 162 | * Fixed broken path / twig name space for the components module. Thanks Mario! 163 | * https://bitbucket.org/mediacurrent/mis_theme_generator_8/issues/6/path-to-components-is-incorrect-in-theme 164 | 165 | ## 1.2.0 (2016-11-23) 166 | 167 | 🐊 If this release had a name it'd be "Danger Zone". We've greatly refactored the Gulp file, updated all node modules and done a _ton_ of other stuff. We've even added a new Drupal JS behavior generator. Read on for more deets. 168 | 169 | ### New Feature 170 | * Updated ESLint config for 'use strict' and switch statements. 171 | * **Added sample breakpoints.yml theme file.** Thanks Jason! 172 | * https://bitbucket.org/mediacurrent/mis_theme_generator_8/issues/2/add-example-themenamebreakpointsyml-file 173 | * Updated node modules to latest versions. 174 | * Added Babel config file. Also added a babel plugin that removes global strict mode from compiled js files. 175 | * **Broke apart Gulp file to individual tasks.** Now Gulp tasks can be found in ... `./gulp-tasks`. They're standard NodeJS modules required into the main `gulpfile.js`. This was done because the gulpfile was getting looooooooong. 176 | * https://bitbucket.org/mediacurrent/mis_theme_generator_8/issues/4/refactor-gulp-file 177 | * **Updated KSS builder (theme) to add in a way to disable the sidebar and remove container padding.** Now you can click a button and kill the sidebar and all padding. It also appends a key + value pair to the URL so you don't have to do it every time you refresh the page. 178 | * Moved all base styles into one file `_base.scss`. Since the base styles weren't touched very much, we think moving them into one file helps with maintainability. 179 | * **Added a new subgenerator that creates Drupal JS Behaviors.** We got tired of searching through old themes for the JS behavior boilerplate. **ES6 / ES2015 ready!** 180 | 181 | ### Bug Fix 182 | * Fixed broken path to Singularity and Breakpoint node modules. Thanks Mario! No idea _how_ this worked originally. 183 | * https://bitbucket.org/mediacurrent/mis_theme_generator_8/issues/3/incorrect-path-to-singularitygs-and 184 | * Removed theme name suggestion as it's a little out of date. Seems silly to have to write "theme" AGAIN within a preprocess function. 185 | * Removed option to _not_ have a base theme as Drupal _will_ use one even if you don't specify it. Setting it to something keeps our theme from being broke in the future if Drupal ever changes which theme is the "used by default" one. 186 | * Removed npm shrinkwrap post install command as it can move depenencies around the shrinkwrap file. This causes issues with continuous integration deployment. 187 | * Updated gitignore to ignore all .map files. Previously it was only ignoring css files and js files could sneak by. 188 | 189 | ### Documentation 190 | * Updated generator readme with details on how to use the new JS Behavior subgenerator. 191 | 192 | ## 1.1.0 (2016-07-26) 193 | 194 | This update is mainly moving source files into a `src` directory. The main theme files that Drupal needs are left in the top level theme folder. If you try to move them, stuff breaks. 195 | 196 | ### New Feature 197 | * Added editor config file. http://editorconfig.org/ 198 | * Moved source files into a new `/src` directory. 199 | 200 | ### Documentation 201 | * Added a new CHANGELOG.md! YAY! 202 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 ZackHawkinsMC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mediacurrent Theme Generator 2 | 3 | The Theme Generator is a scafolding tool built by your friends at [Mediacurrent](https://mediacurrent.com/), which lets you quickly set up a Drupal 8 or 9 theme with sensible defaults and best practices. 4 | 5 | - [What's New - Read the Changelog](CHANGELOG.md) 6 | - [Using the Theme Generator](#using-the-theme-generator) 7 | - [Creating a new Drupal Theme](#creating-a-new-drupal-theme) 8 | - [Setup NodeJS & NPM](#setup-nodejs--npm) 9 | - [Create the theme](#create-the-theme) 10 | - [Note about Drupal 8](#note-about-drupal-8) 11 | - [About the New Theme](#about-the-new-theme) 12 | - [Drupal Module Dependencies](#drupal-module-dependencies) 13 | - [Support](#support) 14 | - [Starter Kit](#starter-kit) 15 | - [Create new components](#create-new-components) 16 | - [A Word About Commiting ./dist Files](#a-word-about-commiting-dist-files) 17 | - [Stuff You Might Want To Change](#stuff-you-might-want-to-change) 18 | - [Go Team](#go-team) 19 | - [Links](#links) 20 | - [Contributing](#contributing) 21 | 22 | ## Using the Theme Generator 23 | 24 | While the theme generator can be run anywhere, it's happiest when it's run from an empty directory you'd like to become your theme. 25 | 26 | While not a requirement we like to use [NVM](https://github.com/creationix/nvm) to manage the version of Node per project. 27 | 28 | ## Creating a new Drupal theme 29 | 30 | 1. Create a new directory. Example: 31 | 32 | ```bash 33 | themes/custom/my_awesome_theme 34 | ``` 35 | 36 | ### Setup NodeJS & NPM 37 | 38 | 1. Move into the new directory and install Node: 39 | 40 | ```bash 41 | cd my_awesome_theme 42 | ``` 43 | 44 | ```bash 45 | nvm install 14 && node -v > .nvmrc 46 | ``` 47 | 48 | - This will install the latest release of NodeJS `v14`. 49 | 50 | - It will create `.nvmrc` in the root of your project. 51 | 52 | 1. Theme Generator is compatible with npm v6. 53 | 54 | ```bash 55 | npm install -g npm@6.14.12 56 | ``` 57 | 58 | From now on, when working on this theme change into its directory and run `nvm use` and NVM will switch to the specified version for you. 59 | 60 | ### Create the theme 61 | 62 | 1. Run the generator (**Do not change this command**): 63 | 64 | ```bash 65 | npm create yo mc-d8-theme 66 | ``` 67 | 68 | - You should be taken through a series of questions that allow you to pick the best options for your theme. 69 | 70 | **IMPORTANT**: Your theme's machine name should always match the directory name you created above. 71 | 72 | More info if you're interested in how this stuff works: 73 | 74 | `npm create` is an alias of `npm init` and uses [npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) under the hood. Find out more about [npm init](https://docs.npmjs.com/cli/init.html). 75 | 76 | ## Note about Drupal 8 77 | 78 | If you are building a Drupal 8 theme, make the following updates after your theme has been created: 79 | 80 | - Update Pattern Lab to v5.14.0 by updating `package.json` as follows: 81 | 82 | ```json 83 | "@pattern-lab/core": "5.14.0", 84 | "@pattern-lab/engine-twig-php": "5.14.0", 85 | "@pattern-lab/uikit-workshop": "5.14.0", 86 | ``` 87 | 88 | - Remove `node_modules` 89 | 90 | - Run `npm install` 91 | 92 | ## About the New Theme 93 | 94 | ### Drupal Module Dependencies 95 | 96 | This theme uses [Twig namespaces](https://symfony.com/doc/current/templates.html#templates-namespaces). In order for these to work in Drupal you must install the [Components Library](https://www.drupal.org/project/components) module. 97 | 98 | ### Support 99 | 100 | The following is supported by your new theme. 101 | 102 | - [Pattern Lab (Node)](https://github.com/pattern-lab/patternlab-node/) 103 | - Component-Based Workflow 104 | - ES6+ (With Source Maps) 105 | - Sass 106 | - Image Compression 107 | - Live reloading 108 | - Sass and JavaScript linting 109 | 110 | ### Starter Kit 111 | 112 | The theme generator allows you to (optionally) add example components. 113 | 114 | - Accordion 115 | - Button 116 | - Card 🌧 117 | - Card List 🌧 118 | - Carousel 119 | - Eyebrow 120 | - Heading 121 | - Hero 122 | - Media 123 | - Drupal Messages (Based off of the Classy base theme) 🌧 124 | - Drupal Tabs 🌧 125 | 126 | 🌧 = Preconfigured for the [Rain Install Profile](https://www.drupal.org/project/rain/). 127 | 128 | These can include both component and Drupal templates that are added to the appropriate place during theme generation. Your theme.libraries.yml is also updated to include the relevant libraries. 129 | 130 | This can also be run within a pre-existing theme using: 131 | 132 | ```bash 133 | npx yo mc-d8-theme:starter-kit 134 | ``` 135 | 136 | ## Create new Components 137 | 138 | You can also generate base components with the right files in place using: 139 | 140 | ```bash 141 | npm run generate 142 | ``` 143 | 144 | This is helpful if you are building out a new theme and would like to quickly create lots of new components with the libraries already wired up. 145 | 146 | ### A Word About Commiting ./dist Files 147 | 148 | **TLDR:** Don't do it if you can avoid it. 149 | 150 | Every time Pattern Lab is rebuilt the cache busting strings will change on CSS and JS files. `dependencyGraph.json` will also be updated every single time which makes reviewing pull requests rather difficult. 151 | 152 | Optimally we want to gitignore all `/.dist` files and run `npm run build` as part of a continuous integration process. 153 | 154 | ### Stuff You Might Want To Change 155 | 156 | #### Supported Browsers 157 | 158 | Change what browsers your theme supports by updating *browserslist* within `package.json`. For options take a look at [browserslist](https://github.com/browserslist/browserslist). 159 | 160 | This impacts CSS browser prefixes and JavaScript compiled files. 161 | 162 | #### Demo Files 163 | 164 | - Swap out `screenshot.png` with your own theme image. 165 | - Remove or replace the font files in `./src/patterns/global/fonts/`. 166 | - Change the colors in `./src/patterns/global/colors/`. 167 | 168 | ### Go Team 169 | 170 | Provided by default are seven npm scripts that point to Gulp tasks. We run gulp through npm scripts. 171 | 172 | 1. Run the default build task (gulp in this instance) and everything in it. 173 | This is the equivalent to running `gulp` on the command line with Gulp installed globally. 174 | 175 | ```bash 176 | npm run build 177 | ``` 178 | 179 | 1. Compile Sass and JS. 180 | 181 | ```bash 182 | npm run compile 183 | ``` 184 | 185 | 1. Watch files and run tasks when they change. 186 | 187 | ```bash 188 | npm run watch 189 | ``` 190 | 191 | 1. Compress png and svg assets. 192 | 193 | ```bash 194 | npm run compress 195 | ``` 196 | 197 | 1. Build Pattern Lab. 198 | 199 | ```bash 200 | npm run styleguide 201 | ``` 202 | 203 | 1. Lint Sass and JS files. 204 | 205 | ```bash 206 | npm run lint 207 | ``` 208 | 209 | 1. Delete compiled Sass, JS and style guide files from the /dist directory. 210 | 211 | ```bash 212 | npm run clean 213 | ``` 214 | 215 | ## Links 216 | 217 | - [`.stylelintrc.yml`](generators/app/templates/stylelintrc.yml) 218 | - [`.eslintrc.json`](generators/app/templates/eslintrc.json) 219 | 220 | ## Contributing 221 | 222 | Would you like to contribute? Want to make a few changes or fix a bug? COME ON OVER! 223 | 224 | ### Clone down this repo 225 | 226 | ```bash 227 | git clone git@github.com:mediacurrent/theme_generator_8.git 228 | ``` 229 | 230 | ```bash 231 | cd theme_generator_8 232 | ``` 233 | 234 | Remove `generator-mc-d8-theme` if you have previously installed it: 235 | 236 | _Tip: use `npm ls -g -depth=0` to see what global node modules are installed._ 237 | 238 | ```bash 239 | npm uninstall generator-mc-d8-theme -g 240 | ``` 241 | 242 | Use the node version of the generator 243 | 244 | ```bash 245 | nvm install 246 | 247 | nvm use 248 | ``` 249 | 250 | Install the generator's dependencies 251 | 252 | ```bash 253 | npm install 254 | ``` 255 | 256 | [Link](https://docs.npmjs.com/cli/link) your local generator files to npm: 257 | 258 | ```bash 259 | npm link 260 | ``` 261 | 262 | Now whenever you run `yo mc-d8-theme` it'll use your locally cloned mc-d8-theme generator. Any updates done to the generator can be tested in real time. 263 | 264 | Break off a feature branch dive right in. After you've got something you'd like to add, push back to the repo and pull request against develop. 265 | 266 | ### IMPORTANT 267 | 268 | To test the changes you've made locally, ensure your new theme uses the same version of node as the generator. 269 | -------------------------------------------------------------------------------- /generators/app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const Generator = require('yeoman-generator'); 3 | const chalk = require('chalk'); 4 | const _ = require('lodash'); 5 | const fs = require('fs'); 6 | const mkdirp = require('mkdirp'); 7 | const path = require('path'); 8 | 9 | // Custom helper modules. 10 | const mcLogo = require('./mc-logo'); 11 | 12 | module.exports = class extends Generator { 13 | prompting() { 14 | // Have Yeoman greet the user. 15 | this.log(mcLogo); 16 | 17 | // Provide the user with prompts. 18 | var prompts = [ 19 | { 20 | name: 'themeName', 21 | message: 'What is your theme\'s human readable name?', 22 | default: _.startCase(this.appname) // Default to current folder name. 23 | }, 24 | { 25 | name: 'themeNameMachine', 26 | message: 'What is your theme\'s machine name? EX: unicorn_theme', 27 | default: function (answers) { 28 | // Default to snake case theme name 29 | return _.snakeCase(answers.themeName); 30 | } 31 | }, 32 | { 33 | name: 'themeDesc', 34 | message: 'What is your theme\'s description?', 35 | default: function (answers) { 36 | // Default to a helpful reminder to change the description later. 37 | // eslint-disable-next-line max-len 38 | return 'Update ' + answers.themeName + '.info.yml if you want to change the theme description later.'; 39 | } 40 | }, 41 | { 42 | type: 'list', 43 | name: 'whichBaseTheme', 44 | // eslint-disable-next-line max-len 45 | message: 'Which base theme would you like to use? If you don\'t want to use a base theme pick "stable" as that\'s what\'s used by Drupal if you don\'t specify a base.', 46 | choices: [ 47 | { 48 | value: 'stable', 49 | name: 'Use Stable as a base theme' 50 | }, 51 | { 52 | value: 'classy', 53 | name: 'Use Classy as a base theme' 54 | } 55 | ] 56 | }, 57 | { 58 | name: 'ignoreDist', 59 | type: 'confirm', 60 | // eslint-disable-next-line max-len 61 | message: 'Should we update the .gitignore to ignore compiled files? (i.e. /dist)', 62 | default: true 63 | } 64 | ]; 65 | 66 | return this.prompt(prompts).then(function (props) { 67 | // Should we ignore ./dist files or not? 68 | this.ignoreDist = props.ignoreDist; 69 | 70 | // Add the base theme to the object. 71 | this.baseTheme = props.whichBaseTheme; 72 | 73 | // Create a underscored version of the theme name. 74 | this.cleanThemeName = _.snakeCase(props.themeName); 75 | 76 | // Use the user provided theme machine name. 77 | this.themeNameMachine = props.themeNameMachine; 78 | 79 | // Create a dashed version of the theme name. 80 | this.dashedThemeName = _.kebabCase(props.themeName); 81 | 82 | // Get pkg info so we can create a 'generated on' comment. 83 | this.pkg = JSON.parse( 84 | fs.readFileSync( 85 | path.resolve(path.join(__dirname, '../../package.json')), 'utf8' 86 | ) 87 | ); 88 | 89 | // To access props later use this.props.someAnswer; 90 | this.props = props; 91 | }.bind(this)); 92 | } 93 | 94 | configuring() { 95 | // Add the Drupal libraries file so we can append additional 96 | // libraries to it if selected by the user. 97 | this.fs.copyTpl( 98 | this.templatePath('_theme_name.libraries.yml'), 99 | this.destinationPath(this.themeNameMachine + '.libraries.yml'), 100 | { 101 | themeNameMachine: this.themeNameMachine 102 | } 103 | ); 104 | 105 | // Prompt the user for start kit components. If any are selected 106 | // they will be copied over to the patterns folder and the libraries.yml 107 | // file will be appended with the component library. 108 | this.composeWith('mc-d8-theme:starter-kit', { 109 | themeName: this.themeNameMachine 110 | }); 111 | } 112 | 113 | writing() { 114 | // Create the project configuration. 115 | // This adds node modules and tools needed. 116 | this.fs.copyTpl( 117 | this.templatePath('_package.json'), 118 | this.destinationPath('package.json'), 119 | { 120 | themeName: this.themeNameMachine 121 | } 122 | ); 123 | // Only ignore ./dist files if the user has selected 124 | // that option. 125 | this.fs.copyTpl( 126 | this.templatePath('gitignore'), 127 | this.destinationPath('.gitignore'), 128 | { 129 | ignoreDist: this.ignoreDist 130 | } 131 | ); 132 | this.fs.copy( 133 | this.templatePath('editorconfig'), 134 | this.destinationPath('.editorconfig') 135 | ); 136 | this.fs.copy( 137 | this.templatePath('prettierrc.json'), 138 | this.destinationPath('.prettierrc.json') 139 | ); 140 | this.fs.copy( 141 | this.templatePath('_README.md'), 142 | this.destinationPath('README.md') 143 | ); 144 | this.fs.copy( 145 | this.templatePath('eslintrc.json'), 146 | this.destinationPath('.eslintrc.json') 147 | ); 148 | this.fs.copy( 149 | this.templatePath('stylelintrc.yml'), 150 | this.destinationPath('.stylelintrc.yml') 151 | ); 152 | // We need the theme machine name so we can set 153 | // correct namespaces. 154 | this.fs.copyTpl( 155 | this.templatePath('_patternlab-config.json'), 156 | this.destinationPath('patternlab-config.json'), 157 | { 158 | themeNameMachine: this.themeNameMachine 159 | } 160 | ); 161 | this.fs.copy( 162 | this.templatePath('_src/patterns/global'), 163 | this.destinationPath('src/patterns/global') 164 | ); 165 | this.fs.copy( 166 | this.templatePath('_src/patterns/components'), 167 | this.destinationPath('src/patterns/components') 168 | ); 169 | this.fs.copy( 170 | this.templatePath('_helper-components/icons'), 171 | this.destinationPath('src/patterns/components/icons') 172 | ); 173 | this.fs.copyTpl( 174 | this.templatePath('_helper-components/icons/icons.md'), 175 | this.destinationPath('src/patterns/components/icons/icons.md'), 176 | { 177 | themeNameMachine: this.themeNameMachine 178 | } 179 | ); 180 | this.fs.copyTpl( 181 | this.templatePath('_helper-components/icons/icons.twig'), 182 | this.destinationPath('src/patterns/components/icons/icons.twig'), 183 | { 184 | themeNameMachine: this.themeNameMachine 185 | } 186 | ); 187 | this.fs.copyTpl( 188 | this.templatePath('_helper-components/icons/icons-macro.twig'), 189 | this.destinationPath('src/patterns/components/icons/icons-macro.twig'), 190 | { 191 | themeNameMachine: this.themeNameMachine 192 | } 193 | ); 194 | this.fs.copyTpl( 195 | this.templatePath('_helper-components/icons/icons-macro.md'), 196 | this.destinationPath('src/patterns/components/icons/icons-macro.md'), 197 | { 198 | themeNameMachine: this.themeNameMachine 199 | } 200 | ); 201 | this.fs.copy( 202 | this.templatePath('_src/patterns/layout/.gitkeep'), 203 | this.destinationPath('src/patterns/layout/.gitkeep') 204 | ); 205 | this.fs.copy( 206 | this.templatePath('_src/patterns/pages/.gitkeep'), 207 | this.destinationPath('src/patterns/pages/.gitkeep') 208 | ); 209 | this.fs.copy( 210 | this.templatePath('_src/styleguide'), 211 | this.destinationPath('src/styleguide') 212 | ); 213 | this.fs.copy( 214 | this.templatePath('_src/templates'), 215 | this.destinationPath('src/templates') 216 | ); 217 | this.fs.copy( 218 | this.templatePath('_src/favicon.ico'), 219 | this.destinationPath('src/favicon.ico') 220 | ); 221 | 222 | // Build out the compiled folders. 223 | mkdirp('dist'); 224 | mkdirp('dist/css'); 225 | mkdirp('dist/fonts'); 226 | mkdirp('dist/images'); 227 | mkdirp('dist/js'); 228 | 229 | // Some folders remain empty so add in a gitkeep 230 | // so they're checked into git. 231 | this.fs.copy( 232 | this.templatePath('gitkeep'), 233 | this.destinationPath('dist/css/.gitkeep') 234 | ); 235 | this.fs.copy( 236 | this.templatePath('gitkeep'), 237 | this.destinationPath('dist/fonts/.gitkeep') 238 | ); 239 | this.fs.copy( 240 | this.templatePath('gitkeep'), 241 | this.destinationPath('dist/images/.gitkeep') 242 | ); 243 | this.fs.copy( 244 | this.templatePath('gitkeep'), 245 | this.destinationPath('dist/js/.gitkeep') 246 | ); 247 | 248 | // Add build tools. 249 | this.fs.copy( 250 | this.templatePath('_gulpfile.js'), 251 | this.destinationPath('gulpfile.js') 252 | ); 253 | this.fs.copy( 254 | this.templatePath('_gulp-tasks'), 255 | this.destinationPath('gulp-tasks') 256 | ); 257 | 258 | // Create the theme files. 259 | // 260 | // Create theme.info.yml with data provided. 261 | this.fs.copyTpl( 262 | this.templatePath('_theme_name.info.yml'), 263 | this.destinationPath(this.themeNameMachine + '.info.yml'), 264 | { 265 | themeName: this.props.themeName, 266 | themeDesc: this.props.themeDesc, 267 | themeNameMachine: this.themeNameMachine, 268 | baseTheme: this.baseTheme, 269 | pkg: this.pkg 270 | } 271 | ); 272 | // Create theme.breakpoints.yml with data provided. 273 | this.fs.copyTpl( 274 | this.templatePath('_theme_name.breakpoints.yml'), 275 | this.destinationPath(this.themeNameMachine + '.breakpoints.yml'), 276 | { 277 | themeName: this.props.themeName, 278 | themeNameMachine: this.themeNameMachine 279 | } 280 | ); 281 | // Create theme.theme with data provided. 282 | this.fs.copyTpl( 283 | this.templatePath('_theme_name.theme'), 284 | this.destinationPath(this.themeNameMachine + '.theme'), 285 | { 286 | themeNameMachine: this.themeNameMachine 287 | } 288 | ); 289 | 290 | this.fs.copy( 291 | this.templatePath('_screenshot.png'), 292 | this.destinationPath('screenshot.png') 293 | ); 294 | } 295 | 296 | install() { 297 | // Need to see if we still need this. 298 | this.npmInstall(); 299 | 300 | // Install the following node modules specifically for Pattern Lab 301 | // and theme generator. 302 | // Adding the `yo generator-mc-d8-theme` so users can quickly 303 | // run the component sub-generator locally. 304 | const npmArray = [ 305 | '@pattern-lab/core', 306 | '@pattern-lab/engine-twig-php', 307 | '@pattern-lab/uikit-workshop', 308 | 'yo', 309 | 'generator-mc-d8-theme' 310 | ]; 311 | 312 | // This runs `npm install ... --save-dev` on the command line. 313 | this.npmInstall(npmArray, { 314 | saveDev: true 315 | }); 316 | } 317 | 318 | end() { 319 | this.log(chalk.cyan.bgBlack.bold( 320 | // eslint-disable-next-line indent 321 | `☠️ NOTE: Your new generated theme contains a fair bit of boilerplate code. 322 | This is by design. If you don't need it PLEASE delete it. 323 | You can always rerun the generator some other time in a different directory 324 | and copy over what you're missing.`)); 325 | this.log(chalk.red('🚀')); 326 | } 327 | }; 328 | -------------------------------------------------------------------------------- /generators/app/mc-logo.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len, quotes */ 2 | 'use strict'; 3 | const chalk = require('chalk'); 4 | 5 | const mcLogo = 6 | chalk.blue("\n . ..... .....") + 7 | chalk.blue("\n .,::loodxOO0o. ,lx0KKKK0Okd:. .:ok0KKKK0ko'") + 8 | chalk.blue("\n .ldOXMMMMMMMx..:ONMWNNWMMMMMMWO' .oKWWNNWMMMMMMXl") + 9 | chalk.blue("\n ,OMMMMMMx,xNKo:'''c0MMMMMMMx.;0W0o;'',oXMMMMMX;") + 10 | chalk.blue("\n :NMMMMMXKXo. ;XMMMMMMXOXKc. lWMMMMWo") + 11 | chalk.blue("\n ;XMMMMMMX: .kMMMMMMMMK; ;XMMMMMx.") + 12 | chalk.blue("\n ;KMMMMMWo cOkkkkOOk; ,KMMMMMx.") + 13 | chalk.blue("\n ;KMWXKOo. .dOKNWMx.") + 14 | chalk.blue("\n .::,......',;::clloooooooooollc::,'.. ..,:,") + 15 | chalk.blue("\n .';cldxO0KXNNNNNNNNNWMMMMMMMWWWMMMWNXKOkdoc;'..") + 16 | chalk.blue("\n .,:looddONMMMMMKl;;,'''...'kMMMMMMWd,,;:cldxO0XWMMMNX0kdlc;'.") + 17 | chalk.blue("\n ..',;:::,'.. ;XMMMMMk. oMMMMMMNc .cXMMMMMNXNWMWNKOxdl:'") + 18 | chalk.blue("\n... ;XMMMMMk. oMMMMMMN: ,KMMMMMO,.,:loxO0KKO:") + 19 | chalk.blue("\n ;XMMMMMk. dMMMMMMNc ,KMMMMMk. ...") + 20 | chalk.blue("\n cNMMMMM0' .xMMMMMMWl :NMMMMMK,") + 21 | chalk.blue("\n .:0MMMMMMWx'. .oXMMMMMMMK:. .;OWMMMMMWk,.") + 22 | chalk.blue("\n .cox0KKKKKKKKKOdl. .:dk0KKKKKKKKK0xo; 'odOKKKKKKKKKOdo:.") + 23 | chalk.blue("\n ................ ................ ...............") + 24 | "\n Welcome to the " + chalk.blue('Mediacurrent D8 theme') + " generator!" + 25 | "\n "; 26 | 27 | module.exports = mcLogo; 28 | -------------------------------------------------------------------------------- /generators/app/templates/_README.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## Browser Support 4 | Autoprefixer & Babel is set to support the last 2 versions of modern browsers 5 | 6 | These can be updated at any time within the `package.json`. 7 | 8 | ## Run the following commands from the theme directory 9 | If you haven't yet, install nvm: 10 | https://github.com/creationix/nvm 11 | 12 | ### Use the right version of node with: 13 | `nvm use` 14 | 15 | _This command will look at your `.nvmrc` file and use the 16 | version node.js specified in it. This ensures all developers 17 | use the same version of node for consistency._ 18 | 19 | ### If that version of node isn't installed, install it with: 20 | `nvm install` 21 | 22 | ### Install npm dependencies with 23 | `npm install` 24 | 25 | _This command looks at `package.json` and installs all 26 | the npm dependencies specified in it. Some of the dependencies 27 | include gulp, autoprefixer, gulp-sass and others._ 28 | 29 | ### Runs default task 30 | `npm run build` 31 | 32 | _This will run whatever the default task is._ 33 | 34 | ### Compiles Sass 35 | `npm run compile` 36 | 37 | _This will perform a one-time Sass compilation._ 38 | 39 | ### Runs the watch command 40 | `npm run watch` 41 | 42 | _This is ideal when you are doing a lot of Sass changes 43 | and you want to make sure every time a change is saved 44 | it automatically gets compiled to CSS_ 45 | 46 | ### Cleans complied directory 47 | `npm run clean` 48 | 49 | _This will perform a one-time deletion of all compiled files within 50 | the dist/ directory._ 51 | -------------------------------------------------------------------------------- /generators/app/templates/_gulp-tasks/clean.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Include Our Plugins 4 | const del = require('del'); 5 | 6 | // Export our tasks. 7 | module.exports = { 8 | // Clean CSS files. 9 | cleanCSS: function() { 10 | return del(['./dist/css/*'], { force: true }); 11 | }, 12 | 13 | // Clean JS files. 14 | cleanJS: function() { 15 | return del(['./dist/js/*'], { force: true }); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /generators/app/templates/_gulp-tasks/compile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Include gulp 4 | const { src, dest } = require('gulp'); 5 | 6 | // Include Our Plugins 7 | const sass = require('gulp-sass')(require('sass')); 8 | const prefix = require('gulp-autoprefixer'); 9 | const sourcemaps = require('gulp-sourcemaps'); 10 | const babel = require('gulp-babel'); 11 | const rename = require('gulp-rename'); 12 | 13 | sass.compiler = require('sass'); 14 | /** 15 | * Error handler function so we can see when errors happen. 16 | * @param {object} err error that was thrown 17 | * @returns {undefined} 18 | */ 19 | function handleError(err) { 20 | // eslint-disable-next-line no-console 21 | console.error(err.toString()); 22 | this.emit('end'); 23 | } 24 | 25 | // Export our tasks. 26 | module.exports = { 27 | // Compile Sass. 28 | compileSass: function() { 29 | return src([ 30 | './src/patterns/**/**/*.scss', 31 | './src/styleguide/*.scss' 32 | ]) 33 | .pipe(sass().on('error', handleError)) 34 | .pipe( 35 | prefix({ 36 | cascade: false 37 | }) 38 | ) 39 | .pipe( 40 | rename(function(path) { 41 | path.dirname = ''; 42 | return path; 43 | }) 44 | ) 45 | .pipe(sourcemaps.write('./')) 46 | .pipe(dest('./dist/css')); 47 | }, 48 | 49 | // Compile JavaScript. 50 | compileJS: function() { 51 | return src(['./src/patterns/**/**/*.js'], { base: './' }) 52 | .pipe(sourcemaps.init()) 53 | .pipe(babel()) 54 | .pipe( 55 | rename(function(path) { 56 | // Currently not using ES6 modules so for now 57 | // es6 files are compiled into individual JS files. 58 | // Eventually this can use ES6 Modules and compile 59 | // all files within a component directory into a single 60 | // foo.bundle.js file. In that case the bundle name should 61 | // reflect the components directory name. 62 | path.dirname = ''; 63 | return path; 64 | }) 65 | ) 66 | .pipe(sourcemaps.write('./')) 67 | .pipe(dest('./dist/js')); 68 | } 69 | }; 70 | -------------------------------------------------------------------------------- /generators/app/templates/_gulp-tasks/compress.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Include gulp 4 | const { src, dest } = require('gulp'); 5 | 6 | // Include Our Plugins 7 | const rename = require('gulp-rename'); 8 | const imagemin = require('gulp-imagemin'); 9 | 10 | // Export our tasks. 11 | module.exports = { 12 | // Compress svg/png/jpg files. 13 | compressAssets: function() { 14 | return src([ 15 | './src/patterns/{global,layout,components}/**/*{.gif,.png,.jpg,.svg}' 16 | ]) 17 | .pipe( 18 | imagemin({ 19 | progressive: true, 20 | svgoPlugins: [ 21 | { 22 | removeViewBox: false 23 | } 24 | ] 25 | }) 26 | ) 27 | .pipe( 28 | rename(function(path) { 29 | path.dirname = ''; 30 | return path; 31 | }) 32 | ) 33 | .pipe(dest('./dist/images')); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /generators/app/templates/_gulp-tasks/concat.js: -------------------------------------------------------------------------------- 1 | // Include gulp 2 | const { src, dest } = require('gulp'); 3 | 4 | // Include Our Plugins. 5 | var concat = require('gulp-concat'); 6 | var order = require('gulp-order'); 7 | var sourcemaps = require('gulp-sourcemaps'); 8 | 9 | // Export our tasks. 10 | module.exports = { 11 | // Concat all CSS into a master bundle. 12 | concatCSS: function () { 13 | return ( 14 | src([ 15 | './dist/css/*.css', 16 | '!./dist/css/all.css', 17 | '!./dist/css/pattern-scaffolding.css' 18 | ]) 19 | .pipe(sourcemaps.init()) 20 | // Reorder the files so global is first. 21 | // If you need to get fancier with the order here's an example: 22 | // .pipe(order([ 23 | // 'dist/css/global.css', 24 | // 'src/components/**/*.css', 25 | // 'dist/css/btn.css', 26 | // 'dist/css/form-item.css', 27 | // 'dist/css/form-float-label.css', 28 | // 'dist/css/*.css' 29 | // ], { base: './' })) 30 | .pipe(order([ 31 | 'dist/css/global.css', 32 | 'dist/css/*.css' 33 | ], { base: './' })) 34 | .pipe(concat('all.css')) 35 | .pipe(sourcemaps.write('./')) 36 | .pipe(dest('./dist/css')) 37 | ); 38 | }, 39 | // Concat all JS into a master bundle. 40 | concatJS: function() { 41 | return ( 42 | src(['./dist/js/*.js', '!./dist/js/all.js']) 43 | // If you need to reorder any of the JS files here's an example: 44 | // .pipe(order([ 45 | // 'dist/js/header.js', 46 | // 'dist/js/button.js', 47 | // 'dist/js/*.js' 48 | // ], { base: './' })) 49 | .pipe(concat('all.js')) 50 | .pipe(dest('./dist/js')) 51 | ); 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /generators/app/templates/_gulp-tasks/format.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Include gulp 4 | const { src, dest } = require('gulp'); 5 | const changed = require('gulp-changed'); 6 | 7 | // Include Our Plugins 8 | const prettierFormat = require('./prettier'); 9 | 10 | // Export our tasks. 11 | module.exports = { 12 | // Format code based on prettier and eslint configs. 13 | // https://github.com/prettier/prettier-eslint 14 | prettier: function() { 15 | return src(['./src/patterns/**/**/*.js'], { base: './' }) 16 | .pipe(prettierFormat()) 17 | .pipe(changed('./', { hasChanged: changed.compareContents })) 18 | // Update the source JS file with the prettier formatted file. 19 | .pipe(dest(file => file.base)); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /generators/app/templates/_gulp-tasks/lint.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Include gulp 4 | const { src } = require('gulp'); 5 | 6 | // Include Our Plugins 7 | const gulpStylelint = require('gulp-stylelint'); 8 | const eslint = require('gulp-eslint'); 9 | 10 | // Export our tasks. 11 | module.exports = { 12 | // Lint Sass based on .stylelintrc.yml config. 13 | lintSass: function () { 14 | return src([ 15 | './src/patterns/{global,layout,components}/**/*.scss', 16 | './src/styleguide/*.scss', 17 | '!./src/patterns/global/utils/*' 18 | ]) 19 | .pipe( 20 | gulpStylelint({ 21 | reporters: [ 22 | { 23 | formatter: 'string', 24 | console: true 25 | } 26 | ] 27 | }) 28 | ); 29 | }, 30 | 31 | // Lint JavaScript based on .eslintrc config. 32 | lintJS: function() { 33 | return src([ 34 | './src/patterns/{global,layout,components}/**/*.js', 35 | '!./src/patterns/components/**/vendors/*' 36 | ]) 37 | .pipe(eslint()) 38 | .pipe(eslint.format()); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /generators/app/templates/_gulp-tasks/move.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Include gulp 4 | const { src, dest } = require('gulp'); 5 | 6 | // Include Our Plugins 7 | const rename = require('gulp-rename'); 8 | 9 | // Export our tasks. 10 | module.exports = { 11 | // Move any fonts to where Pattern Lab is lookinging for them. 12 | moveFonts: function() { 13 | return src( 14 | [ 15 | './src/patterns/global/fonts/**/*.woff', 16 | './src/patterns/global/fonts/**/*.woff2', 17 | './src/patterns/global/fonts/**/*.eot', 18 | './src/patterns/global/fonts/**/*.ttf', 19 | './src/patterns/global/fonts/**/*.svg' 20 | ], 21 | { base: './' } 22 | ) 23 | .pipe( 24 | rename(function(path) { 25 | path.dirname = ''; 26 | return path; 27 | }) 28 | ) 29 | .pipe(dest('./dist/fonts')); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /generators/app/templates/_gulp-tasks/prettier.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // Small Gulp ESLint Prettier plugin based on: 3 | // https://github.com/bhargavrpatel/gulp-prettier 4 | // and using https://github.com/prettier/prettier-eslint. 5 | 6 | // Include Our Plugins. 7 | const Buffer = require('safe-buffer').Buffer; 8 | const through = require('through2'); 9 | const PluginError = require('plugin-error'); 10 | const format = require('prettier-eslint'); 11 | 12 | module.exports = function() { 13 | return through.obj(function(file, encoding, callback) { 14 | if (file.isNull()) { 15 | return callback(null, file); 16 | } 17 | 18 | if (file.isStream()) { 19 | return callback( 20 | new PluginError('gulp-tasks/format', 'Streaming not supported') 21 | ); 22 | } 23 | 24 | const unformattedCode = file.contents.toString('utf8'); 25 | 26 | try { 27 | const formattedCode = format({ 28 | text: unformattedCode, 29 | filePath: file.path 30 | }); 31 | 32 | if (formattedCode !== unformattedCode) { 33 | file.isPrettier = true; 34 | file.contents = Buffer.from(formattedCode); 35 | } 36 | 37 | this.push(file); 38 | } 39 | catch (error) { 40 | this.emit( 41 | 'error', 42 | new PluginError('gulp-tasks/format', error, { fileName: file.path }) 43 | ); 44 | } 45 | 46 | callback(); 47 | }); 48 | }; 49 | -------------------------------------------------------------------------------- /generators/app/templates/_gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Include gulp helpers. 4 | const { series, parallel, watch } = require('gulp'); 5 | 6 | // Include Pattern Lab and config. 7 | const config = require('./patternlab-config.json'); 8 | const patternlab = require('@pattern-lab/core')(config); 9 | 10 | // Include Our tasks. 11 | // 12 | // Each task is broken apart to it's own node module. 13 | // Check out the ./gulp-tasks directory for more. 14 | const { compileSass, compileJS } = require('./gulp-tasks/compile'); 15 | const { lintJS, lintSass } = require('./gulp-tasks/lint'); 16 | const { compressAssets } = require('./gulp-tasks/compress'); 17 | const { cleanCSS, cleanJS } = require('./gulp-tasks/clean'); 18 | const { concatCSS, concatJS } = require('./gulp-tasks/concat'); 19 | const { moveFonts } = require('./gulp-tasks/move'); 20 | const { prettier } = require('./gulp-tasks/format'); 21 | const server = require('browser-sync').create(); 22 | 23 | // Compile Our Sass and JS 24 | exports.compile = parallel(compileSass, compileJS, moveFonts); 25 | 26 | // Lint Sass and JavaScript 27 | exports.lint = parallel(lintSass, lintJS); 28 | 29 | // Format JS files with Prettier and ESlint 30 | exports.format = prettier; 31 | 32 | // Compress Files 33 | exports.compress = compressAssets; 34 | 35 | // Concat all CSS and JS files into a master bundle. 36 | exports.concat = parallel(concatCSS, concatJS); 37 | 38 | // Clean all directories. 39 | exports.clean = parallel(cleanCSS, cleanJS); 40 | 41 | /** 42 | * Start browsersync server. 43 | * @param {function} done callback function. 44 | * @returns {undefined} 45 | */ 46 | function serve(done) { 47 | // See https://browsersync.io/docs/options for more options. 48 | server.init({ 49 | // We want to serve both the patternlab directory, so it gets 50 | // loaded by default AND three directories up which is the 51 | // Drupal core directory. This allows urls that reference 52 | // Drupal core JS files to resolve correctly. 53 | // i.e. /core/misc/drupal.js 54 | server: ['./patternlab/', '../../../'], 55 | notify: false, 56 | open: false 57 | }); 58 | done(); 59 | } 60 | 61 | /** 62 | * Start Pattern Lab build watch process. 63 | * @param {function} done callback function. 64 | * @returns {undefined} 65 | */ 66 | function watchPatternlab(done) { 67 | patternlab 68 | .build({ 69 | cleanPublic: config.cleanPublic, 70 | watch: true 71 | }) 72 | .then(() => { 73 | done(); 74 | }); 75 | } 76 | 77 | /** 78 | * Build Pattern Lab. 79 | * @param {function} done callback function. 80 | * @returns {undefined} 81 | */ 82 | function buildPatternlab(done) { 83 | patternlab 84 | .build({ 85 | cleanPublic: config.cleanPublic, 86 | watch: false 87 | }) 88 | .then(() => { 89 | done(); 90 | }); 91 | } 92 | 93 | /** 94 | * Watch Sass and JS files. 95 | * @returns {undefined} 96 | */ 97 | function watchFiles() { 98 | // Watch all my sass files and compile sass if a file changes. 99 | watch( 100 | [ 101 | './src/patterns/**/**/*.scss', 102 | './src/styleguide/*.scss' 103 | ], 104 | series(parallel(lintSass, compileSass), concatCSS, (done) => { 105 | server.reload('*.css'); 106 | done(); 107 | }) 108 | ); 109 | 110 | // Watch all my JS files and compile if a file changes. 111 | watch( 112 | './src/patterns/**/**/*.js', 113 | series( 114 | prettier, 115 | parallel(lintJS, compileJS), concatJS, (done) => { 116 | server.reload('*.js'); 117 | done(); 118 | } 119 | ) 120 | ); 121 | 122 | // Watch all my images and SVG files and compile if a file changes. 123 | watch( 124 | './src/patterns/**/**/*{.gif,.jpg,.png,.svg}', 125 | series( 126 | parallel(compressAssets), (done) => { 127 | server.reload('*{.gif,.jpg,.png,.svg,.html}'); 128 | done(); 129 | } 130 | ) 131 | ); 132 | 133 | // Reload the browser after patternlab updates. 134 | patternlab.events.on('patternlab-build-end', () => { 135 | server.reload('*.html'); 136 | }); 137 | } 138 | 139 | // Watch task that runs a browsersync server. 140 | exports.watch = series( 141 | parallel(cleanCSS, cleanJS), 142 | parallel( 143 | lintSass, 144 | compileSass, 145 | lintJS, 146 | compileJS, 147 | compressAssets, 148 | moveFonts 149 | ), 150 | parallel(concatCSS, concatJS), 151 | series(watchPatternlab, serve, watchFiles) 152 | ); 153 | 154 | // Build task for Pattern Lab. 155 | exports.styleguide = buildPatternlab; 156 | 157 | // Default Task 158 | exports.default = series( 159 | parallel(cleanCSS, cleanJS), 160 | parallel( 161 | lintSass, 162 | compileSass, 163 | lintJS, 164 | compileJS, 165 | compressAssets, 166 | moveFonts 167 | ), 168 | parallel(concatCSS, concatJS), 169 | buildPatternlab 170 | ); 171 | -------------------------------------------------------------------------------- /generators/app/templates/_helper-components/icons/assets/facebook.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /generators/app/templates/_helper-components/icons/assets/instagram.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /generators/app/templates/_helper-components/icons/assets/pinterest.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /generators/app/templates/_helper-components/icons/assets/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /generators/app/templates/_helper-components/icons/assets/youtube.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /generators/app/templates/_helper-components/icons/icons-macro.md: -------------------------------------------------------------------------------- 1 | --- 2 | hidden: true 3 | --- 4 | -------------------------------------------------------------------------------- /generators/app/templates/_helper-components/icons/icons-macro.twig: -------------------------------------------------------------------------------- 1 | {# 2 | /** 3 | * @file 4 | * Macro for embedding svg icons 5 | * 6 | * @var string name 7 | * The name of the svg to embed. It should match one of the svgs 8 | * that exist within the icons/assets/ directory. 9 | */ 10 | #} 11 | {% macro get(name) %} 12 | {% include '@<%= themeNameMachine %>/icons/assets/' ~ name ~ '.svg' %} 13 | {% endmacro %} 14 | -------------------------------------------------------------------------------- /generators/app/templates/_helper-components/icons/icons.json: -------------------------------------------------------------------------------- 1 | { 2 | "items":[ 3 | { 4 | "icon": "facebook" 5 | }, 6 | { 7 | "icon": "twitter" 8 | }, 9 | { 10 | "icon": "youtube" 11 | }, 12 | { 13 | "icon": "pinterest" 14 | }, 15 | { 16 | "icon": "instagram" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /generators/app/templates/_helper-components/icons/icons.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Icons 3 | --- 4 | 5 | These icons are used throughout the site. 6 | 7 | ## Usage: 8 | 9 | Import the icons macro. 10 | 11 | Call the `.get()` function passing it the icon name. 12 | 13 | **Example:** 14 | ``` 15 | {% import '@<%= themeNameMachine %>/icons/icons-macro-twig' as icons %} 16 | {{ icons.get('facebook') }} 17 | ``` 18 | This will render the svg inline. 19 | ``` 20 | 21 | ``` 22 | -------------------------------------------------------------------------------- /generators/app/templates/_helper-components/icons/icons.twig: -------------------------------------------------------------------------------- 1 | {% import "@<%= themeNameMachine %>/icons/icons-macro.twig" as icons %} 2 |

3 | {% for item in items %} 4 |
5 | {{ icons.get(item.icon, 'pl-icons__icon') }} 6 |

{{ item.icon }}

7 |
8 | {% endfor %} 9 |
10 | -------------------------------------------------------------------------------- /generators/app/templates/_package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%= themeName %>", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "build": "gulp", 6 | "compile": "gulp compile", 7 | "watch": "gulp watch", 8 | "styleguide": "gulp styleguide", 9 | "compress": "gulp compress", 10 | "lint": "gulp lint", 11 | "scss-fix": "npx stylelint './src/**/*.scss' --fix", 12 | "clean": "gulp clean", 13 | "gulp": "gulp", 14 | "generate": "yo mc-d8-theme:component" 15 | }, 16 | "babel": { 17 | "sourceType": "unambiguous", 18 | "presets": [ 19 | [ 20 | "@babel/preset-env" 21 | ] 22 | ] 23 | }, 24 | "browserslist": [ 25 | "last 2 versions", 26 | "not dead" 27 | ], 28 | "devDependencies": { 29 | "@babel/core": "^7.6.4", 30 | "@babel/preset-env": "^7.6.3", 31 | "browser-sync": "^2.26.7", 32 | "del": "^5.1.0", 33 | "gulp": "^4.0.2", 34 | "gulp-autoprefixer": "^7.0.1", 35 | "gulp-babel": "^8.0.0", 36 | "gulp-changed": "^4.0.2", 37 | "gulp-concat": "^2.6.1", 38 | "gulp-eslint": "^6.0.0", 39 | "gulp-imagemin": "^6.1.1", 40 | "gulp-order": "^1.2.0", 41 | "gulp-rename": "^1.4.0", 42 | "gulp-sass": "^5.0.0", 43 | "gulp-sourcemaps": "^3.0.0", 44 | "gulp-stylelint": "^11.0.0", 45 | "prettier-eslint": "^9.0.1", 46 | "sass": "^1.32.8", 47 | "stylelint": "^13.12.0", 48 | "stylelint-config-standard": "^21.0.0", 49 | "stylelint-scss": "^3.19.0" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /generators/app/templates/_patternlab-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "cacheBust": true, 3 | "cleanPublic": true, 4 | "defaultPattern": "all", 5 | "defaultShowPatternInfo": false, 6 | "ishControlsHide": { 7 | "s": false, 8 | "m": false, 9 | "l": false, 10 | "full": false, 11 | "random": false, 12 | "disco": false, 13 | "hay": true, 14 | "mqs": false, 15 | "find": false, 16 | "views-all": false, 17 | "views-annotations": false, 18 | "views-code": false, 19 | "views-new": false, 20 | "tools-all": false, 21 | "tools-docs": false 22 | }, 23 | "ishViewportRange": { 24 | "s": [ 25 | 240, 26 | 500, 27 | 240, 28 | 500, 29 | 240, 30 | 500 31 | ], 32 | "m": [ 33 | 500, 34 | 800, 35 | 500, 36 | 800, 37 | 500, 38 | 800 39 | ], 40 | "l": [ 41 | 800, 42 | 2600, 43 | 800, 44 | 2600, 45 | 800, 46 | 2600 47 | ] 48 | }, 49 | "logLevel": "info", 50 | "outputFileSuffixes": { 51 | "rendered": ".rendered", 52 | "rawTemplate": "", 53 | "markupOnly": ".markup-only" 54 | }, 55 | "paths": { 56 | "source": { 57 | "root": "./src/", 58 | "patterns": "./src/patterns/", 59 | "data": "./src/styleguide/data/", 60 | "meta": "./src/styleguide/meta/", 61 | "annotations": "./src/styleguide/annotations/", 62 | "styleguide": "./dist/", 63 | "patternlabFiles": { 64 | "general-header": "views/partials/general-header.mustache", 65 | "general-footer": "views/partials/general-footer.mustache", 66 | "patternSection": "views/partials/patternSection.mustache", 67 | "patternSectionSubgroup": "views/partials/patternSectionSubgroup.mustache", 68 | "viewall": "views/viewall.mustache" 69 | }, 70 | "js": "./dist/js", 71 | "images": "./dist/images", 72 | "fonts": "./dist/fonts", 73 | "css": "./dist/css" 74 | }, 75 | "public": { 76 | "root": "./patternlab/", 77 | "patterns": "./patternlab/patterns/", 78 | "data": "./patternlab/styleguide/data/", 79 | "annotations": "./patternlab/annotations/", 80 | "styleguide": "./patternlab/styleguide/", 81 | "js": "./patternlab/js", 82 | "images": "./patternlab/images", 83 | "fonts": "./patternlab/fonts", 84 | "css": "./patternlab/css" 85 | } 86 | }, 87 | "patternExtension": "twig", 88 | "patternStateCascade": [ 89 | "inprogress", 90 | "inreview", 91 | "complete" 92 | ], 93 | "patternExportDirectory": "./pattern_exports/", 94 | "patternExportPatternPartials": [], 95 | "serverOptions": { 96 | "wait": 1000 97 | }, 98 | "starterkitSubDir": "dist", 99 | "styleGuideExcludes": [], 100 | "theme": { 101 | "color": "light", 102 | "density": "compact", 103 | "layout": "horizontal" 104 | }, 105 | "uikits": [ 106 | { 107 | "package": "@pattern-lab/uikit-workshop", 108 | "outputDir": "", 109 | "enabled": true, 110 | "excludedPatternStates": [], 111 | "excludedTags": [] 112 | } 113 | ], 114 | "engines": { 115 | "twig": { 116 | "namespaces": [ 117 | { 118 | "id": "uikit", 119 | "recursive": true, 120 | "paths": [ 121 | "./node_modules/@pattern-lab/uikit-workshop/views-twig" 122 | ] 123 | }, 124 | { 125 | "id": "<%= themeNameMachine %>", 126 | "recursive": true, 127 | "paths": [ 128 | "./src/patterns/components", 129 | "./src/patterns/global", 130 | "./src/patterns/pages", 131 | "./src/patterns/layout" 132 | ] 133 | } 134 | ], 135 | "alterTwigEnv": [ 136 | { 137 | "file": "./src/styleguide/drupal-twig/alter-twig.php", 138 | "functions": [ 139 | "twig_extensions" 140 | ] 141 | } 142 | ] 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /generators/app/templates/_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mediacurrent/theme_generator_8/f13ff4bc06f196671b382c29bb9648976c549894/generators/app/templates/_screenshot.png -------------------------------------------------------------------------------- /generators/app/templates/_src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mediacurrent/theme_generator_8/f13ff4bc06f196671b382c29bb9648976c549894/generators/app/templates/_src/favicon.ico -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mediacurrent/theme_generator_8/f13ff4bc06f196671b382c29bb9648976c549894/generators/app/templates/_src/patterns/components/.gitkeep -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/components/hidden/hidden.scss: -------------------------------------------------------------------------------- 1 | // Utility classes to hide elements in different ways. 2 | // 3 | // Drupal provided CSS copied from the stable theme. 4 | 5 | // Import site utilities. 6 | @import '../../global/utils/init'; 7 | 8 | // Hide elements from all users. 9 | 10 | // Used for elements which should not be immediately displayed to any user. An 11 | // example would be collapsible details that will be expanded with a click 12 | // from a user. The effect of this class can be toggled with the jQuery show() 13 | // and hide() functions. 14 | .hidden { 15 | display: none; 16 | } 17 | 18 | // Hide elements visually, but keep them available for screen readers. 19 | 20 | // Used for information required for screen reader users to understand and use 21 | // the site where visual display is undesirable. Information provided in this 22 | // manner should be kept concise, to avoid unnecessary burden on the user. 23 | // "!important" is used to prevent unintentional overrides. 24 | .visually-hidden { 25 | @include element-invisible; 26 | } 27 | 28 | // The .focusable class extends the .visually-hidden class to allow 29 | // the element to be focusable when navigated to via the keyboard. 30 | .visually-hidden.focusable { 31 | @include element-focusable; 32 | } 33 | 34 | // Hide visually and from screen readers, but maintain layout. 35 | .invisible { 36 | visibility: hidden; 37 | } 38 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/colors/_colors.scss: -------------------------------------------------------------------------------- 1 | // Colors. 2 | // 3 | // All Sass color variables prefixed with $color-. 4 | // This makes it easy to figure out what a variable 5 | // is for later. 6 | // 7 | // If you need help naming colors check out: 8 | // http://chir.ag/projects/name-that-color/ 9 | 10 | // Standard. 11 | $color-white: #fff; 12 | $color-black: #1b2b34; 13 | 14 | // Primary. 15 | $color-mandy: #ec5f67; 16 | $color-tan-hide: #f99157; 17 | $color-saffron-mango: #fac863; 18 | $color-de-york: #99c794; 19 | $color-tradewind: #5fb3b3; 20 | $color-danube: #69c; 21 | 22 | // Grays. 23 | $color-gray-dk: #343d46; 24 | $color-gray-mid: #4f5b66; 25 | $color-gray-lt: #65737e; 26 | $color-gray-xlt: #a7adba; 27 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/colors/colors.json: -------------------------------------------------------------------------------- 1 | { 2 | "sections": { 3 | "Primary": [ 4 | { 5 | "name": "$color-mandy", 6 | "hex": "#EC5f67" 7 | }, 8 | { 9 | "name": "$color-tan-hide", 10 | "hex": "#F99157" 11 | }, 12 | { 13 | "name": "$color-saffron-mango", 14 | "hex": "#FAC863" 15 | }, 16 | { 17 | "name": "$color-de-york", 18 | "hex": "#99C794" 19 | }, 20 | { 21 | "name": "$color-tradewind", 22 | "hex": "#5FB3B3" 23 | }, 24 | { 25 | "name": "$color-danube", 26 | "hex": "#6699CC" 27 | } 28 | ], 29 | "Black & White": [ 30 | { 31 | "name": "$color-black", 32 | "hex": "#1B2B34" 33 | }, 34 | { 35 | "name": "$color-white", 36 | "hex": "#FFFFFF" 37 | } 38 | ], 39 | "Grays": [ 40 | { 41 | "name": "$color-gray-dk", 42 | "hex": "#343D46" 43 | }, 44 | { 45 | "name": "$color-gray-mid", 46 | "hex": "#4F5B66" 47 | }, 48 | { 49 | "name": "$color-gray-lt", 50 | "hex": "#65737E" 51 | }, 52 | { 53 | "name": "$color-gray-xlt", 54 | "hex": "#A7ADBA" 55 | } 56 | ] 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/colors/colors.twig: -------------------------------------------------------------------------------- 1 | {# 2 | /** 3 | * @file 4 | * Display for theme color swatches. 5 | * 6 | * Each color swatch is populated from the json file within 7 | * this same directory (colors.json). Add or remove 8 | * any colors there. 9 | */ 10 | #} 11 | 12 | {% for title, section in sections %} 13 |

{{ title }}

14 | {% for color in section %} 15 |
16 |
{{ color.name }}
{{ color.hex }}
17 |
18 | {% endfor %} 19 | {% endfor %} 20 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/fonts/_fonts.scss: -------------------------------------------------------------------------------- 1 | // Fonts generated using: 2 | // https://www.fontsquirrel.com/tools/webfont-generator 3 | // 4 | // For more information on font-face and browser support visit: 5 | // https://css-tricks.com/snippets/css/using-font-face/ 6 | 7 | // Lato Bold. 8 | @font-face { 9 | font-family: 'Lato'; 10 | font-style: normal; 11 | font-weight: 700; 12 | // The browser will hide the text for about 100ms and, if the font has not yet 13 | // been downloaded, will use the fallback text. It will swap to the new font 14 | // after it is downloaded, but only during a short swap period 15 | // https://css-tricks.com/almanac/properties/f/font-display/ 16 | font-display: fallback; 17 | src: 18 | url('../fonts/assets/lato-bold-webfont.woff2') format('woff2'), 19 | url('../fonts/assets/lato-bold-webfont.woff') format('woff'); 20 | } 21 | 22 | // Lato Regular. 23 | @font-face { 24 | font-family: 'Lato'; 25 | font-style: normal; 26 | font-weight: 400; 27 | font-display: fallback; 28 | src: 29 | url('../fonts/assets/lato-regular-webfont.woff2') format('woff2'), 30 | url('../fonts/assets/lato-regular-webfont.woff') format('woff'); 31 | } 32 | 33 | // Lato Regular Italic. 34 | @font-face { 35 | font-family: 'Lato'; 36 | font-weight: 400; 37 | font-style: italic; 38 | font-display: fallback; 39 | src: 40 | url('../fonts/assets/lato-regularitalic-webfont.woff2') format('woff2'), 41 | url('../fonts/assets/lato-regularitalic-webfont.woff') format('woff'); 42 | } 43 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/fonts/assets/lato-bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mediacurrent/theme_generator_8/f13ff4bc06f196671b382c29bb9648976c549894/generators/app/templates/_src/patterns/global/fonts/assets/lato-bold-webfont.woff -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/fonts/assets/lato-bold-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mediacurrent/theme_generator_8/f13ff4bc06f196671b382c29bb9648976c549894/generators/app/templates/_src/patterns/global/fonts/assets/lato-bold-webfont.woff2 -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/fonts/assets/lato-regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mediacurrent/theme_generator_8/f13ff4bc06f196671b382c29bb9648976c549894/generators/app/templates/_src/patterns/global/fonts/assets/lato-regular-webfont.woff -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/fonts/assets/lato-regular-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mediacurrent/theme_generator_8/f13ff4bc06f196671b382c29bb9648976c549894/generators/app/templates/_src/patterns/global/fonts/assets/lato-regular-webfont.woff2 -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/fonts/assets/lato-regularitalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mediacurrent/theme_generator_8/f13ff4bc06f196671b382c29bb9648976c549894/generators/app/templates/_src/patterns/global/fonts/assets/lato-regularitalic-webfont.woff -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/fonts/assets/lato-regularitalic-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mediacurrent/theme_generator_8/f13ff4bc06f196671b382c29bb9648976c549894/generators/app/templates/_src/patterns/global/fonts/assets/lato-regularitalic-webfont.woff2 -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/fonts/fonts.twig: -------------------------------------------------------------------------------- 1 |

Primary font: "Lato", "Helvetica", "Arial", sans-serif;

2 |

Primary font italic: "Lato", "Helvetica", "Arial", sans-serif;

3 |

Primary font bold: "Lato", "Helvetica", "Arial", sans-serif;

4 |

Secondary font: Georgia, Times, "Times New Roman", serif;

5 |

Secondary font italic: Georgia, Times, "Times New Roman", serif;

6 |

Secondary font bold; Georgia, Times, "Times New Roman", serif;

7 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/forms/checkbox.twig: -------------------------------------------------------------------------------- 1 |
2 |
3 | Checkbox * 4 | 9 |
10 |
11 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/forms/inputs.twig: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/forms/radio-buttons.twig: -------------------------------------------------------------------------------- 1 |
2 |
3 | Radio 4 | 9 |
10 |
11 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/forms/select-menu.twig: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 11 |
12 |
13 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/global.scss: -------------------------------------------------------------------------------- 1 | // @file 2 | // Site global scss file. 3 | // 4 | // Import variables and mixins 5 | // Should contain NO css output. 6 | // Only variables / mixins / settings. 7 | @import 'utils/init'; 8 | 9 | // Import font-face fonts. 10 | // 11 | // This should only be imported and loaded once. 12 | @import 'fonts/fonts'; 13 | 14 | // Base 15 | // 16 | // Plain html element styling. Shouldn't require 17 | // any classes. 18 | // 19 | // normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css 20 | // 21 | // NOTE: While this uses normalize.css as a starting 22 | // point it has and will be updated as needed. 23 | // This should be ONLY plain html element styling. 24 | // There should be NO classes, IDs, etc. 25 | 26 | // StyleLint needs to ignore some Normalize specific things: 27 | /* stylelint-disable property-no-vendor-prefix */ 28 | 29 | // https://css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice/ 30 | * { 31 | box-sizing: inherit; 32 | 33 | &::before, 34 | &::after { 35 | box-sizing: inherit; 36 | } 37 | } 38 | 39 | html { 40 | @include font-stack-primary; 41 | font-size: 100%; 42 | line-height: 1.15; 43 | box-sizing: border-box; 44 | -webkit-text-size-adjust: 100%; 45 | } 46 | 47 | // Sections. 48 | body { 49 | margin: 0; 50 | } 51 | 52 | // Render the `main` element consistently in IE11. 53 | main { 54 | display: block; 55 | } 56 | 57 | // Grouping content. 58 | hr { 59 | box-sizing: content-box; 60 | height: 0; 61 | overflow: visible; 62 | } 63 | 64 | pre { 65 | /* stylelint-disable-next-line font-family-no-duplicate-names */ 66 | font-family: monospace, monospace; 67 | font-size: 1em; 68 | } 69 | 70 | // Text-level semantics. 71 | abbr[title] { 72 | border-bottom: 0; 73 | // Fallback for browsers that do not support `text-decoration-style`. 74 | text-decoration: underline; 75 | text-decoration: underline dotted; 76 | } 77 | 78 | b, 79 | strong { 80 | font-weight: bolder; 81 | } 82 | 83 | code, 84 | kbd, 85 | samp { 86 | /* stylelint-disable-next-line font-family-no-duplicate-names */ 87 | font-family: monospace, monospace; 88 | font-size: 1em; 89 | } 90 | 91 | small { 92 | font-size: 80%; 93 | } 94 | 95 | sub, 96 | sup { 97 | font-size: 75%; 98 | line-height: 0; 99 | position: relative; 100 | vertical-align: baseline; 101 | } 102 | 103 | sub { 104 | bottom: -0.25em; 105 | } 106 | 107 | sup { 108 | top: -0.5em; 109 | } 110 | 111 | // Embedded content. 112 | img { 113 | border-style: none; 114 | max-width: 100%; 115 | height: auto; 116 | } 117 | 118 | // Forms 119 | button, 120 | input, 121 | optgroup, 122 | select, 123 | textarea { 124 | font-family: inherit; 125 | font-size: 100%; 126 | line-height: 1.15; 127 | margin: 0; 128 | } 129 | 130 | button, 131 | input { 132 | overflow: visible; 133 | } 134 | 135 | button, 136 | select { 137 | text-transform: none; 138 | } 139 | 140 | // Correct the inability to style clickable types in iOS and Safari. 141 | button, 142 | [type='button'], 143 | [type='reset'], 144 | [type='submit'] { 145 | -webkit-appearance: button; 146 | } 147 | 148 | // Remove the inner border and padding in Firefox. 149 | button::-moz-focus-inner, 150 | [type='button']::-moz-focus-inner, 151 | [type='reset']::-moz-focus-inner, 152 | [type='submit']::-moz-focus-inner { 153 | border-style: none; 154 | padding: 0; 155 | } 156 | 157 | // Restore the focus styles unset by the previous rule. 158 | button:-moz-focusring, 159 | [type='button']:-moz-focusring, 160 | [type='reset']:-moz-focusring, 161 | [type='submit']:-moz-focusring { 162 | outline: 1px dotted $color-danube; 163 | } 164 | 165 | // Correct the padding in Firefox. 166 | fieldset { 167 | padding: 0.35em 0.75em 0.625em; 168 | } 169 | 170 | legend { 171 | box-sizing: border-box; 172 | color: inherit; 173 | display: table; 174 | max-width: 100%; 175 | padding: 0; 176 | white-space: normal; 177 | } 178 | 179 | progress { 180 | vertical-align: baseline; 181 | } 182 | 183 | // Remove the default vertical scrollbar in IE 10+. 184 | textarea { 185 | overflow: auto; 186 | } 187 | 188 | // Correct the cursor style of increment and decrement buttons in Chrome. 189 | [type='number']::-webkit-inner-spin-button, 190 | [type='number']::-webkit-outer-spin-button { 191 | height: auto; 192 | } 193 | 194 | [type='search'] { 195 | -webkit-appearance: textfield; 196 | outline-offset: -2px; 197 | } 198 | 199 | [type='search']::-webkit-search-decoration { 200 | -webkit-appearance: none; 201 | } 202 | 203 | // Correct the inability to style clickable types in iOS and Safari. 204 | ::-webkit-file-upload-button { 205 | -webkit-appearance: button; 206 | font: inherit; 207 | } 208 | 209 | // Interactive. 210 | // Add the correct display in Edge, IE 10+, and Firefox. 211 | details { 212 | display: block; 213 | } 214 | 215 | summary { 216 | display: list-item; 217 | } 218 | 219 | // Misc. 220 | // Add the correct display in IE 10+. 221 | template { 222 | display: none; 223 | } 224 | 225 | // Lists 226 | // Unset default list margin and padding because if not 227 | // we'll have to unset it every. single. time. 228 | ul, 229 | ol, 230 | dl { 231 | margin: 0; 232 | padding: 0; 233 | } 234 | 235 | li { 236 | margin: 0; 237 | padding: 0; 238 | } 239 | 240 | // Headings are always 'just a bit bigger' than body copy. 241 | // https://csswizardry.com/2016/02/managing-typography-on-large-apps/ 242 | h1, 243 | h2, 244 | h3, 245 | h4, 246 | h5, 247 | h6 { 248 | font-size: 1.25rem; 249 | margin: 0 0 1rem; 250 | } 251 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/images/landscape-16x9.twig: -------------------------------------------------------------------------------- 1 | {{ img.landscape_16x9.alt }} 2 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/images/landscape-4x3.twig: -------------------------------------------------------------------------------- 1 | {{ img.landscape_4x3.alt }} 2 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/images/loading-icon.twig: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/images/square.twig: -------------------------------------------------------------------------------- 1 | {{ img.square.alt }} 2 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/lists/definition.twig: -------------------------------------------------------------------------------- 1 |
2 |
Definition List
3 |
A number of connected items or names written or printed consecutively, typically one below the other.
4 |
This is a term.
5 |
This is the definition of that term, which both live in a dl.
6 |
Here is another term.
7 |
And it gets a definition too, which is this line.
8 |
Here is term that shares a definition with the term below.
9 |
And it gets a definition too, which is this line.
10 |
11 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/lists/ordered.twig: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  1. This is a list item in an ordered list
  2. 4 |
  3. An ordered list is a list in which the sequence of items is important. An ordered list does not necessarily contain sequence characters.
  4. 5 |
  5. 6 | Lists can be nested inside of each other 7 |
      8 |
    1. This is a nested list item
    2. 9 |
    3. This is another nested list item in an ordered list
    4. 10 |
    11 |
  6. 12 |
  7. This is the last list item
  8. 13 |
14 |
15 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/lists/unordered.twig: -------------------------------------------------------------------------------- 1 |
2 | 14 |
15 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/tables/table.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 |
Table Heading 1Table Heading 2Table Heading 3Table Heading 4Table Heading 5
Table Footer 1Table Footer 2Table Footer 3Table Footer 4Table Footer 5
Table Cell 1Table Cell 2Table Cell 3Table Cell 4Table Cell 5
Table Cell 1Table Cell 2Table Cell 3Table Cell 4Table Cell 5
Table Cell 1Table Cell 2Table Cell 3Table Cell 4Table Cell 5
Table Cell 1Table Cell 2Table Cell 3Table Cell 4Table Cell 5
51 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/text/blockquote.twig: -------------------------------------------------------------------------------- 1 |
2 |

A block quotation (also known as a long quotation or extract) is a quotation in a written document, that is set off from the main text as a paragraph, or block of text, and typically distinguished visually using indentation and a different typeface or smaller size quotation.

3 |
-------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/text/headings.twig: -------------------------------------------------------------------------------- 1 |

h1 heading

2 |

h2 heading

3 |

h3 heading

4 |

h4 heading

5 |
h5 heading
6 |
h6 heading
7 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/text/hr.twig: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/text/inline-elements.twig: -------------------------------------------------------------------------------- 1 |
2 |

This is a text link.

3 | 4 |

Strong is used to indicate strong importance.

5 | 6 |

This text has added emphasis.

7 | 8 |

The b element is stylistically different text from normal text, without any special importance.

9 | 10 |

The i element is text that is set off from the normal text.

11 | 12 |

The u element is text with an unarticulated, though explicitly rendered, non-textual annotation.

13 | 14 |

This text is deleted and This text is inserted.

15 | 16 |

This text has a strikethrough.

17 | 18 |

Superscript®.

19 | 20 |

Subscript for things like H2O.

21 | 22 |

This small text is small for for fine print, etc..

23 | 24 |

Abbreviation: HTML.

25 | 26 |

Keyboard input: Cmd.

27 | 28 |

This text is a short inline quotation.

29 | 30 |

This is a citation 31 | 32 |

The dfn element indicates a definition.

33 | 34 |

The mark element indicates a highlight.

35 | 36 |

This is what inline code looks like.

37 | 38 |

This is sample output from a computer program.

39 | 40 |

The variable element, such as x = y.

41 |
42 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/text/paragraph.twig: -------------------------------------------------------------------------------- 1 |

A paragraph (from the Greek paragraphos, "to write beside" or "written beside") is a self-contained unit of a discourse in writing dealing with a particular point or idea. A paragraph consists of one or more sentences. Though not required by the syntax of any language, paragraphs are usually an expected part of formal writing, used to organize longer prose.

-------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/text/preformatted-text.twig: -------------------------------------------------------------------------------- 1 |
  	
2 | P R E F O R M A T T E D T E X T
3 | ! " # $ % & ' ( ) * + , - . /
4 | 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
5 | @ A B C D E F G H I J K L M N O
6 | P Q R S T U V W X Y Z [ \ ] ^ _
7 | ` a b c d e f g h i j k l m n o
8 | p q r s t u v w x y z { | } ~ 
9 | 
-------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/text/time.twig: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/utils/_breakpoints.scss: -------------------------------------------------------------------------------- 1 | // Breakpoints. 2 | 3 | $bp-under-xsm: 'max-width: 543px'; 4 | $bp-xsm: 544px; 5 | $bp-sm: 768px; 6 | $bp-md: 1024px; 7 | $bp-lg: 1200px; 8 | $bp-xl: 1540px; 9 | 10 | // Expects $viewport to either be a number: 200px 11 | // Or a string: 'max-width: 300px' 12 | // 13 | // Usage: 14 | // .foo__bar { 15 | // margin-left: 16px; 16 | // margin-right: 16px; 17 | 18 | // @include breakpoint($bp-md) { 19 | // margin-left: 24px; 20 | // margin-right: 24px; 21 | // } 22 | // } 23 | @mixin breakpoint($viewport) { 24 | @if type-of($viewport) == number { 25 | @media screen and ( min-width: $viewport ) { 26 | @content; 27 | } 28 | } 29 | @else { 30 | @media screen and ( $viewport ) { 31 | @content; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/utils/_functions.scss: -------------------------------------------------------------------------------- 1 | // Functions. 2 | 3 | // Function to return a value from a map based on a key. 4 | @function get-var($key, $map) { 5 | @if map-has-key($map, $key) { 6 | @return map-get($map, $key); 7 | } 8 | 9 | @warn 'Unknown `#{$key}` in map. Try "@include print($map);" to see what\'s available.'; 10 | @return null; 11 | } 12 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/utils/_init.scss: -------------------------------------------------------------------------------- 1 | // Utilities: Import Sass mixins, variables, modules etc. 2 | 3 | // Import variables 4 | @import 'variables'; 5 | 6 | // Import colors 7 | @import '../colors/colors'; 8 | 9 | // Import functions 10 | @import 'functions'; 11 | 12 | // Import mixins 13 | @import 'mixins'; 14 | 15 | // Import breakpoints 16 | @import 'breakpoints'; 17 | 18 | // Import typography 19 | @import 'typography'; 20 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/utils/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins. 2 | 3 | // Clearfix 4 | @mixin clearfix { 5 | &::after { 6 | content: ''; 7 | display: table; 8 | clear: both; 9 | } 10 | } 11 | 12 | // Makes an element visually hidden, but accessible. 13 | // @see http://snook.ca/archives/html_and_css/hiding-content-for-accessibility 14 | @mixin element-invisible { 15 | position: absolute !important; 16 | height: 1px; 17 | width: 1px; 18 | overflow: hidden; 19 | clip: rect(1px, 1px, 1px, 1px); 20 | } 21 | 22 | // Turns off the element-invisible effect. 23 | @mixin element-invisible-off { 24 | position: static !important; 25 | clip: auto; 26 | height: auto; 27 | width: auto; 28 | overflow: auto; 29 | } 30 | 31 | // Makes an element visually hidden by default, but visible when focused. 32 | @mixin element-focusable { 33 | @include element-invisible; 34 | 35 | &:active, 36 | &:focus { 37 | @include element-invisible-off; 38 | } 39 | } 40 | 41 | // Helper function for working with Sass maps. 42 | // Example: @include print($configuration); 43 | @mixin print($declarations) { 44 | @each $property, $value in $declarations { 45 | #{$property}: $value; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/utils/_typography.scss: -------------------------------------------------------------------------------- 1 | // Typography 2 | // 3 | // Typography variables. 4 | 5 | $font-sans: 'Lato', 'Arial', sans-serif; 6 | $font-serif: 'Georgia', serif; 7 | 8 | @mixin font-stack-primary { 9 | font-family: $font-sans; 10 | } 11 | 12 | @mixin font-stack-secondary { 13 | font-family: $font-serif; 14 | } 15 | 16 | // Font-Weights. 17 | $font-weight-light: 200; 18 | $font-weight-normal: 400; 19 | $font-weight-bold: 700; 20 | $font-italic: italic; 21 | 22 | // Heading mixins. 23 | @mixin heading-1-style { 24 | @include font-stack-secondary; 25 | font-size: 1.5rem; 26 | // 28px. 27 | line-height: 1.16; 28 | 29 | @include breakpoint($bp-sm) { 30 | font-size: 3rem; 31 | // 56px. 32 | line-height: 1.16; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/global/utils/_variables.scss: -------------------------------------------------------------------------------- 1 | // Variables. 2 | 3 | $zi-highest: 50; 4 | $zi-high: 40; 5 | $zi-medium: 30; 6 | $zi-low: 20; 7 | $zi-lowest: 10; 8 | $zi-ground: 0; 9 | $zi-below-ground: -1; 10 | 11 | // Transition speeds. 12 | $standard--transition-speed: 300ms; 13 | $transition: $standard--transition-speed ease-in-out; 14 | $transition-fast: 0.1s linear; 15 | $motion-slow: 1000ms ease-in-out; 16 | $motion-default: 500ms ease-in-out; 17 | $motion-fast: 200ms ease-in-out; 18 | $motion-super-fast: 50ms ease-in-out; 19 | -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/layout/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mediacurrent/theme_generator_8/f13ff4bc06f196671b382c29bb9648976c549894/generators/app/templates/_src/patterns/layout/.gitkeep -------------------------------------------------------------------------------- /generators/app/templates/_src/patterns/pages/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mediacurrent/theme_generator_8/f13ff4bc06f196671b382c29bb9648976c549894/generators/app/templates/_src/patterns/pages/.gitkeep -------------------------------------------------------------------------------- /generators/app/templates/_src/styleguide/annotations/annotations.js: -------------------------------------------------------------------------------- 1 | { 2 | "el": ".logo", 3 | "title" : "Logo", 4 | "comment": "The logo image is an SVG file, which ensures that the logo displays crisply even on high resolution displays. A PNG fallback is provided for browsers that don't support SVG images.

Further reading: Optimizing Web Experiences for High Resolution Screens

" 5 | } 6 | -------------------------------------------------------------------------------- /generators/app/templates/_src/styleguide/data/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "title" : "Pattern Lab", 3 | "htmlClass": "pl", 4 | "bodyClass": "body", 5 | "modifier": "", 6 | "extra_attributes": "", 7 | "img": { 8 | "landscape_4x3": { 9 | "src": "https://via.placeholder.com/800x600?text=4:3", 10 | "alt": "4:3 aspect ratio sample image" 11 | }, 12 | "landscape_16x9": { 13 | "src": "https://via.placeholder.com/800x450?text=16:9", 14 | "alt": "16:9 aspect ratio sample image" 15 | }, 16 | "square": { 17 | "src": "https://via.placeholder.com/450x450?text=1:1", 18 | "alt": "1:1 aspect ratio sample image" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /generators/app/templates/_src/styleguide/data/listitems.json: -------------------------------------------------------------------------------- 1 | { 2 | "1": { 3 | "title": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 4 | "img": { 5 | "avatar": { 6 | "src": "https://placeimg.com/100/100/people", 7 | "alt": "Avatar" 8 | }, 9 | "square": { 10 | "src": "https://placeimg.com/300/300/nature", 11 | "alt": "Square" 12 | }, 13 | "rectangle": { 14 | "src": "https://placeimg.com/400/300/tech", 15 | "alt": "Rectangle" 16 | }, 17 | "landscape-4x3": { 18 | "src": "https://placeimg.com/400/300/tech", 19 | "alt": "4x3 Image" 20 | }, 21 | "landscape-16x9": { 22 | "src": "https://placeimg.com/640/360/tech", 23 | "alt": "16x9 Image" 24 | } 25 | }, 26 | "description": "Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Etiam vitae turpis sem. Fusce a sollicitudin urna, nec tristique enim. Curabitur mattis consequat egestas..", 27 | "url": "https://lipsum.com/" 28 | }, 29 | "2": { 30 | "title": "Nunc tristique eros sem, vel consectetur elit lacinia gravida", 31 | "img": { 32 | "avatar": { 33 | "src": "https://placeimg.com/100/100/nature", 34 | "alt": "Avatar" 35 | }, 36 | "square": { 37 | "src": "https://placeimg.com/300/300/tech", 38 | "alt": "Square" 39 | }, 40 | "rectangle": { 41 | "src": "https://placeimg.com/400/300/people", 42 | "alt": "Rectangle" 43 | }, 44 | "landscape-4x3": { 45 | "src": "https://placeimg.com/400/300/people", 46 | "alt": "4x3 Image" 47 | }, 48 | "landscape-16x9": { 49 | "src": "https://placeimg.com/640/360/people", 50 | "alt": "16x9 Image" 51 | } 52 | }, 53 | "description": "Ut condimentum ultricies dui, at commodo eros ultricies ac. Aenean libero quam, dignissim eu mattis nec, auctor sed magna. Integer ornare commodo diam, ac finibus est fringilla at. Mauris eget aliquam leo. Proin mauris ipsum, commodo et dignissim porttitor, tincidunt vitae velit. Mauris eget tempus ipsum, id finibus massa. ", 54 | "url": "https://lipsum.com" 55 | }, 56 | "3": { 57 | "title": "Praesent nunc felis, laoreet id viverra a, tempor non mauris", 58 | "img": { 59 | "avatar": { 60 | "src": "https://placeimg.com/100/100/tech", 61 | "alt": "Avatar" 62 | }, 63 | "square": { 64 | "src": "https://placeimg.com/300/300/people", 65 | "alt": "Square" 66 | }, 67 | "rectangle": { 68 | "src": "https://placeimg.com/400/300/nature", 69 | "alt": "Rectangle" 70 | }, 71 | "landscape-4x3": { 72 | "src": "https://placeimg.com/400/300/nature", 73 | "alt": "4x3 Image" 74 | }, 75 | "landscape-16x9": { 76 | "src": "https://placeimg.com/640/360/nature", 77 | "alt": "16x9 Image" 78 | } 79 | }, 80 | "description": "Morbi vestibulum, odio id lobortis sagittis, felis neque pellentesque justo, facilisis viverra orci massa et est. Sed ut scelerisque magna. Etiam fermentum interdum risus molestie imperdiet. Ut rutrum aliquam massa id pretium.", 81 | "url": "https://lipsum.com/" 82 | }, 83 | "4": { 84 | "title": "Vivamus porta sed odio at tincidunt", 85 | "img": { 86 | "avatar": { 87 | "src": "https://placeimg.com/100/100/animals", 88 | "alt": "Avatar" 89 | }, 90 | "square": { 91 | "src": "https://placeimg.com/300/300/arch", 92 | "alt": "Square" 93 | }, 94 | "rectangle": { 95 | "src": "https://placeimg.com/400/300/people/grayscale", 96 | "alt": "Rectangle" 97 | }, 98 | "landscape-4x3": { 99 | "src": "https://placeimg.com/400/300/people/greyscale", 100 | "alt": "4x3 Image" 101 | }, 102 | "landscape-16x9": { 103 | "src": "https://placeimg.com/640/360/people/greyscale", 104 | "alt": "16x9 Image" 105 | } 106 | }, 107 | "description": "Donec eleifend magna iaculis, fermentum elit sed, imperdiet risus. Curabitur semper dolor pulvinar arcu facilisis facilisis. Nullam a dapibus massa.", 108 | "url": "https://lipsum.com" 109 | }, 110 | "5": { 111 | "title": "Mauris enim dui, porttitor id blandit a, porta a tellus", 112 | "img": { 113 | "avatar": { 114 | "src": "https://placeimg.com/100/100/people/grayscale", 115 | "alt": "Avatar" 116 | }, 117 | "square": { 118 | "src": "https://placeimg.com/300/300/animals", 119 | "alt": "Square" 120 | }, 121 | "rectangle": { 122 | "src": "https://placeimg.com/400/300/arch", 123 | "alt": "Rectangle" 124 | }, 125 | "landscape-4x3": { 126 | "src": "https://placeimg.com/400/300/arch", 127 | "alt": "4x3 Image" 128 | }, 129 | "landscape-16x9": { 130 | "src": "https://placeimg.com/640/360/arch", 131 | "alt": "16x9 Image" 132 | } 133 | }, 134 | "description": "Aenean diam ante, porta nec placerat sed, cursus id enim. Sed vehicula tellus ut lacus rutrum, ut venenatis risus placerat. Donec viverra ipsum ut vehicula pulvinar. Nunc viverra cursus euismod.", 135 | "url": "https://lipsum.com" 136 | }, 137 | "6": { 138 | "title": "Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae", 139 | "img": { 140 | "avatar": { 141 | "src": "https://placeimg.com/100/100/arch", 142 | "alt": "Avatar" 143 | }, 144 | "square": { 145 | "src": "https://placeimg.com/300/300/animals", 146 | "alt": "Square" 147 | }, 148 | "rectangle": { 149 | "src": "https://placeimg.com/400/300/people/grayscale", 150 | "alt": "Rectangle" 151 | }, 152 | "landscape-4x3": { 153 | "src": "https://placeimg.com/400/300/people/grayscale", 154 | "alt": "4x3 Image" 155 | }, 156 | "landscape-16x9": { 157 | "src": "https://placeimg.com/640/360/people/grayscale", 158 | "alt": "16x9 Image" 159 | } 160 | }, 161 | "description": "Pellentesque a dui scelerisque, dignissim orci et, euismod sapien. Vivamus eu pretium urna. Mauris lacinia tortor ut aliquam facilisis. Phasellus hendrerit tellus et purus blandit, eu ornare ex auctor. Aliquam erat volutpat. Sed ligula lorem, malesuada scelerisque sollicitudin non, cursus non nulla. Sed suscipit magna nec risus rutrum, id bibendum ligula gravida.", 162 | "url": "https://lipsum.com" 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /generators/app/templates/_src/styleguide/drupal-twig/PatternLabTwigExtensions/BasicTwigExtensions.php: -------------------------------------------------------------------------------- 1 | ') { 95 | return '/'; 96 | } 97 | else { 98 | return $string; 99 | } 100 | }), 101 | new Twig_SimpleFunction('link', [$this, 'inertHref']), 102 | new Twig_SimpleFunction('file_url', [$this, 'returnParam']), 103 | new Twig_SimpleFunction('attach_library', [$this, 'returnNothing']), 104 | ]; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /generators/app/templates/_src/styleguide/drupal-twig/alter-twig.php: -------------------------------------------------------------------------------- 1 | addExtension(new \Twig_Extension_Debug()); 29 | $env->addExtension(new BasicTwigExtensions()); 30 | } 31 | -------------------------------------------------------------------------------- /generators/app/templates/_src/styleguide/meta/_foot.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{{ patternLabFoot }}} 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /generators/app/templates/_src/styleguide/meta/_foot.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ patternLabFoot | raw }} 4 | 5 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /generators/app/templates/_src/styleguide/meta/_head.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ title }} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | {{{ patternLabHead }}} 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /generators/app/templates/_src/styleguide/meta/_head.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ title }} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {{ patternLabHead | raw }} 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /generators/app/templates/_src/styleguide/pattern-scaffolding.scss: -------------------------------------------------------------------------------- 1 | // This stylesheet is for styles you want to include only when displaying demo 2 | // styles for grids, animations, color swatches, etc. 3 | // 4 | // By default we want to add padding so the style guide is more 5 | // readable. This can be overridden in each patterns data file. 6 | 7 | // Import site utilities. 8 | @import '../patterns/global/utils/init'; 9 | 10 | .pl { 11 | padding: 35px; 12 | } 13 | 14 | .no-padding { 15 | padding: 0; 16 | } 17 | 18 | #sg-patterns { 19 | /* stylelint-disable-next-line declaration-no-important */ 20 | box-sizing: border-box !important; 21 | max-width: 100%; 22 | padding: 0 8px; 23 | } 24 | 25 | .pl-swatch { 26 | border: 3px solid rgba($color-black, 0.15); 27 | border-radius: 3px; 28 | display: inline-block; 29 | height: 180px; 30 | margin-bottom: 5px; 31 | position: relative; 32 | vertical-align: top; 33 | width: 180px; 34 | } 35 | 36 | .pl-swatch__hex { 37 | background-color: rgba($color-black, 0.5); 38 | bottom: 0; 39 | color: $color-white; 40 | font-size: small; 41 | left: 0; 42 | padding: 15px; 43 | position: absolute; 44 | width: 100%; 45 | } 46 | 47 | // Increases size of pattern category and pattern name. 48 | .pl-c-category__title a { 49 | /* stylelint-disable-next-line declaration-no-important */ 50 | font-size: 32px !important; 51 | } 52 | 53 | .pl-c-pattern__title a { 54 | /* stylelint-disable-next-line declaration-no-important */ 55 | font-size: 24px !important; 56 | } 57 | 58 | // Patternlab display of icons. 59 | .pl-icons { 60 | 61 | .icon { 62 | fill: $color-gray-dk; 63 | height: 50px; 64 | margin: 20px auto; 65 | max-width: 100%; 66 | vertical-align: middle; 67 | width: auto; 68 | } 69 | } 70 | 71 | .pl-icons__item { 72 | border: 3px solid rgba($color-black, 0.15); 73 | border-radius: 3px; 74 | display: inline-block; 75 | margin-bottom: 5px; 76 | min-width: 100px; 77 | position: relative; 78 | text-align: center; 79 | vertical-align: top; 80 | } 81 | 82 | .pl-icons__name { 83 | background-color: rgba($color-black, 0.25); 84 | font-size: small; 85 | margin: 0; 86 | padding: 15px; 87 | } 88 | -------------------------------------------------------------------------------- /generators/app/templates/_src/templates/.gitkeep: -------------------------------------------------------------------------------- 1 | Drupal templates and template suggestions go here. 2 | -------------------------------------------------------------------------------- /generators/app/templates/_src/templates/field/field.html.twig: -------------------------------------------------------------------------------- 1 | {# 2 | /** 3 | * @file 4 | * Theme override for a field. 5 | * 6 | * Instead of overriding the theming for all fields, you can also just override 7 | * theming for a subset of fields using 8 | * 9 | * @link themeable Theme hook suggestions. @endlink For example, 10 | * here are some theme hook suggestions that can be used for a field_foo field 11 | * on an article node type: 12 | * - field--node--field-foo--article.html.twig 13 | * - field--node--field-foo.html.twig 14 | * - field--node--article.html.twig 15 | * - field--field-foo.html.twig 16 | * - field--text-with-summary.html.twig 17 | * - field.html.twig 18 | * 19 | * Available variables: 20 | * - attributes: HTML attributes for the containing element. 21 | * - label_hidden: Whether to show the field label or not. 22 | * - title_attributes: HTML attributes for the title. 23 | * - label: The label for the field. 24 | * - multiple: TRUE if a field can contain multiple items. 25 | * - items: List of all the field items. Each item contains: 26 | * - attributes: List of HTML attributes for each item. 27 | * - content: The field item's content. 28 | * - entity_type: The entity type to which the field belongs. 29 | * - field_name: The name of the field. 30 | * - field_type: The type of the field. 31 | * - label_display: The display settings for the label. 32 | * 33 | * @see template_preprocess_field() 34 | */ 35 | #} 36 | {% 37 | set title_classes = [ 38 | label_display == 'visually_hidden' ? 'visually-hidden', 39 | ] 40 | %} 41 | {# If the label is hidden don't add any additional markup. #} 42 | {% if label_hidden %} 43 | {% if multiple %} 44 | {% for item in items %} 45 | {{ item.content }} 46 | {% endfor %} 47 | {% else %} 48 | {% for item in items %} 49 | {{ item.content }} 50 | {% endfor %} 51 | {% endif %} 52 | {# If the label is shown add the default Drupal wrapping markup. #} 53 | {% else %} 54 | 55 | {{ label }} 56 | {% if multiple %} 57 |
58 | {% endif %} 59 | {% for item in items %} 60 | {{ item.content }}
61 | {% endfor %} 62 | {% if multiple %} 63 | 64 | {% endif %} 65 | 66 | {% endif %} 67 | -------------------------------------------------------------------------------- /generators/app/templates/_theme_name.breakpoints.yml: -------------------------------------------------------------------------------- 1 | # Breakpoints are used for responsive image configuration within Drupal 8. 2 | # https://www.drupal.org/docs/8/theming-drupal-8/working-with-breakpoints-in-drupal-8 3 | # 4 | # NOTE: While normally used for defining breakpoints for use with the 5 | # element, it's a better idea to use with srcset and sizes instead. 6 | # - https://css-tricks.com/responsive-images-youre-just-changing-resolutions-use-srcset/ 7 | # - https://cloudfour.com/thinks/responsive-images-101-part-5-sizes/ 8 | 9 | # Below is an example of how to create srcset placeholders in Drupal. 10 | # The sizes attribute still needs to be configured manually within 11 | # `admin/config/media/responsive-image-style`. 12 | # More information: https://chromatichq.com/blog/responsive-images-drupal-8-using-srcset 13 | 14 | <%= themeNameMachine %>.srcset.16_9: 15 | label: '16:9' 16 | mediaQuery: '' 17 | weight: 0 18 | multipliers: 19 | - 1x 20 | group: <%= themeName %> Srcset 21 | 22 | <%= themeNameMachine %>.srcset.2_1: 23 | label: '2:1' 24 | mediaQuery: '' 25 | weight: 1 26 | multipliers: 27 | - 1x 28 | group: <%= themeName %> Srcset 29 | -------------------------------------------------------------------------------- /generators/app/templates/_theme_name.info.yml: -------------------------------------------------------------------------------- 1 | # Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= pkg.name %> <%= pkg.version %> 2 | 3 | # Drupal's .info.yml files allow themers to easily specify some of the static 4 | # properties of your theme. Properties such as its name, libraries of style 5 | # sheets and javascripts, and block regions. 6 | # 7 | # Drupal 8 stores a cache of the data in this .info.yml files. If you modify any 8 | # lines in this file, you MUST refresh Drupal 8's cache. You can do this with 9 | # the "drush cr" command or by simply visiting the Appearance page at 10 | # admin/appearance. 11 | 12 | name: <%= themeName %> 13 | type: theme 14 | description: <%= themeDesc %> 15 | package: Other 16 | core_version_requirement: ^8 || ^9 17 | # Classy vs. Stable as a base theme: https://www.lullabot.com/articles/a-tale-of-two-base-themes-in-drupal-8-core 18 | base theme: <%= baseTheme %> 19 | 20 | # This section controls the libraries for your theme. For full documentation, 21 | # see https://www.drupal.org/node/2216195 22 | 23 | # The "libraries-override:" section will allow you to: 24 | # - Replace an entire library. 25 | # - Remove an entire library. 26 | # - Replace an asset in a library with another asset. 27 | # - Remove an asset from a library. 28 | libraries-override: 29 | system/base: 30 | css: 31 | component: 32 | # Replace one file with another. 33 | /core/themes/stable/css/system/components/hidden.module.css: dist/css/hidden.css 34 | 35 | # The "libraries-extend:" section will allow you to add your own JS or CSS to 36 | # existing libraries. 37 | # libraries-extend: 38 | 39 | # The "libraries:" section will add a library to _all_ pages. 40 | libraries: 41 | - <%= themeNameMachine %>/global 42 | # Example adding the webfonts library to every page. 43 | # See <%= themeNameMachine %>.libraries.yml for more info. 44 | #- <%= themeNameMachine %>/webfonts 45 | 46 | # This section is used by the contrib module, Component Libraries. It allows you 47 | # to reference .twig files in your sass/ directory by using the Twig namespace: 48 | # @<%= themeNameMachine %> 49 | # See https://www.drupal.org/project/components for more information. 50 | components: 51 | namespaces: 52 | <%= themeNameMachine %>: 53 | - src/patterns/global 54 | - src/patterns/components 55 | - src/patterns/pages 56 | - src/templates 57 | 58 | # This section lists the regions defined in the theme (our base theme) default 59 | # page.html.twig and maintenance-page.html.twig files. The name before 60 | # the semi-colon is the machine name of the region. The text after the 61 | # semi-colon is the region's description used on the admin/structure/block page. 62 | regions: 63 | header: Header 64 | content: Content 65 | footer: Footer 66 | -------------------------------------------------------------------------------- /generators/app/templates/_theme_name.libraries.yml: -------------------------------------------------------------------------------- 1 | # These lines define the <%= themeNameMachine %>/global library. 2 | global: 3 | css: 4 | # The SMACSS category, "base", is loaded before other categories. Drupal 8 5 | # loads stylesheets based on the SMACSS ordering of: 6 | # base, layout, component, state, theme 7 | base: 8 | dist/css/global.css: {} 9 | 10 | # Example external library. 11 | # webfonts: 12 | # version: 1.x 13 | # js: 14 | # https://fast.fonts.net/example.js: { type: external, attributes: { async: true }} 15 | 16 | # Attach each library by using attach_library() within the components twig template. 17 | # More info: https://www.drupal.org/node/2456753 18 | 19 | # Example component library. 20 | # site-logo: 21 | # css: 22 | # component: 23 | # dist/css/site-logo.css: {} 24 | # # If this library had JavaScript you could specify it like this: 25 | # js: 26 | # js/script.js: {} 27 | # # If this library had dependencies on other libraries, you could specify it like this: 28 | # dependencies: 29 | # - <%= themeNameMachine %>/clearfix 30 | # - <%= themeNameMachine %>/visually-hidden 31 | -------------------------------------------------------------------------------- /generators/app/templates/_theme_name.theme: -------------------------------------------------------------------------------- 1 | theme. 6 | */ 7 | 8 | /** 9 | * Implements hook_preprocess_HOOK() for html.html.twig. 10 | */ 11 | function <%= themeNameMachine %>_preprocess_html(array &$variables) { 12 | 13 | } 14 | 15 | /** 16 | * Implements hook_preprocess_page() for page.html.twig. 17 | */ 18 | function <%= themeNameMachine %>_preprocess_page(array &$variables) { 19 | 20 | } 21 | 22 | /** 23 | * Implements hook_preprocess_form(). 24 | */ 25 | function <%= themeNameMachine %>_preprocess_form(array &$variables) { 26 | 27 | } 28 | -------------------------------------------------------------------------------- /generators/app/templates/editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /generators/app/templates/eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | // http://eslint.org/docs/rules/ 3 | // By default, ESLint will look for configuration files in all parent 4 | // folders up to the root directory. Prevent this by telling ESLint 5 | // that this is the root of the project. 6 | "root": true, 7 | 8 | // Which environments your script is designed to run in. 9 | // Each environment brings with it a certain set of predefined global variables. 10 | "env": { 11 | "browser": true, 12 | "es6": true, 13 | "jquery": true 14 | }, 15 | 16 | "globals": { 17 | // Let ESLint know about defined global variables. 18 | "Drupal": true, 19 | "drupalSettings": true 20 | }, 21 | 22 | "parserOptions": { 23 | "ecmaVersion": 9 24 | }, 25 | 26 | // Inherit settings from ESLint Recommended config. 27 | // Rules above override any rules configured here. 28 | "extends": "eslint:recommended", 29 | 30 | // Let ESLint know that the Gulpfile and any Gulp 31 | // tasks run in Node not the browser. 32 | "overrides": [ 33 | { 34 | "files": [ 35 | "gulpfile.js", 36 | "gulp-tasks/**/*.js" 37 | ], 38 | "env": { 39 | "node": true 40 | } 41 | } 42 | ], 43 | 44 | // 0 - turn the rule off 45 | // 1 - turn the rule on as a warning (doesn't affect exit code) 46 | // 2 - turn the rule on as an error (exit code is 1 when triggered) 47 | "rules": { 48 | // Two space indentation. 49 | "indent": ["error", 2, { "SwitchCase": 1 }], 50 | 51 | // Prefer single quotes over double. 52 | "quotes": ["error", "single"], 53 | 54 | // Specify Unix line endings. 55 | "linebreak-style": ["error", "unix"], 56 | 57 | // Enforce using semicolons. 58 | "semi": ["error", "always"], 59 | 60 | // Enforce camelcase for variables. 61 | "camelcase": "error", 62 | 63 | // Prohibit use of == and != in favor of === and !==. 64 | "eqeqeq": "error", 65 | 66 | // If ESlint considers a file to be a Node module one 'use strict' 67 | // is needed at the top of the file. If not 'use strict' needs to 68 | // be added at the top of the function scope. 69 | "strict": ["error", "safe"], 70 | 71 | // Prohibit use of a variable before it is defined. 72 | "no-undef": "error", 73 | 74 | // Enforce line length to 80 characters. 75 | "max-len": ["error", { 76 | "ignoreUrls": true, 77 | "ignoreStrings": true, 78 | "ignoreRegExpLiterals": true, 79 | "ignoreTemplateLiterals": true 80 | }], 81 | 82 | // Require capitalized names for constructor functions. 83 | "new-cap": "error", 84 | 85 | // Warn when variables are defined but never used. 86 | "no-unused-vars": "warn", 87 | 88 | // Require one var declaration for each variable and 89 | // declare each variable on a newline. 90 | "one-var": ["error", "never"], 91 | 92 | // Enforce stroustrup style for braces. 93 | "brace-style": ["error", "stroustrup"], 94 | 95 | // Validates JSDoc comments are syntactically correct 96 | "valid-jsdoc": "error", 97 | 98 | // Treat var as Block Scoped 99 | "block-scoped-var": "warn", 100 | 101 | // Require Following Curly Brace Conventions 102 | "curly": "error", 103 | 104 | // Disallow Use of Alert 105 | "no-alert": "warn", 106 | 107 | // Disallow eval() 108 | "no-eval": "error", 109 | 110 | // Disallow the type conversion with shorter notations 111 | "no-implicit-coercion": "error", 112 | 113 | // Disallow Functions in Loops 114 | "no-loop-func": "error", 115 | 116 | // Disallow Script URLs 117 | "no-script-url": "error", 118 | 119 | // Disallow Use of the Comma Operator 120 | "no-sequences": "error", 121 | 122 | // Disallow unnecessary concatenation of strings 123 | "no-useless-concat": "error", 124 | 125 | // Disallow Early Use 126 | "no-use-before-define": "error", 127 | 128 | // Require spacing in object literals. 129 | // "object-curly-spacing": ["error", "always"], 130 | 131 | // Require file to end with single newline 132 | "eol-last": "error", 133 | 134 | // Disallow trailing spaces at the end of lines 135 | "no-trailing-spaces": "error", 136 | 137 | // Disallow Dangling Underscores in Identifiers 138 | "no-underscore-dangle": "error", 139 | 140 | // Require JSDoc comment 141 | "require-jsdoc": "error", 142 | 143 | // Require Or Disallow Space Before Blocks 144 | "space-before-blocks": "error", 145 | 146 | // Disallow Yoda Conditions 147 | "yoda": "error" 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /generators/app/templates/gitignore: -------------------------------------------------------------------------------- 1 | # Ignore the node modules folder (created by 'npm install'). 2 | node_modules 3 | 4 | # We absolutely don't want to have the .sass-cache in git. 5 | .sass-cache 6 | 7 | # Ignore any errors thrown by npm. 8 | npm-debug.log 9 | 10 | # Ignore any map files. These don't need to be committed. 11 | *.map 12 | <% if (ignoreDist) { %> 13 | # Ignore any artifact files. 14 | # The patternlab build needs the subfolder to exist but we don't 15 | # want to commit anything in them beyond the .gitkeep file. 16 | dist/css/* 17 | !dist/css/.gitkeep 18 | dist/fonts/* 19 | !dist/fonts/.gitkeep 20 | dist/images/* 21 | !dist/images/.gitkeep 22 | dist/js/* 23 | !dist/js/.gitkeep 24 | 25 | # Ignore Patternlab files that change every time. 26 | patternlab 27 | dependencyGraph.json 28 | <% } %> 29 | -------------------------------------------------------------------------------- /generators/app/templates/gitkeep: -------------------------------------------------------------------------------- 1 | // This is just an empty file so the empty folder will stay in git. 2 | -------------------------------------------------------------------------------- /generators/app/templates/prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "semi": true, 4 | "singleQuote": true, 5 | "trailingComma": "none", 6 | "bracketSpacing": true, 7 | "arrowParens": "always" 8 | } 9 | -------------------------------------------------------------------------------- /generators/component/index.js: -------------------------------------------------------------------------------- 1 | var Generator = require('yeoman-generator'); 2 | var _ = require('lodash'); 3 | var chalk = require('chalk'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const jsYaml = require('js-yaml'); 7 | const assert = require('assert'); 8 | 9 | module.exports = class extends Generator { 10 | constructor(args, options) { 11 | super(args, options); 12 | 13 | // Allow the theme generator main app to pass through the machine name. 14 | // --theme-name=hey_yall 15 | this.option('theme-name', { 16 | type: String, 17 | desc: 'The theme machine name' 18 | }); 19 | 20 | // Allow the user to pass in a component name. 21 | // --name=HeyYall 22 | this.option('name', { 23 | type: String, 24 | desc: 'The component name' 25 | }); 26 | 27 | // Allow the user to opt in to a JS behavior file. 28 | // --include-js 29 | this.option('include-js', { 30 | type: Boolean, 31 | desc: 'Include a JS file' 32 | }); 33 | } 34 | 35 | initializing() { 36 | // Create an object to contain all our name variations. 37 | this.componentName = {}; 38 | 39 | const rawName = this.options.name || ''; 40 | // Preserve the raw layout name. 41 | this.componentName.raw = rawName; 42 | // Create a dashed version of the layout name. 43 | this.componentName.dashed = _.kebabCase(rawName); 44 | 45 | // Grab the theme machine name if it's passed in. 46 | const themeName = this.options.themeName || ''; 47 | this.themeNameMachine = _.snakeCase(themeName); 48 | 49 | // Check to see if we need to include a JS file. 50 | this.includeJS = this.options.includeJs || false; 51 | } 52 | 53 | // Prompts need at least two arguments passed in to work: 54 | // theme name and Component name. Without those we can't create 55 | // a basic component. 56 | prompting() { 57 | // If we DO have both the theme name _and_ component name passed 58 | // as arguments we can skip all the prompts. 59 | if (this.options.themeName && this.options.name) { 60 | return; 61 | } 62 | 63 | let defaultThemeName = ''; 64 | 65 | try { 66 | // See if package.json exists. 67 | fs.accessSync(this.destinationPath('package.json'), fs.constants.R_OK); 68 | // If it does, read it and use the name as our default 69 | // theme machine name. 70 | const pkg = JSON.parse( 71 | fs.readFileSync( 72 | path.resolve(this.destinationPath('package.json')), 'utf8' 73 | ) 74 | ); 75 | defaultThemeName = pkg.name; 76 | } 77 | // If it doesn't, let the user know we can't continue. We need to run from 78 | // the theme root. 79 | catch (err) { 80 | assert.fail( 81 | ` 82 | 🚨 ${chalk.red(this.destinationPath('package.json'))} ${chalk.red('is missing')}. 83 | ${chalk.blue('Make sure you\'re running this command from your theme root.')}` 84 | ); 85 | } 86 | 87 | let prompts = [{ 88 | name: 'themeNameMachine', 89 | message: 'What is your theme\'s machine name? EX: unicorn_theme', 90 | default: defaultThemeName 91 | }, 92 | { 93 | name: 'name', 94 | message: 'What should we name your component? EX: Hero', 95 | default: 'Hero' 96 | }, 97 | { 98 | name: 'includeJSBehavior', 99 | type: 'confirm', 100 | message: 'Would you like to include a JavaScript Behavior file?', 101 | default: false 102 | }]; 103 | 104 | return this.prompt(prompts).then(function (props) { 105 | 106 | // Use the user provided theme machine name. 107 | this.themeNameMachine = _.snakeCase(props.themeNameMachine); 108 | 109 | // Use the component name provided. 110 | this.componentName.raw = props.name; 111 | // Create a dashed version of the layout name. 112 | this.componentName.dashed = _.kebabCase(props.name); 113 | 114 | // See if we need to include a JS behavior file. 115 | this.includeJS = props.includeJSBehavior; 116 | 117 | // To access props later use this.props.someAnswer; 118 | this.props = props; 119 | }.bind(this)); 120 | } 121 | 122 | writing() { 123 | // Build out the library structure so we can append it to the 124 | // libraries.yml file. 125 | let componentLibrary = {}; 126 | const component = this.componentName.dashed; 127 | 128 | // Could prob break this out to it's own thing eventually. 129 | if (this.includeJS) { 130 | componentLibrary = { 131 | [component]: { 132 | css: { 133 | component: { 134 | [`dist/css/${component}.css`]: {} 135 | } 136 | }, 137 | js: { 138 | [`dist/js/${component}.js`]: {} 139 | }, 140 | dependencies: [ 141 | 'core/drupal', 142 | 'core/jquery' 143 | ] 144 | } 145 | }; 146 | } 147 | else { 148 | componentLibrary = { 149 | [component]: { 150 | css: { 151 | component: { 152 | [`dist/css/${component}.css`]: {} 153 | } 154 | } 155 | } 156 | }; 157 | } 158 | 159 | // Add a blank line so the file is nicely formatted and the 160 | // appended data doesn't run into the current data within the file. 161 | fs.appendFileSync( 162 | this.destinationPath(this.themeNameMachine + '.libraries.yml'), 163 | '\r\n' 164 | ); 165 | 166 | // Update the libraries.yml file with the new component library. 167 | fs.appendFile( 168 | this.destinationPath(this.themeNameMachine + '.libraries.yml'), 169 | jsYaml.safeDump(componentLibrary), 170 | (err) => { 171 | if (err) { 172 | this.log( 173 | chalk.red(`Failed to update ${this.themeNameMachine}.libraries.yml`) 174 | ); 175 | } 176 | } 177 | ); 178 | 179 | // Write each file the component needs, adding the component 180 | // name where needed. 181 | this.fs.copyTpl( 182 | this.templatePath('_component/_component.json'), 183 | // eslint-disable-next-line max-len 184 | this.destinationPath('src/patterns/components/' + this.componentName.dashed + '/' + this.componentName.dashed + '.json'), 185 | { 186 | name: this.componentName.raw, 187 | dashed: this.componentName.dashed 188 | } 189 | ); 190 | this.fs.copyTpl( 191 | this.templatePath('_component/_component.scss'), 192 | // eslint-disable-next-line max-len 193 | this.destinationPath('src/patterns/components/' + this.componentName.dashed + '/' + this.componentName.dashed + '.scss'), 194 | { 195 | name: this.componentName.raw, 196 | dashed: this.componentName.dashed 197 | } 198 | ); 199 | this.fs.copyTpl( 200 | this.templatePath('_component/_component.twig'), 201 | // eslint-disable-next-line max-len 202 | this.destinationPath('src/patterns/components/' + this.componentName.dashed + '/' + this.componentName.dashed + '.twig'), 203 | { 204 | dashed: this.componentName.dashed, 205 | themeNameMachine: this.themeNameMachine 206 | } 207 | ); 208 | if (this.includeJS) { 209 | this.fs.copyTpl( 210 | this.templatePath('_component/_component.ejs'), 211 | // eslint-disable-next-line max-len 212 | this.destinationPath('src/patterns/components/' + this.componentName.dashed + '/' + this.componentName.dashed + '.js'), 213 | { 214 | camel: _.camelCase(this.componentName.raw), 215 | dashed: this.componentName.dashed, 216 | themeNameMachine: this.themeNameMachine 217 | } 218 | ); 219 | } 220 | } 221 | 222 | end() { 223 | this.log('------------------------------------------------------------'); 224 | // eslint-disable-next-line max-len 225 | this.log(`🎉 Created a new component named: "${chalk.red(this.componentName.raw)}"!`); 226 | this.log('------------------------------------------------------------'); 227 | 228 | // If the user followed the prompt workflow, make sure they know they 229 | // can run all this on the command line without the prompts. 230 | if (!this.options.name) { 231 | // eslint-disable-next-line max-len 232 | this.log('To generate components faster you can pass in arguments to the subgenerator!'); 233 | this.log('For example: 👇'); 234 | // eslint-disable-next-line max-len 235 | this.log(chalk.blue(`npm run generate -- --name="${this.componentName.raw}" --theme-name="${this.themeNameMachine}"`)); 236 | this.log('Or add a Drupal JavaScript behavior to that with:'); 237 | // eslint-disable-next-line max-len 238 | this.log(chalk.blue(`npm run generate -- --name="${this.componentName.raw}" --theme-name="${this.themeNameMachine}" --include-js`)); 239 | } 240 | } 241 | }; 242 | -------------------------------------------------------------------------------- /generators/component/templates/_component/_component.ejs: -------------------------------------------------------------------------------- 1 | !((document, Drupal, $) => { 2 | 'use strict'; 3 | 4 | /** 5 | * Use this to describe what your behavior does. 6 | */ 7 | Drupal.behaviors.<%= camel %> = { 8 | 9 | attach: function(context) { 10 | // If you don't need jQuery: 11 | // 1. Remove the `jQuery` and `$` refrences from this file. 12 | // 2. Remove the `core/jquery` dependency from the <%= camel %> library 13 | // within the <%= themeNameMachine %>.libraries.yml file. 14 | } 15 | }; 16 | })(document, Drupal, jQuery); 17 | -------------------------------------------------------------------------------- /generators/component/templates/_component/_component.json: -------------------------------------------------------------------------------- 1 | { 2 | "heading": "<%= name %> component", 3 | "body": "This is mock data from the <%= dashed %>.json file" 4 | } 5 | -------------------------------------------------------------------------------- /generators/component/templates/_component/_component.scss: -------------------------------------------------------------------------------- 1 | // Import site utilities. 2 | @import '../../global/utils/init'; 3 | -------------------------------------------------------------------------------- /generators/component/templates/_component/_component.twig: -------------------------------------------------------------------------------- 1 | {{ attach_library('<%= themeNameMachine %>/<%= dashed %>') }} 2 |
3 |

{{ heading }}

4 |

{{ body }}

5 |
6 | -------------------------------------------------------------------------------- /generators/starter-kit/add-dependency.js: -------------------------------------------------------------------------------- 1 | // For specific components we need to add additional dependencies. 2 | module.exports = function addDependency (component, themeNameMachine) { 3 | let libraries = {}; 4 | 5 | // If this is the carousel component, it needs SlickJS added 6 | // as a dependency. 7 | if (component === 'carousel') { 8 | libraries = { 9 | [component]: { 10 | css: { 11 | component: { 12 | [`dist/css/${component}.css`]: {} 13 | } 14 | }, 15 | js: { 16 | [`dist/js/${component}.js`]: {} 17 | }, 18 | dependencies: [ 19 | 'core/drupal', 20 | 'core/jquery', 21 | `${themeNameMachine}/slick-carousel` 22 | ] 23 | } 24 | }; 25 | } 26 | 27 | return Object.keys(libraries).length && libraries; 28 | }; 29 | -------------------------------------------------------------------------------- /generators/starter-kit/add-third-party.js: -------------------------------------------------------------------------------- 1 | // For specific components we need to add additional dependencies. 2 | module.exports = function addThirdParty(libraries) { 3 | 4 | // See if Slick is one of the libraries already. 5 | const containsSlick = libraries.find(lib => lib['slick-carousel']); 6 | // See if the libraries include the carousel library because 7 | // if so, we need to add the SlickJS dependency. 8 | const containsCarousel = libraries.find(lib => lib['carousel']); 9 | 10 | // If slick hasn't been added yet and it's needed, add it. 11 | if (!containsSlick && containsCarousel) { 12 | const slick = { 13 | ['slick-carousel']: { 14 | css: { 15 | component: { 16 | ['/libraries/slick-carousel/slick/slick.css']: {} 17 | } 18 | }, 19 | js: { 20 | ['/libraries/slick-carousel/slick/slick.min.js']: { minified: true } 21 | }, 22 | dependencies: [ 23 | 'core/jquery' 24 | ] 25 | } 26 | }; 27 | // Add slick to the libraries object. 28 | libraries.push(slick); 29 | } 30 | 31 | return libraries; 32 | }; 33 | -------------------------------------------------------------------------------- /generators/starter-kit/build-components.js: -------------------------------------------------------------------------------- 1 | // Scaffolds out the component folders and returns the generated manifest 2 | // used to populate the Drupal libraries file and copy over any 3 | // Drupal templates. 4 | 5 | const fs = require('fs'); 6 | // Experimental, could switch to normal FS I suppose. 7 | const fsPromises = fs.promises; 8 | // Special helper for adding additional library dependencies. 9 | const addDependency = require('./add-dependency'); 10 | 11 | module.exports = async function buildComponents({ 12 | exampleComponents, 13 | app 14 | }) { 15 | // Build an object that will be used to populate the *.libraries.yml file 16 | // with data for any selected example components. 17 | // eslint-disable-next-line max-len 18 | return await Promise.all(exampleComponents.map(async (component) => { 19 | // Copy the selected example component into the theme. 20 | // Exclude the templates folder, it needs to go in a different directory. 21 | app.fs.copyTpl( 22 | [ 23 | app.templatePath(`${component}`), 24 | `!${app.templatePath(`${component}`)}/templates`, 25 | `!${app.templatePath(`${component}/${component}.twig`)}` 26 | ], 27 | app.destinationPath(`src/patterns/components/${component}`), 28 | { 29 | themeNameMachine: app.themeNameMachine 30 | } 31 | ); 32 | 33 | // Copy the twig template, passing in the themeMachineName 34 | // so it can be used as a twig namespace. 35 | app.fs.copyTpl( 36 | app.templatePath(`${component}/${component}.twig`), 37 | // eslint-disable-next-line max-len 38 | app.destinationPath(`src/patterns/components/${component}/${component}.twig`), 39 | { 40 | themeNameMachine: app.themeNameMachine 41 | } 42 | ); 43 | 44 | // Copy any Drupal templates into the templates directory. 45 | fs.readdir( 46 | app.templatePath(`${component}/templates`), 47 | (err, files) => { 48 | if (!err) { 49 | // Make sure the file is a twig file. 50 | const twigFiles = files.filter(name => name.endsWith('.twig')); 51 | // Loop over all template files, pass through the theme machine name, 52 | // and copy them to the src/templates/${component} directory. 53 | twigFiles.forEach(file => { 54 | app.fs.copyTpl( 55 | app.templatePath(`${component}/templates/${file}`), 56 | app.destinationPath(`src/templates/${component}/${file}`), 57 | { 58 | themeNameMachine: app.themeNameMachine 59 | } 60 | ); 61 | }); 62 | } 63 | } 64 | ); 65 | 66 | // Check to see if the example component contains a JS file. 67 | const jsFile = app.templatePath(`${component}/${component}.js`); 68 | try { 69 | await fsPromises.access(jsFile, fs.constants.F_OK); 70 | 71 | // If there's a JS file in the example component, add it to the 72 | // library. 73 | 74 | // Add in additional dependencies OR provide a basic fallback for 75 | // components with JS files. 76 | return addDependency(component, app.themeNameMachine) || { 77 | [component]: { 78 | css: { 79 | component: { 80 | [`dist/css/${component}.css`]: {} 81 | } 82 | }, 83 | js: { 84 | [`dist/js/${component}.js`]: {} 85 | }, 86 | dependencies: [ 87 | 'core/drupal', 88 | 'core/jquery' 89 | ] 90 | } 91 | }; 92 | } 93 | // If there's no JS file, only add the css. 94 | catch (error) { 95 | return { 96 | [component]: { 97 | css: { 98 | component: { 99 | [`dist/css/${component}.css`]: {} 100 | } 101 | } 102 | } 103 | }; 104 | } 105 | })); 106 | }; 107 | -------------------------------------------------------------------------------- /generators/starter-kit/index.js: -------------------------------------------------------------------------------- 1 | const Generator = require('yeoman-generator'); 2 | const _ = require('lodash'); 3 | const chalk = require('chalk'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const jsYaml = require('js-yaml'); 7 | const assert = require('assert'); 8 | const replace = require('replace-in-file'); 9 | 10 | // Helper to generate component libraries. 11 | const buildComponents = require('./build-components'); 12 | // Helper to add in third party dependencies. 13 | const addThirdParty = require('./add-third-party'); 14 | 15 | module.exports = class extends Generator { 16 | constructor(args, options) { 17 | super(args, options); 18 | 19 | // Allow the theme generator main app to pass through the machine name. 20 | // --theme-name=hey_yall 21 | this.option('theme-name', { 22 | type: String, 23 | desc: 'The theme machine name' 24 | }); 25 | } 26 | 27 | initializing() { 28 | // Grab the theme machine name if it's passed in. 29 | const themeName = this.options.themeName || ''; 30 | this.themeNameMachine = _.snakeCase(themeName); 31 | } 32 | 33 | prompting() { 34 | const prompts = [ 35 | { 36 | type: 'checkbox', 37 | name: 'howMuchTheme', 38 | message: 'Which starter components do you want to add to your theme?', 39 | // Be nice for these to be populated from an external repo 40 | // and use a package.json to build this list. 41 | // Or just do it based on folder name. /shrug 42 | choices: [ 43 | { 44 | value: 'accordion', 45 | name: 'Accordion' 46 | }, 47 | { 48 | value: 'button', 49 | name: 'Button' 50 | }, 51 | { 52 | value: 'card', 53 | name: 'Card (Depends on Eyebrow and Heading components.)' 54 | }, 55 | { 56 | value: 'card-list', 57 | name: 'Card List (Depends on Card, Eyebrow and Heading components.)' 58 | }, 59 | { 60 | value: 'carousel', 61 | name: 'Carousel (Depends on Heading, Media and Button components.)' 62 | }, 63 | { 64 | value: 'eyebrow', 65 | name: 'Eyebrow' 66 | }, 67 | { 68 | value: 'heading', 69 | name: 'Heading' 70 | }, 71 | { 72 | value: 'hero', 73 | name: 'Hero (Depends on Heading, Media and Button components.)' 74 | }, 75 | { 76 | value: 'media-item', 77 | name: 'Media' 78 | }, 79 | { 80 | value: 'message', 81 | name: 'Drupal Messages' 82 | }, 83 | { 84 | value: 'tabs', 85 | name: 'Drupal Tabs' 86 | } 87 | ] 88 | } 89 | ]; 90 | 91 | // If there's no theme machine name provided, prompt the user for it. 92 | if (!this.themeNameMachine) { 93 | let defaultThemeName = ''; 94 | 95 | try { 96 | // See if package.json exists. 97 | fs.accessSync(this.destinationPath('package.json'), fs.constants.R_OK); 98 | // If it does, read it and use the name as our default 99 | // theme machine name. 100 | const pkg = JSON.parse( 101 | fs.readFileSync( 102 | path.resolve(this.destinationPath('package.json')), 'utf8' 103 | ) 104 | ); 105 | defaultThemeName = pkg.name; 106 | } 107 | catch (err) { 108 | assert.fail( 109 | ` 110 | 🚨 ${chalk.red(this.destinationPath('package.json'))} ${chalk.red('is missing')}. 111 | ${chalk.blue('Make sure you\'re running this command from your theme root.')}` 112 | ); 113 | } 114 | 115 | prompts.push({ 116 | name: 'themeNameMachine', 117 | message: 'What is your theme\'s machine name? EX: unicorn_theme', 118 | default: defaultThemeName 119 | }); 120 | } 121 | 122 | return this.prompt(prompts).then(function (props) { 123 | // Try to use the name passed in via option else use 124 | // the user provided theme machine name. 125 | this.themeNameMachine = this.themeNameMachine || props.themeNameMachine; 126 | 127 | // Check to see if any of the components that need dependencies 128 | // are selected. 129 | // card requires eyebrow, heading 130 | if (props.howMuchTheme.includes('card')) { 131 | props.howMuchTheme.push('eyebrow', 'heading'); 132 | } 133 | // card-list requires card, eyebrow, heading 134 | if (props.howMuchTheme.includes('card-list')) { 135 | props.howMuchTheme.push('card', 'eyebrow', 'heading'); 136 | } 137 | // carousel OR hero requires heading, media, button 138 | if ( 139 | props.howMuchTheme.includes('carousel') || 140 | props.howMuchTheme.includes('hero') 141 | ) { 142 | props.howMuchTheme.push('heading', 'media-item', 'button'); 143 | } 144 | // props.howMuchTheme is an array of all selected options. 145 | // i.e. [ 'hero', 'tabs', 'messages' ] 146 | // Remove any duplicate components using uniq(). 147 | this.exampleComponents = _.uniq(props.howMuchTheme); 148 | 149 | // Filter out any components that already exist within 150 | // an existing theme. 151 | try { 152 | // Read the theme libraries.yml file to see which components 153 | // already exist. 154 | const librariesFile = jsYaml.safeLoad( 155 | fs.readFileSync( 156 | this.destinationPath(`${this.themeNameMachine}.libraries.yml`), 157 | 'utf8' 158 | ) 159 | ); 160 | const existingLibraries = Object.keys(librariesFile); 161 | // Exclude any components that already exist in the libraries file. 162 | this.exampleComponents = _.difference( 163 | this.exampleComponents, 164 | existingLibraries 165 | ); 166 | } 167 | catch (e) { 168 | // No libraries file found but that's ok. It won't be found unless 169 | // this is run from an existing theme. 170 | } 171 | 172 | // To access props later use this.props.someAnswer; 173 | this.props = props; 174 | }.bind(this)); 175 | } 176 | 177 | writing() { 178 | // If any example components were selected... 179 | if (this.exampleComponents.length > 0) { 180 | // ...copy over the example components. 181 | buildComponents({ 182 | exampleComponents: this.exampleComponents, 183 | app: this 184 | }) 185 | .then(buildComponentsConfig => { 186 | // Add in any third party dependencies before we write 187 | // to the libraries.yml file. 188 | buildComponentsConfig = addThirdParty(buildComponentsConfig); 189 | 190 | // Loop through the different components and append them to the 191 | // libraries.yml file. 192 | buildComponentsConfig.forEach((component) => { 193 | // This is a little weird: 194 | // 1. If this is being run from the parent generator we need to use 195 | // this.fs.append() since we're copying the original libraries.yml 196 | // template. If we just use fs.appendFileSync() there 197 | // will be a conflict. 198 | // 2. However if this is being run as a standalone sub generator 199 | // this has to use this.appendFileSync() because otherwise it tries 200 | // to overwrite the existing libraries file. 201 | 202 | // Find out if this was called via the parent generator: 203 | if (this.options.themeName) { 204 | this.fs.append( 205 | this.destinationPath(this.themeNameMachine + '.libraries.yml'), 206 | jsYaml.safeDump(component), 207 | { 208 | trimEnd: false, 209 | separator: '\r\n' 210 | } 211 | ); 212 | } 213 | else { 214 | // Add a blank line so the file is nicely formatted and the 215 | // appended data doesn't run into the current data within 216 | // the file. 217 | fs.appendFileSync( 218 | this.destinationPath(this.themeNameMachine + '.libraries.yml'), 219 | '\r\n' 220 | ); 221 | 222 | // Update the libraries.yml file with the new component library. 223 | fs.appendFileSync( 224 | this.destinationPath(this.themeNameMachine + '.libraries.yml'), 225 | jsYaml.safeDump(component), 226 | (err) => { 227 | if (err) { 228 | this.log( 229 | chalk.red( 230 | `Failed to update ${this.themeNameMachine}.libraries.yml` 231 | ) 232 | ); 233 | } 234 | } 235 | ); 236 | } 237 | }); 238 | }) 239 | .catch(error => { 240 | // eslint-disable-next-line no-console 241 | console.error(error); 242 | }); 243 | } 244 | } 245 | 246 | install() { 247 | // If `carousel` is selected, attempt to link up the slick 248 | // carousel dependency. It'll still be up to the user to add SlickJS 249 | // as a project dependency. 250 | if (this.exampleComponents.indexOf('carousel') !== -1) { 251 | // If a carousel third party library is required, add it to Pattern Lab 252 | // so it works there. 253 | replace({ 254 | files: this.destinationPath('src/styleguide/meta/_00-head.twig'), 255 | from: //g, 256 | to: '' 257 | }) 258 | .catch(() => { 259 | this.log('Failed to append slick css to Pattern Lab file styleguide/meta/_00-head.twig'); 260 | }); 261 | replace({ 262 | files: this.destinationPath('src/styleguide/meta/_01-foot.twig'), 263 | from: //g, 264 | to: '' 265 | }) 266 | .catch(() => { 267 | this.log('Failed to append slick js to Pattern Lab file styleguide/meta/_01-foot.twig'); 268 | }); 269 | } 270 | } 271 | 272 | end() { 273 | // If `carousel` is selected, inform the user that they need to install 274 | // the SlickJS carousel dependency. 275 | if (this.exampleComponents.indexOf('carousel') !== -1) { 276 | this.log('------------------------------------------------------------'); 277 | this.log('👋 You installed the Carousel component which requires SlickJS.'); 278 | this.log('Install SlickJS using composer with:'); 279 | this.log('composer require npm-asset/slick-carousel --working-dir=../../../../'); 280 | this.log('OR'); 281 | this.log(`Manually add Slick to the /libraries folder and update the carousel library in the ${this.themeNameMachine}.libraries.yml file.`); 282 | this.log('https://github.com/kenwheeler/slick/'); 283 | this.log('------------------------------------------------------------'); 284 | } 285 | } 286 | }; 287 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/accordion/accordion-item.md: -------------------------------------------------------------------------------- 1 | --- 2 | hidden: true 3 | title: "Accordion Item" 4 | --- 5 | The accordion item is the individual accordion that's used inside the accordion grid. 6 | 7 | The component consists of a twig block called `accordion_content`. -------------------------------------------------------------------------------- /generators/starter-kit/templates/accordion/accordion-item.twig: -------------------------------------------------------------------------------- 1 |
2 | {# 3 | Optional: Pass in Drupal specific functionality. 4 | Note that this is mostly relevant to nodes and blocks. 5 | #} 6 | {# 7 | {{ title_prefix }} 8 | {{ title_suffix }} 9 | #} 10 | 11 | {# The button element is the entire clickable area for the accordion, and includes the icon within a pseudo element. #} 12 | 15 | {# Double wrappers used to control theme and inner padding of content container. #} 16 | 23 |
24 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/accordion/accordion.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * Behaviors for the Filter Accordion. 4 | */ 5 | 6 | !((Drupal, $) => { 7 | 'use strict'; 8 | 9 | /** 10 | * Setup and attach the Filter Accordion behaviors. 11 | * 12 | * @type {Drupal~behavior} 13 | */ 14 | Drupal.behaviors.accordion = { 15 | attach: function (context) { 16 | const self = this; 17 | const $accordions = $('.accordion', context); 18 | 19 | $accordions.each(function () { 20 | const $accordion = $(this); 21 | 22 | // Attach click handler for accordion. 23 | const $toggle = $accordion.find('.accordion__toggle'); 24 | $toggle.on('click', function () { 25 | self.toggleClickEvent($accordion, $(this)); 26 | }); 27 | }); 28 | }, 29 | 30 | toggleClickEvent: function ($accordion, $toggle) { 31 | // Identify the matching element. 32 | const $content = $accordion.find('#' + $toggle.attr('aria-controls')); 33 | 34 | if (!$accordion.hasClass('open')) { 35 | // Accordion does not have `.open`, so we are opening the accordion. 36 | $accordion.addClass('open'); 37 | // Toggle the `aria-expanded`. 38 | $toggle.attr('aria-expanded', 'true'); 39 | // Toggle the `aria-hidden` attribute on the content. 40 | $content.attr('aria-hidden', 'false'); 41 | } 42 | else { 43 | // Same as the if, but in reverse. 44 | $accordion.removeClass('open'); 45 | $toggle.attr('aria-expanded', 'false'); 46 | $content.attr('aria-hidden', 'true'); 47 | } 48 | } 49 | }; 50 | })(Drupal, jQuery); 51 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/accordion/accordion.json: -------------------------------------------------------------------------------- 1 | { 2 | "items": [ 3 | { 4 | "modifier": "", 5 | "heading": "Curabitur aliquet quam id dui posuere blandit.", 6 | "content": "

Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Proin eget tortor risus. Donec rutrum congue leo eget malesuada. Donec sollicitudin molestie malesuada. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus.

", 7 | "instance": "1" 8 | }, 9 | { 10 | "heading": "Curabitur aliquet quam", 11 | "content": "

Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Proin eget tortor risus. Donec rutrum congue leo eget malesuada. Donec sollicitudin molestie malesuada. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus.

", 12 | "instance": "2" 13 | }, 14 | { 15 | "heading": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Arcu cursus euismod quis viverra nibh cras pulvinar?", 16 | "content": "

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Est sit amet facilisis magna etiam tempor orci. Auctor eu augue ut lectus arcu bibendum at varius. Risus ultricies tristique nulla aliquet enim tortor at auctor.

", 17 | "instance": "3" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/accordion/accordion.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Accordion Container 3 | --- 4 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/accordion/accordion.scss: -------------------------------------------------------------------------------- 1 | // Import site utilities. 2 | @import '../../global/utils/init'; 3 | 4 | .accordion { 5 | border-bottom: 1px solid $color-gray-mid; 6 | border-left: 1px solid $color-gray-mid; 7 | border-right: 1px solid $color-gray-mid; 8 | border-top: 0; 9 | transition: background-color $transition; 10 | 11 | &:first-child { 12 | border-top: 1px solid $color-gray-mid; 13 | } 14 | } 15 | 16 | .accordion__toggle { 17 | background: $color-gray-xlt; 18 | border: 0; 19 | cursor: pointer; 20 | outline: inherit; 21 | padding: 16px 48px 16px 24px; 22 | position: relative; 23 | text-align: left; 24 | width: 100%; 25 | 26 | &::after { 27 | background-image: url('../images/accordion__arrow.svg'); 28 | background-position: center; 29 | background-repeat: no-repeat; 30 | background-size: contain; 31 | content: ''; 32 | height: 24px; 33 | position: absolute; 34 | right: 24px; 35 | top: 50%; 36 | transform: translateY(-50%); 37 | transition: transform $transition; 38 | width: 24px; 39 | will-change: transform; 40 | 41 | .open & { 42 | transform: translateY(-50%) rotate(-180deg); 43 | } 44 | } 45 | } 46 | 47 | .accordion__heading { 48 | display: block; 49 | font-size: 1rem; 50 | font-weight: bold; 51 | line-height: 1.625; 52 | } 53 | 54 | .accordion__content-wrapper { 55 | max-height: 0; 56 | line-height: 1.625; 57 | overflow: hidden; 58 | transition: max-height $transition; 59 | 60 | .open & { 61 | max-height: 500px; 62 | } 63 | } 64 | 65 | .accordion__content { 66 | padding: 16px 24px; 67 | } 68 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/accordion/accordion.twig: -------------------------------------------------------------------------------- 1 | {{ attach_library('<%= themeNameMachine %>/accordion') }} 2 | 3 |
4 | {# 5 | Optional: Pass in Drupal specific functionality. 6 | Note that this is mostly relevant to nodes and blocks. 7 | #} 8 | {# 9 | {{ title_prefix }} 10 | {{ title_suffix }} 11 | #} 12 | 13 | {% block accordion_items %} 14 | {% for accordion_item in items %} 15 | {% 16 | include '@<%= themeNameMachine %>/accordion/_accordion-item.twig' with { 17 | "modifier": accordion_item.modifier, 18 | "heading": accordion_item.heading, 19 | "content": accordion_item.content, 20 | "instance": accordion_item.instance 21 | } only 22 | %} 23 | {% endfor %} 24 | {% endblock %} 25 |
26 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/accordion/images/accordion__arrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/button/button.json: -------------------------------------------------------------------------------- 1 | { 2 | "modifier": "", 3 | "text": "Button", 4 | "url": "" 5 | } 6 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/button/button.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Button 3 | --- 4 | This component returns a button unless a link is passed, then an `` is returned. -------------------------------------------------------------------------------- /generators/starter-kit/templates/button/button.scss: -------------------------------------------------------------------------------- 1 | // Import site utilities. 2 | @import '../../global/utils/init'; 3 | 4 | .button { 5 | background-color: $color-black; 6 | border: 0; 7 | cursor: pointer; 8 | color: $color-white; 9 | display: inline-block; 10 | font-weight: bold; 11 | padding: 10px 30px; 12 | text-align: center; 13 | transition: background-color $transition, color $transition; 14 | 15 | &:hover, 16 | &:active { 17 | background-color: darken($color-black, 15); 18 | color: $color-white; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/button/button.twig: -------------------------------------------------------------------------------- 1 | {{ attach_library('<%= themeNameMachine %>/button') }} 2 | 3 | {# If a url is passed, we print a , 4 | otherwise we print a 13 | {% endif %} 14 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/button/button~disabled.json: -------------------------------------------------------------------------------- 1 | { 2 | "text": "Disabled Button", 3 | "modifier": "is-disabled" 4 | } 5 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/button/button~primary.json: -------------------------------------------------------------------------------- 1 | { 2 | "text": "Primary Button", 3 | "modifier": "button--primary", 4 | "url": "#" 5 | } 6 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/card-list/card-list.json: -------------------------------------------------------------------------------- 1 | { 2 | "section_title": "Optional Section Title", 3 | "heading_level": "h1", 4 | "items": [ 5 | { 6 | "video": "", 7 | "image": "test image", 8 | "caption": "Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vestibulum id ligula porta felis euismod semper.", 9 | "eyebrow": "Optional Short Title", 10 | "title": "Phasellus auctor, turpis", 11 | "heading_level": "h2", 12 | "subhead": "Optional Subhead Lorem Ipsum Dolor Sit Amet", 13 | "subhead_level": "h3", 14 | "text": "This copy is optional, if nothing is entered nothing will display. Facit nulla in vulputate vulputate aliquam. Commodo esse habent tation nam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin sed orci lacus.", 15 | "url": "#", 16 | "link_text":"Optional Link" 17 | }, 18 | { 19 | "video": "", 20 | "image": "test image", 21 | "caption": "Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vestibulum id ligula porta felis euismod semper.", 22 | "eyebrow": "Optional Short Title", 23 | "title": "Phasellus auctor, turpis", 24 | "heading_level": "h2", 25 | "subhead": "Optional Subhead Lorem Ipsum Dolor Sit Amet", 26 | "subhead_level": "h3", 27 | "text": "This copy is optional, if nothing is entered nothing will display. Facit nulla in vulputate vulputate aliquam. Commodo esse habent tation nam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin sed orci lacus.", 28 | "url": "#", 29 | "link_text":"Optional Link" 30 | }, 31 | { 32 | "video": "", 33 | "image": "test image", 34 | "caption": "Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vestibulum id ligula porta felis euismod semper.", 35 | "eyebrow": "Optional Short Title", 36 | "title": "Phasellus auctor, turpis", 37 | "heading_level": "h2", 38 | "subhead": "Optional Subhead Lorem Ipsum Dolor Sit Amet", 39 | "subhead_level": "h3", 40 | "text": "This copy is optional, if nothing is entered nothing will display. Facit nulla in vulputate vulputate aliquam. Commodo esse habent tation nam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin sed orci lacus.", 41 | "url": "#", 42 | "link_text":"Optional Link" 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/card-list/card-list.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Card List" 3 | --- -------------------------------------------------------------------------------- /generators/starter-kit/templates/card-list/card-list.scss: -------------------------------------------------------------------------------- 1 | // Import site utilities. 2 | @import '../../global/utils/init'; 3 | 4 | .card-list__title { 5 | margin-bottom: 32px; 6 | } 7 | 8 | .card-list__items { 9 | list-style-type: none; 10 | 11 | @include breakpoint($bp-sm) { 12 | display: flex; 13 | margin-left: -16px; 14 | margin-right: -16px; 15 | } 16 | } 17 | 18 | .card-list__item { 19 | 20 | @include breakpoint($bp-sm) { 21 | padding-left: 16px; 22 | padding-right: 16px; 23 | } 24 | 25 | &:not(:first-child) { 26 | margin-top: 48px; 27 | 28 | @include breakpoint($bp-sm) { 29 | margin-top: 0; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/card-list/card-list.twig: -------------------------------------------------------------------------------- 1 | {{ attach_library('<%= themeNameMachine %>/card-list') }} 2 | 3 |
4 | {# 5 | Optional: Pass in Drupal specific functionality. 6 | Note that this is mostly relevant to nodes and blocks. 7 | #} 8 | {# 9 | {{ title_prefix }} 10 | {{ title_suffix }} 11 | #} 12 | 13 | {% if section_title %} 14 | {% 15 | include '@<%= themeNameMachine %>/heading/heading.twig' with { 16 | "title": section_title, 17 | "heading_level": heading_level, 18 | "modifier": "card-list__title" 19 | } only 20 | %} 21 | {% endif %} 22 | 23 |
    24 | {% block card_items %} 25 | {# 26 | This for loop is only used to display the card list 27 | in Pattern Lab. Drupal will override this twig block. 28 | See paragraph--card-list.html.twig for more info. 29 | #} 30 | {% for item in items %} 31 |
  • 32 | {% 33 | include '@<%= themeNameMachine %>/card/card.twig' with { 34 | "image": item.image, 35 | "caption": item.caption, 36 | "eyebrow": item.eyebrow, 37 | "title": item.title, 38 | "heading_level": item.heading_level, 39 | "subhead": item.subhead, 40 | "subhead_level": item.subhead_level, 41 | "text": item.text, 42 | "url": item.url, 43 | "link_text": item.link_text, 44 | "modifier": "card-list__item" 45 | } only 46 | %} 47 |
  • 48 | {% endfor %} 49 | {% endblock %} 50 |
51 |
52 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/card-list/templates/paragraph--card--card-list.html.twig: -------------------------------------------------------------------------------- 1 | {# 2 | /** 3 | * @file 4 | * Theme implementation to display a Card paragraph display. 5 | * 6 | * @see template_preprocess_paragraph() 7 | * 8 | * @ingroup themeable 9 | */ 10 | #} 11 | {% 12 | set classes = [ 13 | 'paragraph', 14 | 'paragraph--type--' ~ paragraph.bundle|clean_class, 15 | view_mode ? 'paragraph--view-mode--' ~ view_mode|clean_class, 16 | not paragraph.isPublished() ? 'paragraph--unpublished', 17 | 'card-list__item' 18 | ] 19 | %} 20 | {# 21 | Requires: https://www.drupal.org/project/twig_field_value. 22 | The field_raw and field_target_entity filters do not provide any cache information. 23 | Without additional measures content printed with these filters will not be update 24 | when changed in the backend. You can workaround this by rendering the field in 25 | your template but not display the result. 26 | #} 27 | {% set catch_cache = content.field_link|render %} 28 | 29 | {% 30 | include "@<%= themeNameMachine %>/card/card.twig" with { 31 | "image": content.field_media, 32 | "title": content.field_title, 33 | "heading_level": "h2", 34 | "subhead": content.field_subhead.0 ? content.field_subhead, 35 | "subhead_level": "p", 36 | "text": content.field_summary.0 ? content.field_summary, 37 | "url": content.field_link|field_raw('uri'), 38 | "link_text": content.field_link|field_raw('title'), 39 | "modifier": attributes.addClass(classes).class, 40 | "extra_attributes": attributes|without(class) 41 | } only 42 | %} 43 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/card-list/templates/paragraph--card-list.html.twig: -------------------------------------------------------------------------------- 1 | {# 2 | /** 3 | * @file 4 | * Theme implementation to display a Card List paragraph. 5 | * 6 | * @see template_preprocess_paragraph() 7 | * 8 | * @ingroup themeable 9 | */ 10 | #} 11 | {% 12 | set classes = [ 13 | 'paragraph', 14 | 'paragraph--type--' ~ paragraph.bundle|clean_class, 15 | view_mode ? 'paragraph--view-mode--' ~ view_mode|clean_class, 16 | not paragraph.isPublished() ? 'paragraph--unpublished' 17 | ] 18 | %} 19 | {% 20 | embed "@<%= themeNameMachine %>/card-list/card-list.twig" with { 21 | "section_title": content.field_title|render, 22 | "heading_level": "h1", 23 | "cards": content.field_card, 24 | "modifier": attributes.addClass(classes).class, 25 | "extra_attributes": attributes|without(class) 26 | } only 27 | %} 28 | {% block card_items %} 29 | {{ cards }} 30 | {% endblock card_items %} 31 | {% endembed %} 32 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/card/card.json: -------------------------------------------------------------------------------- 1 | { 2 | "video": "", 3 | "image": "test image", 4 | "caption": "Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vestibulum id ligula porta felis euismod semper.", 5 | "eyebrow": "Optional eyebrow", 6 | "title": "Phasellus auctor, turpis", 7 | "heading_level": "h2", 8 | "subhead": "Optional Subhead Lorem Ipsum Dolor Sit Amet", 9 | "subhead_level": "h3", 10 | "text": "This copy is optional, if nothing is entered nothing will display. Facit nulla in vulputate vulputate aliquam. Commodo esse habent tation nam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin sed orci lacus.", 11 | "url": "#", 12 | "link_text": "Optional Link" 13 | } 14 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/card/card.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Card 3 | --- -------------------------------------------------------------------------------- /generators/starter-kit/templates/card/card.scss: -------------------------------------------------------------------------------- 1 | // Import site utilities. 2 | @import '../../global/utils/init'; 3 | 4 | .card { 5 | // Add a max-width so this doesn't look super wierd in 6 | // in the style guide. 7 | max-width: 350px; 8 | } 9 | 10 | .card__media-caption { 11 | display: block; 12 | } 13 | 14 | .card__content { 15 | margin-top: 32px; 16 | } 17 | 18 | .card__title { 19 | font-size: 1.5rem; 20 | font-weight: normal; 21 | margin-top: 16px; 22 | 23 | @include breakpoint($bp-sm) { 24 | font-size: 2rem; 25 | } 26 | } 27 | 28 | .card__subhead { 29 | font-size: 1.25rem; 30 | font-weight: normal; 31 | 32 | @include breakpoint($bp-sm) { 33 | font-size: 1.5rem; 34 | } 35 | } 36 | 37 | .card__link { 38 | color: inherit; 39 | display: inline-block; 40 | margin-top: 24px; 41 | } 42 | 43 | // Styles for Wide variation of Card 44 | .card--wide { 45 | display: flex; 46 | flex-direction: column; 47 | 48 | @include breakpoint($bp-xsm) { 49 | flex-direction: row; 50 | } 51 | 52 | .card__media { 53 | flex: 1; 54 | } 55 | 56 | .card__content { 57 | flex: 1; 58 | 59 | @include breakpoint($bp-xsm) { 60 | margin: 0 0 0 24px; 61 | } 62 | } 63 | 64 | .card__eyebrow { 65 | margin-top: 0; 66 | } 67 | } 68 | 69 | .card--media-right { 70 | 71 | @include breakpoint($bp-xsm) { 72 | flex-direction: row-reverse; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/card/card.twig: -------------------------------------------------------------------------------- 1 | {{ attach_library('<%= themeNameMachine %>/card') }} 2 | 3 |
4 | {# 5 | Optional: Pass in Drupal specific functionality. 6 | Note that this is mostly relevant to nodes and blocks. 7 | #} 8 | {# 9 | {{ title_prefix }} 10 | {{ title_suffix }} 11 | #} 12 | 13 |
14 | {% if image %} 15 | {{ image }} 16 | {% elseif video %} 17 | {{ video }} 18 | {% endif %} 19 | 20 | {{ caption }} 21 | 22 |
23 | 24 |
25 | {% if eyebrow %} 26 | {% 27 | include '@<%= themeNameMachine %>/eyebrow/eyebrow.twig' with { 28 | "text": eyebrow, 29 | "modifier": "card__eyebrow" 30 | } only 31 | %} 32 | {% endif %} 33 | 34 | {% if title %} 35 | {% 36 | include '@<%= themeNameMachine %>/heading/heading.twig' with { 37 | "title": title, 38 | "heading_level": heading_level, 39 | "modifier": "card__title", 40 | "url": url 41 | } only 42 | %} 43 | {% endif %} 44 | 45 | {% if subhead %} 46 | {% 47 | include '@<%= themeNameMachine %>/heading/heading.twig' with { 48 | "title": subhead, 49 | "heading_level": subhead_level, 50 | "modifier": "card__subhead" 51 | } only 52 | %} 53 | {% endif %} 54 | 55 | {% if text %} 56 |
57 | {{ text }} 58 |
59 | {% endif %} 60 | 61 | {% if link_text %} 62 | {{ link_text }} 63 | {% endif %} 64 |
65 | 66 |
67 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/card/templates/paragraph--card.html.twig: -------------------------------------------------------------------------------- 1 | {# 2 | /** 3 | * @file 4 | * Theme implementation to display a single Card paragraph. 5 | * 6 | * @see template_preprocess_paragraph() 7 | * 8 | * @ingroup themeable 9 | */ 10 | #} 11 | {% 12 | set classes = [ 13 | 'paragraph', 14 | 'paragraph--type--' ~ paragraph.bundle|clean_class, 15 | view_mode ? 'paragraph--view-mode--' ~ view_mode|clean_class, 16 | not paragraph.isPublished() ? 'paragraph--unpublished' 17 | ] 18 | %} 19 | {# 20 | The field_raw and field_target_entity filters do not provide any cache information. 21 | Without additional measures content printed with these filters will not be update 22 | when changed in the backend. You can workaround this by rendering the field in 23 | your template but not display the result. 24 | #} 25 | {% set catch_cache = content.field_link|render %} 26 | 27 | {% 28 | include "@<%= themeNameMachine %>/card/card.twig" with { 29 | "image": content.field_media, 30 | "title": content.field_title, 31 | "heading_level": "h2", 32 | "subhead": content.field_subhead.0 ? content.field_subhead, 33 | "subhead_level": "p", 34 | "text": content.field_summary.0 ? content.field_summary, 35 | "url": content.field_link|field_raw('uri'), 36 | "link_text": content.field_link|field_raw('title'), 37 | "modifier": attributes.addClass(classes).class, 38 | "extra_attributes": attributes|without(class) 39 | } only 40 | %} 41 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/carousel/carousel-item.md: -------------------------------------------------------------------------------- 1 | --- 2 | hidden: true 3 | --- 4 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/carousel/carousel-item.twig: -------------------------------------------------------------------------------- 1 | 41 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/carousel/carousel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * Behaviors for the Carousel. 4 | */ 5 | 6 | !((Drupal, $) => { 7 | 'use strict'; 8 | 9 | /** 10 | * Setup and attach the Carousel behaviors. 11 | * 12 | * @type {Drupal~behavior} 13 | */ 14 | Drupal.behaviors.carousel = { 15 | attach: function() { 16 | $('.carousel__slick').not('.slick-initialized').slick({ 17 | dots: true, 18 | }); 19 | }, 20 | }; 21 | })(Drupal, jQuery); 22 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/carousel/carousel.json: -------------------------------------------------------------------------------- 1 | { 2 | "modifier": "", 3 | "list": [ 4 | { 5 | "image": "test image", 6 | "title": "Title Lorem Ipsum Dolor", 7 | "button_text": "Button Text", 8 | "url": "#" 9 | }, 10 | { 11 | "image": "test image", 12 | "title": "Title Lorem Ipsum Dolor", 13 | "button_text": "Button Text", 14 | "url": "#" 15 | }, 16 | { 17 | "image": "test image", 18 | "title": "Title Lorem Ipsum Dolor", 19 | "button_text": "Button Text", 20 | "url": "#" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/carousel/carousel.scss: -------------------------------------------------------------------------------- 1 | // Import site utilities. 2 | @import '../../global/utils/init'; 3 | 4 | .carousel-item { 5 | position: relative; 6 | } 7 | 8 | .carousel-item__content { 9 | background: $color-gray-dk; 10 | color: $color-white; 11 | min-width: 300px; 12 | padding: 16px 8px; 13 | text-align: center; 14 | 15 | @include breakpoint($bp-xsm) { 16 | padding: 16px; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/carousel/carousel.twig: -------------------------------------------------------------------------------- 1 | {{ attach_library('<%= themeNameMachine %>/carousel') }} 2 | 3 | 28 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/eyebrow/eyebrow.json: -------------------------------------------------------------------------------- 1 | { 2 | "text": "Some kind of label", 3 | "modifier": "" 4 | } 5 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/eyebrow/eyebrow.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Eyebrow 3 | --- -------------------------------------------------------------------------------- /generators/starter-kit/templates/eyebrow/eyebrow.scss: -------------------------------------------------------------------------------- 1 | // Import site utilities. 2 | @import '../../global/utils/init'; 3 | 4 | .eyebrow__text { 5 | font-size: 0.75rem; 6 | } 7 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/eyebrow/eyebrow.twig: -------------------------------------------------------------------------------- 1 | {{ attach_library('<%= themeNameMachine %>/eyebrow') }} 2 | 3 |
4 |

5 | {{ text }} 6 |

7 |
8 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/heading/heading.json: -------------------------------------------------------------------------------- 1 | { 2 | "heading_level": "h1", 3 | "modifier": "", 4 | "url": "", 5 | "title": "This is a regular heading!" 6 | } 7 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/heading/heading.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Heading 3 | --- -------------------------------------------------------------------------------- /generators/starter-kit/templates/heading/heading.scss: -------------------------------------------------------------------------------- 1 | @import '../../global/utils/init'; 2 | 3 | .heading__link { 4 | color: inherit; 5 | text-decoration: none; 6 | 7 | &:hover { 8 | text-decoration: underline; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/heading/heading.twig: -------------------------------------------------------------------------------- 1 | {{ attach_library('<%= themeNameMachine %>/heading') }} 2 | 3 | {# Defaults to h2 (heading level 2) if no heading level is supplied. #} 4 | <{{ heading_level|default('h2') }} class="heading {{ modifier }}" {{ extra_attributes }}> 5 | 6 | {% if url %} 7 | {{ title }} 8 | {% else %} 9 | {{ title }} 10 | {% endif %} 11 | 12 | 13 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/hero/hero.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "test image", 3 | "title": "Title Lorem Ipsum Dolor", 4 | "heading_level": "h2", 5 | "body": "Some sites need a tagline mauris ac dui sit amet sem facilisis finibus vitae et mi.", 6 | "button_text": "Learn more", 7 | "url": "#" 8 | } 9 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/hero/hero.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hero 3 | --- -------------------------------------------------------------------------------- /generators/starter-kit/templates/hero/hero.scss: -------------------------------------------------------------------------------- 1 | // Import site utilities. 2 | @import '../../global/utils/init'; 3 | 4 | .hero { 5 | position: relative; 6 | } 7 | 8 | .hero__content { 9 | background-color: $color-gray-lt; 10 | padding: 16px; 11 | text-align: center; 12 | 13 | @include breakpoint($bp-sm) { 14 | padding-bottom: 32px; 15 | padding-top: 32px; 16 | } 17 | } 18 | 19 | .hero__heading { 20 | margin-bottom: 8px; 21 | 22 | @include breakpoint($bp-xsm) { 23 | margin-bottom: 16px; 24 | } 25 | } 26 | 27 | .hero__body { 28 | font-size: 1rem; 29 | line-height: 1.625; 30 | margin-bottom: 8px; 31 | 32 | @include breakpoint($bp-xsm) { 33 | font-size: 1.5rem; 34 | margin-bottom: 48px; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/hero/hero.twig: -------------------------------------------------------------------------------- 1 | {{ attach_library('<%= themeNameMachine %>/hero') }} 2 | 3 |
4 | {# 5 | Optional: Pass in Drupal specific functionality. 6 | Note that this is mostly relevant to nodes and blocks. 7 | #} 8 | {# 9 | {{ title_prefix }} 10 | {{ title_suffix }} 11 | #} 12 | 13 | {# Media could be an image or video. #} 14 | {% if image %} 15 |
16 | {% 17 | include '@<%= themeNameMachine %>/media-item/media-item.twig' with { 18 | "image": image 19 | } only 20 | %} 21 |
22 | {% endif %} 23 | 24 |
25 | {% if title %} 26 |
27 | {% 28 | include '@<%= themeNameMachine %>/heading/heading.twig' with { 29 | "title": title, 30 | "heading_level": heading_level, 31 | "modifier": "hero__heading" 32 | } only 33 | %} 34 |
35 | {% endif %} 36 | 37 | {% if body %} 38 |
39 | {{ body }} 40 |
41 | {% endif %} 42 | 43 | {% if button_text %} 44 | {% 45 | include '@<%= themeNameMachine %>/button/button.twig' with { 46 | "text": button_text, 47 | "url": url 48 | } only 49 | %} 50 | {% endif %} 51 |
52 |
53 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/hero/hero~video.json: -------------------------------------------------------------------------------- 1 | { 2 | "video": "", 3 | "title": "Title Lorem Ipsum Dolor", 4 | "heading_level": "h2", 5 | "body": "Some sites need a tagline mauris ac dui sit amet sem facilisis finibus vitae et mi.", 6 | "button_text": "Learn more", 7 | "url": "#" 8 | } 9 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/media-item/media-item.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "test image", 3 | "caption": "Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vestibulum id ligula porta felis euismod semper." 4 | } 5 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/media-item/media-item.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Media Item" 3 | --- -------------------------------------------------------------------------------- /generators/starter-kit/templates/media-item/media-item.scss: -------------------------------------------------------------------------------- 1 | @import '../../global/utils/init'; 2 | 3 | .media-item__image { 4 | 5 | img { 6 | display: block; 7 | } 8 | } 9 | 10 | .media-item__caption { 11 | font-size: 0.875rem; 12 | margin-top: 4px; 13 | max-width: 100%; 14 | word-wrap: break-word; 15 | } 16 | 17 | // Responsive styles for YouTube video embed. 18 | .media-item__responsive-video { 19 | height: 0; 20 | overflow: hidden; 21 | padding-bottom: 56.25%; 22 | position: relative; 23 | 24 | iframe { 25 | height: 100%; 26 | left: 0; 27 | position: absolute; 28 | top: 0; 29 | width: 100%; 30 | } 31 | 32 | video { 33 | width: 100%; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/media-item/media-item.twig: -------------------------------------------------------------------------------- 1 | {{ attach_library('<%= themeNameMachine %>/media-item') }} 2 | 3 |
4 | {# 5 | Optional: Pass in Drupal specific functionality. 6 | Note that this is mostly relevant to nodes and blocks. 7 | #} 8 | {# 9 | {{ title_prefix }} 10 | {{ title_suffix }} 11 | #} 12 | 13 | {% if video %} 14 |
15 | {{ video }} 16 |
17 | {% else %} 18 |
19 | {{ image }} 20 |
21 | {% endif %} 22 | {# Captions are optional. #} 23 | {% if caption %} 24 |
25 | {{ caption }} 26 |
27 | {% endif %} 28 |
29 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/media-item/media-item~video.json: -------------------------------------------------------------------------------- 1 | { 2 | "video": "", 3 | "caption": "Nulla vitae elit libero, a pharetra augue. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vestibulum id ligula porta felis euismod semper." 4 | } 5 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/message/images/check.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/message/images/error.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/message/images/warning.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/message/message.json: -------------------------------------------------------------------------------- 1 | { 2 | "htmlClass": "no-padding", 3 | "message_list": { 4 | "status": [ 5 | "This is a status message" 6 | ], 7 | "warning": [ 8 | "This is a warning message" 9 | ], 10 | "error": [ 11 | "This is a error message" 12 | ] 13 | }, 14 | "status_headings": { 15 | "status": "Status message", 16 | "error": "Error message", 17 | "warning": "Warning message", 18 | "moderation": "Moderation message" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/message/message.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Message 3 | --- -------------------------------------------------------------------------------- /generators/starter-kit/templates/message/message.scss: -------------------------------------------------------------------------------- 1 | // Messages 2 | // 3 | // The messages component is mostly a duplicate of the Classy theme 4 | // styled system messages. There are slight modifiers added to give 5 | // more flexibility. 6 | 7 | // Import site utilities. 8 | @import '../../global/utils/init'; 9 | 10 | .message { 11 | background: no-repeat 10px 17px; 12 | border: 3px solid; 13 | padding: 15px 20px 15px 35px; 14 | word-wrap: break-word; 15 | overflow-wrap: break-word; 16 | } 17 | 18 | .message + .message { 19 | margin-top: 24px; 20 | } 21 | 22 | .message__heading { 23 | @include element-invisible; 24 | } 25 | 26 | .messages__list { 27 | list-style: none; 28 | padding: 0; 29 | margin: 0; 30 | } 31 | 32 | .message__item + .message__item { 33 | margin-top: 12px; 34 | } 35 | 36 | // See .color-success in Seven's colors.css. 37 | .message--status { 38 | color: #325e1c; 39 | background-color: #f3faef; 40 | border-color: #77b259; 41 | background-image: url('../images/check.svg'); 42 | } 43 | 44 | // See .color-warning in Seven's colors.css. 45 | .message--warning { 46 | background-color: #fdf8ed; 47 | background-image: url('../images/warning.svg'); 48 | border-color: #e09600; 49 | color: #734c00; 50 | } 51 | 52 | // See .color-error in Seven's colors.css. 53 | .message--error { 54 | background-color: #ffddde; 55 | color: #a23433; 56 | background-image: url('../images/error.svg'); 57 | border-color: #a23433; 58 | } 59 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/message/message.twig: -------------------------------------------------------------------------------- 1 | {{ attach_library('<%= themeNameMachine %>/message') }} 2 | 3 | {% for type, messages in message_list %} 4 | 24 | {% endfor %} 25 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/message/templates/status-messages.html.twig: -------------------------------------------------------------------------------- 1 | {# 2 | /** 3 | * @file 4 | * Theme override for status messages. 5 | * 6 | * Displays status, error, and warning messages, grouped by type. 7 | * 8 | * An invisible heading identifies the messages for assistive technology. 9 | * Sighted users see a colored box. See http://www.w3.org/TR/WCAG-TECHS/H69.html 10 | * for info. 11 | * 12 | * Add an ARIA label to the contentinfo area so that assistive technology 13 | * user agents will better describe this landmark. 14 | * 15 | * Available variables: 16 | * - message_list: List of messages to be displayed, grouped by type. 17 | * - status_headings: List of all status types. 18 | * - display: (optional) May have a value of 'status' or 'error' when only 19 | * displaying messages of that specific type. 20 | * - attributes: HTML attributes for the element, including: 21 | * - class: HTML classes. 22 | */ 23 | #} 24 | {% block messages %} 25 | {% include '@<%= themeNameMachine %>/message/message.twig' %} 26 | {% endblock messages %} 27 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/tabs/tabs.json: -------------------------------------------------------------------------------- 1 | { 2 | "htmlClass": "no-padding", 3 | "primary": true, 4 | "tabs": [ 5 | { 6 | "text": "View", 7 | "link": "#view", 8 | "is_active": true 9 | }, 10 | { 11 | "text": "Edit", 12 | "link": "#edit", 13 | "is_active": false 14 | }, 15 | { 16 | "text": "Delete", 17 | "link": "#delete", 18 | "is_active": false 19 | }, 20 | { 21 | "text": "Revisions", 22 | "link": "#revisions", 23 | "is_active": false 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/tabs/tabs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tabs 3 | --- -------------------------------------------------------------------------------- /generators/starter-kit/templates/tabs/tabs.scss: -------------------------------------------------------------------------------- 1 | // Styling for Drupal tabs. 2 | 3 | // Import site utilities. 4 | @import '../../global/utils/init'; 5 | 6 | .tabs { 7 | list-style: none; 8 | margin: 0; 9 | padding: 10px 0; 10 | position: relative; 11 | } 12 | 13 | .tabs.primary { 14 | padding: 0; 15 | 16 | &::after { 17 | 18 | @include breakpoint($bp-sm) { 19 | background-color: $color-gray-mid; 20 | bottom: 0; 21 | content: ''; 22 | display: block; 23 | height: 1px; 24 | left: 0; 25 | position: absolute; 26 | width: 100%; 27 | z-index: $zi-below-ground; 28 | } 29 | } 30 | 31 | li { 32 | display: block; 33 | font-size: 0.875rem; 34 | font-weight: $font-weight-bold; 35 | margin: 0 0 8px; 36 | padding: 0; 37 | 38 | @include breakpoint($bp-sm) { 39 | display: inline-block; 40 | margin: 0 8px 0 0; 41 | } 42 | } 43 | 44 | a { 45 | background-color: $color-gray-xlt; 46 | border: 1px solid $color-gray-mid; 47 | color: $color-gray-mid; 48 | display: block; 49 | padding: 8px 24px; 50 | text-decoration: none; 51 | 52 | @include breakpoint($bp-sm) { 53 | border-bottom: 0; 54 | } 55 | } 56 | 57 | a:focus, 58 | a:hover { 59 | background-color: $color-white; 60 | border: 1px solid $color-gray-mid; 61 | border-bottom: 0; 62 | color: $color-black; 63 | } 64 | 65 | .is-active a { 66 | background-color: $color-white; 67 | border-bottom: 0; 68 | color: $color-black; 69 | } 70 | } 71 | 72 | .tabs.secondary { 73 | margin-top: 10px; 74 | 75 | li { 76 | display: inline-block; 77 | font-size: 0.875rem; 78 | margin: 8px 16px 4px 0; 79 | } 80 | 81 | a { 82 | background-color: transparent; 83 | border-bottom: 2px solid $color-gray-lt; 84 | color: $color-gray-mid; 85 | display: block; 86 | padding: 8px 12px 4px; 87 | } 88 | 89 | .is-active a, 90 | a:focus, 91 | a:hover { 92 | background-color: transparent; 93 | border-color: $color-black; 94 | color: $color-black; 95 | text-decoration: none; 96 | } 97 | } 98 | 99 | .tabs__local-actions { 100 | padding: 16px 0; 101 | } 102 | 103 | .tabs__local-actions-links { 104 | margin: 0; 105 | } 106 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/tabs/tabs.twig: -------------------------------------------------------------------------------- 1 | {{ attach_library('<%= themeNameMachine %>/tabs') }} 2 | 3 | {% if primary %} 4 |

{{ 'Primary tabs'|t }}

5 |
    6 | {% block primary %} 7 | {% for tab in tabs %} 8 |
  • 9 | {{ tab.text }} 10 |
  • 11 | {% endfor %} 12 | {% endblock primary %} 13 |
14 | {% endif %} 15 | {% if secondary %} 16 |

{{ 'Secondary tabs'|t }}

17 |
    18 | {% block secondary %} 19 | {% for tab in tabs %} 20 |
  • 21 | {{ tab.text }} 22 |
  • 23 | {% endfor %} 24 | {% endblock secondary %} 25 |
26 | {% endif %} 27 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/tabs/templates/menu-local-task.html.twig: -------------------------------------------------------------------------------- 1 | {# 2 | /** 3 | * @file 4 | * Theme override for a local task link. 5 | * 6 | * Available variables: 7 | * - attributes: HTML attributes for the wrapper element. 8 | * - is_active: Whether the task item is an active tab. 9 | * - link: A rendered link element. 10 | * 11 | * Note: This template renders the content for each task item in 12 | * menu-local-tasks.html.twig. 13 | * 14 | * @see template_preprocess_menu_local_task() 15 | */ 16 | #} 17 | {{ link }} 18 | -------------------------------------------------------------------------------- /generators/starter-kit/templates/tabs/templates/menu-local-tasks.html.twig: -------------------------------------------------------------------------------- 1 | {# 2 | /** 3 | * @file 4 | * Theme override to display primary and secondary local tasks. 5 | * 6 | * Available variables: 7 | * - primary: HTML list items representing primary tasks. 8 | * - secondary: HTML list items representing primary tasks. 9 | * 10 | * Each item in these variables (primary and secondary) can be individually 11 | * themed in menu-local-task.html.twig. 12 | */ 13 | #} 14 | {% embed "@<%= themeNameMachine %>/tabs/tabs.twig" with { 15 | primary: primary, 16 | secondary: secondary 17 | } only %} 18 | {% block primary %} 19 | {{ primary }} 20 | {% endblock %} 21 | {% block secondary %} 22 | {{ secondary }} 23 | {% endblock %} 24 | {% endembed %} 25 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var eslint = require('gulp-eslint'); 3 | var excludeGitignore = require('gulp-exclude-gitignore'); 4 | var mocha = require('gulp-mocha'); 5 | var isparta = require('isparta'); 6 | var istanbul = require('gulp-istanbul'); 7 | var plumber = require('gulp-plumber'); 8 | 9 | gulp.task('static', function () { 10 | return gulp.src('**/*.js') 11 | .pipe(excludeGitignore()) 12 | .pipe(eslint()) 13 | .pipe(eslint.format()) 14 | .pipe(eslint.failAfterError()); 15 | }); 16 | 17 | gulp.task('pre-test', function () { 18 | return gulp.src([ 19 | 'generators/**/*.js', 20 | '!generators/**/annotations/annotations.js' 21 | ]) 22 | .pipe(excludeGitignore()) 23 | .pipe(istanbul({ 24 | includeUntested: true, 25 | instrumenter: isparta.Instrumenter 26 | })) 27 | .pipe(istanbul.hookRequire()); 28 | }); 29 | 30 | gulp.task('test', gulp.series('pre-test', function (cb) { 31 | var mochaErr; 32 | 33 | gulp.src('test/**/*.js') 34 | .pipe(plumber()) 35 | .pipe(mocha({reporter: 'spec'})) 36 | .on('error', function (err) { 37 | mochaErr = err; 38 | }) 39 | .pipe(istanbul.writeReports()) 40 | .on('end', function () { 41 | cb(mochaErr); 42 | }); 43 | })); 44 | 45 | gulp.task('watch', function () { 46 | gulp.watch(['generators/**/*.js', 'test/**'], ['test']); 47 | }); 48 | 49 | gulp.task('default', gulp.series('static', 'test')); 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generator-mc-d8-theme", 3 | "version": "2.5.0", 4 | "description": "Yeoman generator for creating Drupal 8 component based themes.", 5 | "homepage": "https://github.com/mediacurrent/theme_generator_8", 6 | "author": { 7 | "name": "ZackHawkinsMC", 8 | "email": "zack.hawkins@mediacurrent.com", 9 | "url": "" 10 | }, 11 | "repository": "github:mediacurrent/theme_generator_8", 12 | "files": [ 13 | "generators" 14 | ], 15 | "main": "generators/index.js", 16 | "keywords": [ 17 | "yeoman-generator", 18 | "drupal" 19 | ], 20 | "dependencies": { 21 | "chalk": "^1.0.0", 22 | "js-yaml": "^3.13.1", 23 | "replace-in-file": "^5.0.2", 24 | "yeoman-generator": "^3.1.1", 25 | "yosay": "^1.0.0" 26 | }, 27 | "devDependencies": { 28 | "eslint": "^5.0.0", 29 | "eslint-plugin-ejs": "0.0.2", 30 | "gulp": "^4.0.2", 31 | "gulp-eslint": "^5.0.0", 32 | "gulp-exclude-gitignore": "^1.0.0", 33 | "gulp-istanbul": "^1.1.3", 34 | "gulp-line-ending-corrector": "^1.0.1", 35 | "gulp-mocha": "^7.0.2", 36 | "gulp-plumber": "^1.0.0", 37 | "handlebars": "^4.7.6", 38 | "isparta": "^4.1.1", 39 | "lodash": "^4.17.15", 40 | "mkdirp": "^0.5.1", 41 | "path": "^0.12.7", 42 | "yeoman-assert": "^2.0.0", 43 | "yeoman-test": "^1.9.1" 44 | }, 45 | "scripts": { 46 | "test": "gulp" 47 | }, 48 | "license": "MIT" 49 | } 50 | -------------------------------------------------------------------------------- /test/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var assert = require('yeoman-assert'); 4 | var helpers = require('yeoman-test'); 5 | 6 | describe('generator-mc-d-8-theme:app', function () { 7 | before(function () { 8 | return helpers.run(path.join(__dirname, '../generators/app')) 9 | .withPrompts({someAnswer: true}) 10 | .toPromise(); 11 | }); 12 | 13 | it('creates files', function () { 14 | assert.file([ 15 | 'dummyfile.txt' 16 | ]); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/component.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var assert = require('yeoman-assert'); 4 | var helpers = require('yeoman-test'); 5 | 6 | describe('generator-mc-d8-theme:component', function () { 7 | before(function () { 8 | return helpers.run(path.join(__dirname, '../generators/component')) 9 | .withPrompts({someAnswer: true}) 10 | .toPromise(); 11 | }); 12 | 13 | it('creates files', function () { 14 | assert.file([ 15 | 'dummyfile.txt' 16 | ]); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/starter-kit.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var path = require('path'); 3 | var assert = require('yeoman-assert'); 4 | var helpers = require('yeoman-test'); 5 | 6 | describe('generator-mc-d8-theme:starter-kit', function () { 7 | before(function () { 8 | return helpers.run(path.join(__dirname, '../generators/starter-kit')) 9 | .withPrompts({someAnswer: true}) 10 | .toPromise(); 11 | }); 12 | 13 | it('creates files', function () { 14 | assert.file([ 15 | 'dummyfile.txt' 16 | ]); 17 | }); 18 | }); 19 | --------------------------------------------------------------------------------