├── .babelrc ├── .editorconfig ├── .eslintrc.js ├── .github ├── CONTRIBUTING.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .nvmrc ├── .prettierrc ├── .stylelintrc ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── README_APP.md ├── ROADMAP.md ├── config └── main │ ├── app.yaml │ ├── config.yaml │ └── deployment.yaml ├── data ├── en │ ├── items │ │ ├── authoring.json │ │ ├── before.json │ │ ├── best_practices.json │ │ ├── management.json │ │ ├── security.json │ │ ├── team.json │ │ └── testing.json │ └── project │ │ ├── introductions.json │ │ └── translation.json └── jp │ └── items │ └── 利用方法.json ├── diagram-app.png ├── diagram-deployment.png ├── docker-compose.yml ├── gulpfile.babel.js ├── modernizr-config.json ├── package-lock.json ├── package.json ├── src ├── _headers ├── android-chrome-192x192.png ├── apple-touch-icon.png ├── browserconfig.xml ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── humans.txt ├── img │ └── icons │ │ └── 1x1.png ├── manifest.json ├── modernizr-custom.min.js ├── mstile-150x150.png ├── precache-config.js ├── robots.txt ├── safari-pinned-tab.svg ├── scripts │ ├── Utils.js │ ├── components │ │ ├── Analytics.js │ │ ├── Checkboxes.js │ │ ├── Dropdown.js │ │ ├── Filter.js │ │ ├── Init.js │ │ ├── Notation.js │ │ ├── ProgressBar.js │ │ ├── Report.js │ │ ├── Storage.js │ │ ├── Tools.js │ │ └── Ui.js │ └── main.js ├── service-worker.js ├── sitemap.xml ├── styles │ ├── base │ │ ├── _fonts.scss │ │ ├── _form.scss │ │ ├── _generic.scss │ │ ├── _headings.scss │ │ ├── _icons.scss │ │ ├── _links.scss │ │ ├── _media.scss │ │ ├── _print.scss │ │ └── _typography.scss │ ├── components │ │ ├── _c-button.scss │ │ ├── _c-checklist.scss │ │ ├── _c-dropdown.scss │ │ ├── _c-github.scss │ │ ├── _c-list.scss │ │ ├── _c-nav.scss │ │ ├── _c-notation.scss │ │ ├── _c-progress.scss │ │ ├── _c-tags.scss │ │ ├── _c-tools.scss │ │ └── _c-top-alert.scss │ ├── config │ │ ├── _v-colors.scss │ │ ├── _v-namespaces.scss │ │ ├── _v-typography.scss │ │ └── _variables.scss │ ├── layout │ │ ├── _page.scss │ │ ├── _s-aside.scss │ │ ├── _s-checklist.scss │ │ ├── _s-footer.scss │ │ ├── _s-header.scss │ │ └── _s-main.scss │ ├── main.scss │ └── utilities │ │ ├── _utilities.scss │ │ ├── functions │ │ └── _functions.scss │ │ └── mixins │ │ ├── _m-breakpoints.scss │ │ └── _mixins.scss └── views │ ├── base │ ├── after-scripts.pug │ ├── before-scripts.pug │ ├── footer.pug │ ├── head.pug │ ├── header.pug │ ├── layout.pug │ └── social.pug │ ├── components │ ├── c-checklist.pug │ ├── c-corner.pug │ ├── c-nav.pug │ ├── c-new-form.pug │ ├── c-notation.pug │ ├── c-progress.pug │ ├── c-search.pug │ ├── c-tag-filter.pug │ ├── c-tools.pug │ ├── c-top-alert.pug │ ├── checklist │ │ ├── checkbox.pug │ │ ├── class.pug │ │ ├── dropdown.pug │ │ ├── label.pug │ │ └── priority.pug │ ├── s-section-bottom.pug │ ├── s-section-top.pug │ └── svg │ │ ├── arrow.pug │ │ ├── bullet.pug │ │ ├── check.pug │ │ ├── checkbox.pug │ │ ├── checked.pug │ │ ├── code.pug │ │ ├── collapse.pug │ │ ├── expand.pug │ │ ├── eye.pug │ │ ├── print.pug │ │ └── reset.pug │ ├── index-en.pug │ └── mixins │ ├── m-checklist-section.pug │ ├── m-icon.pug │ └── mixins.pug ├── templates ├── app.yaml └── deployment.yaml ├── test ├── localstorage.test.js ├── notation.test.js └── reports.test.js ├── wallaby.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | env: { 7 | browser: true, 8 | es6: true, 9 | }, 10 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 11 | extends: [ 12 | 'airbnb-base/legacy', 13 | 'plugin:flowtype/recommended', 14 | 'prettier', 15 | 'prettier/flowtype' 16 | ], 17 | plugins: [ 18 | 'flowtype', 19 | 'prettier' 20 | ], 21 | // add your custom rules here 22 | 'rules': { 23 | "class-methods-use-this": 0, 24 | // allow debugger during development 25 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, 26 | 27 | 'prettier/prettier': ['error', { 28 | 'useTabs': false, 29 | 'printWidth': 80, 30 | 'tabWidth': 2, 31 | 'singleQuote': true, 32 | 'trailingComma': 'all', 33 | 'bracketSpacing': false 34 | }] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute 2 | 3 | ## Introduction 4 | 5 | First, thank you for considering contributing to Cloudformation checklist! It's people like you that make the open source community such a great community! 😊 6 | 7 | We welcome any type of contribution, not only code. You can help with 8 | - **QA**: file bug reports, the more details you can give the better (e.g. screenshots with the console open) 9 | - **Marketing**: writing blog posts, howto's, printing stickers, ... 10 | - **Community**: presenting the project at meetups, organizing a dedicated meetup for the local community, ... 11 | - **Code**: take a look at the [open issues](issues). Even if you can't write code, commenting on them, showing that you care about a given issue matters. It helps us triage them. 12 | 13 | ## Your First Contribution 14 | 15 | Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). 16 | 17 | ## Submitting code 18 | 19 | Any code change should be submitted as a pull request. The description should explain what the code does and give steps to execute it. The pull request should also contain tests. 20 | 21 | ## Code review process 22 | 23 | The bigger the pull request, the longer it will take to review and merge. Try to break down large pull requests in smaller chunks that are easier to review and merge. 24 | It is also always helpful to have some context for your pull request. What was the purpose? Why does it matter to you? 25 | 26 | ## Questions 27 | 28 | If you have any questions, create an [issue](issue) (protip: do a quick search first to see if someone else didn't ask the same question before!). 29 | 30 | ## Credits 31 | 32 | Thanks to **[David Dias](https://github.com/thedaviddias)** for the original Front-End Checklist on which this one is based. 33 | 34 | ### Sponsors 35 | 36 | 37 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | **Fixes**: # 3 | 4 | 🚨 Please review the [guidelines for contributing](CONTRIBUTING.md) to this repository. 🚨 5 | **Please complete these steps and check these boxes (by putting an x inside the brackets) before filing your PR:** 6 | 7 | - [ ] Check the commits' message styles. 8 | - [ ] Check your code additions will fail neither code linting checks nor unit test. 9 | 10 | #### Short description of what this resolves: 11 | 12 | 13 | #### Proposed changes: 14 | 15 | - 16 | - 17 | - 18 | 19 | 👍 Thank you! 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | test/unit/coverage 7 | test/e2e/reports 8 | selenium-debug.log 9 | dist/ 10 | .tmp/ 11 | 12 | # Editor directories and files 13 | .idea 14 | .vscode 15 | *.suo 16 | *.ntvs* 17 | *.njsproj 18 | *.sln 19 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 8.12.0 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "flow", 3 | "useTabs": false, 4 | "printWidth": 80, 5 | "tabWidth": 2, 6 | "singleQuote": true, 7 | "trailingComma": "all", 8 | "bracketSpacing": false 9 | } 10 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard", 3 | "plugins": [ 4 | "stylelint-scss" 5 | ], 6 | "rules": { 7 | "at-rule-no-unknown": null, 8 | "color-named": [ 9 | "always-where-possible", 10 | ignore: ["inside-function"] 11 | ], 12 | "max-empty-lines": 4, 13 | "string-quotes": "single", 14 | "color-hex-length": "long", 15 | "declaration-colon-newline-after": null, 16 | "font-family-name-quotes": null, 17 | "number-leading-zero": "never", 18 | "value-list-comma-newline-after": null, 19 | "max-nesting-depth": 3, 20 | "unit-whitelist": ['%', 'px', 'rem', 's', 'deg', 'ms', 'fr', 'pt'], 21 | "no-descending-specificity": null 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeshan/cloudformation-checklist/99cb104f8a61a8aa9b40ccd5a90258678547efcb/CHANGELOG.md -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.16.0-slim 2 | 3 | RUN apt-get update && apt-get install -y git && rm -rf /var/cache/apt 4 | 5 | WORKDIR /app 6 | 7 | COPY package-lock.json . 8 | RUN npm install 9 | 10 | COPY package.json ./ 11 | RUN npm install --only=dev 12 | RUN npm i -g gulp 13 | 14 | COPY src src 15 | COPY data data 16 | COPY *.js .babelrc modernizr-config.json package.json .stylelintrc ./ 17 | 18 | ENTRYPOINT ["npm"] 19 | RUN gulp build 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 |   CloudFormation Checklist 4 |

5 | 6 |

The CloudFormation Checklist is a list of all elements you need to have / to test before launching your infra to production.

7 | 8 |

9 |   How To UseContributing 10 |

11 |

12 | Sister project: Lambda Checklist 13 |
14 |   🎮 Lambda Checklist 15 |

16 |

17 | Inspired by the Front-end Checklist 18 |
19 |   🎮 Front-End Checklist 20 |

