├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── README.md ├── lerna.json ├── package-lock.json ├── package.json └── packages └── @htmlgoddess ├── cli ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── README.md ├── bin │ ├── run │ └── run.cmd ├── jest.config.js ├── package-lock.json ├── package.json ├── src │ ├── __tests__ │ │ └── htmlgoddess-cli.test.ts │ ├── commands │ │ ├── a11y │ │ │ └── index.ts │ │ ├── create │ │ │ └── index.ts │ │ ├── format │ │ │ ├── auto.ts │ │ │ └── index.ts │ │ ├── print │ │ │ ├── auto.ts │ │ │ └── index.ts │ │ ├── publish │ │ │ └── index.ts │ │ ├── save │ │ │ └── index.ts │ │ └── serve │ │ │ ├── auto.ts │ │ │ └── index.ts │ ├── index.ts │ └── webpack-config-generator.ts └── tsconfig.json ├── templates ├── README.md ├── basic │ ├── README.md │ └── src │ │ ├── content │ │ └── index.html │ │ ├── css │ │ ├── basic.css │ │ ├── geocities.css │ │ ├── index.css │ │ ├── latex.css │ │ ├── mvp.css │ │ ├── reverend.css │ │ ├── sakura.css │ │ ├── vanilla.css │ │ ├── water.dark.min.css │ │ └── water.light.min.css │ │ └── templates │ │ ├── footer │ │ ├── index.html │ │ └── nav.html │ │ ├── head │ │ └── index.html │ │ ├── index.html │ │ ├── main │ │ └── index.html │ │ └── nav │ │ └── index.html ├── blog │ └── src │ │ ├── content │ │ └── blog │ │ │ ├── index.html │ │ │ └── post │ │ │ └── hello-world.html │ │ └── templates │ │ ├── blog.html │ │ └── blog │ │ └── post.html ├── index.js ├── package-lock.json └── package.json └── webpack-plugin ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── gui.sh ├── html-xxl.png ├── jest.config.js ├── package-lock.json ├── package.json ├── plugin.js ├── scripts └── a11y.js └── tests └── index.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .yarn/install-state.gz 116 | .pnp.* 117 | 118 | .DS_Store 119 | 120 | packages/test -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "textlint" 4 | ], 5 | "cSpell.ignoreWords": [ 6 | "github", 7 | "webpack" 8 | ] 9 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## v0.4.4 3 | - Fix create command issue. 4 | - Move tests to use .gitignore instead of submodules. 5 | 6 | ## v0.3.8 - Alpha 7 | This is a complete rewrite from the current version currently under development. 8 | - Moved to lerna monorepo. 9 | - Using oclif for CLI. 10 | - Webpack plugin moved to its own package. 11 | - fix: serve test leaving process open. 12 | - feat: prompt unit test mocking. 13 | - feat: create command 14 | - feat: print:auto 15 | - test: intercept and setup testing for CLI output 16 | - fix: print:auto double printing. 17 | - task: Cleaned up paths 18 | - task: standardize param name to projectDir. 19 | - add: warning message for user if they try to print when docs has been edited manually. 20 | - fix: mockCLIAnswers not working properly. 21 | - refactor: consolidated template generation code. 22 | - feat: style chooser on create. 23 | 24 | 0.0.2 25 | - fix: git save/publish commands not cleaning up. 26 | - fix: global cli not loading dependancies. 27 | - fix: templates not compiling properly with webpack called as a module. 28 | 29 | 0.0.1 30 | - Abstract core htmlgoddess bin from CLI commands. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # htmlgoddess 2 | A minimalist framework for creating a website like it's 1999. 3 | 4 | ## Requirements 5 | - A computer with the internet, a text editor, and a web browser 6 | - npm 7 | 8 | ## Getting Started 9 | 1. Open terminal 10 | 1. ``` npm install -g @htmlgoddess/cli ``` 11 | This will install the command line utility. 12 | 1. ``` htmlgoddess create path/to/your/new/site ``` 13 | 1. follow the prompts and you should be up and running in no time. 14 | 15 | ## To Do 16 | - refactor: isolate tests. 17 | - add: "host" command. 18 | - add: style choose command to allow the user to change theme after creation. 19 | - feat: Spell checker. 20 | - feat: auto commitizen hook to trigger when committing. 21 | - feat: proofread commands. 22 | - feat: "domain" command. 23 | - feat: downloadable executable. 24 | - refactor: Swap CNAME for yaml config for hosting. 25 | - task: coverage 26 | - convert "docs" to configurable variables 27 | - task: make sure the watcher doesn't leak memory. 28 | - bug: cli.action still outputting to console during tests. 29 | - feat: GUI 30 | 31 | # I don't know any HTML 32 | That's ok. Check out [this video](https://www.youtube.com/watch?v=3RXlQPkJzCM) to get started. 33 | 34 | ## Command Line Menu 35 | When you run ``` npm start ``` and it will give you the following options. 36 | 37 | ## How it works 38 | - Files in the "src" folder are compiled (printed) to static HTML files in the "docs" folder. 39 | - ``` src/templates ``` folder contains the templates. These are compiled with the content folder to generate your static HTML pages. 40 | - ``` src/content ``` folder contains your site content, which is kept in html files that are chunks of HTML code. 41 | - When you run ``` npm run print ``` (or select print from terminal menu), it will compile your content and templates into static HTML files and recreate the docs folder. (NOTE: Everything in docs gets overwritten so only save content in your src directory!) 42 | - You can test your site locally by running the "serve" command from the menu. 43 | - When you are ready to deploy your site, just do 44 | ``` npm run save && npm run publish``` 45 | - You can then point your web server to "docs" whether it be apache, git pages, nginx, or anything. 46 | - You can add any stylesheet that targets plain HTML elements and it should work :) 47 | 48 | ## Templates 49 | - Tags in the template that are self closing like `` or `
` will search for template files matching that same name; either a directory with an index html like ```main/index.html``` or simply a file ```main.html``` 50 | - Tags do not have to be standard HTML. If you make a template foo.html in the templates folder, you can include a `` `` tag and it will replace the contents of foo. 51 | - The template compiling is recursive so you can use templates within templates, however, the nested templates need to be files contained within or adjacent to the parent template. Otherwise it will just be ignored. 52 | - The ``````tag is special and will either pull in a template as the same name of the file (with dir) or the main template ('templates/index.html). 53 | - When you create the pages you want in the content dir. Directories relative to there will show up in your site with the same path. This structure allows for self organizing folders and urls. 54 | 55 | ## Constraints 56 | - No JS 57 | - No attributes except basic href etc. 58 | - No classes. This is what allows you to add any stylesheet that targets vanilla css 59 | - No SASS/SCSS/LESS. This should not be necessary with simple HTML elements 60 | - No React, Angular, or anything else. 61 | - Or not, you can hack anything you want. 62 | 63 | ## Philosophy 64 | HTML was designed to be simple, and for ordinary people to create and consume things on the internet. The web is pretty awesome today but also has gotten pretty complex and it's leaving a lot of people behind. This CMS gets back to basics to give people a way to express themselves freely and easily. 65 | 66 | - The framework tries to leverage as much existing technology and standards as it can. 67 | - HTML is used for everything (as the HTML Goddess has commanded) instead of proprietary template tags and other special syntax. The templating system searches for self closing HTML tags and replaces them with associated templates or content. 68 | - The file system is leveraged for both finding/naming templates and url routing. 69 | - Git serves as the actual database for the CMS along with the HTML files in content and templates. 70 | - The site "prints' ' to the "docs' ' folder, and then you "publish" to git, where you can set up [github pages](https://pages.github.com/). It's agnostic of the web server so you can actually take the files in the docs folder and plunk them anywhere you want and point a web server at them. 71 | - Tags are intended not to need classes or attributes. This allows new themes to be seamlessly dropped in. You can think of vanilla HTML as the interface for applying styling. 72 | - Using JavaScript is discouraged as it shouldn't be unnecessary, though there's nothing in the framework that prevents you using it. 73 | - You can ignore everything I just wrote and do whatever you want. It's the internet! 74 | 75 | 76 | ## Frequent issues 77 | - Dependencies acting weird: 78 | Lerna does some things under the hood to cross-link Dependencies. If you install a new module and things stop working try ```lerna bootstrap``` from the root. 79 | - Test directory not cleaning up. If a test fails it might prevent the test directory from cleaning up. In that case run ``` npm run clean-test-dir ``` which will remove it manually. 80 | 81 | ## Contributing 82 | 1. run tests in packages/cli to make sure everything's up to snuff 83 | ```npm run test``` 84 | 1. Commit changes to the mono repo 85 | ``` npm run commit ``` and follow the prompts 86 | 1. lerna publish --force-publish 87 | This will publish to NPM as well as push a tag to git 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/@htmlgoddess/cli", 4 | "packages/@htmlgoddess/webpack-plugin", 5 | "packages/@htmlgoddess/templates" 6 | ], 7 | "version": "0.4.4" 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "private": true, 4 | "devDependencies": { 5 | "@oclif/dev-cli": "1.22.2", 6 | "@types/node": "10.17.24", 7 | "cz-conventional-changelog": "3.2.0", 8 | "eslint": "5.16.0", 9 | "eslint-config-oclif": "3.1.0", 10 | "eslint-config-oclif-typescript": "0.1.0", 11 | "git-cz": "4.6.2", 12 | "globby": "10.0.2", 13 | "husky": "4.2.5", 14 | "lerna": "^3.22.0", 15 | "ts-node": "8.10.2", 16 | "typescript": "3.9.5" 17 | }, 18 | "scripts": { 19 | "commit": "git-cz", 20 | "clean-test-dir": "rm -rf ./packages/test" 21 | }, 22 | "dependencies": { 23 | "@htmlgoddess/cli": "file:packages/@htmlgoddess/cli", 24 | "@htmlgoddess/templates": "file:packages/@htmlgoddess/templates", 25 | "@htmlgoddess/webpack-plugin": "file:packages/@htmlgoddess/webpack-plugin", 26 | "execa": "4.0.2", 27 | "g": "2.0.1", 28 | "test": "file:packages/test" 29 | }, 30 | "config": { 31 | "commitizen": { 32 | "path": "./node_modules/cz-conventional-changelog" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/.eslintignore: -------------------------------------------------------------------------------- 1 | /lib 2 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "oclif", 4 | "oclif-typescript" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/.gitignore: -------------------------------------------------------------------------------- 1 | *-debug.log 2 | *-error.log 3 | /.nyc_output 4 | /dist 5 | /lib 6 | /tmp 7 | /yarn.lock 8 | node_modules 9 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/README.md: -------------------------------------------------------------------------------- 1 | htmlgoddess-cli 2 | =============== 3 | 4 | Cli tool for the HTMLGoddess static site generator. 5 | 6 | [![oclif](https://img.shields.io/badge/cli-oclif-brightgreen.svg)](https://oclif.io) 7 | [![Version](https://img.shields.io/npm/v/htmlgoddess-cli.svg)](https://npmjs.org/package/htmlgoddess-cli) 8 | [![Downloads/week](https://img.shields.io/npm/dw/htmlgoddess-cli.svg)](https://npmjs.org/package/htmlgoddess-cli) 9 | [![License](https://img.shields.io/npm/l/htmlgoddess-cli.svg)](https://github.com/jonascript/htmlgoddess-cli/blob/master/package.json) 10 | 11 | 12 | * [Usage](#usage) 13 | * [Commands](#commands) 14 | 15 | # Usage 16 | 17 | ```sh-session 18 | $ npm install -g @htmlgoddess/cli 19 | $ htmlgoddess COMMAND 20 | running command... 21 | $ htmlgoddess (-v|--version|version) 22 | @htmlgoddess/cli/0.4.4 darwin-x64 node-v12.18.1 23 | $ htmlgoddess --help [COMMAND] 24 | USAGE 25 | $ htmlgoddess COMMAND 26 | ... 27 | ``` 28 | 29 | # Commands 30 | 31 | * [`htmlgoddess a11y [PROJECTDIR]`](#htmlgoddess-a11y-projectdir) 32 | * [`htmlgoddess create [PROJECTDIR]`](#htmlgoddess-create-projectdir) 33 | * [`htmlgoddess format [PROJECTDIR]`](#htmlgoddess-format-projectdir) 34 | * [`htmlgoddess format:auto [PROJECTDIR]`](#htmlgoddess-formatauto-projectdir) 35 | * [`htmlgoddess help [COMMAND]`](#htmlgoddess-help-command) 36 | * [`htmlgoddess print [PROJECTDIR]`](#htmlgoddess-print-projectdir) 37 | * [`htmlgoddess print:auto [PROJECTDIR]`](#htmlgoddess-printauto-projectdir) 38 | * [`htmlgoddess publish [FILE]`](#htmlgoddess-publish-file) 39 | * [`htmlgoddess save [PROJECTDIR]`](#htmlgoddess-save-projectdir) 40 | * [`htmlgoddess serve [PROJECTPATH]`](#htmlgoddess-serve-projectpath) 41 | * [`htmlgoddess serve:auto [FILE]`](#htmlgoddess-serveauto-file) 42 | 43 | ## `htmlgoddess a11y [PROJECTDIR]` 44 | 45 | runs accessibility validation 46 | 47 | ``` 48 | USAGE 49 | $ htmlgoddess a11y [PROJECTDIR] 50 | 51 | OPTIONS 52 | -h, --help show CLI help 53 | -s, --standard=Section508|WCAG2A|WCAG2AA|WCAG2AAA [default: WCAG2AA] Web Accessibility Guideline standard 54 | -u, --url=url Run on individual URL 55 | 56 | EXAMPLES 57 | $ htmlgoddess a11y 58 | $ htmlgoddess a11y http://localhost:3000/index.html 59 | $ htmlgoddess a11y ./path/to/your/file.html 60 | ``` 61 | 62 | _See code: [lib/commands/a11y/index.js](https://github.com/jonascript/htmlgoddess/blob/v0.4.4/lib/commands/a11y/index.js)_ 63 | 64 | ## `htmlgoddess create [PROJECTDIR]` 65 | 66 | creates a new website project under the grace of the HTML Goddess 67 | 68 | ``` 69 | USAGE 70 | $ htmlgoddess create [PROJECTDIR] 71 | 72 | OPTIONS 73 | -f, --force 74 | -h, --help show CLI help 75 | 76 | EXAMPLES 77 | $ htmlgoddess create ./path/to/directory 78 | $ htmlgoddess create 79 | ``` 80 | 81 | _See code: [lib/commands/create/index.js](https://github.com/jonascript/htmlgoddess/blob/v0.4.4/lib/commands/create/index.js)_ 82 | 83 | ## `htmlgoddess format [PROJECTDIR]` 84 | 85 | formats your HTML 86 | 87 | ``` 88 | USAGE 89 | $ htmlgoddess format [PROJECTDIR] 90 | 91 | OPTIONS 92 | -f, --force 93 | -h, --help show CLI help 94 | -n, --name=name name to print 95 | 96 | EXAMPLE 97 | $ htmlgoddess format 98 | ``` 99 | 100 | _See code: [lib/commands/format/index.js](https://github.com/jonascript/htmlgoddess/blob/v0.4.4/lib/commands/format/index.js)_ 101 | 102 | ## `htmlgoddess format:auto [PROJECTDIR]` 103 | 104 | watches for changes and formats your HTML 105 | 106 | ``` 107 | USAGE 108 | $ htmlgoddess format:auto [PROJECTDIR] 109 | 110 | OPTIONS 111 | -h, --help show CLI help 112 | 113 | EXAMPLE 114 | $ htmlgoddess format:auto 115 | ``` 116 | 117 | _See code: [lib/commands/format/auto.js](https://github.com/jonascript/htmlgoddess/blob/v0.4.4/lib/commands/format/auto.js)_ 118 | 119 | ## `htmlgoddess help [COMMAND]` 120 | 121 | display help for htmlgoddess 122 | 123 | ``` 124 | USAGE 125 | $ htmlgoddess help [COMMAND] 126 | 127 | ARGUMENTS 128 | COMMAND command to show help for 129 | 130 | OPTIONS 131 | --all see all commands in CLI 132 | ``` 133 | 134 | _See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v3.1.0/src/commands/help.ts)_ 135 | 136 | ## `htmlgoddess print [PROJECTDIR]` 137 | 138 | compiles source html/css files from src and prints to docs folder 139 | 140 | ``` 141 | USAGE 142 | $ htmlgoddess print [PROJECTDIR] 143 | 144 | OPTIONS 145 | -a, --[no-]a11y 146 | -f, --force 147 | -h, --help show CLI help 148 | -n, --name=name name to print 149 | 150 | EXAMPLES 151 | $ htmlgoddess print 152 | $ htmlgoddess print ./path/to/your/project 153 | ``` 154 | 155 | _See code: [lib/commands/print/index.js](https://github.com/jonascript/htmlgoddess/blob/v0.4.4/lib/commands/print/index.js)_ 156 | 157 | ## `htmlgoddess print:auto [PROJECTDIR]` 158 | 159 | describe the command here 160 | 161 | ``` 162 | USAGE 163 | $ htmlgoddess print:auto [PROJECTDIR] 164 | 165 | OPTIONS 166 | -d, --debounce=debounce [default: 500] 167 | -f, --force 168 | -h, --help show CLI help 169 | -n, --name=name name to print 170 | 171 | EXAMPLE 172 | $ htmlgoddess print 173 | hello world wide web from ./src/hello.ts! 174 | ``` 175 | 176 | _See code: [lib/commands/print/auto.js](https://github.com/jonascript/htmlgoddess/blob/v0.4.4/lib/commands/print/auto.js)_ 177 | 178 | ## `htmlgoddess publish [FILE]` 179 | 180 | publishes your saved changes to git 181 | 182 | ``` 183 | USAGE 184 | $ htmlgoddess publish [FILE] 185 | 186 | OPTIONS 187 | -f, --force 188 | -h, --help show CLI help 189 | 190 | EXAMPLE 191 | $ htmlgoddess publish 192 | ``` 193 | 194 | _See code: [lib/commands/publish/index.js](https://github.com/jonascript/htmlgoddess/blob/v0.4.4/lib/commands/publish/index.js)_ 195 | 196 | ## `htmlgoddess save [PROJECTDIR]` 197 | 198 | saves all changes in your project 199 | 200 | ``` 201 | USAGE 202 | $ htmlgoddess save [PROJECTDIR] 203 | 204 | OPTIONS 205 | -h, --help show CLI help 206 | 207 | EXAMPLE 208 | $ htmlgoddess save 209 | ``` 210 | 211 | _See code: [lib/commands/save/index.js](https://github.com/jonascript/htmlgoddess/blob/v0.4.4/lib/commands/save/index.js)_ 212 | 213 | ## `htmlgoddess serve [PROJECTPATH]` 214 | 215 | serves your website on a local webserver 216 | 217 | ``` 218 | USAGE 219 | $ htmlgoddess serve [PROJECTPATH] 220 | 221 | OPTIONS 222 | -f, --force 223 | -h, --help show CLI help 224 | -n, --name=name name to print 225 | 226 | EXAMPLE 227 | $ htmlgoddess serve 228 | ``` 229 | 230 | _See code: [lib/commands/serve/index.js](https://github.com/jonascript/htmlgoddess/blob/v0.4.4/lib/commands/serve/index.js)_ 231 | 232 | ## `htmlgoddess serve:auto [FILE]` 233 | 234 | serves your website and auto-reloads when changed. 235 | 236 | ``` 237 | USAGE 238 | $ htmlgoddess serve:auto [FILE] 239 | 240 | OPTIONS 241 | -f, --force 242 | -h, --help show CLI help 243 | -n, --name=name name to print 244 | 245 | EXAMPLE 246 | $ htmlgoddess serve 247 | ``` 248 | 249 | _See code: [lib/commands/serve/auto.js](https://github.com/jonascript/htmlgoddess/blob/v0.4.4/lib/commands/serve/auto.js)_ 250 | 251 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/bin/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('@oclif/command').run() 4 | .then(require('@oclif/command/flush')) 5 | .catch(require('@oclif/errors/handle')) 6 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/bin/run.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | node "%~dp0\run" %* 4 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/jest.config.js: -------------------------------------------------------------------------------- 1 | // For a detailed explanation regarding each configuration property, visit: 2 | // https://jestjs.io/docs/en/configuration.html 3 | 4 | module.exports = { 5 | // All imported modules in your tests should be mocked automatically 6 | automock: false, 7 | 8 | testMatch: [ 9 | "**/__tests__/**/*.+(ts|tsx|js)", 10 | "**/?(*.)+(spec|test).+(ts|tsx|js)", 11 | ], 12 | transform: { 13 | "^.+\\.(ts|tsx)$": "ts-jest", 14 | }, 15 | 16 | // Stop running tests after `n` failures 17 | // bail: 0, 18 | 19 | // Respect "browser" field in package.json when resolving modules 20 | // browser: false, 21 | 22 | // The directory where Jest should store its cached dependency information 23 | // cacheDirectory: "/private/var/folders/5z/fg5_q4cd7wj2clwctw2xq1w00000gn/T/jest_dx", 24 | 25 | // Automatically clear mock calls and instances between every test 26 | // clearMocks: false, 27 | 28 | // Indicates whether the coverage information should be collected while executing the test 29 | // collectCoverage: false, 30 | 31 | // An array of glob patterns indicating a set of files for which coverage information should be collected 32 | // collectCoverageFrom: null, 33 | 34 | // The directory where Jest should output its coverage files 35 | coverageDirectory: "coverage", 36 | 37 | // An array of regexp pattern strings used to skip coverage collection 38 | // coveragePathIgnorePatterns: [ 39 | // "/node_modules/" 40 | // ], 41 | 42 | // A list of reporter names that Jest uses when writing coverage reports 43 | // coverageReporters: [ 44 | // "json", 45 | // "text", 46 | // "lcov", 47 | // "clover" 48 | // ], 49 | 50 | // An object that configures minimum threshold enforcement for coverage results 51 | // coverageThreshold: null, 52 | 53 | // A path to a custom dependency extractor 54 | // dependencyExtractor: null, 55 | 56 | // Make calling deprecated APIs throw helpful error messages 57 | // errorOnDeprecated: false, 58 | 59 | // Force coverage collection from ignored files using an array of glob patterns 60 | // forceCoverageMatch: [], 61 | 62 | // A path to a module which exports an async function that is triggered once before all test suites 63 | // globalSetup: null, 64 | 65 | // A path to a module which exports an async function that is triggered once after all test suites 66 | // globalTeardown: null, 67 | 68 | // A set of global variables that need to be available in all test environments 69 | // globals: {}, 70 | 71 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. 72 | // maxWorkers: "50%", 73 | 74 | // An array of directory names to be searched recursively up from the requiring module's location 75 | // moduleDirectories: [ 76 | // "node_modules" 77 | // ], 78 | 79 | // An array of file extensions your modules use 80 | // moduleFileExtensions: [ 81 | // "js", 82 | // "json", 83 | // "jsx", 84 | // "ts", 85 | // "tsx", 86 | // "node" 87 | // ], 88 | 89 | // A map from regular expressions to module names that allow to stub out resources with a single module 90 | // moduleNameMapper: {}, 91 | 92 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader 93 | // modulePathIgnorePatterns: [], 94 | 95 | // Activates notifications for test results 96 | // notify: false, 97 | 98 | // An enum that specifies notification mode. Requires { notify: true } 99 | // notifyMode: "failure-change", 100 | 101 | // A preset that is used as a base for Jest's configuration 102 | // preset: null, 103 | 104 | // Run tests from one or more projects 105 | // projects: null, 106 | 107 | // Use this configuration option to add custom reporters to Jest 108 | // reporters: undefined, 109 | 110 | // Automatically reset mock state between every test 111 | // resetMocks: false, 112 | 113 | // Reset the module registry before running each individual test 114 | // resetModules: false, 115 | 116 | // A path to a custom resolver 117 | // resolver: null, 118 | 119 | // Automatically restore mock state between every test 120 | // restoreMocks: false, 121 | 122 | // The root directory that Jest should scan for tests and modules within 123 | // rootDir: null, 124 | 125 | // A list of paths to directories that Jest should use to search for files in 126 | // roots: [ 127 | // "" 128 | // ], 129 | 130 | // Allows you to use a custom runner instead of Jest's default test runner 131 | // runner: "jest-runner", 132 | 133 | // The paths to modules that run some code to configure or set up the testing environment before each test 134 | // setupFiles: [], 135 | 136 | // A list of paths to modules that run some code to configure or set up the testing framework before each test 137 | // setupFilesAfterEnv: [], 138 | 139 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing 140 | // snapshotSerializers: [], 141 | 142 | // The test environment that will be used for testing 143 | testEnvironment: "node", 144 | 145 | // Options that will be passed to the testEnvironment 146 | // testEnvironmentOptions: {}, 147 | 148 | // Adds a location field to test results 149 | // testLocationInResults: false, 150 | 151 | // The glob patterns Jest uses to detect test files 152 | // testMatch: [ 153 | // "**/__tests__/**/*.[jt]s?(x)", 154 | // "**/?(*.)+(spec|test).[tj]s?(x)" 155 | // ], 156 | 157 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped 158 | // testPathIgnorePatterns: [ 159 | // "/node_modules/" 160 | // ], 161 | 162 | // The regexp pattern or array of patterns that Jest uses to detect test files 163 | // testRegex: [], 164 | 165 | // This option allows the use of a custom results processor 166 | // testResultsProcessor: null, 167 | 168 | // This option allows use of a custom test runner 169 | // testRunner: "jasmine2", 170 | 171 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href 172 | // testURL: "http://localhost", 173 | 174 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" 175 | // timers: "real", 176 | 177 | // A map from regular expressions to paths to transformers 178 | // transform: null, 179 | 180 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation 181 | // transformIgnorePatterns: [ 182 | // "/node_modules/" 183 | // ], 184 | 185 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them 186 | // unmockedModulePathPatterns: undefined, 187 | 188 | // Indicates whether each individual test should be reported during the run 189 | // verbose: null, 190 | 191 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode 192 | // watchPathIgnorePatterns: [], 193 | 194 | // Whether to use watchman for file crawling 195 | // watchman: true, 196 | }; 197 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@htmlgoddess/cli", 3 | "description": " HTMLGoddess command line utility", 4 | "version": "0.4.4", 5 | "author": "Jonathan Crockett ", 6 | "private": false, 7 | "bin": { 8 | "htmlgoddess": "./bin/run" 9 | }, 10 | "bugs": "https://github.com/jonascript/htmlgoddess/issues", 11 | "dependencies": { 12 | "@htmlgoddess/templates": "^0.4.4", 13 | "@htmlgoddess/webpack-plugin": "^0.4.4", 14 | "@oclif/command": "1.6.1", 15 | "@oclif/config": "1.15.1", 16 | "@oclif/plugin-help": "3.1.0", 17 | "@textlint-rule/textlint-rule-no-dead-link": "3.2.0", 18 | "@webpack-cli/serve": "0.2.0", 19 | "cjs-to-es6": "1.1.1", 20 | "clean-webpack-plugin": "3.0.0", 21 | "cli-ux": "5.4.7", 22 | "command-line-args": "5.1.1", 23 | "copy-webpack-plugin": "6.0.1", 24 | "css-loader": "3.5.3", 25 | "fs-extra": "9.0.1", 26 | "glob": "7.1.6", 27 | "html-beautify-webpack-plugin": "1.0.5", 28 | "html-loader": "1.1.0", 29 | "html-replace-webpack-plugin": "2.5.6", 30 | "html-to-text": "5.1.1", 31 | "html-webpack-plugin": "4.3.0", 32 | "inquirer": "7.3.0", 33 | "isomorphic-git": "1.4.4", 34 | "livereload": "0.9.1", 35 | "mini-css-extract-plugin": "0.9.0", 36 | "ntl": "5.0.0", 37 | "onchange": "7.0.2", 38 | "pa11y": "5.3.0", 39 | "pa11y-reporter-cli": "2.0.0", 40 | "postcss-loader": "3.0.0", 41 | "prettier": "2.0.5", 42 | "pretty": "2.0.0", 43 | "recursive-watch": "1.1.4", 44 | "serve": "11.3.1", 45 | "simple-git": "2.6.0", 46 | "style-loader": "1.2.1", 47 | "textlint": "11.6.3", 48 | "textlint-plugin-html": "0.2.0", 49 | "textlint-rule-ginger": "2.2.1", 50 | "textlint-rule-rousseau": "1.4.6", 51 | "textlint-rule-terminology": "2.1.4", 52 | "tslib": "1.13.0", 53 | "url-loader": "4.1.0", 54 | "wcag-validator": "5.0.0", 55 | "webpack": "4.43.0", 56 | "webpack-cli": "3.3.11", 57 | "webpack-dev-server": "3.11.0", 58 | "webpack-fix-style-only-entries": "0.5.0", 59 | "webpack-livereload-plugin": "2.3.0" 60 | }, 61 | "htmlgoddess": { 62 | "output": "/docs", 63 | "livereload": true 64 | }, 65 | "directories": { 66 | "lib": "lib", 67 | "test": "__tests__" 68 | }, 69 | "engines": { 70 | "node": ">=10.0.0" 71 | }, 72 | "files": [ 73 | "/bin", 74 | "/npm-shrinkwrap.json", 75 | "/oclif.manifest.json", 76 | "lib" 77 | ], 78 | "homepage": "https://github.com/jonascript/htmlgoddess", 79 | "keywords": [ 80 | "oclif" 81 | ], 82 | "license": "ISC", 83 | "main": "./bin/run", 84 | "oclif": { 85 | "commands": "./lib/commands", 86 | "bin": "htmlgoddess", 87 | "plugins": [ 88 | "@oclif/plugin-help" 89 | ] 90 | }, 91 | "devDependencies": { 92 | "@oclif/dev-cli": "^1", 93 | "@types/jest": "26.0.0", 94 | "axios": "0.19.2", 95 | "git-http-mock-server": "1.2.1", 96 | "html-loader": "1.1.0", 97 | "jest": "26.0.1", 98 | "mock-stdin": "1.0.0", 99 | "rimraf": "3.0.2", 100 | "supertest": "4.0.2", 101 | "ts-jest": "26.1.0" 102 | }, 103 | "repository": { 104 | "type": "git", 105 | "url": "https://github.com/jonascript/htmlgoddess", 106 | "directory": "packages/@htmlgoddess/cli" 107 | }, 108 | "scripts": { 109 | "postpack": "rm -f oclif.manifest.json", 110 | "lint": "eslint . --ext .ts --config .eslintrc", 111 | "prepack": "rm -rf lib && tsc -b && oclif-dev manifest && oclif-dev readme", 112 | "test": "jest --runInBand --detectOpenHandles --verbose --forceExit", 113 | "test:watch": "jest -w", 114 | "version": "oclif-dev readme && git add README.md", 115 | "prettier": "prettier 'src/**/*.ts' '__tests__/**/*.ts' --write", 116 | "prettier:watch": "onchange 'src/**/*.ts' '__tests__/**/*.ts' -- prettier {{changed}} --write" 117 | }, 118 | "types": "lib/index.d.ts", 119 | "gitHead": "1428ad252d2c793a2f25112df8c1439060e1277a" 120 | } 121 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/src/__tests__/htmlgoddess-cli.test.ts: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | import * as fs from "fs"; 3 | import * as path from "path"; 4 | import rimraf from "rimraf"; 5 | import { run } from "../index"; 6 | import Create from "../commands/create/index"; 7 | import Format from "../commands/format/index"; 8 | import A11y from "../commands/a11y/index"; 9 | import Print from "../commands/Print/index"; 10 | import PrintAuto from "../commands/Print/auto"; 11 | import Serve from "../commands/Serve/index"; 12 | import Save from "../commands/Save/index"; 13 | import axios from "axios"; 14 | import * as execa from "execa"; 15 | import cli, { ActionBase } from "cli-ux"; 16 | import prompt from "../../node_modules/cli-ux/lib/prompt.js"; 17 | import inquirer from "inquirer"; 18 | import { isExportDeclaration } from "typescript"; 19 | 20 | const TEST_DIR_RELATIVE_PATH = './packages/test'; 21 | 22 | /** 23 | * Mocks the "open" function so that a browser doesn't open while unit testing. 24 | */ 25 | function mockCLIOpen() { 26 | jest.mock("cli-ux", () => { 27 | return { 28 | ...cli, 29 | open: () => { 30 | return; 31 | }, 32 | }; 33 | }); 34 | } 35 | 36 | let cliAnswerQueue = []; 37 | 38 | /** 39 | * @todo this function does not work as expected. When it's called multiple times it doesnt' keep scope. 40 | * Mocking for interactive prompts. 41 | * @param answers 42 | */ 43 | function mockCLIAnswers() { 44 | // Mocking inquirier answer for create 45 | // @todo questions come in as arrays. Currently only using one 46 | // question at a time but should make this handle all cases. 47 | inquirer.prompt = (questions) => { 48 | const answer = cliAnswerQueue.shift(); 49 | return Promise.resolve({ [`${questions[0].name}`]: answer }); 50 | }; 51 | 52 | jest.mock("../../node_modules/cli-ux/lib/prompt.js", () => { 53 | return { 54 | ...prompt, 55 | confirm: (message) => { 56 | console.log(message); 57 | return cliAnswerQueue.shift(); 58 | }, 59 | prompt: async (prompt, icon) => { 60 | console.log(prompt); 61 | return new Promise((resolve, reject) => { 62 | resolve(cliAnswerQueue.shift()); 63 | }); 64 | }, 65 | }; 66 | }); 67 | } 68 | 69 | describe("htmlgoddess Command", () => { 70 | let cliOutput = [], 71 | io = null, 72 | TEST_DIR, 73 | TEST_PROJECT_DIR, 74 | TEST_PRINT_DIR, 75 | TEST_SAVE_DIR, 76 | TEST_SERVE_DIR; 77 | 78 | beforeAll((done) => { 79 | mockCLIAnswers(); 80 | console.log("Changing to test directory."); 81 | process.chdir("../.."); 82 | fs.mkdirSync('test'); 83 | process.chdir("test"); 84 | TEST_DIR = process.cwd(); 85 | 86 | TEST_PROJECT_DIR = path.join(TEST_DIR, "testproject"); 87 | TEST_PRINT_DIR = path.join(TEST_DIR, "testprint"); 88 | TEST_SAVE_DIR = path.join(TEST_DIR, "testsave"); 89 | TEST_SERVE_DIR = path.join(TEST_DIR, 'testserve'); 90 | mockCLIOpen(); 91 | done(); 92 | }); 93 | 94 | afterAll((done) => { 95 | jest.restoreAllMocks(); 96 | // Make sure it's the right dir before deleting the directory. 97 | if (/packages\/test/.test(TEST_DIR)) { 98 | rimraf.sync(TEST_DIR) 99 | } else { 100 | throw new Error('@htmlgoddess/cli test directory does match test. aborting delete. Please delete your test direcory manually') 101 | } 102 | done(); 103 | }); 104 | 105 | beforeEach(() => { 106 | cliAnswerQueue = []; 107 | cliOutput = []; 108 | // jest 109 | // .spyOn(process.stdout, "write") 110 | // .mockImplementation((str, encoding, cb) => { 111 | // cliOutput.push(str); 112 | // return false; 113 | // }); 114 | // jest 115 | // .spyOn(process.stdin, "write") 116 | // .mockImplementation((val) => stdin.push(val)); 117 | }); 118 | 119 | afterEach(() => { 120 | cliAnswerQueue = []; 121 | jest.restoreAllMocks(); 122 | }); 123 | 124 | describe("create", () => { 125 | it("can create a new site", async (done) => { 126 | 127 | const mockAnswers = ["My Test Site", "blog", "latex.css", "Y"] 128 | 129 | cliAnswerQueue.push(...mockAnswers); 130 | 131 | Create.run([TEST_PROJECT_DIR]).then((results) => { 132 | // @todo test console messages from stdout 133 | expect(results.name).toEqual(mockAnswers[0]); 134 | expect(results.template).toEqual(mockAnswers[1]); 135 | expect(results.path).toEqual(TEST_PROJECT_DIR); 136 | expect( 137 | fs.existsSync(path.join(results.path, "src/content/index.html")) 138 | ).toEqual(true); 139 | 140 | expect( 141 | fs.existsSync(path.join(results.path, "src/content/blog/post/hello-world.html")) 142 | ).toEqual(true); 143 | 144 | 145 | expect( 146 | fs.readFileSync( 147 | path.join(results.path, "src/templates/head/index.html"), 148 | "utf-8" 149 | ) 150 | ).toContain('latex.css'); 151 | 152 | 153 | done(); 154 | }); 155 | }); 156 | 157 | it("will throw error when non existent template is given", (done) => { 158 | const mockAnswers = ["My Test Site", "clog", "latex.css", "Y"]; 159 | cliAnswerQueue.push(...mockAnswers) 160 | Create.run([TEST_PROJECT_DIR]).then( 161 | () => {}, 162 | (error) => { 163 | expect(error).toBeTruthy(); 164 | done(); 165 | } 166 | ); 167 | }); 168 | }); 169 | 170 | describe("print", () => { 171 | const time = Date.now(); 172 | jest.setTimeout(10000); 173 | 174 | beforeAll((done) => { 175 | const mockAnswers = ["My Test Site", "blog","latex.css", "Y"]; 176 | cliAnswerQueue.push(...mockAnswers) 177 | Create.run([TEST_PRINT_DIR]).then((results) => { 178 | done(); 179 | }); 180 | }); 181 | 182 | describe("can print", () => { 183 | beforeEach(() => { 184 | fs.writeFileSync( 185 | path.join(TEST_PRINT_DIR, "src/content/can-print.html"), 186 | `

