Add extractor and builder

Tested and working
This commit is contained in:
Angelos Chalaris
2019-08-22 14:04:18 +03:00
parent 343f8fd4c7
commit a8818b1998
101 changed files with 18465 additions and 9908 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@ node_modules/
.cache/
.DS_Store
dist/
src_o/

2478
README.md

File diff suppressed because it is too large Load Diff

18
config.js Normal file
View File

@ -0,0 +1,18 @@
module.exports = {
// Project metadata
name: `30 seconds of CSS`,
description: `A curated collection of useful CSS snippets you can understand in 30 seconds or less.`,
shortName: `30s`,
repositoryUrl: `https://github.com/30-seconds/30-seconds-of-css`,
siteUrl: `https://css.30secondsofcode.org`,
// Path information
snippetPath: `snippets`,
snippetDataPath: `snippet_data`,
assetPath: `assets`,
pagePath: `src/docs/pages`,
staticPartsPath: `src/static-parts`,
// General information
language: `css`,
secondLanguage: `html`,
optionalLanguage: `js`,
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

23084
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,10 +4,12 @@
"description": "A curated collection of useful CSS snippets.",
"main": "index.js",
"scripts": {
"dev": "nodemon -e md,js ./scripts/build.js & parcel index.html",
"build": "node ./scripts/build.js && npm run prettier && npm run parcel",
"parcel": "npx rimraf docs && parcel build index.html -d docs/ --public-url ./",
"prettier": "prettier --write \"**/*.{js,json,md,css,scss}\""
"builder": "node ./scripts/build.js",
"webber": "gatsby build",
"webber:dev": "gatsby develop",
"webber:serve": "gatsby serve",
"extractor": "node ./scripts/extract.js",
"linter": "prettier --write \"**/*.{js,json,md,css,scss}\""
},
"author": "atomiks",
"license": "MIT",
@ -17,22 +19,47 @@
"singleQuote": true
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-preset-env": "^1.6.1",
"babel-preset-stage-2": "^6.24.1",
"caniuse-db": "^1.0.30000813",
"date-fns": "^1.29.0",
"jsdom": "^11.6.2",
"marked": "^0.3.16",
"node-sass": "^4.9.3",
"nodemon": "^1.18.4",
"normalize.css": "^8.0.0",
"parcel-bundler": "^1.11.0",
"prettier": "^1.14.3",
"pretty": "^2.0.0",
"prismjs": "^1.11.0",
"rimraf": "^2.6.2"
"@babel/plugin-proposal-class-properties": "^7.5.0",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/preset-env": "^7.5.4",
"@babel/preset-react": "^7.0.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
"caniuse-db": "^1.0.30000989",
"front-matter": "^3.0.2",
"fs-extra": "^8.1.0",
"gatsby": "^2.12.0",
"gatsby-image": "^2.2.6",
"gatsby-plugin-google-analytics": "^2.1.6",
"gatsby-plugin-manifest": "^2.2.3",
"gatsby-plugin-netlify": "^2.1.3",
"gatsby-plugin-offline": "^2.2.4",
"gatsby-plugin-page-creator": "^2.1.5",
"gatsby-plugin-react-helmet": "^3.1.2",
"gatsby-plugin-sass": "^2.1.3",
"gatsby-plugin-sharp": "^2.2.7",
"gatsby-plugin-sitemap": "^2.2.8",
"gatsby-plugin-transition-link": "^1.12.4",
"gatsby-remark-copy-linked-files": "^2.1.3",
"gatsby-remark-images": "^3.1.6",
"gatsby-remark-prismjs": "^3.3.3",
"gatsby-source-filesystem": "^2.1.5",
"gatsby-transformer-json": "^2.2.2",
"gatsby-transformer-remark": "^2.6.6",
"gatsby-transformer-sharp": "^2.2.3",
"gsap": "^2.1.3",
"kleur": "^3.0.3",
"markdown-builder": "^0.9.0",
"node-sass": "^4.12.0",
"prettier": "^1.18.2",
"prismjs": "^1.16.0",
"prop-types": "^15.7.2",
"react": "^16.8.6",
"react-copy-to-clipboard": "^5.0.1",
"react-css-transition-replace": "^3.0.3",
"react-dom": "^16.8.6",
"react-helmet": "^5.2.1",
"react-redux": "^7.1.0",
"redux": "^4.0.4"
},
"dependencies": {
"feather-icons": "^4.7.0",

View File

@ -1,177 +1,142 @@
const fs = require('fs')
const path = require('path')
const marked = require('marked')
const pretty = require('pretty')
const caniuseDb = require('caniuse-db/data.json')
const sass = require('node-sass')
const { toKebabCase, createElement, template, dom, getCode } = require('../utils/utils.js')
const { differenceInDays } = require('date-fns')
/*
This is the builder script that generates the README file.
Run using `npm run builder`.
*/
// Load modules
const fs = require('fs-extra');
const path = require('path');
const { green, red } = require('kleur');
const util = require('./util');
const markdown = require('markdown-builder');
const { headers, misc, lists } = markdown;
const config = require('../config');
const SNIPPETS_PATH = './snippets'
const TAGS = [
{
name: 'all',
icon: 'check'
},
{
name: 'layout',
icon: 'layout'
},
{
name: 'visual',
icon: 'eye'
},
{
name: 'animation',
icon: 'loader'
},
{
name: 'interactivity',
icon: 'edit-2'
},
{
name: 'other',
icon: 'tag'
}
]
// Paths (relative to package.json)
const SNIPPETS_PATH = `./${config.snippetPath}`;
const STATIC_PARTS_PATH = `./${config.staticPartsPath}`;
const renderer = new marked.Renderer()
renderer.heading = (text, level) => {
if (level === 3) {
return `<h${level} id="${toKebabCase(text)}"><span>${text}</span></h${level}>`
} else {
return ['HTML', 'CSS', 'JavaScript'].includes(text)
? `<h${level} data-type="${text}">${text}</h${level}>`
: `<h${level}>${text}</h${level}>`
}
}
renderer.link = (url, _, text) => `<a href="${url}" target="_blank">${text || url}</a>`
const document = dom('./src/html/index.html')
const components = {
backToTopButton: dom('./src/html/components/back-to-top-button.html'),
sidebar: dom('./src/html/components/sidebar.html'),
header: dom('./src/html/components/header.html'),
main: dom('./src/html/components/main.html'),
tags: dom('./src/html/components/tags.html')
// Terminate if parent commit is a Travis build
if (
util.isTravisCI() &&
/^Travis build: \d+/g.test(process.env['TRAVIS_COMMIT_MESSAGE'])
) {
console.log(
`${green(
'NOBUILD',
)} README build terminated, parent commit is a Travis build!`,
);
process.exit(0);
}
const snippetContainer = components.main.querySelector('.container')
const sidebarLinkContainer = components.sidebar.querySelector('.sidebar__links')
TAGS.slice(1).forEach(tag => {
sidebarLinkContainer.append(
createElement(`
<section data-type="${tag.name}" class="sidebar__section">
<h4 class="sidebar__section-heading">${tag.name}</h4>
</section>
`)
)
})
// Setup everything
let snippets = {},
snippetsArray = [],
startPart = '',
endPart = '',
output = '';
const EMOJIS = {};
for (const snippetFile of fs.readdirSync(SNIPPETS_PATH)) {
const snippetPath = path.join(SNIPPETS_PATH, snippetFile)
const snippetData = fs.readFileSync(snippetPath, 'utf8')
console.time('Builder');
const html = getCode('html', snippetData).trim()
const css = getCode('css', snippetData)
const scopedCSS = sass.renderSync({
data: `[data-scope="${snippetFile}"] { ${css} }`
})
const js = getCode('js', snippetData)
// Synchronously read all snippets from snippets folder and sort them as necessary (case-insensitive)
snippets = util.readSnippets(SNIPPETS_PATH);
snippetsArray = Object.keys(snippets).reduce((acc, key) => {
acc.push(snippets[key]);
return acc;
}, []);
const demo =
`<div class="snippet-demo" data-scope="${snippetFile}">${html}</div>` +
`<style>${scopedCSS.css.toString()}</style>` +
`${js ? `<script>(function(){${js}})();</script>` : ''}`
const markdown = marked(snippetData, { renderer }).replace(
'<h4>Demo</h4>',
`<h4>Demo</h4>${demo}`
)
const snippetEl = createElement(`<div class="snippet">${markdown}</div>`)
snippetContainer.append(snippetEl)
// browser support usage
const featUsageShares = (snippetData.match(/https?:\/\/caniuse\.com\/#feat=.*/g) || []).map(
feat => {
const featData = caniuseDb.data[feat.match(/#feat=(.*)/)[1]]
// caniuse doesn't count "untracked" users, which makes the overall share appear much lower
// than it probably is. Most of these untracked browsers probably support these features.
// Currently it's around 5.3% untracked, so we'll use 4% as probably supporting the feature.
// Also the npm package appears to be show higher usage % than the main website, this shows
// about 0.2% lower than the main website when selecting "tracked users" (as of Feb 2019).
const UNTRACKED_PERCENT = 4
const usage = featData
? Number(featData.usage_perc_y + featData.usage_perc_a) + UNTRACKED_PERCENT
: 100
return Math.min(100, usage)
}
)
const browserSupportHeading = snippetEl.querySelector('h4:last-of-type')
browserSupportHeading.after(
createElement(`
<div>
<div class="snippet__browser-support">
${featUsageShares.length ? Math.min(...featUsageShares).toPrecision(3) : 100}%
</div>
</div>
`)
)
// sidebar link
const link = createElement(
`<a class="sidebar__link" href="#${snippetFile.replace('.md', '')}">${
snippetEl.querySelector('h3').innerHTML
}</a>`
)
// new icon = less than 31 days old
const date = (snippetData.match(/<!--\s*date:\s*(.+?)-->/) || [, ''])[1]
if (date && differenceInDays(new Date(), new Date(date)) < 31) {
const newIcon = '<img alt="New" draggable="false" class="snippet__new" src="./src/img/new.svg">'
snippetEl.prepend(createElement(newIcon))
link.prepend(createElement(newIcon))
// Load static parts for the README file
try {
startPart = fs.readFileSync(
path.join(STATIC_PARTS_PATH, 'README-start.md'),
'utf8',
);
endPart = fs.readFileSync(
path.join(STATIC_PARTS_PATH, 'README-end.md'),
'utf8',
);
} catch (err) {
console.log(`${red('ERROR!')} During static part loading: ${err}`);
process.exit(1);
}
// tags
const tags = (snippetData.match(/<!--\s*tags:\s*(.+?)-->/) || [, ''])[1]
.split(/,\s*/)
.forEach(tag => {
tag = tag.trim().toLowerCase()
snippetEl
.querySelector('h3')
.append(
createElement(
`<span class="tags__tag snippet__tag" data-type="${tag}"><i data-feather="${
TAGS.find(t => t.name === tag).icon
}"></i>${tag}</span>`
)
)
// Create the output for the README file
try {
const tags = util.prepTaggedData(
Object.keys(snippets).reduce((acc, key) => {
acc[key] = snippets[key].attributes.tags;
return acc;
}, {}),
);
sidebarLinkContainer.querySelector(`section[data-type="${tag}"]`).append(link)
})
output += `${startPart}\n`;
// Loop over tags and snippets to create the table of contents
for (const tag of tags) {
const capitalizedTag = util.capitalize(tag, true);
const taggedSnippets = snippetsArray.filter(
snippet => snippet.attributes.tags[0] === tag,
);
output += headers.h3((EMOJIS[tag] || '') + ' ' + capitalizedTag).trim();
output +=
misc.collapsible(
'View contents',
lists.ul(taggedSnippets, snippet =>
misc.link(
`\`${snippet.title}\``,
`${misc.anchor(snippet.title)}${
snippet.attributes.tags.includes('advanced') ? '-' : ''
}`,
),
),
) + '\n';
}
// build dom
TAGS.forEach(tag =>
components.tags.append(
createElement(
`<button class="tags__tag is-large ${tag.name === 'all' ? 'is-active' : ''}" data-type="${
tag.name
}"><i data-feather="${tag.icon}"></i>${tag.name}</button>`
)
)
)
const content = document.querySelector('.content-wrapper')
content.before(components.backToTopButton)
content.before(components.sidebar)
content.append(components.header)
content.append(components.main)
components.main.querySelector('.container').prepend(components.tags)
for (const tag of tags) {
const capitalizedTag = util.capitalize(tag, true);
const taggedSnippets = snippetsArray.filter(
snippet => snippet.attributes.tags[0] === tag,
);
// doctype declaration gets stripped, add it back in
const html = `<!DOCTYPE html>
${pretty(document.documentElement.outerHTML, { ocd: true })}
`
output +=
misc.hr() + headers.h2((EMOJIS[tag] || '') + ' ' + capitalizedTag) + '\n';
fs.writeFileSync('./index.html', html)
for (let snippet of taggedSnippets) {
if (snippet.attributes.tags.includes('advanced'))
output +=
headers.h3(
snippet.title + ' ' + misc.image('advanced', '/advanced.svg'),
) + '\n';
else output += headers.h3(snippet.title) + '\n';
output += snippet.attributes.text;
output += `\`\`\`${config.secondLanguage}\n${snippet.attributes.codeBlocks.html}\n\`\`\`\n\n`;
output += `\`\`\`${config.language}\n${snippet.attributes.codeBlocks.css}\n\`\`\`\n\n`;
if (snippet.attributes.codeBlocks.js)
output += `\`\`\`${config.optionalLanguage}\n${snippet.attributes.codeBlocks.js}\n\`\`\`\n\n`;
output += headers.h4('Explanation');
output += snippet.attributes.explanation;
output += headers.h4('Browser support') + '\n';
output += snippet.attributes.browserSupport.supportPercentage.toFixed(1) + '%';
output += snippet.attributes.browserSupport.text;
output +=
'\n<br>' + misc.link('⬆ Back to top', misc.anchor('Contents')) + '\n';
}
}
// Add the ending static part
output += `\n${endPart}\n`;
// Write to the README file
fs.writeFileSync('README.md', output);
} catch (err) {
console.log(`${red('ERROR!')} During README generation: ${err}`);
process.exit(1);
}
console.log(`${green('SUCCESS!')} README file generated!`);
console.timeEnd('Builder');

80
scripts/extract.js Normal file
View File

@ -0,0 +1,80 @@
/*
This is the extractor script that generates the snippets.json file.
Run using `npm run extractor`.
*/
// Load modules
const fs = require('fs-extra');
const path = require('path');
const { green } = require('kleur');
const util = require('./util');
const config = require('../config');
// Paths (relative to package.json)
const SNIPPETS_PATH = `./${config.snippetPath}`;
const OUTPUT_PATH = `./${config.snippetDataPath}`;
// Check if running on Travis, only build for cron jobs and custom builds
if (
util.isTravisCI() &&
process.env['TRAVIS_EVENT_TYPE'] !== 'cron' &&
process.env['TRAVIS_EVENT_TYPE'] !== 'api'
) {
console.log(
`${green(
'NOBUILD',
)} snippet extraction terminated, not a cron or api build!`,
);
process.exit(0);
}
// Setup everything
let snippets = {},
snippetsArray = [];
console.time('Extractor');
// Synchronously read all snippets from snippets folder and sort them as necessary (case-insensitive)
snippets = util.readSnippets(SNIPPETS_PATH);
snippetsArray = Object.keys(snippets).reduce((acc, key) => {
acc.push(snippets[key]);
return acc;
}, []);
const completeData = {
data: [...snippetsArray],
meta: {
specification: 'http://jsonapi.org/format/',
type: 'snippetArray',
},
};
let listingData = {
data: completeData.data.map(v => ({
id: v.id,
type: 'snippetListing',
title: v.title,
attributes: {
text: v.attributes.text,
tags: v.attributes.tags,
},
meta: {
hash: v.meta.hash,
},
})),
meta: {
specification: 'http://jsonapi.org/format/',
type: 'snippetListingArray',
},
};
// Write files
fs.writeFileSync(
path.join(OUTPUT_PATH, 'snippets.json'),
JSON.stringify(completeData, null, 2),
);
fs.writeFileSync(
path.join(OUTPUT_PATH, 'snippetList.json'),
JSON.stringify(listingData, null, 2),
);
// Display messages and time
console.log(
`${green('SUCCESS!')} snippets.json and snippetList.json files generated!`,
);
console.timeEnd('Extractor');

View File

@ -0,0 +1,12 @@
// Checks if current environment is Travis CI, Cron builds, API builds
const isTravisCI = () => 'TRAVIS' in process.env && 'CI' in process.env;
const isTravisCronOrAPI = () =>
process.env['TRAVIS_EVENT_TYPE'] === 'cron' ||
process.env['TRAVIS_EVENT_TYPE'] === 'api';
const isNotTravisCronOrAPI = () => !isTravisCronOrAPI();
module.exports = {
isTravisCI,
isTravisCronOrAPI,
isNotTravisCronOrAPI,
};

60
scripts/util/helpers.js Normal file
View File

@ -0,0 +1,60 @@
const config = require('../../config');
const getMarkDownAnchor = paragraphTitle =>
paragraphTitle
.trim()
.toLowerCase()
.replace(/[^\w\- ]+/g, '')
.replace(/\s/g, '-')
.replace(/\-+$/, '');
// Creates an object from pairs
const objectFromPairs = arr => arr.reduce((a, v) => ((a[v[0]] = v[1]), a), {});
// Optimizes nodes in an HTML document
const optimizeNodes = (data, regexp, replacer) => {
let count = 0;
let output = data;
do {
output = output.replace(regexp, replacer);
count = 0;
while (regexp.exec(output) !== null) ++count;
} while (count > 0);
return output;
};
// Capitalizes the first letter of a string
const capitalize = (str, lowerRest = false) =>
str.slice(0, 1).toUpperCase() +
(lowerRest ? str.slice(1).toLowerCase() : str.slice(1));
const prepTaggedData = tagDbData =>
[...new Set(Object.entries(tagDbData).map(t => t[1][0]))]
.filter(v => v)
.sort((a, b) =>
capitalize(a, true) === 'Uncategorized'
? 1
: capitalize(b, true) === 'Uncategorized'
? -1
: a.localeCompare(b),
);
const makeExamples = data => {
data =
data.slice(0, data.lastIndexOf(`\`\`\`${config.language}`)).trim() +
misc.collapsible(
'Examples',
data.slice(
data.lastIndexOf(`\`\`\`${config.language}`),
data.lastIndexOf('```'),
) + data.slice(data.lastIndexOf('```')),
);
return `${data}\n<br>${misc.link(
'⬆ Back to top',
misc.anchor('Contents'),
)}\n\n`;
};
module.exports = {
getMarkDownAnchor,
objectFromPairs,
optimizeNodes,
capitalize,
prepTaggedData,
makeExamples,
};

37
scripts/util/index.js Normal file
View File

@ -0,0 +1,37 @@
const {
isTravisCI,
isTravisCronOrAPI,
isNotTravisCronOrAPI,
} = require('./environmentCheck');
const {
getMarkDownAnchor,
objectFromPairs,
optimizeNodes,
capitalize,
prepTaggedData,
makeExamples,
} = require('./helpers');
const {
getFilesInDir,
hashData,
getCodeBlocks,
getTextualContent,
readSnippets,
} = require('./snippetParser');
module.exports = {
isTravisCI,
isTravisCronOrAPI,
isNotTravisCronOrAPI,
getMarkDownAnchor,
objectFromPairs,
optimizeNodes,
capitalize,
prepTaggedData,
makeExamples,
getFilesInDir,
hashData,
getCodeBlocks,
getTextualContent,
readSnippets,
};

View File

@ -0,0 +1,198 @@
const fs = require('fs-extra'),
path = require('path'),
{ red } = require('kleur'),
crypto = require('crypto'),
frontmatter = require('front-matter');
const sass = require('node-sass');
const caniuseDb = require('caniuse-db/data.json');
const config = require('../../config');
// Reade all files in a directory
const getFilesInDir = (directoryPath, withPath, exclude = null) => {
try {
let directoryFilenames = fs.readdirSync(directoryPath);
directoryFilenames.sort((a, b) => {
a = a.toLowerCase();
b = b.toLowerCase();
if (a < b) return -1;
if (a > b) return 1;
return 0;
});
if (withPath) {
// a hacky way to do conditional array.map
return directoryFilenames.reduce((fileNames, fileName) => {
if (
exclude == null ||
!exclude.some(toExclude => fileName === toExclude)
)
fileNames.push(`${directoryPath}/${fileName}`);
return fileNames;
}, []);
}
return directoryFilenames.filter(v => v !== 'README.md');
} catch (err) {
console.log(`${red('ERROR!')} During snippet loading: ${err}`);
process.exit(1);
}
};
// Creates a hash for a value using the SHA-256 algorithm.
const hashData = val =>
crypto
.createHash('sha256')
.update(val)
.digest('hex');
// Gets the code blocks for a snippet file.
const getCodeBlocks = str => {
const regex = /```[.\S\s]*?```/g;
let results = [];
let m = null;
while ((m = regex.exec(str)) !== null) {
if (m.index === regex.lastIndex) regex.lastIndex += 1;
m.forEach((match, groupIndex) => {
results.push(match);
});
}
const replacer = new RegExp(
`\`\`\`${config.language}([\\s\\S]*?)\`\`\``,
'g',
);
const secondReplacer = new RegExp(
`\`\`\`${config.secondLanguage}([\\s\\S]*?)\`\`\``,
'g',
);
const optionalReplacer = new RegExp(
`\`\`\`${config.optionalLanguage}([\\s\\S]*?)\`\`\``,
'g',
);
results = results.map(v =>
v
.replace(replacer, '$1')
.replace(secondReplacer, '$1')
.replace(optionalReplacer, '$1')
.trim()
);
if (results.length > 2)
return {
html: results[0],
css: results[1],
js: results[2],
};
return {
html: results[0],
css: results[1],
js: '',
};
};
// Gets the textual content for a snippet file.
const getTextualContent = str => {
const regex = /([\s\S]*?)```/g;
const results = [];
let m = null;
while ((m = regex.exec(str)) !== null) {
if (m.index === regex.lastIndex) regex.lastIndex += 1;
m.forEach((match, groupIndex) => {
results.push(match);
});
}
return results[1].replace(/\r\n/g, '\n');
};
// Gets the explanation for a snippet file.
const getExplanation = str => {
const regex = /####\s*Explanation([\s\S]*)####/g;
const results = [];
let m = null;
while ((m = regex.exec(str)) !== null) {
if (m.index === regex.lastIndex) regex.lastIndex += 1;
m.forEach((match, groupIndex) => {
results.push(match);
});
}
// console.log(results);
return results[1].replace(/\r\n/g, '\n');
};
// Gets the browser support for a snippet file.
const getBrowserSupport = str => {
const regex = /####\s*Browser [s|S]upport([\s\S]*)/g;
const results = [];
let m = null;
while ((m = regex.exec(str)) !== null) {
if (m.index === regex.lastIndex) regex.lastIndex += 1;
m.forEach((match, groupIndex) => {
results.push(match);
});
}
let browserSupportText = results[1].replace(/\r\n/g, '\n');
const supportPercentage = (browserSupportText.match(/https?:\/\/caniuse\.com\/#feat=.*/g) || []).map(
feat => {
const featData = caniuseDb.data[feat.match(/#feat=(.*)/)[1]];
// caniuse doesn't count "untracked" users, which makes the overall share appear much lower
// than it probably is. Most of these untracked browsers probably support these features.
// Currently it's around 5.3% untracked, so we'll use 4% as probably supporting the feature.
// Also the npm package appears to be show higher usage % than the main website, this shows
// about 0.2% lower than the main website when selecting "tracked users" (as of Feb 2019).
const UNTRACKED_PERCENT = 4;
const usage = featData
? Number(featData.usage_perc_y + featData.usage_perc_a) + UNTRACKED_PERCENT
: 100;
return Math.min(100, usage);
}
)
return {
text: browserSupportText,
supportPercentage: Math.min(...supportPercentage)
}
};
// Synchronously read all snippets and sort them as necessary (case-insensitive)
const readSnippets = snippetsPath => {
const snippetFilenames = getFilesInDir(snippetsPath, false);
let snippets = {};
try {
for (let snippet of snippetFilenames) {
let data = frontmatter(
fs.readFileSync(path.join(snippetsPath, snippet), 'utf8'),
);
snippets[snippet] = {
id: snippet.slice(0, -3),
title: data.attributes.title,
type: 'snippet',
attributes: {
fileName: snippet,
text: getTextualContent(data.body),
explanation: getExplanation(data.body),
browserSupport: getBrowserSupport(data.body),
codeBlocks: getCodeBlocks(data.body),
tags: data.attributes.tags.split(',').map(t => t.trim()),
},
meta: {
hash: hashData(data.body),
},
};
snippets[snippet].attributes.codeBlocks.scopedCss = sass.renderSync({
data: `[data-scope="${snippets[snippet].id}"] { ${snippets[snippet].attributes.codeBlocks.css} }`
}).css.toString();
}
} catch (err) {
console.log(`${red('ERROR!')} During snippet loading: ${err}`);
process.exit(1);
}
return snippets;
};
module.exports = {
getFilesInDir,
hashData,
getCodeBlocks,
getTextualContent,
getExplanation,
getBrowserSupport,
readSnippets,
};

View File

@ -0,0 +1,704 @@
{
"data": [
{
"id": "bouncing-loader",
"type": "snippetListing",
"title": "Bouncing loader",
"attributes": {
"text": "Creates a bouncing loader animation.\n\n",
"tags": [
"animation",
"intermediate"
]
},
"meta": {
"hash": "5093345481ba16eb2d9b547cc55a2ac1993c39ffe09a4c40255c9e4d9cae4cc7"
}
},
{
"id": "box-sizing-reset",
"type": "snippetListing",
"title": "Box-sizing reset",
"attributes": {
"text": "Resets the box-model so that `width`s and `height`s are not affected by their `border`s or `padding`.\n\n",
"tags": [
"layout",
"intermediate"
]
},
"meta": {
"hash": "a67133bc7bf6d831340501643c7efede36b09a7f36516e0cc7fb7d3ff06ee7bd"
}
},
{
"id": "button-border-animation",
"type": "snippetListing",
"title": "Button border animation",
"attributes": {
"text": "Creates a border animation on hover.\n\n",
"tags": [
"animation",
"intermediate"
]
},
"meta": {
"hash": "754e9dc4b820bbd2f8650a06df9cf2d2e3755b0da2ae2be787bf77eb79d0e296"
}
},
{
"id": "calc",
"type": "snippetListing",
"title": "Calc()",
"attributes": {
"text": "The function calc() allows to define CSS values with the use of mathematical expressions, the value adopted for the property is the result of a mathematical expression.\n\n\n\n",
"tags": [
"other",
"intermediate"
]
},
"meta": {
"hash": "fce405ede3127f068a4598d709484dae8b3991594daf5d54fbac043989d04427"
}
},
{
"id": "circle",
"type": "snippetListing",
"title": "Circle",
"attributes": {
"text": "Creates a circle shape with pure CSS.\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "bc4bbf2fdc171fc581c72926d8b383391c1f01ccdb8853a3b4a1a290fddc704e"
}
},
{
"id": "clearfix",
"type": "snippetListing",
"title": "Clearfix",
"attributes": {
"text": "Ensures that an element self-clears its children.\n\n###### Note: This is only useful if you are still using float to build layouts. Please consider using a modern approach with flexbox layout or grid layout.\n\n",
"tags": [
"layout",
"intermediate"
]
},
"meta": {
"hash": "1047b62d1a05d21a0d5cd370eca23ca76dc52454b1aefdb4bddd83d78a624458"
}
},
{
"id": "constant-width-to-height-ratio",
"type": "snippetListing",
"title": "Constant width to height ratio",
"attributes": {
"text": "Given an element of variable width, it will ensure its height remains proportionate in a responsive fashion\n(i.e., its width to height ratio remains constant).\n\n",
"tags": [
"layout",
"intermediate"
]
},
"meta": {
"hash": "6d9a462223d6c4d2cb83b0b3ffea991d18d5b4c2bf81881515ff07a7c49db1c8"
}
},
{
"id": "counter",
"type": "snippetListing",
"title": "Counter",
"attributes": {
"text": "Counters are, in essence, variables maintained by CSS whose values may be incremented by CSS rules to track how many times they're used.\n\n",
"tags": [
"visual",
"other",
"intermediate"
]
},
"meta": {
"hash": "99ba7570de689df640c22feb4a36874d4d7bc4bd1310227cedabe55d95b83f5e"
}
},
{
"id": "custom-scrollbar",
"type": "snippetListing",
"title": "Custom scrollbar",
"attributes": {
"text": "Customizes the scrollbar style for the document and elements with scrollable overflow, on WebKit platforms.\n\n\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "cd46a337bd537c752499182a18575a921f300cf73b490dea2c1378360e670168"
}
},
{
"id": "custom-text-selection",
"type": "snippetListing",
"title": "Custom text selection",
"attributes": {
"text": "Changes the styling of text selection.\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "fa278f7299768bd484a01a9d63c7e8ba684f3870da53388aae5290d5b4dee7bb"
}
},
{
"id": "custom-variables",
"type": "snippetListing",
"title": "Custom variables",
"attributes": {
"text": "CSS variables that contain specific values to be reused throughout a document.\n\n",
"tags": [
"other",
"intermediate"
]
},
"meta": {
"hash": "c03b24d37e43d056aa2e5f6eebb8d61a86d4d9f6587270aa8606df771c27f3cc"
}
},
{
"id": "disable-selection",
"type": "snippetListing",
"title": "Disable selection",
"attributes": {
"text": "Makes the content unselectable.\n\n",
"tags": [
"interactivity",
"intermediate"
]
},
"meta": {
"hash": "ccae3478e0de1ff02bab055c17e2f877bd39cd4994f6b18d0bf85d16e9912dda"
}
},
{
"id": "display-table-centering",
"type": "snippetListing",
"title": "Display table centering",
"attributes": {
"text": "Vertically and horizontally centers a child element within its parent element using `display: table` (as an alternative to `flexbox`).\n\n",
"tags": [
"layout",
"intermediate"
]
},
"meta": {
"hash": "4b2e48224b08521eda138be98210a643ff98733fed8f9dd9d1921a1f56084450"
}
},
{
"id": "donut-spinner",
"type": "snippetListing",
"title": "Donut spinner",
"attributes": {
"text": "Creates a donut spinner that can be used to indicate the loading of content.\n\n",
"tags": [
"animation",
"intermediate"
]
},
"meta": {
"hash": "cf2af5a585eecb3c98ee07d02d2e94ebee5efdf01523313b61faa199309bee0d"
}
},
{
"id": "dynamic-shadow",
"type": "snippetListing",
"title": "Dynamic shadow",
"attributes": {
"text": "Creates a shadow similar to `box-shadow` but based on the colors of the element itself.\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "125c57e66fac5a5f0f231889d6cc2bbab0c8693070254386140468ec39045556"
}
},
{
"id": "easing-variables",
"type": "snippetListing",
"title": "Easing variables",
"attributes": {
"text": "Variables that can be reused for `transition-timing-function` properties, more\npowerful than the built-in `ease`, `ease-in`, `ease-out` and `ease-in-out`.\n\n",
"tags": [
"animation",
"intermediate"
]
},
"meta": {
"hash": "742eec324f2b9eb3a5cf972639069523e1244ee95bc6335192587b8f9352f2da"
}
},
{
"id": "etched-text",
"type": "snippetListing",
"title": "Etched text",
"attributes": {
"text": "Creates an effect where text appears to be \"etched\" or engraved into the background.\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "fade4c04ebb8c2e6d680cc803d54738dbb166b6f608f13a94eacfe0b762fb0c4"
}
},
{
"id": "evenly-distributed-children",
"type": "snippetListing",
"title": "Evenly distributed children",
"attributes": {
"text": "Evenly distributes child elements within a parent element.\n\n",
"tags": [
"layout",
"intermediate"
]
},
"meta": {
"hash": "3489ccfed45a24c536c275e7a74cc8727fd801baae9c2b5123265bcd75e70a37"
}
},
{
"id": "fit-image-in-container",
"type": "snippetListing",
"title": "Fit image in container",
"attributes": {
"text": "Changes the fit and position of an image within its container while preserving its aspect ratio. Previously only possible using a background image and the `background-size` property.\n\n",
"tags": [
"layout",
"visual",
"intermediate"
]
},
"meta": {
"hash": "79cb705dc7ae6fbde94fac8dc3b273dda73be7a4739a2cdf9f160ad6f678133a"
}
},
{
"id": "flexbox-centering",
"type": "snippetListing",
"title": "Flexbox centering",
"attributes": {
"text": "Horizontally and vertically centers a child element within a parent element using `flexbox`.\n\n",
"tags": [
"layout",
"intermediate"
]
},
"meta": {
"hash": "ec62036188c484a98e832c45535eafbf528020f116a5aac0c853b56f6b91162e"
}
},
{
"id": "focus-within",
"type": "snippetListing",
"title": "Focus Within",
"attributes": {
"text": "Changes the appearance of a form if any of its children are focused.\n\n",
"tags": [
"visual",
"interactivity",
"intermediate"
]
},
"meta": {
"hash": "db08a880101d0e82dd0263582e7b251d88450df278e7d79a0a544d1575580c55"
}
},
{
"id": "fullscreen",
"type": "snippetListing",
"title": "Fullscreen",
"attributes": {
"text": "The :fullscreen CSS pseudo-class represents an element that's displayed when the browser is in fullscreen mode.\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "2bb108851d15d46749ba4f1caa2ba08d4754a2ee988e812280925c637f610d40"
}
},
{
"id": "ghost-trick",
"type": "snippetListing",
"title": "Ghost trick",
"attributes": {
"text": "Vertically centers an element in another.\n\n",
"tags": [
"layout",
"intermediate"
]
},
"meta": {
"hash": "622cd2ec51c26b37be3ea38a55d71bbb1b5944f14188b484c4903e23b45086c9"
}
},
{
"id": "gradient-text",
"type": "snippetListing",
"title": "Gradient text",
"attributes": {
"text": "Gives text a gradient color.\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "56a606934b50e0c3c53eb4819ee3fcb57344a9bd7dce91ae26c6f98c99a4c386"
}
},
{
"id": "grid-centering",
"type": "snippetListing",
"title": "Grid centering",
"attributes": {
"text": "Horizontally and vertically centers a child element within a parent element using `grid`.\n\n",
"tags": [
"layout",
"intermediate"
]
},
"meta": {
"hash": "167267471e09e52f20ed40ffb23b8efa1cfba6434fea5b7c82810c62c8ba5ee0"
}
},
{
"id": "hairline-border",
"type": "snippetListing",
"title": "Hairline border",
"attributes": {
"text": "Gives an element a border equal to 1 native device pixel in width, which can look\nvery sharp and crisp.\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "0e45709255d9ca14598c5fa54c2ae5490f81a0ef67c466d6d678da584340428b"
}
},
{
"id": "height-transition",
"type": "snippetListing",
"title": "Height transition",
"attributes": {
"text": "Transitions an element's height from `0` to `auto` when its height is unknown.\n\n",
"tags": [
"animation",
"intermediate"
]
},
"meta": {
"hash": "7ee6c8c6027ccbba77e7c02bdb79cf22a0d22d7acece1ea5408da19a7f986d7a"
}
},
{
"id": "hover-shadow-box-animation",
"type": "snippetListing",
"title": "Hover shadow box animation",
"attributes": {
"text": "Creates a shadow box around the text when it is hovered.\n\n",
"tags": [
"animation",
"intermediate"
]
},
"meta": {
"hash": "8fc3c2c5fc4248b2ec6bfdbc96cb5912936ccd5865d88da7a3a041a32168968a"
}
},
{
"id": "hover-underline-animation",
"type": "snippetListing",
"title": "Hover underline animation",
"attributes": {
"text": "Creates an animated underline effect when the text is hovered over.\n\n<small>**Credit:** https://flatuicolors.com/</small>\n\n",
"tags": [
"animation",
"intermediate"
]
},
"meta": {
"hash": "5ee579737769cd734df27df60b3c0fa296965192f1cdca45d4c43a414c6b9760"
}
},
{
"id": "last-item-with-remaining-available-height",
"type": "snippetListing",
"title": "Last item with remaining available height",
"attributes": {
"text": "Take advantage of available viewport space by giving the last element the remaining available space in current viewport, even when resizing the window.\n\n",
"tags": [
"layout",
"intermediate"
]
},
"meta": {
"hash": "522a5e309cea7f772600ca1d5f89ee911f0aec94b157e2adb94ac3f479445c01"
}
},
{
"id": "mouse-cursor-gradient-tracking",
"type": "snippetListing",
"title": "Mouse cursor gradient tracking",
"attributes": {
"text": "A hover effect where the gradient follows the mouse cursor.\n\n<small class=\"snippet__credit\">**Credit:** [Tobias Reich](https://codepen.io/electerious/pen/MQrRxX)</small>\n\n",
"tags": [
"visual",
"interactivity",
"intermediate"
]
},
"meta": {
"hash": "707e50fd348545fc1f9950ef9cda9e74b28ffe57098cc2849c72358c4bf7bea6"
}
},
{
"id": "not-selector",
"type": "snippetListing",
"title": ":not selector",
"attributes": {
"text": "The `:not` psuedo selector is useful for styling a group of elements, while leaving the last (or specified) element unstyled.\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "7d2727a78b8ee2a55a823116fb01099efa227952d8fba8c35a1d3067675985d5"
}
},
{
"id": "offscreen",
"type": "snippetListing",
"title": "Offscreen",
"attributes": {
"text": "A bulletproof way to completely hide an element visually and positionally in the DOM while still allowing it to be accessed by JavaScript and readable by screen readers. This method is very useful for accessibility ([ADA](https://adata.org/learn-about-ada)) development when more context is needed for visually-impaired users. As an alternative to `display: none` which is not readable by screen readers or `visibility: hidden` which takes up physical space in the DOM.\n\n",
"tags": [
"layout",
"visual",
"intermediate"
]
},
"meta": {
"hash": "000a1fd47f9dafba64a625aefc689e33592adf69c8c5ecffb6de68a83f0218aa"
}
},
{
"id": "overflow-scroll-gradient",
"type": "snippetListing",
"title": "Overflow scroll gradient",
"attributes": {
"text": "Adds a fading gradient to an overflowing element to better indicate there is more content to be scrolled.\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "6ce90f10b574780c196faad6a498c8d5b1e5e2d6c3464ded47ceb1056b24baa3"
}
},
{
"id": "popout-menu",
"type": "snippetListing",
"title": "Popout menu",
"attributes": {
"text": "Reveals an interactive popout menu on hover and focus.\n\n",
"tags": [
"interactivity",
"intermediate"
]
},
"meta": {
"hash": "8f5c19fb14ee8039e93ff0f3185cb0d27971dd2509f6100fdfd290478211a42b"
}
},
{
"id": "pretty-text-underline",
"type": "snippetListing",
"title": "Pretty text underline",
"attributes": {
"text": "A nicer alternative to `text-decoration: underline` or `<u></u>` where descenders do not clip the underline.\nNatively implemented as `text-decoration-skip-ink: auto` but it has less control over the underline.\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "5d0eb22cb50d397c88c5f3a111a6a85d6cfa7d61fb7e88c966fdd55fe9d5b587"
}
},
{
"id": "reset-all-styles",
"type": "snippetListing",
"title": "Reset all styles",
"attributes": {
"text": "Resets all styles to default values with one property. This will not affect `direction` and `unicode-bidi` properties.\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "d601816311504b7521c8bba212e2c4b6fb50317ec10df60991a414bae1c82bf9"
}
},
{
"id": "shape-separator",
"type": "snippetListing",
"title": "Shape separator",
"attributes": {
"text": "Uses an SVG shape to separate two different blocks to create more a interesting visual appearance compared to standard horizontal separation.\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "1ce90fc518399c775df1af832345729bdde2a3c12bbdd2fd66c40b9c57a65aa1"
}
},
{
"id": "sibling-fade",
"type": "snippetListing",
"title": "Sibling fade",
"attributes": {
"text": "Fades out the siblings of a hovered item.\n\n",
"tags": [
"interactivity",
"intermediate"
]
},
"meta": {
"hash": "a1e353f0dc466a24881986bbaf8f2cd9f367fe80d54ac8e1481d82e021236389"
}
},
{
"id": "system-font-stack",
"type": "snippetListing",
"title": "System font stack",
"attributes": {
"text": "Uses the native font of the operating system to get close to a native app feel.\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "0a35134bd1fe1da354810ef3228f50a81105997c0e81138c97cc9ace399adbb2"
}
},
{
"id": "toggle-switch",
"type": "snippetListing",
"title": "Toggle switch",
"attributes": {
"text": "Creates a toggle switch with CSS only.\n\n",
"tags": [
"visual",
"interactivity",
"intermediate"
]
},
"meta": {
"hash": "6af55186736cae8da52a2a92bf7dcf56f6f23ca49ce083ad6653e373fa2d152e"
}
},
{
"id": "transform-centering",
"type": "snippetListing",
"title": "Transform centering",
"attributes": {
"text": "Vertically and horizontally centers a child element within its parent element using `position: absolute` and `transform: translate()` (as an alternative to `flexbox` or `display: table`). Similar to `flexbox`, this method does not require you to know the height or width of your parent or child so it is ideal for responsive applications.\n\n",
"tags": [
"layout",
"intermediate"
]
},
"meta": {
"hash": "67b5f73d1e7aa4850b39c461fd93a548e106569554cf6e87f2a9981ecfb77ad1"
}
},
{
"id": "triangle",
"type": "snippetListing",
"title": "Triangle",
"attributes": {
"text": "Creates a triangle shape with pure CSS.\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "d585e568bd1b8ca9c8a7d2fa52682c80249bf1e6055316bc3526ee54d8d26fb9"
}
},
{
"id": "truncate-text-multiline",
"type": "snippetListing",
"title": "Truncate text multiline",
"attributes": {
"text": "If the text is longer than one line, it will be truncated for `n` lines and end with an gradient fade.\n\n",
"tags": [
"layout",
"intermediate"
]
},
"meta": {
"hash": "487720c3bf69c29c26cd0033d69f7391b634c4d9bf0e72a1da29957b945a9cbf"
}
},
{
"id": "truncate-text",
"type": "snippetListing",
"title": "Truncate text",
"attributes": {
"text": "If the text is longer than one line, it will be truncated and end with an ellipsis `…`.\n\n",
"tags": [
"layout",
"intermediate"
]
},
"meta": {
"hash": "db78085f4583d992126b4ceb74cc680300aa67c16d37b68d55c140fe453840d3"
}
},
{
"id": "zebra-striped-list",
"type": "snippetListing",
"title": "Zebra striped list",
"attributes": {
"text": "Creates a striped list with alternating background colors, which is useful for differentiating siblings that have content spread across a wide row.\n\n",
"tags": [
"visual",
"intermediate"
]
},
"meta": {
"hash": "2c11f668918ede13c492719a76a5b89173aa4e72ad896cd2d7e45bed9671a2f0"
}
}
],
"meta": {
"specification": "http://jsonapi.org/format/",
"type": "snippetListingArray"
}
}

1256
snippet_data/snippets.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,8 +5,6 @@ tags: animation,intermediate
Creates a bouncing loader animation.
#### HTML
```html
<div class="bouncing-loader">
<div></div>
@ -15,8 +13,6 @@ Creates a bouncing loader animation.
</div>
```
#### CSS
```css
@keyframes bouncing-loader {
to {
@ -44,8 +40,6 @@ Creates a bouncing loader animation.
}
```
#### Demo
#### Explanation
Note: `1rem` is usually `16px`.

View File

@ -5,15 +5,11 @@ tags: layout,intermediate
Resets the box-model so that `width`s and `height`s are not affected by their `border`s or `padding`.
#### HTML
```html
<div class="box">border-box</div>
<div class="box content-box">content-box</div>
```
#### CSS
```css
html {
box-sizing: border-box;
@ -37,8 +33,6 @@ html {
}
```
#### Demo
#### Explanation
1. `box-sizing: border-box` makes the addition of `padding` or `border`s not affect an element's `width` or `height`.

View File

@ -5,14 +5,10 @@ tags: animation,intermediate
Creates a border animation on hover.
#### HTML
```html
<div class="button-border"><button class="button">Submit</button></div>
```
#### CSS
```css
.button {
background-color: #c47135;
@ -51,14 +47,8 @@ Creates a border animation on hover.
}
```
#### Demo
#### Explanation
Use the `:before` and `:after` pseduo-elements as borders that animate on hover.
#### Browser support
<!-- date: 2018-10-30 -->

View File

@ -5,14 +5,12 @@ tags: other,intermediate
The function calc() allows to define CSS values with the use of mathematical expressions, the value adopted for the property is the result of a mathematical expression.
#### HTML
```html
<div class="box-example"></div>
```
#### CSS
```css
.box-example {
height: 280px;
@ -21,7 +19,7 @@ The function calc() allows to define CSS values with the use of mathematical exp
}
```
#### Demo
If you want to align a background-image from right and bottom wasn't possible with just straight length values.
So now it's possible using calc():

View File

@ -5,14 +5,10 @@ tags: visual,intermediate
Creates a circle shape with pure CSS.
#### HTML
```html
<div class="circle"></div>
```
#### CSS
```css
.circle {
border-radius: 50%;
@ -22,8 +18,6 @@ Creates a circle shape with pure CSS.
}
```
#### Demo
#### Explanation
`border-radius: 50%` curves the borders of an element to create a circle.

View File

@ -7,8 +7,6 @@ Ensures that an element self-clears its children.
###### Note: This is only useful if you are still using float to build layouts. Please consider using a modern approach with flexbox layout or grid layout.
#### HTML
```html
<div class="clearfix">
<div class="floated">float a</div>
@ -17,8 +15,6 @@ Ensures that an element self-clears its children.
</div>
```
#### CSS
```css
.clearfix::after {
content: '';
@ -31,8 +27,6 @@ Ensures that an element self-clears its children.
}
```
#### Demo
#### Explanation
1. `.clearfix::after` defines a pseudo-element.

View File

@ -6,14 +6,10 @@ tags: layout,intermediate
Given an element of variable width, it will ensure its height remains proportionate in a responsive fashion
(i.e., its width to height ratio remains constant).
#### HTML
```html
<div class="constant-width-to-height-ratio"></div>
```
#### CSS
```css
.constant-width-to-height-ratio {
background: #333;
@ -31,8 +27,6 @@ Given an element of variable width, it will ensure its height remains proportion
}
```
#### Demo
#### Explanation
`padding-top` on the `::before` pseudo-element causes the height of the element to equal a percentage of

View File

@ -5,8 +5,6 @@ tags: visual, other,intermediate
Counters are, in essence, variables maintained by CSS whose values may be incremented by CSS rules to track how many times they're used.
#### HTML
```html
<ul>
<li>List item</li>
@ -22,8 +20,6 @@ Counters are, in essence, variables maintained by CSS whose values may be increm
</ul>
```
#### CSS
```css
ul {
counter-reset: counter;
@ -35,8 +31,6 @@ li::before {
}
```
#### Demo
#### Explanation
You can create a ordered list using any type of HTML.

View File

@ -5,7 +5,7 @@ tags: visual,intermediate
Customizes the scrollbar style for the document and elements with scrollable overflow, on WebKit platforms.
#### HTML
```html
<div class="custom-scrollbar">
@ -18,7 +18,7 @@ Customizes the scrollbar style for the document and elements with scrollable ove
</div>
```
#### CSS
```css
.custom-scrollbar {
@ -43,7 +43,7 @@ Customizes the scrollbar style for the document and elements with scrollable ove
}
```
#### Demo
#### Explanation

View File

@ -5,14 +5,10 @@ tags: visual,intermediate
Changes the styling of text selection.
#### HTML
```html
<p class="custom-text-selection">Select some of this text.</p>
```
#### CSS
```css
::selection {
background: aquamarine;
@ -24,8 +20,6 @@ Changes the styling of text selection.
}
```
#### Demo
#### Explanation
`::selection` defines a pseudo selector on an element to style text within it when selected. Note that if you don't combine any other selector your style will be applied at document root level, to any selectable element.

View File

@ -5,14 +5,10 @@ tags: other,intermediate
CSS variables that contain specific values to be reused throughout a document.
#### HTML
```html
<p class="custom-variables">CSS is awesome!</p>
```
#### CSS
```css
:root {
/* Place variables within here to use the variables globally. */
@ -30,8 +26,6 @@ CSS variables that contain specific values to be reused throughout a document.
}
```
#### Demo
#### Explanation
The variables are defined globally within the `:root` CSS pseudo-class which matches the root element of a tree representing the document. Variables can also be scoped to a selector if defined within the block.

View File

@ -5,23 +5,17 @@ tags: interactivity,intermediate
Makes the content unselectable.
#### HTML
```html
<p>You can select me.</p>
<p class="unselectable">You can't select me!</p>
```
#### CSS
```css
.unselectable {
user-select: none;
}
```
#### Demo
#### Explanation
`user-select: none` specifies that the text cannot be selected.

View File

@ -5,16 +5,12 @@ tags: layout,intermediate
Vertically and horizontally centers a child element within its parent element using `display: table` (as an alternative to `flexbox`).
#### HTML
```html
<div class="container">
<div class="center"><span>Centered content</span></div>
</div>
```
#### CSS
```css
.container {
border: 1px solid #333;
@ -35,8 +31,6 @@ Vertically and horizontally centers a child element within its parent element us
}
```
#### Demo
#### Explanation
1. `display: table` on '.center' allows the element to behave like a `<table>` HTML element.

View File

@ -5,14 +5,10 @@ tags: animation,intermediate
Creates a donut spinner that can be used to indicate the loading of content.
#### HTML
```html
<div class="donut"></div>
```
#### CSS
```css
@keyframes donut-spin {
0% {
@ -33,8 +29,6 @@ Creates a donut spinner that can be used to indicate the loading of content.
}
```
#### Demo
#### Explanation
Use a semi-transparent `border` for the whole element, except one side that will

View File

@ -5,14 +5,10 @@ tags: visual,intermediate
Creates a shadow similar to `box-shadow` but based on the colors of the element itself.
#### HTML
```html
<div class="dynamic-shadow"></div>
```
#### CSS
```css
.dynamic-shadow {
position: relative;
@ -34,8 +30,6 @@ Creates a shadow similar to `box-shadow` but based on the colors of the element
}
```
#### Demo
#### Explanation
1. `position: relative` on the element establishes a Cartesian positioning context for psuedo-elements.

View File

@ -6,14 +6,10 @@ tags: animation,intermediate
Variables that can be reused for `transition-timing-function` properties, more
powerful than the built-in `ease`, `ease-in`, `ease-out` and `ease-in-out`.
#### HTML
```html
<div class="easing-variables">Hover</div>
```
#### CSS
```css
:root {
/* Place variables in here to use globally */
@ -56,8 +52,6 @@ powerful than the built-in `ease`, `ease-in`, `ease-out` and `ease-in-out`.
}
```
#### Demo
#### Explanation
The variables are defined globally within the `:root` CSS pseudo-class which matches the root element of a tree representing the document. In HTML, `:root` represents the `<html>` element and is identical to the selector `html`, except that its specificity is higher.

View File

@ -5,14 +5,10 @@ tags: visual,intermediate
Creates an effect where text appears to be "etched" or engraved into the background.
#### HTML
```html
<p class="etched-text">I appear etched into the background.</p>
```
#### CSS
```css
.etched-text {
text-shadow: 0 2px white;
@ -22,8 +18,6 @@ Creates an effect where text appears to be "etched" or engraved into the backgro
}
```
#### Demo
#### Explanation
`text-shadow: 0 2px white` creates a white shadow offset `0px` horizontally and `2px` vertically

View File

@ -5,8 +5,6 @@ tags: layout,intermediate
Evenly distributes child elements within a parent element.
#### HTML
```html
<div class="evenly-distributed-children">
<p>Item1</p>
@ -15,8 +13,6 @@ Evenly distributes child elements within a parent element.
</div>
```
#### CSS
```css
.evenly-distributed-children {
display: flex;
@ -24,8 +20,6 @@ Evenly distributes child elements within a parent element.
}
```
#### Demo
#### Explanation
1. `display: flex` enables flexbox.

View File

@ -5,15 +5,11 @@ tags: layout, visual,intermediate
Changes the fit and position of an image within its container while preserving its aspect ratio. Previously only possible using a background image and the `background-size` property.
#### HTML
```html
<img class="image image-contain" src="https://picsum.photos/600/200" />
<img class="image image-cover" src="https://picsum.photos/600/200" />
```
#### CSS
```css
.image {
background: #34495e;
@ -33,8 +29,6 @@ Changes the fit and position of an image within its container while preserving i
}
```
#### Demo
#### Explanation
- `object-fit: contain` fits the entire image within the container while preserving its aspect ratio.
@ -44,7 +38,3 @@ Changes the fit and position of an image within its container while preserving i
#### Browser support
- https://caniuse.com/#feat=object-fit
<!-- date: 2018-10-31 -->

View File

@ -5,14 +5,10 @@ tags: layout,intermediate
Horizontally and vertically centers a child element within a parent element using `flexbox`.
#### HTML
```html
<div class="flexbox-centering"><div class="child">Centered content.</div></div>
```
#### CSS
```css
.flexbox-centering {
display: flex;
@ -22,8 +18,6 @@ Horizontally and vertically centers a child element within a parent element usin
}
```
#### Demo
#### Explanation
1. `display: flex` enables flexbox.

View File

@ -5,8 +5,6 @@ tags: visual, interactivity,intermediate
Changes the appearance of a form if any of its children are focused.
#### HTML
```html
<div class="focus-within">
<form>
@ -16,8 +14,6 @@ Changes the appearance of a form if any of its children are focused.
</div>
```
#### CSS
```css
form {
border: 3px solid #2d98da;
@ -31,8 +27,6 @@ form:focus-within {
}
```
#### Demo
<!-- Leave this blank, the build script will generate the demo for you. -->
#### Explanation

View File

@ -5,8 +5,6 @@ tags: visual,intermediate
The :fullscreen CSS pseudo-class represents an element that's displayed when the browser is in fullscreen mode.
#### HTML
```html
<div class="container">
<p><em>Click the button below to enter the element into fullscreen mode. </em></p>
@ -18,8 +16,6 @@ The :fullscreen CSS pseudo-class represents an element that's displayed when the
</div>
```
#### CSS
```css
.container {
margin: 40px auto;
@ -50,8 +46,6 @@ The :fullscreen CSS pseudo-class represents an element that's displayed when the
}
```
#### Demo
#### Explanation
1. `fullscreen` CSS pseudo-class selector is used to select and style an element that is being displayed in fullscreen mode.

View File

@ -5,16 +5,12 @@ tags: layout,intermediate
Vertically centers an element in another.
#### HTML
```html
<div class="ghost-trick">
<div class="ghosting"><p>Vertically centered without changing the position property.</p></div>
</div>
```
#### CSS
```css
.ghosting {
height: 300px;
@ -34,8 +30,6 @@ p {
}
```
#### Demo
#### Explanation
Use the style of a `:before` pseudo-element to vertically align inline elements without changing their `position` property.

View File

@ -5,14 +5,10 @@ tags: visual,intermediate
Gives text a gradient color.
#### HTML
```html
<p class="gradient-text">Gradient text</p>
```
#### CSS
```css
.gradient-text {
background: -webkit-linear-gradient(pink, red);
@ -21,8 +17,6 @@ Gives text a gradient color.
}
```
#### Demo
#### Explanation
1. `background: -webkit-linear-gradient(...)` gives the text element a gradient background.

View File

@ -5,14 +5,10 @@ tags: layout,intermediate
Horizontally and vertically centers a child element within a parent element using `grid`.
#### HTML
```html
<div class="grid-centering"><div class="child">Centered content.</div></div>
```
#### CSS
```css
.grid-centering {
display: grid;
@ -22,8 +18,6 @@ Horizontally and vertically centers a child element within a parent element usin
}
```
#### Demo
#### Explanation
1. `display: grid` enables grid.

View File

@ -6,14 +6,10 @@ tags: visual,intermediate
Gives an element a border equal to 1 native device pixel in width, which can look
very sharp and crisp.
#### HTML
```html
<div class="hairline-border">text</div>
```
#### CSS
```css
.hairline-border {
box-shadow: 0 0 0 1px;
@ -38,8 +34,6 @@ very sharp and crisp.
}
```
#### Demo
#### Explanation
1. `box-shadow`, when only using spread, adds a pseudo-border which can use subpixels\*.

View File

@ -5,8 +5,6 @@ tags: animation,intermediate
Transitions an element's height from `0` to `auto` when its height is unknown.
#### HTML
```html
<div class="trigger">
Hover me to see a height transition.
@ -14,8 +12,6 @@ Transitions an element's height from `0` to `auto` when its height is unknown.
</div>
```
#### CSS
```css
.el {
transition: max-height 0.5s;
@ -28,28 +24,20 @@ Transitions an element's height from `0` to `auto` when its height is unknown.
}
```
#### JavaScript
```js
var el = document.querySelector('.el')
var height = el.scrollHeight
el.style.setProperty('--max-height', height + 'px')
```
#### Demo
#### Explanation
##### CSS
1. `transition: max-height: 0.5s cubic-bezier(...)` specifies that changes to `max-height` should be transitioned over 0.5 seconds, using an `ease-out-quint` timing function.
2. `overflow: hidden` prevents the contents of the hidden element from overflowing its container.
3. `max-height: 0` specifies that the element has no height initially.
4. `.target:hover > .el` specifies that when the parent is hovered over, target a child `.el` within
it and use the `--max-height` variable which was defined by JavaScript.
##### JavaScript
---
1. `el.scrollHeight` is the height of the element including overflow, which will change dynamically
based on the content of the element.
2. `el.style.setProperty(...)` sets the `--max-height` CSS variable which is used to specify the `max-height` of the element the target is hovered over, allowing it to transition smoothly from 0 to auto.

View File

@ -5,14 +5,10 @@ tags: animation,intermediate
Creates a shadow box around the text when it is hovered.
#### HTML
```html
<p class="hover-shadow-box-animation">Box it!</p>
```
#### CSS
```css
.hover-shadow-box-animation {
display: inline-block;
@ -31,8 +27,6 @@ Creates a shadow box around the text when it is hovered.
}
```
#### Demo
#### Explanation
1. `display: inline-block` to set width and length for `p` element thus making it an `inline-block`.

View File

@ -7,14 +7,10 @@ Creates an animated underline effect when the text is hovered over.
<small>**Credit:** https://flatuicolors.com/</small>
#### HTML
```html
<p class="hover-underline-animation">Hover this text to see the effect!</p>
```
#### CSS
```css
.hover-underline-animation {
display: inline-block;
@ -39,8 +35,6 @@ Creates an animated underline effect when the text is hovered over.
}
```
#### Demo
#### Explanation
1. `display: inline-block` makes the block `p` an `inline-block` to prevent the underline from

View File

@ -5,8 +5,6 @@ tags: layout,intermediate
Take advantage of available viewport space by giving the last element the remaining available space in current viewport, even when resizing the window.
#### HTML
```html
<div class="container">
<div>Div 1</div>
@ -15,8 +13,6 @@ Take advantage of available viewport space by giving the last element the remain
</div>
```
#### CSS
```css
html,
body {
@ -36,8 +32,6 @@ body {
}
```
#### Demo
#### Explanation
1. `height: 100%` set the height of container as viewport height.
@ -52,7 +46,3 @@ The parent must have a viewport height. `flex-grow: 1` could be applied to the f
<span class="snippet__support-note">⚠️ Needs prefixes for full support.</span>
- https://caniuse.com/#feat=flexbox
<!-- date: 2018-09-30 -->

View File

@ -7,14 +7,10 @@ A hover effect where the gradient follows the mouse cursor.
<small class="snippet__credit">**Credit:** [Tobias Reich](https://codepen.io/electerious/pen/MQrRxX)</small>
#### HTML
```html
<button class="mouse-cursor-gradient-tracking"><span>Hover me</span></button>
```
#### CSS
```css
.mouse-cursor-gradient-tracking {
position: relative;
@ -50,8 +46,6 @@ A hover effect where the gradient follows the mouse cursor.
}
```
#### JavaScript
```js
var btn = document.querySelector('.mouse-cursor-gradient-tracking')
btn.onmousemove = function(e) {
@ -62,11 +56,11 @@ btn.onmousemove = function(e) {
}
```
#### Demo
#### Explanation
_TODO_
1. `--x` and `--y` are used to track the position of the mouse on the button.
2. `--size` is used to keep modify of the gradient's dimensions.
3. `background: radial-gradient(circle closest-side, pink, transparent);` creates the gradient at the correct postion.
#### Browser support

View File

@ -5,8 +5,6 @@ tags: visual,intermediate
The `:not` psuedo selector is useful for styling a group of elements, while leaving the last (or specified) element unstyled.
#### HTML
```html
<ul class="css-not-selector-shortcut">
<li>One</li>
@ -16,8 +14,6 @@ The `:not` psuedo selector is useful for styling a group of elements, while leav
</ul>
```
#### CSS
```css
.css-not-selector-shortcut {
display: flex;
@ -38,8 +34,6 @@ li:not(:last-child) {
}
```
#### Demo
#### Explanation
`li:not(:last-child)` specifies that the styles should apply to all `li` elements except

View File

@ -5,16 +5,12 @@ tags: layout, visual,intermediate
A bulletproof way to completely hide an element visually and positionally in the DOM while still allowing it to be accessed by JavaScript and readable by screen readers. This method is very useful for accessibility ([ADA](https://adata.org/learn-about-ada)) development when more context is needed for visually-impaired users. As an alternative to `display: none` which is not readable by screen readers or `visibility: hidden` which takes up physical space in the DOM.
#### HTML
```html
<a class="button" href="http://pantswebsite.com">
Learn More <span class="offscreen"> about pants</span>
</a>
```
#### CSS
```css
.offscreen {
border: 0;
@ -28,8 +24,6 @@ A bulletproof way to completely hide an element visually and positionally in the
}
```
#### Demo
#### Explanation
1. Remove all borders.

View File

@ -5,8 +5,6 @@ tags: visual,intermediate
Adds a fading gradient to an overflowing element to better indicate there is more content to be scrolled.
#### HTML
```html
<div class="overflow-scroll-gradient">
<div class="overflow-scroll-gradient__scroller">
@ -22,8 +20,6 @@ Adds a fading gradient to an overflowing element to better indicate there is mor
</div>
```
#### CSS
```css
.overflow-scroll-gradient {
position: relative;
@ -50,8 +46,6 @@ Adds a fading gradient to an overflowing element to better indicate there is mor
}
```
#### Demo
#### Explanation
1. `position: relative` on the parent establishes a Cartesian positioning context for pseudo-elements.

View File

@ -5,14 +5,10 @@ tags: interactivity,intermediate
Reveals an interactive popout menu on hover and focus.
#### HTML
```html
<div class="reference" tabindex="0"><div class="popout-menu">Popout menu</div></div>
```
#### CSS
```css
.reference {
position: relative;
@ -35,8 +31,6 @@ Reveals an interactive popout menu on hover and focus.
}
```
#### Demo
#### Explanation
1. `position: relative` on the reference parent establishes a Cartesian positioning context for its child.

View File

@ -6,14 +6,10 @@ tags: visual,intermediate
A nicer alternative to `text-decoration: underline` or `<u></u>` where descenders do not clip the underline.
Natively implemented as `text-decoration-skip-ink: auto` but it has less control over the underline.
#### HTML
```html
<p class="pretty-text-underline">Pretty text underline without clipping descending letters.</p>
```
#### CSS
```css
.pretty-text-underline {
display: inline;
@ -33,8 +29,6 @@ Natively implemented as `text-decoration-skip-ink: auto` but it has less control
}
```
#### Demo
#### Explanation
1. `text-shadow` uses 4 values with offsets that cover a 4x4 px area to ensure the underline

View File

@ -5,8 +5,6 @@ tags: visual,intermediate
Resets all styles to default values with one property. This will not affect `direction` and `unicode-bidi` properties.
#### HTML
```html
<div class="reset-all-styles">
<h5>Title</h5>
@ -18,16 +16,12 @@ Resets all styles to default values with one property. This will not affect `dir
</div>
```
#### CSS
```css
.reset-all-styles {
all: initial;
}
```
#### Demo
#### Explanation
The `all` property allows you to reset all styles (inherited or not) to default values.

View File

@ -5,14 +5,10 @@ tags: visual,intermediate
Uses an SVG shape to separate two different blocks to create more a interesting visual appearance compared to standard horizontal separation.
#### HTML
```html
<div class="shape-separator"></div>
```
#### CSS
```css
.shape-separator {
position: relative;
@ -29,8 +25,6 @@ Uses an SVG shape to separate two different blocks to create more a interesting
}
```
#### Demo
#### Explanation
1. `position: relative` on the element establishes a Cartesian positioning context for pseudo elements.

View File

@ -5,8 +5,6 @@ tags: interactivity,intermediate
Fades out the siblings of a hovered item.
#### HTML
```html
<div class="sibling-fade">
<span>Item 1</span> <span>Item 2</span> <span>Item 3</span> <span>Item 4</span>
@ -14,8 +12,6 @@ Fades out the siblings of a hovered item.
</div>
```
#### CSS
```css
span {
padding: 0 1rem;
@ -27,8 +23,6 @@ span {
}
```
#### Demo
#### Explanation
1. `transition: opacity 0.2s` specifies that changes to opacity will be transitioned over 0.2 seconds.

View File

@ -5,14 +5,10 @@ tags: visual,intermediate
Uses the native font of the operating system to get close to a native app feel.
#### HTML
```html
<p class="system-font-stack">This text uses the system font.</p>
```
#### CSS
```css
.system-font-stack {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu,
@ -20,8 +16,6 @@ Uses the native font of the operating system to get close to a native app feel.
}
```
#### Demo
#### Explanation
The browser looks for each successive font, preferring the first one if possible, and

View File

@ -5,14 +5,10 @@ tags: visual, interactivity,intermediate
Creates a toggle switch with CSS only.
#### HTML
```html
<input type="checkbox" id="toggle" class="offscreen" /> <label for="toggle" class="switch"></label>
```
#### CSS
```css
.switch {
position: relative;
@ -50,8 +46,6 @@ input[type='checkbox']:checked + .switch {
}
```
#### Demo
#### Explanation
This effect is styling only the `<label>` element to look like a toggle switch, and hiding the actual `<input>` checkbox by positioning it offscreen. When clicking the label associated with the `<input>` element, it sets the `<input>` checkbox into the `:checked` state.
@ -69,7 +63,3 @@ This effect is styling only the `<label>` element to look like a toggle switch,
<span class="snippet__support-note">⚠️ Requires prefixes for full support.</span>
- https://caniuse.com/#feat=transforms2d
<!-- date: 2018-10-03 -->

View File

@ -5,14 +5,10 @@ tags: layout,intermediate
Vertically and horizontally centers a child element within its parent element using `position: absolute` and `transform: translate()` (as an alternative to `flexbox` or `display: table`). Similar to `flexbox`, this method does not require you to know the height or width of your parent or child so it is ideal for responsive applications.
#### HTML
```html
<div class="parent"><div class="child">Centered content</div></div>
```
#### CSS
```css
.parent {
border: 1px solid #333;
@ -30,8 +26,6 @@ Vertically and horizontally centers a child element within its parent element us
}
```
#### Demo
#### Explanation
1. `position: absolute` on the child element allows it to be positioned based on its containing block.

View File

@ -5,14 +5,10 @@ tags: visual,intermediate
Creates a triangle shape with pure CSS.
#### HTML
```html
<div class="triangle"></div>
```
#### CSS
```css
.triangle {
width: 0;
@ -23,8 +19,6 @@ Creates a triangle shape with pure CSS.
}
```
#### Demo
#### Explanation
[View this link for a detailed explanation.](https://stackoverflow.com/q/7073484)

View File

@ -5,8 +5,6 @@ tags: layout,intermediate
If the text is longer than one line, it will be truncated for `n` lines and end with an gradient fade.
#### HTML
```html
<p class="truncate-text-multiline">
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut
@ -14,8 +12,6 @@ If the text is longer than one line, it will be truncated for `n` lines and end
</p>
```
#### CSS
```css
.truncate-text-multiline {
overflow: hidden;
@ -39,8 +35,6 @@ If the text is longer than one line, it will be truncated for `n` lines and end
}
```
#### Demo
#### Explanation
1. `overflow: hidden` prevents the text from overflowing its dimensions

View File

@ -5,14 +5,10 @@ tags: layout,intermediate
If the text is longer than one line, it will be truncated and end with an ellipsis `…`.
#### HTML
```html
<p class="truncate-text">If I exceed one line's width, I will be truncated.</p>
```
#### CSS
```css
.truncate-text {
overflow: hidden;
@ -22,8 +18,6 @@ If the text is longer than one line, it will be truncated and end with an ellips
}
```
#### Demo
#### Explanation
1. `overflow: hidden` prevents the text from overflowing its dimensions

View File

@ -5,8 +5,6 @@ tags: visual,intermediate
Creates a striped list with alternating background colors, which is useful for differentiating siblings that have content spread across a wide row.
#### HTML
```html
<ul>
<li>Item 01</li>
@ -17,16 +15,12 @@ Creates a striped list with alternating background colors, which is useful for d
</ul>
```
#### CSS
```css
li:nth-child(odd) {
background-color: #ddd;
}
```
#### Demo
#### Explanation
1. Use the `:nth-child(odd)` or `:nth-child(even)` pseudo-class to apply a different background color to elements that match based on their position in a group of siblings.
@ -36,7 +30,3 @@ Note that you can use it to apply different styles to other HTML elements like d
#### Browser support
https://caniuse.com/#feat=css-sel3
<!-- date: 2018-10-31 -->

View File

@ -1,45 +0,0 @@
.back-to-top-button {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
font-weight: bold;
background: white;
width: 4rem;
height: 4rem;
position: fixed;
right: 2rem;
bottom: 2rem;
border-radius: 50%;
user-select: none;
box-shadow: 0 0.4rem 0.8rem -0.1rem rgba(0, 32, 128, 0.15);
transition: all 0.2s ease-out;
visibility: hidden;
opacity: 0;
z-index: 1;
border: 1px solid rgba(0, 32, 128, 0.1);
outline: 0;
color: inherit;
&:hover,
&:focus {
transform: scale(1.1);
box-shadow: 0 0.8rem 1.6rem -0.2rem rgba(0, 32, 128, 0.15);
color: #35a8ff;
}
&:focus {
box-shadow: 0 0.8rem 1.6rem -0.2rem rgba(0, 32, 128, 0.15), 0 0 2px 2px #35a8ff;
outline-style: none;
}
&.is-visible {
visibility: visible;
opacity: 1;
}
.feather {
width: 2rem;
height: 2rem;
}
}

View File

@ -1,73 +0,0 @@
html {
font-size: 0.95rem;
box-sizing: border-box;
}
*,
*::after,
*::before {
box-sizing: inherit;
}
body {
font-family: -apple-system, BlinkMacSystemFont, Roboto, Segoe UI, 'Helvetica Neue', Helvetica,
Arial, sans-serif;
background: #f2f3f8;
color: rgb(50, 75, 100);
line-height: 1.5;
}
a {
color: #157bda;
text-decoration: none;
word-wrap: break-word;
overflow-wrap: break-word;
&:hover {
color: #0090ff;
}
}
hr {
border: 0;
border-top: 1px solid rgba(0, 32, 128, 0.1);
}
ul,
ol {
padding-left: 1.25rem;
}
.container {
max-width: 64rem;
padding: 0 2%;
margin: 0 auto;
}
.main > .container {
padding: 0;
}
@media (min-width: 579px) {
.main > .container {
padding: 0 2%;
}
}
@media (min-width: 768px) {
html {
font-size: 1rem;
}
}
@media (min-width: 992px) {
.content-wrapper {
margin-left: 20%;
}
}
@media (min-width: 1400px) {
.content-wrapper {
margin-left: 275px;
}
}

View File

@ -1,76 +0,0 @@
.btn {
display: inline-block;
position: relative;
top: -1px;
font-weight: bold;
font-size: 0.75rem;
text-transform: uppercase;
color: #8385aa;
white-space: nowrap;
border: 1px solid #c8cbf2;
border-radius: 2px;
vertical-align: middle;
line-height: 2;
padding: 0 0.5rem;
margin-right: 0.5rem;
transition: all 0.1s ease-out;
outline: 0;
&.is-large {
font-size: 0.95rem;
border-radius: 0.2rem;
.feather {
top: -2px;
width: 18px;
height: 18px;
}
}
.feather {
vertical-align: middle;
margin-right: 0.25rem;
position: relative;
top: -1px;
width: 14px;
height: 14px;
}
}
button.btn {
user-select: none;
cursor: pointer;
margin-bottom: 1rem;
margin-right: 1rem;
background: white;
&:hover {
background: #8385aa;
border-color: #8385aa;
color: white;
}
&.focus-visible:focus {
box-shadow: 0 0 0 0.25rem transparentize(#8385aa, 0.5);
}
&:active {
box-shadow: inset 0 0.1rem 0.1rem 0.1rem rgba(0, 0, 0, 0.2);
background: darken(#8385aa, 10);
border-color: darken(#8385aa, 10);
}
&.is-active {
background: #7983ff;
border-color: #7983ff;
color: white;
&.focus-visible:focus {
box-shadow: 0 0 0 0.25rem transparentize(#7983ff, 0.5);
}
}
&.codepen-btn {
margin-top: 0.5rem;
}
}

View File

@ -1,927 +0,0 @@
/*!
* Hamburgers
* @description Tasty CSS-animated hamburgers
* @author Jonathan Suh @jonsuh
* @site https://jonsuh.com/hamburgers
* @link https://github.com/jonsuh/hamburgers
*/
.hamburger {
padding: 1rem;
display: inline-block;
cursor: pointer;
transition-property: opacity, filter;
transition-duration: 0.15s;
transition-timing-function: linear;
font: inherit;
color: inherit;
text-transform: none;
background-color: transparent;
border: 0;
margin: 0;
overflow: visible;
outline: 0;
}
.hamburger:hover {
opacity: 0.7;
}
.hamburger-box {
width: 40px;
height: 20px;
display: inline-block;
position: relative;
}
.hamburger-inner {
display: block;
top: 50%;
}
.hamburger-inner,
.hamburger-inner::before,
.hamburger-inner::after {
width: 36px;
height: 2px;
background-color: #e3f5ff;
border-radius: 4px;
position: absolute;
transition-property: transform;
transition-duration: 0.15s;
transition-timing-function: ease;
}
.hamburger-inner::before,
.hamburger-inner::after {
content: '';
display: block;
}
.hamburger-inner::before {
top: -10px;
}
.hamburger-inner::after {
bottom: -10px;
}
/*
* 3DX
*/
.hamburger--3dx .hamburger-box {
perspective: 80px;
}
.hamburger--3dx .hamburger-inner {
transition: transform 0.15s cubic-bezier(0.645, 0.045, 0.355, 1),
background-color 0s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.hamburger--3dx .hamburger-inner::before,
.hamburger--3dx .hamburger-inner::after {
transition: transform 0s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.hamburger--3dx.is-active .hamburger-inner {
background-color: transparent;
transform: rotateY(180deg);
}
.hamburger--3dx.is-active .hamburger-inner::before {
transform: translate3d(0, 10px, 0) rotate(45deg);
}
.hamburger--3dx.is-active .hamburger-inner::after {
transform: translate3d(0, -10px, 0) rotate(-45deg);
}
/*
* 3DX Reverse
*/
.hamburger--3dx-r .hamburger-box {
perspective: 80px;
}
.hamburger--3dx-r .hamburger-inner {
transition: transform 0.15s cubic-bezier(0.645, 0.045, 0.355, 1),
background-color 0s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.hamburger--3dx-r .hamburger-inner::before,
.hamburger--3dx-r .hamburger-inner::after {
transition: transform 0s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.hamburger--3dx-r.is-active .hamburger-inner {
background-color: transparent;
transform: rotateY(-180deg);
}
.hamburger--3dx-r.is-active .hamburger-inner::before {
transform: translate3d(0, 10px, 0) rotate(45deg);
}
.hamburger--3dx-r.is-active .hamburger-inner::after {
transform: translate3d(0, -10px, 0) rotate(-45deg);
}
/*
* 3DY
*/
.hamburger--3dy .hamburger-box {
perspective: 80px;
}
.hamburger--3dy .hamburger-inner {
transition: transform 0.15s cubic-bezier(0.645, 0.045, 0.355, 1),
background-color 0s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.hamburger--3dy .hamburger-inner::before,
.hamburger--3dy .hamburger-inner::after {
transition: transform 0s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.hamburger--3dy.is-active .hamburger-inner {
background-color: transparent;
transform: rotateX(-180deg);
}
.hamburger--3dy.is-active .hamburger-inner::before {
transform: translate3d(0, 10px, 0) rotate(45deg);
}
.hamburger--3dy.is-active .hamburger-inner::after {
transform: translate3d(0, -10px, 0) rotate(-45deg);
}
/*
* 3DY Reverse
*/
.hamburger--3dy-r .hamburger-box {
perspective: 80px;
}
.hamburger--3dy-r .hamburger-inner {
transition: transform 0.15s cubic-bezier(0.645, 0.045, 0.355, 1),
background-color 0s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.hamburger--3dy-r .hamburger-inner::before,
.hamburger--3dy-r .hamburger-inner::after {
transition: transform 0s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.hamburger--3dy-r.is-active .hamburger-inner {
background-color: transparent;
transform: rotateX(180deg);
}
.hamburger--3dy-r.is-active .hamburger-inner::before {
transform: translate3d(0, 10px, 0) rotate(45deg);
}
.hamburger--3dy-r.is-active .hamburger-inner::after {
transform: translate3d(0, -10px, 0) rotate(-45deg);
}
/*
* 3DXY
*/
.hamburger--3dxy .hamburger-box {
perspective: 80px;
}
.hamburger--3dxy .hamburger-inner {
transition: transform 0.15s cubic-bezier(0.645, 0.045, 0.355, 1),
background-color 0s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.hamburger--3dxy .hamburger-inner::before,
.hamburger--3dxy .hamburger-inner::after {
transition: transform 0s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.hamburger--3dxy.is-active .hamburger-inner {
background-color: transparent;
transform: rotateX(180deg) rotateY(180deg);
}
.hamburger--3dxy.is-active .hamburger-inner::before {
transform: translate3d(0, 10px, 0) rotate(45deg);
}
.hamburger--3dxy.is-active .hamburger-inner::after {
transform: translate3d(0, -10px, 0) rotate(-45deg);
}
/*
* 3DXY Reverse
*/
.hamburger--3dxy-r .hamburger-box {
perspective: 80px;
}
.hamburger--3dxy-r .hamburger-inner {
transition: transform 0.15s cubic-bezier(0.645, 0.045, 0.355, 1),
background-color 0s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.hamburger--3dxy-r .hamburger-inner::before,
.hamburger--3dxy-r .hamburger-inner::after {
transition: transform 0s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.hamburger--3dxy-r.is-active .hamburger-inner {
background-color: transparent;
transform: rotateX(180deg) rotateY(180deg) rotateZ(-180deg);
}
.hamburger--3dxy-r.is-active .hamburger-inner::before {
transform: translate3d(0, 10px, 0) rotate(45deg);
}
.hamburger--3dxy-r.is-active .hamburger-inner::after {
transform: translate3d(0, -10px, 0) rotate(-45deg);
}
/*
* Arrow
*/
.hamburger--arrow.is-active .hamburger-inner::before {
transform: translate3d(-8px, 0, 0) rotate(-45deg) scale(0.7, 1);
}
.hamburger--arrow.is-active .hamburger-inner::after {
transform: translate3d(-8px, 0, 0) rotate(45deg) scale(0.7, 1);
}
/*
* Arrow Right
*/
.hamburger--arrow-r.is-active .hamburger-inner::before {
transform: translate3d(8px, 0, 0) rotate(45deg) scale(0.7, 1);
}
.hamburger--arrow-r.is-active .hamburger-inner::after {
transform: translate3d(8px, 0, 0) rotate(-45deg) scale(0.7, 1);
}
/*
* Arrow Alt
*/
.hamburger--arrowalt .hamburger-inner::before {
transition: top 0.1s 0.1s ease, transform 0.1s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.hamburger--arrowalt .hamburger-inner::after {
transition: bottom 0.1s 0.1s ease, transform 0.1s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.hamburger--arrowalt.is-active .hamburger-inner::before {
top: 0;
transform: translate3d(-8px, -10px, 0) rotate(-45deg) scale(0.7, 1);
transition: top 0.1s ease, transform 0.1s 0.1s cubic-bezier(0.895, 0.03, 0.685, 0.22);
}
.hamburger--arrowalt.is-active .hamburger-inner::after {
bottom: 0;
transform: translate3d(-8px, 10px, 0) rotate(45deg) scale(0.7, 1);
transition: bottom 0.1s ease, transform 0.1s 0.1s cubic-bezier(0.895, 0.03, 0.685, 0.22);
}
/*
* Arrow Alt Right
*/
.hamburger--arrowalt-r .hamburger-inner::before {
transition: top 0.1s 0.1s ease, transform 0.1s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.hamburger--arrowalt-r .hamburger-inner::after {
transition: bottom 0.1s 0.1s ease, transform 0.1s cubic-bezier(0.165, 0.84, 0.44, 1);
}
.hamburger--arrowalt-r.is-active .hamburger-inner::before {
top: 0;
transform: translate3d(8px, -10px, 0) rotate(45deg) scale(0.7, 1);
transition: top 0.1s ease, transform 0.1s 0.1s cubic-bezier(0.895, 0.03, 0.685, 0.22);
}
.hamburger--arrowalt-r.is-active .hamburger-inner::after {
bottom: 0;
transform: translate3d(8px, 10px, 0) rotate(-45deg) scale(0.7, 1);
transition: bottom 0.1s ease, transform 0.1s 0.1s cubic-bezier(0.895, 0.03, 0.685, 0.22);
}
/*
* Arrow Turn
*/
.hamburger--arrowturn.is-active .hamburger-inner {
transform: rotate(-180deg);
}
.hamburger--arrowturn.is-active .hamburger-inner::before {
transform: translate3d(8px, 0, 0) rotate(45deg) scale(0.7, 1);
}
.hamburger--arrowturn.is-active .hamburger-inner::after {
transform: translate3d(8px, 0, 0) rotate(-45deg) scale(0.7, 1);
}
/*
* Arrow Turn Right
*/
.hamburger--arrowturn-r.is-active .hamburger-inner {
transform: rotate(-180deg);
}
.hamburger--arrowturn-r.is-active .hamburger-inner::before {
transform: translate3d(-8px, 0, 0) rotate(-45deg) scale(0.7, 1);
}
.hamburger--arrowturn-r.is-active .hamburger-inner::after {
transform: translate3d(-8px, 0, 0) rotate(45deg) scale(0.7, 1);
}
/*
* Boring
*/
.hamburger--boring .hamburger-inner,
.hamburger--boring .hamburger-inner::before,
.hamburger--boring .hamburger-inner::after {
transition-property: none;
}
.hamburger--boring.is-active .hamburger-inner {
transform: rotate(45deg);
}
.hamburger--boring.is-active .hamburger-inner::before {
top: 0;
opacity: 0;
}
.hamburger--boring.is-active .hamburger-inner::after {
bottom: 0;
transform: rotate(-90deg);
}
/*
* Collapse
*/
.hamburger--collapse .hamburger-inner {
top: auto;
bottom: 0;
transition-duration: 0.13s;
transition-delay: 0.13s;
transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--collapse .hamburger-inner::after {
top: -20px;
transition: top 0.2s 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1), opacity 0.1s linear;
}
.hamburger--collapse .hamburger-inner::before {
transition: top 0.12s 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1),
transform 0.13s cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--collapse.is-active .hamburger-inner {
transform: translate3d(0, -10px, 0) rotate(-45deg);
transition-delay: 0.22s;
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
.hamburger--collapse.is-active .hamburger-inner::after {
top: 0;
opacity: 0;
transition: top 0.2s cubic-bezier(0.33333, 0, 0.66667, 0.33333), opacity 0.1s 0.22s linear;
}
.hamburger--collapse.is-active .hamburger-inner::before {
top: 0;
transform: rotate(-90deg);
transition: top 0.1s 0.16s cubic-bezier(0.33333, 0, 0.66667, 0.33333),
transform 0.13s 0.25s cubic-bezier(0.215, 0.61, 0.355, 1);
}
/*
* Collapse Reverse
*/
.hamburger--collapse-r .hamburger-inner {
top: auto;
bottom: 0;
transition-duration: 0.13s;
transition-delay: 0.13s;
transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--collapse-r .hamburger-inner::after {
top: -20px;
transition: top 0.2s 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1), opacity 0.1s linear;
}
.hamburger--collapse-r .hamburger-inner::before {
transition: top 0.12s 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1),
transform 0.13s cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--collapse-r.is-active .hamburger-inner {
transform: translate3d(0, -10px, 0) rotate(45deg);
transition-delay: 0.22s;
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
.hamburger--collapse-r.is-active .hamburger-inner::after {
top: 0;
opacity: 0;
transition: top 0.2s cubic-bezier(0.33333, 0, 0.66667, 0.33333), opacity 0.1s 0.22s linear;
}
.hamburger--collapse-r.is-active .hamburger-inner::before {
top: 0;
transform: rotate(90deg);
transition: top 0.1s 0.16s cubic-bezier(0.33333, 0, 0.66667, 0.33333),
transform 0.13s 0.25s cubic-bezier(0.215, 0.61, 0.355, 1);
}
/*
* Elastic
*/
.hamburger--elastic .hamburger-inner {
top: 2px;
transition-duration: 0.275s;
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.hamburger--elastic .hamburger-inner::before {
top: 10px;
transition: opacity 0.125s 0.275s ease;
}
.hamburger--elastic .hamburger-inner::after {
top: 20px;
transition: transform 0.275s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.hamburger--elastic.is-active .hamburger-inner {
transform: translate3d(0, 10px, 0) rotate(135deg);
transition-delay: 0.075s;
}
.hamburger--elastic.is-active .hamburger-inner::before {
transition-delay: 0s;
opacity: 0;
}
.hamburger--elastic.is-active .hamburger-inner::after {
transform: translate3d(0, -20px, 0) rotate(-270deg);
transition-delay: 0.075s;
}
/*
* Elastic Reverse
*/
.hamburger--elastic-r .hamburger-inner {
top: 2px;
transition-duration: 0.275s;
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.hamburger--elastic-r .hamburger-inner::before {
top: 10px;
transition: opacity 0.125s 0.275s ease;
}
.hamburger--elastic-r .hamburger-inner::after {
top: 20px;
transition: transform 0.275s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.hamburger--elastic-r.is-active .hamburger-inner {
transform: translate3d(0, 10px, 0) rotate(-135deg);
transition-delay: 0.075s;
}
.hamburger--elastic-r.is-active .hamburger-inner::before {
transition-delay: 0s;
opacity: 0;
}
.hamburger--elastic-r.is-active .hamburger-inner::after {
transform: translate3d(0, -20px, 0) rotate(270deg);
transition-delay: 0.075s;
}
/*
* Emphatic
*/
.hamburger--emphatic {
overflow: hidden;
}
.hamburger--emphatic .hamburger-inner {
transition: background-color 0.125s 0.175s ease-in;
}
.hamburger--emphatic .hamburger-inner::before {
left: 0;
transition: transform 0.125s cubic-bezier(0.6, 0.04, 0.98, 0.335), top 0.05s 0.125s linear,
left 0.125s 0.175s ease-in;
}
.hamburger--emphatic .hamburger-inner::after {
top: 10px;
right: 0;
transition: transform 0.125s cubic-bezier(0.6, 0.04, 0.98, 0.335), top 0.05s 0.125s linear,
right 0.125s 0.175s ease-in;
}
.hamburger--emphatic.is-active .hamburger-inner {
transition-delay: 0s;
transition-timing-function: ease-out;
background-color: transparent;
}
.hamburger--emphatic.is-active .hamburger-inner::before {
left: -80px;
top: -80px;
transform: translate3d(80px, 80px, 0) rotate(45deg);
transition: left 0.125s ease-out, top 0.05s 0.125s linear,
transform 0.125s 0.175s cubic-bezier(0.075, 0.82, 0.165, 1);
}
.hamburger--emphatic.is-active .hamburger-inner::after {
right: -80px;
top: -80px;
transform: translate3d(-80px, 80px, 0) rotate(-45deg);
transition: right 0.125s ease-out, top 0.05s 0.125s linear,
transform 0.125s 0.175s cubic-bezier(0.075, 0.82, 0.165, 1);
}
/*
* Emphatic Reverse
*/
.hamburger--emphatic-r {
overflow: hidden;
}
.hamburger--emphatic-r .hamburger-inner {
transition: background-color 0.125s 0.175s ease-in;
}
.hamburger--emphatic-r .hamburger-inner::before {
left: 0;
transition: transform 0.125s cubic-bezier(0.6, 0.04, 0.98, 0.335), top 0.05s 0.125s linear,
left 0.125s 0.175s ease-in;
}
.hamburger--emphatic-r .hamburger-inner::after {
top: 10px;
right: 0;
transition: transform 0.125s cubic-bezier(0.6, 0.04, 0.98, 0.335), top 0.05s 0.125s linear,
right 0.125s 0.175s ease-in;
}
.hamburger--emphatic-r.is-active .hamburger-inner {
transition-delay: 0s;
transition-timing-function: ease-out;
background-color: transparent;
}
.hamburger--emphatic-r.is-active .hamburger-inner::before {
left: -80px;
top: 80px;
transform: translate3d(80px, -80px, 0) rotate(-45deg);
transition: left 0.125s ease-out, top 0.05s 0.125s linear,
transform 0.125s 0.175s cubic-bezier(0.075, 0.82, 0.165, 1);
}
.hamburger--emphatic-r.is-active .hamburger-inner::after {
right: -80px;
top: 80px;
transform: translate3d(-80px, -80px, 0) rotate(45deg);
transition: right 0.125s ease-out, top 0.05s 0.125s linear,
transform 0.125s 0.175s cubic-bezier(0.075, 0.82, 0.165, 1);
}
/*
* Minus
*/
.hamburger--minus .hamburger-inner::before,
.hamburger--minus .hamburger-inner::after {
transition: bottom 0.08s 0s ease-out, top 0.08s 0s ease-out, opacity 0s linear;
}
.hamburger--minus.is-active .hamburger-inner::before,
.hamburger--minus.is-active .hamburger-inner::after {
opacity: 0;
transition: bottom 0.08s ease-out, top 0.08s ease-out, opacity 0s 0.08s linear;
}
.hamburger--minus.is-active .hamburger-inner::before {
top: 0;
}
.hamburger--minus.is-active .hamburger-inner::after {
bottom: 0;
}
/*
* Slider
*/
.hamburger--slider .hamburger-inner {
top: 2px;
}
.hamburger--slider .hamburger-inner::before {
top: 10px;
transition-property: transform, opacity;
transition-timing-function: ease;
transition-duration: 0.15s;
}
.hamburger--slider .hamburger-inner::after {
top: 20px;
}
.hamburger--slider.is-active .hamburger-inner {
transform: translate3d(0, 10px, 0) rotate(45deg);
}
.hamburger--slider.is-active .hamburger-inner::before {
transform: rotate(-45deg) translate3d(-5.71429px, -6px, 0);
opacity: 0;
}
.hamburger--slider.is-active .hamburger-inner::after {
transform: translate3d(0, -20px, 0) rotate(-90deg);
}
/*
* Slider Reverse
*/
.hamburger--slider-r .hamburger-inner {
top: 2px;
}
.hamburger--slider-r .hamburger-inner::before {
top: 10px;
transition-property: transform, opacity;
transition-timing-function: ease;
transition-duration: 0.15s;
}
.hamburger--slider-r .hamburger-inner::after {
top: 20px;
}
.hamburger--slider-r.is-active .hamburger-inner {
transform: translate3d(0, 10px, 0) rotate(-45deg);
}
.hamburger--slider-r.is-active .hamburger-inner::before {
transform: rotate(45deg) translate3d(5.71429px, -6px, 0);
opacity: 0;
}
.hamburger--slider-r.is-active .hamburger-inner::after {
transform: translate3d(0, -20px, 0) rotate(90deg);
}
/*
* Spin
*/
.hamburger--spin .hamburger-inner {
transition-duration: 0.22s;
transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--spin .hamburger-inner::before {
transition: top 0.1s 0.25s ease-in, opacity 0.1s ease-in;
}
.hamburger--spin .hamburger-inner::after {
transition: bottom 0.1s 0.25s ease-in, transform 0.22s cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--spin.is-active .hamburger-inner {
transform: rotate(225deg);
transition-delay: 0.12s;
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
.hamburger--spin.is-active .hamburger-inner::before {
top: 0;
opacity: 0;
transition: top 0.1s ease-out, opacity 0.1s 0.12s ease-out;
}
.hamburger--spin.is-active .hamburger-inner::after {
bottom: 0;
transform: rotate(-90deg);
transition: bottom 0.1s ease-out, transform 0.22s 0.12s cubic-bezier(0.215, 0.61, 0.355, 1);
}
/*
* Spin Reverse
*/
.hamburger--spin-r .hamburger-inner {
transition-duration: 0.22s;
transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--spin-r .hamburger-inner::before {
transition: top 0.1s 0.25s ease-in, opacity 0.1s ease-in;
}
.hamburger--spin-r .hamburger-inner::after {
transition: bottom 0.1s 0.25s ease-in, transform 0.22s cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--spin-r.is-active .hamburger-inner {
transform: rotate(-225deg);
transition-delay: 0.12s;
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
.hamburger--spin-r.is-active .hamburger-inner::before {
top: 0;
opacity: 0;
transition: top 0.1s ease-out, opacity 0.1s 0.12s ease-out;
}
.hamburger--spin-r.is-active .hamburger-inner::after {
bottom: 0;
transform: rotate(90deg);
transition: bottom 0.1s ease-out, transform 0.22s 0.12s cubic-bezier(0.215, 0.61, 0.355, 1);
}
/*
* Spring
*/
.hamburger--spring .hamburger-inner {
top: 2px;
transition: background-color 0s 0.13s linear;
}
.hamburger--spring .hamburger-inner::before {
top: 10px;
transition: top 0.1s 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1),
transform 0.13s cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--spring .hamburger-inner::after {
top: 20px;
transition: top 0.2s 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1),
transform 0.13s cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--spring.is-active .hamburger-inner {
transition-delay: 0.22s;
background-color: transparent;
}
.hamburger--spring.is-active .hamburger-inner::before {
top: 0;
transition: top 0.1s 0.15s cubic-bezier(0.33333, 0, 0.66667, 0.33333),
transform 0.13s 0.22s cubic-bezier(0.215, 0.61, 0.355, 1);
transform: translate3d(0, 10px, 0) rotate(45deg);
}
.hamburger--spring.is-active .hamburger-inner::after {
top: 0;
transition: top 0.2s cubic-bezier(0.33333, 0, 0.66667, 0.33333),
transform 0.13s 0.22s cubic-bezier(0.215, 0.61, 0.355, 1);
transform: translate3d(0, 10px, 0) rotate(-45deg);
}
/*
* Spring Reverse
*/
.hamburger--spring-r .hamburger-inner {
top: auto;
bottom: 0;
transition-duration: 0.13s;
transition-delay: 0s;
transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--spring-r .hamburger-inner::after {
top: -20px;
transition: top 0.2s 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1), opacity 0s linear;
}
.hamburger--spring-r .hamburger-inner::before {
transition: top 0.1s 0.2s cubic-bezier(0.33333, 0.66667, 0.66667, 1),
transform 0.13s cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--spring-r.is-active .hamburger-inner {
transform: translate3d(0, -10px, 0) rotate(-45deg);
transition-delay: 0.22s;
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
.hamburger--spring-r.is-active .hamburger-inner::after {
top: 0;
opacity: 0;
transition: top 0.2s cubic-bezier(0.33333, 0, 0.66667, 0.33333), opacity 0s 0.22s linear;
}
.hamburger--spring-r.is-active .hamburger-inner::before {
top: 0;
transform: rotate(90deg);
transition: top 0.1s 0.15s cubic-bezier(0.33333, 0, 0.66667, 0.33333),
transform 0.13s 0.22s cubic-bezier(0.215, 0.61, 0.355, 1);
}
/*
* Stand
*/
.hamburger--stand .hamburger-inner {
transition: transform 0.075s 0.15s cubic-bezier(0.55, 0.055, 0.675, 0.19),
background-color 0s 0.075s linear;
}
.hamburger--stand .hamburger-inner::before {
transition: top 0.075s 0.075s ease-in, transform 0.075s 0s cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--stand .hamburger-inner::after {
transition: bottom 0.075s 0.075s ease-in,
transform 0.075s 0s cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--stand.is-active .hamburger-inner {
transform: rotate(90deg);
background-color: transparent;
transition: transform 0.075s 0s cubic-bezier(0.215, 0.61, 0.355, 1),
background-color 0s 0.15s linear;
}
.hamburger--stand.is-active .hamburger-inner::before {
top: 0;
transform: rotate(-45deg);
transition: top 0.075s 0.1s ease-out, transform 0.075s 0.15s cubic-bezier(0.215, 0.61, 0.355, 1);
}
.hamburger--stand.is-active .hamburger-inner::after {
bottom: 0;
transform: rotate(45deg);
transition: bottom 0.075s 0.1s ease-out,
transform 0.075s 0.15s cubic-bezier(0.215, 0.61, 0.355, 1);
}
/*
* Stand Reverse
*/
.hamburger--stand-r .hamburger-inner {
transition: transform 0.075s 0.15s cubic-bezier(0.55, 0.055, 0.675, 0.19),
background-color 0s 0.075s linear;
}
.hamburger--stand-r .hamburger-inner::before {
transition: top 0.075s 0.075s ease-in, transform 0.075s 0s cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--stand-r .hamburger-inner::after {
transition: bottom 0.075s 0.075s ease-in,
transform 0.075s 0s cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--stand-r.is-active .hamburger-inner {
transform: rotate(-90deg);
background-color: transparent;
transition: transform 0.075s 0s cubic-bezier(0.215, 0.61, 0.355, 1),
background-color 0s 0.15s linear;
}
.hamburger--stand-r.is-active .hamburger-inner::before {
top: 0;
transform: rotate(-45deg);
transition: top 0.075s 0.1s ease-out, transform 0.075s 0.15s cubic-bezier(0.215, 0.61, 0.355, 1);
}
.hamburger--stand-r.is-active .hamburger-inner::after {
bottom: 0;
transform: rotate(45deg);
transition: bottom 0.075s 0.1s ease-out,
transform 0.075s 0.15s cubic-bezier(0.215, 0.61, 0.355, 1);
}
/*
* Squeeze
*/
.hamburger--squeeze .hamburger-inner {
transition-duration: 0.075s;
transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--squeeze .hamburger-inner::before {
transition: top 0.075s 0.12s ease, opacity 0.075s ease;
}
.hamburger--squeeze .hamburger-inner::after {
transition: bottom 0.075s 0.12s ease, transform 0.075s cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.hamburger--squeeze.is-active .hamburger-inner {
transform: rotate(45deg);
transition-delay: 0.12s;
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
}
.hamburger--squeeze.is-active .hamburger-inner::before {
top: 0;
opacity: 0;
transition: top 0.075s ease, opacity 0.075s 0.12s ease;
}
.hamburger--squeeze.is-active .hamburger-inner::after {
bottom: 0;
transform: rotate(-90deg);
transition: bottom 0.075s ease, transform 0.075s 0.12s cubic-bezier(0.215, 0.61, 0.355, 1);
}
/*
* Vortex
*/
.hamburger--vortex .hamburger-inner {
transition-duration: 0.2s;
transition-timing-function: cubic-bezier(0.19, 1, 0.22, 1);
}
.hamburger--vortex .hamburger-inner::before,
.hamburger--vortex .hamburger-inner::after {
transition-duration: 0s;
transition-delay: 0.1s;
transition-timing-function: linear;
}
.hamburger--vortex .hamburger-inner::before {
transition-property: top, opacity;
}
.hamburger--vortex .hamburger-inner::after {
transition-property: bottom, transform;
}
.hamburger--vortex.is-active .hamburger-inner {
transform: rotate(765deg);
transition-timing-function: cubic-bezier(0.19, 1, 0.22, 1);
}
.hamburger--vortex.is-active .hamburger-inner::before,
.hamburger--vortex.is-active .hamburger-inner::after {
transition-delay: 0s;
}
.hamburger--vortex.is-active .hamburger-inner::before {
top: 0;
opacity: 0;
}
.hamburger--vortex.is-active .hamburger-inner::after {
bottom: 0;
transform: rotate(90deg);
}
/*
* Vortex Reverse
*/
.hamburger--vortex-r .hamburger-inner {
transition-duration: 0.2s;
transition-timing-function: cubic-bezier(0.19, 1, 0.22, 1);
}
.hamburger--vortex-r .hamburger-inner::before,
.hamburger--vortex-r .hamburger-inner::after {
transition-duration: 0s;
transition-delay: 0.1s;
transition-timing-function: linear;
}
.hamburger--vortex-r .hamburger-inner::before {
transition-property: top, opacity;
}
.hamburger--vortex-r .hamburger-inner::after {
transition-property: bottom, transform;
}
.hamburger--vortex-r.is-active .hamburger-inner {
transform: rotate(-765deg);
transition-timing-function: cubic-bezier(0.19, 1, 0.22, 1);
}
.hamburger--vortex-r.is-active .hamburger-inner::before,
.hamburger--vortex-r.is-active .hamburger-inner::after {
transition-delay: 0s;
}
.hamburger--vortex-r.is-active .hamburger-inner::before {
top: 0;
opacity: 0;
}
.hamburger--vortex-r.is-active .hamburger-inner::after {
bottom: 0;
transform: rotate(-90deg);
}

View File

@ -1,127 +0,0 @@
.header {
position: relative;
padding: 5rem 1rem 4rem;
background: #5b67ff;
background: linear-gradient(25deg, #95e2ff, #5f79ff, #8ed5ff);
color: white;
margin-bottom: 2rem;
text-align: center;
overflow: hidden;
z-index: 1;
&::before {
content: '';
position: absolute;
width: 150%;
height: 150%;
top: 0;
left: 0;
opacity: 0.1;
z-index: -1;
}
&::after {
content: '';
position: absolute;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 12'%3E%3Cpath d='m12 0l12 12h-24z' fill='%23f2f3f8'/%3E%3C/svg%3E");
background-size: 24px 24px;
width: 100%;
left: 0;
height: 24px;
bottom: -7px;
z-index: 3;
}
&__content {
position: relative;
z-index: 3;
}
&__logo {
height: 146px;
user-select: none;
}
&__heading {
font-weight: 300;
font-size: 3rem;
margin: 1rem 0;
line-height: 1.2;
}
&__description {
font-size: 1.5rem;
max-width: 600px;
margin: 0 auto 2rem;
font-weight: 300;
letter-spacing: 0.4px;
}
&__css {
font-size: 4rem;
font-weight: bold;
}
&__github-button-wrapper {
height: 28px;
}
&__github-button {
color: white;
}
&__leaves {
position: absolute;
width: 250px;
user-select: none;
}
}
#header__blob {
position: absolute;
width: 240px;
left: -25px;
top: 50px;
user-select: none;
}
#header__leaves1 {
right: -100px;
top: -50px;
}
#header__leaves2 {
left: -250px;
bottom: -100px;
transform: rotate(235deg);
z-index: 2;
width: 400px;
}
@media (min-width: 1150px) {
#header__leaves2 {
left: -100px;
transform: rotate(180deg);
}
#header__blob {
left: 50px;
top: 5px;
}
}
@media (min-width: 579px) {
.header {
padding: 6rem 0 5rem;
&__heading {
font-size: 3.75rem;
}
}
}
@media (min-width: 992px) {
.header {
padding: 2.5rem 0 5rem;
}
}

View File

@ -1,129 +0,0 @@
.sidebar {
background: #273149;
position: fixed;
z-index: 2;
width: 100%;
height: 44px;
box-shadow: 0 0.25rem 0.5rem -0.1rem rgba(0, 32, 128, 0.2);
&__menu {
position: absolute;
font-weight: bold;
border: none;
text-align: left;
text-transform: uppercase;
left: 0;
top: 0;
padding: 0.75rem 1rem;
outline: 0;
}
&__menu-icon {
height: 24px;
}
&__links {
background: #273149;
overflow-y: auto;
transition: transform 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
transform-origin: 0% 0%;
transform: rotateX(-90deg);
visibility: hidden;
opacity: 0;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
max-height: 378px;
margin-top: 44px;
box-shadow: 0 0.25rem 0.5rem -0.1rem rgba(0, 32, 128, 0.2);
padding-bottom: 1rem;
&.is-active {
transform: rotateX(0);
visibility: visible;
opacity: 1;
}
}
&__link {
display: block;
color: #e3f5ff;
padding: 0.75rem;
padding-right: 1.5rem;
margin-right: -0.75rem;
transition: all 0.1s ease-out;
border-left: 2px solid #576a85;
font-weight: 500;
font-size: 0.95rem;
&:hover {
color: #88f4ff;
background: rgba(255, 255, 255, 0.1);
border-color: pink;
}
}
&__section {
padding: 0 0.75rem;
}
&__section-heading {
text-transform: capitalize;
color: #e3f5ff;
margin-bottom: 0.5rem;
}
&__new {
width: 1.25rem;
vertical-align: middle;
margin-right: 0.25rem;
}
}
@media (min-width: 992px) {
.sidebar {
left: 0;
top: 0;
bottom: 0;
width: 20%;
height: 100%;
background: linear-gradient(-30deg, #273149, #1c273f);
box-shadow: 0.4rem 0.4rem 0.8rem rgba(0, 32, 64, 0.1);
overflow-y: auto;
color: white;
@media (min-width: 1400px) {
width: 275px;
}
&__links {
background: none;
box-shadow: none;
visibility: visible;
opacity: 1;
transform: rotateX(0);
margin-top: 0;
max-height: none;
}
&__menu {
display: none;
}
}
}
html:not(.macOS) {
.sidebar {
&::-webkit-scrollbar-track {
background-color: rgba(0, 0, 0, 0.6);
}
&::-webkit-scrollbar {
width: 10px;
background-color: #505b76;
}
&::-webkit-scrollbar-thumb {
background-color: #505b76;
}
}
}

View File

@ -1,148 +0,0 @@
.snippet {
position: relative;
background: white;
padding: 2rem 5%;
box-shadow: 0 0.4rem 0.8rem -0.1rem rgba(0, 32, 128, 0.1), 0 0 0 1px #f0f2f7;
border-radius: 0.25rem;
font-size: 1.1rem;
margin-bottom: 1.5rem;
h3 {
font-size: 2rem;
padding: 0.5rem 0;
border-bottom: 1px solid rgba(0, 32, 128, 0.1);
margin-bottom: 1.25rem;
margin-top: 0;
line-height: 1.3;
span:not(.snippet__tag) {
margin-right: 0.75rem;
}
}
code:not([class*='lang']) {
background: #fcfaff;
border: 1px solid #e2ddff;
color: #4b00da;
border-radius: 0.15rem;
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
font-size: 0.9rem;
padding: 0.2rem 0.4rem;
margin: 0 0.1rem;
}
ol {
margin-top: 0.5rem;
}
ol > li {
margin-bottom: 0.5rem;
}
> p {
margin-top: 0.5rem;
}
h4 {
display: inline-block;
margin: 1rem 0 0.5rem;
font-size: 1.1rem;
line-height: 2;
&[data-type] {
background: #333;
padding: 0 0.5rem;
border-radius: 3px;
font-size: 0.9rem;
text-transform: uppercase;
border: 1px solid #c6d6ea;
border-bottom-color: darken(#c6d6ea, 5);
background: white;
box-shadow: 0 0.25rem 0.5rem -0.1rem rgba(0, 32, 64, 0.15);
background-repeat: no-repeat;
}
&[data-type='HTML'] {
color: white;
border: none;
background-image: linear-gradient(135deg, #ff4c9f, #ff7b74);
}
&[data-type='CSS'] {
color: white;
border: none;
background-image: linear-gradient(135deg, #7983ff, #5f9de9);
}
&[data-type='JavaScript'] {
color: white;
border: none;
background-image: linear-gradient(135deg, #ffb000, #f58818);
}
}
&__browser-support {
display: inline-block;
font-size: 2rem;
font-weight: 200;
line-height: 1;
margin: 0.5rem 0;
}
&__subheading.is-html {
color: #e22f70;
}
&__subheading.is-css {
color: #0a91d4;
}
&__subheading.is-explanation {
color: #4b00da;
}
&__support-note {
color: #9fa5b5;
font-weight: bold;
}
&__requires-javascript {
position: absolute;
background: red;
background: linear-gradient(145deg, #ff003b, #ff4b39);
color: white;
padding: 0.25rem 0.5rem;
font-size: 0.9rem;
transform: rotate(20deg);
font-weight: bold;
top: 1rem;
right: 0;
}
&__tag {
}
&__new {
position: absolute;
top: -1rem;
left: 50%;
transform: translateX(-50%);
width: 3rem;
}
}
.snippet-demo {
background: #f5f6f9;
border-radius: 0.25rem;
padding: 0.75rem 1.25rem;
&.is-distinct {
background: linear-gradient(135deg, #ff4c9f, #ff7b74);
}
}
@media (min-width: 768px) {
.snippet {
&__requires-javascript {
right: -0.5rem;
}
}
}

View File

@ -1,85 +0,0 @@
.tags {
position: relative;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 1rem;
padding: 0 1rem;
&__tag {
display: inline-block;
position: relative;
top: -1px;
font-weight: bold;
font-size: 0.75rem;
text-transform: uppercase;
color: #8385aa;
white-space: nowrap;
border: 1px solid #c8cbf2;
border-radius: 2px;
vertical-align: middle;
line-height: 2;
padding: 0 0.5rem;
margin: 0 0.1rem;
transition: all 0.1s ease-out;
outline: 0;
&.is-large {
font-size: 0.95rem;
border-radius: 0.2rem;
.feather {
top: -2px;
width: 18px;
height: 18px;
}
}
.feather {
vertical-align: middle;
margin-right: 0.25rem;
position: relative;
top: -1px;
width: 14px;
height: 14px;
}
}
button.tags__tag {
user-select: none;
cursor: pointer;
margin-bottom: 1rem;
margin-right: 1rem;
background: white;
&:hover {
background: #8385aa;
border-color: #8385aa;
color: white;
}
&.focus-visible:focus {
box-shadow: 0 0 0 0.25rem transparentize(#8385aa, 0.5);
}
&:active {
box-shadow: inset 0 0.1rem 0.1rem 0.1rem rgba(0, 0, 0, 0.2);
background: darken(#8385aa, 10);
border-color: darken(#8385aa, 10);
}
&.is-active {
background: #7983ff;
border-color: #7983ff;
color: white;
&.focus-visible:focus {
box-shadow: 0 0 0 0.25rem transparentize(#7983ff, 0.5);
}
}
}
}
@media (min-width: 579px) {
}

View File

@ -1,147 +0,0 @@
code[class*='language-'],
pre[class*='language-'] {
color: rgb(215, 236, 255);
background: none;
font-family: 'Operator Mono', 'Roboto Mono', Menlo, Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono',
monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 2;
font-size: 1rem;
-webkit-overflow-scrolling: touch;
margin: 0;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*='language-']::-moz-selection,
pre[class*='language-'] ::-moz-selection,
code[class*='language-']::-moz-selection,
code[class*='language-'] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*='language-']::selection,
pre[class*='language-'] ::selection,
code[class*='language-']::selection,
code[class*='language-'] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*='language-'],
pre[class*='language-'] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*='language-'] {
overflow: auto;
padding: 0.75rem 1.25rem;
}
pre.is-option {
margin: 0;
padding: 0;
}
:not(pre) > code[class*='language-'],
pre[class*='language-'] {
background: linear-gradient(-30deg, #273149, #1c273f);
border-radius: 0.25rem;
}
/* Inline code */
:not(pre) > code[class*='language-'] {
padding: 0.1em;
border-radius: 0.3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #8ca2d3;
}
.token.selector,
.token.attr-name {
color: #c7f683;
}
.token.punctuation {
color: #5ac8e3;
}
.namespace {
opacity: 0.7;
}
.token.tag {
color: #2cefd8;
}
.token.property,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #85b4ff;
}
.token.string,
.language-css .token.string,
.token.url,
.token.attr-value,
.token.char,
.token.builtin,
.token.inserted {
color: #ffd694;
}
.token.operator,
.token.entity,
.style .token.string {
color: #ff9bbe;
}
.token.important,
.token.atrule,
.token.keyword {
color: #b7adff;
}
.token.function {
color: #25d0e5;
}
.token.regex,
.token.variable {
color: #00a8d4;
}
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

View File

@ -1,8 +0,0 @@
@import './components/base.scss';
@import './components/hamburger.scss';
@import './components/sidebar.scss';
@import './components/header.scss';
@import './components/snippet.scss';
@import './components/back-to-top-button.scss';
@import './components/tags.scss';
@import './components/buttons.scss';

View File

@ -1 +0,0 @@
<button class="back-to-top-button" aria-label="back to top"><i data-feather="arrow-up"></i></button>

View File

@ -1 +0,0 @@
<footer class="footer"></footer>

View File

@ -1,17 +0,0 @@
<header class="header">
<div class="container">
<img class="header__leaves" draggable="false" id="header__leaves1" src="./src/img/leaves.svg" alt>
<img class="header__leaves" draggable="false" id="header__leaves2" src="./src/img/leaves.svg" alt>
<img id="header__blob" draggable="false" src="./src/img/blob.png" alt>
<div class="header__content">
<img class="header__logo" draggable="false" src="./src/img/logo.png">
<h1 class="header__heading">30 Seconds of <strong class="header__css">CSS</strong></h1>
<p class="header__description">
A curated collection of useful CSS snippets you can understand in 30 seconds or less.
</p>
<div class="header__github-button-wrapper">
<a class="github-button header__github-button" href="https://github.com/30-seconds/30-seconds-of-css" data-icon="octicon-star" data-size="large" data-show-count="true" aria-label="Star 30-seconds/30-seconds-of-css on GitHub">Star</a>
</div>
</div>
</div>
</header>

View File

@ -1,4 +0,0 @@
<main class="main" id="main">
<div class="container">
</div>
</main>

View File

@ -1,9 +0,0 @@
<nav class="sidebar" aria-label="Table of Contents">
<button class="hamburger hamburger--spin sidebar__menu" type="button" aria-label="Menu" aria-expanded="false">
<span class="hamburger-box">
<span class="hamburger-inner"></span>
</span>
</button>
<div class="sidebar__links">
</div>
</nav>

View File

@ -1 +0,0 @@
<nav class="tags" aria-label="Filter by tags"></nav>

View File

@ -1,34 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>30 Seconds of CSS</title>
<link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png" />
<meta
name="description"
content="A curated collection of useful CSS snippets you can understand in 30 seconds or less. From foundational elements such as clearfix to gradient text color and gradient cursor tracking to CSS easing and far beyond."
/>
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://30-seconds.github.io/30-seconds-of-css/" />
<meta property="twitter:title" content="30 Seconds of CSS - Useful CSS snippets" />
<meta
property="twitter:description"
content="A curated collection of useful CSS snippets you can understand in 30 seconds or less. "
/>
<meta property="twitter:image" content="./src/img/opengraph.png" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://30-seconds.github.io/30-seconds-of-css/" />
<meta property="og:title" content="30 Seconds of CSS - Useful CSS snippets" />
<meta
property="og:description"
content="A curated collection of useful CSS snippets you can understand in 30 seconds or less."
/>
<meta property="og:image" content="./src/img/opengraph.png" />
<script src="./src/js/index.js" defer=""></script>
<script async="" defer="" src="https://buttons.github.io/buttons.js"></script>
</head>
<body>
<div class="content-wrapper"></div>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 255 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

View File

@ -1,14 +0,0 @@
import jump from 'jump.js'
import { select, scrollY, easeOutQuint } from '../deps/utils'
const backToTopButton = select('.back-to-top-button')
window.addEventListener('scroll', () => {
backToTopButton.classList[scrollY() > 500 ? 'add' : 'remove']('is-visible')
})
backToTopButton.onclick = () => {
jump('.header', {
duration: 750,
easing: easeOutQuint
})
}

View File

@ -1,28 +0,0 @@
import { selectAll } from '../deps/utils'
const snippets = selectAll('.snippet')
snippets.forEach(snippet => {
var codepenForm = document.createElement('form')
codepenForm.action = 'https://codepen.io/pen/define'
codepenForm.method = 'POST'
codepenForm.target = '_blank'
var codepenInput = document.createElement('input')
codepenInput.type = 'hidden'
codepenInput.name = 'data'
var codepenButton = document.createElement('button')
codepenButton.classList = 'btn is-large codepen-btn'
codepenButton.innerHTML = '<i data-feather="edit-2"></i>Edit on Codepen'
var css = snippet.querySelector('pre code.lang-css')
var html = snippet.querySelector('pre code.lang-html')
var js = snippet.querySelector('pre code.lang-js')
var data = {
css: css.textContent,
title: snippet.querySelector('h3 > span').textContent,
html: html ? html.textContent : '',
js: js ? js.textContent : ''
}
codepenInput.value = JSON.stringify(data)
codepenForm.appendChild(codepenInput)
codepenForm.appendChild(codepenButton)
snippet.insertBefore(codepenForm, snippet.querySelector('.snippet-demo').nextSibling)
})

View File

@ -1,54 +0,0 @@
import jump from 'jump.js'
import { select, selectAll, easeOutQuint } from '../deps/utils'
const menu = select('.hamburger')
const links = select('.sidebar__links')
const sections = selectAll('.sidebar__section')
const ACTIVE_CLASS = 'is-active'
const toggle = () => {
if (window.innerWidth <= 991) {
const els = [menu, links]
els.forEach(el => el.classList.toggle(ACTIVE_CLASS))
menu.setAttribute('aria-expanded', menu.classList.contains(ACTIVE_CLASS) ? 'true' : 'false')
}
}
menu.addEventListener('click', toggle)
links.addEventListener('click', e => {
const link = e.target.closest('.sidebar__link')
if (link) {
setTimeout(toggle, 50)
jump(link.getAttribute('href'), {
duration: 500,
easing: easeOutQuint,
offset: window.innerWidth <= 991 ? -64 : -32
})
}
})
document.addEventListener('click', e => {
if (
!e.target.closest('.sidebar__links') &&
!e.target.closest('.hamburger') &&
links.classList.contains(ACTIVE_CLASS)
) {
toggle()
}
})
EventHub.on('Tag.click', data => {
data.type_new = data.type.map(el => el.dataset.type)
sections.forEach(section => {
section.style.display = 'block'
//console.log(data.type_new.includes('all'))
if (!data.type_new.includes(section.dataset.type) && !data.type_new.includes('all')) {
section.style.display = 'none'
} else {
section.style.display = ''
}
})
})
export default { toggle }

View File

@ -1,16 +0,0 @@
import { selectAll } from '../deps/utils'
const snippets = selectAll('.snippet')
EventHub.on('Tag.click', data => {
data.type_new = data.type.map(el => el.dataset.type)
snippets.forEach(snippet => {
snippet.style.display = 'block'
if (data.type_new.includes('all')) return
const tags = selectAll('.tags__tag', snippet)
if (!tags.some(el => data.type_new.includes(el.dataset.type))) {
snippet.style.display = 'none'
} else {
snippet.style.display = ''
}
})
})

View File

@ -1,41 +0,0 @@
import { select, selectAll, on } from '../deps/utils'
const tagButtons = selectAll('button.tags__tag')
var isShiftSelected = false
const onClick = function() {
if (isShiftSelected && this.dataset.type === 'all') {
tagButtons.forEach(button => button.classList.remove('is-active'))
this.classList.add('is-active')
} else if (isShiftSelected) {
select('button[data-type=all]').classList.remove('is-active')
if (
this.classList.contains('is-active') &&
selectAll('button.tags__tag.is-active').length > 1
) {
this.classList.remove('is-active')
} else if (this.classList.contains('is-active')) {
this.classList.remove('is-active')
select('button[data-type=all]').classList.add('is-active')
} else {
this.classList.add('is-active')
}
} else {
tagButtons.forEach(button => button.classList.remove('is-active'))
this.classList.add('is-active')
}
EventHub.emit('Tag.click', {
type: [...selectAll('button.tags__tag.is-active')]
})
}
onkeydown = e => {
if (e.shiftKey) {
isShiftSelected = true
}
}
onkeyup = e => {
if (e.key == 'Shift') {
isShiftSelected = false
}
}
tagButtons.forEach(button => on(button, 'click', onClick))

View File

@ -1,16 +0,0 @@
const e = Element.prototype
if (!e.matches) {
e.matches =
e.matchesSelector || e.msMatchesSelector || e.webkitMatchesSelector || e.mozMatchesSelector
}
if (!e.closest) {
e.closest = function(s) {
var el = this
if (!document.documentElement.contains(el)) return null
do {
if (el.matches(s)) return el
el = el.parentElement || el.parentNode
} while (el !== null && el.nodeType === 1)
return null
}
}

View File

@ -1,77 +0,0 @@
export const select = (s, parent = document) => parent.querySelector(s)
export const selectAll = (s, parent = document) => [].slice.call(parent.querySelectorAll(s))
export const scrollY = () => window.scrollY || window.pageYOffset
export const easeOutQuint = (t, b, c, d) => c * ((t = t / d - 1) * t ** 4 + 1) + b
export const on = (el, evt, fn, opts = {}) => {
const delegatorFn = e => e.target.matches(opts.target) && fn.call(e.target, e)
el.addEventListener(evt, opts.target ? delegatorFn : fn, opts.options || false)
if (opts.target) return delegatorFn
}
export const createEventHub = () => ({
hub: Object.create(null),
emit(event, data) {
;(this.hub[event] || []).forEach(handler => handler(data))
},
on(event, handler) {
if (!this.hub[event]) this.hub[event] = []
this.hub[event].push(handler)
},
off(event, handler) {
const i = (this.hub[event] || []).findIndex(h => h === handler)
if (i > -1) this.hub[event].splice(i, 1)
}
})
window.EventHub = createEventHub()
/*
* Make iOS behave normally.
*/
if (/iPhone|iPad|iPod/.test(navigator.platform) && !window.MSStream) {
document.body.style.cursor = 'pointer'
}
if (/Mac/.test(navigator.platform)) {
document.documentElement.classList.add('macOS')
}
/*
* A small utility to fix the letter kerning on macOS Chrome and Firefox when using the system font
* (San Francisco). It is now fixed in the text rendering engine in FF 58 and Chrome 64.
* UPDATE: It appears the applied fix doesn't work when the font is in italics. New fix has been added.
* Must be applied to all browsers for now.
*/
;(() => {
const ua = navigator.userAgent
// macOS 10.11 (El Capitan) came with San Francisco. Previous versions used Helvetica
const isRelevantMacOS =
/Mac/.test(navigator.platform) && (ua.match(/OS X 10[._](\d{1,2})/) || [])[1] >= 11
// Chrome v64 and FF v58 fix the issue
const isAffectedBrowser =
(ua.match(/Chrome\/(\d+)\./) || [])[1] < 64 || (ua.match(/Firefox\/(\d+)\./) || [])[1] < 58
const allEls = [].slice.call(document.querySelectorAll('*'))
if (isRelevantMacOS && isAffectedBrowser) {
document.documentElement.style.letterSpacing = '-0.3px'
allEls.forEach(el => {
const fontSize = parseFloat(getComputedStyle(el).fontSize)
if (fontSize >= 20) el.style.letterSpacing = '0.3px'
})
} else if (isRelevantMacOS && !isAffectedBrowser) {
// Italics fix
allEls.forEach(el => {
const { fontSize, fontStyle } = getComputedStyle(el)
if (fontStyle === 'italic') {
el.style.letterSpacing = parseFloat(fontSize) >= 20 ? '0.3px' : '-0.3px'
}
})
}
})()

View File

@ -1,20 +0,0 @@
// Deps
import 'focus-visible'
import 'normalize.css'
import 'prismjs'
import feather from 'feather-icons'
feather.replace()
// CSS
import '../css/deps/prism.css'
import '../css/index.scss'
// Polyfills
import './deps/polyfills'
// Components
import Sidebar from './components/Sidebar'
import BackToTopButton from './components/BackToTopButton'
import Tag from './components/Tag'
import Snippet from './components/Snippet'
import CodepenCopy from './components/CodepenCopy'

View File

@ -0,0 +1,3 @@
---
_This README is built using [markdown-builder](https://github.com/30-seconds/markdown-builder)._

View File

@ -0,0 +1,22 @@
# 30 Seconds of CSS
<a href="https://css.30secondsofcode.org" target="_blank">![logo](https://i.imgur.com/kPMfyD4.jpg)</a>
[![License](https://img.shields.io/badge/license-CC0--1.0-blue.svg)](https://github.com/30-seconds/30-seconds-of-css/blob/master/LICENSE) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com) [![Insight.io](https://img.shields.io/badge/insight.io-Ready-brightgreen.svg)](https://insight.io/github.com/30-seconds/30-seconds-of-css/tree/master/?source=0)
A curated collection of useful CSS snippets you can understand in 30 seconds or less.
Inspired by [30 seconds of code](https://github.com/30-seconds/30-seconds-of-code).
## View online
https://css.30secondsofcode.org
## Contributing
See CONTRIBUTING.md for the snippet template.
#### Related projects
- [30 Seconds of Code](https://30secondsofcode.org/)
- [30 Seconds of Interviews](https://30secondsofinterviews.org/)
- [30 Seconds of React](https://github.com/30-seconds/30-seconds-of-react)

Some files were not shown because too many files have changed in this diff Show More