21 | 22 | 23 | ## Table of Contents 24 | 25 | See https://cfnchecklist.com 26 | 27 | --- 28 | 29 | ## How to use? 30 | 31 | All items in the **CloudFormation Checklist** are required for the majority of the projects, but some elements can be omitted or are not essential (in the case of an administration web app, you may not need RSS feed for example). We choose to use 3 levels of flexibility: 32 | 33 | * ![Low][low_img] means that the item is **recommended** but can be omitted in some particular situations. 34 | * ![Medium][medium_img] means that the item is **highly recommended** and can eventually be omitted in some really particular cases. Omitting these that engineers may not work at full speed. 35 | * ![High][high_img] means that the item **can't be omitted** by any reason. You may risk security issues in your infra. The testing priority needs to be on these elements first. 36 | 37 | Some resources possess an emoticon to help you understand which type of content / help you may find on the checklist: 38 | 39 | * 📖: documentation or article 40 | * 🛠: online tool / testing tool 41 | * 📹: media or video content 42 | 43 | > You can contribute to the ***CloudFormation Checklist App*** reading the [README_APP file](https://github.com/jeshan/cloudformation-checklist/blob/master/README_APP.md) which explains everything about the project. 44 | 45 | ## CloudFormation Checklist Badge 46 | 47 | If you want to show you are following the rules of the CloudFormation Checklist, put this badge on your README file! 48 | 49 | ➔ [![CloudFormation_Checklist followed](https://img.shields.io/badge/CloudFormation_Checklist-followed-brightgreen.svg)](https://github.com/jeshan/cloudformation-checklist/) 50 | 51 | ```md 52 | [![CloudFormation_Checklist followed](https://img.shields.io/badge/Front‑End_Checklist-followed-brightgreen.svg)](https://github.com/jeshan/cloudformation-checklist/) 53 | ``` 54 | 55 | ## Contributing 56 | 57 | **Open an issue or a pull request to suggest changes or additions.** 58 | 59 | ## Authors 60 | 61 | Original checklist application author: 62 | **[David Dias](https://github.com/thedaviddias)** 63 | 64 | Modified for AWS CloudFormation: 65 | **[Jeshan G. BABOOA](https://github.com/jeshan)** 66 | 67 | ## Infrastructure 68 | The website is a static one deployed on AWS. Contents are stored in Amazon S3 while CloudFront cdn is used to deliver the contents. This is how the stack is: 69 | 70 | ![](/diagram-app.png) 71 | 72 | This is how the deployment pipeline has been set up: 73 | 74 | ![](/diagram-deployment.png) 75 | 76 | 77 | *Images automatically generated with [cfnbuddy](https://www.cfnbuddy.com)* 78 | 79 | 80 | ## License 81 | 82 | [![CC0](https://i.creativecommons.org/p/zero/1.0/88x31.png)](https://creativecommons.org/publicdomain/zero/1.0/) 83 | 84 | [low_img]: https://front-end-checklist.now.sh/low.svg 85 | [medium_img]: https://front-end-checklist.now.sh/medium.svg 86 | [high_img]: https://front-end-checklist.now.sh/high.svg 87 | -------------------------------------------------------------------------------- /README_APP.md: -------------------------------------------------------------------------------- 1 | # CloudFormation Checklist App 2 | 3 | ## Table of Contents 4 | 5 | - [Install](#install) 6 | - [Add new translation](#add-new-translation) 7 | - [Contribute](#contribute) 8 | - [License](#license) 9 | 10 | 11 | 12 | ## Install 13 | 14 | ### Docker 15 | To run site locally: `docker-compose up --build start`, then open `http://localhost:3000` in your browser. 16 | 17 | To build site: `docker-compose up --build dist`. Deploy `dist` directory to your web server. 18 | 19 | ### Other 20 | 21 | To install and run, open a terminal and use the following commands: 22 | 23 | ```shell 24 | npm install 25 | npm start or gulp dev 26 | ``` 27 | ## Authors 28 | 29 | Original checklist application author: 30 | **[David Dias](https://github.com/thedaviddias)** 31 | 32 | Modified for AWS CloudFormation: 33 | **[Jeshan G. BABOOA](https://github.com/jeshan)** 34 | 35 | ## Contribute 36 | 37 | ## License 38 | 39 | [![CC0](https://i.creativecommons.org/p/zero/1.0/88x31.png)](https://creativecommons.org/publicdomain/zero/1.0/) 40 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeshan/cloudformation-checklist/99cb104f8a61a8aa9b40ccd5a90258678547efcb/ROADMAP.md -------------------------------------------------------------------------------- /config/main/app.yaml: -------------------------------------------------------------------------------- 1 | template_path: app.yaml 2 | 3 | parameters: 4 | DomainName: {{stack_group_config.domain_name}} 5 | -------------------------------------------------------------------------------- /config/main/config.yaml: -------------------------------------------------------------------------------- 1 | project_code: cloudformation-checklist 2 | 3 | domain_name: cfnchecklist.com 4 | region: us-east-1 5 | -------------------------------------------------------------------------------- /config/main/deployment.yaml: -------------------------------------------------------------------------------- 1 | template_path: deployment.yaml 2 | 3 | parameters: 4 | Cdn: !stack_output main/app.yaml::DistributionId 5 | DomainName: {{stack_group_config.domain_name}} 6 | -------------------------------------------------------------------------------- /data/en/items/authoring.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Essential DevOps tools", 4 | "priority": "Medium", 5 | "description": "Install these to work faster locally", 6 | "tools": [ 7 | { 8 | "title": "Typeformation plugin for JetBrains IDEs", 9 | "url": "https://plugins.jetbrains.com/plugin/10653-typeformation" 10 | }, 11 | { 12 | "title": "AWS CloudFormation plugin for JetBrains IDEs", 13 | "url": "https://plugins.jetbrains.com/plugin/7371-aws-cloudformation" 14 | }, 15 | { 16 | "title": "CloudFormation support for Visual Studio Code", 17 | "url": "https://marketplace.visualstudio.com/items?itemName=aws-scripting-guy.cform" 18 | }, 19 | { 20 | "title": "Cloudformation YAML Snippets for VS Code", 21 | "url": "https://marketplace.visualstudio.com/items?itemName=dsteenman.cloudformation-yaml-snippets" 22 | }, 23 | { 24 | "title": "Snippets for other IDEs", 25 | "url": "https://github.com/search?q=cloudformation+snippets" 26 | }, 27 | { 28 | "title": "cfn-python-lint IDE plugins", 29 | "url": "https://github.com/aws-cloudformation/cfn-python-lint#editor-plugins" 30 | } 31 | ], 32 | "tags": [ 33 | "all", 34 | "tools", 35 | "ide", 36 | "medium" 37 | ] 38 | }, 39 | { 40 | "title": "Prefer YAML over JSON", 41 | "priority": "Low", 42 | "description": "less verbosity, supports comments", 43 | "tools": [ 44 | { 45 | "title": "Use aws-cfn-template-flip to migrate to YAML", 46 | "url": "https://github.com/awslabs/aws-cfn-template-flip" 47 | } 48 | ], 49 | "tags": [ 50 | "all", 51 | "template", 52 | "low" 53 | ] 54 | }, 55 | { 56 | "title": "Reuse snippets", 57 | "priority": "Medium", 58 | "description": "Reduce the guesswork; CloudFormation snippets available from AWS and on Github", 59 | "documentation": [ 60 | { 61 | "title": "Template Snippets", 62 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/CHAP_TemplateQuickRef.html?shortFooter=true" 63 | }, 64 | { 65 | "title": "Cloudformation samples/snippets", 66 | "url": "https://github.com/search?q=cloudformation+samples" 67 | } 68 | ], 69 | "tags": [ 70 | "all", 71 | "authoring", 72 | "reuse", 73 | "medium" 74 | ] 75 | }, 76 | { 77 | "title": "Organise Your Stacks By Lifecycle and Ownership", 78 | "priority": "High", 79 | "description": "Putting all resources in too few stacks can become cumbersome", 80 | "documentation": [ 81 | { 82 | "title": "Explanation", 83 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html?shortFooter=true#organizingstacks" 84 | } 85 | ], 86 | "tags": [ 87 | "all", 88 | "template", 89 | "team", 90 | "high" 91 | ] 92 | }, 93 | { 94 | "title": "Use Cross-Stack References to Export Shared Resources", 95 | "priority": "Low", 96 | "description": "Decrease overhead to get a stack running", 97 | "documentation": [ 98 | { 99 | "title": "Explanation", 100 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html?shortFooter=true#cross-stack" 101 | } 102 | ], 103 | "tags": [ 104 | "all", 105 | "template", 106 | "low" 107 | ] 108 | }, 109 | { 110 | "title": "Make templates reusable", 111 | "priority": "Medium", 112 | "description": "Reuse Templates to Replicate Stacks in Multiple Environments", 113 | "documentation": [ 114 | { 115 | "title": "Explanation", 116 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html?shortFooter=true#reuse" 117 | } 118 | ], 119 | "tags": [ 120 | "all", 121 | "template", 122 | "reuse", 123 | "medium" 124 | ] 125 | }, 126 | { 127 | "title": "Use AWS-Specific Parameter Types", 128 | "priority": "Low", 129 | "description": "For basic validation and some variable lookups", 130 | "documentation": [ 131 | { 132 | "title": "Explanation", 133 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html?shortFooter=true#parmtypes" 134 | } 135 | ], 136 | "tags": [ 137 | "all", 138 | "template", 139 | "low" 140 | ] 141 | }, 142 | { 143 | "title": "Use Parameter Constraints", 144 | "priority": "Low", 145 | "description": "So as to clarify what inputs the user should be entering", 146 | "documentation": [ 147 | { 148 | "title": "Explanation", 149 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html?shortFooter=true#parmconstraints" 150 | } 151 | ], 152 | "tags": [ 153 | "all", 154 | "template", 155 | "low" 156 | ] 157 | }, 158 | { 159 | "title": "Use AWS::CloudFormation::Init to Deploy Software Applications on Amazon EC2 Instances", 160 | "priority": "Medium", 161 | "description": "To make it declarative instead of procedural", 162 | "documentation": [ 163 | { 164 | "title": "Explanation", 165 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html?shortFooter=true#cfninit" 166 | } 167 | ], 168 | "tags": [ 169 | "all", 170 | "template", 171 | "ec2", 172 | "medium" 173 | ] 174 | }, 175 | { 176 | "title": "Experiment with the console first, then generate the templates/snippets", 177 | "priority": "Medium", 178 | "description": "To reduce the guesswork on how to get the resource definitions right, have a tool generate it accurately", 179 | "tools": [ 180 | { 181 | "title": "AWS Console Recorder (Records actions made in the AWS Management Console and outputs the equivalent CLI/SDK commands and CloudFormation/Terraform templates in your browser)", 182 | "url": "https://github.com/iann0036/AWSConsoleRecorder" 183 | }, 184 | { 185 | "title": "Former2 (Generate CloudFormation / Terraform / Troposphere templates from your existing AWS resources in your browser)", 186 | "url": "https://former2.com" 187 | } 188 | ], 189 | "tags": [ 190 | "all", 191 | "code generator", 192 | "medium" 193 | ] 194 | }, 195 | { 196 | "title": "Check resource lifecycle policies", 197 | "priority": "High", 198 | "description": "Be aware if/when your resources will get replaced by CFN to avoid surprises", 199 | "documentation": [ 200 | { 201 | "title": "AWS Resource and Property Types Reference", 202 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html?shortFooter=true" 203 | } 204 | ], 205 | "tags": [ 206 | "all", 207 | "docs", 208 | "high" 209 | ] 210 | }, 211 | { 212 | "title": "Use custom resources to automate more of your infra", 213 | "priority": "Low", 214 | "description": "because AWS may not cover everything under CFN yet", 215 | "documentation": [ 216 | { 217 | "title": "Docs", 218 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html?shortFooter=true" 219 | } 220 | ], 221 | "tags": [ 222 | "all", 223 | "template", 224 | "low" 225 | ] 226 | }, 227 | { 228 | "title": "Use macros", 229 | "priority": "Medium", 230 | "description": "to reduce duplication, custom lookups, or any arbitrary code to transform template, etc", 231 | "documentation": [ 232 | { 233 | "title": "Docs", 234 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-macros.html?shortFooter=true" 235 | } 236 | ], 237 | "tags": [ 238 | "all", 239 | "template", 240 | "medium" 241 | ] 242 | } 243 | ] 244 | -------------------------------------------------------------------------------- /data/en/items/before.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Reuse templates", 4 | "priority": "High", 5 | "description": "Don't reinvent the wheel; check these tools for vetted open-source templates", 6 | "tools": [ 7 | { 8 | "title": "widdix/aws-cf-templates", 9 | "url": "https://github.com/widdix/aws-cf-templates" 10 | }, 11 | { 12 | "title": "awslabs/aws-cloudformation-templates", 13 | "url": "https://github.com/awslabs/aws-cloudformation-templates" 14 | }, 15 | { 16 | "title": "stelligent/cloudformation_templates", 17 | "url": "https://github.com/stelligent/cloudformation_templates" 18 | }, 19 | { 20 | "title": "AWS Quick Starts", 21 | "url": "https://aws.amazon.com/quickstart" 22 | }, 23 | { 24 | "title": "Other templates on Github", 25 | "url": "https://github.com/search?q=cloudformation+template" 26 | } 27 | ], 28 | "tags": [ 29 | "all", 30 | "before starting", 31 | "github", 32 | "reuse", 33 | "community", 34 | "high" 35 | ] 36 | } 37 | ] 38 | -------------------------------------------------------------------------------- /data/en/items/best_practices.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "High availability practices", 4 | "priority": "Medium", 5 | "description": "", 6 | "documentation": [ 7 | { 8 | "title": "Link", 9 | "url": "https://aws-quickstart.github.io/best-practices.html#ha" 10 | } 11 | ], 12 | "tags": [ 13 | "all", 14 | "medium" 15 | ] 16 | }, 17 | { 18 | "title": "Security practices", 19 | "priority": "High", 20 | "description": "", 21 | "documentation": [ 22 | { 23 | "title": "Link", 24 | "url": "https://aws-quickstart.github.io/best-practices.html#security" 25 | } 26 | ], 27 | "tags": [ 28 | "all", 29 | "security", 30 | "high" 31 | ] 32 | }, 33 | { 34 | "title": "Portability practices", 35 | "priority": "Low", 36 | "description": "", 37 | "documentation": [ 38 | { 39 | "title": "Link", 40 | "url": "https://aws-quickstart.github.io/best-practices.html#port" 41 | } 42 | ], 43 | "tags": [ 44 | "all", 45 | "low" 46 | ] 47 | }, 48 | { 49 | "title": "Managed Services practices", 50 | "priority": "Low", 51 | "description": "Some managed services that you should be using", 52 | "documentation": [ 53 | { 54 | "title": "Link", 55 | "url": "https://aws-quickstart.github.io/best-practices.html#managed" 56 | } 57 | ], 58 | "tags": [ 59 | "all", 60 | "low" 61 | ] 62 | }, 63 | { 64 | "title": "Custom Resources practices", 65 | "priority": "Medium", 66 | "description": "", 67 | "documentation": [ 68 | { 69 | "title": "Link", 70 | "url": "https://aws-quickstart.github.io/best-practices.html#custom" 71 | } 72 | ], 73 | "tags": [ 74 | "all", 75 | "custom resources", 76 | "medium" 77 | ] 78 | }, 79 | { 80 | "title": "Linux practices", 81 | "priority": "Medium", 82 | "description": "", 83 | "documentation": [ 84 | { 85 | "title": "Link", 86 | "url": "https://aws-quickstart.github.io/best-practices.html#linux" 87 | } 88 | ], 89 | "tags": [ 90 | "all", 91 | "ec2", 92 | "medium" 93 | ] 94 | }, 95 | { 96 | "title": "Windows practices", 97 | "priority": "Medium", 98 | "description": "", 99 | "documentation": [ 100 | { 101 | "title": "Link", 102 | "url": "https://aws-quickstart.github.io/best-practices.html#windows" 103 | } 104 | ], 105 | "tags": [ 106 | "all", 107 | "ec2", 108 | "medium" 109 | ] 110 | }, 111 | { 112 | "title": "All platforms practices", 113 | "priority": "Low", 114 | "description": "", 115 | "documentation": [ 116 | { 117 | "title": "Link", 118 | "url": "https://aws-quickstart.github.io/best-practices.html#all-plat" 119 | } 120 | ], 121 | "tags": [ 122 | "all", 123 | "all platforms", 124 | "low" 125 | ] 126 | }, 127 | { 128 | "title": "Template formatting practices", 129 | "priority": "Low", 130 | "description": "", 131 | "documentation": [ 132 | { 133 | "title": "Link", 134 | "url": "https://aws-quickstart.github.io/best-practices.html#formatting" 135 | } 136 | ], 137 | "tags": [ 138 | "all", 139 | "template", 140 | "low" 141 | ] 142 | }, 143 | { 144 | "title": "Other practices (community)", 145 | "priority": "Low", 146 | "description": "Other practices that the community has come up with", 147 | "documentation": [ 148 | { 149 | "title": "alexgibs/cfnstyle", 150 | "url": "https://github.com/alexgibs/cfnstyle" 151 | }, 152 | { 153 | "title": "toddm92/aws (Wiki: CloudFormation Best Practices)", 154 | "url": "https://github.com/toddm92/aws/wiki/CloudFormation-Best-Practices" 155 | } 156 | ], 157 | "tags": [ 158 | "all", 159 | "community", 160 | "low" 161 | ] 162 | } 163 | ] 164 | -------------------------------------------------------------------------------- /data/en/items/management.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Manage All Stack Resources Through AWS CloudFormation", 4 | "priority": "High", 5 | "description": "Doing so can create a mismatch between your stack's template and the current state of your stack resources, which can cause errors if you update or delete the stack", 6 | "documentation": [ 7 | { 8 | "title": "Reference", 9 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html?shortFooter=true#donttouch" 10 | } 11 | ], 12 | "tags": [ 13 | "all", 14 | "management", 15 | "high" 16 | ] 17 | }, 18 | { 19 | "title": "Create Change Sets Before Updating Your Stacks", 20 | "priority": "Low", 21 | "description": "See how proposed changes might impact your resources to avoid accidents", 22 | "documentation": [ 23 | { 24 | "title": "Explanation", 25 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html?shortFooter=true#cfn-best-practices-changesets" 26 | } 27 | ], 28 | "tags": [ 29 | "all", 30 | "management", 31 | "low" 32 | ] 33 | }, 34 | { 35 | "title": "Use Stack Policies", 36 | "priority": "Medium", 37 | "description": "protect critical stack resources from unintentional updates", 38 | "documentation": [ 39 | { 40 | "title": "Explanation", 41 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html?shortFooter=true#stackpolicy" 42 | } 43 | ], 44 | "tags": [ 45 | "all", 46 | "management", 47 | "medium" 48 | ] 49 | }, 50 | { 51 | "title": "Use AWS CloudTrail to Log AWS CloudFormation Calls", 52 | "priority": "Low", 53 | "description": "To track api calls for auditing", 54 | "documentation": [ 55 | { 56 | "title": "Explanation", 57 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html?shortFooter=true#cloudtrail" 58 | } 59 | ], 60 | "tags": [ 61 | "all", 62 | "management", 63 | "low" 64 | ] 65 | }, 66 | { 67 | "title": "Use a CFN launcher like Sceptre", 68 | "priority": "Medium", 69 | "description": "Helps manage templates at scale with your multiple accounts and regions. Check docs for more useful features", 70 | "documentation": [ 71 | { 72 | "title": "Sceptre docs", 73 | "url": "https://sceptre.cloudreach.com/latest/index.html" 74 | } 75 | ], 76 | "tags": [ 77 | "all", 78 | "tools", 79 | "management", 80 | "medium" 81 | ] 82 | }, 83 | { 84 | "title": "Automate drift detection", 85 | "priority": "Low", 86 | "description": "Important to catch unmanaged changes made outside of CloudFormation", 87 | "documentation": [ 88 | { 89 | "title": "Detecting Unmanaged Configuration Changes to Stacks and Resources", 90 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-drift.html?shortFooter=true" 91 | }, 92 | { 93 | "title": "Automate drift detection with AWS Config", 94 | "url": "https://docs.aws.amazon.com/config/latest/developerguide/cloudformation-stack-drift-detection-check.html?shortFooter=true" 95 | } 96 | ], 97 | "tags": [ 98 | "all", 99 | "management", 100 | "automation", 101 | "low" 102 | ] 103 | } 104 | ] 105 | -------------------------------------------------------------------------------- /data/en/items/security.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Restrict IAM permissions", 4 | "priority": "High", 5 | "description": "Grant users only the permissions they need to work with CloudFormation", 6 | "documentation": [ 7 | { 8 | "title": "Use IAM to Control Access", 9 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html?shortFooter=true#use-iam-to-control-access" 10 | } 11 | ], 12 | "tags": [ 13 | "all", 14 | "security", 15 | "team", 16 | "management", 17 | "high" 18 | ] 19 | }, 20 | { 21 | "title": "Do not embed credentials", 22 | "priority": "High", 23 | "description": "Use input parameters instead", 24 | "documentation": [ 25 | { 26 | "title": "Do Not Embed Credentials in Your Templates", 27 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html?shortFooter=true#creds" 28 | } 29 | ], 30 | "tags": [ 31 | "all", 32 | "security", 33 | "high" 34 | ] 35 | }, 36 | { 37 | "title": "Statically analyse your templates with cfn_nag", 38 | "priority": "Medium", 39 | "description": "45+ rules to catch potential insecure infra and misconfiguration", 40 | "tools": [ 41 | { 42 | "title": "stelligent/cfn_nag", 43 | "url": "https://github.com/stelligent/cfn_nag" 44 | }, 45 | { 46 | "title": "List of rules", 47 | "url": "https://github.com/stelligent/cfn_nag/tree/master/lib/cfn-nag/custom_rules" 48 | } 49 | ], 50 | "tags": [ 51 | "all", 52 | "tools", 53 | "security", 54 | "medium" 55 | ] 56 | } 57 | ] 58 | -------------------------------------------------------------------------------- /data/en/items/team.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Use Code Reviews and Revision Controls to Manage Your Templates", 4 | "priority": "High", 5 | "description": "Share and ask feedback from your team", 6 | "documentation": [ 7 | { 8 | "title": "Explanation", 9 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html?shortFooter=true#code" 10 | } 11 | ], 12 | "tags": [ 13 | "all", 14 | "team", 15 | "high" 16 | ] 17 | }, 18 | { 19 | "title": "Share this checklist", 20 | "priority": "Medium", 21 | "description": "Ask each team member to always have this template open", 22 | "tools": [ 23 | { 24 | "title": "CloudFormation checklist", 25 | "url": "https://cfnchecklist.com" 26 | } 27 | ], 28 | "tags": [ 29 | "all", 30 | "team", 31 | "medium" 32 | ] 33 | } 34 | ] 35 | -------------------------------------------------------------------------------- /data/en/items/testing.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Verify Quotas for All Resource Types", 4 | "priority": "Low", 5 | "description": "To avoid hitting AWS api limits during CFN deployments", 6 | "documentation": [ 7 | { 8 | "title": "Explanation", 9 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html?shortFooter=true#limits" 10 | } 11 | ], 12 | "tools": [ 13 | { 14 | "title": "AWS Service Quotas", 15 | "url": "https://console.aws.amazon.com/servicequotas/home" 16 | } 17 | ], 18 | "tags": [ 19 | "all", 20 | "testing", 21 | "low" 22 | ] 23 | }, 24 | { 25 | "title": "Validate Templates Before Using Them", 26 | "priority": "Low", 27 | "description": "save time by catching syntax and semantic errors", 28 | "documentation": [ 29 | { 30 | "title": "Explanation", 31 | "url": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html?shortFooter=true#validate" 32 | } 33 | ], 34 | "tags": [ 35 | "all", 36 | "testing", 37 | "low" 38 | ] 39 | }, 40 | { 41 | "title": "Lint your templates", 42 | "priority": "Low", 43 | "description": "To quickly catch typos, fast validations, if you're following certain best practices, etc", 44 | "tools": [ 45 | { 46 | "title": "cfn-python-lint", 47 | "url": "https://github.com/aws-cloudformation/cfn-python-lint" 48 | } 49 | ], 50 | "tags": [ 51 | "all", 52 | "testing", 53 | "low" 54 | ] 55 | }, 56 | { 57 | "title": "Test your templates with TaskCat in your relevant regions", 58 | "priority": "Low", 59 | "description": "TaskCat deploys your AWS CloudFormation template in multiple AWS Regions and generates a report with a pass/fail grade for each region", 60 | "documentation": [ 61 | { 62 | "title": "Automated testing with TaskCat", 63 | "url": "https://aws-quickstart.github.io/auto-testing.html" 64 | } 65 | ], 66 | "tags": [ 67 | "all", 68 | "testing", 69 | "low" 70 | ] 71 | } 72 | ] 73 | -------------------------------------------------------------------------------- /data/en/project/introductions.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "head": { 3 | "introduction": "" 4 | }, 5 | "html": { 6 | "introduction": "" 7 | } 8 | }] 9 | -------------------------------------------------------------------------------- /data/en/project/translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "SITE_NAME": "The CloudFormation Checklist", 3 | "INDEX_TITLE": "✨ Your best cloud checklist Tool ✨", 4 | "URL_WEBSITE": "https://cfnchecklist.com", 5 | "SITE_TAGLINE": "🗂 The CloudFormation Checklist Application is perfect for meticulous AWS DevOps engineers!", 6 | "SITE_DESCRIPTION": "🗂 The CloudFormation Checklist Application is perfect for meticulous AWS DevOps engineers! Follow the rules and deliver the best of your work in a generated report!", 7 | "SITE_LANGUAGE": "en", 8 | "SITE_DIRECTION": "ltr", 9 | "URL_GITHUB_ROOT": "https://github.com/jeshan/cloudformation-checklist", 10 | "URL_GITHUB_REPO": "https://github.com/jeshan/cloudformation-checklist/tree/gh-pages", 11 | "HIGH_CHECKED": "✓ high priority", 12 | "MEDIUM_CHECKED": "✓ medium priority", 13 | "LOW_CHECKED": "✓ low priority", 14 | "PERCENTAGE_CHECKED": "items are ✓", 15 | "SECTION_DOCUMENTATION": "Documentation", 16 | "SECTION_TOOL": "Tools", 17 | "SECTION_VIDEO": "Videos", 18 | "SECTION_TAG": "Filter by tags", 19 | "alert": { 20 | "JAVASCRIPT_DESACTIVATE": "Your JavaScript seems to be deactivated. Please enable your JavaScript to use all features of the CloudFormation Checklist." 21 | }, 22 | "form": { 23 | "LABEL_PROJECT_NAME": "Project Name", 24 | "TITLE_PROJECT_NAME": "Type the name of your project", 25 | "LABEL_PAGE_TITLE": "Page title or URL", 26 | "TITLE_PAGE_TITLE": "Type the name of your page or URL", 27 | "LABEL_DEVELOPER_NAME": "Developer's name or team", 28 | "TITLE_DEVELOPER_NAME": "Type your name or the name of your team", 29 | "LABEL_SEARCH": "Search", 30 | "BUTTON_START_NEW": "Start new checklist", 31 | "BUTTON_GENERATE_PRINT": "Generate report" 32 | }, 33 | "social": { 34 | "TWEET": "Tweet", 35 | "TWITTER_MSG": "Check the ✨ CloudFormation Checklist Application ✨ for meticulous AWS DevOps engineers! Use it on your daily workflow and generate reports 📑!", 36 | "TWITTER_VIA": "jeshan25", 37 | "TWITTER_HASHTAGS": "cloudformation", 38 | "STAR": "Star", 39 | "GITHUB_STAR_MSG": "Star jeshan/cloudformation-checklist on GitHub" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /data/jp/items/利用方法.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "Doctype", 4 | "priority": "High", 5 | "description": "The Doctype is HTML5 and is at the top of all your HTML pages.", 6 | "code": "https://gist.github.com/thedaviddias/bccee9f4dfa728830cf38bb83838d2d3.js", 7 | "documentation": [ 8 | { 9 | "title": "Determining the character encoding - HTML5 W3C", 10 | "url": "https://www.w3.org/TR/html5/syntax.html#determining-the-character-encoding" 11 | } 12 | ], 13 | "tags": ["all", "Meta tag"] 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /diagram-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeshan/cloudformation-checklist/99cb104f8a61a8aa9b40ccd5a90258678547efcb/diagram-app.png -------------------------------------------------------------------------------- /diagram-deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeshan/cloudformation-checklist/99cb104f8a61a8aa9b40ccd5a90258678547efcb/diagram-deployment.png -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | services: 4 | start: 5 | command: ['start'] 6 | ports: 7 | - 3000:3000 8 | - 3001:3001 9 | build: 10 | context: . 11 | volumes: 12 | - ./data/en/items:/data/en/items 13 | - ./data/en/project:/data/en/project 14 | - ./src:/src 15 | 16 | dist: 17 | entrypoint: cp -r /app/dist / 18 | build: 19 | context: . 20 | volumes: 21 | - ./dist:/dist 22 | -------------------------------------------------------------------------------- /modernizr-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "minify": true, 3 | "options": [ 4 | "domPrefixes", 5 | "prefixes", 6 | "mq", 7 | "prefixedCSSValue", 8 | "testStyles", 9 | "setClasses" 10 | ], 11 | "feature-detects": [ 12 | "test/history", 13 | "test/css/flexbox", 14 | "test/workers/webworkers" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cloudformation-checklist", 3 | "version": "0.0.1", 4 | "description": "The checklist for meticulous AWS DevOps engineers", 5 | "scripts": { 6 | "start": "gulp dev", 7 | "build": "gulp build", 8 | "test": "gulp test", 9 | "eslint-check": "eslint --print-config .eslintrc.js | eslint-config-prettier-check" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://jeshan@github.com/jeshan/cloudformation-checklist.git" 14 | }, 15 | "author": "Jeshan G. Babooa (j@jeshan.co)", 16 | "license": "MIT", 17 | "bugs": { 18 | "url": "https://github.com/jeshan/cloudformation-checklist/issues" 19 | }, 20 | "homepage": "https://github.com/jeshan/cloudformation-checklist#readme", 21 | "devDependencies": { 22 | "babel-cli": "^6.26.0", 23 | "babel-eslint": "^9.0.0", 24 | "babel-loader": "^7.1.4", 25 | "babel-polyfill": "^6.26.0", 26 | "babel-preset-env": "^1.6.1", 27 | "babel-register": "^6.26.0", 28 | "browser-sync": "^2.23.6", 29 | "chai": "^4.1.2", 30 | "critical": "^1.1.0", 31 | "del": "4.1.1", 32 | "eslint-config-airbnb-base": "^13.0.0", 33 | "eslint-config-prettier": "^3.0.0", 34 | "eslint-plugin-flowtype": "^2.46.1", 35 | "eslint-plugin-import": "^2.9.0", 36 | "eslint-plugin-prettier": "^2.6.0", 37 | "flow-bin": "^0.77.0", 38 | "gulp": "^3.9.1", 39 | "gulp-autoprefixer": "^6.0.0", 40 | "gulp-cached": "^1.1.1", 41 | "gulp-cdnizer": "^2.0.0", 42 | "gulp-changed": "^3.2.0", 43 | "gulp-cssnano": "^2.1.2", 44 | "gulp-data": "^1.3.1", 45 | "gulp-eslint": "^5.0.0", 46 | "gulp-group-css-media-queries": "^1.2.2", 47 | "gulp-html-replace": "^1.6.2", 48 | "gulp-htmlmin": "^5.0.0", 49 | "gulp-if": "^2.0.2", 50 | "gulp-imagemin": "^4.1.0", 51 | "gulp-istanbul": "^1.1.3", 52 | "gulp-json-concat": "^0.1.1", 53 | "gulp-mocha": "^6.0.0", 54 | "gulp-newer": "^1.4.0", 55 | "gulp-plumber": "^1.2.0", 56 | "gulp-pug": "^4.0.1", 57 | "gulp-rename": "^1.2.2", 58 | "gulp-sass": "^4.0.1", 59 | "gulp-sourcemaps": "^2.6.4", 60 | "gulp-stylelint": "^7.0.0", 61 | "gulp-util": "^3.0.8", 62 | "gulp-webpack": "^1.5.0", 63 | "html-webpack-plugin": "^3.0.0", 64 | "imagemin-webp": "^4.1.0", 65 | "jsdom": "^12.0.0", 66 | "jsdom-global": "3.0.2", 67 | "modernizr": "^3.6.0", 68 | "postscribe": "^2.0.8", 69 | "prettier": "^1.11.1", 70 | "run-sequence": "^2.2.1", 71 | "sinon": "^6.1.3", 72 | "stylelint": "^9.1.1", 73 | "stylelint-config-standard": "^18.2.0", 74 | "stylelint-scss": "^3.1.3", 75 | "uglifyjs-webpack-plugin": "^2.0.0", 76 | "url-loader": "^1.0.1", 77 | "webpack": "^4.16.1", 78 | "webpack-stream": "^5.0.0", 79 | "yargs": "^12.0.1" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/_headers: -------------------------------------------------------------------------------- 1 | /* 2 | X-Frame-Options: DENY 3 | X-XSS-Protection: 1; mode=block 4 | # Prevent browsers from incorrectly detecting non-scripts as scripts 5 | X-Content-Type-Options: nosniff 6 | # Don't load any resource type not explicitly enabled 7 | # Disable plugins like Flash or Silverlight 8 | # Load images, scripts, stylesheets and fonts from self 9 | # Send reports to report-uri.io 10 | # Content-Security-Policy: default-src 'self' cfnchecklist.com; object-src 'none'; img-src https: app.codesponsor.io www.google.com; script-src https: www.google-analytics.com ajax.googleapis.com platform.twitter.com buttons.github.io; style-src https: ; font-src https: ; report-uri https://cfnchecklist.report-uri.io/r/default/csp/enforce; 11 | -------------------------------------------------------------------------------- /src/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeshan/cloudformation-checklist/99cb104f8a61a8aa9b40ccd5a90258678547efcb/src/android-chrome-192x192.png -------------------------------------------------------------------------------- /src/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeshan/cloudformation-checklist/99cb104f8a61a8aa9b40ccd5a90258678547efcb/src/apple-touch-icon.png -------------------------------------------------------------------------------- /src/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeshan/cloudformation-checklist/99cb104f8a61a8aa9b40ccd5a90258678547efcb/src/favicon-16x16.png -------------------------------------------------------------------------------- /src/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeshan/cloudformation-checklist/99cb104f8a61a8aa9b40ccd5a90258678547efcb/src/favicon-32x32.png -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeshan/cloudformation-checklist/99cb104f8a61a8aa9b40ccd5a90258678547efcb/src/favicon.ico -------------------------------------------------------------------------------- /src/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | Jeshan G. BABOOA -- Cloudformation Nerd -- @jeshan25 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | CSS3, HTML5, Pug, Git, ES6, Webpack, Gulp, Modernizr, Normalize.css 15 | -------------------------------------------------------------------------------- /src/img/icons/1x1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeshan/cloudformation-checklist/99cb104f8a61a8aa9b40ccd5a90258678547efcb/src/img/icons/1x1.png -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "CFN Checklist", 3 | "name": "CloudFormation Checklist", 4 | "start_url": "http://cfnchecklist.com/?utm_source=homescreen", 5 | "icons": [ 6 | { 7 | "src": "/android-chrome-192x192.png", 8 | "sizes": "192x192", 9 | "type": "image/png" 10 | } 11 | ], 12 | "theme_color": "#ffffff", 13 | "background_color": "#ffffff", 14 | "display": "standalone" 15 | } 16 | -------------------------------------------------------------------------------- /src/modernizr-custom.min.js: -------------------------------------------------------------------------------- 1 | /*! modernizr 3.6.0 (Custom Build) | MIT * 2 | * https://modernizr.com/download/?-flexbox-history-webworkers-domprefixes-mq-prefixedcssvalue-prefixes-setclasses-teststyles !*/ 3 | !function(e,n,t){function r(e,n){return typeof e===n}function o(){var e,n,t,o,i,s,a;for(var l in C)if(C.hasOwnProperty(l)){if(e=[],n=C[l],n.name&&(e.push(n.name.toLowerCase()),n.options&&n.options.aliases&&n.options.aliases.length))for(t=0;td;d++)if(v=e[d],y=N.style[v],u(v,"-")&&(v=p(v)),N.style[v]!==t){if(i||r(o,"undefined"))return a(),"pfx"==n?v:!0;try{N.style[v]=o}catch(g){}if(N.style[v]!=y)return a(),"pfx"==n?v:!0}return a(),!1}function v(e,n){return function(){return e.apply(n,arguments)}}function y(e,n,t){var o;for(var i in e)if(e[i]in n)return t===!1?e[i]:(o=n[e[i]],r(o,"function")?v(o,t||n):o);return!1}function h(e,n,t,o,i){var s=e.charAt(0).toUpperCase()+e.slice(1),a=(e+" "+k.join(s+" ")+s).split(" ");return r(n,"string")||r(n,"undefined")?m(a,n,o,i):(a=(e+" "+P.join(s+" ")+s).split(" "),y(a,n,t))}function g(e,n,r){return h(e,t,t,n,r)}var C=[],S={_version:"3.6.0",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,n){var t=this;setTimeout(function(){n(t[e])},0)},addTest:function(e,n,t){C.push({name:e,fn:n,options:t})},addAsyncTest:function(e){C.push({name:null,fn:e})}},Modernizr=function(){};Modernizr.prototype=S,Modernizr=new Modernizr;var x=[],w=n.documentElement,_="svg"===w.nodeName.toLowerCase(),b="Moz O ms Webkit",P=S._config.usePrefixes?b.toLowerCase().split(" "):[];S._domPrefixes=P;var z=S._config.usePrefixes?" -webkit- -moz- -o- -ms- ".split(" "):["",""];S._prefixes=z;var E=function(){var n=e.matchMedia||e.msMatchMedia;return n?function(e){var t=n(e);return t&&t.matches||!1}:function(n){var t=!1;return l("@media "+n+" { #modernizr { position: absolute; } }",function(n){t="absolute"==(e.getComputedStyle?e.getComputedStyle(n,null):n.currentStyle).position}),t}}();S.mq=E;var T=function(e,n){var t=!1,r=s("div"),o=r.style;if(e in o){var i=P.length;for(o[e]=n,t=o[e];i--&&!t;)o[e]="-"+P[i]+"-"+n,t=o[e]}return""===t&&(t=!1),t};S.prefixedCSSValue=T;S.testStyles=l;Modernizr.addTest("history",function(){var n=navigator.userAgent;return-1===n.indexOf("Android 2.")&&-1===n.indexOf("Android 4.0")||-1===n.indexOf("Mobile Safari")||-1!==n.indexOf("Chrome")||-1!==n.indexOf("Windows Phone")||"file:"===location.protocol?e.history&&"pushState"in e.history:!1});var k=S._config.usePrefixes?b.split(" "):[];S._cssomPrefixes=k;var A={elem:s("modernizr")};Modernizr._q.push(function(){delete A.elem});var N={style:A.elem.style};Modernizr._q.unshift(function(){delete N.style}),S.testAllProps=h,S.testAllProps=g,Modernizr.addTest("flexbox",g("flexBasis","1px",!0)),Modernizr.addTest("webworkers","Worker"in e),o(),i(x),delete S.addTest,delete S.addAsyncTest;for(var O=0;O 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 16 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/scripts/Utils.js: -------------------------------------------------------------------------------- 1 | let instance = null; 2 | 3 | class Utils { 4 | constructor() { 5 | if (!instance) { 6 | instance = this; 7 | } 8 | 9 | return instance; 10 | } 11 | 12 | variableList(el) { 13 | const section = instance.getClosest(el, '.js-section'); 14 | const sectionName = section.getAttribute('data-section'); 15 | const sectionId = section.getAttribute('data-section-id'); 16 | 17 | const item = instance.getClosest(el, '.js-item '); 18 | 19 | let itemId; 20 | let itemCheck; 21 | let itemDropdown; 22 | 23 | if (item !== null) { 24 | itemId = item.getAttribute('data-item-id'); 25 | itemCheck = item.getAttribute('data-item-check'); 26 | itemDropdown = item.getAttribute('data-item-dropdown'); 27 | } 28 | 29 | return { 30 | sectionId, 31 | section, 32 | sectionName, 33 | item, 34 | itemId, 35 | itemCheck, 36 | itemDropdown, 37 | }; 38 | } 39 | 40 | visibityEl(container, el, status) { 41 | const tags = container.querySelectorAll(el)[0]; 42 | 43 | if (tags !== undefined) { 44 | if (status === 'hide') { 45 | tags.style.display = 'none'; 46 | } else { 47 | tags.style.display = 'flex'; 48 | } 49 | } 50 | } 51 | 52 | detailsContainer(section) { 53 | const sectionClass = section.classList[1]; 54 | const sectionObj = document.querySelectorAll('.' + sectionClass); 55 | const detailsContainer = sectionObj[0].querySelectorAll('.js-details'); 56 | 57 | return detailsContainer; 58 | } 59 | 60 | /* eslint-disable */ 61 | getClosest(elem, selector) { 62 | if (!Element.prototype.matches) { 63 | Element.prototype.matches = 64 | Element.prototype.matchesSelector || 65 | Element.prototype.mozMatchesSelector || 66 | Element.prototype.msMatchesSelector || 67 | Element.prototype.oMatchesSelector || 68 | Element.prototype.webkitMatchesSelector || 69 | function parse(s) { 70 | let matches = (this.document || this.ownerDocument).querySelectorAll( 71 | s); 72 | let i = matches.length; 73 | while ((--i >= 0 && matches.item(i) !== this)) {} 74 | return i > -1; 75 | }; 76 | } 77 | 78 | // Get closest match 79 | for (; elem && elem !== document; elem = elem.parentNode) { 80 | if (elem.matches(selector)) return elem; 81 | } 82 | 83 | return null; 84 | } 85 | /* eslint-enable */ 86 | } 87 | 88 | const Instance = new Utils(); 89 | Object.freeze(Instance); 90 | 91 | export default Utils; 92 | -------------------------------------------------------------------------------- /src/scripts/components/Analytics.js: -------------------------------------------------------------------------------- 1 | let instance = null; 2 | /** 3 | * 4 | * 5 | * @class Analytics 6 | */ 7 | class Analytics { 8 | constructor() { 9 | if (!instance) { 10 | instance = this; 11 | } 12 | 13 | return instance; 14 | } 15 | 16 | addRef() { 17 | const details = document.querySelectorAll('.js-details'); 18 | 19 | details.forEach(detail => { 20 | const links = detail.querySelectorAll('a'); 21 | links.forEach(link => { 22 | const ref = 'ref=cloudformationchecklist'; 23 | let append = ''; 24 | let href = ''; 25 | 26 | if (link.href.indexOf('?') > 0) { 27 | append = '&'; 28 | } else { 29 | append = '?'; 30 | } 31 | let hashAt = link.href.lastIndexOf('#'); 32 | if (hashAt < 0) { 33 | href = link.href + append + ref; 34 | } else { 35 | href = link.href.substring(0, hashAt) + append + ref; 36 | href += link.href.substring(hashAt); 37 | } 38 | link.setAttribute('href', href); 39 | }); 40 | }); 41 | } 42 | 43 | enableAnalytics() { 44 | instance.addRef(); 45 | } 46 | } 47 | 48 | const Instance = new Analytics(); 49 | Object.freeze(Instance); 50 | export default Analytics; 51 | -------------------------------------------------------------------------------- /src/scripts/components/Checkboxes.js: -------------------------------------------------------------------------------- 1 | import Utils from '../Utils'; 2 | import ProgressBar from './ProgressBar'; 3 | import Storage from './Storage'; 4 | import Notation from './Notation'; 5 | import Dropdown from './Dropdown'; 6 | 7 | let instance = null; 8 | /** 9 | * 10 | * 11 | * @class Checkboxes 12 | */ 13 | class Checkboxes { 14 | constructor() { 15 | if (!instance) { 16 | instance = this; 17 | } 18 | 19 | return instance; 20 | } 21 | 22 | checkItem(idNumber, section, sectionName, item, storage, type) { 23 | 24 | const storageData = storage; 25 | 26 | storageData.forEach(value => { 27 | const el = value; 28 | 29 | if (idNumber === el.id) { 30 | switch (type) { 31 | case 'state': 32 | el.state = 'checked'; 33 | break; 34 | 35 | case 'dropdown': 36 | el.dropdown = 'open'; 37 | break; 38 | 39 | case 'visible': 40 | el.visible = 'hide'; 41 | break; 42 | 43 | default: 44 | break; 45 | } 46 | } 47 | }); 48 | 49 | // Inject new array with new value 50 | storageData.forEach((el, i, arry) => { 51 | localStorage.setItem(sectionName, JSON.stringify(arry)); 52 | }); 53 | 54 | if (type !== 'visible') { 55 | // Count how many key in the localStorage array 56 | const nbrItemsChecked = JSON.parse( 57 | localStorage.getItem(sectionName), 58 | ).length; 59 | 60 | // Update progress bar 61 | new ProgressBar().updateProgressBar(section, nbrItemsChecked); 62 | item.setAttribute('data-item-check', 'true'); 63 | new Notation().updateNotation(); 64 | 65 | new Utils().visibityEl(item, '.c-tags', 'hide'); 66 | new Utils().visibityEl(item, '.js-dropdown', 'hide'); 67 | 68 | 69 | new Notation().updatePriority(); 70 | 71 | gtag('event', 'check', { 72 | 'event_category': 'Click', 73 | 'event_label': 'Item checked' 74 | }); 75 | 76 | if (item !== null) { 77 | const button = item.querySelector('.js-dropdown'); 78 | if (item.getAttribute('data-item-dropdown') === 'open') { 79 | new Dropdown().moveDropdown({button}); 80 | } 81 | } 82 | 83 | 84 | } 85 | // TODO: à refaire // const itemExpanded = new Utils().variableList(list.item); // if (itemExpanded.checklistItem.querySelectorAll('.c-checklist__details')[0].getAttribute('aria-expanded') === true) { // new Dropdown().moveDropdown({itemExpanded, force:true}); // } 86 | 87 | 88 | } 89 | uncheckItem(list) { 90 | let currentObj; 91 | if (list.itemId !== undefined) { 92 | const currentStorage = localStorage.getItem(list.sectionName); 93 | currentObj = JSON.parse(currentStorage); 94 | } 95 | currentObj.forEach((el, i) => { 96 | if (el.id === list.itemId) { 97 | // Remove element from current localStorage object 98 | currentObj.splice(i, 1); 99 | // Count how many key in the localStorage array 100 | localStorage.setItem(list.sectionName, JSON.stringify(currentObj)); 101 | 102 | // Update the progress bar 103 | const nbrItemsChecked = JSON.parse( 104 | localStorage.getItem(list.sectionName), 105 | ).length; 106 | 107 | new ProgressBar().updateProgressBar(list.section, nbrItemsChecked); 108 | new Notation().updateNotation(); 109 | new Utils().visibityEl(list.item, '.c-tags'); 110 | 111 | // Change data-check attribute to uncheck item 112 | new Utils().visibityEl(list.item, '.js-dropdown'); 113 | 114 | list.item.setAttribute('data-item-check', 'false'); 115 | 116 | new Notation().updatePriority(); 117 | 118 | gtag('event', 'uncheck', { 119 | 'event_category': 'Click', 120 | 'event_label': 'Item unchecked' 121 | }); 122 | } 123 | }); 124 | } 125 | checkboxDetection(el) { 126 | // List of all variables needed for checking item 127 | const list = new Utils().variableList(el); 128 | if (el.checked) { 129 | // Check item if unchecked 130 | new Storage().checkingItem(list); 131 | } else { 132 | // Uncheck item if checked 133 | instance.uncheckItem(list); 134 | } 135 | } 136 | enableCheckbox() { 137 | const allCheckboxes = document.querySelectorAll('input[type=checkbox]'); 138 | allCheckboxes.forEach(el => { 139 | // Add a click event listener on all checkboxes 140 | el.addEventListener('click', () => instance.checkboxDetection(el)); 141 | }); 142 | } 143 | } 144 | const Instance = new Checkboxes(); 145 | Object.freeze(Instance); 146 | export default Checkboxes; 147 | -------------------------------------------------------------------------------- /src/scripts/components/Dropdown.js: -------------------------------------------------------------------------------- 1 | import Ui from './Ui'; 2 | import Utils from '../Utils'; 3 | import postscribe from 'postscribe'; 4 | 5 | let instance = null; 6 | /** 7 | * 8 | * 9 | * @class Dropdown 10 | */ 11 | class Dropdown { 12 | constructor() { 13 | if (!instance) { 14 | instance = this; 15 | } 16 | 17 | return instance; 18 | } 19 | 20 | // never called directly 21 | dropdownIcon(btn) { 22 | const item = new Utils().getClosest(btn, '.js-item'); 23 | const ariaStatus = item.getAttribute('data-item-dropdown'); 24 | 25 | if (ariaStatus === 'open') { 26 | item.querySelector('.icon-arrow').classList.add('icon-rotate'); 27 | } else { 28 | item.querySelector('.icon-arrow').classList.remove('icon-rotate'); 29 | } 30 | } 31 | 32 | allDropdown(options) { 33 | 34 | const el = options; 35 | 36 | const section = new Utils().getClosest(el.btn, '.js-section'); 37 | const eachItem = section.querySelectorAll('.js-item[data-item-check="false"]'); 38 | 39 | eachItem.forEach(item => { 40 | if (el.btn.classList.value.includes('js-collapse-all') === true) { 41 | item.setAttribute('data-item-dropdown', 'close'); 42 | instance.removeCode(item); 43 | } else if (item.getAttributeNode('data-item-dropdown').value !== 'open') { 44 | item.setAttribute('data-item-dropdown', 'open'); 45 | instance.loadCode(item); 46 | 47 | new Ui().lazyLoadImg(item); 48 | } 49 | }); 50 | } 51 | 52 | removeCode(el) { 53 | const codeContainer = el.querySelector('.js-code'); 54 | const loader = el.querySelector('.js-loader'); 55 | 56 | if (codeContainer !== null) { 57 | while (codeContainer.hasChildNodes()) { 58 | codeContainer.removeChild(codeContainer.lastChild); 59 | } 60 | loader.style.display = 'inherit'; 61 | } 62 | } 63 | 64 | loadCode(el) { 65 | const codeContainer = el.querySelector('.js-code'); 66 | 67 | if (codeContainer !== null) { 68 | const scriptUrl = codeContainer.getAttribute('data-code-url'); 69 | 70 | let script = document.createElement('script'); 71 | script.src = scriptUrl; 72 | 73 | postscribe(codeContainer, script.outerHTML, { 74 | // eslint-disable-next-line func-names 75 | done: () => { 76 | const loader = el.querySelector('.js-loader'); 77 | loader.style.display = 'none'; 78 | }, 79 | }); 80 | } 81 | 82 | } 83 | 84 | moveDropdown(options) { 85 | const el = options; 86 | 87 | if (el.button !== null) { 88 | const eachItem = new Utils().getClosest(el.button, '.js-item'); 89 | const ariaStatus = eachItem.getAttribute('data-item-dropdown'); 90 | 91 | if (ariaStatus === 'open') { 92 | eachItem.setAttribute('data-item-dropdown', 'close'); 93 | instance.removeCode(eachItem); 94 | instance.dropdownIcon(el.button); 95 | gtag('event', 'close-dropdown', { 96 | 'event_category': 'Click', 97 | 'event_label': 'Close dropdown' 98 | }); 99 | } else if (ariaStatus === 'close') { 100 | eachItem.setAttribute('data-item-dropdown', 'open'); 101 | instance.dropdownIcon(el.button); 102 | instance.loadCode(eachItem); 103 | 104 | new Ui().lazyLoadImg(eachItem); 105 | 106 | gtag('event', 'open-dropdown', { 107 | 'event_category': 'Click', 108 | 'event_label': 'Open dropdown' 109 | }); 110 | } 111 | else {} 112 | } 113 | } 114 | 115 | collapseAllDropdown(options) { 116 | const el = options; 117 | 118 | const eachSection = new Utils().getClosest(el.button, '.js-section') || 119 | el.section; 120 | const itemsBySection = eachSection.querySelectorAll('.js-item'); 121 | 122 | itemsBySection.forEach(item => { 123 | item.setAttribute('data-item-dropdown', 'close'); 124 | }); 125 | 126 | // update local storage state dropdown hide 127 | } 128 | 129 | /** 130 | * Enable all dropdown buttons to be clickable 131 | * 132 | * @param {array} dropdownButtons 133 | * @memberof Dropdown 134 | */ 135 | enableDropdown(dropdownButtons) { 136 | dropdownButtons.forEach(btn => { 137 | btn.addEventListener( 138 | 'click', 139 | e => { 140 | e.preventDefault(); 141 | 142 | // If dropdown button clicked then close / open 143 | instance.moveDropdown({button: btn}); 144 | 145 | e.stopImmediatePropagation(); 146 | }, 147 | false, 148 | ); 149 | }); 150 | } 151 | } 152 | 153 | const Instance = new Dropdown(); 154 | Object.freeze(Instance); 155 | 156 | export default Dropdown; 157 | -------------------------------------------------------------------------------- /src/scripts/components/Filter.js: -------------------------------------------------------------------------------- 1 | let instance = null; 2 | /** 3 | * 4 | * 5 | * @class Filter 6 | */ 7 | class Filter { 8 | constructor() { 9 | if (!instance) { 10 | instance = this; 11 | } 12 | 13 | return instance; 14 | } 15 | 16 | highlightButton(nameFilter, type) { 17 | 18 | const button = document.querySelector( 19 | '[data-' + type + '="' + nameFilter + '"]', 20 | ); 21 | const buttons = document.querySelectorAll('[data-' + type + ']'); 22 | 23 | // Remove all filter-active class on all buttons 24 | buttons.forEach(el => { 25 | el.classList.remove('filter-active'); 26 | }) 27 | 28 | // Add class to highlight button 29 | button.classList.add('filter-active'); 30 | } 31 | 32 | addFilter(el, storageName) { 33 | let storage = []; 34 | const name = storageName; 35 | 36 | if (localStorage.getItem(name) === null) { 37 | storage = [{'filter': 'all'}]; 38 | } else { 39 | let item = {'filter': el}; 40 | storage.push(item); 41 | 42 | instance.highlightButton(item.filter, storageName); 43 | } 44 | 45 | localStorage.setItem(name, JSON.stringify(storage)); 46 | } 47 | 48 | selectBy(dataName) { 49 | let items = document.querySelectorAll('.js-item'); 50 | 51 | items.forEach((el, i) => { 52 | if ((' ' + items[i].className + ' ').indexOf(' ' + dataName + ' ') < 0) { 53 | items[i].setAttribute('data-item-visible', false); 54 | items[i].setAttribute('aria-hidden', true); 55 | } else { 56 | items[i].setAttribute('data-item-visible', true); 57 | items[i].setAttribute('aria-hidden', false); 58 | } 59 | }); 60 | } 61 | 62 | readFilterStorage(name) { 63 | 64 | let storage2 = [{'filter': 'all'}]; 65 | 66 | if (localStorage.getItem(name) !== null) { 67 | const storage = JSON.parse(localStorage.getItem(name)); 68 | 69 | storage.forEach(el => { 70 | instance.selectBy(el.filter); 71 | instance.highlightButton(el.filter, name); 72 | }) 73 | } 74 | else { 75 | localStorage.setItem(name, JSON.stringify(storage2)); 76 | instance.selectBy('all'); 77 | instance.highlightButton('all', 'tag'); 78 | } 79 | } 80 | 81 | enableFilter(name, dataName, filterClass) { 82 | const filterNames = document.querySelectorAll(filterClass); 83 | 84 | filterNames.forEach(el => { 85 | el.addEventListener( 86 | 'click', 87 | e => { 88 | e.preventDefault(); 89 | 90 | instance.selectBy(e.target.getAttribute(dataName)); 91 | instance.addFilter(e.target.getAttribute(dataName), name); 92 | }, 93 | false, 94 | ); 95 | }); 96 | } 97 | } 98 | 99 | const Instance = new Filter(); 100 | Object.freeze(Instance); 101 | 102 | export default Filter; 103 | -------------------------------------------------------------------------------- /src/scripts/components/Init.js: -------------------------------------------------------------------------------- 1 | import Filter from './Filter'; 2 | import Dropdown from './Dropdown'; 3 | import Storage from './Storage'; 4 | import Checkboxes from './Checkboxes'; 5 | import Notation from './Notation'; 6 | import Tools from './Tools'; 7 | import Report from './Report'; 8 | import Ui from './Ui'; 9 | import Analytics from './Analytics'; 10 | 11 | let instance = null; 12 | /** 13 | * 14 | * 15 | * @class Init 16 | */ 17 | class Init { 18 | constructor() { 19 | if (!instance) { 20 | instance = this; 21 | } 22 | 23 | this.sections = document.querySelectorAll('.js-section'); 24 | this.main = document.getElementById('js-main'); 25 | 26 | this.checkboxesInit(); 27 | this.dropdownInit(this.sections); 28 | this.filterInit(); 29 | this.itemsInit(this.sections); 30 | this.notationInit(); 31 | this.toolsInit(); 32 | this.reportInit(); 33 | this.uiInit(); 34 | this.AnalyticsInit(); 35 | 36 | return instance; 37 | } 38 | 39 | /** 40 | * Initialize all elements in the header which are used in the report 41 | * 42 | * @memberof Init 43 | */ 44 | reportInit() { 45 | new Report().enableReport(); 46 | } 47 | 48 | /** 49 | * Initialize Storage and load the content 50 | * 51 | * @memberof Init 52 | */ 53 | itemsInit(sections) { 54 | // Load all items by each section based on localStorage 55 | sections.forEach((section, i) => { 56 | new Storage().readItems(section, i, sections); 57 | }); 58 | // Hide sections based on the localStorage 59 | new Storage().readHideSections(); 60 | } 61 | 62 | /** 63 | * Initialize the notation 64 | * 65 | * @memberof Init 66 | */ 67 | notationInit() { 68 | // Initilize the progress bar for all sections 69 | new Notation().updateNotation(); 70 | 71 | new Notation().readPriority(); 72 | } 73 | 74 | /** 75 | * Initialize buttons 76 | * 77 | * @memberof Init 78 | */ 79 | toolsInit() { 80 | // Enable all buttons in the option section bar 81 | new Tools().enableTools(); 82 | } 83 | 84 | /** 85 | * Initialize Dropdown and collapse on load 86 | * 87 | * @memberof Init 88 | */ 89 | dropdownInit(sections) { 90 | // Enable each dropdown buttons 91 | new Dropdown().enableDropdown(document.querySelectorAll('.js-dropdown')); 92 | 93 | // Collapse on loading each item (except which present into localStorage) 94 | sections.forEach(section => { 95 | new Dropdown().collapseAllDropdown({section}); 96 | }); 97 | } 98 | 99 | /** 100 | * Initialize Checkboxes class 101 | * 102 | * @memberof Init 103 | */ 104 | checkboxesInit() { 105 | // Enable each checkboxe for all items 106 | new Checkboxes().enableCheckbox(); 107 | } 108 | 109 | /** 110 | * Initialize Filters 111 | * 112 | * @memberof Init 113 | */ 114 | filterInit() { 115 | new Filter().readFilterStorage('tag'); 116 | 117 | new Filter().enableFilter('tag', 'data-tag', '.js-filter-tag'); 118 | } 119 | 120 | /** 121 | * Initialize UI components 122 | * 123 | * @memberof Init 124 | */ 125 | uiInit() { 126 | new Ui().enableUi(); 127 | } 128 | 129 | /** 130 | * Add analytics utils 131 | * 132 | * @memberof Init 133 | */ 134 | AnalyticsInit() { 135 | new Analytics().enableAnalytics(); 136 | } 137 | } 138 | 139 | const Instance = new Init(); 140 | Object.freeze(Instance); 141 | 142 | export default Init; 143 | -------------------------------------------------------------------------------- /src/scripts/components/Notation.js: -------------------------------------------------------------------------------- 1 | let instance = null; 2 | /** 3 | * 4 | * 5 | * @class Notation 6 | */ 7 | class Notation { 8 | constructor() { 9 | if (!instance) { 10 | instance = this; 11 | } 12 | 13 | this.main = document.getElementById('js-main'); 14 | this.notationDetails = document.querySelector('.c-notation__details'); 15 | this.items = document.querySelectorAll('.js-item'); 16 | this.priority = []; 17 | 18 | return instance; 19 | } 20 | 21 | updatePriority() { 22 | this.priority.forEach((el, i) => { 23 | const checkedCounter = document.querySelectorAll(`[data-item-priority=${el}][data-item-check='true']`).length 24 | const number = document.querySelectorAll(`.js-detail-${el}`); 25 | number[0].querySelectorAll('.js-notation-checked')[0].innerHTML = checkedCounter 26 | }); 27 | } 28 | 29 | /** 30 | * Read the localStorage about the number of items by priority 31 | * 32 | * @param {any} priorityId 33 | * @memberof Notation 34 | */ 35 | readPriority(priorityId) { 36 | this.items.forEach((el, i) => { 37 | const currentPriority = el.getAttribute('data-item-priority'); 38 | if (this.priority.indexOf(currentPriority) === -1) { 39 | this.priority.push(currentPriority); 40 | } 41 | }) 42 | instance.updatePriority(); 43 | } 44 | 45 | /** 46 | * Update the letter for the global notation 47 | * 48 | * @memberof Notation 49 | */ 50 | updateNotation() { 51 | const items = this.main.querySelectorAll('.js-item').length; 52 | const mainCount = this.main.querySelectorAll('[data-item-check="true"]').length; 53 | 54 | const getPercent = parseInt(mainCount / items * 100, 10); 55 | 56 | const notation = document.getElementById('js-notation'); 57 | const notationLetter = notation.getElementsByClassName('c-notation__letter')[0]; 58 | 59 | notation.setAttribute('data-notation', getPercent); 60 | 61 | switch (true) { 62 | case getPercent <= 20: 63 | notationLetter.innerHTML = 'F'; 64 | break; 65 | 66 | case getPercent <= 40: 67 | notationLetter.innerHTML = 'E'; 68 | break; 69 | 70 | case getPercent <= 60: 71 | notationLetter.innerHTML = 'D'; 72 | break; 73 | 74 | case getPercent <= 80: 75 | notationLetter.innerHTML = 'C'; 76 | break; 77 | 78 | case getPercent < 100: 79 | notationLetter.innerHTML = 'B'; 80 | break; 81 | case getPercent === 100: 82 | notationLetter.innerHTML = 'A'; 83 | break; 84 | default: 85 | } 86 | } 87 | 88 | } 89 | 90 | const Instance = new Notation(); 91 | Object.freeze(Instance); 92 | 93 | export default Notation; 94 | -------------------------------------------------------------------------------- /src/scripts/components/ProgressBar.js: -------------------------------------------------------------------------------- 1 | let instance = null; 2 | /** 3 | * 4 | * 5 | * @class ProgressBar 6 | */ 7 | class ProgressBar { 8 | constructor() { 9 | if (!instance) { 10 | instance = this; 11 | } 12 | 13 | this.main = document.getElementById('js-main'); 14 | 15 | return instance; 16 | } 17 | 18 | /** 19 | * Count all items based on a section 20 | * 21 | * @param {any} el 22 | * @returns the number of items 23 | * @memberof ProgressBar 24 | */ 25 | allItemsCounter(el) { 26 | const nbrItems = el.querySelectorAll('.js-item').length; 27 | 28 | return nbrItems; 29 | } 30 | 31 | /** 32 | * Update the main progress bar in the top of the page 33 | * 34 | * @depends updateProgressBar 35 | * @memberof ProgressBar 36 | */ 37 | updateMainProgressBar() { 38 | const mainCount = this.main.querySelectorAll('[data-item-check="true"]').length; 39 | new ProgressBar().updateProgressBar(this.main, mainCount, 'main'); 40 | } 41 | 42 | /** 43 | * 44 | * 45 | * @param {any} section 46 | * @param {any} checkedItems 47 | * @param {any} type 48 | * @memberof ProgressBar 49 | */ 50 | updateProgressBar(section, checkedItems, type) { 51 | const currentSection = section; 52 | // Total items in the list 53 | // const checklistItem = section.querySelectorAll('.js-item'); 54 | const totalItems = instance.allItemsCounter(section); 55 | 56 | let progressBar; 57 | 58 | switch (type) { 59 | // Main progress bar 60 | case 'main': 61 | progressBar = section.querySelector('.js-all-progress'); 62 | break; 63 | 64 | default: 65 | progressBar = section.querySelector('.js-progress'); 66 | break; 67 | } 68 | 69 | const getPercent = parseInt(checkedItems / totalItems * 100, 10); 70 | 71 | progressBar.setAttribute('value', getPercent); 72 | 73 | currentSection.querySelector('.c-progress__label', 74 | ).innerHTML = `${getPercent} %`; 75 | 76 | // section.getAttribute('data-section') 77 | document.querySelectorAll( 78 | '#js-nav-' + section.getAttribute('data-section'), 79 | )[0].setAttribute('data-notation', getPercent); 80 | } 81 | 82 | /** 83 | * Update all progress bar, the main and on each section 84 | * 85 | * @param {any} section 86 | * @param {number} items 87 | * @memberof ProgressBar 88 | */ 89 | updateAllProgressBars(section, items) { 90 | new ProgressBar().updateProgressBar(section, items); 91 | // new ProgressBar().updateMainProgressBar(); 92 | } 93 | } 94 | 95 | const Instance = new ProgressBar(); 96 | Object.freeze(Instance); 97 | 98 | export default ProgressBar; 99 | -------------------------------------------------------------------------------- /src/scripts/components/Report.js: -------------------------------------------------------------------------------- 1 | let instance = null; 2 | /** 3 | * 4 | * 5 | * @class Report 6 | */ 7 | class Report { 8 | constructor() { 9 | if (!instance) { 10 | instance = this; 11 | } 12 | 13 | this.form = document.querySelector('.s-form'); 14 | this.inputs = this.form.querySelectorAll('input'); 15 | 16 | } 17 | 18 | readInputs() { 19 | this.inputs.forEach(el => { 20 | if (localStorage.getItem(el.name) !== null) { 21 | const storage = JSON.parse(localStorage.getItem(el.name)); 22 | el.value = storage[0] 23 | } 24 | else { 25 | localStorage.getItem(el.name); 26 | } 27 | }); 28 | } 29 | 30 | inputSave() { 31 | 32 | 33 | this.inputs.forEach(el => { 34 | 35 | // Save input value when the user reload the page 36 | window.addEventListener("beforeunload", e => { 37 | 38 | // let storage = []; 39 | // const valueInput = el.target.value; 40 | // const inputName = el.target.name; 41 | // // Push the input value to the storage array 42 | // storage.push(valueInput); 43 | // // Inject the new value typed in the localStorage 44 | // localStorage.setItem(inputName, JSON.stringify(storage)); 45 | }, false); 46 | 47 | // Save input value when change focus 48 | el.addEventListener('blur', e => { 49 | let storage = []; 50 | const valueInput = e.target.value; 51 | const inputName = e.target.name; 52 | // Push the input value to the storage array 53 | storage.push(valueInput); 54 | // Inject the new value typed in the localStorage 55 | localStorage.setItem(inputName, JSON.stringify(storage)); 56 | }); 57 | 58 | }); 59 | } 60 | 61 | resetAll() { 62 | document.querySelector('.js-reset-all').addEventListener('click', () => { 63 | const developerName = localStorage.getItem('developer-name'); 64 | const projectName = localStorage.getItem('project-name'); 65 | const pageTitle = localStorage.getItem('page-title'); 66 | localStorage.clear(); 67 | localStorage.setItem('developer-name', developerName); 68 | localStorage.setItem('project-name', projectName); 69 | localStorage.setItem('page-title', pageTitle); 70 | gtag('event', 'reset-all', { 71 | 'event_category': 'Click', 72 | 'event_label': 'Star new checklist' 73 | }); 74 | window.location.reload(false); 75 | }); 76 | } 77 | 78 | print() { 79 | document.querySelector('.js-print').addEventListener('click', () => { 80 | window.print(); 81 | gtag('event', 'generate-reports', { 82 | 'event_category': 'Click', 83 | 'event_label': 'Generate reports' 84 | }); 85 | }); 86 | } 87 | 88 | enableReport() { 89 | instance.print(); 90 | instance.resetAll(); 91 | instance.inputSave(); 92 | instance.readInputs(this.inputs); 93 | 94 | } 95 | } 96 | const Instance = new Report(); 97 | Object.freeze(Instance); 98 | 99 | export default Report; 100 | -------------------------------------------------------------------------------- /src/scripts/components/Storage.js: -------------------------------------------------------------------------------- 1 | import Utils from '../Utils'; 2 | import ProgressBar from './ProgressBar'; 3 | import Checkboxes from './Checkboxes'; 4 | 5 | let instance = null; 6 | /** 7 | * 8 | * 9 | * @class Storage 10 | */ 11 | class Storage { 12 | constructor() { 13 | if (!instance) { 14 | instance = this; 15 | } 16 | 17 | this.body = document.querySelectorAll('[data-section-id]'); 18 | 19 | return instance; 20 | } 21 | 22 | checkingOption(list, keyName) { 23 | let storage = []; 24 | 25 | if (localStorage.getItem(keyName) === null) { 26 | storage = []; 27 | } else { 28 | // Parse the serialized data back into an aray of objects 29 | storage = JSON.parse(localStorage.getItem(keyName)); 30 | } 31 | 32 | if (storage === []) { 33 | storage.forEach(el => { 34 | if (el.id === list.sectionId && list.itemId === null) { 35 | new Checkboxes().checkItem( 36 | list.sectionId, 37 | list.section, 38 | keyName, 39 | list.item, 40 | storage, 41 | 'visible', 42 | ); 43 | } else { 44 | console.log('error'); 45 | } 46 | }); 47 | } else { 48 | if (list.itemId === undefined) { 49 | new Storage().addItemStorage(list.sectionId, storage, keyName); 50 | 51 | new Checkboxes().checkItem( 52 | list.sectionId, 53 | list.section, 54 | keyName, 55 | list.item, 56 | storage, 57 | 'visible', 58 | ); 59 | } else { 60 | console.log('error'); 61 | } 62 | } 63 | } 64 | 65 | checkingItem(list) { 66 | let storage = []; 67 | 68 | if (list.item !== null) { 69 | if (localStorage.getItem(list.sectionName) === null) { 70 | storage = []; 71 | } else { 72 | // Parse the serialized data back into an aray of objects 73 | storage = JSON.parse(localStorage.getItem(list.sectionName)); 74 | } 75 | } else { 76 | console.log('error'); 77 | } 78 | 79 | if (storage === []) { 80 | storage.forEach(el => { 81 | if (el.id === list.itemId) { 82 | new Checkboxes().checkItem( 83 | list.itemId, 84 | list.section, 85 | list.sectionName, 86 | list.item, 87 | storage, 88 | 'state', 89 | ); 90 | } else { 91 | console.log('error'); 92 | } 93 | }); 94 | } else { 95 | if (list.itemId !== undefined) { 96 | new Storage().addItemStorage(list.itemId, storage, list.sectionName); 97 | 98 | new Checkboxes().checkItem( 99 | list.itemId, 100 | list.section, 101 | list.sectionName, 102 | list.item, 103 | storage, 104 | 'state', 105 | ); 106 | } else { 107 | console.log('error'); 108 | } 109 | } 110 | } 111 | 112 | loadItems(checklistItem, currentObj) { 113 | let listItem = checklistItem; 114 | let ids = []; 115 | 116 | currentObj.map(el => { 117 | return ids.push(el.id); 118 | }); 119 | 120 | listItem.forEach(el => { 121 | if (ids.indexOf(el.getAttribute('data-item-id')) > -1) { 122 | el.setAttribute('data-item-check', 'true'); 123 | 124 | const checkObj = el.querySelectorAll('input[type=checkbox]'); 125 | 126 | document.getElementById(checkObj[0].id).checked = true; 127 | 128 | new Utils().visibityEl(el, '.c-tags', 'hide'); 129 | new Utils().visibityEl(el, '.js-dropdown', 'hide'); 130 | } 131 | }); 132 | } 133 | 134 | readItems(el, i, sections) { 135 | const sectionName = el.getAttribute('data-section'); 136 | 137 | if ( 138 | localStorage.getItem(sectionName) !== null || 139 | localStorage.getItem(sectionName) === undefined 140 | ) { 141 | const currentStorage = localStorage.getItem(sectionName); 142 | const currentObj = JSON.parse(currentStorage); 143 | const checkedItems = currentObj.length; 144 | const itemsBySection = el.querySelectorAll('.js-item'); 145 | 146 | instance.loadItems(itemsBySection, currentObj); 147 | 148 | // Update progress bars 149 | new ProgressBar().updateProgressBar(sections[i], checkedItems); 150 | } 151 | } 152 | 153 | readHideSections() { 154 | let storage; 155 | 156 | if (localStorage.getItem('hide-sections') !== null) { 157 | // Parse the serialized data back into an aray of objects 158 | storage = JSON.parse(localStorage.getItem('hide-sections')); 159 | 160 | this.body.forEach(element => { 161 | storage.forEach(el => { 162 | if (element.getAttribute('data-section-id') === el.id) { 163 | element.querySelectorAll( 164 | '.js-checklist-body', 165 | )[0].setAttribute('data-body-visibility', 'hide'); 166 | element.querySelectorAll( 167 | '.js-checklist-body', 168 | )[0].setAttribute('aria-hidden', 'true'); 169 | element.querySelectorAll( 170 | '.js-hide-section', 171 | )[0].querySelector('.icon-eye').classList.add('icon-eye-hide'); 172 | 173 | element.querySelectorAll( 174 | '.js-hide-section', 175 | )[0].classList.add('is-active'); 176 | } 177 | }); 178 | }); 179 | } 180 | } 181 | 182 | /** 183 | * Add new item into the localStorage 184 | * 185 | * @param {number} itemId 186 | * @param {any} storage 187 | * @param {string} keyName 188 | * @memberof Storage 189 | */ 190 | addItemStorage(itemId, storage, keyName) { 191 | for (let index = 0; index < storage.length; index+=1) { 192 | let value = storage[index]; 193 | if (value.id === itemId) { 194 | return; 195 | } 196 | } 197 | const newItem = {id: itemId}; 198 | 199 | storage.push(newItem); 200 | 201 | // Re-serialize the array back into a string and store it in localStorage 202 | localStorage.setItem(keyName, JSON.stringify(storage)); 203 | } 204 | } 205 | 206 | const Instance = new Storage(); 207 | Object.freeze(Instance); 208 | 209 | export default Storage; 210 | -------------------------------------------------------------------------------- /src/scripts/components/Tools.js: -------------------------------------------------------------------------------- 1 | import Utils from '../Utils'; 2 | import Dropdown from './Dropdown'; 3 | import Checkboxes from './Checkboxes'; 4 | import Storage from './Storage'; 5 | 6 | let instance = null; 7 | /** 8 | * 9 | * 10 | * @class Tools 11 | */ 12 | class Tools { 13 | constructor() { 14 | if (!instance) { 15 | instance = this; 16 | } 17 | 18 | this.checkAllButton = document.querySelectorAll('.js-check-all'); 19 | this.uncheckAllButton = document.querySelectorAll('.js-uncheck-all'); 20 | 21 | this.collapseAllButton = document.querySelectorAll('.js-collapse-all'); 22 | this.expandAllButton = document.querySelectorAll('.js-expand-all'); 23 | 24 | this.hideSectionButton = document.querySelectorAll('.js-hide-section'); 25 | 26 | return instance; 27 | } 28 | 29 | /** 30 | * Chech or uncheck all items in a specific section 31 | * 32 | * @param {any} btn 33 | * @param {string} state 34 | * @memberof Tools 35 | */ 36 | checkUncheckAll(btn, state) { 37 | const list = new Utils().variableList(btn); 38 | const checklistItem = list.section.querySelectorAll('.js-item'); 39 | 40 | checklistItem.forEach(el => { 41 | if (state === 'true') { 42 | // if element is not already checked 43 | if (el.getAttribute('data-item-check') !== 'true') { 44 | // Change each item state 45 | el.setAttribute('data-item-check', state); 46 | 47 | const list2 = new Utils().variableList(el); 48 | 49 | new Storage().checkingItem(list2); 50 | } 51 | } else { 52 | // Change each item state 53 | el.setAttribute('data-item-check', state); 54 | 55 | const list2 = new Utils().variableList(el); 56 | 57 | new Checkboxes().uncheckItem(list2); 58 | } 59 | }); 60 | } 61 | 62 | uncheckOption(list, keyName) { 63 | let currentObj; 64 | 65 | if (list.itemId === undefined) { 66 | const currentStorage = localStorage.getItem(keyName); 67 | currentObj = JSON.parse(currentStorage); 68 | } 69 | 70 | currentObj.forEach((el, i) => { 71 | if (el.id === list.sectionId) { 72 | // Remove element from current localStorage object 73 | currentObj.splice(i, 1); 74 | 75 | localStorage.setItem(keyName, JSON.stringify(currentObj)); 76 | } 77 | }); 78 | } 79 | 80 | /** 81 | * Hide the body 82 | * 83 | * @param {any} btn 84 | * @memberof Tools 85 | */ 86 | hideShow(btn) { 87 | const list = new Utils().variableList(btn); 88 | const body = list.section.querySelectorAll('.js-checklist-body')[0]; 89 | 90 | if (body !== undefined) { 91 | const status = body.getAttribute('data-body-visibility'); 92 | 93 | if (status === 'visible') { 94 | 95 | btn.classList.add('is-active'); 96 | 97 | btn.querySelector('.icon-eye').classList.add('icon-eye-hide'); 98 | 99 | body.setAttribute('data-body-visibility', 'hide'); 100 | body.setAttribute('aria-hidden', 'true'); 101 | 102 | new Storage().checkingOption(list, 'hide-sections'); 103 | 104 | 105 | } else { 106 | 107 | btn.classList.remove('is-active'); 108 | 109 | btn.querySelector('.icon-eye').classList.remove('icon-eye-hide'); 110 | 111 | body.setAttribute('data-body-visibility', 'visible'); 112 | body.setAttribute('aria-hidden', 'false'); 113 | 114 | instance.uncheckOption(list, 'hide-sections'); 115 | } 116 | } 117 | } 118 | 119 | enableTools() { 120 | // Check all checkboxes 121 | this.checkAllButton.forEach(btn => { 122 | btn.addEventListener( 123 | 'click', 124 | () => { 125 | instance.checkUncheckAll(btn, 'true'); 126 | } 127 | ); 128 | }); 129 | 130 | // Uncheck all checkboxes 131 | this.uncheckAllButton.forEach(btn => { 132 | btn.addEventListener( 133 | 'click', 134 | () => { 135 | instance.checkUncheckAll(btn, 'false'); 136 | } 137 | ); 138 | }); 139 | 140 | // Collapse all dropdowns 141 | this.collapseAllButton.forEach(btn => { 142 | btn.addEventListener( 143 | 'click', 144 | () => { 145 | new Dropdown().allDropdown({btn}); 146 | } 147 | ); 148 | }); 149 | 150 | // Expand all dropdowns 151 | this.expandAllButton.forEach(btn => { 152 | btn.addEventListener( 153 | 'click', 154 | () => { 155 | new Dropdown().allDropdown({btn}); 156 | } 157 | ); 158 | }); 159 | 160 | // Hide section 161 | this.hideSectionButton.forEach(btn => { 162 | btn.addEventListener( 163 | 'click', 164 | () => { 165 | instance.hideShow(btn); 166 | } 167 | ); 168 | }); 169 | } 170 | } 171 | 172 | const Instance = new Tools(); 173 | Object.freeze(Instance); 174 | 175 | export default Tools; 176 | // enableReset() { 177 | // const uncheckAll = document.querySelectorAll(".js-uncheck-section"); 178 | // const checkAll = document.querySelectorAll(".js-check-section"); 179 | // uncheckAll.forEach((el, i, array) => { 180 | // el.addEventListener('click', e => { 181 | // e.preventDefault(); 182 | // const section = new Utils().getClosest(el, '.js-section'); 183 | // const sectionName = section.getAttribute('data-section'); 184 | // const checklistItem = new Utils().getClosest(el, '.js-item '); 185 | // localStorage.removeItem(sectionName); 186 | // instance.resetItems(section, sectionName); 187 | // }); 188 | // }); 189 | // checkAll.forEach((el, i, array) => { 190 | // el.addEventListener('click', e => { 191 | // e.preventDefault(); 192 | // const section = new Utils().getClosest(el, '.js-section'); 193 | // const sectionName = section.getAttribute('data-section'); 194 | // const checklistItem = new Utils().getClosest(el, '.js-item '); 195 | // localStorage.addItem(sectionName); 196 | // }); 197 | // }); 198 | // } 199 | -------------------------------------------------------------------------------- /src/scripts/components/Ui.js: -------------------------------------------------------------------------------- 1 | let instance = null; 2 | 3 | class Ui { 4 | constructor() { 5 | if (!instance) { 6 | instance = this; 7 | } 8 | 9 | this.nav = document.querySelector('.c-nav'); 10 | this.scrollToggle = document.querySelectorAll('.js-scroll'); 11 | 12 | return instance; 13 | } 14 | 15 | lazyLoadImg(item) { 16 | [].forEach.call(item.querySelectorAll('img[data-src]'), (img) => { 17 | img.setAttribute('src', img.getAttribute('data-src')); 18 | img.onload = function() { 19 | img.removeAttribute('data-src'); 20 | }; 21 | }); 22 | } 23 | 24 | isScrolledIntoView(el) { 25 | var elemTop = el.getBoundingClientRect().top; 26 | var elemBottom = el.getBoundingClientRect().bottom; 27 | 28 | // Only completely visible elements return true: 29 | var isVisible = elemTop >= 0 && elemBottom <= window.innerHeight; 30 | // Partially visible elements return true: 31 | // isVisible = elemTop < window.innerHeight && elemBottom >= 0; 32 | return isVisible; 33 | } 34 | 35 | smoothScroll(anchor, duration) { 36 | // Calculate how far and how fast to scroll 37 | const startLocation = window.pageYOffset; 38 | const endLocation = anchor.offsetTop + 300; 39 | const distance = endLocation - startLocation; 40 | const increments = distance / (duration / 16); 41 | let stopAnimation; 42 | 43 | // Scroll the page by an increment, and check if it's time to stop 44 | const animateScroll = () => { 45 | window.scrollBy(0, increments); 46 | stopAnimation(); 47 | }; 48 | 49 | // Loop the animation function 50 | const runAnimation = setInterval(animateScroll, 16); 51 | 52 | // If scrolling down 53 | if (increments >= 0) { 54 | // Stop animation when you reach the anchor OR the bottom of the page 55 | stopAnimation = () => { 56 | const travelled = window.pageYOffset; 57 | if ( (travelled >= (endLocation - increments)) || ((window.innerHeight + travelled) >= document.body.offsetHeight) ) { 58 | clearInterval(runAnimation); 59 | } 60 | }; 61 | } 62 | // If scrolling up 63 | else { 64 | // Stop animation when you reach the anchor OR the top of the page 65 | stopAnimation = () => { 66 | const travelled = window.pageYOffset; 67 | if ( travelled <= (endLocation || 0) ) { 68 | clearInterval(runAnimation); 69 | } 70 | }; 71 | } 72 | } 73 | 74 | scrollAnchor() { 75 | 76 | // For each smooth scroll link 77 | this.scrollToggle.forEach(toggle => { 78 | // When the smooth scroll link is clicked 79 | toggle.addEventListener('click', e => { 80 | // Prevent the default link behavior 81 | e.preventDefault(); 82 | // Get anchor link and calculate distance from the top 83 | const dataID = toggle.getAttribute('href'); 84 | const dataTarget = document.querySelector(dataID); 85 | const dataSpeed = toggle.getAttribute('data-speed'); 86 | // If the anchor exists 87 | if (dataTarget) { 88 | // Scroll to the anchor 89 | instance.smoothScroll(dataTarget, dataSpeed || 3000); 90 | 91 | // Push new state history to dynamically change the URL 92 | if(history.pushState) { 93 | history.pushState(null, null, '/'+dataID); 94 | } 95 | else { 96 | location.hash = '/'+dataID; 97 | } 98 | } 99 | }, false); 100 | }); 101 | } 102 | 103 | navScroll() { 104 | 105 | let lastScrollPosition = 0; 106 | let ticking = false; 107 | 108 | window.addEventListener( 109 | 'scroll', 110 | () => { 111 | 112 | lastScrollPosition = window.scrollY; 113 | 114 | if (!ticking) { 115 | 116 | window.requestAnimationFrame(() => { 117 | 118 | // Execute only on desktop and when the menu is already on the side 119 | if (window.innerWidth > 1265) { 120 | if ( 121 | instance.isScrolledIntoView(this.nav) === 122 | false 123 | ) { 124 | this.nav.classList.add('c-nav__sidebar'); 125 | } else { 126 | this.nav.classList.remove('c-nav__sidebar'); 127 | } 128 | } 129 | 130 | ticking = false; 131 | }); 132 | 133 | ticking = true; 134 | } 135 | 136 | }, {passive: true}); 137 | } 138 | 139 | enableUi() { 140 | // instance.scrollAnchor(); 141 | instance.navScroll(); 142 | } 143 | } 144 | 145 | const Instance = new Ui(); 146 | Object.freeze(Instance); 147 | 148 | export default Ui; 149 | -------------------------------------------------------------------------------- /src/scripts/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Init from './components/Init'; 4 | 5 | document.addEventListener('DOMContentLoaded', () => new Init()); 6 | -------------------------------------------------------------------------------- /src/service-worker.js: -------------------------------------------------------------------------------- 1 | // importScripts('/cache-polyfill.js'); 2 | 'use strict'; 3 | 4 | var version = 'v1::'; 5 | 6 | self.addEventListener("install", function(event) { 7 | // console.log('WORKER: install event in progress.'); 8 | event.waitUntil( 9 | /* The caches built-in is a promise-based API that helps you cache responses, 10 | as well as finding and deleting them. 11 | */ 12 | caches 13 | /* You can open a cache by name, and this method returns a promise. We use 14 | a versioned cache name here so that we can remove old cache entries in 15 | one fell swoop later, when phasing out an older service worker. 16 | */ 17 | .open(version + 'fundamentals') 18 | .then(function(cache) { 19 | /* After the cache is opened, we can fill it with the offline fundamentals. 20 | The method below will add all resources we've indicated to the cache, 21 | after making HTTP requests for each of them. 22 | */ 23 | return cache.addAll([ 24 | '/', 25 | '/index.html', 26 | '/styles/main.min.css', 27 | '/scripts/app.bundle.js' 28 | ]); 29 | }) 30 | .then(function() { 31 | // console.log('WORKER: install completed'); 32 | }) 33 | ); 34 | }); 35 | -------------------------------------------------------------------------------- /src/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | https://cfnchecklist.com/ 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/styles/base/_fonts.scss: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // String Replace 3 | // ============================================================================= 4 | 5 | @function str-replace($string, $search, $replace: '') { 6 | $index: str-index($string, $search); 7 | 8 | @if $index { 9 | @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace); 10 | } 11 | 12 | @return $string; 13 | } 14 | // ============================================================================= 15 | // Font Face 16 | // ============================================================================= 17 | @mixin font-face($name, $path, $weight: null, $style: null, $exts: eot woff2 woff ttf svg) { 18 | $src: null; 19 | $extmods: (eot: '?', svg: '#' + str-replace($name, ' ', '_')); 20 | $formats: (otf: 'opentype', ttf: 'truetype'); 21 | 22 | @each $ext in $exts { 23 | $extmod: if(map-has-key($extmods, $ext), $ext + map-get($extmods, $ext), $ext); 24 | $format: if(map-has-key($formats, $ext), map-get($formats, $ext), $ext); 25 | $src: append($src, url(quote($path + '.' + $extmod)) format(quote($format)), comma); 26 | } 27 | 28 | @font-face { 29 | font-family: quote($name); 30 | font-style: $style; 31 | font-weight: $weight; 32 | src: $src; 33 | } 34 | } 35 | 36 | // @include font-face(Samplinal, fonts/Samplinal, 500, normal, eot woff2 woff); 37 | -------------------------------------------------------------------------------- /src/styles/base/_form.scss: -------------------------------------------------------------------------------- 1 | fieldset { 2 | border: 0; 3 | margin: 0; 4 | } 5 | 6 | label:hover { 7 | cursor: pointer; 8 | } 9 | 10 | .s-form { 11 | margin: 10px 0 20px; 12 | 13 | @include mq('handheld-and-up') { 14 | margin-bottom: 20px; 15 | } 16 | } 17 | 18 | .form { 19 | input { 20 | font-size: 1.5rem; 21 | padding: 5px; 22 | display: block; 23 | border: 0; 24 | width: 100%; 25 | border-radius: 0; 26 | border-bottom: 1px solid #757575; 27 | 28 | @include mq('handheld-and-up') { 29 | width: 300px; 30 | } 31 | 32 | &:focus { 33 | outline: none; 34 | } 35 | 36 | &:focus ~ label, 37 | &:valid ~ label { 38 | top: -15px; 39 | font-size: 12px; 40 | color: map-get($colors, $primary); 41 | } 42 | } 43 | 44 | label { 45 | color: #999999; 46 | font-size: 14px; 47 | font-weight: normal; 48 | position: absolute; 49 | pointer-events: none; 50 | left: 5px; 51 | top: 0; 52 | transition: .2s ease all; 53 | } 54 | } 55 | 56 | .form-group { 57 | position: relative; 58 | margin: 20px 0; 59 | 60 | &:last-of-type { 61 | margin: 20px 0 0; 62 | } 63 | } 64 | 65 | .label__title { 66 | font-weight: bold; 67 | display: inline; 68 | } 69 | 70 | label, 71 | .nav li a, 72 | .nav li input { 73 | transition: background-color .08s ease-in-out; 74 | } 75 | -------------------------------------------------------------------------------- /src/styles/base/_generic.scss: -------------------------------------------------------------------------------- 1 | $body-background-color: #ededed !default; 2 | $body-size: 62.5% !default; 3 | $body-rendering: optimizeLegibility !default; 4 | $body-family: $family-sans-serif !default; 5 | $body-color: $text !default; 6 | $body-weight: $weight-normal !default; 7 | $body-line-height: 1.5 !default; 8 | 9 | // $hr-background-color: $border !default; 10 | $hr-height: 1px !default; 11 | $hr-margin: 1.5rem 0 !default; 12 | $strong-color: $text-strong !default; 13 | $strong-weight: $weight-bold !default; 14 | 15 | // Set box-sizing globally to handle padding and border widths 16 | *, 17 | *::after, 18 | *::before { 19 | box-sizing: inherit; 20 | } 21 | 22 | // The base font-size is set at 62.5% for having the convenience 23 | // of sizing rems in a way that is similar to using px: 1.6rem = 16px 24 | html { 25 | background-color: $body-background-color; 26 | min-width: 100%; 27 | overflow-x: hidden; 28 | overflow-y: scroll; 29 | box-sizing: border-box; 30 | font-size: $body-size; 31 | text-rendering: $body-rendering; 32 | -moz-osx-font-smoothing: grayscale; 33 | -webkit-font-smoothing: antialiased; 34 | scroll-behavior: smooth; 35 | } 36 | 37 | // Default body styles 38 | body { 39 | color: $body-color; 40 | font-size: 1rem; 41 | font-weight: $body-weight; 42 | line-height: $body-line-height; 43 | font-family: $family-sans-serif; 44 | margin: 0; 45 | } 46 | -------------------------------------------------------------------------------- /src/styles/base/_headings.scss: -------------------------------------------------------------------------------- 1 | .h1, 2 | .h2, 3 | .h3, 4 | .h4, 5 | .h5, 6 | .h6, 7 | h1, 8 | h2, 9 | h3, 10 | h4, 11 | h5, 12 | h6 { 13 | margin-bottom: .5rem; 14 | font-family: inherit; 15 | font-weight: 500; 16 | line-height: 1.2; 17 | color: inherit; 18 | } 19 | 20 | h1, 21 | .h1 { 22 | margin: 0; 23 | padding: 15px 0 0; 24 | font-weight: 700; 25 | font-size: 2.5rem; 26 | 27 | @include mq('handheld-and-up') { 28 | font-size: 3rem; 29 | } 30 | 31 | @include mq('print') { 32 | font-size: 18pt; 33 | padding: 0 0 5px; 34 | } 35 | } 36 | 37 | h2, 38 | .h2 { 39 | font-size: 2rem; 40 | } 41 | 42 | h3, 43 | .h3 { 44 | font-size: 1.2rem; 45 | font-weight: 700; 46 | 47 | @include mq('handheld-and-up') { 48 | font-size: 1.3rem; 49 | } 50 | } 51 | 52 | h4, 53 | .h4 { 54 | font-size: 1.2rem; 55 | font-weight: 700; 56 | 57 | @include mq('handheld-and-up') { 58 | font-size: 1.3rem; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/styles/base/_icons.scss: -------------------------------------------------------------------------------- 1 | .icon { 2 | width: 18px; 3 | height: 18px; 4 | transition: all 200ms ease-in 0s; 5 | fill: map-get($colors, grey-light); 6 | display: inline-block; 7 | text-align: center; 8 | speak: none; 9 | backface-visibility: hidden; 10 | opacity: 1; 11 | 12 | @include mq('handheld-and-up') { 13 | width: 15px; 14 | height: 15px; 15 | } 16 | } 17 | 18 | button, 19 | .button { 20 | .icon { 21 | margin: 0 .7rem 0 -.2rem; 22 | } 23 | } 24 | 25 | .icon--small { 26 | width: 7px; 27 | height: 7px; 28 | } 29 | 30 | .icon-checkbox { 31 | .path-check { 32 | display: none; 33 | } 34 | } 35 | 36 | .icon-uncheck { 37 | .path-check { 38 | display: none; 39 | } 40 | } 41 | 42 | .icon-checkbox, 43 | .icon-checked { 44 | align-self: flex-start; 45 | width: 11px; 46 | height: 11px; 47 | fill: $primary; 48 | opacity: 1; 49 | 50 | @include mq('handheld-and-up') { 51 | width: 15px; 52 | height: 15px; 53 | } 54 | 55 | [data-item-check='false'] & { 56 | display: block; 57 | } 58 | 59 | [data-item-check='true'] & { 60 | display: none; 61 | } 62 | } 63 | 64 | .icon-checked { 65 | fill: map-get($colors, black); 66 | opacity: .1; 67 | 68 | [data-item-check='false'] & { 69 | display: none; 70 | } 71 | 72 | [data-item-check='true'] & { 73 | display: block; 74 | } 75 | } 76 | 77 | .icon-arrow { 78 | width: 15px; 79 | height: 15px; 80 | position: absolute; 81 | top: 10px; 82 | fill: map-get($colors, black); 83 | opacity: .2; 84 | } 85 | 86 | .icon-rotate { 87 | transform: rotate(180deg); 88 | transition: all 300ms ease-in 0s; 89 | } 90 | 91 | .icon-priority { 92 | &--high { 93 | fill: #f85f5f; 94 | align-self: flex-start; 95 | display: block; 96 | width: 7px; 97 | height: 7px; 98 | 99 | @include mq(lap-and-up) { 100 | margin-top: 0; 101 | width: 7px; 102 | height: 7px; 103 | } 104 | } 105 | 106 | &--medium { 107 | fill: #f2c741; 108 | align-self: flex-start; 109 | display: block; 110 | width: 7px; 111 | height: 7px; 112 | 113 | @include mq(lap-and-up) { 114 | margin-top: 0; 115 | width: 7px; 116 | height: 7px; 117 | } 118 | } 119 | 120 | &--low { 121 | fill: #62c547; 122 | align-self: flex-start; 123 | display: block; 124 | width: 7px; 125 | height: 7px; 126 | 127 | @include mq(lap-and-up) { 128 | margin-top: 0; 129 | width: 7px; 130 | height: 7px; 131 | } 132 | } 133 | } 134 | 135 | .btn--danger { 136 | border-color: #df5941; 137 | color: #df5941; 138 | 139 | .icon { 140 | fill: #df5941; 141 | } 142 | } 143 | 144 | @keyframes bounce { 145 | 0% { 146 | transform: scaleX(1); 147 | } 148 | 149 | 10% { 150 | transform: scaleX(1); 151 | } 152 | 153 | 20% { 154 | transform: scaleX(.8); 155 | } 156 | 157 | 35% { 158 | transform: scaleX(.8); 159 | } 160 | 161 | 45% { 162 | transform: scaleX(1); 163 | } 164 | 165 | 60% { 166 | transform: scaleX(1); 167 | } 168 | 169 | 75% { 170 | transform: scaleX(.8); 171 | } 172 | 173 | 85% { 174 | transform: scaleX(.8); 175 | } 176 | 177 | 100% { 178 | transform: scaleX(1); 179 | } 180 | } 181 | 182 | .code-icons { 183 | 184 | // height: 100%; 185 | margin: 0 auto; 186 | position: relative; 187 | width: 25px; 188 | 189 | &::after, 190 | &::before { 191 | box-sizing: content-box; 192 | content: ''; 193 | height: 10px; 194 | position: absolute; 195 | top: 9px; 196 | transform: rotate(45deg); 197 | width: 10px; 198 | } 199 | 200 | &::before { 201 | border-bottom: 3px solid #cccccc; 202 | border-left: 3px solid #cccccc; 203 | left: 0; 204 | } 205 | 206 | &::after { 207 | border-right: 3px solid #cccccc; 208 | border-top: 3px solid #cccccc; 209 | right: 0; 210 | } 211 | } 212 | 213 | .icon-eye { 214 | path:nth-child(2), 215 | path:nth-child(3) { 216 | display: none; 217 | } 218 | 219 | &-hide { 220 | fill: #415257; 221 | 222 | path:nth-child(2), 223 | path:nth-child(3) { 224 | display: block; 225 | } 226 | } 227 | } 228 | 229 | 230 | 231 | .st0 { 232 | fill: map-get($colors, white); 233 | } 234 | -------------------------------------------------------------------------------- /src/styles/base/_links.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Links 3 | // 4 | 5 | // Link colors 6 | $link: map-get($colors, primary) !default; 7 | $link-invert: map-get($colors, primary-invert) !default; 8 | $link-visited: map-get($colors, purple) !default; 9 | $link-hover: map-get($colors, grey-darker) !default; 10 | $link-hover-border: map-get($colors, grey-light) !default; 11 | $link-focus: map-get($colors, grey-darker) !default; 12 | $link-focus-border: map-get($colors, primary) !default; 13 | $link-active: map-get($colors, grey-darker) !default; 14 | $link-active-border: map-get($colors, grey-dark) !default; 15 | $link-color: $primary !default; 16 | $link-brand-color: #f40057 !default; 17 | $link-info-color: $info !default; 18 | $link-warning-color: $warning !default; 19 | $link-success-color: $success !default; 20 | $link-error-color: $danger !default; 21 | $link-text-decoration: none !default; 22 | $link-hover-text-decoration: underline !default; 23 | 24 | @mixin link--color($color: $link-color) { 25 | &:not(:disabled) { 26 | background-color: transparent; 27 | color: $link-color; 28 | // &:visited { 29 | // color: darken($color, $color-tint); 30 | // } 31 | 32 | &:hover { 33 | background-color: transparent; 34 | color: lighten($color, $color-tint); 35 | } 36 | 37 | &:active { 38 | background-color: transparent; 39 | color: lighten($color, $color-tint); 40 | } 41 | } 42 | } 43 | 44 | a, 45 | .c-link { 46 | @include link--color; 47 | 48 | text-decoration: $link-text-decoration; 49 | cursor: pointer; 50 | 51 | &:hover { 52 | text-decoration: $link-hover-text-decoration; 53 | } 54 | } 55 | 56 | .a-link--info { 57 | @include link--color($link-info-color); 58 | } 59 | 60 | .c-link--warning { 61 | @include link--color($link-warning-color); 62 | } 63 | 64 | .c-link--success { 65 | @include link--color($link-success-color); 66 | } 67 | 68 | .c-link--error { 69 | @include link--color($link-error-color); 70 | } 71 | 72 | // a:not( [href*='localhost:3000'] ):not( [href^='#'] ):not( [href^='/'] ):after { 73 | // content: " (external)"; 74 | // } 75 | -------------------------------------------------------------------------------- /src/styles/base/_media.scss: -------------------------------------------------------------------------------- 1 | .media { 2 | align-items: flex-start; 3 | display: flex; 4 | text-align: left; 5 | } 6 | 7 | .img-logo { 8 | width: 85px; 9 | height: 85px; 10 | 11 | @include mq('handheld-and-up') { 12 | width: 100px; 13 | height: 100px; 14 | } 15 | } 16 | 17 | // frameborder="0" 18 | // allowtransparency="true" 19 | // scrolling="no" 20 | iframe { 21 | background: transparent; 22 | margin: 0; 23 | padding: 0; 24 | border: 0; 25 | } 26 | -------------------------------------------------------------------------------- /src/styles/base/_print.scss: -------------------------------------------------------------------------------- 1 | @media print { 2 | @page { 3 | margin: 20px 15px; 4 | } 5 | 6 | body { 7 | margin: 0; 8 | overflow: hidden; 9 | position: relative; 10 | box-sizing: border-box; 11 | page-break-after: always; 12 | font: 10pt Arial, 'Times New Roman', Times, serif; 13 | line-height: 1.3; 14 | background: map-get($colors, white) !important; 15 | color: map-get($colors, black) !important; 16 | -webkit-print-color-adjust: exact; 17 | } 18 | 19 | /* Defining all page breaks */ 20 | a { 21 | page-break-inside: avoid; 22 | } 23 | 24 | h1, 25 | h2, 26 | h3, 27 | h4, 28 | h5, 29 | h6 { 30 | page-break-after: avoid; 31 | page-break-inside: avoid; 32 | } 33 | 34 | img { 35 | page-break-inside: avoid; 36 | page-break-after: avoid; 37 | } 38 | 39 | table, 40 | pre { 41 | page-break-inside: avoid; 42 | } 43 | 44 | ul, 45 | ol, 46 | dl { 47 | page-break-before: avoid; 48 | } 49 | 50 | /* Displaying link color and link behaviour */ 51 | a:link, 52 | a:visited, 53 | a { 54 | background: transparent; 55 | color: map-get($colors, primary); 56 | font-weight: bold; 57 | text-decoration: underline; 58 | text-align: left; 59 | } 60 | 61 | a[href^='http']::after { 62 | content: initial; 63 | } 64 | $a: after > 65 | 66 | article a[href^='#']::after { 67 | content: ''; 68 | } 69 | 70 | /* stylelint-disable selector-pseudo-class-no-unknown */ 71 | a:not(:local-link)::after { 72 | content: ' < ' attr(href) '> '; 73 | } 74 | /* stylelint-enable */ 75 | 76 | .icon-checkbox, 77 | .icon-checked { 78 | display: none !important; 79 | } 80 | 81 | .c-checklist__body input { 82 | height: auto !important; 83 | top: 4px !important; 84 | opacity: 1 !important; 85 | } 86 | 87 | .c-checklist__body label { 88 | text-decoration: none !important; 89 | opacity: 1 !important; 90 | } 91 | 92 | .c-tags, 93 | .c-github-corner, 94 | .s-header__media, 95 | .s-header__lang, 96 | .img-logo, 97 | .s-content__filter, 98 | .s-content__search, 99 | .s-content__meta, 100 | .s-section__meta, 101 | .s-meta, 102 | .c-notation__generate, 103 | .s-nav { 104 | display: none !important; 105 | } 106 | 107 | .page-main { 108 | display: block !important; 109 | } 110 | 111 | h2 { 112 | font-size: 20pt !important; 113 | } 114 | 115 | label { 116 | font-size: 9pt !important; 117 | } 118 | 119 | 120 | .s-header { 121 | text-align: left !important; 122 | } 123 | 124 | .s-header__banner { 125 | padding: 15px; 126 | } 127 | 128 | .sub-heading { 129 | font-size: 9pt !important; 130 | margin: 0 !important; 131 | } 132 | 133 | .c-checklist__item::before { 134 | display: none; 135 | } 136 | 137 | .label__description { 138 | line-height: 1.5; 139 | } 140 | 141 | .c-checklist__dropdown { 142 | display: none; 143 | } 144 | 145 | .sub-heading::after { 146 | content: ' ★ Generated on http://cfnchecklist.com ★ '; 147 | white-space: pre; 148 | } 149 | 150 | .s-header__checklist__el { 151 | padding: 0 !important; 152 | } 153 | 154 | .c-notation__item { 155 | width: 80px !important; 156 | height: 80px !important; 157 | } 158 | 159 | .c-progress { 160 | padding: 0 20px; 161 | } 162 | 163 | .c-progress__counter, 164 | .c-progress__label { 165 | font-size: 8pt !important; 166 | } 167 | 168 | .c-progress__bar { 169 | display: none !important; 170 | } 171 | 172 | .c-progress__label { 173 | position: relative !important; 174 | padding: 20pt 0 !important; 175 | left: auto !important; 176 | } 177 | 178 | .c-nav { 179 | margin: 0 !important; 180 | position: relative !important; 181 | right: auto !important; 182 | z-index: 10 !important; 183 | } 184 | 185 | .c-nav__list { 186 | position: relative !important; 187 | top: 0 !important; 188 | flex-direction: row !important; 189 | } 190 | 191 | .c-nav__item .c-button { 192 | font-size: 9pt !important; 193 | border: 0 !important; 194 | } 195 | 196 | .form input { 197 | font-size: 9pt !important; 198 | padding: 3px !important; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/styles/base/_typography.scss: -------------------------------------------------------------------------------- 1 | p { 2 | font-size: 1.2rem; 3 | line-height: 1.5; 4 | margin-top: 0; 5 | } 6 | -------------------------------------------------------------------------------- /src/styles/components/_c-button.scss: -------------------------------------------------------------------------------- 1 | // $control-radius: $radius !default; 2 | // $control-radius-small: $radius-small !default; 3 | 4 | 5 | $control-padding-vertical: calc(8px - 1px) !default; 6 | $control-padding-horizontal: calc(11px - 1px) !default; 7 | $control-padding-vertical-up: calc(6px - 1px) !default; 8 | $control-padding-horizontal-up: calc(10px - 1px) !default; 9 | 10 | @mixin control { 11 | -moz-appearance: none; 12 | -webkit-appearance: none; 13 | align-items: center; 14 | border: 1px solid transparent; 15 | border-radius: 3px; 16 | box-shadow: none; 17 | display: inline-flex; 18 | font-size: 1.3rem; 19 | padding: $control-padding-vertical $control-padding-horizontal; 20 | justify-content: flex-start; 21 | line-height: 1.4; 22 | position: relative; 23 | vertical-align: top; 24 | 25 | @include mq(lap-and-up) { 26 | font-size: $size-medium; 27 | height: 34px; 28 | padding: $control-padding-vertical-up $control-padding-horizontal-up; 29 | } 30 | // States 31 | &:focus, 32 | &.is-focused, 33 | &:active, 34 | &.is-active { 35 | outline: none; 36 | } 37 | 38 | &[disabled] { 39 | cursor: not-allowed; 40 | } 41 | } 42 | 43 | // The controls sizes use mixins so they can be used at different breakpoints 44 | @mixin control-small { 45 | // border-radius: $control-radius-small; 46 | font-size: $size-small; 47 | } 48 | 49 | @mixin control-medium { 50 | font-size: $size-medium; 51 | } 52 | 53 | @mixin control-large { 54 | font-size: $size-large; 55 | } 56 | 57 | $button-color: map-get($colors, grey-darker) !default; 58 | $button-background-color: map-get($colors, white) !default; 59 | $button-border-color: #999999 !default; 60 | $button-hover-color: $link-hover !default; 61 | $button-hover-border-color: $link-hover-border !default; 62 | $button-focus-color: $link-focus !default; 63 | $button-focus-border-color: $link-focus-border !default; 64 | 65 | // The button sizes use mixins so they can be used at different breakpoints 66 | @mixin button-small { 67 | // border-radius: $radius-small; 68 | font-size: $size-small; 69 | } 70 | 71 | @mixin button-medium { 72 | font-size: $size-medium; 73 | } 74 | 75 | @mixin button-large { 76 | font-size: $size-large; 77 | } 78 | 79 | // Declarations 80 | // ============================================================================= 81 | #{$namespace-button}, 82 | button { 83 | @include control; 84 | @include unselectable; 85 | 86 | background-color: $button-background-color; 87 | border-color: $button-border-color; 88 | color: $button-color; 89 | cursor: pointer; 90 | justify-content: center; 91 | text-align: center; 92 | white-space: nowrap; 93 | 94 | // States 95 | &:hover, 96 | &.--hovered { 97 | border-color: $button-hover-border-color; 98 | color: $button-hover-color; 99 | transition: all 200ms ease-in 0s; 100 | } 101 | 102 | &:focus, 103 | &.--focused { 104 | border-color: $button-focus-border-color; 105 | color: $button-focus-color; 106 | } 107 | 108 | &:active, 109 | &.--active { 110 | border-color: $button-focus-border-color; 111 | color: $button-focus-color; 112 | } 113 | 114 | // Sizes 115 | &.--small { 116 | @include button-small; 117 | } 118 | 119 | &.--medium { 120 | @include button-medium; 121 | } 122 | 123 | &.--large { 124 | @include button-large; 125 | } 126 | 127 | &.--fill { 128 | background-color: map-get($colors, green); 129 | color: map-get($colors, white); 130 | border-color: map-get($colors, green); 131 | 132 | & .icon { 133 | fill: map-get($colors, white); 134 | } 135 | } 136 | 137 | &-icon { 138 | border-color: map-get($colors, grey-light); 139 | 140 | .icon { 141 | margin: 0; 142 | } 143 | 144 | &:hover { 145 | border-color: $secondary; 146 | 147 | .icon { 148 | fill: $secondary; 149 | } 150 | } 151 | } 152 | } 153 | 154 | #{$namespace-button}__group { 155 | margin: 0 5px; 156 | } 157 | 158 | .s-section__meta .button-icon { 159 | .icon { 160 | margin: 0 !important; 161 | vertical-align: top; 162 | fill: map-get($colors, grey-light); 163 | } 164 | 165 | &:hover { 166 | .icon { 167 | fill: $button-hover-border-color; 168 | } 169 | } 170 | 171 | &.is-active { 172 | border-color: $secondary; 173 | 174 | svg { 175 | fill: $secondary; 176 | } 177 | } 178 | } 179 | 180 | button.filter-active { 181 | border: 2px $primary solid; 182 | color: $primary; 183 | } 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /src/styles/components/_c-checklist.scss: -------------------------------------------------------------------------------- 1 | @if $use-checklist == true { 2 | 3 | // Variables 4 | // ============================================================================= 5 | $checklist-background-color: map-get($colors, white) !default; 6 | $checklist-box-shadow: null !default; 7 | 8 | // Declarations 9 | // ============================================================================= 10 | #{$namespace-checklist} { 11 | & [data-item-visible='false'] { 12 | display: none; 13 | } 14 | 15 | &__list { 16 | margin: 0; 17 | padding: 0; 18 | list-style: none; 19 | } 20 | 21 | &__column { 22 | // padding: 8px 4px; 23 | padding: 0 0 10px; 24 | 25 | @include mq('handheld') { 26 | padding: 2px; 27 | } 28 | 29 | @include mq('handheld-and-up') { 30 | padding: 8px 4px; 31 | } 32 | 33 | @include mq('print') { 34 | padding: 0; 35 | } 36 | 37 | // Priority bullet 38 | &:nth-child(1) { 39 | padding-top: 12px; 40 | 41 | @include mq('handheld-and-up') { 42 | padding-top: 15px; 43 | } 44 | 45 | @include mq('print') { 46 | margin: 0 5px; 47 | padding-top: 5px; 48 | } 49 | } 50 | 51 | // Checkboxe 52 | &:nth-child(2) { 53 | padding-top: 11px; 54 | 55 | @include mq('handheld') { 56 | padding-top: 11px; 57 | } 58 | } 59 | } 60 | 61 | &__intro { 62 | font-size: 1.4rem; 63 | } 64 | 65 | &__body { 66 | display: flex; 67 | flex-flow: row nowrap; 68 | justify-content: space-between; 69 | align-items: stretch; 70 | flex: 1; 71 | 72 | label { 73 | font-weight: 400; 74 | line-height: 1; 75 | position: relative; 76 | display: block; 77 | 78 | @include mq('handheld-and-up') { 79 | font-size: 1.6rem; 80 | line-height: 1.4; 81 | } 82 | 83 | &::before { 84 | border-color: #bdc3c7; 85 | } 86 | } 87 | 88 | input { 89 | position: absolute; 90 | left: 0; 91 | top: 0; 92 | min-width: 15px; 93 | width: 100%; 94 | height: 100%; 95 | z-index: 2; 96 | opacity: 0; 97 | margin: 0; 98 | padding: 0; 99 | cursor: pointer; 100 | } 101 | } 102 | 103 | .label__title, 104 | .label__description { 105 | font-size: 1.3rem; 106 | 107 | @include mq('handheld-and-up') { 108 | font-size: 1.5rem; 109 | } 110 | } 111 | 112 | &__item { 113 | opacity: 1; 114 | height: auto; 115 | transition-timing-function: ease-in; 116 | font-size: 24px; 117 | line-height: 31px; 118 | display: block; 119 | background-color: $checklist-background-color; 120 | box-shadow: $checklist-box-shadow; 121 | overflow: hidden; 122 | width: 100%; 123 | position: relative; 124 | 125 | @include mq('print') { 126 | padding: 5px 0; 127 | } 128 | 129 | &::before { 130 | content: ''; 131 | position: absolute; 132 | border-top: 1px solid #e4ebf0; 133 | width: 100%; 134 | } 135 | 136 | &__inner { 137 | display: flex; 138 | } 139 | 140 | &:nth-child(even) { 141 | background: #f8f7f6; 142 | } 143 | 144 | &:hover { 145 | background-color: rgb(241, 240, 237); 146 | } 147 | } 148 | 149 | &__priority { 150 | order: 1; 151 | flex-basis: 1%; 152 | } 153 | 154 | &__checkbox { 155 | order: 2; 156 | flex-basis: 30px; 157 | position: relative; 158 | display: flex; 159 | align-items: center; 160 | justify-content: center; 161 | 162 | @include mq('print') { 163 | flex-basis: 25px; 164 | } 165 | 166 | @include mq('handheld-and-up') { 167 | flex-basis: 40px; 168 | } 169 | } 170 | 171 | &__label { 172 | order: 3; 173 | flex: 1; 174 | } 175 | 176 | &__dropdown { 177 | order: 4; 178 | flex-basis: 35px; 179 | position: relative; 180 | background-color: #f1f0ed; 181 | 182 | @include mq('desk') { 183 | flex-basis: 3%; 184 | } 185 | 186 | &__button { 187 | position: absolute; 188 | left: 0; 189 | top: 0; 190 | min-width: 15px; 191 | width: 100%; 192 | height: 100%; 193 | z-index: 999; 194 | margin: 0; 195 | padding: 0; 196 | cursor: pointer; 197 | border: 0; 198 | background-color: transparent; 199 | display: flex; 200 | } 201 | 202 | [data-item-check='true'] & { 203 | cursor: not-allowed; 204 | } 205 | } 206 | } 207 | } 208 | 209 | #{$namespace-checklist} { 210 | &__introduction { 211 | font-size: 1.5rem; 212 | margin-top: 0; 213 | } 214 | 215 | &__details { 216 | padding: 10px 10px 0; 217 | 218 | @include mq('handheld-and-up') { 219 | padding: 15px 15px 0; 220 | width: 100%; 221 | } 222 | 223 | &[aria-expanded='false'] { 224 | overflow: hidden; 225 | max-height: 0; 226 | padding-top: 0; 227 | padding-bottom: 0; 228 | margin-top: 0; 229 | margin-bottom: 0; 230 | transition-duration: .3s; 231 | transition-timing-function: cubic-bezier(0, 1, .5, 1); 232 | opacity: 0; 233 | } 234 | 235 | &[aria-expanded='true'] { 236 | transition-duration: .3s; 237 | transition-timing-function: ease-in; 238 | max-height: 1000px; 239 | overflow: hidden; 240 | opacity: 1; 241 | } 242 | 243 | [data-item-check='true'] & { 244 | opacity: .3; 245 | } 246 | } 247 | 248 | &__text { 249 | font-size: 1.4rem; 250 | line-height: 1.5; 251 | margin-top: 0; 252 | } 253 | 254 | &__code { 255 | // TODO: fix the Github Iframe 256 | display: none; 257 | 258 | @include mq('desk') { 259 | display: block; 260 | border: 1px solid #cccccc; 261 | background-color: white; 262 | } 263 | 264 | @include mq('print') { 265 | display: block; 266 | } 267 | 268 | &__loader { 269 | padding: 10px 0; 270 | display: block; 271 | width: 50px; 272 | height: 50px; 273 | margin: 0 auto; 274 | } 275 | } 276 | 277 | &__favicon { 278 | margin-right: 5px; 279 | width: 14px; 280 | height: 14px; 281 | display: inline; 282 | 283 | &[data-src] { 284 | display: none; 285 | } 286 | 287 | .no-js & { 288 | display: none; 289 | } 290 | } 291 | 292 | .documentation__list { 293 | margin: 0; 294 | padding: 0 0 0 20px; 295 | list-style: circle; 296 | 297 | li { 298 | font-size: 1.2rem; 299 | line-height: 1.8; 300 | 301 | @include mq('handheld-and-up') { 302 | font-size: 1.5rem; 303 | line-height: 1.5; 304 | } 305 | } 306 | } 307 | } 308 | 309 | .c-checklist__documentation, 310 | .c-checklist__tools { 311 | padding: 0 0 5px; 312 | margin: 0; 313 | } 314 | 315 | 316 | [data-item-check='true'] label { 317 | text-decoration: line-through; 318 | opacity: .3; 319 | } 320 | -------------------------------------------------------------------------------- /src/styles/components/_c-dropdown.scss: -------------------------------------------------------------------------------- 1 | @if $use-dropdown == true { 2 | [data-item-dropdown='open'] .c-checklist__details { 3 | display: block; 4 | } 5 | 6 | [data-item-dropdown='close'] .c-checklist__details { 7 | display: none; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/styles/components/_c-github.scss: -------------------------------------------------------------------------------- 1 | .github-corner:hover .octo-arm { 2 | animation: octocat-wave 560ms ease-in-out; 3 | } 4 | 5 | .github-corner { 6 | svg { 7 | fill: white; 8 | color: #151513; 9 | position: absolute; 10 | top: 0; 11 | border: 0; 12 | right: 0; 13 | } 14 | 15 | .octo-arm { 16 | transform-origin: 130px 106px; 17 | } 18 | } 19 | 20 | @keyframes octocat-wave { 21 | 0%, 22 | 100% { 23 | transform: rotate(0); 24 | } 25 | 26 | 20%, 27 | 60% { 28 | transform: rotate(-25deg); 29 | } 30 | 31 | 40%, 32 | 80% { 33 | transform: rotate(10deg); 34 | } 35 | } 36 | 37 | @media (max-width: 500px) { 38 | .github-corner:hover .octo-arm { 39 | animation: none; 40 | } 41 | 42 | .github-corner .octo-arm { 43 | animation: octocat-wave 560ms ease-in-out; 44 | } 45 | } 46 | 47 | 48 | .gist { 49 | display: block !important; 50 | position: relative !important; 51 | margin: 0 !important; 52 | width: calc(100%) !important; /* Fill 100% of the parent container with an extra 30px on each side */ 53 | overflow: hidden !important; 54 | } 55 | 56 | .gist-file { 57 | margin: 0 !important; 58 | border: none !important; 59 | } 60 | 61 | .gist-data { 62 | padding: 5px !important; 63 | border: 0 !important; 64 | border-bottom: none !important; 65 | border-radius: 0; 66 | } 67 | 68 | .gist .gist-meta { 69 | color: map-get($colors, black) !important; 70 | border: 0 !important; 71 | border-radius: 0; 72 | padding: 3px !important; 73 | font-size: 8px !important; 74 | 75 | @include mq('handheld-and-up') { 76 | font-size: 10px !important; 77 | padding: 5px !important; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/styles/components/_c-list.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Lists 3 | // 4 | 5 | 6 | // Variables 7 | // ============================================================================= 8 | $list-margin: 0 !default; 9 | $list-padding: 0 0 0 $spacing-medium !default; 10 | $list-unstyled-padding: 0 !default; 11 | $list-unstyled-list-style: none !default; 12 | $list-nested-padding: $list-padding !default; 13 | $list-item-padding: 0 !default; 14 | $list-item-unstyled-list-style: none !default; 15 | $list-ordered-item-padding: 0 $spacing-small 0 0 !default; 16 | $list-inline-padding: 0 !default; 17 | $list-inline-item-padding-right: $spacing-medium !default; 18 | $list-inline-item-bullet-content: '\2022' !default; 19 | $list-inline-item-bullet-padding: 0 $spacing-small 0 0 !default; 20 | 21 | @mixin list--unstyled { 22 | padding: $list-unstyled-padding; 23 | list-style: $list-unstyled-list-style; 24 | } 25 | 26 | @mixin list--inline { 27 | padding: $list-inline-padding; 28 | } 29 | 30 | // Declarations 31 | // ============================================================================= 32 | #{$namespace-list} { 33 | display: block; 34 | margin: $list-margin; 35 | padding: $list-padding; 36 | list-style-position: outside; 37 | 38 | & .c-list { 39 | padding: $list-nested-padding; 40 | } 41 | 42 | &__item { 43 | padding: $list-item-padding; 44 | } 45 | 46 | &__item--unstyled { 47 | list-style: $list-item-unstyled-list-style; 48 | } 49 | 50 | &--unstyled { 51 | @include list--unstyled; 52 | } 53 | 54 | &--ordered { 55 | @include list--unstyled; 56 | 57 | counter-reset: ordered; 58 | 59 | .c-list__item { 60 | &::before { 61 | padding: $list-ordered-item-padding; 62 | content: counters(ordered, '.') ' '; 63 | counter-increment: ordered; 64 | } 65 | } 66 | } 67 | 68 | // &--inline { 69 | // @include list--inline; 70 | 71 | // .c-list--inline { 72 | // @include list--inline; 73 | // } 74 | 75 | // .c-list__item { 76 | // display: inline-block; 77 | // width: auto; 78 | // padding-right: $list-inline-item-padding-right; 79 | // } 80 | 81 | // &:not(.c-list--unstyled) { 82 | // .c-list__item { 83 | // &::before { 84 | // padding: $list-inline-item-bullet-padding; 85 | // content: $list-inline-item-bullet-content; 86 | // } 87 | // } 88 | // } 89 | // } 90 | } 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/styles/components/_c-nav.scss: -------------------------------------------------------------------------------- 1 | @if $use-nav == true { 2 | 3 | // Variables 4 | // ============================================================================= 5 | $nav-height: 64px; 6 | $nav-background: map-get($colors, white); 7 | $nav-border: #dddddd; 8 | $nav-collapse-breakpoint: 768px; 9 | $nav-item-font-size: 14px; 10 | $nav-item-border-width: 4px; 11 | $nav-item-color: #555555; 12 | $nav-item-active-color: #333333; 13 | $nav-item-border: transparent; 14 | $nav-item-active-border: $primary; 15 | 16 | // Declarations 17 | // ============================================================================= 18 | #{$namespace-nav} { 19 | display: flex; 20 | align-items: flex-start; 21 | justify-content: space-between; 22 | margin-top: 15px; 23 | 24 | @include mq(lap-and-up) { 25 | display: flex; 26 | margin: 15px 0; 27 | } 28 | 29 | &--inline { 30 | flex-direction: row; 31 | } 32 | 33 | &__inner { 34 | display: flex; 35 | align-content: center; 36 | justify-content: center; 37 | flex-direction: column; 38 | } 39 | 40 | &__list { 41 | width: 100%; 42 | margin: 0; 43 | padding: 0; 44 | flex-wrap: wrap; 45 | display: flex; 46 | 47 | @include mq(lap-and-up) { 48 | display: flex; 49 | flex-wrap: wrap; 50 | } 51 | } 52 | 53 | &__item { 54 | margin: 0 10px 10px 0; 55 | 56 | @include mq(lap-and-up) { 57 | margin: 0 5px 5px 0; 58 | } 59 | } 60 | 61 | &__status { 62 | padding-left: 3px; 63 | margin-right: 7px; 64 | } 65 | 66 | &__sidebar { 67 | position: absolute; 68 | right: -10px; 69 | // animation: navShow 1s normal; 70 | // z-index: -1; 71 | 72 | .c-nav__list { 73 | position: fixed; 74 | top: 50px; 75 | flex-direction: column; 76 | width: auto; 77 | will-change: transform; 78 | } 79 | 80 | .c-button { 81 | text-align: left; 82 | } 83 | } 84 | 85 | &__item a, 86 | &__item button { 87 | display: block; 88 | text-transform: uppercase; 89 | color: map-get($colors, black); 90 | padding: 5px 10px; 91 | font-size: 1.3rem; 92 | border: 1px solid #cccccc; 93 | 94 | @include mq('handheld-and-up') { 95 | font-size: 14px; 96 | } 97 | 98 | &:hover { 99 | text-decoration: none; 100 | font-weight: $weight-normal; 101 | border: 1px solid $nav-item-active-border; 102 | } 103 | } 104 | } 105 | } 106 | 107 | 108 | @keyframes navShow { 109 | 0% { 110 | right: 150px; 111 | opacity: 0; 112 | } 113 | 114 | 80% { 115 | opacity: .2; 116 | } 117 | 118 | 100% { 119 | right: -10px; 120 | z-index: 9; 121 | opacity: 1; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/styles/components/_c-notation.scss: -------------------------------------------------------------------------------- 1 | .c-notation__details__list { 2 | margin: 0; 3 | padding: 5px 0; 4 | text-align: left; 5 | } 6 | 7 | .c-notation__details__item { 8 | font-size: 1.3rem; 9 | 10 | .icon { 11 | display: inline-block; 12 | margin-right: 5px; 13 | } 14 | } 15 | 16 | // Declarations 17 | // ============================================================================= 18 | .c-notation { 19 | display: flex; 20 | align-content: space-between; 21 | justify-content: space-between; 22 | margin: 15px 0 0; 23 | 24 | &__box { 25 | position: relative; 26 | } 27 | 28 | &__letter { 29 | position: absolute; 30 | top: 50%; 31 | left: 50%; 32 | transform: translate(-50%, -50%); 33 | text-align: center; 34 | font-size: 45px; 35 | color: white; 36 | line-height: 1; 37 | } 38 | 39 | &__item { 40 | width: 75px; 41 | height: 75px; 42 | background: #f3f3f3; 43 | position: relative; 44 | } 45 | 46 | &__generate { 47 | text-align: right; 48 | margin-top: 15px; 49 | } 50 | 51 | &__details { 52 | font-size: 1.2rem; 53 | } 54 | } 55 | $notation: -1; 56 | 57 | @while $notation <= 100 { 58 | $notation: $notation + 1; 59 | 60 | [data-notation='#{$notation}'] { 61 | @if $notation <= 20 { 62 | background-color: $progress-0; 63 | } 64 | 65 | @else if $notation <= 40 { 66 | background-color: $progress-20; 67 | } 68 | 69 | @else if $notation <= 60 { 70 | background-color: $progress-40; 71 | } 72 | 73 | @else if $notation <= 80 { 74 | background-color: $progress-60; 75 | } 76 | 77 | @else if $notation < 100 { 78 | background-color: $progress-80; 79 | } 80 | 81 | @else if $notation == 100 { 82 | background-color: $progress-100; 83 | } 84 | 85 | @else { 86 | background-color: $progress-0; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/styles/components/_c-progress.scss: -------------------------------------------------------------------------------- 1 | @if $use-progress == true { 2 | 3 | $i: 0; 4 | 5 | #{$namespace-progress} { 6 | position: relative; 7 | display: flex; 8 | padding: 10px 0; 9 | 10 | @include mq('handheld-and-up') { 11 | padding: 0; 12 | display: block; 13 | } 14 | 15 | &__counter { 16 | text-align: center; 17 | margin-left: 20px; 18 | 19 | @include mq('handheld-and-up') { 20 | margin: 0; 21 | } 22 | } 23 | 24 | &__label { 25 | font-size: 1.3rem; 26 | font-weight: 600; 27 | 28 | @include mq('handheld-and-up') { 29 | position: absolute; 30 | top: 28%; 31 | left: 50%; 32 | transform: translate(-50%, -50%); 33 | } 34 | } 35 | 36 | &__text { 37 | font-size: 1.3rem; 38 | 39 | @include mq('handheld-and-up') { 40 | font-size: 1.1rem; 41 | padding: 0 5px; 42 | } 43 | } 44 | } 45 | 46 | .c-progress__bar { 47 | background-color: map-get($shades, white-ter); 48 | border: 0; 49 | height: 19px; 50 | border-radius: 3px; 51 | width: 120px; 52 | 53 | @include mq('handheld-and-up') { 54 | height: 20px; 55 | margin: 0 20px; 56 | } 57 | 58 | .c-progress--big & { 59 | height: 30px; 60 | width: 150px; 61 | } 62 | 63 | &::-webkit-progress-value { 64 | border-radius: 2px; 65 | } 66 | 67 | @while $i < 100 { 68 | $i: $i + 1; 69 | $hue: round((65 / 100) * $i); 70 | 71 | &[value='#{$i}'] { 72 | &::-webkit-progress-value { 73 | background-color: adjust-hue($progress-20, $hue); 74 | } 75 | } 76 | } 77 | 78 | &::-webkit-progress-bar { 79 | background-color: map-get($shades, white-ter); 80 | transition: all 300ms ease-in 0s; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/styles/components/_c-tags.scss: -------------------------------------------------------------------------------- 1 | .c-tags { 2 | &__list { 3 | padding: 0; 4 | list-style: none; 5 | position: relative; 6 | opacity: .7; 7 | 8 | [data-item-check='true'] & { 9 | opacity: .2; 10 | } 11 | } 12 | 13 | &__item { 14 | font-size: 1rem; 15 | display: inline; 16 | padding: 3px; 17 | white-space: nowrap; 18 | opacity: 1; 19 | overflow: hidden; 20 | border: .5px solid $border; 21 | 22 | @include mq('handheld-and-up') { 23 | font-size: 11px; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/styles/components/_c-tools.scss: -------------------------------------------------------------------------------- 1 | .c-tools { 2 | display: flex; 3 | 4 | @include mq('print') { 5 | display: none; 6 | } 7 | 8 | .is-active { 9 | border-color: #415257; 10 | } 11 | 12 | .c-button__group:first-child { 13 | margin-left: 0; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/styles/components/_c-top-alert.scss: -------------------------------------------------------------------------------- 1 | @if $use-top-alert == true { 2 | #{$namespace-top-alert} { 3 | display: none; 4 | 5 | .no-js & { 6 | &--alert { 7 | min-height: 35px; 8 | background: orange; 9 | width: 100%; 10 | text-align: center; 11 | font-weight: bold; 12 | color: white; 13 | font-size: 1.3rem; 14 | padding: 5px 0; 15 | 16 | p { 17 | margin: 0; 18 | } 19 | } 20 | } 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/styles/config/_v-colors.scss: -------------------------------------------------------------------------------- 1 | // Colors 2 | $colors: ( 3 | black: hsl(0, 0%, 4%), 4 | black-bis: hsl(0, 0%, 7%), 5 | black-ter: hsl(0, 0%, 14%), 6 | grey-darker: hsl(0, 0%, 21%), 7 | grey-dark: #212529, 8 | grey: hsl(0, 0%, 48%), 9 | grey-light: hsl(0, 0%, 71%), 10 | grey-lighter: hsl(0, 0%, 86%), 11 | white-ter: hsl(0, 0%, 96%), 12 | white-bis: hsl(0, 0%, 98%), 13 | white: hsl(0, 0%, 100%), 14 | orange: #d75637, 15 | yellow: hsl(48, 100%, 67%), 16 | green: hsl(141, 71%, 48%), 17 | blue: #415257, 18 | red: red 19 | ); 20 | 21 | // :root { 22 | // // each item in color map 23 | // @each $name, $color in $colors { 24 | // --color-#{$name}: $color; 25 | // } 26 | // } 27 | 28 | // Lists and maps 29 | $colors: ('white': ($white, $black), 'black': ($black, $white), 'light': ($light, $light-invert), 'dark': ($dark, $dark-invert), 'primary': ($primary, $primary-invert), 'info': ($info, $info-invert), 'success': ($success, $success-invert), 'warning': ($warning, $warning-invert), 'danger': ($danger, $danger-invert)) !default; 30 | 31 | $shades: ('black-bis': map-get($colors, black-bis), 'black-ter': map-get($colors, black-ter), 'grey-darker': map-get($colors, grey-darker), 'grey-dark': map-get($colors, grey-dark), 'grey': map-get($colors, grey), 'grey-light': map-get($colors, grey-light), 'grey-lighter': map-get($colors, grey-lighter), 'white-ter': map-get($colors, white-ter), 'white-bis': map-get($colors, white-bis)) !default; 32 | 33 | $primary: map-get($colors, orange) !default; 34 | $secondary: map-get($colors, blue) !default; 35 | 36 | $info: map-get($colors, blue) !default; 37 | $success: map-get($colors, green) !default; 38 | $warning: map-get($colors, yellow) !default; 39 | $danger: map-get($colors, red) !default; 40 | $light: map-get($colors, white-ter) !default; 41 | $dark: map-get($colors, grey-darker) !default; 42 | $background: map-get($colors, white-ter) !default; 43 | $border: map-get($colors, grey-lighter) !default; 44 | $border-hover: map-get($colors, grey-light) !default; 45 | 46 | $color-tint: 9 !default; 47 | $color-tint-dark: $color-tint !default; 48 | $color-tint-darker: $color-tint * 2 !default; 49 | $color-tint-light: $color-tint !default; 50 | $color-tint-lighter: $color-tint * 2 !default; 51 | 52 | 53 | $progress-0: #6f2110; 54 | $progress-20: #f63a0f; 55 | $progress-40: #f27011; 56 | $progress-60: #f2b01e; 57 | $progress-80: #f2d31b; 58 | $progress-100: #86e01e; 59 | -------------------------------------------------------------------------------- /src/styles/config/_v-namespaces.scss: -------------------------------------------------------------------------------- 1 | 2 | // 3 | $use-top-alert: true !default; 4 | $use-button: true !default; 5 | $use-checklist: true !default; 6 | $use-dropdown: true !default; 7 | $use-list: true !default; 8 | $use-modal: true !default; 9 | $use-nav: true !default; 10 | $use-progress: true !default; 11 | $use-tooltip: true !default; 12 | $use-aside: true !default; 13 | 14 | // Components 15 | $namespace-top-alert: '.c-top-alert' !default; 16 | $namespace-button: '.c-button' !default; 17 | $namespace-checklist: '.c-checklist' !default; 18 | $namespace-dropdown: '.c-dropdown' !default; 19 | $namespace-list: '.c-list' !default; 20 | $namespace-modal: '.c-modal' !default; 21 | $namespace-nav: '.c-nav' !default; 22 | $namespace-progress: '.c-progress' !default; 23 | $namespace-tooltip: '.c-tooltip' !default; 24 | $namespace-aside: '.c-tag-filter' !default; 25 | -------------------------------------------------------------------------------- /src/styles/config/_v-typography.scss: -------------------------------------------------------------------------------- 1 | 2 | $text: map-get($colors, grey-dark) !default; 3 | $text-light: map-get($colors, grey) !default; 4 | $text-strong: map-get($colors, grey-darker) !default; 5 | 6 | // Typography 7 | $family-sans-serif: BlinkMacSystemFont, -apple-system, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif !default; 8 | $family-monospace: monospace !default; 9 | $render-mode: optimizeLegibility !default; 10 | $family-primary: $family-sans-serif !default; 11 | $family-code: $family-monospace !default; 12 | $weight-light: 300 !default; 13 | $weight-normal: 400 !default; 14 | $weight-semibold: 500 !default; 15 | $weight-bold: 700 !default; 16 | $size-1: 3rem !default; 17 | $size-2: 2.5rem !default; 18 | $size-3: 2rem !default; 19 | $size-4: 1.5rem !default; 20 | $size-5: 1.25rem !default; 21 | $size-6: 1rem !default; 22 | $size-7: .75rem !default; 23 | $size-small: $size-7 !default; 24 | $size-normal: $size-6 !default; 25 | $size-medium: $size-5 !default; 26 | $size-large: $size-4 !default; 27 | $sizes: $size-1 $size-2 $size-3 $size-4 $size-5 $size-6 $size-7 !default; 28 | -------------------------------------------------------------------------------- /src/styles/config/_variables.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Spacing 3 | // 4 | $spacing-super: 24px !default; 5 | $spacing-xlarge: 18px !default; 6 | $spacing-large: 16px !default; 7 | $spacing-medium: 14px !default; 8 | $spacing-small: .7px !default; 9 | $spacing-xsmall: 3px !default; 10 | $spacing-tiny: 1px !default; 11 | -------------------------------------------------------------------------------- /src/styles/layout/_page.scss: -------------------------------------------------------------------------------- 1 | $layout-width: '1140px' !default; 2 | $av-element-class-chain: '__' !default; // Chain characters between block and element 3 | $av-modifier-class-chain: '--' !default; // Chain characters between block and modifier 4 | $av-breakpoint-class-chain: '--' !default; // Chain characters between width and breakpoint 5 | $logo-width: 150px; 6 | 7 | .page-wrapper { 8 | display: grid; 9 | grid-template-columns: auto; 10 | grid-template-areas: 11 | 'header' 12 | 'main' 13 | 'footer'; 14 | 15 | @include mq('lap-and-up') { 16 | grid-template-areas: 'header header header' 'main main main' 'footer footer footer'; 17 | } 18 | 19 | .page-header { 20 | grid-area: header; 21 | } 22 | 23 | .page-main { 24 | grid-area: main; 25 | 26 | @include mq('lap-and-up') { 27 | display: grid; 28 | grid-template-columns: auto 150px 960px 150px auto; 29 | } 30 | } 31 | 32 | .page-footer { 33 | grid-area: footer; 34 | } 35 | } 36 | 37 | 38 | .page-header { 39 | &__logo { 40 | grid-area: header-left; 41 | align-self: center; 42 | 43 | .page-header__inner--centered & { 44 | grid-area: header-center; 45 | justify-self: center; 46 | } 47 | } 48 | 49 | &__nav { 50 | grid-area: header-right; 51 | align-self: center; 52 | display: flex; 53 | 54 | & .version-01 { 55 | grid-column-start: 3; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/styles/layout/_s-aside.scss: -------------------------------------------------------------------------------- 1 | .s-aside { 2 | position: fixed; 3 | transform: translateX(960px); 4 | text-align: left; 5 | margin-top: 55px; 6 | font-size: 1.5rem; 7 | top: 300px; 8 | 9 | ul { 10 | margin: 0; 11 | padding: 0; 12 | } 13 | } 14 | 15 | @if $use-aside == true { 16 | 17 | // Variables 18 | // ============================================================================= 19 | $aside-height: 64px; 20 | $aside-background: #f8f7f6; 21 | $aside-border: #dddddd; 22 | $aside-collapse-breakpoint: 768px; 23 | $aside-item-font-size: 14px; 24 | $aside-item-border-width: 4px; 25 | $aside-item-color: #555555; 26 | $aside-item-active-color: #333333; 27 | $aside-item-border: transparent; 28 | $aside-item-active-border: #673ab7; 29 | 30 | // Declarations 31 | // ============================================================================= 32 | #{$namespace-aside} { 33 | display: flex; 34 | align-items: center; 35 | justify-content: space-between; 36 | align-self: center; 37 | flex-flow: wrap row; 38 | 39 | @include mq('print') { 40 | display: none; 41 | } 42 | 43 | @include mq(lap-and-up) { 44 | display: flex; 45 | flex-direction: row; 46 | } 47 | 48 | &__title { 49 | font-size: 1.2rem; 50 | font-weight: 700; 51 | 52 | @include mq('handheld-and-up') { 53 | font-size: 1.3rem; 54 | } 55 | } 56 | 57 | &__inner { 58 | display: flex; 59 | align-content: center; 60 | justify-content: center; 61 | flex-direction: column; 62 | } 63 | 64 | &__list { 65 | flex-direction: row; 66 | width: 100%; 67 | margin: 0; 68 | padding: 10px; 69 | background-color: $aside-background; 70 | 71 | @include mq(lap-and-up) { 72 | display: flex; 73 | flex-wrap: wrap; 74 | } 75 | } 76 | 77 | &__item { 78 | display: inline-block; 79 | } 80 | 81 | &__item a, 82 | &__item button { 83 | display: block; 84 | text-transform: uppercase; 85 | font-size: 1.3rem; 86 | line-height: 1; 87 | color: map-get($colors, black-bis); 88 | padding: 5px 10px; 89 | border: 2px solid transparent; 90 | background-color: transparent; 91 | 92 | &:hover { 93 | text-decoration: none; 94 | font-weight: $weight-normal; 95 | border: 2px solid #dbdbdb; 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/styles/layout/_s-checklist.scss: -------------------------------------------------------------------------------- 1 | .s-checklist { 2 | position: relative; 3 | min-height: 1px; 4 | 5 | @include mq('handheld-and-up') { 6 | padding: 0 10px; 7 | } 8 | 9 | @include mq('print') { 10 | padding: 15px 0; 11 | 12 | &:first-child { 13 | padding: 0 0 15px; 14 | } 15 | } 16 | 17 | &::after { 18 | content: ''; 19 | position: absolute; 20 | height: 2px; 21 | border-top: 1px solid #e4ebf0; 22 | bottom: 0; 23 | width: 98%; 24 | } 25 | 26 | &__inner { 27 | display: grid; 28 | } 29 | 30 | &__body { 31 | padding: 0; 32 | 33 | .introduction { 34 | text-align: center; 35 | } 36 | 37 | .slide { 38 | display: grid; 39 | } 40 | } 41 | 42 | &__header { 43 | padding: 15px 0; 44 | z-index: 58; 45 | 46 | @include mq('print') { 47 | padding: 0; 48 | } 49 | 50 | @include mq('handheld-and-up') { 51 | display: flex; 52 | align-items: center; 53 | justify-content: space-between; 54 | } 55 | } 56 | 57 | &__title { 58 | @include mq('print') { 59 | display: flex; 60 | } 61 | 62 | @include mq('handheld-and-up') { 63 | display: flex; 64 | align-items: center; 65 | justify-content: center; 66 | } 67 | } 68 | 69 | &__header__title { 70 | margin: 0; 71 | text-align: left; 72 | color: rgb(65, 82, 87); 73 | font-weight: bold; 74 | text-transform: uppercase; 75 | line-height: 1.2; 76 | font-size: 2.5rem; 77 | 78 | @include mq('handheld-and-up') { 79 | font-size: 4rem; 80 | margin: 0; 81 | } 82 | } 83 | 84 | &__progress { 85 | display: flex; 86 | } 87 | 88 | &__footer { 89 | text-align: center; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/styles/layout/_s-footer.scss: -------------------------------------------------------------------------------- 1 | .s-footer { 2 | display: grid; 3 | text-align: center; 4 | padding: 20px; 5 | 6 | @include mq(lap-and-up) { 7 | padding: 50px 0; 8 | } 9 | 10 | @include mq('print') { 11 | padding: 10px; 12 | } 13 | 14 | &__badges { 15 | @include mq('print') { 16 | display: none; 17 | } 18 | } 19 | 20 | &__grateful { 21 | @include mq('print') { 22 | display: none; 23 | } 24 | } 25 | 26 | &__made { 27 | font-size: 1.4rem; 28 | 29 | @include mq('print') { 30 | font-size: 7pt; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/styles/layout/_s-header.scss: -------------------------------------------------------------------------------- 1 | .sub-heading { 2 | margin: 5px 15px; 3 | font-family: Montserrat, sans-serif; 4 | color: hsla(0, 0%, 100%, .6); 5 | font-size: 1.3rem; 6 | line-height: 1.5; 7 | 8 | @include mq('handheld-and-up') { 9 | font-size: 1.5rem; 10 | margin: 15px; 11 | } 12 | } 13 | 14 | .s-header { 15 | grid-row: 1; 16 | grid-column-start: 2; 17 | padding: 15px 0; 18 | background-color: #415257; 19 | background-image: linear-gradient(45deg, #415257, #292e49); 20 | color: map-get($colors, white); 21 | font-size: 1rem; 22 | text-align: center; 23 | position: relative; 24 | 25 | @include mq('handheld-and-up') { 26 | padding-top: 25px; 27 | padding-bottom: 60px; 28 | } 29 | 30 | @include mq('print') { 31 | padding: 0; 32 | } 33 | 34 | &__banner { 35 | @include mq('handheld-and-up') { 36 | width: 795px; 37 | margin: 0 auto; 38 | } 39 | } 40 | 41 | &__media__list { 42 | display: flex; 43 | align-items: flex-start; 44 | justify-content: center; 45 | align-self: center; 46 | padding: 0; 47 | } 48 | 49 | &__media__item { 50 | margin: 0 5px; 51 | } 52 | 53 | &__checklist { 54 | display: grid; 55 | grid-template-columns: auto; 56 | 57 | @include mq('print') { 58 | margin: 0; 59 | } 60 | 61 | @include mq('handheld-and-up') { 62 | grid-template-rows: auto auto; 63 | grid-template-columns: repeat(5, 1fr); 64 | padding: 10px; 65 | border: 1px solid lightgray; 66 | } 67 | 68 | &__title { 69 | display: none; 70 | } 71 | 72 | &__el { 73 | display: grid; 74 | 75 | @include mq('handheld-and-up') { 76 | padding: 0 20px 0 0; 77 | } 78 | } 79 | // Form 80 | &__el:nth-child(2) { 81 | grid-column: 6; 82 | grid-row: 1 / 2; 83 | 84 | @include mq('print') { 85 | grid-column: 1 / 4; 86 | grid-row: 1; 87 | } 88 | 89 | @include mq('handheld') { 90 | grid-column: 1 / 4; 91 | grid-row: 1 / 2; 92 | } 93 | 94 | @include mq('lap-and-up') { 95 | grid-column: 1 / 2; 96 | grid-row: 1 / 1; 97 | } 98 | } 99 | // Nav 100 | &__el:nth-child(3) { 101 | grid-column: 6; 102 | grid-row: 3 / 4; 103 | 104 | @include mq('print') { 105 | display: none; 106 | } 107 | 108 | @include mq('handheld') { 109 | grid-column: 1 / 6; 110 | grid-row: 2 / 2; 111 | } 112 | 113 | @include mq('lap-and-up') { 114 | grid-column: 2 / 4; 115 | grid-row: 1 / 1; 116 | } 117 | } 118 | // Notation 119 | &__el:nth-child(4) { 120 | grid-column: 6; 121 | grid-row: 2 / 3; 122 | 123 | @include mq('print') { 124 | grid-column: 5 / 6; 125 | grid-row: 1; 126 | } 127 | 128 | @include mq('handheld') { 129 | grid-column: 4 / 6; 130 | grid-row: 1 / 2; 131 | } 132 | 133 | @include mq('lap-and-up') { 134 | grid-column: 4 / 6; 135 | grid-row: 1 / 1; 136 | padding-right: 0; 137 | } 138 | } 139 | } 140 | 141 | &__lang__list { 142 | display: flex; 143 | flex-direction: row; 144 | align-items: flex-start; 145 | align-self: center; 146 | justify-content: center; 147 | margin-top: 20px; 148 | } 149 | 150 | &__lang__item { 151 | margin: 0 5px; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/styles/layout/_s-main.scss: -------------------------------------------------------------------------------- 1 | .s-main { 2 | display: grid; 3 | grid-column-start: 1; 4 | position: relative; 5 | background-color: map-get($colors, white); 6 | text-align: left; 7 | padding: 0 10px; 8 | 9 | &__section { 10 | margin: 0 0 10px; 11 | } 12 | 13 | @include mq('handheld-and-up') { 14 | display: grid; 15 | grid-column-start: 3; 16 | margin-top: -50px; 17 | width: 100%; 18 | padding: 10px; 19 | border-radius: 5px; 20 | border-color: rgba(0, 0, 0, .1); 21 | border-style: solid; 22 | border-width: 1px; 23 | } 24 | 25 | &__section:nth-child(1) { 26 | order: 2; 27 | 28 | @include mq('handheld-and-up') { 29 | padding: 0 20px; 30 | } 31 | } 32 | 33 | &__section:nth-child(2) { 34 | order: 4; 35 | 36 | @include mq('handheld-and-up') { 37 | padding: 0 20px; 38 | } 39 | } 40 | 41 | &__section:nth-child(3) { 42 | order: 4; 43 | } 44 | 45 | &__section:nth-child(4) { 46 | text-align: center; 47 | order: 5; 48 | } 49 | 50 | &__section:nth-child(5) { 51 | order: 1; 52 | } 53 | 54 | &__item { 55 | background: #ff6600; 56 | } 57 | 58 | &__header { 59 | text-align: center; 60 | z-index: 99999; 61 | } 62 | 63 | &__meta { 64 | display: none; 65 | 66 | @include mq('handheld-and-up') { 67 | display: flex; 68 | justify-content: flex-end; 69 | } 70 | } 71 | } 72 | 73 | 74 | .s-meta { 75 | text-align: left; 76 | } 77 | -------------------------------------------------------------------------------- /src/styles/main.scss: -------------------------------------------------------------------------------- 1 | @charset 'utf-8'; 2 | 3 | // Config and utilities 4 | @import 'config/variables'; 5 | @import 'config/v-namespaces'; 6 | @import 'config/v-colors'; 7 | @import 'config/v-typography'; 8 | @import 'utilities/functions/functions'; 9 | @import 'utilities/mixins/mixins'; 10 | @import 'utilities/utilities'; 11 | 12 | // General styling using DOM element selectors 13 | @import 'base/print'; 14 | @import 'base/fonts'; 15 | @import 'base/icons'; 16 | @import 'base/generic'; 17 | @import 'base/headings'; 18 | @import 'base/form'; 19 | @import 'base/media'; 20 | @import 'base/links'; 21 | @import 'base/typography'; 22 | 23 | // Layout 24 | @import 'layout/page'; 25 | @import 'layout/s-header'; 26 | @import 'layout/s-aside'; 27 | @import 'layout/s-main'; 28 | @import 'layout/s-checklist'; 29 | @import 'layout/s-footer'; 30 | 31 | // Components 32 | @import 'components/c-top-alert'; 33 | @import 'components/c-button'; 34 | @import 'components/c-checklist'; 35 | @import 'components/c-dropdown'; 36 | @import 'components/c-github'; 37 | @import 'components/c-tools'; 38 | @import 'components/c-list'; 39 | @import 'components/c-nav'; 40 | @import 'components/c-progress'; 41 | @import 'components/c-notation'; 42 | @import 'components/c-tags'; 43 | -------------------------------------------------------------------------------- /src/styles/utilities/_utilities.scss: -------------------------------------------------------------------------------- 1 | 2 | main [data-body-visibility='hide'] * { 3 | display: none; 4 | } 5 | 6 | 7 | [data-body-visbility='hide'] { 8 | display: none; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/styles/utilities/functions/_functions.scss: -------------------------------------------------------------------------------- 1 | // Function to choose colors in CSS variables 2 | @function color($color-name) { 3 | @return var(--color-#{$color-name}); 4 | } 5 | -------------------------------------------------------------------------------- /src/styles/utilities/mixins/_m-breakpoints.scss: -------------------------------------------------------------------------------- 1 | $av-breakpoints: ( 2 | 'print': 'print', 'thumb': 'screen and (max-width: 499px)', 'handheld': 'screen and (min-width: 500px) and (max-width: 800px)', 'handheld-and-up': 'screen and (min-width: 500px)', 'pocket': 'screen and (max-width: 800px)', 'lap': 'screen and (min-width: 801px) and (max-width: 1024px)', 'lap-and-up': 'screen and (min-width: 801px)', 'portable': 'screen and (max-width: 1024px)', 'desk': 'screen and (min-width: 1025px)', 'widescreen': 'screen and (min-width: 1160px)', 'retina': 'screen and (-webkit-min-device-pixel-ratio: 2), screen and (min-resolution: 192dpi), screen and (min-resolution: 2dppx)') !default; // Responsive breakpoints. 3 | 4 | @mixin mq($alias) { 5 | // Search breakpoint map for alias 6 | $query: map-get($av-breakpoints, $alias); 7 | // If alias exists, print out media query 8 | @if $query { 9 | @media #{$query} { 10 | @content; 11 | } 12 | } 13 | 14 | @else { 15 | @error 'No breakpoint found for #{$alias}'; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/styles/utilities/mixins/_mixins.scss: -------------------------------------------------------------------------------- 1 | @import 'm-breakpoints'; 2 | 3 | @mixin unselectable { 4 | -webkit-touch-callout: none; 5 | -webkit-user-select: none; 6 | -moz-user-select: none; 7 | -ms-user-select: none; 8 | user-select: none; 9 | } 10 | 11 | ol, 12 | ul { 13 | list-style: none; 14 | } 15 | 16 | .sr-only { 17 | display: none; 18 | } 19 | -------------------------------------------------------------------------------- /src/views/base/after-scripts.pug: -------------------------------------------------------------------------------- 1 | script(src="/scripts/app.bundle.js" async) 2 | script. 3 | 4 | if ('serviceWorker' in navigator) { 5 | //- console.log('CLIENT: service worker registration in progress.'); 6 | navigator.serviceWorker.register('/service-worker.js').then(function() { 7 | //- console.log('CLIENT: service worker registration complete.'); 8 | }, function() { 9 | //- console.log('CLIENT: service worker registration failure.'); 10 | }); 11 | } 12 | else { 13 | //- console.log('CLIENT: service worker is not supported.'); 14 | } 15 | script(async defer data-domain="cfnchecklist.com" src="https://plausible.io/js/plausible.js" doctype) 16 | -------------------------------------------------------------------------------- /src/views/base/before-scripts.pug: -------------------------------------------------------------------------------- 1 | script. 2 | (function(d, s, id) { 3 | var js, fjs = d.getElementsByTagName(s)[0]; 4 | if (d.getElementById(id)) return; 5 | js = d.createElement(s); js.id = id; 6 | js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.10&appId=1824577741189279"; 7 | fjs.parentNode.insertBefore(js, fjs); 8 | }(document, 'script', 'facebook-jssdk')); 9 | -------------------------------------------------------------------------------- /src/views/base/footer.pug: -------------------------------------------------------------------------------- 1 | footer.page-footer 2 | .s-footer 3 | .s-footer__grateful 4 | p Icons made by #[a(href='http://www.freepik.com' target='_blank', rel="nofollow noopener noreferrer") Freepik] from #[a(href='https://www.flaticon.com/', title='Flaticon' rel="nofollow noopener noreferrer") www.flaticon.com] is licensed by #[a(href='http://creativecommons.org/licenses/by/3.0/', title='Creative Commons BY 3.0', target='_blank', rel="noopener noreferrer") CC 3.0 BY] 5 | 6 | .s-footer__made 7 | p Sister project: #[a(href='https://lambdachecklist.com') Lambda checklist] 8 | p Made with ❤️ by #[a(href="https://github.com/jeshan") Jeshan G. BABOOA] based on the original #[a(href='https://frontendchecklist.io') Front-End Checklist by David Dias]. 9 | -------------------------------------------------------------------------------- /src/views/base/head.pug: -------------------------------------------------------------------------------- 1 | 2 | script(async src="https://www.googletagmanager.com/gtag/js?id=UA-109578378-1") 3 | script. 4 | window.dataLayer = window.dataLayer || []; 5 | function gtag(){dataLayer.push(arguments);} 6 | gtag('js', new Date()); 7 | 8 | gtag('config', 'UA-109578378-1'); 9 | script(src="https://platform.twitter.com/widgets.js" async) 10 | script(src="https://buttons.github.io/buttons.js" async) 11 | 12 | meta(charset="utf-8") 13 | 14 | 15 | meta(name='viewport', content='width=device-width, initial-scale=1') 16 | 17 | 18 | title #[=translation.SITE_NAME] - #[=translation.INDEX_TITLE] 19 | 20 | 21 | meta(name='description', content=translation.SITE_DESCRIPTION) 22 | meta(name='google-site-verification', content='vRDgryN3FdAjofpwH1OtdCb9c8VHscUG8VQ6jjnjlb4') 23 | 24 | style. 25 | @charset "UTF-8";ul{list-style:none}*,:after,:before{box-sizing:inherit}html{background-color:#ededed;min-width:100%;overflow-x:hidden;overflow-y:scroll;box-sizing:border-box;font-size:62.5%;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;scroll-behavior:smooth}body{color:#212529;font-size:1rem;font-weight:400;line-height:1.5;font-family:BlinkMacSystemFont,-apple-system,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Helvetica,Arial,sans-serif;margin:0}h1,h2{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.2;color:inherit}h1{margin:0;padding:15px 0 0;font-weight:700;font-size:2.5rem}h2{font-size:2rem}.img-logo{width:85px;height:85px}a{text-decoration:none}a:not(:disabled){background-color:transparent;color:#d75637}p{font-size:1.2rem;line-height:1.5;margin-top:0}.icon{width:18px;height:18px;fill:#b5b5b5;display:inline-block;text-align:center;speak:none;-webkit-backface-visibility:hidden;backface-visibility:hidden;opacity:1}button .icon{margin:0 .7rem 0 -.2rem}.page-wrapper{display:grid;grid-template-columns:auto;grid-template-areas:"a" "b" "c"}.page-wrapper .page-header{grid-area:a}.page-wrapper .page-main{grid-area:b}.sub-heading{margin:5px 15px;font-family:Montserrat,sans-serif;color:hsla(0,0%,100%,.6);font-size:1.3rem;line-height:1.5}.s-header{grid-row:1;grid-column-start:2;padding:15px 0;background-color:#415257;background-image:linear-gradient(45deg,#415257,#292e49);color:#fff;font-size:1rem;text-align:center;position:relative}.s-header__media__list{display:flex;align-items:flex-start;justify-content:center;align-self:center;padding:0}.s-header__media__item{margin:0 5px}.s-header__lang__list{display:flex;flex-direction:row;align-items:flex-start;align-self:center;justify-content:center;margin-top:20px}.s-header__lang__item{margin:0 5px}.c-tag-filter{display:flex;align-items:center;justify-content:space-between;align-self:center;flex-flow:row wrap}.c-tag-filter__title{font-size:1.2rem;font-weight:700}.c-tag-filter__list{flex-direction:row;width:100%;margin:0;padding:10px;background-color:#f8f7f6}.c-tag-filter__item{display:inline-block}.c-tag-filter__item button{display:block;text-transform:uppercase;font-size:1.3rem;line-height:1;color:#121212;padding:5px 10px;border:2px solid transparent;background-color:transparent}.s-main{display:grid;grid-column-start:1;position:relative;background-color:#fff;text-align:left;padding:0 10px}.s-main__section{margin:0 0 10px}.s-main__section:first-child{order:2}.s-main__section:nth-child(2),.s-main__section:nth-child(3){order:4}.s-main__meta{display:none}.s-checklist{position:relative;min-height:1px}.s-checklist:after{content:"";position:absolute;height:2px;border-top:1px solid #e4ebf0;bottom:0;width:98%}.s-checklist__inner{display:grid}.s-checklist__header{padding:15px 0;z-index:4}.s-checklist__header__title{margin:0;text-align:left;color:#415257;font-weight:700;text-transform:uppercase;line-height:1.2;font-size:2.5rem}.c-top-alert{display:none}.no-js .c-top-alert--alert{min-height:35px;background:orange;width:100%;text-align:center;font-weight:700;color:#fff;font-size:1.3rem;padding:5px 0}.no-js .c-top-alert--alert p{margin:0}button{-moz-appearance:none;-webkit-appearance:none;align-items:center;border:1px solid transparent;border-radius:3px;box-shadow:none;display:inline-flex;font-size:1.3rem;padding:7px 10px;justify-content:flex-start;line-height:1.4;position:relative;vertical-align:top;-webkit-touch-callout:none;background-color:#fff;border-color:#999;color:#363636;justify-content:center;text-align:center;white-space:nowrap}.c-button-icon{border-color:#b5b5b5}.c-button-icon .icon{margin:0}.c-button__group{margin:0 5px}.github-corner svg{fill:#fff;color:#151513;position:absolute;top:0;border:0;right:0}.github-corner .octo-arm{-webkit-transform-origin:130px 106px;transform-origin:130px 106px}@-webkit-keyframes a{0%,to{-webkit-transform:rotate(0);transform:rotate(0)}20%,60%{-webkit-transform:rotate(-25deg);transform:rotate(-25deg)}40%,80%{-webkit-transform:rotate(10deg);transform:rotate(10deg)}}@keyframes a{0%,to{-webkit-transform:rotate(0);transform:rotate(0)}20%,60%{-webkit-transform:rotate(-25deg);transform:rotate(-25deg)}40%,80%{-webkit-transform:rotate(10deg);transform:rotate(10deg)}}.c-tools{display:flex}.c-tools .c-button__group:first-child{margin-left:0}.c-progress{position:relative;display:flex;padding:10px 0}.c-progress__counter{text-align:center;margin-left:20px}.c-progress__label{font-size:1.3rem;font-weight:600}.c-progress__text{font-size:1.3rem}.c-progress__bar{background-color:#f5f5f5;border:0;height:19px;border-radius:3px;width:120px}.c-progress__bar::-webkit-progress-value{border-radius:2px}.c-progress__bar::-webkit-progress-bar{background-color:#f5f5f5}@media screen and (min-width:500px){h1{font-size:3rem}.img-logo{width:100px;height:100px}.icon{width:15px;height:15px}.sub-heading{font-size:1.5rem;margin:15px}.s-header{padding-top:25px;padding-bottom:60px}.s-header__banner{width:795px;margin:0 auto}.c-tag-filter__title{font-size:1.3rem}.s-main{display:grid;grid-column-start:3;margin-top:-50px;width:100%;padding:10px;border-radius:5px;border:1px solid rgba(0,0,0,.1)}.s-main__section:first-child,.s-main__section:nth-child(2){padding:0 20px}.s-main__meta{display:flex;justify-content:flex-end}.s-checklist{padding:0 10px}.s-checklist__header{display:flex;align-items:center;justify-content:space-between}.s-checklist__title{display:flex;align-items:center;justify-content:center}.s-checklist__header__title{font-size:4rem;margin:0}.c-progress{padding:0;display:block}.c-progress__counter{margin:0}.c-progress__label{position:absolute;top:28%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.c-progress__text{font-size:1.1rem;padding:0 5px}.c-progress__bar{height:20px;margin:0 20px}}@media screen and (min-width:801px){.page-wrapper{grid-template-areas:"a a a" "b b b" "c c c"}.page-wrapper .page-main{display:grid;grid-template-columns:auto 150px 960px 150px auto}.c-tag-filter{display:flex;flex-direction:row}.c-tag-filter__list{display:flex;flex-wrap:wrap}button{font-size:1.25rem;height:34px;padding:5px 9px}}@media (max-width:500px){.github-corner .octo-arm{-webkit-animation:a .56s ease-in-out;animation:a .56s ease-in-out}} 26 | 27 | link(rel="preload" as="style" href="/styles/main.min.css" onload="this.rel='stylesheet'") 28 | 29 | noscript 30 | link(rel="stylesheet" href="/styles/main.min.css") 31 | 32 | script. 33 | /*! loadCSS. [c]2017 Filament Group, Inc. MIT License */ 34 | !function(a){"use strict";var b=function(b,c,d){function j(a){if(e.body)return a();setTimeout(function(){j(a)})}function l(){f.addEventListener&&f.removeEventListener("load",l),f.media=d||"all"}var g,e=a.document,f=e.createElement("link");if(c)g=c;else{var h=(e.body||e.getElementsByTagName("head")[0]).childNodes;g=h[h.length-1]}var i=e.styleSheets;f.rel="stylesheet",f.href=b,f.media="only x",j(function(){g.parentNode.insertBefore(f,c?g:g.nextSibling)});var k=function(a){for(var b=f.href,c=i.length;c--;)if(i[c].href===b)return a();setTimeout(function(){k(a)})};return f.addEventListener&&f.addEventListener("load",l),f.onloadcssdefined=k,k(l),f};"undefined"!=typeof exports?exports.loadCSS=b:a.loadCSS=b}("undefined"!=typeof global?global:this); 35 | /*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */ 36 | !function(a){if(a.loadCSS){var b=loadCSS.relpreload={};if(b.support=function(){try{return a.document.createElement("link").relList.supports("preload")}catch(a){return!1}},b.poly=function(){for(var b=a.document.getElementsByTagName("link"),c=0;c 39 | link(rel="icon", type="image/png", sizes="32x32", href="/favicon-32x32.png") 40 | link(rel="icon", type="image/png", sizes="16x16", href="/favicon-16x16.png") 41 | 42 | 43 | link(rel="apple-touch-icon", sizes="180x180", href="/apple-touch-icon.png") 44 | 45 | 46 | meta(name="msapplication-config" content="/browserconfig.xml") 47 | 48 | link(rel="mask-icon", href="/safari-pinned-tab.svg", color="#5bbad5") 49 | meta(name="theme-color" content="#2b2b2b") 50 | meta(name="msapplication-TileColor" content="#2b2b2b") 51 | meta(name="msapplication-TileImage" content="/icons/mstile-144x144.png") 52 | 53 | link(rel="manifest" href="/manifest.json") 54 | 55 | include social 56 | 57 | script 58 | include ../../modernizr-custom.min.js 59 | -------------------------------------------------------------------------------- /src/views/base/header.pug: -------------------------------------------------------------------------------- 1 | header.s-header 2 | 3 | include ../components/c-corner 4 | 5 | .s-header__banner 6 | .text-center 7 | h1.font-weight-bold= translation.SITE_NAME 8 | p.sub-heading= translation.SITE_TAGLINE 9 | 10 | .s-header__media 11 | ul.s-header__media__list 12 | li.s-header__media__item 13 | a(href="https://twitter.com/share?ref_src=twsrc%5Etfw" class="twitter-share-button" data-text=translation.social.TWITTER_MSG data-via=translation.social.TWITTER_VIA data-hashtags=translation.social.TWITTER_HASHTAGS data-show-count="false")= translation.social.TWEET 14 | li.s-header__media__item 15 | a.github-button(href=translation.URL_GITHUB_ROOT data-icon="octicon-star" data-show-count="true" aria-label=translation.social.GITHUB_STAR_MSG)= translation.social.STAR 16 | -------------------------------------------------------------------------------- /src/views/base/layout.pug: -------------------------------------------------------------------------------- 1 | block vars 2 | - listSections = [] 3 | 4 | //- Inject all mixins to be able to call these 5 | include ../mixins/mixins 6 | 7 | 8 | doctype html 9 | 10 | html.no-js(lang=translation.SITE_LANGUAGE, dir=translation.SITE_DIRECTION) 11 | head 12 | 13 | //- Include the HEAD of the page 14 | include head 15 | body 16 | 17 | //- Scripts needed to be load just after the body 18 | include before-scripts 19 | 20 | //- Website wrapper 21 | .page-wrapper 22 | 23 | .page-header 24 | //- Top banners with informations for the user 25 | include ../components/c-top-alert 26 | 27 | include header 28 | 29 | .page-main 30 | 31 | //- The main tag is not include in the block main because repeted 32 | main.s-main#js-main 33 | block main 34 | 35 | .page-footer 36 | include footer 37 | 38 | //- Scripts loading 39 | include after-scripts 40 | -------------------------------------------------------------------------------- /src/views/base/social.pug: -------------------------------------------------------------------------------- 1 | meta(property="og:site_name" content="CloudFormation Checklist") 2 | meta(property="og:url" content=translation.URL_WEBSITE) 3 | meta(property="og:type" content="website") 4 | meta(property="og:title" content=translation.INDEX_TITLE) 5 | meta(property="og:description" content=translation.SITE_DESCRIPTION) 6 | meta(property="og:image" content="https://everywhere-8a59.kxcdn.com/img/social/facebook-banner.jpg") 7 | meta(property="og:image:type" content="image/jpeg") 8 | meta(property="og:image:width" content="1200") 9 | meta(property="og:image:height" content="630") 10 | meta(property="og:image:alt" content="The NEW CloudFormation Checklist - Start, Check, Generate and enjoy!") 11 | meta(property="og:locale" content="en_US") 12 | 13 | meta(name="twitter:card" content="summary_large_image") 14 | meta(name="twitter:site" content="@jeshan25") 15 | meta(name="twitter:creator" content="@jeshan25") 16 | meta(name="twitter:image" content="https://everywhere-8a59.kxcdn.com/img/social/facebook-banner.jpg") 17 | -------------------------------------------------------------------------------- /src/views/components/c-checklist.pug: -------------------------------------------------------------------------------- 1 | .c-checklist 2 | 3 | //- Grab introduction based on the name of the section 4 | if introductions[0][sectionTitle.toLowerCase()] 5 | 6 | each el in introductions[0][sectionTitle.toLowerCase()] 7 | 8 | p.c-checklist__intro= el 9 | 10 | //- List of all items elements based on the dataSection variable 11 | ul.c-checklist__list 12 | 13 | each item, i in dataSection 14 | 15 | include ../components/checklist/class 16 | 17 | //- data-item-check (false or true) => Element is checked or not 18 | //- data-item-priority (high, medium or low) => The priority of the task 19 | //- data-item-id (number) => the ID of the elements based on dataSection array 20 | //- data-item-dropdown (open close or false) => State of the dropdown 21 | //- data-item-visible (true or false) => Visibility of the item used by the tag filter 22 | li.c-checklist__item.js-item(class=` ${allClass}`, data-item-priority=priorityClass ,data-item-id=i, data-item-check="false", data-item-dropdown=dataItemDropdown, data-item-visible="true" aria-hidden="false") 23 | 24 | .c-checklist__item__inner 25 | 26 | .c-checklist__body 27 | 28 | //- Columns of the item 29 | 30 | //- Priority column 31 | include ../components/checklist/priority 32 | 33 | //- Checkbox column 34 | include ../components/checklist/checkbox 35 | 36 | //- Label column 37 | include ../components/checklist/label 38 | 39 | //- Dropdown column 40 | include ../components/checklist/dropdown 41 | -------------------------------------------------------------------------------- /src/views/components/c-corner.pug: -------------------------------------------------------------------------------- 1 | .c-github-corner 2 | a(href=translation.URL_GITHUB_ROOT class="github-corner" aria-label="View source on Github") 3 | svg(width="80" height="80" viewBox="0 0 250 250" aria-hidden="true") 4 | path(d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z") 5 | path(d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" class="octo-arm") 6 | path(d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body") 7 | -------------------------------------------------------------------------------- /src/views/components/c-nav.pug: -------------------------------------------------------------------------------- 1 | nav.c-nav 2 | ul.c-nav__list.c-nav--inline 3 | 4 | each item, i in listSections 5 | li.c-nav__item 6 | a.c-button.js-scroll(href=`#section-${item}`) 7 | span.c-nav__status(id=`js-nav-${item}`, data-notation="0") 8 | span= item 9 | -------------------------------------------------------------------------------- /src/views/components/c-new-form.pug: -------------------------------------------------------------------------------- 1 | .s-form 2 | .form 3 | 4 | .form-group 5 | input(type="text", id="project-name" name="project-name", title=translation.form.TITLE_PROJECT_NAME required) 6 | label(for="project-name")= translation.form.LABEL_PROJECT_NAME 7 | 8 | .form-group 9 | input(type="text", id="page-title" name="page-title", title=translation.form.TITLE_PAGE_TITLE required) 10 | label(for="page-title")= translation.form.LABEL_PAGE_TITLE 11 | 12 | .form-group 13 | input(type="text", id="developer-name" name="developer-name", title=translation.form.TITLE_DEVELOPER_NAME required) 14 | label(for="developer-name")= translation.form.LABEL_DEVELOPER_NAME 15 | 16 | span#generated-date 17 | 18 | .s-meta 19 | 20 | button.button.btn--danger.js-reset-all 21 | +svg-icon.icon.icon-reset(data-icon-name='reset') 22 | | #[=translation.form.BUTTON_START_NEW] 23 | -------------------------------------------------------------------------------- /src/views/components/c-notation.pug: -------------------------------------------------------------------------------- 1 | .c-notation 2 | 3 | - priority = [] 4 | - totalHigh = [] 5 | - totalMedium = [] 6 | - totalLow = [] 7 | 8 | each item, i in locals._items 9 | 10 | each item2 in item 11 | 12 | - priority.push(item2.priority) 13 | 14 | each el, i in priority 15 | 16 | if (el == 'High') 17 | - totalHigh.push('1') 18 | 19 | else if (el == 'Medium') 20 | - totalMedium.push('1') 21 | 22 | else 23 | - totalLow.push('1') 24 | 25 | .c-notation__details 26 | ul.c-notation__details__list 27 | li.c-notation__details__item.js-detail-high 28 | +svg-icon.icon.icon--small.icon-priority--high(data-icon-name='bullet') 29 | span.js-notation-checked 0 30 | |/ 31 | span.js-notation-total= totalHigh.length 32 | |  #[=translation.HIGH_CHECKED] 33 | li.c-notation__details__item.js-detail-medium 34 | +svg-icon.icon.icon--small.icon-priority--medium(data-icon-name='bullet') 35 | span.js-notation-checked 0 36 | |/ 37 | span.js-notation-total= totalMedium.length 38 | |  #[=translation.MEDIUM_CHECKED] 39 | li.c-notation__details__item.js-detail-low 40 | +svg-icon.icon.icon--small.icon-priority--low(data-icon-name='bullet') 41 | span.js-notation-checked 0 42 | |/ 43 | span.js-notation-total= totalLow.length 44 | |  #[=translation.LOW_CHECKED] 45 | 46 | .c-notation__box 47 | #js-notation.c-notation__item(data-notation="0") 48 | span.c-notation__letter X 49 | 50 | 51 | .c-notation__generate 52 | button.button.--fill.--large.js-print 53 | +svg-icon.icon.icon-print(data-icon-name='print') 54 | | #[=translation.form.BUTTON_GENERATE_PRINT] 55 | -------------------------------------------------------------------------------- /src/views/components/c-progress.pug: -------------------------------------------------------------------------------- 1 | .c-progress 2 | progress.c-progress__bar.js-progress(value="0", max="100") 3 | .c-progress__counter 4 | span.c-progress__label 0 % 5 | span.c-progress__text  #[=sectionTitle] #[=translation.PERCENTAGE_CHECKED] 6 | -------------------------------------------------------------------------------- /src/views/components/c-search.pug: -------------------------------------------------------------------------------- 1 | .c-search 2 | .form 3 | .form-group 4 | input.search(type="search" id="search") 5 | label(for="search")= translation.form.LABEL_SEARCH 6 | -------------------------------------------------------------------------------- /src/views/components/c-tag-filter.pug: -------------------------------------------------------------------------------- 1 | //- Array container for each filters 2 | - tagFilter = [] 3 | 4 | //- Loop to save each tags present in JSON into tagFilter 5 | each item, i in _items 6 | 7 | each tagList in item 8 | 9 | each tag in tagList.tags 10 | 11 | //- Check if the filter tag already exist in the tagFilter array 12 | - if (tagFilter.indexOf(tag) === -1) 13 | - tagFilter.push(tag) 14 | 15 | //- Filter code implementation 16 | aside.c-tag-filter 17 | h2.c-tag-filter__title= translation.SECTION_TAG 18 | 19 | ul.c-tag-filter__list 20 | 21 | //- Loop to grab each title in the tagFilter array 22 | each title in tagFilter 23 | 24 | //- Add uppercase in the first letter 25 | - titleFilter = title.charAt(0).toUpperCase() + title.slice(1) 26 | 27 | //- Slip and add a dash if the tag is more than 1 word 28 | - dataFilter = title.split(' ').join('-') 29 | 30 | //- Check if title is not empty before printing it 31 | if title != '' 32 | li.c-tag-filter__item 33 | button.js-filter-tag(data-tag=`${dataFilter}`)= titleFilter 34 | -------------------------------------------------------------------------------- /src/views/components/c-tools.pug: -------------------------------------------------------------------------------- 1 | .c-tools 2 | 3 | .c-button__group 4 | button.c-button-icon.js-check-all(title="Check all items") 5 | +svg-icon.icon.icon-check(data-icon-name='check') 6 | 7 | button.c-button-icon.js-uncheck-all(title="Uncheck all items") 8 | +svg-icon.icon.icon-uncheck(data-icon-name='check') 9 | 10 | .c-button__group 11 | button.c-button-icon.js-expand-all(title="Expand all items") 12 | +svg-icon.icon.icon-check(data-icon-name='expand') 13 | 14 | button.c-button-icon.js-collapse-all(title="Collapse all items") 15 | +svg-icon.icon.icon-uncheck(data-icon-name='collapse') 16 | 17 | .c-button__group 18 | button.c-button-icon.js-hide-section(title="Hide current section") 19 | +svg-icon.icon.icon-eye(data-icon-name='eye') 20 | -------------------------------------------------------------------------------- /src/views/components/c-top-alert.pug: -------------------------------------------------------------------------------- 1 | .c-top-alert 2 | .c-top-alert--alert 3 | p= translation.alert.JAVASCRIPT_DESACTIVATE 4 | -------------------------------------------------------------------------------- /src/views/components/checklist/checkbox.pug: -------------------------------------------------------------------------------- 1 | .c-checklist__column.c-checklist__checkbox 2 | input.checkbox(type="checkbox" name=`c-checklist__item-${sectionTitle.toLowerCase()}-${i}` id=`c-checklist__item-${sectionTitle.toLowerCase()}-${i}`) 3 | +svg-icon.icon.icon-checkbox(data-icon-name='check') 4 | +svg-icon.icon.icon-checked(data-icon-name='check') 5 | -------------------------------------------------------------------------------- /src/views/components/checklist/class.pug: -------------------------------------------------------------------------------- 1 | - allClass = '' 2 | - dataItemDropdown 3 | - priorityClass = item.priority.toLowerCase() 4 | 5 | //- Iteration over tags array 6 | each tag in item.tags 7 | 8 | //- Create an empty array to put all values from tags array 9 | - var tempClass = [] 10 | 11 | //- Slip and add a dash if the tag is more than 1 word 12 | - tag = tag.toLowerCase().split(' ').join('-') 13 | 14 | //- Push array value into tempClass array 15 | - tempClass.push(tag) 16 | 17 | - for (var i = 0; i < tempClass.length; i++) 18 | - allClass += tempClass[i] + ' '; 19 | 20 | if item.tools || item.documentation || item.code || item.video 21 | - dataItemDropdown = "open" 22 | else 23 | - dataItemDropdown = "false" 24 | -------------------------------------------------------------------------------- /src/views/components/checklist/dropdown.pug: -------------------------------------------------------------------------------- 1 | .c-checklist__column.c-checklist__dropdown 2 | 3 | //- Check if the item has content, if not hide the button 4 | if item.tools || item.documentation || item.code || item.video 5 | button.button-icon.js-dropdown.c-checklist__dropdown__button(aria-label="Open the description section") 6 | +svg-icon.icon.icon-arrow(data-icon-name='arrow') 7 | -------------------------------------------------------------------------------- /src/views/components/checklist/label.pug: -------------------------------------------------------------------------------- 1 | .c-checklist__column.c-checklist__label 2 | label(for=`c-checklist__item-${sectionTitle.toLowerCase()}-${i}`) 3 | 4 | span.label__title= item.title + ': ' 5 | span.label__description #{item.description} 6 | 7 | if item.tools || item.documentation || item.code || item.video 8 | .c-checklist__details.js-details 9 | 10 | if item.detail 11 | p.c-checklist__text= item.detail 12 | 13 | if item.code 14 | .c-checklist__code 15 | .c-checklist__code__loader.js-loader 16 | .code-icons 17 | .c-checklist__code__github.js-code(data-code-url=`${item.code}`) 18 | 19 | if item.documentation 20 | .c-checklist__documentation 21 | h4= translation.SECTION_DOCUMENTATION 22 | ul.documentation__list 23 | each documentation, i in item.documentation 24 | li 25 | if documentation.url && documentation.title 26 | img.c-checklist__favicon(src="/img/icons/1x1.png" data-src=`https://www.google.com/s2/favicons?domain_url=${documentation.url}`, alt="") 27 | a(href=`${documentation.url}`, target="_blank", rel="noopener noreferrer")= documentation.title 28 | 29 | if item.tools 30 | .c-checklist__tools 31 | h4= translation.SECTION_TOOL 32 | ul.documentation__list 33 | each tool, i in item.tools 34 | li 35 | img.c-checklist__favicon(src="/img/icons/1x1.png" data-src=`https://www.google.com/s2/favicons?domain_url=${tool.url}`, alt="") 36 | a(href=`${tool.url}`, target="_blank", rel="noopener noreferrer")= tool.title 37 | 38 | if item.video 39 | .c-checklist__video 40 | h4= translation.SECTION_VIDEO 41 | ul.video__list 42 | each video, i in item.video 43 | li 44 | img.c-checklist__favicon(src="/img/icons/1x1.png" data-src=`https://www.google.com/s2/favicons?domain_url=${video.url}`, alt="") 45 | a(href=`${video.url}`, target="_blank", rel="noopener noreferrer")= video.title 46 | 47 | .c-tags 48 | ul.c-tags__list 49 | each tag in item.tags 50 | if tag == 'all' 51 | else 52 | li.c-tags__item= tag.toUpperCase() 53 | -------------------------------------------------------------------------------- /src/views/components/checklist/priority.pug: -------------------------------------------------------------------------------- 1 | .c-checklist__column.c-checklist__priority 2 | 3 | if item.priority == "High" 4 | +svg-icon.icon.icon--small.icon-priority--high(aria-label="High priority item" data-icon-name='bullet') 5 | 6 | else if item.priority == "Medium" 7 | +svg-icon.icon.icon--small.icon-priority--medium(aria-label="Medium priority item" data-icon-name='bullet') 8 | 9 | else 10 | +svg-icon.icon.icon--small.icon-priority--low(aria-label="Low priority item", data-icon-name='bullet') 11 | -------------------------------------------------------------------------------- /src/views/components/s-section-bottom.pug: -------------------------------------------------------------------------------- 1 | .s-main__section.s-main__newsletter 2 | 3 | //- Form and navigation anchors 4 | //- Navigation is at the bottom of the page because of the `listSections` generated by each +section present in the article. 5 | header.s-main__section.s-main__header 6 | 7 | .s-header__checklist 8 | 9 | h2.s-header__checklist__title Report and navigation 10 | //- Form to create new checklist 11 | .s-header__checklist__el 12 | include c-new-form 13 | 14 | //- Navigation with anchors based on the sections 15 | .s-header__checklist__el 16 | include c-nav 17 | 18 | //- Letter notation based on number of items checked 19 | .s-header__checklist__el 20 | include c-notation 21 | -------------------------------------------------------------------------------- /src/views/components/s-section-top.pug: -------------------------------------------------------------------------------- 1 | //- List of all sections in the order the mixin +section is called in the article section. 2 | //- Array is populate by 3 | - listSections = [] 4 | 5 | //- Ids of each section generated by the mixin +section when called. 6 | - listSectionId = [] 7 | 8 | //- Section with Ad and search (TODO: to enable after first launch) 9 | .s-main__section.s-main__meta 10 | 11 | //- include c-search 12 | 13 | //- Section with tag filter 14 | .s-main__section.s-main__filter 15 | 16 | include c-tag-filter 17 | -------------------------------------------------------------------------------- /src/views/components/svg/arrow.pug: -------------------------------------------------------------------------------- 1 | svg(xmlns="http://www.w3.org/2000/svg" width='30', height='30' viewBox="0 0 98.148 98.149" aria-hidden="true")&attributes(attributes) 2 | title Arrow 3 | path(d="M97.562 64.692L50.49 17.618c-.75-.75-2.078-.75-2.828 0L.586 64.693C.21 65.068 0 65.577 0 66.106c0 .53.21 1.04.586 1.414l12.987 12.987c.39.39.902.586 1.414.586.512 0 1.023-.195 1.414-.586l32.675-32.674L81.75 80.506c.75.75 2.078.75 2.828 0L97.562 67.52c.782-.78.782-2.048 0-2.828z") 4 | -------------------------------------------------------------------------------- /src/views/components/svg/bullet.pug: -------------------------------------------------------------------------------- 1 | svg(xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 510 510" aria-hidden="true")&attributes(attributes) 2 | path(d="M255 0C114.75 0 0 114.75 0 255s114.75 255 255 255 255-114.75 255-255S395.25 0 255 0z") 3 | -------------------------------------------------------------------------------- /src/views/components/svg/check.pug: -------------------------------------------------------------------------------- 1 | svg(xmlns='http://www.w3.org/2000/svg', width='30', height='30' viewbox='-432 234 94 94' aria-hidden="true")&attributes(attributes) 2 | title Check 3 | path(d='M-349 245v72h-72v-72h72m5-11h-82c-3.3 0-6 2.7-6 6v82c0 3.3 2.7 6 6 6h82c3.3 0 6-2.7 6-6v-82c0-3.3-2.7-6-6-6z') 4 | path.path-check(d='M-349.2 261.1l-7.2-7.2c-.8-.8-2-.8-2.8 0l-37.3 37.3-13.3-13.3c-.8-.8-2-.8-2.8 0l-7.2 7.2c-.4.4-.6.9-.6 1.4s.2 1 .6 1.4l21.9 21.9c.4.4.9.6 1.4.6.5 0 1-.2 1.4-.6l45.9-45.9c.4-.4.6-.9.6-1.4 0-.5-.2-1-.6-1.4z') 5 | -------------------------------------------------------------------------------- /src/views/components/svg/checkbox.pug: -------------------------------------------------------------------------------- 1 | svg(xmlns="http://www.w3.org/2000/svg" width='30', height='30' viewBox="0 0 94 94" aria-hidden="true")&attributes(attributes) 2 | path(d="M94 88c0 3.312-2.688 6-6 6H6c-3.314 0-6-2.688-6-6V6c0-3.313 2.686-6 6-6h82c3.312 0 6 2.687 6 6v82z") 3 | -------------------------------------------------------------------------------- /src/views/components/svg/checked.pug: -------------------------------------------------------------------------------- 1 | svg(xmlns="http://www.w3.org/2000/svg" width='30', height='30' viewBox="0 0 330 330" aria-hidden="true")&attributes(attributes) 2 | path(d="M315 0H15C6.716 0 0 6.716 0 15v300c0 8.284 6.716 15 15 15h300c8.284 0 15-6.716 15-15V15c0-8.284-6.716-15-15-15zm-49.394 107.796l-135.62 135.62c-2.813 2.814-6.63 4.394-10.606 4.394-3.98 0-7.794-1.58-10.607-4.393l-44.38-44.38c-5.858-5.86-5.858-15.356 0-21.214 5.858-5.857 15.354-5.857 21.214 0l33.772 33.774L244.393 86.583c5.857-5.858 15.355-5.858 21.213 0 5.858 5.857 5.858 15.355 0 21.213z") 3 | -------------------------------------------------------------------------------- /src/views/components/svg/code.pug: -------------------------------------------------------------------------------- 1 | svg(xmlns='http://www.w3.org/2000/svg', width='30', height='30', viewbox='0 0 92.812 92.812' aria-hidden="true")&attributes(attributes) 2 | path.code-path-left(d='M92.226 44.992L60.97 13.736c-.78-.78-2.047-.78-2.827 0l-6.73 6.728c-.374.375-.585.884-.585 1.414 0 .53.21 1.04.586 1.414L74.53 46.406 51.415 69.52c-.375.376-.586.885-.586 1.415s.21 1.04.586 1.415l6.73 6.728c.39.39.902.585 1.414.585s1.024-.195 1.415-.586L92.227 47.82c.78-.78.78-2.047 0-2.828z') 3 | path.code-path-right(d='M18.283 46.406l23.114-23.114c.375-.375.586-.884.586-1.414 0-.53-.21-1.04-.586-1.414l-6.728-6.728c-.782-.78-2.05-.78-2.83 0L.587 44.992C.21 45.367 0 45.876 0 46.406s.21 1.04.586 1.414l31.26 31.256c.375.375.884.586 1.415.586.53 0 1.04-.21 1.415-.586l6.724-6.728c.78-.78.78-2.05 0-2.828L18.282 46.406z') 4 | -------------------------------------------------------------------------------- /src/views/components/svg/collapse.pug: -------------------------------------------------------------------------------- 1 | svg(xmlns='http://www.w3.org/2000/svg', width='30', height='30', viewbox='0 0 510 510' aria-hidden="true")&attributes(attributes) 2 | title Minus Collapse 3 | path(d='M255 0C114.75 0 0 114.75 0 255s114.75 255 255 255 255-114.75 255-255S395.25 0 255 0zm127.5 280.5h-255v-51h255v51z') 4 | -------------------------------------------------------------------------------- /src/views/components/svg/expand.pug: -------------------------------------------------------------------------------- 1 | svg(xmlns='http://www.w3.org/2000/svg', width='30', height='30', viewbox='0 0 510 510' aria-hidden="true")&attributes(attributes) 2 | title Plus Expand 3 | path(d='M280.5 127.5h-51v102h-102v51h102v102h51v-102h102v-51h-102v-102zM255 0C114.75 0 0 114.75 0 255s114.75 255 255 255 255-114.75 255-255S395.25 0 255 0zm0 459c-112.2 0-204-91.8-204-204S142.8 51 255 51s204 91.8 204 204-91.8 204-204 204z') 4 | -------------------------------------------------------------------------------- /src/views/components/svg/eye.pug: -------------------------------------------------------------------------------- 1 | svg(xmlns='http://www.w3.org/2000/svg', width='30', height='30' viewbox='-430 231.5 98.5 98.5' aria-hidden="true")&attributes(attributes) 2 | path(d='M-332.8 277.3c-.9-1-21.5-24.9-48-24.9-26.4 0-47.1 23.9-48 24.9-1.7 2-1.7 4.9 0 6.9.9 1 21.5 24.9 48 24.9 26.4 0 47.1-23.9 48-24.9 1.7-2 1.7-4.9 0-6.9zm-39.9-10.6c1.8-1 4.3.1 5.5 2.5 1.3 2.3.9 5-.9 6s-4.3-.1-5.5-2.5c-1.3-2.3-.9-5 .9-6zm-8.1 33.8c-18.5 0-34-14.2-39.4-19.7 3.6-3.8 11.9-11.4 22.6-16-2.1 3.2-3.3 6.9-3.3 11 0 11.1 9 20.1 20.1 20.1s20.1-9 20.1-20.1c0-4.1-1.2-7.9-3.3-11 10.7 4.6 18.9 12.3 22.6 16-5.4 5.5-20.9 19.7-39.4 19.7z') 3 | path(d='M-349.675 239.41l8.202 8.202-74.245 74.246-8.202-8.203z') 4 | path.st0(d='M-416.16 322.39l75.095-75.093 3.465 3.465-75.094 75.094z') 5 | title Eye 6 | -------------------------------------------------------------------------------- /src/views/components/svg/print.pug: -------------------------------------------------------------------------------- 1 | svg(xmlns='http://www.w3.org/2000/svg', width='30', height='30', viewbox='0 0 94.168 94.168' aria-hidden="true")&attributes(attributes) 2 | title Printer 3 | path(d='M93.135 27.803H79.167v6.447c0 1.104-.896 2-2 2H17c-1.103 0-2-.896-2-2v-6.447H1.034c-.57 0-1.033.462-1.033 1.033v36.497c0 .57.462 1.033 1.033 1.033H15V59.92c0-1.105.897-2 2-2h60.167c1.104 0 2 .895 2 2v6.446h13.968c.57 0 1.033-.463 1.033-1.033V28.836c0-.57-.463-1.033-1.033-1.033zM82.98 52.625c-2.425 0-4.392-1.965-4.392-4.39 0-2.424 1.967-4.39 4.39-4.39s4.39 1.966 4.39 4.39c0 2.426-1.968 4.39-4.39 4.39z') 4 | path(d='M75.617 22.522c0-.55-.22-1.073-.605-1.46L61.57 7.62c-.39-.388-.913-.606-1.46-.606H20.615c-1.14 0-2.065.925-2.065 2.066v23.754h57.067V22.522zm-17.333 2.952c-.278 0-.546-.11-.742-.306-.196-.197-.308-.463-.308-.742l.002-12.498 13.546 13.546H58.284z') 5 | path(d='M18.55 85.088c0 1.14.926 2.065 2.066 2.065h52.936c1.142 0 2.065-.925 2.065-2.065v-23.42H18.55v23.42z') 6 | -------------------------------------------------------------------------------- /src/views/components/svg/reset.pug: -------------------------------------------------------------------------------- 1 | svg(xmlns='http://www.w3.org/2000/svg', width='30', height='30', viewbox='0 0 94.073 94.072' aria-hidden="true")&attributes(attributes) 2 | title Two arrows turn around 3 | path(d='M91.465 5.49c-.748-.31-1.61-.138-2.18.435L80.97 14.24C72.045 5.058 60.124 0 47.4 0c-2.693 0-5.408.235-8.07.697C21.22 3.845 6.543 17.405 1.945 35.244c-.155.6-.023 1.235.355 1.724.378.49.96.775 1.58.775h12.738c.84 0 1.59-.524 1.878-1.313 3.73-10.193 12.992-17.97 23.598-19.814 1.747-.303 3.525-.456 5.288-.456 8.428 0 16.3 3.374 22.168 9.5l-8.445 8.444c-.57.572-.742 1.432-.434 2.18.312.747 1.04 1.234 1.85 1.234H90.7c1.104 0 2-.896 2-2V7.338c0-.808-.49-1.537-1.235-1.847z') 4 | path(d='M90.192 56.328H77.455c-.84 0-1.59.523-1.878 1.312-3.73 10.193-12.992 17.972-23.598 19.814-1.75.303-3.526.456-5.29.456-8.427 0-16.3-3.374-22.167-9.5l8.444-8.444c.572-.572.743-1.432.434-2.18-.31-.747-1.038-1.234-1.847-1.234H3.373c-1.103 0-2 .896-2 2v28.18c0 .81.488 1.54 1.236 1.85.745.31 1.606.137 2.178-.436l8.316-8.315c8.922 9.184 20.843 14.242 33.57 14.242 2.692 0 5.407-.235 8.068-.697 18.112-3.146 32.79-16.708 37.387-34.547.154-.6.022-1.234-.355-1.725-.38-.488-.964-.775-1.583-.775z') 5 | -------------------------------------------------------------------------------- /src/views/index-en.pug: -------------------------------------------------------------------------------- 1 | extends base/layout 2 | 3 | //- Block main 4 | block main 5 | 6 | include components/s-section-top 7 | 8 | //- List each section (the order is linked to the order of the c-nav) 9 | //- _items is in the JSON injected in the view with gulp-data (called _project.json) 10 | .s-main__section.s-main__checklist 11 | 12 | if _items !== undefined 13 | 14 | +checklist-section({ 15 | dataSection: _items.before, 16 | sectionTitle: "Before" 17 | }) 18 | 19 | +checklist-section({ 20 | dataSection: _items.authoring, 21 | sectionTitle: "Authoring" 22 | }) 23 | 24 | +checklist-section({ 25 | dataSection: _items.security, 26 | sectionTitle: "Security" 27 | }) 28 | 29 | +checklist-section({ 30 | dataSection: _items.best_practices, 31 | sectionTitle: "Practices" 32 | }) 33 | 34 | +checklist-section({ 35 | dataSection: _items.management, 36 | sectionTitle: "Management" 37 | }) 38 | 39 | +checklist-section({ 40 | dataSection: _items.team, 41 | sectionTitle: "Team" 42 | }) 43 | 44 | +checklist-section({ 45 | dataSection: _items.testing, 46 | sectionTitle: "Testing" 47 | }) 48 | 49 | include components/s-section-bottom 50 | -------------------------------------------------------------------------------- /src/views/mixins/m-checklist-section.pug: -------------------------------------------------------------------------------- 1 | mixin checklist-section(options) 2 | 3 | - sectionId = '0'; //- Start with 0 to have 1 as first section 4 | - sectionTitle = options.sectionTitle || ''; 5 | - dataSection = options.dataSection || []; 6 | - filter = options.filter || 'all'; 7 | 8 | //- Push each section title to build navigation bar 9 | - listSections.push(sectionTitle.toLowerCase()) 10 | 11 | //- Add a 0 in the array everytime the mixin is called 12 | - listSectionId.push(sectionId++) 13 | 14 | 15 | //- Section 16 | section.s-checklist.js-section(class="s-checklist__" + sectionTitle.toLowerCase(), data-section=sectionTitle.toLowerCase(), data-section-id=`${listSectionId.length-1}`, id="section-" + sectionTitle.toLowerCase()) 17 | .s-checklist__inner 18 | .s-checklist__header 19 | 20 | .s-checklist__title 21 | 22 | h2.s-checklist__header__title= sectionTitle 23 | 24 | include ../components/c-progress 25 | 26 | include ../components/c-tools 27 | 28 | .s-checklist__body.js-checklist-body(data-body-visibility="visible", aria-hidden="false") 29 | 30 | //- Include checklist component 31 | include ../components/c-checklist 32 | -------------------------------------------------------------------------------- /src/views/mixins/m-icon.pug: -------------------------------------------------------------------------------- 1 | mixin svg-icon 2 | - 3 | var icon = attributes['data-icon-name'] 4 | 5 | case icon 6 | when 'checkbox' 7 | include ../components/svg/checkbox 8 | 9 | when 'checked' 10 | include ../components/svg/checked 11 | 12 | when 'arrow' 13 | include ../components/svg/arrow 14 | 15 | when 'bullet' 16 | include ../components/svg/bullet 17 | 18 | when 'reset' 19 | include ../components/svg/reset 20 | 21 | when 'eye' 22 | include ../components/svg/eye 23 | 24 | when 'check' 25 | include ../components/svg/check 26 | 27 | when 'collapse' 28 | include ../components/svg/collapse 29 | 30 | when 'expand' 31 | include ../components/svg/expand 32 | 33 | when 'print' 34 | include ../components/svg/print 35 | 36 | when 'code' 37 | include ../components/svg/code 38 | 39 | default 40 | include ../components/svg/checkbox 41 | -------------------------------------------------------------------------------- /src/views/mixins/mixins.pug: -------------------------------------------------------------------------------- 1 | include m-checklist-section 2 | include m-icon 3 | -------------------------------------------------------------------------------- /templates/app.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Description: 'Deploys this website at cfnchecklist.com' 3 | 4 | Parameters: 5 | DomainName: 6 | Type: String 7 | 8 | Resources: 9 | Bucket: 10 | Properties: 11 | AccessControl: Private 12 | BucketName: !Ref DomainName 13 | WebsiteConfiguration: 14 | ErrorDocument: error.html 15 | IndexDocument: index.html 16 | Type: AWS::S3::Bucket 17 | 18 | BucketPolicy: 19 | Type: AWS::S3::BucketPolicy 20 | Properties: 21 | Bucket: !Ref Bucket 22 | PolicyDocument: 23 | Version: 2008-10-17 24 | Statement: 25 | - Sid: PublicReadWebsite 26 | Effect: Allow 27 | Principal: 28 | AWS: '*' 29 | Action: ['s3:GetObject'] 30 | Resource: !Sub 'arn:aws:s3:::${Bucket}/*' 31 | 32 | Certificate: 33 | Properties: 34 | DomainName: !Sub '*.${DomainName}' 35 | DomainValidationOptions: 36 | - DomainName: !Sub '*.${DomainName}' 37 | ValidationDomain: !Ref Bucket 38 | SubjectAlternativeNames: 39 | - !Ref DomainName 40 | - !Sub '*.${DomainName}' 41 | ValidationMethod: DNS 42 | Type: AWS::CertificateManager::Certificate 43 | 44 | Cdn: 45 | Properties: 46 | DistributionConfig: 47 | Aliases: 48 | - !Ref Bucket 49 | DefaultCacheBehavior: 50 | AllowedMethods: 51 | - GET 52 | - HEAD 53 | CachedMethods: 54 | - GET 55 | - HEAD 56 | Compress: true 57 | DefaultTTL: 86400 58 | FieldLevelEncryptionId: '' 59 | ForwardedValues: 60 | Cookies: 61 | Forward: all 62 | QueryString: true 63 | MaxTTL: 31536000 64 | MinTTL: 86400 65 | SmoothStreaming: false 66 | TargetOriginId: !Sub ${DomainName}-S3-origin 67 | ViewerProtocolPolicy: redirect-to-https 68 | DefaultRootObject: index.html 69 | Enabled: true 70 | HttpVersion: http2 71 | IPV6Enabled: true 72 | Origins: 73 | - CustomOriginConfig: 74 | HTTPPort: 80 75 | HTTPSPort: 443 76 | OriginKeepaliveTimeout: 5 77 | OriginProtocolPolicy: http-only 78 | OriginReadTimeout: 30 79 | OriginSSLProtocols: 80 | - SSLv3 81 | - TLSv1 82 | DomainName: !Sub ${DomainName}.s3-website-us-east-1.amazonaws.com 83 | Id: !Sub ${DomainName}-S3-origin 84 | OriginPath: '' 85 | PriceClass: PriceClass_All 86 | Restrictions: 87 | GeoRestriction: 88 | RestrictionType: none 89 | ViewerCertificate: 90 | AcmCertificateArn: !Ref Certificate 91 | MinimumProtocolVersion: TLSv1 92 | SslSupportMethod: sni-only 93 | WebACLId: '' 94 | Type: AWS::CloudFront::Distribution 95 | 96 | Outputs: 97 | DistributionId: 98 | Value: !Ref Cdn 99 | -------------------------------------------------------------------------------- /templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Description: 'Deploys this website at cfnchecklist.com' 3 | 4 | Parameters: 5 | Cdn: 6 | Type: String 7 | DomainName: 8 | Type: String 9 | GithubRepo: 10 | Type: String 11 | Default: jeshan/cloudformation-checklist 12 | 13 | Resources: 14 | DeployCfnChecklist: 15 | Properties: 16 | Artifacts: 17 | Type: NO_ARTIFACTS 18 | BadgeEnabled: false 19 | Cache: 20 | Modes: 21 | - LOCAL_DOCKER_LAYER_CACHE 22 | - LOCAL_SOURCE_CACHE 23 | Type: LOCAL 24 | EncryptionKey: !Sub 'arn:aws:kms:${AWS::Region}:${AWS::AccountId}:alias/aws/s3' 25 | Environment: 26 | ComputeType: BUILD_GENERAL1_SMALL 27 | Image: aws/codebuild/docker:18.09.0 28 | PrivilegedMode: true 29 | Type: LINUX_CONTAINER 30 | LogsConfig: 31 | CloudWatchLogs: 32 | GroupName: !Ref 'LogGroup' 33 | Status: ENABLED 34 | ServiceRole: !GetAtt 'Role.Arn' 35 | Source: 36 | BuildSpec: !Sub |- 37 | version: 0.2 38 | 39 | env: 40 | variables: 41 | LC_ALL: C.UTF-8 42 | LANG: C.UTF-8 43 | 44 | phases: 45 | pre_build: 46 | commands: 47 | - pip3 install sceptre 48 | build: 49 | commands: 50 | - docker-compose build 51 | - docker-compose up dist 52 | - aws s3 sync dist/ s3://${DomainName} 53 | - aws cloudfront create-invalidation --distribution-id ${Cdn} --paths "/*" 54 | - sceptre --no-colour launch -y main 55 | GitCloneDepth: 1 56 | InsecureSsl: false 57 | Location: !Sub https://github.com/${GithubRepo} 58 | Type: GITHUB 59 | TimeoutInMinutes: 10 60 | Triggers: 61 | Webhook: true 62 | FilterGroups: 63 | - - Type: EVENT 64 | Pattern: PUSH 65 | - Type: HEAD_REF 66 | Pattern: '^refs/heads/master$' 67 | Type: AWS::CodeBuild::Project 68 | 69 | LogGroup: 70 | Type: AWS::Logs::LogGroup 71 | 72 | Role: 73 | Properties: 74 | AssumeRolePolicyDocument: 75 | Statement: 76 | - Action: sts:AssumeRole 77 | Effect: Allow 78 | Principal: 79 | Service: codebuild.amazonaws.com 80 | Version: '2012-10-17' 81 | Policies: 82 | - PolicyDocument: 83 | Statement: 84 | - Action: 85 | - logs:CreateLogGroup 86 | - logs:CreateLogStream 87 | - logs:DeleteLogGroup 88 | - logs:DescribeLogGroups 89 | - logs:PutLogEvents 90 | Effect: Allow 91 | Resource: 92 | - !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:*' 93 | - Action: 94 | - s3:* 95 | Effect: Allow 96 | Resource: 97 | - !Sub arn:aws:s3:::${DomainName} 98 | - !Sub arn:aws:s3:::${DomainName}/* 99 | - Action: 100 | - cloudformation:* 101 | Effect: Allow 102 | Resource: 103 | - !Sub arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/cloudformation-checklist-* 104 | - Action: 105 | - cloudfront:CreateInvalidation 106 | Effect: Allow 107 | Resource: '*' 108 | Version: '2012-10-17' 109 | PolicyName: cfn-checklist-pol 110 | Type: AWS::IAM::Role 111 | -------------------------------------------------------------------------------- /test/localstorage.test.js: -------------------------------------------------------------------------------- 1 | let expect = require('chai').expect; 2 | let assert = require('assert'); 3 | -------------------------------------------------------------------------------- /test/notation.test.js: -------------------------------------------------------------------------------- 1 | let expect = require('chai').expect; 2 | let assert = require('assert'); 3 | -------------------------------------------------------------------------------- /test/reports.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const assert = chai.assert; // Using Assert style 3 | const expect = chai.expect; // Using Expect style 4 | const should = chai.should(); // Using Should style 5 | 6 | const sinon = require('sinon'); 7 | const EventEmitter = require('events').EventEmitter; 8 | -------------------------------------------------------------------------------- /wallaby.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function (wallaby) { 3 | return { 4 | files: [ 5 | './src/scripts/**/*.js' 6 | ], 7 | 8 | tests: [ 9 | 'test/**/*.js' 10 | ] 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | 4 | const mainPath = path.resolve(__dirname, 'src/scripts', 'main.js'); 5 | const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); 6 | 7 | module.exports = { 8 | cache: true, 9 | mode: 'production', 10 | entry: { 11 | app: mainPath 12 | }, 13 | output: { 14 | filename: '[name].bundle.js', 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.js$/, 20 | exclude: /node_modules/, 21 | loader: 'babel-loader' 22 | } 23 | ] 24 | }, 25 | optimization: { 26 | minimize: true 27 | } 28 | }; 29 | --------------------------------------------------------------------------------