I am printed ${time}

` 187 | ); 188 | }) 189 | 190 | afterEach(() => { 191 | fs.unlinkSync( 192 | path.join(TEST_PRINT_DIR, "src/content/can-print.html") 193 | ) 194 | fs.unlinkSync( 195 | path.join(TEST_PRINT_DIR, "docs/can-print.html") 196 | ) 197 | }); 198 | 199 | it("can print", (done) => { 200 | Print.run([TEST_PRINT_DIR]).then((result) => { 201 | const output = fs.readFileSync( 202 | path.join(TEST_PRINT_DIR, "docs/can-print.html"), 203 | "utf-8" 204 | ); 205 | expect(output).toContain(`

I am printed ${time}

`); 206 | done(); 207 | }); 208 | }); 209 | }); 210 | describe('print:auto', () => { 211 | beforeEach((done) => { 212 | PrintAuto.run([TEST_PRINT_DIR, "--debounce=800"]).then(() => { 213 | done(); 214 | }) 215 | }); 216 | afterEach(() => { 217 | rimraf.sync(path.join(TEST_PRINT_DIR, 'docs')); 218 | }); 219 | it("can print:auto", async (done) => { 220 | // Gives some time for print:auto debounce 221 | setTimeout(() => { 222 | fs.writeFileSync( 223 | path.join(TEST_PRINT_DIR, "src/content/can-print.html"), 224 | `

