├── src
├── fonts
│ └── .gitkeep
├── images
│ └── .gitkeep
├── styles
│ ├── components
│ │ ├── forms.css
│ │ ├── header.css
│ │ ├── images.css
│ │ └── buttons.css
│ ├── base
│ │ ├── theme.css
│ │ ├── helpers.css
│ │ ├── layout.css
│ │ ├── print.css
│ │ ├── root.css
│ │ ├── typography.css
│ │ └── foundation.css
│ └── style.css
├── js
│ ├── main.js
│ ├── vendor
│ │ └── vendor.js
│ └── modules
│ │ ├── breakpoint.js
│ │ └── debounce.js
├── static
│ ├── social.jpg
│ ├── favicon.ico
│ └── favicon.png
├── data
│ └── data.json
└── html
│ ├── index.html
│ └── layout
│ └── main.njk
├── gulpfile.js
├── lib
│ ├── webpackConfig.js
│ ├── env.js
│ └── handleErrors.js
├── tasks
│ ├── clean.js
│ ├── scripts.js
│ ├── report.js
│ ├── assets.js
│ ├── server.js
│ ├── styles.js
│ ├── images.js
│ └── html.js
├── index.js
└── config.js
├── .eslintignore
├── .babelrc
├── logo.jpg
├── .prettierignore
├── .browserslistrc
├── .prettierrc.js
├── postcss.config.js
├── .editorconfig
├── .gitignore
├── .eslintrc
├── .gitattributes
├── LICENSE
├── package.json
└── README.md
/src/fonts/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/images/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gulpfile.js/lib/webpackConfig.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/styles/components/forms.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | *.min.js
2 | public/
3 | vendor/
--------------------------------------------------------------------------------
/src/js/main.js:
--------------------------------------------------------------------------------
1 | console.log('main.js has loaded');
2 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env"]
3 | }
4 |
--------------------------------------------------------------------------------
/logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davshoward/tux/HEAD/logo.jpg
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | yarn.lock
3 | package-lock.json
4 | public
--------------------------------------------------------------------------------
/src/static/social.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davshoward/tux/HEAD/src/static/social.jpg
--------------------------------------------------------------------------------
/src/js/vendor/vendor.js:
--------------------------------------------------------------------------------
1 | import 'focus-visible';
2 |
3 | console.log('vendor.js has loaded');
4 |
--------------------------------------------------------------------------------
/src/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davshoward/tux/HEAD/src/static/favicon.ico
--------------------------------------------------------------------------------
/src/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davshoward/tux/HEAD/src/static/favicon.png
--------------------------------------------------------------------------------
/gulpfile.js/lib/env.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | prod: process.env.NODE_ENV === 'production',
3 | };
4 |
--------------------------------------------------------------------------------
/src/styles/base/theme.css:
--------------------------------------------------------------------------------
1 | [class*='background-'] {
2 | background: var(--background-color);
3 | }
4 |
--------------------------------------------------------------------------------
/.browserslistrc:
--------------------------------------------------------------------------------
1 | # Browsers we support
2 |
3 | > 1%
4 | last 2 versions
5 | ie 11
6 | ios >= 7
7 | android >= 5
8 | safari >= 9
--------------------------------------------------------------------------------
/src/styles/components/header.css:
--------------------------------------------------------------------------------
1 | .header {
2 | width: 100%;
3 | display: block;
4 | position: relative;
5 | margin: 0;
6 | padding: 0;
7 | }
8 |
--------------------------------------------------------------------------------
/gulpfile.js/tasks/clean.js:
--------------------------------------------------------------------------------
1 | const del = require('del');
2 | const config = require('../config').dest;
3 |
4 | const clean = () => del(config);
5 |
6 | module.exports = clean;
7 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: true,
3 | tabWidth: 4,
4 | singleQuote: true,
5 | trailingComma: 'es5',
6 | jsxBracketSameLine: true,
7 | endOfLine: 'auto',
8 | };
9 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('postcss-import'),
4 | require('postcss-preset-env')({
5 | stage: 1,
6 | }),
7 | require('postcss-nested'),
8 | require('postcss-color-mod-function'),
9 | ],
10 | };
11 |
--------------------------------------------------------------------------------
/src/data/data.json:
--------------------------------------------------------------------------------
1 | {
2 | "setting": {
3 | "url": "",
4 | "imageurl": "/social.png",
5 | "googleplus": "[googleplus username goes here]",
6 | "facebookapp": "[facebookapp id goes here]",
7 | "twitterid": "[twitter id goes here]"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_size = 1
8 | indent_style = tab
9 | max_line_length = 80
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [*.md]
14 | max_line_length = 0
15 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/gulpfile.js/lib/handleErrors.js:
--------------------------------------------------------------------------------
1 | var notify = require('gulp-notify');
2 |
3 | module.exports = function (errorObject, callback) {
4 | notify
5 | .onError(errorObject.toString().split(': ').join(':\n'))
6 | .apply(this, arguments);
7 |
8 | // Keep gulp from hanging on this task
9 | if (typeof this.emit === 'function') this.emit('end');
10 | };
11 |
--------------------------------------------------------------------------------
/gulpfile.js/tasks/scripts.js:
--------------------------------------------------------------------------------
1 | const { src, dest } = require('gulp');
2 | const config = require('../config').scripts;
3 | const webpack = require('webpack');
4 | const webpackStream = require('webpack-stream');
5 |
6 | const scripts = () =>
7 | src(config.src)
8 | .pipe(webpackStream(config.webpack, webpack))
9 | .pipe(dest(config.dest));
10 |
11 | module.exports = scripts;
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## General
2 | *.bak
3 | *.diff
4 | *.err
5 | *.log
6 | *.orig
7 | *.rej
8 | *.swo
9 | *.swp
10 | *.vi
11 | *.zip
12 | *~
13 | .htaccess
14 |
15 | # OS or Editor folders
16 | ._*
17 | .cache
18 | .DS_Store
19 | .idea
20 | .project
21 | .settings
22 | .tmproj
23 | *.esproj
24 | nbproject
25 | Thumbs.db
26 |
27 | ## Development
28 | node_modules
29 | build
30 | dist
31 | site
32 | public
33 |
34 | package-lock.json
--------------------------------------------------------------------------------
/src/styles/style.css:
--------------------------------------------------------------------------------
1 | @import 'base/root.css';
2 | @import 'base/foundation.css';
3 | @import 'base/helpers.css';
4 | @import 'base/typography.css';
5 | @import 'base/layout.css';
6 | @import 'base/theme.css';
7 |
8 | @import 'components/buttons.css';
9 | @import 'components/header.css';
10 | @import 'components/images.css';
11 |
12 | @import 'base/print.css';
13 |
14 | html {
15 | visibility: visible;
16 | opacity: 1;
17 | }
18 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parserOptions": {
3 | "ecmaVersion": 2018,
4 | "sourceType": "module"
5 | },
6 | "env": {
7 | "browser": true,
8 | "commonjs": true,
9 | "es6": true
10 | },
11 | "parser": "babel-eslint",
12 | "rules": {
13 | "indent": ["warn", "tab"],
14 | "semi": ["error", "always"],
15 | "no-unused-vars": [
16 | "warn",
17 | { "vars": "all", "args": "none", "ignoreRestSiblings": false }
18 | ]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/js/modules/breakpoint.js:
--------------------------------------------------------------------------------
1 | export const breakpoint = () => {
2 | // Setup the breakpoint variable
3 | let breakpoint;
4 |
5 | // Get the current breakpoint
6 | const getBreakpoint = function () {
7 | return window
8 | .getComputedStyle(document.body, ':before')
9 | .content.replace(/\"/g, '');
10 | };
11 |
12 | // Calculate breakpoint on page load
13 | breakpoint = getBreakpoint();
14 |
15 | return breakpoint;
16 | };
17 |
--------------------------------------------------------------------------------
/gulpfile.js/tasks/report.js:
--------------------------------------------------------------------------------
1 | const { src } = require('gulp');
2 | const config = require('../config');
3 | const sizereport = require('gulp-sizereport');
4 |
5 | const report = () => {
6 | let srcs = config.report.src;
7 |
8 | config.assets &&
9 | config.assets.length &&
10 | config.assets.forEach((a) => srcs.push[a.dest]);
11 |
12 | srcs = srcs.map((x) => `${x}/**/*`);
13 |
14 | return src(srcs).pipe(sizereport({ gzip: true }));
15 | };
16 |
17 | module.exports = report;
18 |
--------------------------------------------------------------------------------
/src/styles/components/images.css:
--------------------------------------------------------------------------------
1 | .image-background,
2 | .image-contain {
3 | position: absolute;
4 | top: 0;
5 | left: 0;
6 | width: 100%;
7 | height: 100%;
8 |
9 | &.compat-object-fit {
10 | background-position: center center;
11 | background-size: cover;
12 |
13 | img {
14 | display: none;
15 | }
16 | }
17 |
18 | img {
19 | margin: 0;
20 | width: 100%;
21 | height: 100%;
22 | object-fit: cover;
23 | }
24 | }
25 |
26 | .image-contain {
27 | &.compat-object-fit {
28 | background-size: contain;
29 | }
30 |
31 | img {
32 | object-fit: contain;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/gulpfile.js/tasks/assets.js:
--------------------------------------------------------------------------------
1 | const { src, dest } = require('gulp');
2 | const changed = require('gulp-changed');
3 | const config = require('../config').assets;
4 | const handleErrors = require('../lib/handleErrors');
5 | const browserSync = require('browser-sync');
6 |
7 | const assets = (callback) => {
8 | if (config.length <= 0) {
9 | return callback();
10 | }
11 |
12 | config.forEach((entry) => {
13 | src(entry.src)
14 | .pipe(changed(entry.dest))
15 | .on('error', handleErrors)
16 | .pipe(dest(entry.dest))
17 | .pipe(browserSync.stream());
18 | });
19 |
20 | return callback();
21 | };
22 |
23 | module.exports = assets;
24 |
--------------------------------------------------------------------------------
/gulpfile.js/tasks/server.js:
--------------------------------------------------------------------------------
1 | const browserSync = require('browser-sync');
2 | const config = require('../config').server;
3 | const webpackConfig = require('../config').scripts.webpack;
4 | const webpack = require('webpack');
5 | const webpackDevMiddleware = require('webpack-dev-middleware');
6 | const webpackHotMiddleware = require('webpack-hot-middleware');
7 | const bundler = webpack(webpackConfig);
8 |
9 | const server = () => {
10 | config.server.middleware = [
11 | webpackDevMiddleware(bundler, {
12 | stats: 'minimal',
13 | }),
14 | webpackHotMiddleware(bundler),
15 | ];
16 |
17 | browserSync.init(config);
18 | };
19 |
20 | module.exports = server;
21 |
--------------------------------------------------------------------------------
/src/html/index.html:
--------------------------------------------------------------------------------
1 |
2 | {% extends 'layout/main.njk' %} {% set page_title = "Tux 🤵" %} {% set
3 | page_description = "A baseline toolkit to ease the building of static HTML sites
4 | or templated CMS builds. Using Webpack 4, Gulp, PostCSS, Nunjunks and
5 | BrowserSync." %} {% set page_slug = "" %} {%set body_classes = "page-homepage"
6 | %} {% block main %}
7 |
17 |
Tux 🤵
18 |
19 | {% endblock %}
20 |
--------------------------------------------------------------------------------
/src/js/modules/debounce.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Debounce functions for better performance
3 | * (c) 2018 Chris Ferdinandi, MIT License, https://gomakethings.com
4 | * @param {Function} fn The function to debounce
5 | */
6 | export const debounce = function (fn) {
7 | // Setup a timer
8 | var timeout;
9 |
10 | // Return a function to run debounced
11 | return function () {
12 | // Setup the arguments
13 | var context = this;
14 | var args = arguments;
15 |
16 | // If there's a timer, cancel it
17 | if (timeout) {
18 | window.cancelAnimationFrame(timeout);
19 | }
20 |
21 | // Setup the new requestAnimationFrame()
22 | timeout = window.requestAnimationFrame(function () {
23 | fn.apply(context, args);
24 | });
25 | };
26 | };
27 |
--------------------------------------------------------------------------------
/src/styles/base/helpers.css:
--------------------------------------------------------------------------------
1 | .visuallyhidden {
2 | border: 0;
3 | clip: rect(0 0 0 0);
4 | height: 1px;
5 | margin: -1px;
6 | overflow: hidden;
7 | padding: 0;
8 | position: absolute;
9 | width: 1px;
10 |
11 | &.is-focusable {
12 | &:active,
13 | &:focus {
14 | clip: auto;
15 | height: auto;
16 | margin: 0;
17 | overflow: visible;
18 | position: static;
19 | width: auto;
20 | }
21 | }
22 | }
23 |
24 | .coverlink {
25 | position: relative;
26 | text-decoration: none;
27 |
28 | &::after {
29 | content: '';
30 | position: absolute;
31 | left: 0;
32 | top: 0;
33 | right: 0;
34 | bottom: 0;
35 | z-index: 1;
36 | cursor: pointer;
37 | }
38 | }
39 |
40 | .full-bleed {
41 | width: 100vw;
42 | margin-left: 50%;
43 | transform: translateX(-50%);
44 | }
45 |
--------------------------------------------------------------------------------
/gulpfile.js/tasks/styles.js:
--------------------------------------------------------------------------------
1 | const { src, dest } = require("gulp");
2 | const gulpif = require("gulp-if");
3 | const config = require("../config").styles;
4 | const env = require("../lib/env");
5 | const handleErrors = require("../lib/handleErrors");
6 | const browserSync = require("browser-sync");
7 |
8 | const sourcemaps = require("gulp-sourcemaps");
9 | const postcss = require("gulp-postcss");
10 | const cssnano = require("gulp-cssnano");
11 |
12 | const styles = () =>
13 | src(config.src)
14 | .pipe(gulpif(!env.prod, sourcemaps.init()))
15 | .pipe(postcss())
16 | .pipe(gulpif(env.prod, cssnano({ autoprefixer: false })))
17 | .pipe(gulpif(!env.prod, sourcemaps.write("./")))
18 | .on("error", handleErrors)
19 | .pipe(dest(config.dest))
20 | .pipe(browserSync.stream());
21 |
22 | module.exports = styles;
23 |
--------------------------------------------------------------------------------
/src/styles/components/buttons.css:
--------------------------------------------------------------------------------
1 | .button {
2 | appearance: none;
3 | position: relative;
4 | display: inline-flex;
5 | align-items: center;
6 | justify-content: center;
7 | min-width: 6rem;
8 | text-align: center;
9 | white-space: normal;
10 | margin: 0;
11 | padding: var(--flow-sm) var(--flow-md);
12 | font-size: 1rem;
13 | font-family: inherit;
14 | border: 1px solid transparent;
15 | background-color: var(--color-purple-500);
16 | color: white;
17 | transition: 0.2s ease-out;
18 | cursor: pointer;
19 |
20 | &,
21 | &:hover,
22 | &:focus {
23 | text-decoration: none;
24 | outline: none;
25 | }
26 |
27 | &:hover:not([disabled]) {
28 | border-color: white;
29 | }
30 |
31 | &:active {
32 | top: 1px;
33 | }
34 |
35 | &[disabled] {
36 | cursor: not-allowed;
37 | opacity: 0.45;
38 | }
39 |
40 | .icon {
41 | color: inherit;
42 | margin-left: var(--flow-sm);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/gulpfile.js/tasks/images.js:
--------------------------------------------------------------------------------
1 | const { src, dest } = require('gulp');
2 | const gulpif = require('gulp-if');
3 | const changed = require('gulp-changed');
4 | const config = require('../config').images;
5 | const env = require('../lib/env');
6 | const handleErrors = require('../lib/handleErrors');
7 | const browserSync = require('browser-sync');
8 |
9 | const imagemin = require('gulp-imagemin');
10 |
11 | const imageminConfig = [
12 | imagemin.gifsicle({ interlaced: true }),
13 | imagemin.mozjpeg({ progressive: true }),
14 | imagemin.optipng({ optimizationLevel: 5 }),
15 | imagemin.svgo({
16 | plugins: [{ removeViewBox: true }, { cleanupIDs: false }],
17 | }),
18 | ];
19 |
20 | const images = () =>
21 | src(`${config.src}.{${config.ext}}`)
22 | .pipe(changed(config.dest))
23 | .on('error', handleErrors)
24 | .pipe(gulpif(env.prod, imagemin(imageminConfig)))
25 | .pipe(dest(config.dest))
26 | .pipe(browserSync.stream());
27 |
28 | module.exports = images;
29 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # .gitattributes
2 |
3 | # Auto detect text files and perform LF normalization
4 | * text=auto
5 |
6 | # These files are text and should be normalized
7 | *.php text
8 | *.css text
9 | *.js text
10 | *.htm text
11 | *.html text
12 | *.xml text
13 | *.txt text
14 | *.ini text
15 | *.inc text
16 | .htaccess text
17 |
18 | # Documents
19 | *.doc diff=astextplain
20 | *.DOC diff=astextplain
21 | *.docx diff=astextplain
22 | *.DOCX diff=astextplain
23 | *.dot diff=astextplain
24 | *.DOT diff=astextplain
25 | *.pdf diff=astextplain
26 | *.PDF diff=astextplain
27 | *.rtf diff=astextplain
28 | *.RTF diff=astextplain
29 |
30 | # These files are binary and should be left untouched
31 | # (binary is a macro for -text -diff)
32 | *.png binary
33 | *.jpg binary
34 | *.jpeg binary
35 | *.gif binary
36 | *.ico binary
37 | *.mov binary
38 | *.mp4 binary
39 | *.mp3 binary
40 | *.flv binary
41 | *.fla binary
42 | *.swf binary
43 | *.gz binary
44 | *.zip binary
45 | *.7z binary
46 | *.ttf binary
--------------------------------------------------------------------------------
/src/styles/base/layout.css:
--------------------------------------------------------------------------------
1 | body::before {
2 | content: 'xs';
3 | display: none;
4 | visibility: hidden;
5 |
6 | @media (--sm) {
7 | content: 'sm';
8 | }
9 | @media (--md) {
10 | content: 'md';
11 | }
12 | @media (--lg) {
13 | content: 'lg';
14 | }
15 | @media (--xl) {
16 | content: 'xl';
17 | }
18 | }
19 |
20 | body,
21 | html {
22 | min-height: 100vh;
23 | overflow-x: hidden;
24 | }
25 |
26 | .flow {
27 | * + * {
28 | margin-top: var(--flow-md);
29 | }
30 |
31 | *:not(:--heading) + :--heading:not(.no-bump) {
32 | margin-top: var(--flow-lg);
33 | }
34 |
35 | ul ul,
36 | ol ol,
37 | li + li,
38 | dt,
39 | dd {
40 | margin-top: var(--flow-xs);
41 | }
42 | }
43 |
44 | .site-container {
45 | max-width: 120rem;
46 | margin: 0 auto;
47 | }
48 |
49 | .container {
50 | margin: 0 auto;
51 | padding: 0 var(--flow-md);
52 | max-width: 36rem;
53 |
54 | @media (--lg) {
55 | max-width: 62rem;
56 | }
57 |
58 | @media (--xl) {
59 | max-width: 68rem;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/gulpfile.js/index.js:
--------------------------------------------------------------------------------
1 | const { series, parallel, watch } = require('gulp');
2 | const browserSync = require('browser-sync');
3 |
4 | const config = require('./config');
5 | const env = require('./lib/env');
6 |
7 | const clean = require('./tasks/clean');
8 | const styles = require('./tasks/styles');
9 | const html = require('./tasks/html');
10 | const images = require('./tasks/images');
11 | const assets = require('./tasks/assets');
12 | const report = require('./tasks/report');
13 | const scripts = require('./tasks/scripts');
14 | const server = require('./tasks/server');
15 |
16 | const watching = (callback) => {
17 | watch(config.styles.watch, styles);
18 | watch(config.images.watch, images);
19 | watch(config.html.src, html);
20 | watch(config.html.data, html);
21 |
22 | callback();
23 | };
24 |
25 | const tasks = [styles, images, assets, html, scripts];
26 |
27 | module.exports = {
28 | default: env.prod
29 | ? series(clean, parallel(...tasks), report)
30 | : series(clean, parallel(...tasks), watching, server),
31 | };
32 |
--------------------------------------------------------------------------------
/src/styles/base/print.css:
--------------------------------------------------------------------------------
1 | @media print {
2 | *,
3 | *::before,
4 | *::after {
5 | background: transparent !important;
6 | color: #000 !important;
7 | box-shadow: none !important;
8 | text-shadow: none !important;
9 | }
10 |
11 | a,
12 | a:visited {
13 | text-decoration: underline;
14 | }
15 |
16 | a[href]::after {
17 | content: ' (' attr(href) ')';
18 | }
19 |
20 | abbr[title]::after {
21 | content: ' (' attr(title) ')';
22 | }
23 |
24 | .ir a::after,
25 | a[href^='javascript:']::after,
26 | a[href^='#']::after {
27 | content: '';
28 | }
29 |
30 | pre,
31 | blockquote {
32 | border: 1px solid #999;
33 | page-break-inside: avoid;
34 | }
35 |
36 | thead {
37 | display: table-header-group;
38 | }
39 |
40 | tr,
41 | img {
42 | page-break-inside: avoid;
43 | }
44 |
45 | img {
46 | max-width: 100% !important;
47 | }
48 |
49 | @page {
50 | margin: 0.5cm;
51 | }
52 |
53 | p,
54 | h2,
55 | h3 {
56 | orphans: 3;
57 | widows: 3;
58 | }
59 |
60 | h2,
61 | h3 {
62 | page-break-after: avoid;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Davs Howard
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/gulpfile.js/tasks/html.js:
--------------------------------------------------------------------------------
1 | const { src, dest } = require('gulp');
2 | const gulpif = require('gulp-if');
3 | const config = require('../config').html;
4 | const env = require('../lib/env');
5 | const handleErrors = require('../lib/handleErrors');
6 |
7 | const browserSync = require('browser-sync');
8 |
9 | const data = require('gulp-data');
10 | const htmlmin = require('gulp-htmlmin');
11 | const nunjucksRender = require('gulp-nunjucks-render');
12 | const strip = require('gulp-strip-comments');
13 | const fs = require('fs');
14 |
15 | const html = () => {
16 | const exclude = `${config.src}/**/{${config.excludeFolders.join(',')}}/**`;
17 |
18 | const parseData = () => JSON.parse(fs.readFileSync(config.data));
19 |
20 | const nunjucksRenderOptions = {};
21 | nunjucksRenderOptions.path = config.render.path;
22 | nunjucksRenderOptions.envOptions = config.render.envOptions;
23 |
24 | return src([`${config.src}/**/*.{${config.ext}}`, `!${exclude}`])
25 | .pipe(data(parseData))
26 | .on('error', handleErrors)
27 | .pipe(nunjucksRender(nunjucksRenderOptions))
28 | .on('error', handleErrors)
29 | .pipe(gulpif(env.prod, strip()))
30 | .pipe(gulpif(env.prod, htmlmin(config.htmlmin)))
31 | .pipe(dest(config.dest))
32 | .pipe(browserSync.stream());
33 | };
34 |
35 | module.exports = html;
36 |
--------------------------------------------------------------------------------
/src/styles/base/root.css:
--------------------------------------------------------------------------------
1 | @custom-media --sm (width >= 25em);
2 | @custom-media --md (width >= 48em);
3 | @custom-media --lg (width >= 70em);
4 | @custom-media --xl (width >= 96em);
5 | @custom-media --xxl (width >= 120em);
6 | @custom-selector :--heading
7 | h1,
8 | h2,
9 | h3,
10 | h4,
11 | h5,
12 | h6,
13 | .h1,
14 | .h2,
15 | .h3,
16 | .h4,
17 | .h5,
18 | .h6;
19 |
20 | :root {
21 | --flow-xxs: 0.375rem;
22 | --flow-xs: 0.5rem;
23 | --flow-sm: 0.75rem;
24 | --flow-md: 1rem;
25 | --flow-lg: 1.25rem;
26 | --flow-xl: 1.875rem;
27 | --flow-xxl: 2.5rem;
28 |
29 | --fluid-type-min-width: 25;
30 | --fluid-type-max-width: 120;
31 |
32 | --transition-smooth: cubic-bezier(0.765, 0, 0.175, 1);
33 |
34 | --font-base: system-ui;
35 | --font-header: system-ui;
36 |
37 | --color-purple-500: rebeccapurple;
38 | --color-orange-500: orange;
39 |
40 | --color-neutral-900: #1c1c1c;
41 | --color-neutral-800: #2d2d2d;
42 | --color-neutral-700: #595959;
43 | --color-neutral-600: #646464;
44 | --color-neutral-500: #9e9e9e;
45 | --color-neutral-400: #bbbbbb;
46 | --color-neutral-300: #d8d8d8;
47 | --color-neutral-200: #ececec;
48 | --color-neutral-100: #f5f5f5;
49 |
50 | --font-color: #333333;
51 | --heading-color: white;
52 | --link-color: white;
53 |
54 | --outline-color: lightgreen;
55 | --selection-color: lightgreen;
56 | }
57 |
--------------------------------------------------------------------------------
/src/styles/base/typography.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | --heading-min-size: 2;
3 | --heading-max-size: 2.5;
4 | font-size: 2rem;
5 | }
6 |
7 | h2 {
8 | --heading-min-size: 1.75;
9 | --heading-max-size: 2;
10 | font-size: 1.75rem;
11 | }
12 |
13 | h3 {
14 | --heading-min-size: 1.4;
15 | --heading-max-size: 1.6;
16 | font-size: 1.4rem;
17 | }
18 |
19 | h4 {
20 | --heading-min-size: 1.2;
21 | --heading-max-size: 1.4;
22 | font-size: 1.2rem;
23 | }
24 |
25 | h5 {
26 | --heading-min-size: 1;
27 | --heading-max-size: 1.2;
28 | font-size: 1rem;
29 | }
30 |
31 | h6 {
32 | --heading-min-size: 1;
33 | --heading-max-size: 1.2;
34 | font-size: 1rem;
35 | }
36 |
37 | :--heading {
38 | display: inline-block;
39 | width: 100%;
40 | color: var(--heading-color);
41 | font-size: calc(
42 | (var(--heading-min-size) * 1rem) +
43 | (var(--heading-max-size) - var(--heading-min-size)) *
44 | (100vw - (var(--fluid-type-min-width) * 1rem)) /
45 | (var(--fluid-type-max-width) - var(--fluid-type-min-width))
46 | );
47 | font-family: var(--font-header);
48 | line-height: 1.2;
49 | }
50 |
51 | a {
52 | color: var(--link-color);
53 | }
54 |
55 | p,
56 | ul,
57 | ol {
58 | font-size: 1rem;
59 | line-height: 1.4;
60 |
61 | &:empty {
62 | display: none;
63 | }
64 | }
65 |
66 | ul,
67 | ol {
68 | padding-left: var(--flow-md);
69 |
70 | ul,
71 | ol {
72 | padding-left: var(--flow-md);
73 | }
74 | }
75 |
76 | blockquote {
77 | position: relative;
78 | font-style: italic;
79 | padding-left: var(--flow-md);
80 | }
81 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tux",
3 | "version": "2.1.1",
4 | "description": "A baseline toolkit to ease the building of static HTML sites or templated CMS builds.",
5 | "license": "MIT",
6 | "author": "Davs Howard <@davshoward> (https://davshoward.com)",
7 | "keywords": [
8 | "gulp",
9 | "webpack",
10 | "browsersync",
11 | "tux",
12 | "postcss"
13 | ],
14 | "main": "gulpfile.js/index.js",
15 | "scripts": {
16 | "start": "gulp",
17 | "build": "cross-env NODE_ENV=production gulp"
18 | },
19 | "devDependencies": {
20 | "@babel/core": "^7.14.6",
21 | "@babel/preset-env": "^7.14.7",
22 | "babel-eslint": "^10.0.3",
23 | "babel-loader": "^8.2.2",
24 | "browser-sync": "^2.27.4",
25 | "cross-env": "^7.0.3",
26 | "del": "^6.0.0",
27 | "eslint": "^7.29.0",
28 | "glob": "^7.1.7",
29 | "gulp": "^4.0.2",
30 | "gulp-changed": "^4.0.3",
31 | "gulp-cssnano": "^2.1.3",
32 | "gulp-data": "^1.3.1",
33 | "gulp-htmlmin": "^5.0.1",
34 | "gulp-if": "^3.0.0",
35 | "gulp-imagemin": "^7.1.0",
36 | "gulp-notify": "^4.0.0",
37 | "gulp-nunjucks-render": "^2.2.3",
38 | "gulp-postcss": "^9.0.0",
39 | "gulp-sequence": "^1.0.0",
40 | "gulp-sizereport": "^1.2.1",
41 | "gulp-sourcemaps": "^3.0.0",
42 | "gulp-strip-comments": "^2.5.2",
43 | "gulp-watch": "^5.0.1",
44 | "path": "^0.12.7",
45 | "postcss": "^8.3.5",
46 | "postcss-color-mod-function": "^3.0.3",
47 | "postcss-import": "^14.0.2",
48 | "postcss-nested": "^5.0.5",
49 | "postcss-preset-env": "^6.7.0",
50 | "postcss-strip-inline-comments": "^0.1.5",
51 | "require-dir": "^1.2.0",
52 | "webpack": "^5.42.0",
53 | "webpack-dev-middleware": "^5.0.0",
54 | "webpack-hot-middleware": "^2.25.0",
55 | "webpack-stream": "^6.1.2"
56 | },
57 | "dependencies": {
58 | "focus-visible": "^5.2.0"
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | A baseline toolkit to ease the building of static HTML sites or templated CMS builds. Using Webpack 4, Gulp 4, PostCSS, Nunjunks and BrowserSync.
4 |
5 | ## Features 💪
6 |
7 | - HTML - Build templates with Nunjucks
8 | - CSS - PostCSS with autoprefixing, nesting, custom media queries and more - "Use tomorrow’s CSS syntax, today"
9 | - JS - Bundle and transpile ES6 JavaScript with Webpack and Babel
10 | - Assets - Automatically optimise images, manage fonts and static assets
11 | - Development - Live reload with BrowserSync and Webpack's HMR.
12 |
13 | ## Getting started 📖
14 |
15 | ### Requirements
16 |
17 | - Node.js
18 | - npm
19 |
20 | ### Off you go
21 |
22 | #### Clone
23 |
24 | ```bash
25 | git clone https://github.com/davshoward/tux
26 | cd
27 | ```
28 |
29 | #### Install with npm
30 |
31 | ```bash
32 | npm install
33 | ```
34 |
35 | #### Start
36 |
37 | ```bash
38 | npm start
39 | ```
40 |
41 | #### Build
42 |
43 | ```bash
44 | npm run build
45 | ```
46 |
47 | #### Configure
48 |
49 | Customise your own file paths within gulpfile.js/config.js
50 |
51 | ## Contributing
52 |
53 | Welcome any improvements or suggestions :-)
54 |
55 | ## Changelog
56 |
57 | ### 2.1.1
58 |
59 | - Bug fix for images not copying correctly
60 |
61 | ### 2.1.0
62 |
63 | - Updated all dependencies
64 | - Renamed rhythm spacing class to flow (and associated to custom properties)
65 | - Added custom :--heading property
66 | - Added Prettier config
67 |
68 | ### 2.0.0
69 |
70 | - Updated to Gulp 4 and introduced some adjustments to the build process
71 | - Restructured the CSS components
72 | - Introduced new CSS elements including theme file and prefers-reduced-motion override
73 |
74 | ### 1.2.1
75 |
76 | - Added JS-free FOUC fix
77 | - Minor CSS update
78 | - Remove Aria role=document (from PR #1)
79 |
80 | ### 1.2.0
81 |
82 | - Updated foundation css
83 | - Introduce spacing rhythm and fluid headers
84 | - Allow urls to not require .html
85 | - Added css-to-JS breakpoint sync
86 | - Updated dependencies - including adding postcss color-mod
87 |
88 | ### 1.1.1
89 |
90 | - Added focus-visible usage for better baseline accessibility
91 |
92 | ### 1.1.0
93 |
94 | - Fully migrate from SASS to PostCSS
95 | - Updated dependencies
96 |
97 | ### 1.0.0
98 |
99 | - Initial commit
100 |
--------------------------------------------------------------------------------
/src/html/layout/main.njk:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ page_title }}
8 |
9 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
44 |
45 |
46 |
47 |
48 |
49 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
61 |
62 |
63 |
64 | {% block main %} {% endblock %}
65 |
66 |
67 |
68 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/gulpfile.js/config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const glob = require('glob');
3 | const path = require('path');
4 | const env = require('./lib/env');
5 |
6 | const src = 'src';
7 | const dest = 'public';
8 |
9 | const assets = [
10 | {
11 | src: `${src}/fonts/*`,
12 | dest: `${dest}/fonts`,
13 | },
14 | {
15 | src: `${src}/static/*`,
16 | dest: `${dest}/`,
17 | },
18 | ];
19 |
20 | const server = {
21 | files: [],
22 | server: {
23 | baseDir: dest,
24 | middleware: [],
25 | serveStaticOptions: {
26 | extensions: ['html'],
27 | },
28 | },
29 | watchOptions: {
30 | debounceDelay: 2000,
31 | },
32 | };
33 |
34 | const html = {
35 | src: `${src}/html`,
36 | dest: `${dest}/`,
37 | data: `${src}/data/data.json`,
38 | render: {
39 | path: `${src}/html`,
40 | envOptions: {
41 | watch: false,
42 | },
43 | },
44 | htmlmin: {
45 | collapseWhitespace: true,
46 | },
47 | excludeFolders: ['layout', 'components', 'macros'],
48 | ext: ['html', 'njk', 'json'],
49 | };
50 |
51 | const images = {
52 | src: `${src}/images/*`,
53 | dest: `${dest}/`,
54 | watch: `${src}/images/*`,
55 | ext: ['jpg', 'png', 'svg', 'gif', 'webp'],
56 | };
57 |
58 | const styles = {
59 | src: `${src}/styles/*.css`,
60 | dest: `${dest}/styles`,
61 | watch: `${src}/styles/**/*.css`,
62 | };
63 |
64 | const scripts = {
65 | src: `${src}/js/**`,
66 | dest: `${dest}/js`,
67 | webpack: {
68 | mode: env.prod ? 'production' : 'development',
69 | context: path.resolve(__dirname, `../${src}/js`),
70 | entry: {
71 | main: !env.prod
72 | ? ['webpack-hot-middleware/client?reload=true', './main.js']
73 | : ['./main.js'],
74 | vendor: glob.sync(
75 | path.resolve(__dirname, `../${src}/js/vendor/**/*.js`)
76 | ),
77 | },
78 | output: {
79 | path: path.resolve(__dirname, `../${dest}/js`),
80 | filename: '[name].bundle.js',
81 | publicPath: '/js',
82 | },
83 | resolve: {
84 | alias: {
85 | utils: `${src}/js/utils`,
86 | modules: `${src}/js/modules`,
87 | },
88 | },
89 | plugins: !env.prod ? [new webpack.HotModuleReplacementPlugin()] : [],
90 | module: {
91 | rules: [
92 | {
93 | test: /\.js$/,
94 | exclude: /node_modules/,
95 | use: ['babel-loader'],
96 | },
97 | ],
98 | },
99 | },
100 | };
101 |
102 | const report = {
103 | src: [styles.dest, scripts.dest, images.dest],
104 | };
105 |
106 | module.exports = {
107 | src,
108 | dest,
109 | server,
110 | assets,
111 | images,
112 | html,
113 | styles,
114 | scripts,
115 | report,
116 | };
117 |
--------------------------------------------------------------------------------
/src/styles/base/foundation.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 |
5 | &,
6 | &::before,
7 | &::after {
8 | box-sizing: inherit;
9 | }
10 | }
11 |
12 | html {
13 | box-sizing: border-box;
14 | font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell,
15 | Noto Sans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
16 | 'Segoe UI Symbol', 'Noto Color Emoji';
17 | line-height: 1.15;
18 | -ms-text-size-adjust: 100%;
19 | -webkit-text-size-adjust: 100%;
20 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
21 | -ms-overflow-style: scrollbar;
22 | word-break: break-word;
23 | scroll-behavior: smooth;
24 | }
25 |
26 | body {
27 | min-height: 100vh;
28 | color: var(--font-color);
29 | font-family: var(--font-base);
30 | font-size: 1rem;
31 | overflow-wrap: break-word;
32 | }
33 |
34 | main {
35 | display: block;
36 | }
37 |
38 | hr {
39 | height: 0;
40 | overflow: visible;
41 | }
42 |
43 | nav ol,
44 | nav ul {
45 | list-style: none;
46 | }
47 |
48 | [hidden],
49 | .hidden {
50 | display: none !important;
51 | }
52 |
53 | a {
54 | background-color: transparent;
55 | -webkit-text-decoration-skip: objects;
56 |
57 | &:active,
58 | &:hover {
59 | outline: 0;
60 | }
61 | }
62 |
63 | b,
64 | strong {
65 | font-weight: bold;
66 | }
67 |
68 | small {
69 | font-size: 80%;
70 | }
71 |
72 | ::-moz-selection,
73 | ::selection {
74 | background-color: var(--selection-color);
75 | color: var(--font-color);
76 | text-shadow: none;
77 | }
78 |
79 | img {
80 | max-width: 100%;
81 | height: auto;
82 | -ms-interpolation-mode: bicubic;
83 | display: inline-block;
84 | vertical-align: middle;
85 | border: 0;
86 | border-style: none;
87 | }
88 |
89 | button,
90 | input,
91 | select,
92 | textarea {
93 | color: inherit;
94 | font: inherit;
95 | }
96 |
97 | button,
98 | input,
99 | select {
100 | overflow: visible;
101 | }
102 |
103 | button {
104 | background: transparent;
105 | border: 0;
106 | padding: 0;
107 | cursor: pointer;
108 | -webkit-font-smoothing: inherit;
109 | letter-spacing: inherit;
110 | }
111 |
112 | button,
113 | [type='button'],
114 | [type='reset'],
115 | [type='submit'] {
116 | -webkit-appearance: button;
117 | }
118 |
119 | input {
120 | overflow: visible;
121 | }
122 |
123 | textarea {
124 | resize: vertical;
125 | overflow: auto;
126 | }
127 |
128 | select {
129 | text-transform: none;
130 | }
131 |
132 | ::-moz-focus-inner {
133 | border-style: none;
134 | padding: 0;
135 | }
136 |
137 | table {
138 | border-collapse: collapse;
139 | border-spacing: 0;
140 | }
141 |
142 | audio,
143 | canvas,
144 | iframe,
145 | svg,
146 | video {
147 | vertical-align: middle;
148 | }
149 |
150 | audio:not([controls]) {
151 | display: none;
152 | height: 0;
153 | }
154 |
155 | svg:not([fill]) {
156 | fill: currentColor;
157 | }
158 |
159 | svg:not(:root) {
160 | overflow: hidden;
161 | }
162 |
163 | .layout-svgs {
164 | position: absolute;
165 | left: -200vw;
166 | }
167 |
168 | canvas {
169 | display: inline-block;
170 | }
171 |
172 | template {
173 | display: none;
174 | }
175 |
176 | a,
177 | area,
178 | button,
179 | input,
180 | label,
181 | select,
182 | summary,
183 | textarea,
184 | [tabindex] {
185 | touch-action: manipulation;
186 | }
187 |
188 | [aria-busy='true'] {
189 | cursor: progress;
190 | }
191 |
192 | [aria-controls] {
193 | cursor: pointer;
194 | }
195 |
196 | [aria-disabled='true'],
197 | [disabled] {
198 | cursor: not-allowed;
199 | }
200 |
201 | [aria-hidden='false'][hidden]:not(:focus) {
202 | clip: rect(0, 0, 0, 0);
203 | display: inherit;
204 | position: absolute;
205 | }
206 |
207 | .no-js *:focus {
208 | box-shadow: 0 0 0 2px var(--outline-color);
209 | }
210 |
211 | *:focus:not(.focus-visible) {
212 | outline: none;
213 | }
214 |
215 | .focus-visible {
216 | box-shadow: 0 0 0 2px var(--outline-color);
217 | }
218 |
219 | @media (prefers-reduced-motion: reduce) {
220 | html {
221 | scroll-behavior: auto;
222 | }
223 |
224 | * {
225 | transition: none !important;
226 | }
227 | }
228 |
--------------------------------------------------------------------------------