Compare commits
33 Commits
Author | SHA1 | Date |
---|---|---|
Robert McGovern | 14c2c4686d | |
Robert McGovern | 1c26957240 | |
Robert McGovern | b28d2d96e9 | |
Robert McGovern | e98766e476 | |
Robert McGovern | f6e65d037d | |
Robert McGovern | cea312c360 | |
Robert McGovern | f63f5b24c6 | |
Robert McGovern | 62f674c612 | |
Robert McGovern | ca2028046b | |
Robert McGovern | ca1577553d | |
Robert McGovern | c970213cbb | |
Robert McGovern | 27b514e9bd | |
Robert McGovern | 61809329ec | |
Robert McGovern | aaf47fe6d7 | |
Robert McGovern | d4978ee0f1 | |
Robert McGovern | bf006151c9 | |
Robert McGovern | e906cf0c1f | |
Robert McGovern | 4408dc967e | |
Robert McGovern | ef025e58c4 | |
Robert McGovern | 02bd9c09d9 | |
Robert McGovern | aecb4ae267 | |
Robert McGovern | 148ec2449a | |
Robert McGovern | 072cde01bd | |
Robert McGovern | d6ceae8527 | |
Robert McGovern | 1d0ca64efa | |
Robert McGovern | 17378c1f78 | |
Robert McGovern | c6b43bb5ac | |
Robert McGovern | 061d94644f | |
Robert McGovern | c50bf7e227 | |
Robert McGovern | 500d1e62b3 | |
Robert McGovern | 905cfccc38 | |
Robert McGovern | 630f50a3ef | |
Robert McGovern | 0ea20a1cdb |
273
.eleventy.js
|
@ -9,7 +9,6 @@ const autoprefixer = require("autoprefixer");
|
|||
const markdownIt = require("markdown-it");
|
||||
const markdownItRenderer = new markdownIt();
|
||||
const markdownItAnchor = require("markdown-it-anchor");
|
||||
// const relativeUrl = require("eleventy-filter-relative-url");
|
||||
const pluginTOC = require("eleventy-plugin-toc");
|
||||
const moment = require("moment");
|
||||
const description = require("eleventy-plugin-description");
|
||||
|
@ -17,21 +16,28 @@ const pluginRss = require("@11ty/eleventy-plugin-rss");
|
|||
const UpgradeHelper = require("@11ty/eleventy-upgrade-help");
|
||||
const xmlFiltersPlugin = require("eleventy-xml-plugin");
|
||||
const yaml = require("js-yaml");
|
||||
const syntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight");
|
||||
|
||||
const syntaxHighlight = require("eleventy-plugin-syntaxhighlight-chroma");
|
||||
const highlight = require("chroma-highlight");
|
||||
const eleventySass = require("@11tyrocks/eleventy-plugin-sass-lightningcss");
|
||||
|
||||
const inspect = require("node:util").inspect;
|
||||
|
||||
// relativeURL
|
||||
const path = require("path");
|
||||
const urlFilter = require("@11ty/eleventy/src/Filters/Url");
|
||||
const indexify = (url) => url.replace(/(\/[^.]*)$/, "$1index.html");
|
||||
|
||||
module.exports = function (eleventyConfig) {
|
||||
module.exports.config = {
|
||||
pathPrefix: "/",
|
||||
};
|
||||
|
||||
module.exports = async function (eleventyConfig) {
|
||||
// const urlFilter = await import("@11ty/eleventy/src/Filters/Url");
|
||||
const indexify = (url) => url.replace(/(\/[^.]*)$/, "$1index.html");
|
||||
|
||||
let pathPrefix = "/";
|
||||
|
||||
eleventyConfig.addDataExtension("yaml", contents => yaml.load(contents));
|
||||
eleventyConfig.addDataExtension("yml", contents => yaml.load(contents));
|
||||
eleventyConfig.addDataExtension("yaml", (contents) => yaml.load(contents));
|
||||
eleventyConfig.addDataExtension("yml", (contents) => yaml.load(contents));
|
||||
|
||||
// eleventyConfig.addPlugin(UpgradeHelper);
|
||||
|
||||
eleventyConfig.addPlugin(pluginRss);
|
||||
//Blog excerpts
|
||||
|
@ -48,41 +54,62 @@ module.exports = function (eleventyConfig) {
|
|||
// TODO https://www.npmjs.com/package/eleventy-plugin-meta-generator
|
||||
// Eleventy Syntax Highlighting (https://www.11ty.dev/docs/plugins/syntaxhighlight/)
|
||||
// eleventyConfig.addPlugin(require("@11ty/eleventy-plugin-syntaxhighlight"));
|
||||
|
||||
eleventyConfig.addPlugin(syntaxHighlight, {
|
||||
theme: "base16-snazzy",
|
||||
|
||||
alwaysWrapLineHighlights: true,
|
||||
// Change which Eleventy template formats use syntax highlighters
|
||||
// templateFormats: ["*"], // default
|
||||
|
||||
// Use only a subset of template types (11ty.js added in v4.0.0)
|
||||
// templateFormats: ["liquid", "njk", "md", "11ty.js"],
|
||||
|
||||
// init callback lets you customize Prism
|
||||
// init: function({ Prism }) {
|
||||
// Prism.languages.myCustomLanguage = /* */;
|
||||
// },
|
||||
|
||||
// Added in 3.1.1, add HTML attributes to the <pre> or <code> tags
|
||||
preAttributes: {
|
||||
tabindex: 0,
|
||||
|
||||
// Added in 4.1.0 you can use callback functions too
|
||||
"data-language": function({ language, content, options }) {
|
||||
return language;
|
||||
}
|
||||
lexerOverrides: {
|
||||
njk: "vue",
|
||||
liquid: "vue",
|
||||
},
|
||||
codeAttributes: {},
|
||||
});
|
||||
|
||||
// eleventyConfig.addPlugin(syntaxHighlight, {
|
||||
|
||||
// alwaysWrapLineHighlights: true,
|
||||
// // Change which Eleventy template formats use syntax highlighters
|
||||
// // templateFormats: ["*"], // default
|
||||
|
||||
// // Use only a subset of template types (11ty.js added in v4.0.0)
|
||||
// // templateFormats: ["liquid", "njk", "md", "11ty.js"],
|
||||
|
||||
// // init callback lets you customize Prism
|
||||
// // init: function({ Prism }) {
|
||||
// // Prism.languages.myCustomLanguage = /* */;
|
||||
// // },
|
||||
|
||||
// // Added in 3.1.1, add HTML attributes to the <pre> or <code> tags
|
||||
// preAttributes: {
|
||||
// tabindex: 0,
|
||||
|
||||
// // Added in 4.1.0 you can use callback functions too
|
||||
// "data-language": function({ language, content, options }) {
|
||||
// return language;
|
||||
// }
|
||||
// },
|
||||
// codeAttributes: {},
|
||||
// });
|
||||
|
||||
// assert(typeof highlight === "function");
|
||||
// eleventyConfig.addPlugin(highlight);
|
||||
|
||||
// eleventyConfig.addMarkdownHighlighter(
|
||||
// highlight(
|
||||
// `--formatter html --html-only --html-inline-styles --lexer typescript --style base16-snazzy`
|
||||
// )
|
||||
// );
|
||||
|
||||
eleventyConfig.addPlugin(xmlFiltersPlugin);
|
||||
|
||||
// eleventyConfig.addPlugin(eleventySass);
|
||||
|
||||
// Custom Collections
|
||||
eleventyConfig.addCollection("pages", (collection) =>
|
||||
collection.getFilteredByGlob("./src/_pages/**/*")
|
||||
);
|
||||
|
||||
eleventyConfig.addCollection("posts", (collection) =>
|
||||
collection
|
||||
eleventyConfig.addCollection("posts", (collection) => {
|
||||
availablePages = collection
|
||||
.getFilteredByGlob("./src/_posts/**/*")
|
||||
.filter(
|
||||
(item) => item.data.draft !== true && item.date <= new Date()
|
||||
|
@ -94,8 +121,10 @@ module.exports = function (eleventyConfig) {
|
|||
prev: all[i + 1],
|
||||
};
|
||||
return cur;
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return availablePages;
|
||||
});
|
||||
|
||||
eleventyConfig.addCollection("projects", (collection) =>
|
||||
collection
|
||||
|
@ -111,8 +140,7 @@ module.exports = function (eleventyConfig) {
|
|||
collection
|
||||
.getFilteredByGlob("./src/_drafts/**/*")
|
||||
.sort((a, b) => a.data.weight - b.data.weight)
|
||||
) ;
|
||||
|
||||
);
|
||||
|
||||
eleventyConfig.addCollection("tags", (collection) => {
|
||||
let tags = new Set();
|
||||
|
@ -198,10 +226,15 @@ module.exports = function (eleventyConfig) {
|
|||
});
|
||||
|
||||
eleventyConfig.addFilter("inspect", function (obj = {}) {
|
||||
return inspect(obj, {sorted: true});
|
||||
return inspect(obj, { sorted: true });
|
||||
});
|
||||
|
||||
eleventyConfig.addFilter('group_by', groupBy)
|
||||
eleventyConfig.addFilter("debugger", (...args) => {
|
||||
console.log(...args);
|
||||
debugger;
|
||||
});
|
||||
|
||||
eleventyConfig.addFilter("group_by", groupBy);
|
||||
|
||||
eleventyConfig.addLayoutAlias(
|
||||
"archive-taxonomy",
|
||||
|
@ -236,7 +269,11 @@ module.exports = function (eleventyConfig) {
|
|||
eleventyConfig.addPassthroughCopy("src/assets");
|
||||
// eleventyConfig.addPassthroughCopy({ "src/_sass": "assets/css" });
|
||||
|
||||
eleventyConfig.addShortcode("post_url", (collection, slug) => {
|
||||
let availablePages = [];
|
||||
|
||||
eleventyConfig.addShortcode("post_url", (slug) => {
|
||||
let collection = availablePages;
|
||||
|
||||
try {
|
||||
if (collection.length < 1) {
|
||||
throw "Collection appears to be empty";
|
||||
|
@ -256,7 +293,7 @@ module.exports = function (eleventyConfig) {
|
|||
}
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`RMCG:An error occured while searching for the url to ${slug}. Details:`,
|
||||
`RMCG:An error occurred while searching for the url to ${slug}. Details:`,
|
||||
e
|
||||
);
|
||||
}
|
||||
|
@ -265,23 +302,31 @@ module.exports = function (eleventyConfig) {
|
|||
// Set section
|
||||
|
||||
// Configure BrowserSync to serve the 404 page for missing files
|
||||
eleventyConfig.setBrowserSyncConfig({
|
||||
callbacks: {
|
||||
ready: (_err, browserSync) => {
|
||||
const content_404 = fs.readFileSync("dist/404.html");
|
||||
// eleventyConfig.setBrowserSyncConfig({
|
||||
// callbacks: {
|
||||
// ready: (_err, browserSync) => {
|
||||
// const content_404 = fs.readFileSync("dist/404.html");
|
||||
|
||||
browserSync.addMiddleware("*", (_req, res) => {
|
||||
// render the 404 content instead of redirecting
|
||||
res.write(content_404);
|
||||
res.end();
|
||||
});
|
||||
},
|
||||
},
|
||||
// browserSync.addMiddleware("*", (_req, res) => {
|
||||
// // render the 404 content instead of redirecting
|
||||
// res.write(content_404);
|
||||
// res.end();
|
||||
// });
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
|
||||
eleventyConfig.setServerOptions({
|
||||
// Default values are shown:
|
||||
|
||||
// Whether the live reload snippet is used
|
||||
liveReload: true,
|
||||
watch: ["dist/assets/css/main.css"],
|
||||
});
|
||||
|
||||
eleventyConfig.setBrowserSyncConfig({
|
||||
files: "./dist/assets/styles/**/*.css",
|
||||
});
|
||||
// eleventyConfig.setBrowserSyncConfig({
|
||||
// files: "./dist/assets/css/*.css",
|
||||
// });
|
||||
|
||||
// Merge Data (https://www.11ty.dev/docs/data-deep-merge/)
|
||||
eleventyConfig.setDataDeepMerge(true);
|
||||
|
@ -360,7 +405,14 @@ module.exports = function (eleventyConfig) {
|
|||
// }
|
||||
// );
|
||||
|
||||
eleventyConfig.addFilter("relative_url", relativeURLALT);
|
||||
// eleventyConfig.addWatchTarget("dist/assets/css/*.css");
|
||||
|
||||
const { EleventyHtmlBasePlugin } = await import("@11ty/eleventy");
|
||||
|
||||
eleventyConfig.addPlugin(EleventyHtmlBasePlugin);
|
||||
|
||||
eleventyConfig.addFilter("relative_url", relativeURLALT2);
|
||||
|
||||
eleventyConfig.addFilter("absolute_url", absoluteUrl);
|
||||
|
||||
return {
|
||||
|
@ -388,117 +440,38 @@ function numberOfWords(content) {
|
|||
return content.split(/\s+/g).length;
|
||||
}
|
||||
|
||||
function relativeURL(url, pathPrefix = undefined) {
|
||||
if (pathPrefix !== undefined) {
|
||||
// Fall back on original url filter if pathPrefix is set.
|
||||
return urlFilter(url, pathPrefix);
|
||||
// Cheat till I can go through
|
||||
function relativeURLALT2(url) {
|
||||
if (!url.startsWith("/")) {
|
||||
throw new Error("URL is already relative");
|
||||
}
|
||||
|
||||
if (pathPrefix == undefined && this.page == undefined) {
|
||||
return urlFilter(url, "");
|
||||
}
|
||||
|
||||
// Look up the url of the current rendering page, which is accessible via
|
||||
// `this`.
|
||||
//console.log(this); // rmcg
|
||||
|
||||
// rmcg - removed ctx from this.ctx.page.url
|
||||
const currentDir = this.page.url;
|
||||
const filteredUrl = urlFilter(url, "/");
|
||||
|
||||
// Make sure the index.html is expressed.
|
||||
const indexUrl = indexify(filteredUrl);
|
||||
|
||||
// Check that the url doesn't specify a protocol.
|
||||
const u = new URL(indexUrl, "make-relative://");
|
||||
if (u.protocol !== "make-relative:") {
|
||||
// It has a protocol, so just return the filtered URL output.
|
||||
return filteredUrl;
|
||||
}
|
||||
|
||||
// Return the relative path, or `index.html` if it's the same as the current
|
||||
// page's directory.
|
||||
const relativePath = `${
|
||||
path.relative(currentDir, u.pathname) || "index.html"
|
||||
}`;
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Just `{{ '/something' | url }}` will return the relative path to
|
||||
* `/something/index.html`.
|
||||
*
|
||||
* `{{ '/something.with.dots' | url }}` will return the relative path to
|
||||
* `/something.with.dots`.
|
||||
*
|
||||
* @param {string} url the URL to transform
|
||||
* @param {string} [pathPrefix] optional path prefix to force an absolute URL
|
||||
* @returns {string} resulting URL
|
||||
*/
|
||||
function relativeURLALT(url, pathPrefix = undefined) {
|
||||
pathPrefix = "/";
|
||||
// console.log(url);
|
||||
// console.log(pathPrefix);
|
||||
// console.log(this.page);
|
||||
if (pathPrefix !== undefined) {
|
||||
// Fall back on original url filter if pathPrefix is set.
|
||||
return urlFilter(url, pathPrefix);
|
||||
}
|
||||
|
||||
if (pathPrefix == undefined && this.page == undefined) {
|
||||
// console.log("dropping out");
|
||||
return urlFilter(url, "");
|
||||
}
|
||||
|
||||
// Look up the url of the current rendering page, which is accessible via
|
||||
// `this`.
|
||||
console.log(this);
|
||||
const currentDir = this.page.url;
|
||||
const filteredUrl = urlFilter(url, "/");
|
||||
|
||||
// Make sure the index.html is expressed.
|
||||
const indexUrl = indexify(filteredUrl);
|
||||
|
||||
// Check that the url doesn't specify a protocol.
|
||||
const u = new URL(indexUrl, "make-relative://");
|
||||
if (u.protocol !== "make-relative:") {
|
||||
// It has a protocol, so just return the filtered URL output.
|
||||
return filteredUrl;
|
||||
}
|
||||
|
||||
// Return the relative path, or `index.html` if it's the same as the current
|
||||
// page's directory.
|
||||
const relativePath = `${
|
||||
path.relative(currentDir, u.pathname) || "index.html"
|
||||
}`;
|
||||
return relativePath;
|
||||
return url;
|
||||
}
|
||||
|
||||
function absoluteUrl(url) {
|
||||
if (url !== undefined) {
|
||||
return siteURL + url
|
||||
return siteURL + url;
|
||||
} else {
|
||||
return siteURL
|
||||
return siteURL;
|
||||
}
|
||||
}
|
||||
|
||||
function groupBy(array, key) {
|
||||
const get = entry => key.split('.').reduce((acc, key) => acc[key], entry)
|
||||
const get = (entry) => key.split(".").reduce((acc, key) => acc[key], entry);
|
||||
|
||||
const map = array.reduce((acc, entry) => {
|
||||
const value = get(entry)
|
||||
const value = get(entry);
|
||||
|
||||
if (typeof acc[value] === 'undefined') {
|
||||
acc[value] = []
|
||||
if (typeof acc[value] === "undefined") {
|
||||
acc[value] = [];
|
||||
}
|
||||
|
||||
acc[value].push(entry)
|
||||
return acc
|
||||
}, {})
|
||||
acc[value].push(entry);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return Object.keys(map).reduce(
|
||||
(acc, key) => [...acc, { name: key, items: map[key] }],
|
||||
[]
|
||||
)
|
||||
}
|
||||
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Ignore folders generated by Bundler
|
||||
vendor
|
||||
#vendor
|
||||
.bundle
|
||||
|
||||
# Ignore folders generated by Jekyll
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 Robert McGovern
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
10
package.json
|
@ -5,7 +5,8 @@
|
|||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build:sass": "sass --load-path=src/_sass --style=compressed dist/assets/css/tocompile.scss dist/assets/css/main.css",
|
||||
"watch:eleventy": "eleventy --serve",
|
||||
"watch:sass": "npm run build:sass -- --watch",
|
||||
"watch:eleventy": "eleventy --serve --ignore-initial --incremental",
|
||||
"build:eleventy": "eleventy",
|
||||
"clean": "rm -rf dist",
|
||||
"postbuild": "",
|
||||
|
@ -26,14 +27,16 @@
|
|||
"author": "Robert McGovern et all",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@11ty/eleventy": "^2.0.0-beta.1"
|
||||
"@11ty/eleventy": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@11ty/eleventy-navigation": "^0.3.5",
|
||||
"@11ty/eleventy-plugin-rss": "^1.2.0",
|
||||
"@11ty/eleventy-plugin-syntaxhighlight": "^4.2.0",
|
||||
"@11ty/eleventy-upgrade-help": "^2.0.5",
|
||||
"@11ty/eleventy-upgrade-help": "^3.0.1",
|
||||
"@11tyrocks/eleventy-plugin-sass-lightningcss": "^1.0.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"chroma-highlight": "^2.4.2",
|
||||
"const": "^1.0.0",
|
||||
"eleventy-load": "^0.3.1",
|
||||
"eleventy-load-css": "^0.3.0",
|
||||
|
@ -41,6 +44,7 @@
|
|||
"eleventy-load-html": "^0.1.1",
|
||||
"eleventy-load-sass": "^0.1.2",
|
||||
"eleventy-plugin-description": "^0.1.5",
|
||||
"eleventy-plugin-syntaxhighlight-chroma": "^0.0.1",
|
||||
"eleventy-plugin-toc": "^1.1.5",
|
||||
"eleventy-xml-plugin": "^0.1.0",
|
||||
"fs-extra": "^11.1.0",
|
||||
|
|
|
@ -19,7 +19,8 @@
|
|||
"words_per_minute": 200,
|
||||
"head_scripts": [
|
||||
"/assets/js/progress.js",
|
||||
"/assets/js/scroll-to-top.js"
|
||||
"/assets/js/scroll-to-top.js",
|
||||
"/assets/js/copy-code-button.js"
|
||||
],
|
||||
"comments": {
|
||||
"provider": "disqus",
|
||||
|
@ -185,35 +186,15 @@
|
|||
},
|
||||
"footer": {
|
||||
"links": [
|
||||
{
|
||||
"label": "RSS for Dev Posts",
|
||||
"icon": "fas fa-fw fa-rss-square",
|
||||
"url": "/feed-dev.xml"
|
||||
},
|
||||
{
|
||||
"label": "Email",
|
||||
"icon": "fas fa-fw fa-envelope-square",
|
||||
"url": "mailto:rob@tarasis.net"
|
||||
},
|
||||
{
|
||||
"label": "Twitter",
|
||||
"icon": "fab fa-fw fa-twitter-square",
|
||||
"url": "https://twitter.com/tarasis"
|
||||
},
|
||||
{
|
||||
"label": "Mastodon",
|
||||
"icon": "fab fa-fw fa-mastodon",
|
||||
"url": "https://social.tarasis.net/@tarasis"
|
||||
},
|
||||
{
|
||||
"label": "Facebook",
|
||||
"icon": "fab fa-fw fa-facebook",
|
||||
"url": "https://www.facebook.com/tarasis"
|
||||
},
|
||||
{
|
||||
"label": "GitHub",
|
||||
"icon": "fab fa-fw fa-github",
|
||||
"url": "https://github.com/tarasis"
|
||||
},
|
||||
{
|
||||
"label": "Instagram",
|
||||
"icon": "fab fa-fw fa-instagram",
|
||||
"url": "https://instagram.com/tarasis"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -339,7 +320,7 @@
|
|||
"share": true,
|
||||
"related": true,
|
||||
"comments": true,
|
||||
"sidebar":true,
|
||||
"sidebar": true,
|
||||
"show_date": true
|
||||
},
|
||||
"pages": {
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
title: Introducing eleventy-plugin-syntaxhighlighting-chroma
|
||||
tags: [webdev, site]
|
||||
category: [site, webdev]
|
||||
date: 2023-02-11
|
||||
eleventyExcludeFromCollections: true
|
||||
excerpt: An introduction to the 11ty plugin.
|
||||
---
|
||||
|
||||
To do
|
||||
|
||||
[] - Introduce Project
|
||||
[] - Why did I make it
|
||||
[] - how to use it
|
||||
[] - examples
|
||||
[] - future
|
||||
|
||||
One of the things I wanted to do with the revamp of the blog was to make an effort to do more technical writing.
|
||||
|
||||
[11ty]() provides its own [plugin](https://github.com/11ty/eleventy-plugin-syntaxhighlight) for syntax highlighting using the [PrismJS]() library. However it doesn't support all the plugins that PrismJS provides. Also missing is support for line numbers, there is an [open issue]() about that, and also [issues](https://github.com/11ty/eleventy-plugin-syntaxhighlight/issues/32) about using a different highlight engine; mentioned are [torchlight](https://torchlight.dev), [shiki](https://github.com/shikijs/shiki).
|
||||
|
||||
Someone has already written an 11ty plugin, [eleventy-plugin-highlightjs](https://github.com/b-kelly/eleventy-plugin-highlightjs) using HighlightJS.
|
||||
|
||||
##
|
|
@ -1,194 +0,0 @@
|
|||
---
|
||||
title: What happens when I finish a Frontend Mentor Challenge
|
||||
tags: [webdev, site, frontendmentor]
|
||||
category: [programming, webdev]
|
||||
eleventyExcludeFromCollections: true
|
||||
---
|
||||
|
||||
I've been doing challenges from [Frontend Mentor](https://frontendmentor.io) as a means to practice frontend web development. Specifically working with plain HTML, CSS and JavaScript.
|
||||
|
||||
Rather than just take the simple route of a Git repo per challenge, I put them all in a single repo that is pushed to two[^1] servers ([Github](https://github.com/tarasis/tarasis.github.io) and a [Gitea](https://git.tarasis.net/tarasis/tarasis.github.io) instance).
|
||||
|
||||
The repo is actually a website built with [11ty](https://www.11ty.dev) and [Nunjucks](https://mozilla.github.io/nunjucks/) for templating. The challenges, and other pages I build are in the **projects** directory. They are simply copied over to **www* during the build.
|
||||
|
||||
When I do a `git push all`, on my server it runs a script that does the 11ty build. On Github I use a **Github Action**[^2] which builds the site and then a separate action that deploys the page. Vercel watches the main branch on Github for updates and does a similar build action and deployment.
|
||||
|
||||
That is then deployed to three different places: [Github Pages](https://tarasis.github.io), [Vercel](https://tarasis.vercel.app), and my own site at [rmcg.dev](https://rmcg.dev).
|
||||
|
||||
## Digging a little deeper
|
||||
|
||||
The front page of the deployed site acts as a gateway to all of the challenges, for instance this is the newbie section for Frontend Mentor challenges:
|
||||
|
||||
![Screenshot of the newbie section of the my portfolio website](/assets/images/portfolio.png)
|
||||
|
||||
Each section is generated from a json file I created, where I have the service name, location of an svg file, alt text for it, the difficulty level, then there is an array of challenges in that difficulty level.
|
||||
|
||||
```json
|
||||
"services": [
|
||||
{
|
||||
"name": "Frontend Mentor",
|
||||
"svgSource": "/svgs/frontendmentor.svg",
|
||||
"svgAltText": "Frontend Mentor Logo",
|
||||
"description": "Collection of challenges I completed for Frontend Mentor",
|
||||
"cssClass": "",
|
||||
"difficulty": [
|
||||
{
|
||||
"title": "Junior",
|
||||
"cssClass": "frontEndMentorChallenges",
|
||||
"challenges": [
|
||||
{
|
||||
"title": "Advice Generator App",
|
||||
"url": "/FrontendMentor/junior/advice-generator-app/",
|
||||
"description": "",
|
||||
"techUsed": [
|
||||
"html5",
|
||||
"css3",
|
||||
"js"
|
||||
],
|
||||
"screenshotURL": "/FrontendMentor/junior/advice-generator-app/screenshots/mobile.png",
|
||||
"screenshotAltText": "Advice Generator App"
|
||||
}
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
When the 11ty build occurs, it takes two nunjucks snippets then first takes the service section and fills out the details:
|
||||
|
||||
{% raw %}
|
||||
```liquid
|
||||
{% for service in webprojects.services %}
|
||||
<div class="projectsDiv {{service.cssClass}}">
|
||||
<div class="alignedHeader">
|
||||
<h2 class="projectsSection__title">{{service.name}}</h2>
|
||||
{% if service.svgSource%}
|
||||
<img src="{{service.svgSource}}" alt="{{service.svgAltText}}"/>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if service.description%}
|
||||
<p>{{service.description}}</p>
|
||||
{% endif %}
|
||||
|
||||
{% for difficultyLevel in service.difficulty %}
|
||||
{% if difficultyLevel.challenges | length %}
|
||||
<div class="{{difficultyLevel.cssClass}}">
|
||||
{% if difficultyLevel.title%}
|
||||
<h3>{{difficultyLevel.title}} Challenges</h3>
|
||||
{% endif %}
|
||||
{% if difficultyLevel.description%}
|
||||
<p>{{difficultyLevel.description}}</p>
|
||||
{% endif %}
|
||||
<div class="projects-grid">
|
||||
{% for challenge in difficultyLevel.challenges %}
|
||||
{% set projectContent = challenge %}
|
||||
{% include "components/project.njk" %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
If you note the `div` with class `projects-grid`, this is where the second nunjucks snippet occurs. It loops through all of the challenges in the json array represented by `difficultyLevel.challenges`.
|
||||
|
||||
Basically I pass in the challenge to the snippet below, and use the contents to fill out the a card with a screenshot of the finished build, a short description, and shields for HTML/CSS and if used JavaScript. When I get to later challenges I'll add in 11ty, Vue or whatever. (I'll probably simplify the code too so it grabs the tech svg based on the name, rather than having if checks ... we'll see.)
|
||||
|
||||
{% raw %}
|
||||
```liquid
|
||||
{# {% set projectPrefix = project %} #}
|
||||
|
||||
{% if projectContent %}
|
||||
<div class="project-card stacked">
|
||||
<a href="{{projectContent.url}}">
|
||||
<img loading="lazy" class="card__img" src="{{projectContent.screenshotURL}}"
|
||||
alt="{{projectContent.screenshotAltText}}"/>
|
||||
</a>
|
||||
<div class="card__content">
|
||||
<h3 class="card__title">
|
||||
{{projectContent.title}}
|
||||
</h3>
|
||||
{% if projectContent.description | length %}
|
||||
<p class="card__description">
|
||||
{{projectContent.description}}
|
||||
</p>
|
||||
{% endif %}
|
||||
<ul class="card__techUsed">
|
||||
{% for tech in projectContent.techUsed %}
|
||||
{% if tech === "html5" %}
|
||||
<li>
|
||||
<img src="/svgs/html5.svg" alt="HTML5"/>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if tech === "css3" %}
|
||||
<li>
|
||||
<img src="/svgs/css3.svg" alt="CSS3"/>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if tech === "js" %}
|
||||
<li>
|
||||
<img src="/svgs/javascript.svg" alt="JavaScript"/>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
The finished build is then copied and published.
|
||||
|
||||
## Improvements ...
|
||||
|
||||
There are improvements I will make at some point, specifically adding optimisation of the images I use. There is little point is downloading a 1440x800px image for the challenge preview when only a small portion of it is shown. I am aware that during the build process you can have 11ty generate more web friendly images at differing sizes.
|
||||
|
||||
## Final thoughts
|
||||
|
||||
First if you want to know more about whats going on, please do ask I'll do my best to answer. Otherwise I've found 11ty incredibly useful for this purpose, and easy to use. So much so that my intent was/is to move the blog from Jekyll to 11ty ... I just haven't gotten to it yet and because I like the Minimal Mistakes theme I use here.
|
||||
|
||||
Having a static site where I can quickly add a challenge to the front page without having to repeat html myself is just a such a relief. Especially as the file gets longer and longer :) At this point in time I've finished 17 challenges for Frontend Mentor with 75 ish more to go. There's 1 for Dev Challenges, and 5 for FreeCodeCamp.
|
||||
|
||||
**NOTE** ... There are other project directories that aren't listed on the front page. You can see the code in the projects directory on Github / my gitea instance, and live under [others](https://rmcg.dev/others/). They are things I've done based of Youtube videos or other videos.
|
||||
|
||||
[^1]: Because I like redundancy when I can, and I want control of my code.
|
||||
|
||||
[^2]: The build action I use ...
|
||||
|
||||
```
|
||||
name: Build Eleventy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [17.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install dependencies & build
|
||||
run: |
|
||||
npm ci
|
||||
npm run build
|
||||
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3.7.3
|
||||
with:
|
||||
publish_dir: ./www
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
layout: single
|
||||
author_profile: true
|
||||
read_time: true
|
||||
share: true
|
||||
related: true
|
||||
title: Frontend Mentor Review
|
||||
tags: [webdev, programming]
|
||||
category: Programming
|
||||
eleventyExcludeFromCollections: true
|
||||
---
|
||||
|
||||
1. What Frontend Mentor is
|
||||
Site that provides differing levels of challenges
|
||||
Owned by Matt Studdert
|
||||
You build your solution to the challenge using any means that you wish (straight HTML/CSS/JavaScript, or React, Svelte and so on)
|
||||
1. What do you get
|
||||
base files
|
||||
style sheet
|
||||
paid: figma and sketch
|
||||
1. on completing a challenge
|
||||
You submit the challenge
|
||||
Other people can then see your solution
|
||||
slide to compare desktop version against the design image
|
||||
see any questions you have asked
|
||||
can provide comments
|
||||
points
|
||||
1. Peer review
|
||||
Other people who either go directly to your challenge page, or have completed the challenge themselves can see your solution
|
||||
they can provide comments, like your solution, bookmark it
|
||||
1. My thoughts & experience
|
||||
what do I like
|
||||
find it very useful
|
||||
great for building your skills
|
||||
what do I dislike
|
||||
it can be like the blind leading the blind. Its odd to be offering advice / tips when you are learning and likewise from others
|
||||
sometimes no comments or advice
|
||||
improvements?
|
||||
???
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
layout: single
|
||||
title: Numerical Brain Teasers review
|
||||
tags: [dev, review, puzzles]
|
||||
category: [dev, review, puzzles]
|
||||
eleventyExcludeFromCollections: true
|
||||
---
|
||||
|
||||
1. book by Erica Sadun - Numerical Brain Teasers published by Prag Prog
|
||||
1. Born out of puzzles on Prag Prog Twitter feed
|
||||
1. Price $9 for eBook (also Physically available)
|
||||
1. 12 different types of puzzles
|
||||
1. Each type has:
|
||||
a sample
|
||||
a description
|
||||
a little history
|
||||
ways to solve
|
||||
addiotnla puzzles to solve
|
||||
1. Some samples are available on the website
|
||||
1. Thoughts
|
|
@ -179,15 +179,33 @@ $(document).ready(function() {
|
|||
}
|
||||
```
|
||||
|
||||
<p>notice</p>{: .notice}
|
||||
<p>primary</p>{: .notice--primary}
|
||||
<p>info</p>{: .notice--info}
|
||||
<p>warning</p>{: .notice--warning}
|
||||
<p>success</p>{: .notice--success}
|
||||
<p>danger</p>{: .notice--danger}
|
||||
notice
|
||||
{: .notice}
|
||||
|
||||
primary
|
||||
{: .notice--primary}
|
||||
|
||||
info
|
||||
{: .notice--info}
|
||||
|
||||
warning
|
||||
{: .notice--warning}
|
||||
|
||||
success
|
||||
{: .notice--success}
|
||||
|
||||
danger
|
||||
{: .notice--danger}
|
||||
|
||||
TIP: Unless you are wed to the theme, don't try porting it. Really. Headaches await. 🤯
|
||||
{: .notice--warning}
|
||||
|
||||
**success**
|
||||
{: .notice--success}
|
||||
|
||||
|
||||
<div class="notice--success" markdown="1">
|
||||
# Headline for the Notice
|
||||
<h3> Headline for the Notice</h3>
|
||||
<br>
|
||||
Text for the notice
|
||||
</div>
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
layout: single
|
||||
title: Reviews of VPSs
|
||||
tags: [dev, review, site]
|
||||
category: [dev, review, site]
|
||||
eleventyExcludeFromCollections: true
|
||||
---
|
||||
|
||||
1. what is a VPS
|
||||
1. pricing
|
||||
1. Bang for Buck
|
||||
1. HostHatch
|
||||
1. VirMach
|
||||
1. CrunchBits
|
||||
1. Oracle
|
||||
1.
|
|
@ -32,7 +32,8 @@
|
|||
<p class="archive__item-excerpt" itemprop="description">{{ include.aPost.data.excerpt | markdownify | strip_html | truncate: 160 }}</p>
|
||||
{% else %}
|
||||
<!-- RMCG - Result of this isn't perfect but it works, so now all posts show an excerpt of sorts -->
|
||||
<p class="archive__item-excerpt" itemprop="description">{{ include.aPost.template._frontMatter.content strip_html | markdownify | truncate: 160 }}</p>
|
||||
<p class="archive__item-excerpt" itemprop="description">{{ include.aPost.template._cacheRenderedPromise |
|
||||
markdownify | strip_html | truncate: 160 }}</p>
|
||||
{% endif %}
|
||||
|
||||
</article>
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
{% if site.comments.provider and page.comments %}
|
||||
{% if site.comments.provider and comments or site.defaults.posts.comments %}
|
||||
{% case site.comments.provider %}
|
||||
{% when "disqus" %}
|
||||
{% include /comments-providers/disqus.html %}
|
||||
{% include comments-providers/disqus.html %}
|
||||
{% when "discourse" %}
|
||||
{% include /comments-providers/discourse.html %}
|
||||
{% include comments-providers/discourse.html %}
|
||||
{% when "facebook" %}
|
||||
{% include /comments-providers/facebook.html %}
|
||||
{% include comments-providers/facebook.html %}
|
||||
{% when "staticman" %}
|
||||
{% include /comments-providers/staticman.html %}
|
||||
{% include comments-providers/staticman.html %}
|
||||
{% when "staticman_v2" %}
|
||||
{% include /comments-providers/staticman_v2.html %}
|
||||
{% include comments-providers/staticman_v2.html %}
|
||||
{% when "utterances" %}
|
||||
{% include /comments-providers/utterances.html %}
|
||||
{% include comments-providers/utterances.html %}
|
||||
{% when "giscus" %}
|
||||
{% include /comments-providers/giscus.html %}
|
||||
{% include comments-providers/giscus.html %}
|
||||
{% when "custom" %}
|
||||
{% include /comments-providers/custom_scripts.html %}
|
||||
{% include comments-providers/custom_scripts.html %}
|
||||
{% endcase %}
|
||||
{% endif %}
|
|
@ -2,4 +2,6 @@
|
|||
<button id='scroll-to-top'>⏫</button>
|
||||
|
||||
<a rel="me" href="https://social.tarasis.net/@tarasis">For Mastodon verification</a>
|
||||
<link href="https://github.com/tarasis" rel="me">
|
||||
<link href="https://social.tarasis.net/@tarasis" rel="me">
|
||||
<!-- end custom footer snippets -->
|
||||
|
|
|
@ -40,7 +40,20 @@ layout: default
|
|||
</nav>
|
||||
</aside>
|
||||
{% endif %}
|
||||
{{ content }}
|
||||
|
||||
{% assign Content = content %}
|
||||
{% assign withoutDivStart = '<pre ' %}
|
||||
{% assign withDivStart = '<div class="highlight codeblock"><pre ' %}
|
||||
{% assign withoutDivEnd = '</code></pre>' %}
|
||||
{% assign withDivEnd = '</code></pre></div>' %}
|
||||
|
||||
{% if content contains withoutDivStart %}
|
||||
{% assign Content = content | replace: withoutDivStart, withDivStart %}
|
||||
{% assign Content = Content | replace: withoutDivEnd, withDivEnd %}
|
||||
{% endif %}
|
||||
|
||||
{{ Content }}
|
||||
|
||||
{% if page.link %}<div><a href="{{ page.link }}" class="btn btn--primary">{{ ui-text[site.locale].ext_link_label | default: "Direct Link" }}</a></div>{% endif %}
|
||||
</section>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% assign document = post | default: page %}
|
||||
{% assign document = document.data.page %}
|
||||
<!-- {% assign document = document.data.page %} -->
|
||||
|
||||
{% if document.read_time or document.show_date or site.defaults.posts.show_date %}
|
||||
|
||||
|
@ -20,7 +20,18 @@
|
|||
{% assign con = include.content | default: content %}
|
||||
{% assign words = con | strip_html | number_of_words %}
|
||||
|
||||
<!-- There is a minute difference between the time to read on the list pages, and on the article itself. So 5 minutes to read in the list, 4 minutes to read on article itself -->
|
||||
<!-- RMCG Hack to be able to have the time to read the article on both the index page, and the article itself. However there can be minor disparaties -->
|
||||
{% if words == 1 %}
|
||||
{% assign con = document.rawInput %}
|
||||
|
||||
{% if con %}
|
||||
{% assign con = con | markdownify | strip_html %}
|
||||
{% endif %}
|
||||
|
||||
{% assign words = con | strip_html | number_of_words %}
|
||||
{% endif %}
|
||||
|
||||
<!-- There is a minute difference between the time to read on the list pages, and on the article itself. So 4 minutes to read in the list, 5 minutes to read on article itself -->
|
||||
|
||||
<span class="page__meta-readtime">
|
||||
<i class="far {% if include.type == 'grid' and document.read_time and document.show_date %}fa-fw {% endif %}fa-clock" aria-hidden="true"></i>
|
||||
|
|
|
@ -6,12 +6,24 @@ layout: default
|
|||
permalink: /404.html
|
||||
---
|
||||
|
||||
Sorry, but the page you were trying to view does not exist --- perhaps you can try searching for it below.
|
||||
<style type="text/css" media="screen">
|
||||
.container {
|
||||
margin: 10px auto;
|
||||
max-width: 600px;
|
||||
text-align: center;
|
||||
}
|
||||
h1 {
|
||||
margin: 30px 0;
|
||||
font-size: 4em;
|
||||
line-height: 1;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
var GOOG_FIXURL_LANG = 'en';
|
||||
var GOOG_FIXURL_SITE = '{{ site.url }}'
|
||||
</script>
|
||||
<script type="text/javascript"
|
||||
src="//linkhelp.clients.google.com/tbproxy/lh/wm/fixurl.js">
|
||||
</script>
|
||||
<div class="container">
|
||||
<h1>404</h1>
|
||||
|
||||
<p><strong>😬 Page not found 😬</strong></p>
|
||||
<p>Sadly the requested page could not be found.</p>
|
||||
<p><a href="./">Maybe via the home page?</a></p>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"accentColor": ""
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"file-explorer": true,
|
||||
"global-search": true,
|
||||
"switcher": true,
|
||||
"graph": true,
|
||||
"backlink": true,
|
||||
"canvas": true,
|
||||
"outgoing-link": true,
|
||||
"tag-pane": true,
|
||||
"properties": false,
|
||||
"page-preview": true,
|
||||
"daily-notes": true,
|
||||
"templates": true,
|
||||
"note-composer": true,
|
||||
"command-palette": true,
|
||||
"slash-command": false,
|
||||
"editor-status": true,
|
||||
"bookmarks": true,
|
||||
"markdown-importer": false,
|
||||
"zk-prefixer": false,
|
||||
"random-note": false,
|
||||
"outline": true,
|
||||
"word-count": true,
|
||||
"slides": false,
|
||||
"audio-recorder": false,
|
||||
"workspaces": false,
|
||||
"file-recovery": true,
|
||||
"publish": false,
|
||||
"sync": false
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
[
|
||||
"file-explorer",
|
||||
"global-search",
|
||||
"switcher",
|
||||
"graph",
|
||||
"backlink",
|
||||
"canvas",
|
||||
"outgoing-link",
|
||||
"tag-pane",
|
||||
"page-preview",
|
||||
"daily-notes",
|
||||
"templates",
|
||||
"note-composer",
|
||||
"command-palette",
|
||||
"editor-status",
|
||||
"bookmarks",
|
||||
"outline",
|
||||
"word-count",
|
||||
"file-recovery"
|
||||
]
|
|
@ -0,0 +1,157 @@
|
|||
{
|
||||
"main": {
|
||||
"id": "3efdf9896e8f8d7d",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "d350c113c8a17ee4",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "20fb08ab7da8ab29",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "outline-border-radius.md",
|
||||
"mode": "source",
|
||||
"source": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "vertical"
|
||||
},
|
||||
"left": {
|
||||
"id": "a90ae722eee9662a",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "a075e922725f738d",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "83e57e3705c02e99",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "file-explorer",
|
||||
"state": {
|
||||
"sortOrder": "alphabetical"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "36a2573a0eb5690e",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "search",
|
||||
"state": {
|
||||
"query": "",
|
||||
"matchingCase": false,
|
||||
"explainSearch": false,
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c7f023012b3bf67f",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "bookmarks",
|
||||
"state": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300
|
||||
},
|
||||
"right": {
|
||||
"id": "1002ff2537d85cb3",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "b973022c94fd3ea2",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "59225b743243f34a",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "backlink",
|
||||
"state": {
|
||||
"file": "outline-border-radius.md",
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical",
|
||||
"showSearch": false,
|
||||
"searchQuery": "",
|
||||
"backlinkCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "7c323d6a34b49662",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outgoing-link",
|
||||
"state": {
|
||||
"file": "outline-border-radius.md",
|
||||
"linksCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "9ab719731ae718a4",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "tag",
|
||||
"state": {
|
||||
"sortOrder": "frequency",
|
||||
"useHierarchy": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "4e304b46b09b10e8",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outline",
|
||||
"state": {
|
||||
"file": "outline-border-radius.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300,
|
||||
"collapsed": true
|
||||
},
|
||||
"left-ribbon": {
|
||||
"hiddenItems": {
|
||||
"switcher:Open quick switcher": false,
|
||||
"graph:Open graph view": false,
|
||||
"canvas:Create new canvas": false,
|
||||
"daily-notes:Open today's daily note": false,
|
||||
"templates:Insert template": false,
|
||||
"command-palette:Open command palette": false
|
||||
}
|
||||
},
|
||||
"active": "20fb08ab7da8ab29",
|
||||
"lastOpenFiles": [
|
||||
"2023-02-19-copy-code-blocks.md",
|
||||
"2022-10-15-frontend-mentor-challenge-deployment.md",
|
||||
"2023-01-21-moving-blog.md",
|
||||
"2023-01-26-site-moved.md",
|
||||
"2019-02-21-first-post.md"
|
||||
]
|
||||
}
|
|
@ -5,6 +5,7 @@ tags:
|
|||
- ios
|
||||
- swift
|
||||
- coding-challenges
|
||||
- dev
|
||||
category: coding-challenges
|
||||
date: 2020-04-20 15:28:00 +01:00
|
||||
---
|
||||
|
|
|
@ -5,6 +5,7 @@ tags:
|
|||
- ios
|
||||
- swift
|
||||
- coding-challenges
|
||||
- dev
|
||||
category: coding-challenges
|
||||
date: 2020-04-20 13:36:00 +01:00
|
||||
---
|
||||
|
|
|
@ -23,7 +23,7 @@ gallery:
|
|||
title: DevOps Roadmap
|
||||
date: 2020-04-21 00:44:00 +01:00
|
||||
---
|
||||
I mentioned in the [goals post]({% post_url collections.posts, "goals" %}), one of the things I am aiming to do is to start learning web development.
|
||||
I mentioned in the [goals post]({% post_url "goals" %}), one of the things I am aiming to do is to start learning web development.
|
||||
|
||||
I was well aware that what falls under the banner of web development is massive, I just hadn't realised quite how large it was.
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
---
|
||||
title: What happens when I finish a Frontend Mentor Challenge (or how I build and deploy an 11ty site)
|
||||
tags: [webdev, site, frontendmentor]
|
||||
tags: [webdev, site, frontendmentor, dev]
|
||||
category: [programming, webdev]
|
||||
date: 2022-10-15
|
||||
last_modified: 2023-02-22
|
||||
---
|
||||
|
||||
I've been doing challenges from [Frontend Mentor](https://frontendmentor.io) as a means to practice frontend web development. Specifically working with plain HTML, CSS and JavaScript.
|
||||
|
@ -11,7 +12,7 @@ Rather than just take the simple route of a Git repo per challenge, I put them a
|
|||
|
||||
The repo is actually a website built with [11ty](https://www.11ty.dev) and [Nunjucks](https://mozilla.github.io/nunjucks/) for templating. The challenges, and other pages I build are in the **projects** directory. They are simply copied over to **www** during the build.
|
||||
|
||||
When I do a `git push all`, on my server it runs a script that does the 11ty build. On Github I use a **Github Action**[^2] which builds the site and then a separate action that deploys the page. Vercel watches the main branch on Github for updates and does a similar build action and deployment.
|
||||
When I do a `git push all`, on my server it runs a `post-receive` script that does the 11ty build. (script below) On Github I use a **Github Action**[^2] which builds the site and then a separate action that deploys the page. Vercel watches the main branch on Github for updates and does a similar build action and deployment.
|
||||
|
||||
That is then deployed to three different places: [Github Pages](https://tarasis.github.io), [Vercel](https://tarasis.vercel.app), and my own site at [rmcg.dev](https://rmcg.dev).
|
||||
|
||||
|
@ -55,7 +56,7 @@ Each section is generated from a json file I created ([src/_data/webprojects.jso
|
|||
When the 11ty build occurs, it takes two nunjucks snippets then first takes the service section and fills out the details:
|
||||
|
||||
{% raw %}
|
||||
```liquid
|
||||
```njk
|
||||
{% for service in webprojects.services %}
|
||||
<div class="projectsDiv {{service.cssClass}}">
|
||||
<div class="alignedHeader">
|
||||
|
@ -139,11 +140,88 @@ Basically I pass in the challenge to the snippet below, and use the contents to
|
|||
```
|
||||
{% endraw %}
|
||||
|
||||
When my Git server receives a push, a `post-receive` hook is called. It checks that the submission is on the `main` branch, and if it is then it installs any needed npm packages, runs `npm run build` and then rsyncs the files over to the web server. The script
|
||||
|
||||
```shell
|
||||
#!/bin/bash
|
||||
|
||||
HOME=/srv/gitea
|
||||
source /srv/gitea/.profile
|
||||
|
||||
echo $PATH
|
||||
|
||||
# add test so that if bundle isn't found end script
|
||||
|
||||
#export PATH="$HOME/.rvm/bin:$PATH"
|
||||
#eval "$(rbenv init -)"
|
||||
|
||||
##############################
|
||||
########## Settings ##########
|
||||
##############################
|
||||
|
||||
# Useful environment variables (gitea):
|
||||
# GITEA_REPO_NAME Repository name
|
||||
# GITEA_REPO_USER_NAME Repo owner username
|
||||
# GITEA_PUSHER_NAME The username that pushed the commits
|
||||
# GITEA_AUTHOR Same as above
|
||||
# GIT_WORKING_TREE location to checkout the current tree
|
||||
|
||||
# GIT_HOST Domain name the repo is hosted on. Default: git.starbeamrainbowlabs.com
|
||||
|
||||
if [ "${GIT_HOST}" == "" ]; then
|
||||
GIT_HOST="git.tarasis.net";
|
||||
fi
|
||||
|
||||
# The url of the repository in question. SSH is recommended, as then you can use a deploy key.
|
||||
# SSH:
|
||||
GIT_REPO_URL="git@${GIT_HOST}:${GITEA_REPO_USER_NAME}/${GITEA_REPO_NAME}.git";
|
||||
# HTTPS:
|
||||
# git_repo_url="https://git.starbeamrainbowlabs.com/${GITEA_REPO_USER_NAME}/${GITEA_REPO_NAME}.git";
|
||||
|
||||
# The user that pushed the commits
|
||||
GIT_AUTHOR="${GITEA_PUSHER_NAME}";
|
||||
|
||||
# The working tree folder
|
||||
GIT_WORKING_TREE="/srv/deploy/${GITEA_REPO_NAME}"
|
||||
|
||||
##############################
|
||||
|
||||
###### Internal Settings ######
|
||||
|
||||
while read oldref newref ref
|
||||
do
|
||||
if [[ $ref =~ .*/main$ ]];
|
||||
then
|
||||
echo "Main ref received. Deploying main branch to production..."
|
||||
echo "Creating ${GIT_WORKING_TREE} if needed"
|
||||
mkdir -p ${GIT_WORKING_TREE}
|
||||
echo "Checking out repo to working tree folder"
|
||||
git --work-tree=${GIT_WORKING_TREE} --git-dir="/srv/gitea/data/gitea-repositories/${GIT_AUTHOR}/${GITEA_REPO_NAME}.git" checkout -f
|
||||
echo "cd to ${GIT_WORKING_TREE}"
|
||||
cd ${GIT_WORKING_TREE}
|
||||
echo "install npm modules"
|
||||
npm install
|
||||
echo "clean old build files"
|
||||
rm -rf www
|
||||
echo "build files"
|
||||
npm run build
|
||||
echo "rsyncing files to webserver"
|
||||
rsync -azv ${GIT_WORKING_TREE}/www/ <REMOVED>@ams.tarasis.net:/srv/http/rmcg.dev --delete-after
|
||||
echo "Completed"
|
||||
else
|
||||
echo "Ref $ref successfully received. Doing nothing: only the main branch may be deployed on this server."
|
||||
fi
|
||||
done
|
||||
|
||||
```
|
||||
|
||||
The finished build is then copied and published.
|
||||
|
||||
## Improvements ...
|
||||
|
||||
There are improvements I will make at some point, specifically adding optimisation of the images I use. There is little point is downloading a 1440x800px image for the challenge preview when only a small portion of it is shown. I am aware that during the build process you can have 11ty generate more web friendly images at differing sizes.
|
||||
~~There are improvements I will make at some point, specifically adding optimisation of the images I use. There is little point is downloading a 1440x800px image for the challenge preview when only a small portion of it is shown. I am aware that during the build process you can have 11ty generate more web friendly images at differing sizes.~~
|
||||
|
||||
I now do this, but haven't pushed it live yet. I'm waiting till I have completed another Junior level project.
|
||||
|
||||
## Final thoughts
|
||||
|
||||
|
@ -161,7 +239,7 @@ Having a static site where I can quickly add a challenge to the front page witho
|
|||
|
||||
My Github 11ty build action
|
||||
|
||||
```
|
||||
```yaml
|
||||
name: Build Eleventy
|
||||
|
||||
on:
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
title: Site transitioned from Jekyll to 11ty
|
||||
tags: [webdev, site, personal]
|
||||
category: [site, webdev, personal]
|
||||
date: 2023-01-26
|
||||
last_modified: 2023-01-26
|
||||
---
|
||||
|
||||
After numerous hours / days of hacking (much more than I care to admit), I have moved the blog from [Jekyll](https://jekyllrb.com) to the static site generator [11ty](https://www.11ty.dev/). All the while maintaining using the Jekyll theme [Minimal Mistakes Theme](https://mmistakes.github.io/minimal-mistakes/).
|
||||
|
||||
TIP: Unless you are wed to the theme, don't try porting it. Really. Headaches await. 🤯
|
||||
{: .notice--warning}
|
||||
|
||||
There was a surprising amount of hacking needed to get the `liquid` calls to work properly. The main issue is that main of the required variables don't contain the data expected by `Minimal Mistakes`. So this mean debugging to find what is where. For instance the `post` variable doesn't contain a posts `title`, that is usually a global variable. (Same with many others) HOWEVER, there are instances where the `title` might actually be in a passed in variable at `include.aPost.data.title`, or `post.data.title` for the RSS feed file.
|
||||
|
||||
Not everything is in place; specifically site search is currently disabled as is the archive of posts by year, and finally comments aren't working though in theory they should (they had also stopped working on the Jekyll version of the site). All will be returned at some point.
|
||||
|
||||
All that said, despite the change to keep the theme ... I will ultimately be moving away from it. While I like it, I can't easily do some of the things I'd like to do and going my own way will be a way to hopefully show my improving HTML/CSS/JS skills.
|
||||
|
||||
Also todo: add mappings from old urls to new. Most are the same, but a handful have been fixed to remove a space.
|
||||
|
||||
Before I finish I should give some thanks / hat tips so various blog posts / git hub repos for bits I've cribbed.
|
||||
|
||||
First and foremost is a blog / repo by [Quinn Dombrowski](https://quinndombrowski.com/blog/2022/02/10/eleventy-and-me/). Her post was what led me to realise that I could actually get `Minimal Mistakes theme` running on 11ty. I spent a little time with her blog [repo](https://github.com/quinnanya/quinnanya.github.io), initially mostly going through the steps to get it to build with 11ty 1.0.x or 2.0.0beta1.
|
||||
|
||||
Once I got it building, I started trying to port `Minimal Mistakes` myself, only cribbing bits out of `.eleventy.js`. I figured the best way to understand how it was to work with `11ty` was to do the changes to the templates myself. Although in the end I did wholesale rip Quinn's code for pagination on the front page because I couldn't get the original `paginatior` code to work.
|
||||
|
||||
Also of use was in no particular order:
|
||||
* [Xavier's post on migrating Jekyll to Eleventy](https://savjee.be/blog/migrating-this-blog-from-jekyll-to-eleventy/). There are still some ideas I'd like to use from the post and use Xavier's method for loading the disqus comments. Also the section on "Check visual differences" was great.
|
||||
* [ Jérôme's post "From Jekyll to Eleventy"](https://www.webstoemp.com/blog/from-jekyll-to-eleventy/). While I didn't use anything Jérôme mentions, it was helpful to see someone else had gone through the process of moving.
|
||||
* [Ian's similar post "Moving this blog from Jekyll to 11ty"](https://www.ianfeather.co.uk/moving-this-blog-from-jekyll-to-11ty/). Again was just useful reading.
|
||||
* [Kitty's post "From Jekyll to 11ty"](https://kittygiraudel.com/2020/11/30/from-jekyll-to-11ty/). Some useful bits to be found. I sort of used Kitty's permalink, although I used `"permalink": "/{{ page.date | date: 'Y/M/D' }}/{{ page.fileSlug }}/"`. Also made use of `group_by` from `.eleventy.js` from Kitty's [Github repo](https://github.com/KittyGiraudel/site/blob/main/.eleventy.js)
|
||||
* [Rob's post on "Compile Sass with eleventy](https://blog.r0b.io/post/compile-sass-with-eleventy/). While I didn't end up using this technique, I did initially start with it. The reason I didn't was that I wanted to have the initial `main.scss` file be a `liquid` markup file, so my choice of theme was backed in before compiling. So what happens now is I build the 11ty site first, then build sass, compiling the sass files after baking.
|
||||
* [Dennis also had an article on "Compile Sass with Eleventy"](https://www.hendricks.rocks/compile-sass-with-eleventy/).
|
||||
* [Michael's "eleventy-filter-relative-url" plugin](https://www.npmjs.com/package/eleventy-filter-relative-url?activeTab=explore). I ended up putting the code for it directly into my `.eleventy.js` because the package kept causing issues with `npm install`. It required using `--legacy-peer-deps` every time I wanted to install a new package. I ended up altering the code a little too. Ultimately I moved away from the `relative_url` behaviour, function works like an absolute url.
|
||||
|
||||
I'm sure there might be something else I'm forgetting, and I will update the post if I remember.
|
|
@ -0,0 +1,157 @@
|
|||
---
|
||||
title: Copy Code Blocks
|
||||
tags: [webdev, site, dev]
|
||||
category: [site, webdev]
|
||||
date: 2023-02-19
|
||||
---
|
||||
|
||||
I've been reading [Bryce Wray's](https://www.brycewray.com) site for a little while. Its been interesting watching Bryce switch back and forth between [Hugo](https://github.com/brycewray/hugo_site) and [Eleventy / 11ty](https://github.com/brycewray/eleventy_site). Keeping both in sync feature wise.
|
||||
|
||||
Just recently Bryce did a post about adding [Code for Copying Code](https://www.brycewray.com/posts/2023/02/code-copying-code-eleventy-edition/) in Eleventy. Essentially for any code block, it adds an icon (or you could switch it out for text) inside a code block that you can click and it will copy the code to your clipboard.
|
||||
|
||||
On my site, the icon looks like this[^1]:
|
||||
|
||||
![Screenshot of the top right corned of a code block. There is a small grey button with two overlapping squares shown](/assets/images/posts/copy-code-block.png)
|
||||
|
||||
I am using Bryce's JavaScript virtually verbatim. The only difference is that I hook into the `load` event. Source from [copy-code-button.js](https://github.com/tarasis/tarasis.net/blob/main/src/assets/js/copy-code-button.js), with a minor change to the colour of the `svgCheck`.
|
||||
|
||||
```js
|
||||
/*
|
||||
h/t to...
|
||||
- https://www.brycewray.com/posts/2023/02/code-copying-code-eleventy-edition/ & Bryce's gitrepo
|
||||
- https://github.com/brycewray/eleventy_site/blob/main/src/assets/js/copy-code-button.js
|
||||
- https://www.dannyguo.com/blog/how-to-add-copy-to-clipboard-buttons-to-code-blocks-in-hugo/
|
||||
- https://aaronluna.dev/blog/add-copy-button-to-code-blocks-hugo-chroma/
|
||||
- https://simplernerd.com/hugo-add-copy-to-clipboard-button/
|
||||
- https://digitaldrummerj.me/hugo-add-copy-code-snippet-button/
|
||||
*/
|
||||
|
||||
const svgCopy =
|
||||
'<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg><span class="sr-only"> Click to copy code</span>';
|
||||
const svgCheck =
|
||||
'<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true"><path fill-rule="evenodd" fill="rgb(25, 0, 255)" d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"></path></svg><span class="sr-only"> Copied!</span>';
|
||||
|
||||
const addCopyButtons = (clipboard) => {
|
||||
// 1. Look for pre > code elements in the DOM
|
||||
document.querySelectorAll("pre > code").forEach((codeBlock) => {
|
||||
// 2. Create a button that will trigger a copy operation
|
||||
const button = document.createElement("button");
|
||||
button.className = "clipboard-button";
|
||||
button.type = "button";
|
||||
button.innerHTML = svgCopy;
|
||||
button.addEventListener("click", () => {
|
||||
clipboard.writeText(codeBlock.innerText).then(
|
||||
() => {
|
||||
button.blur();
|
||||
button.innerHTML = svgCheck;
|
||||
setTimeout(() => (button.innerHTML = svgCopy), 2000);
|
||||
},
|
||||
(error) => (button.innerHTML = "Error")
|
||||
);
|
||||
});
|
||||
// 3. Append the button directly before the pre tag
|
||||
// (with content-searching fix in place for Prism)
|
||||
const pre = codeBlock.parentNode;
|
||||
pre.parentNode.insertBefore(button, pre);
|
||||
});
|
||||
};
|
||||
|
||||
function addCopyButtonsAfterLoad() {
|
||||
if (navigator && navigator.clipboard) {
|
||||
addCopyButtons(navigator.clipboard);
|
||||
} else {
|
||||
const script = document.createElement("script");
|
||||
script.src =
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/clipboard-polyfill/2.7.0/clipboard-polyfill.promise.js";
|
||||
script.integrity =
|
||||
"sha256-waClS2re9NUbXRsryKoof+F9qc1gjjIhc2eT7ZbIv94=";
|
||||
script.crossOrigin = "anonymous";
|
||||
script.onload = () => addCopyButtons(clipboard);
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("load", addCopyButtonsAfterLoad);
|
||||
```
|
||||
|
||||
As this site is a franken-baby, an 11ty site using the Minimal Mistakes theme, I needed to use `liquid` rather than `njk` code in my page layout file. So in [single.html](https://github.com/tarasis/tarasis.net/blob/main/src/_includes/layouts/single.html) I removed {% raw %}`{{content}}`{% endraw %} and replaced it with:
|
||||
|
||||
{% raw %}
|
||||
```liquid
|
||||
{% assign Content = content %}
|
||||
{% assign withoutDivStart = '<pre ' %}
|
||||
{% assign withDivStart = '<div class="highlight codeblock"><pre ' %}
|
||||
{% assign withoutDivEnd = '</code></pre>' %}
|
||||
{% assign withDivEnd = '</code></pre></div>' %}
|
||||
|
||||
{% if content contains withoutDivStart %}
|
||||
{% assign Content = content | replace: withoutDivStart, withDivStart %}
|
||||
{% assign Content = Content | replace: withoutDivEnd, withDivEnd %}
|
||||
{% endif %}
|
||||
|
||||
{{ Content }}
|
||||
```
|
||||
{% endraw %}
|
||||
|
||||
This wraps a `div` around the `pre + code` block that is output by the syntax highlighting plugin (I'm currently using a [Chroma plugin](https://github.com/tarasis/eleventy-plugin-syntaxhighlighting-chroma) I wrote, more in a later post). This is so we can set `position: relative` on the `div`, and `position:absolute` on the button.
|
||||
|
||||
The last part is the CSS, for me I'm currently putting the changes in [main.scss.liquid](https://github.com/tarasis/tarasis.net/blob/main/src/assets/css/main.scss.liquid). There are only minor color differences from Bryce's code. It also works with code blocks that have line numbers, but only when embedded in a table otherwise it copies the line numbers too. (For instance below is done using `css/lineNumbers/table` [^2])
|
||||
|
||||
```css/lineNumbers/table
|
||||
div.highlight {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
// === for copy-code-to-clipboard
|
||||
|
||||
// h/t https://simplernerd.com/hugo-add-copy-to-clipboard-button/
|
||||
|
||||
.clipboard-button {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
padding: 6px 8px 4px 8px;
|
||||
margin: 5px;
|
||||
// color: gray-500;
|
||||
// border-color: gray-500;
|
||||
// background-color: gray-100;
|
||||
border: 1px solid;
|
||||
border-radius: 6px;
|
||||
font-size: 0.8em;
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
transition: 0.1s;
|
||||
}
|
||||
.clipboard-button > svg {
|
||||
// fill: gray-500;
|
||||
}
|
||||
.clipboard-button:hover {
|
||||
cursor: pointer;
|
||||
border-color: #01b139;
|
||||
background-color: #87d09e;
|
||||
opacity: 1;
|
||||
}
|
||||
.clipboard-button:hover > svg {
|
||||
fill: #1900ff;
|
||||
}
|
||||
.clipboard-button:focus {
|
||||
outline: 0;
|
||||
}
|
||||
// .highlight {
|
||||
// position: relative;
|
||||
// }
|
||||
.highlight:hover > .clipboard-button {
|
||||
opacity: 1;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
line-height: 1.5;
|
||||
}
|
||||
```
|
||||
|
||||
And thats it, the site now supports copying the code from the blocks at the press of a button.
|
||||
|
||||
[^1]: I'm well aware that the following CSS should be out in its own file. Aside, the reason this file has a `liquid` file extension is that during the site build the theme is baked into it from a variable in [site.json](https://github.com/tarasis/tarasis.net/blob/main/src/_data/site.json). I then use a separate step to sass build the compiled file.
|
||||
|
||||
[^2]: I had to do a hack to great rid of additional space that was being added despite `margin`, `padding` and anything else being set to `0`. I have tried various combinations. In theory nothing is adding `padding` or `margin` to the `table`, `tbody`, `tr`, or `td`; indeed they are explicitly set to 0. And yet there was still a chunk of spacing being added. I discovered that setting portions to `display:` `flex` & `inline-flex` solved the issue for Safari / Chrome, and `flex` and `ruby-base` for Firefox. All are hacks, but they provide the results that I want. (I was up to 4am trying to find that behaviours I wanted; not helped by Firefox not yet supporting `:has()` 🙈)
|
|
@ -0,0 +1,72 @@
|
|||
---
|
||||
title: An Needless Exercise in frustration Eleventy 2.x -> 3.0
|
||||
description: ""
|
||||
date: 2024-10-18T13:13:03.320Z
|
||||
preview: ""
|
||||
tags: [site, personal]
|
||||
category: [site, personal]
|
||||
excerpt: A frustrating update to 11ty / Eleventy 3.0 release, esp when I plan to switch to Astro
|
||||
layout: single
|
||||
---
|
||||
|
||||
For reasons of sanity, or insanity really, I figured I might as well update the mechanics of the blog From Eleventy 2.x to the fresh and shiny new 3.0. I figured it couldn't hurt too much surely.
|
||||
|
||||
Boy was I wrong!
|
||||
|
||||
And unfortunately, I didn't take extensive notes as I went along. One of the issues stemmed from having not swapped to `pathPrefix` when v2 came along. Instead I had been using a 3 year old npm eleventy plugin to get relative urls.
|
||||
|
||||
It was loading this particular filter: `const urlFilter = require("@11ty/eleventy/src/Filters/Url");`
|
||||
|
||||
But this caused the build to fail
|
||||
|
||||
```autodetect
|
||||
[11ty] File changed: .eleventy.js
|
||||
[11ty] Eleventy watch error:
|
||||
[11ty] 1. Error in your Eleventy config file '.eleventy.js'. (via EleventyConfigError)
|
||||
[11ty] 2. Package subpath './src/Filters/Url' is not defined by "exports" in /Users/tarasis/Programming/Websites/tarasis.net/node_modules/@11ty/eleventy/package.json imported from /Users/tarasis/Programming/Websites/tarasis.net/.eleventy.js
|
||||
[11ty]
|
||||
[11ty] Original error stack trace: Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './src/Filters/Url' is not defined by "exports" in /Users/tarasis/Programming/Websites/tarasis.net/node_modules/@11ty/eleventy/package.json imported from /Users/tarasis/Programming/Websites/tarasis.net/.eleventy.js
|
||||
[11ty] at exportsNotFound (node:internal/modules/esm/resolve:296:10)
|
||||
[11ty] at packageExportsResolve (node:internal/modules/esm/resolve:643:9)
|
||||
[11ty] at packageResolve (node:internal/modules/esm/resolve:822:14)
|
||||
[11ty] at moduleResolve (node:internal/modules/esm/resolve:908:18)
|
||||
[11ty] at defaultResolve (node:internal/modules/esm/resolve:1038:11)
|
||||
[11ty] at nextResolve (node:internal/modules/esm/hooks:748:28)
|
||||
[11ty] at resolve (file:///Users/tarasis/Programming/Websites/tarasis.net/node_modules/@11ty/eleventy/src/Util/EsmResolver.js:25:11)
|
||||
[11ty] at nextResolve (node:internal/modules/esm/hooks:748:28)
|
||||
[11ty] at Hooks.resolve (node:internal/modules/esm/hooks:240:30)
|
||||
[11ty] at handleMessage (node:internal/modules/esm/worker:199:24)
|
||||
```
|
||||
|
||||
It was fine, I had already taken the code from the plugin and had put it into my `.eleventy.js`, so I figured I could swap it over to the new `await import` for @11ty stuff. Quick modified `module.exports = async function (eleventyConfig) {}` and moved `const urlFilter = await import("@11ty/eleventy/src/Filters/Url");` into that async function.
|
||||
|
||||
Tried again and got the same error.
|
||||
|
||||
Googling on the `require("@11ty/eleventy/src/Filters/Url");` brings up precisely 2 posts. Which was pretty impressive. One from 2020, the other from 2021. So nothing useful. You could previously use it, and now you couldn't ([it (Url.js)](https://github.com/11ty/eleventy/blob/main/src/Filters/Url.js) is still there, exported by [defaultConfig.js](https://github.com/11ty/eleventy/blob/main/src/defaultConfig.js))
|
||||
|
||||
![Image showing precisely 2 hits](/assets/images/posts/2hits.png)
|
||||
|
||||
So this lead me back around to looking at 11ty and URLS, looking at how other people had done relative urls with 11ty and a bunch of debugging and seeing what URLS where being generated, I then discovered `pathPrefix` and the HTML base plugin 😡
|
||||
|
||||
So in `.eleventy.js` I now return the URL right back out
|
||||
|
||||
```js
|
||||
// Cheat till I can go through
|
||||
function relativeURLALT2(url) {
|
||||
if (!url.startsWith("/")) {
|
||||
throw new Error("URL is already relative");
|
||||
}
|
||||
return url;
|
||||
}
|
||||
```
|
||||
|
||||
Why you may wonder, because I don't overly fancy find and replacing `| relative_url }}` 52 times. Especially as I was planning on starting from scratch with Astro.js and having a go at making something more whimsical.
|
||||
|
||||
But once I start the upgrade process, I needed to get it working. Plus side it means that I've managed to fix/hack it to that the "correct"[^1] time while now show (with added caveat that it assumes reading at 200 words a minute), and have excerpts generated for older posts that don't have them in the front matter (aka most of them)
|
||||
|
||||
![Image of a post on the front page, below the title of is the date it was published, and a small clock icon and a number of minutes](/assets/images/posts/post-date-and-reading-time.png)
|
||||
|
||||
I have one issue that remains but I'm not going to fret about it for the moment, the way I have sass set up sometimes means a needed file hasn't been copied over when the sass compile happens, which breaks the compile and no CSS. I will deal with it, but for now I'd rather put the effort into v5 of the much under used TDN Site/Blog
|
||||
|
||||
[^1]: Its fair to say there is an inaccuracy between the index page and what you'll see on the post itself. Its usually off by a minute, with no real guarantee which is more accurate. The data being passed into page_meta.html is different if it comes from a post layout, vs what it gets at the time the pagination is generated. I had it turned off on the index pages because it was say 1 min for all articles.
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
title: Quick Life / Site update
|
||||
description: ""
|
||||
date: 2024-11-04T20:06:03.320Z
|
||||
preview: ""
|
||||
tags: [site, personal]
|
||||
category: [site, personal]
|
||||
# excerpt:
|
||||
layout: single
|
||||
---
|
||||
|
||||
Grateful to have lived another week so I got to hear https://youtube.com/watch?v=52u5Rmti9N8 mashup of Iron Man 3 + Robocop 1987.
|
||||
|
||||
Fretting over 11ty / Astro rather than just writing or designing.
|
||||
|
||||
Procrastination at its finest.
|
||||
|
||||
Quite taken with this post about [personal website vs Portfolio](https://blog.reimenayee.com/a-personal-website-vs-a-portfolio/) by Reimena Yee. Been trying to work out myself what I was going to do in terms of the rebuild of my site. And was strongly considering pulling various bits back together under the one site. I already know that I want to have blog posts, and then shorter notes that don't really need a page of their own. Also know I want to integrate a "now" page, more info at [NowNowNow](https://nownownow.com/about)
|
||||
|
||||
Health, both physical and mental, has been hell over that last 4-5 days. Thur & Fri I drank WAY to much (700ml rum), the result certainly on friday was that I ended up having to be helped home (just). Whatever I did before they found me (mum & uncle), I scrapped my leg and a knuckle and did something to my back and buttock; which may be why my right foot is tingling and keeps cramping.
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
title: Border Radius on an Outline in CSS
|
||||
tags: [webdev, css, dev]
|
||||
category: [css, webdev]
|
||||
date: 2023-02-20
|
||||
excerpt: A little note about using border-radius with an outline.
|
||||
layout: single
|
||||
---
|
||||
|
||||
Yesterday I was fiddling with the site adding a button to [copy code blocks]({% post_url "copy-code-blocks" %}) to the clipboard. While I was doing that I figured I'd also add a border to both proper code blocks and to inline code blocks. Something to make them stand out a little more.
|
||||
|
||||
Initially I considered using an `outline`, rather than `border`. So that it didn't take up pixels as the site uses `box-sizing: border-box`. However, I discovered that `outline` doesn't allow you to control the radius. There was no `outline-radius` like there is `border-radius`.
|
||||
|
||||
After a bit of searching I came across [this](https://stackoverflow.com/questions/5394116/outline-radius) post, which has some solutions and workarounds. Someone noted that there was a Firefox only option: `-moz-outline-radius`. A later [answer](https://stackoverflow.com/a/66661654) noted that as of April 2021 in Firefox you could use `border-radius` but it was the only browser that supported it.
|
||||
|
||||
So I dutifully tried it out, and yes it did work.
|
||||
|
||||
![Screenshot of an outline around some code, with a border-radius applied](/assets/images/posts/outline-with-border-radius.png)
|
||||
|
||||
But then I noticed that it was also working in Safari Technology Preview Release 163! Unfortunately Safari 16.3 (Desktop) doesn't support it.
|
||||
|
||||
One possible bug I noticed while experimenting was that Safari TP would ignore the outline style `dashed` if `border-radius` was set. Safari TP would instead make the outline solid.
|
||||
|
||||
Codepen with example:
|
||||
|
||||
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="bGxpBdz" data-user="tarasis" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
|
||||
<span>See the Pen <a href="https://codepen.io/tarasis/pen/bGxpBdz">
|
||||
Untitled</a> by Robert McGovern (<a href="https://codepen.io/tarasis">@tarasis</a>)
|
||||
on <a href="https://codepen.io">CodePen</a>.</span>
|
||||
</p>
|
||||
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
|
||||
|
||||
The results, as you can see below, are rather different between Safari TP v163, Chrome 110.0.5481.100, and Firefox Developer Edition 111.0b3 (64-bit)
|
||||
|
||||
**Safari TP v163**
|
||||
|
||||
![Image of codepen output in Safari. Of note the second line of text is supposed to be Outlined with border radius, however only the top left and bottom right have curves. The line below should be the same but with dashed line around the text, however the line is solid.](/assets/images/posts/safaritp-codepen.png)
|
||||
|
||||
**Chrome 110.0.5481**
|
||||
|
||||
![Image of codepen output in Chrome. Of note the second line is properly curved. The line below is correctly dashed.](/assets/images/posts/chrome-codepen.png)
|
||||
|
||||
**Firefox Developer Edition 111.0b3**
|
||||
|
||||
![Image of codepen output in Firefox. Of note the second line is outlined with the correct border radius at the beginning and end, however the wrapped text onto the second line has a solid border to its left.](/assets/images/posts/firefox-codepen.png)
|
|
@ -17,7 +17,7 @@ form {
|
|||
display: block;
|
||||
width: 100%;
|
||||
margin-bottom: 5px * 2;
|
||||
*margin-left: -7px;
|
||||
margin-left: -7px;
|
||||
padding: 0;
|
||||
color: $text-color;
|
||||
border: 0;
|
||||
|
@ -25,7 +25,7 @@ form {
|
|||
}
|
||||
|
||||
p {
|
||||
margin-bottom: (5px / 2);
|
||||
margin-bottom: calc(5px / 2);
|
||||
}
|
||||
|
||||
ul {
|
||||
|
@ -45,7 +45,7 @@ button,
|
|||
select,
|
||||
textarea {
|
||||
vertical-align: baseline;
|
||||
*vertical-align: middle;
|
||||
/*vertical-align: middle; */
|
||||
}
|
||||
|
||||
input,
|
||||
|
@ -102,7 +102,7 @@ input[type="radio"] {
|
|||
height: auto;
|
||||
padding: 0;
|
||||
margin: 3px 0;
|
||||
*margin-top: 0;
|
||||
/* *margin-top: 0; */
|
||||
line-height: normal;
|
||||
cursor: pointer;
|
||||
border-radius: 0;
|
||||
|
@ -114,8 +114,8 @@ input[type="checkbox"],
|
|||
input[type="radio"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
*width: 13px;
|
||||
*height: 13px;
|
||||
/* *width: 13px; */
|
||||
/* *height: 13px; */
|
||||
}
|
||||
|
||||
input[type="image"] {
|
||||
|
@ -138,12 +138,12 @@ input[type="submit"] {
|
|||
width: auto;
|
||||
height: auto;
|
||||
cursor: pointer;
|
||||
*overflow: visible;
|
||||
/* *overflow: visible; */
|
||||
}
|
||||
|
||||
select,
|
||||
input[type="file"] {
|
||||
*margin-top: 4px;
|
||||
/* *margin-top: 4px; */
|
||||
}
|
||||
|
||||
select {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
========================================================================== */
|
||||
|
||||
@function em($target, $context: $doc-font-size) {
|
||||
@return ($target / $context) * 1em;
|
||||
@return (calc($target / $context)) * 1em;
|
||||
}
|
||||
|
||||
|
||||
|
@ -65,7 +65,7 @@
|
|||
$green: green($color);
|
||||
$blue: blue($color);
|
||||
|
||||
$yiq: (($red*299)+($green*587)+($blue*114))/1000;
|
||||
$yiq: calc((($red * 299) + ($green * 587) + ($blue * 114)) / 1000);
|
||||
|
||||
@if $yiq-debug { @debug $yiq, $threshold; }
|
||||
|
||||
|
|
|
@ -64,8 +64,8 @@ audio,
|
|||
canvas,
|
||||
video {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
*zoom: 1;
|
||||
/* display: inline; */
|
||||
/* zoom: 1; */
|
||||
}
|
||||
|
||||
/* Prevents modern browsers from displaying 'audio' without controls */
|
||||
|
@ -142,7 +142,7 @@ textarea {
|
|||
|
||||
button,
|
||||
input {
|
||||
*overflow: visible; /* inner spacing ie IE6/7*/
|
||||
/* *overflow: visible; */ /* inner spacing ie IE6/7*/
|
||||
line-height: normal; /* FF3/4 have !important on line-height in UA stylesheet*/
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
//////////////////////////////
|
||||
// Default Variables
|
||||
//////////////////////////////
|
||||
$Breakpoint-Settings: (
|
||||
'default media': all,
|
||||
'default feature': min-width,
|
||||
'default pair': width,
|
||||
|
||||
'force all media type': false,
|
||||
'to ems': false,
|
||||
'transform resolutions': true,
|
||||
|
||||
'no queries': false,
|
||||
'no query fallbacks': false,
|
||||
|
||||
'base font size': 16px,
|
||||
|
||||
'legacy syntax': false
|
||||
);
|
||||
|
||||
$breakpoint: () !default;
|
||||
|
||||
//////////////////////////////
|
||||
// Imports
|
||||
//////////////////////////////
|
||||
@import "settings";
|
||||
@import "context";
|
||||
@import "helpers";
|
||||
@import "parsers";
|
||||
@import "no-query";
|
||||
|
||||
@import "respond-to";
|
||||
|
||||
@import "legacy-settings";
|
||||
|
||||
//////////////////////////////
|
||||
// Breakpoint Mixin
|
||||
//////////////////////////////
|
||||
|
||||
@mixin breakpoint($query, $no-query: false) {
|
||||
@include legacy-settings-warning;
|
||||
|
||||
// Reset contexts
|
||||
@include private-breakpoint-reset-contexts();
|
||||
|
||||
$breakpoint: breakpoint($query, false);
|
||||
|
||||
$query-string: map-get($breakpoint, 'query');
|
||||
$query-fallback: map-get($breakpoint, 'fallback');
|
||||
|
||||
$private-breakpoint-context-holder: map-get($breakpoint, 'context holder') !global;
|
||||
$private-breakpoint-query-count: map-get($breakpoint, 'query count') !global;
|
||||
|
||||
// Allow for an as-needed override or usage of no query fallback.
|
||||
@if $no-query != false {
|
||||
$query-fallback: $no-query;
|
||||
}
|
||||
|
||||
@if $query-fallback != false {
|
||||
$context-setter: private-breakpoint-set-context('no-query', $query-fallback);
|
||||
}
|
||||
|
||||
// Print Out Query String
|
||||
@if not breakpoint-get('no queries') {
|
||||
@media #{$query-string} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@if breakpoint-get('no query fallbacks') != false or breakpoint-get('no queries') == true {
|
||||
|
||||
$type: type-of(breakpoint-get('no query fallbacks'));
|
||||
$print: false;
|
||||
|
||||
@if ($type == 'bool') {
|
||||
$print: true;
|
||||
}
|
||||
@else if ($type == 'string') {
|
||||
@if $query-fallback == breakpoint-get('no query fallbacks') {
|
||||
$print: true;
|
||||
}
|
||||
}
|
||||
@else if ($type == 'list') {
|
||||
@each $wrapper in breakpoint-get('no query fallbacks') {
|
||||
@if $query-fallback == $wrapper {
|
||||
$print: true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write Fallback
|
||||
@if ($query-fallback != false) and ($print == true) {
|
||||
$type-fallback: type-of($query-fallback);
|
||||
|
||||
@if ($type-fallback != 'bool') {
|
||||
#{$query-fallback} & {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
@else {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include private-breakpoint-reset-contexts();
|
||||
}
|
||||
|
||||
|
||||
@mixin mq($query, $no-query: false) {
|
||||
@include breakpoint($query, $no-query) {
|
||||
@content;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
//////////////////////////////
|
||||
// Private Breakpoint Variables
|
||||
//////////////////////////////
|
||||
$private-breakpoint-context-holder: ();
|
||||
$private-breakpoint-query-count: 0 !default;
|
||||
|
||||
//////////////////////////////
|
||||
// Breakpoint Has Context
|
||||
// Returns whether or not you are inside a Breakpoint query
|
||||
//////////////////////////////
|
||||
@function breakpoint-has-context() {
|
||||
@if length($private-breakpoint-query-count) {
|
||||
@return true;
|
||||
}
|
||||
@else {
|
||||
@return false;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// Breakpoint Get Context
|
||||
// $feature: Input feature to get it's current MQ context. Returns false if no context
|
||||
//////////////////////////////
|
||||
@function breakpoint-get-context($feature) {
|
||||
@if map-has-key($private-breakpoint-context-holder, $feature) {
|
||||
$get: map-get($private-breakpoint-context-holder, $feature);
|
||||
// Special handling of no-query from get side so /false/ prepends aren't returned
|
||||
@if $feature == 'no-query' {
|
||||
@if type-of($get) == 'list' and length($get) > 1 and nth($get, 1) == false {
|
||||
$get: nth($get, length($get));
|
||||
}
|
||||
}
|
||||
@return $get;
|
||||
}
|
||||
@else {
|
||||
@if breakpoint-has-context() and $feature == 'media' {
|
||||
@return breakpoint-get('default media');
|
||||
}
|
||||
@else {
|
||||
@return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// Private function to set context
|
||||
//////////////////////////////
|
||||
@function private-breakpoint-set-context($feature, $value) {
|
||||
@if $value == 'monochrome' {
|
||||
$feature: 'monochrome';
|
||||
}
|
||||
|
||||
$current: map-get($private-breakpoint-context-holder, $feature);
|
||||
@if $current and length($current) == $private-breakpoint-query-count {
|
||||
@warn "You have already queried against `#{$feature}`. Unexpected things may happen if you query against the same feature more than once in the same `and` query. Breakpoint is overwriting the current context with `#{$value}`";
|
||||
}
|
||||
|
||||
@if not map-has-key($private-breakpoint-context-holder, $feature) {
|
||||
$v-holder: ();
|
||||
@for $i from 1 to $private-breakpoint-query-count {
|
||||
@if $feature == 'media' {
|
||||
$v-holder: append($v-holder, breakpoint-get('default media'));
|
||||
}
|
||||
@else {
|
||||
$v-holder: append($v-holder, false);
|
||||
}
|
||||
}
|
||||
$v-holder: append($v-holder, $value);
|
||||
$private-breakpoint-context-holder: map-merge($private-breakpoint-context-holder, ($feature: $v-holder)) !global;
|
||||
}
|
||||
@else {
|
||||
$v-holder: map-get($private-breakpoint-context-holder, $feature);
|
||||
$length: length($v-holder);
|
||||
@for $i from $length to $private-breakpoint-query-count - 1 {
|
||||
@if $feature == 'media' {
|
||||
$v-holder: append($v-holder, breakpoint-get('default media'));
|
||||
}
|
||||
@else {
|
||||
$v-holder: append($v-holder, false);
|
||||
}
|
||||
}
|
||||
$v-holder: append($v-holder, $value);
|
||||
$private-breakpoint-context-holder: map-merge($private-breakpoint-context-holder, ($feature: $v-holder)) !global;
|
||||
}
|
||||
|
||||
@return true;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// Private function to reset context
|
||||
//////////////////////////////
|
||||
@mixin private-breakpoint-reset-contexts {
|
||||
$private-breakpoint-context-holder: () !global;
|
||||
$private-breakpoint-query-count: 0 !global;
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
//////////////////////////////
|
||||
// Converts the input value to Base EMs
|
||||
//////////////////////////////
|
||||
@function breakpoint-to-base-em($value) {
|
||||
$value-unit: unit($value);
|
||||
|
||||
// Will convert relative EMs into root EMs.
|
||||
@if breakpoint-get('base font size') and type-of(breakpoint-get('base font size')) == 'number' and $value-unit == 'em' {
|
||||
$base-unit: unit(breakpoint-get('base font size'));
|
||||
|
||||
@if $base-unit == 'px' or $base-unit == '%' or $base-unit == 'em' or $base-unit == 'pt' {
|
||||
@return base-conversion($value) / base-conversion(breakpoint-get('base font size')) * 1em;
|
||||
}
|
||||
@else {
|
||||
@warn '#{breakpoint-get(\'base font size\')} is not set in valid units for font size!';
|
||||
@return false;
|
||||
}
|
||||
}
|
||||
@else {
|
||||
@return base-conversion($value);
|
||||
}
|
||||
}
|
||||
|
||||
@function base-conversion($value) {
|
||||
$unit: unit($value);
|
||||
|
||||
@if $unit == 'px' {
|
||||
@return calc($value / 16px * 1em);
|
||||
}
|
||||
@else if $unit == '%' {
|
||||
@return calc($value / 100% * 1em);
|
||||
}
|
||||
@else if $unit == 'em' {
|
||||
@return $value;
|
||||
}
|
||||
@else if $unit == 'pt' {
|
||||
@return calc($value / 12pt * 1em);
|
||||
}
|
||||
@else {
|
||||
@return $value;
|
||||
// @warn 'Everything is terrible! What have you done?!';
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// Returns whether the feature can have a min/max pair
|
||||
//////////////////////////////
|
||||
$breakpoint-min-max-features: 'color',
|
||||
'color-index',
|
||||
'aspect-ratio',
|
||||
'device-aspect-ratio',
|
||||
'device-height',
|
||||
'device-width',
|
||||
'height',
|
||||
'monochrome',
|
||||
'resolution',
|
||||
'width';
|
||||
|
||||
@function breakpoint-min-max($feature) {
|
||||
@each $item in $breakpoint-min-max-features {
|
||||
@if $feature == $item {
|
||||
@return true;
|
||||
}
|
||||
}
|
||||
@return false;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// Returns whether the feature can have a string value
|
||||
//////////////////////////////
|
||||
$breakpoint-string-features: 'orientation',
|
||||
'scan',
|
||||
'color',
|
||||
'aspect-ratio',
|
||||
'device-aspect-ratio',
|
||||
'pointer',
|
||||
'luminosity';
|
||||
|
||||
@function breakpoint-string-value($feature) {
|
||||
@each $item in $breakpoint-string-features {
|
||||
@if breakpoint-min-max($item) {
|
||||
@if $feature == 'min-#{$item}' or $feature == 'max-#{$item}' {
|
||||
@return true;
|
||||
}
|
||||
}
|
||||
@else if $feature == $item {
|
||||
@return true;
|
||||
}
|
||||
}
|
||||
@return false;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// Returns whether the feature is a media type
|
||||
//////////////////////////////
|
||||
$breakpoint-media-types: 'all',
|
||||
'braille',
|
||||
'embossed',
|
||||
'handheld',
|
||||
'print',
|
||||
'projection',
|
||||
'screen',
|
||||
'speech',
|
||||
'tty',
|
||||
'tv';
|
||||
|
||||
@function breakpoint-is-media($feature) {
|
||||
@each $media in $breakpoint-media-types {
|
||||
@if ($feature == $media) or ($feature == 'not #{$media}') or ($feature == 'only #{$media}') {
|
||||
@return true;
|
||||
}
|
||||
}
|
||||
|
||||
@return false;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// Returns whether the feature can stand alone
|
||||
//////////////////////////////
|
||||
$breakpoint-single-string-features: 'color',
|
||||
'color-index',
|
||||
'grid',
|
||||
'monochrome';
|
||||
|
||||
@function breakpoint-single-string($feature) {
|
||||
@each $item in $breakpoint-single-string-features {
|
||||
@if $feature == $item {
|
||||
@return true;
|
||||
}
|
||||
}
|
||||
@return false;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// Returns whether the feature
|
||||
//////////////////////////////
|
||||
@function breakpoint-is-resolution($feature) {
|
||||
$resolutions: 'device-pixel-ratio', 'dpr';
|
||||
|
||||
@if breakpoint-get('transform resolutions') {
|
||||
$resolutions: append($resolutions, 'resolution');
|
||||
}
|
||||
|
||||
@each $reso in $resolutions {
|
||||
@if index($feature, $reso) or index($feature, 'min-#{$reso}') or index($feature, 'max-#{$reso}') {
|
||||
@return true;
|
||||
}
|
||||
}
|
||||
|
||||
@return false;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
@mixin legacy-settings-warning {
|
||||
$legacyVars: (
|
||||
'default-media': 'default media',
|
||||
'default-feature': 'default feature',
|
||||
'force-media-all': 'force all media type',
|
||||
'to-ems': 'to ems',
|
||||
'resolutions': 'transform resolutions',
|
||||
'no-queries': 'no queries',
|
||||
'no-query-fallbacks': 'no query fallbacks',
|
||||
'base-font-size': 'base font size',
|
||||
'legacy-syntax': 'legacy syntax'
|
||||
);
|
||||
|
||||
@each $legacy, $new in $legacyVars {
|
||||
@if global-variable-exists('breakpoint-' + $legacy) {
|
||||
@warn "In order to avoid variable namspace collisions, we have updated the way to change settings for Breakpoint. Please change all instances of `$breakpoint-#{$legacy}: {{setting}}` to `@include breakpoint-set('#{$new}', {{setting}})`. Variable settings, as well as this warning will be deprecated in a future release."
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////
|
||||
// Hand correct each setting
|
||||
//////////////////////////////
|
||||
@if global-variable-exists('breakpoint-default-media') and $breakpoint-default-media != breakpoint-get('default media') {
|
||||
@include breakpoint-set('default media', $breakpoint-default-media);
|
||||
}
|
||||
@if global-variable-exists('breakpoint-default-feature') and $breakpoint-default-feature != breakpoint-get('default feature') {
|
||||
@include breakpoint-set('default feature', $breakpoint-default-feature);
|
||||
}
|
||||
@if global-variable-exists('breakpoint-force-media-all') and $breakpoint-force-media-all != breakpoint-get('force all media type') {
|
||||
@include breakpoint-set('force all media type', $breakpoint-force-media-all);
|
||||
}
|
||||
@if global-variable-exists('breakpoint-to-ems') and $breakpoint-to-ems != breakpoint-get('to ems') {
|
||||
@include breakpoint-set('to ems', $breakpoint-to-ems);
|
||||
}
|
||||
@if global-variable-exists('breakpoint-resolutions') and $breakpoint-resolutions != breakpoint-get('transform resolutions') {
|
||||
@include breakpoint-set('transform resolutions', $breakpoint-resolutions);
|
||||
}
|
||||
@if global-variable-exists('breakpoint-no-queries') and $breakpoint-no-queries != breakpoint-get('no queries') {
|
||||
@include breakpoint-set('no queries', $breakpoint-no-queries);
|
||||
}
|
||||
@if global-variable-exists('breakpoint-no-query-fallbacks') and $breakpoint-no-query-fallbacks != breakpoint-get('no query fallbacks') {
|
||||
@include breakpoint-set('no query fallbacks', $breakpoint-no-query-fallbacks);
|
||||
}
|
||||
@if global-variable-exists('breakpoint-base-font-size') and $breakpoint-base-font-size != breakpoint-get('base font size') {
|
||||
@include breakpoint-set('base font size', $breakpoint-base-font-size);
|
||||
}
|
||||
@if global-variable-exists('breakpoint-legacy-syntax') and $breakpoint-legacy-syntax != breakpoint-get('legacy syntax') {
|
||||
@include breakpoint-set('legacy syntax', $breakpoint-legacy-syntax);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
@function breakpoint-no-query($query) {
|
||||
@if type-of($query) == 'list' {
|
||||
$keyword: nth($query, 1);
|
||||
|
||||
@if type-of($keyword) == 'string' and ($keyword == 'no-query' or $keyword == 'no query' or $keyword == 'fallback') {
|
||||
@return nth($query, 2);
|
||||
}
|
||||
@else {
|
||||
@return false;
|
||||
}
|
||||
}
|
||||
@else {
|
||||
@return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,215 @@
|
|||
//////////////////////////////
|
||||
// Import Parser Pieces
|
||||
//////////////////////////////
|
||||
@import "parsers/query";
|
||||
@import "parsers/single";
|
||||
@import "parsers/double";
|
||||
@import "parsers/triple";
|
||||
@import "parsers/resolution";
|
||||
|
||||
$Memo-Exists: function-exists(memo-get) and function-exists(memo-set);
|
||||
|
||||
//////////////////////////////
|
||||
// Breakpoint Function
|
||||
//////////////////////////////
|
||||
@function breakpoint($query, $contexts...) {
|
||||
$run: true;
|
||||
$return: ();
|
||||
|
||||
// Grab the Memo Output if Memoization can be a thing
|
||||
@if $Memo-Exists {
|
||||
$return: memo-get(breakpoint, breakpoint $query $contexts);
|
||||
|
||||
@if $return != null {
|
||||
$run: false;
|
||||
}
|
||||
}
|
||||
|
||||
@if not $Memo-Exists or $run {
|
||||
// Internal Variables
|
||||
$query-string: '';
|
||||
$query-fallback: false;
|
||||
$return: ();
|
||||
|
||||
// Reserve Global Private Breakpoint Context
|
||||
$holder-context: $private-breakpoint-context-holder;
|
||||
$holder-query-count: $private-breakpoint-query-count;
|
||||
|
||||
// Reset Global Private Breakpoint Context
|
||||
$private-breakpoint-context-holder: () !global;
|
||||
$private-breakpoint-query-count: 0 !global;
|
||||
|
||||
|
||||
// Test to see if it's a comma-separated list
|
||||
$or-list: if(list-separator($query) == 'comma', true, false);
|
||||
|
||||
|
||||
@if ($or-list == false and breakpoint-get('legacy syntax') == false) {
|
||||
$query-string: breakpoint-parse($query);
|
||||
}
|
||||
@else {
|
||||
$length: length($query);
|
||||
|
||||
$last: nth($query, $length);
|
||||
$query-fallback: breakpoint-no-query($last);
|
||||
|
||||
@if ($query-fallback != false) {
|
||||
$length: $length - 1;
|
||||
}
|
||||
|
||||
@if (breakpoint-get('legacy syntax') == true) {
|
||||
$mq: ();
|
||||
|
||||
@for $i from 1 through $length {
|
||||
$mq: append($mq, nth($query, $i), comma);
|
||||
}
|
||||
|
||||
$query-string: breakpoint-parse($mq);
|
||||
}
|
||||
@else {
|
||||
$query-string: '';
|
||||
@for $i from 1 through $length {
|
||||
$query-string: $query-string + if($i == 1, '', ', ') + breakpoint-parse(nth($query, $i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$return: ('query': $query-string,
|
||||
'fallback': $query-fallback,
|
||||
'context holder': $private-breakpoint-context-holder,
|
||||
'query count': $private-breakpoint-query-count
|
||||
);
|
||||
@if length($contexts) > 0 and nth($contexts, 1) != false {
|
||||
@if $query-fallback != false {
|
||||
$context-setter: private-breakpoint-set-context('no-query', $query-fallback);
|
||||
}
|
||||
$context-map: ();
|
||||
@each $context in $contexts {
|
||||
$context-map: map-merge($context-map, ($context: breakpoint-get-context($context)));
|
||||
}
|
||||
$return: map-merge($return, (context: $context-map));
|
||||
}
|
||||
|
||||
// Reset Global Private Breakpoint Context
|
||||
$private-breakpoint-context-holder: () !global;
|
||||
$private-breakpoint-query-count: 0 !global;
|
||||
|
||||
@if $Memo-Exists {
|
||||
$holder: memo-set(breakpoint, breakpoint $query $contexts, $return);
|
||||
}
|
||||
}
|
||||
|
||||
@return $return;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// General Breakpoint Parser
|
||||
//////////////////////////////
|
||||
@function breakpoint-parse($query) {
|
||||
// Increase number of 'and' queries
|
||||
$private-breakpoint-query-count: $private-breakpoint-query-count + 1 !global;
|
||||
|
||||
// Set up Media Type
|
||||
$query-print: '';
|
||||
|
||||
$force-all: ((breakpoint-get('force all media type') == true) and (breakpoint-get('default media') == 'all'));
|
||||
$empty-media: true;
|
||||
@if ($force-all == true) or (breakpoint-get('default media') != 'all') {
|
||||
// Force the print of the default media type if (force all is true and default media type is all) or (default media type is not all)
|
||||
$query-print: breakpoint-get('default media');
|
||||
$empty-media: false;
|
||||
}
|
||||
|
||||
|
||||
$query-resolution: false;
|
||||
|
||||
$query-holder: breakpoint-parse-query($query);
|
||||
|
||||
|
||||
|
||||
// Loop over each parsed out query and write it to $query-print
|
||||
$first: true;
|
||||
|
||||
@each $feature in $query-holder {
|
||||
$length: length($feature);
|
||||
|
||||
// Parse a single feature
|
||||
@if ($length == 1) {
|
||||
// Feature is currently a list, grab the actual value
|
||||
$feature: nth($feature, 1);
|
||||
|
||||
// Media Type must by convention be the first item, so it's safe to flat override $query-print, which right now should only be the default media type
|
||||
@if (breakpoint-is-media($feature)) {
|
||||
@if ($force-all == true) or ($feature != 'all') {
|
||||
// Force the print of the default media type if (force all is true and default media type is all) or (default media type is not all)
|
||||
$query-print: $feature;
|
||||
$empty-media: false;
|
||||
|
||||
// Set Context
|
||||
$context-setter: private-breakpoint-set-context(media, $query-print);
|
||||
}
|
||||
}
|
||||
@else {
|
||||
$parsed: breakpoint-parse-single($feature, $empty-media, $first);
|
||||
$query-print: '#{$query-print} #{$parsed}';
|
||||
$first: false;
|
||||
}
|
||||
}
|
||||
// Parse a double feature
|
||||
@else if ($length == 2) {
|
||||
@if (breakpoint-is-resolution($feature) != false) {
|
||||
$query-resolution: $feature;
|
||||
}
|
||||
@else {
|
||||
$parsed: null;
|
||||
// If it's a string/number pair,
|
||||
// we check to see if one is a single-string value,
|
||||
// then we parse it as a normal double
|
||||
$alpha: nth($feature, 1);
|
||||
$beta: nth($feature, 2);
|
||||
@if breakpoint-single-string($alpha) or breakpoint-single-string($beta) {
|
||||
$parsed: breakpoint-parse-single($alpha, $empty-media, $first);
|
||||
$query-print: '#{$query-print} #{$parsed}';
|
||||
$first: false;
|
||||
$parsed: breakpoint-parse-single($beta, $empty-media, $first);
|
||||
$query-print: '#{$query-print} #{$parsed}';
|
||||
}
|
||||
@else {
|
||||
$parsed: breakpoint-parse-double($feature, $empty-media, $first);
|
||||
$query-print: '#{$query-print} #{$parsed}';
|
||||
$first: false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Parse a triple feature
|
||||
@else if ($length == 3) {
|
||||
$parsed: breakpoint-parse-triple($feature, $empty-media, $first);
|
||||
$query-print: '#{$query-print} #{$parsed}';
|
||||
$first: false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@if ($query-resolution != false) {
|
||||
$query-print: breakpoint-build-resolution($query-print, $query-resolution, $empty-media, $first);
|
||||
}
|
||||
|
||||
// Loop through each feature that's been detected so far and append 'false' to the the value list to increment their counters
|
||||
@each $f, $v in $private-breakpoint-context-holder {
|
||||
$v-holder: $v;
|
||||
$length: length($v-holder);
|
||||
@if length($v-holder) < $private-breakpoint-query-count {
|
||||
@for $i from $length to $private-breakpoint-query-count {
|
||||
@if $f == 'media' {
|
||||
$v-holder: append($v-holder, breakpoint-get('default media'));
|
||||
}
|
||||
@else {
|
||||
$v-holder: append($v-holder, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
$private-breakpoint-context-holder: map-merge($private-breakpoint-context-holder, ($f: $v-holder)) !global;
|
||||
}
|
||||
|
||||
@return $query-print;
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
////////////////////////
|
||||
// Default the Breakpoints variable
|
||||
////////////////////////
|
||||
$breakpoints: () !default;
|
||||
$BREAKPOINTS: () !default;
|
||||
|
||||
////////////////////////
|
||||
// Respond-to API Mixin
|
||||
////////////////////////
|
||||
@mixin respond-to($context, $no-query: false) {
|
||||
@if length($breakpoints) > 0 and length($BREAKPOINTS) == 0 {
|
||||
@warn "In order to avoid variable namespace collisions, we have updated the way to add breakpoints for respond-to. Please change all instances of `$breakpoints: add-breakpoint()` to `@include add-breakpoint()`. The `add-breakpoint()` function will be deprecated in a future release.";
|
||||
$BREAKPOINTS: $breakpoints !global;
|
||||
$breakpoints: () !global;
|
||||
}
|
||||
|
||||
@if type-of($BREAKPOINTS) != 'map' {
|
||||
// Just in case someone writes gibberish to the $breakpoints variable.
|
||||
@warn "Your breakpoints aren't a map! `respond-to` expects a map. Please check the value of $BREAKPOINTS variable.";
|
||||
@content;
|
||||
}
|
||||
@else if map-has-key($BREAKPOINTS, $context) {
|
||||
@include breakpoint(map-get($BREAKPOINTS, $context), $no-query) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
@else if not map-has-key($BREAKPOINTS, $context) {
|
||||
@warn "`#{$context}` isn't a defined breakpoint! Please add it using `$breakpoints: add-breakpoint(`#{$context}`, $value);`";
|
||||
@content;
|
||||
}
|
||||
@else {
|
||||
@warn "You haven't created any breakpoints yet! Make some already! `@include add-breakpoint($name, $bkpt)`";
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// Add Breakpoint to Breakpoints
|
||||
// TODO: Remove function in next release
|
||||
//////////////////////////////
|
||||
@function add-breakpoint($name, $bkpt, $overwrite: false) {
|
||||
$output: ($name: $bkpt);
|
||||
|
||||
@if length($breakpoints) == 0 {
|
||||
@return $output;
|
||||
}
|
||||
@else {
|
||||
@if map-has-key($breakpoints, $name) and $overwrite != true {
|
||||
@warn "You already have a breakpoint named `#{$name}`, please choose another breakpoint name, or pass in `$overwrite: true` to overwrite the previous breakpoint.";
|
||||
@return $breakpoints;
|
||||
}
|
||||
@else if not map-has-key($breakpoints, $name) or $overwrite == true {
|
||||
@return map-merge($breakpoints, $output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin add-breakpoint($name, $bkpt, $overwrite: false) {
|
||||
$output: ($name: $bkpt);
|
||||
|
||||
@if length($BREAKPOINTS) == 0 {
|
||||
$BREAKPOINTS: $output !global;
|
||||
}
|
||||
@else {
|
||||
@if map-has-key($BREAKPOINTS, $name) and $overwrite != true {
|
||||
@warn "You already have a breakpoint named `#{$name}`, please choose another breakpoint name, or pass in `$overwrite: true` to overwrite the previous breakpoint.";
|
||||
$BREAKPOINTS: $BREAKPOINTS !global;
|
||||
}
|
||||
@else if not map-has-key($BREAKPOINTS, $name) or $overwrite == true {
|
||||
$BREAKPOINTS: map-merge($BREAKPOINTS, $output) !global;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@function get-breakpoint($name: false) {
|
||||
@if $name == false {
|
||||
@return $BREAKPOINTS;
|
||||
}
|
||||
@else {
|
||||
@return map-get($BREAKPOINTS, $name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
//////////////////////////////
|
||||
// Has Setting
|
||||
//////////////////////////////
|
||||
@function breakpoint-has($setting) {
|
||||
@if map-has-key($breakpoint, $setting) {
|
||||
@return true;
|
||||
}
|
||||
@else {
|
||||
@return false;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// Get Settings
|
||||
//////////////////////////////
|
||||
@function breakpoint-get($setting) {
|
||||
@if breakpoint-has($setting) {
|
||||
@return map-get($breakpoint, $setting);
|
||||
}
|
||||
@else {
|
||||
@return map-get($Breakpoint-Settings, $setting);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// Set Settings
|
||||
//////////////////////////////
|
||||
@function breakpoint-set($setting, $value) {
|
||||
@if (str-index($setting, '-') or str-index($setting, '_')) and str-index($setting, ' ') == null {
|
||||
@warn "Words in Breakpoint settings should be separated by spaces, not dashes or underscores. Please replace dashes and underscores between words with spaces. Settings will not work as expected until changed.";
|
||||
}
|
||||
$breakpoint: map-merge($breakpoint, ($setting: $value)) !global;
|
||||
@return true;
|
||||
}
|
||||
|
||||
@mixin breakpoint-change($setting, $value) {
|
||||
$breakpoint-change: breakpoint-set($setting, $value);
|
||||
}
|
||||
|
||||
@mixin breakpoint-set($setting, $value) {
|
||||
@include breakpoint-change($setting, $value);
|
||||
}
|
||||
|
||||
@mixin bkpt-change($setting, $value) {
|
||||
@include breakpoint-change($setting, $value);
|
||||
}
|
||||
@mixin bkpt-set($setting, $value) {
|
||||
@include breakpoint-change($setting, $value);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
// Remove Setting
|
||||
//////////////////////////////
|
||||
@function breakpoint-reset($settings...) {
|
||||
@if length($settings) == 1 {
|
||||
$settings: nth($settings, 1);
|
||||
}
|
||||
|
||||
@each $setting in $settings {
|
||||
$breakpoint: map-remove($breakpoint, $setting) !global;
|
||||
}
|
||||
@return true;
|
||||
}
|
||||
|
||||
@mixin breakpoint-reset($settings...) {
|
||||
$breakpoint-reset: breakpoint-reset($settings);
|
||||
}
|
||||
|
||||
@mixin bkpt-reset($settings...) {
|
||||
$breakpoint-reset: breakpoint-reset($settings);
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
//////////////////////////////
|
||||
// Import Pieces
|
||||
//////////////////////////////
|
||||
@import "double/default-pair";
|
||||
@import "double/double-string";
|
||||
@import "double/default";
|
||||
|
||||
@function breakpoint-parse-double($feature, $empty-media, $first) {
|
||||
$parsed: '';
|
||||
$leader: '';
|
||||
// If we're forcing
|
||||
@if not ($empty-media) or not ($first) {
|
||||
$leader: 'and ';
|
||||
}
|
||||
|
||||
$first: nth($feature, 1);
|
||||
$second: nth($feature, 2);
|
||||
|
||||
// If we've got two numbers, we know we need to use the default pair because there are no media queries that has a media feature that is a number
|
||||
@if type-of($first) == 'number' and type-of($second) == 'number' {
|
||||
$parsed: breakpoint-parse-default-pair($first, $second);
|
||||
}
|
||||
// If they are both strings, we send it through the string parser
|
||||
@else if type-of($first) == 'string' and type-of($second) == 'string' {
|
||||
$parsed: breakpoint-parse-double-string($first, $second);
|
||||
}
|
||||
// If it's a string/number pair, we parse it as a normal double
|
||||
@else {
|
||||
$parsed: breakpoint-parse-double-default($first, $second);
|
||||
}
|
||||
|
||||
@return $leader + $parsed;
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
@function breakpoint-parse-query($query) {
|
||||
// Parse features out of an individual query
|
||||
$feature-holder: ();
|
||||
$query-holder: ();
|
||||
$length: length($query);
|
||||
|
||||
@if $length == 2 {
|
||||
// If we've got a string/number, number/string, check to see if it's a valid string/number pair or two singles
|
||||
@if (type-of(nth($query, 1)) == 'string' and type-of(nth($query, 2)) == 'number') or (type-of(nth($query, 1)) == 'number' and type-of(nth($query, 2)) == 'string') {
|
||||
|
||||
$number: '';
|
||||
$value: '';
|
||||
|
||||
@if type-of(nth($query, 1)) == 'string' {
|
||||
$number: nth($query, 2);
|
||||
$value: nth($query, 1);
|
||||
}
|
||||
@else {
|
||||
$number: nth($query, 1);
|
||||
$value: nth($query, 2);
|
||||
}
|
||||
|
||||
// If the string value can be a single value, check to see if the number passed in is a valid input for said single value. Fortunately, all current single-value options only accept unitless numbers, so this check is easy.
|
||||
@if breakpoint-single-string($value) {
|
||||
@if unitless($number) {
|
||||
$feature-holder: append($value, $number, space);
|
||||
$query-holder: append($query-holder, $feature-holder, comma);
|
||||
@return $query-holder;
|
||||
}
|
||||
}
|
||||
// If the string is a media type, split the query
|
||||
@if breakpoint-is-media($value) {
|
||||
$query-holder: append($query-holder, nth($query, 1));
|
||||
$query-holder: append($query-holder, nth($query, 2));
|
||||
@return $query-holder;
|
||||
}
|
||||
// If it's not a single feature, we're just going to assume it's a proper string/value pair, and roll with it.
|
||||
@else {
|
||||
$feature-holder: append($value, $number, space);
|
||||
$query-holder: append($query-holder, $feature-holder, comma);
|
||||
@return $query-holder;
|
||||
}
|
||||
|
||||
}
|
||||
// If they're both numbers, we assume it's a double and roll with that
|
||||
@else if (type-of(nth($query, 1)) == 'number' and type-of(nth($query, 2)) == 'number') {
|
||||
$feature-holder: append(nth($query, 1), nth($query, 2), space);
|
||||
$query-holder: append($query-holder, $feature-holder, comma);
|
||||
@return $query-holder;
|
||||
}
|
||||
// If they're both strings and neither are singles, we roll with that.
|
||||
@else if (type-of(nth($query, 1)) == 'string' and type-of(nth($query, 2)) == 'string') {
|
||||
@if not breakpoint-single-string(nth($query, 1)) and not breakpoint-single-string(nth($query, 2)) {
|
||||
$feature-holder: append(nth($query, 1), nth($query, 2), space);
|
||||
$query-holder: append($query-holder, $feature-holder, comma);
|
||||
@return $query-holder;
|
||||
}
|
||||
}
|
||||
}
|
||||
@else if $length == 3 {
|
||||
// If we've got three items and none is a list, we check to see
|
||||
@if type-of(nth($query, 1)) != 'list' and type-of(nth($query, 2)) != 'list' and type-of(nth($query, 3)) != 'list' {
|
||||
// If none of the items are single string values and none of the values are media values, we're good.
|
||||
@if (not breakpoint-single-string(nth($query, 1)) and not breakpoint-single-string(nth($query, 2)) and not breakpoint-single-string(nth($query, 3))) and ((not breakpoint-is-media(nth($query, 1)) and not breakpoint-is-media(nth($query, 2)) and not breakpoint-is-media(nth($query, 3)))) {
|
||||
$feature-holder: append(nth($query, 1), nth($query, 2), space);
|
||||
$feature-holder: append($feature-holder, nth($query, 3), space);
|
||||
$query-holder: append($query-holder, $feature-holder, comma);
|
||||
@return $query-holder;
|
||||
}
|
||||
// let's check to see if the first item is a media type
|
||||
@else if breakpoint-is-media(nth($query, 1)) {
|
||||
$query-holder: append($query-holder, nth($query, 1));
|
||||
$feature-holder: append(nth($query, 2), nth($query, 3), space);
|
||||
$query-holder: append($query-holder, $feature-holder);
|
||||
@return $query-holder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If it's a single item, or if it's not a special case double or triple, we can simply return the query.
|
||||
@return $query;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
@import "resolution/resolution";
|
||||
|
||||
@function breakpoint-build-resolution($query-print, $query-resolution, $empty-media, $first) {
|
||||
$leader: '';
|
||||
// If we're forcing
|
||||
@if not ($empty-media) or not ($first) {
|
||||
$leader: 'and ';
|
||||
}
|
||||
|
||||
@if breakpoint-get('transform resolutions') and $query-resolution {
|
||||
$resolutions: breakpoint-make-resolutions($query-resolution);
|
||||
$length: length($resolutions);
|
||||
$query-holder: '';
|
||||
|
||||
@for $i from 1 through $length {
|
||||
$query: '#{$query-print} #{$leader}#{nth($resolutions, $i)}';
|
||||
@if $i == 1 {
|
||||
$query-holder: $query;
|
||||
}
|
||||
@else {
|
||||
$query-holder: '#{$query-holder}, #{$query}';
|
||||
}
|
||||
}
|
||||
|
||||
@return $query-holder;
|
||||
}
|
||||
@else {
|
||||
// Return with attached resolution
|
||||
@return $query-print;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
//////////////////////////////
|
||||
// Import Pieces
|
||||
//////////////////////////////
|
||||
@import "single/default";
|
||||
|
||||
@function breakpoint-parse-single($feature, $empty-media, $first) {
|
||||
$parsed: '';
|
||||
$leader: '';
|
||||
// If we're forcing
|
||||
@if not ($empty-media) or not ($first) {
|
||||
$leader: 'and ';
|
||||
}
|
||||
|
||||
// If it's a single feature that can stand alone, we let it
|
||||
@if (breakpoint-single-string($feature)) {
|
||||
$parsed: $feature;
|
||||
// Set Context
|
||||
$context-setter: private-breakpoint-set-context($feature, $feature);
|
||||
}
|
||||
// If it's not a stand alone feature, we pass it off to the default handler.
|
||||
@else {
|
||||
$parsed: breakpoint-parse-default($feature);
|
||||
}
|
||||
|
||||
@return $leader + '(' + $parsed + ')';
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
//////////////////////////////
|
||||
// Import Pieces
|
||||
//////////////////////////////
|
||||
@import "triple/default";
|
||||
|
||||
@function breakpoint-parse-triple($feature, $empty-media, $first) {
|
||||
$parsed: '';
|
||||
$leader: '';
|
||||
|
||||
// If we're forcing
|
||||
@if not ($empty-media) or not ($first) {
|
||||
$leader: 'and ';
|
||||
}
|
||||
|
||||
// separate the string features from the value numbers
|
||||
$string: null;
|
||||
$numbers: null;
|
||||
@each $val in $feature {
|
||||
@if type-of($val) == string {
|
||||
$string: $val;
|
||||
}
|
||||
@else {
|
||||
@if type-of($numbers) == 'null' {
|
||||
$numbers: $val;
|
||||
}
|
||||
@else {
|
||||
$numbers: append($numbers, $val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$parsed: breakpoint-parse-triple-default($string, nth($numbers, 1), nth($numbers, 2));
|
||||
|
||||
@return $leader + $parsed;
|
||||
|
||||
}
|
21
src/_sass/minimal-mistakes/vendor/breakpoint/parsers/double/_default-pair.scss
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
@function breakpoint-parse-default-pair($first, $second) {
|
||||
$default: breakpoint-get('default pair');
|
||||
$min: '';
|
||||
$max: '';
|
||||
|
||||
// Sort into min and max
|
||||
$min: min($first, $second);
|
||||
$max: max($first, $second);
|
||||
|
||||
// Set Context
|
||||
$context-setter: private-breakpoint-set-context(min-#{$default}, $min);
|
||||
$context-setter: private-breakpoint-set-context(max-#{$default}, $max);
|
||||
|
||||
// Make them EMs if need be
|
||||
@if (breakpoint-get('to ems') == true) {
|
||||
$min: breakpoint-to-base-em($min);
|
||||
$max: breakpoint-to-base-em($max);
|
||||
}
|
||||
|
||||
@return '(min-#{$default}: #{$min}) and (max-#{$default}: #{$max})';
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
@function breakpoint-parse-double-default($first, $second) {
|
||||
$feature: '';
|
||||
$value: '';
|
||||
|
||||
@if type-of($first) == 'string' {
|
||||
$feature: $first;
|
||||
$value: $second;
|
||||
}
|
||||
@else {
|
||||
$feature: $second;
|
||||
$value: $first;
|
||||
}
|
||||
|
||||
// Set Context
|
||||
$context-setter: private-breakpoint-set-context($feature, $value);
|
||||
|
||||
@if (breakpoint-get('to ems') == true) {
|
||||
$value: breakpoint-to-base-em($value);
|
||||
}
|
||||
|
||||
@return '(#{$feature}: #{$value})'
|
||||
}
|
22
src/_sass/minimal-mistakes/vendor/breakpoint/parsers/double/_double-string.scss
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
@function breakpoint-parse-double-string($first, $second) {
|
||||
$feature: '';
|
||||
$value: '';
|
||||
|
||||
// Test to see which is the feature and which is the value
|
||||
@if (breakpoint-string-value($first) == true) {
|
||||
$feature: $first;
|
||||
$value: $second;
|
||||
}
|
||||
@else if (breakpoint-string-value($second) == true) {
|
||||
$feature: $second;
|
||||
$value: $first;
|
||||
}
|
||||
@else {
|
||||
@warn "Neither #{$first} nor #{$second} is a valid media query name.";
|
||||
}
|
||||
|
||||
// Set Context
|
||||
$context-setter: private-breakpoint-set-context($feature, $value);
|
||||
|
||||
@return '(#{$feature}: #{$value})';
|
||||
}
|
60
src/_sass/minimal-mistakes/vendor/breakpoint/parsers/resolution/_resolution.scss
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
@function breakpoint-make-resolutions($resolution) {
|
||||
$length: length($resolution);
|
||||
|
||||
$output: ();
|
||||
|
||||
@if $length == 2 {
|
||||
$feature: '';
|
||||
$value: '';
|
||||
|
||||
// Find which is number
|
||||
@if type-of(nth($resolution, 1)) == 'number' {
|
||||
$value: nth($resolution, 1);
|
||||
}
|
||||
@else {
|
||||
$value: nth($resolution, 2);
|
||||
}
|
||||
|
||||
// Determine min/max/standard
|
||||
@if index($resolution, 'min-resolution') {
|
||||
$feature: 'min-';
|
||||
}
|
||||
@else if index($resolution, 'max-resolution') {
|
||||
$feature: 'max-';
|
||||
}
|
||||
|
||||
$standard: '(#{$feature}resolution: #{$value})';
|
||||
|
||||
// If we're not dealing with dppx,
|
||||
@if unit($value) != 'dppx' {
|
||||
$base: 96dpi;
|
||||
@if unit($value) == 'dpcm' {
|
||||
$base: 243.84dpcm;
|
||||
}
|
||||
// Write out feature tests
|
||||
$webkit: '';
|
||||
$moz: '';
|
||||
$webkit: '(-webkit-#{$feature}device-pixel-ratio: #{$value / $base})';
|
||||
$moz: '(#{$feature}-moz-device-pixel-ratio: #{$value / $base})';
|
||||
// Append to output
|
||||
$output: append($output, $standard, space);
|
||||
$output: append($output, $webkit, space);
|
||||
$output: append($output, $moz, space);
|
||||
}
|
||||
@else {
|
||||
$webkit: '';
|
||||
$moz: '';
|
||||
$webkit: '(-webkit-#{$feature}device-pixel-ratio: #{$value / 1dppx})';
|
||||
$moz: '(#{$feature}-moz-device-pixel-ratio: #{$value / 1dppx})';
|
||||
$fallback: '(#{$feature}resolution: #{$value / 1dppx * 96dpi})';
|
||||
// Append to output
|
||||
$output: append($output, $standard, space);
|
||||
$output: append($output, $webkit, space);
|
||||
$output: append($output, $moz, space);
|
||||
$output: append($output, $fallback, space);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@return $output;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
@function breakpoint-parse-default($feature) {
|
||||
$default: breakpoint-get('default feature');
|
||||
|
||||
// Set Context
|
||||
$context-setter: private-breakpoint-set-context($default, $feature);
|
||||
|
||||
@if (breakpoint-get('to ems') == true) and (type-of($feature) == 'number') {
|
||||
@return '#{$default}: #{breakpoint-to-base-em($feature)}';
|
||||
}
|
||||
@else {
|
||||
@return '#{$default}: #{$feature}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
@function breakpoint-parse-triple-default($feature, $first, $second) {
|
||||
|
||||
// Sort into min and max
|
||||
$min: min($first, $second);
|
||||
$max: max($first, $second);
|
||||
|
||||
// Set Context
|
||||
$context-setter: private-breakpoint-set-context(min-#{$feature}, $min);
|
||||
$context-setter: private-breakpoint-set-context(max-#{$feature}, $max);
|
||||
|
||||
// Make them EMs if need be
|
||||
@if (breakpoint-get('to ems') == true) {
|
||||
$min: breakpoint-to-base-em($min);
|
||||
$max: breakpoint-to-base-em($max);
|
||||
}
|
||||
|
||||
@return '(min-#{$feature}: #{$min}) and (max-#{$feature}: #{$max})';
|
||||
}
|
|
@ -0,0 +1,649 @@
|
|||
/* Magnific Popup CSS */
|
||||
|
||||
@import "settings";
|
||||
|
||||
////////////////////////
|
||||
//
|
||||
// Contents:
|
||||
//
|
||||
// 1. Default Settings
|
||||
// 2. General styles
|
||||
// - Transluscent overlay
|
||||
// - Containers, wrappers
|
||||
// - Cursors
|
||||
// - Helper classes
|
||||
// 3. Appearance
|
||||
// - Preloader & text that displays error messages
|
||||
// - CSS reset for buttons
|
||||
// - Close icon
|
||||
// - "1 of X" counter
|
||||
// - Navigation (left/right) arrows
|
||||
// - Iframe content type styles
|
||||
// - Image content type styles
|
||||
// - Media query where size of arrows is reduced
|
||||
// - IE7 support
|
||||
//
|
||||
////////////////////////
|
||||
|
||||
|
||||
|
||||
////////////////////////
|
||||
// 1. Default Settings
|
||||
////////////////////////
|
||||
|
||||
$mfp-overlay-color: #0b0b0b !default;
|
||||
$mfp-overlay-opacity: 0.8 !default;
|
||||
$mfp-shadow: 0 0 8px rgba(0, 0, 0, 0.6) !default; // shadow on image or iframe
|
||||
$mfp-popup-padding-left: 8px !default; // Padding from left and from right side
|
||||
$mfp-popup-padding-left-mobile: 6px !default; // Same as above, but is applied when width of window is less than 800px
|
||||
|
||||
$mfp-z-index-base: 1040 !default; // Base z-index of popup
|
||||
$mfp-include-arrows: true !default; // include styles for nav arrows
|
||||
$mfp-controls-opacity: 0.65 !default;
|
||||
$mfp-controls-color: #FFF !default;
|
||||
$mfp-controls-border-color: #3F3F3F !default;
|
||||
$mfp-inner-close-icon-color: #333 !default;
|
||||
$mfp-controls-text-color: #CCC !default; // Color of preloader and "1 of X" indicator
|
||||
$mfp-controls-text-color-hover: #FFF !default;
|
||||
$mfp-IE7support: true !default; // Very basic IE7 support
|
||||
|
||||
// Iframe-type options
|
||||
$mfp-include-iframe-type: true !default;
|
||||
$mfp-iframe-padding-top: 40px !default;
|
||||
$mfp-iframe-background: #000 !default;
|
||||
$mfp-iframe-max-width: 900px !default;
|
||||
$mfp-iframe-ratio: calc(9/16) !default;
|
||||
|
||||
// Image-type options
|
||||
$mfp-include-image-type: true !default;
|
||||
$mfp-image-background: #444 !default;
|
||||
$mfp-image-padding-top: 40px !default;
|
||||
$mfp-image-padding-bottom: 40px !default;
|
||||
$mfp-include-mobile-layout-for-image: true !default; // Removes paddings from top and bottom
|
||||
|
||||
// Image caption options
|
||||
$mfp-caption-title-color: #F3F3F3 !default;
|
||||
$mfp-caption-subtitle-color: #BDBDBD !default;
|
||||
|
||||
// A11y
|
||||
$mfp-use-visuallyhidden: false !default; // Hide content from browsers, but make it available for screen readers
|
||||
|
||||
|
||||
|
||||
////////////////////////
|
||||
// 2. General styles
|
||||
////////////////////////
|
||||
|
||||
// Transluscent overlay
|
||||
.mfp-bg {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: $mfp-z-index-base + 2;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
|
||||
background: $mfp-overlay-color;
|
||||
opacity: $mfp-overlay-opacity;
|
||||
@if $mfp-IE7support {
|
||||
filter: unquote("alpha(opacity=#{$mfp-overlay-opacity*100})");
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper for popup
|
||||
.mfp-wrap {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: $mfp-z-index-base + 3;
|
||||
position: fixed;
|
||||
outline: none !important;
|
||||
-webkit-backface-visibility: hidden; // fixes webkit bug that can cause "false" scrollbar
|
||||
}
|
||||
|
||||
// Root container
|
||||
.mfp-container {
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
padding: 0 $mfp-popup-padding-left;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// Vertical centerer helper
|
||||
.mfp-container {
|
||||
&:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove vertical centering when popup has class `mfp-align-top`
|
||||
.mfp-align-top {
|
||||
.mfp-container {
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Popup content holder
|
||||
.mfp-content {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
z-index: $mfp-z-index-base + 5;
|
||||
}
|
||||
.mfp-inline-holder,
|
||||
.mfp-ajax-holder {
|
||||
.mfp-content {
|
||||
width: 100%;
|
||||
cursor: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Cursors
|
||||
.mfp-ajax-cur {
|
||||
cursor: progress;
|
||||
}
|
||||
.mfp-zoom-out-cur {
|
||||
&, .mfp-image-holder .mfp-close {
|
||||
cursor: -moz-zoom-out;
|
||||
cursor: -webkit-zoom-out;
|
||||
cursor: zoom-out;
|
||||
}
|
||||
}
|
||||
.mfp-zoom {
|
||||
cursor: pointer;
|
||||
cursor: -webkit-zoom-in;
|
||||
cursor: -moz-zoom-in;
|
||||
cursor: zoom-in;
|
||||
}
|
||||
.mfp-auto-cursor {
|
||||
.mfp-content {
|
||||
cursor: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.mfp-close,
|
||||
.mfp-arrow,
|
||||
.mfp-preloader,
|
||||
.mfp-counter {
|
||||
-webkit-user-select:none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
// Hide the image during the loading
|
||||
.mfp-loading {
|
||||
&.mfp-figure {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper class that hides stuff
|
||||
@if $mfp-use-visuallyhidden {
|
||||
// From HTML5 Boilerplate https://github.com/h5bp/html5-boilerplate/blob/v4.2.0/doc/css.md#visuallyhidden
|
||||
.mfp-hide {
|
||||
border: 0 !important;
|
||||
clip: rect(0 0 0 0) !important;
|
||||
height: 1px !important;
|
||||
margin: -1px !important;
|
||||
overflow: hidden !important;
|
||||
padding: 0 !important;
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
}
|
||||
} @else {
|
||||
.mfp-hide {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////
|
||||
// 3. Appearance
|
||||
////////////////////////
|
||||
|
||||
// Preloader and text that displays error messages
|
||||
.mfp-preloader {
|
||||
color: $mfp-controls-text-color;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: auto;
|
||||
text-align: center;
|
||||
margin-top: -0.8em;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
z-index: $mfp-z-index-base + 4;
|
||||
a {
|
||||
color: $mfp-controls-text-color;
|
||||
&:hover {
|
||||
color: $mfp-controls-text-color-hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hide preloader when content successfully loaded
|
||||
.mfp-s-ready {
|
||||
.mfp-preloader {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Hide content when it was not loaded
|
||||
.mfp-s-error {
|
||||
.mfp-content {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// CSS-reset for buttons
|
||||
button {
|
||||
&.mfp-close,
|
||||
&.mfp-arrow {
|
||||
overflow: visible;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
-webkit-appearance: none;
|
||||
display: block;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
z-index: $mfp-z-index-base + 6;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
&::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Close icon
|
||||
.mfp-close {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
opacity: $mfp-controls-opacity;
|
||||
@if $mfp-IE7support {
|
||||
filter: unquote("alpha(opacity=#{$mfp-controls-opacity*100})");
|
||||
}
|
||||
padding: 0 0 18px 10px;
|
||||
color: $mfp-controls-color;
|
||||
|
||||
font-style: normal;
|
||||
font-size: 28px;
|
||||
font-family: $serif;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
@if $mfp-IE7support {
|
||||
filter: unquote("alpha(opacity=#{1*100})");
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
top: 1px;
|
||||
}
|
||||
}
|
||||
.mfp-close-btn-in {
|
||||
.mfp-close {
|
||||
color: $mfp-inner-close-icon-color;
|
||||
}
|
||||
}
|
||||
.mfp-image-holder,
|
||||
.mfp-iframe-holder {
|
||||
.mfp-close {
|
||||
color: $mfp-controls-color;
|
||||
right: -6px;
|
||||
text-align: right;
|
||||
padding-right: 6px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// "1 of X" counter
|
||||
.mfp-counter {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
color: $mfp-controls-text-color;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
// Navigation arrows
|
||||
@if $mfp-include-arrows {
|
||||
.mfp-arrow {
|
||||
position: absolute;
|
||||
opacity: $mfp-controls-opacity;
|
||||
@if $mfp-IE7support {
|
||||
filter: unquote("alpha(opacity=#{$mfp-controls-opacity*100})");
|
||||
}
|
||||
margin: 0;
|
||||
top: 50%;
|
||||
margin-top: -55px;
|
||||
padding: 0;
|
||||
width: 90px;
|
||||
height: 110px;
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
&:active {
|
||||
margin-top: -54px;
|
||||
}
|
||||
&:hover,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
@if $mfp-IE7support {
|
||||
filter: unquote("alpha(opacity=#{1*100})");
|
||||
}
|
||||
}
|
||||
&:before,
|
||||
&:after,
|
||||
.mfp-b,
|
||||
.mfp-a {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
margin-top: 35px;
|
||||
margin-left: 35px;
|
||||
border: medium inset transparent;
|
||||
}
|
||||
|
||||
&:after,
|
||||
.mfp-a {
|
||||
|
||||
border-top-width: 13px;
|
||||
border-bottom-width: 13px;
|
||||
top:8px;
|
||||
}
|
||||
|
||||
&:before,
|
||||
.mfp-b {
|
||||
border-top-width: 21px;
|
||||
border-bottom-width: 21px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.mfp-arrow-left {
|
||||
left: 0;
|
||||
|
||||
&:after,
|
||||
.mfp-a {
|
||||
border-right: 17px solid $mfp-controls-color;
|
||||
margin-left: 31px;
|
||||
}
|
||||
&:before,
|
||||
.mfp-b {
|
||||
margin-left: 25px;
|
||||
border-right: 27px solid $mfp-controls-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
.mfp-arrow-right {
|
||||
right: 0;
|
||||
&:after,
|
||||
.mfp-a {
|
||||
border-left: 17px solid $mfp-controls-color;
|
||||
margin-left: 39px
|
||||
}
|
||||
&:before,
|
||||
.mfp-b {
|
||||
border-left: 27px solid $mfp-controls-border-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Iframe content type
|
||||
@if $mfp-include-iframe-type {
|
||||
.mfp-iframe-holder {
|
||||
padding-top: $mfp-iframe-padding-top;
|
||||
padding-bottom: $mfp-iframe-padding-top;
|
||||
.mfp-content {
|
||||
line-height: 0;
|
||||
width: 100%;
|
||||
max-width: $mfp-iframe-max-width;
|
||||
}
|
||||
.mfp-close {
|
||||
top: -40px;
|
||||
}
|
||||
}
|
||||
.mfp-iframe-scaler {
|
||||
width: 100%;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
padding-top: $mfp-iframe-ratio * 100%;
|
||||
iframe {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-shadow: $mfp-shadow;
|
||||
background: $mfp-iframe-background;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Image content type
|
||||
@if $mfp-include-image-type {
|
||||
|
||||
/* Main image in popup */
|
||||
img {
|
||||
&.mfp-img {
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
line-height: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: $mfp-image-padding-top 0 $mfp-image-padding-bottom;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* The shadow behind the image */
|
||||
.mfp-figure {
|
||||
line-height: 0;
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: $mfp-image-padding-top;
|
||||
bottom: $mfp-image-padding-bottom;
|
||||
display: block;
|
||||
right: 0;
|
||||
width: auto;
|
||||
height: auto;
|
||||
z-index: -1;
|
||||
box-shadow: $mfp-shadow;
|
||||
background: $mfp-image-background;
|
||||
}
|
||||
small {
|
||||
color: $mfp-caption-subtitle-color;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
line-height: 14px;
|
||||
}
|
||||
figure {
|
||||
margin: 0;
|
||||
}
|
||||
figcaption {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0; // reset for bottom spacing
|
||||
}
|
||||
}
|
||||
.mfp-bottom-bar {
|
||||
margin-top: -$mfp-image-padding-bottom + 4;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
cursor: auto;
|
||||
}
|
||||
.mfp-title {
|
||||
text-align: left;
|
||||
line-height: 18px;
|
||||
color: $mfp-caption-title-color;
|
||||
word-wrap: break-word;
|
||||
padding-right: 36px; // leave some space for counter at right side
|
||||
}
|
||||
|
||||
.mfp-image-holder {
|
||||
.mfp-content {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mfp-gallery {
|
||||
.mfp-image-holder {
|
||||
.mfp-figure {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@if $mfp-include-mobile-layout-for-image {
|
||||
@media screen and (max-width: 800px) and (orientation:landscape), screen and (max-height: 300px) {
|
||||
/**
|
||||
* Remove all paddings around the image on small screen
|
||||
*/
|
||||
.mfp-img-mobile {
|
||||
.mfp-image-holder {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
img {
|
||||
&.mfp-img {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.mfp-figure {
|
||||
// The shadow behind the image
|
||||
&:after {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
small {
|
||||
display: inline;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
.mfp-bottom-bar {
|
||||
background: rgba(0,0,0,0.6);
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
top: auto;
|
||||
padding: 3px 5px;
|
||||
position: fixed;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
&:empty {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.mfp-counter {
|
||||
right: 5px;
|
||||
top: 3px;
|
||||
}
|
||||
.mfp-close {
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
line-height: 35px;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Scale navigation arrows and reduce padding from sides
|
||||
@media all and (max-width: 900px) {
|
||||
.mfp-arrow {
|
||||
-webkit-transform: scale(0.75);
|
||||
transform: scale(0.75);
|
||||
}
|
||||
.mfp-arrow-left {
|
||||
-webkit-transform-origin: 0;
|
||||
transform-origin: 0;
|
||||
}
|
||||
.mfp-arrow-right {
|
||||
-webkit-transform-origin: 100%;
|
||||
transform-origin: 100%;
|
||||
}
|
||||
.mfp-container {
|
||||
padding-left: $mfp-popup-padding-left-mobile;
|
||||
padding-right: $mfp-popup-padding-left-mobile;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// IE7 support
|
||||
// Styles that make popup look nicier in old IE
|
||||
@if $mfp-IE7support {
|
||||
.mfp-ie7 {
|
||||
.mfp-img {
|
||||
padding: 0;
|
||||
}
|
||||
.mfp-bottom-bar {
|
||||
width: 600px;
|
||||
left: 50%;
|
||||
margin-left: -300px;
|
||||
margin-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.mfp-container {
|
||||
padding: 0;
|
||||
}
|
||||
.mfp-content {
|
||||
padding-top: 44px;
|
||||
}
|
||||
.mfp-close {
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
////////////////////////
|
||||
// Settings //
|
||||
////////////////////////
|
||||
|
||||
// overlay
|
||||
$mfp-overlay-color: #000; // Color of overlay screen
|
||||
$mfp-overlay-opacity: 0.8; // Opacity of overlay screen
|
||||
$mfp-shadow: 0 0 8px rgba(0, 0, 0, 0.6); // Shadow on image or iframe
|
||||
|
||||
// spacing
|
||||
$mfp-popup-padding-left: 8px; // Padding from left and from right side
|
||||
$mfp-popup-padding-left-mobile: 6px; // Same as above, but is applied when width of window is less than 800px
|
||||
|
||||
$mfp-z-index-base: 1040; // Base z-index of popup
|
||||
|
||||
// controls
|
||||
$mfp-include-arrows: true; // Include styles for nav arrows
|
||||
$mfp-controls-opacity: 1; // Opacity of controls
|
||||
$mfp-controls-color: #fff; // Color of controls
|
||||
$mfp-controls-border-color: #fff; // Border color of controls
|
||||
$mfp-inner-close-icon-color: #fff; // Color of close button when inside
|
||||
$mfp-controls-text-color: #ccc; // Color of preloader and "1 of X" indicator
|
||||
$mfp-controls-text-color-hover: #fff; // Hover color of preloader and "1 of X" indicator
|
||||
$mfp-IE7support: true; // Very basic IE7 support
|
||||
|
||||
// Iframe-type options
|
||||
$mfp-include-iframe-type: true; // Enable Iframe-type popups
|
||||
$mfp-iframe-padding-top: 40px; // Iframe padding top
|
||||
$mfp-iframe-background: #000; // Background color of iframes
|
||||
$mfp-iframe-max-width: 900px; // Maximum width of iframes
|
||||
$mfp-iframe-ratio: calc(9/16); // Ratio of iframe (9/16 = widescreen, 3/4 = standard, etc.)
|
||||
|
||||
// Image-type options
|
||||
$mfp-include-image-type: true; // Enable Image-type popups
|
||||
$mfp-image-background: #444 !default;
|
||||
$mfp-image-padding-top: 40px; // Image padding top
|
||||
$mfp-image-padding-bottom: 40px; // Image padding bottom
|
||||
$mfp-include-mobile-layout-for-image: true; // Removes paddings from top and bottom
|
||||
|
||||
// Image caption options
|
||||
$mfp-caption-title-color: #f3f3f3; // Caption title color
|
||||
$mfp-caption-subtitle-color: #bdbdbd; // Caption subtitle color
|
||||
.mfp-counter { font-family: $serif; } // Caption font family
|
||||
|
||||
// A11y
|
||||
$mfp-use-visuallyhidden: false;
|
|
@ -0,0 +1,4 @@
|
|||
// Su
|
||||
// ==
|
||||
|
||||
@import 'susy/su';
|
|
@ -0,0 +1,13 @@
|
|||
// Susy (Prefixed)
|
||||
// ===============
|
||||
|
||||
$susy-version: 3;
|
||||
|
||||
@import 'susy/utilities';
|
||||
@import 'susy/su-validate';
|
||||
@import 'susy/su-math';
|
||||
@import 'susy/settings';
|
||||
@import 'susy/normalize';
|
||||
@import 'susy/parse';
|
||||
@import 'susy/syntax-helpers';
|
||||
@import 'susy/api';
|
|
@ -0,0 +1,5 @@
|
|||
// Susy (Un-Prefixed)
|
||||
// ==================
|
||||
|
||||
@import 'susy-prefix';
|
||||
@import 'susy/unprefix';
|
|
@ -0,0 +1,5 @@
|
|||
// SVG Grid Background
|
||||
// ===================
|
||||
|
||||
@import 'svg-grid/prefix';
|
||||
@import 'svg-grid/svg-unprefix';
|
|
@ -0,0 +1,7 @@
|
|||
// Prefixed SVG Plugin
|
||||
// ===================
|
||||
|
||||
@import 'svg-settings';
|
||||
@import 'svg-utilities';
|
||||
@import 'svg-grid-math';
|
||||
@import 'svg-api';
|
|
@ -0,0 +1,114 @@
|
|||
/// Plugin: SVG Grid Image
|
||||
/// ======================
|
||||
/// @group plugin_svg-grid
|
||||
/// @see susy-svg-grid
|
||||
|
||||
|
||||
|
||||
/// ## Overview
|
||||
/// If you want to generate svg-backgrounds
|
||||
/// for help visualizing and debugging your grids,
|
||||
/// import the SVG Grid Plugin.
|
||||
///
|
||||
/// The plugin adds `svg-grid-colors` setting
|
||||
/// to your global defaults,
|
||||
/// which you can override in `$susy`.
|
||||
/// It also provides you with a new function,
|
||||
/// `susy-svg-grid()`,
|
||||
/// which will return inline svg for use in
|
||||
/// backgrounds or generated content.
|
||||
///
|
||||
/// This function come with an unprefixed alias by default,
|
||||
/// using the `svg-grid` import.
|
||||
/// If you only only want prefixed versions of the API,
|
||||
/// import the `svg-grid/prefix` partial instead.
|
||||
///
|
||||
/// @group plugin_svg-grid
|
||||
///
|
||||
/// @example scss - importing the plugin
|
||||
/// // The full path to import Susy will depend on your setup…
|
||||
///
|
||||
/// // unprefixed
|
||||
/// @import 'plugins/svg-grid';
|
||||
///
|
||||
/// // prefixed
|
||||
/// @import 'plugins/svg-grid/prefix';
|
||||
///
|
||||
/// @example scss - generating background grids
|
||||
/// .grid {
|
||||
/// background: susy-svg-grid() no-repeat scroll;
|
||||
/// }
|
||||
|
||||
|
||||
|
||||
// SVG Grid
|
||||
// --------
|
||||
/// Return inline svg-data in to display the grid.
|
||||
///
|
||||
/// @group plugin_svg-grid
|
||||
///
|
||||
/// @param {Map | List} $grid [$susy] -
|
||||
/// Map or shorthand defining the current grid
|
||||
/// @param {Color | List | null} $colors [null] -
|
||||
/// Column color, or list of colors for column-gradient,
|
||||
/// used to override the global `svg-grid-colors` setting
|
||||
/// @param {Length | null} $offset [null] -
|
||||
/// Manually override the default grid-image offset,
|
||||
/// to account for grid edges
|
||||
///
|
||||
/// @return {String} -
|
||||
/// CSS inline-data SVG string, in `url(<svg>)` format,
|
||||
/// for use in image or content properties
|
||||
/// @example scss
|
||||
/// .grid {
|
||||
/// background: susy-svg-grid() no-repeat scroll;
|
||||
/// }
|
||||
@function susy-svg-grid(
|
||||
$grid: $susy,
|
||||
$colors: null,
|
||||
$offset: null
|
||||
) {
|
||||
// Grid parsing & normalizing
|
||||
$grid: susy-compile($grid, $context-only: true);
|
||||
|
||||
// Color and gradient handling
|
||||
$gradient: '';
|
||||
|
||||
@if (not $colors) {
|
||||
$colors: susy-get('svg-grid-colors');
|
||||
}
|
||||
|
||||
@if length($colors) > 1 {
|
||||
$gradient: _susy-svg-gradient($colors);
|
||||
$colors: 'url(%23susy-svg-gradient)';
|
||||
} @else {
|
||||
$colors: _susy-svg-color($colors);
|
||||
}
|
||||
|
||||
// Get a default image-width
|
||||
$span: (
|
||||
'span': map-get($grid, 'columns'),
|
||||
'spread': map-get($grid, 'container-spread'),
|
||||
);
|
||||
$span: map-merge($grid, $span);
|
||||
$image-width: su-call('su-span', $span);
|
||||
$image-width: if((type-of($image-width) == 'number'), $image-width, 100%);
|
||||
|
||||
// SVG construction
|
||||
$columns: map-get($grid, 'columns');
|
||||
$offset: $offset or _susy-svg-offset($grid);
|
||||
|
||||
$attrs: 'fill="#{$colors}" width="#{$image-width}"';
|
||||
$svg: 'data:image/svg+xml,';
|
||||
$svg: $svg + '%3Csvg xmlns="http://www.w3.org/2000/svg" #{$attrs} %3E';
|
||||
$svg: $svg + $gradient;
|
||||
|
||||
@for $column from 1 through length($columns) {
|
||||
$width: susy-span(1 narrow at $column, $grid);
|
||||
$x: _susy-svg-column-position($column, $grid);
|
||||
|
||||
$svg: $svg + _susy-svg-rect($x, $width, $offset);
|
||||
}
|
||||
|
||||
@return url('#{$svg}%3C/svg%3E');
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// SVG Grid Math
|
||||
// =============
|
||||
|
||||
|
||||
|
||||
// SVG Column Position
|
||||
// -------------------
|
||||
/// Determine the proper horizontal position
|
||||
/// for a column rectangle
|
||||
///
|
||||
/// @access private
|
||||
///
|
||||
/// @param {Integer} $column -
|
||||
/// 1-indexed column location on the grid
|
||||
/// @param {Map} $grid -
|
||||
/// Normalized settings map representing the current grid
|
||||
///
|
||||
/// @return {Length} -
|
||||
/// Horizontal position of svg column rectangle,
|
||||
/// as distance from the grid edge
|
||||
@function _susy-svg-column-position(
|
||||
$column,
|
||||
$grid
|
||||
) {
|
||||
$x: $column - 1;
|
||||
|
||||
@if ($x > 0) {
|
||||
$x: susy-span(first $x wide, $grid);
|
||||
}
|
||||
|
||||
@return $x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// SVG Offset
|
||||
// ----------
|
||||
/// Determine if a grid image needs to be offset,
|
||||
/// to account for edge gutters.
|
||||
///
|
||||
/// @access private
|
||||
///
|
||||
/// @param {Map} $grid -
|
||||
/// Normalized settings map representing the current grid
|
||||
///
|
||||
/// @return {Length | null} -
|
||||
/// Expected distance from container edge to first column,
|
||||
/// based on spread values and gutter-widths
|
||||
@function _susy-svg-offset(
|
||||
$grid
|
||||
) {
|
||||
$columns: su-valid-columns(map-get($grid, 'columns'));
|
||||
$gutters: su-valid-gutters(map-get($grid, 'gutters'));
|
||||
$container: su-valid-spread(map-get($grid, 'container-spread')) + 1;
|
||||
|
||||
@if ($container == 0) {
|
||||
@return null;
|
||||
}
|
||||
|
||||
$gutter: su-call('su-gutter', $grid);
|
||||
|
||||
@if (type-of($gutter) == 'string') {
|
||||
@return 'calc(#{$container} * #{$gutter} / 2)';
|
||||
}
|
||||
|
||||
@return $container * $gutter / 2;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// SVG Settings
|
||||
// ============
|
||||
|
||||
|
||||
// Susy SVG Defaults
|
||||
// =================
|
||||
/// This plugin adds the `svg-grid-colors` property
|
||||
/// and default value to `$_susy-defaults` —
|
||||
/// you can override that value in `$susy`
|
||||
/// or any other grid settings map.
|
||||
/// @group plugin_svg-grid
|
||||
$_susy-defaults: map-merge((
|
||||
'svg-grid-colors': hsla(120, 50%, 50%, 0.5) hsla(120, 50%, 75%, 0.5),
|
||||
), $_susy-defaults);
|
|
@ -0,0 +1,18 @@
|
|||
// Unprefix Susy SVG Grid
|
||||
// ======================
|
||||
|
||||
|
||||
|
||||
// SVG Grid
|
||||
// --------
|
||||
/// Un-prefixed alias for `susy-svg-grid`
|
||||
///
|
||||
/// @group plugin_svg-grid
|
||||
/// @alias susy-svg-grid
|
||||
@function svg-grid(
|
||||
$grid: $susy,
|
||||
$colors: susy-get('svg-grid-colors'),
|
||||
$offset: null
|
||||
) {
|
||||
@return susy-svg-grid($grid, $colors, $offset);
|
||||
}
|
133
src/_sass/minimal-mistakes/vendor/susy/plugins/svg-grid/_svg-utilities.scss
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
// SVG Utilities
|
||||
// =============
|
||||
|
||||
|
||||
|
||||
// SVG Validate Units
|
||||
// ------------------
|
||||
/// Make sure a length is supported in svg
|
||||
///
|
||||
/// @access private
|
||||
///
|
||||
/// @param {Length} $length -
|
||||
/// The length to validate
|
||||
/// @param {String} $name [null] -
|
||||
/// Optional name of length origin,
|
||||
/// for error reporting
|
||||
///
|
||||
/// @return {Length} -
|
||||
/// An svg-validated length, or comparable valid length
|
||||
@function _susy-svg-validate-units(
|
||||
$length,
|
||||
$name: null
|
||||
) {
|
||||
$_svg-units: ('em', 'ex', 'px', 'pt', 'pc', 'cm', 'mm', 'in', '%');
|
||||
$string: type-of($length) == 'string';
|
||||
|
||||
@if ($length == 0) or ($string) or index($_svg-units, unit($length)) {
|
||||
@return $length;
|
||||
}
|
||||
|
||||
@return _susy-error(
|
||||
'`#{unit($length)}` #{$name} units are not supported in SVG',
|
||||
'_susy-svg-validate-units');
|
||||
}
|
||||
|
||||
|
||||
|
||||
// SVG Rect
|
||||
// --------
|
||||
/// Build a single svg rectangle
|
||||
///
|
||||
/// @access private
|
||||
///
|
||||
/// @param {Length} $x -
|
||||
/// Horizontal position for the rectangle
|
||||
/// @param {Length} $width -
|
||||
/// Width of the rectangle
|
||||
/// @param {Length} $offset [null] -
|
||||
/// Offset the rectangle, to account for edge gutters
|
||||
///
|
||||
/// @return {String} -
|
||||
/// Escaped string representing one svg rectangle
|
||||
@function _susy-svg-rect(
|
||||
$x,
|
||||
$width,
|
||||
$offset: null
|
||||
) {
|
||||
$x: _susy-svg-validate-units($x);
|
||||
$width: _susy-svg-validate-units($width);
|
||||
$offset: if($offset == 0, null, $offset);
|
||||
|
||||
@if (type-of($offset) == 'number') and (type-of($x) == 'number') {
|
||||
@if comparable($x, $offset) {
|
||||
$x: $x + $offset;
|
||||
} @else {
|
||||
$x: 'calc(#{$x} + #{$offset})';
|
||||
}
|
||||
} @else if $offset and ($x != 0) {
|
||||
$x: 'calc(#{$x} + #{$offset})';
|
||||
} @else if $offset {
|
||||
$x: $offset;
|
||||
}
|
||||
|
||||
@return '%3Crect x="#{$x}" width="#{$width}" height="100%"/%3E';
|
||||
}
|
||||
|
||||
|
||||
|
||||
// SVG Color
|
||||
// ---------
|
||||
/// Stringify colors, and escape hex symbol
|
||||
///
|
||||
/// @access private
|
||||
///
|
||||
/// @param {Color} $color -
|
||||
/// Color to stringify and escape
|
||||
///
|
||||
/// @return {String} -
|
||||
/// Escaped string value of color
|
||||
@function _susy-svg-color(
|
||||
$color
|
||||
) {
|
||||
$color: inspect($color); // convert to string
|
||||
|
||||
@if (str-index($color, '#') == 1) {
|
||||
$color: '%23' + str-slice($color, 2);
|
||||
}
|
||||
|
||||
@return $color;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// SVG Gradient
|
||||
// ------------
|
||||
/// Create a multi-color svg gradient
|
||||
///
|
||||
/// @access private
|
||||
///
|
||||
/// @param {List} $colors -
|
||||
/// List of colors to be equally spaced from `0%` to `100%`
|
||||
/// in each column rectangle
|
||||
///
|
||||
/// @return {String} -
|
||||
/// Escaped string representing one svg gradient
|
||||
/// (`id="susy-svg-gradient"`)
|
||||
@function _susy-svg-gradient(
|
||||
$colors
|
||||
) {
|
||||
$gradient: '%3Cdefs%3E%3ClinearGradient spreadMethod="pad"';
|
||||
$gradient: '#{$gradient} id="susy-svg-gradient"';
|
||||
$gradient: '#{$gradient} x1="0%" y1="0%" x2="100%" y2="0%"%3E';
|
||||
|
||||
@for $i from 1 through length($colors) {
|
||||
$color: _susy-svg-color(nth($colors, $i));
|
||||
$offset: percentage(($i - 1) / (length($colors) - 1));
|
||||
$stop: '%3Cstop offset="#{$offset}" style="stop-color:#{$color};" /%3E';
|
||||
|
||||
$gradient: $gradient + $stop;
|
||||
}
|
||||
|
||||
@return $gradient + '%3C/linearGradient%3E%3C/defs%3E';
|
||||
}
|
|
@ -0,0 +1,318 @@
|
|||
/// Susy3 API Functions
|
||||
/// ===================
|
||||
/// These three functions form the core of Susy's
|
||||
/// layout-building grid API.
|
||||
///
|
||||
/// - Use `span()` and `gutter()` to return any grid-width,
|
||||
/// and apply the results wherever you need them:
|
||||
/// CSS `width`, `margin`, `padding`, `flex-basis`, `transform`, etc.
|
||||
/// - For asymmetrical-fluid grids,
|
||||
/// `slice()` can help manage your nesting context.
|
||||
///
|
||||
/// All three functions come with an unprefixed alias by default,
|
||||
/// using the `susy` import.
|
||||
/// Import the `susy-prefix` partial instead,
|
||||
/// if you only only want prefixed versions of the API.
|
||||
///
|
||||
/// This is a thin syntax-sugar shell around
|
||||
/// the "Su" core-math functions: `su-span`, `su-gutter`, and `su-slice`.
|
||||
/// If you prefer the more constrained syntax of the math engine,
|
||||
/// you are welcome to use those functions instead.
|
||||
///
|
||||
/// @group b-api
|
||||
/// @see susy-span
|
||||
/// @see susy-gutter
|
||||
/// @see susy-slice
|
||||
/// @see su-span
|
||||
/// @see su-gutter
|
||||
/// @see su-slice
|
||||
|
||||
|
||||
|
||||
/// ## Shorthand
|
||||
///
|
||||
/// All functions draw on the same shorthand syntax in two parts,
|
||||
/// seperated by the word `of`.
|
||||
///
|
||||
/// ### Span Syntax: `<width>` [`<location>` `<spread>`]
|
||||
/// The first part describes the
|
||||
/// **span** width, location, and spread in any order.
|
||||
/// Only the width is required:
|
||||
///
|
||||
/// - `span(2)` will return the width of 2 columns.
|
||||
/// - `span(3 wide)` will return 3-columns, with an additional gutter.
|
||||
/// - location is only needed with asymmetrical grids,
|
||||
/// where `span(3 at 2)` will return the width of
|
||||
/// specific columns on the grid.
|
||||
/// Since these are functions, they will not handle placement for you.
|
||||
///
|
||||
/// ### Context Syntax: `[of <columns> <container-spread> <gutters>]`
|
||||
/// The second half of Susy's shorthand
|
||||
/// describes the grid-**context** –
|
||||
/// available columns, container-spread, and optional gutter override –
|
||||
/// in any order.
|
||||
/// All of these settings have globally-defined defaults:
|
||||
///
|
||||
/// - `span(2 of 6)` will set the context to
|
||||
/// a slice of 6 columns from the global grid.
|
||||
/// More details below.
|
||||
/// - `span(2 of 12 wide)` changes the container-spread
|
||||
/// as well as the column-context.
|
||||
/// - `span(2 of 12 set-gutters 0.5em)`
|
||||
/// will override the global gutters setting
|
||||
/// for this one calculation.
|
||||
///
|
||||
/// A single unitless number for `columns`
|
||||
/// will be treated as a slice of the parent grid.
|
||||
/// On a grid with `columns: susy-repeat(12, 120px)`,
|
||||
/// the shorthand `of 4` will use the parent `120px` column-width.
|
||||
/// You can also be more explicit,
|
||||
/// and say `of susy-repeat(4, 100px)`.
|
||||
/// If you are using asymmetrical grids,
|
||||
/// like `columns: (1 1 2 3 5 8)`,
|
||||
/// Susy can't slice it for you without knowing which columns you want.
|
||||
/// The `slice` function accepts exactly the same syntax as `span`,
|
||||
/// but returns a list of columns rather than a width.
|
||||
/// Use it in your context like `of slice(first 3)`.
|
||||
///
|
||||
/// @group b-api
|
||||
|
||||
|
||||
|
||||
// Susy Span
|
||||
// ---------
|
||||
/// This is the primary function in Susy —
|
||||
/// used to return the width of a span across one or more columns,
|
||||
/// and any relevant gutters along the way.
|
||||
/// With the default settings,
|
||||
/// `span(3)` will return the width of 3 columns,
|
||||
/// and the 2 intermediate gutters.
|
||||
/// This can be used to set the `width` property of grid elements,
|
||||
/// or `margin` and `padding`
|
||||
/// to push, pull, and pad your elements.
|
||||
///
|
||||
/// - This is a thin syntax-sugar shell around
|
||||
/// the core-math `su-span()` function.
|
||||
/// - The un-prefixed alias `span()` is available by default.
|
||||
///
|
||||
/// @group b-api
|
||||
/// @see su-span
|
||||
/// @see $susy
|
||||
///
|
||||
/// @param {list} $span -
|
||||
/// Shorthand expression to define the width of the span,
|
||||
/// optionally containing:
|
||||
/// - a count, length, or column-list span.
|
||||
/// - `at $n`, `first`, or `last` location on asymmetrical grids,
|
||||
/// where `at 1 == first`,
|
||||
/// and `last` will calculate the proper location
|
||||
/// based on columns and span.
|
||||
/// - `narrow`, `wide`, or `wider` for optionally spreading
|
||||
/// across adjacent gutters.
|
||||
/// - `of $n <spread>` for available grid columns
|
||||
/// and spread of the container.
|
||||
/// Span counts like `of 6` are valid
|
||||
/// in the context of symmetrical grids,
|
||||
/// where Susy can safely infer a slice of the parent columns.
|
||||
/// - and `set-gutters $n` to override global gutter settings.
|
||||
///
|
||||
/// @param {map} $config [()] -
|
||||
/// Optional map of Susy grid configuration settings.
|
||||
/// See `$susy` documentation for details.
|
||||
///
|
||||
/// @return {length} -
|
||||
/// Calculated length value, using the units given,
|
||||
/// or converting to `%` for fraction-based grids,
|
||||
/// or a full `calc` function when units/fractions
|
||||
/// are not comparable outside the browser.
|
||||
///
|
||||
/// @example scss - span half the grid
|
||||
/// .foo {
|
||||
/// // the result is a bit under 50% to account for gutters
|
||||
/// width: susy-span(6 of 12);
|
||||
/// }
|
||||
///
|
||||
/// @example scss - span a specific segment of asymmetrical grid
|
||||
/// .foo {
|
||||
/// width: susy-span(3 at 3 of (1 2 3 5 8));
|
||||
/// }
|
||||
@function susy-span(
|
||||
$span,
|
||||
$config: ()
|
||||
) {
|
||||
$output: susy-compile($span, $config);
|
||||
|
||||
@if map-get($output, 'span') {
|
||||
@return su-call('su-span', $output);
|
||||
}
|
||||
|
||||
$actual: '[#{type-of($span)}] `#{inspect($span)}`';
|
||||
@return _susy-error(
|
||||
'Unable to determine span value from #{$actual}.',
|
||||
'susy-span');
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Susy Gutter
|
||||
// -----------
|
||||
/// The gutter function returns
|
||||
/// the width of a single gutter on your grid,
|
||||
/// to be applied where you see fit –
|
||||
/// on `margins`, `padding`, `transform`, or element `width`.
|
||||
///
|
||||
/// - This is a thin syntax-sugar shell around
|
||||
/// the core-math `su-gutter()` function.
|
||||
/// - The un-prefixed alias `gutter()` is available by default.
|
||||
///
|
||||
/// @group b-api
|
||||
/// @see su-gutter
|
||||
/// @see $susy
|
||||
///
|
||||
/// @param {list | number} $context [null] -
|
||||
/// Optional context for nested gutters,
|
||||
/// including shorthand for
|
||||
/// `columns`, `gutters`, and `container-spread`
|
||||
/// (additional shorthand will be ignored)
|
||||
///
|
||||
/// @param {map} $config [()] -
|
||||
/// Optional map of Susy grid configuration settings.
|
||||
/// See `$susy` documentation for details.
|
||||
///
|
||||
/// @return {length} -
|
||||
/// Width of a gutter as `%` of current context,
|
||||
/// or in the units defined by `column-width` when available
|
||||
///
|
||||
/// @example scss - add gutters before or after an element
|
||||
/// .floats {
|
||||
/// float: left;
|
||||
/// width: span(3 of 6);
|
||||
/// margin-left: gutter(of 6);
|
||||
/// }
|
||||
///
|
||||
/// @example scss - add gutters to padding
|
||||
/// .flexbox {
|
||||
/// flex: 1 1 span(3 wide of 6 wide);
|
||||
/// padding: gutter(of 6) / 2;
|
||||
/// }
|
||||
///
|
||||
@function susy-gutter(
|
||||
$context: susy-get('columns'),
|
||||
$config: ()
|
||||
) {
|
||||
$context: susy-compile($context, $config, 'context-only');
|
||||
|
||||
@return su-call('su-gutter', $context);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Susy Slice
|
||||
// ----------
|
||||
/// Working with asymmetrical grids (un-equal column widths)
|
||||
/// can be challenging –
|
||||
/// expecially when they involve fluid/fractional elements.
|
||||
/// Describing a context `of (15em 6em 6em 6em 15em)` is a lot
|
||||
/// to put inside the span or gutter function shorthand.
|
||||
/// This slice function returns a sub-slice of asymmetrical columns to use
|
||||
/// for a nested context.
|
||||
/// `slice(3 at 2)` will give you a subset of the global grid,
|
||||
/// spanning 3 columns, starting with the second.
|
||||
///
|
||||
/// - This is a thin syntax-sugar shell around
|
||||
/// the core-math `su-slice()` function.
|
||||
/// - The un-prefixed alias `slice()` is available by default.
|
||||
///
|
||||
/// @group b-api
|
||||
/// @see su-slice
|
||||
/// @see $susy
|
||||
///
|
||||
/// @param {list} $span -
|
||||
/// Shorthand expression to define the subset span, optionally containing:
|
||||
/// - `at $n`, `first`, or `last` location on asymmetrical grids;
|
||||
/// - `of $n <spread>` for available grid columns
|
||||
/// and spread of the container
|
||||
/// - Span-counts like `of 6` are only valid
|
||||
/// in the context of symmetrical grids
|
||||
/// - Valid spreads include `narrow`, `wide`, or `wider`
|
||||
///
|
||||
/// @param {map} $config [()] -
|
||||
/// Optional map of Susy grid configuration settings.
|
||||
/// See `$susy` documentation for details.
|
||||
///
|
||||
/// @return {list} -
|
||||
/// Subset list of columns for use for a nested context
|
||||
///
|
||||
/// @example scss - Return a nested segment of asymmetrical grid
|
||||
/// $context: susy-slice(3 at 3 of (1 2 3 5 8));
|
||||
/// /* $context: #{$context}; */
|
||||
@function susy-slice(
|
||||
$span,
|
||||
$config: ()
|
||||
) {
|
||||
$span: susy-compile($span, $config);
|
||||
|
||||
@return su-call('su-slice', $span);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// ## Building Grids
|
||||
/// The web has come a long way
|
||||
/// since the days of double-margin-hacks
|
||||
/// and inconsistent subpixel rounding.
|
||||
/// In addition to floats and tables,
|
||||
/// we can now use much more powerful tools,
|
||||
/// like flexbox and CSS grid,
|
||||
/// to build more interesting and responsive layouts.
|
||||
///
|
||||
/// With Susy3, we hope you'll start moving in that direction.
|
||||
/// You can still build classic 12-column Grid Systems,
|
||||
/// and we'll help you get there,
|
||||
/// but Susy3 is primarily designed for a grid-math-on-demand
|
||||
/// approach to layout:
|
||||
/// applying our functions only where you really need grid math.
|
||||
/// Read the [intro article by OddBird][welcome] for more details.
|
||||
///
|
||||
/// [welcome]: http://oddbird.net/2017/06/28/susy3/
|
||||
///
|
||||
/// @group b-api
|
||||
/// @link http://oddbird.net/2017/06/28/susy3/ Article: Welcome to Susy3
|
||||
///
|
||||
/// @example scss - floats
|
||||
/// .float {
|
||||
/// width: span(3);
|
||||
/// margin-right: gutter();
|
||||
/// }
|
||||
///
|
||||
/// @example scss - flexbox
|
||||
/// .flexbox {
|
||||
/// flex: 1 1 span(3);
|
||||
/// // half a gutter on either side…
|
||||
/// padding: 0 gutter() / 2;
|
||||
/// }
|
||||
///
|
||||
/// @example scss - pushing and pulling
|
||||
/// .push-3 {
|
||||
/// margin-left: span(3 wide);
|
||||
/// }
|
||||
///
|
||||
/// .pull-3 {
|
||||
/// margin-left: 0 - span(3 wide);
|
||||
/// }
|
||||
///
|
||||
/// @example scss - building an attribute system
|
||||
/// // markup example: <div data-span="last 3"></div>
|
||||
/// [data-span] {
|
||||
/// float: left;
|
||||
///
|
||||
/// &:not([data-span*='last']) {
|
||||
/// margin-right: gutter();
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// @for $span from 1 through length(susy-get('columns')) {
|
||||
/// [data-span*='#{$span}'] {
|
||||
/// width: span($span);
|
||||
/// }
|
||||
/// }
|
|
@ -0,0 +1,261 @@
|
|||
/// Syntax Normalization
|
||||
/// ====================
|
||||
/// Susy is divided into two layers:
|
||||
/// "Su" provides the core math functions with a stripped-down syntax,
|
||||
/// while "Susy" adds global settings, shorthand syntax,
|
||||
/// and other helpers.
|
||||
/// Each setting (e.g. span, location, columns, spread, etc.)
|
||||
/// has a single canonical syntax in Su.
|
||||
///
|
||||
/// This normalization module helps translate between those layers,
|
||||
/// transforming parsed Susy input into
|
||||
/// values that Su will understand.
|
||||
///
|
||||
/// @group x-normal
|
||||
///
|
||||
/// @see susy-normalize
|
||||
/// @see susy-normalize-span
|
||||
/// @see susy-normalize-columns
|
||||
/// @see susy-normalize-spread
|
||||
/// @see susy-normalize-location
|
||||
|
||||
|
||||
|
||||
// Susy Normalize
|
||||
// --------------
|
||||
/// Normalize the values in a configuration map.
|
||||
/// In addition to the global `$susy` properties,
|
||||
/// this map can include local span-related imformation,
|
||||
/// like `span` and `location`.
|
||||
///
|
||||
/// Normalization does not check that values are valid,
|
||||
/// which will happen in the Su math layer.
|
||||
/// These functions merely look for known Susy syntax –
|
||||
/// returning a map with those shorthand values
|
||||
/// converted into low-level data for Su.
|
||||
/// For example `span: all` and `location: first`
|
||||
/// will be converted into specific numbers.
|
||||
///
|
||||
/// @group x-normal
|
||||
/// @see $susy
|
||||
/// @see susy-parse
|
||||
///
|
||||
/// @param {map} $config -
|
||||
/// Map of Susy configuration settings to normalize.
|
||||
/// See `$susy` and `susy-parse()` documentation for details.
|
||||
/// @param {map | null} $context [null] -
|
||||
/// Map of Susy configuration settings to use as global reference,
|
||||
/// or `null` to use global settings.
|
||||
///
|
||||
/// @return {map} -
|
||||
/// Map of Susy configuration settings,
|
||||
/// with all values normalized for Su math functions.
|
||||
@function susy-normalize(
|
||||
$config,
|
||||
$context: null
|
||||
) {
|
||||
// Spread
|
||||
@each $setting in ('spread', 'container-spread') {
|
||||
$value: map-get($config, $setting);
|
||||
|
||||
@if $value {
|
||||
$value: susy-normalize-spread($value);
|
||||
$config: map-merge($config, ($setting: $value));
|
||||
}
|
||||
}
|
||||
|
||||
// Columns
|
||||
$columns: map-get($config, 'columns');
|
||||
|
||||
@if $columns {
|
||||
$columns: susy-normalize-columns($columns, $context);
|
||||
$config: map-merge($config, ('columns': $columns));
|
||||
}
|
||||
|
||||
@if not $columns {
|
||||
$map: type-of($context) == 'map';
|
||||
$columns: if($map, map-get($context, 'columns'), null);
|
||||
$columns: $columns or susy-get('columns');
|
||||
}
|
||||
|
||||
// Span
|
||||
$span: map-get($config, 'span');
|
||||
|
||||
@if $span {
|
||||
$span: susy-normalize-span($span, $columns);
|
||||
$config: map-merge($config, ('span': $span));
|
||||
}
|
||||
|
||||
// Location
|
||||
$location: map-get($config, 'location');
|
||||
|
||||
@if $location {
|
||||
$location: susy-normalize-location($span, $location, $columns);
|
||||
$config: map-merge($config, ('location': $location));
|
||||
}
|
||||
|
||||
@return $config;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Normalize Span
|
||||
// --------------
|
||||
/// Normalize `span` shorthand for Su.
|
||||
/// Su span syntax allows an explicit length (e.g. `3em`),
|
||||
/// unitless column-span number (e.g. `3` columns),
|
||||
/// or an explicit list of columns (e.g. `(3 5 8)`).
|
||||
///
|
||||
/// Susy span syntax also allows the `all` keyword,
|
||||
/// which will be converted to a slice of the context
|
||||
/// in normalization.
|
||||
///
|
||||
/// @group x-normal
|
||||
///
|
||||
/// @param {number | list | 'all'} $span -
|
||||
/// Span value to normalize.
|
||||
/// @param {list} $columns -
|
||||
/// Normalized list of columns in the grid
|
||||
///
|
||||
/// @return {number | list} -
|
||||
/// Number or list value for `$span`
|
||||
@function susy-normalize-span(
|
||||
$span,
|
||||
$columns: susy-get('columns')
|
||||
) {
|
||||
@if ($span == 'all') {
|
||||
@return length($columns);
|
||||
}
|
||||
|
||||
@return $span;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Normalize Columns
|
||||
// -----------------
|
||||
/// Normalize `column` shorthand for Su.
|
||||
/// Su column syntax only allows column lists (e.g. `120px 1 1 1 120px`).
|
||||
///
|
||||
/// Susy span syntax also allows a unitless `slice` number (e.g `of 5`),
|
||||
/// which will be converted to a slice of the context
|
||||
/// in normalization.
|
||||
///
|
||||
/// @group x-normal
|
||||
///
|
||||
/// @param {list | integer} $columns -
|
||||
/// List of available columns,
|
||||
/// or unitless integer representing a slice of
|
||||
/// the available context.
|
||||
/// @param {map | null} $context [null] -
|
||||
/// Map of Susy configuration settings to use as global reference,
|
||||
/// or `null` to access global settings.
|
||||
///
|
||||
/// @return {list} -
|
||||
/// Columns list value, normalized for Su input.
|
||||
///
|
||||
/// @throws
|
||||
/// when attempting to access a slice of asymmetrical context
|
||||
@function susy-normalize-columns(
|
||||
$columns,
|
||||
$context: null
|
||||
) {
|
||||
$context: $context or susy-settings();
|
||||
|
||||
@if type-of($columns) == 'list' {
|
||||
@return _susy-flatten($columns);
|
||||
}
|
||||
|
||||
@if (type-of($columns) == 'number') and (unitless($columns)) {
|
||||
$span: $columns;
|
||||
$context: map-get($context, 'columns');
|
||||
$symmetrical: susy-repeat(length($context), nth($context, 1));
|
||||
|
||||
@if ($context == $symmetrical) {
|
||||
@return susy-repeat($span, nth($context, 1));
|
||||
} @else {
|
||||
$actual: 'of `#{$span}`';
|
||||
$columns: 'grid-columns `#{$context}`';
|
||||
@return _susy-error(
|
||||
'context-slice #{$actual} can not be determined based on #{$columns}.',
|
||||
'susy-normalize-columns');
|
||||
}
|
||||
}
|
||||
|
||||
@return $columns;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Normalize Spread
|
||||
// ----------------
|
||||
/// Normalize `spread` shorthand for Su.
|
||||
/// Su spread syntax only allows the numbers `-1`, `0`, or `1` –
|
||||
/// representing the number of gutters covered
|
||||
/// in relation to columns spanned.
|
||||
///
|
||||
/// Susy spread syntax also allows keywords for each value –
|
||||
/// `narrow` for `-1`, `wide` for `0`, or `wider` for `1` –
|
||||
/// which will be converted to their respective integers
|
||||
/// in normalization.
|
||||
///
|
||||
/// @group x-normal
|
||||
///
|
||||
/// @param {0 | 1 | -1 | 'narrow' | 'wide' | 'wider'} $spread -
|
||||
/// Spread across adjacent gutters, relative to a column-count —
|
||||
/// either `narrow` (-1), `wide` (0), or `wider` (1)
|
||||
///
|
||||
/// @return {number} -
|
||||
/// Numeric value for `$spread`
|
||||
@function susy-normalize-spread(
|
||||
$spread
|
||||
) {
|
||||
$normal-spread: (
|
||||
'narrow': -1,
|
||||
'wide': 0,
|
||||
'wider': 1,
|
||||
);
|
||||
|
||||
@return map-get($normal-spread, $spread) or $spread;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Normalize Location
|
||||
// ------------------
|
||||
/// Normalize `location` shorthand for Su.
|
||||
/// Su location syntax requires the (1-indexed) number for a column.
|
||||
///
|
||||
/// Susy also allows the `first` and `last` keywords,
|
||||
/// where `first` is always `1`,
|
||||
/// and `last` is calculated based on span and column values.
|
||||
/// Both keywords are normalized into an integer index
|
||||
/// in normalization.
|
||||
///
|
||||
/// @group x-normal
|
||||
///
|
||||
/// @param {number} $span -
|
||||
/// Number of grid-columns to be spanned
|
||||
/// @param {integer | 'first' | 'last'} $location -
|
||||
/// Starting (1-indexed) column position of a span,
|
||||
/// or a named location keyword.
|
||||
/// @param {list} $columns -
|
||||
/// Already-normalized list of columns in the grid.
|
||||
///
|
||||
/// @return {integer} -
|
||||
/// Numeric value for `$location`
|
||||
@function susy-normalize-location(
|
||||
$span,
|
||||
$location,
|
||||
$columns
|
||||
) {
|
||||
$count: length($columns);
|
||||
$normal-locations: (
|
||||
'first': 1,
|
||||
'alpha': 1,
|
||||
'last': $count - $span + 1,
|
||||
'omega': $count - $span + 1,
|
||||
);
|
||||
|
||||
@return map-get($normal-locations, $location) or $location;
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
/// Shorthand Syntax Parser
|
||||
/// =======================
|
||||
/// The syntax parser converts [shorthand syntax][short]
|
||||
/// into a map of settings that can be compared/merged with
|
||||
/// other config maps and global setting.
|
||||
///
|
||||
/// [short]: b-api.html
|
||||
///
|
||||
/// @group x-parser
|
||||
|
||||
|
||||
|
||||
// Parse
|
||||
// -----
|
||||
/// The `parse` function provides all the syntax-sugar in Susy,
|
||||
/// converting user shorthand
|
||||
/// into a usable map of keys and values
|
||||
/// that can be normalized and passed to Su.
|
||||
///
|
||||
/// @group x-parser
|
||||
/// @see $susy
|
||||
///
|
||||
/// @param {list} $shorthand -
|
||||
/// Shorthand expression to define the width of the span,
|
||||
/// optionally containing:
|
||||
/// - a count, length, or column-list span;
|
||||
/// - `at $n`, `first`, or `last` location on asymmetrical grids;
|
||||
/// - `narrow`, `wide`, or `wider` for optionally spreading
|
||||
/// across adjacent gutters;
|
||||
/// - `of $n <spread>` for available grid columns
|
||||
/// and spread of the container
|
||||
/// (span counts like `of 6` are only valid
|
||||
/// in the context of symmetrical grids);
|
||||
/// - and `set-gutters $n` to override global gutter settings
|
||||
/// @param {bool} $context-only [false] -
|
||||
/// Allow the parser to ignore span and span-spread values,
|
||||
/// only parsing context and container-spread.
|
||||
/// This makes it possible to accept spanless values,
|
||||
/// like the `gutters()` syntax.
|
||||
/// When parsing context-only,
|
||||
/// the `of` indicator is optional.
|
||||
///
|
||||
/// @return {map} -
|
||||
/// Map of span and grid settings
|
||||
/// parsed from shorthand input –
|
||||
/// including all the properties available globally –
|
||||
/// `columns`, `gutters`, `spread`, `container-spread` –
|
||||
/// along with the span-specific properties
|
||||
/// `span`, and `location`.
|
||||
///
|
||||
/// @throw
|
||||
/// when a shorthand value is not recognized
|
||||
@function susy-parse(
|
||||
$shorthand,
|
||||
$context-only: false
|
||||
) {
|
||||
$parse-error: 'Unknown shorthand property:';
|
||||
$options: (
|
||||
'first': 'location',
|
||||
'last': 'location',
|
||||
'alpha': 'location',
|
||||
'omega': 'location',
|
||||
'narrow': 'spread',
|
||||
'wide': 'spread',
|
||||
'wider': 'spread',
|
||||
);
|
||||
|
||||
$return: ();
|
||||
$span: null;
|
||||
$columns: null;
|
||||
|
||||
$of: null;
|
||||
$next: false;
|
||||
|
||||
// Allow context-only shorthand, without span
|
||||
@if ($context-only) and (not index($shorthand, 'of')) {
|
||||
@if su-valid-columns($shorthand, 'fail-silent') {
|
||||
$shorthand: 'of' $shorthand;
|
||||
} @else {
|
||||
$shorthand: join('of', $shorthand);
|
||||
}
|
||||
}
|
||||
|
||||
// loop through the shorthand list
|
||||
@for $i from 1 through length($shorthand) {
|
||||
$item: nth($shorthand, $i);
|
||||
$type: type-of($item);
|
||||
$error: false;
|
||||
$details: '[#{$type}] `#{$item}`';
|
||||
|
||||
// if we know what's supposed to be coming next…
|
||||
@if $next {
|
||||
|
||||
// Add to the return map
|
||||
$return: map-merge($return, ($next: $item));
|
||||
|
||||
// Reset next to `false`
|
||||
$next: false;
|
||||
|
||||
} @else { // If we don't know what's supposed to be coming…
|
||||
|
||||
// Keywords…
|
||||
@if ($type == 'string') {
|
||||
// Check the map for keywords…
|
||||
@if map-has-key($options, $item) {
|
||||
$setting: map-get($options, $item);
|
||||
|
||||
// Spread could be on the span or the container…
|
||||
@if ($setting == 'spread') and ($of) {
|
||||
$return: map-merge($return, ('container-spread': $item));
|
||||
} @else {
|
||||
$return: map-merge($return, ($setting: $item));
|
||||
}
|
||||
|
||||
} @else if ($item == 'all') {
|
||||
// `All` is a span shortcut
|
||||
$span: 'all';
|
||||
} @else if ($item == 'at') {
|
||||
// Some keywords setup what's next…
|
||||
$next: 'location';
|
||||
} @else if ($item == 'set-gutters') {
|
||||
$next: 'gutters';
|
||||
} @else if ($item == 'of') {
|
||||
$of: true;
|
||||
} @else {
|
||||
$error: true;
|
||||
}
|
||||
|
||||
} @else if ($type == 'number') or ($type == 'list') { // Numbers & lists…
|
||||
|
||||
@if not ($span or $of) {
|
||||
// We don't have a span, and we're not expecting context…
|
||||
$span: $item;
|
||||
} @else if ($of) and (not $columns) {
|
||||
// We are expecting context…
|
||||
$columns: $item;
|
||||
} @else {
|
||||
$error: true;
|
||||
}
|
||||
|
||||
} @else {
|
||||
$error: true;
|
||||
}
|
||||
}
|
||||
|
||||
@if $error {
|
||||
@return _susy-error('#{$parse-error} #{$details}', 'susy-parse');
|
||||
}
|
||||
}
|
||||
|
||||
// If we have span, merge it in
|
||||
@if $span {
|
||||
$return: map-merge($return, ('span': $span));
|
||||
}
|
||||
|
||||
// If we have columns, merge them in
|
||||
@if $columns {
|
||||
$return: map-merge($return, ('columns': $columns));
|
||||
}
|
||||
|
||||
// Return the map of settings…
|
||||
@return $return;
|
||||
}
|
|
@ -0,0 +1,329 @@
|
|||
/// Susy3 Configuration
|
||||
/// ===================
|
||||
/// Susy3 has 4 core settings, in a single settings map.
|
||||
/// You'll notice a few differences from Susy2:
|
||||
///
|
||||
/// **Columns** no longer accept a single number, like `12`,
|
||||
/// but use a syntax more similar to the new
|
||||
/// CSS [grid-template-columns][columns] –
|
||||
/// a list of relative sizes for each column on the grid.
|
||||
/// Unitless numbers in Susy act very similar to `fr` units in CSS,
|
||||
/// and the `susy-repeat()` function (similar to the css `repeat()`)
|
||||
/// helps quickly establish equal-width columns.
|
||||
///
|
||||
/// [columns]: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns
|
||||
///
|
||||
/// - `susy-repeat(12)` will create 12 fluid, equal-width columns
|
||||
/// - `susy-repeat(6, 120px)` will create 6 equal `120px`-wide columns
|
||||
/// - `120px susy-repeat(4) 120px` will create 6 columns,
|
||||
/// the first and last are `120px`,
|
||||
/// while the middle 4 are equal fractions of the remainder.
|
||||
/// Susy will output `calc()` values in order to achieve this.
|
||||
///
|
||||
/// **Gutters** haven't changed –
|
||||
/// a single fraction or explicit width –
|
||||
/// but the `calc()` output feature
|
||||
/// means you can now use any combination of units and fractions
|
||||
/// to create static-gutters on a fluid grid, etc.
|
||||
///
|
||||
/// **Spread** existed in the Susy2 API as a span option,
|
||||
/// and was otherwise handled behind the scenes.
|
||||
/// Now we're giving you full control over all spread issues.
|
||||
/// You can find a more [detailed explanation of spread on the blog][spread].
|
||||
///
|
||||
/// [spread]: http://oddbird.net/2017/06/13/susy-spread/
|
||||
///
|
||||
/// You can access your global settings at any time
|
||||
/// with the `susy-settings()` function,
|
||||
/// or grab a single setting from the global scope
|
||||
/// with `susy-get('columns')`, `susy-get('gutters')` etc.
|
||||
///
|
||||
/// @group a-config
|
||||
/// @link http://oddbird.net/2017/06/13/susy-spread/
|
||||
/// Article: Understanding Spread in Susy3
|
||||
///
|
||||
/// @see $susy
|
||||
/// @see susy-settings
|
||||
/// @see susy-get
|
||||
|
||||
|
||||
|
||||
// Susy
|
||||
// ----
|
||||
/// The grid is defined in a single map variable,
|
||||
/// with four initial properties:
|
||||
/// `columns`, `gutters`, `spread` and `container-spread`.
|
||||
/// Anything you put in the root `$susy` variable map
|
||||
/// will be treated as a global project default.
|
||||
/// You can create similar configuration maps
|
||||
/// under different variable names,
|
||||
/// to override the defaults as-needed.
|
||||
///
|
||||
/// @group a-config
|
||||
/// @type Map
|
||||
///
|
||||
/// @see $_susy-defaults
|
||||
/// @see {function} susy-repeat
|
||||
/// @link
|
||||
/// https://codepen.io/mirisuzanne/pen/EgmJJp?editors=1100
|
||||
/// Spread examples on CodePen
|
||||
///
|
||||
/// @prop {list} columns -
|
||||
/// Columns are described by a list of numbers,
|
||||
/// representing the relative width of each column.
|
||||
/// The syntax is a simplified version of CSS native
|
||||
/// `grid-template-columns`,
|
||||
/// expecting a list of grid-column widths.
|
||||
/// Unitless numbers create fractional fluid columns
|
||||
/// (similar to the CSS-native `fr` unit),
|
||||
/// while length values (united numbers)
|
||||
/// are used to define static columns.
|
||||
/// You can mix-and match units and fractions,
|
||||
/// to create a mixed grid.
|
||||
/// Susy will generate `calc()` values when necessary,
|
||||
/// to make all your units work together.
|
||||
///
|
||||
/// Use the `susy-repeat($count, $value)` function
|
||||
/// to more easily repetative columns,
|
||||
/// similar to the CSS-native `repeat()`.
|
||||
///
|
||||
/// - `susy-repeat(8)`:
|
||||
/// an 8-column, symmetrical, fluid grid.
|
||||
/// <br />Identical to `(1 1 1 1 1 1 1 1)`.
|
||||
/// - `susy-repeat(6, 8em)`:
|
||||
/// a 6-column, symmetrical, em-based grid.
|
||||
/// <br />Identical to `(8em 8em 8em 8em 8em 8em)`.
|
||||
/// - `(300px susy-repeat(4) 300px)`:
|
||||
/// a 6-column, asymmetrical, mixed fluid/static grid
|
||||
/// using `calc()` output.
|
||||
/// <br />Identical to `(300px 1 1 1 1 300px)`.
|
||||
///
|
||||
/// **NOTE** that `12` is no longer a valid 12-column grid definition,
|
||||
/// and you must list all the columns individually
|
||||
/// (or by using the `susy-repeat()` function).
|
||||
///
|
||||
/// @prop {number} gutters -
|
||||
/// Gutters are defined as a single width,
|
||||
/// or fluid ratio, similar to the native-CSS
|
||||
/// `grid-column-gap` syntax.
|
||||
/// Similar to columns,
|
||||
/// gutters can use any valid CSS length unit,
|
||||
/// or unitless numbers to define a relative fraction.
|
||||
///
|
||||
/// - `0.5`:
|
||||
/// a fluid gutter, half the size of a single-fraction column.
|
||||
/// - `1em`:
|
||||
/// a static gutter, `1em` wide.
|
||||
///
|
||||
/// Mix static gutters with fluid columns, or vice versa,
|
||||
/// and Susy will generate the required `calc()` to make it work.
|
||||
///
|
||||
/// @prop {string} spread [narrow] -
|
||||
/// Spread of an element across adjacent gutters:
|
||||
/// either `narrow` (none), `wide` (one), or `wider` (two)
|
||||
///
|
||||
/// - Both spread settings default to `narrow`,
|
||||
/// the most common use-case.
|
||||
/// A `narrow` spread only has gutters *between* columns
|
||||
/// (one less gutter than columns).
|
||||
/// This is how all css-native grids work,
|
||||
/// and most margin-based grid systems.
|
||||
/// - A `wide` spread includes the same number of gutters as columns,
|
||||
/// spanning across a single side-gutter.
|
||||
/// This is how most padding-based grid systems often work,
|
||||
/// and is also useful for pushing and pulling elements into place.
|
||||
/// - The rare `wider` spread includes gutters
|
||||
/// on both sides of the column-span
|
||||
/// (one more gutters than columns).
|
||||
///
|
||||
/// @prop {string} container-spread [narrow] -
|
||||
/// Spread of a container around adjacent gutters:
|
||||
/// either `narrow` (none), `wide` (one), or `wider` (two).
|
||||
/// See `spread` property for details.
|
||||
///
|
||||
/// @since 3.0.0-beta.1 -
|
||||
/// `columns` setting no longer accepts numbers
|
||||
/// (e.g. `12`) for symmetrical fluid grids,
|
||||
/// or the initial `12 x 120px` syntax for
|
||||
/// symmetrical fixed-unit grids.
|
||||
/// Use `susy-repeat(12)` or `susy-repeat(12, 120px)` instead.
|
||||
///
|
||||
/// @example scss - default values
|
||||
/// // 4 symmetrical, fluid columns
|
||||
/// // gutters are 1/4 the size of a column
|
||||
/// // elements span 1 less gutter than columns
|
||||
/// // containers span 1 less gutter as well
|
||||
/// $susy: (
|
||||
/// 'columns': susy-repeat(4),
|
||||
/// 'gutters': 0.25,
|
||||
/// 'spread': 'narrow',
|
||||
/// 'container-spread': 'narrow',
|
||||
/// );
|
||||
///
|
||||
/// @example scss - inside-static gutters
|
||||
/// // 6 symmetrical, fluid columns…
|
||||
/// // gutters are static, triggering calc()…
|
||||
/// // elements span equal columns & gutters…
|
||||
/// // containers span equal columns & gutters…
|
||||
/// $susy: (
|
||||
/// 'columns': susy-repeat(6),
|
||||
/// 'gutters': 0.5em,
|
||||
/// 'spread': 'wide',
|
||||
/// 'container-spread': 'wide',
|
||||
/// );
|
||||
$susy: () !default;
|
||||
|
||||
|
||||
|
||||
// Susy Repeat
|
||||
// -----------
|
||||
/// Similar to the `repeat(<count>, <value>)` function
|
||||
/// that is available in native CSS Grid templates,
|
||||
/// the `susy-repeat()` function helps generate repetative layouts
|
||||
/// by repeating any value a given number of times.
|
||||
/// Where Susy previously allowed `8` as a column definition
|
||||
/// for 8 equal columns, you should now use `susy-repeat(8)`.
|
||||
///
|
||||
/// @group a-config
|
||||
///
|
||||
/// @param {integer} $count -
|
||||
/// The number of repetitions, e.g. `12` for a 12-column grid.
|
||||
/// @param {*} $value [1] -
|
||||
/// The value to be repeated.
|
||||
/// Technically any value can be repeated here,
|
||||
/// but the function exists to repeat column-width descriptions:
|
||||
/// e.g. the default `1` for single-fraction fluid columns,
|
||||
/// `5em` for a static column,
|
||||
/// or even `5em 120px` if you are alternating column widths.
|
||||
///
|
||||
/// @return {list} -
|
||||
/// List of repeated values
|
||||
///
|
||||
/// @example scss
|
||||
/// // 12 column grid, with 5em columns
|
||||
/// $susy: (
|
||||
/// columns: susy-repeat(12, 5em),
|
||||
/// );
|
||||
///
|
||||
/// @example scss
|
||||
/// // asymmetrical 5-column grid
|
||||
/// $susy: (
|
||||
/// columns: 20px susy-repeat(3, 100px) 20px,
|
||||
/// );
|
||||
///
|
||||
/// /* result: #{susy-get('columns')} */
|
||||
@function susy-repeat(
|
||||
$count,
|
||||
$value: 1
|
||||
) {
|
||||
$return: ();
|
||||
|
||||
@for $i from 1 through $count {
|
||||
$return: join($return, $value);
|
||||
}
|
||||
|
||||
@return $return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Susy Defaults
|
||||
// -------------
|
||||
/// Configuration map of Susy factory defaults.
|
||||
/// Do not override this map directly –
|
||||
/// use `$susy` for user and project setting overrides.
|
||||
///
|
||||
/// @access private
|
||||
/// @type Map
|
||||
///
|
||||
/// @see $susy
|
||||
///
|
||||
/// @prop {number | list} columns [susy-repeat(4)]
|
||||
/// @prop {number} gutters [0.25]
|
||||
/// @prop {string} spread ['narrow']
|
||||
/// @prop {string} container-spread ['narrow']
|
||||
$_susy-defaults: (
|
||||
'columns': susy-repeat(4),
|
||||
'gutters': 0.25,
|
||||
'spread': 'narrow',
|
||||
'container-spread': 'narrow',
|
||||
);
|
||||
|
||||
|
||||
|
||||
// Susy Settings
|
||||
// -------------
|
||||
/// Return a combined map of Susy settings,
|
||||
/// based on the factory defaults (`$_susy-defaults`),
|
||||
/// user-defined project configuration (`$susy`),
|
||||
/// and any local overrides required –
|
||||
/// such as a configuration map passed into a function.
|
||||
///
|
||||
/// @group a-config
|
||||
///
|
||||
/// @param {maps} $overrides… -
|
||||
/// Optional map override of global configuration settings.
|
||||
/// See `$susy` above for properties.
|
||||
///
|
||||
/// @return {map} -
|
||||
/// Combined map of Susy configuration settings,
|
||||
/// in order of specificity:
|
||||
/// any `$overrides...`,
|
||||
/// then `$susy` project settings,
|
||||
/// and finally the `$_susy-defaults`
|
||||
///
|
||||
/// @example scss - global settings
|
||||
/// @each $key, $value in susy-settings() {
|
||||
/// /* #{$key}: #{$value} */
|
||||
/// }
|
||||
///
|
||||
/// @example scss - local settings
|
||||
/// $local: ('columns': 1 2 3 5 8);
|
||||
///
|
||||
/// @each $key, $value in susy-settings($local) {
|
||||
/// /* #{$key}: #{$value} */
|
||||
/// }
|
||||
@function susy-settings(
|
||||
$overrides...
|
||||
) {
|
||||
$settings: map-merge($_susy-defaults, $susy);
|
||||
|
||||
@each $config in $overrides {
|
||||
$settings: map-merge($settings, $config);
|
||||
}
|
||||
|
||||
@return $settings;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Susy Get
|
||||
// --------
|
||||
/// Return the current global value of any Susy setting
|
||||
///
|
||||
/// @group a-config
|
||||
///
|
||||
/// @param {string} $key -
|
||||
/// Setting to retrieve from the configuration.
|
||||
///
|
||||
/// @return {*} -
|
||||
/// Value mapped to `$key` in the configuration maps,
|
||||
/// in order of specificity:
|
||||
/// `$susy`, then `$_susy-defaults`
|
||||
///
|
||||
/// @example scss -
|
||||
/// /* columns: #{susy-get('columns')} */
|
||||
/// /* gutters: #{susy-get('gutters')} */
|
||||
@function susy-get(
|
||||
$key
|
||||
) {
|
||||
$settings: susy-settings();
|
||||
|
||||
@if not map-has-key($settings, $key) {
|
||||
@return _susy-error(
|
||||
'There is no Susy setting called `#{$key}`',
|
||||
'susy-get');
|
||||
}
|
||||
|
||||
@return map-get($settings, $key);
|
||||
}
|
|
@ -0,0 +1,441 @@
|
|||
/// Grid Math Engine
|
||||
/// ================
|
||||
/// The `su` functions give you direct access to the math layer,
|
||||
/// without any syntax-sugar like shorthand parsing, and normalization.
|
||||
/// If you prefer named arguments, and stripped-down syntax,
|
||||
/// you can use these functions directly in your code –
|
||||
/// replacing `span`, `gutter`, and `slice`.
|
||||
///
|
||||
/// These functions are also useful
|
||||
/// for building mixins or other extensions to Susy.
|
||||
/// Apply the Susy syntax to new mixins and functions,
|
||||
/// using our "Plugin Helpers",
|
||||
/// or write your own syntax and pass the normalized results along
|
||||
/// to `su` for compilation.
|
||||
///
|
||||
/// @group su-math
|
||||
///
|
||||
/// @see su-span
|
||||
/// @see su-gutter
|
||||
/// @see su-slice
|
||||
/// @ignore _su-sum
|
||||
/// @ignore _su-calc-span
|
||||
/// @ignore _su-calc-sum
|
||||
/// @ignore _su-needs-calc-output
|
||||
|
||||
|
||||
|
||||
// Su Span
|
||||
// -------
|
||||
/// Calculates and returns a CSS-ready span width,
|
||||
/// based on normalized span and context data –
|
||||
/// a low-level version of `susy-span`,
|
||||
/// with all of the logic and none of the syntax sugar.
|
||||
///
|
||||
/// - Grids defined with unitless numbers will return `%` values.
|
||||
/// - Grids defined with comparable units
|
||||
/// will return a value in the units provided.
|
||||
/// - Grids defined with a mix of units,
|
||||
/// or a combination of untiless numbers and unit-lengths,
|
||||
/// will return a `calc()` string.
|
||||
///
|
||||
/// @group su-math
|
||||
/// @see susy-span
|
||||
///
|
||||
/// @param {number | list} $span -
|
||||
/// Number or list of grid columns to span
|
||||
/// @param {list} $columns -
|
||||
/// List of columns available
|
||||
/// @param {number} $gutters -
|
||||
/// Width of a gutter in column-comparable units
|
||||
/// @param {0 | 1 | -1} $spread -
|
||||
/// Number of gutters spanned,
|
||||
/// relative to `span` count
|
||||
/// @param {0 | 1 | -1} $container-spread [$spread] -
|
||||
/// Number of gutters spanned,
|
||||
/// relative to `columns` count
|
||||
/// @param {integer} $location [1] -
|
||||
/// Optional position of sub-span among full set of columns
|
||||
///
|
||||
/// @return {length} -
|
||||
/// Relative or static length of a span on the grid
|
||||
@function su-span(
|
||||
$span,
|
||||
$columns,
|
||||
$gutters,
|
||||
$spread,
|
||||
$container-spread: $spread,
|
||||
$location: 1
|
||||
) {
|
||||
$span: su-valid-span($span);
|
||||
$columns: su-valid-columns($columns);
|
||||
$gutters: su-valid-gutters($gutters);
|
||||
$spread: su-valid-spread($spread);
|
||||
|
||||
@if (type-of($span) == 'number') {
|
||||
@if (not unitless($span)) {
|
||||
@return $span;
|
||||
}
|
||||
|
||||
$location: su-valid-location($span, $location, $columns);
|
||||
$span: su-slice($span, $columns, $location, $validate: false);
|
||||
}
|
||||
|
||||
@if _su-needs-calc-output($span, $columns, $gutters, $spread, not 'validate') {
|
||||
@return _su-calc-span($span, $columns, $gutters, $spread, $container-spread, not 'validate');
|
||||
}
|
||||
|
||||
$span-width: _su-sum($span, $gutters, $spread, $validate: false);
|
||||
|
||||
@if unitless($span-width) {
|
||||
$container-spread: su-valid-spread($container-spread);
|
||||
$container: _su-sum($columns, $gutters, $container-spread, $validate: false);
|
||||
@return percentage(calc($span-width / $container));
|
||||
}
|
||||
|
||||
@return $span-width;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Su Gutter
|
||||
// ---------
|
||||
/// Calculates and returns a CSS-ready gutter width,
|
||||
/// based on normalized grid data –
|
||||
/// a low-level version of `susy-gutter`,
|
||||
/// with all of the logic and none of the syntax sugar.
|
||||
///
|
||||
/// - Grids defined with unitless numbers will return `%` values.
|
||||
/// - Grids defined with comparable units
|
||||
/// will return a value in the units provided.
|
||||
/// - Grids defined with a mix of units,
|
||||
/// or a combination of untiless numbers and unit-lengths,
|
||||
/// will return a `calc()` string.
|
||||
///
|
||||
/// @group su-math
|
||||
/// @see susy-gutter
|
||||
///
|
||||
/// @param {list} $columns -
|
||||
/// List of columns in the grid
|
||||
/// @param {number} $gutters -
|
||||
/// Width of a gutter in column-comparable units
|
||||
/// @param {0 | 1 | -1} $container-spread -
|
||||
/// Number of gutters spanned,
|
||||
/// relative to `columns` count
|
||||
///
|
||||
/// @return {length} -
|
||||
/// Relative or static length of one gutter in a grid
|
||||
@function su-gutter(
|
||||
$columns,
|
||||
$gutters,
|
||||
$container-spread
|
||||
) {
|
||||
@if (type-of($gutters) == 'number') {
|
||||
@if ($gutters == 0) or (not unitless($gutters)) {
|
||||
@return $gutters;
|
||||
}
|
||||
}
|
||||
|
||||
@if _su-needs-calc-output($gutters, $columns, $gutters, -1, not 'validate') {
|
||||
@return _su-calc-span($gutters, $columns, $gutters, -1, $container-spread, not 'validate');
|
||||
}
|
||||
|
||||
$container: _su-sum($columns, $gutters, $container-spread);
|
||||
@return percentage(calc($gutters / $container));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Su Slice
|
||||
// --------
|
||||
/// Returns a list of columns
|
||||
/// based on a given span/location slice of the grid –
|
||||
/// a low-level version of `susy-slice`,
|
||||
/// with all of the logic and none of the syntax sugar.
|
||||
///
|
||||
/// @group su-math
|
||||
/// @see susy-slice
|
||||
///
|
||||
/// @param {number} $span -
|
||||
/// Number of grid columns to span
|
||||
/// @param {list} $columns -
|
||||
/// List of columns in the grid
|
||||
/// @param {number} $location [1] -
|
||||
/// Starting index of a span in the list of columns
|
||||
/// @param {bool} $validate [true] -
|
||||
/// Check that arguments are valid before proceeding
|
||||
///
|
||||
/// @return {list} -
|
||||
/// Subset list of grid columns, based on span and location
|
||||
@function su-slice(
|
||||
$span,
|
||||
$columns,
|
||||
$location: 1,
|
||||
$validate: true
|
||||
) {
|
||||
@if $validate {
|
||||
$columns: su-valid-columns($columns);
|
||||
$location: su-valid-location($span, $location, $columns);
|
||||
}
|
||||
|
||||
$floor: floor($span);
|
||||
$sub-columns: ();
|
||||
|
||||
@for $i from $location to ($location + $floor) {
|
||||
$sub-columns: append($sub-columns, nth($columns, $i));
|
||||
}
|
||||
|
||||
@if $floor != $span {
|
||||
$remainder: $span - $floor;
|
||||
$column: $location + $floor;
|
||||
$sub-columns: append($sub-columns, nth($columns, $column) * $remainder);
|
||||
}
|
||||
|
||||
@return $sub-columns;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Su Sum
|
||||
// ------
|
||||
/// Get the total sum of column-units in a layout.
|
||||
///
|
||||
/// @group su-math
|
||||
/// @access private
|
||||
///
|
||||
/// @param {list} $columns -
|
||||
/// List of columns in the grid
|
||||
/// @param {number} $gutters -
|
||||
/// Width of a gutter in column-comparable units
|
||||
/// @param {0 | 1 | -1} $spread -
|
||||
/// Number of gutters spanned,
|
||||
/// relative to `columns` count
|
||||
/// @param {bool} $validate [true] -
|
||||
/// Check that arguments are valid before proceeding
|
||||
///
|
||||
/// @return {number} -
|
||||
/// Total sum of column-units in a grid
|
||||
@function _su-sum(
|
||||
$columns,
|
||||
$gutters,
|
||||
$spread,
|
||||
$validate: true
|
||||
) {
|
||||
@if $validate {
|
||||
$columns: su-valid-span($columns);
|
||||
$gutters: su-valid-gutters($gutters);
|
||||
$spread: su-valid-spread($spread);
|
||||
}
|
||||
|
||||
// Calculate column-sum
|
||||
$column-sum: 0;
|
||||
@each $column in $columns {
|
||||
$column-sum: $column-sum + $column;
|
||||
}
|
||||
|
||||
$gutter-sum: (ceil(length($columns)) + $spread) * $gutters;
|
||||
$total: if(($gutter-sum > 0), $column-sum + $gutter-sum, $column-sum);
|
||||
|
||||
@return $total;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Su Calc
|
||||
// -------
|
||||
/// Return a usable span width as a `calc()` function,
|
||||
/// in order to create mixed-unit grids.
|
||||
///
|
||||
/// @group su-math
|
||||
/// @access private
|
||||
///
|
||||
/// @param {number | list} $span -
|
||||
/// Pre-sliced list of grid columns to span
|
||||
/// @param {list} $columns -
|
||||
/// List of columns available
|
||||
/// @param {number} $gutters -
|
||||
/// Width of a gutter in column-comparable units
|
||||
/// @param {0 | 1 | -1} $spread -
|
||||
/// Number of gutters spanned,
|
||||
/// relative to `span` count
|
||||
/// @param {0 | 1 | -1} $container-spread [$spread] -
|
||||
/// Number of gutters spanned,
|
||||
/// relative to `columns` count
|
||||
/// @param {bool} $validate [true] -
|
||||
/// Check that arguments are valid before proceeding
|
||||
///
|
||||
/// @return {length} -
|
||||
/// Relative or static length of a span on the grid
|
||||
@function _su-calc-span(
|
||||
$span,
|
||||
$columns,
|
||||
$gutters,
|
||||
$spread,
|
||||
$container-spread: $spread,
|
||||
$validate: true
|
||||
) {
|
||||
@if $validate {
|
||||
$span: su-valid-span($span);
|
||||
$columns: su-valid-columns($columns);
|
||||
$gutters: su-valid-gutters($gutters);
|
||||
$spread: su-valid-spread($spread);
|
||||
$container-spread: su-valid-spread($container-spread);
|
||||
}
|
||||
|
||||
// Span and context
|
||||
$span: _su-calc-sum($span, $gutters, $spread, not 'validate');
|
||||
$context: _su-calc-sum($columns, $gutters, $container-spread, not 'validate');
|
||||
|
||||
// Fixed and fluid
|
||||
$fixed-span: map-get($span, 'fixed');
|
||||
$fluid-span: map-get($span, 'fluid');
|
||||
$fixed-context: map-get($context, 'fixed');
|
||||
$fluid-context: map-get($context, 'fluid');
|
||||
|
||||
$calc: '#{$fixed-span}';
|
||||
$fluid-calc: '(100% - #{$fixed-context})';
|
||||
|
||||
// Fluid-values
|
||||
@if (not $fluid-span) {
|
||||
$fluid-calc: null;
|
||||
} @else if ($fluid-span != $fluid-context) {
|
||||
$fluid-span: '* #{$fluid-span}';
|
||||
$fluid-context: if($fluid-context, '/ #{$fluid-context}', '');
|
||||
$fluid-calc: '(#{$fluid-calc $fluid-context $fluid-span})';
|
||||
}
|
||||
|
||||
@if $fluid-calc {
|
||||
$calc: if(($calc != ''), '#{$calc} + ', '');
|
||||
$calc: '#{$calc + $fluid-calc}';
|
||||
}
|
||||
|
||||
@return calc(#{unquote($calc)});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Su Calc-Sum
|
||||
// -----------
|
||||
/// Get the total sum of fixed and fluid column-units
|
||||
/// for creating a mixed-unit layout with `calc()` values.
|
||||
///
|
||||
/// @group su-math
|
||||
/// @access private
|
||||
///
|
||||
/// @param {list} $columns -
|
||||
/// List of columns available
|
||||
/// @param {number} $gutters -
|
||||
/// Width of a gutter in column-comparable units
|
||||
/// @param {0 | 1 | -1} $spread -
|
||||
/// Number of gutters spanned,
|
||||
/// relative to `span` count
|
||||
/// @param {bool} $validate [true] -
|
||||
/// Check that arguments are valid before proceeding
|
||||
///
|
||||
/// @return {map} -
|
||||
/// Map with `fixed` and `fluid` keys
|
||||
/// containing the proper math as strings
|
||||
@function _su-calc-sum(
|
||||
$columns,
|
||||
$gutters,
|
||||
$spread,
|
||||
$validate: true
|
||||
) {
|
||||
@if $validate {
|
||||
$columns: su-valid-span($columns);
|
||||
$gutters: su-valid-gutters($gutters);
|
||||
$spread: su-valid-spread($spread);
|
||||
}
|
||||
|
||||
$fluid: 0;
|
||||
$fixed: ();
|
||||
$calc: null;
|
||||
|
||||
// Gutters
|
||||
$gutters: $gutters * (length($columns) + $spread);
|
||||
|
||||
// Columns
|
||||
@each $col in append($columns, $gutters) {
|
||||
@if unitless($col) {
|
||||
$fluid: $fluid + $col;
|
||||
} @else {
|
||||
$fixed: _su-map-add-units($fixed, $col);
|
||||
}
|
||||
}
|
||||
|
||||
// Compile Fixed Units
|
||||
@each $unit, $total in $fixed {
|
||||
@if ($total != (0 * $total)) {
|
||||
$calc: if($calc, '#{$calc} + #{$total}', '#{$total}');
|
||||
}
|
||||
}
|
||||
|
||||
// Calc null or string
|
||||
@if $calc {
|
||||
$calc: if(str-index($calc, '+'), '(#{$calc})', '#{$calc}');
|
||||
}
|
||||
|
||||
// Fluid 0 => null
|
||||
$fluid: if(($fluid == 0), null, $fluid);
|
||||
|
||||
|
||||
// Return map
|
||||
$return: (
|
||||
'fixed': $calc,
|
||||
'fluid': $fluid,
|
||||
);
|
||||
|
||||
@return $return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Needs Calc
|
||||
// ----------
|
||||
/// Check if `calc()` will be needed in defining a span,
|
||||
/// if the necessary units in a grid are not comparable.
|
||||
///
|
||||
/// @group su-math
|
||||
/// @access private
|
||||
///
|
||||
/// @param {list} $span -
|
||||
/// Slice of columns to span
|
||||
/// @param {list} $columns -
|
||||
/// List of available columns in the grid
|
||||
/// @param {number} $gutters -
|
||||
/// Width of a gutter
|
||||
/// @param {0 | 1 | -1} $spread -
|
||||
/// Number of gutters spanned,
|
||||
/// relative to `span` count
|
||||
/// @param {bool} $validate [true] -
|
||||
/// Check that arguments are valid before proceeding
|
||||
///
|
||||
/// @return {bool} -
|
||||
/// `True` when units do not match, and `calc()` will be required
|
||||
@function _su-needs-calc-output(
|
||||
$span,
|
||||
$columns,
|
||||
$gutters,
|
||||
$spread,
|
||||
$validate: true
|
||||
) {
|
||||
@if $validate {
|
||||
$span: su-valid-span($span);
|
||||
$columns: su-valid-columns($columns);
|
||||
$gutters: su-valid-gutters($gutters);
|
||||
}
|
||||
|
||||
$has-gutter: if((length($span) > 1) or ($spread >= 0), true, false);
|
||||
$check: if($has-gutter, append($span, $gutters), $span);
|
||||
$safe-span: _su-is-comparable($check...);
|
||||
|
||||
@if ($safe-span == 'static') {
|
||||
@return false;
|
||||
} @else if (not $safe-span) {
|
||||
@return true;
|
||||
}
|
||||
|
||||
$safe-fluid: _su-is-comparable($gutters, $columns...);
|
||||
|
||||
@return not $safe-fluid;
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
/// Validation
|
||||
/// ==========
|
||||
/// Each argument to Su has a single canonical syntax.
|
||||
/// These validation functions check to ensure
|
||||
/// that each argument is valid,
|
||||
/// in order to provide useful errors
|
||||
/// before attempting to calculate the results/
|
||||
///
|
||||
/// @group x-validation
|
||||
///
|
||||
/// @see su-valid-columns
|
||||
/// @see su-valid-gutters
|
||||
/// @see su-valid-spread
|
||||
/// @see su-valid-location
|
||||
|
||||
|
||||
|
||||
// Valid Span
|
||||
// ----------
|
||||
/// Check that the `span` argument
|
||||
/// is a number, length, or column-list
|
||||
///
|
||||
/// @group x-validation
|
||||
///
|
||||
/// @param {number | list} $span -
|
||||
/// Number of columns, or length of span
|
||||
///
|
||||
/// @return {number | list} -
|
||||
/// Validated `$span` number, length, or columns list
|
||||
///
|
||||
/// @throw
|
||||
/// when span value is not a number, or valid column list
|
||||
@function su-valid-span(
|
||||
$span
|
||||
) {
|
||||
$type: type-of($span);
|
||||
@if ($type == 'number') {
|
||||
@return $span;
|
||||
} @else if ($type == 'list') and su-valid-columns($span, 'silent-failure') {
|
||||
@return $span;
|
||||
}
|
||||
|
||||
$actual: '[#{type-of($span)}] `#{inspect($span)}`';
|
||||
@return _susy-error(
|
||||
'#{$actual} is not a valid number, length, or column-list for $span.',
|
||||
'su-valid-span');
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Valid Columns
|
||||
// -------------
|
||||
/// Check that the `columns` argument is a valid
|
||||
/// list of column-lengths
|
||||
///
|
||||
/// @group x-validation
|
||||
///
|
||||
/// @param {list} $columns -
|
||||
/// List of column-lengths
|
||||
/// @param {bool} $silent-failure [true] -
|
||||
/// Set false to return null on failure
|
||||
///
|
||||
/// @return {list} -
|
||||
/// Validated `$columns` list
|
||||
///
|
||||
/// @throw
|
||||
/// when column value is not a valid list of numbers
|
||||
@function su-valid-columns(
|
||||
$columns,
|
||||
$silent-failure: false
|
||||
) {
|
||||
@if (type-of($columns) == 'list') {
|
||||
$fail: false;
|
||||
|
||||
@each $col in $columns {
|
||||
@if (type-of($col) != 'number') {
|
||||
$fail: true;
|
||||
}
|
||||
}
|
||||
|
||||
@if not $fail {
|
||||
@return $columns;
|
||||
}
|
||||
}
|
||||
|
||||
// Silent Failure
|
||||
@if $silent-failure {
|
||||
@return null;
|
||||
}
|
||||
|
||||
// Error Message
|
||||
$actual: '[#{type-of($columns)}] `#{inspect($columns)}`';
|
||||
|
||||
@return _susy-error(
|
||||
'#{$actual} is not a valid list of numbers for $columns.',
|
||||
'su-valid-columns');
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Valid Gutters
|
||||
// -------------
|
||||
/// Check that the `gutters` argument is a valid number
|
||||
///
|
||||
/// @group x-validation
|
||||
///
|
||||
/// @param {number} $gutters -
|
||||
/// Width of a gutter
|
||||
///
|
||||
/// @return {number} -
|
||||
/// Validated `$gutters` number
|
||||
///
|
||||
/// @throw
|
||||
/// when gutter value is not a number
|
||||
@function su-valid-gutters(
|
||||
$gutters
|
||||
) {
|
||||
$type: type-of($gutters);
|
||||
|
||||
@if ($type == 'number') {
|
||||
@return $gutters;
|
||||
}
|
||||
|
||||
$actual: '[#{$type}] `#{inspect($gutters)}`';
|
||||
@return _susy-error(
|
||||
'#{$actual} is not a number or length for $gutters.',
|
||||
'su-valid-gutters');
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Valid Spread
|
||||
// ------------
|
||||
/// Check that the `spread` argument is a valid
|
||||
/// intiger between `-1` and `1`
|
||||
///
|
||||
/// @group x-validation
|
||||
///
|
||||
/// @param {0 | 1 | -1} $spread -
|
||||
/// Number of gutters to include in a span,
|
||||
/// relative to the number columns
|
||||
///
|
||||
/// @return {0 | 1 | -1} -
|
||||
/// Validated `$spread` number
|
||||
///
|
||||
/// @throw
|
||||
/// when spread value is not a valid spread
|
||||
@function su-valid-spread(
|
||||
$spread
|
||||
) {
|
||||
@if index(0 1 -1, $spread) {
|
||||
@return $spread;
|
||||
}
|
||||
|
||||
$actual: '[#{type-of($spread)}] `#{inspect($spread)}`';
|
||||
@return _susy-error(
|
||||
'#{$actual} is not a normalized [0 | 1 | -1] value for `$spread`.',
|
||||
'su-valid-spread');
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Valid Location
|
||||
// --------------
|
||||
/// Check that the `location` argument is a valid number,
|
||||
/// within the scope of available columns
|
||||
///
|
||||
/// @group x-validation
|
||||
///
|
||||
/// @param {number} $span -
|
||||
/// Number of grid-columns to be spanned
|
||||
/// @param {integer | string} $location -
|
||||
/// Starting (1-indexed) column-position of that span
|
||||
/// @param {list} $columns -
|
||||
/// List of available columns in the grid
|
||||
///
|
||||
/// @return {integer} -
|
||||
/// Validated `$location` intiger
|
||||
///
|
||||
/// @throw
|
||||
/// when location value is not a valid index,
|
||||
/// given the context and span.
|
||||
@function su-valid-location(
|
||||
$span,
|
||||
$location,
|
||||
$columns
|
||||
) {
|
||||
$count: length($columns);
|
||||
|
||||
@if $location {
|
||||
@if (type-of($location) != 'number') or (not unitless($location)) {
|
||||
$actual: '[#{type-of($location)}] `#{$location}`';
|
||||
@return _susy-error(
|
||||
'#{$actual} is not a unitless number for $location.',
|
||||
'su-valid-location');
|
||||
} @else if (round($location) != $location) {
|
||||
@return _susy-error(
|
||||
'Location (`#{$location}`) must be a 1-indexed intiger position.',
|
||||
'su-valid-location');
|
||||
} @else if ($location > $count) or ($location < 1) {
|
||||
@return _susy-error(
|
||||
'Position `#{$location}` does not exist in grid `#{$columns}`.',
|
||||
'su-valid-location');
|
||||
} @else if ($location + $span - 1 > $count) {
|
||||
$details: 'grid `#{$columns}` for span `#{$span}` at `#{$location}`';
|
||||
@return _susy-error(
|
||||
'There are not enough columns in #{$details}.',
|
||||
'su-valid-location');
|
||||
}
|
||||
}
|
||||
|
||||
@return $location;
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
/// Syntax Utilities for Extending Susy
|
||||
/// ===================================
|
||||
/// There are many steps involved
|
||||
/// when translating between the Susy syntax layer,
|
||||
/// and the Su core math.
|
||||
/// That entire process can be condensed with these two functions.
|
||||
/// For anyone that wants to access the full power of Susy,
|
||||
/// and build their own plugins, functions, or mixins –
|
||||
/// this is the primary API for compiling user input,
|
||||
/// and accessing the core math.
|
||||
///
|
||||
/// This is the same technique we use internally,
|
||||
/// to keep our API layer simple and light-weight.
|
||||
/// Every function accepts two arguments,
|
||||
/// a "shorthand" description of the span or context,
|
||||
/// and an optional settings-map to override global defaults.
|
||||
///
|
||||
/// - Use `susy-compile()` to parse, merge, and normalize
|
||||
/// all the user settings into a single map.
|
||||
/// - Then use `su-call()` to call one of the core math functions,
|
||||
/// with whatever data is needed for that function.
|
||||
///
|
||||
/// @group plugin-utils
|
||||
/// @see susy-compile
|
||||
/// @see su-call
|
||||
///
|
||||
/// @example scss - Susy API `gutter` function
|
||||
/// @function susy-gutter(
|
||||
/// $context: susy-get('columns'),
|
||||
/// $config: ()
|
||||
/// ) {
|
||||
/// // compile and normalize all user arguments and global settings
|
||||
/// $context: susy-compile($context, $config, 'context-only');
|
||||
/// // call `su-gutter` with the appropriate data
|
||||
/// @return su-call('su-gutter', $context);
|
||||
/// }
|
||||
///
|
||||
/// @example scss - Sample `span` mixin for floated grids
|
||||
/// @mixin span(
|
||||
/// $span,
|
||||
/// $config: ()
|
||||
/// ) {
|
||||
/// $context: susy-compile($span, $config);
|
||||
/// width: su-call('su-span', $context);
|
||||
///
|
||||
/// @if index($span, 'last') {
|
||||
/// float: right;
|
||||
/// } @else {
|
||||
/// float: left;
|
||||
/// margin-right: su-call('su-gutter', $context);
|
||||
/// }
|
||||
/// }
|
||||
|
||||
|
||||
|
||||
// Compile
|
||||
// -------
|
||||
/// Susy's syntax layer has various moving parts,
|
||||
/// with syntax-parsing for the grid/span shorthand,
|
||||
/// and normalization for each of the resulting values.
|
||||
/// The compile function rolls this all together
|
||||
/// in a single call –
|
||||
/// for quick access from our internal API functions,
|
||||
/// or any additional functions and mixins you add to your project.
|
||||
/// Pass user input and configuration maps to the compiler,
|
||||
/// and it will hand back a map of values ready for Su.
|
||||
/// Combine this with the `su-call` function
|
||||
/// to quickly parse, normalize, and process grid calculations.
|
||||
///
|
||||
/// @group plugin-utils
|
||||
/// @see su-call
|
||||
///
|
||||
/// @param {list | map} $shorthand -
|
||||
/// Shorthand expression to define the width of the span,
|
||||
/// optionally containing:
|
||||
/// - a count, length, or column-list span;
|
||||
/// - `at $n`, `first`, or `last` location on asymmetrical grids;
|
||||
/// - `narrow`, `wide`, or `wider` for optionally spreading
|
||||
/// across adjacent gutters;
|
||||
/// - `of $n <spread>` for available grid columns
|
||||
/// and spread of the container
|
||||
/// (span counts like `of 6` are only valid
|
||||
/// in the context of symmetrical grids);
|
||||
/// - and `set-gutters $n` to override global gutter settings
|
||||
/// @param {map} $config [null] -
|
||||
/// Optional map of Susy grid configuration settings
|
||||
/// @param {bool} $context-only [false] -
|
||||
/// Allow the parser to ignore span and span-spread values,
|
||||
/// only parsing context and container-spread
|
||||
///
|
||||
/// @return {map} -
|
||||
/// Parsed and normalized map of settings,
|
||||
/// based on global and local configuration,
|
||||
/// alongwith shorthad adjustments.
|
||||
///
|
||||
/// @example scss -
|
||||
/// $user-input: 3 wide of susy-repeat(6, 120px) set-gutters 10px;
|
||||
/// $grid-data: susy-compile($user-input, $susy);
|
||||
///
|
||||
/// @each $key, $value in $grid-data {
|
||||
/// /* #{$key}: #{$value}, */
|
||||
/// }
|
||||
@function susy-compile(
|
||||
$short,
|
||||
$config: null,
|
||||
$context-only: false
|
||||
) {
|
||||
// Get and normalize config
|
||||
$config: if($config, susy-settings($config), susy-settings());
|
||||
$normal-config: susy-normalize($config);
|
||||
|
||||
// Parse and normalize shorthand
|
||||
@if (type-of($short) != 'map') and (length($short) > 0) {
|
||||
$short: susy-parse($short, $context-only);
|
||||
}
|
||||
|
||||
$normal-short: susy-normalize($short, $normal-config);
|
||||
|
||||
// Merge and return
|
||||
@return map-merge($normal-config, $normal-short);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Call
|
||||
// ----
|
||||
/// The Susy parsing and normalization process
|
||||
/// results in a map of configuration settings,
|
||||
/// much like the global `$susy` settings map.
|
||||
/// In order to pass that information along to Su math functions,
|
||||
/// the proper values have to be picked out,
|
||||
/// and converted to arguments.
|
||||
///
|
||||
/// The `su-call` function streamlines that process,
|
||||
/// weeding out the unnecessary data,
|
||||
/// and passing the rest along to Su in the proper format.
|
||||
/// Combine this with `susy-compile` to quickly parse,
|
||||
/// normalize, and process grid calculations.
|
||||
///
|
||||
/// @group plugin-utils
|
||||
///
|
||||
/// @require su-span
|
||||
/// @require su-gutter
|
||||
/// @require su-slice
|
||||
/// @see susy-compile
|
||||
///
|
||||
/// @param {'su-span' | 'su-gutter' | 'su-slice'} $name -
|
||||
/// Name of the Su math function to call.
|
||||
/// @param {map} $config -
|
||||
/// Parsed and normalized map of Susy configuration settings
|
||||
/// to use for math-function arguments.
|
||||
///
|
||||
/// @return {*} -
|
||||
/// Results of the function being called.
|
||||
///
|
||||
/// @example scss -
|
||||
/// $user-input: 3 wide of susy-repeat(6, 120px) set-gutters 10px;
|
||||
/// $grid-data: susy-compile($user-input, $susy);
|
||||
///
|
||||
/// .su-span {
|
||||
/// width: su-call('su-span', $grid-data);
|
||||
/// }
|
||||
@function su-call(
|
||||
$name,
|
||||
$config
|
||||
) {
|
||||
$grid-function-args: (
|
||||
'su-span': ('span', 'columns', 'gutters', 'spread', 'container-spread', 'location'),
|
||||
'su-gutter': ('columns', 'gutters', 'container-spread'),
|
||||
'su-slice': ('span', 'columns', 'location'),
|
||||
);
|
||||
|
||||
$args: map-get($grid-function-args, $name);
|
||||
|
||||
@if not $args {
|
||||
$options: 'Try one of these: #{map-keys($grid-function-args)}';
|
||||
@return _susy-error(
|
||||
'#{$name} is not a public Su function. #{$options}',
|
||||
'su-call');
|
||||
}
|
||||
|
||||
$call: if(function-exists('get-function'), get-function($name), $name);
|
||||
$output: ();
|
||||
|
||||
@each $arg in $args {
|
||||
$value: map-get($config, $arg);
|
||||
$output: if($value, map-merge($output, ($arg: $value)), $output);
|
||||
}
|
||||
|
||||
@return call($call, $output...);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// Unprefix Susy
|
||||
// =============
|
||||
|
||||
|
||||
// Span
|
||||
// ----
|
||||
/// Un-prefixed alias for `susy-span`
|
||||
/// (available by default)
|
||||
///
|
||||
/// @group api
|
||||
/// @alias susy-span
|
||||
///
|
||||
/// @param {list} $span
|
||||
/// @param {map} $config [()]
|
||||
@function span(
|
||||
$span,
|
||||
$config: ()
|
||||
) {
|
||||
@return susy-span($span, $config);
|
||||
}
|
||||
|
||||
|
||||
// Gutter
|
||||
// ------
|
||||
/// Un-prefixed alias for `susy-gutter`
|
||||
/// (available by default)
|
||||
///
|
||||
/// @group api
|
||||
/// @alias susy-gutter
|
||||
///
|
||||
/// @param {integer | list} $context [null] -
|
||||
/// @param {map} $config [()]
|
||||
@function gutter(
|
||||
$context: susy-get('columns'),
|
||||
$config: ()
|
||||
) {
|
||||
@return susy-gutter($context, $config);
|
||||
}
|
||||
|
||||
|
||||
// Slice
|
||||
// -----
|
||||
/// Un-prefixed alias for `susy-slice`
|
||||
/// (available by default)
|
||||
///
|
||||
/// @group api
|
||||
/// @alias susy-slice
|
||||
///
|
||||
/// @param {list} $span
|
||||
/// @param {map} $config [()]
|
||||
@function slice(
|
||||
$span,
|
||||
$config: ()
|
||||
) {
|
||||
@return susy-slice($span, $config);
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
// Sass Utilities
|
||||
// ==============
|
||||
// - Susy Error Output Override [variable]
|
||||
// - Susy Error [function]
|
||||
|
||||
|
||||
|
||||
// Susy Error Output Override
|
||||
// --------------------------
|
||||
/// Turn off error output for testing
|
||||
/// @group x-utility
|
||||
/// @access private
|
||||
$_susy-error-output-override: false !default;
|
||||
|
||||
|
||||
|
||||
// Susy Error
|
||||
// ----------
|
||||
/// Optionally return error messages without failing,
|
||||
/// as a way to test error cases
|
||||
///
|
||||
/// @group x-utility
|
||||
/// @access private
|
||||
///
|
||||
/// @param {string} $message -
|
||||
/// A useful error message, explaining the problem
|
||||
/// @param {string} $source -
|
||||
/// The original source of the error for debugging
|
||||
/// @param {bool} $override [$_susy-error-output-override] -
|
||||
/// Optionally return the error rather than failing
|
||||
/// @return {string} -
|
||||
/// Combined error with source and message
|
||||
/// @throws When `$override == true`
|
||||
@function _susy-error(
|
||||
$message,
|
||||
$source,
|
||||
$override: $_susy-error-output-override
|
||||
) {
|
||||
@if $override {
|
||||
@return 'ERROR [#{$source}] #{$message}';
|
||||
}
|
||||
|
||||
@error '[#{$source}] #{$message}';
|
||||
}
|
||||
|
||||
|
||||
// Su Is Comparable
|
||||
// ----------------
|
||||
/// Check that the units in a grid are comparable
|
||||
///
|
||||
/// @group x-validation
|
||||
/// @access private
|
||||
///
|
||||
/// @param {numbers} $lengths… -
|
||||
/// Arglist of all the number values to compare
|
||||
/// (columns, gutters, span, etc)
|
||||
///
|
||||
/// @return {'fluid' | 'static' | false} -
|
||||
/// The type of span (fluid or static) when units match,
|
||||
/// or `false` for mismatched units
|
||||
@function _su-is-comparable(
|
||||
$lengths...
|
||||
) {
|
||||
$first: nth($lengths, 1);
|
||||
|
||||
@if (length($lengths) == 1) {
|
||||
@return if(unitless($first), 'fluid', 'static');
|
||||
}
|
||||
|
||||
@for $i from 2 through length($lengths) {
|
||||
$comp: nth($lengths, $i);
|
||||
|
||||
$fail: not comparable($first, $comp);
|
||||
$fail: $fail or (unitless($first) and not unitless($comp));
|
||||
$fail: $fail or (unitless($comp) and not unitless($first));
|
||||
|
||||
@if $fail {
|
||||
@return false;
|
||||
}
|
||||
}
|
||||
|
||||
@return if(unitless($first), 'fluid', 'static');
|
||||
}
|
||||
|
||||
|
||||
// Su Map Add Units
|
||||
// ----------------
|
||||
/// The calc features use a map of units and values
|
||||
/// to compile the proper algorythm.
|
||||
/// This function adds a new value to any comparable existing unit/value,
|
||||
/// or adds a new unit/value pair to the map
|
||||
///
|
||||
/// @group x-utility
|
||||
/// @access private
|
||||
///
|
||||
/// @param {map} $map -
|
||||
/// A map of unit/value pairs, e.g. ('px': 120px)
|
||||
/// @param {length} $value -
|
||||
/// A new length to be added to the map
|
||||
/// @return {map} -
|
||||
/// The updated map, with new value added
|
||||
///
|
||||
/// @example scss -
|
||||
/// $map: (0px: 120px);
|
||||
/// $map: _su-map-add-units($map, 1in); // add a comparable unit
|
||||
/// $map: _su-map-add-units($map, 3vw); // add a new unit
|
||||
///
|
||||
/// @each $units, $value in $map {
|
||||
/// /* #{$units}: #{$value} */
|
||||
/// }
|
||||
@function _su-map-add-units(
|
||||
$map,
|
||||
$value
|
||||
) {
|
||||
$unit: $value * 0;
|
||||
$has: map-get($map, $unit) or 0;
|
||||
|
||||
@if ($has == 0) {
|
||||
@each $try, $could in $map {
|
||||
$match: comparable($try, $value);
|
||||
$unit: if($match, $try, $unit);
|
||||
$has: if($match, $could, $has);
|
||||
}
|
||||
}
|
||||
|
||||
@return map-merge($map, ($unit: $has + $value));
|
||||
}
|
||||
|
||||
|
||||
// Susy Flatten
|
||||
// ------------
|
||||
/// Flatten a multidimensional list
|
||||
///
|
||||
/// @group x-utility
|
||||
/// @access private
|
||||
///
|
||||
/// @param {list} $list -
|
||||
/// The list to be flattened
|
||||
/// @return {list} -
|
||||
/// The flattened list
|
||||
///
|
||||
/// @example scss -
|
||||
/// $list: 120px (30em 30em) 120px;
|
||||
/// /* #{_susy-flatten($list)} */
|
||||
@function _susy-flatten(
|
||||
$list
|
||||
) {
|
||||
$flat: ();
|
||||
|
||||
// Don't iterate over maps
|
||||
@if (type-of($list) == 'map') {
|
||||
@return $list;
|
||||
}
|
||||
|
||||
// Iterate over lists (or single items)
|
||||
@each $item in $list {
|
||||
@if (type-of($item) == 'list') {
|
||||
$item: _susy-flatten($item);
|
||||
$flat: join($flat, $item);
|
||||
} @else {
|
||||
$flat: append($flat, $item);
|
||||
}
|
||||
}
|
||||
|
||||
// Return flattened list
|
||||
@return $flat;
|
||||
}
|
|
@ -5,143 +5,7 @@ permalink: /assets/css/tocompile.scss
|
|||
|
||||
@charset "utf-8";
|
||||
|
||||
.author__urls.social-icons .svg-inline--fa,
|
||||
.page__footer-follow .social-icons .svg-inline--fa {
|
||||
color: rgba(102, 235, 97, 0.829) !important;
|
||||
}
|
||||
|
||||
/* Colors */
|
||||
$background-color: #141010 !default;
|
||||
$text-color: #fff6fbb9 !default;
|
||||
$primary-color: #ff00ff !default;
|
||||
$link-color: $primary-color !default;
|
||||
$link-color-hover: mix(#fff, $link-color, 25%) !default;
|
||||
$link-color-visited: mix(#000, $link-color, 25%) !default;
|
||||
$code-background-color: mix(rgb(43, 39, 39), $background-color, 15%) !default;
|
||||
$code-background-color-dark: mix(rgb(59, 54, 54), $background-color, 20%) !default;
|
||||
|
||||
/* Notices */
|
||||
|
||||
$success-color: #3fa63f !default;
|
||||
$warning-color: #d67f05 !default;
|
||||
$danger-color: #ff0000 !default;
|
||||
$info-color: #3b9cba !default;
|
||||
|
||||
// section not needed under 11ty
|
||||
/* neon syntax highlighting (base16) */
|
||||
$base00: #1a191a;
|
||||
$base01: #e0e0e0;
|
||||
$base02: #b4a6a6;
|
||||
$base03: #928e8e;
|
||||
$base04: #ccc2c2;
|
||||
$base05: #bd6262;
|
||||
$base06: #a74040;
|
||||
$base07: #701b1b;
|
||||
$base08: #ff0086;
|
||||
$base09: #fd8900;
|
||||
$base0a: #aba800;
|
||||
$base0b: #00c918;
|
||||
$base0c: #1faaaa;
|
||||
$base0d: #3777e6;
|
||||
$base0e: #ad00a1;
|
||||
$base0f: #926e5c;
|
||||
// base16-atelier-heath
|
||||
// base16-synth-midnight-dark
|
||||
// base16-summerfruit-dark
|
||||
// base16-seti
|
||||
// base16-outrun-dark (a bit bright)
|
||||
// base16-material-darker
|
||||
// base16-harmonic-dark
|
||||
// base16-google-dark (little dark for my tastes)
|
||||
// base16-default-dark (little dull)
|
||||
// base16-classic-dark
|
||||
// base16-brewer
|
||||
|
||||
// section not needed under 11ty
|
||||
// Hacking the syntax highlighting
|
||||
.nb {
|
||||
/* Name.Builtin */
|
||||
color: $base0f !important;
|
||||
}
|
||||
|
||||
.o {
|
||||
/* Operator */
|
||||
color: $base04 !important;
|
||||
}
|
||||
.n {
|
||||
/* Name */
|
||||
color: $base05 !important;
|
||||
}
|
||||
.p {
|
||||
/* Punctuation */
|
||||
color: #3ab469 !important;
|
||||
}
|
||||
.sr {
|
||||
/* Literal.String.Regex */
|
||||
color: $base0c !important;
|
||||
}
|
||||
|
||||
// Progress Bar
|
||||
#progress-bar {
|
||||
background: linear-gradient(to right, red, orange , yellow, green,
|
||||
blue, indigo, violet var(--scroll), transparent 0);
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// Scroll to top
|
||||
#scroll-to-top {
|
||||
background: rgb(215, 210, 210);
|
||||
display:block;
|
||||
position:fixed;
|
||||
font-size:25px;
|
||||
bottom:25px;
|
||||
right:10%;
|
||||
opacity:0;
|
||||
z-index:99;
|
||||
text-align:center;
|
||||
transform:translateY(30%);
|
||||
transition:transform 0.5s ease-in-out, opacity 0.5s ease-in-out
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
#scroll-to-top {
|
||||
font-size:20px
|
||||
}
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
#scroll-to-top {
|
||||
right:5%;
|
||||
bottom:15px
|
||||
}
|
||||
}
|
||||
#scroll-to-top :last-child {
|
||||
transition:transform 0.7s ease-in-out
|
||||
}
|
||||
#scroll-to-top :last-child:active,
|
||||
#scroll-to-top :last-child:hover {
|
||||
transform:translateY(-10%)
|
||||
}
|
||||
#scroll-to-top span {
|
||||
cursor:pointer;
|
||||
color:#dee3ee
|
||||
}
|
||||
#scroll-to-top span:hover .up-arrow,
|
||||
#scroll-to-top span:active .up-arrow {
|
||||
color:#00adb5
|
||||
}
|
||||
|
||||
tt,
|
||||
code,
|
||||
kbd,
|
||||
samp,
|
||||
pre {
|
||||
color: #1bb6be !important
|
||||
}
|
||||
|
||||
$github-color: #fff !default;
|
||||
@import "my-vars.scss";
|
||||
|
||||
//@import "progress.css"; // for progress bar
|
||||
//@import "minimal-mistakes/skins/{{ site.minimal_mistakes_skin | default: 'default' }}"; // skin
|
||||
|
@ -150,3 +14,6 @@ $github-color: #fff !default;
|
|||
//@import "assets/css/override-notices.scss"
|
||||
@import "override-notices.scss";
|
||||
|
||||
.footnotes ol, .footnotes li {
|
||||
font-size: 1em;
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
@charset "utf-8";
|
||||
|
||||
.author__urls.social-icons .svg-inline--fa,
|
||||
.page__footer-follow .social-icons .svg-inline--fa {
|
||||
color: rgba(102, 235, 97, 0.829) !important;
|
||||
}
|
||||
|
||||
/* Colors */
|
||||
$background-color: #141010 !default;
|
||||
$text-color: #fff6fbb9 !default;
|
||||
$primary-color: #ca34fc !default;
|
||||
$link-color: $primary-color !default;
|
||||
$link-color-hover: mix(#fff, $link-color, 25%) !default;
|
||||
$link-color-visited: mix(#000, $link-color, 25%) !default;
|
||||
$code-background-color: mix(rgb(43, 39, 39), $background-color, 15%) !default;
|
||||
$code-background-color-dark: mix(rgb(59, 54, 54), $background-color, 20%) !default;
|
||||
|
||||
/* Notices */
|
||||
|
||||
$success-color: #02d702 !default;
|
||||
$warning-color: #d67f05 !default;
|
||||
$danger-color: #d90404 !default;
|
||||
$info-color: #3b9cba !default;
|
||||
|
||||
// section not needed under 11ty
|
||||
/* neon syntax highlighting (base16) */
|
||||
$base00: #1a191a;
|
||||
$base01: #e0e0e0;
|
||||
$base02: #b4a6a6;
|
||||
$base03: #928e8e;
|
||||
$base04: #ccc2c2;
|
||||
$base05: #bd6262;
|
||||
$base06: #a74040;
|
||||
$base07: #701b1b;
|
||||
$base08: #ff0086;
|
||||
$base09: #fd8900;
|
||||
$base0a: #aba800;
|
||||
$base0b: #00c918;
|
||||
$base0c: #1faaaa;
|
||||
$base0d: #3777e6;
|
||||
$base0e: #ad00a1;
|
||||
$base0f: #926e5c;
|
||||
// base16-atelier-heath
|
||||
// base16-synth-midnight-dark
|
||||
// base16-summerfruit-dark
|
||||
// base16-seti
|
||||
// base16-outrun-dark (a bit bright)
|
||||
// base16-material-darker
|
||||
// base16-harmonic-dark
|
||||
// base16-google-dark (little dark for my tastes)
|
||||
// base16-default-dark (little dull)
|
||||
// base16-classic-dark
|
||||
// base16-brewer
|
||||
|
||||
// section not needed under 11ty
|
||||
// Hacking the syntax highlighting
|
||||
.nb {
|
||||
/* Name.Builtin */
|
||||
color: $base0f !important;
|
||||
}
|
||||
|
||||
.o {
|
||||
/* Operator */
|
||||
color: $base04 !important;
|
||||
}
|
||||
.n {
|
||||
/* Name */
|
||||
color: $base05 !important;
|
||||
}
|
||||
.p {
|
||||
/* Punctuation */
|
||||
color: #3ab469 !important;
|
||||
}
|
||||
.sr {
|
||||
/* Literal.String.Regex */
|
||||
color: $base0c !important;
|
||||
}
|
||||
|
||||
// Progress Bar
|
||||
#progress-bar {
|
||||
background: linear-gradient(to right, red, orange , yellow, green,
|
||||
blue, indigo, violet var(--scroll), transparent 0);
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
// Scroll to top
|
||||
#scroll-to-top {
|
||||
background: rgb(215, 210, 210);
|
||||
display:block;
|
||||
position:fixed;
|
||||
font-size:25px;
|
||||
bottom:25px;
|
||||
right:10%;
|
||||
opacity:0;
|
||||
z-index:99;
|
||||
text-align:center;
|
||||
transform:translateY(30%);
|
||||
transition:transform 0.5s ease-in-out, opacity 0.5s ease-in-out
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
#scroll-to-top {
|
||||
font-size:20px
|
||||
}
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
#scroll-to-top {
|
||||
right:5%;
|
||||
bottom:15px
|
||||
}
|
||||
}
|
||||
#scroll-to-top :last-child {
|
||||
transition:transform 0.7s ease-in-out
|
||||
}
|
||||
#scroll-to-top :last-child:active,
|
||||
#scroll-to-top :last-child:hover {
|
||||
transform:translateY(-10%)
|
||||
}
|
||||
#scroll-to-top span {
|
||||
cursor:pointer;
|
||||
color:#dee3ee
|
||||
}
|
||||
#scroll-to-top span:hover .up-arrow,
|
||||
#scroll-to-top span:active .up-arrow {
|
||||
color:#00adb5
|
||||
}
|
||||
|
||||
tt,
|
||||
code,
|
||||
kbd,
|
||||
samp,
|
||||
pre {
|
||||
color: #1bb6be !important
|
||||
}
|
||||
|
||||
$github-color: #fff !default;
|
||||
|
||||
div.highlight {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div pre {
|
||||
border: 4px #7e1c1c solid;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
code {
|
||||
outline: 1px #7e1c1c solid;
|
||||
}
|
||||
|
||||
code > span {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
// messes up table code blocks
|
||||
//code > span:first-child {
|
||||
// padding-top: 5px;
|
||||
//}
|
||||
|
||||
//code > span:last-child {
|
||||
// padding-bottom: 5px;
|
||||
//}
|
||||
|
||||
// hack to remove outline that above sets
|
||||
td > div > pre {
|
||||
border: unset;
|
||||
}
|
||||
|
||||
pre > code > div {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
td {
|
||||
display: inline-flex;
|
||||
// below is hack so Firefox looks the same as Safari / Chrome.
|
||||
// Both will ignore the assignment because they don't know about "ruby-base"
|
||||
// https://caniuse.com/mdn-css_properties_display_ruby_values
|
||||
display: ruby-base;
|
||||
}
|
||||
|
||||
// === for copy-code-to-clipboard
|
||||
|
||||
// h/t https://simplernerd.com/hugo-add-copy-to-clipboard-button/
|
||||
|
||||
.clipboard-button {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
padding: 6px 8px 4px 8px;
|
||||
margin: 5px;
|
||||
// color: gray-500;
|
||||
// border-color: gray-500;
|
||||
// background-color: gray-100;
|
||||
border: 1px solid;
|
||||
border-radius: 6px;
|
||||
font-size: 0.8em;
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
transition: 0.1s;
|
||||
}
|
||||
.clipboard-button > svg {
|
||||
// fill: gray-500;
|
||||
}
|
||||
.clipboard-button:hover {
|
||||
cursor: pointer;
|
||||
border-color: #01b139;
|
||||
background-color: #87d09e;
|
||||
opacity: 1;
|
||||
}
|
||||
.clipboard-button:hover > svg {
|
||||
fill: #1900ff;
|
||||
}
|
||||
.clipboard-button:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.highlight:hover > .clipboard-button {
|
||||
opacity: 1;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
line-height: 1.5;
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
font-family: $global-font-family;
|
||||
font-size: $type-size-6 !important;
|
||||
text-indent: initial; /* override*/
|
||||
background-color: mix(#fff, $notice-color, 55%); //original percentage was 90
|
||||
background-color: mix(#fff, $notice-color, 65%); //original percentage was 90
|
||||
border-radius: $border-radius;
|
||||
box-shadow: 0 1px 1px rgba($notice-color, 0.25);
|
||||
|
||||
|
|
After Width: | Height: | Size: 224 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 6.9 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 69 KiB |
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
h/t to...
|
||||
- https://www.brycewray.com/posts/2023/02/code-copying-code-eleventy-edition/ & Bryce's gitrepo
|
||||
- https://github.com/brycewray/eleventy_site/blob/main/src/assets/js/copy-code-button.js
|
||||
- https://www.dannyguo.com/blog/how-to-add-copy-to-clipboard-buttons-to-code-blocks-in-hugo/
|
||||
- https://aaronluna.dev/blog/add-copy-button-to-code-blocks-hugo-chroma/
|
||||
- https://simplernerd.com/hugo-add-copy-to-clipboard-button/
|
||||
- https://digitaldrummerj.me/hugo-add-copy-code-snippet-button/
|
||||
*/
|
||||
|
||||
const svgCopy =
|
||||
'<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true"><path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path></svg><span class="sr-only"> Click to copy code</span>';
|
||||
const svgCheck =
|
||||
'<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true"><path fill-rule="evenodd" fill="rgb(25, 0, 255)" d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"></path></svg><span class="sr-only"> Copied!</span>';
|
||||
|
||||
const addCopyButtons = (clipboard) => {
|
||||
// 1. Look for pre > code elements in the DOM
|
||||
document.querySelectorAll("pre > code").forEach((codeBlock) => {
|
||||
// 2. Create a button that will trigger a copy operation
|
||||
const button = document.createElement("button");
|
||||
button.className = "clipboard-button";
|
||||
button.type = "button";
|
||||
button.innerHTML = svgCopy;
|
||||
button.addEventListener("click", () => {
|
||||
clipboard.writeText(codeBlock.innerText).then(
|
||||
() => {
|
||||
button.blur();
|
||||
button.innerHTML = svgCheck;
|
||||
setTimeout(() => (button.innerHTML = svgCopy), 2000);
|
||||
},
|
||||
(error) => (button.innerHTML = "Error")
|
||||
);
|
||||
});
|
||||
// 3. Append the button directly before the pre tag
|
||||
// (with content-searching fix in place for Prism)
|
||||
const pre = codeBlock.parentNode;
|
||||
pre.parentNode.insertBefore(button, pre);
|
||||
});
|
||||
};
|
||||
|
||||
function addCopyButtonsAfterLoad() {
|
||||
if (navigator && navigator.clipboard) {
|
||||
addCopyButtons(navigator.clipboard);
|
||||
} else {
|
||||
const script = document.createElement("script");
|
||||
script.src =
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/clipboard-polyfill/2.7.0/clipboard-polyfill.promise.js";
|
||||
script.integrity =
|
||||
"sha256-waClS2re9NUbXRsryKoof+F9qc1gjjIhc2eT7ZbIv94=";
|
||||
script.crossOrigin = "anonymous";
|
||||
script.onload = () => addCopyButtons(clipboard);
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("load", addCopyButtonsAfterLoad);
|
|
@ -0,0 +1,44 @@
|
|||
---json
|
||||
{
|
||||
"permalink": "feed-dev.xml",
|
||||
"eleventyExcludeFromCollections": true,
|
||||
"metadata": {
|
||||
"title": "TDN: RMCG",
|
||||
"subtitle": "👨💻 🚶♂️ 💭 🤯",
|
||||
"language": "en",
|
||||
"url": "https://tarasis.net/",
|
||||
"author": {
|
||||
"name": "Robert McGovern",
|
||||
"email": "rob@tarasis.net"
|
||||
}
|
||||
}
|
||||
}
|
||||
---
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="{{ metadata.url }}">
|
||||
<title>{{ metadata.title }}</title>
|
||||
<subtitle>{{ metadata.subtitle }}</subtitle>
|
||||
<link href="{{ permalink | absoluteUrl(metadata.url) }}" rel="self"/>
|
||||
<link href="{{ metadata.url }}"/>
|
||||
<updated>{{ collections.posts | getNewestCollectionItemDate | dateToRfc3339 }}</updated>
|
||||
<id>{{ metadata.url }}</id>
|
||||
<author>
|
||||
<name>{{ metadata.author.name }}</name>
|
||||
<email>{{ metadata.author.email }}</email>
|
||||
</author>
|
||||
{%- for post in collections.posts %}
|
||||
{%- for tag in post.data.tags %}
|
||||
{# {{ post | inspect }} #}
|
||||
{%- if tag === "dev" %}
|
||||
{%- set absolutePostUrl = post.url | absoluteUrl(metadata.url) %}
|
||||
<entry>
|
||||
<title>{{ post.data.title }}</title>
|
||||
<link href="{{ absolutePostUrl }}"/>
|
||||
<updated>{{ post.date | dateToRfc3339 }}</updated>
|
||||
<id>{{ absolutePostUrl }}</id>
|
||||
<content xml:lang="{{ metadata.language }}" type="html">{{ post.templateContent | htmlToAbsoluteUrls(absolutePostUrl) }}</content>
|
||||
</entry>
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- endfor %}
|
||||
</feed>
|
|
@ -26,7 +26,7 @@
|
|||
<name>{{ metadata.author.name }}</name>
|
||||
<email>{{ metadata.author.email }}</email>
|
||||
</author>
|
||||
{%- for post in collections.posts | reverse %}
|
||||
{%- for post in collections.posts %}
|
||||
{%- set absolutePostUrl = post.url | absoluteUrl(metadata.url) %}
|
||||
<entry>
|
||||
<title>{{ post.data.title }}</title>
|
||||
|
|
|
@ -7,9 +7,9 @@ pagination:
|
|||
---
|
||||
|
||||
{%- for post in pagination.items -%}
|
||||
<h2><a href="{% post_url collections.posts, post.fileSlug %}">{{ post.data.title }}</a></h2>
|
||||
<em>{{ post.data.date | toUTCString}}</em>
|
||||
<p>{{ post.templateContent | description }} <a href="{% post_url collections.posts, post.fileSlug %}"><em>read more</em></a></p>
|
||||
|
||||
{% include archive-single.html type=entries_layout aPost=post %}
|
||||
|
||||
{%- endfor -%}
|
||||
|
||||
|
||||
|
|