I am auto printed ${time}

` 225 | ); 226 | 227 | setTimeout(() => { 228 | const output = fs.readFileSync( 229 | path.join(TEST_PRINT_DIR, "docs/can-print.html"), 230 | "utf-8" 231 | ); 232 | expect(output).toContain(`

I am auto printed ${time}

`); 233 | done(); 234 | }, 3000); 235 | }, 1000); 236 | }); 237 | 238 | 239 | }); 240 | 241 | it("will ask the user to confirm when docs html file has been modified", (done) => { 242 | fs.writeFileSync( 243 | path.join(TEST_PRINT_DIR, "src/content/can-print.html"), 244 | `

I am printed ${time}

` 245 | ); 246 | 247 | Print.run([TEST_PRINT_DIR]).then(async (output) => { 248 | cliAnswerQueue.push('n') 249 | setTimeout(() => { 250 | fs.appendFileSync( 251 | path.join(TEST_PRINT_DIR, "docs/can-print.html"), 252 | ``, 253 | { flag: "a", encoding: "utf8" } 254 | ); 255 | 256 | const stats = fs.statSync( 257 | path.join(TEST_PRINT_DIR, "docs/can-print.html") 258 | ); 259 | 260 | Print.run([TEST_PRINT_DIR]).then( 261 | (output) => { 262 | done(); 263 | }, 264 | (err) => { 265 | const fileContent = fs.readFileSync( 266 | path.join(TEST_PRINT_DIR, "docs/can-print.html"), 267 | "utf-8" 268 | ); 269 | expect(fileContent).toContain( 270 | `` 271 | ); 272 | done(); 273 | } 274 | ); 275 | }, 3000); 276 | }); 277 | }); 278 | 279 | it("lets the user confirm to overwrite changes to the docs folder", (done) => { 280 | fs.writeFileSync( 281 | path.join(TEST_PRINT_DIR, "src/content/can-print.html"), 282 | `

I am printed ${time}

` 283 | ); 284 | 285 | Print.run([TEST_PRINT_DIR]).then(async (output) => { 286 | setTimeout(() => { 287 | fs.appendFileSync( 288 | path.join(TEST_PRINT_DIR, "docs/can-print.html"), 289 | ``, 290 | { flag: "a", encoding: "utf8" } 291 | ); 292 | 293 | const stats = fs.statSync( 294 | path.join(TEST_PRINT_DIR, "docs/can-print.html") 295 | ); 296 | 297 | cliAnswerQueue.push("yes"); 298 | Print.run([TEST_PRINT_DIR]).then( 299 | (output) => { 300 | const fileContent = fs.readFileSync( 301 | path.join(TEST_PRINT_DIR, "docs/can-print.html"), 302 | "utf-8" 303 | ); 304 | expect(fileContent).not.toContain( 305 | `` 306 | ); 307 | done(); 308 | }, 309 | (err) => { 310 | done(); 311 | } 312 | ); 313 | }, 3000); 314 | }); 315 | }); 316 | }); 317 | 318 | describe("format", () => { 319 | beforeEach((done) => { 320 | fs.writeFileSync( 321 | path.join(TEST_PROJECT_DIR, "src/content/can-format.html"), 322 | "

I am formatted

" 323 | ); 324 | done(); 325 | }); 326 | 327 | // afterEach((done) => { 328 | // done(); 329 | // }); 330 | 331 | it("can format", (done) => { 332 | Format.run([TEST_PROJECT_DIR]).then((result) => { 333 | const output = fs.readFileSync( 334 | path.join(TEST_PROJECT_DIR, "src/content/can-format.html"), 335 | "utf-8" 336 | ); 337 | expect(output).toContain("

I am formatted

"); 338 | 339 | done(); 340 | }); 341 | }); 342 | 343 | it("can format with a path", (done) => { 344 | Format.run([path.join(TEST_PROJECT_DIR)]).then((result) => { 345 | const output = fs.readFileSync( 346 | path.join(TEST_PROJECT_DIR, "src/content/can-format.html"), 347 | "utf-8" 348 | ); 349 | expect(output).toContain("

I am formatted

"); 350 | 351 | done(); 352 | }); 353 | }); 354 | 355 | // it("can format:auto", async () => {}); 356 | }); 357 | 358 | // @todo 359 | describe("serve", () => { 360 | beforeEach(async () => { 361 | const mockAnswers = ["My Test Site", "blog", "latex.css", "Y"] 362 | cliAnswerQueue.push(...mockAnswers); 363 | await Create.run([TEST_SERVE_DIR]); 364 | await Print.run([TEST_SERVE_DIR]) 365 | }) 366 | it("can serve", (done) => { 367 | Serve.run([TEST_SERVE_DIR]).then((process) => { 368 | console.log('serving', TEST_SERVE_DIR) 369 | setTimeout(async () => { 370 | const response = await axios.get("http://127.0.0.1:3000"); 371 | expect(response.status).toEqual(200); 372 | done(); 373 | }, 2000); 374 | }); 375 | }); 376 | // it("can serve without param passed", (done) => { 377 | // }) 378 | // it("can serve:auto", async () => {}); 379 | }); 380 | 381 | describe("save", () => { 382 | const time = Date.now(); 383 | beforeEach(async () => { 384 | const mockAnswers = ["My Test Site", "blog", "latex.css", "Y"] 385 | cliAnswerQueue.push(...mockAnswers); 386 | await Create.run([TEST_SAVE_DIR]); 387 | fs.writeFileSync( 388 | path.join(TEST_SAVE_DIR, "src/content/can-save.html"), 389 | `

I am saved at ${time}

` 390 | ); 391 | }); 392 | 393 | afterEach((done) => { 394 | done(); 395 | }); 396 | 397 | it("can save", (done) => { 398 | // @todo make sure this cleans up 399 | Save.run([TEST_SAVE_DIR]).then((result) => { 400 | const output = execa.sync("git", ["diff", "HEAD~1", "HEAD"]); 401 | expect(output.stdout).toContain(`+

I am saved at ${time}

`); 402 | done(); 403 | }); 404 | }); 405 | }); 406 | 407 | // @todo 408 | describe("a11y", () => { 409 | const time = Date.now(); 410 | beforeEach((done) => { 411 | Print.run([TEST_PROJECT_DIR, "--no-a11y"]).then((results) => { 412 | done(); 413 | }); 414 | }); 415 | 416 | it("can validate accesibility", (done) => { 417 | A11y.run([TEST_PROJECT_DIR]).then((results) => { 418 | done(); 419 | }); 420 | }); 421 | 422 | it("can validate against one url", (done) => { 423 | A11y.run([ 424 | TEST_PROJECT_DIR, 425 | `--url=${path.join(TEST_PROJECT_DIR, "/docs/index.html")}`, 426 | ]).then((results) => { 427 | done(); 428 | }); 429 | }); 430 | 431 | it("will catch errors", async () => { 432 | fs.writeFileSync( 433 | path.join(TEST_PROJECT_DIR, "src/content/not-a11y.html"), 434 | `

I am not accessible ${time}.

` 435 | ); 436 | 437 | await run(["print", TEST_PROJECT_DIR, "--no-a11y"]); 438 | let results; 439 | try { 440 | results = await A11y.run([TEST_PROJECT_DIR]); 441 | } catch (error) { 442 | results = error; 443 | } 444 | 445 | expect(results.length).toEqual(1); 446 | expect(results[0].issues[0].message).toEqual( 447 | "Img element missing an alt attribute. Use the alt attribute to specify a short text alternative." 448 | ); 449 | 450 | }); 451 | // it("can serve without param passed", (done) => { 452 | // }) 453 | // it("can serve:auto", async () => {}); 454 | }); 455 | 456 | // @todo keeping for reference 457 | // describe('cli-ux', () => { 458 | // it("can test cli-ux", async (done) => { 459 | 460 | // const promptPromise = cli.prompt('Require input?') 461 | // process.stdin.emit('data', '') 462 | // process.stdin.emit('data', 'answer'); 463 | // const answer = await promptPromise; 464 | // expect(answer).toEqual('answer'); 465 | // await cli.done(); 466 | // done(); 467 | // }) 468 | // }) 469 | // }); 470 | 471 | // 472 | // @todo mock origin so that publish can be tested 473 | // 474 | // describe("publish", () => { 475 | // it("can publish", async (done) => { 476 | // const time = Date.now(); 477 | 478 | // fs.writeFileSync( 479 | // path.join("src/content/can-publish.html"), 480 | // `

I am published at ${time}

