Merge branch 'master' into copyToClipboard
This commit is contained in:
46
CODE_OF_CONDUCT.md
Normal file
46
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment include:
|
||||||
|
|
||||||
|
* Using welcoming and inclusive language
|
||||||
|
* Being respectful of differing viewpoints and experiences
|
||||||
|
* Gracefully accepting constructive criticism
|
||||||
|
* Focusing on what is best for the community
|
||||||
|
* Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||||
|
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at cc.glows@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||||
|
|
||||||
|
[homepage]: http://contributor-covenant.org
|
||||||
|
[version]: http://contributor-covenant.org/version/1/4/
|
||||||
@ -42,3 +42,5 @@ Brief description
|
|||||||
If no link is provided, it defaults to 99+%. -->
|
If no link is provided, it defaults to 99+%. -->
|
||||||
|
|
||||||
* https://caniuse.com/#feat=some-feature
|
* https://caniuse.com/#feat=some-feature
|
||||||
|
|
||||||
|
<!-- tags: (separate each by a comma) -->
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
178
docs/index.html
178
docs/index.html
File diff suppressed because one or more lines are too long
832
index.html
832
index.html
File diff suppressed because it is too large
Load Diff
34
package-lock.json
generated
34
package-lock.json
generated
@ -236,7 +236,7 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"browserslist": "1.7.7",
|
"browserslist": "1.7.7",
|
||||||
"caniuse-db": "1.0.30000810",
|
"caniuse-db": "1.0.30000812",
|
||||||
"normalize-range": "0.1.2",
|
"normalize-range": "0.1.2",
|
||||||
"num2fraction": "1.2.2",
|
"num2fraction": "1.2.2",
|
||||||
"postcss": "5.2.18",
|
"postcss": "5.2.18",
|
||||||
@ -261,7 +261,7 @@
|
|||||||
"integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
|
"integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"caniuse-db": "1.0.30000810",
|
"caniuse-db": "1.0.30000812",
|
||||||
"electron-to-chromium": "1.3.34"
|
"electron-to-chromium": "1.3.34"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1703,7 +1703,7 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"browserslist": "1.7.7",
|
"browserslist": "1.7.7",
|
||||||
"caniuse-db": "1.0.30000810",
|
"caniuse-db": "1.0.30000812",
|
||||||
"lodash.memoize": "4.1.2",
|
"lodash.memoize": "4.1.2",
|
||||||
"lodash.uniq": "4.5.0"
|
"lodash.uniq": "4.5.0"
|
||||||
},
|
},
|
||||||
@ -1714,16 +1714,16 @@
|
|||||||
"integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
|
"integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"caniuse-db": "1.0.30000810",
|
"caniuse-db": "1.0.30000812",
|
||||||
"electron-to-chromium": "1.3.34"
|
"electron-to-chromium": "1.3.34"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"caniuse-db": {
|
"caniuse-db": {
|
||||||
"version": "1.0.30000810",
|
"version": "1.0.30000812",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000810.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000812.tgz",
|
||||||
"integrity": "sha1-vSWDDEHvq2Qzmi44H0lnc0PIRQk=",
|
"integrity": "sha1-KcKN1pJ+5D6MKrZI5SNqyRbJfWk=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
@ -1934,6 +1934,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"classnames": {
|
||||||
|
"version": "2.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.5.tgz",
|
||||||
|
"integrity": "sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0="
|
||||||
|
},
|
||||||
"cli-boxes": {
|
"cli-boxes": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
|
||||||
@ -3107,6 +3112,14 @@
|
|||||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"feather-icons": {
|
||||||
|
"version": "4.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/feather-icons/-/feather-icons-4.7.0.tgz",
|
||||||
|
"integrity": "sha512-/dKhEelpOazdN0MtcBZTgqo1tZHn35y40V0sGPi6FzzwNbQ5H6vzTqYWWMeRtKw5JePmog7RthR/PYPA1lYNFA==",
|
||||||
|
"requires": {
|
||||||
|
"classnames": "2.2.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"filename-regex": {
|
"filename-regex": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
|
||||||
@ -3148,6 +3161,11 @@
|
|||||||
"integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=",
|
"integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"focus-visible": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-K+bqYCQOPs5qHFag28o36TyILpqx43Qlq8/YxWdXEOWy3LZmxk8P+dhI5oPzuC28nwwIcerRNwRNiT3Msk0MqA=="
|
||||||
|
},
|
||||||
"for-in": {
|
"for-in": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
|
||||||
@ -7601,7 +7619,7 @@
|
|||||||
"integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
|
"integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"caniuse-db": "1.0.30000810",
|
"caniuse-db": "1.0.30000812",
|
||||||
"electron-to-chromium": "1.3.34"
|
"electron-to-chromium": "1.3.34"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
"babel-cli": "^6.26.0",
|
"babel-cli": "^6.26.0",
|
||||||
"babel-preset-env": "^1.6.1",
|
"babel-preset-env": "^1.6.1",
|
||||||
"babel-preset-stage-2": "^6.24.1",
|
"babel-preset-stage-2": "^6.24.1",
|
||||||
"caniuse-db": "^1.0.30000810",
|
"caniuse-db": "^1.0.30000812",
|
||||||
"jsdom": "^11.6.2",
|
"jsdom": "^11.6.2",
|
||||||
"marked": "^0.3.16",
|
"marked": "^0.3.16",
|
||||||
"node-sass": "^4.7.2",
|
"node-sass": "^4.7.2",
|
||||||
@ -25,5 +25,9 @@
|
|||||||
"prettier": "^1.10.2",
|
"prettier": "^1.10.2",
|
||||||
"pretty": "^2.0.0",
|
"pretty": "^2.0.0",
|
||||||
"prismjs": "^1.11.0"
|
"prismjs": "^1.11.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"feather-icons": "^4.7.0",
|
||||||
|
"focus-visible": "^4.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
137
scripts/build.js
137
scripts/build.js
@ -2,61 +2,82 @@ const fs = require('fs')
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
const marked = require('marked')
|
const marked = require('marked')
|
||||||
const pretty = require('pretty')
|
const pretty = require('pretty')
|
||||||
const { JSDOM } = require('jsdom')
|
|
||||||
const caniuseDb = require('caniuse-db/data.json')
|
const caniuseDb = require('caniuse-db/data.json')
|
||||||
const { toKebabCase, emptyHTML } = require('../utils/utils.js')
|
const { toKebabCase, createElement, template, dom } = require('../utils/utils.js')
|
||||||
|
|
||||||
const renderer = new marked.Renderer()
|
|
||||||
renderer.heading = (text, level) =>
|
|
||||||
level === 3
|
|
||||||
? `<h${level} id="${toKebabCase(text)}">${text}</h${level}>`
|
|
||||||
: `<h${level} data-type="${text}">${text}</h${level}>`
|
|
||||||
renderer.link = (url, _, text) => `<a href="${url}" target="_blank">${text || url}</a>`
|
|
||||||
|
|
||||||
const SNIPPETS_PATH = './snippets'
|
const SNIPPETS_PATH = './snippets'
|
||||||
const SNIPPET_CONTAINER_SELECTOR = '.main > .container'
|
const TAGS = [
|
||||||
|
{
|
||||||
|
name: 'all',
|
||||||
|
icon: 'check'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'layout',
|
||||||
|
icon: 'layout'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'visual',
|
||||||
|
icon: 'eye'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'animation',
|
||||||
|
icon: 'loader'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'interactivity',
|
||||||
|
icon: 'edit-2'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
const createElement = str => {
|
const renderer = new marked.Renderer()
|
||||||
const el = document.createElement('div')
|
renderer.heading = (text, level) => {
|
||||||
el.innerHTML = str
|
if (level === 3) {
|
||||||
return el.firstElementChild
|
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')
|
||||||
}
|
}
|
||||||
|
|
||||||
const template = markdown => `
|
const snippetContainer = components.main.querySelector('.container')
|
||||||
<div class="snippet">
|
const sidebarLinkContainer = components.sidebar.querySelector('.sidebar__links')
|
||||||
${markdown}
|
TAGS.slice(1).forEach(tag => {
|
||||||
</div>
|
sidebarLinkContainer.append(
|
||||||
`
|
createElement(`
|
||||||
|
<section data-type="${tag.name}" class="sidebar__section">
|
||||||
const document = new JSDOM(fs.readFileSync('./index.html', 'utf8')).window.document
|
<h4 class="sidebar__section-heading">${tag.name}</h4>
|
||||||
const snippetContainer = document.querySelector('.main > .container')
|
</section>
|
||||||
const sidebarLinks = document.querySelector('.sidebar__links')
|
`)
|
||||||
emptyHTML(snippetContainer, sidebarLinks)
|
)
|
||||||
|
})
|
||||||
|
|
||||||
for (const snippetFile of fs.readdirSync(SNIPPETS_PATH)) {
|
for (const snippetFile of fs.readdirSync(SNIPPETS_PATH)) {
|
||||||
const snippetPath = path.join(SNIPPETS_PATH, snippetFile)
|
const snippetPath = path.join(SNIPPETS_PATH, snippetFile)
|
||||||
const snippetData = fs.readFileSync(snippetPath, 'utf8')
|
const snippetData = fs.readFileSync(snippetPath, 'utf8')
|
||||||
const markdown = marked(snippetData, { renderer })
|
const markdown = marked(snippetData, { renderer })
|
||||||
const snippetTemplate = template(markdown)
|
const snippetEl = createElement(`<div class="snippet">${markdown}</div>`)
|
||||||
const el = createElement(snippetTemplate)
|
snippetContainer.append(snippetEl)
|
||||||
snippetContainer.append(el)
|
|
||||||
|
|
||||||
sidebarLinks.append(
|
|
||||||
createElement(
|
|
||||||
`<a class="sidebar__link" href="#${snippetFile.replace('.md', '')}">${
|
|
||||||
el.querySelector('h3').innerHTML
|
|
||||||
}</a>`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
// browser support usage
|
||||||
const featUsageShares = (snippetData.match(/https?:\/\/caniuse\.com\/#feat=.*/g) || []).map(
|
const featUsageShares = (snippetData.match(/https?:\/\/caniuse\.com\/#feat=.*/g) || []).map(
|
||||||
feat => {
|
feat => {
|
||||||
const featData = caniuseDb.data[feat.match(/#feat=(.*)/)[1]]
|
const featData = caniuseDb.data[feat.match(/#feat=(.*)/)[1]]
|
||||||
return featData ? Number(featData.usage_perc_y + featData.usage_perc_a) : 100
|
return featData ? Number(featData.usage_perc_y + featData.usage_perc_a) : 100
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
const browserSupportHeading = snippetEl.querySelector('h4:last-of-type')
|
||||||
el.querySelector('h4:last-of-type').after(
|
browserSupportHeading.after(
|
||||||
createElement(`
|
createElement(`
|
||||||
<div>
|
<div>
|
||||||
<div class="snippet__browser-support">
|
<div class="snippet__browser-support">
|
||||||
@ -65,8 +86,50 @@ for (const snippetFile of fs.readdirSync(SNIPPETS_PATH)) {
|
|||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// sidebar link
|
||||||
|
const link = createElement(
|
||||||
|
`<a class="sidebar__link" href="#${snippetFile.replace('.md', '')}">${
|
||||||
|
snippetEl.querySelector('h3').innerHTML
|
||||||
|
}</a>`
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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>`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
sidebarLinkContainer.querySelector(`section[data-type="${tag}"]`).append(link)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
// doctype declaration gets stripped, add it back in
|
// doctype declaration gets stripped, add it back in
|
||||||
const html = `<!DOCTYPE html>
|
const html = `<!DOCTYPE html>
|
||||||
${pretty(document.documentElement.outerHTML, { ocd: true })}
|
${pretty(document.documentElement.outerHTML, { ocd: true })}
|
||||||
|
|||||||
113
snippets/bouncing-loader.md
Normal file
113
snippets/bouncing-loader.md
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
### Bouncing loader
|
||||||
|
|
||||||
|
Creates a bouncing loader animation.
|
||||||
|
|
||||||
|
#### HTML
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div class="bouncing-loader">
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### CSS
|
||||||
|
|
||||||
|
```css
|
||||||
|
@keyframes bouncing-loader {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0.1;
|
||||||
|
transform: translateY(-1rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.bouncing-loader {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.bouncing-loader > div {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
margin: 3rem 0.2rem;
|
||||||
|
background: #8385aa;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: bouncing-loader 0.6s infinite alternate;
|
||||||
|
}
|
||||||
|
.bouncing-loader > div:nth-of-type(2) {
|
||||||
|
animation-delay: 0.2s;
|
||||||
|
}
|
||||||
|
.bouncing-loader > div:nth-of-type(3) {
|
||||||
|
animation-delay: 0.4s;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Demo
|
||||||
|
|
||||||
|
<div class="snippet-demo">
|
||||||
|
<div class="snippet-demo__bouncing-loader">
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes bouncing-loader {
|
||||||
|
from {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 0.1;
|
||||||
|
transform: translateY(-1rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.snippet-demo__bouncing-loader {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.snippet-demo__bouncing-loader > div {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
margin: 3rem 0.2rem;
|
||||||
|
background: #8385aa;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: bouncing-loader 0.6s infinite alternate;
|
||||||
|
}
|
||||||
|
.snippet-demo__bouncing-loader > div:nth-child(2) {
|
||||||
|
animation-delay: 0.2s;
|
||||||
|
}
|
||||||
|
.snippet-demo__bouncing-loader > div:nth-child(3) {
|
||||||
|
animation-delay: 0.4s;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
#### Explanation
|
||||||
|
|
||||||
|
Note: `1rem` is usually `16px`.
|
||||||
|
|
||||||
|
1. `@keyframes` defines an animation that has two states, where the element changes `opacity`, and is translated up on the 2D plane using `transform: translateY()`.
|
||||||
|
|
||||||
|
2. `.bouncing-loader` is the parent container of the bouncing circles and uses `display: flex`
|
||||||
|
and `justify-content: center` to position them in the in the center.
|
||||||
|
|
||||||
|
3. Using `.bouncing-loader > div`, we target the three child `div`s of the parent to be styled. The `div`s are given a width and height of `1rem`, using `border-radius: 50%` to turn them from squares to circles.
|
||||||
|
|
||||||
|
4. `margin: 3rem 0.2rem` specifies that each circle has a top/bottom margin of `3rem` and left/right margin
|
||||||
|
of `0.2rem` so that they do not directly touch each other, giving them some breathing room.
|
||||||
|
|
||||||
|
5. `animation` is a shorthand property for the various animation properties: `animation-name`, `animation-duration`, `animation-iteration-count`, `animation-direction` are used.
|
||||||
|
|
||||||
|
6. `animation-delay` is used on the second and third `div` respectively, so that each element does not start the animation at the same time.
|
||||||
|
|
||||||
|
#### Browser support
|
||||||
|
|
||||||
|
<span class="snippet__support-note">✅ No caveats.</span>
|
||||||
|
|
||||||
|
* https://caniuse.com/#feat=css-animation
|
||||||
|
|
||||||
|
<!-- tags: animation -->
|
||||||
@ -44,3 +44,5 @@ html {
|
|||||||
<span class="snippet__support-note">✅ No caveats.</span>
|
<span class="snippet__support-note">✅ No caveats.</span>
|
||||||
|
|
||||||
* https://caniuse.com/#feat=css3-boxsizing
|
* https://caniuse.com/#feat=css3-boxsizing
|
||||||
|
|
||||||
|
<!-- tags: layout -->
|
||||||
|
|||||||
50
snippets/circle.md
Normal file
50
snippets/circle.md
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
### Circle
|
||||||
|
|
||||||
|
Creates a circle shape with pure CSS.
|
||||||
|
|
||||||
|
#### HTML
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div class="circle"></div>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### CSS
|
||||||
|
|
||||||
|
```css
|
||||||
|
.circle {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Demo
|
||||||
|
|
||||||
|
<div class="snippet-demo">
|
||||||
|
<div class="snippet-demo__circle"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.snippet-demo__circle {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
background: #333;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
#### Explanation
|
||||||
|
|
||||||
|
`border-radius: 50%` curves the borders of an element to create a circle.
|
||||||
|
|
||||||
|
Since a circle has the same radius at any given point, the `width` and `height` must be the same. Differing
|
||||||
|
values will create an ellipse.
|
||||||
|
|
||||||
|
#### Browser support
|
||||||
|
|
||||||
|
<span class="snippet__support-note">✅ No caveats.</span>
|
||||||
|
|
||||||
|
* https://caniuse.com/#feat=border-radius
|
||||||
|
|
||||||
|
<!-- tags: visual -->
|
||||||
@ -18,7 +18,7 @@ Ensures that an element self-clears its children.
|
|||||||
|
|
||||||
```css
|
```css
|
||||||
.clearfix::after {
|
.clearfix::after {
|
||||||
content: "";
|
content: '';
|
||||||
display: block;
|
display: block;
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
@ -59,4 +59,6 @@ Ensures that an element self-clears its children.
|
|||||||
|
|
||||||
#### Browser support
|
#### Browser support
|
||||||
|
|
||||||
<span class="snippet__support-note">✅ No caveats.</span>
|
<span class="snippet__support-note">⚠️ For this snippet to work properly you need to ensure that there are no non-floating children in the container and that there are no tall floats before the clearfixed container but in the same formatting context (e.g. floated columns).</span>
|
||||||
|
|
||||||
|
<!-- tags: layout -->
|
||||||
|
|||||||
@ -43,3 +43,5 @@ causes an element's height to become a percentage of its parent's width, i.e. `5
|
|||||||
#### Browser support
|
#### Browser support
|
||||||
|
|
||||||
<span class="snippet__support-note">⚠️ `padding-top` pushes any content within the element to the bottom.</span>
|
<span class="snippet__support-note">⚠️ `padding-top` pushes any content within the element to the bottom.</span>
|
||||||
|
|
||||||
|
<!-- tags: layout -->
|
||||||
|
|||||||
@ -19,13 +19,13 @@ Customizes the scrollbar style for the document and elements with scrollable ove
|
|||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
box-shadow: inset 0 0 6px rgba(0,0,0,0.5);
|
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scrollable element */
|
/* Scrollable element */
|
||||||
@ -80,3 +80,5 @@ There are many other pseudo-elements that you can use to style scrollbars. For m
|
|||||||
<span class="snippet__support-note">⚠️ Scrollbar styling doesn't appear to be on any standards track.</span>
|
<span class="snippet__support-note">⚠️ Scrollbar styling doesn't appear to be on any standards track.</span>
|
||||||
|
|
||||||
* https://caniuse.com/#feat=css-scrollbar
|
* https://caniuse.com/#feat=css-scrollbar
|
||||||
|
|
||||||
|
<!-- tags: visual -->
|
||||||
|
|||||||
@ -48,3 +48,5 @@ Changes the styling of text selection.
|
|||||||
in any specification.</span>
|
in any specification.</span>
|
||||||
|
|
||||||
* https://caniuse.com/#feat=css-selection
|
* https://caniuse.com/#feat=css-selection
|
||||||
|
|
||||||
|
<!-- tags: visual -->
|
||||||
|
|||||||
@ -37,5 +37,8 @@ Makes the content unselectable.
|
|||||||
#### Browser support
|
#### Browser support
|
||||||
|
|
||||||
<span class="snippet__support-note">⚠️ Requires prefixes for full support.</span>
|
<span class="snippet__support-note">⚠️ Requires prefixes for full support.</span>
|
||||||
|
<span class="snippet__support-note">⚠️ This is not a secure method to prevent users from copying content.</span>
|
||||||
|
|
||||||
* https://caniuse.com/#feat=user-select-none
|
* https://caniuse.com/#feat=user-select-none
|
||||||
|
|
||||||
|
<!-- tags: interactivity -->
|
||||||
|
|||||||
@ -12,8 +12,12 @@ Creates a donut spinner that can be used to indicate the loading of content.
|
|||||||
|
|
||||||
```css
|
```css
|
||||||
@keyframes donut-spin {
|
@keyframes donut-spin {
|
||||||
0% { transform: rotate(0deg); }
|
0% {
|
||||||
100% { transform: rotate(360deg); }
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.donut {
|
.donut {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -59,3 +63,5 @@ serve as the loading indicator for the donut. Use `animation` to rotate the elem
|
|||||||
|
|
||||||
* https://caniuse.com/#feat=css-animation
|
* https://caniuse.com/#feat=css-animation
|
||||||
* https://caniuse.com/#feat=transforms2d
|
* https://caniuse.com/#feat=transforms2d
|
||||||
|
|
||||||
|
<!-- tags: animation -->
|
||||||
|
|||||||
@ -104,3 +104,5 @@ The variables are defined globally within the `:root` CSS pseudo-class which mat
|
|||||||
<span class="snippet__support-note">✅ No caveats.</span>
|
<span class="snippet__support-note">✅ No caveats.</span>
|
||||||
|
|
||||||
* https://caniuse.com/#feat=css-variables
|
* https://caniuse.com/#feat=css-variables
|
||||||
|
|
||||||
|
<!-- tags: animation -->
|
||||||
|
|||||||
@ -49,3 +49,5 @@ of the background.
|
|||||||
<span class="snippet__support-note">✅ No caveats.</span>
|
<span class="snippet__support-note">✅ No caveats.</span>
|
||||||
|
|
||||||
* https://caniuse.com/#feat=css-textshadow
|
* https://caniuse.com/#feat=css-textshadow
|
||||||
|
|
||||||
|
<!-- tags: visual -->
|
||||||
|
|||||||
@ -51,3 +51,5 @@ Alternatively, use `justify-content: space-around` to distribute the children wi
|
|||||||
<span class="snippet__support-note">⚠️ Needs prefixes for full support.</span>
|
<span class="snippet__support-note">⚠️ Needs prefixes for full support.</span>
|
||||||
|
|
||||||
* https://caniuse.com/#feat=flexbox
|
* https://caniuse.com/#feat=flexbox
|
||||||
|
|
||||||
|
<!-- tags: layout -->
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
### Horizontal and vertical centering
|
### Flexbox centering
|
||||||
|
|
||||||
Horizontally and vertically centers a child element within a parent element.
|
Horizontally and vertically centers a child element within a parent element using `flexbox`.
|
||||||
|
|
||||||
#### HTML
|
#### HTML
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div class="horizontal-and-vertical-centering">
|
<div class="flexbox-centering">
|
||||||
<div class="child"></div>
|
<div class="child">Centered content.</div>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
#### CSS
|
#### CSS
|
||||||
|
|
||||||
```css
|
```css
|
||||||
.horizontal-and-vertical-centering {
|
.flexbox-centering {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -23,13 +23,13 @@ Horizontally and vertically centers a child element within a parent element.
|
|||||||
#### Demo
|
#### Demo
|
||||||
|
|
||||||
<div class="snippet-demo">
|
<div class="snippet-demo">
|
||||||
<div class="snippet-demo__horizontal-and-vertical-centering">
|
<div class="snippet-demo__flexbox-centering">
|
||||||
<p class="snippet-demo__horizontal-and-vertical-centering__child">Centered content.</p>
|
<p class="snippet-demo__flexbox-centering__child">Centered content.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.snippet-demo__horizontal-and-vertical-centering {
|
.snippet-demo__flexbox-centering {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -48,3 +48,5 @@ Horizontally and vertically centers a child element within a parent element.
|
|||||||
<span class="snippet__support-note">⚠️ Needs prefixes for full support.</span>
|
<span class="snippet__support-note">⚠️ Needs prefixes for full support.</span>
|
||||||
|
|
||||||
* https://caniuse.com/#feat=flexbox
|
* https://caniuse.com/#feat=flexbox
|
||||||
|
|
||||||
|
<!-- tags: layout -->
|
||||||
@ -49,3 +49,5 @@ Gives text a gradient color.
|
|||||||
<span class="snippet__support-note">⚠️ Uses non-standard properties.</span>
|
<span class="snippet__support-note">⚠️ Uses non-standard properties.</span>
|
||||||
|
|
||||||
* https://caniuse.com/#feat=text-stroke
|
* https://caniuse.com/#feat=text-stroke
|
||||||
|
|
||||||
|
<!-- tags: visual -->
|
||||||
|
|||||||
52
snippets/grid-centering.md
Normal file
52
snippets/grid-centering.md
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
### Grid centering
|
||||||
|
|
||||||
|
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;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Demo
|
||||||
|
|
||||||
|
<div class="snippet-demo">
|
||||||
|
<div class="snippet-demo__grid-centering">
|
||||||
|
<p class="snippet-demo__grid-centering__child">Centered content.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.snippet-demo__grid-centering {
|
||||||
|
display: grid;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
#### Explanation
|
||||||
|
|
||||||
|
1. `display: grid` enables grid.
|
||||||
|
2. `justify-content: center` centers the child horizontally.
|
||||||
|
3. `align-items: center` centers the child vertically.
|
||||||
|
|
||||||
|
#### Browser support
|
||||||
|
|
||||||
|
<span class="snippet__support-note">✅ No caveats.</span>
|
||||||
|
|
||||||
|
* https://caniuse.com/#feat=css-grid
|
||||||
|
|
||||||
|
<!-- tags: layout -->
|
||||||
@ -81,3 +81,5 @@ very sharp and crisp.
|
|||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
\*Chrome does not support subpixel values on `border`. Safari does not support subpixel values on `box-shadow`. Firefox supports subpixel values on both.
|
\*Chrome does not support subpixel values on `border`. Safari does not support subpixel values on `box-shadow`. Firefox supports subpixel values on both.
|
||||||
|
|
||||||
|
<!-- tags: visual -->
|
||||||
|
|||||||
@ -69,7 +69,7 @@ Creates an animated underline effect when the text is hovered over.
|
|||||||
#### Explanation
|
#### Explanation
|
||||||
|
|
||||||
1. `display: inline-block` makes the block `p` an `inline-block` to prevent the underline from
|
1. `display: inline-block` makes the block `p` an `inline-block` to prevent the underline from
|
||||||
spanning the entire parent width rather than just the content (text).
|
spanning the entire parent width rather than just the content (text).
|
||||||
2. `position: relative` on the element establishes a Cartesian positioning context for pseudo-elements.
|
2. `position: relative` on the element establishes a Cartesian positioning context for pseudo-elements.
|
||||||
3. `::after` defines a pseudo-element.
|
3. `::after` defines a pseudo-element.
|
||||||
4. `position: absolute` takes the pseudo element out of the flow of the document and positions it in relation to the parent.
|
4. `position: absolute` takes the pseudo element out of the flow of the document and positions it in relation to the parent.
|
||||||
@ -77,11 +77,11 @@ spanning the entire parent width rather than just the content (text).
|
|||||||
6. `transform: scaleX(0)` initially scales the pseudo element to 0 so it has no width and is not visible.
|
6. `transform: scaleX(0)` initially scales the pseudo element to 0 so it has no width and is not visible.
|
||||||
7. `bottom: 0` and `left: 0` position it to the bottom left of the block.
|
7. `bottom: 0` and `left: 0` position it to the bottom left of the block.
|
||||||
8. `transition: transform 0.25s ease-out` means changes to `transform` will be transitioned over 0.25 seconds
|
8. `transition: transform 0.25s ease-out` means changes to `transform` will be transitioned over 0.25 seconds
|
||||||
with an `ease-out` timing function.
|
with an `ease-out` timing function.
|
||||||
9. `transform-origin: bottom right` means the transform anchor point is positioned at the bottom right of the block.
|
9. `transform-origin: bottom right` means the transform anchor point is positioned at the bottom right of the block.
|
||||||
10. `:hover::after` then uses `scaleX(1)` to transition the width to 100%, then changes the `transform-origin`
|
10. `:hover::after` then uses `scaleX(1)` to transition the width to 100%, then changes the `transform-origin`
|
||||||
to `bottom left` so that the anchor point is reversed, allowing it transition out in the other direction when
|
to `bottom left` so that the anchor point is reversed, allowing it transition out in the other direction when
|
||||||
hovered off.
|
hovered off.
|
||||||
|
|
||||||
#### Browser support
|
#### Browser support
|
||||||
|
|
||||||
@ -89,3 +89,5 @@ hovered off.
|
|||||||
|
|
||||||
* https://caniuse.com/#feat=transforms2d
|
* https://caniuse.com/#feat=transforms2d
|
||||||
* https://caniuse.com/#feat=css-transitions
|
* https://caniuse.com/#feat=css-transitions
|
||||||
|
|
||||||
|
<!-- tags: animation -->
|
||||||
|
|||||||
@ -41,7 +41,7 @@ A hover effect where the gradient follows the mouse cursor.
|
|||||||
height: var(--size);
|
height: var(--size);
|
||||||
background: radial-gradient(circle closest-side, pink, transparent);
|
background: radial-gradient(circle closest-side, pink, transparent);
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
transition: width .2s ease, height .2s ease;
|
transition: width 0.2s ease, height 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mouse-cursor-gradient-tracking:hover::before {
|
.mouse-cursor-gradient-tracking:hover::before {
|
||||||
@ -53,7 +53,7 @@ A hover effect where the gradient follows the mouse cursor.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
var btn = document.querySelector('.mouse-cursor-gradient-tracking')
|
var btn = document.querySelector('.mouse-cursor-gradient-tracking')
|
||||||
btn.onmousemove = function (e) {
|
btn.onmousemove = function(e) {
|
||||||
var x = e.pageX - btn.offsetLeft
|
var x = e.pageX - btn.offsetLeft
|
||||||
var y = e.pageY - btn.offsetTop
|
var y = e.pageY - btn.offsetTop
|
||||||
btn.style.setProperty('--x', x + 'px')
|
btn.style.setProperty('--x', x + 'px')
|
||||||
@ -131,7 +131,10 @@ var y = e.pageY - btn.offsetTop - btn.offsetParent.offsetTop
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### Browser support
|
#### Browser support
|
||||||
|
|
||||||
<div class="snippet__requires-javascript">Requires JavaScript</div>
|
<div class="snippet__requires-javascript">Requires JavaScript</div>
|
||||||
<span class="snippet__support-note">⚠️ Requires JavaScript.</span>
|
<span class="snippet__support-note">⚠️ Requires JavaScript.</span>
|
||||||
|
|
||||||
* https://caniuse.com/#feat=css-variables
|
* https://caniuse.com/#feat=css-variables
|
||||||
|
|
||||||
|
<!-- tags: visual, interactivity -->
|
||||||
|
|||||||
@ -22,16 +22,19 @@ Adds a fading gradient to an overflowing element to better indicate there is mor
|
|||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 300px;
|
width: 240px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
background: linear-gradient(rgba(255, 255, 255, 0.001), white); /* transparent keyword is broken in Safari */
|
background: linear-gradient(
|
||||||
|
rgba(255, 255, 255, 0.001),
|
||||||
|
white
|
||||||
|
); /* transparent keyword is broken in Safari */
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
.overflow-scroll-gradient__scroller {
|
.overflow-scroll-gradient__scroller {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
background: white;
|
background: white;
|
||||||
width: 300px;
|
width: 240px;
|
||||||
height: 250px;
|
height: 200px;
|
||||||
padding: 15px 0;
|
padding: 15px 0;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -56,7 +59,7 @@ Adds a fading gradient to an overflowing element to better indicate there is mor
|
|||||||
content: '';
|
content: '';
|
||||||
background: linear-gradient(rgba(255, 255, 255, 0.001), white);
|
background: linear-gradient(rgba(255, 255, 255, 0.001), white);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 300px;
|
width: 240px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@ -64,8 +67,8 @@ Adds a fading gradient to an overflowing element to better indicate there is mor
|
|||||||
.snippet-demo__overflow-scroll-gradient__scroller {
|
.snippet-demo__overflow-scroll-gradient__scroller {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
background: white;
|
background: white;
|
||||||
width: 300px;
|
width: 240px;
|
||||||
height: 250px;
|
height: 200px;
|
||||||
padding: 15px 0;
|
padding: 15px 0;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -73,7 +76,7 @@ Adds a fading gradient to an overflowing element to better indicate there is mor
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.querySelector('.snippet-demo__overflow-scroll-gradient__scroller').innerHTML = 'content '.repeat(200)
|
document.querySelector('.snippet-demo__overflow-scroll-gradient__scroller').innerHTML = 'content '.repeat(100)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
#### Explanation
|
#### Explanation
|
||||||
@ -81,9 +84,9 @@ document.querySelector('.snippet-demo__overflow-scroll-gradient__scroller').inne
|
|||||||
1. `position: relative` on the parent establishes a Cartesian positioning context for pseudo-elements.
|
1. `position: relative` on the parent establishes a Cartesian positioning context for pseudo-elements.
|
||||||
2. `::after` defines a pseudo element.
|
2. `::after` defines a pseudo element.
|
||||||
3. `background-image: linear-gradient(...)` adds a linear gradient that fades from transparent to white
|
3. `background-image: linear-gradient(...)` adds a linear gradient that fades from transparent to white
|
||||||
(top to bottom).
|
(top to bottom).
|
||||||
4. `position: absolute` takes the pseudo element out of the flow of the document and positions it in relation to the parent.
|
4. `position: absolute` takes the pseudo element out of the flow of the document and positions it in relation to the parent.
|
||||||
5. `width: 300px` matches the size of the scrolling element (which is a child of the parent that has
|
5. `width: 240px` matches the size of the scrolling element (which is a child of the parent that has
|
||||||
the pseudo element).
|
the pseudo element).
|
||||||
6. `height: 25px` is the height of the fading gradient pseudo-element, which should be kept relatively small.
|
6. `height: 25px` is the height of the fading gradient pseudo-element, which should be kept relatively small.
|
||||||
7. `bottom: 0` positions the pseudo-element at the bottom of the parent.
|
7. `bottom: 0` positions the pseudo-element at the bottom of the parent.
|
||||||
@ -94,3 +97,5 @@ document.querySelector('.snippet-demo__overflow-scroll-gradient__scroller').inne
|
|||||||
<span class="snippet__support-note">✅ No caveats.</span>
|
<span class="snippet__support-note">✅ No caveats.</span>
|
||||||
|
|
||||||
* https://caniuse.com/#feat=css-gradients
|
* https://caniuse.com/#feat=css-gradients
|
||||||
|
|
||||||
|
<!-- tags: visual -->
|
||||||
|
|||||||
@ -75,3 +75,5 @@ Reveals an interactive popout menu on hover.
|
|||||||
#### Browser support
|
#### Browser support
|
||||||
|
|
||||||
<span class="snippet__support-note">✅ No caveats.</span>
|
<span class="snippet__support-note">✅ No caveats.</span>
|
||||||
|
|
||||||
|
<!-- tags: interactivity -->
|
||||||
|
|||||||
@ -16,10 +16,7 @@ Natively implemented as `text-decoration-skip-ink: auto` but it has less control
|
|||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
display: inline;
|
display: inline;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
text-shadow: 1px 1px 0 #f5f6f9,
|
text-shadow: 1px 1px 0 #f5f6f9, -1px 1px 0 #f5f6f9, -1px -1px 0 #f5f6f9, 1px -1px 0 #f5f6f9;
|
||||||
-1px 1px 0 #f5f6f9,
|
|
||||||
-1px -1px 0 #f5f6f9,
|
|
||||||
1px -1px 0 #f5f6f9;
|
|
||||||
background-image: linear-gradient(90deg, currentColor 100%, transparent 100%);
|
background-image: linear-gradient(90deg, currentColor 100%, transparent 100%);
|
||||||
background-position: 0 0.98em;
|
background-position: 0 0.98em;
|
||||||
background-repeat: repeat-x;
|
background-repeat: repeat-x;
|
||||||
@ -84,3 +81,5 @@ Natively implemented as `text-decoration-skip-ink: auto` but it has less control
|
|||||||
|
|
||||||
* https://caniuse.com/#feat=css-textshadow
|
* https://caniuse.com/#feat=css-textshadow
|
||||||
* https://caniuse.com/#feat=css-gradients
|
* https://caniuse.com/#feat=css-gradients
|
||||||
|
|
||||||
|
<!-- tags: visual -->
|
||||||
|
|||||||
47
snippets/reset-all-styles.md
Normal file
47
snippets/reset-all-styles.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
### Reset all styles
|
||||||
|
|
||||||
|
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">
|
||||||
|
<h4>Title</h4>
|
||||||
|
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure id exercitationem nulla qui repellat laborum vitae, molestias tempora velit natus. Quas, assumenda nisi. Quisquam enim qui iure, consequatur velit sit?</p>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### CSS
|
||||||
|
|
||||||
|
```css
|
||||||
|
.reset-all-styles {
|
||||||
|
all: initial;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Demo
|
||||||
|
|
||||||
|
<div class="snippet-demo">
|
||||||
|
<div class="snippet-demo__reset-all-styles">
|
||||||
|
<h3>Title</h3>
|
||||||
|
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure id exercitationem nulla qui repellat laborum vitae, molestias tempora velit natus. Quas, assumenda nisi. Quisquam enim qui iure, consequatur velit sit?</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.snippet-demo__reset-all-styles {
|
||||||
|
all: initial;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
#### Explanation
|
||||||
|
|
||||||
|
The `all` property allows you to reset all styles (inherited or not) to default values.
|
||||||
|
|
||||||
|
#### Browser support
|
||||||
|
|
||||||
|
<span class="snippet__support-note">⚠️ MS Edge status is under consideration.</span>
|
||||||
|
|
||||||
|
* https://caniuse.com/#feat=css-all
|
||||||
|
|
||||||
|
<!-- tags: visual -->
|
||||||
@ -14,6 +14,7 @@ Uses an SVG shape to separate two different blocks to create more a interesting
|
|||||||
.shape-separator {
|
.shape-separator {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
|
background: #333;
|
||||||
}
|
}
|
||||||
.shape-separator::after {
|
.shape-separator::after {
|
||||||
content: '';
|
content: '';
|
||||||
@ -64,3 +65,5 @@ Uses an SVG shape to separate two different blocks to create more a interesting
|
|||||||
<span class="snippet__support-note">✅ No caveats.</span>
|
<span class="snippet__support-note">✅ No caveats.</span>
|
||||||
|
|
||||||
* https://caniuse.com/#feat=svg
|
* https://caniuse.com/#feat=svg
|
||||||
|
|
||||||
|
<!-- tags: visual -->
|
||||||
|
|||||||
74
snippets/sibling-fade.md
Normal file
74
snippets/sibling-fade.md
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
### Sibling fade
|
||||||
|
|
||||||
|
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>
|
||||||
|
<span>Item 5</span>
|
||||||
|
<span>Item 6</span>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### CSS
|
||||||
|
|
||||||
|
```css
|
||||||
|
span {
|
||||||
|
padding: 0 1rem;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sibling-fade:hover span:not(:hover) {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Demo
|
||||||
|
|
||||||
|
<div class="snippet-demo">
|
||||||
|
<nav class="snippet-demo__sibling-fade">
|
||||||
|
<span>Item 1</span>
|
||||||
|
<span>Item 2</span>
|
||||||
|
<span>Item 3</span>
|
||||||
|
<span>Item 4</span>
|
||||||
|
<span>Item 5</span>
|
||||||
|
<span>Item 6</span>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.snippet-demo__sibling-fade {
|
||||||
|
cursor: default;
|
||||||
|
line-height: 2;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.snippet-demo__sibling-fade span {
|
||||||
|
padding: 1rem;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.snippet-demo__sibling-fade:hover span:not(:hover) {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
#### Explanation
|
||||||
|
|
||||||
|
1. `transition: opacity 0.2s` specifies that changes to opacity will be transitioned over 0.2 seconds.
|
||||||
|
2. `.sibling-fade:hover span:not(:hover)` specifies that when the parent is hovered, select any `span` children
|
||||||
|
that are not currently being hovered and change their opacity to `0.5`.
|
||||||
|
|
||||||
|
#### Browser support
|
||||||
|
|
||||||
|
<span class="snippet__support-note">✅ No caveats.</span>
|
||||||
|
|
||||||
|
* https://caniuse.com/#feat=css-sel3
|
||||||
|
* https://caniuse.com/#feat=css-transitions
|
||||||
|
|
||||||
|
<!-- tags: interactivity -->
|
||||||
@ -12,7 +12,8 @@ Uses the native font of the operating system to get close to a native app feel.
|
|||||||
|
|
||||||
```css
|
```css
|
||||||
.system-font-stack {
|
.system-font-stack {
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu,
|
||||||
|
Cantarell, 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -46,3 +47,5 @@ falls back to the next if it cannot find the font (on the system or defined in C
|
|||||||
#### Browser support
|
#### Browser support
|
||||||
|
|
||||||
<span class="snippet__support-note">✅ No caveats.</span>
|
<span class="snippet__support-note">✅ No caveats.</span>
|
||||||
|
|
||||||
|
<!-- tags: visual -->
|
||||||
|
|||||||
@ -89,3 +89,5 @@ Experiment with the `px` values to change the proportion of the triangle.
|
|||||||
#### Browser support
|
#### Browser support
|
||||||
|
|
||||||
<span class="snippet__support-note">✅ No caveats.</span>
|
<span class="snippet__support-note">✅ No caveats.</span>
|
||||||
|
|
||||||
|
<!-- tags: visual -->
|
||||||
|
|||||||
@ -51,3 +51,5 @@ If the text is longer than one line, it will be truncated and end with an ellips
|
|||||||
<span class="snippet__support-note">⚠️ Only works for single line elements.</span>
|
<span class="snippet__support-note">⚠️ Only works for single line elements.</span>
|
||||||
|
|
||||||
* https://caniuse.com/#feat=text-overflow
|
* https://caniuse.com/#feat=text-overflow
|
||||||
|
|
||||||
|
<!-- tags: layout -->
|
||||||
|
|||||||
@ -1,5 +1,12 @@
|
|||||||
html {
|
html {
|
||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::after,
|
||||||
|
*::before {
|
||||||
|
box-sizing: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@ -32,7 +39,7 @@ ol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
max-width: 1000px;
|
max-width: 64rem;
|
||||||
padding: 0 2%;
|
padding: 0 2%;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|||||||
76
src/css/components/buttons.scss
Normal file
76
src/css/components/buttons.scss
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,8 @@
|
|||||||
.header {
|
.header {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 5rem 1rem 4rem;
|
padding: 5rem 1rem 4rem;
|
||||||
background: linear-gradient(#5cd2ff, #5b67ff, #681ae4);
|
background: #5b67ff;
|
||||||
|
background: linear-gradient(45deg, #5cd2ff, #5b67ff, #681ae4);
|
||||||
color: white;
|
color: white;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@ -35,6 +35,7 @@
|
|||||||
max-height: 378px;
|
max-height: 378px;
|
||||||
margin-top: 44px;
|
margin-top: 44px;
|
||||||
box-shadow: 0 0.25rem 0.5rem -0.1rem rgba(0, 32, 128, 0.2);
|
box-shadow: 0 0.25rem 0.5rem -0.1rem rgba(0, 32, 128, 0.2);
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
transform: rotateX(0);
|
transform: rotateX(0);
|
||||||
@ -59,6 +60,16 @@
|
|||||||
border-color: pink;
|
border-color: pink;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__section {
|
||||||
|
padding: 0 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__section-heading {
|
||||||
|
text-transform: capitalize;
|
||||||
|
color: #e3f5ff;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 992px) {
|
@media (min-width: 992px) {
|
||||||
|
|||||||
@ -14,6 +14,10 @@
|
|||||||
margin-bottom: 1.25rem;
|
margin-bottom: 1.25rem;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
|
|
||||||
|
span:not(.snippet__tag) {
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
code:not([class*='lang']) {
|
code:not([class*='lang']) {
|
||||||
@ -42,16 +46,20 @@
|
|||||||
h4 {
|
h4 {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 1rem 0 0.5rem;
|
margin: 1rem 0 0.5rem;
|
||||||
|
font-size: 1.1rem;
|
||||||
line-height: 2;
|
line-height: 2;
|
||||||
|
|
||||||
|
&[data-type] {
|
||||||
|
background: #333;
|
||||||
padding: 0 0.5rem;
|
padding: 0 0.5rem;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
background: #333;
|
|
||||||
border: 1px solid #c6d6ea;
|
border: 1px solid #c6d6ea;
|
||||||
border-bottom-color: darken(#c6d6ea, 5);
|
border-bottom-color: darken(#c6d6ea, 5);
|
||||||
background: white;
|
background: white;
|
||||||
box-shadow: 0 0.25rem 0.5rem -0.1rem rgba(0, 32, 64, 0.15);
|
box-shadow: 0 0.25rem 0.5rem -0.1rem rgba(0, 32, 64, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
&[data-type='HTML'] {
|
&[data-type='HTML'] {
|
||||||
color: white;
|
color: white;
|
||||||
@ -107,6 +115,9 @@
|
|||||||
top: 1rem;
|
top: 1rem;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__tag {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.snippet-demo {
|
.snippet-demo {
|
||||||
|
|||||||
84
src/css/components/tags.scss
Normal file
84
src/css/components/tags.scss
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
.tags {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 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-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.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) {
|
||||||
|
}
|
||||||
@ -4,3 +4,5 @@
|
|||||||
@import './components/header.scss';
|
@import './components/header.scss';
|
||||||
@import './components/snippet.scss';
|
@import './components/snippet.scss';
|
||||||
@import './components/back-to-top-button.scss';
|
@import './components/back-to-top-button.scss';
|
||||||
|
@import './components/tags.scss';
|
||||||
|
@import './components/buttons.scss';
|
||||||
|
|||||||
1
src/html/components/back-to-top-button.html
Normal file
1
src/html/components/back-to-top-button.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<button class="back-to-top-button" aria-label="back to top">↑</button>
|
||||||
1
src/html/components/footer.html
Normal file
1
src/html/components/footer.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<footer class="footer"></footer>
|
||||||
10
src/html/components/header.html
Normal file
10
src/html/components/header.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<header class="header">
|
||||||
|
<div class="container">
|
||||||
|
<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>
|
||||||
|
<a class="github-button header__github-button" href="https://github.com/atomiks/30-seconds-of-css" data-icon="octicon-star" data-size="large" data-show-count="true" aria-label="Star atomiks/30-seconds-of-css on GitHub">Star</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
4
src/html/components/main.html
Normal file
4
src/html/components/main.html
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<main class="main" id="main">
|
||||||
|
<div class="container">
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
9
src/html/components/sidebar.html
Normal file
9
src/html/components/sidebar.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<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>
|
||||||
1
src/html/components/tags.html
Normal file
1
src/html/components/tags.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<nav class="tags" aria-label="Filter by tags"></nav>
|
||||||
15
src/html/index.html
Normal file
15
src/html/index.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!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>
|
||||||
|
<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.">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.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>
|
||||||
28
src/js/components/CodepenCopy.js
Normal file
28
src/js/components/CodepenCopy.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
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)
|
||||||
|
})
|
||||||
@ -1,38 +0,0 @@
|
|||||||
import jump from '../deps/jump'
|
|
||||||
import { select, selectAll, easeOutQuint } from '../deps/utils'
|
|
||||||
|
|
||||||
const menu = select('.hamburger')
|
|
||||||
const links = select('.sidebar__links')
|
|
||||||
const ACTIVE_CLASS = 'is-active'
|
|
||||||
|
|
||||||
const toggle = () => {
|
|
||||||
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 => {
|
|
||||||
setTimeout(toggle, 40)
|
|
||||||
if (e.target.classList.contains('sidebar__link')) {
|
|
||||||
e.preventDefault()
|
|
||||||
jump(e.target.getAttribute('href'), {
|
|
||||||
duration: 750,
|
|
||||||
offset: window.innerWidth <= 768 ? -64 : -32,
|
|
||||||
easing: easeOutQuint
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
document.addEventListener('click', e => {
|
|
||||||
if (
|
|
||||||
!e.target.closest('.sidebar__links') &&
|
|
||||||
!e.target.closest('.hamburger') &&
|
|
||||||
links.classList.contains(ACTIVE_CLASS)
|
|
||||||
) {
|
|
||||||
toggle()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export default { toggle }
|
|
||||||
50
src/js/components/Sidebar.js
Normal file
50
src/js/components/Sidebar.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import jump from '../deps/jump'
|
||||||
|
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 => {
|
||||||
|
sections.forEach(section => {
|
||||||
|
section.style.display = 'block'
|
||||||
|
if (section.dataset.type !== data.type && data.type !== 'all') {
|
||||||
|
section.style.display = 'none'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export default { toggle }
|
||||||
13
src/js/components/Snippet.js
Normal file
13
src/js/components/Snippet.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { selectAll } from '../deps/utils'
|
||||||
|
|
||||||
|
const snippets = selectAll('.snippet')
|
||||||
|
EventHub.on('Tag.click', data => {
|
||||||
|
snippets.forEach(snippet => {
|
||||||
|
snippet.style.display = 'block'
|
||||||
|
if (data.type === 'all') return
|
||||||
|
const tags = selectAll('.tags__tag', snippet)
|
||||||
|
if (!tags.some(el => el.dataset.type === data.type)) {
|
||||||
|
snippet.style.display = 'none'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
14
src/js/components/Tag.js
Normal file
14
src/js/components/Tag.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { select, selectAll, on } from '../deps/utils'
|
||||||
|
|
||||||
|
const tagButtons = selectAll('button.tags__tag')
|
||||||
|
|
||||||
|
const onClick = function() {
|
||||||
|
tagButtons.forEach(button => button.classList.remove('is-active'))
|
||||||
|
this.classList.add('is-active')
|
||||||
|
|
||||||
|
EventHub.emit('Tag.click', {
|
||||||
|
type: this.dataset.type
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
tagButtons.forEach(button => on(button, 'click', onClick))
|
||||||
@ -1,8 +1,34 @@
|
|||||||
export const select = s => document.querySelector(s)
|
export const select = (s, parent = document) => parent.querySelector(s)
|
||||||
export const selectAll = s => [].slice.call(document.querySelectorAll(s))
|
|
||||||
|
export const selectAll = (s, parent = document) => [].slice.call(parent.querySelectorAll(s))
|
||||||
|
|
||||||
export const scrollY = () => window.scrollY || window.pageYOffset
|
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 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.
|
* Make iOS behave normally.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
// Deps
|
// Deps
|
||||||
|
import 'focus-visible'
|
||||||
import 'normalize.css'
|
import 'normalize.css'
|
||||||
import 'prismjs'
|
import 'prismjs'
|
||||||
|
import feather from 'feather-icons'
|
||||||
|
feather.replace()
|
||||||
|
|
||||||
// CSS
|
// CSS
|
||||||
import '../css/deps/prism.css'
|
import '../css/deps/prism.css'
|
||||||
@ -10,5 +13,8 @@ import '../css/index.scss'
|
|||||||
import './deps/polyfills'
|
import './deps/polyfills'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import Menu from './components/Menu'
|
import Sidebar from './components/Sidebar'
|
||||||
import BackToTopButton from './components/BackToTopButton'
|
import BackToTopButton from './components/BackToTopButton'
|
||||||
|
import Tag from './components/Tag'
|
||||||
|
import Snippet from './components/Snippet'
|
||||||
|
import CodepenCopy from './components/CodepenCopy'
|
||||||
|
|||||||
@ -1,8 +1,20 @@
|
|||||||
|
const fs = require('fs')
|
||||||
|
const { JSDOM } = require('jsdom')
|
||||||
|
|
||||||
exports.toKebabCase = str =>
|
exports.toKebabCase = str =>
|
||||||
str &&
|
str &&
|
||||||
str
|
str
|
||||||
.match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
|
.match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g)
|
||||||
.map(x => x.toLowerCase())
|
.map(x => x.toLowerCase())
|
||||||
.join('-');
|
.join('-')
|
||||||
|
|
||||||
exports.emptyHTML = (...els) => els.forEach(el => (el.innerHTML = ''))
|
exports.dom = path => {
|
||||||
|
const doc = new JSDOM(fs.readFileSync(path, 'utf8')).window.document
|
||||||
|
return path.includes('component') ? doc.body.firstElementChild : doc
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.createElement = str => {
|
||||||
|
const el = new JSDOM().window.document.createElement('div')
|
||||||
|
el.innerHTML = str
|
||||||
|
return el.firstElementChild
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user