` 481 | // ); 482 | 483 | // await run(["publish"]); 484 | 485 | // execa.sync("git", ["checkout", "master"]); 486 | 487 | // //@todo gracefully handle commits and rollbacks 488 | // done(); 489 | // }); 490 | // }); 491 | }); 492 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/src/commands/a11y/index.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags } from "@oclif/command"; 2 | import chalk from "chalk"; 3 | import glob from "glob"; 4 | import pa11y from "pa11y"; 5 | import pa11yReportCLI from "pa11y-reporter-cli"; 6 | import path from "path"; 7 | import fs from "fs"; 8 | import cli from "cli-ux"; 9 | 10 | // Workaround to suppress node event emitter warning 11 | // @todo queue requests to avoid setting too many listeners at once 12 | require("events").EventEmitter.defaultMaxListeners = 100; 13 | 14 | export default class A11y extends Command { 15 | static description = "runs accessibility validation"; 16 | 17 | static examples = [ 18 | `$ htmlgoddess a11y`, 19 | `$ htmlgoddess a11y http://localhost:3000/index.html`, 20 | `$ htmlgoddess a11y ./path/to/your/file.html`, 21 | ]; 22 | 23 | static flags = { 24 | help: flags.help({ char: "h" }), 25 | url: flags.string({ 26 | char: "u", 27 | description: "Run on individual URL", 28 | default: "", 29 | }), 30 | // flag with a value (-n, --name=VALUE) 31 | standard: flags.string({ 32 | char: "s", 33 | description: "Web Accessibility Guideline standard", 34 | default: "WCAG2AA", 35 | options: ["Section508", "WCAG2A", "WCAG2AA", "WCAG2AAA"], 36 | }), 37 | }; 38 | 39 | static args = [{ name: "projectDir" }]; 40 | 41 | async run() { 42 | const { args, flags } = this.parse(A11y); 43 | 44 | const projectDir = args.projectDir ? args.projectDir : process.cwd(); 45 | const { standard, url } = flags; 46 | 47 | cli.action.start(`Validating site to Accessibility standard: ${standard}`); 48 | 49 | return new Promise(async (resolve, reject) => { 50 | const htmlFiles = url 51 | ? [url] 52 | : glob.sync(projectDir + "/docs/**/*+(*.htm|*.html)"); 53 | 54 | const options = { 55 | standard, 56 | }; 57 | // this.log(htmlFiles); 58 | // Run tests against multiple URLs 59 | const results = await Promise.all( 60 | htmlFiles.map((file) => { 61 | return pa11y(file, options); 62 | }) 63 | ); 64 | 65 | cli.action.stop("done"); 66 | 67 | const cliResults = pa11yReportCLI.results(results[0]); 68 | 69 | const resultsWithIssues = results.filter( 70 | (result) => result.issues.length 71 | ); 72 | 73 | if (resultsWithIssues.length === 0) { 74 | this.log(chalk.green("A11Y Tests All Passed!")); 75 | resolve(htmlFiles); 76 | } else { 77 | resultsWithIssues.forEach((result) => { 78 | const cliResults = pa11yReportCLI.results(result); 79 | this.log(chalk.red(cliResults)); 80 | }); 81 | reject(resultsWithIssues); 82 | } 83 | }); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/src/commands/create/index.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags } from "@oclif/command"; 2 | import execa from "execa"; 3 | import path from "path"; 4 | import cli from "cli-ux"; 5 | import inquirer from "inquirer"; 6 | import chalk from "chalk"; 7 | import * as git from "isomorphic-git"; 8 | import http from "isomorphic-git/http/node"; 9 | import { getTemplatePath, getAllTemplateNames, getAllStyleSheets } from "@htmlgoddess/templates"; 10 | import fs from "fs-extra"; 11 | import glob from 'glob'; 12 | 13 | export default class Create extends Command { 14 | static description = 15 | "creates a new website project under the grace of the HTML Goddess"; 16 | 17 | static examples = [ 18 | `$ htmlgoddess create ./path/to/directory`, 19 | `$ htmlgoddess create`, 20 | ]; 21 | 22 | static flags = { 23 | help: flags.help({ char: "h" }), 24 | // flag with no value (-f, --force) 25 | force: flags.boolean({ char: "f" }), 26 | }; 27 | 28 | static args = [{ name: "projectDir" }]; 29 | 30 | // async catch(error) { 31 | // this.log("error", "error"); 32 | // // do something or 33 | // // re-throw to be handled globally 34 | // throw error; 35 | // } 36 | 37 | run(): Promise { 38 | const { args, flags } = this.parse(Create); 39 | 40 | const projectDir = args.projectDir ? args.projectDir : process.cwd(); 41 | 42 | return new Promise(async (resolve, reject) => { 43 | this.log(""); 44 | this.log(chalk.green("HTML Goddess CLI")); 45 | this.log(""); 46 | const name = await cli.prompt("What is the name of your site?"); 47 | this.log(""); 48 | // const template = await cli.prompt("What is the name of your template?"); 49 | // this.log(""); 50 | 51 | // @todo with inquirer when I can figure out how mock prompts 52 | const { template } = await inquirer.prompt([ 53 | { 54 | name: "template", 55 | message: "select a template", 56 | type: "list", 57 | choices: getAllTemplateNames(), 58 | }, 59 | ]); 60 | 61 | const basicTemplateDir = getTemplatePath('basic'); 62 | 63 | const templateDir = getTemplatePath(template); 64 | 65 | const { stylesheet } = await inquirer.prompt([ 66 | { 67 | name: "stylesheet", 68 | message: "select a stylesheet", 69 | type: "list", 70 | choices: getAllStyleSheets(), 71 | }, 72 | ]); 73 | 74 | if (!templateDir) { 75 | this.log(chalk.red(`Template does not exist. ${templateDir}`)); 76 | return reject(`Template does not exist. ${templateDir}`); 77 | } 78 | 79 | this.log(""); 80 | 81 | const confirm = await cli.confirm( 82 | `The name of your site is ${chalk.keyword("orange")( 83 | name 84 | )}. It is a ${template} and it will be installed at ${projectDir}. Please confirm. (y/n)` 85 | ); 86 | 87 | this.log(""); 88 | 89 | cli.action.start("Installing your site..."); 90 | 91 | try { 92 | // Basic template is used as base and merged with 93 | // selected template 94 | fs.copySync(basicTemplateDir, projectDir, { 95 | errorOnExist: true, 96 | overwrite: false, 97 | }); 98 | fs.copySync(templateDir, projectDir, { 99 | errorOnExist: false, 100 | overwrite: true, 101 | }); 102 | 103 | const htmlTemplateFiles = await glob.sync( 104 | `${projectDir}/src/templates/**/*+(*.htm|*.html)` 105 | ); 106 | 107 | // Goes through all the templates and sets them to the 108 | // selected css file. 109 | // @todo move this to its own function. 110 | for (let x = 0; x < htmlTemplateFiles.length; x++) { 111 | let templateContent = fs.readFileSync(htmlTemplateFiles[x], 'utf8'); 112 | 113 | const outputTemplateContent = templateContent.replace(/(]*?>)/ig, `$1${stylesheet}$3`); 114 | 115 | if (templateContent !== outputTemplateContent) { 116 | fs.writeFileSync(htmlTemplateFiles[x], outputTemplateContent); 117 | } 118 | } 119 | 120 | } catch (error) { 121 | cli.action.stop(chalk.red("error")); 122 | this.log(""); 123 | this.log(chalk.red(error)); 124 | this.log(""); 125 | 126 | return reject("Destination directory already exists"); 127 | } 128 | 129 | if (!fs.existsSync(path.join(projectDir, "src/content/index.html"))) { 130 | cli.action.stop(chalk.red("error")); 131 | this.log(""); 132 | this.log(chalk.red("Something went wrong when creating site files")); 133 | this.log(""); 134 | 135 | return reject("Something went wrong when creating site files"); 136 | } 137 | this.log('git init time', projectDir) 138 | 139 | process.chdir(projectDir); 140 | await execa("git", ["init", "--quiet"]); 141 | await execa("git", ["add", '.']); 142 | await execa("git", ["commit", "-m", "Saving content edit."]); 143 | 144 | cli.action.stop("done!"); 145 | 146 | this.log(""); 147 | this.log(`✨ Successfully created project: ${name}`); 148 | this.log(""); 149 | this.log(`👉 Get started with the following commands:`); 150 | this.log(""); 151 | this.log(chalk.green(`1) cd ${projectDir}`)); 152 | this.log(chalk.green(`2) htmlgoddess print`)); 153 | this.log(chalk.green(`3) htmlgoddess serve`)); 154 | this.log(""); 155 | this.log(chalk.green(`run htmlgoddess --help for a full list of commands.`)); 156 | 157 | resolve({ name, template, path: projectDir }); 158 | }); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/src/commands/format/auto.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags } from "@oclif/command"; 2 | import execa from "execa"; 3 | 4 | export default class FormatAuto extends Command { 5 | static description = "watches for changes and formats your HTML"; 6 | 7 | static examples = [ 8 | `$ htmlgoddess format:auto 9 | `, 10 | ]; 11 | 12 | static flags = { 13 | help: flags.help({ char: "h" }), 14 | // flag with no value (-f, --force) 15 | }; 16 | 17 | static args = [{ name: "projectDir" }]; 18 | 19 | async run() { 20 | const { args, flags } = this.parse(FormatAuto); 21 | 22 | const projectDir = args.projectDir ? args.projectDir : process.cwd(); 23 | 24 | execa("onchange", [ 25 | `${projectDir}/src/**/*.html`, 26 | `${projectDir}/src/**/*.css`, 27 | "--", 28 | "prettier", 29 | "{{changed}}", 30 | "--write", 31 | ]).stdout.pipe(process.stdout); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/src/commands/format/index.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags } from "@oclif/command"; 2 | import execa from "execa"; 3 | import prettier from "prettier"; 4 | import fs from "fs"; 5 | import glob from "glob"; 6 | import cli from "cli-ux"; 7 | import chalk from "chalk"; 8 | 9 | export default class Format extends Command { 10 | static description = "formats your HTML"; 11 | 12 | static examples = [ 13 | `$ htmlgoddess format 14 | `, 15 | ]; 16 | 17 | static flags = { 18 | help: flags.help({ char: "h" }), 19 | // flag with a value (-n, --name=VALUE) 20 | name: flags.string({ char: "n", description: "name to print" }), 21 | // flag with no value (-f, --force) 22 | force: flags.boolean({ char: "f" }), 23 | }; 24 | 25 | static strict = false; 26 | 27 | static args = [{ name: "projectDir", required: false }]; 28 | 29 | async run() { 30 | const { args, flags } = this.parse(Format); 31 | 32 | const projectDir = args.projectDir ? args.projectDir : process.cwd(); 33 | 34 | cli.action.start( 35 | `Formatting source files for your website in ${projectDir}...` 36 | ); 37 | return new Promise(async (resolve, reject) => { 38 | glob(`${projectDir}/src/**/*+(.htm|.html|.css)`, (err, matches) => { 39 | for (let x = 0; x < matches.length; x++) { 40 | const html = fs.readFileSync(matches[x], "utf-8"); 41 | if (!prettier.check(html, { filepath: matches[x] })) { 42 | const output = prettier.format(html, { filepath: matches[x] }); 43 | fs.writeFileSync(matches[x], output); 44 | this.log("formatted", chalk.green(matches[x])); 45 | } 46 | } 47 | cli.action.stop(chalk.green(`done`)); 48 | resolve(true); 49 | }); 50 | }); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/src/commands/print/auto.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags } from "@oclif/command"; 2 | import Print from "../print"; 3 | import watch from "recursive-watch"; 4 | import path from "path"; 5 | 6 | export default class AutoPrint extends Command { 7 | static description = "describe the command here"; 8 | 9 | static examples = [ 10 | `$ htmlgoddess print 11 | hello world wide web from ./src/hello.ts! 12 | `, 13 | ]; 14 | 15 | static flags = { 16 | help: flags.help({ char: "h" }), 17 | 18 | debounce: flags.integer({ char: "d", default: 500 }), 19 | // flag with a value (-n, --name=VALUE) 20 | name: flags.string({ char: "n", description: "name to print" }), 21 | // flag with no value (-f, --force) 22 | force: flags.boolean({ char: "f" }), 23 | }; 24 | 25 | static args = [{ name: "projectDir" }]; 26 | 27 | async run() { 28 | const { args, flags } = this.parse(AutoPrint); 29 | 30 | const projectDir = args.projectDir 31 | ? args.projectDir 32 | : path.join(process.cwd()); 33 | 34 | const projectSrcDir = path.join(projectDir, "/src"); 35 | 36 | const { debounce } = flags; 37 | 38 | return new Promise((resolve, reject) => { 39 | this.log("Watching: ", projectSrcDir); 40 | // ... or a directory 41 | 42 | let timestamp = Date.now(); 43 | 44 | const unwatch = watch(projectSrcDir, (filename) => { 45 | if (Date.now() - timestamp > debounce) { 46 | timestamp = Date.now(); 47 | this.log(filename, "changed. Reprinting website to docs"); 48 | Print.run([projectDir, "--no-a11y"]).then((results) => { 49 | this.log("Website auto printed."); 50 | }); 51 | } 52 | }); 53 | 54 | resolve(unwatch); 55 | }); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/src/commands/print/index.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags } from "@oclif/command"; 2 | import execa from "execa"; 3 | import path from "path"; 4 | import cli from "cli-ux"; 5 | import webpack from "webpack"; 6 | import A11y from "../a11y"; 7 | import fs from "fs"; 8 | import glob from "glob"; 9 | import getWebpackConfig from "../../webpack-config-generator"; 10 | import chalk from "chalk"; 11 | import { fileURLToPath } from "url"; 12 | import { config } from "process"; 13 | export default class Print extends Command { 14 | static description = 15 | "compiles source html/css files from src and prints to docs folder"; 16 | 17 | static examples = [ 18 | `$ htmlgoddess print`, 19 | `$ htmlgoddess print ./path/to/your/project`, 20 | ]; 21 | 22 | static flags = { 23 | help: flags.help({ char: "h" }), 24 | 25 | a11y: flags.boolean({ char: "a", default: true, allowNo: true }), 26 | // flag with a value (-n, --name=VALUE) 27 | name: flags.string({ char: "n", description: "name to print" }), 28 | // flag with no value (-f, --force) 29 | force: flags.boolean({ char: "f" }), 30 | }; 31 | 32 | static strict = false; 33 | 34 | static args = [{ name: "projectDir", hidden: false, required: false }]; 35 | 36 | async run() { 37 | const { args, flags } = this.parse(Print); 38 | const projectDir = args.projectDir ? args.projectDir : process.cwd(); 39 | 40 | const { a11y } = flags; 41 | 42 | cli.action.start( 43 | `Printing your website from ${projectDir}/src to ${projectDir}/docs` 44 | ); 45 | return new Promise(async (resolve, reject) => { 46 | // Check the last modified time of the folder and see if it matches 47 | if (fs.existsSync(`${projectDir}/docs`)) { 48 | const printedFiles = await glob.sync( 49 | `${projectDir}/docs/**/*+(*.htm|*.html)` 50 | ); 51 | 52 | const filesModified = printedFiles.filter((file) => { 53 | const stats = fs.statSync(file); 54 | return stats.mtimeMs - stats.birthtimeMs > 2000; 55 | }); 56 | 57 | if (filesModified.length) { 58 | const confirmPrint = await cli.confirm( 59 | chalk.yellowBright( 60 | `Hold on! It looks like someone has edited an html file in your docs folder. This folder is wiped every time you print which means any changes in docs will be ERASED. 61 | 62 | Files changed: 63 | 64 | ${filesModified.join("\n")} 65 | 66 | Are you sure you want to continue? (y/n)` 67 | ) 68 | ); 69 | 70 | this.log("CONFIRM PRINT", confirmPrint); 71 | if (!confirmPrint) { 72 | cli.action.stop(chalk.red("cancelled.")); 73 | return reject("cancelled"); 74 | } 75 | } 76 | } 77 | 78 | const compiler = webpack(getWebpackConfig(projectDir), (err, stats) => { 79 | if (err) { 80 | console.error(err); 81 | return; 82 | } 83 | 84 | cli.action.stop(chalk.green("done.")); 85 | 86 | if (a11y) { 87 | A11y.run([projectDir]).then( 88 | (results) => { 89 | resolve(stats); 90 | }, 91 | (results) => { 92 | resolve(stats); 93 | } 94 | ); 95 | } else { 96 | resolve(stats); 97 | } 98 | }); 99 | }); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/src/commands/publish/index.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags } from "@oclif/command"; 2 | import execa from "execa"; 3 | export default class Publish extends Command { 4 | static description = "publishes your saved changes to git"; 5 | 6 | static examples = [ 7 | `$ htmlgoddess publish 8 | `, 9 | ]; 10 | 11 | static flags = { 12 | help: flags.help({ char: "h" }), 13 | // flag with a value (-n, --name=VALUE) 14 | // flag with no value (-f, --force) 15 | force: flags.boolean({ char: "f" }), 16 | }; 17 | 18 | static args = [{ name: "file" }]; 19 | 20 | async run() { 21 | const { args, flags } = this.parse(Publish); 22 | this.log("Publishing work"); 23 | await execa("git", ["push", "origin"]).stdout.pipe(process.stdout); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/src/commands/save/index.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags } from "@oclif/command"; 2 | import execa from "execa"; 3 | import * as git from "isomorphic-git"; 4 | import fs from "fs"; 5 | import cli from "cli-ux"; 6 | 7 | export default class Save extends Command { 8 | static description = "saves all changes in your project"; 9 | 10 | static examples = [ 11 | `$ htmlgoddess save 12 | `, 13 | ]; 14 | 15 | static flags = { 16 | help: flags.help({ char: "h" }), 17 | }; 18 | 19 | static args = [{ name: "projectDir" }]; 20 | 21 | async run() { 22 | const { args, flags } = this.parse(Save); 23 | 24 | const projectDir = args.projectDir ? args.projectDir : process.cwd(); 25 | 26 | this.log("Saving work", projectDir); 27 | await execa("git", ["add", projectDir]).stdout.pipe(process.stdout); 28 | await cli.wait(200); 29 | await execa("git", ["commit", "-m", "Saving content edit."]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/src/commands/serve/auto.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags } from "@oclif/command"; 2 | 3 | export default class ServeAuto extends Command { 4 | static description = "serves your website and auto-reloads when changed."; 5 | 6 | static examples = [ 7 | `$ htmlgoddess serve 8 | `, 9 | ]; 10 | 11 | static flags = { 12 | help: flags.help({ char: "h" }), 13 | // flag with a value (-n, --name=VALUE) 14 | name: flags.string({ char: "n", description: "name to print" }), 15 | // flag with no value (-f, --force) 16 | force: flags.boolean({ char: "f" }), 17 | }; 18 | 19 | static args = [{ name: "file" }]; 20 | 21 | async run() { 22 | const { args, flags } = this.parse(ServeAuto); 23 | // serve(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/src/commands/serve/index.ts: -------------------------------------------------------------------------------- 1 | import { Command, flags } from "@oclif/command"; 2 | import execa from "execa"; 3 | import path from "path"; 4 | import express from "express"; 5 | import livereload from "livereload"; 6 | import fs from "fs"; 7 | import cli from "cli-ux"; 8 | import chalk from "chalk"; 9 | 10 | export default class Serve extends Command { 11 | static description = "serves your website on a local webserver"; 12 | 13 | static examples = [ 14 | `$ htmlgoddess serve 15 | `, 16 | ]; 17 | 18 | public subprocess = null; 19 | 20 | static flags = { 21 | help: flags.help({ char: "h" }), 22 | // flag with a value (-n, --name=VALUE) 23 | name: flags.string({ char: "n", description: "name to print" }), 24 | // flag with no value (-f, --force) 25 | force: flags.boolean({ char: "f" }), 26 | }; 27 | 28 | static args = [{ name: "projectPath" }]; 29 | 30 | run() { 31 | const { args, flags } = this.parse(Serve); 32 | 33 | const projectPath = args.projectPath ? args.projectPath : process.cwd(); 34 | 35 | return new Promise((resolve, reject) => { 36 | cli.action.start(`Starting server from ${projectPath}/docs`); 37 | const app = express(); 38 | const port = 3000; 39 | const liveReloadScript = ` 40 | 41 | 45 | 46 | `; 47 | 48 | /** 49 | * Injects live reload script into all html GET requests. 50 | */ 51 | app.get("*(.html|.htm|/)", (req, res, next) => { 52 | res.format({ 53 | html: function () { 54 | let filename = path.join(projectPath, "/docs", req.url); 55 | if (filename.charAt(filename.length - 1) === "/") { 56 | filename += "index.html"; 57 | } 58 | 59 | if (fs.existsSync(filename)) { 60 | const content = fs 61 | .readFileSync(filename, "utf-8") 62 | .replace("", `${liveReloadScript}`); 63 | res.send(content); 64 | } 65 | }, 66 | default: function () { 67 | next(); 68 | }, 69 | }); 70 | }); 71 | 72 | app.use(express.static("docs")); 73 | 74 | app.listen(port, () => { 75 | cli.action.stop(chalk.green(`started.`)); 76 | this.log(""); 77 | this.log( 78 | `Local server listening at: ${chalk.keyword("green")( 79 | `http://localhost:${port}` 80 | )}` 81 | ); 82 | this.log("Opening in your browers..."); 83 | 84 | cli.open(`http://localhost:${port}`); 85 | 86 | this.log(""); 87 | const liveReloadServer = livereload.createServer(); 88 | this.log( 89 | `Live reload server listening at: ${chalk.keyword("purple")( 90 | `http://localhost:${liveReloadServer.config.port}` 91 | )}` 92 | ); 93 | liveReloadServer.watch(projectPath + "/docs"); 94 | }); 95 | 96 | resolve(app); 97 | }); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/src/index.ts: -------------------------------------------------------------------------------- 1 | export { run } from "@oclif/command"; 2 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/src/webpack-config-generator.ts: -------------------------------------------------------------------------------- 1 | import glob from "glob"; 2 | import fs from "fs"; 3 | import webpack from "webpack"; 4 | import HtmlWebpackPlugin from "html-webpack-plugin"; 5 | import MiniCssExtractPlugin from "mini-css-extract-plugin"; 6 | import FixStyleOnlyEntriesPlugin from "webpack-fix-style-only-entries"; 7 | import { CleanWebpackPlugin } from "clean-webpack-plugin"; 8 | import CopyPlugin from "copy-webpack-plugin"; 9 | import HtmlReplaceWebpackPlugin from "html-replace-webpack-plugin"; 10 | import HtmlBeautifyPlugin from "html-beautify-webpack-plugin"; 11 | import HtmlGoddessPlugin from "@htmlgoddess/webpack-plugin"; 12 | import htmlLoader from "html-loader"; 13 | import styleLoader from "css-loader"; 14 | import path from "path"; 15 | 16 | function getWebpackConfig(projectDir: string): any { 17 | const plugins = []; 18 | const htmlFiles = glob.sync(projectDir + "/src/content/**/*+(*.htm|*.html)"); 19 | 20 | for (let x = 0; x < htmlFiles.length; x++) { 21 | const pathObj = path.parse(htmlFiles[x]); 22 | let templateRelPath = htmlFiles[x].replace( 23 | /.+src\/content\//, 24 | "src/templates/" 25 | ); 26 | let relOutputPath = htmlFiles[x].replace(/.+src\/content\//, ""); 27 | let templateFilename = "index.html"; 28 | let templateAbsolutePath; 29 | 30 | // If the file is not in a directory, make it the name of the timeplate 31 | if ( 32 | fs.existsSync( 33 | path.join(projectDir, path.dirname(templateRelPath) + ".html") 34 | ) 35 | ) { 36 | templateAbsolutePath = path.join( 37 | projectDir, 38 | path.dirname(templateRelPath) + ".html" 39 | ); 40 | } else { 41 | templateAbsolutePath = path.join( 42 | projectDir, 43 | path.dirname(templateRelPath), 44 | "index.html" 45 | ); 46 | } 47 | 48 | plugins.push( 49 | new HtmlWebpackPlugin({ 50 | filename: relOutputPath, 51 | templateParameters: { 52 | contentPath: htmlFiles[x], 53 | }, 54 | template: templateAbsolutePath, 55 | }) 56 | ); 57 | } 58 | 59 | return { 60 | entry: path.join(projectDir, "src/css/index.css"), // Prevents unnecessary main.js from being included. 61 | output: { 62 | path: path.join(projectDir, "docs"), 63 | }, 64 | context: projectDir, 65 | mode: "development", 66 | stats: "errors-warnings", 67 | module: { 68 | rules: [ 69 | { 70 | test: /\.html$/i, // Enables live reload 71 | loader: require.resolve("html-loader"), 72 | }, 73 | { 74 | // For CSS modules 75 | // For pure CSS - /\.css$/i, 76 | // For Sass/SCSS - /\.((c|sa|sc)ss)$/i, 77 | // For Less - /\.((c|le)ss)$/i, 78 | test: /\.((c|sa|sc)ss)$/i, 79 | // include: paths.appSrc, 80 | loaders: [require.resolve("css-loader")], 81 | }, 82 | { 83 | test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/i, 84 | loader: require.resolve("url-loader"), 85 | options: { 86 | limit: 8192, 87 | }, 88 | }, 89 | ], 90 | }, 91 | plugins: [ 92 | new HtmlGoddessPlugin({ projectDir }), 93 | ...plugins, 94 | new FixStyleOnlyEntriesPlugin({ silent: true }), 95 | new CleanWebpackPlugin({ 96 | dry: false, 97 | verbose: false, 98 | cleanOnceBeforeBuildPatterns: ["**/*", "!CNAME"], 99 | }), 100 | new CopyPlugin({ 101 | patterns: [ 102 | { 103 | flatten: true, 104 | toType: "dir", 105 | from: projectDir + "/src/css/*.css", 106 | to: "css", 107 | }, 108 | ], 109 | }), 110 | ], 111 | }; 112 | } 113 | 114 | export = getWebpackConfig; 115 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "importHelpers": true, 5 | "noImplicitAny": false, 6 | "module": "commonjs", 7 | "outDir": "lib", 8 | "rootDir": "src", 9 | "strict": false, 10 | "target": "es2017", 11 | "allowSyntheticDefaultImports": true, 12 | "esModuleInterop": true, 13 | "skipLibCheck": true, 14 | "allowJs": true, 15 | }, 16 | "include": [ 17 | "src/**/*", "__tests__", 18 | ], 19 | "exclude": ["**/*.test.ts"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/README.md: -------------------------------------------------------------------------------- 1 | # @htmlgoddess/template 2 | 3 | A package of starter templates used in HTML Goddess CLI. 4 | 5 | All templates work any vanilla (reset) css files that target plain HTML elements. 6 | 7 | ## Templates 8 | - Basic - The barebones template. 9 | - Blog - A starter blog template. 10 | 11 | ## Todo 12 | - Gallery Template 13 | - Emergency Site Template 14 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/README.md: -------------------------------------------------------------------------------- 1 | # `test` 2 | 3 | > TODO: description 4 | 5 | ## Usage 6 | 7 | ``` 8 | const test = require('test'); 9 | 10 | // TODO: DEMONSTRATE API 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/src/content/index.html: -------------------------------------------------------------------------------- 1 |
2 |

Hello World

3 |

This is your homepage

4 |
5 |
6 |
-------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/src/css/basic.css: -------------------------------------------------------------------------------- 1 | /* Basic.css */ 2 | 3 | * { 4 | box-sizing: border-box; 5 | } 6 | 7 | :root { 8 | --sans: 1em/1.6 system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, 9 | Oxygen, Ubuntu, Cantarell, Droid Sans, Helvetica Neue, Fira Sans, sans-serif; 10 | --mono: "Courier New", Courier, "Ubuntu Mono", "Liberation Mono", monospace; 11 | --c1: #0074d9; 12 | --c2: #eee; 13 | --c3: #fff; 14 | --c4: #000; 15 | --c5: #fff; 16 | --m1: 8px; 17 | --rc: 8px; 18 | } 19 | 20 | @media (prefers-color-scheme: dark) { 21 | :root { 22 | --c2: #333; 23 | --c3: #1e1f20; 24 | --c4: #fff; 25 | } 26 | } 27 | 28 | html { 29 | -ms-text-size-adjust: 100%; 30 | -webkit-text-size-adjust: 100%; 31 | } 32 | 33 | /* General settings */ 34 | 35 | body { 36 | margin: 0; 37 | font: var(--sans); 38 | font-weight: 400; 39 | font-style: normal; 40 | text-rendering: optimizeLegibility; 41 | -webkit-font-smoothing: antialiased; 42 | background-color: var(--c3); 43 | color: var(--c4); 44 | } 45 | img, 46 | iframe { 47 | border: none; 48 | max-width: 100%; 49 | } 50 | 51 | a { 52 | color: var(--c1); 53 | text-decoration: none; 54 | } 55 | 56 | a:hover { 57 | color: var(--c1); 58 | text-decoration: underline; 59 | } 60 | 61 | pre { 62 | font: 1em/1.6 var(--mono); 63 | background: var(--c2); 64 | padding: 1em; 65 | overflow: auto; 66 | } 67 | 68 | code { 69 | font: 1em/1.6 var(--mono); 70 | } 71 | 72 | blockquote { 73 | border-left: 5px solid var(--c2); 74 | padding: 1em 1.5em; 75 | margin: 0; 76 | } 77 | 78 | hr { 79 | border: 0; 80 | border-bottom: 1px solid var(--c4); 81 | } 82 | 83 | /* Headlines */ 84 | 85 | h1, 86 | h2, 87 | h3, 88 | h4, 89 | h5, 90 | h6 { 91 | margin: 0.6em 0; 92 | font-weight: normal; 93 | } 94 | 95 | h1 { 96 | font-size: 2.625em; 97 | line-height: 1.2; 98 | } 99 | 100 | h2 { 101 | font-size: 1.625em; 102 | line-height: 1.2; 103 | } 104 | 105 | h3 { 106 | font-size: 1.3125em; 107 | line-height: 1.24; 108 | } 109 | 110 | h4 { 111 | font-size: 1.1875em; 112 | line-height: 1.23; 113 | } 114 | 115 | h5, 116 | h6 { 117 | font-size: 1em; 118 | font-weight: bold; 119 | } 120 | 121 | /* Table */ 122 | 123 | table { 124 | border-collapse: collapse; 125 | border-spacing: 0; 126 | margin: 1em 0; 127 | } 128 | 129 | th, 130 | td { 131 | text-align: left; 132 | vertical-align: top; 133 | border: 1px solid; 134 | padding: 0.4em; 135 | } 136 | 137 | thead, 138 | tfoot { 139 | background: var(--c2); 140 | } 141 | 142 | /* Rounded Corners*/ 143 | 144 | pre, 145 | code, 146 | input, 147 | select, 148 | textarea, 149 | button, 150 | img { 151 | border-radius: var(--rc); 152 | } 153 | 154 | /* Forms */ 155 | 156 | input, 157 | select, 158 | textarea { 159 | font-size: 1em; 160 | color: var(--c4); 161 | background: var(--c2); 162 | border: 0; 163 | padding: 0.6em; 164 | } 165 | 166 | button, 167 | input [type="submit"], 168 | input[type="reset"], 169 | input[type="button"] { 170 | font-size: 1em; 171 | display: inline-block; 172 | color: var(--c5); 173 | background: var(--c1); 174 | border: 0; 175 | margin: 0 4px; 176 | padding: 0.6em; 177 | cursor: pointer; 178 | text-align: center; 179 | } 180 | 181 | button:hover, 182 | button:focus, 183 | input:hover, 184 | textarea:hover, 185 | select:hover { 186 | opacity: 0.8; 187 | } 188 | 189 | /* Infinite Grid */ 190 | 191 | section { 192 | display: flex; 193 | flex-flow: row wrap; 194 | } 195 | 196 | [style*="--c:"], 197 | section > section, 198 | aside, 199 | article { 200 | flex: var(--c, 1); 201 | margin: var(--m1); 202 | } 203 | 204 | /* Cards */ 205 | 206 | article { 207 | background: var(--c2); 208 | border-radius: var(--rc); 209 | padding: 1em; 210 | box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.3); 211 | } 212 | 213 | [style*="--c:"]:first-child, 214 | section > section:first-child, 215 | article:first-child { 216 | margin-left: 0; 217 | } 218 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/src/css/geocities.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-image: url("images/background.gif"); 3 | background-size: auto; 4 | margin: auto; 5 | width: 900px; 6 | } 7 | 8 | header { 9 | background-image: linear-gradient(red, yellow); 10 | padding: 10px; 11 | margin: 15px 0px; 12 | display: flex; 13 | justify-content: space-between; 14 | } 15 | 16 | header > img { 17 | height: 75px; 18 | } 19 | 20 | h1 { 21 | font-family: Monospace; 22 | color: gray; 23 | } 24 | 25 | footer { 26 | } 27 | 28 | .oldschool { 29 | padding: 10px; 30 | display: flex; 31 | justify-content: center; 32 | } 33 | 34 | .oldschool > a { 35 | cursor: pointer; 36 | } 37 | 38 | .links { 39 | padding: 15px; 40 | display: flex; 41 | justify-content: space-between; 42 | align-content: center; 43 | } 44 | 45 | .links > img, 46 | .links > a > img { 47 | height: 75px; 48 | } 49 | 50 | .links > a { 51 | font-family: Monospace; 52 | color: white; 53 | font-size: 20px; 54 | line-height: 75px; 55 | } 56 | 57 | .blink { 58 | animation: blinker 1s linear infinite; 59 | color: white; 60 | } 61 | 62 | @keyframes blinker { 63 | 50% { 64 | opacity: 0; 65 | } 66 | } 67 | 68 | @keyframes rotate { 69 | from { 70 | transform: rotateY(0deg); 71 | -webkit-transform: rotateY(0deg); 72 | -moz-transform: rotateY(0deg); 73 | -ms-transform: rotateY(0deg); 74 | -o-transform: rotateY(0deg); 75 | } 76 | to { 77 | transform: rotateY(-360deg); 78 | -webkit-transform: rotateY(-360deg); 79 | -moz-transform: rotateY(-360deg); 80 | -ms-transform: rotateY(-360deg); 81 | -o-transform: rotateY(-360deg); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/src/css/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --background-color: black; 3 | --color: white; 4 | --font-family: "Lucida Console", Monaco, monospace; 5 | --font-weight: normal; 6 | --font-size: 20px; 7 | --line-height: 1.5em; 8 | letter-spacing: 0.01px; 9 | } 10 | 11 | a { 12 | --color: gold; 13 | } 14 | 15 | p { 16 | margin: 3rem 0; 17 | } 18 | 19 | /* I have attempted to list all the pertinent properties to override defaults in the user agent css */ 20 | body { 21 | display: block; 22 | position: static; 23 | visibility: visible; 24 | vertical-align: baseline; /* baseline is the default. Aligns the baseline of the element's text content with the baseline of the parent element. Probably irrelevant in body. */ 25 | margin: 0; 26 | border: 0; 27 | padding: 0; 28 | height: auto; 29 | width: auto; 30 | 31 | background-color: var(--background-color); 32 | color: var(--color); 33 | font-family: var(--font-family); 34 | font-weight: var(--font-weight); 35 | font-size: var(--font-size); 36 | line-height: var(--line-height); 37 | text-decoration: none; /* can be underline, overline, line-through */ 38 | 39 | text-align: justify; 40 | text-justify: auto; /* justification algorithm (auto selected by browser) */ 41 | text-align-last: left; /* may not be supported in some browsers */ 42 | text-indent: 0; /* first line of text */ 43 | overflow: auto; /* what to do when content overflows content area of the element; scroll bars */ 44 | /* text-overflow: auto; syntax error, auto not valid. */ 45 | white-space: normal; /* weird and dangerous; keep at normal, which is the default. */ 46 | word-wrap: break-word; /* normal is the default, which allows longwords to overflow the element's box; break-word breaks and wraps them */ 47 | } 48 | 49 | div { 50 | display: block; 51 | /* 52 | border-style:solid; /* for testing: divs will not display border without this *.../ 53 | border-width: 3px; 54 | border-color: green; 55 | */ 56 | } 57 | 58 | div.main-text-area { 59 | margin-left: 2em; 60 | margin-right: 2em; 61 | height: auto; 62 | } 63 | 64 | div.indent { 65 | margin-left: 2em; 66 | height: auto; 67 | } 68 | 69 | div.item { 70 | margin-bottom: 1.5em; 71 | /* border-color: red; 72 | background-color: yellow; */ 73 | } 74 | 75 | div.select-group { 76 | display: block; 77 | } 78 | 79 | p, 80 | h1, 81 | ol, 82 | ul, 83 | li { 84 | margin: 0; 85 | margin-top: 0.5em; 86 | margin-bottom: 1em; 87 | } 88 | 89 | ul, 90 | ol { 91 | padding: 0; 92 | padding-left: 1em; /* this is the indent created for the list, but may partially be filled with the numbers or bullets (see note below). */ 93 | padding-right: 1em; 94 | } 95 | 96 | /* The number or bullet appears to be displaced some minimum amount to the left of the li padding, and expands further to the left with more digits in a number. It appears to me that, depending on that minimum displacement amount plus the width of the number/bullet, it falls into the li margin-left and/or the ol/ul padding-left. */ 97 | 98 | li { 99 | margin-left: 2em; /* number or bullet may partially go in this margin. See not above.*/ 100 | padding-left: 0px; /* this minimumizes the displacement of the number/bullet to the left of the li */ 101 | } 102 | 103 | li.tight-list { 104 | margin-top: 0.3em; 105 | margin-bottom: 0.3em; 106 | } 107 | 108 | h1 { 109 | margin-top: 1em; 110 | font-weight: 800; 111 | font-size: 25px; 112 | text-align: left; 113 | } 114 | 115 | h1.centered-heading { 116 | text-align: center; 117 | text-align-last: center; 118 | } 119 | 120 | input, 121 | textarea { 122 | display: block; 123 | } 124 | 125 | input#captchaInput { 126 | width: 50px; 127 | display: inline; 128 | } 129 | 130 | input[type="text"], 131 | input[type="email"], 132 | input[type="tel"], 133 | textarea { 134 | box-sizing: border-box; /* without this, the width: 100% will be too big (major hassel) */ 135 | width: 100%; 136 | font-weight: 500; 137 | font-size: 18px; 138 | font-family: arial, verdana, helvetica, sans-serif; /* avoid user-agent overwrite of body */ 139 | text-align: left; 140 | padding: 5px; 141 | border-width: 3px; 142 | border-color: #8896d1; 143 | border-style: solid; 144 | } 145 | 146 | input[type="text"], 147 | input[type="email"], 148 | input[type="tel"] { 149 | max-width: 300px; 150 | } 151 | 152 | #goddess-email-confirm { 153 | margin-top: 3px; 154 | } 155 | 156 | textarea { 157 | max-width: 900px; 158 | } 159 | 160 | select { 161 | color: magenta; 162 | font-weight: 500; 163 | font-size: 18px; 164 | font-family: arial, verdana, helvetica, sans-serif; /* avoid user-agent overwrite of body */ 165 | text-align: left; 166 | padding: 5px; 167 | border-width: 3px; 168 | border-color: #8896d1; 169 | border-style: solid; 170 | background-color: lightgreen; 171 | } 172 | 173 | input[type="file"], 174 | button { 175 | color: magenta; 176 | font-weight: 500; 177 | font-size: 18px; 178 | font-family: arial, verdana, helvetica, sans-serif; /* avoid user-agent overwrite of body */ 179 | text-align: left; 180 | padding: 5px; 181 | border-width: 3px; 182 | border-color: #8896d1; 183 | border-style: solid; 184 | background-color: lightgreen; 185 | width: 400px; 186 | height: auto; 187 | } 188 | 189 | option { 190 | color: magenta; 191 | } 192 | 193 | /* Careful with these three ::placeholder selectors... it appears they cannot be combined into one statement. */ 194 | 195 | ::-webkit-input-placeholder { 196 | /* Edge, Chrome */ 197 | color: pink; 198 | opacity: 1; 199 | } 200 | 201 | :-ms-input-placeholder { 202 | /* Internet Explorer */ 203 | color: pink; 204 | opacity: 1; 205 | } 206 | 207 | ::placeholder { 208 | color: pink; 209 | opacity: 1; 210 | } 211 | 212 | img.page-main-photo { 213 | display: block; 214 | margin-left: auto; 215 | margin-right: auto; 216 | margin-top: 1em; 217 | width: 80%; 218 | } 219 | 220 | img.non-photo { 221 | width: 60%; 222 | } 223 | 224 | img.thumb-image { 225 | /* "thumb-image" is output from js */ 226 | border: 0; 227 | display: block; 228 | max-width: 150px; 229 | padding: 10px; 230 | height: auto; 231 | margin-left: auto; 232 | margin-right: auto; 233 | } 234 | 235 | div.video-frame { 236 | margin-left: auto; 237 | margin-right: auto; 238 | width: 80% !important; 239 | } 240 | 241 | p.photo-date { 242 | text-align: center; 243 | text-align-last: center; 244 | font-size: 14px; 245 | } 246 | 247 | p.footer { 248 | font-size: 13px; 249 | } 250 | 251 | a { 252 | color: var(--color); 253 | } 254 | 255 | a.plain-link { 256 | color: rgb(47, 0, 152); 257 | } 258 | 259 | hr { 260 | border-top: 1px solid rgb(47, 0, 152); 261 | width: 50%; 262 | } 263 | 264 | .red { 265 | color: #ff0000; 266 | } 267 | 268 | div.error, 269 | p.error { 270 | font-size: 20px; 271 | font-weight: 900; 272 | color: rgb(255, 0, 0); 273 | } 274 | 275 | div.success, 276 | p.success { 277 | font-size: 20px; 278 | font-weight: 900; 279 | color: rgb(0, 135, 0); 280 | } 281 | 282 | @media screen and (max-width: 500px) { 283 | /* phones */ 284 | body { 285 | /* background-color: pink; /* for test purposes */ 286 | text-align: left; 287 | } 288 | div.indent { 289 | margin-left: 0.5em; 290 | } 291 | div.main-text-area { 292 | margin-left: 5px; 293 | margin-right: 5px; 294 | } 295 | ul, 296 | ol { 297 | padding-left: 15px; 298 | padding-right: 15px; 299 | } 300 | li { 301 | margin-left: 15px; 302 | } 303 | img.page-main-photo { 304 | margin-top: 5px; 305 | width: 95%; 306 | } 307 | div.video-frame { 308 | width: 100% !important; 309 | } 310 | input[type="file"], 311 | button { 312 | width: 90%; 313 | } 314 | } 315 | 316 | @media screen and (min-width: 1000px) { 317 | /* wide screens */ 318 | body { 319 | /* background-color: lightgreen; /* for test purposes */ 320 | width: 1000px; 321 | margin-left: auto; 322 | margin-right: auto; 323 | } 324 | div.video-frame { 325 | width: 60% !important; 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/src/css/latex.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * LaTeX.css (https://latex.now.sh/) 3 | * 4 | * Source: https://github.com/vincentdoerig/latex-css 5 | * Licensed under MIT (https://github.com/vincentdoerig/latex-css/blob/master/LICENSE) 6 | */ 7 | a:current { 8 | color: black !important; 9 | } 10 | @font-face { 11 | font-family: "Latin Modern"; 12 | font-style: normal; 13 | font-weight: normal; 14 | font-display: swap; 15 | src: url("./fonts/LM-regular.woff2") format("woff2"), 16 | url("./fonts/LM-regular.woff") format("woff"), 17 | url("./fonts/LM-regular.ttf") format("truetype"); 18 | } 19 | 20 | @font-face { 21 | font-family: "Latin Modern"; 22 | font-style: italic; 23 | font-weight: normal; 24 | font-display: swap; 25 | src: url("./fonts/LM-italic.woff2") format("woff2"), 26 | url("./fonts/LM-italic.woff") format("woff"), 27 | url("./fonts/LM-italic.ttf") format("truetype"); 28 | } 29 | 30 | @font-face { 31 | font-family: "Latin Modern"; 32 | font-style: normal; 33 | font-weight: bold; 34 | font-display: swap; 35 | src: url("./fonts/LM-bold.woff2") format("woff2"), 36 | url("./fonts/LM-bold.woff") format("woff"), 37 | url("./fonts/LM-bold.ttf") format("truetype"); 38 | } 39 | 40 | @font-face { 41 | font-family: "Latin Modern"; 42 | font-style: italic; 43 | font-weight: bold; 44 | font-display: swap; 45 | src: url("./fonts/LM-bold-italic.woff2") format("woff2"), 46 | url("./fonts/LM-bold-italic.woff") format("woff"), 47 | url("./fonts/LM-bold-italic.ttf") format("truetype"); 48 | } 49 | 50 | /* Box sizing rules */ 51 | *, 52 | *::before, 53 | *::after { 54 | box-sizing: border-box; 55 | } 56 | 57 | /* Remove default margin */ 58 | body, 59 | h1, 60 | h2, 61 | h3, 62 | h4, 63 | p, 64 | ul[class], 65 | ol[class], 66 | li, 67 | figure, 68 | figcaption, 69 | dl, 70 | dd { 71 | margin: 0; 72 | } 73 | 74 | /* Make default font-size 1rem and add smooth scrolling to anchors */ 75 | html { 76 | font-size: 1rem; 77 | scroll-behavior: smooth; 78 | } 79 | 80 | body { 81 | font-family: "Latin Modern", Georgia, Cambria, "Times New Roman", Times, serif; 82 | line-height: 1.8; 83 | 84 | max-width: 80ch; 85 | min-height: 100vh; 86 | overflow-x: hidden; 87 | margin: 0 auto; 88 | padding: 2rem 1.25rem; 89 | 90 | counter-reset: theorem; 91 | counter-reset: definition; 92 | 93 | color: hsl(0, 5%, 10%); 94 | background-color: hsl(210, 20%, 98%); 95 | 96 | text-rendering: optimizeSpeed; 97 | } 98 | 99 | /* Justify and hyphenate all paragraphs */ 100 | p { 101 | text-align: justify; 102 | hyphens: auto; 103 | -webkit-hyphens: auto; 104 | -moz-hyphens: auto; 105 | margin-top: 1rem; 106 | } 107 | 108 | /* A elements that don't have a class get default styles */ 109 | a:not([class]) { 110 | text-decoration-skip-ink: auto; 111 | } 112 | 113 | /* Make links red */ 114 | a, 115 | a:visited { 116 | color: #a00; 117 | } 118 | 119 | /* Make images easier to work with */ 120 | img { 121 | max-width: 100%; 122 | display: block; 123 | } 124 | 125 | /* Inherit fonts for inputs and buttons */ 126 | input, 127 | button, 128 | textarea, 129 | select { 130 | font: inherit; 131 | } 132 | 133 | /* Prevent textarea from overflowing */ 134 | textarea { 135 | width: 100%; 136 | } 137 | 138 | /* Natural flow and rhythm in articles by default */ 139 | article > * + * { 140 | margin-top: 1em; 141 | } 142 | 143 | /* Styles for inline code or code snippets */ 144 | code, 145 | pre, 146 | kbd { 147 | font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", 148 | monospace; 149 | font-size: 85%; 150 | } 151 | pre { 152 | padding: 1rem 1.4rem; 153 | max-width: 100%; 154 | overflow: auto; 155 | border: 1px solid hsl(210, 15%, 49%); 156 | border-radius: 4px; 157 | background: hsl(210, 40%, 96%); 158 | } 159 | pre code { 160 | font-size: 95%; 161 | position: relative; 162 | } 163 | kbd { 164 | background: hsl(210, 5%, 100%); 165 | border: 1px solid hsl(210, 5%, 70%); 166 | border-radius: 2px; 167 | padding: 2px 4px; 168 | font-size: 75%; 169 | } 170 | 171 | /* Make table 100% width, add borders between rows */ 172 | table { 173 | border-collapse: collapse; 174 | border-spacing: 0; 175 | width: 100%; 176 | max-width: 100%; 177 | } 178 | th, 179 | td { 180 | text-align: left; 181 | padding: 0.5rem; 182 | } 183 | td { 184 | border-bottom: 1px solid hsl(0, 0%, 85%); 185 | } 186 | thead th { 187 | border-bottom: 2px solid hsl(0, 0%, 70%); 188 | } 189 | tfoot th { 190 | border-top: 2px solid hsl(0, 0%, 70%); 191 | } 192 | 193 | /* Center align the title */ 194 | h1:first-child { 195 | text-align: center; 196 | } 197 | 198 | /* Nested ordered list for ToC */ 199 | nav ol { 200 | counter-reset: item; 201 | padding-left: 2rem; 202 | } 203 | nav li { 204 | display: inline-block; 205 | margin: 0 1rem; 206 | } 207 | /* nav li:before { 208 | content: counters(item, ".") " "; 209 | counter-increment: item; 210 | padding-right: 0.85rem; 211 | } */ 212 | 213 | /* Center definitions (most useful for display equations) */ 214 | dl dd { 215 | text-align: center; 216 | } 217 | 218 | /* Theorem */ 219 | .theorem { 220 | counter-increment: theorem; 221 | display: block; 222 | margin: 12px 0; 223 | font-style: italic; 224 | } 225 | .theorem::before { 226 | content: "Theorem " counter(theorem) ". "; 227 | font-weight: bold; 228 | font-style: normal; 229 | } 230 | 231 | footer { 232 | margin-top: 300px; 233 | } 234 | 235 | /* Lemma */ 236 | .lemma { 237 | counter-increment: theorem; 238 | display: block; 239 | margin: 12px 0; 240 | font-style: italic; 241 | } 242 | .lemma::before { 243 | content: "Lemma " counter(theorem) ". "; 244 | font-weight: bold; 245 | font-style: normal; 246 | } 247 | 248 | /* Proof */ 249 | .proof { 250 | display: block; 251 | margin: 12px 0; 252 | font-style: normal; 253 | position: relative; 254 | } 255 | .proof::before { 256 | content: "Proof. " attr(title); 257 | font-style: italic; 258 | } 259 | .proof:after { 260 | content: "◾️"; 261 | position: absolute; 262 | right: -12px; 263 | bottom: -2px; 264 | } 265 | 266 | /* Definition */ 267 | .definition { 268 | counter-increment: definition; 269 | display: block; 270 | margin: 12px 0; 271 | font-style: normal; 272 | } 273 | .definition::before { 274 | content: "Definition " counter(definition) ". "; 275 | font-weight: bold; 276 | font-style: normal; 277 | } 278 | 279 | /* Center align author name, use small caps and add vertical spacing */ 280 | .author { 281 | margin: 0.85rem 0; 282 | font-variant-caps: small-caps; 283 | text-align: center; 284 | } 285 | 286 | /* Make footnote text smaller and left align it (looks bad with long URLs) */ 287 | .footnotes p { 288 | text-align: left; 289 | line-height: 1.5; 290 | font-size: 85%; 291 | margin-bottom: 0.4rem; 292 | } 293 | .footnotes { 294 | border-top: 1px solid hsl(0, 0%, 39%); 295 | } 296 | 297 | /* Center title and paragraph */ 298 | .abstract, 299 | .abstract p { 300 | text-align: center; 301 | } 302 | .abstract { 303 | margin: 2.25rem 0; 304 | } 305 | 306 | /* Format the LaTeX symbol correctly (a higher up, e lower) */ 307 | .latex span:nth-child(1) { 308 | text-transform: uppercase; 309 | font-size: 0.75em; 310 | vertical-align: 0.25em; 311 | margin-left: -0.36em; 312 | margin-right: -0.15em; 313 | line-height: 1ex; 314 | } 315 | 316 | .latex span:nth-child(2) { 317 | text-transform: uppercase; 318 | vertical-align: -0.5ex; 319 | margin-left: -0.1667em; 320 | margin-right: -0.125em; 321 | line-height: 1ex; 322 | } 323 | 324 | /* Heading typography */ 325 | h1 { 326 | font-size: 2.5rem; 327 | line-height: 3.25rem; 328 | margin-bottom: 1.625rem; 329 | } 330 | 331 | h2 { 332 | font-size: 1.7rem; 333 | line-height: 2rem; 334 | margin-top: 3rem; 335 | } 336 | 337 | h3 { 338 | font-size: 1.4rem; 339 | margin-top: 2.5rem; 340 | } 341 | 342 | h4 { 343 | font-size: 1.2rem; 344 | margin-top: 2rem; 345 | } 346 | 347 | h5 { 348 | font-size: 1rem; 349 | margin-top: 1.8rem; 350 | } 351 | 352 | h6 { 353 | font-size: 1rem; 354 | font-style: italic; 355 | font-weight: normal; 356 | margin-top: 2.5rem; 357 | } 358 | 359 | h3, 360 | h4, 361 | h5, 362 | h6 { 363 | line-height: 1.625rem; 364 | } 365 | 366 | h1 + h2 { 367 | margin-top: 1.625rem; 368 | } 369 | 370 | h2 + h3, 371 | h3 + h4, 372 | h4 + h5 { 373 | margin-top: 0.8rem; 374 | } 375 | 376 | h5 + h6 { 377 | margin-top: -0.8rem; 378 | } 379 | 380 | h2, 381 | h3, 382 | h4, 383 | h5, 384 | h6 { 385 | margin-bottom: 0.8rem; 386 | } 387 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/src/css/mvp.css: -------------------------------------------------------------------------------- 1 | /* MVP.css v1.6.2 - https://github.com/andybrewer/mvp */ 2 | 3 | :root { 4 | --border-radius: 5px; 5 | --box-shadow: 2px 2px 10px; 6 | --color: #118bee; 7 | --color-accent: #118bee15; 8 | --color-bg: #fff; 9 | --color-bg-secondary: #e9e9e9; 10 | --color-secondary: #920de9; 11 | --color-secondary-accent: #920de90b; 12 | --color-shadow: #f4f4f4; 13 | --color-text: #000; 14 | --color-text-secondary: #999; 15 | --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 16 | Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 17 | --hover-brightness: 1.2; 18 | --justify-important: center; 19 | --justify-normal: left; 20 | --line-height: 1.5; 21 | --width-card: 285px; 22 | --width-card-medium: 460px; 23 | --width-card-wide: 800px; 24 | --width-content: 1080px; 25 | } 26 | 27 | /* 28 | @media (prefers-color-scheme: dark) { 29 | :root { 30 | --color: #0097fc; 31 | --color-accent: #0097fc4f; 32 | --color-bg: #333; 33 | --color-bg-secondary: #555; 34 | --color-secondary: #e20de9; 35 | --color-secondary-accent: #e20de94f; 36 | --color-shadow: #bbbbbb20; 37 | --color-text: #f7f7f7; 38 | --color-text-secondary: #aaa; 39 | } 40 | } 41 | */ 42 | 43 | /* Layout */ 44 | article aside { 45 | background: var(--color-secondary-accent); 46 | border-left: 4px solid var(--color-secondary); 47 | padding: 0.01rem 0.8rem; 48 | } 49 | 50 | body { 51 | background: var(--color-bg); 52 | color: var(--color-text); 53 | font-family: var(--font-family); 54 | line-height: var(--line-height); 55 | margin: 0; 56 | overflow-x: hidden; 57 | padding: 1rem 0; 58 | } 59 | 60 | footer, 61 | header, 62 | main { 63 | margin: 0 auto; 64 | max-width: var(--width-content); 65 | padding: 2rem 1rem; 66 | } 67 | 68 | hr { 69 | background-color: var(--color-bg-secondary); 70 | border: none; 71 | height: 1px; 72 | margin: 4rem 0; 73 | } 74 | 75 | section { 76 | display: flex; 77 | flex-wrap: wrap; 78 | justify-content: var(--justify-important); 79 | } 80 | 81 | section aside { 82 | border: 1px solid var(--color-bg-secondary); 83 | border-radius: var(--border-radius); 84 | box-shadow: var(--box-shadow) var(--color-shadow); 85 | margin: 1rem; 86 | padding: 1.25rem; 87 | width: var(--width-card); 88 | } 89 | 90 | section aside:hover { 91 | box-shadow: var(--box-shadow) var(--color-bg-secondary); 92 | } 93 | 94 | section aside img { 95 | max-width: 100%; 96 | } 97 | 98 | [hidden] { 99 | display: none; 100 | } 101 | 102 | /* Headers */ 103 | article header, 104 | div header, 105 | main header { 106 | padding-top: 0; 107 | } 108 | 109 | header { 110 | text-align: var(--justify-important); 111 | } 112 | 113 | header a b, 114 | header a em, 115 | header a i, 116 | header a strong { 117 | margin-left: 0.5rem; 118 | margin-right: 0.5rem; 119 | } 120 | 121 | header nav img { 122 | margin: 1rem 0; 123 | } 124 | 125 | section header { 126 | padding-top: 0; 127 | width: 100%; 128 | } 129 | 130 | /* Nav */ 131 | nav { 132 | align-items: center; 133 | display: flex; 134 | font-weight: bold; 135 | justify-content: space-between; 136 | margin-bottom: 7rem; 137 | } 138 | 139 | nav ul { 140 | list-style: none; 141 | padding: 0; 142 | } 143 | 144 | nav ul li { 145 | display: inline-block; 146 | margin: 0 0.5rem; 147 | position: relative; 148 | text-align: left; 149 | } 150 | 151 | /* Nav Dropdown */ 152 | nav ul li:hover ul { 153 | display: block; 154 | } 155 | 156 | nav ul li ul { 157 | background: var(--color-bg); 158 | border: 1px solid var(--color-bg-secondary); 159 | border-radius: var(--border-radius); 160 | box-shadow: var(--box-shadow) var(--color-shadow); 161 | display: none; 162 | height: auto; 163 | left: -2px; 164 | padding: 0.5rem 1rem; 165 | position: absolute; 166 | top: 1.7rem; 167 | white-space: nowrap; 168 | width: auto; 169 | } 170 | 171 | nav ul li ul li, 172 | nav ul li ul li a { 173 | display: block; 174 | } 175 | 176 | /* Typography */ 177 | code, 178 | samp { 179 | background-color: var(--color-accent); 180 | border-radius: var(--border-radius); 181 | color: var(--color-text); 182 | display: inline-block; 183 | margin: 0 0.1rem; 184 | padding: 0 0.5rem; 185 | } 186 | 187 | details { 188 | margin: 1.3rem 0; 189 | } 190 | 191 | details summary { 192 | font-weight: bold; 193 | cursor: pointer; 194 | } 195 | 196 | h1, 197 | h2, 198 | h3, 199 | h4, 200 | h5, 201 | h6 { 202 | line-height: var(--line-height); 203 | } 204 | 205 | mark { 206 | padding: 0.1rem; 207 | } 208 | 209 | ol li, 210 | ul li { 211 | padding: 0.2rem 0; 212 | } 213 | 214 | p { 215 | margin: 0.75rem 0; 216 | padding: 0; 217 | } 218 | 219 | pre { 220 | margin: 1rem 0; 221 | max-width: var(--width-card-wide); 222 | padding: 1rem 0; 223 | } 224 | 225 | pre code, 226 | pre samp { 227 | display: block; 228 | max-width: var(--width-card-wide); 229 | padding: 0.5rem 2rem; 230 | white-space: pre-wrap; 231 | } 232 | 233 | small { 234 | color: var(--color-text-secondary); 235 | } 236 | 237 | sup { 238 | background-color: var(--color-secondary); 239 | border-radius: var(--border-radius); 240 | color: var(--color-bg); 241 | font-size: xx-small; 242 | font-weight: bold; 243 | margin: 0.2rem; 244 | padding: 0.2rem 0.3rem; 245 | position: relative; 246 | top: -2px; 247 | } 248 | 249 | /* Links */ 250 | a { 251 | color: var(--color-secondary); 252 | display: inline-block; 253 | font-weight: bold; 254 | text-decoration: none; 255 | } 256 | 257 | a:hover { 258 | filter: brightness(var(--hover-brightness)); 259 | text-decoration: underline; 260 | } 261 | 262 | a b, 263 | a em, 264 | a i, 265 | a strong, 266 | button { 267 | border-radius: var(--border-radius); 268 | display: inline-block; 269 | font-size: medium; 270 | font-weight: bold; 271 | line-height: var(--line-height); 272 | margin: 0.5rem 0; 273 | padding: 1rem 2rem; 274 | } 275 | 276 | button { 277 | font-family: var(--font-family); 278 | } 279 | 280 | button:hover { 281 | cursor: pointer; 282 | filter: brightness(var(--hover-brightness)); 283 | } 284 | 285 | a b, 286 | a strong, 287 | button { 288 | background-color: var(--color); 289 | border: 2px solid var(--color); 290 | color: var(--color-bg); 291 | } 292 | 293 | a em, 294 | a i { 295 | border: 2px solid var(--color); 296 | border-radius: var(--border-radius); 297 | color: var(--color); 298 | display: inline-block; 299 | padding: 1rem 2rem; 300 | } 301 | 302 | /* Images */ 303 | figure { 304 | margin: 0; 305 | padding: 0; 306 | } 307 | 308 | figure img { 309 | max-width: 100%; 310 | } 311 | 312 | figure figcaption { 313 | color: var(--color-text-secondary); 314 | } 315 | 316 | /* Forms */ 317 | 318 | button:disabled, 319 | input:disabled { 320 | background: var(--color-bg-secondary); 321 | border-color: var(--color-bg-secondary); 322 | color: var(--color-text-secondary); 323 | cursor: not-allowed; 324 | } 325 | 326 | button[disabled]:hover { 327 | filter: none; 328 | } 329 | 330 | form { 331 | border: 1px solid var(--color-bg-secondary); 332 | border-radius: var(--border-radius); 333 | box-shadow: var(--box-shadow) var(--color-shadow); 334 | display: block; 335 | max-width: var(--width-card-wide); 336 | min-width: var(--width-card); 337 | padding: 1.5rem; 338 | text-align: var(--justify-normal); 339 | } 340 | 341 | form header { 342 | margin: 1.5rem 0; 343 | padding: 1.5rem 0; 344 | } 345 | 346 | input, 347 | label, 348 | select, 349 | textarea { 350 | display: block; 351 | font-size: inherit; 352 | max-width: var(--width-card-wide); 353 | } 354 | 355 | input[type="checkbox"], 356 | input[type="radio"] { 357 | display: inline-block; 358 | } 359 | 360 | input[type="checkbox"] + label, 361 | input[type="radio"] + label { 362 | display: inline-block; 363 | font-weight: normal; 364 | position: relative; 365 | top: 1px; 366 | } 367 | 368 | input, 369 | select, 370 | textarea { 371 | border: 1px solid var(--color-bg-secondary); 372 | border-radius: var(--border-radius); 373 | margin-bottom: 1rem; 374 | padding: 0.4rem 0.8rem; 375 | } 376 | 377 | input[readonly], 378 | textarea[readonly] { 379 | background-color: var(--color-bg-secondary); 380 | } 381 | 382 | label { 383 | font-weight: bold; 384 | margin-bottom: 0.2rem; 385 | } 386 | 387 | /* Tables */ 388 | table { 389 | border: 1px solid var(--color-bg-secondary); 390 | border-radius: var(--border-radius); 391 | border-spacing: 0; 392 | display: inline-block; 393 | max-width: 100%; 394 | overflow-x: auto; 395 | padding: 0; 396 | white-space: nowrap; 397 | } 398 | 399 | table td, 400 | table th, 401 | table tr { 402 | padding: 0.4rem 0.8rem; 403 | text-align: var(--justify-important); 404 | } 405 | 406 | table thead { 407 | background-color: var(--color); 408 | border-collapse: collapse; 409 | border-radius: var(--border-radius); 410 | color: var(--color-bg); 411 | margin: 0; 412 | padding: 0; 413 | } 414 | 415 | table thead th:first-child { 416 | border-top-left-radius: var(--border-radius); 417 | } 418 | 419 | table thead th:last-child { 420 | border-top-right-radius: var(--border-radius); 421 | } 422 | 423 | table thead th:first-child, 424 | table tr td:first-child { 425 | text-align: var(--justify-normal); 426 | } 427 | 428 | table tr:nth-child(even) { 429 | background-color: var(--color-accent); 430 | } 431 | 432 | /* Quotes */ 433 | blockquote { 434 | display: block; 435 | font-size: x-large; 436 | line-height: var(--line-height); 437 | margin: 1rem auto; 438 | max-width: var(--width-card-medium); 439 | padding: 1.5rem 1rem; 440 | text-align: var(--justify-important); 441 | } 442 | 443 | blockquote footer { 444 | color: var(--color-text-secondary); 445 | display: block; 446 | font-size: small; 447 | line-height: var(--line-height); 448 | padding: 1.5rem 0; 449 | } 450 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/src/css/reverend.css: -------------------------------------------------------------------------------- 1 | body { 2 | display: block; 3 | position: static; 4 | visibility: visible; 5 | vertical-align: baseline; 6 | margin: 0 auto; 7 | border: 0; 8 | padding: 0; 9 | height: auto; 10 | width: 1000px; 11 | 12 | background-color: rgb(225, 242, 251); 13 | color: rgb(47, 0, 152); /* text color */ 14 | font-family: arial, verdana, helvetica, sans-serif; 15 | font-weight: 400; 16 | font-size: 16px; 17 | line-height: 1.2; 18 | text-decoration: normal; 19 | font-weight: normal; 20 | 21 | text-align: justify; 22 | text-justify: auto; 23 | text-align-last: left; 24 | text-indent: 0; 25 | overflow: auto; 26 | white-space: normal; 27 | word-wrap: break-word; 28 | } 29 | 30 | div { 31 | display: block; 32 | } 33 | 34 | p, 35 | h1, 36 | ol, 37 | ul, 38 | li { 39 | margin: 0; 40 | margin-top: 0.5em; 41 | margin-bottom: 1em; 42 | } 43 | 44 | ul, 45 | ol { 46 | padding: 0; 47 | padding-left: 1em; /* this is the indent created for the list, but may partially be filled with the numbers or bullets (see note below). */ 48 | padding-right: 1em; 49 | } 50 | 51 | /* The number or bullet appears to be displaced some minimum amount to the left of the li padding, and expands further to the left with more digits in a number. It appears to me that, depending on that minimum displacement amount plus the width of the number/bullet, it falls into the li margin-left and/or the ol/ul padding-left. */ 52 | 53 | li { 54 | margin-left: 2em; /* number or bullet may partially go in this margin. See not above.*/ 55 | padding-left: 0px; /* this minimumizes the displacement of the number/bullet to the left of the li */ 56 | } 57 | 58 | h1 { 59 | margin-top: 1em; 60 | font-weight: 800; 61 | font-size: 25px; 62 | text-align: left; 63 | } 64 | 65 | input, 66 | textarea { 67 | display: block; 68 | } 69 | 70 | input#captchaInput { 71 | width: 50px; 72 | display: inline; 73 | } 74 | 75 | input[type="text"], 76 | input[type="email"], 77 | input[type="tel"], 78 | textarea { 79 | box-sizing: border-box; /* without this, the width: 100% will be too big (major hassel) */ 80 | width: 100%; 81 | color: magenta; 82 | font-weight: 500; 83 | font-size: 18px; 84 | font-family: arial, verdana, helvetica, sans-serif; /* avoid user-agent overwrite of body */ 85 | text-align: left; 86 | padding: 5px; 87 | border-width: 3px; 88 | border-color: #8896d1; 89 | border-style: solid; 90 | } 91 | 92 | input[type="text"], 93 | input[type="email"], 94 | input[type="tel"] { 95 | max-width: 300px; 96 | } 97 | 98 | #goddess-email-confirm { 99 | margin-top: 3px; 100 | } 101 | 102 | textarea { 103 | max-width: 900px; 104 | } 105 | 106 | select { 107 | color: magenta; 108 | font-weight: 500; 109 | font-size: 18px; 110 | font-family: arial, verdana, helvetica, sans-serif; /* avoid user-agent overwrite of body */ 111 | text-align: left; 112 | padding: 5px; 113 | border-width: 3px; 114 | border-color: #8896d1; 115 | border-style: solid; 116 | background-color: lightgreen; 117 | } 118 | 119 | input[type="file"], 120 | button { 121 | color: magenta; 122 | font-weight: 500; 123 | font-size: 18px; 124 | font-family: arial, verdana, helvetica, sans-serif; /* avoid user-agent overwrite of body */ 125 | text-align: left; 126 | padding: 5px; 127 | border-width: 3px; 128 | border-color: #8896d1; 129 | border-style: solid; 130 | background-color: lightgreen; 131 | width: 400px; 132 | height: auto; 133 | } 134 | 135 | option { 136 | color: magenta; 137 | } 138 | 139 | /* Careful with these three ::placeholder selectors... it appears they cannot be combined into one statement. */ 140 | 141 | ::-webkit-input-placeholder { 142 | /* Edge, Chrome */ 143 | color: pink; 144 | opacity: 1; 145 | } 146 | 147 | :-ms-input-placeholder { 148 | /* Internet Explorer */ 149 | color: pink; 150 | opacity: 1; 151 | } 152 | 153 | ::placeholder { 154 | color: pink; 155 | opacity: 1; 156 | } 157 | 158 | a { 159 | color: magenta; 160 | font-weight: 600; 161 | } 162 | 163 | hr { 164 | border-top: 1px solid rgb(47, 0, 152); 165 | width: 50%; 166 | } 167 | 168 | @media screen and (max-width: 1024px) { 169 | /* wide screens */ 170 | body { 171 | /* background-color: lightgreen; /* for test purposes */ 172 | padding: 0 5%; 173 | width: 90%; 174 | } 175 | } 176 | 177 | @media screen and (max-width: 768px) { 178 | body { 179 | /* background-color: lightgreen; /* for test purposes */ 180 | padding: 0 5%; 181 | width: 90%; 182 | } 183 | /* phones */ 184 | ul, 185 | ol { 186 | padding-left: 15px; 187 | padding-right: 15px; 188 | } 189 | li { 190 | margin-left: 15px; 191 | } 192 | input[type="file"], 193 | button { 194 | width: 90%; 195 | } 196 | } 197 | 198 | nav { 199 | display: block; 200 | width: 100%; 201 | } 202 | 203 | nav ul { 204 | display: inline-flex; 205 | width: 100%; 206 | } 207 | 208 | nav li { 209 | display: flex; 210 | flex-grow: 1; 211 | justify-content: center; 212 | margin: 0; 213 | text-transform: lowercase; 214 | } 215 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/src/css/sakura.css: -------------------------------------------------------------------------------- 1 | /* Sakura.css v1.3.0 2 | * ================ 3 | * Minimal css theme. 4 | * Project: https://github.com/oxalorg/sakura/ 5 | */ 6 | /* Body */ 7 | html { 8 | font-size: 62.5%; 9 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 10 | "Helvetica Neue", Arial, "Noto Sans", sans-serif; 11 | } 12 | 13 | body { 14 | font-size: 1.8rem; 15 | line-height: 1.618; 16 | max-width: 38em; 17 | margin: auto; 18 | color: #4a4a4a; 19 | background-color: #f9f9f9; 20 | padding: 13px; 21 | } 22 | 23 | @media (max-width: 684px) { 24 | body { 25 | font-size: 1.53rem; 26 | } 27 | } 28 | 29 | @media (max-width: 382px) { 30 | body { 31 | font-size: 1.35rem; 32 | } 33 | } 34 | 35 | h1, 36 | h2, 37 | h3, 38 | h4, 39 | h5, 40 | h6 { 41 | line-height: 1.1; 42 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, 43 | "Helvetica Neue", Arial, "Noto Sans", sans-serif; 44 | font-weight: 700; 45 | margin-top: 3rem; 46 | margin-bottom: 1.5rem; 47 | overflow-wrap: break-word; 48 | word-wrap: break-word; 49 | -ms-word-break: break-all; 50 | word-break: break-word; 51 | -ms-hyphens: auto; 52 | -moz-hyphens: auto; 53 | -webkit-hyphens: auto; 54 | hyphens: auto; 55 | } 56 | 57 | h1 { 58 | font-size: 2.35em; 59 | } 60 | 61 | h2 { 62 | font-size: 2em; 63 | } 64 | 65 | h3 { 66 | font-size: 1.75em; 67 | } 68 | 69 | h4 { 70 | font-size: 1.5em; 71 | } 72 | 73 | h5 { 74 | font-size: 1.25em; 75 | } 76 | 77 | h6 { 78 | font-size: 1em; 79 | } 80 | 81 | p { 82 | margin-top: 0px; 83 | margin-bottom: 2.5rem; 84 | } 85 | 86 | small, 87 | sub, 88 | sup { 89 | font-size: 75%; 90 | } 91 | 92 | hr { 93 | border-color: #2c8898; 94 | } 95 | 96 | a { 97 | text-decoration: none; 98 | color: #2c8898; 99 | } 100 | a:hover { 101 | color: #982c61; 102 | border-bottom: 2px solid #4a4a4a; 103 | } 104 | 105 | ul { 106 | padding-left: 1.4em; 107 | margin-top: 0px; 108 | margin-bottom: 2.5rem; 109 | } 110 | 111 | li { 112 | margin-bottom: 0.4em; 113 | } 114 | 115 | blockquote { 116 | font-style: italic; 117 | margin-left: 1.5em; 118 | padding-left: 1em; 119 | border-left: 3px solid #2c8898; 120 | } 121 | 122 | img { 123 | height: auto; 124 | max-width: 100%; 125 | margin-top: 0px; 126 | margin-bottom: 2.5rem; 127 | } 128 | 129 | /* Pre and Code */ 130 | pre { 131 | background-color: #f1f1f1; 132 | display: block; 133 | padding: 1em; 134 | overflow-x: auto; 135 | margin-top: 0px; 136 | margin-bottom: 2.5rem; 137 | } 138 | 139 | code { 140 | font-size: 0.9em; 141 | padding: 0 0.5em; 142 | background-color: #f1f1f1; 143 | white-space: pre-wrap; 144 | } 145 | 146 | pre > code { 147 | padding: 0; 148 | background-color: transparent; 149 | white-space: pre; 150 | } 151 | 152 | /* Tables */ 153 | table { 154 | text-align: justify; 155 | width: 100%; 156 | border-collapse: collapse; 157 | } 158 | 159 | td, 160 | th { 161 | padding: 0.5em; 162 | border-bottom: 1px solid #f1f1f1; 163 | } 164 | 165 | /* Buttons, forms and input */ 166 | input, 167 | textarea { 168 | border: 1px solid #4a4a4a; 169 | } 170 | input:focus, 171 | textarea:focus { 172 | border: 1px solid #2c8898; 173 | } 174 | 175 | textarea { 176 | width: 100%; 177 | } 178 | 179 | .button, 180 | button, 181 | input[type="submit"], 182 | input[type="reset"], 183 | input[type="button"] { 184 | display: inline-block; 185 | padding: 5px 10px; 186 | text-align: center; 187 | text-decoration: none; 188 | white-space: nowrap; 189 | background-color: #2c8898; 190 | color: #f9f9f9; 191 | border-radius: 1px; 192 | border: 1px solid #2c8898; 193 | cursor: pointer; 194 | box-sizing: border-box; 195 | } 196 | .button[disabled], 197 | button[disabled], 198 | input[type="submit"][disabled], 199 | input[type="reset"][disabled], 200 | input[type="button"][disabled] { 201 | cursor: default; 202 | opacity: 0.5; 203 | } 204 | .button:focus, 205 | .button:hover, 206 | button:focus, 207 | button:hover, 208 | input[type="submit"]:focus, 209 | input[type="submit"]:hover, 210 | input[type="reset"]:focus, 211 | input[type="reset"]:hover, 212 | input[type="button"]:focus, 213 | input[type="button"]:hover { 214 | background-color: #982c61; 215 | border-color: #982c61; 216 | color: #f9f9f9; 217 | outline: 0; 218 | } 219 | 220 | textarea, 221 | select, 222 | input[type] { 223 | color: #4a4a4a; 224 | padding: 6px 10px; 225 | /* The 6px vertically centers text on FF, ignored by Webkit */ 226 | margin-bottom: 10px; 227 | background-color: #f1f1f1; 228 | border: 1px solid #f1f1f1; 229 | border-radius: 4px; 230 | box-shadow: none; 231 | box-sizing: border-box; 232 | } 233 | textarea:focus, 234 | select:focus, 235 | input[type]:focus { 236 | border: 1px solid #2c8898; 237 | outline: 0; 238 | } 239 | 240 | input[type="checkbox"]:focus { 241 | outline: 1px dotted #2c8898; 242 | } 243 | 244 | label, 245 | legend, 246 | fieldset { 247 | display: block; 248 | margin-bottom: 0.5rem; 249 | font-weight: 600; 250 | } 251 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/src/css/vanilla.css: -------------------------------------------------------------------------------- 1 | /* Reset */ 2 | html, 3 | body, 4 | div, 5 | span, 6 | applet, 7 | object, 8 | iframe, 9 | h1, 10 | h2, 11 | h3, 12 | h4, 13 | h5, 14 | h6, 15 | p, 16 | blockquote, 17 | pre, 18 | a, 19 | abbr, 20 | acronym, 21 | address, 22 | big, 23 | cite, 24 | code, 25 | del, 26 | dfn, 27 | em, 28 | img, 29 | ins, 30 | kbd, 31 | q, 32 | s, 33 | samp, 34 | small, 35 | strike, 36 | strong, 37 | sub, 38 | sup, 39 | tt, 40 | var, 41 | b, 42 | u, 43 | i, 44 | center, 45 | dl, 46 | dt, 47 | dd, 48 | ol, 49 | ul, 50 | li, 51 | fieldset, 52 | form, 53 | label, 54 | legend, 55 | table, 56 | caption, 57 | tbody, 58 | tfoot, 59 | thead, 60 | tr, 61 | th, 62 | td, 63 | article, 64 | aside, 65 | canvas, 66 | details, 67 | embed, 68 | figure, 69 | figcaption, 70 | footer, 71 | header, 72 | hgroup, 73 | menu, 74 | nav, 75 | output, 76 | ruby, 77 | section, 78 | summary, 79 | time, 80 | mark, 81 | audio, 82 | video { 83 | margin: 0; 84 | padding: 0; 85 | border: 0; 86 | font-size: 100%; 87 | font: inherit; 88 | vertical-align: baseline; 89 | } 90 | * { 91 | box-sizing: border-box; 92 | } 93 | 94 | /* Variables */ 95 | :root { 96 | --desktop-font-size: 1.3rem/1.5; 97 | --mobile-font-size: 1.1rem/1.4; 98 | --text-color: #2d2d2d; 99 | --link-color: blue; 100 | --primary-color: lightsteelblue; 101 | --secondary-color: aliceblue; 102 | --tertiary-color: whitesmoke; 103 | } 104 | 105 | /* Typography */ 106 | body { 107 | color: var(--text-color); 108 | font: var(--desktop-font-size) -apple-system, BlinkMacSystemFont, "Segoe UI", 109 | Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", 110 | "Segoe UI Symbol"; 111 | } 112 | 113 | h1, 114 | h2, 115 | h3, 116 | h4, 117 | h5, 118 | h6, 119 | p, 120 | blockquote, 121 | dl, 122 | img, 123 | figure { 124 | margin: 2rem 0; 125 | } 126 | 127 | h1, 128 | h2, 129 | h3, 130 | h4, 131 | h5, 132 | h6 { 133 | font-weight: bold; 134 | } 135 | h1 { 136 | font-size: 200%; 137 | } 138 | h2 { 139 | font-size: 150%; 140 | } 141 | h3 { 142 | font-size: 120%; 143 | } 144 | h4, 145 | h5, 146 | h6 { 147 | font-size: 100%; 148 | } 149 | h5, 150 | h6 { 151 | text-transform: uppercase; 152 | } 153 | 154 | header h1 { 155 | border-bottom: 1px solid; 156 | } 157 | 158 | p { 159 | margin: 2rem 0; 160 | } 161 | 162 | a, 163 | a:visited { 164 | color: var(--link-color); 165 | } 166 | 167 | strong, 168 | time, 169 | b { 170 | font-weight: bold; 171 | } 172 | em, 173 | dfn, 174 | i { 175 | font-style: italic; 176 | } 177 | sub { 178 | font-size: 60%; 179 | vertical-align: bottom; 180 | } 181 | small { 182 | font-size: 80%; 183 | } 184 | 185 | blockquote, 186 | q { 187 | background: var(--secondary-color); 188 | border-left: 10px solid var(--primary-color); 189 | font-family: "Georgia", serif; 190 | padding: 1rem; 191 | } 192 | blockquote p:first-child { 193 | margin-top: 0; 194 | } 195 | cite { 196 | font-family: "Georgia", serif; 197 | font-style: italic; 198 | font-weight: bold; 199 | } 200 | 201 | kbd, 202 | code, 203 | samp, 204 | pre, 205 | var { 206 | font-family: monospace; 207 | font-weight: bold; 208 | } 209 | code, 210 | pre { 211 | background: var(--tertiary-color); 212 | padding: 0.5rem 1rem; 213 | } 214 | code pre, 215 | pre code { 216 | padding: 0; 217 | } 218 | 219 | /* Elements */ 220 | hr { 221 | background: var(--text-color); 222 | border: 0; 223 | height: 1px; 224 | margin: 4rem 0; 225 | } 226 | 227 | img { 228 | max-width: 100%; 229 | } 230 | 231 | figure { 232 | border: 1px solid var(--primary-color); 233 | display: inline-block; 234 | padding: 1rem; 235 | width: auto; 236 | } 237 | figure img { 238 | margin: 0; 239 | } 240 | figure figcaption { 241 | font-size: 80%; 242 | } 243 | 244 | ul, 245 | ol { 246 | margin: 2rem 0; 247 | padding: 0 0 0 4rem; 248 | } 249 | 250 | dl dd { 251 | padding-left: 2rem; 252 | } 253 | 254 | table { 255 | border: 1px solid var(--primary-color); 256 | border-collapse: collapse; 257 | table-layout: fixed; 258 | width: 100%; 259 | } 260 | table caption { 261 | margin: 2rem 0; 262 | } 263 | table thead { 264 | text-align: center; 265 | } 266 | table tbody { 267 | text-align: right; 268 | } 269 | table tr { 270 | border-bottom: 1px solid var(--primary-color); 271 | } 272 | table tbody tr:nth-child(even) { 273 | background: var(--tertiary-color); 274 | } 275 | table th { 276 | background: var(--secondary-color); 277 | font-weight: bold; 278 | } 279 | table th, 280 | table td { 281 | padding: 1rem; 282 | } 283 | table th:not(last-of-type), 284 | table td:not(last-of-type) { 285 | border-right: 1px solid var(--primary-color); 286 | } 287 | 288 | input { 289 | border: 1px solid var(--text-color); 290 | padding: 0.8rem; 291 | } 292 | input:focus, 293 | input:active { 294 | background-color: var(--secondary-color); 295 | border-color: var(--link-color); 296 | } 297 | 298 | /* Mobile Styling */ 299 | @media screen and (max-width: 50rem) { 300 | body { 301 | font: var(--mobile-font-size) -apple-system, BlinkMacSystemFont, "Segoe UI", 302 | Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", 303 | "Segoe UI Emoji", "Segoe UI Symbol"; 304 | } 305 | table { 306 | table-layout: auto; 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/src/css/water.dark.min.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | body { 3 | font-family: system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, 4 | Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 5 | line-height: 1.4; 6 | max-width: 800px; 7 | margin: 20px auto; 8 | padding: 0 10px; 9 | color: #dbdbdb; 10 | background: #202b38; 11 | text-rendering: optimizeLegibility; 12 | } 13 | button, 14 | input, 15 | textarea { 16 | transition: background-color 0.1s linear, border-color 0.1s linear, 17 | color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; 18 | } 19 | h1 { 20 | font-size: 2.2em; 21 | margin-top: 0; 22 | } 23 | h1, 24 | h2, 25 | h3, 26 | h4, 27 | h5, 28 | h6 { 29 | margin-bottom: 12px; 30 | } 31 | h1, 32 | h2, 33 | h3, 34 | h4, 35 | h5, 36 | h6, 37 | strong { 38 | color: #fff; 39 | } 40 | b, 41 | h1, 42 | h2, 43 | h3, 44 | h4, 45 | h5, 46 | h6, 47 | strong, 48 | th { 49 | font-weight: 600; 50 | } 51 | blockquote { 52 | border-left: 4px solid rgba(0, 150, 191, 0.67); 53 | margin: 1.5em 0; 54 | padding: 0.5em 1em; 55 | font-style: italic; 56 | } 57 | blockquote > footer { 58 | margin-top: 10px; 59 | font-style: normal; 60 | } 61 | address, 62 | blockquote cite { 63 | font-style: normal; 64 | } 65 | a[href^="mailto"]:before { 66 | content: "📧 "; 67 | } 68 | a[href^="tel"]:before { 69 | content: "📞 "; 70 | } 71 | a[href^="sms"]:before { 72 | content: "💬 "; 73 | } 74 | button, 75 | input[type="button"], 76 | input[type="checkbox"], 77 | input[type="submit"] { 78 | cursor: pointer; 79 | } 80 | input:not([type="checkbox"]):not([type="radio"]), 81 | select { 82 | display: block; 83 | } 84 | button, 85 | input, 86 | select, 87 | textarea { 88 | color: #fff; 89 | background-color: #161f27; 90 | font-family: inherit; 91 | font-size: inherit; 92 | margin-right: 6px; 93 | margin-bottom: 6px; 94 | padding: 10px; 95 | border: none; 96 | border-radius: 6px; 97 | outline: none; 98 | } 99 | button, 100 | input:not([type="checkbox"]):not([type="radio"]), 101 | select, 102 | textarea { 103 | -webkit-appearance: none; 104 | } 105 | textarea { 106 | margin-right: 0; 107 | width: 100%; 108 | box-sizing: border-box; 109 | resize: vertical; 110 | } 111 | button, 112 | input[type="button"], 113 | input[type="submit"] { 114 | padding-right: 30px; 115 | padding-left: 30px; 116 | } 117 | button:hover, 118 | input[type="button"]:hover, 119 | input[type="submit"]:hover { 120 | background: #324759; 121 | } 122 | button:focus, 123 | input:focus, 124 | select:focus, 125 | textarea:focus { 126 | box-shadow: 0 0 0 2px rgba(0, 150, 191, 0.67); 127 | } 128 | button:active, 129 | input[type="button"]:active, 130 | input[type="checkbox"]:active, 131 | input[type="radio"]:active, 132 | input[type="submit"]:active { 133 | transform: translateY(2px); 134 | } 135 | button:disabled, 136 | input:disabled, 137 | select:disabled, 138 | textarea:disabled { 139 | cursor: not-allowed; 140 | opacity: 0.5; 141 | } 142 | ::-webkit-input-placeholder { 143 | color: #a9a9a9; 144 | } 145 | :-ms-input-placeholder { 146 | color: #a9a9a9; 147 | } 148 | ::-ms-input-placeholder { 149 | color: #a9a9a9; 150 | } 151 | ::placeholder { 152 | color: #a9a9a9; 153 | } 154 | a { 155 | text-decoration: none; 156 | color: #41adff; 157 | } 158 | a:hover { 159 | text-decoration: underline; 160 | } 161 | code, 162 | kbd { 163 | background: #161f27; 164 | color: #ffbe85; 165 | padding: 5px; 166 | border-radius: 6px; 167 | } 168 | pre > code { 169 | padding: 10px; 170 | display: block; 171 | overflow-x: auto; 172 | } 173 | img { 174 | max-width: 100%; 175 | } 176 | hr { 177 | border: none; 178 | border-top: 1px solid #dbdbdb; 179 | } 180 | table { 181 | border-collapse: collapse; 182 | margin-bottom: 10px; 183 | width: 100%; 184 | } 185 | td, 186 | th { 187 | padding: 6px; 188 | text-align: left; 189 | } 190 | th { 191 | border-bottom: 1px solid #dbdbdb; 192 | } 193 | tbody tr:nth-child(2n) { 194 | background-color: #161f27; 195 | } 196 | ::-webkit-scrollbar { 197 | height: 10px; 198 | width: 10px; 199 | } 200 | ::-webkit-scrollbar-track { 201 | background: #161f27; 202 | border-radius: 6px; 203 | } 204 | ::-webkit-scrollbar-thumb { 205 | background: #324759; 206 | border-radius: 6px; 207 | } 208 | ::-webkit-scrollbar-thumb:hover { 209 | background: #415c73; 210 | } 211 | /*# sourceMappingURL=dark.min.css.map */ 212 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/src/css/water.light.min.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | body { 3 | font-family: system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, 4 | Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 5 | line-height: 1.4; 6 | max-width: 800px; 7 | margin: 20px auto; 8 | padding: 0 10px; 9 | color: #363636; 10 | background: #fff; 11 | text-rendering: optimizeLegibility; 12 | } 13 | button, 14 | input, 15 | textarea { 16 | transition: background-color 0.1s linear, border-color 0.1s linear, 17 | color 0.1s linear, box-shadow 0.1s linear, transform 0.1s ease; 18 | } 19 | h1 { 20 | font-size: 2.2em; 21 | margin-top: 0; 22 | } 23 | h1, 24 | h2, 25 | h3, 26 | h4, 27 | h5, 28 | h6 { 29 | margin-bottom: 12px; 30 | } 31 | h1, 32 | h2, 33 | h3, 34 | h4, 35 | h5, 36 | h6, 37 | strong { 38 | color: #000; 39 | } 40 | b, 41 | h1, 42 | h2, 43 | h3, 44 | h4, 45 | h5, 46 | h6, 47 | strong, 48 | th { 49 | font-weight: 600; 50 | } 51 | blockquote { 52 | border-left: 4px solid rgba(0, 150, 191, 0.67); 53 | margin: 1.5em 0; 54 | padding: 0.5em 1em; 55 | font-style: italic; 56 | } 57 | blockquote > footer { 58 | margin-top: 10px; 59 | font-style: normal; 60 | } 61 | address, 62 | blockquote cite { 63 | font-style: normal; 64 | } 65 | a[href^="mailto"]:before { 66 | content: "📧 "; 67 | } 68 | a[href^="tel"]:before { 69 | content: "📞 "; 70 | } 71 | a[href^="sms"]:before { 72 | content: "💬 "; 73 | } 74 | button, 75 | input[type="button"], 76 | input[type="checkbox"], 77 | input[type="submit"] { 78 | cursor: pointer; 79 | } 80 | input:not([type="checkbox"]):not([type="radio"]), 81 | select { 82 | display: block; 83 | } 84 | button, 85 | input, 86 | select, 87 | textarea { 88 | color: #000; 89 | background-color: #efefef; 90 | font-family: inherit; 91 | font-size: inherit; 92 | margin-right: 6px; 93 | margin-bottom: 6px; 94 | padding: 10px; 95 | border: none; 96 | border-radius: 6px; 97 | outline: none; 98 | } 99 | button, 100 | input:not([type="checkbox"]):not([type="radio"]), 101 | select, 102 | textarea { 103 | -webkit-appearance: none; 104 | } 105 | textarea { 106 | margin-right: 0; 107 | width: 100%; 108 | box-sizing: border-box; 109 | resize: vertical; 110 | } 111 | button, 112 | input[type="button"], 113 | input[type="submit"] { 114 | padding-right: 30px; 115 | padding-left: 30px; 116 | } 117 | button:hover, 118 | input[type="button"]:hover, 119 | input[type="submit"]:hover { 120 | background: #ddd; 121 | } 122 | button:focus, 123 | input:focus, 124 | select:focus, 125 | textarea:focus { 126 | box-shadow: 0 0 0 2px rgba(0, 150, 191, 0.67); 127 | } 128 | button:active, 129 | input[type="button"]:active, 130 | input[type="checkbox"]:active, 131 | input[type="radio"]:active, 132 | input[type="submit"]:active { 133 | transform: translateY(2px); 134 | } 135 | button:disabled, 136 | input:disabled, 137 | select:disabled, 138 | textarea:disabled { 139 | cursor: not-allowed; 140 | opacity: 0.5; 141 | } 142 | ::-webkit-input-placeholder { 143 | color: #949494; 144 | } 145 | :-ms-input-placeholder { 146 | color: #949494; 147 | } 148 | ::-ms-input-placeholder { 149 | color: #949494; 150 | } 151 | ::placeholder { 152 | color: #949494; 153 | } 154 | a { 155 | text-decoration: none; 156 | color: #0076d1; 157 | } 158 | a:hover { 159 | text-decoration: underline; 160 | } 161 | code, 162 | kbd { 163 | background: #efefef; 164 | color: #000; 165 | padding: 5px; 166 | border-radius: 6px; 167 | } 168 | pre > code { 169 | padding: 10px; 170 | display: block; 171 | overflow-x: auto; 172 | } 173 | img { 174 | max-width: 100%; 175 | } 176 | hr { 177 | border: none; 178 | border-top: 1px solid #dbdbdb; 179 | } 180 | table { 181 | border-collapse: collapse; 182 | margin-bottom: 10px; 183 | width: 100%; 184 | } 185 | td, 186 | th { 187 | padding: 6px; 188 | text-align: left; 189 | } 190 | th { 191 | border-bottom: 1px solid #dbdbdb; 192 | } 193 | tbody tr:nth-child(2n) { 194 | background-color: #efefef; 195 | } 196 | ::-webkit-scrollbar { 197 | height: 10px; 198 | width: 10px; 199 | } 200 | ::-webkit-scrollbar-track { 201 | background: #efefef; 202 | border-radius: 6px; 203 | } 204 | ::-webkit-scrollbar-thumb { 205 | background: #d5d5d5; 206 | border-radius: 6px; 207 | } 208 | ::-webkit-scrollbar-thumb:hover { 209 | background: #c4c4c4; 210 | } 211 | /*# sourceMappingURL=light.min.css.map */ 212 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/src/templates/footer/index.html: -------------------------------------------------------------------------------- 1 |
2 | Copyright © 2020, all rights reserved. 3 |
4 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/src/templates/footer/nav.html: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/src/templates/head/index.html: -------------------------------------------------------------------------------- 1 | 2 | Seeking Awesome HTML Goddess 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/@htmlgoddess/templates/basic/src/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |